UNPKG

@controladad/ng-base

Version:
1 lines 633 kB
{"version":3,"file":"controladad-ng-base.mjs","sources":["../../../../libs/base/src/configs/tokens.ts","../../../../libs/base/src/configs/icons.ts","../../../../libs/base/src/configs/global-config.ts","../../../../libs/base/src/core/interceptors/api.interceptor.ts","../../../../libs/base/src/core/states/_base.store.ts","../../../../libs/base/src/core/states/app-base.store.ts","../../../../libs/base/src/core/helpers/errors.helper.ts","../../../../libs/base/src/core/helpers/date.helper.ts","../../../../libs/base/src/core/helpers/text.helper.ts","../../../../libs/base/src/core/helpers/router.helper.ts","../../../../libs/base/src/core/helpers/signal.helper.ts","../../../../libs/base/src/core/helpers/object.helper.ts","../../../../libs/base/src/core/helpers/observable.helper.ts","../../../../libs/base/src/core/helpers/array.helper.ts","../../../../libs/base/src/core/helpers/role.helper.ts","../../../../libs/base/src/core/helpers/providers.helper.ts","../../../../libs/base/src/shared/classes/sort-model.ts","../../../../libs/base/src/shared/classes/selection-model.ts","../../../../libs/base/src/shared/data/index.ts","../../../../libs/base/src/shared/classes/filter-model.ts","../../../../libs/base/src/shared/components/ui/icon/icon.component.ts","../../../../libs/base/src/shared/components/ui/icon/icon.component.html","../../../../libs/base/src/shared/components/ui/button/button.component.ts","../../../../libs/base/src/shared/components/ui/button/button.component.html","../../../../libs/base/src/shared/components/ui/checkbox/checkbox.component.ts","../../../../libs/base/src/shared/components/ui/checkbox/checkbox.component.html","../../../../libs/base/src/shared/components/ui/checkbox-group/checkbox-group.component.ts","../../../../libs/base/src/shared/components/ui/checkbox-group/checkbox-group.component.html","../../../../libs/base/src/shared/components/ui/chips/chips.component.ts","../../../../libs/base/src/shared/components/ui/chips/chips.component.html","../../../../libs/base/src/shared/components/ui/chips-group/chips-group.component.ts","../../../../libs/base/src/shared/components/ui/chips-group/chips-group.component.html","../../../../libs/base/src/shared/components/ui/control-error/control-error.component.ts","../../../../libs/base/src/shared/components/ui/control-error/control-error.component.html","../../../../libs/base/src/shared/directives/ui-radio-compare-with.directive.ts","../../../../libs/base/src/shared/directives/permission-hide.directive.ts","../../../../libs/base/src/shared/directives/for-number.directive.ts","../../../../libs/base/src/shared/directives/tooltip.directive.ts","../../../../libs/base/src/shared/directives/mask/config.ts","../../../../libs/base/src/shared/directives/mask/input-mask.directive.ts","../../../../libs/base/src/shared/directives/mask/constants.ts","../../../../libs/base/src/shared/directives/ng-is-in-view.directive.ts","../../../../libs/base/src/shared/components/ui/select-options/select-options.component.ts","../../../../libs/base/src/shared/components/ui/select-options/select-options.component.html","../../../../libs/base/src/shared/components/ui/select-options/select-options-trigger-for.directive.ts","../../../../libs/base/src/shared/components/ui/field/field.component.ts","../../../../libs/base/src/shared/components/ui/field/field.component.html","../../../../libs/base/src/shared/components/ui/date-picker/date-picker.component.ts","../../../../libs/base/src/shared/components/ui/date-picker/date-picker.component.html","../../../../libs/base/src/shared/components/ui/license-plate/license-plate.component.ts","../../../../libs/base/src/shared/components/ui/license-plate/license-plate.component.html","../../../../libs/base/src/shared/components/ui/pagination/pagination.component.ts","../../../../libs/base/src/shared/components/ui/pagination/pagination.component.html","../../../../libs/base/src/shared/components/ui/radio/radio.component.ts","../../../../libs/base/src/shared/components/ui/radio/radio.component.html","../../../../libs/base/src/shared/components/ui/slider/slider.component.ts","../../../../libs/base/src/shared/components/ui/slider/slider.component.html","../../../../libs/base/src/shared/components/ui/switch/switch.component.ts","../../../../libs/base/src/shared/components/ui/switch/switch.component.html","../../../../libs/base/src/shared/components/ui/tabs/tabs.component.ts","../../../../libs/base/src/shared/components/ui/tabs/tabs.component.html","../../../../libs/base/src/shared/components/ui/loader-screen/loader-screen.component.ts","../../../../libs/base/src/shared/components/ui/loader-screen/loader-screen.component.html","../../../../libs/base/src/shared/components/ui/skeleton/skeleton.component.ts","../../../../libs/base/src/shared/components/ui/skeleton/skeleton.component.html","../../../../libs/base/src/shared/components/ui/field-view/field-view.component.ts","../../../../libs/base/src/shared/components/ui/field-view/field-view.component.html","../../../../libs/base/src/shared/components/features/bottom-controls/bottom-controls.component.ts","../../../../libs/base/src/shared/components/features/bottom-controls/bottom-controls.component.html","../../../../libs/base/src/shared/components/layouts/dialog-layout/dialog-layout.component.ts","../../../../libs/base/src/shared/components/layouts/dialog-layout/dialog-layout.component.html","../../../../libs/base/src/shared/components/features/form-builder/form-builder.ts","../../../../libs/base/src/shared/components/features/form-builder/control-builder.component.ts","../../../../libs/base/src/shared/components/features/form-builder/control-builder.component.html","../../../../libs/base/src/shared/components/features/form-builder/form-builder.component.ts","../../../../libs/base/src/shared/components/features/form-builder/form-builder.component.html","../../../../libs/base/src/shared/components/features/license-plate-select/license-plate-select.component.ts","../../../../libs/base/src/shared/components/features/license-plate-select/license-plate-select.component.html","../../../../libs/base/src/shared/components/features/login-form/login-form.component.ts","../../../../libs/base/src/shared/components/features/login-form/login-form.component.html","../../../../libs/base/src/shared/components/features/printable-table/printable-table.component.ts","../../../../libs/base/src/shared/components/features/printable-table/printable-table.component.html","../../../../libs/base/src/shared/components/features/snackbar/snackbar-base/snackbar-base.component.ts","../../../../libs/base/src/shared/components/features/snackbar/snackbar-base/snackbar-base.component.html","../../../../libs/base/src/shared/components/features/snackbar/snackbar.service.ts","../../../../libs/base/src/shared/components/features/table/table-filter-bar/table-filter-bar.component.ts","../../../../libs/base/src/shared/components/features/table/table-filter-bar/table-filter-bar.component.html","../../../../libs/base/src/shared/components/features/table/table-form-menu/table-form-menu.component.ts","../../../../libs/base/src/shared/components/features/table/table-form-menu/table-form-menu.component.html","../../../../libs/base/src/shared/components/features/table/columns/_table_col_base.ts","../../../../libs/base/src/shared/components/features/table/columns/table-col-selection/table-col-selection.component.ts","../../../../libs/base/src/shared/components/features/table/columns/table-col-selection/table-col-selection.component.html","../../../../libs/base/src/shared/components/features/table/columns/table-col-index/table-col-index.component.ts","../../../../libs/base/src/shared/components/features/table/columns/table-col-index/table-col-index.component.html","../../../../libs/base/src/shared/components/features/table/table-filter-menu/table-filter-menu.component.ts","../../../../libs/base/src/shared/components/features/table/table-filter-menu/table-filter-menu.component.html","../../../../libs/base/src/shared/components/features/table/table-filter/table-filter.component.ts","../../../../libs/base/src/shared/components/features/table/table-filter/table-filter.component.html","../../../../libs/base/src/shared/components/features/table/table-sort/table-sort.component.ts","../../../../libs/base/src/shared/components/features/table/table-sort/table-sort.component.html","../../../../libs/base/src/shared/pipes/badge.pipe.ts","../../../../libs/base/src/shared/pipes/typeof.pipe.ts","../../../../libs/base/src/shared/pipes/datefns.pipe.ts","../../../../libs/base/src/shared/components/features/table/columns/table-col-default/table-col-default.component.ts","../../../../libs/base/src/shared/components/features/table/columns/table-col-default/table-col-default.component.html","../../../../libs/base/src/shared/components/features/table/columns/table-col-action/table-col-action.component.ts","../../../../libs/base/src/shared/components/features/table/columns/table-col-action/table-col-action.component.html","../../../../libs/base/src/shared/components/features/table/table-header/table-header.component.ts","../../../../libs/base/src/shared/components/features/table/table-header/table-header.component.html","../../../../libs/base/src/shared/components/dialog/components/_base-dialog.component.ts","../../../../libs/base/src/shared/components/dialog/components/prompt-dialog/prompt-dialog.component.ts","../../../../libs/base/src/shared/components/dialog/components/prompt-dialog/prompt-dialog.component.html","../../../../libs/base/src/shared/components/dialog/dialog-invoker.service.ts","../../../../libs/base/src/shared/components/dialog/components/input-dialog/input-dialog.component.ts","../../../../libs/base/src/shared/components/dialog/components/input-dialog/input-dialog.component.html","../../../../libs/base/src/shared/components/dialog/components/description-dialog/description-dialog.component.ts","../../../../libs/base/src/shared/components/dialog/components/description-dialog/description-dialog.component.html","../../../../libs/base/src/shared/components/dialog/components/change-password-dialog/change-password-dialog.component.ts","../../../../libs/base/src/shared/components/dialog/components/change-password-dialog/change-password-dialog.component.html","../../../../libs/base/src/shared/components/dialog/components/calendar-dialog/calendar-dialog.component.ts","../../../../libs/base/src/shared/components/dialog/components/calendar-dialog/calendar-dialog.component.html","../../../../libs/base/src/shared/components/dialog/dialog.service.ts","../../../../libs/base/src/shared/components/features/table/table.component.ts","../../../../libs/base/src/shared/components/features/table/table.component.html","../../../../libs/base/src/shared/components/features/table/adapters/table-strapi-adapter.service.ts","../../../../libs/base/src/shared/components/features/initialize-protection/initialize-protection.component.ts","../../../../libs/base/src/shared/components/features/initialize-protection/initialize-protection.component.html","../../../../libs/base/src/shared/components/features/image-uploader/image-uploader.component.ts","../../../../libs/base/src/shared/components/features/image-uploader/image-uploader.component.html","../../../../libs/base/src/core/states/auth-base.store.ts","../../../../libs/base/src/core/interceptors/token.interceptor.ts","../../../../libs/base/src/core/interceptors/error.interceptor.ts","../../../../libs/base/src/core/adapters/material-datefns-jalali.adapter.ts","../../../../libs/base/src/core/adapters/get-offline.adapter.ts","../../../../libs/base/src/core/guards/auth.guard.ts","../../../../libs/base/src/core/guards/non-auth.guard.ts","../../../../libs/base/src/core/services/route-helper.service.ts","../../../../libs/base/src/core/services/_base-api.ts","../../../../libs/base/src/core/guards/permission.guard.ts","../../../../libs/base/src/core/models/api.model.ts","../../../../libs/base/src/globals.ts","../../../../libs/base/src/providers.ts","../../../../libs/base/src/controladad-ng-base.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport const API_BASEURL = new InjectionToken<string>('API_BASEURL');\nexport const ENVIRONMENT = new InjectionToken<any>('ENVIRONMENT');\n","import { inject } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\nimport { MatIconRegistry } from '@angular/material/icon';\n\n// prettier-ignore\nconst baseIcons = [\n 'add', 'arrow-down', 'arrow-up', 'arrow-left', 'arrow-right', 'calendar', 'camera', 'check', 'check-double',\n 'chevron-down', 'chevron-left', 'chevron-right', 'chevron-up', 'clock', 'close', 'delete', 'dropdown', 'edit',\n 'edit-box', 'error', 'excel-file', 'eye', 'eye-slash', 'filter', 'filter-filled', 'hashtag', 'info', 'info-circle',\n 'list', 'location', 'location-check', 'login', 'logout', 'menu', 'nav', 'numeric-down', 'numeric-up', 'paper',\n 'paper-details', 'password', 'phone', 'plate', 'play', 'plus', 'power', 'print', 'question-circle', 'refresh',\n 'reports', 'save', 'search', 'settings', 'sort', 'sort-down', 'sort-up', 'time', 'trash', 'trash-alt', 'user',\n 'user-circle', 'users', 'wrench'\n] as const;\n\nexport type BASE_ICONS = (typeof baseIcons)[number];\n\nlet isBaseRegistered = false;\nexport function registerIcons(icons?: string[]) {\n const sanitizer = inject(DomSanitizer);\n const iconRegistry = inject(MatIconRegistry);\n\n if (!isBaseRegistered) {\n for (const icon of baseIcons) {\n iconRegistry.addSvgIcon(icon, sanitizer.bypassSecurityTrustResourceUrl(`./assets/base/icons/${icon}.svg`));\n }\n isBaseRegistered = true;\n }\n\n for (const icon of icons ?? []) {\n iconRegistry.addSvgIcon(icon, sanitizer.bypassSecurityTrustResourceUrl(`./assets/icons/${icon}.svg`));\n }\n}\n","export interface CacBaseLocalizationConfig {\n // array of supported languages, the first index will be selected as default.\n //\n // Default Value: ['en']\n langs: string[];\n localesPath?: string;\n localesData?: { [p: string]: { dateLocale?: any; dateFormats?: any; localeData?: any } };\n\n forceDateFnsLib?: 'jalali' | 'georgian' | undefined,\n}\n\nexport class CacGlobalConfig {\n // Do not change this variable manually, it's automatically updated via the provider.\n static defaultLang = 'en';\n static applicationName = '';\n static localization: CacBaseLocalizationConfig = {\n langs: ['en'],\n };\n // applies application name to store keys, for example if your store key is `auth`, it will become `app_name_auth`\n static applyPrefixToStorageKeys = true;\n\n static generateStoreKey(key: string) {\n return `${this.applyPrefixToStorageKeys && this.applicationName.length ? `${this.applicationName}_` : ''}${key}`;\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';\nimport { Observable } from 'rxjs';\nimport { API_BASEURL } from '../../configs';\n\n@Injectable()\nexport class ApiInterceptor implements HttpInterceptor {\n readonly apiBaseUrl = inject(API_BASEURL);\n\n // using inject() causes circular error, idk why...\n // readonly apiBaseUrl = inject(API_BASEURL);\n\n intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {\n request = request.clone({\n url: request.url.startsWith('/') ? this.apiBaseUrl + request.url : request.url,\n });\n return next.handle(request);\n }\n}\n","import { createStore, Store, withProps } from '@ngneat/elf';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { localStorageStrategy, persistState, StateStorage } from '@ngneat/elf-persist-state';\nimport { CacGlobalConfig } from '../../configs';\nimport { Signal } from '@angular/core';\n\nexport class BaseStore<T extends object> {\n public state$;\n public signal!: Signal<T | undefined>;\n public store: Store<any, T>;\n\n constructor(public storeOpts: {\n key: string;\n // ignore mutations in key such as applying application as prefix (mutations can be controlled from Global Config)\n exactKey?: boolean;\n default?: T;\n // defaults to localStorage\n storageStrategy?: StateStorage;\n }) {\n const key = storeOpts.exactKey ? storeOpts.key : CacGlobalConfig.generateStoreKey(storeOpts.key);\n const store = createStore({ name: key }, withProps<T>(storeOpts.default ?? {} as never));\n persistState(store, {\n key: key,\n storage: storeOpts.storageStrategy ?? localStorageStrategy,\n });\n\n this.store = store;\n this.state$ = this.store.pipe();\n\n try {\n this.signal = toSignal(this.state$);\n } catch { /* empty */ }\n }\n\n get state() {\n return this.get();\n }\n\n get() {\n return this.store.getValue();\n }\n\n patch(value: Partial<T>) {\n return this.store.update((s) => ({\n ...s,\n ...value,\n }));\n }\n}\n","import { Injectable } from '@angular/core';\nimport { select } from '@ngneat/elf';\nimport { BaseStore } from './_base.store';\nimport { CacGlobalConfig } from '../../configs';\n\nexport interface AppBaseStoreProps {\n rememberMe?: boolean;\n lang?: string;\n}\n\nexport class _AppBaseStore<T extends AppBaseStoreProps> extends BaseStore<T> {\n constructor(defaults?: T) {\n super({\n key: 'app',\n default: defaults,\n });\n }\n\n rememberMe$ = this.store.pipe(select(this.rememberMe));\n lang$ = this.store.pipe(select(this.lang));\n\n rememberMe() {\n return this.get().rememberMe ?? false;\n }\n\n setRememberMe(value: boolean) {\n // @ts-ignore\n this.patch({\n rememberMe: value,\n });\n }\n\n lang() {\n return this.get().lang ?? CacGlobalConfig.defaultLang;\n }\n\n setLang(value: string) {\n // @ts-ignore\n this.patch({\n lang: value,\n });\n }\n}\n\n// This is dummy, used to make service out of the `_AppBaseStore`. For extension, `_AppBaseStore` should be used\n@Injectable({\n providedIn: 'root',\n})\nexport class AppBaseStore extends _AppBaseStore<AppBaseStoreProps> {\n constructor() {\n super();\n }\n}\n","import { APIError } from '../models';\n\nconst SERVER_ERROR = {\n 0: $localize`:@@base.errors.server.0:Error connecting, Check your network connection.`,\n 401: $localize`:@@base.errors.server.401:Your session has expired, please login again.`,\n 403: $localize`:@@base.errors.server.403:You do not have permission to continue.`,\n 404: $localize`:@@base.errors.server.404:The requested destination was not found.`,\n 500: $localize`:@@base.errors.server.500:An error occurred on the server.`,\n 503: $localize`:@@base.errors.server.503:The server is unavailable.`,\n default: $localize`:@@base.errors.server.default:Error.`,\n} as const;\n\nconst API_ERROR = {\n 1: $localize`:@@base.errors.api.default:Username or Password is incorrect`,\n} as const;\n\nexport class ErrorHelper {\n\n static getResponseErrorMessage(serverCode: number, apiError?: APIError) {\n return serverCode === 400 && apiError ? this.getApiErrorMessage(apiError) : this.getServerErrorMessage(serverCode);\n }\n\n static getServerErrorMessage(status: number) {\n return SERVER_ERROR[status as never] ?? SERVER_ERROR.default;\n }\n\n static getApiErrorMessage(error: { code: number; message: string }) {\n return API_ERROR[error.code as never] ?? error.message;\n }\n\n // we pass the error object, returned by the api to this function\n static parseApiErrorObject(error: any): APIError | undefined {\n if (!error) return undefined;\n if (typeof error === 'object') {\n if ('error' in error && typeof error.error === 'string') {\n return error.error;\n }\n if ('errors' in error && typeof error.errors === 'object') {\n const message = this.extractMessageFromObjectRecord(error.errors);\n return message\n ? {\n code: -1,\n message,\n }\n : undefined;\n }\n if ('error' in error && typeof error === 'object' && 'code' in error.error && 'message' in error.error) {\n return this.parseApiErrorObject(error.error);\n }\n return error;\n } else if (typeof error === 'string') {\n try {\n return JSON.parse(error);\n } catch {\n return {\n code: -1,\n message: error,\n };\n }\n }\n return undefined;\n }\n\n private static extractMessageFromObjectRecord(object: any): string | undefined {\n const values = Object.values(object);\n for (const value of values) {\n if (value instanceof Array && value.length > 0) {\n return value[0];\n } else if (typeof value === 'string') {\n return value;\n }\n }\n return undefined;\n }\n}\n","import * as jalali from 'date-fns-jalali';\nimport * as dateFns from 'date-fns';\nimport { getStore } from '@ngneat/elf';\nimport { CacGlobalConfig } from '../../configs';\n\nlet _cachedDateFns: typeof dateFns;\n// Get DateFns library based on current language\nexport function DateFns(): typeof dateFns {\n if (_cachedDateFns) return _cachedDateFns;\n\n if (CacGlobalConfig.localization.forceDateFnsLib === 'jalali') _cachedDateFns = jalali as any;\n else if (CacGlobalConfig.localization.forceDateFnsLib === 'georgian') _cachedDateFns = dateFns;\n else {\n const store = getStore<any>(CacGlobalConfig.generateStoreKey('app'));\n _cachedDateFns = (store?.getValue().lang === 'fa' ? jalali : dateFns) as any;\n }\n\n return _cachedDateFns;\n}\n\nexport function getDatesIntervalInHHMMSS(start: Date, end: Date, showSymbol?: boolean) {\n const hours = Math.abs(DateFns().differenceInHours(start, end));\n const duration = DateFns().intervalToDuration({ start, end });\n\n const result = {\n hours,\n minutes: duration.minutes,\n seconds: duration.seconds,\n negative: start > end,\n };\n return {\n formatted: `${result.hours?.toFixed().padStart(2, '0')}:${result.minutes\n ?.toFixed()\n .padStart(2, '0')}:${result.seconds?.toFixed().padStart(2, '0')}${\n showSymbol ? ` (${result.negative ? '-' : '+'})` : ''\n }`,\n ...result,\n };\n}\n\nexport function getFormattedDate(date: string | Date | undefined, format = 'yyyy/MM/dd') {\n return !date ? '' : DateFns().format(typeof date === 'string' ? new Date(date) : date, format);\n}\n\nexport function parseDate(date: string, format: string) {\n return DateFns().parse(date, format, new Date());\n}\n\nexport function getDurationInHHMM(minutes: number) {\n const h = `${Math.floor(minutes / 60)}`.padStart(2, '0');\n const m = `${minutes % 60}`.padStart(2, '0');\n return `${h}:${m}`;\n}\n\nexport function getHHMMInDuration(text: string) {\n const split = text.split(':');\n const h = split.at(0);\n const m = split.at(1);\n if (!h || !m) return 0;\n const parsedH = parseInt(h);\n const parsedM = parseInt(m);\n if (isNaN(parsedH) || isNaN(parsedM)) return 0;\n return parseInt(h) * 60 + parseInt(m);\n}\n","export function getFullName(obj: any, defaultValue = '') {\n const firstNameKeys = ['firstName', 'first_name', 'firstname', 'FirstName'];\n const lastNameKeys = ['lastName', 'last_name', 'lastname', 'LastName'];\n\n if (!obj) return defaultValue;\n\n const firstNameKey = firstNameKeys.find((key) => key in obj);\n const lastNameKey = lastNameKeys.find((key) => key in obj);\n\n const name = `${firstNameKey ? (obj[firstNameKey] ?? '') : ''} ${lastNameKey ? (obj[lastNameKey] ?? '') : ''}`.trim();\n\n return name.length ? name : defaultValue;\n}\n\nexport function toPascalCase(text: string | undefined | null): string {\n let result = text ?? '';\n if (result.length) {\n result = result.charAt(0).toUpperCase() + result.slice(1);\n }\n return result;\n}\n","import { Class, RouteExtended } from '../interfaces';\n\nexport async function lazyLoad<T extends object>(component: Promise<T>, selector?: (o: T) => Class): Promise<Class> {\n const entry = await component;\n if (selector) return selector(entry);\n const props = Object.values(entry);\n if (props.length) return props[0] as Class;\n console.error('LAZY LOAD ERROR', entry);\n throw new Error('Entry has no exported components!!');\n}\n\nexport function resolveRouteChildren(route: RouteExtended) {\n if (route.children) return route.children;\n // _loadedRoutes is a private property of Route which holds the list of lazy loaded routes config. we need to use it!\n // @ts-ignore\n if (route._loadedRoutes) return route._loadedRoutes;\n}\n\nexport function isRouteExtended(r: RouteExtended): r is RouteExtended {\n return 'layout' in r;\n}\n","import { DestroyRef, Signal } from '@angular/core';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\n\nexport function effectDep<T>(dep: Signal<T>, fn: (state: T) => void, destroyRef?: DestroyRef) {\n return toObservable(dep).pipe(takeUntilDestroyed(destroyRef)).subscribe(fn);\n}\n","import type { ItemToId } from '../interfaces';\n\nexport function clone<T>(o: T) {\n return structuredClone(o);\n}\n\nexport const objectToId: ItemToId<any> = (t) => ('id' in t ? t.id : typeof t === 'object' ? Object.values(t).at(0) : t);\n\nexport function omit<T extends object, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> {\n keys.forEach((key) => delete obj[key]);\n return obj;\n}\n\nfunction isObject(item: any) {\n return item && typeof item === 'object' && !Array.isArray(item);\n}\n\nexport function deepMerge(target: any, ...sources: any[]) {\n if (!sources.length) return target;\n const source = sources.shift();\n\n if (isObject(target) && isObject(source)) {\n for (const key in source) {\n if (isObject(source[key])) {\n if (!target[key]) Object.assign(target, { [key]: {} });\n deepMerge(target[key], source[key]);\n } else {\n Object.assign(target, { [key]: source[key] });\n }\n }\n }\n\n return deepMerge(target, ...sources);\n}\n","import { distinctUntilChanged, filter, map, MonoTypeOperatorFunction, Observable, of, pipe, tap } from 'rxjs';\nimport { switchMap } from 'rxjs/operators';\nimport { DestroyRef } from '@angular/core';\n\nexport function getObservable<T>(value: T | Observable<T>): Observable<T> {\n return value instanceof Observable ? value : of(value);\n}\n\nexport function startWithTap<T>(callback: () => void) {\n return (source: Observable<T>) =>\n of({}).pipe(\n tap(callback),\n switchMap(() => source),\n );\n}\n\nexport function filterEmpty<T>(): MonoTypeOperatorFunction<T> {\n return (source: Observable<T>) =>\n source.pipe(\n filter((x) =>\n x instanceof Array\n ? x.filter((t) => t !== undefined && t !== null).length === x.length\n : x !== undefined && x !== null,\n ),\n );\n}\n\nexport function distinctUntilChangedWithTimeout<T>(\n timeout: number,\n compare?: (pre: any, cur: any) => boolean,\n): MonoTypeOperatorFunction<T> {\n return pipe(\n map((t) => ({ value: t, date: new Date() })),\n distinctUntilChanged(\n (pre, cur) =>\n (compare ? compare(pre?.value, cur?.value) : pre?.value === cur?.value) &&\n pre.date.getTime() > cur.date.getTime() - timeout,\n ),\n map((t) => t.value),\n );\n}\n\nexport function destroyRefObservable(ref: DestroyRef) {\n return new Observable<void>((observer) => {\n ref!.onDestroy(observer.next.bind(observer));\n });\n}\n","import { ItemRecord } from '../interfaces';\n\nexport function getFromItemRecord<T, U>(items: ItemRecord<T, U>[], value: T | U | undefined | null) {\n return items.find((t) => t.value === value || (t.alt !== undefined && t.alt === value));\n}\n\nexport function flatten<T>(array: T[][]): T[] {\n return ([] as T[]).concat(...array);\n}\n\nexport function includes<T>(array: T[], terms: T | T[]) {\n if (!(terms instanceof Array)) return array.includes(terms);\n for (const term of terms) {\n if (array.includes(term)) return true;\n }\n return false;\n}\n\nexport function arraySafeAt<T>(array: T[], index: number | undefined | null): T | null {\n if (index === null || index === undefined) return null;\n return array.at(index) ?? null;\n}\n\nexport function dedupe<T>(array: T[]) {\n return [...new Set(array)];\n}\n\nexport function dedupeObj<T extends object>(array: T[], key: keyof T) {\n return array.filter((value, index) => index === array.findIndex((t) => t[key] === value[key]));\n}\n\nexport function subset<T>(array: T[], sub: T[]) {\n return sub.every((val) => array.includes(val));\n}\n\nexport function arraysEqual(a: any[], b: any[]) {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (a.length !== b.length) return false;\n\n for (let i = 0; i < a.length; ++i) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n","const ACTION_TYPES = ['read', 'create', 'update', 'delete', 'print', 'export', 'output', 'other'] as const;\n\nexport type ActionTypes = (typeof ACTION_TYPES)[number];\n\nexport function getAllActions() {\n return ACTION_TYPES;\n}\n\nexport function permissionNameToKey(name: string, action: ActionTypes) {\n switch (action) {\n case 'create':\n return `Add${name}`;\n case 'update':\n return `Update${name}`;\n case 'read':\n return `Get${name}`;\n case 'delete':\n return '';\n case 'print':\n return `PrintListOf${name}`;\n case 'export':\n return `ExportAll${name}`;\n case 'output':\n return `${name}Output`;\n case `other`:\n return `${name}`;\n }\n}\n\nexport function permissionActionToLabel(action: ActionTypes) {\n switch (action) {\n case 'create':\n return $localize`:@@base.permissions.crudActions.create:Add`;\n case 'update':\n return $localize`:@@base.permissions.crudActions.update:Edit`;\n case 'read':\n return $localize`:@@base.permissions.crudActions.read:View Only`;\n case 'delete':\n return $localize`:@@base.permissions.crudActions.delete:Delete`;\n case 'print':\n return $localize`:@@base.permissions.crudActions.print:Print`;\n case 'export':\n return $localize`:@@base.permissions.crudActions.export:Export excel`;\n case 'output':\n return $localize`:@@base.permissions.crudActions.output:Export`;\n case `other`:\n return '';\n }\n}\n","import { inject, InjectionToken, ProviderToken } from '@angular/core';\n\nfunction isClass(obj: any) {\n const isCtorClass = obj.constructor && obj.constructor.toString().substring(0, 5) === 'class';\n if (obj.prototype === undefined) {\n return isCtorClass;\n }\n const isPrototypeCtorClass =\n obj.prototype.constructor &&\n obj.prototype.constructor.toString &&\n obj.prototype.constructor.toString().substring(0, 5) === 'class';\n return isCtorClass || isPrototypeCtorClass;\n}\n\nexport function provide<T>(token: InjectionToken<T>, value: T | (() => T), multi = false) {\n // @ts-ignore\n if (typeof value === 'function') {\n return { provide: token, useFactory: value, multi };\n } else if (isClass(value)) {\n return { provide: token, useClass: value, multi };\n } else {\n return { provide: token, useValue: value, multi };\n }\n}\n\nexport function componentWithDefaultConfig<T>(\n component: any,\n token: InjectionToken<T>,\n defaultValues: Partial<T> = {},\n) {\n const valuesInjected = injectOptional(token) as T[] | T | undefined;\n if (!valuesInjected) return;\n\n const values = valuesInjected instanceof Array ? valuesInjected : [valuesInjected];\n\n let defaults = {\n ...defaultValues,\n };\n for (const val of values) {\n defaults = {\n ...defaults,\n ...val,\n };\n }\n\n for (const key in defaults) {\n // @ts-ignore\n component[key] = defaults[key];\n }\n}\n\nexport function injectOptional<T>(token: ProviderToken<T>): T | undefined {\n try {\n return inject(token);\n } catch {\n return undefined;\n }\n}\n","import { signal } from '@angular/core';\nimport { debounceTime, Subject } from 'rxjs';\nimport { DataGetRequest, DataSortDirection } from '../../core';\n\nexport class SortModel {\n private _key = signal<string | undefined>(undefined);\n private _direction = signal<DataSortDirection>('desc');\n private _changes$ = new Subject<[string | undefined, DataSortDirection]>();\n\n key = this._key.asReadonly();\n direction = this._direction.asReadonly();\n changes$ = this._changes$.pipe(debounceTime(50));\n\n constructor(key?: string, direction?: DataSortDirection) {\n this._key.set(key);\n this._direction.set(direction ?? 'desc');\n }\n\n setKey(sort: string | undefined) {\n this._key.set(sort);\n this.emit();\n }\n setDirection(direction: DataSortDirection) {\n this._direction.set(direction);\n this.emit();\n }\n\n create(): DataGetRequest['sort'] | undefined {\n const key = this.key();\n return key\n ? {\n key: key,\n direction: this.direction(),\n }\n : undefined;\n }\n\n private emit() {\n this._changes$.next([this._key(), this._direction()]);\n }\n}\n","import { computed, DestroyRef, signal } from '@angular/core';\nimport { ItemToId } from '../../core';\nimport { FormControlExtended } from '@al00x/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { startWith } from 'rxjs';\n\n// TODO: Update constructor initial parameters (turn into object, it's messy currently)\n// TODO: add formControl binding\n// TODO: turn off multiple mode by default\n\nexport class SelectionModel<T> {\n private _totalCount = 0;\n private _selectedCount = 0;\n private _multiple;\n private _itemToId: ItemToId<T>;\n private _currentViewItems: T[] = [];\n private _boundFormControl?: FormControlExtended;\n private _blockBoundUpdate = false;\n\n // unique identifier generated randomly\n id: string;\n\n public indeterminate = signal(false);\n public allSelected = signal(false);\n public selected = signal<T[]>([]);\n public selectedIds = signal<{ [p: string | number]: boolean }>({});\n public selectedCount = computed(() => this.selected()?.length ?? 0);\n public hasSelection = computed(() => this.selectedCount() !== 0);\n\n constructor(itemsCount?: number, multiple?: boolean, initial?: T[], itemToId?: ItemToId<T>) {\n this.id = crypto.randomUUID();\n this._totalCount = itemsCount ?? 0;\n this._multiple = multiple ?? true;\n this._itemToId = itemToId ?? (((t) => (t && typeof t === 'object' && 'id' in t ? t.id : t)) as ItemToId<T>);\n initial ? this.select(...initial) : null;\n }\n\n get isMultiple() {\n return this._multiple;\n }\n\n public select(...items: T[]) {\n if (this._multiple) {\n this.set(...[...new Set([...this.selected(), ...items])]);\n } else if (items.length) {\n // this.clear();\n this.set(items[0]);\n }\n }\n\n public deselect(...items: T[]) {\n const itemsId = items.map(this._itemToId);\n this.set(...this.selected().filter((t) => !itemsId.includes(this._itemToId(t))));\n }\n\n public toggle(...items: T[]) {\n const selected = this.selected();\n\n if (this._multiple) {\n let newItems: T[] = [...selected];\n if (newItems.length > 0) {\n for (const item of items) {\n const index = selected.findIndex((t) => this._itemToId(t) === this._itemToId(item));\n if (index !== -1) {\n newItems.splice(index, 1);\n } else {\n newItems.push(item);\n }\n }\n } else {\n newItems = [...items];\n }\n\n this.set(...newItems);\n } else {\n if (selected.some(t => t === items[0])) {\n this.clear();\n } else {\n this.set(items[0]);\n }\n }\n }\n\n public selectAll() {\n this.select(...this._currentViewItems);\n }\n\n public deselectAll() {\n this.deselect(...this._currentViewItems);\n }\n\n public toggleAll() {\n if (this._selectedCount === this._totalCount) {\n this.deselectAll();\n } else {\n this.selectAll();\n }\n }\n\n public clear() {\n if (this._selectedCount === 0) return;\n this.set();\n }\n\n public set(...items: T[]) {\n this.selected.set(items);\n this.selectedIds.set(\n items\n .map((t) => this._itemToId(t))\n .reduce(\n (pre, cur) => ({\n ...pre,\n [cur]: true,\n }),\n {},\n ),\n );\n this._selectedCount = items.length;\n\n this.calculateSelectionState();\n this.updateBoundedFormControl();\n }\n\n public isSelected(item: T) {\n return !!this.selectedIds()[this._itemToId(item)];\n }\n\n public setTotalCount(count: number) {\n this._totalCount = count;\n\n this.calculateSelectionState();\n }\n\n public setItems(items: T[], setTotalCount = true) {\n this._currentViewItems = items;\n if (setTotalCount) {\n this._totalCount = items.length;\n }\n\n this.calculateSelectionState();\n }\n\n public setItemToIdFn(fn: ItemToId<T>) {\n this._itemToId = fn;\n }\n\n public setMultiple(value?: boolean) {\n this._multiple = value === undefined ? true : value;\n }\n\n public bindFormControl(control: FormControlExtended | undefined, destroyRef?: DestroyRef) {\n this._boundFormControl = control;\n if (!control) return;\n\n control.valueChanges.pipe(startWith(control.value), takeUntilDestroyed(destroyRef)).subscribe((v) => {\n if (this._blockBoundUpdate || v === this.selectedIds()) {\n this._blockBoundUpdate = false;\n return;\n }\n\n if (v === null || v === undefined) {\n this.clear();\n } else {\n this.set(...(v instanceof Array ? v : [v]));\n }\n });\n }\n\n private updateBoundedFormControl() {\n if (!this._boundFormControl) return;\n\n this._blockBoundUpdate = true;\n this._boundFormControl.setValue(this.selectedIds());\n this._boundFormControl.setSelectedItems(this.selected());\n }\n\n private calculateSelectionState() {\n const selectedViewItemsCount = this._currentViewItems\n .map(this._itemToId)\n .filter((t) => this.selectedIds()[t]).length;\n if (this._selectedCount === 0 || selectedViewItemsCount === 0) {\n this.indeterminate.set(false);\n this.allSelected.set(false);\n } else if (this._selectedCount === this._totalCount) {\n this.indeterminate.set(false);\n this.allSelected.set(true);\n } else {\n this.indeterminate.set(selectedViewItemsCount !== this._currentViewItems.length);\n this.allSelected.set(!this.indeterminate());\n }\n }\n}\n","import type { ItemRecord } from '../../core';\n\nexport const ActiveValues: ItemRecord<boolean>[] = [\n { value: true, label: 'Active' },\n { value: false, label: 'Inactive' },\n];\n\nexport const SuspendedValues: ItemRecord<boolean>[] = [\n { value: false, label: 'Active' },\n { value: true, label: 'Inactive' },\n];\n\nexport const BooleanValues: ItemRecord<boolean>[] = [\n { value: true, label: 'Yes' },\n { value: false, label: 'No' },\n];\n","import { computed, signal } from '@angular/core';\nimport { BehaviorSubject, debounceTime, Subject } from 'rxjs';\nimport { DataFilterTypes, DataGetRequest, getFromItemRecord, getFormattedDate, ItemRecords$ } from '../../core';\nimport type { FormBuilderInputType, TableColumnData, TableColumnFilter, TableFilterOptions } from '../components';\nimport { BooleanValues } from '../data';\n\nexport interface FilterValue {\n key?: string;\n value?: Date | string | number | boolean | string[] | number[];\n displayText?: string | string[];\n type: DataFilterTypes;\n controlType?: FormBuilderInputType;\n}\n\nexport interface FilterOptions {\n prop: string;\n label: string;\n items?: ItemRecords$<any>;\n filterable?: TableColumnFilter;\n icon?: string;\n}\n\nexport interface FilterItem {\n // prop is used to identify the filter\n prop: string;\n values: FilterValue[] | undefined;\n // key is for the filtering purpose\n key: string;\n label: string;\n // The prob used, has been set manually and strictly (ex: when using col.filterable as a string)\n strictKey?: boolean;\n // Displayable text for filter\n formatted?: {\n full: string;\n prefix: string;\n text: string;\n suffix: string;\n };\n icon?: string;\n}\n\nexport type FilterObject = { [p: string]: FilterItem | undefined };\n\nexport type TableFilterObject = { [p: string]: FilterValue[] };\n\nexport class FilterModel {\n protected _filters = signal<FilterObject>({});\n private _changes$ = new Subject<FilterObject | undefined>();\n\n filters = computed(() => {\n const obj = this._filters();\n if (!obj) return this.overrideEmpty;\n Object.keys(obj).forEach((key) => obj[key] === undefined && delete obj[key]);\n if (Object.keys(obj).length === 0) return this.overrideEmpty;\n return obj as FilterObject;\n });\n filtersArray = computed(() => {\n const filters = this.filters();\n if (!filters) return [];\n return Object.values(filters) as FilterItem[];\n });\n hasFilter = computed(() => {\n return this.filters() !== undefined;\n });\n changes$ = this._changes$.pipe(debounceTime(50));\n\n constructor(\n init?: FilterObject,\n public overrideEmpty?: FilterObject,\n ) {\n this._filters.set(init ?? overrideEmpty ?? {});\n }\n\n set(data: FilterOptions, values: FilterValue[] | undefined, emit = true) {\n if (values === undefined || values.every((t) => t.value === undefined || t.value === null)) {\n this.remove(data.prop);\n return;\n }\n\n this._filters.set({\n ...this._filters(),\n [data.prop]: this.createFilterItem(data, values),\n });\n\n if (emit) this.emitChanges();\n }\n\n remove(prop: string) {\n this._filters.set({\n ...this._filters(),\n [prop]: undefined,\n });\n\n this.emitChanges();\n }\n\n clear() {\n this._filters.set({});\n\n this.emitChanges();\n }\n\n create(): DataGetRequest['filters'] {\n const filters = this.filtersArray();\n return filters\n .filter((t) => !!t.values && t.values.length > 0)\n .map((item) =>\n item.values!.map((v) => ({\n key: v.key ?? item.key,\n strictKey: !!item.strictKey || !!v.key,\n value: this.mapValueToString(v.value),\n type: v.type,\n })),\n )\n .reduce((pre, cur) => pre.concat(cur), []);\n }\n\n // Emit will be called automatically, use this is you disabled emit on any functions\n emitChanges() {\n this._changes$.next(this._filters());\n }\n\n protected createFilterItem(opts: FilterOptions, value?: FilterValue[]) {\n const item: FilterItem = {\n ...this.getKey(opts),\n values: undefined,\n prop: opts.prop,\n label: opts.label,\n icon: opts.icon,\n };\n return this.setValueForFilterItem(item, value, opts.items);\n }\n\n protected setValueForFilterItem(item: FilterItem, values?: FilterValue[], records?: ItemRecords$<any>) {\n const newValues = values\n ? values.every((t) => t.value === null || t.value === undefined)\n ? undefined\n : values.map((t) => {\n const value = t.value;\n\n if (value instanceof Date && t.controlType === 'date') {\n if (t.type === 'lower') {\n value.setHours(23, 59, 59, 999);\n } else {\n value.setHours(0, 0, 0, 0);\n }\n }\n\n return {\n ...t,\n value,\n } as typeof t;\n })\n : undefined;\n\n item.values = newValues;\n item.formatted = newValues ? this.formatFilterItem(item, records) : undefined;\n return item;\n }\n\n private mapValueToString(value: FilterValue['value']): string | string[] {\n if (value instanceof Array) {\n return value.map((t) => this.mapValueToString(t) as string);\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false';\n }\n if (typeof value === 'number') {\n return value.toString();\n }\n return value ?? '';\n }\n\n private mapValueToReadable(filterValue: FilterValue, records?: ItemRecords$<any>): string {\n const value = filterValue.value;\n if (value instanceof Date) {\n if (filterValue.controlType === 'datetime') {\n return getFormattedDate(value, 'HH:mm ,yyyy/MM/dd');\n }\n return getFormattedDate(value);\n }\n if (filterValue.displayText && filterValue.displayText.length) {\n return filterValue.displayText instanceof Array ? filterValue.displayText.join(', ') : filterValue.displayText;\n }\n if (records && records instanceof Array) {\n return getFromItemRecord(records, value)?.label ?? '';\n }\n if (typeof value === 'boolean') {\n return value ? $localize`:@@base.values.trueText:Yes` : $localize`:@@base.values.falseText:No`;\n }\n if (typeof value === 'number') {\n return value.toString();\n }\n return value instanceof Array ? (value.length ? value.join(', ') : '') : value ?? '';\n }\n\n private getKey(data: FilterOptions): { key: string; strictKey?: boolean } {\n if (data.filterable === true) {\n return { key: data.prop };\n } else if (typeof data.filterable === 'string') {\n return data.filterable !== '' ? { key: data.filterable, strictKey: true } : { key: data.prop };\n }\n return { key: data.prop };\n }\n\n private formatFilterItem(item: FilterItem, records?: ItemRecords$<any>): FilterItem['formatted'] {\n let prefix = '';\n let suffix = '';\n\n const equal = item.values?.find((t) => t.type === 'equal' || t.type === 'contains');\n const greater = item.values?.find((t) => t.type === 'greater');\n const lower = item.values?.find((t) => t.type === 'lower');\n\n const equalValue =\n equal?.value !== undefined && equal?.value !== null ? this.mapValueToReadable(equal, records) : undefined;\n const greaterValue =\n greater?.value !== undefined && greater?.value !== null ? this.mapValueToReadable(greater, records) : undefined;\n const lowerValue =\n lower?.value !== undefined && lower?.value !== null ? this.mapValueToReadable(lower, records) : undefined;\n\n if (equalValue !== undefined && equalValue !== null) {\n suffix += ` : ${equalValue}`;\n } else if (greaterValue !== undefined && greaterValue !== null && lowerValue !== undefined && lowerValue !== null) {\n prefix += `${greaterValue} < `;\n suffix += ` < ${lowerValue}`;\n } else if (greaterValue !== undefined && greaterValue !== null) {\n suffix += ` > ${greaterValue}`;\n } else if (lowerValue !== undefined && lowerValue !== null) {\n suffix += ` < ${lowerValue}`;\n }\n\n if (prefix === '' && suffix === '') return undefined;\n return {\n full: `${prefix}${item.label}${suffix}`,\n text: item.label,\n prefix: prefix,\n suffix: suffix,\n };\n }\n}\n\nexport class TableFilterModel extends FilterModel {\n private _columns?: TableColumnData<any>[];\n\n columnFilters: { [p: string]: TableFilterOptions[] } = {};\n columnLabels: { [p: string]: string } = {};\n\n columnsChanged$ = new BehaviorSubject<void>(undefined);\n\n constructor(\n public initValue?: FilterObject,\n public overrideEmptyValue?: TableFilterObject,\n ) {\n super(initValue);\n }\n\n override set(columnProp: FilterOptions | string, values: FilterValue[] | undefined, emit = true) {\n if (typeof columnProp === 'object') {\n super.set(columnProp, values, emit);\n return;\n }\n\n if (!this._columns) return;\n\n const col = this._columns.find((t) => t.prop === columnProp);\n\n if (!col) {\n console.error(`Table filter failed with the given prop: '${columnProp}'`);\n return;\n }\n\n super.set(col, values, emit);\n }\n\n setColumns(columns: typeof this._columns) {\n this._columns = columns;\n this.updateFilters();\n this.setOverrideEmpty();\n\n this.columnsChanged$.next();\n }\n\n getColumnFilters(columnProp: string): TableFilterOptions[] | undefined {\n return this.columnFilters[columnProp];\n }\n\n private setOverrideEmpty() {\n if (!this.overrideEmptyValue || !this._columns) return;\n\n this.overrideEmpty = {};\n for (const prop in this.overrideEmptyValue) {\n const column = this._columns.find((t) => t.prop === prop);\n if (!column) continue;\n\n const value = this.overrideEmptyValue[prop];\n const item = this.createFilterItem(column, value);\n this.overrideEmpty[item.prop] = item;\n }\n if (!this.initValue) {\n this._filters.set(this.overrideEmpty);\n }\n }\n\n private updateFilters() {\n if (!this._columns) return;\n\n this.columnFilters = {};\n this.columnLabels = {};\n for (const column of this._columns) {\n let filters: TableFilterOptions[] | undefined;\n if (column.filterable instanceof Array) {\n filters = column.filterable;\n } else if (column.filterable) {\n switch (column.type ?? 'text') {\n case 'number': {\n filters = [\n { inputType: 'number', type: 'equal' },\n { inputType: 'number', type: 'greater' },\n { inputType: 'number', type: 'lower' },\n ];\n break;\n }\n case 'boolean': {\n filters = [{ type: 'equal', controlType: 'select', items: column.items ?? BooleanValues }];\n break;\n }\n case 'plate':\n filters = [{ type: 'equal', controlType: 'plate' }];\n break;\n default: {\n filters = [\n {\n type: 'contains',\n controlType: column.items ? 'select' : 'input',\n items: column.items ?? undefined,\n },\n ];\n }\n }\n }\n\n if