From 53f8f0af2917cba0a4f4fe94b6bc1f44b6c4d962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Sat, 2 Oct 2021 15:44:19 +0300 Subject: [PATCH] Added some components & styles --- src/assets/icons/logout.svg | 14 ++++ src/assets/icons/menu.svg | 8 ++ src/assets/styles/common-components.scss | 90 +++++++++++++++++++++ src/assets/styles/common-styles.scss | 1 + src/lib/icons/icons.module.ts | 12 ++- src/lib/services/composite-route.guard.ts | 40 +++++++++ src/lib/services/dialog.service.ts | 56 +++++++++++++ src/lib/services/index.ts | 2 + src/lib/utils/functions.ts | 14 +++- src/lib/utils/index.ts | 1 + src/lib/utils/pruning-translation-loader.ts | 29 +++++++ 11 files changed, 261 insertions(+), 6 deletions(-) create mode 100755 src/assets/icons/logout.svg create mode 100644 src/assets/icons/menu.svg create mode 100644 src/assets/styles/common-components.scss create mode 100644 src/lib/services/composite-route.guard.ts create mode 100644 src/lib/services/dialog.service.ts create mode 100644 src/lib/utils/pruning-translation-loader.ts diff --git a/src/assets/icons/logout.svg b/src/assets/icons/logout.svg new file mode 100755 index 0000000..b9ff468 --- /dev/null +++ b/src/assets/icons/logout.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/assets/icons/menu.svg b/src/assets/icons/menu.svg new file mode 100644 index 0000000..fac3ef6 --- /dev/null +++ b/src/assets/icons/menu.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/src/assets/styles/common-components.scss b/src/assets/styles/common-components.scss new file mode 100644 index 0000000..fa36537 --- /dev/null +++ b/src/assets/styles/common-components.scss @@ -0,0 +1,90 @@ +.oval, +.square { + font-weight: 600; + display: flex; + justify-content: center; + align-items: center; + height: 24px; + width: 24px; + min-width: 24px; + font-size: 10px; + line-height: 12px; + text-align: center; + text-transform: uppercase; + border: none; + box-sizing: border-box; + + &.large { + height: 32px; + width: 32px; + font-size: 13px; + } + + &.gray-dark { + background-color: var(--iqser-grey-6); + } + + &.gray-primary { + background-color: var(--iqser-grey-6); + color: var(--iqser-primary); + } + + &.lightgray-dark { + background-color: var(--iqser-grey-4); + } + + &.lightgray-primary { + background-color: var(--iqser-grey-4); + color: var(--iqser-primary); + } + + &.darkgray-white { + background-color: var(--iqser-accent); + color: var(--iqser-white); + } + + &.lightgray-white { + background-color: var(--iqser-grey-5); + color: var(--iqser-white); + } + + &.primary--white { + background-color: var(--iqser-primary); + color: var(--iqser-white); + } + + &.white-dark { + border: 1px solid var(--iqser-grey-4); + } + + &.inactive { + background-color: var(--iqser-grey-6); + color: var(--iqser-grey-7); + } +} + +.oval { + border-radius: 50%; +} + +.stats-subtitle { + display: flex; + + > div { + display: flex; + justify-content: center; + align-items: center; + width: fit-content; + + mat-icon { + width: 10px; + height: 10px; + line-height: 13px; + margin-right: 6px; + } + + &:not(:last-child) { + margin-right: 12px; + } + } +} diff --git a/src/assets/styles/common-styles.scss b/src/assets/styles/common-styles.scss index 43d57f2..e599e86 100644 --- a/src/assets/styles/common-styles.scss +++ b/src/assets/styles/common-styles.scss @@ -5,3 +5,4 @@ @use 'common-full-pages'; @use 'common-layout'; @use 'common-dialogs'; +@use 'common-components'; diff --git a/src/lib/icons/icons.module.ts b/src/lib/icons/icons.module.ts index 1ad00b7..58f472a 100644 --- a/src/lib/icons/icons.module.ts +++ b/src/lib/icons/icons.module.ts @@ -6,7 +6,7 @@ import { DomSanitizer } from '@angular/platform-browser'; @NgModule({ imports: [CommonModule, MatIconModule], declarations: [], - exports: [MatIconModule] + exports: [MatIconModule], }) export class IqserIconsModule { constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) { @@ -20,14 +20,20 @@ export class IqserIconsModule { 'help-outline', 'lanes', 'list', + 'logout', + 'menu', 'offline', 'refresh', 'search', 'sort-asc', - 'sort-desc' + 'sort-desc', ]); icons.forEach(icon => { - _iconRegistry.addSvgIconInNamespace('iqser', icon, _sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`)); + _iconRegistry.addSvgIconInNamespace( + 'iqser', + icon, + _sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`), + ); }); } } diff --git a/src/lib/services/composite-route.guard.ts b/src/lib/services/composite-route.guard.ts new file mode 100644 index 0000000..57ab3c2 --- /dev/null +++ b/src/lib/services/composite-route.guard.ts @@ -0,0 +1,40 @@ +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Injectable, InjectionToken, Injector } from '@angular/core'; +import { from, of } from 'rxjs'; +import { LoadingService } from '../loading'; + +@Injectable({ + providedIn: 'root', +}) +export class CompositeRouteGuard implements CanActivate { + constructor(protected readonly _injector: Injector, private readonly _loadingService: LoadingService) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + this._loadingService.start(); + + const routeGuards = []>route.data.routeGuards; + + if (routeGuards) { + for (let i = 0; i < routeGuards.length; i++) { + const routeGuard = this._injector.get(routeGuards[i]); + let canActivateResult = routeGuard.canActivate(route, state); + if (canActivateResult instanceof Promise) { + canActivateResult = from(canActivateResult); + } + if (typeof canActivateResult === 'boolean' || canActivateResult instanceof UrlTree) { + canActivateResult = of(canActivateResult); + } + + const result = await canActivateResult.toPromise(); + if (!result) { + this._loadingService.stop(); + return false; + } + } + } + + this._loadingService.stop(); + + return true; + } +} diff --git a/src/lib/services/dialog.service.ts b/src/lib/services/dialog.service.ts new file mode 100644 index 0000000..5b47fb4 --- /dev/null +++ b/src/lib/services/dialog.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@angular/core'; +import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog'; +import { ComponentType } from '@angular/cdk/portal'; + +export const largeDialogConfig: MatDialogConfig = { + width: '90vw', + maxWidth: '90vw', + maxHeight: '90vh', + autoFocus: false, +} as const; + +export const defaultDialogConfig: MatDialogConfig = { + width: '662px', + maxWidth: '90vw', + autoFocus: false, +} as const; + +@Injectable() +export abstract class DialogService { + protected readonly _config: { + [key in T]: { + component: ComponentType; + dialogConfig?: MatDialogConfig; + }; + }; + + protected constructor(protected readonly _dialog: MatDialog) {} + + openDialog( + type: T, + $event: MouseEvent, + data: unknown, + cb?: (...params) => unknown, + finallyCb?: (...params) => unknown | Promise, + ): MatDialogRef { + const config = this._config[type]; + + $event?.stopPropagation(); + const ref = this._dialog.open(config.component, { + ...defaultDialogConfig, + ...(config.dialogConfig || {}), + data, + }); + // eslint-disable-next-line @typescript-eslint/no-misused-promises + ref.afterClosed().subscribe(async result => { + if (result && cb) { + await cb(result); + } + + if (finallyCb) { + await finallyCb(result); + } + }); + return ref; + } +} diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index 6b2e67d..214c413 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -1,3 +1,5 @@ +export * from './dialog.service'; export * from './toaster.service'; export * from './error-message.service'; export * from './generic.service'; +export * from './composite-route.guard'; diff --git a/src/lib/utils/functions.ts b/src/lib/utils/functions.ts index 9ed07df..ebb6d58 100644 --- a/src/lib/utils/functions.ts +++ b/src/lib/utils/functions.ts @@ -1,15 +1,15 @@ -import {tap} from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; export function capitalize(value: string): string { if (!value) { - return ""; + return ''; } return value.charAt(0).toUpperCase() + value.slice(1); } export function humanize(value: string, lowercase = true): string { if (!value) { - return ""; + return ''; } const words = (lowercase ? value.toLowerCase() : value).split(/[ \-_]+/); @@ -17,3 +17,11 @@ export function humanize(value: string, lowercase = true): string { } export const log = tap(console.log); + +export const toNumber = (str: string) => { + try { + return parseInt(`${str}`.replace(/\D/g, ''), 10); + } catch (e) { + return 0; + } +}; diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 9edaef4..1b27d07 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -12,3 +12,4 @@ export * from './decorators/debounce.decorator'; export * from './decorators/on-change.decorator'; export * from './http-encoder'; export * from './types/iqser-types'; +export * from './pruning-translation-loader'; diff --git a/src/lib/utils/pruning-translation-loader.ts b/src/lib/utils/pruning-translation-loader.ts new file mode 100644 index 0000000..55a8b07 --- /dev/null +++ b/src/lib/utils/pruning-translation-loader.ts @@ -0,0 +1,29 @@ +import { HttpClient } from '@angular/common/http'; +import { TranslateLoader } from '@ngx-translate/core'; +import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; + +export class PruningTranslationLoader implements TranslateLoader { + constructor(private _http: HttpClient, private _prefix: string, private _suffix: string) {} + + getTranslation(lang: string): Observable> { + return this._http + .get(`${this._prefix}${lang}${this._suffix}`) + .pipe(map((result: Record) => this._process(result))); + } + + private _process(object: unknown): Record { + return ( + Object.keys(object) + // eslint-disable-next-line no-prototype-builtins + .filter(key => object.hasOwnProperty(key) && object[key] !== '') + .reduce( + (result: Record, key) => ( + (result[key] = typeof object[key] === 'object' ? this._process(object[key]) : object[key]), + result + ), + {}, + ) + ); + } +}