UNPKG

@js-smart/ng-kit

Version:

Reusable Angular components built with Angular Material and Bootstrap 5.x, Utility classes/functions for Date, Form and String operations

1 lines 168 kB
{"version":3,"file":"js-smart-ng-kit.mjs","sources":["../../../projects/ng-kit/src/lib/components/alert/alert.component.ts","../../../projects/ng-kit/src/lib/components/alert/alert.component.html","../../../projects/ng-kit/src/lib/components/spinner/spinner.component.ts","../../../projects/ng-kit/src/lib/components/spinner/spinner.component.html","../../../projects/ng-kit/src/lib/components/autocomplete/autocomplete.component.ts","../../../projects/ng-kit/src/lib/components/autocomplete/autocomplete.component.html","../../../projects/ng-kit/src/lib/directives/autocomplete-suffix/autocomplete-clear-button.component.ts","../../../projects/ng-kit/src/lib/directives/autocomplete-suffix/autocomplete-dropdown-button.component.ts","../../../projects/ng-kit/src/lib/directives/autocomplete-suffix/autocomplete-suffix.directive.ts","../../../projects/ng-kit/src/lib/directives/ngx-print/print-options.ts","../../../projects/ng-kit/src/lib/directives/ngx-print/ngx-print.directive.ts","../../../projects/ng-kit/src/lib/directives/prevent-multiple-clicks/prevent-multiple-clicks.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/base-button/base-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/base-button/base-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/bs-link-button/bs-link-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/bs-link-button/bs-link-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/close-button/close-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/delete-button/delete-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/delete-button/delete-button.directive.ts","../../../projects/ng-kit/src/lib/svg-icons/edit-solid-svg/edit-solid-svg-icon.registry.ts","../../../projects/ng-kit/src/lib/svg-icons/edit-solid-svg/edit-solid-svg.component.ts","../../../projects/ng-kit/src/lib/components/buttons/edit-bs-button/edit-bs-button.component.ts","../../../projects/ng-kit/src/lib/svg-icons/edit-solid-svg/append-edit-solid-svg-icon.util.ts","../../../projects/ng-kit/src/lib/components/buttons/edit-bs-button/edit-bs-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/edit-button/edit-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/edit-button/edit-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/edit-svg-icon-button/edit-svg-icon-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/edit-svg-icon-button/edit-svg-icon-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/excel-export-button/excel-export-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/excel-export-button/excel-export-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/manage-button/manage-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/manage-button/manage-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/pdf-export-button/pdf-export-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/pdf-export-button/pdf-export-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/primary-button/primary-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/primary-button/primary-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/save-primary-button/save-primary-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/save-primary-button/save-primary-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/search-button/search-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/success-button/success-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/success-button/success-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/view-button/view-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/view-button/view-button.directive.ts","../../../projects/ng-kit/src/lib/components/buttons/view-primary-button/view-primary-button.component.ts","../../../projects/ng-kit/src/lib/components/buttons/view-primary-button/view-primary-button.directive.ts","../../../projects/ng-kit/src/lib/components/confirm-dialog/confirm-dialog.component.ts","../../../projects/ng-kit/src/lib/components/snack-bar/error-snack-bar/error-snack-bar.component.ts","../../../projects/ng-kit/src/lib/components/snack-bar/success-snack-bar/success-snack-bar.component.ts","../../../projects/ng-kit/src/lib/services/mat-snack-bar.service.ts","../../../projects/ng-kit/src/lib/store/entity-store.ts","../../../projects/ng-kit/src/lib/store/store.ts","../../../projects/ng-kit/src/lib/util/progress-util.ts","../../../projects/ng-kit/src/lib/query/query-client.token.ts","../../../projects/ng-kit/src/lib/query/inject-mutation.ts","../../../projects/ng-kit/src/lib/query/inject-query.ts","../../../projects/ng-kit/src/js-smart-ng-kit.ts"],"sourcesContent":["import { ChangeDetectionStrategy, ChangeDetectorRef, Component, effect, inject, input, type OnInit, output, signal } from '@angular/core';\n\nexport type AlertType = 'info' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'dark' | 'light';\n\n/**\n * Boostrap Alert component that can be used to alert messages to the user\n *\n * @author Pavan Kumar Jadda\n * @since 12.0.0\n */\n@Component({\n\tselector: 'lib-alert, alert',\n\ttemplateUrl: './alert.component.html',\n\tstyleUrls: ['./alert.component.scss'],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AlertComponent implements OnInit {\n\tcdr = inject(ChangeDetectorRef);\n\t/**\n\t * Type of the BootStrap Alert. Following values are supported. See BootStrap docs for more information\n\t */\n\ttype = input<AlertType>('info');\n\n\t/**\n\t * Is alert visible or open\n\t */\n\tisOpen = input(true);\n\n\t/**\n\t * Writable signal for isOpen\n\t */\n\topen = signal(this.isOpen());\n\n\t/**\n\t * If set, displays an inline “Close” button\n\t */\n\tdismissible = input(true);\n\n\t/**\n\t * If set, dismisses the alert after Dismiss Timeout\n\t */\n\tdismissOnTimeout = input(true);\n\n\t/**\n\t * Number in milliseconds, after which alert will be closed. Default value is 5000 ms\n\t */\n\tdismissTimeout = input(5000);\n\n\t/**\n\t * Additional classes to be added to the alert. This can be used to add custom styles to the alert\n\t */\n\tclass = input('');\n\n\t/**\n\t * Emits when the alert is closed.\n\t */\n\tclosed = output<void>();\n\n\tconstructor() {\n\t\t// React to isOpen input changes\n\t\teffect(() => {\n\t\t\tthis.open.set(this.isOpen());\n\t\t});\n\t}\n\n\t/**\n\t * Initialize the component and settings\n\t *\n\t * @author Pavan Kumar Jadda\n\t * @since 12.0.0\n\t */\n\tngOnInit(): void {\n\t\tif (this.dismissOnTimeout()) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.closeAlert();\n\t\t\t\tthis.cdr.markForCheck();\n\t\t\t}, this.dismissTimeout());\n\t\t}\n\t}\n\n\t/**\n\t * Closes BootStrap Alert if not open\n\t *\n\t * @author Pavan Kumar Jadda\n\t * @since 12.0.0\n\t */\n\tcloseAlert(): void {\n\t\tif (!this.open()) {\n\t\t\treturn;\n\t\t}\n\t\tthis.open.set(false);\n\t\tthis.closed.emit();\n\t}\n}\n","@if (open()) {\n\t<div class=\"row {{ class() }}\">\n\t\t<div class=\"col-xs-12 col-sm-12 col-md-auto mx-auto\">\n\t\t\t<div class=\"alert alert-{{ type() }} alert-dismissible alert_div\" role=\"alert\" aria-live=\"polite\">\n\t\t\t\t<ng-content></ng-content>\n\t\t\t\t@if (dismissible()) {\n\t\t\t\t\t<button (click)=\"closeAlert()\" aria-label=\"Close\" class=\"btn-close\" data-bs-dismiss=\"alert\" type=\"button\"></button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t</div>\n}\n","import { Component, input, ChangeDetectionStrategy } from '@angular/core';\nimport { ThemePalette } from '@angular/material/core';\nimport { MatProgressSpinnerModule } from '@angular/material/progress-spinner';\n\n@Component({\n\tselector: 'spinner,lib-spinner',\n\timports: [MatProgressSpinnerModule],\n\ttemplateUrl: './spinner.component.html',\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\tstyleUrls: ['./spinner.component.scss'],\n})\nexport class SpinnerComponent {\n\t/**\n\t * Use Boostrap Spinner. Default `true`\n\t */\n\tbootstrapSpinner = input(true);\n\n\t/**\n\t * Diameter of the Angular Material spinner\n\t */\n\tdiameter = input(50);\n\n\t/**\n\t * Color of the Angular Material spinner\n\t */\n\tcolor = input<ThemePalette>('primary');\n\n\t/**\n\t * Stroke Width of the Angular Material spinner\n\t */\n\tstrokeWidth = input(5);\n}\n","<!-- Angular Material Spinner -->\n@if (!bootstrapSpinner()) {\n\t<mat-spinner [color]=\"color()\" [diameter]=\"diameter()\" [strokeWidth]=\"strokeWidth()\" class=\"mx-auto\"> </mat-spinner>\n}\n\n<!-- Bootstrap Spinner -->\n@if (bootstrapSpinner()) {\n\t<div class=\"d-flex justify-content-center\">\n\t\t<div class=\"spinner-border bs-spinner\" role=\"status\"></div>\n\t</div>\n}\n","import { NgClass } from '@angular/common';\nimport { Component, computed, ElementRef, forwardRef, input, output, signal, viewChild, ChangeDetectionStrategy } from '@angular/core';\nimport { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';\nimport { MatAutocomplete, MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatFormFieldAppearance, MatFormFieldModule } from '@angular/material/form-field';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatProgressSpinnerModule } from '@angular/material/progress-spinner';\n\n/**\n * Reusable Auto Complete component that extends MatAutoComplete to show Clear icon and Arrow buttons\n *\n * @author Pavan Kumar Jadda\n * @since 12.0.0\n */\n@Component({\n\tselector: 'autocomplete, lib-autocomplete',\n\timports: [\n\t\tNgClass,\n\t\tReactiveFormsModule,\n\t\tMatAutocompleteModule,\n\t\tMatInputModule,\n\t\tMatFormFieldModule,\n\t\tMatIconModule,\n\t\tMatButtonModule,\n\t\tMatProgressSpinnerModule,\n\t],\n\ttemplateUrl: './autocomplete.component.html',\n\tstyleUrls: ['./autocomplete.component.css'],\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\tproviders: [\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: forwardRef(() => AutocompleteComponent),\n\t\t\tmulti: true,\n\t\t},\n\t],\n})\nexport class AutocompleteComponent<T = string> implements ControlValueAccessor {\n\t/** Gets reference to the MatAutocompleteTrigger to programmatically open/close the panel */\n\tautocompleteTrigger = viewChild.required(MatAutocompleteTrigger);\n\n\t/** MatAutocomplete instance (used to scroll the selected option into view when the panel opens) */\n\tmatAutocomplete = viewChild.required(MatAutocomplete);\n\n\t/** Gets reference to the input element for re-focusing after clear */\n\tinputElement = viewChild.required<ElementRef<HTMLInputElement>>('inputEl');\n\n\t/** Label of the autocomplete form field */\n\tlabel = input('Select Value');\n\n\t/** Placeholder text displayed inside the input when empty */\n\tplaceholder = input('');\n\n\t/** Appearance of the form field. Defaults to `outline` */\n\tappearance = input<MatFormFieldAppearance>('outline');\n\n\t/** List of CSS classes to apply to the form field */\n\tclasses = input('');\n\n\t/** List of options to display in the dropdown */\n\toptions = input<T[]>([]);\n\n\t/**\n\t * Function that maps an option to its display string.\n\t * Used for rendering options in the dropdown and showing the selected value in the input.\n\t * Defaults to `String(value)` which works for primitive string options.\n\t */\n\tdisplayWith = input<(value: T) => string>((value: T) => String(value));\n\n\t/** Whether the autocomplete is in a loading state. Shows a spinner instead of options */\n\tloading = input(false);\n\n\t/** Text displayed when the autocomplete is in a loading state. Defaults to 'Loading...' */\n\tloadingText = input('Loading...');\n\n\t/** Text displayed when no options match the filter input. Defaults to 'No values found' */\n\tnoOptionsText = input('No values found');\n\n\t/** Emits the selected value when an option is picked from the dropdown */\n\tselectionChange = output<T>();\n\n\t/** Emits the raw text in the input on each keystroke (filter text while typing) */\n\tonInputChange = output<string>();\n\n\t/** Internal form control for the autocomplete input */\n\tcontrol = new FormControl<T | string | null>(null);\n\n\t/** Signal that tracks whether the autocomplete panel is currently open */\n\tisExpanded = signal(false);\n\n\t/** Signal that tracks the current filter text typed by the user */\n\tfilterText = signal('');\n\n\t/**\n\t * Computed signal that filters options based on the current filter text.\n\t * Uses the `displayWith` function to extract searchable text from each option.\n\t * Returns all options when filter text is empty.\n\t */\n\tfilteredOptions = computed(() => {\n\t\tconst filterValue = this.filterText().toLowerCase();\n\t\tconst displayFn = this.displayWith();\n\t\treturn this.options().filter((option) => displayFn(option).toLowerCase().includes(filterValue));\n\t});\n\n\t/**\n\t * Display function passed to mat-autocomplete's [displayWith] to render the\n\t * selected value in the input field. Returns empty string for null/undefined values.\n\t */\n\tdisplayFn = (value: T): string => {\n\t\tif (value == null || value === ('' as unknown as T)) {\n\t\t\treturn '';\n\t\t}\n\t\treturn this.displayWith()(value);\n\t};\n\n\t/**\n\t * Writes a new value to the internal control. Called by the parent form\n\t * when the form control value is set programmatically.\n\t */\n\twriteValue(value: T | null): void {\n\t\t// Use null (not '') so MatAutocompleteTrigger clears mat-option selection and stays in sync with the parent CVA.\n\t\tconst internal = value == null ? null : value;\n\t\tthis.control.setValue(internal as T | string | null, { emitEvent: false });\n\t}\n\n\t/**\n\t * Registers a callback function that is called when the control's value\n\t * changes in the UI (option selected or input cleared).\n\t */\n\tregisterOnChange(fn: (value: T | null) => void): void {\n\t\tthis.onChange = fn;\n\t}\n\n\t/**\n\t * Registers a callback function that is called when the control is\n\t * first interacted with (blur or selection).\n\t */\n\tregisterOnTouched(fn: () => void): void {\n\t\tthis.onTouched = fn;\n\t}\n\n\t/**\n\t * Sets the disabled state of the internal control. Called by the parent form\n\t * when `control.disable()` or `control.enable()` is invoked.\n\t */\n\tsetDisabledState(isDisabled: boolean): void {\n\t\tif (isDisabled) {\n\t\t\tthis.control.disable();\n\t\t} else {\n\t\t\tthis.control.enable();\n\t\t}\n\t}\n\n\t/** Updates the filter text signal as the user types in the input */\n\tonInput(event: Event): void {\n\t\tconst value = (event.target as HTMLInputElement).value;\n\t\tthis.filterText.set(value);\n\t\tthis.onInputChange.emit(value);\n\t}\n\n\t/**\n\t * Clears the input value, resets the filter, notifies the parent form,\n\t * and re-focuses the input element.\n\t */\n\tclearInput(event: Event): void {\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.autocompleteTrigger().closePanel();\n\t\tthis.control.setValue(null, { emitEvent: false });\n\t\tthis.filterText.set('');\n\t\tthis.onChange(null);\n\t\tthis.onTouched();\n\t\tqueueMicrotask(() => this.inputElement().nativeElement.focus());\n\t}\n\n\t/** Runs when the autocomplete overlay opens: keep expanded state and scroll the selected option into view. */\n\tonPanelOpened(): void {\n\t\tthis.isExpanded.set(true);\n\t\tthis.scrollSelectedOptionIntoView();\n\t}\n\n\tprivate scrollSelectedOptionIntoView(): void {\n\t\trequestAnimationFrame(() => {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tconst panelEl = this.matAutocomplete().panel?.nativeElement as HTMLElement | undefined;\n\t\t\t\tif (!panelEl) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst selected = panelEl.querySelector('.mat-mdc-option.mdc-list-item--selected') as HTMLElement | null;\n\t\t\t\tselected?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n\t\t\t});\n\t\t});\n\t}\n\n\t/** Opens the autocomplete panel programmatically */\n\topenPanel(): void {\n\t\tthis.autocompleteTrigger().openPanel();\n\t}\n\n\t/**\n\t * Handles option selection from the dropdown. Resets the filter text,\n\t * notifies the parent form of the new value, and emits the selectionChange output.\n\t */\n\tonOptionSelected(value: T): void {\n\t\tthis.filterText.set('');\n\t\tthis.onChange(value);\n\t\tthis.onTouched();\n\t\tthis.selectionChange.emit(value);\n\t}\n\n\t/** Callback function registered by the parent form to propagate value changes */\n\tprivate onChange: (value: T | null) => void = (_: T | null) => {\n\t\t/* noop */\n\t};\n\n\t/** Callback function registered by the parent form to propagate touched state */\n\tprivate onTouched: () => void = () => {\n\t\t/* noop */\n\t};\n}\n","<mat-form-field\n\t#origin=\"matAutocompleteOrigin\"\n\t[appearance]=\"appearance()\"\n\t[ngClass]=\"classes()\"\n\tmatAutocompleteOrigin\n\tsubscriptSizing=\"dynamic\">\n\t<mat-label>{{ label() }}</mat-label>\n\n\t<input\n\t\t#inputEl\n\t\t#trigger=\"matAutocompleteTrigger\"\n\t\t(click)=\"trigger.openPanel()\"\n\t\t(input)=\"onInput($event)\"\n\t\t[formControl]=\"control\"\n\t\t[matAutocompleteConnectedTo]=\"origin\"\n\t\t[matAutocomplete]=\"auto\"\n\t\t[placeholder]=\"placeholder()\"\n\t\tmatInput\n\t\ttype=\"text\" />\n\n\t<div class=\"autocomplete-suffix\" matSuffix>\n\t\t@if (control.value && !control.disabled) {\n\t\t\t<button type=\"button\" (click)=\"clearInput($event)\" aria-label=\"Clear value\" class=\"suffix-action-btn\" mat-icon-button>\n\t\t\t\t<mat-icon>close</mat-icon>\n\t\t\t</button>\n\t\t} @else if (!control.disabled) {\n\t\t\t<span aria-hidden=\"true\" class=\"suffix-placeholder\"></span>\n\t\t}\n\t\t@if (!control.disabled) {\n\t\t\t<button\n\t\t\t\t(click)=\"trigger.panelOpen ? trigger.closePanel() : trigger.openPanel(); $event.stopPropagation()\"\n\t\t\t\t[attr.aria-label]=\"trigger.panelOpen ? 'Close options' : 'Open options'\"\n\t\t\t\tclass=\"suffix-action-btn\"\n\t\t\t\tmat-icon-button\n\t\t\t\ttype=\"button\">\n\t\t\t\t@if (isExpanded()) {\n\t\t\t\t\t<mat-icon>arrow_drop_up</mat-icon>\n\t\t\t\t} @else {\n\t\t\t\t\t<mat-icon>arrow_drop_down</mat-icon>\n\t\t\t\t}\n\t\t\t</button>\n\t\t}\n\t</div>\n\n\t<mat-autocomplete\n\t\t#auto=\"matAutocomplete\"\n\t\t(closed)=\"isExpanded.set(false)\"\n\t\t(opened)=\"onPanelOpened()\"\n\t\t(optionSelected)=\"onOptionSelected($event.option.value)\"\n\t\t[displayWith]=\"displayFn\">\n\t\t@if (loading()) {\n\t\t\t<mat-option disabled>\n\t\t\t\t<div style=\"display: flex; align-items: center; gap: 8px\">\n\t\t\t\t\t<mat-spinner diameter=\"20\"></mat-spinner>\n\t\t\t\t\t<span>{{ loadingText() }}</span>\n\t\t\t\t</div>\n\t\t\t</mat-option>\n\t\t} @else {\n\t\t\t@for (option of filteredOptions(); track option) {\n\t\t\t\t<mat-option [value]=\"option\">{{ displayWith()(option) }}</mat-option>\n\t\t\t} @empty {\n\t\t\t\t<mat-option disabled>{{ noOptionsText() }}</mat-option>\n\t\t\t}\n\t\t}\n\t</mat-autocomplete>\n</mat-form-field>\n","import { Component, output, ChangeDetectionStrategy } from '@angular/core';\nimport { MatIconButton } from '@angular/material/button';\nimport { MatIcon } from '@angular/material/icon';\n\n@Component({\n\tselector: 'autocomplete-clear-button',\n\tstandalone: true,\n\timports: [MatIconButton, MatIcon],\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\ttemplate: `\n\t\t<button\n\t\t\tmat-icon-button\n\t\t\ttype=\"button\"\n\t\t\taria-label=\"Clear value\"\n\t\t\tstyle=\"padding: 0 !important; width: 32px; height: 32px;\"\n\t\t\t(click)=\"clicked.emit($event)\">\n\t\t\t<mat-icon>close</mat-icon>\n\t\t</button>\n\t`,\n})\nexport class AutocompleteClearButtonComponent {\n\treadonly clicked = output<MouseEvent>();\n}\n","import { Component, output, signal, ChangeDetectionStrategy } from '@angular/core';\nimport { MatIconButton } from '@angular/material/button';\nimport { MatIcon } from '@angular/material/icon';\n\n@Component({\n\tselector: 'autocomplete-dropdown-button',\n\tstandalone: true,\n\timports: [MatIconButton, MatIcon],\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\ttemplate: `\n\t\t<button\n\t\t\tmat-icon-button\n\t\t\ttype=\"button\"\n\t\t\tstyle=\"padding: 0 !important; width: 32px; height: 32px;\"\n\t\t\t[attr.aria-label]=\"expanded() ? 'Close options' : 'Open options'\"\n\t\t\t(click)=\"clicked.emit($event)\">\n\t\t\t<mat-icon>{{ expanded() ? 'arrow_drop_up' : 'arrow_drop_down' }}</mat-icon>\n\t\t</button>\n\t`,\n})\nexport class AutocompleteDropdownButtonComponent {\n\treadonly expanded = signal(false);\n\treadonly clicked = output<MouseEvent>();\n}\n","import { AfterViewInit, ApplicationRef, ComponentRef, createComponent, DestroyRef, Directive, ElementRef, EnvironmentInjector, inject, Renderer2 } from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { MatAutocompleteTrigger } from '@angular/material/autocomplete';\nimport { AutocompleteClearButtonComponent } from './autocomplete-clear-button.component';\nimport { AutocompleteDropdownButtonComponent } from './autocomplete-dropdown-button.component';\n\n/**\n * Single directive that adds clear and dropdown-toggle icon buttons to a\n * Material autocomplete input. Unlike {@link LibAutocompleteActionsDirective},\n * this directive is applied directly to the `input` element and does not require\n * an anchor directive or a separate suffix component.\n *\n * Usage:\n * ```html\n * <mat-form-field appearance=\"outline\">\n * <mat-label>City</mat-label>\n * <input matInput [matAutocomplete]=\"auto\" formControlName=\"city\" autocompleteSuffix />\n * <mat-autocomplete #auto=\"matAutocomplete\"> … </mat-autocomplete>\n * </mat-form-field>\n * ```\n *\n * @author Pavan Kumar Jadda\n * @since 21.8.0\n */\n@Directive({\n\tselector: 'input[matAutocomplete][autocompleteSuffix]',\n\tstandalone: true,\n})\nexport class AutocompleteSuffixDirective implements AfterViewInit {\n\tprivate readonly trigger = inject(MatAutocompleteTrigger);\n\tprivate readonly el = inject(ElementRef<HTMLInputElement>);\n\tprivate readonly renderer = inject(Renderer2);\n\tprivate readonly destroyRef = inject(DestroyRef);\n\tprivate readonly ngControl = inject(NgControl, { optional: true, self: true });\n\tprivate readonly appRef = inject(ApplicationRef);\n\tprivate readonly envInjector = inject(EnvironmentInjector);\n\n\tprivate clearRef: ComponentRef<AutocompleteClearButtonComponent> | null = null;\n\tprivate dropdownRef: ComponentRef<AutocompleteDropdownButtonComponent> | null = null;\n\tprivate isExpanded = false;\n\tprivate readonly unlisten: (() => void)[] = [];\n\n\tngAfterViewInit(): void {\n\t\tthis.createSuffixElements();\n\t\tthis.setupListeners();\n\t\tthis.updateClearButtonVisibility();\n\t}\n\n\tprivate createSuffixElements(): void {\n\t\tconst formField = this.el.nativeElement.closest('mat-form-field');\n\t\tif (!formField) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst flexContainer = formField.querySelector('.mat-mdc-form-field-flex');\n\t\tif (!flexContainer) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet suffixWrapper = flexContainer.querySelector('.mat-mdc-form-field-icon-suffix');\n\t\tif (!suffixWrapper) {\n\t\t\tsuffixWrapper = this.renderer.createElement('div');\n\t\t\tthis.renderer.addClass(suffixWrapper, 'mat-mdc-form-field-icon-suffix');\n\t\t\tthis.renderer.setAttribute(suffixWrapper, 'data-mat-icon-type', 'font');\n\t\t\tthis.renderer.appendChild(flexContainer, suffixWrapper);\n\t\t}\n\n\t\tconst container = this.renderer.createElement('div');\n\t\tthis.renderer.setStyle(container, 'display', 'flex');\n\t\tthis.renderer.setStyle(container, 'align-items', 'center');\n\n\t\tthis.clearRef = createComponent(AutocompleteClearButtonComponent, { environmentInjector: this.envInjector });\n\t\tthis.appRef.attachView(this.clearRef.hostView);\n\t\tthis.clearRef.instance.clicked.subscribe((e) => this.clearInput(e));\n\t\tthis.renderer.setStyle(this.clearRef.location.nativeElement, 'visibility', 'hidden');\n\n\t\tthis.dropdownRef = createComponent(AutocompleteDropdownButtonComponent, { environmentInjector: this.envInjector });\n\t\tthis.appRef.attachView(this.dropdownRef.hostView);\n\t\tthis.dropdownRef.instance.clicked.subscribe((e) => this.togglePanel(e));\n\n\t\tthis.renderer.appendChild(container, this.clearRef.location.nativeElement);\n\t\tthis.renderer.appendChild(container, this.dropdownRef.location.nativeElement);\n\t\tthis.renderer.appendChild(suffixWrapper, container);\n\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.clearRef?.destroy();\n\t\t\tthis.dropdownRef?.destroy();\n\t\t});\n\t}\n\n\tprivate setupListeners(): void {\n\t\tthis.unlisten.push(this.renderer.listen(this.el.nativeElement, 'input', () => this.updateClearButtonVisibility()));\n\n\t\tconst autocomplete = this.trigger.autocomplete;\n\n\t\tconst openedSub = autocomplete.opened.subscribe(() => {\n\t\t\tthis.isExpanded = true;\n\t\t\tthis.updateDropdownIcon();\n\t\t\tthis.scrollSelectedOptionIntoView();\n\t\t});\n\t\tconst closedSub = autocomplete.closed.subscribe(() => {\n\t\t\tthis.isExpanded = false;\n\t\t\tthis.updateDropdownIcon();\n\t\t\tthis.updateClearButtonVisibility();\n\t\t});\n\n\t\tconst selectedSub = autocomplete.optionSelected.subscribe(() => this.updateClearButtonVisibility());\n\n\t\tlet valueChangesSub: { unsubscribe(): void } | null = null;\n\t\tif (this.ngControl?.control) {\n\t\t\tvalueChangesSub = this.ngControl.control.valueChanges.subscribe(() => this.updateClearButtonVisibility());\n\t\t}\n\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.unlisten.forEach((fn) => fn());\n\t\t\topenedSub.unsubscribe();\n\t\t\tclosedSub.unsubscribe();\n\t\t\tselectedSub.unsubscribe();\n\t\t\tvalueChangesSub?.unsubscribe();\n\t\t});\n\t}\n\n\tprivate clearInput(event: Event): void {\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.trigger.closePanel();\n\t\tthis.el.nativeElement.value = '';\n\n\t\tif (this.ngControl?.control) {\n\t\t\tthis.ngControl.control.setValue(null, { emitEvent: true });\n\t\t\tthis.ngControl.control.markAsTouched();\n\t\t}\n\n\t\tthis.updateClearButtonVisibility();\n\t\tqueueMicrotask(() => this.el.nativeElement.focus());\n\t}\n\n\tprivate togglePanel(event: Event): void {\n\t\tevent.stopPropagation();\n\t\tif (this.trigger.panelOpen) {\n\t\t\tthis.trigger.closePanel();\n\t\t} else {\n\t\t\tthis.trigger.openPanel();\n\t\t}\n\t}\n\n\tprivate updateClearButtonVisibility(): void {\n\t\tconst hasValue = !!(this.el.nativeElement.value || this.ngControl?.control?.value);\n\t\tif (this.clearRef) {\n\t\t\tthis.renderer.setStyle(this.clearRef.location.nativeElement, 'visibility', hasValue ? 'visible' : 'hidden');\n\t\t}\n\t}\n\n\tprivate scrollSelectedOptionIntoView(): void {\n\t\trequestAnimationFrame(() => {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tconst panelEl = this.trigger.autocomplete.panel?.nativeElement as HTMLElement | undefined;\n\t\t\t\tif (!panelEl) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst selected = panelEl.querySelector('.mat-mdc-option.mdc-list-item--selected') as HTMLElement | null;\n\t\t\t\tselected?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate updateDropdownIcon(): void {\n\t\tif (this.dropdownRef) {\n\t\t\tthis.dropdownRef.instance.expanded.set(this.isExpanded);\n\t\t}\n\t}\n}\n","export class PrintOptions {\n\tprintSectionId = '';\n\tprintTitle = '';\n\tuseExistingCss = false;\n\tbodyClass = '';\n\topenNewTab = false;\n\tpreviewOnly = false;\n\tcloseWindow = true;\n\tprintDelay = 0;\n\n\tconstructor(options?: Partial<PrintOptions>) {\n\t\tif (options) {\n\t\t\tObject.assign(this, options);\n\t\t}\n\t}\n}\n","import { Directive, DOCUMENT, HostListener, inject, Input } from '@angular/core';\nimport { MatPaginator } from '@angular/material/paginator';\nimport { MatTableDataSource } from '@angular/material/table';\nimport { PrintOptions } from './print-options';\n\n/**\n * Reusable Angular directory that prints given contents of HTML element\n *\n * @since 12.0.0\n * @author Pavan Kumar Jadda\n */\n@Directive({\n\tselector: 'button[ngxPrint], button[print]',\n\tstandalone: true,\n})\nexport class NgxPrintDirective<T = unknown> {\n\tdocument = inject(DOCUMENT);\n\t/**\n\t * ID of the HTML element those contents need to be printed\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() printSectionId: string | undefined;\n\t/**\n\t * Title of the HTML element those contents need to be printed\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() printTitle: string | undefined;\n\t/**\n\t * If `true`, uses CSS of HTMl element, otherwise no CSS applied\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() useExistingCss = false;\n\t/**\n\t * A delay in milliseconds to force the print dialog to wait before opened. Default: 0\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() printDelay = 0;\n\t/**\n\t * Instance of the Mat Table Data Source\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() matTableDataSource!: MatTableDataSource<T>;\n\t/**\n\t * Instance of the Mat Paginator\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() paginator!: MatPaginator;\n\t/**\n\t * ID of the Mat Paginator\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() paginatorId = '';\n\t/**\n\t * HTML tag ID of the Mat-Table Input Filter\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() inputFilterId = '';\n\t/**\n\t * If `true`, referenced table is Mat-Table\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() isMatTable = false;\n\t/**\n\t * If `true` Mat-Table paginator will be hidden\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input() hideMatTablePaginator = false;\n\n\tpublic printStyleArray: string[] = [];\n\tprintOptions = new PrintOptions();\n\n\t/**\n\t * List of Style sheet files\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\tprivate styleSheetFileArray = '';\n\n\t/**\n\t * Prevents the print dialog from opening on the window\n\t *\n\t * @memberof NgxPrintDirective\n\t */\n\t@Input() set previewOnly(value: boolean) {\n\t\tthis.printOptions = { ...this.printOptions, previewOnly: value };\n\t}\n\n\t/**\n\t * List of CSS properties that needs to be applied while printing the document\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input()\n\tset printStyle({ values }: PrintStyleParams) {\n\t\tfor (const key in values) {\n\t\t\tif (Object.prototype.hasOwnProperty.call(values, key)) {\n\t\t\t\tthis.printStyleArray.push((key + JSON.stringify(values[key])).replace(/['\"]+/g, ''));\n\t\t\t}\n\t\t}\n\t\tthis.returnStyleValues();\n\t}\n\n\t/**\n\t * Sets given style sheet files to print document\n\t *\n\t * @param cssList Comma separated value of CSS file names\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@Input()\n\tset styleSheetFile(cssList: string) {\n\t\tif (cssList.indexOf(',') !== -1) {\n\t\t\tconst cssFileArray = cssList.split(',');\n\t\t\tfor (const cssFileName of cssFileArray) {\n\t\t\t\tthis.styleSheetFileArray = this.styleSheetFileArray + NgxPrintDirective.linkTagFn(cssFileName);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.styleSheetFileArray = NgxPrintDirective.linkTagFn(cssList);\n\t\t}\n\t}\n\n\t/**\n\t * Build link HTMl tag based on given file name\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\tprivate static linkTagFn(cssFileName: string): string {\n\t\treturn `<link rel=\"stylesheet\" type=\"text/css\" href=\"${cssFileName}\">`;\n\t}\n\n\t/**\n\t * Gets HTML element by tag name\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\tprivate static getElementTag(tag: keyof HTMLElementTagNameMap): string {\n\t\tconst html: string[] = [];\n\t\tconst elements = document.getElementsByTagName(tag);\n\t\t// @ts-ignore\n\t\tfor (const element of elements) {\n\t\t\thtml.push(element.outerHTML);\n\t\t}\n\t\treturn html.join('\\r\\n');\n\t}\n\n\t/**\n\t * Print the element upon clicking the button\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\t@HostListener('click')\n\tpublic print(): void {\n\t\t//Hide paginator for Material table\n\t\tif (this.isMatTable && this.hideMatTablePaginator && this.matTableDataSource) {\n\t\t\tthis.matTableDataSource.paginator = null;\n\t\t}\n\n\t\tsetTimeout(() => {\n\t\t\tif (this.isMatTable) {\n\t\t\t\tthis.hideMatPaginatorBeforePrinting();\n\t\t\t}\n\n\t\t\t// Do something after\n\t\t\tlet printContents;\n\t\t\tlet popupWin;\n\t\t\tlet styles = '';\n\t\t\tlet links = '';\n\n\t\t\tif (this.useExistingCss) {\n\t\t\t\tstyles = NgxPrintDirective.getElementTag('style');\n\t\t\t\tlinks = NgxPrintDirective.getElementTag('link');\n\t\t\t}\n\t\t\tif (this.printSectionId) {\n\t\t\t\tprintContents = this.document.getElementById(this.printSectionId)?.innerHTML;\n\t\t\t\tpopupWin = window.open('', '_blank', 'top=0,left=0,height=auto,width=auto');\n\t\t\t\tpopupWin?.document.open();\n\t\t\t\tpopupWin?.document.write(`\n <html lang=\"en-us\">\n <head>\n <title>${this.printTitle ? this.printTitle : ''}</title>\n ${this.returnStyleValues()}\n ${this.styleSheetFileArray}\n ${styles}\n ${links}\n </head>\n <body>\n ${printContents}\n <script defer>\n function triggerPrint() {\n window.removeEventListener('load', triggerPrint, false);\n setTimeout(() => {\n window.print();\n setTimeout(function() { window.close(); }, 0);\n }, ${this.printDelay});\n }\n window.addEventListener('load', triggerPrint, false);\n </script>\n </body>\n </html>`);\n\n\t\t\t\tpopupWin?.document.close();\n\n\t\t\t\t//Revert back the mat-paginator after printing\n\t\t\t\tif (this.isMatTable) {\n\t\t\t\t\tthis.showMatPaginatorAfterPrinting();\n\t\t\t\t}\n\t\t\t}\n\t\t}, 1000); //1 second timeout to hide paginator\n\t}\n\n\t/**\n\t * Hide Mat Paginator before Printing\n\t *\n\t * @since 12.0.1\n\t * @author Pavan Kumar Jadda\n\t */\n\tprivate hideMatPaginatorBeforePrinting(): void {\n\t\t// @ts-ignore\n\t\tthis.document.getElementById(this.paginatorId).style.display = 'none';\n\t\tif (this.document.getElementById(this.inputFilterId) != null) {\n\t\t\t// @ts-ignore\n\t\t\tthis.document.getElementById(this.inputFilterId).style.display = 'none';\n\t\t}\n\t}\n\n\t/**\n\t * Show Mat Paginator after Printing\n\t *\n\t * @since 12.0.1\n\t * @author Pavan Kumar Jadda\n\t */\n\tprivate showMatPaginatorAfterPrinting(): void {\n\t\tthis.matTableDataSource.paginator = this.paginator;\n\t\t// @ts-ignore\n\t\tthis.document.getElementById(this.paginatorId).style.display = 'block';\n\t\tif (this.document.getElementById(this.inputFilterId) != null) {\n\t\t\t// @ts-ignore\n\t\t\tthis.document.getElementById(this.inputFilterId).style.display = 'block';\n\t\t}\n\t}\n\n\t/**\n\t * @returns the string that create the stylesheet which will be injected later within <style></style> tag. Join/replace to transform an array objects to css-styled string\n\t *\n\t * @since 12.0.0\n\t * @author Pavan Kumar Jadda\n\t */\n\tprivate returnStyleValues(): string {\n\t\treturn `<style> ${this.printStyleArray.join(' ').replace(/,/g, ';')} </style>`;\n\t}\n}\n\ninterface PrintStyleParams {\n\tvalues: Record<string, Record<string, string>>;\n}\n","import { Directive, HostListener, input, OnDestroy, OnInit, output } from '@angular/core';\nimport { Subject, Subscription, throttleTime } from 'rxjs';\n\n@Directive({\n\tselector: '[preventMultipleClicks]',\n\tstandalone: true,\n})\nexport class PreventMultipleClicksDirective implements OnInit, OnDestroy {\n\tthrottleTime = input(2000);\n\tthrottleClick = output<Event>();\n\tprivate readonly clicks = new Subject<Event>();\n\tprivate subscription: Subscription | undefined;\n\n\t/**\n\t * Intercepts click event and stops default navigation. After first click set {@link throttleTime} to 2000 to prevent duplicate clicks\n\t *\n\t * @param event DOM event\n\t *\n\t * @author Pavan Kumar Jadda\n\t * @since 2.3.27\n\t */\n\t@HostListener('click', ['$event'])\n\tclickEvent(event: Event): void {\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.clicks.next(event);\n\t}\n\n\tngOnInit(): void {\n\t\tthis.subscription = this.clicks.pipe(throttleTime(this.throttleTime())).subscribe((e) => this.throttleClick.emit(e));\n\t}\n\n\tngOnDestroy(): void {\n\t\tthis.subscription?.unsubscribe();\n\t}\n}\n","import { Directive, DOCUMENT, effect, ElementRef, inject, input, OnInit, signal } from '@angular/core';\n\n@Directive()\nexport abstract class BaseButtonDirective implements OnInit {\n\ticon = input<string>('');\n\tlabel = input<string>('');\n\tloadingLabel = input<string>('Loading...');\n\tloading = input<boolean>(false);\n\telementRef = inject(ElementRef);\n\tdocument = inject(DOCUMENT);\n\n\tprotected originalText = signal('');\n\tprotected iconSpan = signal<HTMLElement | null>(null);\n\n\tconstructor() {\n\t\tthis.elementRef.nativeElement.classList.add('btn');\n\n\t\teffect(() => {\n\t\t\t// If a consumer provides a label input, always use it. Otherwise, fall back to the initially captured host text.\n\t\t\tconst nextText = this.label() || this.originalText();\n\t\t\tthis.originalText.set(nextText);\n\n\t\t\t// Keep the icon element in sync with [icon]\n\t\t\tthis.createIcon();\n\n\t\t\t// Re-render on any relevant signal change\n\t\t\tthis.updateContent();\n\t\t});\n\t}\n\n\tngOnInit(): void {\n\t\t// Capture original text before creating icon\n\t\tthis.originalText.set(this.elementRef.nativeElement.textContent?.trim() || 'Button');\n\n\t\t// Create icon after capturing text\n\t\tthis.createIcon();\n\t}\n\n\tprotected createIcon(): void {\n\t\tif (this.icon()) {\n\t\t\tconst iconElement = this.document.createElement('mat-icon');\n\t\t\ticonElement.classList.add('mat-icon', 'material-icons', 'pe-2');\n\t\t\ticonElement.textContent = this.icon();\n\t\t\tthis.iconSpan.set(iconElement);\n\t\t}\n\t}\n\n\tprotected updateContent(): void {\n\t\tconst element = this.elementRef.nativeElement;\n\t\telement.innerHTML = '';\n\n\t\tif (this.loading()) {\n\t\t\tthis.showLoadingState(element);\n\t\t} else {\n\t\t\tthis.showNormalState(element);\n\t\t}\n\t}\n\n\tprotected showLoadingState(element: HTMLElement): void {\n\t\tconst newSpan = this.document.createElement('span');\n\t\tnewSpan.classList.add('spinner-border', 'spinner-border-sm', 'me-2');\n\t\tnewSpan.setAttribute('role', 'status');\n\n\t\telement.appendChild(newSpan);\n\t\telement.appendChild(this.document.createTextNode(this.loadingLabel()));\n\t\telement.setAttribute('disabled', 'true');\n\t}\n\n\tprotected showNormalState(element: HTMLElement): void {\n\t\tconst iconElement = this.iconSpan();\n\t\tif (iconElement) {\n\t\t\telement.appendChild(iconElement);\n\t\t}\n\n\t\telement.appendChild(this.document.createTextNode(this.originalText()));\n\t\telement.removeAttribute('disabled');\n\t}\n}\n","import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';\n\n@Component({\n\tstandalone: true,\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\ttemplate: ``,\n})\nexport class BaseButtonComponent {\n\t/**\n\t * Is search in progress and loading the data\n\t */\n\tloading = input(false, { transform: (value: boolean | undefined) => value ?? false });\n\n\t/**\n\t * Is button disabled\n\t */\n\tdisabled = input<boolean>(false);\n\n\t/**\n\t * Type of the button. Following values are supported. See BootStrap docs for more information\n\t * <pre>\n\t * 1. button\n\t * 2. submit\n\t * </pre>\n\t */\n\ttype = input('button');\n\n\t/**\n\t * If set, shows when action in Progress\n\t */\n\tloadingLabel = input('Saving...');\n\n\t/**\n\t * If set, shows when Delete is not in progress\n\t */\n\tlabel = input('Save');\n\n\t/**\n\t * If set, shows the icon. Otherwise, shows delete icon\n\t */\n\ticon = input('save');\n\n\t/**\n\t * If set, shows material icon otherwise hides the icons\n\t */\n\tshowIcon = input(true);\n\n\t/**\n\t * If set, sets the style of the button\n\t */\n\tstyle = input<any | null | undefined>();\n\n\t/**\n\t * If set, sets the class of the button\n\t */\n\tclasses = input('btn');\n\n\t/**\n\t * If set, sets the data-cy attribute for the button\n\t */\n\tdataCy = input('save-button');\n\n\t/**\n\t * Output event when button is clicked\n\t */\n\tonClick = output<MouseEvent>();\n\n\t/**\n\t * Output event when button is focused\n\t */\n\tonFocus = output<FocusEvent>();\n\n\t/**\n\t * Output event when button is blurred\n\t */\n\tonBlur = output<FocusEvent>();\n\n\t/**\n\t * Output event when key is pressed\n\t */\n\tonKeyDown = output<KeyboardEvent>();\n\n\t/**\n\t * Output event when key is up\n\t */\n\tonKeyUp = output<KeyboardEvent>();\n}\n","import { Component, input, ChangeDetectionStrategy } from '@angular/core';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatAnchor } from '@angular/material/button';\nimport { BaseButtonComponent } from '../base-button/base-button.component';\n\n@Component({\n\tselector: 'bs-link-button',\n\timports: [MatIcon, MatAnchor],\n\ttemplate: `\n\t\t<a\n\t\t\ttype=\"{{ type() }}\"\n\t\t\tclass=\"{{ classes() }}\"\n\t\t\t(click)=\"onClick.emit($event)\"\n\t\t\t(focus)=\"onFocus.emit($event)\"\n\t\t\t(blur)=\"onBlur.emit($event)\"\n\t\t\t(keydown)=\"onKeyDown.emit($event)\"\n\t\t\t(keyup)=\"onKeyUp.emit($event)\"\n\t\t\t[disabled]=\"disabled()\"\n\t\t\t[type]=\"type()\"\n\t\t\t[style]=\"style()\"\n\t\t\t[attr.data-cy]=\"'bs-link-button'\"\n\t\t\tmat-button>\n\t\t\t<mat-icon>{{ icon() }}</mat-icon>\n\t\t\t{{ label() }}\n\t\t</a>\n\t`,\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\tstyleUrls: ['../../../../assets/app-buttons.css'],\n})\nexport class BsLinkButtonComponent extends BaseButtonComponent {\n\toverride label = input('Edit');\n\toverride icon = input('search');\n\toverride classes = input('btn text-primary');\n\n\tconstructor() {\n\t\tsuper();\n\t}\n}\n","import { Directive, DOCUMENT, effect, ElementRef, inject, input } from '@angular/core';\n\n@Directive({\n\tselector: '[bsLinkButton]',\n})\nexport class BsLinkButtonDirective {\n\ticon = input<string>('search');\n\telementRef = inject(ElementRef);\n\tdocument = inject(DOCUMENT);\n\n\tconstructor() {\n\t\tthis.elementRef.nativeElement.classList.add('btn', 'text-primary');\n\n\t\teffect(() => {\n\t\t\tthis.updateContent();\n\t\t});\n\t}\n\n\tprivate updateContent(): void {\n\t\t// if icon present, add material-icons class and set text content\n\t\tif (this.icon()) {\n\t\t\tconst iconSpan = this.document.createElement('span');\n\t\t\ticonSpan.classList.add('material-icons', 'pe-2');\n\t\t\ticonSpan.textContent = this.icon();\n\t\t\tthis.elementRef.nativeElement.appendChild(iconSpan);\n\t\t}\n\t}\n}\n","import { Directive, ElementRef, inject } from '@angular/core';\n\n@Directive({\n\tselector: '[closeButton]',\n})\nexport class CloseButtonDirective {\n\telementRef = inject(ElementRef);\n\n\tconstructor() {\n\t\tthis.elementRef.nativeElement.classList.add('secondary-button');\n\t}\n}\n","import { Component, input, ChangeDetectionStrategy } from '@angular/core';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatButtonModule } from '@angular/material/button';\nimport { BaseButtonComponent } from '../base-button/base-button.component';\n\n@Component({\n\tselector: 'delete-button',\n\timports: [MatButtonModule, MatIconModule],\n\ttemplate: `\n\t\t<button\n\t\t\tmat-raised-button\n\t\t\tclass=\"btn {{ classes() }}\"\n\t\t\t(click)=\"onClick.emit($event)\"\n\t\t\t(focus)=\"onFocus.emit($event)\"\n\t\t\t(blur)=\"onBlur.emit($event)\"\n\t\t\t(keydown)=\"onKeyDown.emit($event)\"\n\t\t\t(keyup)=\"onKeyUp.emit($event)\"\n\t\t\t[disabled]=\"disabled() || loading()\"\n\t\t\t[type]=\"type()\"\n\t\t\t[style]=\"style()\"\n\t\t\t[attr.data-cy]=\"'delete-button'\">\n\t\t\t@if (loading()) {\n\t\t\t\t<span aria-hidden=\"true\" class=\"spinner-border spinner-border-sm\" role=\"status\"></span>\n\t\t\t}\n\t\t\t@if (!loading()) {\n\t\t\t\t<mat-icon>{{ icon() }}</mat-icon>\n\t\t\t}\n\t\t\t{{ loading() ? loadingLabel() : label() }}\n\t\t</button>\n\t`,\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\tstyleUrls: ['../../../../assets/app-buttons.css'],\n})\nexport class DeleteButtonComponent extends BaseButtonComponent {\n\toverride loadingLabel = input('Deleting...');\n\toverride label = input('Delete');\n\toverride icon = input('delete');\n\toverride classes = input('delete-button');\n\n\tconstructor() {\n\t\tsuper();\n\t}\n}\n","import { Directive, input } from '@angular/core';\nimport { BaseButtonDirective } from '../base-button/base-button.directive';\n\n@Directive({\n\tselector: '[deleteButton]',\n})\nexport class DeleteButtonDirective extends BaseButtonDirective {\n\toverride icon = input<string>('delete');\n\toverride loadingLabel = input<string>('Deleting...');\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.elementRef.nativeElement.classList.add('delete-button');\n\t}\n}\n","import { inject, Injectable } from '@angular/core';\nimport { MatIconRegistry } from '@angular/material/icon';\nimport { DomSanitizer } from '@angular/platform-browser';\n\nexport const EDIT_SOLID_SVG_ICON_NAME = 'edit-solid';\n\nconst EDIT_ICON = `\n<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 512 512\">\n <path d=\"M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L368 46.1 465.9 144 490.3 119.6c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.7 15.7-7.4 21.9-13.5L432 177.9 334.1 80 172.4 241.7zM96 64C43 64 0 107 0 160L0 416c0 53 43 96 96 96l256 0c53 0 96-43 96-96l0-96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-256c0-17.7 14.3-32 32-32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L96 64z\"/>\n</svg>\n`;\n\n/** Registers the edit-solid SVG icon once for the application. */\n@Injectable({ providedIn: 'root' })\nexport class EditSolidSvgIconRegistry {\n\tconstructor() {\n\t\tconst iconRegistry = inject(MatIconRegistry);\n\t\tconst sanitizer = inject(DomSanitizer);\n\n\t\ticonRegistry.addSvgIconLiteral(\n\t\t\tEDIT_SOLID_SVG_ICON_NAME,\n\t\t\tsanitizer.bypassSecurityTrustHtml(EDIT_ICON),\n\t\t);\n\t}\n}\n","import { ChangeDetectionStrategy, Component, inject, input } from '@angular/core';\nimport { MatIconModule } from '@angular/material/icon';\nimport { EDIT_SOLID_SVG_ICON_NAME, EditSolidSvgIconRegistry } from './edit-solid-svg-icon.registry';\n\n@Component({\n\tselector: 'edit-solid-svg',\n\timports: [MatIconModule],\n\ttemplate: `\n\t\t<mat-icon\n\t\t\t[class]=\"iconClass()\"\n\t\t\t[style.font-size]=\"size()\"\n\t\t\taria-hidden=\"true\"\n\t\t\t[svgIcon]=\"svgIconName\" />\n\t`,\n\tstyles: [\n\t\t`\n\t\t\t:host {\n\t\t\t\tdisplay: contents;\n\t\t\t}\n\n\t\t\tmat-icon {\n\t\t\t\tdisplay: inline-flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\tflex-shrink: 0;\n\t\t\t\twidth: 1em;\n\t\t\t\theight: 1em;\n\t\t\t\tline-height: 1;\n\t\t\t\toverflow: visible;\n\t\t\t\tvertical-align: middle;\n\t\t\t}\n\n\t\t\tmat-icon svg {\n\t\t\t\tdisplay: block;\n\t\t\t}\n\t\t`,\n\t],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class EditSolidSvgComponent {\n\tprotected readonly svgIconName = EDIT_SOLID_SVG_ICON_NAME;\n\ticonClass = input('');\n\tsize = input('1rem');\n\n\t// Ensure the SVG icon is registered once per application.\n\tprivate readonly _ = inject(EditSolidSvgIconRegistry);\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { EditSolidSvgComponent } from '../../../svg-icons/edit-solid-svg/edit-solid-svg.component';\nimport { BaseButtonComponent } from '../base-button/base-button.component';\n\n@Component({\n\tselector: 'edit-bs-button',\n\timports: [MatButtonModule, EditSolidSvgComponent],\n\ttemplate: `\n\t\t<button\n\t\t\tcolor=\"primary\"\n\t\t\tclass=\"{{ classes() }}\"\n\t\t\t(click)=\"onClick.emit($event)\"\n\t\t\t(focus)=\"onFocus.emit($event)\"\n\t\t\t(blur)=\"onBlur.emit($event)\"\n\t\t\t(keydown)=\"onKeyDown.emit($event)\"\n\t\t\t(keyup)=\"onKeyUp.emit($event)\"\n\t\t\t[disabled]=\"disabled()\"\n\t\t\t[type]=\"type()\"\n\t\t\t[style]=\"style()\"\n\t\t\t[attr.data-cy]=\"'edit-bs-button'\"\n\t\t\tmat-button>\n\t\t\t<edit-solid-svg></edit-solid-svg>\n\t\t\t{{ label() }}\n\t\t</button>\n\t`,\n\tchangeDetection: ChangeDetectionStrategy.Eager,\n\tstyleUrls: ['../../../../assets/app-buttons.css'],\n})\nexport class EditBsButtonComponent extends BaseButtonComponent {\n\toverride label = input('Edit');\n\toverride classes = input('text-primary');\n\n\tconstructor() {\n\t\tsuper();\n\t}\n}\n","import { ComponentRef, ViewContainerRef } from '@angular/core';\nimport { EditSolidSvgComponent } from './edit-solid-svg.component';\n\n/**\n * Mounts {@link EditSolidSvgComponent} into a host element with a text label.\n * Destroys any previously mounted icon component reference.\n */\nexport function mountEditSolidSvgIcon(\n\tviewContainerRef: ViewContainerRef,\n\tdocument: Document,\n\thost: HTMLElement,\n\tlabel: string,\n\ticonComponentRef: ComponentRef<EditSolidSvgComponent> | null,\n): ComponentRef<EditSolidSvgComponent> {\n\ticonComponentRef?.destroy();\n\n\tconst componentRef = viewContainerRef.createComponent(EditSolidSvgComponent);\n\n\thost.textContent = '';\n\tho