@sneat/ui 0.1.3 → 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
@@ -1,283 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- Component,
4
- computed,
5
- EventEmitter,
6
- forwardRef,
7
- input,
8
- Input,
9
- OnChanges,
10
- OnDestroy,
11
- Output,
12
- signal,
13
- SimpleChanges,
14
- ViewChild,
15
- inject,
16
- } from '@angular/core';
17
- import {
18
- ControlValueAccessor,
19
- FormsModule,
20
- NG_VALUE_ACCESSOR,
21
- } from '@angular/forms';
22
- import { RouterModule } from '@angular/router';
23
- import {
24
- IonButton,
25
- IonButtons,
26
- IonCheckbox,
27
- IonIcon,
28
- IonInput,
29
- IonItem,
30
- IonItemDivider,
31
- IonItemGroup,
32
- IonLabel,
33
- IonList,
34
- IonRadio,
35
- IonRadioGroup,
36
- IonSelect,
37
- IonSelectOption,
38
- IonSpinner,
39
- } from '@ionic/angular/standalone';
40
- import { ErrorLogger, IErrorLogger } from '@sneat/core';
41
- import { NEVER, Observable, Subject, takeUntil } from 'rxjs';
42
- import { ISelectItem } from '../selector-interfaces';
43
-
44
- @Component({
45
- providers: [
46
- {
47
- provide: NG_VALUE_ACCESSOR,
48
- useExisting: forwardRef(() => SelectFromListComponent),
49
- multi: true,
50
- },
51
- ],
52
- imports: [
53
- FormsModule,
54
- RouterModule,
55
- IonItem,
56
- IonIcon,
57
- IonSelect,
58
- IonSelectOption,
59
- IonButtons,
60
- IonButton,
61
- IonInput,
62
- IonItemDivider,
63
- IonRadioGroup,
64
- IonList,
65
- IonLabel,
66
- IonCheckbox,
67
- IonRadio,
68
- IonItemGroup,
69
- IonSpinner,
70
- ],
71
- changeDetection: ChangeDetectionStrategy.OnPush,
72
- selector: 'sneat-select-from-list',
73
- templateUrl: './select-from-list.component.html',
74
- })
75
- export class SelectFromListComponent
76
- implements ControlValueAccessor, OnChanges, OnDestroy
77
- {
78
- private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
79
-
80
- @Input() value: string | undefined = '';
81
- @Output() readonly valueChange = new EventEmitter<string>();
82
-
83
- @Input() filterLabel = 'Find';
84
- @Input() label = 'Please choose';
85
- @Input() listLabel?: 'divider';
86
- @Input() listLabelColor?:
87
- | 'light'
88
- | 'medium'
89
- | 'primary'
90
- | 'secondary'
91
- | 'tertiary';
92
- @Input() isFilterable?: boolean;
93
- @Input() isLoading?: boolean;
94
- @Input() items?: readonly ISelectItem[];
95
- @Input() items$: Observable<ISelectItem[]> = NEVER;
96
-
97
- private $items = signal<undefined | readonly ISelectItem[]>(undefined);
98
-
99
- @Input() lastItemLines?: 'none' | 'inset' | 'full';
100
- @Input() labelPlacement?: 'start' | 'end' | 'fixed' | 'stacked';
101
- @Input() justify?: 'start' | 'end' | 'space-between';
102
- @Input() other: 'top' | 'bottom' | 'none' = 'none';
103
- @Input() canAdd = false;
104
- @Input() filterItem?: (item: ISelectItem, filter: string) => boolean;
105
-
106
- @Output() readonly filterChanged = new EventEmitter<string>();
107
-
108
- @Input() selectMode: 'single' | 'multiple' = 'single';
109
-
110
- @Input() isReadonly = false;
111
-
112
- public $isProcessing = input<boolean>();
113
-
114
- @Input() sortBy?: 'title' | 'id';
115
-
116
- // @Input() ngModel?: string;
117
- // @Output() readonly ngModelChange = new EventEmitter<string>();
118
-
119
- private destroyed = new Subject<void>();
120
-
121
- @ViewChild(IonInput, { static: false }) addInput?: IonInput;
122
- @ViewChild('filterInput', { static: false }) filterInput?: IonInput;
123
- @ViewChild('selectInput', { static: false }) selectInput?: IonSelect;
124
-
125
- protected readonly $displayItems = signal<undefined | readonly ISelectItem[]>(
126
- undefined,
127
- );
128
-
129
- protected readonly $hiddenCount = computed(() => {
130
- return (this.$items()?.length || 0) - (this.$displayItems()?.length || 0);
131
- });
132
-
133
- protected isDisabled = false;
134
-
135
- protected readonly $filter = signal('');
136
-
137
- public focus(): void {
138
- setTimeout(() => {
139
- this.filterInput
140
- ?.setFocus()
141
- .catch(
142
- this.errorLogger.logErrorHandler(
143
- 'Failed to set focus to filter input',
144
- ),
145
- );
146
- }, 100);
147
- }
148
-
149
- ngOnChanges(changes: SimpleChanges): void {
150
- if (changes['items']) {
151
- this.$items.set(this.items);
152
- this.applyFilter();
153
- }
154
- if (changes['items$'] && this.items$) {
155
- this.items$?.pipe(takeUntil(this.destroyed)).subscribe((items) => {
156
- this.$items.set(items);
157
- this.applyFilter();
158
- });
159
- }
160
- }
161
-
162
- private applyFilter(): void {
163
- const f = this.$filter().trim().toLowerCase();
164
- // console.log('SelectFromListComponent.applyFilter', f);
165
- const items = this.$items();
166
- let displayItems = f
167
- ? items?.filter(
168
- (v) =>
169
- v.title.toLowerCase().includes(f) ||
170
- v.longTitle?.toLowerCase().includes(f) ||
171
- (this.filterItem && this.filterItem(v, f)),
172
- )
173
- : items;
174
- if (displayItems && this.sortBy) {
175
- switch (this.sortBy) {
176
- case 'title':
177
- displayItems = [...displayItems].toSorted((a, b) =>
178
- a.title.localeCompare(b.title),
179
- );
180
- break;
181
- case 'id':
182
- displayItems = [...displayItems].toSorted((a, b) =>
183
- a.id.localeCompare(b.id),
184
- );
185
- break;
186
- }
187
- }
188
- this.$displayItems.set(displayItems);
189
- }
190
-
191
- protected select(item: ISelectItem): void {
192
- switch (this.selectMode) {
193
- case 'multiple':
194
- return;
195
- case 'single':
196
- this.$selectedItem.set(item);
197
- this.onChange(item.id);
198
- if (this.$filter()) {
199
- this.clearFilter();
200
- }
201
- }
202
- }
203
-
204
- protected readonly $selectedItem = signal<ISelectItem | undefined>(undefined);
205
-
206
- protected onRadioChanged(event: Event): void {
207
- if (this.selectMode !== 'single') {
208
- return;
209
- }
210
- const value = (event as CustomEvent).detail['value'] as string;
211
- this.value = value;
212
- this.onChange(value);
213
- this.clearFilter();
214
- }
215
-
216
- protected onSelectChanged(): void {
217
- // this.value = (event as CustomEvent).detail['value'] as string;
218
- this.onChange(this.value || '');
219
- }
220
-
221
- protected onChange = (id: string) => {
222
- this.value = id;
223
- if (this.$selectedItem()?.id !== id) {
224
- const selectedItem = this.items?.find((item) => item.id === id);
225
- this.$selectedItem.set(selectedItem);
226
- }
227
- this.valueChange.emit(this.value);
228
- };
229
-
230
- onTouched: () => void = () => void 0;
231
-
232
- registerOnChange(fn: (v: unknown) => void): void {
233
- this.onChange = fn;
234
- }
235
-
236
- registerOnTouched(fn: () => void): void {
237
- this.onTouched = fn;
238
- }
239
-
240
- setDisabledState(isDisabled: boolean): void {
241
- this.isDisabled = isDisabled;
242
- }
243
-
244
- writeValue(obj: unknown): void {
245
- this.value = obj as string;
246
- }
247
-
248
- protected onFilterChanged(
249
- event: CustomEvent,
250
- source: 'ionChange' | 'ionInput',
251
- ): void {
252
- void source;
253
- this.$filter.set(event.detail.value || '');
254
- this.applyFilter();
255
- this.filterChanged.emit(this.$filter());
256
- }
257
-
258
- protected clearFilter(): void {
259
- this.$filter.set('');
260
- this.filterChanged.emit(this.$filter());
261
- this.applyFilter();
262
- }
263
-
264
- protected deselect(): void {
265
- this.value = '';
266
- this.onChange(this.value);
267
- }
268
-
269
- protected onAdd(event: Event): void {
270
- event.preventDefault();
271
- this.value = this.$filter();
272
- this.onChange(this.value);
273
- }
274
-
275
- protected onCheckboxChange(event: CustomEvent, item: ISelectItem): void {
276
- void event;
277
- void item;
278
- }
279
-
280
- ngOnDestroy(): void {
281
- this.destroyed.next();
282
- }
283
- }
@@ -1,43 +0,0 @@
1
- import {
2
- Directive,
3
- inject,
4
- InjectionToken,
5
- Input,
6
- signal,
7
- } from '@angular/core';
8
- import { ModalController, PopoverController } from '@ionic/angular/standalone';
9
- import { SneatBaseComponent } from '../components/sneat-base.component';
10
-
11
- export const OverlayController = new InjectionToken<
12
- ModalController | PopoverController
13
- >('OverlayController');
14
-
15
- @Directive()
16
- export abstract class SelectorBaseComponent<T> extends SneatBaseComponent {
17
- @Input() public selectMode?: 'single' | 'multiple';
18
-
19
- protected readonly overlayController = inject(OverlayController);
20
-
21
- protected $selectedItems = signal<readonly T[]>([]);
22
-
23
- protected close(event?: Event): void {
24
- event?.stopPropagation();
25
- event?.preventDefault();
26
- this.overlayController
27
- .dismiss({ selectedItems: this.$selectedItems() })
28
- .catch(
29
- this.errorLogger.logErrorHandler(
30
- 'failed to dismiss contact selector modal',
31
- ),
32
- );
33
- }
34
- }
35
-
36
- @Directive()
37
- export abstract class SelectorModalComponent<
38
- T,
39
- > extends SelectorBaseComponent<T> {
40
- protected get modalController() {
41
- return this.overlayController as ModalController;
42
- }
43
- }
@@ -1,62 +0,0 @@
1
- import { inject } from '@angular/core';
2
- import { ModalController, ModalOptions } from '@ionic/angular/standalone';
3
- import type { ComponentProps, ComponentRef } from '@ionic/core';
4
- import { ErrorLogger } from '@sneat/core';
5
- import { ISelectItem } from './selector-interfaces';
6
- import { ISelectorOptions } from './selector-options';
7
-
8
- export abstract class SelectorBaseService<T = ISelectItem> {
9
- protected readonly errorLogger = inject(ErrorLogger);
10
- private readonly modalController = inject(ModalController);
11
-
12
- protected constructor(private readonly component: ComponentRef) {}
13
-
14
- public async selectSingleInModal(
15
- options: ISelectorOptions<T>,
16
- ): Promise<T | null> {
17
- const result = await this.selectMultipleInModal(options);
18
- return result ? result[0] : null;
19
- }
20
-
21
- // We make it protected so each service must override it for easiness of navigation
22
- protected async selectMultipleInModal(
23
- options: ISelectorOptions<T>,
24
- ): Promise<T[] | undefined> {
25
-
26
- let result: readonly T[] | undefined = undefined;
27
-
28
- const onSelected = options.onSelected;
29
-
30
- options = {
31
- ...options,
32
- onSelected: async (items?: T[]): Promise<void> => {
33
- if (onSelected) {
34
- await onSelected(items);
35
- }
36
- await this.modalController.dismiss(items);
37
- result = items;
38
- },
39
- };
40
- let componentProps: ComponentProps<unknown> = {
41
- ...options.componentProps,
42
- selectMode: 'multiple',
43
- onSelected: options.onSelected,
44
- mode: 'modal',
45
- };
46
- if (options.title) {
47
- componentProps = { ...componentProps, title: options.title };
48
- }
49
- const modalOptions: ModalOptions = {
50
- component: this.component,
51
- componentProps: componentProps,
52
- keyboardClose: true,
53
- };
54
- const modal = await this.modalController.create(modalOptions);
55
- await modal.present();
56
-
57
- // 🔹 Track when the modal is dismissed
58
- await modal.onDidDismiss();
59
-
60
- return result;
61
- }
62
- }
@@ -1,28 +0,0 @@
1
- export type IonicColor =
2
- | 'danger'
3
- | 'success'
4
- | 'warning'
5
- | 'primary'
6
- | 'secondary'
7
- | 'tertiary'
8
- | 'dark'
9
- | 'medium'
10
- | 'light';
11
-
12
- export interface ISelectItem {
13
- readonly id: string;
14
- readonly title: string;
15
- readonly shortTitle?: string;
16
- readonly longTitle?: string;
17
- readonly description1?: string;
18
- readonly description2?: string;
19
- readonly emoji?: string;
20
- readonly iconName?: string;
21
- readonly iconColor?: IonicColor;
22
- readonly labelColor?: IonicColor;
23
- }
24
-
25
- export interface ISelectItemEvent {
26
- item: ISelectItem;
27
- event: Event;
28
- }
@@ -1,18 +0,0 @@
1
- import { ComponentProps } from '@ionic/core';
2
- import { Signal } from '@angular/core';
3
- import { Observable } from 'rxjs';
4
-
5
- export interface ISelectorEvents<T> {
6
- readonly onSelected?: (item?: T[]) => Promise<void>;
7
- readonly onAdded?: (item: T) => Observable<void>;
8
- readonly onRemoved?: (item: T) => Observable<void>;
9
- }
10
-
11
- export interface ISelectorOptions<T> extends ISelectorEvents<T> {
12
- readonly items?: Signal<readonly T[]>;
13
- readonly selectedItems?: readonly T[];
14
- readonly max?: number;
15
- readonly title?: string;
16
-
17
- componentProps?: ComponentProps<unknown>;
18
- }
package/src/test-setup.ts DELETED
@@ -1,3 +0,0 @@
1
- import { setupTestEnvironment } from '@sneat/core/testing';
2
-
3
- setupTestEnvironment();
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.angular.json",
3
- "files": [],
4
- "include": [],
5
- "references": [
6
- {
7
- "path": "./tsconfig.lib.json"
8
- },
9
- {
10
- "path": "./tsconfig.spec.json"
11
- }
12
- ]
13
- }
package/tsconfig.lib.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.lib.base.json",
3
- "exclude": [
4
- "vite.config.ts",
5
- "vite.config.mts",
6
- "vitest.config.ts",
7
- "vitest.config.mts",
8
- "src/**/*.test.ts",
9
- "src/**/*.spec.ts",
10
- "src/**/*.test.tsx",
11
- "src/**/*.spec.tsx",
12
- "src/**/*.test.js",
13
- "src/**/*.spec.js",
14
- "src/**/*.test.jsx",
15
- "src/**/*.spec.jsx",
16
- "src/test-setup.ts",
17
- "src/lib/testing/**/*"
18
- ]
19
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.lib.json",
3
- "compilerOptions": {
4
- "declarationMap": false
5
- },
6
- "angularCompilerOptions": {}
7
- }
@@ -1,31 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../dist/out-tsc",
5
- "types": [
6
- "vitest/globals",
7
- "vitest/importMeta",
8
- "vite/client",
9
- "node",
10
- "vitest"
11
- ]
12
- },
13
- "include": [
14
- "vite.config.ts",
15
- "vite.config.mts",
16
- "vitest.config.ts",
17
- "vitest.config.mts",
18
- "src/**/*.test.ts",
19
- "src/**/*.spec.ts",
20
- "src/**/*.test.tsx",
21
- "src/**/*.spec.tsx",
22
- "src/**/*.test.js",
23
- "src/**/*.spec.js",
24
- "src/**/*.test.jsx",
25
- "src/**/*.spec.jsx",
26
- "src/**/*.d.ts"
27
- ],
28
- "files": [
29
- "src/test-setup.ts"
30
- ]
31
- }
package/vite.config.mts DELETED
@@ -1,10 +0,0 @@
1
- /// <reference types='vitest' />
2
- import { defineConfig } from 'vitest/config';
3
- import { createBaseViteConfig } from '../../vite.config.base';
4
-
5
- export default defineConfig(() =>
6
- createBaseViteConfig({
7
- dirname: __dirname,
8
- name: 'ui',
9
- }),
10
- );
File without changes