@sneat/ui 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/esm2022/index.js +4 -0
  2. package/esm2022/index.js.map +1 -0
  3. package/esm2022/lib/components/index.js +3 -0
  4. package/esm2022/lib/components/index.js.map +1 -0
  5. package/esm2022/lib/components/sneat-base-modal.component.js +24 -0
  6. package/esm2022/lib/components/sneat-base-modal.component.js.map +1 -0
  7. package/esm2022/lib/components/sneat-base.component.js +58 -0
  8. package/esm2022/lib/components/sneat-base.component.js.map +1 -0
  9. package/esm2022/lib/focus.js +17 -0
  10. package/esm2022/lib/focus.js.map +1 -0
  11. package/esm2022/lib/selector/index.js +7 -0
  12. package/esm2022/lib/selector/index.js.map +1 -0
  13. package/esm2022/lib/selector/multi-selector/index.js +3 -0
  14. package/esm2022/lib/selector/multi-selector/index.js.map +1 -0
  15. package/esm2022/lib/selector/multi-selector/multi-selector.component.js +70 -0
  16. package/esm2022/lib/selector/multi-selector/multi-selector.component.js.map +1 -0
  17. package/esm2022/lib/selector/multi-selector/multi-selector.service.js +49 -0
  18. package/esm2022/lib/selector/multi-selector/multi-selector.service.js.map +1 -0
  19. package/esm2022/lib/selector/select-from-list/index.js +2 -0
  20. package/esm2022/lib/selector/select-from-list/index.js.map +1 -0
  21. package/esm2022/lib/selector/select-from-list/select-from-list.component.js +234 -0
  22. package/esm2022/lib/selector/select-from-list/select-from-list.component.js.map +1 -0
  23. package/esm2022/lib/selector/selector-base.component.js +36 -0
  24. package/esm2022/lib/selector/selector-base.component.js.map +1 -0
  25. package/esm2022/lib/selector/selector-base.service.js +49 -0
  26. package/esm2022/lib/selector/selector-base.service.js.map +1 -0
  27. package/esm2022/lib/selector/selector-interfaces.js +2 -0
  28. package/esm2022/lib/selector/selector-interfaces.js.map +1 -0
  29. package/esm2022/lib/selector/selector-options.js +2 -0
  30. package/esm2022/lib/selector/selector-options.js.map +1 -0
  31. package/esm2022/sneat-ui.js +5 -0
  32. package/esm2022/sneat-ui.js.map +1 -0
  33. package/lib/components/sneat-base-modal.component.d.ts +9 -0
  34. package/lib/components/sneat-base.component.d.ts +28 -0
  35. package/lib/focus.d.ts +3 -0
  36. package/lib/selector/multi-selector/multi-selector.component.d.ts +17 -0
  37. package/lib/selector/multi-selector/multi-selector.service.d.ts +9 -0
  38. package/lib/selector/select-from-list/select-from-list.component.d.ts +60 -0
  39. package/lib/selector/selector-base.component.d.ts +18 -0
  40. package/lib/selector/selector-base.service.d.ts +11 -0
  41. package/lib/selector/selector-interfaces.d.ts +17 -0
  42. package/lib/selector/selector-options.d.ts +15 -0
  43. package/package.json +14 -2
  44. package/sneat-ui.d.ts +5 -0
  45. package/eslint.config.js +0 -7
  46. package/ng-package.json +0 -7
  47. package/project.json +0 -38
  48. package/src/lib/components/sneat-base-modal.component.ts +0 -22
  49. package/src/lib/components/sneat-base.component.spec.ts +0 -71
  50. package/src/lib/components/sneat-base.component.ts +0 -78
  51. package/src/lib/focus.ts +0 -19
  52. package/src/lib/selector/multi-selector/multi-selector.component.html +0 -26
  53. package/src/lib/selector/multi-selector/multi-selector.component.spec.ts +0 -147
  54. package/src/lib/selector/multi-selector/multi-selector.component.ts +0 -79
  55. package/src/lib/selector/multi-selector/multi-selector.service.spec.ts +0 -91
  56. package/src/lib/selector/multi-selector/multi-selector.service.ts +0 -49
  57. package/src/lib/selector/select-from-list/select-from-list.component.html +0 -210
  58. package/src/lib/selector/select-from-list/select-from-list.component.spec.ts +0 -297
  59. package/src/lib/selector/select-from-list/select-from-list.component.ts +0 -283
  60. package/src/lib/selector/selector-base.component.ts +0 -43
  61. package/src/lib/selector/selector-base.service.ts +0 -62
  62. package/src/lib/selector/selector-interfaces.ts +0 -28
  63. package/src/lib/selector/selector-options.ts +0 -18
  64. package/src/test-setup.ts +0 -3
  65. package/tsconfig.json +0 -13
  66. package/tsconfig.lib.json +0 -19
  67. package/tsconfig.lib.prod.json +0 -7
  68. package/tsconfig.spec.json +0 -31
  69. package/vite.config.mts +0 -10
  70. /package/{src/index.ts → index.d.ts} +0 -0
  71. /package/{src/lib/components/index.ts → lib/components/index.d.ts} +0 -0
  72. /package/{src/lib/selector/index.ts → lib/selector/index.d.ts} +0 -0
  73. /package/{src/lib/selector/multi-selector/index.ts → lib/selector/multi-selector/index.d.ts} +0 -0
  74. /package/{src/lib/selector/select-from-list/index.ts → lib/selector/select-from-list/index.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-from-list.component.js","sourceRoot":"","sources":["../../../../../../../libs/ui/src/lib/selector/select-from-list/select-from-list.component.ts","../../../../../../../libs/ui/src/lib/selector/select-from-list/select-from-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n EventEmitter,\n forwardRef,\n input,\n Input,\n OnChanges,\n OnDestroy,\n Output,\n signal,\n SimpleChanges,\n ViewChild,\n inject,\n} from '@angular/core';\nimport {\n ControlValueAccessor,\n FormsModule,\n NG_VALUE_ACCESSOR,\n} from '@angular/forms';\nimport { RouterModule } from '@angular/router';\nimport {\n IonButton,\n IonButtons,\n IonCheckbox,\n IonIcon,\n IonInput,\n IonItem,\n IonItemDivider,\n IonItemGroup,\n IonLabel,\n IonList,\n IonRadio,\n IonRadioGroup,\n IonSelect,\n IonSelectOption,\n IonSpinner,\n} from '@ionic/angular/standalone';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { NEVER, Observable, Subject, takeUntil } from 'rxjs';\nimport { ISelectItem } from '../selector-interfaces';\n\n@Component({\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SelectFromListComponent),\n multi: true,\n },\n ],\n imports: [\n FormsModule,\n RouterModule,\n IonItem,\n IonIcon,\n IonSelect,\n IonSelectOption,\n IonButtons,\n IonButton,\n IonInput,\n IonItemDivider,\n IonRadioGroup,\n IonList,\n IonLabel,\n IonCheckbox,\n IonRadio,\n IonItemGroup,\n IonSpinner,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n selector: 'sneat-select-from-list',\n templateUrl: './select-from-list.component.html',\n})\nexport class SelectFromListComponent\n implements ControlValueAccessor, OnChanges, OnDestroy\n{\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n\n @Input() value: string | undefined = '';\n @Output() readonly valueChange = new EventEmitter<string>();\n\n @Input() filterLabel = 'Find';\n @Input() label = 'Please choose';\n @Input() listLabel?: 'divider';\n @Input() listLabelColor?:\n | 'light'\n | 'medium'\n | 'primary'\n | 'secondary'\n | 'tertiary';\n @Input() isFilterable?: boolean;\n @Input() isLoading?: boolean;\n @Input() items?: readonly ISelectItem[];\n @Input() items$: Observable<ISelectItem[]> = NEVER;\n\n private $items = signal<undefined | readonly ISelectItem[]>(undefined);\n\n @Input() lastItemLines?: 'none' | 'inset' | 'full';\n @Input() labelPlacement?: 'start' | 'end' | 'fixed' | 'stacked';\n @Input() justify?: 'start' | 'end' | 'space-between';\n @Input() other: 'top' | 'bottom' | 'none' = 'none';\n @Input() canAdd = false;\n @Input() filterItem?: (item: ISelectItem, filter: string) => boolean;\n\n @Output() readonly filterChanged = new EventEmitter<string>();\n\n @Input() selectMode: 'single' | 'multiple' = 'single';\n\n @Input() isReadonly = false;\n\n public $isProcessing = input<boolean>();\n\n @Input() sortBy?: 'title' | 'id';\n\n // @Input() ngModel?: string;\n // @Output() readonly ngModelChange = new EventEmitter<string>();\n\n private destroyed = new Subject<void>();\n\n @ViewChild(IonInput, { static: false }) addInput?: IonInput;\n @ViewChild('filterInput', { static: false }) filterInput?: IonInput;\n @ViewChild('selectInput', { static: false }) selectInput?: IonSelect;\n\n protected readonly $displayItems = signal<undefined | readonly ISelectItem[]>(\n undefined,\n );\n\n protected readonly $hiddenCount = computed(() => {\n return (this.$items()?.length || 0) - (this.$displayItems()?.length || 0);\n });\n\n protected isDisabled = false;\n\n protected readonly $filter = signal('');\n\n public focus(): void {\n setTimeout(() => {\n this.filterInput\n ?.setFocus()\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to set focus to filter input',\n ),\n );\n }, 100);\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['items']) {\n this.$items.set(this.items);\n this.applyFilter();\n }\n if (changes['items$'] && this.items$) {\n this.items$?.pipe(takeUntil(this.destroyed)).subscribe((items) => {\n this.$items.set(items);\n this.applyFilter();\n });\n }\n }\n\n private applyFilter(): void {\n const f = this.$filter().trim().toLowerCase();\n // console.log('SelectFromListComponent.applyFilter', f);\n const items = this.$items();\n let displayItems = f\n ? items?.filter(\n (v) =>\n v.title.toLowerCase().includes(f) ||\n v.longTitle?.toLowerCase().includes(f) ||\n (this.filterItem && this.filterItem(v, f)),\n )\n : items;\n if (displayItems && this.sortBy) {\n switch (this.sortBy) {\n case 'title':\n displayItems = [...displayItems].toSorted((a, b) =>\n a.title.localeCompare(b.title),\n );\n break;\n case 'id':\n displayItems = [...displayItems].toSorted((a, b) =>\n a.id.localeCompare(b.id),\n );\n break;\n }\n }\n this.$displayItems.set(displayItems);\n }\n\n protected select(item: ISelectItem): void {\n switch (this.selectMode) {\n case 'multiple':\n return;\n case 'single':\n this.$selectedItem.set(item);\n this.onChange(item.id);\n if (this.$filter()) {\n this.clearFilter();\n }\n }\n }\n\n protected readonly $selectedItem = signal<ISelectItem | undefined>(undefined);\n\n protected onRadioChanged(event: Event): void {\n if (this.selectMode !== 'single') {\n return;\n }\n const value = (event as CustomEvent).detail['value'] as string;\n this.value = value;\n this.onChange(value);\n this.clearFilter();\n }\n\n protected onSelectChanged(): void {\n // this.value = (event as CustomEvent).detail['value'] as string;\n this.onChange(this.value || '');\n }\n\n protected onChange = (id: string) => {\n this.value = id;\n if (this.$selectedItem()?.id !== id) {\n const selectedItem = this.items?.find((item) => item.id === id);\n this.$selectedItem.set(selectedItem);\n }\n this.valueChange.emit(this.value);\n };\n\n onTouched: () => void = () => void 0;\n\n registerOnChange(fn: (v: unknown) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.isDisabled = isDisabled;\n }\n\n writeValue(obj: unknown): void {\n this.value = obj as string;\n }\n\n protected onFilterChanged(\n event: CustomEvent,\n source: 'ionChange' | 'ionInput',\n ): void {\n void source;\n this.$filter.set(event.detail.value || '');\n this.applyFilter();\n this.filterChanged.emit(this.$filter());\n }\n\n protected clearFilter(): void {\n this.$filter.set('');\n this.filterChanged.emit(this.$filter());\n this.applyFilter();\n }\n\n protected deselect(): void {\n this.value = '';\n this.onChange(this.value);\n }\n\n protected onAdd(event: Event): void {\n event.preventDefault();\n this.value = this.$filter();\n this.onChange(this.value);\n }\n\n protected onCheckboxChange(event: CustomEvent, item: ISelectItem): void {\n void event;\n void item;\n }\n\n ngOnDestroy(): void {\n this.destroyed.next();\n }\n}\n","@if (value) {\n <ion-item\n [lines]=\"lastItemLines\"\n [class]=\"{ 'sneat-tiny-end-padding': !isReadonly }\"\n >\n @if ($selectedItem()?.iconName) {\n <ion-icon\n slot=\"start\"\n [name]=\"$selectedItem()?.iconName\"\n [color]=\"\n $selectedItem()?.iconColor || $selectedItem()?.labelColor || 'medium'\n \"\n />\n }\n <ion-select\n #selectInput\n interface=\"popover\"\n [label]=\"label\"\n [(ngModel)]=\"value\"\n (ionChange)=\"onSelectChanged()\"\n [disabled]=\"isReadonly || !!$isProcessing()\"\n >\n @for (item of items; track item.id) {\n <ion-select-option [value]=\"item.id\">\n {{ item.emoji }}\n @if (item.shortTitle) {\n {{ item.shortTitle }}\n } @else {\n {{ item.title }}\n }\n </ion-select-option>\n }\n <!--\t\t<ion-select-option value=\"other\">OTHER</ion-select-option>-->\n </ion-select>\n @if ($isProcessing()) {\n <ion-spinner name=\"lines-small\" color=\"medium\" slot=\"end\" />\n } @else if (!isReadonly) {\n <ion-buttons slot=\"end\" class=\"ion-no-margin\">\n <ion-button color=\"medium\" title=\"Deselect\" (click)=\"deselect()\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n} @else {\n @if (isFilterable) {\n <ion-item class=\"sneat-tiny-end-padding\">\n <ion-input\n #filterInput\n color=\"medium\"\n placeholder=\"filter\"\n [label]=\"filterLabel\"\n [value]=\"$filter()\"\n (ionChange)=\"onFilterChanged($event, 'ionChange')\"\n (ionInput)=\"onFilterChanged($event, 'ionInput')\"\n />\n @if ($filter()) {\n <ion-buttons slot=\"end\">\n @if (canAdd && $hiddenCount()) {\n <ion-button\n title=\"Use this\"\n color=\"primary\"\n (click)=\"onAdd($event)\"\n >\n <ion-icon name=\"add-outline\" />\n <ion-label>Add</ion-label>\n </ion-button>\n }\n <ion-button (click)=\"clearFilter()\" title=\"Clear filter\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n }\n\n @if (items && !items.length) {\n <ion-item-divider>\n <ion-label>No items yet.</ion-label>\n </ion-item-divider>\n }\n\n @if (labelPlacement) {\n <ion-radio-group [(ngModel)]=\"value\" (ionChange)=\"onRadioChanged($event)\">\n <ion-list class=\"ion-no-padding\" lines=\"full\">\n @if (listLabel === \"divider\") {\n <ion-item [color]=\"listLabelColor\">\n <ion-label>{{ label }}</ion-label>\n </ion-item>\n }\n @for (item of $displayItems(); track item.id) {\n <ion-item\n [lines]=\"item.description1 ? 'inset' : 'full'\"\n (click)=\"select(item)\"\n tappable=\"true\"\n >\n @switch (selectMode) {\n @case (\"multiple\") {\n <ion-checkbox\n slot=\"start\"\n [value]=\"item.id\"\n [checked]=\"false\"\n [disabled]=\"isDisabled\"\n (ionChange)=\"onCheckboxChange($event, item)\"\n />\n <ion-label>\n <!-- TODO: duplicate code with the next case -->\n {{ item.emoji }}\n @if (item.shortTitle) {\n @if (item.longTitle) {\n {{ item.longTitle }} - {{ item.shortTitle }}\n } @else {\n {{ item.title }} - {{ item.shortTitle }}\n }\n } @else if (item.longTitle) {\n {{ item.longTitle }}\n } @else {\n {{ item.title }}\n }\n </ion-label>\n }\n @case (\"single\") {\n <ion-radio\n [value]=\"item.id\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"\n justify ||\n (!labelPlacement || labelPlacement === 'start'\n ? 'space-between'\n : 'start')\n \"\n >\n {{ item.emoji }}\n @if (item.shortTitle) {\n @if (item.longTitle) {\n {{ item.longTitle }} - {{ item.shortTitle }}\n } @else {\n {{ item.title }} - {{ item.shortTitle }}\n }\n } @else if (item.longTitle) {\n {{ item.longTitle }}\n } @else {\n {{ item.title }}\n }\n </ion-radio>\n }\n }\n </ion-item>\n @if (item.description1) {\n <ion-item-divider>\n <ion-label>\n {{ item.description1 }}\n @if (item.description2) {\n <i>{{ item.description2 }}</i>\n }\n </ion-label>\n </ion-item-divider>\n }\n }\n </ion-list>\n </ion-radio-group>\n } @else {\n <ion-item-group>\n @for (item of $displayItems(); track item.id; let last = $last) {\n <ion-item\n [lines]=\"item.description1 ? 'inset' : last ? lastItemLines : 'full'\"\n button\n (click)=\"select(item)\"\n >\n @if (item.iconName) {\n <ion-icon\n slot=\"start\"\n [name]=\"item.iconName\"\n [color]=\"item.iconColor || item.labelColor || 'medium'\"\n />\n }\n @if (!labelPlacement) {\n <ion-label [color]=\"item.labelColor\">\n <span class=\"ion-margin-end\">{{ item.emoji }}</span>\n {{ item.title }}\n </ion-label>\n }\n </ion-item>\n @if (item.description1) {\n <ion-item [lines]=\"last ? lastItemLines : 'full'\">\n <ion-label color=\"medium\">\n {{ item.description1 }}\n @if (item.description2) {\n <i>{{ item.description2 }}</i>\n }\n </ion-label>\n </ion-item>\n }\n }\n </ion-item-group>\n }\n @if ($hiddenCount(); as hiddenCount) {\n <ion-item-divider>\n <ion-label color=\"medium\"\n >{{ hiddenCount }} out of {{ items?.length }} items are hidden by filter\n </ion-label>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"clearFilter()\">\n <ion-icon name=\"close-outline\" slot=\"start\" />\n <ion-label>Clear filter</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item-divider>\n }\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { Directive, inject, InjectionToken, Input, signal, } from '@angular/core';
2
+ import { SneatBaseComponent } from '../components/sneat-base.component';
3
+ import * as i0 from "@angular/core";
4
+ export const OverlayController = new InjectionToken('OverlayController');
5
+ export class SelectorBaseComponent extends SneatBaseComponent {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.overlayController = inject(OverlayController);
9
+ this.$selectedItems = signal([], ...(ngDevMode ? [{ debugName: "$selectedItems" }] : []));
10
+ }
11
+ close(event) {
12
+ event?.stopPropagation();
13
+ event?.preventDefault();
14
+ this.overlayController
15
+ .dismiss({ selectedItems: this.$selectedItems() })
16
+ .catch(this.errorLogger.logErrorHandler('failed to dismiss contact selector modal'));
17
+ }
18
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SelectorBaseComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
19
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: SelectorBaseComponent, isStandalone: true, inputs: { selectMode: "selectMode" }, usesInheritance: true, ngImport: i0 }); }
20
+ }
21
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SelectorBaseComponent, decorators: [{
22
+ type: Directive
23
+ }], propDecorators: { selectMode: [{
24
+ type: Input
25
+ }] } });
26
+ export class SelectorModalComponent extends SelectorBaseComponent {
27
+ get modalController() {
28
+ return this.overlayController;
29
+ }
30
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SelectorModalComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
31
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: SelectorModalComponent, isStandalone: true, usesInheritance: true, ngImport: i0 }); }
32
+ }
33
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SelectorModalComponent, decorators: [{
34
+ type: Directive
35
+ }] });
36
+ //# sourceMappingURL=selector-base.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-base.component.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/selector/selector-base.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,MAAM,EACN,cAAc,EACd,KAAK,EACL,MAAM,GACP,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;;AAExE,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,cAAc,CAEjD,mBAAmB,CAAC,CAAC;AAGvB,MAAM,OAAgB,qBAAyB,SAAQ,kBAAkB;IADzE;;QAIqB,sBAAiB,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAEvD,mBAAc,GAAG,MAAM,CAAe,EAAE,0DAAC,CAAC;KAarD;IAXW,KAAK,CAAC,KAAa;QAC3B,KAAK,EAAE,eAAe,EAAE,CAAC;QACzB,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,IAAI,CAAC,iBAAiB;aACnB,OAAO,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;aACjD,KAAK,CACJ,IAAI,CAAC,WAAW,CAAC,eAAe,CAC9B,0CAA0C,CAC3C,CACF,CAAC;IACN,CAAC;8GAjBmB,qBAAqB;kGAArB,qBAAqB;;2FAArB,qBAAqB;kBAD1C,SAAS;;sBAEP,KAAK;;AAoBR,MAAM,OAAgB,sBAEpB,SAAQ,qBAAwB;IAChC,IAAc,eAAe;QAC3B,OAAO,IAAI,CAAC,iBAAoC,CAAC;IACnD,CAAC;8GALmB,sBAAsB;kGAAtB,sBAAsB;;2FAAtB,sBAAsB;kBAD3C,SAAS","sourcesContent":["import {\n Directive,\n inject,\n InjectionToken,\n Input,\n signal,\n} from '@angular/core';\nimport { ModalController, PopoverController } from '@ionic/angular/standalone';\nimport { SneatBaseComponent } from '../components/sneat-base.component';\n\nexport const OverlayController = new InjectionToken<\n ModalController | PopoverController\n>('OverlayController');\n\n@Directive()\nexport abstract class SelectorBaseComponent<T> extends SneatBaseComponent {\n @Input() public selectMode?: 'single' | 'multiple';\n\n protected readonly overlayController = inject(OverlayController);\n\n protected $selectedItems = signal<readonly T[]>([]);\n\n protected close(event?: Event): void {\n event?.stopPropagation();\n event?.preventDefault();\n this.overlayController\n .dismiss({ selectedItems: this.$selectedItems() })\n .catch(\n this.errorLogger.logErrorHandler(\n 'failed to dismiss contact selector modal',\n ),\n );\n }\n}\n\n@Directive()\nexport abstract class SelectorModalComponent<\n T,\n> extends SelectorBaseComponent<T> {\n protected get modalController() {\n return this.overlayController as ModalController;\n }\n}\n"]}
@@ -0,0 +1,49 @@
1
+ import { inject } from '@angular/core';
2
+ import { ModalController } from '@ionic/angular/standalone';
3
+ import { ErrorLogger } from '@sneat/core';
4
+ export class SelectorBaseService {
5
+ constructor(component) {
6
+ this.component = component;
7
+ this.errorLogger = inject(ErrorLogger);
8
+ this.modalController = inject(ModalController);
9
+ }
10
+ async selectSingleInModal(options) {
11
+ const result = await this.selectMultipleInModal(options);
12
+ return result ? result[0] : null;
13
+ }
14
+ // We make it protected so each service must override it for easiness of navigation
15
+ async selectMultipleInModal(options) {
16
+ let result = undefined;
17
+ const onSelected = options.onSelected;
18
+ options = {
19
+ ...options,
20
+ onSelected: async (items) => {
21
+ if (onSelected) {
22
+ await onSelected(items);
23
+ }
24
+ await this.modalController.dismiss(items);
25
+ result = items;
26
+ },
27
+ };
28
+ let componentProps = {
29
+ ...options.componentProps,
30
+ selectMode: 'multiple',
31
+ onSelected: options.onSelected,
32
+ mode: 'modal',
33
+ };
34
+ if (options.title) {
35
+ componentProps = { ...componentProps, title: options.title };
36
+ }
37
+ const modalOptions = {
38
+ component: this.component,
39
+ componentProps: componentProps,
40
+ keyboardClose: true,
41
+ };
42
+ const modal = await this.modalController.create(modalOptions);
43
+ await modal.present();
44
+ // 🔹 Track when the modal is dismissed
45
+ await modal.onDidDismiss();
46
+ return result;
47
+ }
48
+ }
49
+ //# sourceMappingURL=selector-base.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-base.service.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/selector/selector-base.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,eAAe,EAAgB,MAAM,2BAA2B,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,MAAM,OAAgB,mBAAmB;IAIvC,YAAuC,SAAuB;QAAvB,cAAS,GAAT,SAAS,CAAc;QAH3C,gBAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QACpC,oBAAe,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IAEM,CAAC;IAE3D,KAAK,CAAC,mBAAmB,CAC9B,OAA4B;QAE5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnC,CAAC;IAED,mFAAmF;IACzE,KAAK,CAAC,qBAAqB,CACnC,OAA4B;QAG5B,IAAI,MAAM,GAA6B,SAAS,CAAC;QAEjD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAEtC,OAAO,GAAG;YACR,GAAG,OAAO;YACV,UAAU,EAAE,KAAK,EAAE,KAAW,EAAiB,EAAE;gBAC/C,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;gBAC1B,CAAC;gBACD,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC1C,MAAM,GAAG,KAAK,CAAC;YACjB,CAAC;SACF,CAAC;QACF,IAAI,cAAc,GAA4B;YAC5C,GAAG,OAAO,CAAC,cAAc;YACzB,UAAU,EAAE,UAAU;YACtB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,IAAI,EAAE,OAAO;SACd,CAAC;QACF,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,cAAc,GAAG,EAAE,GAAG,cAAc,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAC/D,CAAC;QACD,MAAM,YAAY,GAAiB;YACjC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,cAAc,EAAE,cAAc;YAC9B,aAAa,EAAE,IAAI;SACpB,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC9D,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;QAEtB,uCAAuC;QACvC,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC;QAE3B,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import { inject } from '@angular/core';\nimport { ModalController, ModalOptions } from '@ionic/angular/standalone';\nimport type { ComponentProps, ComponentRef } from '@ionic/core';\nimport { ErrorLogger } from '@sneat/core';\nimport { ISelectItem } from './selector-interfaces';\nimport { ISelectorOptions } from './selector-options';\n\nexport abstract class SelectorBaseService<T = ISelectItem> {\n protected readonly errorLogger = inject(ErrorLogger);\n private readonly modalController = inject(ModalController);\n\n protected constructor(private readonly component: ComponentRef) {}\n\n public async selectSingleInModal(\n options: ISelectorOptions<T>,\n ): Promise<T | null> {\n const result = await this.selectMultipleInModal(options);\n return result ? result[0] : null;\n }\n\n // We make it protected so each service must override it for easiness of navigation\n protected async selectMultipleInModal(\n options: ISelectorOptions<T>,\n ): Promise<T[] | undefined> {\n\n let result: readonly T[] | undefined = undefined;\n\n const onSelected = options.onSelected;\n\n options = {\n ...options,\n onSelected: async (items?: T[]): Promise<void> => {\n if (onSelected) {\n await onSelected(items);\n }\n await this.modalController.dismiss(items);\n result = items;\n },\n };\n let componentProps: ComponentProps<unknown> = {\n ...options.componentProps,\n selectMode: 'multiple',\n onSelected: options.onSelected,\n mode: 'modal',\n };\n if (options.title) {\n componentProps = { ...componentProps, title: options.title };\n }\n const modalOptions: ModalOptions = {\n component: this.component,\n componentProps: componentProps,\n keyboardClose: true,\n };\n const modal = await this.modalController.create(modalOptions);\n await modal.present();\n\n // 🔹 Track when the modal is dismissed\n await modal.onDidDismiss();\n\n return result;\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=selector-interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-interfaces.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/selector/selector-interfaces.ts"],"names":[],"mappings":"","sourcesContent":["export type IonicColor =\n | 'danger'\n | 'success'\n | 'warning'\n | 'primary'\n | 'secondary'\n | 'tertiary'\n | 'dark'\n | 'medium'\n | 'light';\n\nexport interface ISelectItem {\n readonly id: string;\n readonly title: string;\n readonly shortTitle?: string;\n readonly longTitle?: string;\n readonly description1?: string;\n readonly description2?: string;\n readonly emoji?: string;\n readonly iconName?: string;\n readonly iconColor?: IonicColor;\n readonly labelColor?: IonicColor;\n}\n\nexport interface ISelectItemEvent {\n item: ISelectItem;\n event: Event;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=selector-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-options.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/selector/selector-options.ts"],"names":[],"mappings":"","sourcesContent":["import { ComponentProps } from '@ionic/core';\nimport { Signal } from '@angular/core';\nimport { Observable } from 'rxjs';\n\nexport interface ISelectorEvents<T> {\n readonly onSelected?: (item?: T[]) => Promise<void>;\n readonly onAdded?: (item: T) => Observable<void>;\n readonly onRemoved?: (item: T) => Observable<void>;\n}\n\nexport interface ISelectorOptions<T> extends ISelectorEvents<T> {\n readonly items?: Signal<readonly T[]>;\n readonly selectedItems?: readonly T[];\n readonly max?: number;\n readonly title?: string;\n\n componentProps?: ComponentProps<unknown>;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ export * from './index';
5
+ //# sourceMappingURL=sneat-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-ui.js","sourceRoot":"","sources":["../../../../libs/ui/src/sneat-ui.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,SAAS,CAAC","sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"]}
@@ -0,0 +1,9 @@
1
+ import { SneatBaseComponent } from './sneat-base.component';
2
+ import * as i0 from "@angular/core";
3
+ export declare abstract class SneatBaseModalComponent extends SneatBaseComponent {
4
+ private readonly modalController;
5
+ protected close(): void;
6
+ protected dismissModal(data?: unknown, role?: string, id?: string): void;
7
+ static ɵfac: i0.ɵɵFactoryDeclaration<SneatBaseModalComponent, never>;
8
+ static ɵprov: i0.ɵɵInjectableDeclaration<SneatBaseModalComponent>;
9
+ }
@@ -0,0 +1,28 @@
1
+ import { InjectionToken, OnDestroy } from '@angular/core';
2
+ import { MonoTypeOperatorFunction, Subscription } from 'rxjs';
3
+ import * as i0 from "@angular/core";
4
+ export interface IConsole {
5
+ log(...data: unknown[]): void;
6
+ warn(...data: unknown[]): void;
7
+ trace(...data: unknown[]): void;
8
+ error(...data: unknown[]): void;
9
+ }
10
+ export declare const ClassName: InjectionToken<string>;
11
+ export declare abstract class SneatBaseComponent implements OnDestroy {
12
+ private readonly destroyed;
13
+ protected readonly destroyed$: import("rxjs").Observable<void>;
14
+ protected readonly subs: Subscription;
15
+ protected readonly console: IConsole;
16
+ protected readonly errorLogger: import("@sneat/core").IErrorLogger;
17
+ protected readonly logError: (e: unknown, message?: string, options?: import("@sneat/core").ILogErrorOptions) => void;
18
+ protected readonly logErrorHandler: (message?: string, options?: import("@sneat/core").ILogErrorOptions) => (error: unknown) => void;
19
+ protected readonly setFocusToInput: (input?: import("@ionic/angular/standalone").IonInput | import("@ionic/angular/standalone").IonTextarea, delay?: number) => void;
20
+ protected readonly className: string;
21
+ constructor();
22
+ protected log(msg: string, ...data: unknown[]): void;
23
+ protected takeUntilDestroyed<T>(): MonoTypeOperatorFunction<T>;
24
+ ngOnDestroy(): void;
25
+ protected unsubscribe(reason?: string): void;
26
+ static ɵfac: i0.ɵɵFactoryDeclaration<SneatBaseComponent, never>;
27
+ static ɵprov: i0.ɵɵInjectableDeclaration<SneatBaseComponent>;
28
+ }
package/lib/focus.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { IonInput, IonTextarea } from '@ionic/angular/standalone';
2
+ import { IErrorLogger } from '@sneat/core';
3
+ export declare function createSetFocusToInput(errorLogger: IErrorLogger): (input?: IonInput | IonTextarea, delay?: number) => void;
@@ -0,0 +1,17 @@
1
+ import { EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
2
+ import { ISelectItem, ISelectItemEvent } from '../selector-interfaces';
3
+ import { SelectorBaseComponent } from '../selector-base.component';
4
+ import * as i0 from "@angular/core";
5
+ export declare class MultiSelectorComponent<T = ISelectItem> extends SelectorBaseComponent<T> implements OnChanges {
6
+ title: string;
7
+ canRemove: boolean;
8
+ allItems?: ISelectItem[];
9
+ selectedIDs?: readonly string[];
10
+ readonly removeItems: EventEmitter<ISelectItemEvent[]>;
11
+ readonly addItems: EventEmitter<ISelectItemEvent[]>;
12
+ protected selectedItems?: ISelectItem[];
13
+ ngOnChanges(changes: SimpleChanges): void;
14
+ protected removeItem(event: Event, item: ISelectItem): void;
15
+ static ɵfac: i0.ɵɵFactoryDeclaration<MultiSelectorComponent<any>, never>;
16
+ static ɵcmp: i0.ɵɵComponentDeclaration<MultiSelectorComponent<any>, "sneat-multi-selector", never, { "title": { "alias": "title"; "required": false; }; "canRemove": { "alias": "canRemove"; "required": false; }; "allItems": { "alias": "allItems"; "required": false; }; "selectedIDs": { "alias": "selectedIDs"; "required": false; }; }, { "removeItems": "removeItems"; "addItems": "addItems"; }, never, never, true, never>;
17
+ }
@@ -0,0 +1,9 @@
1
+ import { ModalController } from '@ionic/angular/standalone';
2
+ import { IErrorLogger } from '@sneat/core';
3
+ import { ISelectItem } from '../selector-interfaces';
4
+ export declare class MultiSelectorService {
5
+ private readonly errorLogger;
6
+ private readonly modalController;
7
+ constructor(errorLogger: IErrorLogger, modalController: ModalController);
8
+ selectMultiple(items: ISelectItem[], selectedItems: ISelectItem[]): Promise<ISelectItem[]>;
9
+ }
@@ -0,0 +1,60 @@
1
+ import { EventEmitter, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
2
+ import { ControlValueAccessor } from '@angular/forms';
3
+ import { IonInput, IonSelect } from '@ionic/angular/standalone';
4
+ import { Observable } from 'rxjs';
5
+ import { ISelectItem } from '../selector-interfaces';
6
+ import * as i0 from "@angular/core";
7
+ export declare class SelectFromListComponent implements ControlValueAccessor, OnChanges, OnDestroy {
8
+ private readonly errorLogger;
9
+ value: string | undefined;
10
+ readonly valueChange: EventEmitter<string>;
11
+ filterLabel: string;
12
+ label: string;
13
+ listLabel?: 'divider';
14
+ listLabelColor?: 'light' | 'medium' | 'primary' | 'secondary' | 'tertiary';
15
+ isFilterable?: boolean;
16
+ isLoading?: boolean;
17
+ items?: readonly ISelectItem[];
18
+ items$: Observable<ISelectItem[]>;
19
+ private $items;
20
+ lastItemLines?: 'none' | 'inset' | 'full';
21
+ labelPlacement?: 'start' | 'end' | 'fixed' | 'stacked';
22
+ justify?: 'start' | 'end' | 'space-between';
23
+ other: 'top' | 'bottom' | 'none';
24
+ canAdd: boolean;
25
+ filterItem?: (item: ISelectItem, filter: string) => boolean;
26
+ readonly filterChanged: EventEmitter<string>;
27
+ selectMode: 'single' | 'multiple';
28
+ isReadonly: boolean;
29
+ $isProcessing: import("@angular/core").InputSignal<boolean | undefined>;
30
+ sortBy?: 'title' | 'id';
31
+ private destroyed;
32
+ addInput?: IonInput;
33
+ filterInput?: IonInput;
34
+ selectInput?: IonSelect;
35
+ protected readonly $displayItems: import("@angular/core").WritableSignal<readonly ISelectItem[] | undefined>;
36
+ protected readonly $hiddenCount: import("@angular/core").Signal<number>;
37
+ protected isDisabled: boolean;
38
+ protected readonly $filter: import("@angular/core").WritableSignal<string>;
39
+ focus(): void;
40
+ ngOnChanges(changes: SimpleChanges): void;
41
+ private applyFilter;
42
+ protected select(item: ISelectItem): void;
43
+ protected readonly $selectedItem: import("@angular/core").WritableSignal<ISelectItem | undefined>;
44
+ protected onRadioChanged(event: Event): void;
45
+ protected onSelectChanged(): void;
46
+ protected onChange: (id: string) => void;
47
+ onTouched: () => void;
48
+ registerOnChange(fn: (v: unknown) => void): void;
49
+ registerOnTouched(fn: () => void): void;
50
+ setDisabledState(isDisabled: boolean): void;
51
+ writeValue(obj: unknown): void;
52
+ protected onFilterChanged(event: CustomEvent, source: 'ionChange' | 'ionInput'): void;
53
+ protected clearFilter(): void;
54
+ protected deselect(): void;
55
+ protected onAdd(event: Event): void;
56
+ protected onCheckboxChange(event: CustomEvent, item: ISelectItem): void;
57
+ ngOnDestroy(): void;
58
+ static ɵfac: i0.ɵɵFactoryDeclaration<SelectFromListComponent, never>;
59
+ static ɵcmp: i0.ɵɵComponentDeclaration<SelectFromListComponent, "sneat-select-from-list", never, { "value": { "alias": "value"; "required": false; }; "filterLabel": { "alias": "filterLabel"; "required": false; }; "label": { "alias": "label"; "required": false; }; "listLabel": { "alias": "listLabel"; "required": false; }; "listLabelColor": { "alias": "listLabelColor"; "required": false; }; "isFilterable": { "alias": "isFilterable"; "required": false; }; "isLoading": { "alias": "isLoading"; "required": false; }; "items": { "alias": "items"; "required": false; }; "items$": { "alias": "items$"; "required": false; }; "lastItemLines": { "alias": "lastItemLines"; "required": false; }; "labelPlacement": { "alias": "labelPlacement"; "required": false; }; "justify": { "alias": "justify"; "required": false; }; "other": { "alias": "other"; "required": false; }; "canAdd": { "alias": "canAdd"; "required": false; }; "filterItem": { "alias": "filterItem"; "required": false; }; "selectMode": { "alias": "selectMode"; "required": false; }; "isReadonly": { "alias": "isReadonly"; "required": false; }; "$isProcessing": { "alias": "$isProcessing"; "required": false; "isSignal": true; }; "sortBy": { "alias": "sortBy"; "required": false; }; }, { "valueChange": "valueChange"; "filterChanged": "filterChanged"; }, never, never, true, never>;
60
+ }
@@ -0,0 +1,18 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ import { ModalController, PopoverController } from '@ionic/angular/standalone';
3
+ import { SneatBaseComponent } from '../components/sneat-base.component';
4
+ import * as i0 from "@angular/core";
5
+ export declare const OverlayController: InjectionToken<ModalController | PopoverController>;
6
+ export declare abstract class SelectorBaseComponent<T> extends SneatBaseComponent {
7
+ selectMode?: 'single' | 'multiple';
8
+ protected readonly overlayController: ModalController | PopoverController;
9
+ protected $selectedItems: import("@angular/core").WritableSignal<readonly T[]>;
10
+ protected close(event?: Event): void;
11
+ static ɵfac: i0.ɵɵFactoryDeclaration<SelectorBaseComponent<any>, never>;
12
+ static ɵdir: i0.ɵɵDirectiveDeclaration<SelectorBaseComponent<any>, never, never, { "selectMode": { "alias": "selectMode"; "required": false; }; }, {}, never, never, true, never>;
13
+ }
14
+ export declare abstract class SelectorModalComponent<T> extends SelectorBaseComponent<T> {
15
+ protected get modalController(): ModalController;
16
+ static ɵfac: i0.ɵɵFactoryDeclaration<SelectorModalComponent<any>, never>;
17
+ static ɵdir: i0.ɵɵDirectiveDeclaration<SelectorModalComponent<any>, never, never, {}, {}, never, never, true, never>;
18
+ }
@@ -0,0 +1,11 @@
1
+ import type { ComponentRef } from '@ionic/core';
2
+ import { ISelectItem } from './selector-interfaces';
3
+ import { ISelectorOptions } from './selector-options';
4
+ export declare abstract class SelectorBaseService<T = ISelectItem> {
5
+ private readonly component;
6
+ protected readonly errorLogger: import("@sneat/core").IErrorLogger;
7
+ private readonly modalController;
8
+ protected constructor(component: ComponentRef);
9
+ selectSingleInModal(options: ISelectorOptions<T>): Promise<T | null>;
10
+ protected selectMultipleInModal(options: ISelectorOptions<T>): Promise<T[] | undefined>;
11
+ }
@@ -0,0 +1,17 @@
1
+ export type IonicColor = 'danger' | 'success' | 'warning' | 'primary' | 'secondary' | 'tertiary' | 'dark' | 'medium' | 'light';
2
+ export interface ISelectItem {
3
+ readonly id: string;
4
+ readonly title: string;
5
+ readonly shortTitle?: string;
6
+ readonly longTitle?: string;
7
+ readonly description1?: string;
8
+ readonly description2?: string;
9
+ readonly emoji?: string;
10
+ readonly iconName?: string;
11
+ readonly iconColor?: IonicColor;
12
+ readonly labelColor?: IonicColor;
13
+ }
14
+ export interface ISelectItemEvent {
15
+ item: ISelectItem;
16
+ event: Event;
17
+ }
@@ -0,0 +1,15 @@
1
+ import { ComponentProps } from '@ionic/core';
2
+ import { Signal } from '@angular/core';
3
+ import { Observable } from 'rxjs';
4
+ export interface ISelectorEvents<T> {
5
+ readonly onSelected?: (item?: T[]) => Promise<void>;
6
+ readonly onAdded?: (item: T) => Observable<void>;
7
+ readonly onRemoved?: (item: T) => Observable<void>;
8
+ }
9
+ export interface ISelectorOptions<T> extends ISelectorEvents<T> {
10
+ readonly items?: Signal<readonly T[]>;
11
+ readonly selectedItems?: readonly T[];
12
+ readonly max?: number;
13
+ readonly title?: string;
14
+ componentProps?: ComponentProps<unknown>;
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sneat/ui",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -12,5 +12,17 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "tslib": "2.8.1"
15
- }
15
+ },
16
+ "module": "esm2022/sneat-ui.js",
17
+ "typings": "sneat-ui.d.ts",
18
+ "exports": {
19
+ "./package.json": {
20
+ "default": "./package.json"
21
+ },
22
+ ".": {
23
+ "types": "./sneat-ui.d.ts",
24
+ "default": "./esm2022/sneat-ui.js"
25
+ }
26
+ },
27
+ "sideEffects": false
16
28
  }
package/sneat-ui.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ /// <amd-module name="@sneat/ui" />
5
+ export * from './index';
package/eslint.config.js DELETED
@@ -1,7 +0,0 @@
1
- const baseConfig = require('../../eslint.config.js');
2
- const { sneatLibConfig } = require('../../eslint.lib.config.js');
3
-
4
- module.exports = [
5
- ...baseConfig,
6
- ...sneatLibConfig(__dirname),
7
- ];
package/ng-package.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
- "dest": "../../dist/libs/ui",
4
- "lib": {
5
- "entryFile": "src/index.ts"
6
- }
7
- }
package/project.json DELETED
@@ -1,38 +0,0 @@
1
- {
2
- "name": "ui",
3
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
- "projectType": "library",
5
- "sourceRoot": "libs/ui/src",
6
- "prefix": "sneat",
7
- "targets": {
8
- "build": {
9
- "executor": "@nx/angular:ng-packagr-lite",
10
- "outputs": [
11
- "{workspaceRoot}/dist/libs/ui"
12
- ],
13
- "options": {
14
- "project": "libs/ui/ng-package.json",
15
- "tsConfig": "libs/ui/tsconfig.lib.json"
16
- },
17
- "configurations": {
18
- "production": {
19
- "tsConfig": "libs/ui/tsconfig.lib.prod.json"
20
- },
21
- "development": {}
22
- },
23
- "defaultConfiguration": "production"
24
- },
25
- "test": {
26
- "executor": "@nx/vitest:test",
27
- "outputs": [
28
- "{workspaceRoot}/coverage/libs/ui"
29
- ],
30
- "options": {
31
- "tsConfig": "libs/ui/tsconfig.spec.json"
32
- }
33
- },
34
- "lint": {
35
- "executor": "@nx/eslint:lint"
36
- }
37
- }
38
- }
@@ -1,22 +0,0 @@
1
- import { inject, Injectable } from '@angular/core';
2
- import { SneatBaseComponent } from './sneat-base.component';
3
- import { ModalController } from '@ionic/angular/standalone';
4
-
5
- @Injectable()
6
- export abstract class SneatBaseModalComponent extends SneatBaseComponent {
7
- private readonly modalController = inject(ModalController);
8
-
9
- protected close(): void {
10
- this.dismissModal();
11
- }
12
-
13
- protected dismissModal(data?: unknown, role?: string, id?: string): void {
14
- this.modalController
15
- .dismiss(data, role, id)
16
- .catch(
17
- this.errorLogger.logErrorHandler(
18
- `Failed to close modal ${this.className}`,
19
- ),
20
- );
21
- }
22
- }
@@ -1,71 +0,0 @@
1
- import { SneatBaseComponent } from './sneat-base.component';
2
- import { IErrorLogger } from '@sneat/core';
3
- import { Subject, Subscription } from 'rxjs';
4
-
5
- class TestComponent extends SneatBaseComponent {
6
- constructor(errorLogger: IErrorLogger, className: string) {
7
- super();
8
- // @ts-expect-error accessing private property
9
- this.errorLogger = errorLogger;
10
- // @ts-expect-error accessing private property
11
- this.className = className;
12
- // @ts-expect-error accessing private property
13
- this.destroyed = new Subject<void>();
14
- // @ts-expect-error accessing private property
15
- this.subs = new Subscription();
16
- }
17
-
18
- public getDestroyed(): Subject<void> {
19
- // @ts-expect-error accessing private property
20
- return this.destroyed;
21
- }
22
-
23
- public getSubs(): Subscription {
24
- // @ts-expect-error accessing private property
25
- return this.subs;
26
- }
27
-
28
- public log(_msg: string) {
29
- void _msg;
30
- // override log to avoid console output
31
- }
32
- }
33
-
34
- describe('SneatBaseComponent', () => {
35
- let component: TestComponent;
36
- let errorLoggerMock: IErrorLogger;
37
-
38
- beforeEach(() => {
39
- errorLoggerMock = {
40
- logError: vi.fn(),
41
- logErrorHandler: vi.fn(),
42
- } as unknown as IErrorLogger;
43
- // Use Object.create to bypass constructor that calls inject()
44
- component = Object.create(TestComponent.prototype) as TestComponent;
45
- // @ts-expect-error accessing private property
46
- component.errorLogger = errorLoggerMock;
47
- // @ts-expect-error accessing private property
48
- component.className = 'TestComponent';
49
- // @ts-expect-error accessing private property
50
- component.destroyed = new Subject<void>();
51
- // @ts-expect-error accessing private property
52
- component.subs = new Subscription();
53
-
54
- // @ts-expect-error accessing private property
55
- vi.spyOn(component.destroyed, 'next');
56
- // @ts-expect-error accessing private property
57
- vi.spyOn(component.subs, 'unsubscribe');
58
- });
59
-
60
- it('should create', () => {
61
- expect(component).toBeTruthy();
62
- });
63
-
64
- it('should emit on destroyed and unsubscribe when ngOnDestroy is called', () => {
65
- component.ngOnDestroy();
66
- // @ts-expect-error accessing private property
67
- expect(component.destroyed.next).toHaveBeenCalled();
68
- // @ts-expect-error accessing private property
69
- expect(component.subs.unsubscribe).toHaveBeenCalled();
70
- });
71
- });