Compare commits

..

11 Commits

Author SHA1 Message Date
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
994 changed files with 8994 additions and 87580 deletions

View File

@@ -1,95 +0,0 @@
{
"root": true,
"env": {
"es6": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/ng-cli-compat",
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "",
"style": "camelCase"
}
],
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"@typescript-eslint/no-inferrable-types": "off",
"arrow-parens": [
"error",
"as-needed",
{
"requireForBlockBody": true
}
],
"brace-style": [
"off",
"off"
],
"import/order": "off",
"max-len": [
"error",
{
"ignorePattern": "^import |^export | implements",
"code": 180
}
],
"no-underscore-dangle": "off",
"object-shorthand": "off",
"quote-props": [
"error",
"consistent"
],
"quotes": [
"error",
"single"
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@
# profiling files # profiling files
chrome-profiler-events*.json chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors # IDEs and editors
/.idea /.idea

View File

@@ -2,9 +2,6 @@
// @ 3rd party credits // @ 3rd party credits
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// Flags
https://github.com/Yummygum/flagpack-core
// Icons // Icons
Material - https://material.io/tools/icons Material - https://material.io/tools/icons
Feather - https://feathericons.com/ Feather - https://feathericons.com/

View File

@@ -1,7 +1,5 @@
# Fuse - Admin template and Starter project for Angular # Fuse - Admin template and Starter project for Angular
This project was generated with [Angular CLI](https://github.com/angular/angular-cli)
## Development server ## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
@@ -20,8 +18,8 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
## Running end-to-end tests ## 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 [Protractor](http://www.protractortest.org/).
## Further help ## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -22,7 +22,7 @@
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "aot": true,
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"apexcharts", "apexcharts",
"highlight.js", "highlight.js",
@@ -33,12 +33,7 @@
"assets": [ "assets": [
"src/favicon-16x16.png", "src/favicon-16x16.png",
"src/favicon-32x32.png", "src/favicon-32x32.png",
"src/assets", "src/assets"
{
"glob": "_redirects",
"input": "src",
"output": "/"
}
], ],
"stylePreprocessorOptions": { "stylePreprocessorOptions": {
"includePaths": [ "includePaths": [
@@ -57,6 +52,19 @@
}, },
"configurations": { "configurations": {
"production": { "production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [ "budgets": [
{ {
"type": "initial", "type": "initial",
@@ -68,37 +76,20 @@
"maximumWarning": "100kb", "maximumWarning": "100kb",
"maximumError": "150kb" "maximumError": "150kb"
} }
], ]
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
} }
}, }
"defaultConfiguration": "production"
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "fuse:build"
},
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "fuse:build:production" "browserTarget": "fuse:build:production"
},
"development": {
"browserTarget": "fuse:build:development"
} }
}, }
"defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
@@ -113,32 +104,44 @@
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js", "karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [ "assets": [
"src/favicon-16x16.png", "src/favicon-16x16.png",
"src/favicon-32x32.png", "src/favicon-32x32.png",
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"src/styles/styles.scss" "src/styles.scss"
], ],
"scripts": [] "scripts": []
} }
}, },
"lint": { "lint": {
"builder": "@angular-eslint/builder:lint", "builder": "@angular-devkit/build-angular:tslint",
"options": { "options": {
"lintFilePatterns": [ "tsConfig": [
"src/**/*.ts", "tsconfig.app.json",
"src/**/*.html" "tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
] ]
} }
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "fuse:serve"
},
"configurations": {
"production": {
"devServerTarget": "fuse:serve:production"
}
}
} }
} }
} }
}, },
"defaultProject": "fuse", "defaultProject": "fuse"
"cli": {
"defaultCollection": "@angular-eslint/schematics"
}
} }

40
e2e/protractor.conf.js Normal file
View File

@@ -0,0 +1,40 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const {SpecReporter, StacktraceOption} = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout : 11000,
specs : [
'./src/**/*.e2e-spec.ts'
],
capabilities : {
browserName: 'chrome'
},
directConnect : true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl : 'http://localhost:4200/',
framework : 'jasmine',
jasmineNodeOpts : {
showColors : true,
defaultTimeoutInterval: 30000,
print : function ()
{
}
},
onPrepare()
{
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

23
e2e/src/app.e2e-spec.ts Normal file
View File

@@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', async () => {
await page.navigateTo();
expect(await page.getTitleText()).toEqual('Welcome to Fuse!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE
} as logging.Entry));
});
});

14
e2e/src/app.po.ts Normal file
View File

@@ -0,0 +1,14 @@
import { browser, by, element } from 'protractor';
export class AppPage
{
async navigateTo(): Promise<unknown>
{
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string>
{
return element(by.css('app-root h1')).getText();
}
}

13
e2e/tsconfig.json Normal file
View File

@@ -0,0 +1,13 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"node"
]
}
}

View File

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

11871
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,28 @@
{ {
"name": "@fuse/demo", "name": "@fuse/demo",
"version": "13.4.0", "version": "12.3.0",
"description": "Fuse - Angular Admin Template and Starter Project",
"author": "https://themeforest.net/user/srcn",
"license": "https://themeforest.net/licenses/standard", "license": "https://themeforest.net/licenses/standard",
"private": true, "private": true,
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build --prod",
"test": "ng test", "test": "ng test",
"lint": "ng lint" "lint": "ng lint",
"e2e": "ng e2e"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "12.1.4", "@angular/animations": "11.2.12",
"@angular/cdk": "12.1.4", "@angular/cdk": "11.2.11",
"@angular/common": "12.1.4", "@angular/common": "11.2.12",
"@angular/compiler": "12.1.4", "@angular/compiler": "11.2.12",
"@angular/core": "12.1.4", "@angular/core": "11.2.12",
"@angular/forms": "12.1.4", "@angular/forms": "11.2.12",
"@angular/material": "12.1.4", "@angular/material": "11.2.11",
"@angular/material-moment-adapter": "12.1.4", "@angular/material-moment-adapter": "11.2.11",
"@angular/platform-browser": "12.1.4", "@angular/platform-browser": "11.2.12",
"@angular/platform-browser-dynamic": "12.1.4", "@angular/platform-browser-dynamic": "11.2.12",
"@angular/router": "12.1.4", "@angular/router": "11.2.12",
"@fullcalendar/angular": "4.4.5-beta", "@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.2", "@fullcalendar/core": "4.4.2",
"@fullcalendar/daygrid": "4.4.2", "@fullcalendar/daygrid": "4.4.2",
@@ -32,59 +31,53 @@
"@fullcalendar/moment": "4.4.2", "@fullcalendar/moment": "4.4.2",
"@fullcalendar/rrule": "4.4.2", "@fullcalendar/rrule": "4.4.2",
"@fullcalendar/timegrid": "4.4.2", "@fullcalendar/timegrid": "4.4.2",
"@ngneat/transloco": "2.22.0", "apexcharts": "3.26.1",
"apexcharts": "3.27.3",
"crypto-js": "3.3.0", "crypto-js": "3.3.0",
"highlight.js": "11.1.0", "highlight.js": "10.7.2",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"moment": "2.29.1", "moment": "2.29.1",
"ng-apexcharts": "1.5.12", "ng-apexcharts": "1.5.9",
"ngx-markdown": "12.0.1", "ngx-markdown": "11.1.3",
"ngx-quill": "14.1.2", "ngx-quill": "13.3.1",
"perfect-scrollbar": "1.5.2", "perfect-scrollbar": "1.5.0",
"quill": "1.3.7", "quill": "1.3.7",
"rrule": "2.6.8", "rrule": "2.6.8",
"rxjs": "6.6.7", "rxjs": "6.6.7",
"tslib": "2.3.0", "tslib": "2.2.0",
"web-animations-js": "2.3.2", "web-animations-js": "2.3.2",
"zone.js": "0.11.4" "zone.js": "0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "12.1.4", "@angular-devkit/build-angular": "0.1102.11",
"@angular-eslint/builder": "12.3.1", "@angular/cli": "11.2.11",
"@angular-eslint/eslint-plugin": "12.3.1", "@angular/compiler-cli": "11.2.12",
"@angular-eslint/eslint-plugin-template": "12.3.1", "@angular/language-service": "11.2.12",
"@angular-eslint/schematics": "12.3.1", "@tailwindcss/aspect-ratio": "0.2.0",
"@angular-eslint/template-parser": "12.3.1", "@tailwindcss/line-clamp": "0.2.0",
"@angular/cli": "12.1.4", "@tailwindcss/typography": "0.4.0",
"@angular/compiler-cli": "12.1.4",
"@tailwindcss/aspect-ratio": "0.2.1",
"@tailwindcss/line-clamp": "0.2.1",
"@tailwindcss/typography": "0.4.1",
"@types/chroma-js": "2.1.3", "@types/chroma-js": "2.1.3",
"@types/crypto-js": "3.1.47", "@types/crypto-js": "3.1.47",
"@types/highlight.js": "10.1.0", "@types/highlight.js": "10.1.0",
"@types/jasmine": "3.8.2", "@types/jasmine": "3.6.9",
"@types/lodash": "4.14.171", "@types/lodash": "4.14.168",
"@types/lodash-es": "4.17.4", "@types/lodash-es": "4.17.4",
"@types/node": "12.20.17", "@types/node": "12.20.10",
"@typescript-eslint/eslint-plugin": "4.28.5", "autoprefixer": "10.2.5",
"@typescript-eslint/parser": "4.28.5", "chroma-js": "2.1.1",
"autoprefixer": "10.3.1", "codelyzer": "6.0.1",
"chroma-js": "2.1.2", "jasmine-core": "3.6.0",
"eslint": "7.31.0", "jasmine-spec-reporter": "5.0.2",
"eslint-plugin-import": "2.23.4", "karma": "6.1.2",
"eslint-plugin-jsdoc": "36.0.6",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.8.0",
"karma": "6.3.4",
"karma-chrome-launcher": "3.1.0", "karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.3", "karma-coverage": "2.0.3",
"karma-jasmine": "4.0.1", "karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.7.0", "karma-jasmine-html-reporter": "1.5.4",
"lodash": "4.17.21", "lodash": "4.17.21",
"postcss": "8.3.6", "postcss": "8.2.13",
"tailwindcss": "2.2.7", "protractor": "7.0.0",
"typescript": "4.3.5" "tailwindcss": "2.1.2",
"ts-node": "8.3.0",
"tslint": "6.1.3",
"typescript": "4.1.5"
} }
} }

View File

@@ -1,14 +1,14 @@
export class FuseAnimationCurves export class FuseAnimationCurves
{ {
static standard = 'cubic-bezier(0.4, 0.0, 0.2, 1)'; static STANDARD_CURVE = 'cubic-bezier(0.4, 0.0, 0.2, 1)';
static deceleration = 'cubic-bezier(0.0, 0.0, 0.2, 1)'; static DECELERATION_CURVE = 'cubic-bezier(0.0, 0.0, 0.2, 1)';
static acceleration = 'cubic-bezier(0.4, 0.0, 1, 1)'; static ACCELERATION_CURVE = 'cubic-bezier(0.4, 0.0, 1, 1)';
static sharp = 'cubic-bezier(0.4, 0.0, 0.6, 1)'; static SHARP_CURVE = 'cubic-bezier(0.4, 0.0, 0.6, 1)';
} }
export class FuseAnimationDurations export class FuseAnimationDurations
{ {
static complex = '375ms'; static COMPLEX = '375ms';
static entering = '225ms'; static ENTERING = '225ms';
static exiting = '195ms'; static EXITING = '195ms';
} }

View File

@@ -24,7 +24,7 @@ const expandCollapse = trigger('expandCollapse',
animate('{{timings}}'), animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )

View File

@@ -25,7 +25,7 @@ const fadeIn = trigger('fadeIn',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -58,7 +58,7 @@ const fadeInTop = trigger('fadeInTop',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -91,7 +91,7 @@ const fadeInBottom = trigger('fadeInBottom',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -124,7 +124,7 @@ const fadeInLeft = trigger('fadeInLeft',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -157,7 +157,7 @@ const fadeInRight = trigger('fadeInRight',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -188,7 +188,7 @@ const fadeOut = trigger('fadeOut',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -221,7 +221,7 @@ const fadeOutTop = trigger('fadeOutTop',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -254,7 +254,7 @@ const fadeOutBottom = trigger('fadeOutBottom',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -287,7 +287,7 @@ const fadeOutLeft = trigger('fadeOutLeft',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -320,7 +320,7 @@ const fadeOutRight = trigger('fadeOutRight',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )

View File

@@ -1 +1 @@
export * from '@fuse/animations/public-api'; export * from './public-api';

View File

@@ -1,10 +1,10 @@
import { expandCollapse } from '@fuse/animations/expand-collapse'; import { expandCollapse } from './expand-collapse';
import { fadeIn, fadeInBottom, fadeInLeft, fadeInRight, fadeInTop, fadeOut, fadeOutBottom, fadeOutLeft, fadeOutRight, fadeOutTop } from '@fuse/animations/fade'; import { fadeIn, fadeInBottom, fadeInLeft, fadeInRight, fadeInTop, fadeOut, fadeOutBottom, fadeOutLeft, fadeOutRight, fadeOutTop } from './fade';
import { shake } from '@fuse/animations/shake'; import { shake } from './shake';
import { slideInBottom, slideInLeft, slideInRight, slideInTop, slideOutBottom, slideOutLeft, slideOutRight, slideOutTop } from '@fuse/animations/slide'; import { slideInBottom, slideInLeft, slideInRight, slideInTop, slideOutBottom, slideOutLeft, slideOutRight, slideOutTop } from './slide';
import { zoomIn, zoomOut } from '@fuse/animations/zoom'; import { zoomIn, zoomOut } from './zoom';
export const fuseAnimations = [ export const FuseAnimations = [
expandCollapse, expandCollapse,
fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight, fadeIn, fadeInTop, fadeInBottom, fadeInLeft, fadeInRight,
fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight, fadeOut, fadeOutTop, fadeOutBottom, fadeOutLeft, fadeOutRight,

View File

@@ -25,7 +25,7 @@ const slideInTop = trigger('slideInTop',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -56,7 +56,7 @@ const slideInBottom = trigger('slideInBottom',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -87,7 +87,7 @@ const slideInLeft = trigger('slideInLeft',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -118,7 +118,7 @@ const slideInRight = trigger('slideInRight',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -149,7 +149,7 @@ const slideOutTop = trigger('slideOutTop',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -180,7 +180,7 @@ const slideOutBottom = trigger('slideOutBottom',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -211,7 +211,7 @@ const slideOutLeft = trigger('slideOutLeft',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )
@@ -242,7 +242,7 @@ const slideOutRight = trigger('slideOutRight',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )

View File

@@ -28,7 +28,7 @@ const zoomIn = trigger('zoomIn',
transition('void => *', animate('{{timings}}'), transition('void => *', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.entering} ${FuseAnimationCurves.deceleration}` timings: `${FuseAnimationDurations.ENTERING} ${FuseAnimationCurves.DECELERATION_CURVE}`
} }
} }
) )
@@ -62,7 +62,7 @@ const zoomOut = trigger('zoomOut',
transition('* => void', animate('{{timings}}'), transition('* => void', animate('{{timings}}'),
{ {
params: { params: {
timings: `${FuseAnimationDurations.exiting} ${FuseAnimationCurves.acceleration}` timings: `${FuseAnimationDurations.EXITING} ${FuseAnimationCurves.ACCELERATION_CURVE}`
} }
} }
) )

View File

@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Ho
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators'; import { filter, takeUntil } from 'rxjs/operators';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion'; import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { fuseAnimations } from '@fuse/animations'; import { FuseAnimations } from '@fuse/animations';
import { FuseAlertAppearance, FuseAlertType } from '@fuse/components/alert/alert.types'; import { FuseAlertAppearance, FuseAlertType } from '@fuse/components/alert/alert.types';
import { FuseAlertService } from '@fuse/components/alert/alert.service'; import { FuseAlertService } from '@fuse/components/alert/alert.service';
import { FuseUtilsService } from '@fuse/services/utils/utils.service'; import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@@ -13,16 +13,14 @@ import { FuseUtilsService } from '@fuse/services/utils/utils.service';
styleUrls : ['./alert.component.scss'], styleUrls : ['./alert.component.scss'],
encapsulation : ViewEncapsulation.None, encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
animations : fuseAnimations, animations : FuseAnimations,
exportAs : 'fuseAlert' exportAs : 'fuseAlert'
}) })
export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_dismissible: BooleanInput; static ngAcceptInputType_dismissible: BooleanInput;
static ngAcceptInputType_dismissed: BooleanInput; static ngAcceptInputType_dismissed: BooleanInput;
static ngAcceptInputType_showIcon: BooleanInput; static ngAcceptInputType_showIcon: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() appearance: FuseAlertAppearance = 'soft'; @Input() appearance: FuseAlertAppearance = 'soft';
@Input() dismissed: boolean = false; @Input() dismissed: boolean = false;
@@ -117,7 +115,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
// Subscribe to the dismiss calls // Subscribe to the dismiss calls
this._fuseAlertService.onDismiss this._fuseAlertService.onDismiss
.pipe( .pipe(
filter(name => this.name === name), filter((name) => this.name === name),
takeUntil(this._unsubscribeAll) takeUntil(this._unsubscribeAll)
) )
.subscribe(() => { .subscribe(() => {
@@ -129,7 +127,7 @@ export class FuseAlertComponent implements OnChanges, OnInit, OnDestroy
// Subscribe to the show calls // Subscribe to the show calls
this._fuseAlertService.onShow this._fuseAlertService.onShow
.pipe( .pipe(
filter(name => this.name === name), filter((name) => this.name === name),
takeUntil(this._unsubscribeAll) takeUntil(this._unsubscribeAll)
) )
.subscribe(() => { .subscribe(() => {

View File

@@ -1,6 +1,6 @@
import { Component, HostBinding, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { Component, HostBinding, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion'; import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
import { fuseAnimations } from '@fuse/animations'; import { FuseAnimations } from '@fuse/animations';
import { FuseCardFace } from '@fuse/components/card/card.types'; import { FuseCardFace } from '@fuse/components/card/card.types';
@Component({ @Component({
@@ -8,15 +8,13 @@ import { FuseCardFace } from '@fuse/components/card/card.types';
templateUrl : './card.component.html', templateUrl : './card.component.html',
styleUrls : ['./card.component.scss'], styleUrls : ['./card.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
animations : fuseAnimations, animations : FuseAnimations,
exportAs : 'fuseCard' exportAs : 'fuseCard'
}) })
export class FuseCardComponent implements OnChanges export class FuseCardComponent implements OnChanges
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_expanded: BooleanInput; static ngAcceptInputType_expanded: BooleanInput;
static ngAcceptInputType_flippable: BooleanInput; static ngAcceptInputType_flippable: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() expanded: boolean = false; @Input() expanded: boolean = false;
@Input() face: FuseCardFace = 'front'; @Input() face: FuseCardFace = 'front';

View File

@@ -23,14 +23,14 @@ import { Moment } from 'moment';
}) })
export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnDestroy export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnDestroy
{ {
@Output() readonly rangeChanged: EventEmitter<{ start: string; end: string }> = new EventEmitter<{ start: string; end: string }>(); @Output() readonly rangeChanged: EventEmitter<{ start: string, end: string }> = new EventEmitter<{ start: string; end: string }>();
@ViewChild('matMonthView1') private _matMonthView1: MatMonthView<any>; @ViewChild('matMonthView1') private _matMonthView1: MatMonthView<any>;
@ViewChild('matMonthView2') private _matMonthView2: MatMonthView<any>; @ViewChild('matMonthView2') private _matMonthView2: MatMonthView<any>;
@ViewChild('pickerPanelOrigin', {read: ElementRef}) private _pickerPanelOrigin: ElementRef; @ViewChild('pickerPanelOrigin', {read: ElementRef}) private _pickerPanelOrigin: ElementRef;
@ViewChild('pickerPanel') private _pickerPanel: TemplateRef<any>; @ViewChild('pickerPanel') private _pickerPanel: TemplateRef<any>;
@HostBinding('class.fuse-date-range') private _defaultClassNames = true; @HostBinding('class.fuse-date-range') private _defaultClassNames = true;
activeDates: { month1: Moment | null; month2: Moment | null } = { activeDates: { month1: Moment | null, month2: Moment | null } = {
month1: null, month1: null,
month2: null month2: null
}; };
@@ -41,7 +41,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _onChange: (value: any) => void; private _onChange: (value: any) => void;
private _onTouched: (value: any) => void; private _onTouched: (value: any) => void;
private _programmaticChange!: boolean; private _programmaticChange!: boolean;
private _range: { start: Moment | null; end: Moment | null } = { private _range: { start: Moment | null, end: Moment | null } = {
start: null, start: null,
end : null end : null
}; };
@@ -61,9 +61,9 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _viewContainerRef: ViewContainerRef private _viewContainerRef: ViewContainerRef
) )
{ {
this._onChange = (): void => { this._onChange = () => {
}; };
this._onTouched = (): void => { this._onTouched = () => {
}; };
this.dateFormat = 'DD/MM/YYYY'; this.dateFormat = 'DD/MM/YYYY';
this.timeFormat = '12'; this.timeFormat = '12';
@@ -330,7 +330,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
* *
* @param range * @param range
*/ */
writeValue(range: { start: string; end: string }): void writeValue(range: { start: string, end: string }): void
{ {
// Set this change as a programmatic one // Set this change as a programmatic one
this._programmaticChange = true; this._programmaticChange = true;
@@ -361,7 +361,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
// @ TODO: Workaround until "angular/issues/20007" resolved // @ TODO: Workaround until "angular/issues/20007" resolved
this.writeValue = (): void => { this.writeValue = () => {
}; };
} }
@@ -481,8 +481,11 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
*/ */
dateFilter(): any dateFilter(): any
{ {
// If we are selecting the end date, disable all the dates that comes before the start date return (date: Moment): boolean => {
return (date: Moment): boolean => !(this.setWhichDate === 'end' && date.isBefore(this._range.start, 'day'));
// If we are selecting the end date, disable all the dates that comes before the start date
return !(this.setWhichDate === 'end' && date.isBefore(this._range.start, 'day'));
};
} }
/** /**
@@ -664,7 +667,7 @@ export class FuseDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _parseTime(value: string): Moment private _parseTime(value: string): Moment
{ {
// Parse the time using the time regexp // Parse the time using the time regexp
const timeArr = value.split(this._timeRegExp).filter(part => part !== ''); const timeArr = value.split(this._timeRegExp).filter((part) => part !== '');
// Get the meridiem // Get the meridiem
const meridiem = timeArr[2] || null; const meridiem = timeArr[2] || null;

View File

@@ -14,11 +14,9 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
}) })
export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_fixed: BooleanInput; static ngAcceptInputType_fixed: BooleanInput;
static ngAcceptInputType_opened: BooleanInput; static ngAcceptInputType_opened: BooleanInput;
static ngAcceptInputType_transparentOverlay: BooleanInput; static ngAcceptInputType_transparentOverlay: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() fixed: boolean = false; @Input() fixed: boolean = false;
@Input() mode: FuseDrawerMode = 'side'; @Input() mode: FuseDrawerMode = 'side';
@@ -78,40 +76,6 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
}; };
} }
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -341,13 +305,6 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1})) animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
]).create(this._overlay); ]).create(this._overlay);
// Once the animation is done...
this._player.onDone(() => {
// Destroy the player
this._player.destroy();
});
// Play the animation // Play the animation
this._player.play(); this._player.play();
@@ -380,9 +337,6 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
// Once the animation is done... // Once the animation is done...
this._player.onDone(() => { this._player.onDone(() => {
// Destroy the player
this._player.destroy();
// If the backdrop still exists... // If the backdrop still exists...
if ( this._overlay ) if ( this._overlay )
{ {
@@ -393,6 +347,36 @@ export class FuseDrawerComponent implements OnChanges, OnInit, OnDestroy
}); });
} }
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
/** /**
* Open/close the drawer * Open/close the drawer
* *

View File

@@ -1,12 +1,7 @@
<!-- Button --> <!-- Button -->
<button <button
mat-icon-button mat-icon-button
[matTooltip]="tooltip || 'Toggle Fullscreen'" [matTooltip]="'Toggle Fullscreen'"
(click)="toggleFullscreen()"> (click)="toggleFullscreen()">
<ng-container [ngTemplateOutlet]="iconTpl || defaultIconTpl"></ng-container>
</button>
<!-- Default icon -->
<ng-template #defaultIconTpl>
<mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon> <mat-icon [svgIcon]="'heroicons_outline:arrows-expand'"></mat-icon>
</ng-template> </button>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Inject, Input, OnInit, TemplateRef, ViewEncapsulation } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fullscreen.types'; import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fullscreen.types';
@@ -11,8 +11,6 @@ import { FSDocument, FSDocumentElement } from '@fuse/components/fullscreen/fulls
}) })
export class FuseFullscreenComponent implements OnInit export class FuseFullscreenComponent implements OnInit
{ {
@Input() iconTpl: TemplateRef<any>;
@Input() tooltip: string;
private _fsDoc: FSDocument; private _fsDoc: FSDocument;
private _fsDocEl: FSDocumentElement; private _fsDocEl: FSDocumentElement;
private _isFullscreen: boolean = false; private _isFullscreen: boolean = false;

View File

@@ -3,17 +3,15 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { FuseFullscreenComponent } from '@fuse/components/fullscreen/fullscreen.component'; import { FuseFullscreenComponent } from '@fuse/components/fullscreen/fullscreen.component';
import { CommonModule } from '@angular/common';
@NgModule({ @NgModule({
declarations: [ declarations: [
FuseFullscreenComponent FuseFullscreenComponent
], ],
imports: [ imports : [
MatButtonModule, MatButtonModule,
MatIconModule, MatIconModule,
MatTooltipModule, MatTooltipModule
CommonModule
], ],
exports : [ exports : [
FuseFullscreenComponent FuseFullscreenComponent

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import hljs from 'highlight.js'; import * as hljs from 'highlight.js';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -60,7 +60,7 @@ export class FuseHighlightService
} }
// Iterate through the lines // Iterate through the lines
lines.filter(line => line.length) lines.filter((line) => line.length)
.forEach((line, index) => { .forEach((line, index) => {
// Always get the indentation of the first line so we can // Always get the indentation of the first line so we can
@@ -77,6 +77,6 @@ export class FuseHighlightService
// Iterate through the lines one more time, remove the extra // Iterate through the lines one more time, remove the extra
// indentation, join them together and return it // indentation, join them together and return it
return lines.map(line => line.substring(indentation)).join('\n'); return lines.map((line) => line.substring(indentation)).join('\n');
} }
} }

View File

@@ -1 +1 @@
export * from '@fuse/components/masonry/public-api'; export * from '@fuse/components/card/public-api';

View File

@@ -1,5 +1,5 @@
import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core'; import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core';
import { fuseAnimations } from '@fuse/animations'; import { FuseAnimations } from '@fuse/animations';
import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
@Component({ @Component({
@@ -7,7 +7,7 @@ import { FuseMediaWatcherService } from '@fuse/services/media-watcher';
templateUrl : './masonry.component.html', templateUrl : './masonry.component.html',
styleUrls : ['./masonry.component.scss'], styleUrls : ['./masonry.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
animations : fuseAnimations, animations : FuseAnimations,
exportAs : 'fuseMasonry' exportAs : 'fuseMasonry'
}) })
export class FuseMasonryComponent implements OnChanges, AfterViewInit export class FuseMasonryComponent implements OnChanges, AfterViewInit

View File

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

View File

@@ -1,11 +1,9 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component'; import { FuseHorizontalNavigationComponent } from '@fuse/components/navigation/horizontal/horizontal.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service'; import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types'; import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@Component({ @Component({
selector : 'fuse-horizontal-navigation-basic-item', selector : 'fuse-horizontal-navigation-basic-item',
@@ -18,7 +16,6 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
@Input() item: FuseNavigationItem; @Input() item: FuseNavigationItem;
@Input() name: string; @Input() name: string;
isActiveMatchOptions: IsActiveMatchOptions;
private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent; private _fuseHorizontalNavigationComponent: FuseHorizontalNavigationComponent;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -27,15 +24,9 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
*/ */
constructor( constructor(
private _changeDetectorRef: ChangeDetectorRef, private _changeDetectorRef: ChangeDetectorRef,
private _fuseNavigationService: FuseNavigationService, private _fuseNavigationService: FuseNavigationService
private _fuseUtilsService: FuseUtilsService
) )
{ {
// Set the equivalent of {exact: false} as default for active match options.
// We are not assigning the item.isActiveMatchOptions directly to the
// [routerLinkActiveOptions] because if it's "undefined" initially, the router
// will throw an error and stop working.
this.isActiveMatchOptions = this._fuseUtilsService.subsetMatchOptions;
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -47,20 +38,9 @@ export class FuseHorizontalNavigationBasicItemComponent implements OnInit, OnDes
*/ */
ngOnInit(): void ngOnInit(): void
{ {
// Set the "isActiveMatchOptions" either from item's
// "isActiveMatchOptions" or the equivalent form of
// item's "exactMatch" option
this.isActiveMatchOptions =
this.item.isActiveMatchOptions ?? this.item.exactMatch
? this._fuseUtilsService.exactMatchOptions
: this._fuseUtilsService.subsetMatchOptions;
// Get the parent navigation component // Get the parent navigation component
this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name); this._fuseHorizontalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
// Mark for check
this._changeDetectorRef.markForCheck();
// Subscribe to onRefreshed on the navigation component // Subscribe to onRefreshed on the navigation component
this._fuseHorizontalNavigationComponent.onRefreshed.pipe( this._fuseHorizontalNavigationComponent.onRefreshed.pipe(
takeUntil(this._unsubscribeAll) takeUntil(this._unsubscribeAll)

View File

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

View File

@@ -15,9 +15,7 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
}) })
export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDestroy export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_child: BooleanInput; static ngAcceptInputType_child: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() child: boolean = false; @Input() child: boolean = false;
@Input() item: FuseNavigationItem; @Input() item: FuseNavigationItem;
@@ -81,15 +79,4 @@ export class FuseHorizontalNavigationBranchItemComponent implements OnInit, OnDe
// Mark for check // Mark for check
this._changeDetectorRef.markForCheck(); 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

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

View File

@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
import { ReplaySubject, Subject } from 'rxjs'; import { ReplaySubject, Subject } from 'rxjs';
import { fuseAnimations } from '@fuse/animations'; import { FuseAnimations } from '@fuse/animations';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types'; import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service'; import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseUtilsService } from '@fuse/services/utils/utils.service'; import { FuseUtilsService } from '@fuse/services/utils/utils.service';
@@ -9,7 +9,7 @@ import { FuseUtilsService } from '@fuse/services/utils/utils.service';
selector : 'fuse-horizontal-navigation', selector : 'fuse-horizontal-navigation',
templateUrl : './horizontal.component.html', templateUrl : './horizontal.component.html',
styleUrls : ['./horizontal.component.scss'], styleUrls : ['./horizontal.component.scss'],
animations : fuseAnimations, animations : FuseAnimations,
encapsulation : ViewEncapsulation.None, encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseHorizontalNavigation' exportAs : 'fuseHorizontalNavigation'

View File

@@ -46,7 +46,7 @@ export class FuseNavigationService
* *
* @param name * @param name
*/ */
getComponent<T>(name: string): T getComponent(name: string): any
{ {
return this._componentRegistry.get(name); return this._componentRegistry.get(name);
} }
@@ -67,6 +67,7 @@ export class FuseNavigationService
* Get navigation from storage by key * Get navigation from storage by key
* *
* @param key * @param key
* @returns {any}
*/ */
getNavigation(key: string): FuseNavigationItem[] getNavigation(key: string): FuseNavigationItem[]
{ {
@@ -96,6 +97,7 @@ export class FuseNavigationService
* *
* @param navigation * @param navigation
* @param flatNavigation * @param flatNavigation
* @returns {FuseNavigationItem[]}
*/ */
getFlatNavigation(navigation: FuseNavigationItem[], flatNavigation: FuseNavigationItem[] = []): FuseNavigationItem[] getFlatNavigation(navigation: FuseNavigationItem[], flatNavigation: FuseNavigationItem[] = []): FuseNavigationItem[]
{ {

View File

@@ -1,5 +1,3 @@
import { IsActiveMatchOptions } from '@angular/router';
export interface FuseNavigationItem export interface FuseNavigationItem
{ {
id?: string; id?: string;
@@ -15,17 +13,9 @@ export interface FuseNavigationItem
hidden?: (item: FuseNavigationItem) => boolean; hidden?: (item: FuseNavigationItem) => boolean;
active?: boolean; active?: boolean;
disabled?: boolean; disabled?: boolean;
tooltip?: string;
link?: string; link?: string;
externalLink?: boolean; externalLink?: boolean;
target?:
| '_blank'
| '_self'
| '_parent'
| '_top'
| string;
exactMatch?: boolean; exactMatch?: boolean;
isActiveMatchOptions?: IsActiveMatchOptions;
function?: (item: FuseNavigationItem) => void; function?: (item: FuseNavigationItem) => void;
classes?: { classes?: {
title?: string; title?: string;

View File

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

View File

@@ -15,10 +15,8 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
}) })
export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnInit, OnDestroy export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_autoCollapse: BooleanInput; static ngAcceptInputType_autoCollapse: BooleanInput;
static ngAcceptInputType_skipChildren: BooleanInput; static ngAcceptInputType_skipChildren: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() activeItemId: string; @Input() activeItemId: string;
@Input() autoCollapse: boolean; @Input() autoCollapse: boolean;
@@ -103,21 +101,6 @@ export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnIn
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
} }
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Private methods // @ Private methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -185,4 +168,19 @@ export class FuseVerticalNavigationAsideItemComponent implements OnChanges, OnIn
// Mark for check // Mark for check
this._changeDetectorRef.markForCheck(); this._changeDetectorRef.markForCheck();
} }
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
} }

View File

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

View File

@@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component'; import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
@@ -18,7 +17,6 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
@Input() item: FuseNavigationItem; @Input() item: FuseNavigationItem;
@Input() name: string; @Input() name: string;
isActiveMatchOptions: IsActiveMatchOptions;
private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent; private _fuseVerticalNavigationComponent: FuseVerticalNavigationComponent;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -31,11 +29,6 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
private _fuseUtilsService: FuseUtilsService private _fuseUtilsService: FuseUtilsService
) )
{ {
// Set the equivalent of {exact: false} as default for active match options.
// We are not assigning the item.isActiveMatchOptions directly to the
// [routerLinkActiveOptions] because if it's "undefined" initially, the router
// will throw an error and stop working.
this.isActiveMatchOptions = this._fuseUtilsService.subsetMatchOptions;
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -47,20 +40,9 @@ export class FuseVerticalNavigationBasicItemComponent implements OnInit, OnDestr
*/ */
ngOnInit(): void ngOnInit(): void
{ {
// Set the "isActiveMatchOptions" either from item's
// "isActiveMatchOptions" or the equivalent form of
// item's "exactMatch" option
this.isActiveMatchOptions =
this.item.isActiveMatchOptions ?? this.item.exactMatch
? this._fuseUtilsService.exactMatchOptions
: this._fuseUtilsService.subsetMatchOptions;
// Get the parent navigation component // Get the parent navigation component
this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name); this._fuseVerticalNavigationComponent = this._fuseNavigationService.getComponent(this.name);
// Mark for check
this._changeDetectorRef.markForCheck();
// Subscribe to onRefreshed on the navigation component // Subscribe to onRefreshed on the navigation component
this._fuseVerticalNavigationComponent.onRefreshed.pipe( this._fuseVerticalNavigationComponent.onRefreshed.pipe(
takeUntil(this._unsubscribeAll) takeUntil(this._unsubscribeAll)

View File

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

View File

@@ -3,7 +3,7 @@ import { NavigationEnd, Router } from '@angular/router';
import { BooleanInput } from '@angular/cdk/coercion'; import { BooleanInput } from '@angular/cdk/coercion';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators'; import { filter, takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { FuseAnimations } from '@fuse/animations';
import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component'; import { FuseVerticalNavigationComponent } from '@fuse/components/navigation/vertical/vertical.component';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service'; import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types'; import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types';
@@ -12,14 +12,12 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
selector : 'fuse-vertical-navigation-collapsable-item', selector : 'fuse-vertical-navigation-collapsable-item',
templateUrl : './collapsable.component.html', templateUrl : './collapsable.component.html',
styles : [], styles : [],
animations : fuseAnimations, animations : FuseAnimations,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, OnDestroy export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_autoCollapse: BooleanInput; static ngAcceptInputType_autoCollapse: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() autoCollapse: boolean; @Input() autoCollapse: boolean;
@Input() item: FuseNavigationItem; @Input() item: FuseNavigationItem;
@@ -181,6 +179,84 @@ export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, O
this._unsubscribeAll.complete(); this._unsubscribeAll.complete();
} }
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check if the given item has the given url
* in one of its children
*
* @param item
* @param currentUrl
* @private
*/
private _hasActiveChild(item: FuseNavigationItem, currentUrl: string): boolean
{
const children = item.children;
if ( !children )
{
return false;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._hasActiveChild(child, currentUrl) )
{
return true;
}
}
// Check if the child has a link and is active
if ( child.link && this._router.isActive(child.link, child.exactMatch || false) )
{
return true;
}
}
return false;
}
/**
* Check if this is a children
* of the given item
*
* @param parent
* @param item
* @return {boolean}
* @private
*/
private _isChildrenOf(parent: FuseNavigationItem, item: FuseNavigationItem): boolean
{
const children = parent.children;
if ( !children )
{
return false;
}
if ( children.indexOf(item) > -1 )
{
return true;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._isChildrenOf(child, item) )
{
return true;
}
}
}
return false;
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Public methods // @ Public methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -267,81 +343,4 @@ export class FuseVerticalNavigationCollapsableItemComponent implements OnInit, O
{ {
return item.id || index; return item.id || index;
} }
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check if the given item has the given url
* in one of its children
*
* @param item
* @param currentUrl
* @private
*/
private _hasActiveChild(item: FuseNavigationItem, currentUrl: string): boolean
{
const children = item.children;
if ( !children )
{
return false;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._hasActiveChild(child, currentUrl) )
{
return true;
}
}
// Check if the child has a link and is active
if ( child.link && this._router.isActive(child.link, child.exactMatch || false) )
{
return true;
}
}
return false;
}
/**
* Check if this is a children
* of the given item
*
* @param parent
* @param item
* @private
*/
private _isChildrenOf(parent: FuseNavigationItem, item: FuseNavigationItem): boolean
{
const children = parent.children;
if ( !children )
{
return false;
}
if ( children.indexOf(item) > -1 )
{
return true;
}
for ( const child of children )
{
if ( child.children )
{
if ( this._isChildrenOf(child, item) )
{
return true;
}
}
}
return false;
}
} }

View File

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

View File

@@ -14,9 +14,7 @@ import { FuseNavigationItem } from '@fuse/components/navigation/navigation.types
}) })
export class FuseVerticalNavigationGroupItemComponent implements OnInit, OnDestroy export class FuseVerticalNavigationGroupItemComponent implements OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_autoCollapse: BooleanInput; static ngAcceptInputType_autoCollapse: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() autoCollapse: boolean; @Input() autoCollapse: boolean;
@Input() item: FuseNavigationItem; @Input() item: FuseNavigationItem;

View File

@@ -335,7 +335,6 @@ fuse-vertical-navigation {
} }
> .fuse-vertical-navigation-item-children { > .fuse-vertical-navigation-item-children {
margin-top: 6px;
> *:last-child { > *:last-child {
padding-bottom: 6px; padding-bottom: 6px;

View File

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

View File

@@ -4,7 +4,7 @@ import { NavigationEnd, Router } from '@angular/router';
import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay'; import { ScrollStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';
import { merge, ReplaySubject, Subject, Subscription } from 'rxjs'; import { merge, ReplaySubject, Subject, Subscription } from 'rxjs';
import { delay, filter, takeUntil } from 'rxjs/operators'; import { delay, filter, takeUntil } from 'rxjs/operators';
import { fuseAnimations } from '@fuse/animations'; import { FuseAnimations } from '@fuse/animations';
import { FuseNavigationItem, FuseVerticalNavigationAppearance, FuseVerticalNavigationMode, FuseVerticalNavigationPosition } from '@fuse/components/navigation/navigation.types'; import { FuseNavigationItem, FuseVerticalNavigationAppearance, FuseVerticalNavigationMode, FuseVerticalNavigationPosition } from '@fuse/components/navigation/navigation.types';
import { FuseNavigationService } from '@fuse/components/navigation/navigation.service'; import { FuseNavigationService } from '@fuse/components/navigation/navigation.service';
import { FuseScrollbarDirective } from '@fuse/directives/scrollbar/scrollbar.directive'; import { FuseScrollbarDirective } from '@fuse/directives/scrollbar/scrollbar.directive';
@@ -15,18 +15,16 @@ import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
selector : 'fuse-vertical-navigation', selector : 'fuse-vertical-navigation',
templateUrl : './vertical.component.html', templateUrl : './vertical.component.html',
styleUrls : ['./vertical.component.scss'], styleUrls : ['./vertical.component.scss'],
animations : fuseAnimations, animations : FuseAnimations,
encapsulation : ViewEncapsulation.None, encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'fuseVerticalNavigation' exportAs : 'fuseVerticalNavigation'
}) })
export class FuseVerticalNavigationComponent implements OnChanges, OnInit, AfterViewInit, OnDestroy export class FuseVerticalNavigationComponent implements OnChanges, OnInit, AfterViewInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_inner: BooleanInput; static ngAcceptInputType_inner: BooleanInput;
static ngAcceptInputType_opened: BooleanInput; static ngAcceptInputType_opened: BooleanInput;
static ngAcceptInputType_transparentOverlay: BooleanInput; static ngAcceptInputType_transparentOverlay: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() appearance: FuseVerticalNavigationAppearance = 'default'; @Input() appearance: FuseVerticalNavigationAppearance = 'default';
@Input() autoCollapse: boolean = true; @Input() autoCollapse: boolean = true;
@@ -73,10 +71,10 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
private _fuseUtilsService: FuseUtilsService private _fuseUtilsService: FuseUtilsService
) )
{ {
this._handleAsideOverlayClick = (): void => { this._handleAsideOverlayClick = () => {
this.closeAside(); this.closeAside();
}; };
this._handleOverlayClick = (): void => { this._handleOverlayClick = () => {
this.close(); this.close();
}; };
} }
@@ -153,40 +151,6 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
}); });
} }
// -----------------------------------------------------------------------------------------------------
// @ Decorated methods
// -----------------------------------------------------------------------------------------------------
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks // @ Lifecycle hooks
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -374,10 +338,6 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
*/ */
ngOnDestroy(): void ngOnDestroy(): void
{ {
// Forcefully close the navigation and aside in case they are opened
this.close();
this.closeAside();
// Deregister the navigation component from the registry // Deregister the navigation component from the registry
this._fuseNavigationService.deregisterComponent(this.name); this._fuseNavigationService.deregisterComponent(this.name);
@@ -712,6 +672,36 @@ export class FuseVerticalNavigationComponent implements OnChanges, OnInit, After
}); });
} }
/**
* On mouseenter
*
* @private
*/
@HostListener('mouseenter')
private _onMouseenter(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = true;
}
/**
* On mouseleave
*
* @private
*/
@HostListener('mouseleave')
private _onMouseleave(): void
{
// Enable the animations
this._enableAnimations();
// Set the hovered
this._hovered = false;
}
/** /**
* Open/close the navigation * Open/close the navigation
* *

View File

@@ -0,0 +1,114 @@
import { ChangeDetectorRef, Directive, ElementRef, HostBinding, HostListener, Input, NgZone, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { Subject } from 'rxjs';
@Directive({
selector: 'textarea[fuseAutogrow]',
exportAs: 'fuseAutogrow'
})
export class FuseAutogrowDirective implements OnChanges, OnInit, OnDestroy
{
// tslint:disable-next-line:no-input-rename
@Input('fuseAutogrowVerticalPadding') padding: number = 8;
@HostBinding('rows') private _rows: number = 1;
private _height: string = 'auto';
private _unsubscribeAll: Subject<any> = new Subject<any>();
/**
* Constructor
*/
constructor(
private _elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
private _ngZone: NgZone
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Host binding for component inline styles
*/
@HostBinding('style') get styleList(): any
{
return {
'height' : this._height,
'overflow': 'hidden',
'resize' : 'none'
};
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Padding
if ( 'fuseAutogrowVerticalPadding' in changes )
{
// Resize
this._resize();
}
}
/**
* On init
*/
ngOnInit(): void
{
// Resize for the first time
this._resize();
}
/**
* On destroy
*/
ngOnDestroy(): void
{
// Unsubscribe from all subscriptions
this._unsubscribeAll.next();
this._unsubscribeAll.complete();
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Resize on 'input' and 'ngModelChange' events
*
* @private
*/
@HostListener('input')
@HostListener('ngModelChange')
private _resize(): 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._height = 'auto';
// Detect the changes so the height is applied
this._changeDetectorRef.detectChanges();
// Get the scrollHeight and subtract the vertical padding
this._height = `${this._elementRef.nativeElement.scrollHeight - this.padding}px`;
// Detect the changes one more time to apply the final height
this._changeDetectorRef.detectChanges();
});
});
}
}

View File

@@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { FuseAutogrowDirective } from '@fuse/directives/autogrow/autogrow.directive';
@NgModule({
declarations: [
FuseAutogrowDirective
],
exports : [
FuseAutogrowDirective
]
})
export class FuseAutogrowModule
{
}

View File

@@ -0,0 +1 @@
export * from '@fuse/directives/autogrow/public-api';

View File

@@ -0,0 +1,2 @@
export * from '@fuse/directives/autogrow/autogrow.directive';
export * from '@fuse/directives/autogrow/autogrow.module';

View File

@@ -17,9 +17,7 @@ import { ScrollbarGeometry, ScrollbarPosition } from '@fuse/directives/scrollbar
}) })
export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
{ {
/* eslint-disable @typescript-eslint/naming-convention */
static ngAcceptInputType_fuseScrollbar: BooleanInput; static ngAcceptInputType_fuseScrollbar: BooleanInput;
/* eslint-enable @typescript-eslint/naming-convention */
@Input() fuseScrollbar: boolean = true; @Input() fuseScrollbar: boolean = true;
@Input() fuseScrollbarOptions: PerfectScrollbar.Options; @Input() fuseScrollbarOptions: PerfectScrollbar.Options;
@@ -248,8 +246,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to X * Scroll to X
* *
* @param x * @param {number} x
* @param speed * @param {number} speed
*/ */
scrollToX(x: number, speed?: number): void scrollToX(x: number, speed?: number): void
{ {
@@ -259,8 +257,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to Y * Scroll to Y
* *
* @param y * @param {number} y
* @param speed * @param {number} speed
*/ */
scrollToY(y: number, speed?: number): void scrollToY(y: number, speed?: number): void
{ {
@@ -270,8 +268,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to top * Scroll to top
* *
* @param offset * @param {number} offset
* @param speed * @param {number} speed
*/ */
scrollToTop(offset: number = 0, speed?: number): void scrollToTop(offset: number = 0, speed?: number): void
{ {
@@ -281,8 +279,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to bottom * Scroll to bottom
* *
* @param offset * @param {number} offset
* @param speed * @param {number} speed
*/ */
scrollToBottom(offset: number = 0, speed?: number): void scrollToBottom(offset: number = 0, speed?: number): void
{ {
@@ -293,8 +291,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to left * Scroll to left
* *
* @param offset * @param {number} offset
* @param speed * @param {number} speed
*/ */
scrollToLeft(offset: number = 0, speed?: number): void scrollToLeft(offset: number = 0, speed?: number): void
{ {
@@ -304,8 +302,8 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to right * Scroll to right
* *
* @param offset * @param {number} offset
* @param speed * @param {number} speed
*/ */
scrollToRight(offset: number = 0, speed?: number): void scrollToRight(offset: number = 0, speed?: number): void
{ {
@@ -316,10 +314,10 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
/** /**
* Scroll to element * Scroll to element
* *
* @param qs * @param {string} qs
* @param offset * @param {number} offset
* @param ignoreVisible If true, scrollToElement won't happen if element is already inside the current viewport * @param {boolean} ignoreVisible If true, scrollToElement won't happen if element is already inside the current viewport
* @param speed * @param {number} speed
*/ */
scrollToElement(qs: string, offset: number = 0, ignoreVisible: boolean = false, speed?: number): void scrollToElement(qs: string, offset: number = 0, ignoreVisible: boolean = false, speed?: number): void
{ {
@@ -389,7 +387,7 @@ export class FuseScrollbarDirective implements OnChanges, OnInit, OnDestroy
const cosParameter = (oldValue - value) / 2; const cosParameter = (oldValue - value) / 2;
const step = (newTimestamp: number): void => { const step = (newTimestamp: number) => {
scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp)); scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp));
newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount)); newValue = Math.round(value + cosParameter + cosParameter * Math.cos(scrollCount));

View File

@@ -1,6 +1,5 @@
import { NgModule, Optional, SkipSelf } from '@angular/core'; import { NgModule, Optional, SkipSelf } from '@angular/core';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; 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 { FuseMediaWatcherModule } from '@fuse/services/media-watcher/media-watcher.module';
import { FuseSplashScreenModule } from '@fuse/services/splash-screen/splash-screen.module'; import { FuseSplashScreenModule } from '@fuse/services/splash-screen/splash-screen.module';
import { FuseTailwindConfigModule } from '@fuse/services/tailwind/tailwind.module'; import { FuseTailwindConfigModule } from '@fuse/services/tailwind/tailwind.module';
@@ -8,7 +7,6 @@ import { FuseUtilsModule } from '@fuse/services/utils/utils.module';
@NgModule({ @NgModule({
imports : [ imports : [
FuseConfirmationModule,
FuseMediaWatcherModule, FuseMediaWatcherModule,
FuseSplashScreenModule, FuseSplashScreenModule,
FuseTailwindConfigModule, FuseTailwindConfigModule,

View File

@@ -51,7 +51,7 @@ export class FuseMockApiInterceptor implements HttpInterceptor
delay(handler.delay ?? this._defaultDelay ?? 0), delay(handler.delay ?? this._defaultDelay ?? 0),
switchMap((response) => { switchMap((response) => {
// If there is no response data, // If there is no response mock-api,
// throw an error response // throw an error response
if ( !response ) if ( !response )
{ {
@@ -64,7 +64,7 @@ export class FuseMockApiInterceptor implements HttpInterceptor
return throwError(response); return throwError(response);
} }
// Parse the response data // Parse the response mock-api
const data = { const data = {
status: response[0], status: response[0],
body : response[1] body : response[1]

View File

@@ -29,7 +29,7 @@ export class FuseMockApiModule
{ {
provide : APP_INITIALIZER, provide : APP_INITIALIZER,
deps : [...mockApiServices], deps : [...mockApiServices],
useFactory: () => (): any => null, useFactory: () => () => null,
multi : true multi : true
}, },
{ {

View File

@@ -9,11 +9,11 @@ import { FuseMockApiMethods } from '@fuse/lib/mock-api/mock-api.types';
export class FuseMockApiService export class FuseMockApiService
{ {
private _handlers: { [key: string]: Map<string, FuseMockApiHandler> } = { private _handlers: { [key: string]: Map<string, FuseMockApiHandler> } = {
'delete': new Map<string, FuseMockApiHandler>(), DELETE: new Map<string, FuseMockApiHandler>(),
'get' : new Map<string, FuseMockApiHandler>(), GET : new Map<string, FuseMockApiHandler>(),
'patch' : new Map<string, FuseMockApiHandler>(), PATCH : new Map<string, FuseMockApiHandler>(),
'post' : new Map<string, FuseMockApiHandler>(), POST : new Map<string, FuseMockApiHandler>(),
'put' : new Map<string, FuseMockApiHandler>() PUT : new Map<string, FuseMockApiHandler>()
}; };
/** /**
@@ -34,10 +34,10 @@ export class FuseMockApiService
* @param method * @param method
* @param url * @param url
*/ */
findHandler(method: string, url: string): { handler: FuseMockApiHandler | undefined; urlParams: { [key: string]: string } } findHandler(method: string, url: string): { handler: FuseMockApiHandler | undefined, urlParams: { [key: string]: string } }
{ {
// Prepare the return object // Prepare the return object
const matchingHandler: { handler: FuseMockApiHandler | undefined; urlParams: { [key: string]: string } } = { const matchingHandler: { handler: FuseMockApiHandler | undefined, urlParams: { [key: string]: string } } = {
handler : undefined, handler : undefined,
urlParams: {} urlParams: {}
}; };
@@ -46,7 +46,7 @@ export class FuseMockApiService
const urlParts = url.split('/'); const urlParts = url.split('/');
// Get all related request handlers // Get all related request handlers
const handlers = this._handlers[method.toLowerCase()]; const handlers = this._handlers[method.toUpperCase()];
// Iterate through the handlers // Iterate through the handlers
handlers.forEach((handler, handlerUrl) => { handlers.forEach((handler, handlerUrl) => {
@@ -93,7 +93,7 @@ export class FuseMockApiService
*/ */
onDelete(url: string, delay?: number): FuseMockApiHandler onDelete(url: string, delay?: number): FuseMockApiHandler
{ {
return this._registerHandler('delete', url, delay); return this._registerHandler('DELETE', url, delay);
} }
/** /**
@@ -104,7 +104,7 @@ export class FuseMockApiService
*/ */
onGet(url: string, delay?: number): FuseMockApiHandler onGet(url: string, delay?: number): FuseMockApiHandler
{ {
return this._registerHandler('get', url, delay); return this._registerHandler('GET', url, delay);
} }
/** /**
@@ -115,7 +115,7 @@ export class FuseMockApiService
*/ */
onPatch(url: string, delay?: number): FuseMockApiHandler onPatch(url: string, delay?: number): FuseMockApiHandler
{ {
return this._registerHandler('patch', url, delay); return this._registerHandler('PATCH', url, delay);
} }
/** /**
@@ -126,7 +126,7 @@ export class FuseMockApiService
*/ */
onPost(url: string, delay?: number): FuseMockApiHandler onPost(url: string, delay?: number): FuseMockApiHandler
{ {
return this._registerHandler('post', url, delay); return this._registerHandler('POST', url, delay);
} }
/** /**
@@ -137,7 +137,7 @@ export class FuseMockApiService
*/ */
onPut(url: string, delay?: number): FuseMockApiHandler onPut(url: string, delay?: number): FuseMockApiHandler
{ {
return this._registerHandler('put', url, delay); return this._registerHandler('PUT', url, delay);
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@@ -6,8 +6,8 @@ export type FuseMockApiReplyCallback =
| undefined; | undefined;
export type FuseMockApiMethods = export type FuseMockApiMethods =
| 'get' | 'GET'
| 'post' | 'POST'
| 'put' | 'PUT'
| 'patch' | 'PATCH'
| 'delete'; | 'DELETE';

View File

@@ -16,7 +16,7 @@ export class FuseMockApiUtils
*/ */
static guid(): string static guid(): string
{ {
/* eslint-disable */ /* tslint:disable */
let d = new Date().getTime(); let d = new Date().getTime();
@@ -32,6 +32,6 @@ export class FuseMockApiUtils
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
}); });
/* eslint-enable */ /* tslint:enable */
} }
} }

View File

@@ -28,7 +28,9 @@ export class FuseFindByKeyPipe implements PipeTransform
// If the given value is an array of strings... // If the given value is an array of strings...
if ( Array.isArray(value) ) if ( Array.isArray(value) )
{ {
return value.map(item => source.find(sourceItem => sourceItem[key] === item)); return value.map((item) => {
return source.find((sourceItem) => sourceItem[key] === item);
});
} }
// If the value is a string... // If the value is a string...

View File

@@ -1,31 +0,0 @@
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

@@ -1,57 +0,0 @@
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
});
}
}

View File

@@ -1,22 +0,0 @@
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

@@ -1,85 +0,0 @@
<div class="relative flex flex-col md:w-128 -m-6">
<!-- 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 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

@@ -1,38 +0,0 @@
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',
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

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

View File

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

View File

@@ -7,7 +7,7 @@ import { FuseTailwindService } from '@fuse/services/tailwind/tailwind.service';
@Injectable() @Injectable()
export class FuseMediaWatcherService export class FuseMediaWatcherService
{ {
private _onMediaChange: ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }> = new ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }>(1); private _onMediaChange: ReplaySubject<{ matchingAliases: string[], matchingQueries: any }> = new ReplaySubject<{ matchingAliases: string[], matchingQueries: any }>(1);
/** /**
* Constructor * Constructor
@@ -18,7 +18,7 @@ export class FuseMediaWatcherService
) )
{ {
this._fuseTailwindConfigService.tailwindConfig$.pipe( this._fuseTailwindConfigService.tailwindConfig$.pipe(
switchMap(config => this._breakpointObserver.observe(Object.values(config.breakpoints)).pipe( switchMap((config) => this._breakpointObserver.observe(Object.values(config.breakpoints)).pipe(
map((state) => { map((state) => {
// Prepare the observable values and set their defaults // Prepare the observable values and set their defaults
@@ -57,7 +57,7 @@ export class FuseMediaWatcherService
/** /**
* Getter for _onMediaChange * Getter for _onMediaChange
*/ */
get onMediaChange$(): Observable<{ matchingAliases: string[]; matchingQueries: any }> get onMediaChange$(): Observable<{ matchingAliases: string[], matchingQueries: any }>
{ {
return this._onMediaChange.asObservable(); return this._onMediaChange.asObservable();
} }

View File

@@ -1 +1 @@
export * from '@fuse/services/tailwind/public-api'; export * from '@fuse/services/media-watcher/public-api';

View File

@@ -1,2 +1,2 @@
export * from '@fuse/services/tailwind/tailwind.module'; export * from '@fuse/services/media-watcher/media-watcher.module';
export * from '@fuse/services/tailwind/tailwind.service'; export * from '@fuse/services/media-watcher/media-watcher.service';

View File

@@ -1 +1 @@
export * from '@fuse/services/utils/public-api'; export * from '@fuse/services/config/public-api';

View File

@@ -1,5 +1,4 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { IsActiveMatchOptions } from '@angular/router';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -13,36 +12,6 @@ export class FuseUtilsService
{ {
} }
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Get the equivalent "IsActiveMatchOptions" options for "exact = true".
*/
get exactMatchOptions(): IsActiveMatchOptions
{
return {
paths : 'exact',
fragment : 'ignored',
matrixParams: 'ignored',
queryParams : 'exact'
};
}
/**
* Get the equivalent "IsActiveMatchOptions" options for "exact = false".
*/
get subsetMatchOptions(): IsActiveMatchOptions
{
return {
paths : 'subset',
fragment : 'ignored',
matrixParams: 'ignored',
queryParams : 'subset'
};
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Public methods // @ Public methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------

View File

@@ -136,8 +136,11 @@
.mat-flat-button, .mat-flat-button,
.mat-raised-button, .mat-raised-button,
.mat-stroked-button { .mat-stroked-button {
padding: 0 20px !important;
border-radius: 9999px !important; .fuse-mat-rounded & {
padding: 0 20px;
border-radius: 9999px;
}
} }
/* Target all buttons */ /* Target all buttons */
@@ -175,7 +178,11 @@
/* Add hover and focus style on all buttons */ /* Add hover and focus style on all buttons */
.mat-button-focus-overlay { .mat-button-focus-overlay {
@apply bg-gray-400 bg-opacity-20 dark:bg-black dark:bg-opacity-5 #{'!important'}; @apply bg-gray-400 bg-opacity-20 #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05) !important;
}
} }
/* On palette colored buttons, use a darker color */ /* On palette colored buttons, use a darker color */
@@ -246,7 +253,11 @@
/* Add hover and focus styles */ /* Add hover and focus styles */
.mat-button-focus-overlay { .mat-button-focus-overlay {
@apply bg-gray-400 bg-opacity-20 dark:bg-black dark:bg-opacity-5 #{'!important'}; @apply bg-gray-400 bg-opacity-20 #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05) !important;
}
} }
/* On primary colored buttons, use the primary color as focus overlay */ /* On primary colored buttons, use the primary color as focus overlay */
@@ -319,11 +330,19 @@
/* Border color */ /* Border color */
&:not(.mat-button-disabled) { &:not(.mat-button-disabled) {
@apply border-gray-300 dark:border-gray-500 #{'!important'}; @apply border-gray-300 #{'!important'};
.dark & {
@apply border-gray-500 #{'!important'};
}
} }
&.mat-button-disabled { &.mat-button-disabled {
@apply border-gray-200 dark:border-gray-600 #{'!important'}; @apply border-gray-200 #{'!important'};
.dark & {
@apply border-gray-600 #{'!important'};
}
} }
} }
@@ -331,8 +350,6 @@
/* @ Button Toggle /* @ Button Toggle
/* ----------------------------------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------------------------------- */
.mat-button-toggle-group { .mat-button-toggle-group {
border: none !important;
@apply space-x-1;
&.mat-button-toggle-group-appearance-standard { &.mat-button-toggle-group-appearance-standard {
@@ -340,27 +357,36 @@
background-clip: padding-box; background-clip: padding-box;
} }
} }
}
.mat-button-toggle { /* Rounded design */
border-radius: 9999px; .fuse-mat-rounded {
overflow: hidden;
.mat-button-toggle-group {
border: none !important; border: none !important;
font-weight: 500; @apply space-x-1;
&.mat-button-toggle-checked { .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-label-content { .mat-button-toggle-label-content {
@apply text-default #{'!important'}; padding: 0 20px;
@apply text-secondary;
} }
}
.mat-button-toggle-label-content { .mat-ripple {
padding: 0 20px; border-radius: 9999px;
@apply text-secondary; }
}
.mat-ripple {
border-radius: 9999px;
} }
} }
} }
@@ -400,13 +426,6 @@
font-weight: 500 !important; font-weight: 500 !important;
} }
/* ----------------------------------------------------------------------------------------------------- */
/* @ Dialog
/* ----------------------------------------------------------------------------------------------------- */
.mat-dialog-container {
border-radius: 16px !important;
}
/* ----------------------------------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------------------------------- */
/* @ Drawer /* @ Drawer
/* ----------------------------------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------------------------------- */
@@ -523,7 +542,13 @@
border-radius: 6px; border-radius: 6px;
padding: 0 16px; padding: 0 16px;
border-width: 1px; border-width: 1px;
@apply shadow-sm bg-white border-gray-300 dark:bg-black dark:bg-opacity-5 dark:border-gray-500 #{'!important'}; background-color: white;
@apply border-gray-300 shadow-sm #{'!important'};
.dark & {
background-color: rgba(0, 0, 0, 0.05) !important;
@apply border-gray-500 #{'!important'};
}
.mat-form-field-prefix { .mat-form-field-prefix {
@@ -608,28 +633,12 @@
@apply icon-size-6; @apply icon-size-6;
} }
/* Make mat-select usable as prefix and suffix */ /* Make mat-select usable as */
/* prefix and suffix */
.mat-select { .mat-select {
display: flex; display: flex;
align-items: center; align-items: center;
&:focus {
.mat-select-trigger {
.mat-select-value {
@apply text-primary #{'!important'};
}
.mat-select-arrow-wrapper {
.mat-select-arrow {
border-top-color: var(--fuse-primary) !important;
}
}
}
}
.mat-select-trigger { .mat-select-trigger {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -654,7 +663,6 @@
.mat-select-arrow { .mat-select-arrow {
min-height: 0; min-height: 0;
@apply text-gray-500 dark:text-gray-400 #{'!important'};
} }
} }
} }
@@ -680,8 +688,8 @@
align-self: stretch; align-self: stretch;
min-height: 36px; min-height: 36px;
height: auto; height: auto;
margin: 14px 0; margin: 10px 0;
padding: 0 6px 0 0; padding: 4px 6px 4px 0 !important;
transform: none; transform: none;
} }
@@ -1024,7 +1032,11 @@
.mat-form-field-prefix, .mat-form-field-prefix,
.mat-form-field-suffix { .mat-form-field-suffix {
@apply bg-default border-gray-300 dark:border-gray-500 #{'!important'}; @apply border-gray-300 bg-default #{'!important'};
.dark & {
@apply border-gray-500 #{'!important'};
}
} }
} }
} }
@@ -1319,55 +1331,62 @@
opacity: 0 !important; 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 { .mat-tab-label {
opacity: 1 !important; 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 /* @ Textarea
/* ----------------------------------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------------------------------- */

View File

@@ -1,24 +1,24 @@
@use '~@angular/material' as mat;
@use "sass:map"; @use "sass:map";
@import '~@angular/material/theming';
/* Include the core Angular Material styles */ /** Include the core Angular Material styles */
@include mat.core(); @include mat-core();
/* Create a base theme without color. /** Configure the Angular Material typography */
This will globally set the density and typography for all future color themes. */ @include angular-material-typography(
@include mat.all-component-themes(( mat-typography-config(
color: null, $font-family: theme('fontFamily.sans'),
density: -2, $title: mat-typography-level(1.25rem, 2rem, 600),
typography: mat.define-typography-config( $body-2: mat-typography-level(0.875rem, 1.5rem, 600),
$font-family: theme('fontFamily.sans'), $button: mat-typography-level(0.875rem, 0.875rem, 500),
$title: mat.define-typography-level(1.25rem, 2rem, 600), $input: mat-typography-level(0.875rem, 1.2857142857, 400) // line-height: 20px
$body-2: mat.define-typography-level(0.875rem, 1.5rem, 600), )
$button: mat.define-typography-level(0.875rem, 0.875rem, 500), );
$input: mat.define-typography-level(0.875rem, 1.2857142857, 400) /* line-height: 20px */
)
));
/* Prepare the Background and Foreground maps */ /** Configure the Angular Material density **/
@include angular-material-density(-2);
/** Prepare the Background and Foreground maps */
$background-light: ( $background-light: (
status-bar: #CBD5E1, /* blueGray.300 */ status-bar: #CBD5E1, /* blueGray.300 */
app-bar: #FFFFFF, app-bar: #FFFFFF,
@@ -93,7 +93,7 @@ $foreground-dark: (
slider-off-active: #94A3B8 /* blueGray.400 */ slider-off-active: #94A3B8 /* blueGray.400 */
); );
/* Generate Primary, Accent and Warn palettes */ /** Generate Primary, Accent and Warn palettes */
$palettes: (); $palettes: ();
@each $name in (primary, accent, warn) { @each $name in (primary, accent, warn) {
$palettes: map.merge($palettes, (#{$name}: ( $palettes: map.merge($palettes, (#{$name}: (
@@ -129,13 +129,13 @@ $palettes: ();
))); )));
} }
/* Generate Angular Material themes. Since we are using CSS Custom Properties, /** Generate Angular Material themes. Since we are using CSS Custom Properties,
we don't have to generate a separate Angular Material theme for each color we don't have to generate a separate Angular Material theme for each color
set. We can just create one light and one dark theme and then switch the set. We can just create one light and one dark theme and then switch the
CSS Custom Properties to dynamically switch the colors. */ CSS Custom Properties to dynamically switch the colors. */
body.light, body.light,
body .light { body .light {
$base-light-theme: mat.define-light-theme(( $base-light-theme: mat-light-theme((
color: ($palettes) color: ($palettes)
)); ));
@@ -147,16 +147,23 @@ body .light {
is-dark: map.get(map.get($base-light-theme, color), is-dark), is-dark: map.get(map.get($base-light-theme, color), is-dark),
foreground: $foreground-light, foreground: $foreground-light,
background: $background-light background: $background-light
) ),
typography: null,
density: null,
primary: map.get(map.get($base-light-theme, color), primary),
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
); );
/* Use all-component-colors to only generate the colors */ @include angular-material-theme($light-theme);
@include mat.all-component-colors($light-theme);
} }
body.dark, body.dark,
body .dark { body .dark {
$base-dark-theme: mat.define-dark-theme(( $base-dark-theme: mat-dark-theme((
color: ($palettes) color: ($palettes)
)); ));
@@ -168,9 +175,16 @@ body .dark {
is-dark: map.get(map.get($base-dark-theme, color), is-dark), is-dark: map.get(map.get($base-dark-theme, color), is-dark),
foreground: $foreground-dark, foreground: $foreground-dark,
background: $background-dark background: $background-dark
) ),
typography: null,
density: null,
primary: map.get(map.get($base-dark-theme, color), primary),
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
); );
/* Use all-component-colors to only generate the colors */ @include angular-material-theme($dark-theme);
@include mat.all-component-colors($dark-theme);
} }

View File

@@ -1 +1 @@
export * from '@fuse/validators/public-api'; export * from './public-api';

View File

@@ -1 +1 @@
export * from '@fuse/validators/validators'; export * from './validators';

View File

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

View File

@@ -1 +0,0 @@
/* /index.html 200

View File

@@ -14,8 +14,9 @@ import { AppComponent } from 'app/app.component';
import { appRoutes } from 'app/app.routing'; import { appRoutes } from 'app/app.routing';
const routerConfig: ExtraOptions = { const routerConfig: ExtraOptions = {
scrollPositionRestoration: 'enabled',
preloadingStrategy : PreloadAllModules, preloadingStrategy : PreloadAllModules,
scrollPositionRestoration: 'enabled' relativeLinkResolution : 'legacy'
}; };
@NgModule({ @NgModule({
@@ -27,18 +28,18 @@ const routerConfig: ExtraOptions = {
BrowserAnimationsModule, BrowserAnimationsModule,
RouterModule.forRoot(appRoutes, routerConfig), RouterModule.forRoot(appRoutes, routerConfig),
// Fuse, FuseConfig & FuseMockAPI // Fuse & Fuse Mock API
FuseModule, FuseModule,
FuseConfigModule.forRoot(appConfig), FuseConfigModule.forRoot(appConfig),
FuseMockApiModule.forRoot(mockApiServices), FuseMockApiModule.forRoot(mockApiServices),
// Core module of your application // Core
CoreModule, CoreModule,
// Layout module of your application // Layout
LayoutModule, LayoutModule,
// 3rd party modules that require global configuration via forRoot // 3rd party modules
MarkdownModule.forRoot({}) MarkdownModule.forRoot({})
], ],
bootstrap : [ bootstrap : [

View File

@@ -1,11 +1,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { forkJoin, Observable } from 'rxjs'; import { forkJoin, Observable } from 'rxjs';
import { MessagesService } from 'app/layout/common/messages/messages.service'; import { map } from 'rxjs/operators';
import { NavigationService } from 'app/core/navigation/navigation.service'; import { InitialData } from 'app/app.types';
import { NotificationsService } from 'app/layout/common/notifications/notifications.service';
import { ShortcutsService } from 'app/layout/common/shortcuts/shortcuts.service';
import { UserService } from 'app/core/user/user.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -15,13 +13,7 @@ export class InitialDataResolver implements Resolve<any>
/** /**
* Constructor * Constructor
*/ */
constructor( constructor(private _httpClient: HttpClient)
private _messagesService: MessagesService,
private _navigationService: NavigationService,
private _notificationsService: NotificationsService,
private _shortcutsService: ShortcutsService,
private _userService: UserService
)
{ {
} }
@@ -35,15 +27,29 @@ export class InitialDataResolver implements Resolve<any>
* @param route * @param route
* @param state * @param state
*/ */
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<InitialData>
{ {
// Fork join multiple API endpoint calls to wait all of them to finish // Fork join multiple API endpoint calls to wait all of them to finish
return forkJoin([ return forkJoin([
this._navigationService.get(), this._httpClient.get<any>('api/common/messages'),
this._messagesService.getAll(), this._httpClient.get<any>('api/common/navigation'),
this._notificationsService.getAll(), this._httpClient.get<any>('api/common/notifications'),
this._shortcutsService.getAll(), this._httpClient.get<any>('api/common/shortcuts'),
this._userService.get() this._httpClient.get<any>('api/common/user')
]); ]).pipe(
map(([messages, navigation, notifications, shortcuts, user]) => ({
messages,
navigation: {
compact : navigation.compact,
default : navigation.default,
futuristic: navigation.futuristic,
horizontal: navigation.horizontal
},
notifications,
shortcuts,
user
})
)
);
} }
} }

View File

@@ -5,19 +5,18 @@ import { LayoutComponent } from 'app/layout/layout.component';
import { InitialDataResolver } from 'app/app.resolvers'; import { InitialDataResolver } from 'app/app.resolvers';
// @formatter:off // @formatter:off
/* eslint-disable max-len */ // tslint:disable:max-line-length
/* eslint-disable @typescript-eslint/explicit-function-return-type */
export const appRoutes: Route[] = [ export const appRoutes: Route[] = [
// Redirect empty path to '/dashboards/project' // Redirect empty path to '/example'
{path: '', pathMatch : 'full', redirectTo: 'dashboards/project'}, {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' // 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 // 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. // 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 // Auth routes for guests
{ {
@@ -74,137 +73,7 @@ export const appRoutes: Route[] = [
initialData: InitialDataResolver, initialData: InitialDataResolver,
}, },
children : [ children : [
{path: 'example', loadChildren: () => import('app/modules/admin/example/example.module').then(m => m.ExampleModule)},
// 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)},
{path: 'finance', loadChildren: () => import('app/modules/admin/dashboards/finance/finance.module').then(m => m.FinanceModule)},
{path: 'crypto', loadChildren: () => import('app/modules/admin/dashboards/crypto/crypto.module').then(m => m.CryptoModule)},
]},
// 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: [
// Material Components
{path: 'material-components', loadChildren: () => import('app/modules/admin/ui/material-components/material-components.module').then(m => m.MaterialComponentsModule)},
// Fuse Components
{path: 'fuse-components', loadChildren: () => import('app/modules/admin/ui/fuse-components/fuse-components.module').then(m => m.FuseComponentsModule)},
// Other Components
{path: 'other-components', loadChildren: () => import('app/modules/admin/ui/other-components/other-components.module').then(m => m.OtherComponentsModule)},
// 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)},
// Confirmation Dialog
{path: 'confirmation-dialog', loadChildren: () => import('app/modules/admin/ui/confirmation-dialog/confirmation-dialog.module').then(m => m.ConfirmationDialogModule)},
// 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)}
]},
// 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'}
] ]
} }
]; ];

19
src/app/app.types.ts Normal file
View File

@@ -0,0 +1,19 @@
import { FuseNavigationItem } from '@fuse/components/navigation';
import { Message } from 'app/layout/common/messages/messages.types';
import { Notification } from 'app/layout/common/notifications/notifications.types';
import { Shortcut } from 'app/layout/common/shortcuts/shortcuts.types';
import { User } from 'app/core/user/user.model';
export interface InitialData
{
messages: Message[];
navigation: {
compact: FuseNavigationItem[],
default: FuseNavigationItem[],
futuristic: FuseNavigationItem[],
horizontal: FuseNavigationItem[]
};
notifications: Notification[];
shortcuts: Shortcut[];
user: User;
}

View File

@@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { AuthService } from 'app/core/auth/auth.service';
import { AuthInterceptor } from 'app/core/auth/auth.interceptor';
@NgModule({
imports : [
HttpClientModule
],
providers: [
AuthService,
{
provide : HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi : true
}
]
})
export class AuthModule
{
}

View File

@@ -29,12 +29,12 @@ export class AuthService
*/ */
set accessToken(token: string) set accessToken(token: string)
{ {
localStorage.setItem('accessToken', token); localStorage.setItem('access_token', token);
} }
get accessToken(): string get accessToken(): string
{ {
return localStorage.getItem('accessToken') ?? ''; return localStorage.getItem('access_token') ?? '';
} }
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -66,7 +66,7 @@ export class AuthService
* *
* @param credentials * @param credentials
*/ */
signIn(credentials: { email: string; password: string }): Observable<any> signIn(credentials: { email: string, password: string }): Observable<any>
{ {
// Throw error, if the user is already logged in // Throw error, if the user is already logged in
if ( this._authenticated ) if ( this._authenticated )
@@ -78,7 +78,7 @@ export class AuthService
switchMap((response: any) => { switchMap((response: any) => {
// Store the access token in the local storage // Store the access token in the local storage
this.accessToken = response.accessToken; this.accessToken = response.access_token;
// Set the authenticated flag to true // Set the authenticated flag to true
this._authenticated = true; this._authenticated = true;
@@ -99,17 +99,17 @@ export class AuthService
{ {
// Renew token // Renew token
return this._httpClient.post('api/auth/refresh-access-token', { return this._httpClient.post('api/auth/refresh-access-token', {
accessToken: this.accessToken access_token: this.accessToken
}).pipe( }).pipe(
catchError(() => catchError(() => {
// Return false // Return false
of(false) return of(false);
), }),
switchMap((response: any) => { switchMap((response: any) => {
// Store the access token in the local storage // Store the access token in the local storage
this.accessToken = response.accessToken; this.accessToken = response.access_token;
// Set the authenticated flag to true // Set the authenticated flag to true
this._authenticated = true; this._authenticated = true;
@@ -129,7 +129,7 @@ export class AuthService
signOut(): Observable<any> signOut(): Observable<any>
{ {
// Remove the access token from the local storage // Remove the access token from the local storage
localStorage.removeItem('accessToken'); localStorage.removeItem('access_token');
// Set the authenticated flag to false // Set the authenticated flag to false
this._authenticated = false; this._authenticated = false;
@@ -143,7 +143,7 @@ export class AuthService
* *
* @param user * @param user
*/ */
signUp(user: { name: string; email: string; password: string; company: string }): Observable<any> signUp(user: { name: string, email: string, password: string, company: string }): Observable<any>
{ {
return this._httpClient.post('api/auth/sign-up', user); return this._httpClient.post('api/auth/sign-up', user);
} }
@@ -153,7 +153,7 @@ export class AuthService
* *
* @param credentials * @param credentials
*/ */
unlockSession(credentials: { email: string; password: string }): Observable<any> unlockSession(credentials: { email: string, password: string }): Observable<any>
{ {
return this._httpClient.post('api/auth/unlock-session', credentials); return this._httpClient.post('api/auth/unlock-session', credentials);
} }

View File

@@ -14,38 +14,6 @@ export class AuthUtils
{ {
} }
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Is token expired?
*
* @param token
* @param offsetSeconds
*/
static isTokenExpired(token: string, offsetSeconds?: number): boolean
{
// Return if there is no token
if ( !token || token === '' )
{
return true;
}
// Get the expiration date
const date = this._getTokenExpirationDate(token);
offsetSeconds = offsetSeconds || 0;
if ( date === null )
{
return true;
}
// Check if the token is expired
return !(date.valueOf() > new Date().valueOf() + offsetSeconds * 1000);
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Private methods // @ Private methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -71,7 +39,7 @@ export class AuthUtils
); );
} }
/* eslint-disable */ /* tslint:disable */
for ( for (
// initialize result and counters // initialize result and counters
let bc = 0, bs: any, buffer: any, idx = 0; let bc = 0, bs: any, buffer: any, idx = 0;
@@ -92,7 +60,7 @@ export class AuthUtils
// try to find character in table (0-63, not found => -1) // try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer); buffer = chars.indexOf(buffer);
} }
/* eslint-enable */ /* tslint:enable */
return output; return output;
} }
@@ -107,7 +75,9 @@ export class AuthUtils
{ {
return decodeURIComponent( return decodeURIComponent(
Array.prototype.map Array.prototype.map
.call(this._b64decode(str), (c: any) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) .call(this._b64decode(str), (c: any) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('') .join('')
); );
} }
@@ -201,4 +171,36 @@ export class AuthUtils
return date; return date;
} }
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Is token expired?
*
* @param token
* @param offsetSeconds
*/
static isTokenExpired(token: string, offsetSeconds?: number): boolean
{
// Return if there is no token
if ( !token || token === '' )
{
return true;
}
// Get the expiration date
const date = this._getTokenExpirationDate(token);
offsetSeconds = offsetSeconds || 0;
if ( date === null )
{
return true;
}
// Check if the token is expired
return !(date.valueOf() > new Date().valueOf() + offsetSeconds * 1000);
}
} }

View File

@@ -19,6 +19,39 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
{ {
} }
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @param redirectURL
* @private
*/
private _check(redirectURL: string): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is not authenticated...
if ( !authenticated )
{
// Redirect to the sign-in page
this._router.navigate(['sign-in'], {queryParams: {redirectURL}});
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Public methods // @ Public methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -57,37 +90,4 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad
{ {
return this._check('/'); return this._check('/');
} }
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @param redirectURL
* @private
*/
private _check(redirectURL: string): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is not authenticated...
if ( !authenticated )
{
// Redirect to the sign-in page
this._router.navigate(['sign-in'], {queryParams: {redirectURL}});
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
} }

View File

@@ -19,6 +19,38 @@ export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
{ {
} }
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @private
*/
private _check(): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is authenticated...
if ( authenticated )
{
// Redirect to the root
this._router.navigate(['']);
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Public methods // @ Public methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -55,36 +87,4 @@ export class NoAuthGuard implements CanActivate, CanActivateChild, CanLoad
{ {
return this._check(); return this._check();
} }
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Check the authenticated status
*
* @private
*/
private _check(): Observable<boolean>
{
// Check the authentication status
return this._authService.check()
.pipe(
switchMap((authenticated) => {
// If the user is authenticated...
if ( authenticated )
{
// Redirect to the root
this._router.navigate(['']);
// Prevent the access
return of(false);
}
// Allow the access
return of(true);
})
);
}
} }

View File

@@ -1,13 +1,21 @@
import { NgModule, Optional, SkipSelf } from '@angular/core'; import { NgModule, Optional, SkipSelf } from '@angular/core';
import { AuthModule } from 'app/core/auth/auth.module'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IconsModule } from 'app/core/icons/icons.module'; import { DomSanitizer } from '@angular/platform-browser';
import { TranslocoCoreModule } from 'app/core/transloco/transloco.module'; import { MatIconRegistry } from '@angular/material/icon';
import { AuthService } from 'app/core/auth/auth.service';
import { AuthInterceptor } from 'app/core/auth/auth.interceptor';
@NgModule({ @NgModule({
imports: [ imports : [
AuthModule, HttpClientModule
IconsModule, ],
TranslocoCoreModule providers: [
AuthService,
{
provide : HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi : true
}
] ]
}) })
export class CoreModule export class CoreModule
@@ -16,6 +24,8 @@ export class CoreModule
* Constructor * Constructor
*/ */
constructor( constructor(
private _domSanitizer: DomSanitizer,
private _matIconRegistry: MatIconRegistry,
@Optional() @SkipSelf() parentModule?: CoreModule @Optional() @SkipSelf() parentModule?: CoreModule
) )
{ {
@@ -24,5 +34,14 @@ export class CoreModule
{ {
throw new Error('CoreModule has already been loaded. Import this module in the AppModule only.'); throw new Error('CoreModule has already been loaded. Import this module in the AppModule only.');
} }
// Register icon sets
this._matIconRegistry.addSvgIconSet(this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-twotone.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-solid.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('iconsmind', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/iconsmind.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('feather', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/feather.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-solid.svg'));
} }
} }

View File

@@ -1,25 +0,0 @@
import { NgModule } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { MatIconRegistry } from '@angular/material/icon';
@NgModule()
export class IconsModule
{
/**
* Constructor
*/
constructor(
private _domSanitizer: DomSanitizer,
private _matIconRegistry: MatIconRegistry
)
{
// Register icon sets
this._matIconRegistry.addSvgIconSet(this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-twotone.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('mat_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/material-solid.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('iconsmind', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/iconsmind.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('feather', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/feather.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_outline', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-outline.svg'));
this._matIconRegistry.addSvgIconSetInNamespace('heroicons_solid', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/heroicons-solid.svg'));
}
}

View File

@@ -1,48 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Navigation } from 'app/core/navigation/navigation.types';
@Injectable({
providedIn: 'root'
})
export class NavigationService
{
private _navigation: ReplaySubject<Navigation> = new ReplaySubject<Navigation>(1);
/**
* Constructor
*/
constructor(private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Accessors
// -----------------------------------------------------------------------------------------------------
/**
* Getter for navigation
*/
get navigation$(): Observable<Navigation>
{
return this._navigation.asObservable();
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get all navigation data
*/
get(): Observable<Navigation>
{
return this._httpClient.get<Navigation>('api/common/navigation').pipe(
tap((navigation) => {
this._navigation.next(navigation);
})
);
}
}

View File

@@ -1,9 +0,0 @@
import { FuseNavigationItem } from '@fuse/components/navigation';
export interface Navigation
{
compact: FuseNavigationItem[];
default: FuseNavigationItem[];
futuristic: FuseNavigationItem[];
horizontal: FuseNavigationItem[];
}

View File

@@ -1,32 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Translation, TranslocoLoader } from '@ngneat/transloco';
@Injectable({
providedIn: 'root'
})
export class TranslocoHttpLoader implements TranslocoLoader
{
/**
* Constructor
*/
constructor(
private _httpClient: HttpClient)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Get translation
*
* @param lang
*/
getTranslation(lang: string): Observable<Translation>
{
return this._httpClient.get<Translation>(`./assets/i18n/${lang}.json`);
}
}

View File

@@ -1,51 +0,0 @@
import { Translation, TRANSLOCO_CONFIG, TRANSLOCO_LOADER, translocoConfig, TranslocoModule, TranslocoService } from '@ngneat/transloco';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { environment } from 'environments/environment';
import { TranslocoHttpLoader } from 'app/core/transloco/transloco.http-loader';
@NgModule({
exports : [
TranslocoModule
],
providers: [
{
// Provide the default Transloco configuration
provide : TRANSLOCO_CONFIG,
useValue: translocoConfig({
availableLangs : [
{
id : 'en',
label: 'English'
},
{
id : 'tr',
label: 'Turkish'
}
],
defaultLang : 'en',
fallbackLang : 'en',
reRenderOnLangChange: true,
prodMode : environment.production
})
},
{
// Provide the default Transloco loader
provide : TRANSLOCO_LOADER,
useClass: TranslocoHttpLoader
},
{
// Preload the default language before the app starts to prevent empty/jumping content
provide : APP_INITIALIZER,
deps : [TranslocoService],
useFactory: (translocoService: TranslocoService): any => (): Promise<Translation> => {
const defaultLang = translocoService.getDefaultLang();
translocoService.setActiveLang(defaultLang);
return translocoService.load(defaultLang).toPromise();
},
multi : true
}
]
})
export class TranslocoCoreModule
{
}

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Observable, ReplaySubject } from 'rxjs'; import { Observable, ReplaySubject } from 'rxjs';
import { map, tap } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { User } from 'app/core/user/user.types'; import { User } from 'app/core/user/user.model';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -42,18 +42,6 @@ export class UserService
// @ Public methods // @ Public methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
/**
* Get the current logged in user data
*/
get(): Observable<User>
{
return this._httpClient.get<User>('api/common/user').pipe(
tap((user) => {
this._user.next(user);
})
);
}
/** /**
* Update the user * Update the user
* *
@@ -63,6 +51,7 @@ export class UserService
{ {
return this._httpClient.patch<User>('api/common/user', {user}).pipe( return this._httpClient.patch<User>('api/common/user', {user}).pipe(
map((response) => { map((response) => {
// Execute the observable
this._user.next(response); this._user.next(response);
}) })
); );

View File

@@ -1,35 +0,0 @@
<!-- Button -->
<button
mat-icon-button
[matMenuTriggerFor]="languages">
<ng-container *ngTemplateOutlet="flagImage; context: {$implicit: activeLang}"></ng-container>
</button>
<!-- Language menu -->
<mat-menu
[xPosition]="'before'"
#languages="matMenu">
<ng-container *ngFor="let lang of availableLangs; trackBy: trackByFn">
<button
mat-menu-item
(click)="setActiveLang(lang.id)">
<span class="flex items-center">
<ng-container *ngTemplateOutlet="flagImage; context: {$implicit: lang.id}"></ng-container>
<span class="ml-3">{{lang.label}}</span>
</span>
</button>
</ng-container>
</mat-menu>
<!-- Flag image template -->
<ng-template
let-lang
#flagImage>
<span class="relative w-6 shadow rounded-sm overflow-hidden">
<span class="absolute inset-0 ring-1 ring-inset ring-black ring-opacity-10"></span>
<img
class="w-full"
[src]="'assets/images/flags/' + flagCodes[lang].toUpperCase() + '.svg'"
[alt]="'Flag image for ' + lang">
</span>
</ng-template>

View File

@@ -1,153 +0,0 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { take } from 'rxjs/operators';
import { AvailableLangs, TranslocoService } from '@ngneat/transloco';
import { FuseNavigationService, FuseVerticalNavigationComponent } from '@fuse/components/navigation';
@Component({
selector : 'languages',
templateUrl : './languages.component.html',
encapsulation : ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'languages'
})
export class LanguagesComponent implements OnInit, OnDestroy
{
availableLangs: AvailableLangs;
activeLang: string;
flagCodes: any;
/**
* Constructor
*/
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _fuseNavigationService: FuseNavigationService,
private _translocoService: TranslocoService
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Get the available languages from transloco
this.availableLangs = this._translocoService.getAvailableLangs();
// Subscribe to language changes
this._translocoService.langChanges$.subscribe((activeLang) => {
// Get the active lang
this.activeLang = activeLang;
// Update the navigation
this._updateNavigation(activeLang);
});
// Set the country iso codes for languages for flags
this.flagCodes = {
'en': 'us',
'tr': 'tr'
};
}
/**
* On destroy
*/
ngOnDestroy(): void
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Set the active lang
*
* @param lang
*/
setActiveLang(lang: string): void
{
// Set the active lang
this._translocoService.setActiveLang(lang);
}
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// -----------------------------------------------------------------------------------------------------
// @ Private methods
// -----------------------------------------------------------------------------------------------------
/**
* Update the navigation
*
* @param lang
* @private
*/
private _updateNavigation(lang: string): void
{
// For the demonstration purposes, we will only update the Dashboard names
// from the navigation but you can do a full swap and change the entire
// navigation data.
//
// You can import the data from a file or request it from your backend,
// it's up to you.
// Get the component -> navigation data -> item
const navComponent = this._fuseNavigationService.getComponent<FuseVerticalNavigationComponent>('mainNavigation');
// Return if the navigation component does not exist
if ( !navComponent )
{
return null;
}
// Get the flat navigation data
const navigation = navComponent.navigation;
// Get the Project dashboard item and update its title
const projectDashboardItem = this._fuseNavigationService.getItem('dashboards.project', navigation);
if ( projectDashboardItem )
{
this._translocoService.selectTranslate('Project').pipe(take(1))
.subscribe((translation) => {
// Set the title
projectDashboardItem.title = translation;
// Refresh the navigation component
navComponent.refresh();
});
}
// Get the Analytics dashboard item and update its title
const analyticsDashboardItem = this._fuseNavigationService.getItem('dashboards.analytics', navigation);
if ( analyticsDashboardItem )
{
this._translocoService.selectTranslate('Analytics').pipe(take(1))
.subscribe((translation) => {
// Set the title
analyticsDashboardItem.title = translation;
// Refresh the navigation component
navComponent.refresh();
});
}
}
}

View File

@@ -1,24 +0,0 @@
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { LanguagesComponent } from 'app/layout/common/languages/languages.component';
import { SharedModule } from 'app/shared/shared.module';
@NgModule({
declarations: [
LanguagesComponent
],
imports : [
MatButtonModule,
MatIconModule,
MatMenuModule,
SharedModule
],
exports : [
LanguagesComponent
]
})
export class LanguagesModule
{
}

View File

@@ -46,7 +46,7 @@
<!-- Content --> <!-- Content -->
<div class="relative flex flex-col flex-auto sm:max-h-120 divide-y overflow-y-auto bg-card"> <div class="relative flex flex-col flex-auto sm:max-h-120 divide-y overflow-y-auto bg-card">
<!-- Messages --> <!-- Messages -->
<ng-container *ngFor="let message of messages; trackBy: trackByFn"> <ng-container *ngFor="let message of messages">
<div <div
class="flex group hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5" class="flex group hover:bg-gray-50 dark:hover:bg-black dark:hover:bg-opacity-5"
[ngClass]="{'unread': !message.read}"> [ngClass]="{'unread': !message.read}">

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal'; import { TemplatePortal } from '@angular/cdk/portal';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
@@ -14,12 +14,12 @@ import { MessagesService } from 'app/layout/common/messages/messages.service';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
exportAs : 'messages' exportAs : 'messages'
}) })
export class MessagesComponent implements OnInit, OnDestroy export class MessagesComponent implements OnInit, OnChanges, OnDestroy
{ {
@Input() messages: Message[];
@ViewChild('messagesOrigin') private _messagesOrigin: MatButton; @ViewChild('messagesOrigin') private _messagesOrigin: MatButton;
@ViewChild('messagesPanel') private _messagesPanel: TemplateRef<any>; @ViewChild('messagesPanel') private _messagesPanel: TemplateRef<any>;
messages: Message[];
unreadCount: number = 0; unreadCount: number = 0;
private _overlayRef: OverlayRef; private _overlayRef: OverlayRef;
private _unsubscribeAll: Subject<any> = new Subject<any>(); private _unsubscribeAll: Subject<any> = new Subject<any>();
@@ -40,6 +40,21 @@ export class MessagesComponent implements OnInit, OnDestroy
// @ Lifecycle hooks // @ Lifecycle hooks
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void
{
// Messages
if ( 'messages' in changes )
{
// Store the messages on the service
this._messagesService.store(changes.messages.currentValue);
}
}
/** /**
* On init * On init
*/ */
@@ -140,17 +155,6 @@ export class MessagesComponent implements OnInit, OnDestroy
this._messagesService.delete(message.id).subscribe(); this._messagesService.delete(message.id).subscribe();
} }
/**
* Track by function for ngFor loops
*
* @param index
* @param item
*/
trackByFn(index: number, item: any): any
{
return item.id || index;
}
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
// @ Private methods // @ Private methods
// ----------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------
@@ -167,7 +171,7 @@ export class MessagesComponent implements OnInit, OnDestroy
scrollStrategy : this._overlay.scrollStrategies.block(), scrollStrategy : this._overlay.scrollStrategies.block(),
positionStrategy: this._overlay.position() positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._messagesOrigin._elementRef.nativeElement) .flexibleConnectedTo(this._messagesOrigin._elementRef.nativeElement)
.withLockedPosition(true) .withLockedPosition()
.withPush(true) .withPush(true)
.withPositions([ .withPositions([
{ {

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