Partidas Presupuestarias completo

parents
# 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
package.json
package-lock.json
dist
e2e/**
karma.conf.js
commitlint.config.js
{
"root": true,
"ignorePatterns": ["projects/**/*"],
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": ["tsconfig.json", "e2e/tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"plugin:prettier/recommended"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "vex",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "vex",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended"],
"rules": {}
},
{
"files": ["*.html"],
"excludedFiles": ["*inline-template-*.component.html"],
"extends": ["plugin:prettier/recommended"],
"rules": {
"prettier/prettier": [
"error",
{
"parser": "angular"
}
]
}
}
]
}
# 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
/.angular/cache
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
/ARCHIVE/*
package.json
package-lock.json
yarn.lock
dist
.angular
{
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"semi": true,
"bracketSpacing": true,
"arrowParens": "avoid",
"trailingComma": "none",
"bracketSameLine": true,
"printWidth": 80,
"endOfLine": "lf"
}
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"vex": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "vex",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/out",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets",
"src/_redirects",
"src/README.md",
"src/CHANGELOG.md",
"src/.htaccess",
{
"glob": "**/*.svg",
"input": "node_modules/@material-design-icons/svg",
"output": "assets/img/icons/material-design-icons/"
}
],
"styles": ["src/styles.scss", "src/@vex/styles/tailwind.scss"],
"scripts": [],
"allowedCommonJsDependencies": [
"simplebar",
"highlight.js",
"faker",
"showdown",
"core-js",
"dom-set",
"dom-plane",
"angular-calendar/date-adapters/date-fns",
"calendar-utils/date-adapters/date-fns",
"@mattlewis92/dom-autoscroller",
"apexcharts",
"can-use-dom",
"lodash.debounce",
"lodash.memoize",
"lodash.throttle",
"luxon",
"quill",
"fast-sha256",
"moment"
],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "3mb",
"maximumError": "5mb"
}
]
},
"test": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.test.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "3mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "vex:build"
},
"configurations": {
"production": {
"browserTarget": "vex:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "vex: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": []
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "vex:serve"
},
"configurations": {
"production": {
"devServerTarget": "vex:serve:production"
}
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
}
},
"cli": {
"analytics": false,
"schematicCollections": ["@angular-eslint/schematics"]
}
}
// @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 } }));
}
};
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 vex!');
});
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)
);
});
});
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>;
}
}
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": ["jasmine", "jasminewd2", "node"]
}
}
// 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/vex'),
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
});
};
This diff is collapsed.
{
"name": "vex",
"version": "1.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "NODE_ENV=production node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --configuration production",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"format": "prettier --write \"./src/**/*.{ts,json,html}\"",
"lint-format": "npm run format && ng lint --fix"
},
"private": true,
"dependencies": {
"@angular/animations": "^14.0.0",
"@angular/cdk": "^14.0.0",
"@angular/common": "^14.0.0",
"@angular/compiler": "^14.0.0",
"@angular/core": "^14.0.0",
"@angular/flex-layout": "^14.0.0-beta.41",
"@angular/forms": "^14.0.0",
"@angular/material": "^14.0.0",
"@angular/material-moment-adapter": "^14.2.6",
"@angular/platform-browser": "^14.0.0",
"@angular/platform-browser-dynamic": "^14.0.0",
"@angular/router": "^14.0.0",
"@material-design-icons/svg": "^0.11.1",
"@ngneat/hot-toast": "^4.1.0",
"@ngneat/overview": "^3.0.0",
"@ngneat/until-destroy": "~9.2.0",
"@ngrx/component": "^14.0.0",
"@ngx-loading-bar/core": "^6.0.2",
"@ngx-loading-bar/router": "^6.0.2",
"angular-calendar": "~0.29.0",
"angular-oauth2-oidc": "^13.0.1",
"apexcharts": "~3.35.3",
"cors": "^2.8.5",
"date-fns": "~2.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"highlight.js": "~11.5.1",
"luxon": "~2.3.0",
"material-icons": "^1.13.6",
"moment": "^2.29.4",
"ngx-quicklink": "~0.2.7",
"ngx-quill": "~17.0.0",
"ngx-showdown": "^6.0.0",
"prettier-eslint": "^15.0.1",
"quill": "~1.3.7",
"rxjs": "^7.4.0",
"save-dev": "^0.0.1-security",
"showdown": "^1.9.1",
"simplebar": "~5.3.8",
"tailwindcss": "^3.0.24",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.0.0",
"@angular-eslint/builder": "14.1.2",
"@angular-eslint/eslint-plugin": "14.1.2",
"@angular-eslint/eslint-plugin-template": "14.1.2",
"@angular-eslint/schematics": "14.1.2",
"@angular-eslint/template-parser": "14.1.2",
"@angular/cli": "^14.0.0",
"@angular/compiler-cli": "^14.0.0",
"@angular/language-service": "^14.0.0",
"@faker-js/faker": "^7.1.0",
"@types/jasmine": "^3.6.11",
"@types/jasminewd2": "^2.0.9",
"@types/luxon": "^2.3.2",
"@types/node": "^14.14.45",
"@types/showdown": "~1.9.4",
"@typescript-eslint/eslint-plugin": "5.37.0",
"@typescript-eslint/parser": "5.37.0",
"autoprefixer": "^10.4.0",
"eslint": "^8.23.1",
"husky": "^8.0.0",
"jasmine-core": "~3.10.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.4",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"postcss": "~8.4.14",
"postcss-scss": "~4.0.4",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"typescript": "~4.7.3"
}
}
RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
import {
animate,
state,
style,
transition,
trigger
} from '@angular/animations';
export const dropdownAnimation = trigger('dropdown', [
state(
'false',
style({
height: 0,
opacity: 0
})
),
state(
'true',
style({
height: '*',
opacity: 1
})
),
transition('false <=> true', animate('300ms cubic-bezier(.35, 0, .25, 1)'))
]);
import { animate, style, transition, trigger } from '@angular/animations';
export function fadeInRightAnimation(duration: number) {
return trigger('fadeInRight', [
transition(':enter', [
style({
transform: 'translateX(-20px)',
opacity: 0
}),
animate(
`${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`,
style({
transform: 'translateX(0)',
opacity: 1
})
)
])
]);
}
export const fadeInRight400ms = fadeInRightAnimation(400);
import { animate, style, transition, trigger } from '@angular/animations';
export function fadeInUpAnimation(duration: number) {
return trigger('fadeInUp', [
transition(':enter', [
style({
transform: 'translateY(20px)',
opacity: 0
}),
animate(
`${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`,
style({
transform: 'translateY(0)',
opacity: 1
})
)
])
]);
}
export const fadeInUp400ms = fadeInUpAnimation(400);
import {
animate,
group,
style,
transition,
trigger
} from '@angular/animations';
export const popoverAnimation = trigger('transformPopover', [
transition(':enter', [
style({
opacity: 0,
transform: 'scale(0.6)'
}),
group([
animate(
'100ms linear',
style({
opacity: 1
})
),
animate(
'150ms cubic-bezier(0, 0, 0.2, 1)',
style({
transform: 'scale(1)'
})
)
])
]),
transition(':leave', [
style({
opacity: 1
}),
animate(
'100ms linear',
style({
opacity: 0
})
)
])
]);
import { animate, style, transition, trigger } from '@angular/animations';
export function scaleFadeInAnimation(duration: number) {
return trigger('scaleFadeIn', [
transition(':enter', [
style({
transform: 'scale(0.8)',
opacity: 0
}),
animate(
`${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`,
style({
transform: 'scale(1)',
opacity: 1
})
)
])
]);
}
export const scaleFadeIn400ms = scaleFadeInAnimation(400);
import { animate, style, transition, trigger } from '@angular/animations';
export const scaleInOutAnimation = trigger('scaleInOut', [
transition(':enter', [
style({
transform: 'scale(0)',
opacity: 0
}),
animate(
'0.2s cubic-bezier(0.35, 0, 0.25, 1)',
style({
transform: 'scale(1)',
opacity: 1
})
)
]),
transition(':leave', [
style({
transform: 'scale(1)',
opacity: 1
}),
animate(
'0.2s cubic-bezier(0.35, 0, 0.25, 1)',
style({
transform: 'scale(0)',
opacity: 0
})
)
])
]);
import { animate, style, transition, trigger } from '@angular/animations';
export function scaleInAnimation(duration: number) {
return trigger('scaleIn', [
transition(':enter', [
style({
transform: 'scale(0)'
}),
animate(
`${duration}ms cubic-bezier(0.35, 0, 0.25, 1)`,
style({
transform: 'scale(1)'
})
)
])
]);
}
export const scaleIn400ms = scaleInAnimation(400);
import {
animateChild,
query,
stagger,
transition,
trigger
} from '@angular/animations';
export function staggerAnimation(timing: number) {
return trigger('stagger', [
transition('* => *', [
// each time the binding value changes
query(':enter', stagger(timing, animateChild()), { optional: true })
])
]);
}
export const stagger80ms = staggerAnimation(80);
export const stagger60ms = staggerAnimation(60);
export const stagger40ms = staggerAnimation(40);
export const stagger20ms = staggerAnimation(20);
import { Component, HostBinding } from '@angular/core';
@Component({
selector: 'vex-breadcrumb',
template: ` <ng-content></ng-content> `,
styles: []
})
export class BreadcrumbComponent {
@HostBinding('class') class =
'vex-breadcrumb body-2 text-hint leading-none hover:text-primary no-underline trans-ease-out ltr:mr-2 rtl:ml-2';
constructor() {}
}
import { Component, OnDestroy } from '@angular/core';
import { Breadcrumb } from 'src/@vex/interfaces/breadcrumb.interface';
import { Subscription } from 'rxjs';
import { BreadcrumbService } from 'src/app/core/services/breadcrumb.service';
@Component({
selector: 'vex-breadcrumbs',
template: `
<div class="flex items-center">
<vex-breadcrumb>
<a [routerLink]="['/']">
<mat-icon svgIcon="mat:home" class="icon-sm"></mat-icon>
</a>
</vex-breadcrumb>
<ng-container *ngFor="let crumb of crumbs">
<div class="w-1 h-1 bg-gray rounded-full ltr:mr-2 rtl:ml-2"></div>
<vex-breadcrumb>
<a *ngIf="crumb.routerLink" [routerLink]="crumb.routerLink">
{{ crumb.label }}
</a>
<ng-container *ngIf="!crumb.routerLink">
{{ crumb.label }}
</ng-container>
</vex-breadcrumb>
</ng-container>
</div>
`
})
export class BreadcrumbsComponent implements OnDestroy {
subscription: Subscription;
crumbs: Breadcrumb[] = [];
constructor(public breadcrumbService: BreadcrumbService) {
this.subscription = breadcrumbService.itemsHandler.subscribe(response => {
this.crumbs = response;
});
}
ngOnDestroy() {
if (this.subscription) {
//this.subscription.unsubscribe();
}
}
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BreadcrumbsComponent } from './breadcrumbs.component';
import { MatIconModule } from '@angular/material/icon';
import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
@NgModule({
imports: [CommonModule, RouterModule, MatIconModule],
declarations: [BreadcrumbsComponent, BreadcrumbComponent],
exports: [BreadcrumbsComponent]
})
export class BreadcrumbsModule {}
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
Input,
NgZone,
OnChanges,
OnInit,
SimpleChanges,
ViewChild
} from '@angular/core';
import { asapScheduler } from 'rxjs';
// @ts-ignore
import ApexCharts from 'apexcharts';
export interface ApexOptions {
annotations?: ApexAnnotations;
chart?: ApexChart;
colors?: any[];
dataLabels?: ApexDataLabels;
fill?: ApexFill;
grid?: ApexGrid;
labels?: string[] | number[];
legend?: ApexLegend;
markers?: ApexMarkers;
noData?: ApexNoData;
plotOptions?: ApexPlotOptions;
responsive?: ApexResponsive[];
series?: ApexAxisChartSeries | ApexNonAxisChartSeries;
states?: ApexStates;
stroke?: ApexStroke;
subtitle?: ApexTitleSubtitle;
theme?: ApexTheme;
title?: ApexTitleSubtitle;
tooltip?: ApexTooltip;
xaxis?: ApexXAxis;
yaxis?: ApexYAxis | ApexYAxis[];
}
@Component({
selector: 'vex-chart',
template: ` <div #chart></div> `,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChartComponent implements OnInit, OnChanges {
@Input() options: ApexOptions;
@Input() series: ApexAxisChartSeries | ApexNonAxisChartSeries;
@Input() autoUpdateSeries = true;
public chart: ApexCharts;
@ViewChild('chart', { static: true }) private chartElement: ElementRef;
constructor(private cd: ChangeDetectorRef, private ngZone: NgZone) {}
ngOnInit() {
asapScheduler.schedule(() => {
this._createElement();
});
}
ngOnChanges(changes: SimpleChanges): void {
asapScheduler.schedule(() => {
if (
this.autoUpdateSeries &&
Object.keys(changes).filter(c => c !== 'series').length === 0
) {
this.chart.updateSeries(this.series, true);
return;
}
this._createElement();
});
}
public render(): Promise<void> {
return this.chart.render();
}
private _createElement() {
if (this.series) {
this.options.series = this.series;
}
if (this.chart) {
this.chart.destroy();
}
this.ngZone.runOutsideAngular(() => {
this.chart = new ApexCharts(
this.chartElement.nativeElement,
this.options
);
this.render();
});
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ChartComponent } from './chart.component';
@NgModule({
declarations: [ChartComponent],
imports: [CommonModule],
exports: [ChartComponent]
})
export class ChartModule {}
export interface ColorVariable {
light: string;
default: string;
contrast: string;
}
export const colorVariables: Record<string, ColorVariable> = {
blue: {
light: 'rgba(99, 102, 241, .1)',
default: 'rgb(99, 102, 241)',
contrast: 'rgb(255, 255, 255)'
},
gray: {
light: 'rgba(158, 158, 158, 0.1)',
default: 'rgb(158, 158, 158)',
contrast: 'rgb(255, 255, 255)'
},
red: {
light: 'rgba(244, 67, 54, 0.1)',
default: 'rgb(244, 67, 54)',
contrast: 'rgb(255, 255, 255)'
},
orange: {
light: 'rgba(255, 152, 0, 0.1)',
default: 'rgb(255, 152, 0)',
contrast: 'rgb(0, 0, 0)'
},
'deep-orange': {
light: 'rgba(255, 87, 34, 0.1)',
default: 'rgb(255, 87, 34)',
contrast: 'rgb(255, 255, 255)'
},
amber: {
light: 'rgba(255, 193, 7, 0.1)',
default: 'rgb(255, 193, 7)',
contrast: 'rgb(0, 0, 0)'
},
green: {
light: 'rgba(76, 175, 80, 0.1)',
default: 'rgb(76, 175, 80)',
contrast: 'rgb(255, 255, 255)'
},
teal: {
light: 'rgba(0, 150, 136, 0.1)',
default: 'rgb(0, 150, 136)',
contrast: 'rgb(255, 255, 255)'
},
cyan: {
light: 'rgba(0, 188, 212, 0.1)',
default: 'rgb(0, 188, 212)',
contrast: 'rgb(255, 255, 255)'
},
purple: {
light: 'rgba(156, 39, 176, 0.1)',
default: 'rgb(156, 39, 176)',
contrast: 'rgb(255, 255, 255)'
},
'deep-purple': {
light: 'rgba(103, 58, 183, 0.1)',
default: 'rgb(103, 58, 183)',
contrast: 'rgb(255, 255, 255)'
},
pink: {
light: 'rgba(233, 30, 99, 0.1)',
default: 'rgb(233, 30, 99)',
contrast: 'rgb(255, 255, 255)'
},
espe: {
light: 'rgba(62, 111, 65, 0.1)',
default: 'rgb(62, 111, 65)',
contrast: 'rgb(255, 255, 255)'
}
};
<button
(click)="openConfig.emit()"
class="config-panel-toggle"
color="primary"
mat-fab
type="button">
<mat-icon svgIcon="mat:settings"></mat-icon>
</button>
.config-panel-toggle {
bottom: var(--padding);
position: fixed;
right: var(--padding);
z-index: 100;
}
::ng-deep [dir='rtl'] .config-panel-toggle {
left: var(--padding);
right: unset;
}
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'vex-config-panel-toggle',
templateUrl: './config-panel-toggle.component.html',
styleUrls: ['./config-panel-toggle.component.scss']
})
export class ConfigPanelToggleComponent {
@Output() openConfig = new EventEmitter();
constructor() {}
}
<div *ngIf="config$ | async as setting" class="config-panel">
<h2 class="headline mb-4">
<mat-icon class="mr-3" svgIcon="mat:settings"></mat-icon>
<span>Configuración</span>
</h2>
<div class="section">
<h5 class="subheading">ESTILOS</h5>
<div class="rounded-full mt-2 flex items-center cursor-pointer
relative bg-contrast-black text-contrast-white select-none" matRipple>
<div class="m-2 h-6 w-6 rounded-full">
<mat-icon svgIcon="mat:check"></mat-icon>
<mat-icon svgIcon="mat:close"></mat-icon>
</div>
<p class="ml-1 font-medium text-sm">Calificación Comisión</p>
<div class="rounded-full mt-2 flex items-center cursor-pointer
relative bg-contrast-black text-contrast-white select-none" matRipple>
<div class="m-2 h-6 w-6 rounded-full">
<mat-icon svgIcon="mat:check"></mat-icon>
<mat-icon svgIcon="mat:close"></mat-icon>
</div>
<p class="ml-1 font-medium text-sm">Configuraciones</p>
</div>
</div>
.config-panel {
padding: var(--padding-16) var(--padding);
}
.heading {
margin-bottom: var(--padding);
}
.section {
border-bottom: 1px solid var(--foreground-divider);
margin-bottom: var(--padding-16);
padding-bottom: var(--padding-16);
&:last-child {
border-bottom: none;
}
}
.section-content {
margin-inline-start: var(--padding-12);
.subheading {
margin-top: var(--padding);
}
}
.subheading {
@apply my-4 uppercase text-xs text-secondary font-medium;
}
.layout + .layout {
margin-top: var(--padding);
}
.layout-image {
&:hover {
.layout-image-overlay {
background: rgba(0, 0, 0, 0.7);
opacity: 1;
visibility: visible;
}
}
.layout-image-overlay {
border-radius: var(--border-radius);
bottom: 0;
height: 100%;
left: 0;
opacity: 0;
position: absolute;
right: 0;
top: 0;
transition: var(--trans-ease-out);
visibility: hidden;
width: 100%;
button {
padding: 0 8px;
}
}
}
.vex-color-picker {
user-select: none;
transition: var(--trans-ease-out);
&:hover,
&.selected {
background: currentColor !important;
p,
div {
color: white;
}
}
p {
transition: var(--trans-ease-out);
}
}
.color {
align-items: center;
border-radius: 50%;
box-shadow: var(--elevation-z8);
display: flex;
flex-direction: row;
height: 36px;
justify-content: center;
margin-inline-end: var(--padding-16);
text-align: center;
vertical-align: middle;
width: 36px;
&.light {
background: white;
color: #000;
}
&.dark {
background: #303030;
color: white;
}
&.flat {
background: #f5f5f5;
color: #000;
}
}
mat-slide-toggle + mat-slide-toggle,
mat-slide-toggle + mat-checkbox,
mat-checkbox + mat-slide-toggle,
mat-checkbox + mat-checkbox {
display: block;
margin-top: var(--padding-12);
}
.style-name {
font: var(--font-body-2);
}
import { Component, Inject } from '@angular/core';
import { ConfigService } from '../../config/config.service';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { map } from 'rxjs/operators';
import { LayoutService } from '../../services/layout.service';
import { MatRadioChange } from '@angular/material/radio';
import { VexConfigName } from '../../config/config-name.model';
import { ColorVariable, colorVariables } from './color-variables';
import { DOCUMENT } from '@angular/common';
import { ColorSchemeName } from '../../config/colorSchemeName';
import { Observable } from 'rxjs';
import { VexConfig } from '../../config/vex-config.interface';
import { CSSValue } from '../../interfaces/css-value.type';
import { isNil } from '../../utils/isNil';
import { defaultRoundedButtonBorderRadius } from '../../config/constants';
@Component({
selector: 'vex-config-panel',
templateUrl: './config-panel.component.html',
styleUrls: ['./config-panel.component.scss']
})
export class ConfigPanelComponent {
configs: VexConfig[] = this.configService.configs;
colorVariables: Record<string, ColorVariable> = colorVariables;
config$: Observable<VexConfig> = this.configService.config$;
isRTL$: Observable<boolean> = this.config$.pipe(
map(config => config.direction === 'rtl')
);
isDark$: Observable<boolean> = this.config$.pipe(
map(config => config.style.colorScheme === ColorSchemeName.dark)
);
borderRadius$: Observable<number> = this.config$.pipe(
map(config => config.style.borderRadius.value)
);
ConfigName = VexConfigName;
ColorSchemeName = ColorSchemeName;
selectedColor = colorVariables.blue;
roundedCornerValues: CSSValue[] = [
{
value: 0,
unit: 'rem'
},
{
value: 0.25,
unit: 'rem'
},
{
value: 0.5,
unit: 'rem'
},
{
value: 0.75,
unit: 'rem'
},
{
value: 1,
unit: 'rem'
},
{
value: 1.25,
unit: 'rem'
},
{
value: 1.5,
unit: 'rem'
},
{
value: 1.75,
unit: 'rem'
}
];
roundedButtonValue: CSSValue = defaultRoundedButtonBorderRadius;
constructor(
private configService: ConfigService,
private layoutService: LayoutService,
@Inject(DOCUMENT) private document: Document
) {}
setConfig(layout: VexConfigName, colorScheme: ColorSchemeName): void {
this.configService.setConfig(layout);
this.configService.updateConfig({
style: {
colorScheme
}
});
}
selectColor(color: ColorVariable): void {
this.selectedColor = color;
this.configService.updateConfig({
style: {
colors: {
primary: {
default: color.default,
contrast: color.contrast
}
}
}
});
}
isSelectedColor(color: ColorVariable): boolean {
return color === this.selectedColor;
}
enableDarkMode(): void {
this.configService.updateConfig({
style: {
colorScheme: ColorSchemeName.dark
}
});
}
disableDarkMode(): void {
this.configService.updateConfig({
style: {
colorScheme: ColorSchemeName.light
}
});
}
layoutRTLChange(change: MatSlideToggleChange): void {
this.configService.updateConfig({
direction: change.checked ? 'rtl' : 'ltr'
});
}
toolbarPositionChange(change: MatRadioChange): void {
this.configService.updateConfig({
toolbar: {
fixed: change.value === 'fixed'
}
});
}
footerVisibleChange(change: MatSlideToggleChange): void {
this.configService.updateConfig({
footer: {
visible: change.checked
}
});
}
footerPositionChange(change: MatRadioChange): void {
this.configService.updateConfig({
footer: {
fixed: change.value === 'fixed'
}
});
}
isSelectedBorderRadius(borderRadius: CSSValue, config: VexConfig): boolean {
return (
borderRadius.value === config.style.borderRadius.value &&
borderRadius.unit === config.style.borderRadius.unit
);
}
selectBorderRadius(borderRadius: CSSValue): void {
this.configService.updateConfig({
style: {
borderRadius: borderRadius
}
});
}
isSelectedButtonStyle(
buttonStyle: CSSValue | undefined,
config: VexConfig
): boolean {
if (isNil(config.style.button.borderRadius) && isNil(buttonStyle)) {
return true;
}
return buttonStyle?.value === config.style.button.borderRadius?.value;
}
selectButtonStyle(borderRadius: CSSValue | undefined): void {
this.configService.updateConfig({
style: {
button: {
borderRadius: borderRadius
}
}
});
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ConfigPanelComponent } from './config-panel.component';
import { ConfigPanelToggleComponent } from './config-panel-toggle/config-panel-toggle.component';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatRadioModule } from '@angular/material/radio';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatRippleModule } from '@angular/material/core';
import { ReactiveComponentModule } from '@ngrx/component';
import { MatSliderModule } from '@angular/material/slider';
@NgModule({
imports: [
CommonModule,
MatButtonModule,
MatIconModule,
MatRadioModule,
MatSlideToggleModule,
MatRippleModule,
ReactiveComponentModule,
MatSliderModule
],
declarations: [ConfigPanelComponent, ConfigPanelToggleComponent],
exports: [ConfigPanelComponent, ConfigPanelToggleComponent]
})
export class ConfigPanelModule {}
import {
Directive,
EventEmitter,
HostBinding,
Input,
NgZone,
OnChanges,
Output,
SimpleChanges
} from '@angular/core';
import { HighlightResult } from './highlight.model';
import { HighlightService } from './highlight.service';
@Directive({
selector: '[vexHighlight]'
})
export class HighlightDirective implements OnChanges {
@HostBinding('class.hljs') hljsWrapper = true;
@HostBinding('innerHTML') highlightedCode: string;
/** An optional array of language names and aliases restricting detection to only those languages.
* The subset can also be set with configure, but the local parameter overrides the option if set.
*/
@Input() languages: string[];
/** Highlight code input */
@Input('vexHighlight') code;
/** Stream that emits when code string is highlighted */
@Output() highlighted = new EventEmitter<HighlightResult>();
constructor(
private _highlightService: HighlightService,
private _zone: NgZone
) {}
ngOnChanges(changes: SimpleChanges) {
if (
changes.code &&
changes.code.currentValue !== changes.code.previousValue
) {
this.highlightElement(this.code, this.languages);
}
}
/**
* Highlighting with language detection and fix markup.
* @param code Accepts a string with the code to highlight
* @param languages An optional array of language names and aliases restricting detection to only those languages.
* The subset can also be set with configure, but the local parameter overrides the option if set.
*/
highlightElement(code: string, languages?: string[]) {
this._zone.runOutsideAngular(() => {
const res = this._highlightService.highlightAuto(code, languages);
this.highlightedCode = res.value;
this.highlighted.emit(res);
});
}
}
import { InjectionToken } from '@angular/core';
export const HIGHLIGHT_OPTIONS = new InjectionToken<HighlightOptions>(
'HIGHLIGHT_OPTIONS'
);
export interface HighlightOptions {
languages?: () => HighlightLanguage[];
config?: HighlightConfig;
}
export interface HighlightLanguage {
name: string;
func: () => any;
}
export interface HighlightConfig {
/** tabReplace: a string used to replace TAB characters in indentation. */
tabReplace?: string;
/** useBR: a flag to generate <br> tags instead of new-line characters in the output, useful when code is marked up using a non-<pre> container. */
useBR?: boolean;
/** classPrefix: a string prefix added before class names in the generated markup, used for backwards compatibility with stylesheets. */
classPrefix?: string;
/** languages: an array of language names and aliases restricting auto detection to only these languages. */
languages?: string[];
}
export interface HighlightResult {
language?: string;
r?: number;
second_best?: any;
top?: any;
value?: string;
}
import { NgModule } from '@angular/core';
import { HighlightDirective } from './highlight.directive';
import { HIGHLIGHT_OPTIONS, HighlightOptions } from './highlight.model';
/**
* Import every language you wish to highlight here
* NOTE: The name of each language must match the file name its imported from
*/
import xml from 'highlight.js/lib/languages/xml';
import scss from 'highlight.js/lib/languages/scss';
import typescript from 'highlight.js/lib/languages/typescript';
import { HighlightService } from './highlight.service';
/**
* Import every language you wish to highlight here
* NOTE: The name of each language must match the file name its imported from
*/
export function hljsLanguages() {
return [
{ name: 'typescript', func: typescript },
{ name: 'scss', func: scss },
{ name: 'xml', func: xml }
];
}
@NgModule({
providers: [
{
provide: HIGHLIGHT_OPTIONS,
useValue: {
languages: hljsLanguages
} as HighlightOptions
},
HighlightService
],
declarations: [HighlightDirective],
imports: [],
exports: [HighlightDirective]
})
export class HighlightModule {}
import { Inject, Injectable } from '@angular/core';
import {
HIGHLIGHT_OPTIONS,
HighlightConfig,
HighlightLanguage,
HighlightOptions,
HighlightResult
} from './highlight.model';
import hljs from 'highlight.js';
@Injectable()
export class HighlightService {
constructor(@Inject(HIGHLIGHT_OPTIONS) options: HighlightOptions) {
if (options) {
// Register HighlightJS languages
options
.languages()
.map((language: HighlightLanguage) =>
this.registerLanguage(language.name, language.func)
);
if (options.config) {
// Set global config if present
this.configure(options.config);
}
}
// Throw an error if no languages were registered.
if (this.listLanguages().length < 1) {
throw new Error('[HighlightJS]: No languages were registered!');
}
}
/**
* Core highlighting function.
* @param name Accepts a language name, or an alias
* @param value A string with the code to highlight.
* @param ignoreIllegals When present and evaluates to a true value, forces highlighting to finish
* even in case of detecting illegal syntax for the language instead of throwing an exception.
* When present, the function will restart parsing from this state instead of initializing a new one
*/
highlight(
name: string,
value: string,
ignoreIllegals: boolean
): HighlightResult {
return hljs.highlight(name, value, ignoreIllegals);
}
/**
* Highlighting with language detection.
* @param value Accepts a string with the code to highlight
* @param languageSubset An optional array of language names and aliases restricting detection to only those languages.
* The subset can also be set with configure, but the local parameter overrides the option if set.
*/
highlightAuto(value: string, languageSubset: string[]): HighlightResult {
return hljs.highlightAuto(value, languageSubset);
}
/**
* Applies highlighting to a DOM node containing code.
* The function uses language detection by default but you can specify the language in the class attribute of the DOM node.
* See the class reference for all available language names and aliases.
* @param block The element to apply highlight on.
*/
highlightBlock(block: HTMLElement) {
hljs.highlightBlock(block);
}
/**
* Configures global options:
*/
configure(config: HighlightConfig) {
hljs.configure(config);
}
/**
* Applies highlighting to all <pre><code>..</code></pre> blocks on a page.
*/
initHighlighting() {
hljs.initHighlighting();
}
/**
* Adds new language to the library under the specified name. Used mostly internally.
* @param name A string with the name of the language being registered
* @param language A function that returns an object which represents the language definition.
* The function is passed the hljs object to be able to use common regular expressions defined within it.
*/
registerLanguage(name: string, language: any) {
hljs.registerLanguage(name, language);
}
/**
* @return The languages names list.
*/
listLanguages(): string[] {
return hljs.listLanguages();
}
/**
* Looks up a language by name or alias.
* @param name Language name
* @return The language object if found, undefined otherwise.
*/
getLanguage(name: string): any {
return hljs.getLanguage(name);
}
}
<div class="card overflow-auto flex flex-col md:flex-row sm:mr-6">
<div class="bg-primary/10 p-gutter flex-auto max-w-[300px]">
<h2 class="headline mb-4 text-primary">Mega Menu</h2>
<p class="caption">
Far far away, behind the word mountains, far from the countries Vokalia
and Consonantia, there live the blind texts.
</p>
<p class="caption">
Separated they live in Bookmarksgrove right at the coast of the Semantics,
a large language ocean.
</p>
<button class="mt-4 w-full" color="primary" mat-flat-button type="button">
Learn More
</button>
</div>
<div class="p-gutter flex-auto max-w-[400px]">
<h3 class="body-2 m-0">Features</h3>
<div class="mt-4 grid grid-cols-3">
<a
(click)="close()"
*ngFor="let feature of features"
[routerLink]="feature.route"
class="text-dark p-3 text-center hover:bg-primary/10 hover:text-primary trans-ease-out rounded block no-underline">
<mat-icon
[svgIcon]="feature.icon"
class="icon-xl"
color="primary"></mat-icon>
<div class="body-2 mt-2">{{ feature.label }}</div>
</a>
</div>
</div>
<div class="p-gutter flex-auto max-w-[350px]">
<h3 class="body-2 m-0">Pages</h3>
<div class="mt-6 grid grid-cols-2 gap-x-12 gap-y-4">
<a
(click)="close()"
*ngFor="let page of pages"
[routerLink]="page.route"
class="text-dark body-1 no-underline hover:text-primary trans-ease-out"
>{{ page.label }}</a
>
</div>
</div>
</div>
import { Component } from '@angular/core';
import { PopoverRef } from '../popover/popover-ref';
export interface MegaMenuFeature {
icon: string;
label: string;
route: string;
}
export interface MegaMenuPage {
label: string;
route: string;
}
@Component({
selector: 'vex-mega-menu',
templateUrl: './mega-menu.component.html'
})
export class MegaMenuComponent {
features: MegaMenuFeature[] = [
{
icon: 'mat:layers',
label: 'Dashboard',
route: '/'
},
{
icon: 'mat:assignment',
label: 'AIO-Table',
route: '/apps/aio-table'
},
{
icon: 'mat:contact_support',
label: 'Help Center',
route: '/apps/help-center'
},
{
icon: 'mat:contacts',
label: 'Contacts',
route: '/apps/contacts/grid'
},
{
icon: 'mat:assessment',
label: 'Scrumboard',
route: '/apps/scrumboard/1'
},
{
icon: 'mat:book',
label: 'Documentation',
route: '/documentation'
}
];
pages: MegaMenuPage[] = [
{
label: 'All-In-One Table',
route: '/apps/aio-table'
},
{
label: 'Authentication',
route: '/login'
},
{
label: 'Components',
route: '/ui/components/overview'
},
{
label: 'Documentation',
route: '/documentation'
},
{
label: 'FAQ',
route: '/pages/faq'
},
{
label: 'Form Elements',
route: '/ui/forms/form-elements'
},
{
label: 'Form Wizard',
route: '/ui/forms/form-wizard'
},
{
label: 'Guides',
route: '/pages/guides'
},
{
label: 'Help Center',
route: '/apps/help-center'
},
{
label: 'Scrumboard',
route: '/apps/scrumboard'
}
];
constructor(private popoverRef: PopoverRef<MegaMenuComponent>) {}
close() {
this.popoverRef.close();
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MegaMenuComponent } from './mega-menu.component';
import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
@NgModule({
declarations: [MegaMenuComponent],
imports: [CommonModule, MatButtonModule, RouterModule, MatIconModule],
exports: [MegaMenuComponent]
})
export class MegaMenuModule {}
<a
*ngIf="isLink(item) && !isFunction(item.route)"
[class.hover:bg-hover]="!(isActive$ | async)(item)"
[ngClass]="{
'bg-primary text-primary-contrast': (isActive$ | async)(item),
'navigation-color': !(isActive$ | async)(item)
}"
[routerLink]="item.route"
class="navigation-item"
matRipple>
{{ item.label }}
</a>
<div
(click)="item.route()"
*ngIf="isLink(item) && isFunction(item.route)"
[class.hover:bg-hover]="!(isActive$ | async)(item)"
class="navigation-item navigation-color"
matRipple>
{{ item.label }}
</div>
<ng-container
*ngIf="(isSubheading(item) && item.children?.length > 0) || isDropdown(item)">
<div
[class.hover:bg-hover]="!(isActive$ | async)(item)"
[matMenuTriggerFor]="menu"
[ngClass]="{
'bg-primary text-primary-contrast': (isActive$ | async)(item),
'navigation-color': !(isActive$ | async)(item)
}"
class="navigation-item"
matRipple>
{{ item.label }}
</div>
<mat-menu #menu="matMenu" yPosition="below">
<ng-container *ngFor="let child of item.children">
<a
*ngIf="isLink(child) && !isFunction(child.route)"
[routerLink]="child.route"
[ngClass]="{ 'text-primary': (isActive$ | async)(child) }"
class="navigation-menu-item"
mat-menu-item>
<mat-icon [svgIcon]="child.icon" class="text-current"></mat-icon>
<span>{{ child.label }}</span>
</a>
<div
(click)="child.route()"
*ngIf="isLink(child) && isFunction(child.route)"
class="navigation-menu-item"
mat-menu-item>
<mat-icon [svgIcon]="child.icon" class="text-current"></mat-icon>
<span>{{ child.label }}</span>
</div>
<ng-container *ngIf="isDropdown(child)">
<button
[matMenuTriggerFor]="level1"
[ngClass]="{ 'text-primary': (isActive$ | async)(child) }"
class="navigation-menu-item"
mat-menu-item>
<mat-icon [svgIcon]="child.icon" class="text-current"></mat-icon>
<span>{{ child.label }}</span>
</button>
<mat-menu #level1="matMenu" yPosition="below">
<ng-container *ngFor="let item of child.children">
<ng-container
[ngTemplateOutletContext]="{ item: item }"
[ngTemplateOutlet]="linkTemplate"></ng-container>
<ng-container *ngIf="isDropdown(item)">
<button
[matMenuTriggerFor]="level2"
class="navigation-menu-item"
[ngClass]="{ 'text-primary': (isActive$ | async)(item) }"
mat-menu-item>
{{ item.label }}
</button>
<mat-menu #level2="matMenu" yPosition="below">
<ng-container *ngFor="let child of item.children">
<ng-container
[ngTemplateOutletContext]="{ item: child }"
[ngTemplateOutlet]="linkTemplate"></ng-container>
<ng-container *ngIf="isDropdown(child)">
<button
[matMenuTriggerFor]="level3"
class="navigation-menu-item"
[ngClass]="{ 'text-primary': (isActive$ | async)(child) }"
mat-menu-item>
{{ child.label }}
</button>
<mat-menu #level3="matMenu" yPosition="below">
<ng-container *ngFor="let item of child.children">
<ng-container
[ngTemplateOutletContext]="{ item: item }"
[ngTemplateOutlet]="linkTemplate"></ng-container>
<ng-container *ngIf="isDropdown(item)">
<button
[matMenuTriggerFor]="level4"
class="navigation-menu-item"
[ngClass]="{
'text-primary': (isActive$ | async)(item)
}"
mat-menu-item>
{{ item.label }}
</button>
<mat-menu #level4="matMenu" yPosition="below">
<ng-container *ngFor="let child of item.children">
<ng-container
[ngTemplateOutletContext]="{ item: child }"
[ngTemplateOutlet]="
linkTemplate
"></ng-container>
<ng-container *ngIf="isDropdown(child)">
<button
[matMenuTriggerFor]="level5"
class="navigation-menu-item"
[ngClass]="{
'text-primary': (isActive$ | async)(child)
}"
mat-menu-item>
{{ child.label }}
</button>
<mat-menu #level5="matMenu" yPosition="below">
<ng-container
*ngFor="let item of child.children">
<ng-container
[ngTemplateOutletContext]="{ item: item }"
[ngTemplateOutlet]="
linkTemplate
"></ng-container>
</ng-container>
</mat-menu>
</ng-container>
</ng-container>
</mat-menu>
</ng-container>
</ng-container>
</mat-menu>
</ng-container>
</ng-container>
</mat-menu>
</ng-container>
</ng-container>
</mat-menu>
</ng-container>
</ng-container>
</mat-menu>
<ng-template #linkTemplate let-item="item">
<a
*ngIf="isLink(item) && !isFunction(item.route)"
[routerLink]="item.route"
class="navigation-menu-item"
[ngClass]="{ 'text-primary': (isActive$ | async)(item) }"
mat-menu-item
>{{ item.label }}</a
>
<div
(click)="item.route()"
*ngIf="isLink(item) && isFunction(item.route)"
class="navigation-menu-item"
[ngClass]="{ 'text-primary': (isActive$ | async)(item) }"
mat-menu-item>
{{ item.label }}
</div>
</ng-template>
</ng-container>
.navigation-item {
@apply rounded cursor-pointer text-sm font-medium px-4 py-2 relative select-none no-underline block;
margin-inline-end: var(--padding-8);
transition: var(--trans-ease-out);
}
.navigation-color {
color: var(--navigation-color);
}
.navigation-menu-item {
transition: var(--trans-ease-out);
&:hover {
@apply text-primary;
.mat-icon {
@apply text-primary;
}
}
}
import { Component, Input } from '@angular/core';
import {
NavigationItem,
NavigationLink
} from '../../interfaces/navigation-item.interface';
import { filter, map, startWith } from 'rxjs/operators';
import { NavigationEnd, Router } from '@angular/router';
import { NavigationService } from '../../services/navigation.service';
import { trackByRoute } from '../../utils/track-by';
@Component({
selector: 'vex-navigation-item',
templateUrl: './navigation-item.component.html',
styleUrls: ['./navigation-item.component.scss']
})
export class NavigationItemComponent {
@Input() item: NavigationItem;
isActive$ = this.router.events.pipe(
filter(event => event instanceof NavigationEnd),
startWith(null),
map(() => (item: NavigationItem) => this.hasActiveChilds(item))
);
isLink = this.navigationService.isLink;
isDropdown = this.navigationService.isDropdown;
isSubheading = this.navigationService.isSubheading;
trackByRoute = trackByRoute;
constructor(
private navigationService: NavigationService,
private router: Router
) {}
hasActiveChilds(parent: NavigationItem): boolean {
if (this.isLink(parent)) {
return this.router.isActive(parent.route as string, true);
}
if (this.isDropdown(parent) || this.isSubheading(parent)) {
return parent.children.some(child => {
if (this.isDropdown(child)) {
return this.hasActiveChilds(child);
}
if (this.isLink(child) && !this.isFunction(child.route)) {
return this.router.isActive(child.route as string, true);
}
return false;
});
}
return false;
}
isFunction(prop: NavigationLink['route']) {
return prop instanceof Function;
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NavigationItemComponent } from './navigation-item.component';
import { MatMenuModule } from '@angular/material/menu';
import { MatIconModule } from '@angular/material/icon';
import { RouterModule } from '@angular/router';
import { MatRippleModule } from '@angular/material/core';
@NgModule({
declarations: [NavigationItemComponent],
imports: [
CommonModule,
MatMenuModule,
MatIconModule,
RouterModule,
MatRippleModule
],
exports: [NavigationItemComponent]
})
export class NavigationItemModule {}
import { Directive, HostBinding } from '@angular/core';
@Directive({
selector: '[vexPageLayoutContent],vex-page-layout-content'
})
export class PageLayoutContentDirective {
@HostBinding('class') class = 'vex-page-layout-content';
constructor() {}
}
import { Directive, HostBinding } from '@angular/core';
@Directive({
selector: '[vexPageLayoutHeader],vex-page-layout-header'
})
export class PageLayoutHeaderDirective {
@HostBinding('class') class = 'vex-page-layout-header';
constructor() {}
}
.vex-page-layout {
display: block;
}
.vex-page-layout-simple {
.vex-page-layout-content {
padding-bottom: var(--padding-gutter);
padding-top: var(--padding-gutter);
}
}
.vex-page-layout-card {
padding-bottom: var(--padding);
.vex-page-layout-header {
margin-bottom: calc(var(--page-layout-toolbar-height) * -1);
padding-bottom: var(--page-layout-toolbar-height);
}
.vex-page-layout-content {
> * > .mat-tab-group .mat-tab-label,
> .mat-tab-group .mat-tab-label {
height: var(--page-layout-toolbar-height);
&.mat-tab-label-active {
opacity: 1;
}
}
}
}
.vex-page-layout-header {
align-items: center;
@apply bg-primary/10;
box-sizing: content-box !important;
display: flex;
flex-direction: row;
height: calc(
var(--page-layout-header-height) - var(--page-layout-toolbar-height)
);
padding-left: var(--padding);
padding-right: var(--padding);
place-content: center flex-start;
}
.vex-page-layout-content {
box-sizing: border-box;
display: block;
padding-left: var(--padding-gutter);
padding-right: var(--padding-gutter);
}
import {
Component,
HostBinding,
Input,
ViewEncapsulation
} from '@angular/core';
@Component({
selector: 'vex-page-layout',
template: '<ng-content></ng-content>',
encapsulation: ViewEncapsulation.None,
styleUrls: ['./page-layout.component.scss']
})
export class PageLayoutComponent {
@Input() mode: 'card' | 'simple' = 'simple';
@HostBinding('class') class = 'vex-page-layout';
constructor() {}
@HostBinding('class.vex-page-layout-card')
get isCard() {
return this.mode === 'card';
}
@HostBinding('class.vex-page-layout-simple')
get isSimple() {
return this.mode === 'simple';
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PageLayoutComponent } from './page-layout.component';
import { PageLayoutHeaderDirective } from './page-layout-header.directive';
import { PageLayoutContentDirective } from './page-layout-content.directive';
@NgModule({
imports: [CommonModule],
declarations: [
PageLayoutComponent,
PageLayoutHeaderDirective,
PageLayoutContentDirective
],
exports: [
PageLayoutComponent,
PageLayoutHeaderDirective,
PageLayoutContentDirective
]
})
export class PageLayoutModule {}
import { OverlayRef } from '@angular/cdk/overlay';
import { Subject } from 'rxjs';
import { TemplateRef, Type } from '@angular/core';
export interface PopoverCloseEvent<T = any> {
type: 'backdropClick' | 'close';
data: T;
}
export type PopoverContent = TemplateRef<any> | Type<any> | string | any;
export class PopoverRef<T = any> {
private afterClosed = new Subject<PopoverCloseEvent<T>>();
afterClosed$ = this.afterClosed.asObservable();
constructor(
public overlay: OverlayRef,
public content: PopoverContent,
public data: T
) {
overlay.backdropClick().subscribe(() => {
this._close('backdropClick', null);
});
}
close(data?: T) {
this._close('close', data);
}
private _close(type: PopoverCloseEvent['type'], data?: T) {
this.overlay.dispose();
this.afterClosed.next({
type,
data
});
this.afterClosed.complete();
}
}
<div @transformPopover class="vex-popover">
<ng-container [ngSwitch]="renderMethod">
<div *ngSwitchCase="'text'" [innerHTML]="content"></div>
<ng-container *ngSwitchCase="'template'">
<ng-container
*ngTemplateOutlet="content; context: context"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'component'">
<ng-container *ngComponentOutlet="content"></ng-container>
</ng-container>
</ng-container>
</div>
:host,
.vex-popover {
width: 100%;
height: 100%;
}
import { Component, OnInit, TemplateRef } from '@angular/core';
import { PopoverContent, PopoverRef } from './popover-ref';
import { popoverAnimation } from '../../animations/popover.animation';
@Component({
selector: 'vex-popover',
templateUrl: './popover.component.html',
styleUrls: ['./popover.component.scss'],
animations: [popoverAnimation]
})
export class PopoverComponent implements OnInit {
renderMethod: 'template' | 'component' | 'text' = 'component';
content: PopoverContent;
context;
constructor(private popoverRef: PopoverRef) {}
ngOnInit() {
this.content = this.popoverRef.content;
if (typeof this.content === 'string') {
this.renderMethod = 'text';
}
if (this.content instanceof TemplateRef) {
this.renderMethod = 'template';
this.context = {
close: this.popoverRef.close.bind(this.popoverRef)
};
}
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PopoverComponent } from './popover.component';
import { OverlayModule } from '@angular/cdk/overlay';
@NgModule({
declarations: [PopoverComponent],
imports: [CommonModule, OverlayModule]
})
export class PopoverModule {}
import { ElementRef, Injectable, Injector } from '@angular/core';
import {
ConnectionPositionPair,
Overlay,
OverlayConfig,
PositionStrategy
} from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { PopoverContent, PopoverRef } from './popover-ref';
import { PopoverComponent } from './popover.component';
export interface PopoverParams<T> {
width?: string | number;
height?: string | number;
origin: ElementRef | HTMLElement;
position?: ConnectionPositionPair[];
content: PopoverContent;
data?: T;
offsetY?: number;
offsetX?: number;
}
@Injectable({
providedIn: 'root'
})
export class PopoverService {
constructor(private overlay: Overlay, private injector: Injector) {}
open<T>({
origin,
content,
data,
width,
height,
position,
offsetX,
offsetY
}: PopoverParams<T>): PopoverRef<T> {
const overlayRef = this.overlay.create(
this.getOverlayConfig({
origin,
width,
height,
position,
offsetX,
offsetY
})
);
const popoverRef = new PopoverRef<T>(overlayRef, content, data);
const injector = this.createInjector(popoverRef, this.injector);
overlayRef.attach(new ComponentPortal(PopoverComponent, null, injector));
return popoverRef;
}
private static getPositions(): ConnectionPositionPair[] {
return [
{
originX: 'center',
originY: 'top',
overlayX: 'center',
overlayY: 'bottom'
},
{
originX: 'center',
originY: 'bottom',
overlayX: 'center',
overlayY: 'top'
}
];
}
private getOverlayConfig({
origin,
width,
height,
position,
offsetX,
offsetY
}): OverlayConfig {
return new OverlayConfig({
hasBackdrop: true,
width,
height,
backdropClass: 'popover-backdrop',
positionStrategy: this.getOverlayPosition({
origin,
position,
offsetX,
offsetY
}),
scrollStrategy: this.overlay.scrollStrategies.reposition()
});
}
createInjector(popoverRef: PopoverRef, injector: Injector) {
return Injector.create({
providers: [
{
provide: PopoverRef,
useValue: popoverRef
}
],
parent: injector
});
}
private getOverlayPosition({
origin,
position,
offsetX,
offsetY
}): PositionStrategy {
return this.overlay
.position()
.flexibleConnectedTo(origin)
.withPositions(position || PopoverService.getPositions())
.withFlexibleDimensions(true)
.withDefaultOffsetY(offsetY || 0)
.withDefaultOffsetX(offsetX || 0)
.withTransformOriginOn('.vex-popover')
.withPush(true);
}
}
<mat-progress-bar
[class.visible]="(value$ | async) > 0 && (value$ | async) !== 100"
[value]="value$ | async"
class="progress-bar"
mode="determinate"></mat-progress-bar>
$transition-duration: 500ms;
$transition-delay: 200ms;
$transition-timing-function: var(--trans-ease-out-timing-function);
.progress-bar {
left: 0;
opacity: 0;
position: fixed;
right: 0;
top: 0;
visibility: hidden;
z-index: 99999;
transition: opacity $transition-duration $transition-timing-function
$transition-delay,
visibility 0ms $transition-timing-function
($transition-duration + $transition-delay);
&.visible {
opacity: 1;
visibility: visible;
transition: opacity $transition-duration $transition-timing-function,
visibility 0ms $transition-timing-function 0ms;
}
}
import { Component } from '@angular/core';
import { LoadingBarService } from '@ngx-loading-bar/core';
import { delayWhen, interval, Observable, of } from 'rxjs';
@Component({
selector: 'vex-progress-bar',
templateUrl: './progress-bar.component.html',
styleUrls: ['./progress-bar.component.scss']
})
export class ProgressBarComponent {
value$: Observable<number> = this.loader
.useRef('router')
.value$.pipe(
delayWhen(value => (value === 0 ? interval(200) : of(undefined)))
);
constructor(public loader: LoadingBarService) {}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProgressBarComponent } from './progress-bar.component';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import {
LOADING_BAR_CONFIG,
LoadingBarConfig,
LoadingBarModule
} from '@ngx-loading-bar/core';
import { LoadingBarRouterModule } from '@ngx-loading-bar/router';
@NgModule({
declarations: [ProgressBarComponent],
imports: [
CommonModule,
MatProgressBarModule,
LoadingBarModule,
LoadingBarRouterModule
],
exports: [ProgressBarComponent],
providers: [
{
provide: LOADING_BAR_CONFIG,
useValue: {
latencyThreshold: 80
} as LoadingBarConfig
}
]
})
export class ProgressBarModule {}
:host {
display: block;
min-height: 0;
}
import {
AfterContentInit,
Component,
ElementRef,
HostBinding,
Input,
NgZone,
OnDestroy
} from '@angular/core';
import SimpleBar from 'simplebar';
@Component({
selector: 'vex-scrollbar',
template: ` <ng-content></ng-content>`,
styleUrls: ['./scrollbar.component.scss']
})
export class ScrollbarComponent implements AfterContentInit, OnDestroy {
@Input() options: Partial<any>;
@HostBinding('class') class = 'vex-scrollbar';
scrollbarRef: SimpleBar;
constructor(private _element: ElementRef, private zone: NgZone) {}
ngAfterContentInit() {
this.zone.runOutsideAngular(() => {
this.scrollbarRef = new SimpleBar(
this._element.nativeElement,
this.options
);
});
}
ngOnDestroy(): void {
/**
* Exists, but not typed in the type definition
* https://github.com/Grsmto/simplebar/blob/master/packages/simplebar/src/simplebar.js#L903
*/
if (this.scrollbarRef && (this.scrollbarRef as any).unMount) {
(this.scrollbarRef as any).unMount();
}
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ScrollbarComponent } from './scrollbar.component';
@NgModule({
declarations: [ScrollbarComponent],
imports: [CommonModule],
exports: [ScrollbarComponent]
})
export class ScrollbarModule {}
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatRippleModule } from '@angular/material/core';
@Component({
standalone: true,
imports: [CommonModule, MatIconModule, MatButtonModule, MatRippleModule],
selector: 'vex-search-modal',
template: `
<div class="-m-6">
<div class="flex items-center gap-4 px-6 py-3 border-b border-divider">
<mat-icon
svgIcon="mat:search"
class="text-secondary flex-none"></mat-icon>
<input
type="text"
placeholder="Search..."
class="text-xl font-medium bg-transparent outline-none flex-auto placeholder-secondary" />
<button
class="flex-none ltr:-mr-2 rtl:-ml-2 text-secondary"
type="button"
mat-icon-button>
<mat-icon svgIcon="mat:settings"></mat-icon>
</button>
</div>
<div class="p-4">
<div class="text-xs font-semibold text-secondary px-2 mb-2">
Contacts
</div>
<div class="space-y-1">
<div
class="px-2 py-2 hover:bg-hover rounded transition duration-200 ease-out flex items-center gap-4 cursor-pointer select-none"
matRipple>
<img
src="assets/img/avatars/4.jpg"
class="w-8 h-8 rounded-full flex-none" />
<div class="flex-auto text-base font-medium">Alice Miller</div>
<div
class="flex-none text-xs text-secondary font-medium flex items-center gap-2">
<div>found in Contacts</div>
<mat-icon
svgIcon="mat:contacts"
class="icon-xs flex-none"></mat-icon>
</div>
<mat-icon
svgIcon="mat:chevron_right"
class="icon-sm flex-none"></mat-icon>
</div>
<div
class="px-2 py-2 hover:bg-hover rounded transition duration-200 ease-out flex items-center gap-4 cursor-pointer select-none"
matRipple>
<img
src="assets/img/avatars/3.jpg"
class="w-8 h-8 rounded-full flex-none" />
<div class="flex-auto text-base font-medium">Frank White</div>
<div
class="flex-none text-xs text-secondary font-medium flex items-center gap-2">
<div>found in Contacts</div>
<mat-icon
svgIcon="mat:contacts"
class="icon-xs flex-none"></mat-icon>
</div>
<mat-icon
svgIcon="mat:chevron_right"
class="icon-sm flex-none"></mat-icon>
</div>
</div>
</div>
<div class="p-4">
<div class="text-xs font-semibold text-secondary px-2 mb-2">Pages</div>
<div class="space-y-1">
<div
class="px-2 py-2 hover:bg-hover rounded transition duration-200 ease-out flex items-center gap-4 cursor-pointer select-none"
matRipple>
<div
class="flex items-center justify-center w-8 h-8 rounded-full bg-foreground/20">
<mat-icon svgIcon="mat:web" class="icon-sm flex-none"></mat-icon>
</div>
<div class="flex-auto text-base font-medium">
<div>Scrumboard</div>
<div class="text-secondary text-xs">/apps/scrumboard</div>
</div>
<mat-icon
svgIcon="mat:chevron_right"
class="icon-sm flex-none"></mat-icon>
</div>
<div
class="px-2 py-2 hover:bg-hover rounded transition duration-200 ease-out flex items-center gap-4 cursor-pointer select-none"
matRipple>
<div
class="flex items-center justify-center w-8 h-8 rounded-full bg-foreground/20">
<mat-icon svgIcon="mat:web" class="icon-sm flex-none"></mat-icon>
</div>
<div class="flex-auto text-base font-medium">
<div>Mailbox</div>
<div class="text-secondary text-xs">/apps/mailbox</div>
</div>
<mat-icon
svgIcon="mat:chevron_right"
class="icon-sm flex-none"></mat-icon>
</div>
</div>
</div>
<div class="p-4">
<div class="text-xs font-semibold text-secondary px-2 mb-2">Tasks</div>
<div class="space-y-1">
<div
class="px-2 py-2 hover:bg-hover rounded transition duration-200 ease-out flex items-center gap-4 cursor-pointer select-none"
matRipple>
<div
class="flex items-center justify-center w-8 h-8 rounded-full bg-foreground/20">
<mat-icon
svgIcon="mat:check"
class="icon-sm flex-none"
color="primary"></mat-icon>
</div>
<div class="flex-auto text-base font-medium">
Configure OrderController as defined in RVT-11
</div>
<mat-icon
svgIcon="mat:chevron_right"
class="icon-sm flex-none"></mat-icon>
</div>
<div
class="px-2 py-2 hover:bg-hover rounded transition duration-200 ease-out flex items-center gap-4 cursor-pointer select-none"
matRipple>
<div
class="flex items-center justify-center w-8 h-8 rounded-full bg-foreground/20">
<mat-icon
svgIcon="mat:check"
class="icon-sm flex-none"
color="primary"></mat-icon>
</div>
<div class="flex-auto text-base font-medium">
Add more data-models to product-controller
</div>
<mat-icon
svgIcon="mat:chevron_right"
class="icon-sm flex-none"></mat-icon>
</div>
</div>
</div>
</div>
`,
styles: [``]
})
export class SearchModalComponent {
constructor() {}
}
<div (keyup.escape)="close()" [class.show]="show$ | async" class="search">
<button
(click)="close()"
class="ltr:right-12 rtl:left-12 top-12 absolute"
color="primary"
mat-icon-button
type="button">
<mat-icon svgIcon="mat:close"></mat-icon>
</button>
<input
#searchInput
(keyup.enter)="search()"
[formControl]="searchCtrl"
class="search-input"
placeholder="Search..." />
<div class="search-hint">Hit enter to search</div>
</div>
<div (click)="close()" *ngIf="show$ | async" class="search-overlay"></div>
.search {
@apply flex flex-col items-center justify-center text-center fixed top-0 left-0 w-full opacity-0;
height: 50vh;
pointer-events: none;
transform: scale(0.75);
transition: all 0.25s cubic-bezier(0.2, 1, 0.3, 1);
z-index: 1050;
&.show {
opacity: 1;
pointer-events: auto;
transform: scale(1);
transition: all 0.5s cubic-bezier(0.2, 1, 0.3, 1);
}
}
.search-input {
border-bottom: 1px solid var(--foreground-divider);
font-size: 7vw;
line-height: 3rem;
width: 75%;
@apply m-0 rounded-none border-r-0 border-l-0 border-t-0 font-bold bg-transparent;
&:focus {
outline: none;
}
}
.search-hint {
width: 75%;
@apply text-base text-right py-4 mx-auto text-hint font-bold;
}
.search-overlay {
@apply fixed w-full bottom-0 left-0 opacity-0;
height: 50vh;
}
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { SearchComponent } from './search.component';
describe('SearchComponent', () => {
let component: SearchComponent;
let fixture: ComponentFixture<SearchComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [SearchComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {
Component,
ElementRef,
OnDestroy,
OnInit,
ViewChild
} from '@angular/core';
import { LayoutService } from '../../services/layout.service';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { filter } from 'rxjs/operators';
import { UntypedFormControl } from '@angular/forms';
import { SearchService } from '../../services/search.service';
@UntilDestroy()
@Component({
selector: 'vex-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit, OnDestroy {
show$ = this.layoutService.searchOpen$;
searchCtrl = new UntypedFormControl();
@ViewChild('searchInput', { static: true }) input: ElementRef;
constructor(
private layoutService: LayoutService,
private searchService: SearchService
) {}
ngOnInit() {
this.searchService.isOpenSubject.next(true);
this.searchCtrl.valueChanges
.pipe(untilDestroyed(this))
.subscribe(value => this.searchService.valueChangesSubject.next(value));
this.show$
.pipe(
filter(show => show),
untilDestroyed(this)
)
.subscribe(() => this.input.nativeElement.focus());
}
close() {
this.layoutService.closeSearch();
this.searchCtrl.setValue(undefined);
this.searchService.isOpenSubject.next(false);
}
search() {
this.searchService.submitSubject.next(this.searchCtrl.value);
this.close();
}
ngOnDestroy(): void {
this.layoutService.closeSearch();
this.searchCtrl.setValue(undefined);
this.searchService.isOpenSubject.next(false);
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SearchComponent } from './search.component';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [SearchComponent],
imports: [CommonModule, MatButtonModule, MatIconModule, ReactiveFormsModule],
exports: [SearchComponent]
})
export class SearchModule {}
.secondary-toolbar {
background: var(--secondary-toolbar-background);
height: var(--secondary-toolbar-height);
margin-top: calc(var(--secondary-toolbar-height) * -1);
&.fixed {
width: var(--toolbar-width);
}
}
.secondary-toolbar-placeholder {
height: var(--secondary-toolbar-height);
}
::ng-deep .is-mobile .fixed {
width: 100%;
}
::ng-deep body .fixed {
width: calc(100% - var(--sidenav-width));
}
::ng-deep {
vex-breadcrumbs {
@apply hidden;
}
@screen sm {
vex-breadcrumbs {
@apply block;
}
}
}
import { Component, Input } from '@angular/core';
import { ConfigService } from '../../config/config.service';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
@Component({
selector: 'vex-secondary-toolbar',
template: `
<div class="secondary-toolbar-placeholder">&nbsp;</div>
<div
[ngClass]="{
fixed: fixed$ | async,
'w-full': (fixed$ | async) === false
}"
class="secondary-toolbar shadow-b py-1 z-40 border-t flex">
<div
class="px-gutter flex items-center flex-auto"
[class.container]="isVerticalLayout$ | async">
<h1
*ngIf="current"
class="subheading-2 font-medium m-0 ltr:pr-3 rtl:pl-3 ltr:border-r rtl:border-l ltr:mr-3 rtl:ml-3 flex-none">
{{ current }}
</h1>
<ng-content></ng-content>
</div>
</div>
`,
styleUrls: ['./secondary-toolbar.component.scss']
})
export class SecondaryToolbarComponent {
@Input() current: string;
@Input() crumbs: string[];
fixed$ = this.configService.config$.pipe(map(config => config.toolbar.fixed));
isVerticalLayout$: Observable<boolean> = this.configService
.select(config => config.layout)
.pipe(map(layout => layout === 'vertical'));
constructor(private readonly configService: ConfigService) {}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SecondaryToolbarComponent } from './secondary-toolbar.component';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { BreadcrumbsModule } from '../breadcrumbs/breadcrumbs.module';
@NgModule({
declarations: [SecondaryToolbarComponent],
imports: [
CommonModule,
RouterModule,
MatButtonModule,
MatIconModule,
BreadcrumbsModule
],
exports: [SecondaryToolbarComponent]
})
export class SecondaryToolbarModule {}
<mat-nav-list>
<a (click)="close()" [routerLink]="[]" mat-list-item>
<mat-icon mat-list-icon svgIcon="logo:gmail"></mat-icon>
<span mat-line>Send with Gmail</span>
</a>
<a (click)="close()" [routerLink]="[]" mat-list-item>
<mat-icon mat-list-icon svgIcon="logo:facebook"></mat-icon>
<span mat-line>Post on Facebook</span>
</a>
<a (click)="close()" [routerLink]="[]" mat-list-item>
<mat-icon mat-list-icon svgIcon="logo:twitter"></mat-icon>
<span mat-line>Share on Twitter</span>
</a>
<a (click)="close()" [routerLink]="[]" mat-list-item>
<mat-icon mat-list-icon svgIcon="logo:whatsapp"></mat-icon>
<span mat-line>Share on WhatsApp</span>
</a>
</mat-nav-list>
import { Component } from '@angular/core';
import { MatBottomSheetRef } from '@angular/material/bottom-sheet';
@Component({
selector: 'vex-share-bottom-sheet',
templateUrl: './share-bottom-sheet.component.html',
styleUrls: ['./share-bottom-sheet.component.scss']
})
export class ShareBottomSheetComponent {
constructor(
private _bottomSheetRef: MatBottomSheetRef<ShareBottomSheetComponent>
) {}
close() {
this._bottomSheetRef.dismiss();
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ShareBottomSheetComponent } from './share-bottom-sheet.component';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
import { MatListModule } from '@angular/material/list';
import { RouterModule } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
@NgModule({
declarations: [ShareBottomSheetComponent],
imports: [
CommonModule,
MatBottomSheetModule,
MatListModule,
RouterModule,
MatIconModule
],
exports: [MatBottomSheetModule]
})
export class ShareBottomSheetModule {}
<div
(click)="close()"
[class.opaque]="invisibleBackdrop"
[class.show]="opened"
class="backdrop"></div>
<div
[class.open]="opened"
[class.position-left]="positionLeft"
[class.position-right]="positionRight"
class="sidebar">
<ng-content></ng-content>
</div>
.sidebar {
background: var(--background-foreground);
bottom: 0;
box-shadow: var(--elevation-z8);
display: flex;
flex: 1 0 auto;
flex-direction: column;
max-width: 80vw;
overflow-x: hidden;
overflow-y: auto;
position: fixed;
top: 0;
transition-duration: var(--trans-ease-in-duration);
transition-property: transform, visibility;
transition-timing-function: var(--trans-ease-in-timing-function);
visibility: hidden;
width: var(--sidenav-width);
z-index: 1000;
@screen sm {
max-width: unset;
}
&.position-left {
left: 0;
transform: translateX(-100%);
}
&.position-right {
right: 0;
transform: translateX(100%);
}
&.open {
transform: translateX(0);
visibility: visible;
}
}
.backdrop {
background-color: transparent;
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
transition-duration: 400ms;
transition-property: background-color, visibility;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
visibility: hidden;
z-index: 800 !important;
&.show {
background-color: rgba(0, 0, 0, 0.6);
visibility: visible;
}
&.opaque {
background-color: transparent;
}
}
import { Component, HostBinding, Inject, Input } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'vex-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss']
})
export class SidebarComponent {
@Input() position: 'left' | 'right' = 'left';
@Input() invisibleBackdrop: boolean;
@HostBinding('class') class = 'vex-sidebar';
constructor(@Inject(DOCUMENT) private document: Document) {}
private _opened: boolean;
get opened() {
return this._opened;
}
@Input() set opened(opened: boolean) {
this._opened = opened;
opened ? this.enableScrollblock() : this.disableScrollblock();
}
get positionLeft() {
return this.position === 'left';
}
get positionRight() {
return this.position === 'right';
}
enableScrollblock() {
if (!this.document.body.classList.contains('vex-scrollblock')) {
this.document.body.classList.add('vex-scrollblock');
}
}
disableScrollblock() {
if (this.document.body.classList.contains('vex-scrollblock')) {
this.document.body.classList.remove('vex-scrollblock');
}
}
open() {
this.opened = true;
}
close() {
this.opened = false;
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SidebarComponent } from './sidebar.component';
@NgModule({
imports: [CommonModule],
declarations: [SidebarComponent],
exports: [SidebarComponent]
})
export class SidebarModule {}
<div class="vex-user-menu">
<a
(click)="close()"
[routerLink]="['/apps/social']"
class="vex-user-menu-item"
matRipple
matRippleColor="rgb(var(--color-primary), 0.1)">
<mat-icon
class="vex-user-menu-item__icon icon-sm"
svgIcon="mat:account_circle"></mat-icon>
<div class="vex-user-menu-item__label">Your Profile</div>
</a>
<a
(click)="close()"
[routerLink]="['/']"
class="vex-user-menu-item"
matRipple
matRippleColor="rgb(var(--color-primary), 0.1)">
<mat-icon
class="vex-user-menu-item__icon icon-sm"
svgIcon="mat:insights"></mat-icon>
<div class="vex-user-menu-item__label">Analytics</div>
<div class="vex-user-menu-item__badge">NEW</div>
</a>
<a
(click)="close()"
[routerLink]="['/apps/social/timeline']"
class="vex-user-menu-item"
matRipple
matRippleColor="rgb(var(--color-primary), 0.1)">
<mat-icon
class="vex-user-menu-item__icon icon-sm"
svgIcon="mat:settings"></mat-icon>
<div class="vex-user-menu-item__label">Account Settings</div>
</a>
<div class="border-b border-divider mx-4"></div>
<div
class="vex-user-menu-item"
matRipple
matRippleColor="rgb(var(--color-primary), 0.1)">
<mat-icon
class="vex-user-menu-item__icon icon-sm"
svgIcon="mat:switch_account"></mat-icon>
<div class="vex-user-menu-item__label">Switch Account</div>
<mat-icon
class="vex-user-menu-item__icon icon-sm"
svgIcon="mat:chevron_right"></mat-icon>
</div>
<a
(click)="close()"
[routerLink]="['/login']"
class="vex-user-menu-item"
matRipple
matRippleColor="rgb(var(--color-primary), 0.1)">
<mat-icon
class="vex-user-menu-item__icon icon-sm"
svgIcon="mat:logout"></mat-icon>
<div class="vex-user-menu-item__label">Sign Out</div>
</a>
</div>
.vex-user-menu {
@apply bg-foreground shadow-lg border border-divider rounded py-2;
}
.vex-user-menu-item {
@apply relative flex items-center gap-4 px-2 mx-2 py-2 rounded hover:bg-primary/10 cursor-pointer transition duration-100 ease-out select-none h-10;
&:hover {
.vex-user-menu-item__icon {
@apply text-primary;
}
}
}
.vex-user-menu-item__label {
@apply flex-auto font-medium transition duration-100 ease-out;
}
.vex-user-menu-item__badge {
@apply px-2 py-1 rounded bg-primary/10 text-primary text-2xs font-semibold flex-none;
}
.vex-user-menu-item__icon {
@apply flex-none transition duration-100 ease-out;
}
.vex-user-menu-item__dropdown-icon {
}
import { Component } from '@angular/core';
import { PopoverRef } from '../popover/popover-ref';
@Component({
selector: 'vex-user-menu',
templateUrl: './user-menu.component.html',
styleUrls: ['./user-menu.component.scss']
})
export class UserMenuComponent {
constructor(private readonly popoverRef: PopoverRef) {}
close(): void {
/** Wait for animation to complete and then close */
setTimeout(() => this.popoverRef.close(), 250);
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserMenuComponent } from './user-menu.component';
import { MatRippleModule } from '@angular/material/core';
import { RouterModule } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
@NgModule({
declarations: [UserMenuComponent],
imports: [CommonModule, MatRippleModule, RouterModule, MatIconModule]
})
export class UserMenuModule {}
<div class="card bg-primary flex">
<div class="p-4 ml-4 flex-none self-center hidden sm:block">
<img
src="assets/img/illustrations/checklist.svg"
style="height: 168px; width: 152px" />
</div>
<div class="p-6 text-primary-contrast flex-auto">
<h2 class="headline m-0">Good Job, David!</h2>
<p class="caption m-0 opacity-50">
You've finished all of your tasks for this week.
</p>
<div class="mt-4 flex items-center">
<mat-icon class="mr-2" svgIcon="mat:check_circle"></mat-icon>
<span class="body-2 cursor-pointer">Finish Dashboard Design</span>
</div>
<div class="mt-1 flex items-center">
<mat-icon class="mr-2" svgIcon="mat:check_circle"></mat-icon>
<span class="body-2 cursor-pointer">Fix Issue #74</span>
</div>
<div class="mt-1 flex items-center">
<mat-icon class="mr-2" svgIcon="mat:check_circle"></mat-icon>
<span class="body-2 cursor-pointer">Publish version 2.3.3</span>
</div>
</div>
</div>
import { Component } from '@angular/core';
@Component({
selector: 'vex-widget-assistant',
templateUrl: './widget-assistant.component.html',
styleUrls: ['./widget-assistant.component.scss']
})
export class WidgetAssistantComponent {
constructor() {}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { WidgetAssistantComponent } from './widget-assistant.component';
import { MatIconModule } from '@angular/material/icon';
@NgModule({
declarations: [WidgetAssistantComponent],
imports: [CommonModule, MatIconModule],
exports: [WidgetAssistantComponent]
})
export class WidgetAssistantModule {}
<div class="card">
<div class="border-b py-4 px-6 flex items-center">
<h2 class="m-0 title flex-auto">User & Session Analytics</h2>
<button mat-icon-button type="button">
<mat-icon class="text-secondary" svgIcon="mat:cloud_download"></mat-icon>
</button>
<button mat-icon-button type="button">
<mat-icon class="text-secondary" svgIcon="mat:more_horiz"></mat-icon>
</button>
</div>
<div class="pt-3 pb-1 pr-6">
<vex-chart [options]="options" [series]="series"></vex-chart>
</div>
</div>
import { Component, Input } from '@angular/core';
import { ApexOptions } from '../../chart/chart.component';
import { defaultChartOptions } from '../../../utils/default-chart-options';
import { createDateArray } from '../../../utils/create-date-array';
@Component({
selector: 'vex-widget-large-chart',
templateUrl: './widget-large-chart.component.html',
styleUrls: ['./widget-large-chart.component.scss']
})
export class WidgetLargeChartComponent {
@Input() series: ApexNonAxisChartSeries | ApexAxisChartSeries;
@Input() options: ApexOptions = defaultChartOptions({
grid: {
show: true,
strokeDashArray: 3,
padding: {
left: 16
}
},
chart: {
type: 'area',
height: 384,
sparkline: {
enabled: false
},
zoom: {
enabled: false
}
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 0.9,
opacityFrom: 0.7,
opacityTo: 0.5,
stops: [0, 90, 100]
}
},
colors: ['#008ffb', '#ff9800'],
labels: createDateArray(12),
xaxis: {
type: 'datetime',
labels: {
show: true
}
},
yaxis: {
labels: {
show: true
}
},
legend: {
show: true,
itemMargin: {
horizontal: 4,
vertical: 4
}
}
});
constructor() {}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { WidgetLargeChartComponent } from './widget-large-chart.component';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { ChartModule } from '../../chart/chart.module';
@NgModule({
declarations: [WidgetLargeChartComponent],
imports: [CommonModule, MatIconModule, MatButtonModule, ChartModule],
exports: [WidgetLargeChartComponent]
})
export class WidgetLargeChartModule {}
<div class="card">
<div class="border-b py-4 px-6 flex items-center">
<h2 class="m-0 title flex-auto">Sales Overview</h2>
<button mat-icon-button type="button">
<mat-icon class="text-secondary" svgIcon="mat:cloud_download"></mat-icon>
</button>
<button mat-icon-button type="button">
<mat-icon class="text-secondary" svgIcon="mat:more_horiz"></mat-icon>
</button>
</div>
<div class="py-6 px-6 grid grid-cols-1 md:grid-cols-2 gap-12">
<div class="pb-6 pt-4 px-6 flex flex-col">
<h2 class="display-2 font-bold m-0">{{ total }}</h2>
<h3 class="title font-medium mt-2 mb-0">Sales this month</h3>
<div class="mt-6 flex items-center gap-4">
<div
class="w-8 h-8 rounded text-green bg-green-light flex-none flex items-center justify-center">
<mat-icon class="icon-sm" svgIcon="mat:arrow_drop_up"></mat-icon>
</div>
<p class="text-secondary m-0">
<span class="font-medium text-green">11% more sales</span> in
comparison to last month.
</p>
</div>
<div class="mt-4 flex items-center gap-4">
<div
class="w-8 h-8 rounded text-red bg-red-light flex-none flex items-center justify-center">
<mat-icon class="icon-sm" svgIcon="mat:arrow_drop_down"></mat-icon>
</div>
<p class="text-secondary m-0">
<span class="font-medium text-red">-2% revenue per sale</span> in
comparison to last month.
</p>
</div>
<div class="mt-6 flex-auto flex flex-col justify-end">
<button color="primary" mat-flat-button type="button">
View Details
</button>
</div>
</div>
<vex-chart [options]="options" [series]="series"></vex-chart>
</div>
</div>
import { Component, Input } from '@angular/core';
import { ApexOptions } from '../../chart/chart.component';
import { defaultChartOptions } from '../../../utils/default-chart-options';
import { createDateArray } from '../../../utils/create-date-array';
@Component({
selector: 'vex-widget-large-goal-chart',
templateUrl: './widget-large-goal-chart.component.html'
})
export class WidgetLargeGoalChartComponent {
@Input() total: string;
@Input() series: ApexNonAxisChartSeries | ApexAxisChartSeries;
@Input() options: ApexOptions = defaultChartOptions({
grid: {
show: true,
strokeDashArray: 3,
padding: {
left: 16
}
},
chart: {
type: 'line',
height: 300,
sparkline: {
enabled: false
},
zoom: {
enabled: false
}
},
stroke: {
width: 4
},
labels: createDateArray(12),
xaxis: {
type: 'datetime',
labels: {
show: true
}
},
yaxis: {
labels: {
show: true
}
}
});
constructor() {}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { WidgetLargeGoalChartComponent } from './widget-large-goal-chart.component';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ChartModule } from '../../chart/chart.module';
@NgModule({
declarations: [WidgetLargeGoalChartComponent],
imports: [CommonModule, MatButtonModule, MatIconModule, ChartModule],
exports: [WidgetLargeGoalChartComponent]
})
export class WidgetLargeGoalChartModule {}
<div
(mouseenter)="showButton = true"
(mouseleave)="showButton = false"
class="card relative hover:shadow-lg trans-shadow overflow-hidden">
<div class="p-6 flex items-center justify-between gap-6">
<div>
<h2 class="headline font-bold m-0">{{ value }}</h2>
<p class="caption text-secondary m-0">{{ label }}</p>
</div>
<div
[ngClass]="iconClass"
class="rounded-full w-12 h-12 flex items-center justify-center">
<mat-icon [svgIcon]="icon"></mat-icon>
</div>
</div>
<vex-chart [options]="options" [series]="series"></vex-chart>
<button
(click)="openSheet()"
*ngIf="showButton"
@scaleInOut
class="absolute -top-3 -right-3 bg-foreground shadow-xl"
color="primary"
mat-icon-button
type="button">
<mat-icon svgIcon="mat:share"></mat-icon>
</button>
</div>
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ApexOptions } from '../../chart/chart.component';
import { defaultChartOptions } from '../../../utils/default-chart-options';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { ShareBottomSheetComponent } from '../../share-bottom-sheet/share-bottom-sheet.component';
import { scaleInOutAnimation } from '../../../animations/scale-in-out.animation';
@Component({
selector: 'vex-widget-quick-line-chart',
templateUrl: './widget-quick-line-chart.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [scaleInOutAnimation]
})
export class WidgetQuickLineChartComponent {
@Input() icon: string;
@Input() value: string;
@Input() label: string;
@Input() iconClass: string;
@Input() options: ApexOptions = defaultChartOptions({
chart: {
type: 'area',
height: 100
}
});
@Input() series: ApexNonAxisChartSeries | ApexAxisChartSeries;
showButton: boolean;
constructor(private _bottomSheet: MatBottomSheet) {}
openSheet() {
this._bottomSheet.open(ShareBottomSheetComponent);
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { WidgetQuickLineChartComponent } from './widget-quick-line-chart.component';
import { ChartModule } from '../../chart/chart.module';
import { MatIconModule } from '@angular/material/icon';
import { ShareBottomSheetModule } from '../../share-bottom-sheet/share-bottom-sheet.module';
import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [WidgetQuickLineChartComponent],
imports: [
CommonModule,
ChartModule,
MatIconModule,
ShareBottomSheetModule,
MatButtonModule
],
exports: [WidgetQuickLineChartComponent]
})
export class WidgetQuickLineChartModule {}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment