Compare commits

...

110 Commits

Author SHA1 Message Date
sercan
82244fd40e Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 15:50:30 +03:00
sercan
c5433cb917 Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 15:49:55 +03:00
sercan
b6a3023ebd Increase the version number 2021-08-31 15:49:10 +03:00
sercan
693e44dbaf (QuickChat) Fixed: Overflowing issue 2021-08-31 15:47:35 +03:00
sercan
cf5f13d78e Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 10:18:08 +03:00
sercan
206ef711a4 (QuickChat) Don't use types from outside 2021-08-31 10:17:57 +03:00
sercan
ef85907d1b Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 10:17:19 +03:00
sercan
7e0cc1249e (QuickChat) Don't use types from outside 2021-08-31 10:17:06 +03:00
sercan
138a43da59 (QuickChat) Don't use types from outside 2021-08-31 10:16:13 +03:00
sercan
cafc3188cf Merge remote-tracking branch 'origin/demo' into starter 2021-08-31 10:07:33 +03:00
sercan
fe7e2514a6 Updated dependencies, increased the version number & updated the changelog 2021-08-31 09:43:10 +03:00
sercan
b8f0d9f0b2 (QuickChat) Added the Quick Chat bar 2021-08-31 09:21:28 +03:00
sercan
2a4a392153 (layout) Separated the Settings drawer from the layout component 2021-08-31 09:20:55 +03:00
sercan
c2fa88f4d4 (fuse/drawer) Fixed: Final opacity of the overlay is not permanent due to player being destroyed right after the animation 2021-08-17 22:05:13 +03:00
sercan
6381362326 Merge remote-tracking branch 'origin/demo' into starter 2021-08-12 17:05:40 +03:00
sercan
67d25012ec Increased the version number & updated the changelog 2021-08-12 17:04:57 +03:00
sercan
e1942f46fd Merge remote-tracking branch 'origin/demo' into starter 2021-08-12 16:57:58 +03:00
sercan
6de6a07778 Decreased budget sizes since new Fuse is a lot smaller compared to the one with the old design 2021-08-12 13:57:29 +03:00
sercan
e7ab0ea13f (apps/mailbox) Fixed: Compose dialog doesn't work correctly on small height resolutions
(apps/mailbox) Style improvements
2021-08-12 13:45:34 +03:00
sercan
4b686d86cc (@fuse/overrides) Fixed: Quill editor is not styled correctly by default 2021-08-12 13:42:59 +03:00
sercan
190395f14b (@fuse/theming) Better structuring on the themes.scss file
(@fuse) Disabled Angular Material 'theme' sanity check since we use 'all-component-themes' without a color map
2021-08-12 13:17:53 +03:00
sercan
4a9b8ee91e (dependencies) Updated Angular, Angular Material and various other dependencies 2021-08-12 11:32:04 +03:00
sercan
488fe8a1eb (ui/page-layouts) Fixed: Demo layout navigation appearance is not correct 2021-08-11 22:25:49 +03:00
sercan
db9a2a2433 (dependencies) Updated Angular, Angular Material and various other dependencies 2021-08-11 22:25:15 +03:00
sercan
3dda8479cf (tailwindcss) Removed old jsdoc from the config file 2021-08-08 22:27:24 +03:00
sercan
7c402670a1 (docs/confirmation) Updated the docs of the confirmation dialog 2021-08-04 10:10:07 +03:00
sercan
f6bf0fb5d3 (fuse/confirmation) Fixed: Dialog size cannot be updated using dialogRef's "updateSize" method 2021-08-04 09:54:35 +03:00
sercan
d44c004d01 Removed empty "styles" from component decorators 2021-08-04 09:46:36 +03:00
sercan
ce656430c8 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:40:12 +03:00
sercan
4ec40271c5 Updated the package.json version number 2021-07-29 10:39:16 +03:00
sercan
c93b1ce232 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:23:27 +03:00
sercan
b874b37db2 Merge remote-tracking branch 'origin/demo' into starter 2021-07-29 10:21:30 +03:00
sercan
b21cdf1655 Updated the changelog and increased the version number 2021-07-29 10:19:28 +03:00
sercan
6fff259fe3 (dependencies) Updated Angular, Angular Material and various other dependencies 2021-07-29 10:15:13 +03:00
sercan
8fcb0aea03 (docs) Updated the multi language guide 2021-07-29 10:14:52 +03:00
sercan
2c90770d9b (index) Updated the title, description and keywords 2021-07-23 22:29:45 +03:00
sercan
1581ea74cc (dashboards/finance) Added finance dashboard
(dashboards/crypto) Added crypto dashboard
2021-07-23 22:23:42 +03:00
sercan
569809aabb (dashboards/project) Small tweaks 2021-07-23 22:23:02 +03:00
sercan
dde9333120 (dashboards/project) Small tweaks on the header 2021-07-23 21:30:51 +03:00
sercan
10ec1790ca (dashboards/project) Module import order 2021-07-21 12:22:23 +03:00
sercan
a2ff55d4c1 (dashboards/project) Light header on light themes, small adjustments in the project selector 2021-07-21 12:22:02 +03:00
sercan
966e2db743 (apps/contacts) Small adjustments for better consistency
(apps/ecommerce/inventory) Small adjustments for better consistency
2021-07-21 11:36:43 +03:00
sercan
4287361a09 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 20:49:22 +03:00
sercan
4f19eb6171 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 20:48:48 +03:00
sercan
49cccde93b Increased the version number & updated the changelog 2021-07-16 20:47:50 +03:00
sercan
952b64297b (fuse/confirmation) Fixed: Confirmation dialog colors are not correct for the Dark mode 2021-07-16 20:45:35 +03:00
sercan
9b3ff6a724 Updated changelog 2021-07-16 12:56:35 +03:00
sercan
cd13f9d9a4 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 12:38:44 +03:00
sercan
db8c52f093 Merge remote-tracking branch 'origin/demo' into starter 2021-07-16 12:36:58 +03:00
sercan
961b86c8cb (apps/contacts) Added confirmation to the "Delete contact" action using FuseConfirmationService
(apps/ecommerce/inventory) Added confirmation to the "Delete product" action using FuseConfirmationService
(apps/scrumboard) Added confirmation to the "Delete list" action using FuseConfirmationService
(apps/tasks) Added confirmation to the "Delete task" action using FuseConfirmationService
2021-07-16 12:28:43 +03:00
sercan
8b977c0eeb Updated Angular, Angular Material and couple other dependencies & increased the version number 2021-07-16 12:00:46 +03:00
sercan
35e56cd6af (fuse/confirmation) Small tweak on the docs 2021-07-12 12:39:52 +03:00
sercan
c04550b887 (ui/confirmation-dialog) Created a separate page for FuseConfirmationService and moved the example configurator to there for better visibility 2021-07-12 12:37:47 +03:00
sercan
747b4f44c8 (ui/advanced-search) Moved the routing information into the module file 2021-07-12 12:37:06 +03:00
sercan
c4914c80b3 (fuse/confirmation) Small fix on the docs 2021-07-10 22:11:39 +03:00
sercan
178d09597b (fuse/confirmation) First iteration of the FuseConfirmationService 2021-07-10 22:03:58 +03:00
sercan
d206c55e6e (ui/fuse-components) Renamed the component reference 2021-07-09 11:49:21 +03:00
sercan
9abe887df1 (docs) Moved Fuse Components and Other Components into UI for better visibility and better categorization 2021-07-09 11:30:00 +03:00
sercan
b87fdc407c (Navigation) Casing fixes on navigation data 2021-07-09 11:12:52 +03:00
sercan
58440b63a7 (ui/material-components) Renamed the component 2021-07-09 11:12:07 +03:00
sercan
63f6edee9a (tailwindcss) Fixed: Ordered lists with "s" modifier causes builder to throw errors 2021-07-05 23:13:38 +03:00
sercan
2189232273 Merge remote-tracking branch 'origin/demo' into starter 2021-07-02 23:11:01 +03:00
sercan
ca8e422b21 (app.module) Alphabetical order on Router's extra options 2021-07-02 23:10:41 +03:00
sercan
36b89e75db Update package name 2021-07-02 23:05:27 +03:00
sercan
8c2ff122b2 Merge remote-tracking branch 'origin/demo' into starter 2021-07-02 23:01:10 +03:00
sercan
cc703081ca (dashboards/analytics) Removed unused chart options declarations 2021-07-02 22:59:47 +03:00
sercan
038c74df50 (transloco) Defined a fallback language 2021-07-02 22:50:25 +03:00
sercan
9abbf5fec2 (karma.conf.js) Updated the default path 2021-07-02 22:50:19 +03:00
sercan
0ebc16ec05 (package.json) Removed "e2e" script since we don't provide e2e testing environment by default 2021-07-02 22:50:10 +03:00
sercan
b2cb512b0e Merge remote-tracking branch 'origin/demo' into starter 2021-07-01 14:29:26 +03:00
sercan
21652570c2 Merge remote-tracking branch 'origin/demo' into starter 2021-07-01 14:28:05 +03:00
sercan
56c4eb0356 Updated changelog 2021-07-01 14:26:49 +03:00
sercan
a8a4f2b18d Updated changelog and increased the version number 2021-07-01 13:31:41 +03:00
sercan
36c8727616 (layout/settings-drawer) Fixed: Issues on small screen devices 2021-07-01 12:55:02 +03:00
sercan
5ea37aed99 (layouts) Fixed: Header buttons are not fitting on certain layouts
(layouts) Hide the "fuse-fullscreen" button on smaller devices since they don't support fullscreen
2021-07-01 12:50:34 +03:00
sercan
cbce71f155 (apps/help-center) Fixed: Small dark mode issues 2021-07-01 12:37:27 +03:00
sercan
ed47050232 (apps/ecommerce/inventory) Fixed: Tags selector border colors are not correct on dark mode 2021-07-01 12:37:09 +03:00
sercan
c86a538d41 (Angular Material) Apply rounded styles by default 2021-07-01 12:23:49 +03:00
sercan
f369206ef8 (layout/common) Explicitly define the overlay position strategy properties
(apps/contacts) Explicitly define the overlay position strategy properties
(apps/mailbox) Explicitly define the overlay position strategy properties
(apps/tasks) Explicitly define the overlay position strategy properties
2021-06-25 22:57:14 +03:00
sercan
1659d4babd (tailwindcss) Fixed: Dark mode classes are not being purged correctly due to the wrong safelist entry 2021-06-25 12:21:34 +03:00
sercan
fa37f99d33 (docs) Use alternative spacing since 12s are removed 2021-06-25 11:09:30 +03:00
sercan
331968ac5b (tailwindcss) Breaking: Removed 5, 6 & 12 fractional spacing values since they are not used in Demo by default and they are mostly not needed because of Flex and Grid. If you happen to use them, you can manually add them back. 2021-06-25 11:08:56 +03:00
sercan
27274c84d6 (@fuse/navigation) Fixed: Vertical navigation blocks scroll if it's destroyed while in 'over' mode and opened 2021-06-24 18:04:19 +03:00
sercan
4b8a101a3e (transloco) Fixed: Language files cannot be loaded if using a base href other than "/" 2021-06-24 10:54:03 +03:00
sercan
8080a85d40 (fuse-drawer) Fixed: Memory leak due to the animation player, thanks to Vadym Pidoplichko for coming up the issue and the solution. 2021-06-24 10:29:34 +03:00
sercan
36784c405f (package.json) Added "description" and "author" fields 2021-06-21 18:56:15 +03:00
sercan
8f4f7886d5 (fuse/navigation) Moved *ngIf directives into their own "ng-container" containers 2021-06-20 21:59:24 +03:00
sercan
d693a08136 (general) Tweaked the "Angular Material components" navigation item title 2021-06-20 21:30:10 +03:00
sercan
0c0ef40de3 (docs) Added docs about navigation tooltip 2021-06-20 21:29:37 +03:00
sercan
f4d737d3a3 (fuse/navigation) Added "tooltip" property to show tooltips on navigation items using MatTooltip 2021-06-20 21:28:47 +03:00
sercan
90891eb201 Merge remote-tracking branch 'origin/demo' into starter 2021-06-15 08:36:32 +03:00
sercan
6c7201b77a Merge remote-tracking branch 'origin/demo' into starter 2021-06-03 13:28:30 +03:00
sercan
90f869e7b9 Merge remote-tracking branch 'origin/demo' into starter 2021-05-31 10:46:26 +03:00
sercan
2d2db97416 Merge remote-tracking branch 'origin/demo' into starter 2021-05-24 14:33:12 +03:00
sercan
5daa2260e6 Merge remote-tracking branch 'origin/demo' into starter 2021-05-24 14:29:35 +03:00
sercan
af984fcba1 Merge remote-tracking branch 'origin/demo' into starter 2021-05-23 07:13:28 +03:00
sercan
97d3662417 Merge remote-tracking branch 'origin/demo' into starter 2021-05-21 12:17:06 +03:00
sercan
d897a244c8 Merge remote-tracking branch 'origin/demo' into starter 2021-05-18 16:25:53 +03:00
sercan
d146a92c79 Merge remote-tracking branch 'origin/demo' into starter 2021-05-15 13:18:13 +03:00
sercan
27b6858b76 Merge remote-tracking branch 'origin/demo' into starter 2021-05-06 17:12:50 +03:00
sercan
fcfba4c9e4 Merge remote-tracking branch 'origin/demo' into starter 2021-04-30 19:40:30 +03:00
sercan
40894e0aa3 Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/app.routing.ts
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/apps/academy/academy.service.ts
#	src/app/modules/admin/apps/academy/details/details.component.html
#	src/app/modules/admin/apps/academy/list/list.component.html
#	src/app/modules/admin/apps/mailbox/list/list.component.ts
#	src/app/modules/admin/docs/changelog/changelog.ts
#	src/app/modules/admin/pages/pricing/modern/modern.component.html
#	src/app/modules/admin/pages/pricing/simple/simple.component.html
#	src/app/modules/admin/pages/pricing/single/single.component.html
#	src/app/modules/admin/pages/pricing/table/table.component.html
2021-04-30 19:40:07 +03:00
sercan
8dcf21cb1a Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:23:15 +03:00
sercan
d917f03883 Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 10:20:06 +03:00
sercan
0f2ddbda83 Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/docs/changelog/changelog.ts
2021-04-26 09:56:44 +03:00
sercan
fa0d74504b Merge remote-tracking branch 'origin/demo' into starter 2021-04-26 09:56:29 +03:00
sercan
ad2b19a07a Merge remote-tracking branch 'origin/demo' into starter
# Conflicts:
#	src/app/app.routing.ts
#	src/app/mock-api/common/navigation/data.ts
#	src/app/modules/admin/apps/contacts/details/details.component.html
#	src/app/modules/admin/apps/file-manager/list/list.component.html
#	src/app/modules/admin/apps/file-manager/list/list.component.ts
#	src/app/modules/landing/home/home.component.html
2021-04-26 09:31:42 +03:00
sercan
4bf11591a2 (Assets) Added avatar images back 2021-04-19 13:08:24 +03:00
sercan
f45a605b4e Preparing the starter 2021-04-15 17:43:28 +03:00
sercan
c150a8902c Starter 2021-04-15 17:23:49 +03:00
824 changed files with 8757 additions and 75522 deletions

View File

@@ -12,7 +12,7 @@ Run `ng generate component component-name` to generate a new component. You can
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
@@ -20,7 +20,7 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice.
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help

View File

@@ -60,13 +60,13 @@
"budgets": [
{
"type": "initial",
"maximumWarning": "5mb",
"maximumError": "8mb"
"maximumWarning": "3mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "100kb",
"maximumError": "150kb"
"maximumWarning": "75kb",
"maximumError": "90kb"
}
],
"fileReplacements": [

View File

@@ -26,7 +26,7 @@ module.exports = function (config)
suppressAll: true // removes the duplicated traces
},
coverageReporter : {
dir : require('path').join(__dirname, './coverage/angular12'),
dir : require('path').join(__dirname, './coverage/fuse'),
subdir : '.',
reporters: [
{type: 'html'},

6259
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,30 @@
{
"name": "@fuse/demo",
"version": "13.1.0",
"name": "@fuse/starter",
"version": "13.6.1",
"description": "Fuse - Angular Admin Template and Starter Project",
"author": "https://themeforest.net/user/srcn",
"license": "https://themeforest.net/licenses/standard",
"private": true,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
"lint": "ng lint"
},
"dependencies": {
"@angular/animations": "12.0.4",
"@angular/cdk": "12.0.4",
"@angular/common": "12.0.4",
"@angular/compiler": "12.0.4",
"@angular/core": "12.0.4",
"@angular/forms": "12.0.4",
"@angular/material": "12.0.4",
"@angular/material-moment-adapter": "12.0.4",
"@angular/platform-browser": "12.0.4",
"@angular/platform-browser-dynamic": "12.0.4",
"@angular/router": "12.0.4",
"@angular/animations": "12.2.3",
"@angular/cdk": "12.2.3",
"@angular/common": "12.2.3",
"@angular/compiler": "12.2.3",
"@angular/core": "12.2.3",
"@angular/forms": "12.2.3",
"@angular/material": "12.2.3",
"@angular/material-moment-adapter": "12.2.3",
"@angular/platform-browser": "12.2.3",
"@angular/platform-browser-dynamic": "12.2.3",
"@angular/router": "12.2.3",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2",
@@ -31,60 +33,59 @@
"@fullcalendar/moment": "4.4.2",
"@fullcalendar/rrule": "4.4.2",
"@fullcalendar/timegrid": "4.4.2",
"@ngneat/transloco": "2.21.0",
"apexcharts": "3.27.1",
"@ngneat/transloco": "2.22.0",
"apexcharts": "3.28.1",
"crypto-js": "3.3.0",
"highlight.js": "11.0.1",
"highlight.js": "11.2.0",
"lodash-es": "4.17.21",
"moment": "2.29.1",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "12.0.1",
"ngx-quill": "14.0.0",
"perfect-scrollbar": "1.5.1",
"ngx-quill": "14.3.0",
"perfect-scrollbar": "1.5.2",
"quill": "1.3.7",
"rrule": "2.6.8",
"rxjs": "6.6.7",
"tslib": "2.3.0",
"tslib": "2.3.1",
"web-animations-js": "2.3.2",
"zone.js": "0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "12.0.4",
"@angular-eslint/builder": "12.1.0",
"@angular-eslint/eslint-plugin": "12.1.0",
"@angular-eslint/eslint-plugin-template": "12.1.0",
"@angular-eslint/schematics": "12.1.0",
"@angular-eslint/template-parser": "12.1.0",
"@angular/cli": "12.0.4",
"@angular/compiler-cli": "12.0.4",
"@angular/language-service": "12.0.4",
"@angular-devkit/build-angular": "12.2.3",
"@angular-eslint/builder": "12.3.1",
"@angular-eslint/eslint-plugin": "12.3.1",
"@angular-eslint/eslint-plugin-template": "12.3.1",
"@angular-eslint/schematics": "12.3.1",
"@angular-eslint/template-parser": "12.3.1",
"@angular/cli": "12.2.3",
"@angular/compiler-cli": "12.2.3",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
"@tailwindcss/typography": "0.4.1",
"@types/chroma-js": "2.1.3",
"@types/crypto-js": "3.1.47",
"@types/highlight.js": "10.1.0",
"@types/jasmine": "3.6.11",
"@types/lodash": "4.14.170",
"@types/jasmine": "3.8.2",
"@types/lodash": "4.14.172",
"@types/lodash-es": "4.17.4",
"@types/node": "12.20.15",
"@typescript-eslint/eslint-plugin": "4.26.1",
"@typescript-eslint/parser": "4.26.1",
"autoprefixer": "10.2.6",
"@types/node": "12.20.21",
"@typescript-eslint/eslint-plugin": "4.30.0",
"@typescript-eslint/parser": "4.30.0",
"autoprefixer": "10.3.3",
"chroma-js": "2.1.2",
"eslint": "7.28.0",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-jsdoc": "35.2.0",
"eslint": "7.32.0",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-jsdoc": "36.0.8",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.7.1",
"jasmine-core": "3.8.0",
"karma": "6.3.4",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.6.0",
"karma-jasmine-html-reporter": "1.7.0",
"lodash": "4.17.21",
"postcss": "8.3.3",
"tailwindcss": "2.1.4",
"typescript": "4.2.4"
"postcss": "8.3.6",
"tailwindcss": "2.2.9",
"typescript": "4.3.5"
}
}

View File

@@ -116,7 +116,7 @@ fuse-drawer {
left: 0;
right: 0;
z-index: 299;
opacity: 0;
opacity: 1;
background-color: rgba(0, 0, 0, 0.6);
/* Fixed mode */

View File

@@ -211,6 +211,12 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
*/
ngOnDestroy(): void
{
// Finish the animation
if ( this._player )
{
this._player.finish();
}
// Deregister the drawer from the registry
this._fuseDrawerService.deregisterComponent(this.name);
}
@@ -338,9 +344,18 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// Create the enter animation and attach it to the player
this._player = this._animationBuilder.build([
style({opacity: 0}),
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
]).create(this._overlay);
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
this._player = null;
});
// Play the animation
this._player.play();
@@ -373,6 +388,10 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
this._player = null;
// If the backdrop still exists...
if ( this._overlay )
{

View File

@@ -5,71 +5,83 @@
[ngClass]="item.classes?.wrapper">
<!-- Item with an internal link -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="item.link && !item.externalLink && !item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an external link -->
<a
class="fuse-horizontal-navigation-item"
*ngIf="item.link && item.externalLink && !item.function && !item.disabled"
[href]="item.link"
[target]="item.target || '_self'">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
<a
class="fuse-horizontal-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a function -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="!item.link && item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an internal link and function -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="item.link && !item.externalLink && item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-horizontal-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an external link and function -->
<a
class="fuse-horizontal-navigation-item"
*ngIf="item.link && item.externalLink && item.function && !item.disabled"
[href]="item.link"
[target]="item.target || '_self'"
(click)="item.function(item)"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
<a
class="fuse-horizontal-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a no link and no function -->
<div
class="fuse-horizontal-navigation-item"
*ngIf="!item.link && !item.function && !item.disabled"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && !item.function && !item.disabled">
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item is disabled -->
<div
class="fuse-horizontal-navigation-item fuse-horizontal-navigation-item-disabled"
*ngIf="item.disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.disabled">
<div class="fuse-horizontal-navigation-item fuse-horizontal-navigation-item-disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
</div>
@@ -77,11 +89,12 @@
<ng-template #itemTemplate>
<!-- Icon -->
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-horizontal-navigation-item-title-wrapper">
@@ -90,24 +103,24 @@
{{item.title}}
</span>
</div>
<div
class="fuse-horizontal-navigation-item-subtitle text-hint"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-horizontal-navigation-item-subtitle text-hint">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-horizontal-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-horizontal-navigation-item-badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</ng-template>

View File

@@ -10,7 +10,6 @@ import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@Component({
selector : 'fuse-horizontal-navigation-basic-item',
templateUrl : './basic.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDestroy

View File

@@ -1,59 +1,63 @@
<div
*ngIf="!child"
[ngClass]="{'fuse-horizontal-navigation-menu-active': trigger.menuOpen,
<ng-container *ngIf="!child">
<div
[ngClass]="{'fuse-horizontal-navigation-menu-active': trigger.menuOpen,
'fuse-horizontal-navigation-menu-active-forced': item.active}"
[matMenuTriggerFor]="matMenu"
(onMenuOpen)="triggerChangeDetection()"
(onMenuClose)="triggerChangeDetection()"
#trigger="matMenuTrigger">
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
</div>
[matMenuTriggerFor]="matMenu"
(onMenuOpen)="triggerChangeDetection()"
(onMenuClose)="triggerChangeDetection()"
#trigger="matMenuTrigger">
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
</div>
</ng-container>
<mat-menu
class="fuse-horizontal-navigation-menu-panel"
[overlapTrigger]="false"
#matMenu="matMenu">
<ng-container *ngFor="let item of item.children">
<ng-container *ngFor="let item of item.children; trackBy: trackByFn">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<div
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'basic'"
[disabled]="item.disabled"
mat-menu-item>
<fuse-horizontal-navigation-basic-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
</div>
<ng-container *ngIf="item.type === 'basic'">
<div
class="fuse-horizontal-navigation-menu-item"
[disabled]="item.disabled"
mat-menu-item>
<fuse-horizontal-navigation-basic-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
</div>
</ng-container>
<!-- Branch: aside, collapsable, group -->
<div
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'"
[disabled]="item.disabled"
[matMenuTriggerFor]="branch.matMenu"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
<fuse-horizontal-navigation-branch-item
[child]="true"
[item]="item"
[name]="name"
#branch></fuse-horizontal-navigation-branch-item>
</div>
<ng-container *ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'">
<div
class="fuse-horizontal-navigation-menu-item"
[disabled]="item.disabled"
[matMenuTriggerFor]="branch.matMenu"
mat-menu-item>
<ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
<fuse-horizontal-navigation-branch-item
[child]="true"
[item]="item"
[name]="name"
#branch></fuse-horizontal-navigation-branch-item>
</div>
</ng-container>
<!-- Divider -->
<div
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'divider'"
mat-menu-item>
<fuse-horizontal-navigation-divider-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-divider-item>
</div>
<ng-container *ngIf="item.type === 'divider'">
<div
class="fuse-horizontal-navigation-menu-item"
mat-menu-item>
<fuse-horizontal-navigation-divider-item
[item]="item"
[name]="name"></fuse-horizontal-navigation-divider-item>
</div>
</ng-container>
</ng-container>
@@ -74,14 +78,16 @@
<div
class="fuse-horizontal-navigation-item"
[ngClass]="{'fuse-horizontal-navigation-item-disabled': item.disabled,
'fuse-horizontal-navigation-item-active-forced': item.active}">
'fuse-horizontal-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<!-- Icon -->
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-horizontal-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-horizontal-navigation-item-title-wrapper">
@@ -90,25 +96,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-horizontal-navigation-item-subtitle text-hint"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-horizontal-navigation-item-subtitle text-hint">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-horizontal-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-horizontal-navigation-item-badge">
<div
class="fuse-horizontal-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</div>
</div>

View File

@@ -10,7 +10,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-horizontal-navigation-branch-item',
templateUrl : './branch.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDestroy
@@ -81,4 +80,15 @@ export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDe
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
}

View File

@@ -8,7 +8,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-horizontal-navigation-divider-item',
templateUrl : './divider.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseHorizontalNavigationDividerItemComponent implements OnInit, OnDestroy

View File

@@ -8,7 +8,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-horizontal-navigation-spacer-item',
templateUrl : './spacer.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseHorizontalNavigationSpacerItemComponent implements OnInit, OnDestroy

View File

@@ -1,30 +1,33 @@
<div class="fuse-horizontal-navigation-wrapper">
<ng-container *ngFor="let item of navigation">
<ng-container *ngFor="let item of navigation; trackBy: trackByFn">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-horizontal-navigation-basic-item
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-horizontal-navigation-basic-item
class="fuse-horizontal-navigation-menu-item"
[item]="item"
[name]="name"></fuse-horizontal-navigation-basic-item>
</ng-container>
<!-- Branch: aside, collapsable, group -->
<fuse-horizontal-navigation-branch-item
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'"
[item]="item"
[name]="name"></fuse-horizontal-navigation-branch-item>
<ng-container *ngIf="item.type === 'aside' || item.type === 'collapsable' || item.type === 'group'">
<fuse-horizontal-navigation-branch-item
class="fuse-horizontal-navigation-menu-item"
[item]="item"
[name]="name"></fuse-horizontal-navigation-branch-item>
</ng-container>
<!-- Spacer -->
<fuse-horizontal-navigation-spacer-item
class="fuse-horizontal-navigation-menu-item"
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-horizontal-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-horizontal-navigation-spacer-item
class="fuse-horizontal-navigation-menu-item"
[item]="item"
[name]="name"></fuse-horizontal-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -15,6 +15,7 @@ export interface FuseNavigationItem
hidden?: (item: FuseNavigationItem) => boolean;
active?: boolean;
disabled?: boolean;
tooltip?: string;
link?: string;
externalLink?: boolean;
target?:

View File

@@ -7,14 +7,16 @@
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active': active,
'fuse-vertical-navigation-item-disabled': item.disabled,
'fuse-vertical-navigation-item-active-forced': item.active}">
'fuse-vertical-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -23,25 +25,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</div>
@@ -57,35 +59,40 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -10,7 +10,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-vertical-navigation-aside-item',
templateUrl : './aside.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnInit, OnDestroy

View File

@@ -5,70 +5,84 @@
[ngClass]="item.classes?.wrapper">
<!-- Item with an internal link -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && !item.externalLink && !item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && !item.externalLink && !item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with an external link -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && item.externalLink && !item.function && !item.disabled"
[href]="item.link"
[target]="item.target || '_self'">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && !item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a function -->
<div
class="fuse-vertical-navigation-item"
*ngIf="!item.link && item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && item.function && !item.disabled">
<div
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item with an internal link and function -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && !item.externalLink && item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && !item.externalLink && item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[routerLink]="[item.link]"
[routerLinkActive]="'fuse-vertical-navigation-item-active'"
[routerLinkActiveOptions]="isActiveMatchOptions"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with an external link and function -->
<a
class="fuse-vertical-navigation-item"
*ngIf="item.link && item.externalLink && item.function && !item.disabled"
[href]="item.link"
[target]="item.target || '_self'"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
<ng-container *ngIf="item.link && item.externalLink && item.function && !item.disabled">
<a
class="fuse-vertical-navigation-item"
[href]="item.link"
[target]="item.target || '_self'"
[matTooltip]="item.tooltip || ''"
(click)="item.function(item)">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</a>
</ng-container>
<!-- Item with a no link and no function -->
<div
class="fuse-vertical-navigation-item"
*ngIf="!item.link && !item.function && !item.disabled"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="!item.link && !item.function && !item.disabled">
<div
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-active-forced': item.active}"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
<!-- Item is disabled -->
<div
class="fuse-vertical-navigation-item fuse-vertical-navigation-item-disabled"
*ngIf="item.disabled">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
<ng-container *ngIf="item.disabled">
<div
class="fuse-vertical-navigation-item fuse-vertical-navigation-item-disabled"
[matTooltip]="item.tooltip || ''">
<ng-container *ngTemplateOutlet="itemTemplate"></ng-container>
</div>
</ng-container>
</div>
@@ -76,11 +90,12 @@
<ng-template #itemTemplate>
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -89,24 +104,24 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</ng-template>

View File

@@ -10,7 +10,6 @@ import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@Component({
selector : 'fuse-vertical-navigation-basic-item',
templateUrl : './basic.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestroy

View File

@@ -6,14 +6,16 @@
<div
class="fuse-vertical-navigation-item"
[ngClass]="{'fuse-vertical-navigation-item-disabled': item.disabled}"
[matTooltip]="item.tooltip || ''"
(click)="toggleCollapsable()">
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -22,25 +24,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
<!-- Arrow -->
<mat-icon
@@ -62,35 +64,40 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -11,7 +11,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-vertical-navigation-collapsable-item',
templateUrl : './collapsable.component.html',
styles : [],
animations : fuseAnimations,
changeDetection: ChangeDetectionStrategy.OnPush
})

View File

@@ -8,7 +8,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-vertical-navigation-divider-item',
templateUrl : './divider.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseVerticalNavigationDividerItemComponent implements OnInit, OnDestroy

View File

@@ -7,11 +7,12 @@
<div class="fuse-vertical-navigation-item">
<!-- Icon -->
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
*ngIf="item.icon"
[svgIcon]="item.icon"></mat-icon>
<ng-container *ngIf="item.icon">
<mat-icon
class="fuse-vertical-navigation-item-icon"
[ngClass]="item.classes?.icon"
[svgIcon]="item.icon"></mat-icon>
</ng-container>
<!-- Title & Subtitle -->
<div class="fuse-vertical-navigation-item-title-wrapper">
@@ -20,25 +21,25 @@
{{item.title}}
</span>
</div>
<div
class="fuse-vertical-navigation-item-subtitle"
*ngIf="item.subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
<ng-container *ngIf="item.subtitle">
<div class="fuse-vertical-navigation-item-subtitle">
<span [ngClass]="item.classes?.subtitle">
{{item.subtitle}}
</span>
</div>
</ng-container>
</div>
<!-- Badge -->
<div
class="fuse-vertical-navigation-item-badge"
*ngIf="item.badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
<ng-container *ngIf="item.badge">
<div class="fuse-vertical-navigation-item-badge">
<div
class="fuse-vertical-navigation-item-badge-content"
[ngClass]="item.badge.classes">
{{item.badge.title}}
</div>
</div>
</div>
</ng-container>
</div>
@@ -50,35 +51,40 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>

View File

@@ -9,7 +9,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-vertical-navigation-group-item',
templateUrl : './group.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseVerticalNavigationGroupItemComponent implements OnInit, OnDestroy

View File

@@ -8,7 +8,6 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
@Component({
selector : 'fuse-vertical-navigation-spacer-item',
templateUrl : './spacer.component.html',
styles : [],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FuseVerticalNavigationSpacerItemComponent implements OnInit, OnDestroy

View File

@@ -24,46 +24,52 @@
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Aside -->
<fuse-vertical-navigation-aside-item
*ngIf="item.type === 'aside'"
[item]="item"
[name]="name"
[activeItemId]="activeAsideItemId"
[autoCollapse]="autoCollapse"
[skipChildren]="true"
(click)="toggleAside(item)"></fuse-vertical-navigation-aside-item>
<ng-container *ngIf="item.type === 'aside'">
<fuse-vertical-navigation-aside-item
[item]="item"
[name]="name"
[activeItemId]="activeAsideItemId"
[autoCollapse]="autoCollapse"
[skipChildren]="true"
(click)="toggleAside(item)"></fuse-vertical-navigation-aside-item>
</ng-container>
<!-- Basic -->
<fuse-vertical-navigation-basic-item
*ngIf="item.type === 'basic'"
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
<ng-container *ngIf="item.type === 'basic'">
<fuse-vertical-navigation-basic-item
[item]="item"
[name]="name"></fuse-vertical-navigation-basic-item>
</ng-container>
<!-- Collapsable -->
<fuse-vertical-navigation-collapsable-item
*ngIf="item.type === 'collapsable'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
<ng-container *ngIf="item.type === 'collapsable'">
<fuse-vertical-navigation-collapsable-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-collapsable-item>
</ng-container>
<!-- Divider -->
<fuse-vertical-navigation-divider-item
*ngIf="item.type === 'divider'"
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
<ng-container *ngIf="item.type === 'divider'">
<fuse-vertical-navigation-divider-item
[item]="item"
[name]="name"></fuse-vertical-navigation-divider-item>
</ng-container>
<!-- Group -->
<fuse-vertical-navigation-group-item
*ngIf="item.type === 'group'"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-group-item>
<ng-container *ngIf="item.type === 'group'">
<fuse-vertical-navigation-group-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-group-item>
</ng-container>
<!-- Spacer -->
<fuse-vertical-navigation-spacer-item
*ngIf="item.type === 'spacer'"
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
<ng-container *ngIf="item.type === 'spacer'">
<fuse-vertical-navigation-spacer-item
[item]="item"
[name]="name"></fuse-vertical-navigation-spacer-item>
</ng-container>
</ng-container>
@@ -84,31 +90,33 @@
</div>
<!-- Aside -->
<div
class="fuse-vertical-navigation-aside-wrapper"
*ngIf="activeAsideItemId"
fuseScrollbar
[fuseScrollbarOptions]="{wheelPropagation: false, suppressScrollX: true}"
[@fadeInLeft]="position === 'left'"
[@fadeInRight]="position === 'right'"
[@fadeOutLeft]="position === 'left'"
[@fadeOutRight]="position === 'right'">
<ng-container *ngIf="activeAsideItemId">
<div
class="fuse-vertical-navigation-aside-wrapper"
fuseScrollbar
[fuseScrollbarOptions]="{wheelPropagation: false, suppressScrollX: true}"
[@fadeInLeft]="position === 'left'"
[@fadeInRight]="position === 'right'"
[@fadeOutLeft]="position === 'left'"
[@fadeOutRight]="position === 'right'">
<!-- Items -->
<ng-container *ngFor="let item of navigation; trackBy: trackByFn">
<!-- Items -->
<ng-container *ngFor="let item of navigation; trackBy: trackByFn">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Skip the hidden items -->
<ng-container *ngIf="(item.hidden && !item.hidden(item)) || !item.hidden">
<!-- Aside -->
<fuse-vertical-navigation-aside-item
*ngIf="item.type === 'aside' && item.id === activeAsideItemId"
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-aside-item>
<!-- Aside -->
<ng-container *ngIf="item.type === 'aside' && item.id === activeAsideItemId">
<fuse-vertical-navigation-aside-item
[item]="item"
[name]="name"
[autoCollapse]="autoCollapse"></fuse-vertical-navigation-aside-item>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</div>
</div>
</ng-container>

View File

@@ -374,6 +374,10 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
*/
ngOnDestroy(): void
{
// Forcefully close the navigation and aside in case they are opened
this.close();
this.closeAside();
// Deregister the navigation component from the registry
this._fuseNavigationService.deregisterComponent(this.name);

View File

@@ -1,5 +1,7 @@
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { MATERIAL_SANITY_CHECKS } from '@angular/material/core';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
import { FuseConfirmationModule } from '@fuse/services/confirmation';
import { FuseMediaWatcherModule } from '@fuse/services/media-watcher/media-watcher.module';
import { FuseSplashScreenModule } from '@fuse/services/splash-screen/splash-screen.module';
import { FuseTailwindConfigModule } from '@fuse/services/tailwind/tailwind.module';
@@ -7,12 +9,22 @@ import { FuseUtilsModule } from '@fuse/services/utils/utils.module';
@NgModule({
imports : [
FuseConfirmationModule,
FuseMediaWatcherModule,
FuseSplashScreenModule,
FuseTailwindConfigModule,
FuseUtilsModule
],
providers: [
{
// Disable 'theme' sanity check
provide : MATERIAL_SANITY_CHECKS,
useValue: {
doctype: true,
theme : false,
version: true
}
},
{
// Use the 'fill' appearance on Angular Material form fields by default
provide : MAT_FORM_FIELD_DEFAULT_OPTIONS,

View File

@@ -0,0 +1,31 @@
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { FuseConfirmationService } from '@fuse/services/confirmation/confirmation.service';
import { FuseConfirmationDialogComponent } from '@fuse/services/confirmation/dialog/dialog.component';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [
FuseConfirmationDialogComponent
],
imports: [
MatButtonModule,
MatDialogModule,
MatIconModule,
CommonModule
],
providers : [
FuseConfirmationService
]
})
export class FuseConfirmationModule
{
/**
* Constructor
*/
constructor(private _fuseConfirmationService: FuseConfirmationService)
{
}
}

View File

@@ -0,0 +1,58 @@
import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { merge } from 'lodash-es';
import { FuseConfirmationDialogComponent } from '@fuse/services/confirmation/dialog/dialog.component';
import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
@Injectable()
export class FuseConfirmationService
{
private _defaultConfig: FuseConfirmationConfig = {
title : 'Confirm action',
message : 'Are you sure you want to confirm this action?',
icon : {
show : true,
name : 'heroicons_outline:exclamation',
color: 'warn'
},
actions : {
confirm: {
show : true,
label: 'Confirm',
color: 'warn'
},
cancel : {
show : true,
label: 'Cancel'
}
},
dismissible: false
};
/**
* Constructor
*/
constructor(
private _matDialog: MatDialog
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
open(config: FuseConfirmationConfig = {}): MatDialogRef<FuseConfirmationDialogComponent>
{
// Merge the user config with the default config
const userConfig = merge({}, this._defaultConfig, config);
// Open the dialog
return this._matDialog.open(FuseConfirmationDialogComponent, {
autoFocus : false,
disableClose: !userConfig.dismissible,
data : userConfig,
panelClass : 'fuse-confirmation-dialog-panel'
});
}
}

View File

@@ -0,0 +1,22 @@
export interface FuseConfirmationConfig
{
title?: string;
message?: string;
icon?: {
show?: boolean;
name?: string;
color?: 'primary' | 'accent' | 'warn' | 'basic' | 'info' | 'success' | 'warning' | 'error';
};
actions?: {
confirm?: {
show?: boolean;
label?: string;
color?: 'primary' | 'accent' | 'warn';
};
cancel?: {
show?: boolean;
label?: string;
};
};
dismissible?: boolean;
}

View File

@@ -0,0 +1,85 @@
<div class="relative flex flex-col w-full h-full">
<!-- Dismiss button -->
<ng-container *ngIf="data.dismissible">
<div class="absolute top-0 right-0 pt-4 pr-4">
<button
mat-icon-button
[matDialogClose]="undefined">
<mat-icon
class="text-secondary"
[svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>
</ng-container>
<!-- Content -->
<div class="flex flex-col sm:flex-row flex-auto items-center sm:items-start p-8 pb-6 sm:pb-8">
<!-- Icon -->
<ng-container *ngIf="data.icon.show">
<div
class="flex flex-0 items-center justify-center w-10 h-10 sm:mr-4 rounded-full"
[ngClass]="{'text-primary-600 bg-primary-100 dark:text-primary-50 dark:bg-primary-600': data.icon.color === 'primary',
'text-accent-600 bg-accent-100 dark:text-accent-50 dark:bg-accent-600': data.icon.color === 'accent',
'text-warn-600 bg-warn-100 dark:text-warn-50 dark:bg-warn-600': data.icon.color === 'warn',
'text-gray-600 bg-gray-100 dark:text-gray-50 dark:bg-gray-600': data.icon.color === 'basic',
'text-blue-600 bg-blue-100 dark:text-blue-50 dark:bg-blue-600': data.icon.color === 'info',
'text-green-500 bg-green-100 dark:text-green-50 dark:bg-green-500': data.icon.color === 'success',
'text-amber-500 bg-amber-100 dark:text-amber-50 dark:bg-amber-500': data.icon.color === 'warning',
'text-red-600 bg-red-100 dark:text-red-50 dark:bg-red-600': data.icon.color === 'error'
}">
<mat-icon
class="text-current"
[svgIcon]="data.icon.name"></mat-icon>
</div>
</ng-container>
<ng-container *ngIf="data.title || data.message">
<div class="flex flex-col items-center sm:items-start mt-4 sm:mt-0 sm:pr-8 space-y-1 text-center sm:text-left">
<!-- Title -->
<ng-container *ngIf="data.title">
<div
class="text-xl leading-6 font-medium"
[innerHTML]="data.title"></div>
</ng-container>
<!-- Message -->
<ng-container *ngIf="data.message">
<div
class="text-secondary"
[innerHTML]="data.message"></div>
</ng-container>
</div>
</ng-container>
</div>
<!-- Actions -->
<ng-container *ngIf="data.actions.confirm.show || data.actions.cancel.show">
<div class="flex items-center justify-center sm:justify-end px-6 py-4 space-x-3 bg-gray-50 dark:bg-black dark:bg-opacity-10">
<!-- Cancel -->
<ng-container *ngIf="data.actions.cancel.show">
<button
mat-stroked-button
[matDialogClose]="'cancelled'">
{{data.actions.cancel.label}}
</button>
</ng-container>
<!-- Confirm -->
<ng-container *ngIf="data.actions.confirm.show">
<button
mat-flat-button
[color]="data.actions.confirm.color"
[matDialogClose]="'confirmed'">
{{data.actions.confirm.label}}
</button>
</ng-container>
</div>
</ng-container>
</div>

View File

@@ -0,0 +1,52 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
@Component({
selector : 'fuse-confirmation-dialog',
templateUrl : './dialog.component.html',
styles : [
/* language=SCSS */
`
.fuse-confirmation-dialog-panel {
@screen md {
@apply w-128;
}
.mat-dialog-container {
padding: 0 !important;
}
}
`
],
encapsulation: ViewEncapsulation.None
})
export class FuseConfirmationDialogComponent implements OnInit
{
/**
* Constructor
*/
constructor(
@Inject(MAT_DIALOG_DATA) public data: FuseConfirmationConfig,
public matDialogRef: MatDialogRef<FuseConfirmationDialogComponent>
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
}

View File

@@ -0,0 +1 @@
export * from '@fuse/services/confirmation/public-api';

View File

@@ -0,0 +1,3 @@
export * from '@fuse/services/confirmation/confirmation.module';
export * from '@fuse/services/confirmation/confirmation.service';
export * from '@fuse/services/confirmation/confirmation.types';

View File

@@ -136,11 +136,8 @@
.mat-flat-button,
.mat-raised-button,
.mat-stroked-button {
.fuse-mat-rounded & {
padding: 0 20px;
border-radius: 9999px;
}
padding: 0 20px !important;
border-radius: 9999px !important;
}
/* Target all buttons */
@@ -334,6 +331,8 @@
/* @ Button Toggle
/* ----------------------------------------------------------------------------------------------------- */
.mat-button-toggle-group {
border: none !important;
@apply space-x-1;
&.mat-button-toggle-group-appearance-standard {
@@ -341,36 +340,27 @@
background-clip: padding-box;
}
}
}
/* Rounded design */
.fuse-mat-rounded {
.mat-button-toggle-group {
.mat-button-toggle {
border-radius: 9999px;
overflow: hidden;
border: none !important;
@apply space-x-1;
font-weight: 500;
.mat-button-toggle {
border-radius: 9999px;
overflow: hidden;
border: none !important;
font-weight: 500;
&.mat-button-toggle-checked {
.mat-button-toggle-label-content {
@apply text-default #{'!important'};
}
}
&.mat-button-toggle-checked {
.mat-button-toggle-label-content {
padding: 0 20px;
@apply text-secondary;
@apply text-default #{'!important'};
}
}
.mat-ripple {
border-radius: 9999px;
}
.mat-button-toggle-label-content {
padding: 0 20px;
@apply text-secondary;
}
.mat-ripple {
border-radius: 9999px;
}
}
}
@@ -1329,62 +1319,55 @@
opacity: 0 !important;
}
}
.mat-tab-header {
border-bottom: none !important;
.mat-tab-label-container {
padding: 0 24px;
.mat-tab-list {
.mat-tab-labels {
.mat-tab-label {
min-width: 0 !important;
height: 40px !important;
padding: 0 20px !important;
border-radius: 9999px !important;
@apply text-secondary;
&.mat-tab-label-active {
@apply bg-gray-700 bg-opacity-10 dark:bg-gray-50 dark:bg-opacity-10 #{'!important'};
@apply text-default #{'!important'};
}
+ .mat-tab-label {
margin-left: 4px;
}
.mat-tab-label-content {
line-height: 20px;
}
}
}
.mat-ink-bar {
display: none !important;
}
}
}
}
.mat-tab-body-content {
padding: 24px;
}
}
.mat-tab-label {
opacity: 1 !important;
}
/* Rounded design */
.fuse-mat-rounded {
.mat-tab-group {
.mat-tab-header {
border-bottom: none !important;
.mat-tab-label-container {
padding: 0 24px;
.mat-tab-list {
.mat-tab-labels {
.mat-tab-label {
min-width: 0 !important;
height: 40px !important;
padding: 0 20px !important;
border-radius: 9999px !important;
@apply text-secondary;
&.mat-tab-label-active {
@apply bg-gray-700 bg-opacity-10 dark:bg-gray-50 dark:bg-opacity-10 #{'!important'};
@apply text-default #{'!important'};
}
+ .mat-tab-label {
margin-left: 4px;
}
.mat-tab-label-content {
line-height: 20px;
}
}
}
.mat-ink-bar {
display: none !important;
}
}
}
}
.mat-tab-body-content {
padding: 24px;
}
}
}
/* ----------------------------------------------------------------------------------------------------- */
/* @ Textarea
/* ----------------------------------------------------------------------------------------------------- */

View File

@@ -4,11 +4,12 @@
.ql-toolbar {
border-radius: 6px 6px 0 0;
padding: 0 !important;
@apply bg-gray-100 border-gray-300;
@apply bg-gray-100;
@apply border-gray-300 border-opacity-100 #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05);
@apply border-gray-500;
@apply border-gray-500 #{'!important'};
}
.ql-formats {
@@ -81,26 +82,22 @@
.ql-container {
overflow: hidden;
border-radius: 0 0 6px 6px;
@apply border-gray-300 shadow-sm;
@apply border-gray-300 border-opacity-100 shadow-sm #{'!important'};
.dark & {
@apply border-gray-500;
@apply border-gray-500 #{'!important'};
}
.ql-editor {
min-height: 160px;
max-height: 160px;
height: 160px;
@apply bg-gray-50;
@apply bg-card;
.dark & {
background-color: rgba(0, 0, 0, 0.05);
}
&:focus {
@apply bg-card;
}
&.ql-blank::before {
@apply text-hint;
}

View File

@@ -18,81 +18,6 @@
)
));
/* Prepare the Background and Foreground maps */
$background-light: (
status-bar: #CBD5E1, /* blueGray.300 */
app-bar: #FFFFFF,
background: #F1F5F9, /* blueGray.100 */
hover: rgba(148, 163, 184, 0.12), /* blueGray.400 + opacity */
card: #FFFFFF,
dialog: #FFFFFF,
disabled-button: rgba(148, 163, 184, 0.38), /* blueGray.400 + opacity */
raised-button: #FFFFFF,
focused-button: #64748B, /* blueGray.500 */
selected-button: #E2E8F0, /* blueGray.200 */
selected-disabled-button: #E2E8F0, /* blueGray.200 */
disabled-button-toggle: #CBD5E1, /* blueGray.300 */
unselected-chip: #E2E8F0, /* blueGray.200 */
disabled-list-option: #CBD5E1, /* blueGray.300 */
tooltip: #1E293B /* blueGray.800 */
);
$background-dark: (
status-bar: #0F172A, /* blueGray.900 */
app-bar: #0F172A, /* blueGray.900 */
background: #0F172A, /* blueGray.900 */
hover: rgba(255, 255, 255, 0.05),
card: #1E293B, /* blueGray.800 */
dialog: #1E293B, /* blueGray.800 */
disabled-button: rgba(15, 23, 42, 0.38), /* blueGray.900 + opacity */
raised-button: #0F172A, /* blueGray.900 */
focused-button: #E2E8F0, /* blueGray.200 */
selected-button: rgba(255, 255, 255, 0.05),
selected-disabled-button: #1E293B, /* blueGray.800 */
disabled-button-toggle: #0F172A, /* blueGray.900 */
unselected-chip: #475569, /* blueGray.600 */
disabled-list-option: #E2E8F0, /* blueGray.200 */
tooltip: #64748B /* blueGray.500 */
);
$foreground-light: (
base: #000000,
divider: #E2E8F0, /* blueGray.200 */
dividers: #E2E8F0, /* blueGray.200 */
disabled: #94A3B8, /* blueGray.400 */
disabled-button: #94A3B8, /* blueGray.400 */
disabled-text: #94A3B8, /* blueGray.400 */
elevation: #000000,
hint-text: #94A3B8, /* blueGray.400 */
secondary-text: #64748B, /* blueGray.500 */
icon: #64748B, /* blueGray.500 */
icons: #64748B, /* blueGray.500 */
mat-icon: #64748B, /* blueGray.500 */
text: #1E293B, /* blueGray.800 */
slider-min: #1E293B, /* blueGray.800 */
slider-off: #CBD5E1, /* blueGray.300 */
slider-off-active: #94A3B8 /* blueGray.400 */
);
$foreground-dark: (
base: #FFFFFF,
divider: rgba(241, 245, 249, 0.12), /* blueGray.100 + opacity */
dividers: rgba(241, 245, 249, 0.12), /* blueGray.100 + opacity */
disabled: #475569, /* blueGray.600 */
disabled-button: #1E293B, /* blueGray.800 */
disabled-text: #475569, /* blueGray.600 */
elevation: #000000,
hint-text: #64748B, /* blueGray.500 */
secondary-text: #94A3B8, /* blueGray.400 */
icon: #F1F5F9, /* blueGray.100 */
icons: #F1F5F9, /* blueGray.100 */
mat-icon: #94A3B8, /* blueGray.400 */
text: #FFFFFF,
slider-min: #FFFFFF,
slider-off: #64748B, /* blueGray.500 */
slider-off-active: #94A3B8 /* blueGray.400 */
);
/* Generate Primary, Accent and Warn palettes */
$palettes: ();
@each $name in (primary, accent, warn) {
@@ -145,8 +70,41 @@ body .light {
accent: map.get(map.get($base-light-theme, color), accent),
warn: map.get(map.get($base-light-theme, color), warn),
is-dark: map.get(map.get($base-light-theme, color), is-dark),
foreground: $foreground-light,
background: $background-light
foreground: (
base: #000000,
divider: #E2E8F0, /* blueGray.200 */
dividers: #E2E8F0, /* blueGray.200 */
disabled: #94A3B8, /* blueGray.400 */
disabled-button: #94A3B8, /* blueGray.400 */
disabled-text: #94A3B8, /* blueGray.400 */
elevation: #000000,
hint-text: #94A3B8, /* blueGray.400 */
secondary-text: #64748B, /* blueGray.500 */
icon: #64748B, /* blueGray.500 */
icons: #64748B, /* blueGray.500 */
mat-icon: #64748B, /* blueGray.500 */
text: #1E293B, /* blueGray.800 */
slider-min: #1E293B, /* blueGray.800 */
slider-off: #CBD5E1, /* blueGray.300 */
slider-off-active: #94A3B8 /* blueGray.400 */
),
background: (
status-bar: #CBD5E1, /* blueGray.300 */
app-bar: #FFFFFF,
background: #F1F5F9, /* blueGray.100 */
hover: rgba(148, 163, 184, 0.12), /* blueGray.400 + opacity */
card: #FFFFFF,
dialog: #FFFFFF,
disabled-button: rgba(148, 163, 184, 0.38), /* blueGray.400 + opacity */
raised-button: #FFFFFF,
focused-button: #64748B, /* blueGray.500 */
selected-button: #E2E8F0, /* blueGray.200 */
selected-disabled-button: #E2E8F0, /* blueGray.200 */
disabled-button-toggle: #CBD5E1, /* blueGray.300 */
unselected-chip: #E2E8F0, /* blueGray.200 */
disabled-list-option: #CBD5E1, /* blueGray.300 */
tooltip: #1E293B /* blueGray.800 */
)
)
);
@@ -166,8 +124,41 @@ body .dark {
accent: map.get(map.get($base-dark-theme, color), accent),
warn: map.get(map.get($base-dark-theme, color), warn),
is-dark: map.get(map.get($base-dark-theme, color), is-dark),
foreground: $foreground-dark,
background: $background-dark
foreground: (
base: #FFFFFF,
divider: rgba(241, 245, 249, 0.12), /* blueGray.100 + opacity */
dividers: rgba(241, 245, 249, 0.12), /* blueGray.100 + opacity */
disabled: #475569, /* blueGray.600 */
disabled-button: #1E293B, /* blueGray.800 */
disabled-text: #475569, /* blueGray.600 */
elevation: #000000,
hint-text: #64748B, /* blueGray.500 */
secondary-text: #94A3B8, /* blueGray.400 */
icon: #F1F5F9, /* blueGray.100 */
icons: #F1F5F9, /* blueGray.100 */
mat-icon: #94A3B8, /* blueGray.400 */
text: #FFFFFF,
slider-min: #FFFFFF,
slider-off: #64748B, /* blueGray.500 */
slider-off-active: #94A3B8 /* blueGray.400 */
),
background: (
status-bar: #0F172A, /* blueGray.900 */
app-bar: #0F172A, /* blueGray.900 */
background: #0F172A, /* blueGray.900 */
hover: rgba(255, 255, 255, 0.05),
card: #1E293B, /* blueGray.800 */
dialog: #1E293B, /* blueGray.800 */
disabled-button: rgba(15, 23, 42, 0.38), /* blueGray.900 + opacity */
raised-button: #0F172A, /* blueGray.900 */
focused-button: #E2E8F0, /* blueGray.200 */
selected-button: rgba(255, 255, 255, 0.05),
selected-disabled-button: #1E293B, /* blueGray.800 */
disabled-button-toggle: #0F172A, /* blueGray.900 */
unselected-chip: #475569, /* blueGray.600 */
disabled-list-option: #E2E8F0, /* blueGray.200 */
tooltip: #64748B /* blueGray.500 */
)
)
);

View File

@@ -1,3 +1,3 @@
import { Version } from '@fuse/version/version';
export const FUSE_VERSION = new Version('13.1.0').full;
export const FUSE_VERSION = new Version('13.6.1').full;

View File

@@ -14,8 +14,8 @@ import { AppComponent } from 'app/app.component';
import { appRoutes } from 'app/app.routing';
const routerConfig: ExtraOptions = {
scrollPositionRestoration: 'enabled',
preloadingStrategy : PreloadAllModules
preloadingStrategy : PreloadAllModules,
scrollPositionRestoration: 'enabled'
};
@NgModule({

View File

@@ -4,6 +4,7 @@ import { forkJoin, Observable } from 'rxjs';
import { MessagesService } from 'app/layout/common/messages/messages.service';
import { NavigationService } from 'app/core/navigation/navigation.service';
import { NotificationsService } from 'app/layout/common/notifications/notifications.service';
import { QuickChatService } from 'app/layout/common/quick-chat/quick-chat.service';
import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service';
import { UserService } from 'app/core/user/user.service';
@@ -19,6 +20,7 @@ export class InitialDataResolver implements Resolve<any>
private _messagesService: MessagesService,
private _navigationService: NavigationService,
private _notificationsService: NotificationsService,
private _quickChatService: QuickChatService,
private _shortcutsService: ShortcutsService,
private _userService: UserService
)
@@ -42,6 +44,7 @@ export class InitialDataResolver implements Resolve<any>
this._navigationService.get(),
this._messagesService.getAll(),
this._notificationsService.getAll(),
this._quickChatService.getChats(),
this._shortcutsService.getAll(),
this._userService.get()
]);

View File

@@ -5,19 +5,18 @@ import { LayoutComponent } from 'app/layout/layout.component';
import { InitialDataResolver } from 'app/app.resolvers';
// @formatter:off
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
// tslint:disable:max-line-length
export const appRoutes: Route[] = [
// Redirect empty path to '/dashboards/project'
{path: '', pathMatch : 'full', redirectTo: 'dashboards/project'},
// Redirect empty path to '/example'
{path: '', pathMatch : 'full', redirectTo: 'example'},
// Redirect signed in user to the '/dashboards/project'
// Redirect signed in user to the '/example'
//
// After the user signs in, the sign in page will redirect the user to the 'signed-in-redirect'
// path. Below is another redirection for that path to redirect the user to the desired
// location. This is a small convenience to keep all main routes together here on this file.
{path: 'signed-in-redirect', pathMatch : 'full', redirectTo: 'dashboards/project'},
{path: 'signed-in-redirect', pathMatch : 'full', redirectTo: 'example'},
// Auth routes for guests
{
@@ -74,132 +73,7 @@ export const appRoutes: Route[] = [
initialData: InitialDataResolver,
},
children : [
// Dashboards
{path: 'dashboards', children: [
{path: 'project', loadChildren: () => import('app/modules/admin/dashboards/project/project.module').then(m => m.ProjectModule)},
{path: 'analytics', loadChildren: () => import('app/modules/admin/dashboards/analytics/analytics.module').then(m => m.AnalyticsModule)},
]},
// Apps
{path: 'apps', children: [
{path: 'academy', loadChildren: () => import('app/modules/admin/apps/academy/academy.module').then(m => m.AcademyModule)},
{path: 'calendar', loadChildren: () => import('app/modules/admin/apps/calendar/calendar.module').then(m => m.CalendarModule)},
{path: 'chat', loadChildren: () => import('app/modules/admin/apps/chat/chat.module').then(m => m.ChatModule)},
{path: 'contacts', loadChildren: () => import('app/modules/admin/apps/contacts/contacts.module').then(m => m.ContactsModule)},
{path: 'ecommerce', loadChildren: () => import('app/modules/admin/apps/ecommerce/ecommerce.module').then(m => m.ECommerceModule)},
{path: 'file-manager', loadChildren: () => import('app/modules/admin/apps/file-manager/file-manager.module').then(m => m.FileManagerModule)},
{path: 'help-center', loadChildren: () => import('app/modules/admin/apps/help-center/help-center.module').then(m => m.HelpCenterModule)},
{path: 'mailbox', loadChildren: () => import('app/modules/admin/apps/mailbox/mailbox.module').then(m => m.MailboxModule)},
{path: 'notes', loadChildren: () => import('app/modules/admin/apps/notes/notes.module').then(m => m.NotesModule)},
{path: 'scrumboard', loadChildren: () => import('app/modules/admin/apps/scrumboard/scrumboard.module').then(m => m.ScrumboardModule)},
{path: 'tasks', loadChildren: () => import('app/modules/admin/apps/tasks/tasks.module').then(m => m.TasksModule)},
]},
// Pages
{path: 'pages', children: [
// Activities
{path: 'activities', loadChildren: () => import('app/modules/admin/pages/activities/activities.module').then(m => m.ActivitiesModule)},
// Authentication
{path: 'authentication', loadChildren: () => import('app/modules/admin/pages/authentication/authentication.module').then(m => m.AuthenticationModule)},
// Coming soon
{path: 'coming-soon', loadChildren: () => import('app/modules/admin/pages/coming-soon/coming-soon.module').then(m => m.ComingSoonModule)},
// Error
{path: 'error', children: [
{path: '404', loadChildren: () => import('app/modules/admin/pages/error/error-404/error-404.module').then(m => m.Error404Module)},
{path: '500', loadChildren: () => import('app/modules/admin/pages/error/error-500/error-500.module').then(m => m.Error500Module)}
]},
// Invoice
{path: 'invoice', children: [
{path: 'printable', children: [
{path: 'compact', loadChildren: () => import('app/modules/admin/pages/invoice/printable/compact/compact.module').then(m => m.CompactModule)},
{path: 'modern', loadChildren: () => import('app/modules/admin/pages/invoice/printable/modern/modern.module').then(m => m.ModernModule)}
]}
]},
// Maintenance
{path: 'maintenance', loadChildren: () => import('app/modules/admin/pages/maintenance/maintenance.module').then(m => m.MaintenanceModule)},
// Pricing
{path: 'pricing', children: [
{path: 'modern', loadChildren: () => import('app/modules/admin/pages/pricing/modern/modern.module').then(m => m.PricingModernModule)},
{path: 'simple', loadChildren: () => import('app/modules/admin/pages/pricing/simple/simple.module').then(m => m.PricingSimpleModule)},
{path: 'single', loadChildren: () => import('app/modules/admin/pages/pricing/single/single.module').then(m => m.PricingSingleModule)},
{path: 'table', loadChildren: () => import('app/modules/admin/pages/pricing/table/table.module').then(m => m.PricingTableModule)}
]},
// Profile
{path: 'profile', loadChildren: () => import('app/modules/admin/pages/profile/profile.module').then(m => m.ProfileModule)},
// Settings
{path: 'settings', loadChildren: () => import('app/modules/admin/pages/settings/settings.module').then(m => m.SettingsModule)},
]},
// User interface
{path: 'ui', children: [
// Angular Material
{path: 'angular-material', loadChildren: () => import('app/modules/admin/ui/angular-material/angular-material.module').then(m => m.AngularMaterialModule)},
// TailwindCSS
{path: 'tailwindcss', loadChildren: () => import('app/modules/admin/ui/tailwindcss/tailwindcss.module').then(m => m.TailwindCSSModule)},
// Advanced search
{path: 'advanced-search', loadChildren: () => import('app/modules/admin/ui/advanced-search/advanced-search.module').then(m => m.AdvancedSearchModule)},
// Animations
{path: 'animations', loadChildren: () => import('app/modules/admin/ui/animations/animations.module').then(m => m.AnimationsModule)},
// Cards
{path: 'cards', loadChildren: () => import('app/modules/admin/ui/cards/cards.module').then(m => m.CardsModule)},
// Colors
{path: 'colors', loadChildren: () => import('app/modules/admin/ui/colors/colors.module').then(m => m.ColorsModule)},
// Datatable
{path: 'datatable', loadChildren: () => import('app/modules/admin/ui/datatable/datatable.module').then(m => m.DatatableModule)},
// Forms
{path: 'forms', children: [
{path: 'fields', loadChildren: () => import('app/modules/admin/ui/forms/fields/fields.module').then(m => m.FormsFieldsModule)},
{path: 'layouts', loadChildren: () => import('app/modules/admin/ui/forms/layouts/layouts.module').then(m => m.FormsLayoutsModule)},
{path: 'wizards', loadChildren: () => import('app/modules/admin/ui/forms/wizards/wizards.module').then(m => m.FormsWizardsModule)}
]},
// Icons
{path: 'icons', loadChildren: () => import('app/modules/admin/ui/icons/icons.module').then(m => m.IconsModule)},
// Page layouts
{path: 'page-layouts', loadChildren: () => import('app/modules/admin/ui/page-layouts/page-layouts.module').then(m => m.PageLayoutsModule)},
// Typography
{path: 'typography', loadChildren: () => import('app/modules/admin/ui/typography/typography.module').then(m => m.TypographyModule)}
]},
// Documentation
{path: 'docs', children: [
// Changelog
{path: 'changelog', loadChildren: () => import('app/modules/admin/docs/changelog/changelog.module').then(m => m.ChangelogModule)},
// Guides
{path: 'guides', loadChildren: () => import('app/modules/admin/docs/guides/guides.module').then(m => m.GuidesModule)},
// Core features
{path: 'core-features', loadChildren: () => import('app/modules/admin/docs/core-features/core-features.module').then(m => m.CoreFeaturesModule)},
// Other components
{path: 'other-components', loadChildren: () => import('app/modules/admin/docs/other-components/other-components.module').then(m => m.OtherComponentsModule)},
]},
// 404 & Catch all
{path: '404-not-found', pathMatch: 'full', loadChildren: () => import('app/modules/admin/pages/error/error-404/error-404.module').then(m => m.Error404Module)},
{path: '**', redirectTo: '404-not-found'}
{path: 'example', loadChildren: () => import('app/modules/admin/example/example.module').then(m => m.ExampleModule)},
]
}
];

View File

@@ -27,6 +27,6 @@ export class TranslocoHttpLoader implements TranslocoLoader
*/
getTranslation(lang: string): Observable<Translation>
{
return this._httpClient.get<Translation>(`/assets/i18n/${lang}.json`);
return this._httpClient.get<Translation>(`./assets/i18n/${lang}.json`);
}
}

View File

@@ -23,6 +23,7 @@ import { TranslocoHttpLoader } from 'app/core/transloco/transloco.http-loader';
}
],
defaultLang : 'en',
fallbackLang : 'en',
reRenderOnLangChange: true,
prodMode : environment.production
})

View File

@@ -167,7 +167,7 @@ export class MessagesComponent implements OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._messagesOrigin._elementRef.nativeElement)
.withLockedPosition()
.withLockedPosition(true)
.withPush(true)
.withPositions([
{

View File

@@ -167,7 +167,7 @@ export class NotificationsComponent implements OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._notificationsOrigin._elementRef.nativeElement)
.withLockedPosition()
.withLockedPosition(true)
.withPush(true)
.withPositions([
{

View File

@@ -0,0 +1,196 @@
<div class="fixed lg:sticky top-0 bottom-0 lg:left-full w-full sm:w-96 lg:w-16 lg:h-screen lg:shadow">
<div
class="flex flex-col w-full sm:w-96 h-full transform transition-transform duration-400 ease-drawer bg-card"
[ngClass]="{'-translate-x-full sm:-translate-x-96 lg:-translate-x-80 shadow': opened, 'translate-x-0': !opened}">
<!-- Header -->
<div
class="quick-chat-header flex flex-0 items-center justify-start cursor-pointer"
(click)="toggle()">
<!-- Toggle -->
<ng-container *ngIf="!opened || (opened && !selectedChat)">
<div class="flex flex-auto items-center justify-center">
<div class="flex flex-0 items-center justify-center w-16">
<mat-icon
class="icon-size-6"
[svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</div>
<div class="text-lg font-medium text-secondary">Team Chat</div>
<button
class="ml-auto mr-4"
mat-icon-button>
<mat-icon [svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>
</ng-container>
<!-- Contact info -->
<ng-container *ngIf="opened && selectedChat">
<div class="flex flex-auto items-center ml-3">
<div class="relative flex flex-0 items-center justify-center w-10 h-10">
<ng-container *ngIf="chat.contact.avatar">
<img
class="w-full h-full rounded-full object-cover"
[src]="chat.contact.avatar"
alt="Contact avatar"/>
</ng-container>
<ng-container *ngIf="!chat.contact.avatar">
<div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
{{chat.contact.name.charAt(0)}}
</div>
</ng-container>
</div>
<div class="ml-4 text-lg font-medium leading-5 truncate">{{chat.contact.name}}</div>
<button
class="ml-auto mr-4"
mat-icon-button>
<mat-icon [svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>
</ng-container>
</div>
<!-- Content -->
<div class="flex flex-auto border-t overflow-hidden">
<!-- Chat list -->
<div
class="flex-0 w-16 h-full overflow-y-auto overscroll-y-contain sm:overflow-hidden sm:overscroll-auto"
fuseScrollbar
[fuseScrollbarOptions]="{wheelPropagation: false}">
<div class="flex-auto">
<ng-container *ngFor="let chat of chats; trackBy: trackByFn">
<div
class="flex items-center py-3 px-4 cursor-pointer"
[ngClass]="{'hover:bg-gray-100 dark:hover:bg-hover': !selectedChat || selectedChat.id !== chat.id,
'bg-primary-50 dark:bg-hover': selectedChat && selectedChat.id === chat.id}"
(click)="selectChat(chat.id)">
<div class="relative flex flex-0 items-center justify-center w-8 h-8">
<ng-container *ngIf="chat.unreadCount > 0">
<div
class="absolute bottom-0 right-0 flex-0 w-2 h-2 -ml-0.5 rounded-full ring-2 ring-bg-card dark:ring-gray-900 bg-primary dark:bg-primary-500 text-on-primary"
[class.ring-primary-50]="selectedChat && selectedChat.id === chat.id"></div>
</ng-container>
<ng-container *ngIf="chat.contact.avatar">
<img
class="w-full h-full rounded-full object-cover"
[src]="chat.contact.avatar"
alt="Contact avatar"/>
</ng-container>
<ng-container *ngIf="!chat.contact.avatar">
<div class="flex items-center justify-center w-full h-full rounded-full text-lg uppercase bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-200">
{{chat.contact.name.charAt(0)}}
</div>
</ng-container>
</div>
</div>
</ng-container>
</div>
</div>
<!-- Conversation -->
<div class="flex flex-col flex-auto border-l overflow-hidden bg-gray-50 dark:bg-transparent">
<ng-container *ngIf="chat; else selectChatOrStartNew">
<div class="flex flex-col-reverse overflow-y-auto overscroll-y-contain">
<div class="flex flex-col flex-auto flex-shrink p-6">
<ng-container *ngFor="let message of chat.messages; let i = index; let first = first; let last = last; trackBy: trackByFn">
<!-- Start of the day -->
<ng-container *ngIf="first || (chat.messages[i - 1].createdAt | date:'d') !== (message.createdAt | date:'d')">
<div class="flex items-center justify-center my-3 -mx-6">
<div class="flex-auto border-b"></div>
<div class="flex-0 mx-4 text-sm font-medium leading-5 text-secondary">
{{message.createdAt | date: 'longDate'}}
</div>
<div class="flex-auto border-b"></div>
</div>
</ng-container>
<div
class="flex flex-col"
[ngClass]="{'items-end': message.isMine,
'items-start': !message.isMine,
'mt-0.5': i > 0 && chat.messages[i - 1].isMine === message.isMine,
'mt-3': i > 0 && chat.messages[i - 1].isMine !== message.isMine}">
<!-- Bubble -->
<div
class="relative max-w-3/4 px-3 py-2 rounded-lg"
[ngClass]="{'bg-blue-500 text-blue-50': message.isMine,
'bg-gray-500 text-gray-50': !message.isMine}">
<!-- Speech bubble tail -->
<ng-container *ngIf="last || chat.messages[i + 1].isMine !== message.isMine">
<div
class="absolute bottom-0 w-3 transform"
[ngClass]="{'text-blue-500 -right-1 -mr-px mb-px': message.isMine,
'text-gray-500 -left-1 -ml-px mb-px -scale-x-1': !message.isMine}">
<ng-container *ngTemplateOutlet="speechBubbleExtension"></ng-container>
</div>
</ng-container>
<!-- Message -->
<div
class="min-w-4 leading-5"
[innerHTML]="message.value">
</div>
</div>
<!-- Time -->
<ng-container
*ngIf="first
|| last
|| chat.messages[i + 1].isMine !== message.isMine
|| chat.messages[i + 1].createdAt !== message.createdAt">
<div
class="my-0.5 text-sm font-medium text-secondary"
[ngClass]="{'mr-3': message.isMine,
'ml-3': !message.isMine}">
{{message.createdAt | date:'HH:mm'}}
</div>
</ng-container>
</div>
</ng-container>
</div>
</div>
<!-- Message field -->
<div class="flex items-end p-4 border-t bg-gray-50 dark:bg-transparent">
<mat-form-field class="fuse-mat-dense fuse-mat-no-subscript fuse-mat-rounded fuse-mat-bold w-full">
<textarea
class="min-h-5 my-0 resize-none"
style="margin: 11px 0 !important; padding: 0 !important;"
[rows]="1"
matInput
#messageInput></textarea>
</mat-form-field>
<div class="flex items-center h-11 my-px ml-4">
<button
mat-icon-button>
<mat-icon
class="transform rotate-90"
[svgIcon]="'heroicons_outline:paper-airplane'"></mat-icon>
</button>
</div>
</div>
</ng-container>
</div>
</div>
</div>
</div>
<!-- Select chat or start new template -->
<ng-template #selectChatOrStartNew>
<div class="flex flex-col flex-auto items-center justify-center w-full h-full p-4">
<mat-icon
class="icon-size-20"
[svgIcon]="'iconsmind:speach_bubble'"></mat-icon>
<div class="mt-4 text-xl text-center font-medium tracking-tight text-secondary">Select a conversation</div>
</div>
</ng-template>
<!-- Speech bubble tail SVG -->
<!-- @formatter:off -->
<ng-template #speechBubbleExtension>
<svg width="100%" height="100%" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M1.01522827,0.516204834 C-8.83532715,54.3062744 61.7609863,70.5215302 64.8009949,64.3061218 C68.8074951,54.8859711 30.1663208,52.9997559 37.5036011,0.516204834 L1.01522827,0.516204834 Z" fill="currentColor" fill-rule="nonzero"></path>
</g>
</svg>
</ng-template>
<!-- @formatter:on -->

View File

@@ -0,0 +1,58 @@
quick-chat {
z-index: 399;
> div {
overflow: hidden;
}
&.quick-chat-opened {
> div {
overflow: visible;
}
}
:not(&.quick-chat-opened) {
> div {
overflow: visible;
animation: addOverflowHidden 1ms linear 400ms;
animation-fill-mode: forwards;
}
}
}
/* Adjustments depending on the selected layout */
.quick-chat-header {
height: 64px;
enterprise-layout &,
modern-layout & {
height: 80px !important;
}
}
/* Overlay */
.quick-chat-overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 299;
opacity: 1;
background-color: transparent;
}
@keyframes addOverflowHidden {
0% {
overflow: visible
}
99% {
overflow: visible;
}
100% {
overflow: hidden;
}
}

View File

@@ -0,0 +1,275 @@
import { Component, ElementRef, HostBinding, HostListener, NgZone, OnDestroy, OnInit, Renderer2, ViewChild, ViewEncapsulation } from '@angular/core';
import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { QuickChatService } from 'app/layout/common/quick-chat/quick-chat.service';
import { Chat } from 'app/layout/common/quick-chat/quick-chat.types';
@Component({
selector : 'quick-chat',
templateUrl : './quick-chat.component.html',
styleUrls : ['./quick-chat.component.scss'],
encapsulation: ViewEncapsulation.None,
exportAs : 'quickChat'
})
export class QuickChatComponent implements OnInit, OnDestroy
{
@ViewChild('messageInput') messageInput: ElementRef;
chat: Chat;
chats: Chat[];
opened: boolean = false;
selectedChat: Chat;
private _scrollStrategy: ScrollStrategy = this._scrollStrategyOptions.block();
private _overlay: HTMLElement;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _elementRef: ElementRef,
private _renderer2: Renderer2,
private _ngZone: NgZone,
private _quickChatService: QuickChatService,
private _scrollStrategyOptions: ScrollStrategyOptions
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* Host binding for component classes
*/
@HostBinding('class') get classList(): any
{
return {
'quick-chat-opened': this.opened
};
}
/**
* Resize on 'input' and 'ngModelChange' events
*
* @private
*/
@HostListener('input')
@HostListener('ngModelChange')
private _resizeMessageInput(): void
{
// This doesn't need to trigger Angular's change detection by itself
this._ngZone.runOutsideAngular(() => {
setTimeout(() => {
// Set the height to 'auto' so we can correctly read the scrollHeight
this.messageInput.nativeElement.style.height = 'auto';
// Get the scrollHeight and subtract the vertical padding
this.messageInput.nativeElement.style.height = `${this.messageInput.nativeElement.scrollHeight}px`;
});
});
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Chat
this._quickChatService.chat$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((chat: Chat) => {
this.chat = chat;
});
// Chats
this._quickChatService.chats$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((chats: Chat[]) => {
this.chats = chats;
});
// Selected chat
this._quickChatService.chat$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((chat: Chat) => {
this.selectedChat = chat;
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open the panel
*/
open(): void
{
// Return if the panel has already opened
if ( this.opened )
{
return;
}
// Open the panel
this._toggleOpened(true);
}
/**
* Close the panel
*/
close(): void
{
// Return if the panel has already closed
if ( !this.opened )
{
return;
}
// Close the panel
this._toggleOpened(false);
}
/**
* Toggle the panel
*/
toggle(): void
{
if ( this.opened )
{
this.close();
}
else
{
this.open();
}
}
/**
* Select the chat
*
* @param id
*/
selectChat(id: string): void
{
// Open the panel
this._toggleOpened(true);
// Get the chat data
this._quickChatService.getChatById(id).subscribe();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Show the backdrop
*
* @private
*/
private _showOverlay(): void
{
// Try hiding the overlay in case there is one already opened
this._hideOverlay();
// Create the backdrop element
this._overlay = this._renderer2.createElement('div');
// Return if overlay couldn't be create for some reason
if ( !this._overlay )
{
return;
}
// Add a class to the backdrop element
this._overlay.classList.add('quick-chat-overlay');
// Append the backdrop to the parent of the panel
this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._overlay);
// Enable block scroll strategy
this._scrollStrategy.enable();
// Add an event listener to the overlay
this._overlay.addEventListener('click', () => {
this.close();
});
}
/**
* Hide the backdrop
*
* @private
*/
private _hideOverlay(): void
{
if ( !this._overlay )
{
return;
}
// If the backdrop still exists...
if ( this._overlay )
{
// Remove the backdrop
this._overlay.parentNode.removeChild(this._overlay);
this._overlay = null;
}
// Disable block scroll strategy
this._scrollStrategy.disable();
}
/**
* Open/close the panel
*
* @param open
* @private
*/
private _toggleOpened(open: boolean): void
{
// Set the opened
this.opened = open;
// If the panel opens, show the overlay
if ( open )
{
this._showOverlay();
}
// Otherwise, hide the overlay
else
{
this._hideOverlay();
}
}
}

View File

@@ -0,0 +1,32 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { FuseDrawerModule } from '@fuse/components/drawer';
import { FuseScrollbarModule } from '@fuse/directives/scrollbar';
import { SharedModule } from 'app/shared/shared.module';
import { QuickChatComponent } from 'app/layout/common/quick-chat/quick-chat.component';
@NgModule({
declarations: [
QuickChatComponent
],
imports: [
RouterModule,
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
FuseDrawerModule,
FuseScrollbarModule,
SharedModule,
],
exports : [
QuickChatComponent
]
})
export class QuickChatModule
{
}

View File

@@ -0,0 +1,85 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
import { Chat } from 'app/layout/common/quick-chat/quick-chat.types';
@Injectable({
providedIn: 'root'
})
export class QuickChatService
{
private _chat: BehaviorSubject<Chat> = new BehaviorSubject(null);
private _chats: BehaviorSubject<Chat[]> = new BehaviorSubject<Chat[]>(null);
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for chat
*/
get chat$(): Observable<Chat>
{
return this._chat.asObservable();
}
/**
* Getter for chat
*/
get chats$(): Observable<Chat[]>
{
return this._chats.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get chats
*/
getChats(): Observable<any>
{
return this._httpClient.get<Chat[]>('api/apps/chat/chats').pipe(
tap((response: Chat[]) => {
this._chats.next(response);
})
);
}
/**
* Get chat
*
* @param id
*/
getChatById(id: string): Observable<any>
{
return this._httpClient.get<Chat>('api/apps/chat/chat', {params: {id}}).pipe(
map((chat) => {
// Update the chat
this._chat.next(chat);
// Return the chat
return chat;
}),
switchMap((chat) => {
if ( !chat )
{
return throwError('Could not found chat with id of ' + id + '!');
}
return of(chat);
})
);
}
}

View File

@@ -1,10 +1,20 @@
export interface Profile
export interface Chat
{
id?: string;
name?: string;
email?: string;
avatar?: string;
about?: string;
contactId?: string;
contact?: Contact;
unreadCount?: number;
muted?: boolean;
lastMessage?: string;
lastMessageAt?: string;
messages?: {
id?: string;
chatId?: string;
contactId?: string;
isMine?: boolean;
value?: string;
createdAt?: string;
}[];
}
export interface Contact
@@ -34,22 +44,3 @@ export interface Contact
links?: any[];
};
}
export interface Chat
{
id?: string;
contactId?: string;
contact?: Contact;
unreadCount?: number;
muted?: boolean;
lastMessage?: string;
lastMessageAt?: string;
messages?: {
id?: string;
chatId?: string;
contactId?: string;
isMine?: boolean;
value?: string;
createdAt?: string;
}[];
}

View File

@@ -0,0 +1,474 @@
<div
class="fixed flex items-center justify-center right-0 w-10 h-10 shadow-lg rounded-l-lg z-90 cursor-pointer bg-red-600 bg-opacity-90 print:hidden"
[class.lg:right-0]="config.layout === 'centered' || config.layout === 'material'"
[class.lg:right-16]="config.layout !== 'centered' && config.layout !== 'material'"
style="top: 275px"
(click)="settingsDrawer.toggle()">
<mat-icon
class="icon-size-5 text-white animate-spin-slow"
[svgIcon]="'heroicons_solid:cog'"></mat-icon>
</div>
<fuse-drawer
class="w-screen min-w-screen sm:w-100 sm:min-w-100 z-999"
fixed
[mode]="'over'"
[name]="'settingsDrawer'"
[position]="'right'"
#settingsDrawer>
<div class="flex flex-col w-full overflow-auto bg-card">
<div class="flex flex-row items-center px-6 h-20 min-h-20 text-white bg-primary">
<mat-icon
class="icon-size-7 text-current"
[svgIcon]="'heroicons_solid:cog'"></mat-icon>
<div class="ml-3 text-2xl font-semibold tracking-tight">Settings</div>
<button
class="ml-auto"
mat-icon-button
(click)="settingsDrawer.close()">
<mat-icon
class="text-current"
[svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>
<div class="flex flex-col p-6">
<!-- Theme -->
<div class="text-md font-semibold text-secondary">THEME</div>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3 mt-6">
<ng-container *ngFor="let theme of themes">
<div
class="flex items-center justify-center px-4 py-3 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.theme === theme[0]"
(click)="setTheme(theme[0])">
<div
class="flex-0 w-3 h-3 rounded-full"
[style.background-color]="theme[1].primary"></div>
<div
class="ml-2.5 font-medium leading-5 truncate"
[class.text-secondary]="config.theme !== theme[0]">
{{theme[0] | titlecase}}
</div>
</div>
</ng-container>
</div>
<hr class="my-8">
<!-- Scheme -->
<div class="text-md font-semibold text-secondary">SCHEME</div>
<div class="grid grid-cols-3 gap-3 justify-items-start mt-6">
<!-- Auto -->
<div
class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.scheme === 'auto'"
matTooltip="Automatically sets the scheme based on user's operating system's color scheme preference using 'prefer-color-scheme' media query."
(click)="setScheme('auto')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:lightning-bolt'"></mat-icon>
</div>
<div
class="flex items-center ml-2 font-medium leading-5"
[class.text-secondary]="config.scheme !== 'auto'">
Auto
</div>
</div>
<!-- Dark -->
<div
class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.scheme === 'dark'"
(click)="setScheme('dark')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:moon'"></mat-icon>
</div>
<div
class="flex items-center ml-2 font-medium leading-5"
[class.text-secondary]="config.scheme !== 'dark'">
Dark
</div>
</div>
<!-- Light -->
<div
class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.scheme === 'light'"
(click)="setScheme('light')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:sun'"></mat-icon>
</div>
<div
class="flex items-center ml-2 font-medium leading-5"
[class.text-secondary]="config.scheme !== 'light'">
Light
</div>
</div>
</div>
<hr class="my-8">
<!-- Layout -->
<div class="text-md font-semibold text-secondary">LAYOUT</div>
<div class="grid grid-cols-3 gap-3 mt-6">
<!-- Empty -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('empty')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'empty'">
<div class="flex flex-col flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'empty'">
Empty
</div>
</div>
<!-- Classic -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('classic')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'classic'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="mt-3 mx-1.5 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'classic'">
Classic
</div>
</div>
<!-- Classy -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('classy')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'classy'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center mt-1 mx-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-auto rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-0.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="w-4 h-4 mt-2.5 mx-auto rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="mt-2 mx-1 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-2">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'classy'">
Classy
</div>
</div>
<!-- Compact -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('compact')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'compact'">
<div class="w-5 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-3 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'compact'">
Compact
</div>
</div>
<!-- Dense -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('dense')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'dense'">
<div class="w-4 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'dense'">
Dense
</div>
</div>
<!-- Futuristic -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('futuristic')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'futuristic'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="flex flex-col flex-auto h-full py-3 px-1.5 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex-auto"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'futuristic'">
Futuristic
</div>
</div>
<!-- Thin -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('thin')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'thin'">
<div class="w-3 bg-gray-100 dark:bg-gray-800">
<div class="w-1.5 h-1.5 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'thin'">
Thin
</div>
</div>
<div class="col-span-2"></div>
<!-- Centered -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('centered')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'centered'">
<div class="flex flex-col flex-auto my-1 mx-2 border rounded-md overflow-hidden">
<div class="flex items-center h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex ml-1.5">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex items-center justify-end ml-auto mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'centered'">
Centered
</div>
</div>
<!-- Enterprise -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('enterprise')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'enterprise'">
<div class="flex items-center h-3 px-2 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex items-center h-3 px-2 border-t border-b space-x-1 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex flex-col flex-auto my-1 mx-2 border rounded overflow-hidden">
<div class="flex flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'enterprise'">
Enterprise
</div>
</div>
<!-- Material -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('material')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'material'">
<div class="flex flex-col flex-auto my-1 mx-2 border rounded overflow-hidden">
<div class="flex items-center h-4 px-2 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex items-center h-2 px-2 space-x-1 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'material'">
Material
</div>
</div>
<!-- Modern -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('modern')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'modern'">
<div class="flex items-center h-4 px-2 border-b bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center h-3 ml-2 space-x-1">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto">
<div class="flex flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'modern'">
Modern
</div>
</div>
</div>
</div>
</div>
</fuse-drawer>

View File

@@ -0,0 +1,124 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseConfigService } from '@fuse/services/config';
import { FuseTailwindService } from '@fuse/services/tailwind';
import { AppConfig, Scheme, Theme } from 'app/core/config/app.config';
import { Layout } from 'app/layout/layout.types';
@Component({
selector : 'settings',
templateUrl : './settings.component.html',
styles : [
`
settings {
position: static;
display: block;
flex: none;
width: auto;
}
`
],
encapsulation: ViewEncapsulation.None
})
export class SettingsComponent implements OnInit, OnDestroy
{
config: AppConfig;
layout: Layout;
scheme: 'dark' | 'light';
theme: string;
themes: [string, any][] = [];
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _router: Router,
private _fuseConfigService: FuseConfigService,
private _fuseTailwindService: FuseTailwindService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the themes
this._fuseTailwindService.tailwindConfig$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config) => {
this.themes = Object.entries(config.themes);
});
// Subscribe to config changes
this._fuseConfigService.config$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((config: AppConfig) => {
// Store the config
this.config = config;
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the layout on the config
*
* @param layout
*/
setLayout(layout: string): void
{
// Clear the 'layout' query param to allow layout changes
this._router.navigate([], {
queryParams : {
layout: null
},
queryParamsHandling: 'merge'
}).then(() => {
// Set the config
this._fuseConfigService.config = {layout};
});
}
/**
* Set the scheme on the config
*
* @param scheme
*/
setScheme(scheme: Scheme): void
{
this._fuseConfigService.config = {scheme};
}
/**
* Set the theme on the config
*
* @param theme
*/
setTheme(theme: Theme): void
{
this._fuseConfigService.config = {theme};
}
}

View File

@@ -0,0 +1,28 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FuseDrawerModule } from '@fuse/components/drawer';
import { SettingsComponent } from 'app/layout/common/settings/settings.component';
import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [
SettingsComponent
],
imports: [
CommonModule,
RouterModule,
MatIconModule,
MatTooltipModule,
FuseDrawerModule,
MatButtonModule
],
exports : [
SettingsComponent
]
})
export class SettingsModule
{
}

View File

@@ -221,7 +221,7 @@ export class ShortcutsComponent implements OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._shortcutsOrigin._elementRef.nativeElement)
.withLockedPosition()
.withLockedPosition(true)
.withPush(true)
.withPositions([
{

View File

@@ -44,468 +44,4 @@
<!-- ----------------------------------------------------------------------------------------------------- -->
<!-- Settings drawer - Remove this to remove the drawer and its button -->
<!-- ----------------------------------------------------------------------------------------------------- -->
<div
class="fixed flex items-center justify-center right-0 w-10 h-10 shadow-lg rounded-l-lg z-999 cursor-pointer bg-red-600 bg-opacity-90 print:hidden"
style="top: 275px"
(click)="settingsDrawer.toggle()">
<mat-icon
class="icon-size-5 text-white animate-spin-slow"
[svgIcon]="'heroicons_solid:cog'"></mat-icon>
</div>
<fuse-drawer
class="w-100 min-w-100"
fixed
transparentOverlay
[mode]="'over'"
[name]="'settingsDrawer'"
[position]="'right'"
#settingsDrawer>
<div class="flex flex-col w-full overflow-auto bg-card">
<div class="flex flex-row items-center px-6 h-20 min-h-20 text-white bg-primary">
<mat-icon
class="icon-size-7 text-current"
[svgIcon]="'heroicons_solid:cog'"></mat-icon>
<div class="ml-3 text-2xl font-semibold tracking-tight">Settings</div>
</div>
<div class="flex flex-col p-6">
<!-- Theme -->
<div class="text-md font-semibold text-secondary">THEME</div>
<div class="grid grid-cols-3 gap-3 mt-6">
<ng-container *ngFor="let theme of themes">
<div
class="flex items-center px-4 py-3 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.theme === theme[0]"
(click)="setTheme(theme[0])">
<div
class="w-4 h-4 rounded-full"
[style.background-color]="theme[1].primary"></div>
<div
class="ml-2.5 font-medium leading-5"
[class.text-secondary]="config.theme !== theme[0]">
{{theme[0] | titlecase}}
</div>
</div>
</ng-container>
</div>
<hr class="my-8">
<!-- Scheme -->
<div class="text-md font-semibold text-secondary">SCHEME</div>
<div class="grid grid-cols-3 gap-3 justify-items-start mt-6">
<!-- Auto -->
<div
class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.scheme === 'auto'"
[matTooltip]="'Automatically sets the scheme based on user\'s operating system\'s color scheme preference using \'prefer-color-scheme\' media query.'"
(click)="setScheme('auto')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:lightning-bolt'"></mat-icon>
</div>
<div
class="flex items-center ml-2 font-medium leading-5"
[class.text-secondary]="config.scheme !== 'auto'">
Auto
</div>
</div>
<!-- Dark -->
<div
class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.scheme === 'dark'"
(click)="setScheme('dark')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:moon'"></mat-icon>
</div>
<div
class="flex items-center ml-2 font-medium leading-5"
[class.text-secondary]="config.scheme !== 'dark'">
Dark
</div>
</div>
<!-- Light -->
<div
class="flex items-center py-3 pl-5 pr-6 rounded-full cursor-pointer ring-inset ring-primary bg-hover"
[class.ring-2]="config.scheme === 'light'"
(click)="setScheme('light')">
<div class="flex items-center rounded-full overflow-hidden">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:sun'"></mat-icon>
</div>
<div
class="flex items-center ml-2 font-medium leading-5"
[class.text-secondary]="config.scheme !== 'light'">
Light
</div>
</div>
</div>
<hr class="my-8">
<!-- Layout -->
<div class="text-md font-semibold text-secondary">LAYOUT</div>
<div class="grid grid-cols-3 gap-3 mt-6">
<!-- Empty -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('empty')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'empty'">
<div class="flex flex-col flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'empty'">
Empty
</div>
</div>
<!-- Classic -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('classic')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'classic'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="mt-3 mx-1.5 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'classic'">
Classic
</div>
</div>
<!-- Classy -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('classy')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'classy'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center mt-1 mx-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-auto rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-0.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="w-4 h-4 mt-2.5 mx-auto rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="mt-2 mx-1 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-2">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'classy'">
Classy
</div>
</div>
<!-- Compact -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('compact')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'compact'">
<div class="w-5 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-3 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-2.5 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'compact'">
Compact
</div>
</div>
<!-- Dense -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('dense')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'dense'">
<div class="w-4 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="w-2 h-2 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'dense'">
Dense
</div>
</div>
<!-- Futuristic -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('futuristic')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'futuristic'">
<div class="w-8 bg-gray-100 dark:bg-gray-800">
<div class="flex flex-col flex-auto h-full py-3 px-1.5 space-y-1">
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex-auto"></div>
<div class="h-1 rounded-sm bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'futuristic'">
Futuristic
</div>
</div>
<!-- Thin -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('thin')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'thin'">
<div class="w-3 bg-gray-100 dark:bg-gray-800">
<div class="w-1.5 h-1.5 mt-2 mx-auto rounded-sm bg-gray-300 dark:bg-gray-700"></div>
<div class="flex flex-col items-center w-full mt-2 space-y-1">
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto border-l">
<div class="h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex items-center justify-end h-full mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'thin'">
Thin
</div>
</div>
<div class="col-span-2"></div>
<!-- Centered -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('centered')">
<div
class="flex h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'centered'">
<div class="flex flex-col flex-auto my-1 mx-2 border rounded-md overflow-hidden">
<div class="flex items-center h-3 bg-gray-100 dark:bg-gray-800">
<div class="flex ml-1.5">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex items-center justify-end ml-auto mr-1.5">
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 ml-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'centered'">
Centered
</div>
</div>
<!-- Enterprise -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('enterprise')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'enterprise'">
<div class="flex items-center h-3 px-2 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex items-center h-3 px-2 border-t border-b space-x-1 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex flex-col flex-auto my-1 mx-2 border rounded overflow-hidden">
<div class="flex flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'enterprise'">
Enterprise
</div>
</div>
<!-- Material -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('material')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'material'">
<div class="flex flex-col flex-auto my-1 mx-2 border rounded overflow-hidden">
<div class="flex items-center h-4 px-2 bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex items-center h-2 px-2 space-x-1 bg-gray-100 dark:bg-gray-800">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex flex-auto border-t bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'material'">
Material
</div>
</div>
<!-- Modern -->
<div
class="flex flex-col cursor-pointer"
(click)="setLayout('modern')">
<div
class="flex flex-col h-20 rounded-md overflow-hidden border-2 hover:opacity-80"
[class.border-primary]="config.layout === 'modern'">
<div class="flex items-center h-4 px-2 border-b bg-gray-100 dark:bg-gray-800">
<div class="w-2 h-2 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="flex items-center h-3 ml-2 space-x-1">
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-3 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
<div class="flex items-center justify-end ml-auto space-x-1">
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
<div class="w-1 h-1 rounded-full bg-gray-300 dark:bg-gray-700"></div>
</div>
</div>
<div class="flex flex-col flex-auto">
<div class="flex flex-auto bg-gray-50 dark:bg-gray-900"></div>
</div>
</div>
<div
class="mt-2 text-md font-medium text-center text-secondary"
[class.text-primary]="config.layout === 'modern'">
Modern
</div>
</div>
</div>
</div>
</div>
</fuse-drawer>
<settings></settings>

View File

@@ -5,10 +5,9 @@ import { combineLatest, Subject } from 'rxjs';
import { filter, map, takeUntil } from 'rxjs/operators';
import { FuseConfigService } from '@fuse/services/config';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { FuseTailwindService } from '@fuse/services/tailwind/tailwind.service';
import { FUSE_VERSION } from '@fuse/version';
import { Layout } from 'app/layout/layout.types';
import { AppConfig, Scheme, Theme } from 'app/core/config/app.config';
import { AppConfig } from 'app/core/config/app.config';
@Component({
selector : 'layout',
@@ -22,7 +21,6 @@ export class LayoutComponent implements OnInit, OnDestroy
layout: Layout;
scheme: 'dark' | 'light';
theme: string;
themes: [string, any][] = [];
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
@@ -34,8 +32,7 @@ export class LayoutComponent implements OnInit, OnDestroy
private _renderer2: Renderer2,
private _router: Router,
private _fuseConfigService: FuseConfigService,
private _fuseMediaWatcherService: FuseMediaWatcherService,
private _fuseTailwindConfigService: FuseTailwindService
private _fuseMediaWatcherService: FuseMediaWatcherService
)
{
}
@@ -49,11 +46,6 @@ export class LayoutComponent implements OnInit, OnDestroy
*/
ngOnInit(): void
{
// Get the themes
this._fuseTailwindConfigService.tailwindConfig$.subscribe((config) => {
this.themes = Object.entries(config.themes);
});
// Set the theme and scheme based on the configuration
combineLatest([
this._fuseConfigService.config$,
@@ -123,50 +115,6 @@ export class LayoutComponent implements OnInit, OnDestroy
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the layout on the config
*
* @param layout
*/
setLayout(layout: string): void
{
// Clear the 'layout' query param to allow layout changes
this._router.navigate([], {
queryParams : {
layout: null
},
queryParamsHandling: 'merge'
}).then(() => {
// Set the config
this._fuseConfigService.config = {layout};
});
}
/**
* Set the scheme on the config
*
* @param scheme
*/
setScheme(scheme: Scheme): void
{
this._fuseConfigService.config = {scheme};
}
/**
* Set the theme on the config
*
* @param theme
*/
setTheme(theme: Theme): void
{
this._fuseConfigService.config = {theme};
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------

View File

@@ -14,6 +14,7 @@ import { CompactLayoutModule } from 'app/layout/layouts/vertical/compact/compact
import { DenseLayoutModule } from 'app/layout/layouts/vertical/dense/dense.module';
import { FuturisticLayoutModule } from 'app/layout/layouts/vertical/futuristic/futuristic.module';
import { ThinLayoutModule } from 'app/layout/layouts/vertical/thin/thin.module';
import { SettingsModule } from 'app/layout/common/settings/settings.module';
import { SharedModule } from 'app/shared/shared.module';
const layoutModules = [
@@ -39,12 +40,13 @@ const layoutModules = [
declarations: [
LayoutComponent
],
imports : [
imports: [
MatIconModule,
MatTooltipModule,
FuseDrawerModule,
SharedModule,
...layoutModules
SettingsModule,
...layoutModules,
],
exports : [
LayoutComponent,

View File

@@ -62,9 +62,9 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>

View File

@@ -45,13 +45,19 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
<user></user>
</div>
</div>
@@ -86,3 +92,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { EnterpriseLayoutComponent } from 'app/layout/layouts/horizontal/enterpr
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -51,9 +51,9 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-1 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>

View File

@@ -54,13 +54,19 @@
</button>
</ng-container>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
<user></user>
</div>
</div>
@@ -78,3 +84,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { ModernLayoutComponent } from 'app/layout/layouts/horizontal/modern/mode
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -35,13 +35,19 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
<user></user>
</div>
</div>
@@ -59,3 +65,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { ClassicLayoutComponent } from 'app/layout/layouts/vertical/classic/clas
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -65,12 +65,18 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
</div>
</div>
@@ -87,3 +93,6 @@
</div>-->
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseFullscreenModule } from '@fuse/components/fullscreen/fullscreen.mod
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { ClassyLayoutComponent } from 'app/layout/layouts/vertical/classy/classy
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -30,13 +30,19 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
<user></user>
</div>
</div>
@@ -54,3 +60,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { CompactLayoutComponent } from 'app/layout/layouts/vertical/compact/comp
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -39,13 +39,19 @@
</button>
</div>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
<user></user>
</div>
</div>
@@ -63,3 +69,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { DenseLayoutComponent } from 'app/layout/layouts/vertical/dense/dense.co
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -44,13 +44,19 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
</div>
</div>
@@ -67,3 +73,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { FuturisticLayoutComponent } from 'app/layout/layouts/vertical/futuristi
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

View File

@@ -31,13 +31,19 @@
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<!-- Components -->
<div class="flex items-center pl-2 ml-auto space-x-2">
<div class="flex items-center pl-2 ml-auto space-x-0.5 sm:space-x-2">
<languages></languages>
<fuse-fullscreen></fuse-fullscreen>
<fuse-fullscreen class="hidden md:block"></fuse-fullscreen>
<search [appearance]="'bar'"></search>
<shortcuts></shortcuts>
<messages></messages>
<notifications></notifications>
<button
class="lg:hidden"
mat-icon-button
(click)="quickChat.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:chat-alt-2'"></mat-icon>
</button>
<user></user>
</div>
</div>
@@ -55,3 +61,6 @@
</div>
</div>
<!-- Quick chat -->
<quick-chat #quickChat="quickChat"></quick-chat>

View File

@@ -10,6 +10,7 @@ import { FuseNavigationModule } from '@fuse/components/navigation';
import { LanguagesModule } from 'app/layout/common/languages/languages.module';
import { MessagesModule } from 'app/layout/common/messages/messages.module';
import { NotificationsModule } from 'app/layout/common/notifications/notifications.module';
import { QuickChatModule } from 'app/layout/common/quick-chat/quick-chat.module';
import { SearchModule } from 'app/layout/common/search/search.module';
import { ShortcutsModule } from 'app/layout/common/shortcuts/shortcuts.module';
import { UserModule } from 'app/layout/common/user/user.module';
@@ -32,6 +33,7 @@ import { ThinLayoutComponent } from 'app/layout/layouts/vertical/thin/thin.compo
LanguagesModule,
MessagesModule,
NotificationsModule,
QuickChatModule,
SearchModule,
ShortcutsModule,
UserModule,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import { cloneDeep } from 'lodash-es';
import { FuseMockApiService } from '@fuse/lib/mock-api';
import { crypto as cryptoData } from 'app/mock-api/dashboards/crypto/data';
@Injectable({
providedIn: 'root'
})
export class CryptoMockApi
{
private _crypto: any = cryptoData;
/**
* Constructor
*/
constructor(private _fuseMockApiService: FuseMockApiService)
{
// Register Mock API handlers
this.registerHandlers();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register Mock API handlers
*/
registerHandlers(): void
{
// -----------------------------------------------------------------------------------------------------
// @ Crypto - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/dashboards/crypto')
.reply(() => [200, cloneDeep(this._crypto)]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import { cloneDeep } from 'lodash-es';
import { FuseMockApiService } from '@fuse/lib/mock-api';
import { finance as financeData } from 'app/mock-api/dashboards/finance/data';
@Injectable({
providedIn: 'root'
})
export class FinanceMockApi
{
private _finance: any = financeData;
/**
* Constructor
*/
constructor(private _fuseMockApiService: FuseMockApiService)
{
// Register Mock API handlers
this.registerHandlers();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Register Mock API handlers
*/
registerHandlers(): void
{
// -----------------------------------------------------------------------------------------------------
// @ Sales - GET
// -----------------------------------------------------------------------------------------------------
this._fuseMockApiService
.onGet('api/dashboards/finance')
.reply(() => [200, cloneDeep(this._finance)]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,10 @@ import { AuthMockApi } from 'app/mock-api/common/auth/api';
import { CalendarMockApi } from 'app/mock-api/apps/calendar/api';
import { ChatMockApi } from 'app/mock-api/apps/chat/api';
import { ContactsMockApi } from 'app/mock-api/apps/contacts/api';
import { CryptoMockApi } from 'app/mock-api/dashboards/crypto/api';
import { ECommerceInventoryMockApi } from 'app/mock-api/apps/ecommerce/inventory/api';
import { FileManagerMockApi } from 'app/mock-api/apps/file-manager/api';
import { FinanceMockApi } from 'app/mock-api/dashboards/finance/api';
import { HelpCenterMockApi } from 'app/mock-api/apps/help-center/api';
import { IconsMockApi } from 'app/mock-api/ui/icons/api';
import { MailboxMockApi } from 'app/mock-api/apps/mailbox/api';
@@ -29,8 +31,10 @@ export const mockApiServices = [
CalendarMockApi,
ChatMockApi,
ContactsMockApi,
CryptoMockApi,
ECommerceInventoryMockApi,
FileManagerMockApi,
FinanceMockApi,
HelpCenterMockApi,
IconsMockApi,
MailboxMockApi,

View File

@@ -1 +0,0 @@
<router-outlet></router-outlet>

View File

@@ -1,17 +0,0 @@
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
@Component({
selector : 'academy',
templateUrl : './academy.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AcademyComponent
{
/**
* Constructor
*/
constructor()
{
}
}

View File

@@ -1,44 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FuseFindByKeyPipeModule } from '@fuse/pipes/find-by-key';
import { SharedModule } from 'app/shared/shared.module';
import { academyRoutes } from 'app/modules/admin/apps/academy/academy.routing';
import { AcademyComponent } from 'app/modules/admin/apps/academy/academy.component';
import { AcademyDetailsComponent } from 'app/modules/admin/apps/academy/details/details.component';
import { AcademyListComponent } from 'app/modules/admin/apps/academy/list/list.component';
import { MatTabsModule } from '@angular/material/tabs';
@NgModule({
declarations: [
AcademyComponent,
AcademyDetailsComponent,
AcademyListComponent
],
imports : [
RouterModule.forChild(academyRoutes),
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatProgressBarModule,
MatSelectModule,
MatSidenavModule,
MatSlideToggleModule,
MatTooltipModule,
FuseFindByKeyPipeModule,
SharedModule,
MatTabsModule
]
})
export class AcademyModule
{
}

View File

@@ -1,110 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
import { AcademyService } from 'app/modules/admin/apps/academy/academy.service';
@Injectable({
providedIn: 'root'
})
export class AcademyCategoriesResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _academyService: AcademyService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Category[]>
{
return this._academyService.getCategories();
}
}
@Injectable({
providedIn: 'root'
})
export class AcademyCoursesResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _academyService: AcademyService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Course[]>
{
return this._academyService.getCourses();
}
}
@Injectable({
providedIn: 'root'
})
export class AcademyCourseResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(
private _router: Router,
private _academyService: AcademyService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Course>
{
return this._academyService.getCourseById(route.paramMap.get('id'))
.pipe(
// Error here means the requested task is not available
catchError((error) => {
// Log the error
console.error(error);
// Get the parent url
const parentUrl = state.url.split('/').slice(0, -1).join('/');
// Navigate to there
this._router.navigateByUrl(parentUrl);
// Throw an error
return throwError(error);
})
);
}
}

View File

@@ -1,32 +0,0 @@
import { Route } from '@angular/router';
import { AcademyComponent } from 'app/modules/admin/apps/academy/academy.component';
import { AcademyListComponent } from 'app/modules/admin/apps/academy/list/list.component';
import { AcademyDetailsComponent } from 'app/modules/admin/apps/academy/details/details.component';
import { AcademyCategoriesResolver, AcademyCourseResolver, AcademyCoursesResolver } from 'app/modules/admin/apps/academy/academy.resolvers';
export const academyRoutes: Route[] = [
{
path : '',
component: AcademyComponent,
resolve : {
categories: AcademyCategoriesResolver
},
children : [
{
path : '',
pathMatch: 'full',
component: AcademyListComponent,
resolve : {
courses: AcademyCoursesResolver
}
},
{
path : ':id',
component: AcademyDetailsComponent,
resolve : {
course: AcademyCourseResolver
}
}
]
}
];

View File

@@ -1,105 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { map, switchMap, tap } from 'rxjs/operators';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
@Injectable({
providedIn: 'root'
})
export class AcademyService
{
// Private
private _categories: BehaviorSubject<Category[] | null> = new BehaviorSubject(null);
private _course: BehaviorSubject<Course | null> = new BehaviorSubject(null);
private _courses: BehaviorSubject<Course[] | null> = new BehaviorSubject(null);
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for categories
*/
get categories$(): Observable<Category[]>
{
return this._categories.asObservable();
}
/**
* Getter for courses
*/
get courses$(): Observable<Course[]>
{
return this._courses.asObservable();
}
/**
* Getter for course
*/
get course$(): Observable<Course>
{
return this._course.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get categories
*/
getCategories(): Observable<Category[]>
{
return this._httpClient.get<Category[]>('api/apps/academy/categories').pipe(
tap((response: any) => {
this._categories.next(response);
})
);
}
/**
* Get courses
*/
getCourses(): Observable<Course[]>
{
return this._httpClient.get<Course[]>('api/apps/academy/courses').pipe(
tap((response: any) => {
this._courses.next(response);
})
);
}
/**
* Get course by id
*/
getCourseById(id: string): Observable<Course>
{
return this._httpClient.get<Course>('api/apps/academy/courses/course', {params: {id}}).pipe(
map((course) => {
// Update the course
this._course.next(course);
// Return the course
return course;
}),
switchMap((course) => {
if ( !course )
{
return throwError('Could not found course with id of ' + id + '!');
}
return of(course);
})
);
}
}

View File

@@ -1,29 +0,0 @@
export interface Category
{
id?: string;
title?: string;
slug?: string;
}
export interface Course
{
id?: string;
title?: string;
slug?: string;
description?: string;
category?: string;
duration?: number;
steps?: {
order?: number;
title?: string;
subtitle?: string;
content?: string;
}[];
totalSteps?: number;
updatedAt?: number;
featured?: boolean;
progress?: {
currentStep?: number;
completed?: number;
};
}

View File

@@ -1,201 +0,0 @@
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden">
<mat-drawer-container class="flex-auto h-full">
<!-- Drawer -->
<mat-drawer
class="w-90 dark:bg-gray-900"
[autoFocus]="false"
[mode]="drawerMode"
[opened]="drawerOpened"
#matDrawer>
<div class="flex flex-col items-start p-8 border-b">
<!-- Back to courses -->
<a
class="inline-flex items-center leading-6 text-primary hover:underline"
[routerLink]="['..']">
<span class="inline-flex items-center">
<mat-icon
class="icon-size-5 text-current"
[svgIcon]="'heroicons_solid:arrow-sm-left'"></mat-icon>
<span class="ml-1.5 font-medium leading-5">Back to courses</span>
</span>
</a>
<!-- Course category -->
<ng-container *ngIf="(course.category | fuseFindByKey:'slug':categories) as category">
<div
class="mt-7 py-0.5 px-3 rounded-full text-sm font-semibold"
[ngClass]="{'text-blue-800 bg-blue-100 dark:text-blue-50 dark:bg-blue-500': category.slug === 'web',
'text-green-800 bg-green-100 dark:text-green-50 dark:bg-green-500': category.slug === 'android',
'text-pink-800 bg-pink-100 dark:text-pink-50 dark:bg-pink-500': category.slug === 'cloud',
'text-amber-800 bg-amber-100 dark:text-amber-50 dark:bg-amber-500': category.slug === 'firebase'}">
{{category.title}}
</div>
</ng-container>
<!-- Course title & description -->
<div class="mt-3 text-2xl font-semibold">{{course.title}}</div>
<div class="text-secondary">{{course.description}}</div>
<!-- Course time -->
<div class="mt-6 flex items-center leading-5 text-md text-secondary">
<mat-icon
class="icon-size-5 text-hint"
[svgIcon]="'heroicons_solid:clock'"></mat-icon>
<div class="ml-1.5">{{course.duration}} minutes</div>
</div>
</div>
<!-- Steps -->
<div class="py-2 px-8">
<ol>
<ng-container *ngFor="let step of course.steps; let last = last; trackBy: trackByFn">
<li
class="relative group py-6"
[class.current-step]="step.order === currentStep">
<ng-container *ngIf="!last">
<div
class="absolute top-6 left-4 w-0.5 h-full -ml-px"
[ngClass]="{'bg-primary': step.order < currentStep,
'bg-gray-300 dark:bg-gray-600': step.order >= currentStep}"></div>
</ng-container>
<div
class="relative flex items-start cursor-pointer"
(click)="goToStep(step.order)">
<div
class="flex flex-0 items-center justify-center w-8 h-8 rounded-full ring-2 ring-inset ring-transparent bg-card dark:bg-default"
[ngClass]="{'bg-primary dark:bg-primary text-on-primary group-hover:bg-primary-800': step.order < currentStep,
'ring-primary': step.order === currentStep,
'ring-gray-300 dark:ring-gray-600 group-hover:ring-gray-400': step.order > currentStep}">
<!-- Check icon, show if the step is completed -->
<ng-container *ngIf="step.order < currentStep">
<mat-icon
class="icon-size-5 text-current"
[svgIcon]="'heroicons_solid:check'"></mat-icon>
</ng-container>
<!-- Step order, show if the step is the current step -->
<ng-container *ngIf="step.order === currentStep">
<div class="text-md font-semibold text-primary dark:text-primary-500">{{step.order + 1}}</div>
</ng-container>
<!-- Step order, show if the step is not completed -->
<ng-container *ngIf="step.order > currentStep">
<div class="text-md font-semibold text-hint group-hover:text-secondary">{{step.order + 1}}</div>
</ng-container>
</div>
<div class="ml-4">
<div class="font-medium leading-4">{{step.title}}</div>
<div class="mt-1.5 text-md leading-4 text-secondary">{{step.subtitle}}</div>
</div>
</div>
</li>
</ng-container>
</ol>
</div>
</mat-drawer>
<!-- Drawer content -->
<mat-drawer-content class="flex flex-col overflow-hidden">
<!-- Header -->
<div class="lg:hidden flex flex-0 items-center py-2 pl-4 pr-6 sm:py-4 md:pl-6 md:pr-8 border-b lg:border-b-0 bg-card dark:bg-transparent">
<!-- Title & Actions -->
<button
mat-icon-button
[routerLink]="['..']">
<mat-icon [svgIcon]="'heroicons_outline:arrow-sm-left'"></mat-icon>
</button>
<h2 class="ml-2.5 text-md sm:text-xl font-medium tracking-tight truncate">
{{course.title}}
</h2>
</div>
<mat-progress-bar
class="hidden lg:block flex-0 h-0.5 w-full"
[value]="100 * (currentStep + 1) / course.totalSteps"></mat-progress-bar>
<!-- Main -->
<div
class="flex-auto overflow-y-auto"
cdkScrollable>
<!-- Steps -->
<mat-tab-group
class="fuse-mat-no-header"
[animationDuration]="'200'"
#courseSteps>
<ng-container *ngFor="let step of course.steps; trackBy: trackByFn">
<mat-tab>
<ng-template matTabContent>
<div
class="prose prose-sm max-w-3xl mx-auto sm:my-2 lg:mt-4 p-6 sm:p-10 sm:py-12 rounded-2xl shadow overflow-hidden bg-card"
[innerHTML]="step.content"></div>
</ng-template>
</mat-tab>
</ng-container>
</mat-tab-group>
<!-- Navigation - Desktop -->
<div class="z-10 sticky hidden lg:flex bottom-4 p-4">
<div class="flex items-center justify-center mx-auto p-2 rounded-full shadow-lg bg-primary">
<button
class="flex-0"
mat-flat-button
[color]="'primary'"
(click)="goToPreviousStep()">
<mat-icon
class="mr-2"
[svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
<span class="mr-1">Prev</span>
</button>
<div class="flex items-center justify-center mx-2.5 font-medium leading-5 text-on-primary">
<span>{{currentStep + 1}}</span>
<span class="mx-0.5 text-hint">/</span>
<span>{{course.totalSteps}}</span>
</div>
<button
class="flex-0"
mat-flat-button
[color]="'primary'"
(click)="goToNextStep()">
<span class="ml-1">Next</span>
<mat-icon
class="ml-2"
[svgIcon]="'heroicons_outline:arrow-narrow-right'"></mat-icon>
</button>
</div>
</div>
</div>
<!-- Progress & Navigation - Mobile -->
<div class="lg:hidden flex items-center p-4 border-t bg-card">
<button
mat-icon-button
(click)="matDrawer.toggle()">
<mat-icon [svgIcon]="'heroicons_outline:view-list'"></mat-icon>
</button>
<div class="flex items-center justify-center ml-1 lg:ml-2 font-medium leading-5">
<span>{{currentStep + 1}}</span>
<span class="mx-0.5 text-hint">/</span>
<span>{{course.totalSteps}}</span>
</div>
<mat-progress-bar
class="flex-auto ml-6 rounded-full"
[value]="100 * (currentStep + 1) / course.totalSteps"></mat-progress-bar>
<button
class="ml-4"
mat-icon-button
(click)="goToPreviousStep()">
<mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-left'"></mat-icon>
</button>
<button
class="ml-0.5"
mat-icon-button
(click)="goToNextStep()">
<mat-icon [svgIcon]="'heroicons_outline:arrow-narrow-right'"></mat-icon>
</button>
</div>
</mat-drawer-content>
</mat-drawer-container>
</div>

View File

@@ -1,204 +0,0 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { MatTabGroup } from '@angular/material/tabs';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
import { AcademyService } from 'app/modules/admin/apps/academy/academy.service';
@Component({
selector : 'academy-details',
templateUrl : './details.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AcademyDetailsComponent implements OnInit, OnDestroy
{
@ViewChild('courseSteps', {static: true}) courseSteps: MatTabGroup;
categories: Category[];
course: Course;
currentStep: number = 0;
drawerMode: 'over' | 'side' = 'side';
drawerOpened: boolean = true;
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
@Inject(DOCUMENT) private _document: Document,
private _academyService: AcademyService,
private _changeDetectorRef: ChangeDetectorRef,
private _elementRef: ElementRef,
private _fuseMediaWatcherService: FuseMediaWatcherService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the categories
this._academyService.categories$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((categories: Category[]) => {
// Get the categories
this.categories = categories;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Get the course
this._academyService.course$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((course: Course) => {
// Get the course
this.course = course;
// Go to step
this.goToStep(course.progress.currentStep);
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Subscribe to media changes
this._fuseMediaWatcherService.onMediaChange$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe(({matchingAliases}) => {
// Set the drawerMode and drawerOpened
if ( matchingAliases.includes('lg') )
{
this.drawerMode = 'side';
this.drawerOpened = true;
}
else
{
this.drawerMode = 'over';
this.drawerOpened = false;
}
// Mark for check
this._changeDetectorRef.markForCheck();
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Go to given step
*
* @param step
*/
goToStep(step: number): void
{
// Set the current step
this.currentStep = step;
// Go to the step
this.courseSteps.selectedIndex = this.currentStep;
// Mark for check
this._changeDetectorRef.markForCheck();
}
/**
* Go to previous step
*/
goToPreviousStep(): void
{
// Return if we already on the first step
if ( this.currentStep === 0 )
{
return;
}
// Go to step
this.goToStep(this.currentStep - 1);
// Scroll the current step selector from sidenav into view
this._scrollCurrentStepElementIntoView();
}
/**
* Go to next step
*/
goToNextStep(): void
{
// Return if we already on the last step
if ( this.currentStep === this.course.totalSteps - 1 )
{
return;
}
// Go to step
this.goToStep(this.currentStep + 1);
// Scroll the current step selector from sidenav into view
this._scrollCurrentStepElementIntoView();
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Scrolls the current step element from
* sidenav into the view. This only happens when
* previous/next buttons pressed as we don't want
* to change the scroll position of the sidebar
* when the user actually clicks around the sidebar.
*
* @private
*/
private _scrollCurrentStepElementIntoView(): void
{
// Wrap everything into setTimeout so we can make sure that the 'current-step' class points to correct element
setTimeout(() => {
// Get the current step element and scroll it into view
const currentStepElement = this._document.getElementsByClassName('current-step')[0];
if ( currentStepElement )
{
currentStepElement.scrollIntoView({
behavior: 'smooth',
block : 'start'
});
}
});
}
}

View File

@@ -1,196 +0,0 @@
<div
class="absolute inset-0 flex flex-col min-w-0 overflow-y-auto"
cdkScrollable>
<!-- Header -->
<div class="relative flex-0 py-8 px-4 sm:p-16 overflow-hidden bg-gray-800 dark">
<!-- Background - @formatter:off -->
<!-- Rings -->
<svg class="absolute inset-0 pointer-events-none"
viewBox="0 0 960 540" width="100%" height="100%" preserveAspectRatio="xMidYMax slice" xmlns="http://www.w3.org/2000/svg">
<g class="text-gray-700 opacity-25" fill="none" stroke="currentColor" stroke-width="100">
<circle r="234" cx="196" cy="23"></circle>
<circle r="234" cx="790" cy="491"></circle>
</g>
</svg>
<!-- @formatter:on -->
<div class="z-10 relative flex flex-col items-center">
<h2 class="text-xl font-semibold">FUSE ACADEMY</h2>
<div class="mt-1 text-4xl sm:text-7xl font-extrabold tracking-tight leading-tight text-center">
What do you want to learn today?
</div>
<div class="max-w-2xl mt-6 sm:text-2xl text-center tracking-tight text-secondary">
Our courses will step you through the process of a building small applications, or adding new features to existing applications.
</div>
</div>
</div>
<!-- Main -->
<div class="flex flex-auto p-6 sm:p-10">
<div class="flex flex-col flex-auto w-full max-w-xs sm:max-w-5xl mx-auto">
<!-- Filters -->
<div class="flex flex-col sm:flex-row items-center justify-between w-full max-w-xs sm:max-w-none">
<mat-form-field class="fuse-mat-no-subscript w-full sm:w-36">
<mat-select
[value]="'all'"
(selectionChange)="filterByCategory($event)">
<mat-option [value]="'all'">All</mat-option>
<ng-container *ngFor="let category of categories; trackBy: trackByFn">
<mat-option [value]="category.slug">{{category.title}}</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<mat-form-field
class="fuse-mat-no-subscript w-full sm:w-72 mt-4 sm:mt-0 sm:ml-4"
[floatLabel]="'always'">
<mat-icon
matPrefix
class="icon-size-5"
[svgIcon]="'heroicons_solid:search'"></mat-icon>
<input
(input)="filterByQuery(query.value)"
placeholder="Search by title or description"
matInput
#query>
</mat-form-field>
<mat-slide-toggle
class="mt-8 sm:mt-0 sm:ml-auto"
[color]="'primary'"
(change)="toggleCompleted($event)">
Hide completed
</mat-slide-toggle>
</div>
<!-- Courses -->
<ng-container *ngIf="this.filteredCourses.length; else noCourses">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8 mt-8 sm:mt-10">
<ng-container *ngFor="let course of filteredCourses; trackBy: trackByFn">
<!-- Course -->
<div class="flex flex-col h-96 shadow rounded-2xl overflow-hidden bg-card">
<div class="flex flex-col p-6">
<div class="flex items-center justify-between">
<!-- Course category -->
<ng-container *ngIf="(course.category | fuseFindByKey:'slug':categories) as category">
<div
class="py-0.5 px-3 rounded-full text-sm font-semibold"
[ngClass]="{'text-blue-800 bg-blue-100 dark:text-blue-50 dark:bg-blue-500': category.slug === 'web',
'text-green-800 bg-green-100 dark:text-green-50 dark:bg-green-500': category.slug === 'android',
'text-pink-800 bg-pink-100 dark:text-pink-50 dark:bg-pink-500': category.slug === 'cloud',
'text-amber-800 bg-amber-100 dark:text-amber-50 dark:bg-amber-500': category.slug === 'firebase'}">
{{category.title}}
</div>
</ng-container>
<!-- Completed at least once -->
<div class="flex items-center">
<ng-container *ngIf="course.progress.completed > 0">
<mat-icon
class="icon-size-5 text-green-600"
[svgIcon]="'heroicons_solid:badge-check'"
[matTooltip]="'You completed this course at least once'"></mat-icon>
</ng-container>
</div>
</div>
<!-- Course title & description -->
<div class="mt-4 text-lg font-medium">{{course.title}}</div>
<div class="mt-0.5 line-clamp-2 text-secondary">{{course.description}}</div>
<div class="w-12 h-1 my-6 border-t-2"></div>
<!-- Course time -->
<div class="flex items-center leading-5 text-md text-secondary">
<mat-icon
class="icon-size-5 text-hint"
[svgIcon]="'heroicons_solid:clock'"></mat-icon>
<div class="ml-1.5">{{course.duration}} minutes</div>
</div>
<!-- Course completion -->
<div class="flex items-center mt-2 leading-5 text-md text-secondary">
<mat-icon
class="icon-size-5 text-hint"
[svgIcon]="'heroicons_solid:academic-cap'"></mat-icon>
<ng-container *ngIf="course.progress.completed === 0">
<div class="ml-1.5">Never completed</div>
</ng-container>
<ng-container *ngIf="course.progress.completed > 0">
<div class="ml-1.5">
<span>Completed</span>
<span class="ml-1">
<!-- Once -->
<ng-container *ngIf="course.progress.completed === 1">once</ng-container>
<!-- Twice -->
<ng-container *ngIf="course.progress.completed === 2">twice</ng-container>
<!-- Others -->
<ng-container *ngIf="course.progress.completed > 2">{{course.progress.completed}}
{{course.progress.completed | i18nPlural: {
'=0' : 'time',
'=1' : 'time',
'other': 'times'
} }}
</ng-container>
</span>
</div>
</ng-container>
</div>
</div>
<!-- Footer -->
<div class="flex flex-col w-full mt-auto">
<!-- Course progress -->
<div class="relative h-0.5">
<div
class="z-10 absolute inset-x-0 h-6 -mt-3"
[matTooltip]="course.progress.currentStep / course.totalSteps | percent"
[matTooltipPosition]="'above'"
[matTooltipClass]="'-mb-0.5'"></div>
<mat-progress-bar
class="h-0.5"
[value]="(100 * course.progress.currentStep) / course.totalSteps"></mat-progress-bar>
</div>
<!-- Course launch button -->
<div class="px-6 py-4 text-right bg-gray-50 dark:bg-transparent">
<button
mat-stroked-button
[routerLink]="[course.id]">
<span class="inline-flex items-center">
<!-- Not started -->
<ng-container *ngIf="course.progress.currentStep === 0">
<!-- Never completed -->
<ng-container *ngIf="course.progress.completed === 0">
<span>Start</span>
</ng-container>
<!-- Completed before -->
<ng-container *ngIf="course.progress.completed > 0">
<span>Start again</span>
</ng-container>
</ng-container>
<!-- Started -->
<ng-container *ngIf="course.progress.currentStep > 0">
<span>Continue</span>
</ng-container>
<mat-icon
class="ml-1.5 icon-size-5"
[svgIcon]="'heroicons_solid:arrow-sm-right'"></mat-icon>
</span>
</button>
</div>
</div>
</div>
</ng-container>
</div>
</ng-container>
<!-- No courses -->
<ng-template #noCourses>
<div class="flex flex-auto flex-col items-center justify-center bg-gray-100 dark:bg-transparent">
<mat-icon
class="icon-size-20"
[svgIcon]="'iconsmind:file_search'"></mat-icon>
<div class="mt-6 text-2xl font-semibold tracking-tight text-secondary">No courses found!</div>
</div>
</ng-template>
</div>
</div>
</div>

View File

@@ -1,157 +0,0 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MatSelectChange } from '@angular/material/select';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AcademyService } from 'app/modules/admin/apps/academy/academy.service';
import { Category, Course } from 'app/modules/admin/apps/academy/academy.types';
@Component({
selector : 'academy-list',
templateUrl : './list.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AcademyListComponent implements OnInit, OnDestroy
{
categories: Category[];
courses: Course[];
filteredCourses: Course[];
filters: {
categorySlug$: BehaviorSubject<string>;
query$: BehaviorSubject<string>;
hideCompleted$: BehaviorSubject<boolean>;
} = {
categorySlug$ : new BehaviorSubject('all'),
query$ : new BehaviorSubject(''),
hideCompleted$: new BehaviorSubject(false)
};
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _activatedRoute: ActivatedRoute,
private _changeDetectorRef: ChangeDetectorRef,
private _router: Router,
private _academyService: AcademyService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the categories
this._academyService.categories$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((categories: Category[]) => {
this.categories = categories;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Get the courses
this._academyService.courses$
.pipe(takeUntil(this._unsubscribeAll))
.subscribe((courses: Course[]) => {
this.courses = this.filteredCourses = courses;
// Mark for check
this._changeDetectorRef.markForCheck();
});
// Filter the courses
combineLatest([this.filters.categorySlug$, this.filters.query$, this.filters.hideCompleted$])
.subscribe(([categorySlug, query, hideCompleted]) => {
// Reset the filtered courses
this.filteredCourses = this.courses;
// Filter by category
if ( categorySlug !== 'all' )
{
this.filteredCourses = this.filteredCourses.filter(course => course.category === categorySlug);
}
// Filter by search query
if ( query !== '' )
{
this.filteredCourses = this.filteredCourses.filter(course => course.title.toLowerCase().includes(query.toLowerCase())
|| course.description.toLowerCase().includes(query.toLowerCase())
|| course.category.toLowerCase().includes(query.toLowerCase()));
}
// Filter by completed
if ( hideCompleted )
{
this.filteredCourses = this.filteredCourses.filter(course => course.progress.completed === 0);
}
});
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Filter by search query
*
* @param query
*/
filterByQuery(query: string): void
{
this.filters.query$.next(query);
}
/**
* Filter by category
*
* @param change
*/
filterByCategory(change: MatSelectChange): void
{
this.filters.categorySlug$.next(change.value);
}
/**
* Show/hide completed courses
*
* @param change
*/
toggleCompleted(change: MatSlideToggleChange): void
{
this.filters.hideCompleted$.next(change.checked);
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
}

View File

@@ -1,386 +0,0 @@
<div class="absolute inset-0 flex flex-col min-w-0 overflow-hidden dark:bg-gray-900">
<mat-drawer-container class="flex-auto h-full bg-transparent">
<!-- Drawer -->
<mat-drawer
class="w-60 dark:bg-gray-900"
[autoFocus]="false"
[mode]="drawerMode"
[opened]="drawerOpened"
#drawer>
<calendar-sidebar (calendarUpdated)="onCalendarUpdated($event)"></calendar-sidebar>
</mat-drawer>
<mat-drawer-content class="flex">
<!-- Main -->
<div class="flex flex-col flex-auto">
<!-- Header -->
<div class="flex flex-0 flex-wrap items-center p-4 border-b bg-card">
<button
mat-icon-button
(click)="toggleDrawer()">
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
<div class="ml-4 text-2xl font-semibold tracking-tight whitespace-nowrap">
{{viewTitle}}
</div>
<button
class="ml-5"
mat-icon-button
(click)="previous()">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:chevron-left'"></mat-icon>
</button>
<button
mat-icon-button
(click)="next()">
<mat-icon
class="icon-size-5"
[svgIcon]="'heroicons_solid:chevron-right'"></mat-icon>
</button>
<button
class="hidden md:inline-flex"
mat-icon-button
(click)="today()">
<mat-icon [svgIcon]="'heroicons_outline:calendar'"></mat-icon>
</button>
<div class="hidden md:block ml-auto">
<mat-form-field class="fuse-mat-dense fuse-mat-no-subscript w-30 ml-2">
<mat-select
(selectionChange)="changeView(viewChanger.value)"
[value]="view"
#viewChanger="matSelect">
<mat-option [value]="'dayGridMonth'">Month</mat-option>
<mat-option [value]="'timeGridWeek'">Week</mat-option>
<mat-option [value]="'timeGridDay'">Day</mat-option>
<mat-option [value]="'listYear'">Schedule</mat-option>
</mat-select>
</mat-form-field>
</div>
<!-- Mobile menu -->
<div class="md:hidden ml-auto">
<button
class=""
[matMenuTriggerFor]="actionsMenu"
mat-icon-button>
<mat-icon [svgIcon]="'heroicons_outline:dots-vertical'"></mat-icon>
<mat-menu #actionsMenu="matMenu">
<button
mat-menu-item
(click)="today()">
<mat-icon [svgIcon]="'heroicons_outline:calendar'"></mat-icon>
<span>Go to today</span>
</button>
<button
[matMenuTriggerFor]="actionsViewsMenu"
mat-menu-item>
<mat-icon [svgIcon]="'heroicons_outline:view-grid'"></mat-icon>
<span>View</span>
</button>
</mat-menu>
<mat-menu #actionsViewsMenu="matMenu">
<button
mat-menu-item
[disabled]="view === 'dayGridMonth'"
(click)="changeView('dayGridMonth')">
<span>Month</span>
</button>
<button
mat-menu-item
[disabled]="view === 'timeGridWeek'"
(click)="changeView('timeGridWeek')">
<span>Week</span>
</button>
<button
mat-menu-item
[disabled]="view === 'timeGridDay'"
(click)="changeView('timeGridDay')">
<span>Day</span>
</button>
<button
mat-menu-item
[disabled]="view === 'listYear'"
(click)="changeView('listYear')">
<span>Schedule</span>
</button>
</mat-menu>
</button>
</div>
</div>
<!-- FullCalendar -->
<div class="flex flex-col flex-auto">
<full-calendar
[defaultView]="view"
[events]="events"
[firstDay]="settings.startWeekOn"
[handleWindowResize]="false"
[header]="false"
[height]="'parent'"
[plugins]="calendarPlugins"
[views]="views"
(dateClick)="onDateClick($event)"
(eventClick)="onEventClick($event)"
(eventRender)="onEventRender($event)"
#fullCalendar></full-calendar>
</div>
<!-- Event panel -->
<ng-template #eventPanel>
<!-- Preview mode -->
<ng-container *ngIf="panelMode === 'view'">
<div class="flex-auto p-8">
<!-- Info -->
<div class="flex">
<mat-icon [svgIcon]="'heroicons_outline:information-circle'"></mat-icon>
<div class="flex flex-auto justify-between ml-6">
<!-- Info -->
<div>
<div class="text-3xl font-semibold tracking-tight leading-none">{{event.title || '(No title)'}}</div>
<div class="mt-0.5 text-secondary">{{event.start | date:'EEEE, MMMM d'}}</div>
<div class="text-secondary">{{recurrenceStatus}}</div>
</div>
<!-- Actions -->
<div class="flex -mt-2 -mr-2 ml-10">
<!-- Non-recurring event -->
<ng-container *ngIf="!event.recurrence">
<!-- Edit -->
<button
mat-icon-button
(click)="changeEventPanelMode('edit', 'single')">
<mat-icon [svgIcon]="'heroicons_outline:pencil-alt'"></mat-icon>
</button>
<!-- Delete -->
<button
mat-icon-button
(click)="deleteEvent(event)">
<mat-icon [svgIcon]="'heroicons_outline:trash'"></mat-icon>
</button>
</ng-container>
<!-- Recurring event -->
<ng-container *ngIf="event.recurrence">
<!-- Edit -->
<button
mat-icon-button
[matMenuTriggerFor]="editMenu">
<mat-icon [svgIcon]="'heroicons_outline:pencil-alt'"></mat-icon>
</button>
<mat-menu #editMenu="matMenu">
<button
mat-menu-item
(click)="changeEventPanelMode('edit', 'single')">
This event
</button>
<button
mat-menu-item
*ngIf="!event.isFirstInstance"
(click)="changeEventPanelMode('edit', 'future')">
This and following events
</button>
<button
mat-menu-item
(click)="changeEventPanelMode('edit', 'all')">
All events
</button>
</mat-menu>
<!-- Delete -->
<button
mat-icon-button
[matMenuTriggerFor]="deleteMenu">
<mat-icon [svgIcon]="'heroicons_outline:trash'"></mat-icon>
</button>
<mat-menu #deleteMenu="matMenu">
<button
mat-menu-item
(click)="deleteEvent(event, 'single')">
This event
</button>
<button
mat-menu-item
*ngIf="!event.isFirstInstance"
(click)="deleteEvent(event, 'future')">
This and following events
</button>
<button
mat-menu-item
(click)="deleteEvent(event, 'all')">
All events
</button>
</mat-menu>
</ng-container>
</div>
</div>
</div>
<!-- Description -->
<div
class="flex mt-6"
*ngIf="event.description">
<mat-icon [svgIcon]="'heroicons_outline:menu-alt-2'"></mat-icon>
<div class="flex-auto ml-6">{{event.description}}</div>
</div>
<!-- Calendar -->
<div class="flex mt-6">
<mat-icon [svgIcon]="'heroicons_outline:calendar'"></mat-icon>
<div class="flex flex-auto items-center ml-6">
<div
class="w-2 h-2 rounded-full"
[ngClass]="getCalendar(event.calendarId).color"></div>
<div class="ml-3 leading-none">{{getCalendar(event.calendarId).title}}</div>
</div>
</div>
</div>
</ng-container>
<!-- Add / Edit mode -->
<ng-container *ngIf="panelMode === 'add' || panelMode === 'edit'">
<form
class="flex flex-col w-full p-6 pt-8 sm:pt-10 sm:pr-8"
[formGroup]="eventForm">
<!-- Title -->
<div class="flex items-center">
<mat-icon
class="hidden sm:inline-flex mr-6"
[svgIcon]="'heroicons_outline:pencil-alt'"></mat-icon>
<mat-form-field class="fuse-mat-no-subscript flex-auto">
<input
matInput
[formControlName]="'title'"
[placeholder]="'Event title'">
</mat-form-field>
</div>
<!-- Dates -->
<div class="flex items-start mt-6">
<mat-icon
class="hidden sm:inline-flex mt-3 mr-6"
[svgIcon]="'heroicons_outline:calendar'"></mat-icon>
<div class="flex-auto">
<fuse-date-range
[formControlName]="'range'"
[dateFormat]="settings.dateFormat"
[timeRange]="!eventForm.get('allDay').value"
[timeFormat]="settings.timeFormat"></fuse-date-range>
<mat-checkbox
class="mt-4"
[color]="'primary'"
[formControlName]="'allDay'">
All day
</mat-checkbox>
</div>
</div>
<!-- Recurrence -->
<div
class="flex items-center mt-6"
*ngIf="!event.recurrence || eventEditMode !== 'single'">
<mat-icon
class="hidden sm:inline-flex mr-6 transform -scale-x-1"
[svgIcon]="'heroicons_outline:refresh'"></mat-icon>
<div
class="flex flex-auto items-center h-12 px-4 rounded-md border cursor-pointer shadow-sm border-gray-300 dark:bg-black dark:bg-opacity-5 dark:border-gray-500"
(click)="openRecurrenceDialog()">
<div class="flex-auto">
{{recurrenceStatus || 'Does not repeat'}}
</div>
</div>
</div>
<!-- Calendar -->
<div class="flex items-center mt-6">
<mat-icon
class="hidden sm:inline-flex mr-6"
[svgIcon]="'heroicons_outline:tag'"></mat-icon>
<mat-form-field class="fuse-mat-no-subscript flex-auto">
<mat-select
[formControlName]="'calendarId'"
(change)="$event.stopImmediatePropagation()">
<mat-select-trigger class="inline-flex items-center leading-none">
<span
class="w-3 h-3 rounded-full"
[ngClass]="getCalendar(eventForm.get('calendarId').value)?.color"></span>
<span class="ml-3">{{getCalendar(eventForm.get('calendarId').value)?.title}}</span>
</mat-select-trigger>
<ng-container *ngFor="let calendar of calendars">
<mat-option [value]="calendar.id">
<div class="inline-flex items-center">
<span
class="w-3 h-3 rounded-full"
[ngClass]="calendar.color"></span>
<span class="ml-3">{{calendar.title}}</span>
</div>
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
<!-- Description -->
<div class="flex items-start mt-6">
<mat-icon
class="hidden sm:inline-flex mr-6 mt-3"
[svgIcon]="'heroicons_outline:menu-alt-2'"></mat-icon>
<mat-form-field class="fuse-mat-textarea fuse-mat-no-subscript flex-auto">
<textarea
matInput
matTextareaAutosize
[matAutosizeMinRows]="1"
[formControlName]="'description'"
[placeholder]="'Event description'">
</textarea>
</mat-form-field>
</div>
<!-- Actions -->
<div class="ml-auto mt-6">
<button
class="add"
*ngIf="panelMode === 'add'"
mat-flat-button
type="button"
[color]="'primary'"
(click)="addEvent()">
Add
</button>
<button
class="save"
*ngIf="panelMode === 'edit'"
mat-flat-button
type="button"
[color]="'primary'"
(click)="updateEvent()">
Save
</button>
</div>
</form>
</ng-container>
</ng-template>
</div>
</mat-drawer-content>
</mat-drawer-container>
</div>

View File

@@ -1,121 +0,0 @@
calendar {
/* Tweak: FullCalendar CSS only height to improve resize performance */
/* With this tweak, we can disable "handleWindowResize" option of FullCalendar */
/* which disables the height calculations on window resize and increases the */
/* overall performance. */
/* This tweak only affects the Calendar app's FullCalendar. */
full-calendar {
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 100%;
.fc-view-container {
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 100%;
.fc-view {
/* Day grid - Month view */
/* Time grid - Week view */
/* Time grid - Day view */
&.fc-dayGridMonth-view,
&.fc-timeGridWeek-view,
&.fc-timeGridDay-view {
display: flex;
flex-direction: column;
flex: 1 0 auto;
width: 100%;
height: 100%;
> table {
display: flex;
flex-direction: column;
flex: 1 0 auto;
height: 100%;
> thead {
display: flex;
}
> tbody {
display: flex;
flex: 1 1 auto;
overflow: hidden;
> tr {
display: flex;
> td {
display: flex;
flex-direction: column;
.fc-scroller {
flex: 1 1 auto;
overflow: hidden scroll !important;
height: auto !important;
}
}
}
}
}
}
/* Day grid - Month view */
&.fc-dayGridMonth-view {
> table {
> tbody {
> tr {
> td {
.fc-scroller {
display: flex;
> .fc-day-grid {
display: flex;
flex-direction: column;
min-height: 580px;
> .fc-row {
flex: 1 0 0;
height: auto !important;
}
}
}
}
}
}
}
}
/* List - Year view */
&.fc-listYear-view {
width: 100%;
height: 100%;
.fc-scroller {
width: 100%;
height: 100% !important;
overflow: hidden !important;
}
}
}
}
}
}
/* Event panel */
.calendar-event-panel {
border-radius: 8px;
@apply shadow-2xl bg-card;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { MAT_DATE_FORMATS } from '@angular/material/core';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatMomentDateModule } from '@angular/material-moment-adapter';
import { MatRadioModule } from '@angular/material/radio';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FullCalendarModule } from '@fullcalendar/angular';
import { FuseDateRangeModule } from '@fuse/components/date-range';
import { SharedModule } from 'app/shared/shared.module';
import { CalendarComponent } from 'app/modules/admin/apps/calendar/calendar.component';
import { CalendarRecurrenceComponent } from 'app/modules/admin/apps/calendar/recurrence/recurrence.component';
import { CalendarSettingsComponent } from 'app/modules/admin/apps/calendar/settings/settings.component';
import { CalendarSidebarComponent } from 'app/modules/admin/apps/calendar/sidebar/sidebar.component';
import { calendarRoutes } from 'app/modules/admin/apps/calendar/calendar.routing';
@NgModule({
declarations: [
CalendarComponent,
CalendarRecurrenceComponent,
CalendarSettingsComponent,
CalendarSidebarComponent
],
imports : [
RouterModule.forChild(calendarRoutes),
ScrollingModule,
MatButtonModule,
MatButtonToggleModule,
MatCheckboxModule,
MatDatepickerModule,
MatDialogModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatMenuModule,
MatMomentDateModule,
MatRadioModule,
MatSelectModule,
MatSidenavModule,
MatTooltipModule,
FullCalendarModule,
FuseDateRangeModule,
SharedModule
],
providers : [
{
provide : MAT_DATE_FORMATS,
useValue: {
parse : {
dateInput: 'DD.MM.YYYY'
},
display: {
dateInput : 'DD.MM.YYYY',
monthYearLabel : 'MMM YYYY',
dateA11yLabel : 'DD.MM.YYYY',
monthYearA11yLabel: 'MMMM YYYY'
}
}
}
]
})
export class CalendarModule
{
}

View File

@@ -1,89 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { CalendarService } from 'app/modules/admin/apps/calendar/calendar.service';
import { Calendar, CalendarSettings, CalendarWeekday } from 'app/modules/admin/apps/calendar/calendar.types';
@Injectable({
providedIn: 'root'
})
export class CalendarCalendarsResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _calendarService: CalendarService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Calendar[]>
{
return this._calendarService.getCalendars();
}
}
@Injectable({
providedIn: 'root'
})
export class CalendarSettingsResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _calendarService: CalendarService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CalendarSettings>
{
return this._calendarService.getSettings();
}
}
@Injectable({
providedIn: 'root'
})
export class CalendarWeekdaysResolver implements Resolve<any>
{
/**
* Constructor
*/
constructor(private _calendarService: CalendarService)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Resolver
*
* @param route
* @param state
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CalendarWeekday[]>
{
return this._calendarService.getWeekdays();
}
}

View File

@@ -1,23 +0,0 @@
import { Route } from '@angular/router';
import { CalendarComponent } from 'app/modules/admin/apps/calendar/calendar.component';
import { CalendarSettingsComponent } from 'app/modules/admin/apps/calendar/settings/settings.component';
import { CalendarCalendarsResolver, CalendarSettingsResolver, CalendarWeekdaysResolver } from 'app/modules/admin/apps/calendar/calendar.resolvers';
export const calendarRoutes: Route[] = [
{
path : '',
component: CalendarComponent,
resolve : {
calendars: CalendarCalendarsResolver,
settings : CalendarSettingsResolver,
weekdays : CalendarWeekdaysResolver
}
},
{
path : 'settings',
component: CalendarSettingsComponent,
resolve : {
settings: CalendarSettingsResolver
}
}
];

Some files were not shown because too many files have changed in this diff Show More