Project is initialized
This commit is contained in:
commit
9e8ea6c188
13
.editorconfig
Normal file
13
.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Editor configuration, see https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
46
.gitignore
vendored
Normal file
46
.gitignore
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
# Only exists if Bazel was run
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# profiling files
|
||||||
|
chrome-profiler-events.json
|
||||||
|
speed-measure-plugin.json
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System Files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
13
.vscode/settings.json
vendored
Normal file
13
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnPaste": true,
|
||||||
|
"editor.autoClosingBrackets": "languageDefined",
|
||||||
|
"editor.trimAutoWhitespace": true,
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"files.trimFinalNewlines": true,
|
||||||
|
"git.ignoreLimitWarning": true,
|
||||||
|
"prettier.singleQuote": true,
|
||||||
|
"debug.node.autoAttach": "on"
|
||||||
|
}
|
27
README.md
Normal file
27
README.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Backend
|
||||||
|
|
||||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.2.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
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).
|
132
angular.json
Normal file
132
angular.json
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"backend": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/backend",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"aot": false,
|
||||||
|
"assets": ["src/favicon.ico", "src/assets"],
|
||||||
|
"styles": ["src/styles.scss"],
|
||||||
|
"scripts": []
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"extractCss": true,
|
||||||
|
"namedChunks": false,
|
||||||
|
"aot": true,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true,
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "2mb",
|
||||||
|
"maximumError": "5mb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hmr": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.hmr.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ec": {
|
||||||
|
"sourceMap": true,
|
||||||
|
"extractCss": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "backend:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "backend:build:production"
|
||||||
|
},
|
||||||
|
"hmr": {
|
||||||
|
"hmr": true,
|
||||||
|
"browserTarget": "backend:build:hmr"
|
||||||
|
},
|
||||||
|
"ec": {
|
||||||
|
"browserTarget": "backend:build:ec"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "backend:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"karmaConfig": "karma.conf.js",
|
||||||
|
"assets": ["src/favicon.ico", "src/assets"],
|
||||||
|
"styles": ["src/styles.scss"],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"tsconfig.app.json",
|
||||||
|
"tsconfig.spec.json",
|
||||||
|
"e2e/tsconfig.json"
|
||||||
|
],
|
||||||
|
"exclude": ["**/node_modules/**"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"e2e": {
|
||||||
|
"builder": "@angular-devkit/build-angular:protractor",
|
||||||
|
"options": {
|
||||||
|
"protractorConfig": "e2e/protractor.conf.js",
|
||||||
|
"devServerTarget": "backend:serve"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"devServerTarget": "backend:serve:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultProject": "backend"
|
||||||
|
}
|
12
browserslist
Normal file
12
browserslist
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||||
|
# For additional information regarding the format and rule options, please see:
|
||||||
|
# https://github.com/browserslist/browserslist#queries
|
||||||
|
|
||||||
|
# You can see what browsers were selected by your queries by running:
|
||||||
|
# npx browserslist
|
||||||
|
|
||||||
|
> 0.5%
|
||||||
|
last 2 versions
|
||||||
|
Firefox ESR
|
||||||
|
not dead
|
||||||
|
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
32
e2e/protractor.conf.js
Normal file
32
e2e/protractor.conf.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// @ts-check
|
||||||
|
// Protractor configuration file, see link for more information
|
||||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||||
|
|
||||||
|
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type { import("protractor").Config }
|
||||||
|
*/
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
specs: [
|
||||||
|
'./src/**/*.e2e-spec.ts'
|
||||||
|
],
|
||||||
|
capabilities: {
|
||||||
|
'browserName': 'chrome'
|
||||||
|
},
|
||||||
|
directConnect: true,
|
||||||
|
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: true } }));
|
||||||
|
}
|
||||||
|
};
|
23
e2e/src/app.e2e-spec.ts
Normal file
23
e2e/src/app.e2e-spec.ts
Normal 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', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getTitleText()).toEqual('Welcome to backend!');
|
||||||
|
});
|
||||||
|
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
});
|
11
e2e/src/app.po.ts
Normal file
11
e2e/src/app.po.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
|
export class AppPage {
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get(browser.baseUrl) as Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTitleText() {
|
||||||
|
return element(by.css('app-root h1')).getText() as Promise<string>;
|
||||||
|
}
|
||||||
|
}
|
13
e2e/tsconfig.json
Normal file
13
e2e/tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/e2e",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"jasminewd2",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
32
karma.conf.js
Normal file
32
karma.conf.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '',
|
||||||
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-jasmine-html-reporter'),
|
||||||
|
require('karma-coverage-istanbul-reporter'),
|
||||||
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
|
},
|
||||||
|
coverageIstanbulReporter: {
|
||||||
|
dir: require('path').join(__dirname, './coverage/backend'),
|
||||||
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
|
fixWebpackSourcePaths: true
|
||||||
|
},
|
||||||
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false,
|
||||||
|
restartOnFileChange: true
|
||||||
|
});
|
||||||
|
};
|
12472
package-lock.json
generated
Normal file
12472
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
74
package.json
Normal file
74
package.json
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"name": "backend",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve --open",
|
||||||
|
"start:hmr": "ng serve --configuration hmr --source-map=false --hmr-warning=false",
|
||||||
|
"start:hmr:sourcemaps": "ng serve --configuration hmr --source-map=true --hmr-warning=false",
|
||||||
|
"build": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --dev",
|
||||||
|
"build:stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --dev --stats-json",
|
||||||
|
"build:prod": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod",
|
||||||
|
"build:prod:stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod --stats-json",
|
||||||
|
"test": "ng test",
|
||||||
|
"lint": "ng lint",
|
||||||
|
"e2e": "ng e2e"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "~8.1.2",
|
||||||
|
"@angular/cdk": "^8.1.1",
|
||||||
|
"@angular/common": "~8.1.2",
|
||||||
|
"@angular/compiler": "~8.1.2",
|
||||||
|
"@angular/core": "~8.1.2",
|
||||||
|
"@angular/flex-layout": "^8.0.0-beta.26",
|
||||||
|
"@angular/forms": "~8.1.2",
|
||||||
|
"@angular/material": "^8.1.1",
|
||||||
|
"@angular/material-moment-adapter": "^8.1.1",
|
||||||
|
"@angular/platform-browser": "~8.1.2",
|
||||||
|
"@angular/platform-browser-dynamic": "~8.1.2",
|
||||||
|
"@angular/router": "~8.1.2",
|
||||||
|
"@ngrx/effects": "^8.1.0",
|
||||||
|
"@ngrx/router-store": "^8.1.0",
|
||||||
|
"@ngrx/store": "^8.1.0",
|
||||||
|
"@ngx-translate/core": "^11.0.1",
|
||||||
|
"@swimlane/ngx-datatable": "^15.0.2",
|
||||||
|
"d3": "^5.9.7",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
|
"lodash": "^4.17.15",
|
||||||
|
"moment": "^2.24.0",
|
||||||
|
"ngx-cookie-service": "^2.2.0",
|
||||||
|
"perfect-scrollbar": "^1.4.0",
|
||||||
|
"prismjs": "^1.17.1",
|
||||||
|
"rxjs": "^6.5.2",
|
||||||
|
"tslib": "^1.9.0",
|
||||||
|
"web-animations-js": "^2.3.2",
|
||||||
|
"zone.js": "~0.9.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "~0.801.2",
|
||||||
|
"@angular/cli": "~8.1.2",
|
||||||
|
"@angular/compiler-cli": "~8.1.2",
|
||||||
|
"@angular/language-service": "~8.1.2",
|
||||||
|
"@angularclass/hmr": "^2.1.3",
|
||||||
|
"@ngrx/store-devtools": "^8.1.0",
|
||||||
|
"@types/lodash": "^4.14.136",
|
||||||
|
"@types/node": "~8.9.4",
|
||||||
|
"@types/jasmine": "~3.3.8",
|
||||||
|
"@types/jasminewd2": "~2.0.3",
|
||||||
|
"@types/prismjs": "^1.16.0",
|
||||||
|
"codelyzer": "^5.0.0",
|
||||||
|
"jasmine-core": "~3.4.0",
|
||||||
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
|
"karma": "~4.1.0",
|
||||||
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
|
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||||
|
"karma-jasmine": "~2.0.1",
|
||||||
|
"karma-jasmine-html-reporter": "^1.4.0",
|
||||||
|
"ngrx-store-freeze": "^0.2.4",
|
||||||
|
"protractor": "~5.4.0",
|
||||||
|
"ts-node": "~7.0.0",
|
||||||
|
"tslint": "~5.15.0",
|
||||||
|
"typescript": "~3.4.3"
|
||||||
|
}
|
||||||
|
}
|
552
src/@fuse/animations/index.ts
Normal file
552
src/@fuse/animations/index.ts
Normal file
|
@ -0,0 +1,552 @@
|
||||||
|
import {
|
||||||
|
sequence,
|
||||||
|
trigger,
|
||||||
|
animate,
|
||||||
|
style,
|
||||||
|
group,
|
||||||
|
query,
|
||||||
|
transition,
|
||||||
|
animateChild,
|
||||||
|
state,
|
||||||
|
animation,
|
||||||
|
useAnimation,
|
||||||
|
stagger
|
||||||
|
} from '@angular/animations';
|
||||||
|
|
||||||
|
const customAnimation = animation(
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
opacity: '{{opacity}}',
|
||||||
|
transform: 'scale({{scale}}) translate3d({{x}}, {{y}}, {{z}})'
|
||||||
|
}),
|
||||||
|
animate('{{duration}} {{delay}} cubic-bezier(0.0, 0.0, 0.2, 1)', style('*'))
|
||||||
|
],
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
duration: '200ms',
|
||||||
|
delay: '0ms',
|
||||||
|
opacity: '0',
|
||||||
|
scale: '1',
|
||||||
|
x: '0',
|
||||||
|
y: '0',
|
||||||
|
z: '0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const fuseAnimations = [
|
||||||
|
trigger('animate', [
|
||||||
|
transition('void => *', [useAnimation(customAnimation)])
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('animateStagger', [
|
||||||
|
state('50', style('*')),
|
||||||
|
state('100', style('*')),
|
||||||
|
state('200', style('*')),
|
||||||
|
|
||||||
|
transition(
|
||||||
|
'void => 50',
|
||||||
|
query('@*', [stagger('50ms', [animateChild()])], { optional: true })
|
||||||
|
),
|
||||||
|
transition(
|
||||||
|
'void => 100',
|
||||||
|
query('@*', [stagger('100ms', [animateChild()])], { optional: true })
|
||||||
|
),
|
||||||
|
transition(
|
||||||
|
'void => 200',
|
||||||
|
query('@*', [stagger('200ms', [animateChild()])], { optional: true })
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('fadeInOut', [
|
||||||
|
state(
|
||||||
|
'0, void',
|
||||||
|
style({
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'1, *',
|
||||||
|
style({
|
||||||
|
opacity: 1
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('1 => 0', animate('300ms ease-out')),
|
||||||
|
transition('0 => 1', animate('300ms ease-in')),
|
||||||
|
transition('void <=> *', animate('300ms ease-in'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('slideInOut', [
|
||||||
|
state(
|
||||||
|
'0',
|
||||||
|
style({
|
||||||
|
height: '0px'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'1',
|
||||||
|
style({
|
||||||
|
height: '*'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('1 => 0', animate('300ms ease-out')),
|
||||||
|
transition('0 => 1', animate('300ms ease-in'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('slideIn', [
|
||||||
|
transition('void => left', [
|
||||||
|
style({
|
||||||
|
transform: 'translateX(100%)'
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'300ms ease-in',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
transition('left => void', [
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)'
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'300ms ease-in',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(-100%)'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
transition('void => right', [
|
||||||
|
style({
|
||||||
|
transform: 'translateX(-100%)'
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'300ms ease-in',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
transition('right => void', [
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)'
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'300ms ease-in',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(100%)'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('slideInLeft', [
|
||||||
|
state(
|
||||||
|
'void',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(-100%)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'*',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('void => *', animate('300ms')),
|
||||||
|
transition('* => void', animate('300ms'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('slideInRight', [
|
||||||
|
state(
|
||||||
|
'void',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(100%)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'*',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('void => *', animate('300ms')),
|
||||||
|
transition('* => void', animate('300ms'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('slideInTop', [
|
||||||
|
state(
|
||||||
|
'void',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(-100%)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'*',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(0)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('void => *', animate('300ms')),
|
||||||
|
transition('* => void', animate('300ms'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('slideInBottom', [
|
||||||
|
state(
|
||||||
|
'void',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(100%)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'*',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(0)'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('void => *', animate('300ms')),
|
||||||
|
transition('* => void', animate('300ms'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('expandCollapse', [
|
||||||
|
state(
|
||||||
|
'void',
|
||||||
|
style({
|
||||||
|
height: '0px'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
state(
|
||||||
|
'*',
|
||||||
|
style({
|
||||||
|
height: '*'
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transition('void => *', animate('300ms ease-out')),
|
||||||
|
transition('* => void', animate('300ms ease-in'))
|
||||||
|
]),
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Router animations
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
trigger('routerTransitionLeft', [
|
||||||
|
transition('* => *', [
|
||||||
|
query(
|
||||||
|
'content > :enter, content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateX(100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
sequence([
|
||||||
|
group([
|
||||||
|
query(
|
||||||
|
'content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(-100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({ transform: 'translateX(100%)' }),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0%)',
|
||||||
|
opacity: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
query('content > :leave', animateChild(), { optional: true }),
|
||||||
|
query('content > :enter', animateChild(), { optional: true })
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('routerTransitionRight', [
|
||||||
|
transition('* => *', [
|
||||||
|
query(
|
||||||
|
'content > :enter, content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateX(-100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
sequence([
|
||||||
|
group([
|
||||||
|
query(
|
||||||
|
'content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({ transform: 'translateX(-100%)' }),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateX(0%)',
|
||||||
|
opacity: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
query('content > :leave', animateChild(), { optional: true }),
|
||||||
|
query('content > :enter', animateChild(), { optional: true })
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('routerTransitionUp', [
|
||||||
|
transition('* => *', [
|
||||||
|
query(
|
||||||
|
'content > :enter, content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateY(100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
group([
|
||||||
|
query(
|
||||||
|
'content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateY(0)',
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(-100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({ transform: 'translateY(100%)' }),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(0%)',
|
||||||
|
opacity: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
query('content > :leave', animateChild(), { optional: true }),
|
||||||
|
query('content > :enter', animateChild(), { optional: true })
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('routerTransitionDown', [
|
||||||
|
transition('* => *', [
|
||||||
|
query(
|
||||||
|
'content > :enter, content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateY(-100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
sequence([
|
||||||
|
group([
|
||||||
|
query(
|
||||||
|
'content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
transform: 'translateY(0)',
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(100%)',
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({ transform: 'translateY(-100%)' }),
|
||||||
|
animate(
|
||||||
|
'600ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
transform: 'translateY(0%)',
|
||||||
|
opacity: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
query('content > :leave', animateChild(), { optional: true }),
|
||||||
|
query('content > :enter', animateChild(), { optional: true })
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
trigger('routerTransitionFade', [
|
||||||
|
transition(
|
||||||
|
'* => *',
|
||||||
|
group([
|
||||||
|
query(
|
||||||
|
'content > :enter, content > :leave ',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :leave',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
opacity: 1
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'300ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
opacity: 0
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query(
|
||||||
|
'content > :enter',
|
||||||
|
[
|
||||||
|
style({
|
||||||
|
opacity: 0
|
||||||
|
}),
|
||||||
|
animate(
|
||||||
|
'300ms cubic-bezier(0.0, 0.0, 0.2, 1)',
|
||||||
|
style({
|
||||||
|
opacity: 1
|
||||||
|
})
|
||||||
|
)
|
||||||
|
],
|
||||||
|
{ optional: true }
|
||||||
|
),
|
||||||
|
query('content > :enter', animateChild(), { optional: true }),
|
||||||
|
query('content > :leave', animateChild(), { optional: true })
|
||||||
|
])
|
||||||
|
)
|
||||||
|
])
|
||||||
|
];
|
|
@ -0,0 +1,12 @@
|
||||||
|
<h1 matDialogTitle>Confirm</h1>
|
||||||
|
<div mat-dialog-content>{{ confirmMessage }}</div>
|
||||||
|
<div mat-dialog-actions class="pt-24">
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
class="mat-accent mr-16"
|
||||||
|
(click)="dialogRef.close(true)"
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</button>
|
||||||
|
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
||||||
|
</div>
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-confirm-dialog',
|
||||||
|
templateUrl: './confirm-dialog.component.html',
|
||||||
|
styleUrls: ['./confirm-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseConfirmDialogComponent {
|
||||||
|
public confirmMessage: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {MatDialogRef<FuseConfirmDialogComponent>} dialogRef
|
||||||
|
*/
|
||||||
|
constructor(public dialogRef: MatDialogRef<FuseConfirmDialogComponent>) {}
|
||||||
|
}
|
12
src/@fuse/components/confirm-dialog/confirm-dialog.module.ts
Normal file
12
src/@fuse/components/confirm-dialog/confirm-dialog.module.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
|
||||||
|
import { FuseConfirmDialogComponent } from 'src/@fuse/components/confirm-dialog/confirm-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseConfirmDialogComponent],
|
||||||
|
imports: [MatDialogModule, MatButtonModule],
|
||||||
|
entryComponents: [FuseConfirmDialogComponent]
|
||||||
|
})
|
||||||
|
export class FuseConfirmDialogModule {}
|
37
src/@fuse/components/countdown/countdown.component.html
Normal file
37
src/@fuse/components/countdown/countdown.component.html
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<div class="fuse-countdown">
|
||||||
|
<div class="time days">
|
||||||
|
<div class="value">
|
||||||
|
{{ countdown.days }}
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="time hours">
|
||||||
|
<div class="value">
|
||||||
|
{{ countdown.hours }}
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
hours
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="time minutes">
|
||||||
|
<div class="value">
|
||||||
|
{{ countdown.minutes }}
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
minutes
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="time seconds">
|
||||||
|
<div class="value">
|
||||||
|
{{ countdown.seconds }}
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
seconds
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
30
src/@fuse/components/countdown/countdown.component.scss
Normal file
30
src/@fuse/components/countdown/countdown.component.scss
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
fuse-countdown {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.fuse-countdown {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.time {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 12px;
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
107
src/@fuse/components/countdown/countdown.component.ts
Normal file
107
src/@fuse/components/countdown/countdown.component.ts
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { interval, Subject } from 'rxjs';
|
||||||
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-countdown',
|
||||||
|
templateUrl: './countdown.component.html',
|
||||||
|
styleUrls: ['./countdown.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class FuseCountdownComponent implements OnInit, OnDestroy {
|
||||||
|
// Event date
|
||||||
|
@Input('eventDate')
|
||||||
|
eventDate;
|
||||||
|
|
||||||
|
countdown: any;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
// Set the defaults
|
||||||
|
this.countdown = {
|
||||||
|
days: '',
|
||||||
|
hours: '',
|
||||||
|
minutes: '',
|
||||||
|
seconds: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
const currDate = moment();
|
||||||
|
const eventDate = moment(this.eventDate);
|
||||||
|
|
||||||
|
// Get the difference in between the current date and event date in seconds
|
||||||
|
let diff = eventDate.diff(currDate, 'seconds');
|
||||||
|
|
||||||
|
// Calculate the remaining time for the first time so there will be no
|
||||||
|
// delay on the countdown
|
||||||
|
this.countdown = this._secondsToRemaining(diff);
|
||||||
|
|
||||||
|
// Create a subscribable interval
|
||||||
|
const countDown = interval(1000).pipe(
|
||||||
|
map(value => {
|
||||||
|
return (diff = diff - 1);
|
||||||
|
}),
|
||||||
|
map(value => {
|
||||||
|
return this._secondsToRemaining(value);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Subscribe to the countdown interval
|
||||||
|
countDown.pipe(takeUntil(this._unsubscribeAll)).subscribe(value => {
|
||||||
|
this.countdown = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Private methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts given seconds to a remaining time
|
||||||
|
*
|
||||||
|
* @param seconds
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _secondsToRemaining(seconds): any {
|
||||||
|
const timeLeft = moment.duration(seconds, 'seconds');
|
||||||
|
|
||||||
|
return {
|
||||||
|
days: timeLeft.asDays().toFixed(0),
|
||||||
|
hours: timeLeft.hours(),
|
||||||
|
minutes: timeLeft.minutes(),
|
||||||
|
seconds: timeLeft.seconds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
9
src/@fuse/components/countdown/countdown.module.ts
Normal file
9
src/@fuse/components/countdown/countdown.module.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { FuseCountdownComponent } from 'src/@fuse/components/countdown/countdown.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseCountdownComponent],
|
||||||
|
exports: [FuseCountdownComponent]
|
||||||
|
})
|
||||||
|
export class FuseCountdownModule {}
|
13
src/@fuse/components/countdown/countdown.theme.scss
Normal file
13
src/@fuse/components/countdown/countdown.theme.scss
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
@mixin fuse-countdown-theme($theme) {
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
|
fuse-countdown {
|
||||||
|
.fuse-countdown {
|
||||||
|
.time {
|
||||||
|
.title {
|
||||||
|
color: map-get($foreground, secondary-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
<!-- DEMO CONTENT -->
|
||||||
|
<div class="demo-content line-height-1.75">
|
||||||
|
<h1 class="m-0">Early Sunrise in Winter</h1>
|
||||||
|
<h4 class="mt-0 secondary-text">Demo Content</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tortor
|
||||||
|
nibh, convallis sed purus nec, auctor venenatis nisl. Suspendisse potenti.
|
||||||
|
Nullam sagittis nulla in diam finibus, sed pharetra velit vestibulum.
|
||||||
|
Suspendisse euismod in urna eu posuere.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<blockquote class="my-24">
|
||||||
|
<p>
|
||||||
|
Nunc vel lacinia lorem. Nullam tincidunt sed purus eu placerat. Donec id
|
||||||
|
dictum erat. Etiam enim ex, dapibus et tortor id, posuere pretium est.
|
||||||
|
Maecenas fringilla ipsum vitae neque elementum, at eleifend ante
|
||||||
|
sollicitudin. Donec viverra augue dolor, a venenatis tellus consectetur
|
||||||
|
sit amet.
|
||||||
|
</p>
|
||||||
|
<footer>
|
||||||
|
John Doe
|
||||||
|
</footer>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Ut ornare sit amet velit vel congue. Ut nec tristique eros. Lorem ipsum
|
||||||
|
dolor sit amet, consectetur
|
||||||
|
<b>adipiscing elit</b>. Vivamus sed lorem quis nibh porta iaculis.
|
||||||
|
Vestibulum ut eleifend ante, at semper mi. Nam imperdiet est nisi, quis
|
||||||
|
hendrerit tellus convallis et. Morbi in luctus neque. Curabitur elementum ut
|
||||||
|
est et gravida. In hac habitasse platea dictumst. In et placerat eros, eu
|
||||||
|
tempor turpis. Curabitur ac felis finibus, elementum lectus vitae, venenatis
|
||||||
|
est. Integer mollis nisl a eros scelerisque varius. Etiam venenatis lectus
|
||||||
|
vel erat condimentum tristique vel vel mi. Nulla id euismod mi, et mollis
|
||||||
|
tellus.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Orci varius natoque penatibus et magnis dis parturient montes, nascetur
|
||||||
|
ridiculus mus. Class aptent taciti sociosqu ad litora torquent per conubia
|
||||||
|
nostra, per inceptos himenaeos. Curabitur vitae sagittis odio. Suspendisse
|
||||||
|
ullamcorper nunc non pellentesque laoreet. Curabitur eu tortor id quam
|
||||||
|
pretium mattis. Proin ut quam velit.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<img
|
||||||
|
class="mt-24 w-100-p"
|
||||||
|
src="assets/images/demo-content/morain-lake.jpg"
|
||||||
|
style="max-width: 640px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="mt-8 mb-24 secondary-text">
|
||||||
|
<em
|
||||||
|
>Nullam tincidunt sed purus eu placerat. Donec id dictum erat. Etiam enim
|
||||||
|
ex, dapibus et tortor id.</em
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Quisque sit amet risus enim. Aliquam sit amet interdum justo, at ultricies
|
||||||
|
sapien. Suspendisse et semper urna, in gravida eros. Quisque id nibh
|
||||||
|
iaculis, euismod urna sed, egestas nisi. Donec eros metus, congue a
|
||||||
|
imperdiet feugiat, sagittis nec ipsum. Quisque dapibus mollis felis non
|
||||||
|
tristique.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Ut auctor, metus sed dapibus tempus, urna diam auctor odio, in malesuada
|
||||||
|
odio risus vitae nisi. Etiam blandit ante urna, vitae placerat massa mollis
|
||||||
|
in. Duis nec urna ac purus semper dictum ut eget justo. Aenean non sagittis
|
||||||
|
augue. Sed venenatis rhoncus enim eget ornare.
|
||||||
|
<a href="#">Donec viverra sed felis at venenatis.</a>
|
||||||
|
Mauris aliquam fringilla nulla, sit amet congue felis dignissim at.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Orci varius</li>
|
||||||
|
<li>Magnis dis</li>
|
||||||
|
<li>Conubia nostra</li>
|
||||||
|
<li>Semper urna</li>
|
||||||
|
<li>Donec viverra</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Quisque accumsan augue tempor ante mollis, sed placerat diam porttitor.
|
||||||
|
Vestibulum dignissim sem vel velit eleifend, non pellentesque quam
|
||||||
|
convallis. Pellentesque est dolor, dignissim ac tortor tristique, hendrerit
|
||||||
|
iaculis metus. Praesent pulvinar quam eu leo consectetur faucibus.
|
||||||
|
Vestibulum purus diam, gravida sagittis feugiat sit amet, tincidunt in
|
||||||
|
ligula. Sed semper vestibulum magna. Lorem ipsum dolor sit amet, consectetur
|
||||||
|
adipiscing elit. Suspendisse tortor nibh, convallis sed purus nec, auctor
|
||||||
|
venenatis nisl. Suspendisse potenti.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Nullam sagittis nulla in diam finibus, sed pharetra velit vestibulum.
|
||||||
|
Suspendisse euismod in urna eu posuere. Etiam blandit nunc arcu, et
|
||||||
|
consectetur orci blandit a. Aliquam condimentum pharetra quam at ultricies.
|
||||||
|
Nunc vel lacinia lorem. Nullam tincidunt sed purus eu placerat. Donec id
|
||||||
|
dictum erat. Etiam enim ex, dapibus et tortor id, posuere pretium est.
|
||||||
|
Maecenas fringilla ipsum vitae neque elementum, at eleifend ante
|
||||||
|
sollicitudin. Donec viverra augue dolor, a venenatis tellus consectetur sit
|
||||||
|
amet.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<!-- / DEMO CONTENT -->
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-demo-content',
|
||||||
|
templateUrl: './demo-content.component.html',
|
||||||
|
styleUrls: ['./demo-content.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseDemoContentComponent {
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<div class="demo-sidebar">
|
||||||
|
<mat-list>
|
||||||
|
<h3 matSubheader>Sidebar Demo</h3>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 1</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 2</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 3</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 4</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 5</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 6</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 7</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 8</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 9</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 10</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 11</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 12</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 13</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 14</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 15</span>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-list-item>
|
||||||
|
<span>Sidebar Item 16</span>
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-demo-sidebar',
|
||||||
|
templateUrl: './demo-sidebar.component.html',
|
||||||
|
styleUrls: ['./demo-sidebar.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseDemoSidebarComponent {
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
}
|
15
src/@fuse/components/demo/demo.module.ts
Normal file
15
src/@fuse/components/demo/demo.module.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
import { MatListModule } from '@angular/material/list';
|
||||||
|
|
||||||
|
import { FuseDemoContentComponent } from './demo-content/demo-content.component';
|
||||||
|
import { FuseDemoSidebarComponent } from './demo-sidebar/demo-sidebar.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseDemoContentComponent, FuseDemoSidebarComponent],
|
||||||
|
imports: [RouterModule, MatDividerModule, MatListModule],
|
||||||
|
exports: [FuseDemoContentComponent, FuseDemoSidebarComponent]
|
||||||
|
})
|
||||||
|
export class FuseDemoModule {}
|
9
src/@fuse/components/highlight/highlight.component.scss
Normal file
9
src/@fuse/components/highlight/highlight.component.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
background: #263238;
|
||||||
|
cursor: text;
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
147
src/@fuse/components/highlight/highlight.component.ts
Normal file
147
src/@fuse/components/highlight/highlight.component.ts
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
ElementRef,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import * as Prism from 'prismjs/prism';
|
||||||
|
import 'src/@fuse/components/highlight/prism-languages';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-highlight',
|
||||||
|
template: '',
|
||||||
|
styleUrls: ['./highlight.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseHighlightComponent implements OnInit, OnDestroy {
|
||||||
|
// Source
|
||||||
|
@ContentChild('source', { static: true })
|
||||||
|
source: ElementRef;
|
||||||
|
|
||||||
|
// Lang
|
||||||
|
@Input('lang')
|
||||||
|
lang: string;
|
||||||
|
|
||||||
|
// Path
|
||||||
|
@Input('path')
|
||||||
|
path: string;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ElementRef} _elementRef
|
||||||
|
* @param {HttpClient} _httpClient
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _elementRef: ElementRef,
|
||||||
|
private _httpClient: HttpClient
|
||||||
|
) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// If there is no language defined, return...
|
||||||
|
if (!this.lang) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the path is defined...
|
||||||
|
if (this.path) {
|
||||||
|
// Get the source
|
||||||
|
this._httpClient
|
||||||
|
.get(this.path, { responseType: 'text' })
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(response => {
|
||||||
|
// Highlight it
|
||||||
|
this.highlight(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the path is not defined and the source element exists...
|
||||||
|
if (!this.path && this.source) {
|
||||||
|
// Highlight it
|
||||||
|
this.highlight(this.source.nativeElement.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlight the given source code
|
||||||
|
*
|
||||||
|
* @param sourceCode
|
||||||
|
*/
|
||||||
|
highlight(sourceCode): void {
|
||||||
|
// Split the source into lines
|
||||||
|
const sourceLines = sourceCode.split('\n');
|
||||||
|
|
||||||
|
// Remove the first and the last line of the source
|
||||||
|
// code if they are blank lines. This way, the html
|
||||||
|
// can be formatted properly while using fuse-highlight
|
||||||
|
// component
|
||||||
|
if (!sourceLines[0].trim()) {
|
||||||
|
sourceLines.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceLines[sourceLines.length - 1].trim()) {
|
||||||
|
sourceLines.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first non-whitespace char index in
|
||||||
|
// the first line of the source code
|
||||||
|
const indexOfFirstChar = sourceLines[0].search(/\S|$/);
|
||||||
|
|
||||||
|
// Generate the trimmed source
|
||||||
|
let source = '';
|
||||||
|
|
||||||
|
// Iterate through all the lines
|
||||||
|
sourceLines.forEach((line, index) => {
|
||||||
|
// Trim the beginning white space depending on the index
|
||||||
|
// and concat the source code
|
||||||
|
source = source + line.substr(indexOfFirstChar, line.length);
|
||||||
|
|
||||||
|
// If it's not the last line...
|
||||||
|
if (index !== sourceLines.length - 1) {
|
||||||
|
// Add a line break at the end
|
||||||
|
source = source + '\n';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate the highlighted code
|
||||||
|
const highlightedCode = Prism.highlight(source, Prism.languages[this.lang]);
|
||||||
|
|
||||||
|
// Replace the innerHTML of the component with the highlighted code
|
||||||
|
this._elementRef.nativeElement.innerHTML =
|
||||||
|
'<pre><code class="highlight language-' +
|
||||||
|
this.lang +
|
||||||
|
'">' +
|
||||||
|
highlightedCode +
|
||||||
|
'</code></pre>';
|
||||||
|
}
|
||||||
|
}
|
9
src/@fuse/components/highlight/highlight.module.ts
Normal file
9
src/@fuse/components/highlight/highlight.module.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { FuseHighlightComponent } from 'src/@fuse/components/highlight/highlight.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseHighlightComponent],
|
||||||
|
exports: [FuseHighlightComponent]
|
||||||
|
})
|
||||||
|
export class FuseHighlightModule {}
|
18
src/@fuse/components/highlight/prism-languages.ts
Normal file
18
src/@fuse/components/highlight/prism-languages.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'prismjs/prism';
|
||||||
|
import 'prismjs/components/prism-bash';
|
||||||
|
import 'prismjs/components/prism-c';
|
||||||
|
import 'prismjs/components/prism-cpp';
|
||||||
|
import 'prismjs/components/prism-csharp';
|
||||||
|
import 'prismjs/components/prism-css';
|
||||||
|
import 'prismjs/components/prism-diff';
|
||||||
|
import 'prismjs/components/prism-markup';
|
||||||
|
import 'prismjs/components/prism-markup-templating';
|
||||||
|
import 'prismjs/components/prism-java';
|
||||||
|
import 'prismjs/components/prism-javascript';
|
||||||
|
import 'prismjs/components/prism-json';
|
||||||
|
import 'prismjs/components/prism-perl';
|
||||||
|
import 'prismjs/components/prism-php';
|
||||||
|
import 'prismjs/components/prism-python';
|
||||||
|
import 'prismjs/components/prism-sass';
|
||||||
|
import 'prismjs/components/prism-scss';
|
||||||
|
import 'prismjs/components/prism-typescript';
|
12
src/@fuse/components/index.ts
Normal file
12
src/@fuse/components/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export * from './confirm-dialog/confirm-dialog.module';
|
||||||
|
export * from './countdown/countdown.module';
|
||||||
|
export * from './demo/demo.module';
|
||||||
|
export * from './highlight/highlight.module';
|
||||||
|
export * from './material-color-picker/material-color-picker.module';
|
||||||
|
export * from './navigation/navigation.module';
|
||||||
|
export * from './progress-bar/progress-bar.module';
|
||||||
|
export * from './search-bar/search-bar.module';
|
||||||
|
export * from './shortcuts/shortcuts.module';
|
||||||
|
export * from './sidebar/sidebar.module';
|
||||||
|
export * from './theme-options/theme-options.module';
|
||||||
|
export * from './widget/widget.module';
|
|
@ -0,0 +1,93 @@
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="mat-elevation-z1"
|
||||||
|
[matMenuTriggerFor]="colorMenu"
|
||||||
|
(menuOpened)="onMenuOpen()"
|
||||||
|
[ngClass]="selectedPalette + '-' + selectedHue"
|
||||||
|
>
|
||||||
|
<mat-icon>palette</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-menu
|
||||||
|
#colorMenu="matMenu"
|
||||||
|
class="fuse-material-color-picker-menu mat-elevation-z8"
|
||||||
|
>
|
||||||
|
<header
|
||||||
|
[ngClass]="selectedColor?.class || 'accent'"
|
||||||
|
class="mat-elevation-z4"
|
||||||
|
fxLayout="row"
|
||||||
|
fxLayoutAlign="space-between center"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="secondary-text"
|
||||||
|
[style.visibility]="view === 'hues' ? 'visible' : 'hidden'"
|
||||||
|
(click)="goToPalettesView($event)"
|
||||||
|
aria-label="Palette"
|
||||||
|
>
|
||||||
|
<mat-icon class="s-20">arrow_back</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span *ngIf="selectedColor?.palette">
|
||||||
|
{{ selectedColor.palette }} {{ selectedColor.hue }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span *ngIf="!selectedColor?.palette">
|
||||||
|
Select a Color
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="remove-color-button secondary-text"
|
||||||
|
(click)="removeColor($event)"
|
||||||
|
aria-label="Remove color"
|
||||||
|
matTooltip="Remove color"
|
||||||
|
>
|
||||||
|
<mat-icon class="s-20">delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div [ngSwitch]="view" class="views">
|
||||||
|
<div class="view" *ngSwitchCase="'palettes'">
|
||||||
|
<div
|
||||||
|
fxLayout="row wrap"
|
||||||
|
fxLayoutAlign="start start"
|
||||||
|
class="colors"
|
||||||
|
fusePerfectScrollbar
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="color"
|
||||||
|
fxLayout="row"
|
||||||
|
fxLayoutAlign="center center"
|
||||||
|
*ngFor="let color of colors | keys"
|
||||||
|
[ngClass]="color.key"
|
||||||
|
[class.selected]="selectedPalette === color.key"
|
||||||
|
(click)="selectPalette($event, color.key)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="view" *ngSwitchCase="'hues'">
|
||||||
|
<div
|
||||||
|
fxLayout="row wrap"
|
||||||
|
fxLayoutAlign="start start"
|
||||||
|
class="colors"
|
||||||
|
fusePerfectScrollbar
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="color"
|
||||||
|
fxLayout="row"
|
||||||
|
fxLayoutAlign="center center"
|
||||||
|
*ngFor="let hue of hues"
|
||||||
|
[fxHide]="
|
||||||
|
(selectedPalette === 'fuse-white' && hue !== '500') ||
|
||||||
|
(selectedPalette === 'fuse-black' && hue !== '500')
|
||||||
|
"
|
||||||
|
[ngClass]="selectedPalette + '-' + hue"
|
||||||
|
[class.selected]="selectedHue === hue"
|
||||||
|
(click)="selectHue($event, hue)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
|
@ -0,0 +1,39 @@
|
||||||
|
.fuse-material-color-picker-menu {
|
||||||
|
width: 245px;
|
||||||
|
|
||||||
|
.mat-menu-content {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.views {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 165px;
|
||||||
|
|
||||||
|
.view {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.colors {
|
||||||
|
padding: 1px 0 0 0;
|
||||||
|
margin-left: -1px;
|
||||||
|
|
||||||
|
.color {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: 0 0 1px 1px;
|
||||||
|
border-radius: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-radius 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
forwardRef,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { fuseAnimations } from 'src/@fuse/animations';
|
||||||
|
import { MatColors } from 'src/@fuse/mat-colors';
|
||||||
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
|
||||||
|
export const FUSE_MATERIAL_COLOR_PICKER_VALUE_ACCESSOR: any = {
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => FuseMaterialColorPickerComponent),
|
||||||
|
multi: true
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-material-color-picker',
|
||||||
|
templateUrl: './material-color-picker.component.html',
|
||||||
|
styleUrls: ['./material-color-picker.component.scss'],
|
||||||
|
animations: fuseAnimations,
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
providers: [FUSE_MATERIAL_COLOR_PICKER_VALUE_ACCESSOR]
|
||||||
|
})
|
||||||
|
export class FuseMaterialColorPickerComponent implements ControlValueAccessor {
|
||||||
|
colors: any;
|
||||||
|
hues: string[];
|
||||||
|
view: string;
|
||||||
|
selectedColor: any;
|
||||||
|
selectedPalette: string;
|
||||||
|
selectedHue: string;
|
||||||
|
|
||||||
|
// Color changed
|
||||||
|
@Output()
|
||||||
|
colorChanged: EventEmitter<any>;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _color: string;
|
||||||
|
private _modelChange: (value: any) => void;
|
||||||
|
private _modelTouched: (value: any) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
// Set the defaults
|
||||||
|
this.colorChanged = new EventEmitter();
|
||||||
|
this.colors = MatColors.all;
|
||||||
|
this.hues = [
|
||||||
|
'50',
|
||||||
|
'100',
|
||||||
|
'200',
|
||||||
|
'300',
|
||||||
|
'400',
|
||||||
|
'500',
|
||||||
|
'600',
|
||||||
|
'700',
|
||||||
|
'800',
|
||||||
|
'900',
|
||||||
|
'A100',
|
||||||
|
'A200',
|
||||||
|
'A400',
|
||||||
|
'A700'
|
||||||
|
];
|
||||||
|
this.selectedHue = '500';
|
||||||
|
this.view = 'palettes';
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._color = '';
|
||||||
|
this._modelChange = () => {};
|
||||||
|
this._modelTouched = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Accessors
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected class
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
set color(value) {
|
||||||
|
if (!value || value === '' || this._color === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the color value (red-400, blue-500, fuse-navy-700 etc.)
|
||||||
|
const colorParts = value.split('-');
|
||||||
|
|
||||||
|
// Take the very last part as the selected hue value
|
||||||
|
this.selectedHue = colorParts[colorParts.length - 1];
|
||||||
|
|
||||||
|
// Remove the last part
|
||||||
|
colorParts.pop();
|
||||||
|
|
||||||
|
// Rejoin the remaining parts as the selected palette name
|
||||||
|
this.selectedPalette = colorParts.join('-');
|
||||||
|
|
||||||
|
// Store the color value
|
||||||
|
this._color = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get color(): string {
|
||||||
|
return this._color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Control Value Accessor implementation
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register on change function
|
||||||
|
*
|
||||||
|
* @param fn
|
||||||
|
*/
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this._modelChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register on touched function
|
||||||
|
*
|
||||||
|
* @param fn
|
||||||
|
*/
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
this._modelTouched = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write value to the view from model
|
||||||
|
*
|
||||||
|
* @param color
|
||||||
|
*/
|
||||||
|
writeValue(color: any): void {
|
||||||
|
// Return if null
|
||||||
|
if (!color) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the color
|
||||||
|
this.color = color;
|
||||||
|
|
||||||
|
// Update the selected color
|
||||||
|
this.updateSelectedColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select palette
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param palette
|
||||||
|
*/
|
||||||
|
selectPalette(event, palette): void {
|
||||||
|
// Stop propagation
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// Go to 'hues' view
|
||||||
|
this.view = 'hues';
|
||||||
|
|
||||||
|
// Update the selected palette
|
||||||
|
this.selectedPalette = palette;
|
||||||
|
|
||||||
|
// Update the selected color
|
||||||
|
this.updateSelectedColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select hue
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param hue
|
||||||
|
*/
|
||||||
|
selectHue(event, hue): void {
|
||||||
|
// Stop propagation
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// Update the selected huse
|
||||||
|
this.selectedHue = hue;
|
||||||
|
|
||||||
|
// Update the selected color
|
||||||
|
this.updateSelectedColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove color
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
removeColor(event): void {
|
||||||
|
// Stop propagation
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// Return to the 'palettes' view
|
||||||
|
this.view = 'palettes';
|
||||||
|
|
||||||
|
// Clear the selected palette and hue
|
||||||
|
this.selectedPalette = '';
|
||||||
|
this.selectedHue = '';
|
||||||
|
|
||||||
|
// Update the selected color
|
||||||
|
this.updateSelectedColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update selected color
|
||||||
|
*/
|
||||||
|
updateSelectedColor(): void {
|
||||||
|
if (
|
||||||
|
this.selectedColor &&
|
||||||
|
this.selectedColor.palette === this.selectedPalette &&
|
||||||
|
this.selectedColor.hue === this.selectedHue
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the selected color object
|
||||||
|
this.selectedColor = {
|
||||||
|
palette: this.selectedPalette,
|
||||||
|
hue: this.selectedHue,
|
||||||
|
class: this.selectedPalette + '-' + this.selectedHue,
|
||||||
|
bg:
|
||||||
|
this.selectedPalette === ''
|
||||||
|
? ''
|
||||||
|
: MatColors.getColor(this.selectedPalette)[this.selectedHue],
|
||||||
|
fg:
|
||||||
|
this.selectedPalette === ''
|
||||||
|
? ''
|
||||||
|
: MatColors.getColor(this.selectedPalette).contrast[this.selectedHue]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Emit the color changed event
|
||||||
|
this.colorChanged.emit(this.selectedColor);
|
||||||
|
|
||||||
|
// Mark the model as touched
|
||||||
|
this._modelTouched(this.selectedColor.class);
|
||||||
|
|
||||||
|
// Update the model
|
||||||
|
this._modelChange(this.selectedColor.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to palettes view
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
goToPalettesView(event): void {
|
||||||
|
// Stop propagation
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.view = 'palettes';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On menu open
|
||||||
|
*/
|
||||||
|
onMenuOpen(): void {
|
||||||
|
if (this.selectedPalette === '') {
|
||||||
|
this.view = 'palettes';
|
||||||
|
} else {
|
||||||
|
this.view = 'hues';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
import { FusePipesModule } from 'src/@fuse/pipes/pipes.module';
|
||||||
|
|
||||||
|
import { FuseMaterialColorPickerComponent } from 'src/@fuse/components/material-color-picker/material-color-picker.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseMaterialColorPickerComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
|
||||||
|
FlexLayoutModule,
|
||||||
|
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
|
||||||
|
FusePipesModule
|
||||||
|
],
|
||||||
|
exports: [FuseMaterialColorPickerComponent]
|
||||||
|
})
|
||||||
|
export class FuseMaterialColorPickerModule {}
|
|
@ -0,0 +1,11 @@
|
||||||
|
@mixin fuse-material-color-picker-theme($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
.fuse-material-color-picker-menu {
|
||||||
|
.mat-menu-content {
|
||||||
|
.views {
|
||||||
|
background: #303030;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
<ng-container *ngIf="!item.hidden">
|
||||||
|
<!-- normal collapse -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="!item.url && !item.function"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.url -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && !item.function"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && !item.function"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.function -->
|
||||||
|
<span
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="!item.url && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- item.url && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ng-template #itemContent>
|
||||||
|
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{ item.icon }}</mat-icon>
|
||||||
|
<span class="nav-link-title" [translate]="item.translate">{{
|
||||||
|
item.title
|
||||||
|
}}</span>
|
||||||
|
<span
|
||||||
|
class="nav-link-badge"
|
||||||
|
*ngIf="item.badge"
|
||||||
|
[translate]="item.badge.translate"
|
||||||
|
[ngStyle]="{ 'background-color': item.badge.bg, color: item.badge.fg }"
|
||||||
|
>
|
||||||
|
{{ item.badge.title }}
|
||||||
|
</span>
|
||||||
|
<mat-icon class="collapsable-arrow">keyboard_arrow_right</mat-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="children" [ngClass]="{ open: isOpen }">
|
||||||
|
<div class="{{ fuseConfig.layout.navbar.primaryBackground }}">
|
||||||
|
<ng-container *ngFor="let item of item.children">
|
||||||
|
<fuse-nav-horizontal-item
|
||||||
|
*ngIf="item.type == 'item'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-horizontal-item>
|
||||||
|
<fuse-nav-horizontal-collapsable
|
||||||
|
*ngIf="item.type == 'collapsable'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-horizontal-collapsable>
|
||||||
|
<fuse-nav-horizontal-collapsable
|
||||||
|
*ngIf="item.type == 'group'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-horizontal-collapsable>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,2 @@
|
||||||
|
:host {
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
HostBinding,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { fuseAnimations } from 'src/@fuse/animations';
|
||||||
|
import { FuseConfigService } from 'src/@fuse/services/config.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-nav-horizontal-collapsable',
|
||||||
|
templateUrl: './collapsable.component.html',
|
||||||
|
styleUrls: ['./collapsable.component.scss'],
|
||||||
|
animations: fuseAnimations
|
||||||
|
})
|
||||||
|
export class FuseNavHorizontalCollapsableComponent
|
||||||
|
implements OnInit, OnDestroy {
|
||||||
|
fuseConfig: any;
|
||||||
|
isOpen = false;
|
||||||
|
|
||||||
|
@HostBinding('class')
|
||||||
|
classes = 'nav-collapsable nav-item';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
item: any;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
constructor(private _fuseConfigService: FuseConfigService) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to config changes
|
||||||
|
this._fuseConfigService.config
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(config => {
|
||||||
|
this.fuseConfig = config;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open
|
||||||
|
*/
|
||||||
|
@HostListener('mouseenter')
|
||||||
|
open(): void {
|
||||||
|
this.isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close
|
||||||
|
*/
|
||||||
|
@HostListener('mouseleave')
|
||||||
|
close(): void {
|
||||||
|
this.isOpen = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
<ng-container *ngIf="!item.hidden">
|
||||||
|
<!-- item.url -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && !item.function"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && !item.function"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.function -->
|
||||||
|
<span
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="!item.url && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- item.url && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ng-template #itemContent>
|
||||||
|
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{ item.icon }}</mat-icon>
|
||||||
|
<span class="nav-link-title" [translate]="item.translate">{{
|
||||||
|
item.title
|
||||||
|
}}</span>
|
||||||
|
<span
|
||||||
|
class="nav-link-badge"
|
||||||
|
*ngIf="item.badge"
|
||||||
|
[translate]="item.badge.translate"
|
||||||
|
[ngStyle]="{ 'background-color': item.badge.bg, color: item.badge.fg }"
|
||||||
|
>
|
||||||
|
{{ item.badge.title }}
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,2 @@
|
||||||
|
:host {
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Component, HostBinding, Input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-nav-horizontal-item',
|
||||||
|
templateUrl: './item.component.html',
|
||||||
|
styleUrls: ['./item.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseNavHorizontalItemComponent {
|
||||||
|
@HostBinding('class')
|
||||||
|
classes = 'nav-item';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
item: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
}
|
45
src/@fuse/components/navigation/navigation.component.html
Normal file
45
src/@fuse/components/navigation/navigation.component.html
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<div
|
||||||
|
class="nav"
|
||||||
|
[ngClass]="{
|
||||||
|
horizontal: layout === 'horizontal',
|
||||||
|
vertical: layout === 'vertical'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<!-- Vertical Navigation Layout -->
|
||||||
|
<ng-container *ngIf="layout === 'vertical'">
|
||||||
|
<ng-container *ngFor="let item of navigation">
|
||||||
|
<fuse-nav-vertical-group
|
||||||
|
*ngIf="item.type == 'group'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-group>
|
||||||
|
<fuse-nav-vertical-collapsable
|
||||||
|
*ngIf="item.type == 'collapsable'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-collapsable>
|
||||||
|
<fuse-nav-vertical-item
|
||||||
|
*ngIf="item.type == 'item'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-item>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<!-- / Vertical Navigation Layout -->
|
||||||
|
|
||||||
|
<!-- Horizontal Navigation Layout -->
|
||||||
|
<ng-container *ngIf="layout === 'horizontal'">
|
||||||
|
<ng-container *ngFor="let item of navigation">
|
||||||
|
<fuse-nav-horizontal-collapsable
|
||||||
|
*ngIf="item.type == 'group'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-horizontal-collapsable>
|
||||||
|
<fuse-nav-horizontal-collapsable
|
||||||
|
*ngIf="item.type == 'collapsable'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-horizontal-collapsable>
|
||||||
|
<fuse-nav-horizontal-item
|
||||||
|
*ngIf="item.type == 'item'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-horizontal-item>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<!-- / Horizontal Navigation Layout -->
|
||||||
|
</div>
|
12
src/@fuse/components/navigation/navigation.component.scss
Normal file
12
src/@fuse/components/navigation/navigation.component.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
@import 'src/@fuse/scss/fuse';
|
||||||
|
|
||||||
|
fuse-navigation {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
|
||||||
|
> .nav {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
79
src/@fuse/components/navigation/navigation.component.ts
Normal file
79
src/@fuse/components/navigation/navigation.component.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { merge, Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseNavigationService } from 'src/@fuse/components/navigation/navigation.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-navigation',
|
||||||
|
templateUrl: './navigation.component.html',
|
||||||
|
styleUrls: ['./navigation.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FuseNavigationComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
layout = 'vertical';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
navigation: any;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} _changeDetectorRef
|
||||||
|
* @param {FuseNavigationService} _fuseNavigationService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private _fuseNavigationService: FuseNavigationService
|
||||||
|
) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Load the navigation either from the input or from the service
|
||||||
|
this.navigation =
|
||||||
|
this.navigation || this._fuseNavigationService.getCurrentNavigation();
|
||||||
|
|
||||||
|
// Subscribe to the current navigation changes
|
||||||
|
this._fuseNavigationService.onNavigationChanged
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
// Load the navigation
|
||||||
|
this.navigation = this._fuseNavigationService.getCurrentNavigation();
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to navigation item
|
||||||
|
merge(
|
||||||
|
this._fuseNavigationService.onNavigationItemAdded,
|
||||||
|
this._fuseNavigationService.onNavigationItemUpdated,
|
||||||
|
this._fuseNavigationService.onNavigationItemRemoved
|
||||||
|
)
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
36
src/@fuse/components/navigation/navigation.module.ts
Normal file
36
src/@fuse/components/navigation/navigation.module.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { MatRippleModule } from '@angular/material/core';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { FuseNavigationComponent } from './navigation.component';
|
||||||
|
import { FuseNavVerticalItemComponent } from './vertical/item/item.component';
|
||||||
|
import { FuseNavVerticalCollapsableComponent } from './vertical/collapsable/collapsable.component';
|
||||||
|
import { FuseNavVerticalGroupComponent } from './vertical/group/group.component';
|
||||||
|
import { FuseNavHorizontalItemComponent } from './horizontal/item/item.component';
|
||||||
|
import { FuseNavHorizontalCollapsableComponent } from './horizontal/collapsable/collapsable.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule,
|
||||||
|
|
||||||
|
MatIconModule,
|
||||||
|
MatRippleModule,
|
||||||
|
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
exports: [FuseNavigationComponent],
|
||||||
|
declarations: [
|
||||||
|
FuseNavigationComponent,
|
||||||
|
FuseNavVerticalGroupComponent,
|
||||||
|
FuseNavVerticalItemComponent,
|
||||||
|
FuseNavVerticalCollapsableComponent,
|
||||||
|
FuseNavHorizontalItemComponent,
|
||||||
|
FuseNavHorizontalCollapsableComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FuseNavigationModule {}
|
392
src/@fuse/components/navigation/navigation.service.ts
Normal file
392
src/@fuse/components/navigation/navigation.service.ts
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { FuseNavigationItem } from 'src/@fuse/types';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FuseNavigationService {
|
||||||
|
onItemCollapsed: Subject<any>;
|
||||||
|
onItemCollapseToggled: Subject<any>;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _onNavigationChanged: BehaviorSubject<any>;
|
||||||
|
private _onNavigationRegistered: BehaviorSubject<any>;
|
||||||
|
private _onNavigationUnregistered: BehaviorSubject<any>;
|
||||||
|
private _onNavigationItemAdded: BehaviorSubject<any>;
|
||||||
|
private _onNavigationItemUpdated: BehaviorSubject<any>;
|
||||||
|
private _onNavigationItemRemoved: BehaviorSubject<any>;
|
||||||
|
|
||||||
|
private _currentNavigationKey: string;
|
||||||
|
private _registry: { [key: string]: any } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
// Set the defaults
|
||||||
|
this.onItemCollapsed = new Subject();
|
||||||
|
this.onItemCollapseToggled = new Subject();
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._currentNavigationKey = null;
|
||||||
|
this._onNavigationChanged = new BehaviorSubject(null);
|
||||||
|
this._onNavigationRegistered = new BehaviorSubject(null);
|
||||||
|
this._onNavigationUnregistered = new BehaviorSubject(null);
|
||||||
|
this._onNavigationItemAdded = new BehaviorSubject(null);
|
||||||
|
this._onNavigationItemUpdated = new BehaviorSubject(null);
|
||||||
|
this._onNavigationItemRemoved = new BehaviorSubject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Accessors
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onNavigationChanged
|
||||||
|
*
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
get onNavigationChanged(): Observable<any> {
|
||||||
|
return this._onNavigationChanged.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onNavigationRegistered
|
||||||
|
*
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
get onNavigationRegistered(): Observable<any> {
|
||||||
|
return this._onNavigationRegistered.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onNavigationUnregistered
|
||||||
|
*
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
get onNavigationUnregistered(): Observable<any> {
|
||||||
|
return this._onNavigationUnregistered.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onNavigationItemAdded
|
||||||
|
*
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
get onNavigationItemAdded(): Observable<any> {
|
||||||
|
return this._onNavigationItemAdded.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onNavigationItemUpdated
|
||||||
|
*
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
get onNavigationItemUpdated(): Observable<any> {
|
||||||
|
return this._onNavigationItemUpdated.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get onNavigationItemRemoved
|
||||||
|
*
|
||||||
|
* @returns {Observable<any>}
|
||||||
|
*/
|
||||||
|
get onNavigationItemRemoved(): Observable<any> {
|
||||||
|
return this._onNavigationItemRemoved.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given navigation
|
||||||
|
* with the given key
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param navigation
|
||||||
|
*/
|
||||||
|
register(key, navigation): void {
|
||||||
|
// Check if the key already being used
|
||||||
|
if (this._registry[key]) {
|
||||||
|
console.error(
|
||||||
|
`The navigation with the key '${key}' already exists. Either unregister it first or use a unique key.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the registry
|
||||||
|
this._registry[key] = navigation;
|
||||||
|
|
||||||
|
// Notify the subject
|
||||||
|
this._onNavigationRegistered.next([key, navigation]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister the navigation from the registry
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
unregister(key): void {
|
||||||
|
// Check if the navigation exists
|
||||||
|
if (!this._registry[key]) {
|
||||||
|
console.warn(
|
||||||
|
`The navigation with the key '${key}' doesn't exist in the registry.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister the sidebar
|
||||||
|
delete this._registry[key];
|
||||||
|
|
||||||
|
// Notify the subject
|
||||||
|
this._onNavigationUnregistered.next(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get navigation from registry by key
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
getNavigation(key): any {
|
||||||
|
// Check if the navigation exists
|
||||||
|
if (!this._registry[key]) {
|
||||||
|
console.warn(
|
||||||
|
`The navigation with the key '${key}' doesn't exist in the registry.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the sidebar
|
||||||
|
return this._registry[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get flattened navigation array
|
||||||
|
*
|
||||||
|
* @param navigation
|
||||||
|
* @param flatNavigation
|
||||||
|
* @returns {any[]}
|
||||||
|
*/
|
||||||
|
getFlatNavigation(
|
||||||
|
navigation,
|
||||||
|
flatNavigation: FuseNavigationItem[] = []
|
||||||
|
): any {
|
||||||
|
for (const item of navigation) {
|
||||||
|
if (item.type === 'item') {
|
||||||
|
flatNavigation.push(item);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === 'collapsable' || item.type === 'group') {
|
||||||
|
if (item.children) {
|
||||||
|
this.getFlatNavigation(item.children, flatNavigation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flatNavigation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current navigation
|
||||||
|
*
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
getCurrentNavigation(): any {
|
||||||
|
if (!this._currentNavigationKey) {
|
||||||
|
console.warn(`The current navigation is not set.`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getNavigation(this._currentNavigationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the navigation with the key
|
||||||
|
* as the current navigation
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
setCurrentNavigation(key): void {
|
||||||
|
// Check if the sidebar exists
|
||||||
|
if (!this._registry[key]) {
|
||||||
|
console.warn(
|
||||||
|
`The navigation with the key '${key}' doesn't exist in the registry.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the current navigation key
|
||||||
|
this._currentNavigationKey = key;
|
||||||
|
|
||||||
|
// Notify the subject
|
||||||
|
this._onNavigationChanged.next(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get navigation item by id from the
|
||||||
|
* current navigation
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param {any} navigation
|
||||||
|
* @returns {any | boolean}
|
||||||
|
*/
|
||||||
|
getNavigationItem(id, navigation = null): any | boolean {
|
||||||
|
if (!navigation) {
|
||||||
|
navigation = this.getCurrentNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of navigation) {
|
||||||
|
if (item.id === id) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.children) {
|
||||||
|
const childItem = this.getNavigationItem(id, item.children);
|
||||||
|
|
||||||
|
if (childItem) {
|
||||||
|
return childItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parent of the navigation item
|
||||||
|
* with the id
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param {any} navigation
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
getNavigationItemParent(id, navigation = null, parent = null): any {
|
||||||
|
if (!navigation) {
|
||||||
|
navigation = this.getCurrentNavigation();
|
||||||
|
parent = navigation;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of navigation) {
|
||||||
|
if (item.id === id) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.children) {
|
||||||
|
const childItem = this.getNavigationItemParent(id, item.children, item);
|
||||||
|
|
||||||
|
if (childItem) {
|
||||||
|
return childItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a navigation item to the specified location
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
addNavigationItem(item, id): void {
|
||||||
|
// Get the current navigation
|
||||||
|
const navigation: any[] = this.getCurrentNavigation();
|
||||||
|
|
||||||
|
// Add to the end of the navigation
|
||||||
|
if (id === 'end') {
|
||||||
|
navigation.push(item);
|
||||||
|
|
||||||
|
// Trigger the observable
|
||||||
|
this._onNavigationItemAdded.next(true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the start of the navigation
|
||||||
|
if (id === 'start') {
|
||||||
|
navigation.unshift(item);
|
||||||
|
|
||||||
|
// Trigger the observable
|
||||||
|
this._onNavigationItemAdded.next(true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to a specific location
|
||||||
|
const parent: any = this.getNavigationItem(id);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
// Check if parent has a children entry,
|
||||||
|
// and add it if it doesn't
|
||||||
|
if (!parent.children) {
|
||||||
|
parent.children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the item
|
||||||
|
parent.children.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the observable
|
||||||
|
this._onNavigationItemAdded.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update navigation item with the given id
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param properties
|
||||||
|
*/
|
||||||
|
updateNavigationItem(id, properties): void {
|
||||||
|
// Get the navigation item
|
||||||
|
const navigationItem = this.getNavigationItem(id);
|
||||||
|
|
||||||
|
// If there is no navigation with the give id, return
|
||||||
|
if (!navigationItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the navigation properties
|
||||||
|
_.merge(navigationItem, properties);
|
||||||
|
|
||||||
|
// Trigger the observable
|
||||||
|
this._onNavigationItemUpdated.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove navigation item with the given id
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
removeNavigationItem(id): void {
|
||||||
|
const item = this.getNavigationItem(id);
|
||||||
|
|
||||||
|
// Return, if there is not such an item
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the parent of the item
|
||||||
|
let parent = this.getNavigationItemParent(id);
|
||||||
|
|
||||||
|
// This check is required because of the first level
|
||||||
|
// of the navigation, since the first level is not
|
||||||
|
// inside the 'children' array
|
||||||
|
parent = parent.children || parent;
|
||||||
|
|
||||||
|
// Remove the item
|
||||||
|
parent.splice(parent.indexOf(item), 1);
|
||||||
|
|
||||||
|
// Trigger the observable
|
||||||
|
this._onNavigationItemRemoved.next(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
<ng-container *ngIf="!item.hidden">
|
||||||
|
<!-- normal collapsable -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="!item.url && !item.function"
|
||||||
|
(click)="toggleOpen($event)"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.url -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && !item.function"
|
||||||
|
(click)="toggleOpen($event)"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && !item.function"
|
||||||
|
(click)="toggleOpen($event)"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.function -->
|
||||||
|
<span
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="!item.url && item.function"
|
||||||
|
(click)="toggleOpen($event); item.function()"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- item.url && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && item.function"
|
||||||
|
(click)="toggleOpen($event); item.function()"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && item.function"
|
||||||
|
(click)="toggleOpen($event); item.function()"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ng-template #itemContent>
|
||||||
|
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{ item.icon }}</mat-icon>
|
||||||
|
<span class="nav-link-title" [translate]="item.translate">{{
|
||||||
|
item.title
|
||||||
|
}}</span>
|
||||||
|
<span
|
||||||
|
class="nav-link-badge"
|
||||||
|
*ngIf="item.badge"
|
||||||
|
[translate]="item.badge.translate"
|
||||||
|
[ngStyle]="{ 'background-color': item.badge.bg, color: item.badge.fg }"
|
||||||
|
>
|
||||||
|
{{ item.badge.title }}
|
||||||
|
</span>
|
||||||
|
<mat-icon class="collapsable-arrow">keyboard_arrow_right</mat-icon>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="children" [@slideInOut]="isOpen">
|
||||||
|
<ng-container *ngFor="let item of item.children">
|
||||||
|
<fuse-nav-vertical-item
|
||||||
|
*ngIf="item.type == 'item'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-item>
|
||||||
|
<fuse-nav-vertical-collapsable
|
||||||
|
*ngIf="item.type == 'collapsable'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-collapsable>
|
||||||
|
<fuse-nav-vertical-group
|
||||||
|
*ngIf="item.type == 'group'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-group>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,35 @@
|
||||||
|
:host {
|
||||||
|
.folded:not(.unfolded) & {
|
||||||
|
.nav-link {
|
||||||
|
> span {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
.children {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
.collapsable-arrow {
|
||||||
|
transition: transform 0.3s ease-in-out, opacity 0.25s ease-in-out 0.1s;
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .children {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
> .nav-link {
|
||||||
|
.collapsable-arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,240 @@
|
||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
|
import { merge, Subject } from 'rxjs';
|
||||||
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseNavigationItem } from 'src/@fuse/types';
|
||||||
|
import { fuseAnimations } from 'src/@fuse/animations';
|
||||||
|
import { FuseNavigationService } from 'src/@fuse/components/navigation/navigation.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-nav-vertical-collapsable',
|
||||||
|
templateUrl: './collapsable.component.html',
|
||||||
|
styleUrls: ['./collapsable.component.scss'],
|
||||||
|
animations: fuseAnimations
|
||||||
|
})
|
||||||
|
export class FuseNavVerticalCollapsableComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
item: FuseNavigationItem;
|
||||||
|
|
||||||
|
@HostBinding('class')
|
||||||
|
classes = 'nav-collapsable nav-item';
|
||||||
|
|
||||||
|
@HostBinding('class.open')
|
||||||
|
public isOpen = false;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} _changeDetectorRef
|
||||||
|
* @param {FuseNavigationService} _fuseNavigationService
|
||||||
|
* @param {Router} _router
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private _fuseNavigationService: FuseNavigationService,
|
||||||
|
private _router: Router
|
||||||
|
) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Listen for router events
|
||||||
|
this._router.events
|
||||||
|
.pipe(
|
||||||
|
filter(event => event instanceof NavigationEnd),
|
||||||
|
takeUntil(this._unsubscribeAll)
|
||||||
|
)
|
||||||
|
.subscribe((event: NavigationEnd) => {
|
||||||
|
// Check if the url can be found in
|
||||||
|
// one of the children of this item
|
||||||
|
if (this.isUrlInChildren(this.item, event.urlAfterRedirects)) {
|
||||||
|
this.expand();
|
||||||
|
} else {
|
||||||
|
this.collapse();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for collapsing of any navigation item
|
||||||
|
this._fuseNavigationService.onItemCollapsed
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(clickedItem => {
|
||||||
|
if (clickedItem && clickedItem.children) {
|
||||||
|
// Check if the clicked item is one
|
||||||
|
// of the children of this item
|
||||||
|
if (this.isChildrenOf(this.item, clickedItem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the url can be found in
|
||||||
|
// one of the children of this item
|
||||||
|
if (this.isUrlInChildren(this.item, this._router.url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the clicked item is not this item, collapse...
|
||||||
|
if (this.item !== clickedItem) {
|
||||||
|
this.collapse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if the url can be found in
|
||||||
|
// one of the children of this item
|
||||||
|
if (this.isUrlInChildren(this.item, this._router.url)) {
|
||||||
|
this.expand();
|
||||||
|
} else {
|
||||||
|
this.collapse();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to navigation item
|
||||||
|
merge(
|
||||||
|
this._fuseNavigationService.onNavigationItemAdded,
|
||||||
|
this._fuseNavigationService.onNavigationItemUpdated,
|
||||||
|
this._fuseNavigationService.onNavigationItemRemoved
|
||||||
|
)
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle collapse
|
||||||
|
*
|
||||||
|
* @param ev
|
||||||
|
*/
|
||||||
|
toggleOpen(ev): void {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
this.isOpen = !this.isOpen;
|
||||||
|
|
||||||
|
// Navigation collapse toggled...
|
||||||
|
this._fuseNavigationService.onItemCollapsed.next(this.item);
|
||||||
|
this._fuseNavigationService.onItemCollapseToggled.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the collapsable navigation
|
||||||
|
*/
|
||||||
|
expand(): void {
|
||||||
|
if (this.isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isOpen = true;
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
|
||||||
|
this._fuseNavigationService.onItemCollapseToggled.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse the collapsable navigation
|
||||||
|
*/
|
||||||
|
collapse(): void {
|
||||||
|
if (!this.isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isOpen = false;
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
|
||||||
|
this._fuseNavigationService.onItemCollapseToggled.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given parent has the
|
||||||
|
* given item in one of its children
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param item
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isChildrenOf(parent, item): 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given url can be found
|
||||||
|
* in one of the given parent's children
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param url
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isUrlInChildren(parent, url): boolean {
|
||||||
|
const children = parent.children;
|
||||||
|
|
||||||
|
if (!children) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
if (child.children) {
|
||||||
|
if (this.isUrlInChildren(child, url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child.url === url || url.includes(child.url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<ng-container *ngIf="!item.hidden">
|
||||||
|
<div class="group-title" [ngClass]="item.classes">
|
||||||
|
<span class="hint-text" [translate]="item.translate">{{ item.title }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="group-items">
|
||||||
|
<ng-container *ngFor="let item of item.children">
|
||||||
|
<fuse-nav-vertical-group
|
||||||
|
*ngIf="item.type == 'group'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-group>
|
||||||
|
<fuse-nav-vertical-collapsable
|
||||||
|
*ngIf="item.type == 'collapsable'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-collapsable>
|
||||||
|
<fuse-nav-vertical-item
|
||||||
|
*ngIf="item.type == 'item'"
|
||||||
|
[item]="item"
|
||||||
|
></fuse-nav-vertical-item>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,21 @@
|
||||||
|
:host {
|
||||||
|
.folded:not(.unfolded) & {
|
||||||
|
> .group-title {
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
min-width: 1.6rem;
|
||||||
|
border-top: 2px solid;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { merge, Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseNavigationItem } from 'src/@fuse/types';
|
||||||
|
import { FuseNavigationService } from 'src/@fuse/components/navigation/navigation.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-nav-vertical-group',
|
||||||
|
templateUrl: './group.component.html',
|
||||||
|
styleUrls: ['./group.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseNavVerticalGroupComponent implements OnInit, OnDestroy {
|
||||||
|
@HostBinding('class')
|
||||||
|
classes = 'nav-group nav-item';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
item: FuseNavigationItem;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} _changeDetectorRef
|
||||||
|
* @param {FuseNavigationService} _fuseNavigationService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private _fuseNavigationService: FuseNavigationService
|
||||||
|
) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to navigation item
|
||||||
|
merge(
|
||||||
|
this._fuseNavigationService.onNavigationItemAdded,
|
||||||
|
this._fuseNavigationService.onNavigationItemUpdated,
|
||||||
|
this._fuseNavigationService.onNavigationItemRemoved
|
||||||
|
)
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
<ng-container *ngIf="!item.hidden">
|
||||||
|
<!-- item.url -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && !item.function"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && !item.function"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.function -->
|
||||||
|
<span
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="!item.url && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- item.url && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && !item.externalUrl && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
[routerLink]="[item.url]"
|
||||||
|
[routerLinkActive]="['active', 'accent']"
|
||||||
|
[routerLinkActiveOptions]="{ exact: item.exactMatch || false }"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- item.externalUrl && item.function -->
|
||||||
|
<a
|
||||||
|
class="nav-link"
|
||||||
|
[ngClass]="item.classes"
|
||||||
|
*ngIf="item.url && item.externalUrl && item.function"
|
||||||
|
(click)="item.function()"
|
||||||
|
[href]="item.url"
|
||||||
|
[target]="item.openInNewTab ? '_blank' : '_self'"
|
||||||
|
>
|
||||||
|
<ng-container *ngTemplateOutlet="itemContent"></ng-container>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ng-template #itemContent>
|
||||||
|
<mat-icon class="nav-link-icon" *ngIf="item.icon">{{ item.icon }}</mat-icon>
|
||||||
|
<span class="nav-link-title" [translate]="item.translate">{{
|
||||||
|
item.title
|
||||||
|
}}</span>
|
||||||
|
<span
|
||||||
|
class="nav-link-badge"
|
||||||
|
*ngIf="item.badge"
|
||||||
|
[translate]="item.badge.translate"
|
||||||
|
[ngStyle]="{ 'background-color': item.badge.bg, color: item.badge.fg }"
|
||||||
|
>
|
||||||
|
{{ item.badge.title }}
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,11 @@
|
||||||
|
:host {
|
||||||
|
.folded:not(.unfolded) & {
|
||||||
|
.nav-link {
|
||||||
|
> .nav-link-title,
|
||||||
|
> .nav-link-badge {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { merge, Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseNavigationItem } from 'src/@fuse/types';
|
||||||
|
import { FuseNavigationService } from 'src/@fuse/components/navigation/navigation.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-nav-vertical-item',
|
||||||
|
templateUrl: './item.component.html',
|
||||||
|
styleUrls: ['./item.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseNavVerticalItemComponent implements OnInit, OnDestroy {
|
||||||
|
@HostBinding('class')
|
||||||
|
classes = 'nav-item';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
item: FuseNavigationItem;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} _changeDetectorRef
|
||||||
|
* @param {FuseNavigationService} _fuseNavigationService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private _fuseNavigationService: FuseNavigationService
|
||||||
|
) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to navigation item
|
||||||
|
merge(
|
||||||
|
this._fuseNavigationService.onNavigationItemAdded,
|
||||||
|
this._fuseNavigationService.onNavigationItemUpdated,
|
||||||
|
this._fuseNavigationService.onNavigationItemRemoved
|
||||||
|
)
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<ng-container *ngIf="visible">
|
||||||
|
<mat-progress-bar
|
||||||
|
color="accent"
|
||||||
|
[bufferValue]="bufferValue"
|
||||||
|
[mode]="mode"
|
||||||
|
[value]="value"
|
||||||
|
></mat-progress-bar>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,16 @@
|
||||||
|
@import 'src/@fuse/scss/fuse';
|
||||||
|
|
||||||
|
fuse-progress-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 99998;
|
||||||
|
|
||||||
|
mat-progress-bar {
|
||||||
|
.mat-progress-bar-buffer {
|
||||||
|
background-color: #c5c6cb !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
src/@fuse/components/progress-bar/progress-bar.component.ts
Normal file
85
src/@fuse/components/progress-bar/progress-bar.component.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseProgressBarService } from 'src/@fuse/components/progress-bar/progress-bar.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-progress-bar',
|
||||||
|
templateUrl: './progress-bar.component.html',
|
||||||
|
styleUrls: ['./progress-bar.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class FuseProgressBarComponent implements OnInit, OnDestroy {
|
||||||
|
bufferValue: number;
|
||||||
|
mode: 'determinate' | 'indeterminate' | 'buffer' | 'query';
|
||||||
|
value: number;
|
||||||
|
visible: boolean;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {FuseProgressBarService} _fuseProgressBarService
|
||||||
|
*/
|
||||||
|
constructor(private _fuseProgressBarService: FuseProgressBarService) {
|
||||||
|
// Set the defaults
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to the progress bar service properties
|
||||||
|
|
||||||
|
// Buffer value
|
||||||
|
this._fuseProgressBarService.bufferValue
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(bufferValue => {
|
||||||
|
this.bufferValue = bufferValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
this._fuseProgressBarService.mode
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(mode => {
|
||||||
|
this.mode = mode;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Value
|
||||||
|
this._fuseProgressBarService.value
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(value => {
|
||||||
|
this.value = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Visible
|
||||||
|
this._fuseProgressBarService.visible
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(visible => {
|
||||||
|
this.visible = visible;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
}
|
23
src/@fuse/components/progress-bar/progress-bar.module.ts
Normal file
23
src/@fuse/components/progress-bar/progress-bar.module.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
|
||||||
|
import { FuseProgressBarComponent } from './progress-bar.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseProgressBarComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule,
|
||||||
|
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatProgressBarModule
|
||||||
|
],
|
||||||
|
exports: [FuseProgressBarComponent]
|
||||||
|
})
|
||||||
|
export class FuseProgressBarModule {}
|
130
src/@fuse/components/progress-bar/progress-bar.service.ts
Normal file
130
src/@fuse/components/progress-bar/progress-bar.service.ts
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
NavigationCancel,
|
||||||
|
NavigationEnd,
|
||||||
|
NavigationError,
|
||||||
|
NavigationStart,
|
||||||
|
Router
|
||||||
|
} from '@angular/router';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FuseProgressBarService {
|
||||||
|
// Private
|
||||||
|
private _bufferValue: BehaviorSubject<number>;
|
||||||
|
private _mode: BehaviorSubject<string>;
|
||||||
|
private _value: BehaviorSubject<number>;
|
||||||
|
private _visible: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {Router} _router
|
||||||
|
*/
|
||||||
|
constructor(private _router: Router) {
|
||||||
|
// Initialize the service
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Accessors
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer value
|
||||||
|
*/
|
||||||
|
get bufferValue(): Observable<any> {
|
||||||
|
return this._bufferValue.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
setBufferValue(value: number): void {
|
||||||
|
this._bufferValue.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode
|
||||||
|
*/
|
||||||
|
get mode(): Observable<any> {
|
||||||
|
return this._mode.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
setMode(value: 'determinate' | 'indeterminate' | 'buffer' | 'query'): void {
|
||||||
|
this._mode.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value
|
||||||
|
*/
|
||||||
|
get value(): Observable<any> {
|
||||||
|
return this._value.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value: number): void {
|
||||||
|
this._value.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible
|
||||||
|
*/
|
||||||
|
get visible(): Observable<any> {
|
||||||
|
return this._visible.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Private methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _init(): void {
|
||||||
|
// Initialize the behavior subjects
|
||||||
|
this._bufferValue = new BehaviorSubject(0);
|
||||||
|
this._mode = new BehaviorSubject('indeterminate');
|
||||||
|
this._value = new BehaviorSubject(0);
|
||||||
|
this._visible = new BehaviorSubject(false);
|
||||||
|
|
||||||
|
// Subscribe to the router events to show/hide the loading bar
|
||||||
|
this._router.events
|
||||||
|
.pipe(filter(event => event instanceof NavigationStart))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._router.events
|
||||||
|
.pipe(
|
||||||
|
filter(
|
||||||
|
event =>
|
||||||
|
event instanceof NavigationEnd ||
|
||||||
|
event instanceof NavigationError ||
|
||||||
|
event instanceof NavigationCancel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the progress bar
|
||||||
|
*/
|
||||||
|
show(): void {
|
||||||
|
this._visible.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the progress bar
|
||||||
|
*/
|
||||||
|
hide(): void {
|
||||||
|
this._visible.next(false);
|
||||||
|
}
|
||||||
|
}
|
33
src/@fuse/components/search-bar/search-bar.component.html
Normal file
33
src/@fuse/components/search-bar/search-bar.component.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<div class="fuse-search-bar" [ngClass]="{ expanded: !collapsed }">
|
||||||
|
<div class="fuse-search-bar-content">
|
||||||
|
<label for="fuse-search-bar-input">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="fuse-search-bar-expander"
|
||||||
|
aria-label="Expand Search Bar"
|
||||||
|
(click)="expand()"
|
||||||
|
*ngIf="collapsed"
|
||||||
|
>
|
||||||
|
<mat-icon class="s-24 secondary-text">search</mat-icon>
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id="fuse-search-bar-input"
|
||||||
|
class="ml-24"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
(input)="search($event)"
|
||||||
|
fxFlex
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="fuse-search-bar-collapser"
|
||||||
|
(click)="collapse()"
|
||||||
|
aria-label="Collapse Search Bar"
|
||||||
|
>
|
||||||
|
<mat-icon class="s-24 secondary-text">close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
88
src/@fuse/components/search-bar/search-bar.component.scss
Normal file
88
src/@fuse/components/search-bar/search-bar.component.scss
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
@import 'src/@fuse/scss/fuse';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
.fuse-search-bar {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
min-width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
@include media-breakpoint('xs') {
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fuse-search-bar-content {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.fuse-search-bar-expander,
|
||||||
|
.fuse-search-bar-collapser {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 20px;
|
||||||
|
margin: 0;
|
||||||
|
width: 64px !important;
|
||||||
|
height: 64px !important;
|
||||||
|
line-height: 64px !important;
|
||||||
|
|
||||||
|
@include media-breakpoint('xs') {
|
||||||
|
height: 56px !important;
|
||||||
|
line-height: 56px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fuse-search-bar-loader {
|
||||||
|
width: 64px !important;
|
||||||
|
height: 64px !important;
|
||||||
|
line-height: 64px !important;
|
||||||
|
|
||||||
|
@include media-breakpoint('xs') {
|
||||||
|
height: 56px !important;
|
||||||
|
line-height: 56px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fuse-search-bar-collapser {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fuse-search-bar-input {
|
||||||
|
display: none;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
min-height: 64px;
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
.fuse-search-bar-content {
|
||||||
|
#fuse-search-bar-input {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fuse-search-bar-collapser {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
&.fuse-search-bar-expanded {
|
||||||
|
#toolbar {
|
||||||
|
z-index: 999 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
src/@fuse/components/search-bar/search-bar.component.ts
Normal file
93
src/@fuse/components/search-bar/search-bar.component.ts
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseConfigService } from 'src/@fuse/services/config.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-search-bar',
|
||||||
|
templateUrl: './search-bar.component.html',
|
||||||
|
styleUrls: ['./search-bar.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseSearchBarComponent implements OnInit, OnDestroy {
|
||||||
|
collapsed: boolean;
|
||||||
|
fuseConfig: any;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
input: EventEmitter<any>;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {FuseConfigService} _fuseConfigService
|
||||||
|
*/
|
||||||
|
constructor(private _fuseConfigService: FuseConfigService) {
|
||||||
|
// Set the defaults
|
||||||
|
this.input = new EventEmitter();
|
||||||
|
this.collapsed = true;
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to config changes
|
||||||
|
this._fuseConfigService.config
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(config => {
|
||||||
|
this.fuseConfig = config;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse
|
||||||
|
*/
|
||||||
|
collapse(): void {
|
||||||
|
this.collapsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand
|
||||||
|
*/
|
||||||
|
expand(): void {
|
||||||
|
this.collapsed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
search(event): void {
|
||||||
|
this.input.emit(event.target.value);
|
||||||
|
}
|
||||||
|
}
|
15
src/@fuse/components/search-bar/search-bar.module.ts
Normal file
15
src/@fuse/components/search-bar/search-bar.module.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
|
||||||
|
import { FuseSearchBarComponent } from './search-bar.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseSearchBarComponent],
|
||||||
|
imports: [CommonModule, RouterModule, MatButtonModule, MatIconModule],
|
||||||
|
exports: [FuseSearchBarComponent]
|
||||||
|
})
|
||||||
|
export class FuseSearchBarModule {}
|
9
src/@fuse/components/search-bar/search-bar.theme.scss
Normal file
9
src/@fuse/components/search-bar/search-bar.theme.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
@mixin fuse-search-bar-theme($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
.fuse-search-bar {
|
||||||
|
&.expanded {
|
||||||
|
background-color: map-get($background, background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
src/@fuse/components/shortcuts/shortcuts.component.html
Normal file
113
src/@fuse/components/shortcuts/shortcuts.component.html
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<div id="fuse-shortcuts" #shortcuts>
|
||||||
|
|
||||||
|
<div class="shortcuts-mobile-toggle" *ngIf="!mobileShortcutsPanelActive" fxLayout="row" fxLayoutAlign="start center"
|
||||||
|
fxHide fxShow.lt-md>
|
||||||
|
<button mat-icon-button (click)="showMobileShortcutsPanel()">
|
||||||
|
<mat-icon class="amber-600-fg">star</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="shortcuts" fxLayout="row" fxHide fxShow.gt-sm>
|
||||||
|
|
||||||
|
<div fxLayout="row" fxLayoutAlign="space-between center" fxFlex="0 1 auto">
|
||||||
|
|
||||||
|
<div fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<div class="w-40 h-40 p-4" fxLayout="row" fxLayoutAlign="center center"
|
||||||
|
*ngFor="let shortcutItem of shortcutItems">
|
||||||
|
|
||||||
|
<a mat-icon-button matTooltip="{{shortcutItem.title}}" [routerLink]="shortcutItem.url">
|
||||||
|
<mat-icon class="secondary-text" *ngIf="shortcutItem.icon">{{shortcutItem.icon}}</mat-icon>
|
||||||
|
<span *ngIf="!shortcutItem.icon" class="h2 secondary-text text-bold">
|
||||||
|
{{shortcutItem.title.substr(0, 1).toUpperCase()}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="addMenu" matTooltip="Click to add/remove shortcut"
|
||||||
|
(menuOpened)="onMenuOpen()">
|
||||||
|
<mat-icon class="amber-600-fg">star</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="shortcuts-mobile-close" fxLayout="row" fxLayoutAlign="start center" fxHide fxShow.lt-md>
|
||||||
|
<button mat-icon-button (click)="hideMobileShortcutsPanel()">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-menu #addMenu="matMenu" class="w-240">
|
||||||
|
|
||||||
|
<mat-form-field class="px-16 w-100-p" (click)="$event.stopPropagation()" floatLabel="never">
|
||||||
|
<input #searchInput matInput placeholder="Search for an app or a page" (input)="search($event)">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<mat-nav-list *ngIf="!searching" style="max-height: 312px; overflow: auto" fusePerfectScrollbar>
|
||||||
|
|
||||||
|
<mat-list-item *ngFor="let shortcutItem of shortcutItems"
|
||||||
|
(click)="toggleShortcut($event, shortcutItem)">
|
||||||
|
|
||||||
|
<div class="w-100-p" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<mat-icon mat-list-icon class="mr-8 secondary-text" *ngIf="shortcutItem.icon">
|
||||||
|
{{shortcutItem.icon}}
|
||||||
|
</mat-icon>
|
||||||
|
|
||||||
|
<span class="h2 w-32 h-32 p-4 mr-8 secondary-text text-bold" fxLayout="row"
|
||||||
|
fxLayoutAlign="center center" *ngIf="!shortcutItem.icon">
|
||||||
|
{{shortcutItem.title.substr(0, 1).toUpperCase()}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<p matLine fxFlex>{{shortcutItem.title}}</p>
|
||||||
|
|
||||||
|
<mat-icon class="ml-8 amber-fg">star</mat-icon>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
<mat-list-item *ngIf="shortcutItems.length === 0">
|
||||||
|
<p>
|
||||||
|
<small>No shortcuts yet!</small>
|
||||||
|
</p>
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
</mat-nav-list>
|
||||||
|
|
||||||
|
<mat-nav-list *ngIf="searching" style="max-height: 312px; overflow: auto" fusePerfectScrollbar>
|
||||||
|
|
||||||
|
<mat-list-item *ngFor="let navigationItem of filteredNavigationItems"
|
||||||
|
(click)="toggleShortcut($event, navigationItem)">
|
||||||
|
|
||||||
|
<div class="w-100-p" fxLayout="row" fxLayoutAlign="start center">
|
||||||
|
|
||||||
|
<mat-icon mat-list-icon class="mr-8 secondary-text" *ngIf="navigationItem.icon">
|
||||||
|
{{navigationItem.icon}}
|
||||||
|
</mat-icon>
|
||||||
|
|
||||||
|
<span class="h2 w-32 h-32 p-4 mr-8 secondary-text text-bold" fxLayout="row"
|
||||||
|
fxLayoutAlign="center center" *ngIf="!navigationItem.icon">
|
||||||
|
{{navigationItem.title.substr(0, 1).toUpperCase()}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<p matLine fxFlex>{{navigationItem.title}}</p>
|
||||||
|
|
||||||
|
<mat-icon class="ml-8 amber-fg" *ngIf="isInShortcuts(navigationItem)">star</mat-icon>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</mat-list-item>
|
||||||
|
|
||||||
|
</mat-nav-list>
|
||||||
|
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
30
src/@fuse/components/shortcuts/shortcuts.component.scss
Normal file
30
src/@fuse/components/shortcuts/shortcuts.component.scss
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
@import 'src/@fuse/scss/fuse';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
|
||||||
|
@include media-breakpoint('lt-md') {
|
||||||
|
|
||||||
|
#fuse-shortcuts {
|
||||||
|
|
||||||
|
&.show-mobile-panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 99;
|
||||||
|
padding: 0 8px;
|
||||||
|
|
||||||
|
.shortcuts {
|
||||||
|
display: flex !important;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: 1 1 auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
239
src/@fuse/components/shortcuts/shortcuts.component.ts
Normal file
239
src/@fuse/components/shortcuts/shortcuts.component.ts
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Renderer2,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
|
import { MediaObserver } from '@angular/flex-layout';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseMatchMediaService } from 'src/@fuse/services/match-media.service';
|
||||||
|
import { FuseNavigationService } from 'src/@fuse/components/navigation/navigation.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-shortcuts',
|
||||||
|
templateUrl: './shortcuts.component.html',
|
||||||
|
styleUrls: ['./shortcuts.component.scss']
|
||||||
|
})
|
||||||
|
export class FuseShortcutsComponent
|
||||||
|
implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
shortcutItems: any[];
|
||||||
|
navigationItems: any[];
|
||||||
|
filteredNavigationItems: any[];
|
||||||
|
searching: boolean;
|
||||||
|
mobileShortcutsPanelActive: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
navigation: any;
|
||||||
|
|
||||||
|
@ViewChild('searchInput', { static: false })
|
||||||
|
searchInputField;
|
||||||
|
|
||||||
|
@ViewChild('shortcuts', { static: false })
|
||||||
|
shortcutsEl: ElementRef;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {CookieService} _cookieService
|
||||||
|
* @param {FuseMatchMediaService} _fuseMatchMediaService
|
||||||
|
* @param {FuseNavigationService} _fuseNavigationService
|
||||||
|
* @param {MediaObserver} _mediaObserver
|
||||||
|
* @param {Renderer2} _renderer
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _cookieService: CookieService,
|
||||||
|
private _fuseMatchMediaService: FuseMatchMediaService,
|
||||||
|
private _fuseNavigationService: FuseNavigationService,
|
||||||
|
private _mediaObserver: MediaObserver,
|
||||||
|
private _renderer: Renderer2
|
||||||
|
) {
|
||||||
|
// Set the defaults
|
||||||
|
this.shortcutItems = [];
|
||||||
|
this.searching = false;
|
||||||
|
this.mobileShortcutsPanelActive = false;
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Get the navigation items and flatten them
|
||||||
|
this.filteredNavigationItems = this.navigationItems = this._fuseNavigationService.getFlatNavigation(
|
||||||
|
this.navigation
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this._cookieService.check('FUSE2.shortcuts')) {
|
||||||
|
this.shortcutItems = JSON.parse(
|
||||||
|
this._cookieService.get('FUSE2.shortcuts')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// User's shortcut items
|
||||||
|
this.shortcutItems = [
|
||||||
|
{
|
||||||
|
title: 'Calendar',
|
||||||
|
type: 'item',
|
||||||
|
icon: 'today',
|
||||||
|
url: '/apps/calendar'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Mail',
|
||||||
|
type: 'item',
|
||||||
|
icon: 'email',
|
||||||
|
url: '/apps/mail'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Contacts',
|
||||||
|
type: 'item',
|
||||||
|
icon: 'account_box',
|
||||||
|
url: '/apps/contacts'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'To-Do',
|
||||||
|
type: 'item',
|
||||||
|
icon: 'check_box',
|
||||||
|
url: '/apps/todo'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
// Subscribe to media changes
|
||||||
|
this._fuseMatchMediaService.onMediaChange
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
if (this._mediaObserver.isActive('gt-sm')) {
|
||||||
|
this.hideMobileShortcutsPanel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
search(event): void {
|
||||||
|
const value = event.target.value.toLowerCase();
|
||||||
|
|
||||||
|
if (value === '') {
|
||||||
|
this.searching = false;
|
||||||
|
this.filteredNavigationItems = this.navigationItems;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searching = true;
|
||||||
|
|
||||||
|
this.filteredNavigationItems = this.navigationItems.filter(
|
||||||
|
navigationItem => {
|
||||||
|
return navigationItem.title.toLowerCase().includes(value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle shortcut
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param itemToToggle
|
||||||
|
*/
|
||||||
|
toggleShortcut(event, itemToToggle): void {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
for (let i = 0; i < this.shortcutItems.length; i++) {
|
||||||
|
if (this.shortcutItems[i].url === itemToToggle.url) {
|
||||||
|
this.shortcutItems.splice(i, 1);
|
||||||
|
|
||||||
|
// Save to the cookies
|
||||||
|
this._cookieService.set(
|
||||||
|
'FUSE2.shortcuts',
|
||||||
|
JSON.stringify(this.shortcutItems)
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shortcutItems.push(itemToToggle);
|
||||||
|
|
||||||
|
// Save to the cookies
|
||||||
|
this._cookieService.set(
|
||||||
|
'FUSE2.shortcuts',
|
||||||
|
JSON.stringify(this.shortcutItems)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is in shortcuts?
|
||||||
|
*
|
||||||
|
* @param navigationItem
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
isInShortcuts(navigationItem): any {
|
||||||
|
return this.shortcutItems.find(item => {
|
||||||
|
return item.url === navigationItem.url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On menu open
|
||||||
|
*/
|
||||||
|
onMenuOpen(): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.searchInputField.nativeElement.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show mobile shortcuts
|
||||||
|
*/
|
||||||
|
showMobileShortcutsPanel(): void {
|
||||||
|
this.mobileShortcutsPanelActive = true;
|
||||||
|
this._renderer.addClass(
|
||||||
|
this.shortcutsEl.nativeElement,
|
||||||
|
'show-mobile-panel'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide mobile shortcuts
|
||||||
|
*/
|
||||||
|
hideMobileShortcutsPanel(): void {
|
||||||
|
this.mobileShortcutsPanelActive = false;
|
||||||
|
this._renderer.removeClass(
|
||||||
|
this.shortcutsEl.nativeElement,
|
||||||
|
'show-mobile-panel'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
37
src/@fuse/components/shortcuts/shortcuts.module.ts
Normal file
37
src/@fuse/components/shortcuts/shortcuts.module.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatListModule } from '@angular/material/list';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { CookieService } from 'ngx-cookie-service';
|
||||||
|
|
||||||
|
import { FuseShortcutsComponent } from './shortcuts.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseShortcutsComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule,
|
||||||
|
|
||||||
|
FlexLayoutModule,
|
||||||
|
|
||||||
|
MatButtonModule,
|
||||||
|
MatDividerModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatListModule,
|
||||||
|
MatTooltipModule
|
||||||
|
],
|
||||||
|
exports: [FuseShortcutsComponent],
|
||||||
|
providers: [CookieService]
|
||||||
|
})
|
||||||
|
export class FuseShortcutsModule {}
|
9
src/@fuse/components/shortcuts/shortcuts.theme.scss
Normal file
9
src/@fuse/components/shortcuts/shortcuts.theme.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
@mixin fuse-shortcuts-theme($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
#fuse-shortcuts {
|
||||||
|
&.show-mobile-panel {
|
||||||
|
background-color: map-get($background, background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/@fuse/components/sidebar/sidebar.component.html
Normal file
1
src/@fuse/components/sidebar/sidebar.component.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<ng-content></ng-content>
|
65
src/@fuse/components/sidebar/sidebar.component.scss
Normal file
65
src/@fuse/components/sidebar/sidebar.component.scss
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
@import 'src/@fuse/scss/fuse';
|
||||||
|
|
||||||
|
fuse-sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
width: 280px;
|
||||||
|
min-width: 280px;
|
||||||
|
max-width: 280px;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.35);
|
||||||
|
|
||||||
|
@include media-breakpoint('xs') {
|
||||||
|
min-width: 0 !important;
|
||||||
|
max-width: 80vw !important;
|
||||||
|
width: 80vw !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left-positioned {
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right-positioned {
|
||||||
|
right: 0;
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.locked-open {
|
||||||
|
position: relative !important;
|
||||||
|
transform: translateX(0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.folded {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.animations-enabled {
|
||||||
|
transition-property: transform, width, min-width, max-width;
|
||||||
|
transition-duration: 150ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fuse-sidebar-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 999;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
803
src/@fuse/components/sidebar/sidebar.component.ts
Normal file
803
src/@fuse/components/sidebar/sidebar.component.ts
Normal file
|
@ -0,0 +1,803 @@
|
||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostBinding,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
Renderer2,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
animate,
|
||||||
|
AnimationBuilder,
|
||||||
|
AnimationPlayer,
|
||||||
|
style
|
||||||
|
} from '@angular/animations';
|
||||||
|
import { MediaObserver } from '@angular/flex-layout';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseSidebarService } from './sidebar.service';
|
||||||
|
import { FuseMatchMediaService } from 'src/@fuse/services/match-media.service';
|
||||||
|
import { FuseConfigService } from 'src/@fuse/services/config.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-sidebar',
|
||||||
|
templateUrl: './sidebar.component.html',
|
||||||
|
styleUrls: ['./sidebar.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class FuseSidebarComponent implements OnInit, OnDestroy {
|
||||||
|
// Name
|
||||||
|
@Input()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// Key
|
||||||
|
@Input()
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
// Position
|
||||||
|
@Input()
|
||||||
|
position: 'left' | 'right';
|
||||||
|
|
||||||
|
// Open
|
||||||
|
@HostBinding('class.open')
|
||||||
|
opened: boolean;
|
||||||
|
|
||||||
|
// Locked Open
|
||||||
|
@Input()
|
||||||
|
lockedOpen: string;
|
||||||
|
|
||||||
|
// isLockedOpen
|
||||||
|
@HostBinding('class.locked-open')
|
||||||
|
isLockedOpen: boolean;
|
||||||
|
|
||||||
|
// Folded width
|
||||||
|
@Input()
|
||||||
|
foldedWidth: number;
|
||||||
|
|
||||||
|
// Folded auto trigger on hover
|
||||||
|
@Input()
|
||||||
|
foldedAutoTriggerOnHover: boolean;
|
||||||
|
|
||||||
|
// Folded unfolded
|
||||||
|
@HostBinding('class.unfolded')
|
||||||
|
unfolded: boolean;
|
||||||
|
|
||||||
|
// Invisible overlay
|
||||||
|
@Input()
|
||||||
|
invisibleOverlay: boolean;
|
||||||
|
|
||||||
|
// Folded changed
|
||||||
|
@Output()
|
||||||
|
foldedChanged: EventEmitter<boolean>;
|
||||||
|
|
||||||
|
// Opened changed
|
||||||
|
@Output()
|
||||||
|
openedChanged: EventEmitter<boolean>;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _folded: boolean;
|
||||||
|
private _fuseConfig: any;
|
||||||
|
private _wasActive: boolean;
|
||||||
|
private _wasFolded: boolean;
|
||||||
|
private _backdrop: HTMLElement | null = null;
|
||||||
|
private _player: AnimationPlayer;
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
@HostBinding('class.animations-enabled')
|
||||||
|
private _animationsEnabled: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {AnimationBuilder} _animationBuilder
|
||||||
|
* @param {ChangeDetectorRef} _changeDetectorRef
|
||||||
|
* @param {ElementRef} _elementRef
|
||||||
|
* @param {FuseConfigService} _fuseConfigService
|
||||||
|
* @param {FuseMatchMediaService} _fuseMatchMediaService
|
||||||
|
* @param {FuseSidebarService} _fuseSidebarService
|
||||||
|
* @param {MediaObserver} _mediaObserver
|
||||||
|
* @param {Renderer2} _renderer
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _animationBuilder: AnimationBuilder,
|
||||||
|
private _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private _elementRef: ElementRef,
|
||||||
|
private _fuseConfigService: FuseConfigService,
|
||||||
|
private _fuseMatchMediaService: FuseMatchMediaService,
|
||||||
|
private _fuseSidebarService: FuseSidebarService,
|
||||||
|
private _mediaObserver: MediaObserver,
|
||||||
|
private _renderer: Renderer2
|
||||||
|
) {
|
||||||
|
// Set the defaults
|
||||||
|
this.foldedAutoTriggerOnHover = true;
|
||||||
|
this.foldedWidth = 64;
|
||||||
|
this.foldedChanged = new EventEmitter();
|
||||||
|
this.openedChanged = new EventEmitter();
|
||||||
|
this.opened = false;
|
||||||
|
this.position = 'left';
|
||||||
|
this.invisibleOverlay = false;
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._animationsEnabled = false;
|
||||||
|
this._folded = false;
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Accessors
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Folded
|
||||||
|
*
|
||||||
|
* @param {boolean} value
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
set folded(value: boolean) {
|
||||||
|
// Set the folded
|
||||||
|
this._folded = value;
|
||||||
|
|
||||||
|
// Return if the sidebar is closed
|
||||||
|
if (!this.opened) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Programmatically add/remove padding to the element
|
||||||
|
// that comes after or before based on the position
|
||||||
|
let sibling, styleRule;
|
||||||
|
|
||||||
|
const styleValue = this.foldedWidth + 'px';
|
||||||
|
|
||||||
|
// Get the sibling and set the style rule
|
||||||
|
if (this.position === 'left') {
|
||||||
|
sibling = this._elementRef.nativeElement.nextElementSibling;
|
||||||
|
styleRule = 'padding-left';
|
||||||
|
} else {
|
||||||
|
sibling = this._elementRef.nativeElement.previousElementSibling;
|
||||||
|
styleRule = 'padding-right';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no sibling, return...
|
||||||
|
if (!sibling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If folded...
|
||||||
|
if (value) {
|
||||||
|
// Fold the sidebar
|
||||||
|
this.fold();
|
||||||
|
|
||||||
|
// Set the folded width
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'min-width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'max-width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set the style and class
|
||||||
|
this._renderer.setStyle(sibling, styleRule, styleValue);
|
||||||
|
this._renderer.addClass(this._elementRef.nativeElement, 'folded');
|
||||||
|
}
|
||||||
|
// If unfolded...
|
||||||
|
else {
|
||||||
|
// Unfold the sidebar
|
||||||
|
this.unfold();
|
||||||
|
|
||||||
|
// Remove the folded width
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'width');
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'min-width');
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'max-width');
|
||||||
|
|
||||||
|
// Remove the style and class
|
||||||
|
this._renderer.removeStyle(sibling, styleRule);
|
||||||
|
this._renderer.removeClass(this._elementRef.nativeElement, 'folded');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the 'foldedChanged' event
|
||||||
|
this.foldedChanged.emit(this.folded);
|
||||||
|
}
|
||||||
|
|
||||||
|
get folded(): boolean {
|
||||||
|
return this._folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to config changes
|
||||||
|
this._fuseConfigService.config
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(config => {
|
||||||
|
this._fuseConfig = config;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register the sidebar
|
||||||
|
this._fuseSidebarService.register(this.name, this);
|
||||||
|
|
||||||
|
// Setup visibility
|
||||||
|
this._setupVisibility();
|
||||||
|
|
||||||
|
// Setup position
|
||||||
|
this._setupPosition();
|
||||||
|
|
||||||
|
// Setup lockedOpen
|
||||||
|
this._setupLockedOpen();
|
||||||
|
|
||||||
|
// Setup folded
|
||||||
|
this._setupFolded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// If the sidebar is folded, unfold it to revert modifications
|
||||||
|
if (this.folded) {
|
||||||
|
this.unfold();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister the sidebar
|
||||||
|
this._fuseSidebarService.unregister(this.name);
|
||||||
|
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Private methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the visibility of the sidebar
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _setupVisibility(): void {
|
||||||
|
// Remove the existing box-shadow
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'box-shadow',
|
||||||
|
'none'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make the sidebar invisible
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'visibility',
|
||||||
|
'hidden'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the sidebar position
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _setupPosition(): void {
|
||||||
|
// Add the correct class name to the sidebar
|
||||||
|
// element depending on the position attribute
|
||||||
|
if (this.position === 'right') {
|
||||||
|
this._renderer.addClass(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'right-positioned'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._renderer.addClass(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'left-positioned'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the lockedOpen handler
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _setupLockedOpen(): void {
|
||||||
|
// Return if the lockedOpen wasn't set
|
||||||
|
if (!this.lockedOpen) {
|
||||||
|
// Return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the wasActive for the first time
|
||||||
|
this._wasActive = false;
|
||||||
|
|
||||||
|
// Set the wasFolded
|
||||||
|
this._wasFolded = this.folded;
|
||||||
|
|
||||||
|
// Show the sidebar
|
||||||
|
this._showSidebar();
|
||||||
|
|
||||||
|
// Act on every media change
|
||||||
|
this._fuseMatchMediaService.onMediaChange
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
// Get the active status
|
||||||
|
const isActive = this._mediaObserver.isActive(this.lockedOpen);
|
||||||
|
|
||||||
|
// If the both status are the same, don't act
|
||||||
|
if (this._wasActive === isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate the lockedOpen
|
||||||
|
if (isActive) {
|
||||||
|
// Set the lockedOpen status
|
||||||
|
this.isLockedOpen = true;
|
||||||
|
|
||||||
|
// Show the sidebar
|
||||||
|
this._showSidebar();
|
||||||
|
|
||||||
|
// Force the the opened status to true
|
||||||
|
this.opened = true;
|
||||||
|
|
||||||
|
// Emit the 'openedChanged' event
|
||||||
|
this.openedChanged.emit(this.opened);
|
||||||
|
|
||||||
|
// If the sidebar was folded, forcefully fold it again
|
||||||
|
if (this._wasFolded) {
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Fold
|
||||||
|
this.folded = true;
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide the backdrop if any exists
|
||||||
|
this._hideBackdrop();
|
||||||
|
}
|
||||||
|
// De-Activate the lockedOpen
|
||||||
|
else {
|
||||||
|
// Set the lockedOpen status
|
||||||
|
this.isLockedOpen = false;
|
||||||
|
|
||||||
|
// Unfold the sidebar in case if it was folded
|
||||||
|
this.unfold();
|
||||||
|
|
||||||
|
// Force the the opened status to close
|
||||||
|
this.opened = false;
|
||||||
|
|
||||||
|
// Emit the 'openedChanged' event
|
||||||
|
this.openedChanged.emit(this.opened);
|
||||||
|
|
||||||
|
// Hide the sidebar
|
||||||
|
this._hideSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new active status
|
||||||
|
this._wasActive = isActive;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the initial folded status
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _setupFolded(): void {
|
||||||
|
// Return, if sidebar is not folded
|
||||||
|
if (!this.folded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the sidebar is closed
|
||||||
|
if (!this.opened) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Programmatically add/remove padding to the element
|
||||||
|
// that comes after or before based on the position
|
||||||
|
let sibling, styleRule;
|
||||||
|
|
||||||
|
const styleValue = this.foldedWidth + 'px';
|
||||||
|
|
||||||
|
// Get the sibling and set the style rule
|
||||||
|
if (this.position === 'left') {
|
||||||
|
sibling = this._elementRef.nativeElement.nextElementSibling;
|
||||||
|
styleRule = 'padding-left';
|
||||||
|
} else {
|
||||||
|
sibling = this._elementRef.nativeElement.previousElementSibling;
|
||||||
|
styleRule = 'padding-right';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no sibling, return...
|
||||||
|
if (!sibling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold the sidebar
|
||||||
|
this.fold();
|
||||||
|
|
||||||
|
// Set the folded width
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'min-width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'max-width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set the style and class
|
||||||
|
this._renderer.setStyle(sibling, styleRule, styleValue);
|
||||||
|
this._renderer.addClass(this._elementRef.nativeElement, 'folded');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the backdrop
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _showBackdrop(): void {
|
||||||
|
// Create the backdrop element
|
||||||
|
this._backdrop = this._renderer.createElement('div');
|
||||||
|
|
||||||
|
// Add a class to the backdrop element
|
||||||
|
this._backdrop.classList.add('fuse-sidebar-overlay');
|
||||||
|
|
||||||
|
// Add a class depending on the invisibleOverlay option
|
||||||
|
if (this.invisibleOverlay) {
|
||||||
|
this._backdrop.classList.add('fuse-sidebar-overlay-invisible');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the backdrop to the parent of the sidebar
|
||||||
|
this._renderer.appendChild(
|
||||||
|
this._elementRef.nativeElement.parentElement,
|
||||||
|
this._backdrop
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the enter animation and attach it to the player
|
||||||
|
this._player = this._animationBuilder
|
||||||
|
.build([animate('300ms ease', style({ opacity: 1 }))])
|
||||||
|
.create(this._backdrop);
|
||||||
|
|
||||||
|
// Play the animation
|
||||||
|
this._player.play();
|
||||||
|
|
||||||
|
// Add an event listener to the overlay
|
||||||
|
this._backdrop.addEventListener('click', () => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the backdrop
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _hideBackdrop(): void {
|
||||||
|
if (!this._backdrop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the leave animation and attach it to the player
|
||||||
|
this._player = this._animationBuilder
|
||||||
|
.build([animate('300ms ease', style({ opacity: 0 }))])
|
||||||
|
.create(this._backdrop);
|
||||||
|
|
||||||
|
// Play the animation
|
||||||
|
this._player.play();
|
||||||
|
|
||||||
|
// Once the animation is done...
|
||||||
|
this._player.onDone(() => {
|
||||||
|
// If the backdrop still exists...
|
||||||
|
if (this._backdrop) {
|
||||||
|
// Remove the backdrop
|
||||||
|
this._backdrop.parentNode.removeChild(this._backdrop);
|
||||||
|
this._backdrop = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change some properties of the sidebar
|
||||||
|
* and make it visible
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _showSidebar(): void {
|
||||||
|
// Remove the box-shadow style
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'box-shadow');
|
||||||
|
|
||||||
|
// Make the sidebar invisible
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'visibility');
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change some properties of the sidebar
|
||||||
|
* and make it invisible
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _hideSidebar(delay = true): void {
|
||||||
|
const delayAmount = delay ? 300 : 0;
|
||||||
|
|
||||||
|
// Add a delay so close animation can play
|
||||||
|
setTimeout(() => {
|
||||||
|
// Remove the box-shadow
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'box-shadow',
|
||||||
|
'none'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make the sidebar invisible
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'visibility',
|
||||||
|
'hidden'
|
||||||
|
);
|
||||||
|
}, delayAmount);
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the animations
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _enableAnimations(): void {
|
||||||
|
// Return if animations already enabled
|
||||||
|
if (this._animationsEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._animationsEnabled = true;
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the sidebar
|
||||||
|
*/
|
||||||
|
open(): void {
|
||||||
|
if (this.opened || this.isLockedOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Show the sidebar
|
||||||
|
this._showSidebar();
|
||||||
|
|
||||||
|
// Show the backdrop
|
||||||
|
this._showBackdrop();
|
||||||
|
|
||||||
|
// Set the opened status
|
||||||
|
this.opened = true;
|
||||||
|
|
||||||
|
// Emit the 'openedChanged' event
|
||||||
|
this.openedChanged.emit(this.opened);
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the sidebar
|
||||||
|
*/
|
||||||
|
close(): void {
|
||||||
|
if (!this.opened || this.isLockedOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Hide the backdrop
|
||||||
|
this._hideBackdrop();
|
||||||
|
|
||||||
|
// Set the opened status
|
||||||
|
this.opened = false;
|
||||||
|
|
||||||
|
// Emit the 'openedChanged' event
|
||||||
|
this.openedChanged.emit(this.opened);
|
||||||
|
|
||||||
|
// Hide the sidebar
|
||||||
|
this._hideSidebar();
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle open/close the sidebar
|
||||||
|
*/
|
||||||
|
toggleOpen(): void {
|
||||||
|
if (this.opened) {
|
||||||
|
this.close();
|
||||||
|
} else {
|
||||||
|
this.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouseenter
|
||||||
|
*/
|
||||||
|
@HostListener('mouseenter')
|
||||||
|
onMouseEnter(): void {
|
||||||
|
// Only work if the auto trigger is enabled
|
||||||
|
if (!this.foldedAutoTriggerOnHover) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unfoldTemporarily();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouseleave
|
||||||
|
*/
|
||||||
|
@HostListener('mouseleave')
|
||||||
|
onMouseLeave(): void {
|
||||||
|
// Only work if the auto trigger is enabled
|
||||||
|
if (!this.foldedAutoTriggerOnHover) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.foldTemporarily();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fold the sidebar permanently
|
||||||
|
*/
|
||||||
|
fold(): void {
|
||||||
|
// Only work if the sidebar is not folded
|
||||||
|
if (this.folded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Fold
|
||||||
|
this.folded = true;
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unfold the sidebar permanently
|
||||||
|
*/
|
||||||
|
unfold(): void {
|
||||||
|
// Only work if the sidebar is folded
|
||||||
|
if (!this.folded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Unfold
|
||||||
|
this.folded = false;
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the sidebar fold/unfold permanently
|
||||||
|
*/
|
||||||
|
toggleFold(): void {
|
||||||
|
if (this.folded) {
|
||||||
|
this.unfold();
|
||||||
|
} else {
|
||||||
|
this.fold();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fold the temporarily unfolded sidebar back
|
||||||
|
*/
|
||||||
|
foldTemporarily(): void {
|
||||||
|
// Only work if the sidebar is folded
|
||||||
|
if (!this.folded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Fold the sidebar back
|
||||||
|
this.unfolded = false;
|
||||||
|
|
||||||
|
// Set the folded width
|
||||||
|
const styleValue = this.foldedWidth + 'px';
|
||||||
|
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'min-width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
this._renderer.setStyle(
|
||||||
|
this._elementRef.nativeElement,
|
||||||
|
'max-width',
|
||||||
|
styleValue
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unfold the sidebar temporarily
|
||||||
|
*/
|
||||||
|
unfoldTemporarily(): void {
|
||||||
|
// Only work if the sidebar is folded
|
||||||
|
if (!this.folded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the animations
|
||||||
|
this._enableAnimations();
|
||||||
|
|
||||||
|
// Unfold the sidebar temporarily
|
||||||
|
this.unfolded = true;
|
||||||
|
|
||||||
|
// Remove the folded width
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'width');
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'min-width');
|
||||||
|
this._renderer.removeStyle(this._elementRef.nativeElement, 'max-width');
|
||||||
|
|
||||||
|
// Mark for check
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
}
|
9
src/@fuse/components/sidebar/sidebar.module.ts
Normal file
9
src/@fuse/components/sidebar/sidebar.module.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { FuseSidebarComponent } from './sidebar.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseSidebarComponent],
|
||||||
|
exports: [FuseSidebarComponent]
|
||||||
|
})
|
||||||
|
export class FuseSidebarModule {}
|
73
src/@fuse/components/sidebar/sidebar.service.ts
Normal file
73
src/@fuse/components/sidebar/sidebar.service.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { FuseSidebarComponent } from './sidebar.component';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FuseSidebarService {
|
||||||
|
// Private
|
||||||
|
private _registry: { [key: string]: FuseSidebarComponent } = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the sidebar to the registry
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param sidebar
|
||||||
|
*/
|
||||||
|
register(key, sidebar): void {
|
||||||
|
// Check if the key already being used
|
||||||
|
if (this._registry[key]) {
|
||||||
|
console.error(
|
||||||
|
`The sidebar with the key '${key}' already exists. Either unregister it first or use a unique key.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the registry
|
||||||
|
this._registry[key] = sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the sidebar from the registry
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
unregister(key): void {
|
||||||
|
// Check if the sidebar exists
|
||||||
|
if (!this._registry[key]) {
|
||||||
|
console.warn(
|
||||||
|
`The sidebar with the key '${key}' doesn't exist in the registry.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister the sidebar
|
||||||
|
delete this._registry[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the sidebar with the given key
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @returns {FuseSidebarComponent}
|
||||||
|
*/
|
||||||
|
getSidebar(key): FuseSidebarComponent {
|
||||||
|
// Check if the sidebar exists
|
||||||
|
if (!this._registry[key]) {
|
||||||
|
console.warn(
|
||||||
|
`The sidebar with the key '${key}' doesn't exist in the registry.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the sidebar
|
||||||
|
return this._registry[key];
|
||||||
|
}
|
||||||
|
}
|
15
src/@fuse/components/sidebar/sidebar.theme.scss
Normal file
15
src/@fuse/components/sidebar/sidebar.theme.scss
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@mixin fuse-sidebar-theme($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
fuse-sidebar {
|
||||||
|
background: map-get($background, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fuse-sidebar-overlay {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
|
&.fuse-sidebar-overlay-invisible {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
545
src/@fuse/components/theme-options/theme-options.component.html
Normal file
545
src/@fuse/components/theme-options/theme-options.component.html
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
<div class="theme-options-panel" fusePerfectScrollbar>
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
|
||||||
|
<span class="title">Theme Options</span>
|
||||||
|
|
||||||
|
<button mat-icon-button class="close-button" (click)="toggleSidebarOpen('themeOptionsPanel')">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form [formGroup]="form">
|
||||||
|
|
||||||
|
<!-- COLOR THEME -->
|
||||||
|
<div class="group">
|
||||||
|
|
||||||
|
<h2>Color themes</h2>
|
||||||
|
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="colorTheme">
|
||||||
|
<mat-radio-button class="mb-12" value="theme-default">Default Light</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="theme-yellow-light">Yellow Light</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="theme-blue-gray-dark">Blue-Gray Dark</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="theme-pink-dark">Pink Dark</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LAYOUT STYLES -->
|
||||||
|
<div class="group" formGroupName="layout">
|
||||||
|
|
||||||
|
<h2>Layout Styles</h2>
|
||||||
|
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="style">
|
||||||
|
|
||||||
|
<mat-radio-button class="mb-12" value="vertical-layout-1">
|
||||||
|
Vertical Layout #1
|
||||||
|
</mat-radio-button>
|
||||||
|
|
||||||
|
<mat-radio-button class="mb-12" value="vertical-layout-2">
|
||||||
|
Vertical Layout #2
|
||||||
|
</mat-radio-button>
|
||||||
|
|
||||||
|
<mat-radio-button class="mb-12" value="vertical-layout-3">
|
||||||
|
Vertical Layout #3
|
||||||
|
</mat-radio-button>
|
||||||
|
|
||||||
|
<mat-radio-button class="mb-12" value="horizontal-layout-1">
|
||||||
|
Horizontal Layout #1
|
||||||
|
</mat-radio-button>
|
||||||
|
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<!-- DIFFERENT FORMS BASED ON LAYOUT STYLES -->
|
||||||
|
<ng-container [ngSwitch]="fuseConfig.layout.style">
|
||||||
|
|
||||||
|
<!-- VERTICAL LAYOUT #1 -->
|
||||||
|
<ng-container *ngSwitchCase="'vertical-layout-1'">
|
||||||
|
|
||||||
|
<!-- LAYOUT WIDTH -->
|
||||||
|
<div class="group mt-32">
|
||||||
|
|
||||||
|
<h2>Layout Width</h2>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
|
||||||
|
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NAVBAR -->
|
||||||
|
<div class="group" formGroupName="navbar">
|
||||||
|
|
||||||
|
<h2>Navbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<mat-slide-toggle class="mt-24" formControlName="folded">
|
||||||
|
Folded
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-16" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-8">Variant:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Primary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="primaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Secondary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="secondaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TOOLBAR -->
|
||||||
|
<div class="group" formGroupName="toolbar">
|
||||||
|
|
||||||
|
<h2>Toolbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above">Above</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below-static">Below Static</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below-fixed">Below Fixed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<div class="group" formGroupName="footer">
|
||||||
|
|
||||||
|
<h2>Footer</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above">Above</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below-static">Below Static</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below-fixed">Below Fixed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SIDE PANEL -->
|
||||||
|
<div class="group" formGroupName="sidepanel">
|
||||||
|
|
||||||
|
<h2>Side Panel</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- VERTICAL LAYOUT #2 -->
|
||||||
|
<ng-container *ngSwitchCase="'vertical-layout-2'">
|
||||||
|
|
||||||
|
<!-- LAYOUT WIDTH -->
|
||||||
|
<div class="group mt-32">
|
||||||
|
|
||||||
|
<h2>Layout Width</h2>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
|
||||||
|
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NAVBAR -->
|
||||||
|
<div class="group" formGroupName="navbar">
|
||||||
|
|
||||||
|
<h2>Navbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<mat-slide-toggle class="mt-24" formControlName="folded">
|
||||||
|
Folded
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-16" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-8">Variant:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Primary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="primaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Secondary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="secondaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TOOLBAR -->
|
||||||
|
<div class="group" formGroupName="toolbar">
|
||||||
|
|
||||||
|
<h2>Toolbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below">Below</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<div class="group" formGroupName="footer">
|
||||||
|
|
||||||
|
<h2>Footer</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below">Below</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SIDE PANEL -->
|
||||||
|
<div class="group" formGroupName="sidepanel">
|
||||||
|
|
||||||
|
<h2>Side Panel</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- VERTICAL LAYOUT #3 -->
|
||||||
|
<ng-container *ngSwitchCase="'vertical-layout-3'">
|
||||||
|
|
||||||
|
<!-- LAYOUT WIDTH -->
|
||||||
|
<div class="group mt-32">
|
||||||
|
|
||||||
|
<h2>Layout Width</h2>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
|
||||||
|
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NAVBAR -->
|
||||||
|
<div class="group" formGroupName="navbar">
|
||||||
|
|
||||||
|
<h2>Navbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<mat-slide-toggle class="mt-24" formControlName="folded">
|
||||||
|
Folded
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-16" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-8">Variant:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Primary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="primaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Secondary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="secondaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TOOLBAR -->
|
||||||
|
<div class="group" formGroupName="toolbar">
|
||||||
|
|
||||||
|
<h2>Toolbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<div class="group" formGroupName="footer">
|
||||||
|
|
||||||
|
<h2>Footer</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SIDE PANEL -->
|
||||||
|
<div class="group" formGroupName="sidepanel">
|
||||||
|
|
||||||
|
<h2>Side Panel</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- HORIZONTAL LAYOUT #1 -->
|
||||||
|
<ng-container *ngSwitchCase="'horizontal-layout-1'">
|
||||||
|
|
||||||
|
<!-- LAYOUT WIDTH -->
|
||||||
|
<div class="group mt-32">
|
||||||
|
|
||||||
|
<h2>Layout Width</h2>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="width">
|
||||||
|
<mat-radio-button class="mb-12" value="fullwidth">Fullwidth</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="boxed">Boxed</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NAVBAR -->
|
||||||
|
<div class="group" formGroupName="navbar">
|
||||||
|
|
||||||
|
<h2>Navbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-16" value="top">Top</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-8">Variant (Vertical):</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="variant">
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-1">Style 1</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-16" value="vertical-style-2">Style 2</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Primary background:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="primaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
<h3 class="mt-16 mb-8">Secondary background (Vertical):</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="secondaryBackground">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TOOLBAR -->
|
||||||
|
<div class="group" formGroupName="toolbar">
|
||||||
|
|
||||||
|
<h2>Toolbar</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above">Above</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="below">Below</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<div class="group" formGroupName="footer">
|
||||||
|
|
||||||
|
<h2>Footer</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="above-fixed">Above Fixed</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="above-static">Above Static</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<mat-checkbox class="mt-24" formControlName="customBackgroundColor">
|
||||||
|
Use custom background color
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<h3 class="mt-24 mb-8">Background color:</h3>
|
||||||
|
<fuse-material-color-picker class="mb-16" formControlName="background">
|
||||||
|
</fuse-material-color-picker>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SIDE PANEL -->
|
||||||
|
<div class="group" formGroupName="sidepanel">
|
||||||
|
|
||||||
|
<h2>Side Panel</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle formControlName="hidden">
|
||||||
|
Hide
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
<h3 class="mt-24">Position:</h3>
|
||||||
|
<mat-radio-group fxLayout="column" fxLayoutAlign="start start" formControlName="position">
|
||||||
|
<mat-radio-button class="mb-12" value="left">Left</mat-radio-button>
|
||||||
|
<mat-radio-button class="mb-12" value="right">Right</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CUSTOM SCROLLBARS -->
|
||||||
|
<div class="group">
|
||||||
|
|
||||||
|
<h2>Custom scrollbars</h2>
|
||||||
|
|
||||||
|
<mat-slide-toggle class="mb-12" formControlName="customScrollbars">
|
||||||
|
Enable custom scrollbars
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,75 @@
|
||||||
|
@import 'src/@fuse/scss/fuse';
|
||||||
|
|
||||||
|
@keyframes rotating {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse-theme-options {
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.theme-options-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
padding: 40px 24px 24px 24px;
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 28px 16px 8px 16px;
|
||||||
|
margin: 16px 0;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
position: absolute;
|
||||||
|
top: -11px;
|
||||||
|
left: 8px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 24px 0 16px 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:first-of-type {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
336
src/@fuse/components/theme-options/theme-options.component.ts
Normal file
336
src/@fuse/components/theme-options/theme-options.component.ts
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
HostBinding,
|
||||||
|
Inject,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Renderer2,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { fuseAnimations } from 'src/@fuse/animations';
|
||||||
|
import { FuseConfigService } from 'src/@fuse/services/config.service';
|
||||||
|
import { FuseNavigationService } from 'src/@fuse/components/navigation/navigation.service';
|
||||||
|
import { FuseSidebarService } from 'src/@fuse/components/sidebar/sidebar.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-theme-options',
|
||||||
|
templateUrl: './theme-options.component.html',
|
||||||
|
styleUrls: ['./theme-options.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
animations: fuseAnimations
|
||||||
|
})
|
||||||
|
export class FuseThemeOptionsComponent implements OnInit, OnDestroy {
|
||||||
|
fuseConfig: any;
|
||||||
|
form: FormGroup;
|
||||||
|
|
||||||
|
@HostBinding('class.bar-closed')
|
||||||
|
barClosed: boolean;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {DOCUMENT} document
|
||||||
|
* @param {FormBuilder} _formBuilder
|
||||||
|
* @param {FuseConfigService} _fuseConfigService
|
||||||
|
* @param {FuseNavigationService} _fuseNavigationService
|
||||||
|
* @param {FuseSidebarService} _fuseSidebarService
|
||||||
|
* @param {Renderer2} _renderer
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
@Inject(DOCUMENT) private document: any,
|
||||||
|
private _formBuilder: FormBuilder,
|
||||||
|
private _fuseConfigService: FuseConfigService,
|
||||||
|
private _fuseNavigationService: FuseNavigationService,
|
||||||
|
private _fuseSidebarService: FuseSidebarService,
|
||||||
|
private _renderer: Renderer2
|
||||||
|
) {
|
||||||
|
// Set the defaults
|
||||||
|
this.barClosed = true;
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Build the config form
|
||||||
|
// noinspection TypeScriptValidateTypes
|
||||||
|
this.form = this._formBuilder.group({
|
||||||
|
colorTheme: new FormControl(),
|
||||||
|
customScrollbars: new FormControl(),
|
||||||
|
layout: this._formBuilder.group({
|
||||||
|
style: new FormControl(),
|
||||||
|
width: new FormControl(),
|
||||||
|
navbar: this._formBuilder.group({
|
||||||
|
primaryBackground: new FormControl(),
|
||||||
|
secondaryBackground: new FormControl(),
|
||||||
|
folded: new FormControl(),
|
||||||
|
hidden: new FormControl(),
|
||||||
|
position: new FormControl(),
|
||||||
|
variant: new FormControl()
|
||||||
|
}),
|
||||||
|
toolbar: this._formBuilder.group({
|
||||||
|
background: new FormControl(),
|
||||||
|
customBackgroundColor: new FormControl(),
|
||||||
|
hidden: new FormControl(),
|
||||||
|
position: new FormControl()
|
||||||
|
}),
|
||||||
|
footer: this._formBuilder.group({
|
||||||
|
background: new FormControl(),
|
||||||
|
customBackgroundColor: new FormControl(),
|
||||||
|
hidden: new FormControl(),
|
||||||
|
position: new FormControl()
|
||||||
|
}),
|
||||||
|
sidepanel: this._formBuilder.group({
|
||||||
|
hidden: new FormControl(),
|
||||||
|
position: new FormControl()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to the config changes
|
||||||
|
this._fuseConfigService.config
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(config => {
|
||||||
|
// Update the stored config
|
||||||
|
this.fuseConfig = config;
|
||||||
|
|
||||||
|
// Set the config form values without emitting an event
|
||||||
|
// so that we don't end up with an infinite loop
|
||||||
|
this.form.setValue(config, { emitEvent: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to the specific form value changes (layout.style)
|
||||||
|
this.form
|
||||||
|
.get('layout.style')
|
||||||
|
.valueChanges.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(value => {
|
||||||
|
// Reset the form values based on the
|
||||||
|
// selected layout style
|
||||||
|
this._resetFormValues(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to the form value changes
|
||||||
|
this.form.valueChanges
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(config => {
|
||||||
|
// Update the config
|
||||||
|
this._fuseConfigService.config = config;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add customize nav item that opens the bar programmatically
|
||||||
|
const customFunctionNavItem = {
|
||||||
|
id: 'custom-function',
|
||||||
|
title: 'Custom Function',
|
||||||
|
type: 'group',
|
||||||
|
icon: 'settings',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'customize',
|
||||||
|
title: 'Customize',
|
||||||
|
type: 'item',
|
||||||
|
icon: 'settings',
|
||||||
|
function: () => {
|
||||||
|
this.toggleSidebarOpen('themeOptionsPanel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
this._fuseNavigationService.addNavigationItem(customFunctionNavItem, 'end');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
|
||||||
|
// Remove the custom function menu
|
||||||
|
this._fuseNavigationService.removeNavigationItem('custom-function');
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Private methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the form values based on the
|
||||||
|
* selected layout style
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _resetFormValues(value): void {
|
||||||
|
switch (value) {
|
||||||
|
// Vertical Layout #1
|
||||||
|
case 'vertical-layout-1': {
|
||||||
|
this.form.patchValue({
|
||||||
|
layout: {
|
||||||
|
width: 'fullwidth',
|
||||||
|
navbar: {
|
||||||
|
primaryBackground: 'fuse-navy-700',
|
||||||
|
secondaryBackground: 'fuse-navy-900',
|
||||||
|
folded: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'left',
|
||||||
|
variant: 'vertical-style-1'
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
background: 'fuse-white-500',
|
||||||
|
customBackgroundColor: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'below-static'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
background: 'fuse-navy-900',
|
||||||
|
customBackgroundColor: true,
|
||||||
|
hidden: false,
|
||||||
|
position: 'below-static'
|
||||||
|
},
|
||||||
|
sidepanel: {
|
||||||
|
hidden: false,
|
||||||
|
position: 'right'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Layout #2
|
||||||
|
case 'vertical-layout-2': {
|
||||||
|
this.form.patchValue({
|
||||||
|
layout: {
|
||||||
|
width: 'fullwidth',
|
||||||
|
navbar: {
|
||||||
|
primaryBackground: 'fuse-navy-700',
|
||||||
|
secondaryBackground: 'fuse-navy-900',
|
||||||
|
folded: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'left',
|
||||||
|
variant: 'vertical-style-1'
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
background: 'fuse-white-500',
|
||||||
|
customBackgroundColor: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'below'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
background: 'fuse-navy-900',
|
||||||
|
customBackgroundColor: true,
|
||||||
|
hidden: false,
|
||||||
|
position: 'below'
|
||||||
|
},
|
||||||
|
sidepanel: {
|
||||||
|
hidden: false,
|
||||||
|
position: 'right'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Layout #3
|
||||||
|
case 'vertical-layout-3': {
|
||||||
|
this.form.patchValue({
|
||||||
|
layout: {
|
||||||
|
width: 'fullwidth',
|
||||||
|
navbar: {
|
||||||
|
primaryBackground: 'fuse-navy-700',
|
||||||
|
secondaryBackground: 'fuse-navy-900',
|
||||||
|
folded: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'left',
|
||||||
|
layout: 'vertical-style-1'
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
background: 'fuse-white-500',
|
||||||
|
customBackgroundColor: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'above-static'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
background: 'fuse-navy-900',
|
||||||
|
customBackgroundColor: true,
|
||||||
|
hidden: false,
|
||||||
|
position: 'above-static'
|
||||||
|
},
|
||||||
|
sidepanel: {
|
||||||
|
hidden: false,
|
||||||
|
position: 'right'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal Layout #1
|
||||||
|
case 'horizontal-layout-1': {
|
||||||
|
this.form.patchValue({
|
||||||
|
layout: {
|
||||||
|
width: 'fullwidth',
|
||||||
|
navbar: {
|
||||||
|
primaryBackground: 'fuse-navy-700',
|
||||||
|
secondaryBackground: 'fuse-navy-900',
|
||||||
|
folded: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'top',
|
||||||
|
variant: 'vertical-style-1'
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
background: 'fuse-white-500',
|
||||||
|
customBackgroundColor: false,
|
||||||
|
hidden: false,
|
||||||
|
position: 'above'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
background: 'fuse-navy-900',
|
||||||
|
customBackgroundColor: true,
|
||||||
|
hidden: false,
|
||||||
|
position: 'above-fixed'
|
||||||
|
},
|
||||||
|
sidepanel: {
|
||||||
|
hidden: false,
|
||||||
|
position: 'right'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle sidebar open
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
toggleSidebarOpen(key): void {
|
||||||
|
this._fuseSidebarService.getSidebar(key).toggleOpen();
|
||||||
|
}
|
||||||
|
}
|
46
src/@fuse/components/theme-options/theme-options.module.ts
Normal file
46
src/@fuse/components/theme-options/theme-options.module.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatOptionModule } from '@angular/material/core';
|
||||||
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||||
|
|
||||||
|
import { FuseDirectivesModule } from 'src/@fuse/directives/directives';
|
||||||
|
import { FuseMaterialColorPickerModule } from 'src/@fuse/components/material-color-picker/material-color-picker.module';
|
||||||
|
import { FuseSidebarModule } from 'src/@fuse/components/sidebar/sidebar.module';
|
||||||
|
|
||||||
|
import { FuseThemeOptionsComponent } from 'src/@fuse/components/theme-options/theme-options.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseThemeOptionsComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
FlexLayoutModule,
|
||||||
|
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatDividerModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatOptionModule,
|
||||||
|
MatRadioModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
|
||||||
|
FuseDirectivesModule,
|
||||||
|
FuseMaterialColorPickerModule,
|
||||||
|
FuseSidebarModule
|
||||||
|
],
|
||||||
|
exports: [FuseThemeOptionsComponent]
|
||||||
|
})
|
||||||
|
export class FuseThemeOptionsModule {}
|
23
src/@fuse/components/theme-options/theme-options.theme.scss
Normal file
23
src/@fuse/components/theme-options/theme-options.theme.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
@mixin fuse-theme-options-theme($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
|
fuse-theme-options {
|
||||||
|
.theme-options-panel {
|
||||||
|
form {
|
||||||
|
.group {
|
||||||
|
border: 1px solid map-get($foreground, divider);
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
background: map-get($background, background);
|
||||||
|
color: map-get($foreground, secondary-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: map-get($foreground, secondary-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/@fuse/components/widget/widget-toggle.directive.ts
Normal file
13
src/@fuse/components/widget/widget-toggle.directive.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { Directive, ElementRef } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[fuseWidgetToggle]'
|
||||||
|
})
|
||||||
|
export class FuseWidgetToggleDirective {
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ElementRef} elementRef
|
||||||
|
*/
|
||||||
|
constructor(public elementRef: ElementRef) {}
|
||||||
|
}
|
1
src/@fuse/components/widget/widget.component.html
Normal file
1
src/@fuse/components/widget/widget.component.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<ng-content></ng-content>
|
87
src/@fuse/components/widget/widget.component.scss
Normal file
87
src/@fuse/components/widget/widget.component.scss
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
fuse-widget {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
perspective: 3000px;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transition: transform 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .fuse-widget-front {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: visible;
|
||||||
|
width: 100%;
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: transform 0.5s ease-out 0s, visibility 0s ease-in 0.2s,
|
||||||
|
opacity 0s ease-in 0.2s;
|
||||||
|
transform: rotateY(0deg);
|
||||||
|
backface-visibility: hidden;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .fuse-widget-back {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
left: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: transform 0.5s ease-out 0s, visibility 0s ease-in 0.2s,
|
||||||
|
opacity 0s ease-in 0.2s;
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
backface-visibility: hidden;
|
||||||
|
border: 1px solid;
|
||||||
|
|
||||||
|
[fuseWidgetToggle] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.flipped {
|
||||||
|
> .fuse-widget-front {
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .fuse-widget-back {
|
||||||
|
display: block;
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotateY(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
&.mat-form-field-type-mat-select {
|
||||||
|
.mat-form-field-wrapper {
|
||||||
|
padding: 16px 0;
|
||||||
|
|
||||||
|
.mat-form-field-infix {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-underline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
src/@fuse/components/widget/widget.component.ts
Normal file
68
src/@fuse/components/widget/widget.component.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import {
|
||||||
|
AfterContentInit,
|
||||||
|
Component,
|
||||||
|
ContentChildren,
|
||||||
|
ElementRef,
|
||||||
|
HostBinding,
|
||||||
|
QueryList,
|
||||||
|
Renderer2,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FuseWidgetToggleDirective } from './widget-toggle.directive';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'fuse-widget',
|
||||||
|
templateUrl: './widget.component.html',
|
||||||
|
styleUrls: ['./widget.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class FuseWidgetComponent implements AfterContentInit {
|
||||||
|
@HostBinding('class.flipped')
|
||||||
|
flipped = false;
|
||||||
|
|
||||||
|
@ContentChildren(FuseWidgetToggleDirective, { descendants: true })
|
||||||
|
toggleButtons: QueryList<FuseWidgetToggleDirective>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ElementRef} _elementRef
|
||||||
|
* @param {Renderer2} _renderer
|
||||||
|
*/
|
||||||
|
constructor(private _elementRef: ElementRef, private _renderer: Renderer2) {}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After content init
|
||||||
|
*/
|
||||||
|
ngAfterContentInit(): void {
|
||||||
|
// Listen for the flip button click
|
||||||
|
setTimeout(() => {
|
||||||
|
this.toggleButtons.forEach(flipButton => {
|
||||||
|
this._renderer.listen(
|
||||||
|
flipButton.elementRef.nativeElement,
|
||||||
|
'click',
|
||||||
|
event => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.toggle();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the flipped status
|
||||||
|
*/
|
||||||
|
toggle(): void {
|
||||||
|
this.flipped = !this.flipped;
|
||||||
|
}
|
||||||
|
}
|
10
src/@fuse/components/widget/widget.module.ts
Normal file
10
src/@fuse/components/widget/widget.module.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { FuseWidgetComponent } from './widget.component';
|
||||||
|
import { FuseWidgetToggleDirective } from './widget-toggle.directive';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [FuseWidgetComponent, FuseWidgetToggleDirective],
|
||||||
|
exports: [FuseWidgetComponent, FuseWidgetToggleDirective]
|
||||||
|
})
|
||||||
|
export class FuseWidgetModule {}
|
12
src/@fuse/components/widget/widget.theme.scss
Normal file
12
src/@fuse/components/widget/widget.theme.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
@mixin fuse-widget-theme($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
|
fuse-widget {
|
||||||
|
> .fuse-widget-front,
|
||||||
|
> .fuse-widget-back {
|
||||||
|
background: map-get($background, card);
|
||||||
|
border-color: map-get($foreground, divider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/@fuse/directives/directives.ts
Normal file
28
src/@fuse/directives/directives.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { FuseIfOnDomDirective } from 'src/@fuse/directives/fuse-if-on-dom/fuse-if-on-dom.directive';
|
||||||
|
import { FuseInnerScrollDirective } from 'src/@fuse/directives/fuse-inner-scroll/fuse-inner-scroll.directive';
|
||||||
|
import { FusePerfectScrollbarDirective } from 'src/@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive';
|
||||||
|
import {
|
||||||
|
FuseMatSidenavHelperDirective,
|
||||||
|
FuseMatSidenavTogglerDirective
|
||||||
|
} from 'src/@fuse/directives/fuse-mat-sidenav/fuse-mat-sidenav.directive';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
FuseIfOnDomDirective,
|
||||||
|
FuseInnerScrollDirective,
|
||||||
|
FuseMatSidenavHelperDirective,
|
||||||
|
FuseMatSidenavTogglerDirective,
|
||||||
|
FusePerfectScrollbarDirective
|
||||||
|
],
|
||||||
|
imports: [],
|
||||||
|
exports: [
|
||||||
|
FuseIfOnDomDirective,
|
||||||
|
FuseInnerScrollDirective,
|
||||||
|
FuseMatSidenavHelperDirective,
|
||||||
|
FuseMatSidenavTogglerDirective,
|
||||||
|
FusePerfectScrollbarDirective
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FuseDirectivesModule {}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import {
|
||||||
|
AfterContentChecked,
|
||||||
|
Directive,
|
||||||
|
ElementRef,
|
||||||
|
TemplateRef,
|
||||||
|
ViewContainerRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[fuseIfOnDom]'
|
||||||
|
})
|
||||||
|
export class FuseIfOnDomDirective implements AfterContentChecked {
|
||||||
|
isCreated: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ElementRef} _elementRef
|
||||||
|
* @param {TemplateRef<any>} _templateRef
|
||||||
|
* @param {ViewContainerRef} _viewContainerRef
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _elementRef: ElementRef,
|
||||||
|
private _templateRef: TemplateRef<any>,
|
||||||
|
private _viewContainerRef: ViewContainerRef
|
||||||
|
) {
|
||||||
|
// Set the defaults
|
||||||
|
this.isCreated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After content checked
|
||||||
|
*/
|
||||||
|
ngAfterContentChecked(): void {
|
||||||
|
if (
|
||||||
|
document.body.contains(this._elementRef.nativeElement) &&
|
||||||
|
!this.isCreated
|
||||||
|
) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this._viewContainerRef.createEmbeddedView(this._templateRef);
|
||||||
|
}, 300);
|
||||||
|
this.isCreated = true;
|
||||||
|
} else if (
|
||||||
|
this.isCreated &&
|
||||||
|
!document.body.contains(this._elementRef.nativeElement)
|
||||||
|
) {
|
||||||
|
this._viewContainerRef.clear();
|
||||||
|
this.isCreated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
ElementRef,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Renderer2
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseMatchMediaService } from 'src/@fuse/services/match-media.service';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '.inner-scroll'
|
||||||
|
})
|
||||||
|
export class FuseInnerScrollDirective implements OnInit, OnDestroy {
|
||||||
|
// Private
|
||||||
|
private _parent: any;
|
||||||
|
private _grandParent: any;
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ElementRef} _elementRef
|
||||||
|
* @param {FuseMatchMediaService} _fuseMediaMatchService
|
||||||
|
* @param {Renderer2} _renderer
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _elementRef: ElementRef,
|
||||||
|
private _fuseMediaMatchService: FuseMatchMediaService,
|
||||||
|
private _renderer: Renderer2
|
||||||
|
) {
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Get the parent
|
||||||
|
this._parent = this._renderer.parentNode(this._elementRef.nativeElement);
|
||||||
|
|
||||||
|
// Return, if there is no parent
|
||||||
|
if (!this._parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the grand parent
|
||||||
|
this._grandParent = this._renderer.parentNode(this._parent);
|
||||||
|
|
||||||
|
// Register to the media query changes
|
||||||
|
this._fuseMediaMatchService.onMediaChange
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(alias => {
|
||||||
|
if (alias === 'xs') {
|
||||||
|
this._removeClass();
|
||||||
|
} else {
|
||||||
|
this._addClass();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Return, if there is no parent
|
||||||
|
if (!this._parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the class
|
||||||
|
this._removeClass();
|
||||||
|
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Private methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the class name
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _addClass(): void {
|
||||||
|
// Add the inner-scroll class
|
||||||
|
this._renderer.addClass(this._grandParent, 'inner-scroll');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the class name
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _removeClass(): void {
|
||||||
|
// Remove the inner-scroll class
|
||||||
|
this._renderer.removeClass(this._grandParent, 'inner-scroll');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
HostListener,
|
||||||
|
OnDestroy,
|
||||||
|
HostBinding
|
||||||
|
} from '@angular/core';
|
||||||
|
import { MatSidenav } from '@angular/material/sidenav';
|
||||||
|
import { MediaObserver } from '@angular/flex-layout';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { FuseMatchMediaService } from 'src/@fuse/services/match-media.service';
|
||||||
|
import { FuseMatSidenavHelperService } from 'src/@fuse/directives/fuse-mat-sidenav/fuse-mat-sidenav.service';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[fuseMatSidenavHelper]'
|
||||||
|
})
|
||||||
|
export class FuseMatSidenavHelperDirective implements OnInit, OnDestroy {
|
||||||
|
@HostBinding('class.mat-is-locked-open')
|
||||||
|
isLockedOpen: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fuseMatSidenavHelper: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
matIsLockedOpen: string;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {FuseMatchMediaService} _fuseMatchMediaService
|
||||||
|
* @param {FuseMatSidenavHelperService} _fuseMatSidenavHelperService
|
||||||
|
* @param {MatSidenav} _matSidenav
|
||||||
|
* @param {MediaObserver} _mediaObserver
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _fuseMatchMediaService: FuseMatchMediaService,
|
||||||
|
private _fuseMatSidenavHelperService: FuseMatSidenavHelperService,
|
||||||
|
private _matSidenav: MatSidenav,
|
||||||
|
private _mediaObserver: MediaObserver
|
||||||
|
) {
|
||||||
|
// Set the defaults
|
||||||
|
this.isLockedOpen = true;
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Register the sidenav to the service
|
||||||
|
this._fuseMatSidenavHelperService.setSidenav(
|
||||||
|
this.fuseMatSidenavHelper,
|
||||||
|
this._matSidenav
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.matIsLockedOpen &&
|
||||||
|
this._mediaObserver.isActive(this.matIsLockedOpen)
|
||||||
|
) {
|
||||||
|
this.isLockedOpen = true;
|
||||||
|
this._matSidenav.mode = 'side';
|
||||||
|
this._matSidenav.toggle(true);
|
||||||
|
} else {
|
||||||
|
this.isLockedOpen = false;
|
||||||
|
this._matSidenav.mode = 'over';
|
||||||
|
this._matSidenav.toggle(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fuseMatchMediaService.onMediaChange
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(() => {
|
||||||
|
if (
|
||||||
|
this.matIsLockedOpen &&
|
||||||
|
this._mediaObserver.isActive(this.matIsLockedOpen)
|
||||||
|
) {
|
||||||
|
this.isLockedOpen = true;
|
||||||
|
this._matSidenav.mode = 'side';
|
||||||
|
this._matSidenav.toggle(true);
|
||||||
|
} else {
|
||||||
|
this.isLockedOpen = false;
|
||||||
|
this._matSidenav.mode = 'over';
|
||||||
|
this._matSidenav.toggle(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[fuseMatSidenavToggler]'
|
||||||
|
})
|
||||||
|
export class FuseMatSidenavTogglerDirective {
|
||||||
|
@Input()
|
||||||
|
fuseMatSidenavToggler: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {FuseMatSidenavHelperService} _fuseMatSidenavHelperService
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _fuseMatSidenavHelperService: FuseMatSidenavHelperService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On click
|
||||||
|
*/
|
||||||
|
@HostListener('click')
|
||||||
|
onClick(): void {
|
||||||
|
this._fuseMatSidenavHelperService
|
||||||
|
.getSidenav(this.fuseMatSidenavToggler)
|
||||||
|
.toggle();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatSidenav } from '@angular/material/sidenav';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FuseMatSidenavHelperService {
|
||||||
|
sidenavInstances: MatSidenav[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.sidenavInstances = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Accessors
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set sidenav
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param instance
|
||||||
|
*/
|
||||||
|
setSidenav(id, instance): void {
|
||||||
|
this.sidenavInstances[id] = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sidenav
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
getSidenav(id): any {
|
||||||
|
return this.sidenavInstances[id];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,508 @@
|
||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Directive,
|
||||||
|
ElementRef,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit
|
||||||
|
} from '@angular/core';
|
||||||
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
|
import { Platform } from '@angular/cdk/platform';
|
||||||
|
import { fromEvent, Subject } from 'rxjs';
|
||||||
|
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
|
||||||
|
import PerfectScrollbar from 'perfect-scrollbar';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import {
|
||||||
|
FusePerfectScrollbarGeometry,
|
||||||
|
FusePerfectScrollbarPosition
|
||||||
|
} from 'src/@fuse/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.interfaces';
|
||||||
|
import { FuseConfigService } from 'src/@fuse/services/config.service';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[fusePerfectScrollbar]'
|
||||||
|
})
|
||||||
|
export class FusePerfectScrollbarDirective
|
||||||
|
implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
isInitialized: boolean;
|
||||||
|
isMobile: boolean;
|
||||||
|
ps: PerfectScrollbar | any;
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private _animation: number | null;
|
||||||
|
private _enabled: boolean | '';
|
||||||
|
private _debouncedUpdate: any;
|
||||||
|
private _options: any;
|
||||||
|
private _unsubscribeAll: Subject<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {ElementRef} elementRef
|
||||||
|
* @param {FuseConfigService} _fuseConfigService
|
||||||
|
* @param {Platform} _platform
|
||||||
|
* @param {Router} _router
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
public elementRef: ElementRef,
|
||||||
|
private _fuseConfigService: FuseConfigService,
|
||||||
|
private _platform: Platform,
|
||||||
|
private _router: Router
|
||||||
|
) {
|
||||||
|
// Set the defaults
|
||||||
|
this.isInitialized = false;
|
||||||
|
this.isMobile = false;
|
||||||
|
|
||||||
|
// Set the private defaults
|
||||||
|
this._animation = null;
|
||||||
|
this._enabled = false;
|
||||||
|
this._debouncedUpdate = _.debounce(this.update, 150);
|
||||||
|
this._options = {
|
||||||
|
updateOnRouteChange: false
|
||||||
|
};
|
||||||
|
this._unsubscribeAll = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Accessors
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perfect Scrollbar options
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
set fusePerfectScrollbarOptions(value) {
|
||||||
|
// Merge the options
|
||||||
|
this._options = _.merge({}, this._options, value);
|
||||||
|
|
||||||
|
// Destroy and re-init the PerfectScrollbar to update its options
|
||||||
|
setTimeout(() => {
|
||||||
|
this._destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this._init();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get fusePerfectScrollbarOptions(): any {
|
||||||
|
// Return the options
|
||||||
|
return this._options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is enabled
|
||||||
|
*
|
||||||
|
* @param {boolean | ""} value
|
||||||
|
*/
|
||||||
|
@Input('fusePerfectScrollbar')
|
||||||
|
set enabled(value: boolean | '') {
|
||||||
|
// If nothing is provided with the directive (empty string),
|
||||||
|
// we will take that as a true
|
||||||
|
if (value === '') {
|
||||||
|
value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return, if both values are the same
|
||||||
|
if (this.enabled === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the value
|
||||||
|
this._enabled = value;
|
||||||
|
|
||||||
|
// If enabled...
|
||||||
|
if (this.enabled) {
|
||||||
|
// Init the directive
|
||||||
|
this._init();
|
||||||
|
} else {
|
||||||
|
// Otherwise destroy it
|
||||||
|
this._destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get enabled(): boolean | '' {
|
||||||
|
// Return the enabled status
|
||||||
|
return this._enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Lifecycle hooks
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On init
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Subscribe to window resize event
|
||||||
|
fromEvent(window, 'resize')
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this._unsubscribeAll),
|
||||||
|
debounceTime(150)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
// Update the PerfectScrollbar
|
||||||
|
this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After view init
|
||||||
|
*/
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
// Check if scrollbars enabled or not from the main config
|
||||||
|
this._fuseConfigService.config
|
||||||
|
.pipe(takeUntil(this._unsubscribeAll))
|
||||||
|
.subscribe(settings => {
|
||||||
|
this.enabled = settings.customScrollbars;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Scroll to the top on every route change
|
||||||
|
if (this.fusePerfectScrollbarOptions.updateOnRouteChange) {
|
||||||
|
this._router.events
|
||||||
|
.pipe(
|
||||||
|
takeUntil(this._unsubscribeAll),
|
||||||
|
filter(event => event instanceof NavigationEnd)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.scrollToTop();
|
||||||
|
this.update();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On destroy
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this._destroy();
|
||||||
|
|
||||||
|
// Unsubscribe from all subscriptions
|
||||||
|
this._unsubscribeAll.next();
|
||||||
|
this._unsubscribeAll.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Private methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_init(): void {
|
||||||
|
// Return, if already initialized
|
||||||
|
if (this.isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if is mobile
|
||||||
|
if (this._platform.ANDROID || this._platform.IOS) {
|
||||||
|
this.isMobile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if it's mobile
|
||||||
|
if (this.isMobile) {
|
||||||
|
// Return...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set as initialized
|
||||||
|
this.isInitialized = true;
|
||||||
|
|
||||||
|
// Initialize the perfect-scrollbar
|
||||||
|
this.ps = new PerfectScrollbar(this.elementRef.nativeElement, {
|
||||||
|
...this.fusePerfectScrollbarOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
// Unbind 'keydown' events of PerfectScrollbar since it causes an extremely
|
||||||
|
// high CPU usage on Angular Material inputs.
|
||||||
|
// Loop through all the event elements of this PerfectScrollbar instance
|
||||||
|
this.ps.event.eventElements.forEach(eventElement => {
|
||||||
|
// If we hit to the element with a 'keydown' event...
|
||||||
|
if (typeof eventElement.handlers['keydown'] !== 'undefined') {
|
||||||
|
// Unbind it
|
||||||
|
eventElement.element.removeEventListener(
|
||||||
|
'keydown',
|
||||||
|
eventElement.handlers['keydown'][0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_destroy(): void {
|
||||||
|
if (!this.isInitialized || !this.ps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the perfect-scrollbar
|
||||||
|
this.ps.destroy();
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
this.ps = null;
|
||||||
|
this.isInitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update scrollbars on window resize
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
@HostListener('window:resize')
|
||||||
|
_updateOnResize(): void {
|
||||||
|
this._debouncedUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
// @ Public methods
|
||||||
|
// -----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document click
|
||||||
|
*
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
@HostListener('document:click', ['$event'])
|
||||||
|
documentClick(event: Event): void {
|
||||||
|
if (!this.isInitialized || !this.ps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the scrollbar on document click..
|
||||||
|
// This isn't the most elegant solution but there is no other way
|
||||||
|
// of knowing when the contents of the scrollable container changes.
|
||||||
|
// Therefore, we update scrollbars on every document click.
|
||||||
|
this.ps.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the scrollbar
|
||||||
|
*/
|
||||||
|
update(): void {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the perfect-scrollbar
|
||||||
|
this.ps.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the scrollbar
|
||||||
|
*/
|
||||||
|
destroy(): void {
|
||||||
|
this.ngOnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the geometry of the scrollable element
|
||||||
|
*
|
||||||
|
* @param prefix
|
||||||
|
*/
|
||||||
|
geometry(prefix: string = 'scroll'): FusePerfectScrollbarGeometry {
|
||||||
|
return new FusePerfectScrollbarGeometry(
|
||||||
|
this.elementRef.nativeElement[prefix + 'Left'],
|
||||||
|
this.elementRef.nativeElement[prefix + 'Top'],
|
||||||
|
this.elementRef.nativeElement[prefix + 'Width'],
|
||||||
|
this.elementRef.nativeElement[prefix + 'Height']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the scrollable element
|
||||||
|
*
|
||||||
|
* @param absolute
|
||||||
|
*/
|
||||||
|
position(absolute: boolean = false): FusePerfectScrollbarPosition {
|
||||||
|
if (!absolute && this.ps) {
|
||||||
|
return new FusePerfectScrollbarPosition(
|
||||||
|
this.ps.reach.x || 0,
|
||||||
|
this.ps.reach.y || 0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new FusePerfectScrollbarPosition(
|
||||||
|
this.elementRef.nativeElement.scrollLeft,
|
||||||
|
this.elementRef.nativeElement.scrollTop
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param speed
|
||||||
|
*/
|
||||||
|
scrollTo(x: number, y?: number, speed?: number): void {
|
||||||
|
if (y == null && speed == null) {
|
||||||
|
this.animateScrolling('scrollTop', x, speed);
|
||||||
|
} else {
|
||||||
|
if (x != null) {
|
||||||
|
this.animateScrolling('scrollLeft', x, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y != null) {
|
||||||
|
this.animateScrolling('scrollTop', y, speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to X
|
||||||
|
*
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} speed
|
||||||
|
*/
|
||||||
|
scrollToX(x: number, speed?: number): void {
|
||||||
|
this.animateScrolling('scrollLeft', x, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to Y
|
||||||
|
*
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} speed
|
||||||
|
*/
|
||||||
|
scrollToY(y: number, speed?: number): void {
|
||||||
|
this.animateScrolling('scrollTop', y, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to top
|
||||||
|
*
|
||||||
|
* @param {number} offset
|
||||||
|
* @param {number} speed
|
||||||
|
*/
|
||||||
|
scrollToTop(offset?: number, speed?: number): void {
|
||||||
|
this.animateScrolling('scrollTop', offset || 0, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to left
|
||||||
|
*
|
||||||
|
* @param {number} offset
|
||||||
|
* @param {number} speed
|
||||||
|
*/
|
||||||
|
scrollToLeft(offset?: number, speed?: number): void {
|
||||||
|
this.animateScrolling('scrollLeft', offset || 0, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to right
|
||||||
|
*
|
||||||
|
* @param {number} offset
|
||||||
|
* @param {number} speed
|
||||||
|
*/
|
||||||
|
scrollToRight(offset?: number, speed?: number): void {
|
||||||
|
const left =
|
||||||
|
this.elementRef.nativeElement.scrollWidth -
|
||||||
|
this.elementRef.nativeElement.clientWidth;
|
||||||
|
this.animateScrolling('scrollLeft', left - (offset || 0), speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to bottom
|
||||||
|
*
|
||||||
|
* @param {number} offset
|
||||||
|
* @param {number} speed
|
||||||
|
*/
|
||||||
|
scrollToBottom(offset?: number, speed?: number): void {
|
||||||
|
const top =
|
||||||
|
this.elementRef.nativeElement.scrollHeight -
|
||||||
|
this.elementRef.nativeElement.clientHeight;
|
||||||
|
this.animateScrolling('scrollTop', top - (offset || 0), speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to element
|
||||||
|
*
|
||||||
|
* @param qs
|
||||||
|
* @param offset
|
||||||
|
* @param speed
|
||||||
|
*/
|
||||||
|
scrollToElement(qs: string, offset?: number, speed?: number): void {
|
||||||
|
const element = this.elementRef.nativeElement.querySelector(qs);
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elementPos = element.getBoundingClientRect();
|
||||||
|
const scrollerPos = this.elementRef.nativeElement.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (this.elementRef.nativeElement.classList.contains('ps--active-x')) {
|
||||||
|
const currentPos = this.elementRef.nativeElement['scrollLeft'];
|
||||||
|
const position = elementPos.left - scrollerPos.left + currentPos;
|
||||||
|
|
||||||
|
this.animateScrolling('scrollLeft', position + (offset || 0), speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.elementRef.nativeElement.classList.contains('ps--active-y')) {
|
||||||
|
const currentPos = this.elementRef.nativeElement['scrollTop'];
|
||||||
|
const position = elementPos.top - scrollerPos.top + currentPos;
|
||||||
|
|
||||||
|
this.animateScrolling('scrollTop', position + (offset || 0), speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate scrolling
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* @param value
|
||||||
|
* @param speed
|
||||||
|
*/
|
||||||
|
animateScrolling(target: string, value: number, speed?: number): void {
|
||||||
|
if (this._animation) {
|
||||||
|
window.cancelAnimationFrame(this._animation);
|
||||||
|
this._animation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!speed || typeof window === 'undefined') {
|
||||||
|
this.elementRef.nativeElement[target] = value;
|
||||||
|
} else if (value !== this.elementRef.nativeElement[target]) {
|
||||||
|
let newValue = 0;
|
||||||
|
let scrollCount = 0;
|
||||||
|
|
||||||
|
let oldTimestamp = performance.now();
|
||||||
|
let oldValue = this.elementRef.nativeElement[target];
|
||||||
|
|
||||||
|
const cosParameter = (oldValue - value) / 2;
|
||||||
|
|
||||||
|
const step = (newTimestamp: number) => {
|
||||||
|
scrollCount += Math.PI / (speed / (newTimestamp - oldTimestamp));
|
||||||
|
newValue = Math.round(
|
||||||
|
value + cosParameter + cosParameter * Math.cos(scrollCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only continue animation if scroll position has not changed
|
||||||
|
if (this.elementRef.nativeElement[target] === oldValue) {
|
||||||
|
if (scrollCount >= Math.PI) {
|
||||||
|
this.animateScrolling(target, value, 0);
|
||||||
|
} else {
|
||||||
|
this.elementRef.nativeElement[target] = newValue;
|
||||||
|
|
||||||
|
// On a zoomed out page the resulting offset may differ
|
||||||
|
oldValue = this.elementRef.nativeElement[target];
|
||||||
|
oldTimestamp = newTimestamp;
|
||||||
|
|
||||||
|
this._animation = window.requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.requestAnimationFrame(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
export class FusePerfectScrollbarGeometry {
|
||||||
|
public x: number;
|
||||||
|
public y: number;
|
||||||
|
|
||||||
|
public w: number;
|
||||||
|
public h: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number, w: number, h: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.w = w;
|
||||||
|
this.h = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FusePerfectScrollbarPosition {
|
||||||
|
public x: number | 'start' | 'end';
|
||||||
|
public y: number | 'start' | 'end';
|
||||||
|
|
||||||
|
constructor(x: number | 'start' | 'end', y: number | 'start' | 'end') {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
31
src/@fuse/fuse.module.ts
Normal file
31
src/@fuse/fuse.module.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import {
|
||||||
|
ModuleWithProviders,
|
||||||
|
NgModule,
|
||||||
|
Optional,
|
||||||
|
SkipSelf
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { FUSE_CONFIG } from 'src/@fuse/services/config.service';
|
||||||
|
|
||||||
|
@NgModule()
|
||||||
|
export class FuseModule {
|
||||||
|
constructor(@Optional() @SkipSelf() parentModule: FuseModule) {
|
||||||
|
if (parentModule) {
|
||||||
|
throw new Error(
|
||||||
|
'FuseModule is already loaded. Import it in the AppModule only!'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static forRoot(config): ModuleWithProviders {
|
||||||
|
return {
|
||||||
|
ngModule: FuseModule,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: FUSE_CONFIG,
|
||||||
|
useValue: config
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user