@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,4 @@
1
+ export * from './lib/focus';
2
+ export * from './lib/components';
3
+ export * from './lib/selector';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/ui/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC","sourcesContent":["export * from './lib/focus';\nexport * from './lib/components';\nexport * from './lib/selector';\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './sneat-base.component';
2
+ export * from './sneat-base-modal.component';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC","sourcesContent":["export * from './sneat-base.component';\nexport * from './sneat-base-modal.component';\n"]}
@@ -0,0 +1,24 @@
1
+ import { inject, Injectable } from '@angular/core';
2
+ import { SneatBaseComponent } from './sneat-base.component';
3
+ import { ModalController } from '@ionic/angular/standalone';
4
+ import * as i0 from "@angular/core";
5
+ export class SneatBaseModalComponent extends SneatBaseComponent {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.modalController = inject(ModalController);
9
+ }
10
+ close() {
11
+ this.dismissModal();
12
+ }
13
+ dismissModal(data, role, id) {
14
+ this.modalController
15
+ .dismiss(data, role, id)
16
+ .catch(this.errorLogger.logErrorHandler(`Failed to close modal ${this.className}`));
17
+ }
18
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatBaseModalComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
19
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatBaseModalComponent }); }
20
+ }
21
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatBaseModalComponent, decorators: [{
22
+ type: Injectable
23
+ }] });
24
+ //# sourceMappingURL=sneat-base-modal.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-base-modal.component.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/components/sneat-base-modal.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;;AAG5D,MAAM,OAAgB,uBAAwB,SAAQ,kBAAkB;IADxE;;QAEmB,oBAAe,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;KAe5D;IAbW,KAAK;QACb,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAES,YAAY,CAAC,IAAc,EAAE,IAAa,EAAE,EAAW;QAC/D,IAAI,CAAC,eAAe;aACjB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;aACvB,KAAK,CACJ,IAAI,CAAC,WAAW,CAAC,eAAe,CAC9B,yBAAyB,IAAI,CAAC,SAAS,EAAE,CAC1C,CACF,CAAC;IACN,CAAC;8GAfmB,uBAAuB;kHAAvB,uBAAuB;;2FAAvB,uBAAuB;kBAD5C,UAAU","sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { SneatBaseComponent } from './sneat-base.component';\nimport { ModalController } from '@ionic/angular/standalone';\n\n@Injectable()\nexport abstract class SneatBaseModalComponent extends SneatBaseComponent {\n private readonly modalController = inject(ModalController);\n\n protected close(): void {\n this.dismissModal();\n }\n\n protected dismissModal(data?: unknown, role?: string, id?: string): void {\n this.modalController\n .dismiss(data, role, id)\n .catch(\n this.errorLogger.logErrorHandler(\n `Failed to close modal ${this.className}`,\n ),\n );\n }\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { inject, Injectable, InjectionToken } from '@angular/core';
2
+ import { createSetFocusToInput } from '../focus';
3
+ import { ErrorLogger } from '@sneat/core';
4
+ import { Subject, Subscription } from 'rxjs';
5
+ import { takeUntil } from 'rxjs/operators';
6
+ import * as i0 from "@angular/core";
7
+ export const ClassName = new InjectionToken('className');
8
+ export class SneatBaseComponent {
9
+ constructor() {
10
+ // protected $isInitialized = signal(false);
11
+ //
12
+ // // eslint-disable-next-line @angular-eslint/contextual-lifecycle
13
+ // public ngOnInit() {
14
+ // console.info(`${this.className}.SneatBaseComponent.ngOnInit()`);
15
+ // // $isInitialized is for workaround for https://angular.dev/errors/NG0950
16
+ // // Required input is accessed before a value is set.
17
+ // // Might be excessive and should be removed if we can find a better way.
18
+ // this.$isInitialized.set(true);
19
+ // }
20
+ this.destroyed = new Subject();
21
+ // Signals that the component is destroyed and should not be used anymore
22
+ this.destroyed$ = this.destroyed.asObservable();
23
+ // All active subscriptions of a component. Will be unsubscribed on destroy
24
+ this.subs = new Subscription();
25
+ this.console = console;
26
+ this.errorLogger = inject(ErrorLogger);
27
+ this.logError = this.errorLogger.logError;
28
+ this.logErrorHandler = this.errorLogger.logErrorHandler;
29
+ // Passes focus to the input element
30
+ this.setFocusToInput = createSetFocusToInput(this.errorLogger);
31
+ this.className = inject(ClassName);
32
+ this.log(`${this.className}.SneatBaseComponent.constructor()`);
33
+ }
34
+ log(msg, ...data) {
35
+ this.console.log(msg, ...data);
36
+ }
37
+ // protected readonly takeUntilDestroyed = <T>() =>
38
+ // takeUntil<T>(this.destroyed$);
39
+ takeUntilDestroyed() {
40
+ return takeUntil(this.destroyed$);
41
+ }
42
+ ngOnDestroy() {
43
+ this.log(`${this.className}.SneatBaseComponent.ngOnDestroy()`);
44
+ this.unsubscribe(`${this.className}.SneatBaseComponent.ngOnDestroy()`);
45
+ this.destroyed?.next();
46
+ this.destroyed?.complete();
47
+ }
48
+ unsubscribe(reason) {
49
+ this.log(`${this.className}.SneatBaseComponent.unsubscribe(reason: ${reason})`);
50
+ this.subs.unsubscribe();
51
+ }
52
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
53
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatBaseComponent }); }
54
+ }
55
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SneatBaseComponent, decorators: [{
56
+ type: Injectable
57
+ }], ctorParameters: () => [] });
58
+ //# sourceMappingURL=sneat-base.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sneat-base.component.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/components/sneat-base.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAa,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAA4B,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;;AAY3C,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,cAAc,CAAS,WAAW,CAAC,CAAC;AAGjE,MAAM,OAAgB,kBAAkB;IA+BtC;QA9BA,4CAA4C;QAC5C,EAAE;QACF,mEAAmE;QACnE,sBAAsB;QACtB,oEAAoE;QACpE,6EAA6E;QAC7E,wDAAwD;QACxD,4EAA4E;QAC5E,kCAAkC;QAClC,IAAI;QAEa,cAAS,GAAG,IAAI,OAAO,EAAQ,CAAC;QACjD,yEAAyE;QACtD,eAAU,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;QAE9D,2EAA2E;QACxD,SAAI,GAAG,IAAI,YAAY,EAAE,CAAC;QAE1B,YAAO,GAAa,OAAO,CAAC;QAE5B,gBAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAElC,aAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;QACrC,oBAAe,GAAG,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC;QAEtE,oCAAoC;QACjB,oBAAe,GAAG,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE1D,cAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAG/C,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,mCAAmC,CAAC,CAAC;IACjE,CAAC;IAES,GAAG,CAAC,GAAW,EAAE,GAAG,IAAe;QAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,mDAAmD;IACnD,kCAAkC;IACxB,kBAAkB;QAC1B,OAAO,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IAEM,WAAW;QAChB,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,mCAAmC,CAAC,CAAC;QAC/D,IAAI,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,SAAS,mCAAmC,CAAC,CAAC;QACvE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAES,WAAW,CAAC,MAAe;QACnC,IAAI,CAAC,GAAG,CACN,GAAG,IAAI,CAAC,SAAS,2CAA2C,MAAM,GAAG,CACtE,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAC1B,CAAC;8GAzDmB,kBAAkB;kHAAlB,kBAAkB;;2FAAlB,kBAAkB;kBADvC,UAAU","sourcesContent":["import { inject, Injectable, InjectionToken, OnDestroy } from '@angular/core';\nimport { createSetFocusToInput } from '../focus';\nimport { ErrorLogger } from '@sneat/core';\nimport { MonoTypeOperatorFunction, Subject, Subscription } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\nexport interface IConsole {\n log(...data: unknown[]): void;\n\n warn(...data: unknown[]): void;\n\n trace(...data: unknown[]): void;\n\n error(...data: unknown[]): void;\n}\n\nexport const ClassName = new InjectionToken<string>('className');\n\n@Injectable()\nexport abstract class SneatBaseComponent implements OnDestroy {\n // protected $isInitialized = signal(false);\n //\n // // eslint-disable-next-line @angular-eslint/contextual-lifecycle\n // public ngOnInit() {\n // \tconsole.info(`${this.className}.SneatBaseComponent.ngOnInit()`);\n // \t// $isInitialized is for workaround for https://angular.dev/errors/NG0950\n // \t// Required input is accessed before a value is set.\n // \t// Might be excessive and should be removed if we can find a better way.\n // \tthis.$isInitialized.set(true);\n // }\n\n private readonly destroyed = new Subject<void>();\n // Signals that the component is destroyed and should not be used anymore\n protected readonly destroyed$ = this.destroyed.asObservable();\n\n // All active subscriptions of a component. Will be unsubscribed on destroy\n protected readonly subs = new Subscription();\n\n protected readonly console: IConsole = console;\n\n protected readonly errorLogger = inject(ErrorLogger);\n\n protected readonly logError = this.errorLogger.logError;\n protected readonly logErrorHandler = this.errorLogger.logErrorHandler;\n\n // Passes focus to the input element\n protected readonly setFocusToInput = createSetFocusToInput(this.errorLogger);\n\n protected readonly className = inject(ClassName);\n\n public constructor() {\n this.log(`${this.className}.SneatBaseComponent.constructor()`);\n }\n\n protected log(msg: string, ...data: unknown[]): void {\n this.console.log(msg, ...data);\n }\n\n // protected readonly takeUntilDestroyed = <T>() =>\n // \ttakeUntil<T>(this.destroyed$);\n protected takeUntilDestroyed<T>(): MonoTypeOperatorFunction<T> {\n return takeUntil(this.destroyed$);\n }\n\n public ngOnDestroy(): void {\n this.log(`${this.className}.SneatBaseComponent.ngOnDestroy()`);\n this.unsubscribe(`${this.className}.SneatBaseComponent.ngOnDestroy()`);\n this.destroyed?.next();\n this.destroyed?.complete();\n }\n\n protected unsubscribe(reason?: string): void {\n this.log(\n `${this.className}.SneatBaseComponent.unsubscribe(reason: ${reason})`,\n );\n this.subs.unsubscribe();\n }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ export function createSetFocusToInput(errorLogger) {
2
+ return (input, delay = 100) => {
3
+ if (!input) {
4
+ console.error('can not set focus to undefined input');
5
+ return;
6
+ }
7
+ setTimeout(() => {
8
+ requestAnimationFrame(() => {
9
+ // input.getInputElement().then(el => el.focus()).catch(errorLogger.logErrorHandler('failed to set focus to input'));
10
+ input
11
+ .setFocus()
12
+ .catch(errorLogger.logErrorHandler('failed to set focus to input'));
13
+ });
14
+ }, delay);
15
+ };
16
+ }
17
+ //# sourceMappingURL=focus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/focus.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,qBAAqB,CAAC,WAAyB;IAC7D,OAAO,CAAC,KAA8B,EAAE,KAAK,GAAG,GAAG,EAAQ,EAAE;QAC3D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;YACtD,OAAO;QACT,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,qBAAqB,CAAC,GAAG,EAAE;gBACzB,qHAAqH;gBACrH,KAAK;qBACF,QAAQ,EAAE;qBACV,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,8BAA8B,CAAC,CAAC,CAAC;YACxE,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { IonInput, IonTextarea } from '@ionic/angular/standalone';\nimport { IErrorLogger } from '@sneat/core';\n\nexport function createSetFocusToInput(errorLogger: IErrorLogger) {\n return (input?: IonInput | IonTextarea, delay = 100): void => {\n if (!input) {\n console.error('can not set focus to undefined input');\n return;\n }\n setTimeout(() => {\n requestAnimationFrame(() => {\n // input.getInputElement().then(el => el.focus()).catch(errorLogger.logErrorHandler('failed to set focus to input'));\n input\n .setFocus()\n .catch(errorLogger.logErrorHandler('failed to set focus to input'));\n });\n }, delay);\n };\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export * from './selector-interfaces';
2
+ export * from './selector-options';
3
+ export * from './selector-base.service';
4
+ export * from './selector-base.component';
5
+ export * from './multi-selector';
6
+ export * from './select-from-list';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/ui/src/lib/selector/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC","sourcesContent":["export * from './selector-interfaces';\nexport * from './selector-options';\nexport * from './selector-base.service';\nexport * from './selector-base.component';\nexport * from './multi-selector';\nexport * from './select-from-list';\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './multi-selector.service';
2
+ export * from './multi-selector.component';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../libs/ui/src/lib/selector/multi-selector/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC","sourcesContent":["export * from './multi-selector.service';\nexport * from './multi-selector.component';\n"]}
@@ -0,0 +1,70 @@
1
+ import { Component, EventEmitter, Input, Output, } from '@angular/core';
2
+ import { IonButton, IonButtons, IonCard, IonIcon, IonItem, IonItemDivider, IonLabel, IonList, ModalController, } from '@ionic/angular/standalone';
3
+ import { ClassName } from '../../components';
4
+ import { OverlayController, SelectorBaseComponent, } from '../selector-base.component';
5
+ import * as i0 from "@angular/core";
6
+ export class MultiSelectorComponent extends SelectorBaseComponent {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.title = 'Select';
10
+ this.canRemove = false;
11
+ this.removeItems = new EventEmitter();
12
+ this.addItems = new EventEmitter();
13
+ }
14
+ ngOnChanges(changes) {
15
+ if (changes['allItems']) {
16
+ this.selectedItems = this.allItems?.filter((item) => this.selectedIDs?.includes(item.id));
17
+ }
18
+ }
19
+ removeItem(event, item) {
20
+ event.stopPropagation();
21
+ this.selectedItems = this.selectedItems?.filter((i) => i.id !== item.id);
22
+ this.removeItems.emit([{ event, item }]);
23
+ }
24
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MultiSelectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
25
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: MultiSelectorComponent, isStandalone: true, selector: "sneat-multi-selector", inputs: { title: "title", canRemove: "canRemove", allItems: "allItems", selectedIDs: "selectedIDs" }, outputs: { removeItems: "removeItems", addItems: "addItems" }, providers: [
26
+ {
27
+ provide: ClassName,
28
+ useValue: 'MultiSelectorComponent',
29
+ },
30
+ {
31
+ provide: OverlayController,
32
+ useClass: ModalController,
33
+ },
34
+ ], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ion-card>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\">{{ title }}</ion-label>\n <ion-buttons slot=\"end\">\n <ion-button color=\"medium\">\n <ion-icon name=\"add-outline\" />\n <ion-label>Add</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item-divider>\n\n <ion-list>\n @for (item of selectedItems; track item.id) {\n <ion-item>\n <ion-label>{{ item.title }}</ion-label>\n @if (canRemove) {\n <ion-buttons slot=\"end\">\n <ion-button title=\"Remove\" (click)=\"removeItem($event, item)\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n }\n </ion-list>\n</ion-card>\n", dependencies: [{ kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }] }); }
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MultiSelectorComponent, decorators: [{
37
+ type: Component,
38
+ args: [{ selector: 'sneat-multi-selector', imports: [
39
+ IonCard,
40
+ IonItemDivider,
41
+ IonLabel,
42
+ IonButtons,
43
+ IonButton,
44
+ IonItem,
45
+ IonIcon,
46
+ IonList,
47
+ ], providers: [
48
+ {
49
+ provide: ClassName,
50
+ useValue: 'MultiSelectorComponent',
51
+ },
52
+ {
53
+ provide: OverlayController,
54
+ useClass: ModalController,
55
+ },
56
+ ], template: "<ion-card>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\">{{ title }}</ion-label>\n <ion-buttons slot=\"end\">\n <ion-button color=\"medium\">\n <ion-icon name=\"add-outline\" />\n <ion-label>Add</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item-divider>\n\n <ion-list>\n @for (item of selectedItems; track item.id) {\n <ion-item>\n <ion-label>{{ item.title }}</ion-label>\n @if (canRemove) {\n <ion-buttons slot=\"end\">\n <ion-button title=\"Remove\" (click)=\"removeItem($event, item)\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n }\n </ion-list>\n</ion-card>\n" }]
57
+ }], propDecorators: { title: [{
58
+ type: Input
59
+ }], canRemove: [{
60
+ type: Input
61
+ }], allItems: [{
62
+ type: Input
63
+ }], selectedIDs: [{
64
+ type: Input
65
+ }], removeItems: [{
66
+ type: Output
67
+ }], addItems: [{
68
+ type: Output
69
+ }] } });
70
+ //# sourceMappingURL=multi-selector.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-selector.component.js","sourceRoot":"","sources":["../../../../../../../libs/ui/src/lib/selector/multi-selector/multi-selector.component.ts","../../../../../../../libs/ui/src/lib/selector/multi-selector/multi-selector.component.html"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,YAAY,EACZ,KAAK,EAEL,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,SAAS,EACT,UAAU,EACV,OAAO,EACP,OAAO,EACP,OAAO,EACP,cAAc,EACd,QAAQ,EACR,OAAO,EACP,eAAe,GAChB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;;AA0BpC,MAAM,OAAO,sBACX,SAAQ,qBAAwB;IAzBlC;;QA4BW,UAAK,GAAG,QAAQ,CAAC;QAEjB,cAAS,GAAG,KAAK,CAAC;QAIR,gBAAW,GAAG,IAAI,YAAY,EAAsB,CAAC;QACrD,aAAQ,GAAG,IAAI,YAAY,EAAsB,CAAC;KAiBtE;IAbC,WAAW,CAAC,OAAsB;QAChC,IAAI,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAClD,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CACpC,CAAC;QACJ,CAAC;IACH,CAAC;IAES,UAAU,CAAC,KAAY,EAAE,IAAiB;QAClD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3C,CAAC;8GA3BU,sBAAsB;kGAAtB,sBAAsB,wOAXtB;YACT;gBACE,OAAO,EAAE,SAAS;gBAClB,QAAQ,EAAE,wBAAwB;aACnC;YACD;gBACE,OAAO,EAAE,iBAAiB;gBAC1B,QAAQ,EAAE,eAAe;aAC1B;SACF,sEChDH,iwBA0BA,4CDII,OAAO,yLACP,cAAc,kGACd,QAAQ,6FACR,UAAU,8EACV,SAAS,oPACT,OAAO,0NACP,OAAO,2JACP,OAAO;;2FAaE,sBAAsB;kBAxBlC,SAAS;+BACE,sBAAsB,WAEvB;wBACP,OAAO;wBACP,cAAc;wBACd,QAAQ;wBACR,UAAU;wBACV,SAAS;wBACT,OAAO;wBACP,OAAO;wBACP,OAAO;qBACR,aACU;wBACT;4BACE,OAAO,EAAE,SAAS;4BAClB,QAAQ,EAAE,wBAAwB;yBACnC;wBACD;4BACE,OAAO,EAAE,iBAAiB;4BAC1B,QAAQ,EAAE,eAAe;yBAC1B;qBACF;;sBAMA,KAAK;;sBAEL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,MAAM;;sBACN,MAAM","sourcesContent":["import {\n Component,\n EventEmitter,\n Input,\n OnChanges,\n Output,\n SimpleChanges,\n} from '@angular/core';\nimport {\n IonButton,\n IonButtons,\n IonCard,\n IonIcon,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonList,\n ModalController,\n} from '@ionic/angular/standalone';\nimport { ClassName } from '../../components';\nimport { ISelectItem, ISelectItemEvent } from '../selector-interfaces';\nimport {\n OverlayController,\n SelectorBaseComponent,\n} from '../selector-base.component';\n\n@Component({\n selector: 'sneat-multi-selector',\n templateUrl: './multi-selector.component.html',\n imports: [\n IonCard,\n IonItemDivider,\n IonLabel,\n IonButtons,\n IonButton,\n IonItem,\n IonIcon,\n IonList,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'MultiSelectorComponent',\n },\n {\n provide: OverlayController,\n useClass: ModalController,\n },\n ],\n})\nexport class MultiSelectorComponent<T = ISelectItem>\n extends SelectorBaseComponent<T>\n implements OnChanges\n{\n @Input() title = 'Select';\n\n @Input() canRemove = false;\n @Input() public allItems?: ISelectItem[];\n @Input() public selectedIDs?: readonly string[];\n\n @Output() readonly removeItems = new EventEmitter<ISelectItemEvent[]>();\n @Output() readonly addItems = new EventEmitter<ISelectItemEvent[]>();\n\n protected selectedItems?: ISelectItem[];\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['allItems']) {\n this.selectedItems = this.allItems?.filter((item) =>\n this.selectedIDs?.includes(item.id),\n );\n }\n }\n\n protected removeItem(event: Event, item: ISelectItem): void {\n event.stopPropagation();\n this.selectedItems = this.selectedItems?.filter((i) => i.id !== item.id);\n this.removeItems.emit([{ event, item }]);\n }\n}\n","<ion-card>\n <ion-item-divider color=\"light\">\n <ion-label color=\"medium\">{{ title }}</ion-label>\n <ion-buttons slot=\"end\">\n <ion-button color=\"medium\">\n <ion-icon name=\"add-outline\" />\n <ion-label>Add</ion-label>\n </ion-button>\n </ion-buttons>\n </ion-item-divider>\n\n <ion-list>\n @for (item of selectedItems; track item.id) {\n <ion-item>\n <ion-label>{{ item.title }}</ion-label>\n @if (canRemove) {\n <ion-buttons slot=\"end\">\n <ion-button title=\"Remove\" (click)=\"removeItem($event, item)\">\n <ion-icon name=\"close-outline\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n }\n </ion-list>\n</ion-card>\n"]}
@@ -0,0 +1,49 @@
1
+ import { __decorate, __metadata, __param } from "tslib";
2
+ import { Inject } from '@angular/core';
3
+ import { ModalController } from '@ionic/angular/standalone';
4
+ import { ErrorLogger } from '@sneat/core';
5
+ import { MultiSelectorComponent } from './multi-selector.component';
6
+ let MultiSelectorService = class MultiSelectorService {
7
+ constructor(errorLogger, modalController) {
8
+ this.errorLogger = errorLogger;
9
+ this.modalController = modalController;
10
+ }
11
+ selectMultiple(items, selectedItems) {
12
+ const result = new Promise((resolve, reject) => {
13
+ const modalOptions = {
14
+ component: MultiSelectorComponent,
15
+ componentProps: {
16
+ items,
17
+ selectedItems,
18
+ },
19
+ keyboardClose: true,
20
+ };
21
+ this.modalController
22
+ .create(modalOptions)
23
+ .then((modal) => {
24
+ modal
25
+ .onDidDismiss()
26
+ .then((res) => resolve(res.data?.selectedItems || []))
27
+ .catch((err) => {
28
+ this.errorLogger.logError(err, 'Failed to handle modal dismiss');
29
+ reject(err);
30
+ });
31
+ modal.present().catch((err) => {
32
+ reject(err);
33
+ this.errorLogger.logError('Failed to present modal');
34
+ });
35
+ })
36
+ .catch((err) => {
37
+ this.errorLogger.logError(err, 'Failed to create modal');
38
+ reject(err);
39
+ });
40
+ });
41
+ return result;
42
+ }
43
+ };
44
+ MultiSelectorService = __decorate([
45
+ __param(0, Inject(ErrorLogger)),
46
+ __metadata("design:paramtypes", [Object, ModalController])
47
+ ], MultiSelectorService);
48
+ export { MultiSelectorService };
49
+ //# sourceMappingURL=multi-selector.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-selector.service.js","sourceRoot":"","sources":["../../../../../../../libs/ui/src/lib/selector/multi-selector/multi-selector.service.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,eAAe,EAAgB,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAgB,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,IAAa,oBAAoB,GAAjC,MAAa,oBAAoB;IAC/B,YACwC,WAAyB,EAC9C,eAAgC;QADX,gBAAW,GAAX,WAAW,CAAc;QAC9C,oBAAe,GAAf,eAAe,CAAiB;IAChD,CAAC;IAEJ,cAAc,CACZ,KAAoB,EACpB,aAA4B;QAE5B,MAAM,MAAM,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC5D,MAAM,YAAY,GAAiB;gBACjC,SAAS,EAAE,sBAAsB;gBACjC,cAAc,EAAE;oBACd,KAAK;oBACL,aAAa;iBACd;gBACD,aAAa,EAAE,IAAI;aACpB,CAAC;YACF,IAAI,CAAC,eAAe;iBACjB,MAAM,CAAC,YAAY,CAAC;iBACpB,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;gBACd,KAAK;qBACF,YAAY,EAAE;qBACd,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC;qBACrD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBACb,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,gCAAgC,CAAC,CAAC;oBACjE,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC,CAAC,CAAC;gBACL,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBAC5B,MAAM,CAAC,GAAG,CAAC,CAAC;oBACZ,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC;gBACvD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBACb,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC,CAAC;gBACzD,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AA1CY,oBAAoB;IAE5B,WAAA,MAAM,CAAC,WAAW,CAAC,CAAA;6CACc,eAAe;GAHxC,oBAAoB,CA0ChC","sourcesContent":["import { Inject } from '@angular/core';\nimport { ModalController, ModalOptions } from '@ionic/angular/standalone';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { ISelectItem } from '../selector-interfaces';\nimport { MultiSelectorComponent } from './multi-selector.component';\n\nexport class MultiSelectorService {\n constructor(\n @Inject(ErrorLogger) private readonly errorLogger: IErrorLogger,\n private readonly modalController: ModalController,\n ) {}\n\n selectMultiple(\n items: ISelectItem[],\n selectedItems: ISelectItem[],\n ): Promise<ISelectItem[]> {\n const result = new Promise<ISelectItem[]>((resolve, reject) => {\n const modalOptions: ModalOptions = {\n component: MultiSelectorComponent,\n componentProps: {\n items,\n selectedItems,\n },\n keyboardClose: true,\n };\n this.modalController\n .create(modalOptions)\n .then((modal) => {\n modal\n .onDidDismiss()\n .then((res) => resolve(res.data?.selectedItems || []))\n .catch((err) => {\n this.errorLogger.logError(err, 'Failed to handle modal dismiss');\n reject(err);\n });\n modal.present().catch((err) => {\n reject(err);\n this.errorLogger.logError('Failed to present modal');\n });\n })\n .catch((err) => {\n this.errorLogger.logError(err, 'Failed to create modal');\n reject(err);\n });\n });\n\n return result;\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export * from './select-from-list.component';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../libs/ui/src/lib/selector/select-from-list/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC","sourcesContent":["export * from './select-from-list.component';\n"]}
@@ -0,0 +1,234 @@
1
+ import { ChangeDetectionStrategy, Component, computed, EventEmitter, forwardRef, input, Input, Output, signal, ViewChild, inject, } from '@angular/core';
2
+ import { FormsModule, NG_VALUE_ACCESSOR, } from '@angular/forms';
3
+ import { RouterModule } from '@angular/router';
4
+ import { IonButton, IonButtons, IonCheckbox, IonIcon, IonInput, IonItem, IonItemDivider, IonItemGroup, IonLabel, IonList, IonRadio, IonRadioGroup, IonSelect, IonSelectOption, IonSpinner, } from '@ionic/angular/standalone';
5
+ import { ErrorLogger } from '@sneat/core';
6
+ import { NEVER, Observable, Subject, takeUntil } from 'rxjs';
7
+ import * as i0 from "@angular/core";
8
+ import * as i1 from "@angular/forms";
9
+ export class SelectFromListComponent {
10
+ constructor() {
11
+ this.errorLogger = inject(ErrorLogger);
12
+ this.value = '';
13
+ this.valueChange = new EventEmitter();
14
+ this.filterLabel = 'Find';
15
+ this.label = 'Please choose';
16
+ this.items$ = NEVER;
17
+ this.$items = signal(undefined, ...(ngDevMode ? [{ debugName: "$items" }] : []));
18
+ this.other = 'none';
19
+ this.canAdd = false;
20
+ this.filterChanged = new EventEmitter();
21
+ this.selectMode = 'single';
22
+ this.isReadonly = false;
23
+ this.$isProcessing = input(...(ngDevMode ? [undefined, { debugName: "$isProcessing" }] : []));
24
+ // @Input() ngModel?: string;
25
+ // @Output() readonly ngModelChange = new EventEmitter<string>();
26
+ this.destroyed = new Subject();
27
+ this.$displayItems = signal(undefined, ...(ngDevMode ? [{ debugName: "$displayItems" }] : []));
28
+ this.$hiddenCount = computed(() => {
29
+ return (this.$items()?.length || 0) - (this.$displayItems()?.length || 0);
30
+ }, ...(ngDevMode ? [{ debugName: "$hiddenCount" }] : []));
31
+ this.isDisabled = false;
32
+ this.$filter = signal('', ...(ngDevMode ? [{ debugName: "$filter" }] : []));
33
+ this.$selectedItem = signal(undefined, ...(ngDevMode ? [{ debugName: "$selectedItem" }] : []));
34
+ this.onChange = (id) => {
35
+ this.value = id;
36
+ if (this.$selectedItem()?.id !== id) {
37
+ const selectedItem = this.items?.find((item) => item.id === id);
38
+ this.$selectedItem.set(selectedItem);
39
+ }
40
+ this.valueChange.emit(this.value);
41
+ };
42
+ this.onTouched = () => void 0;
43
+ }
44
+ focus() {
45
+ setTimeout(() => {
46
+ this.filterInput
47
+ ?.setFocus()
48
+ .catch(this.errorLogger.logErrorHandler('Failed to set focus to filter input'));
49
+ }, 100);
50
+ }
51
+ ngOnChanges(changes) {
52
+ if (changes['items']) {
53
+ this.$items.set(this.items);
54
+ this.applyFilter();
55
+ }
56
+ if (changes['items$'] && this.items$) {
57
+ this.items$?.pipe(takeUntil(this.destroyed)).subscribe((items) => {
58
+ this.$items.set(items);
59
+ this.applyFilter();
60
+ });
61
+ }
62
+ }
63
+ applyFilter() {
64
+ const f = this.$filter().trim().toLowerCase();
65
+ // console.log('SelectFromListComponent.applyFilter', f);
66
+ const items = this.$items();
67
+ let displayItems = f
68
+ ? items?.filter((v) => v.title.toLowerCase().includes(f) ||
69
+ v.longTitle?.toLowerCase().includes(f) ||
70
+ (this.filterItem && this.filterItem(v, f)))
71
+ : items;
72
+ if (displayItems && this.sortBy) {
73
+ switch (this.sortBy) {
74
+ case 'title':
75
+ displayItems = [...displayItems].toSorted((a, b) => a.title.localeCompare(b.title));
76
+ break;
77
+ case 'id':
78
+ displayItems = [...displayItems].toSorted((a, b) => a.id.localeCompare(b.id));
79
+ break;
80
+ }
81
+ }
82
+ this.$displayItems.set(displayItems);
83
+ }
84
+ select(item) {
85
+ switch (this.selectMode) {
86
+ case 'multiple':
87
+ return;
88
+ case 'single':
89
+ this.$selectedItem.set(item);
90
+ this.onChange(item.id);
91
+ if (this.$filter()) {
92
+ this.clearFilter();
93
+ }
94
+ }
95
+ }
96
+ onRadioChanged(event) {
97
+ if (this.selectMode !== 'single') {
98
+ return;
99
+ }
100
+ const value = event.detail['value'];
101
+ this.value = value;
102
+ this.onChange(value);
103
+ this.clearFilter();
104
+ }
105
+ onSelectChanged() {
106
+ // this.value = (event as CustomEvent).detail['value'] as string;
107
+ this.onChange(this.value || '');
108
+ }
109
+ registerOnChange(fn) {
110
+ this.onChange = fn;
111
+ }
112
+ registerOnTouched(fn) {
113
+ this.onTouched = fn;
114
+ }
115
+ setDisabledState(isDisabled) {
116
+ this.isDisabled = isDisabled;
117
+ }
118
+ writeValue(obj) {
119
+ this.value = obj;
120
+ }
121
+ onFilterChanged(event, source) {
122
+ void source;
123
+ this.$filter.set(event.detail.value || '');
124
+ this.applyFilter();
125
+ this.filterChanged.emit(this.$filter());
126
+ }
127
+ clearFilter() {
128
+ this.$filter.set('');
129
+ this.filterChanged.emit(this.$filter());
130
+ this.applyFilter();
131
+ }
132
+ deselect() {
133
+ this.value = '';
134
+ this.onChange(this.value);
135
+ }
136
+ onAdd(event) {
137
+ event.preventDefault();
138
+ this.value = this.$filter();
139
+ this.onChange(this.value);
140
+ }
141
+ onCheckboxChange(event, item) {
142
+ void event;
143
+ void item;
144
+ }
145
+ ngOnDestroy() {
146
+ this.destroyed.next();
147
+ }
148
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SelectFromListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
149
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: SelectFromListComponent, isStandalone: true, selector: "sneat-select-from-list", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, filterLabel: { classPropertyName: "filterLabel", publicName: "filterLabel", isSignal: false, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: false, isRequired: false, transformFunction: null }, listLabel: { classPropertyName: "listLabel", publicName: "listLabel", isSignal: false, isRequired: false, transformFunction: null }, listLabelColor: { classPropertyName: "listLabelColor", publicName: "listLabelColor", isSignal: false, isRequired: false, transformFunction: null }, isFilterable: { classPropertyName: "isFilterable", publicName: "isFilterable", isSignal: false, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: false, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: false, isRequired: false, transformFunction: null }, items$: { classPropertyName: "items$", publicName: "items$", isSignal: false, isRequired: false, transformFunction: null }, lastItemLines: { classPropertyName: "lastItemLines", publicName: "lastItemLines", isSignal: false, isRequired: false, transformFunction: null }, labelPlacement: { classPropertyName: "labelPlacement", publicName: "labelPlacement", isSignal: false, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: false, isRequired: false, transformFunction: null }, other: { classPropertyName: "other", publicName: "other", isSignal: false, isRequired: false, transformFunction: null }, canAdd: { classPropertyName: "canAdd", publicName: "canAdd", isSignal: false, isRequired: false, transformFunction: null }, filterItem: { classPropertyName: "filterItem", publicName: "filterItem", isSignal: false, isRequired: false, transformFunction: null }, selectMode: { classPropertyName: "selectMode", publicName: "selectMode", isSignal: false, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "isReadonly", isSignal: false, isRequired: false, transformFunction: null }, $isProcessing: { classPropertyName: "$isProcessing", publicName: "$isProcessing", isSignal: true, isRequired: false, transformFunction: null }, sortBy: { classPropertyName: "sortBy", publicName: "sortBy", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", filterChanged: "filterChanged" }, providers: [
150
+ {
151
+ provide: NG_VALUE_ACCESSOR,
152
+ useExisting: forwardRef(() => SelectFromListComponent),
153
+ multi: true,
154
+ },
155
+ ], viewQueries: [{ propertyName: "addInput", first: true, predicate: IonInput, descendants: true }, { propertyName: "filterInput", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "selectInput", first: true, predicate: ["selectInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@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", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { kind: "component", type: IonRadioGroup, selector: "ion-radio-group", inputs: ["allowEmptySelection", "compareWith", "errorText", "helperText", "name", "value"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonRadio, selector: "ion-radio", inputs: ["alignment", "color", "disabled", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonItemGroup, selector: "ion-item-group" }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
156
+ }
157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SelectFromListComponent, decorators: [{
158
+ type: Component,
159
+ args: [{ providers: [
160
+ {
161
+ provide: NG_VALUE_ACCESSOR,
162
+ useExisting: forwardRef(() => SelectFromListComponent),
163
+ multi: true,
164
+ },
165
+ ], imports: [
166
+ FormsModule,
167
+ RouterModule,
168
+ IonItem,
169
+ IonIcon,
170
+ IonSelect,
171
+ IonSelectOption,
172
+ IonButtons,
173
+ IonButton,
174
+ IonInput,
175
+ IonItemDivider,
176
+ IonRadioGroup,
177
+ IonList,
178
+ IonLabel,
179
+ IonCheckbox,
180
+ IonRadio,
181
+ IonItemGroup,
182
+ IonSpinner,
183
+ ], changeDetection: ChangeDetectionStrategy.OnPush, selector: 'sneat-select-from-list', template: "@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" }]
184
+ }], propDecorators: { value: [{
185
+ type: Input
186
+ }], valueChange: [{
187
+ type: Output
188
+ }], filterLabel: [{
189
+ type: Input
190
+ }], label: [{
191
+ type: Input
192
+ }], listLabel: [{
193
+ type: Input
194
+ }], listLabelColor: [{
195
+ type: Input
196
+ }], isFilterable: [{
197
+ type: Input
198
+ }], isLoading: [{
199
+ type: Input
200
+ }], items: [{
201
+ type: Input
202
+ }], items$: [{
203
+ type: Input
204
+ }], lastItemLines: [{
205
+ type: Input
206
+ }], labelPlacement: [{
207
+ type: Input
208
+ }], justify: [{
209
+ type: Input
210
+ }], other: [{
211
+ type: Input
212
+ }], canAdd: [{
213
+ type: Input
214
+ }], filterItem: [{
215
+ type: Input
216
+ }], filterChanged: [{
217
+ type: Output
218
+ }], selectMode: [{
219
+ type: Input
220
+ }], isReadonly: [{
221
+ type: Input
222
+ }], $isProcessing: [{ type: i0.Input, args: [{ isSignal: true, alias: "$isProcessing", required: false }] }], sortBy: [{
223
+ type: Input
224
+ }], addInput: [{
225
+ type: ViewChild,
226
+ args: [IonInput, { static: false }]
227
+ }], filterInput: [{
228
+ type: ViewChild,
229
+ args: ['filterInput', { static: false }]
230
+ }], selectInput: [{
231
+ type: ViewChild,
232
+ args: ['selectInput', { static: false }]
233
+ }] } });
234
+ //# sourceMappingURL=select-from-list.component.js.map