@windwalker-io/unicorn-next 0.1.12 → 0.1.14

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.
package/dist/index.d.ts CHANGED
@@ -190,6 +190,10 @@ export declare function createUnicorn(): UnicornApp;
190
190
 
191
191
  export declare function createUnicornWithPlugins(): UnicornApp;
192
192
 
193
+ declare type CssSource = string | (() => Promise<{
194
+ default: string;
195
+ }>);
196
+
193
197
  export declare function data(name: string, data?: any): any;
194
198
 
195
199
  export declare function data<T = void, R = [T] extends [void] ? any : T | undefined>(name: string): R;
@@ -209,7 +213,7 @@ export declare function delegate(wrapper: Element | string, selector: string, ev
209
213
 
210
214
  export { deleteConfirm }
211
215
 
212
- declare function destroy(selector: string): void;
216
+ declare function destroy(selector: string | HTMLElement): void;
213
217
 
214
218
  declare interface Dictionary<T = any> {
215
219
  [key: string]: T;
@@ -299,15 +303,23 @@ declare interface FileDragOptions {
299
303
 
300
304
  export declare function forceArray<T>(item: T | T[]): T[];
301
305
 
306
+ export declare interface FormSubmitOptions {
307
+ form?: string | Element;
308
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
309
+ url?: string;
310
+ data?: Record<string, any>;
311
+ }
312
+
302
313
  export declare interface FormValidationOptions {
303
314
  scroll: boolean;
304
315
  validatedClass: null;
305
316
  fieldSelector: null;
306
317
  scrollOffset: number;
307
318
  enabled: boolean;
319
+ fieldDefaults?: Partial<Omit<FieldValidationOptions, 'inputOptions' | 'formSelector'>>;
308
320
  }
309
321
 
310
- declare function get(selector: string, options?: Record<string, any>): Promise<TinymceController>;
322
+ declare function get(selector: string | HTMLElement, options?: Record<string, any>): Promise<TinymceController>;
311
323
 
312
324
  export declare function getBoundedInstance<T = any, E = Element>(selector: E, name: string, callback?: ((el: E) => any)): T;
313
325
 
@@ -356,9 +368,9 @@ declare interface IFrameModalOptions {
356
368
 
357
369
  export declare function initAlpineComponent(directive: string): Promise<void>;
358
370
 
359
- export declare function injectCssToDocument(doc: Document, ...css: (string | CSSStyleSheet)[]): CSSStyleSheet[];
371
+ export declare function injectCssToDocument(doc: Document, ...css: (CssSource | CSSStyleSheet)[]): Promise<CSSStyleSheet[]>;
360
372
 
361
- export declare function injectCssToDocument(...css: (string | CSSStyleSheet)[]): CSSStyleSheet[];
373
+ export declare function injectCssToDocument(...css: (CssSource | CSSStyleSheet)[]): Promise<CSSStyleSheet[]>;
362
374
 
363
375
  declare type InjectionKey<T = any> = string | symbol | Constructor<T>;
364
376
 
@@ -490,7 +502,7 @@ declare interface ModalListenMessagesOptions {
490
502
  modalSelector: string;
491
503
  }
492
504
 
493
- declare type ModalSelectCallback = (item: any) => void;
505
+ declare type ModalSelectCallback = (item: any, ...args: any[]) => void;
494
506
 
495
507
  declare interface ModalSelectModule {
496
508
  createCallback: typeof createCallback;
@@ -614,12 +626,13 @@ declare class S3MultipartUploader extends S3MultipartUploader_base {
614
626
  path: string;
615
627
  partNumber: number;
616
628
  chunkSize: number;
629
+ abortController?: AbortController;
617
630
  onUploadProgress: (e: AxiosProgressEvent) => void;
618
631
  }): Promise<{
619
632
  blob: Blob;
620
633
  etag: string;
621
634
  }>;
622
- protected request<T = Record<string, any>>(action: RouteActions, body: Record<string, any>): Promise<T>;
635
+ protected request<T = Record<string, any>>(action: RouteActions, body: Record<string, any>, config?: Partial<AxiosRequestConfig>): Promise<T>;
623
636
  abort(id: string, path: string): Promise<void>;
624
637
  updateProgress(loaded: number, total: number, options: S3MultipartUploaderRequestOptions): void;
625
638
  resolveRoute(action: RouteActions): Promise<string>;
@@ -673,6 +686,7 @@ declare interface S3MultipartUploaderRequestOptions {
673
686
  ContentDisposition?: string;
674
687
  ACL?: 'public-read' | 'private' | 'authenticated-read' | 'public-read-write' | string;
675
688
  extra?: Record<string, any>;
689
+ abortController?: AbortController;
676
690
  }
677
691
 
678
692
  declare class S3Uploader extends S3Uploader_base implements EventAwareInterface {
@@ -748,6 +762,7 @@ declare interface S3UploaderRequestOptions {
748
762
  'Content-Type'?: string;
749
763
  'Content-Disposition'?: string;
750
764
  key?: string;
765
+ signal?: AbortSignal;
751
766
  [name: string]: any;
752
767
  }
753
768
 
@@ -858,6 +873,10 @@ export declare function slideToggle(target: string | HTMLElement, duration?: num
858
873
 
859
874
  export declare function slideUp(target: string | HTMLElement, duration?: number): Promise<Animation | void>;
860
875
 
876
+ declare type Source = string | (() => Promise<{
877
+ default: string;
878
+ }>);
879
+
861
880
  declare type StartEventHandler = (fileData: FormData) => void;
862
881
 
863
882
  declare type SuccessEventHandler = (url: string, res: S3UploaderResponse) => void;
@@ -877,6 +896,7 @@ declare class TinymceController {
877
896
  getValue(): string;
878
897
  setValue(text: string): void;
879
898
  imageUploadHandler(blobInfo: UploadHandlerParams[0], progress: UploadHandlerParams[1]): Promise<any>;
899
+ destroy(): void;
880
900
  }
881
901
 
882
902
  declare interface TinymceModule {
@@ -948,10 +968,11 @@ export declare class UnicornAssetUri {
948
968
  declare class UnicornFieldValidation {
949
969
  protected el: HTMLElement;
950
970
  $input: InputElements | undefined;
951
- options: FieldValidationOptions;
971
+ options: Partial<FieldValidationOptions>;
952
972
  static is: string;
953
973
  constructor(el: HTMLElement, options?: Partial<FieldValidationOptions>);
954
- mergeOptions(options: Partial<FieldValidationOptions>): FieldValidationOptions;
974
+ setOptions(options: Partial<FieldValidationOptions>): this;
975
+ get mergedOptions(): FieldValidationOptions;
955
976
  get $form(): HTMLFormElement;
956
977
  get errorSelector(): string;
957
978
  get selector(): string;
@@ -976,6 +997,7 @@ declare class UnicornFieldValidation {
976
997
  */
977
998
  updateValidClass(valid: Boolean): void;
978
999
  getFormValidation(element?: Nullable<HTMLFormElement>): UnicornFormValidation | null;
1000
+ get globalOptions(): Partial<FieldValidationOptions>;
979
1001
  getValidator(name: string): [Validator, Record<string, any>] | null;
980
1002
  handleCustomResult(result: boolean | string | undefined, validator?: Nullable<Validator>): boolean;
981
1003
  handleAsyncCustomResult(result: boolean, validator?: Nullable<Validator>): boolean;
@@ -1030,6 +1052,7 @@ declare class UnicornFormElement {
1030
1052
  * Make a DELETE request.
1031
1053
  */
1032
1054
  delete(url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean;
1055
+ destroy(): void;
1033
1056
  /**
1034
1057
  * @see https://stackoverflow.com/a/53739792
1035
1058
  *
@@ -1058,6 +1081,7 @@ declare class UnicornFormValidation {
1058
1081
  prepareFields(inputs: HTMLElement[]): Promise<void>;
1059
1082
  prepareFieldWrapper(input: HTMLElement): HTMLElement | null;
1060
1083
  findFields(containsPresets?: boolean): HTMLElement[];
1084
+ getFieldComponents(containsPresets?: boolean): UnicornFieldValidation[];
1061
1085
  getFieldComponent(input: HTMLElement): UnicornFieldValidation | null;
1062
1086
  validateAll(fields?: Nullable<HTMLElement[]>): boolean;
1063
1087
  validateAllAsync(fields?: Nullable<HTMLElement[]>): Promise<boolean>;
@@ -1272,9 +1296,9 @@ export declare function useCheckboxesMultiSelect(selector?: Nullable<string | HT
1272
1296
  */
1273
1297
  export declare function useColorPicker(selector?: Nullable<string | HTMLElement | NodeListOf<HTMLElement>>, options?: Partial<SpectrumOptions>): Promise<any>;
1274
1298
 
1275
- export declare function useCssImport(...hrefs: string[]): Promise<CSSStyleSheet[]>;
1299
+ export declare function useCssImport(...hrefs: Source[]): Promise<CSSStyleSheet[]>;
1276
1300
 
1277
- export declare function useCssIncludes(...hrefs: string[]): Promise<void[]>;
1301
+ export declare function useCssIncludes(...hrefs: Source[]): Promise<void[]>;
1278
1302
 
1279
1303
  export declare function useDisableIfStackNotEmpty(buttonSelector?: string, stackName?: string): void;
1280
1304
 
@@ -1308,6 +1332,8 @@ export declare function useFormAsync(ele?: string | Element, options?: Record<st
1308
1332
 
1309
1333
  export declare function useFormComponent(ele?: string | Element, options?: Record<string, any>): Promise<UnicornFormElement | null>;
1310
1334
 
1335
+ export declare function useFormSubmit(options?: FormSubmitOptions): Promise<boolean>;
1336
+
1311
1337
  export declare function useFormValidation(): Promise<ValidationModule>;
1312
1338
 
1313
1339
  export declare function useFormValidation(selector: string | Element): Promise<UnicornFormValidation | null>;
@@ -1367,7 +1393,7 @@ export declare function useS3Uploader(): Promise<S3UploaderModule>;
1367
1393
 
1368
1394
  export declare function useS3Uploader(name: string, options?: Partial<S3UploaderGlobalOptions>): Promise<S3Uploader>;
1369
1395
 
1370
- export declare function useScriptImport(src: string, attrs?: Record<string, string>): Promise<void>;
1396
+ export declare function useScriptImport(src: Source, attrs?: Record<string, string>): Promise<void>;
1371
1397
 
1372
1398
  export declare function useSeriesImport(...src: any[]): Promise<any>;
1373
1399
 
@@ -1424,6 +1450,7 @@ export declare function useUnicornPhpAdapter(app?: UnicornApp): {
1424
1450
  modalTree: typeof useFieldModalTree;
1425
1451
  multiUploader: typeof useFieldMultiUploader;
1426
1452
  tomSelect: typeof useTomSelect;
1453
+ listDependent: typeof useListDependent;
1427
1454
  bootstrap: {
1428
1455
  tooltip: typeof useBs5Tooltip;
1429
1456
  buttonRadio: {
@@ -1480,10 +1507,9 @@ export declare function watchAttributes<T extends HTMLElement>(el: T, callback?:
1480
1507
  export { }
1481
1508
 
1482
1509
 
1483
- declare module '@windwalker-io/unicorn-next' {
1484
- interface UnicornApp {
1485
- /** @deprecated Only for code generator use. */
1486
- $ui: typeof methods;
1510
+ declare global {
1511
+ interface Node {
1512
+ __unicorn?: any;
1487
1513
  }
1488
1514
  }
1489
1515
 
@@ -1496,16 +1522,13 @@ declare global {
1496
1522
  }
1497
1523
 
1498
1524
 
1499
- declare global {
1500
- interface Node {
1501
- __unicorn?: any;
1525
+ declare module '@windwalker-io/unicorn-next' {
1526
+ interface UnicornApp {
1527
+ /** @deprecated Only for code generator use. */
1528
+ $ui: typeof methods;
1502
1529
  }
1503
1530
  }
1504
1531
 
1505
- declare global {
1506
- var S: any;
1507
- }
1508
-
1509
1532
 
1510
1533
  declare module 'axios' {
1511
1534
  interface AxiosRequestConfig {
@@ -1517,9 +1540,8 @@ declare module 'axios' {
1517
1540
  }
1518
1541
  }
1519
1542
 
1520
-
1521
1543
  declare global {
1522
- var tinymce: TinyMCE;
1544
+ var S: any;
1523
1545
  }
1524
1546
 
1525
1547
 
@@ -1528,3 +1550,8 @@ declare global {
1528
1550
  bootstrap: typeof bootstrap;
1529
1551
  }
1530
1552
  }
1553
+
1554
+
1555
+ declare global {
1556
+ var tinymce: TinyMCE;
1557
+ }
package/dist/unicorn.js CHANGED
@@ -1,9 +1,9 @@
1
- import { ax, ag, aL, b9, aK, aH, _, h, aO, aM, aJ, b, J, I, aQ, d, e, ah, b3, am, an, ac, D, C, ae, aC, t, T, S, ad, x, y, A, aN, U, B, f, a8, i, aA, l, N, z, ay, aP, p, a6, aq, au, av, aw, at, c, r, w, v, M, G, F, s, al, Q, R, P, E, L, aj, K, aF, q, a0, $, a1, Y, V, ab, aE, X, W, aR, aS, aT, aU, aV, aW, aX, j, a4, a2, aY, g, k, a5, a3, aZ, u, a_, aa, ap, Z, aB, as, a$, ar, n, b1, b0, ai, aD, b2, m, o, b4, b5, O, aG, b6, ak, a, ao, b8, aI, b7, az, a9 } from "./chunks/unicorn.js";
1
+ import { ax, ag, aL, ba, aK, aH, _, h, aO, aM, aJ, b, J, I, aQ, d, e, ah, b4, am, an, ac, D, C, ae, aC, t, T, S, ad, x, y, A, aN, U, B, f, a8, i, aA, l, N, z, ay, aP, p, a6, aq, au, av, aw, at, c, r, w, v, M, G, F, s, al, Q, R, P, E, L, aj, K, aF, q, a0, $, a1, Y, V, ab, aE, X, W, aR, aS, aT, aU, aV, aW, aX, j, a4, a2, aY, aZ, g, k, a5, a3, a_, u, a$, aa, ap, Z, aB, as, b0, ar, n, b2, b1, ai, aD, b3, m, o, b5, b6, O, aG, b7, ak, a, ao, b9, aI, b8, az, a9 } from "./chunks/unicorn.js";
2
2
  export {
3
3
  ax as AttributeMutationObserver,
4
4
  ag as EventMixin,
5
5
  aL as UnicornAssetUri,
6
- b9 as UnicornPhpAdapter,
6
+ ba as UnicornPhpAdapter,
7
7
  aK as UnicornSystemUri,
8
8
  aH as UnicornUI,
9
9
  _ as __,
@@ -18,7 +18,7 @@ export {
18
18
  d as clearMessages,
19
19
  e as clearNotifies,
20
20
  ah as createQueue,
21
- b3 as createStack,
21
+ b4 as createStack,
22
22
  am as createUnicorn,
23
23
  an as createUnicornWithPlugins,
24
24
  ac as data,
@@ -90,39 +90,40 @@ export {
90
90
  a4 as useForm,
91
91
  a2 as useFormAsync,
92
92
  aY as useFormComponent,
93
+ aZ as useFormSubmit,
93
94
  g as useFormValidation,
94
95
  k as useFormValidationInstance,
95
96
  a5 as useGrid,
96
97
  a3 as useGridAsync,
97
- aZ as useGridComponent,
98
+ a_ as useGridComponent,
98
99
  u as useHttpClient,
99
- a_ as useIframeModal,
100
+ a$ as useIframeModal,
100
101
  aa as useImport,
101
102
  ap as useInject,
102
103
  Z as useKeepAlive,
103
104
  aB as useLang,
104
105
  as as useLegacy,
105
- a$ as useListDependent,
106
+ b0 as useListDependent,
106
107
  ar as useMacro,
107
108
  n as useQueue,
108
- b1 as useS3MultipartUploader,
109
- b0 as useS3Uploader,
109
+ b2 as useS3MultipartUploader,
110
+ b1 as useS3Uploader,
110
111
  ai as useScriptImport,
111
112
  aD as useSeriesImport,
112
- b2 as useShowOn,
113
+ b3 as useShowOn,
113
114
  m as useStack,
114
115
  o as useSystemUri,
115
- b4 as useTinymce,
116
- b5 as useTinymceHook,
116
+ b5 as useTinymce,
117
+ b6 as useTinymceHook,
117
118
  O as useTomSelect,
118
119
  aG as useUI,
119
- b6 as useUIBootstrap5,
120
+ b7 as useUIBootstrap5,
120
121
  ak as useUITheme,
121
122
  a as useUniDirective,
122
123
  ao as useUnicorn,
123
- b8 as useUnicornPhpAdapter,
124
+ b9 as useUnicornPhpAdapter,
124
125
  aI as useVueComponentField,
125
- b7 as useWebDirective,
126
+ b8 as useWebDirective,
126
127
  az as wait,
127
128
  a9 as watchAttributes
128
129
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windwalker-io/unicorn-next",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Unicorn framework js library",
5
5
  "type": "module",
6
6
  "typings": "dist/index.d.ts",
@@ -63,7 +63,7 @@
63
63
  ]
64
64
  },
65
65
  "devDependencies": {
66
- "@lyrasoft/ts-toolkit": "^0.2.0",
66
+ "@lyrasoft/ts-toolkit": "^0.2.4",
67
67
  "@rubenbimmel/alpine-class-component": "^0.0.4",
68
68
  "@types/alpinejs": "^3",
69
69
  "@types/bootstrap": "^5",
@@ -88,6 +88,10 @@ export class KeepTab {
88
88
  const tabTrigger = this.findTabButtonByHref(href);
89
89
 
90
90
  if (tabTrigger) {
91
+ if (tabTrigger?.getAttribute('disabled') != null || tabTrigger.classList.contains('disabled')) {
92
+ return;
93
+ }
94
+
91
95
  Tab.getOrCreateInstance(tabTrigger).show();
92
96
  }
93
97
  }
@@ -1,11 +1,14 @@
1
1
  import type { UnicornFormElement } from '../module/form';
2
- import { selectOne, module } from '../service';
2
+ import { module, selectOne } from '../service';
3
+ import { Nullable } from '../types';
3
4
 
4
5
  let formElement: typeof UnicornFormElement;
5
6
 
6
7
  export async function useFormAsync(): Promise<UnicornFormElement>;
7
- export async function useFormAsync(ele?: string | Element, options?: Record<string, any>): Promise<UnicornFormElement | null>;
8
- export async function useFormAsync(ele?: string | Element, options: Record<string, any> = {}): Promise<UnicornFormElement | null> {
8
+ export async function useFormAsync(ele?: string | Element,
9
+ options?: Record<string, any>): Promise<UnicornFormElement | null>;
10
+ export async function useFormAsync(ele?: string | Element,
11
+ options: Record<string, any> = {}): Promise<UnicornFormElement | null> {
9
12
  const { UnicornFormElement } = await import('../module/form');
10
13
 
11
14
  formElement ??= UnicornFormElement;
@@ -16,15 +19,26 @@ export async function useFormAsync(ele?: string | Element, options: Record<strin
16
19
  export function useForm(): UnicornFormElement;
17
20
  export function useForm(ele?: string | Element, options?: Record<string, any>): UnicornFormElement | null;
18
21
  export function useForm(ele?: string | Element, options: Record<string, any> = {}): UnicornFormElement | null {
22
+ if (!formElement) {
23
+ throw new Error('Form module is not loaded. Please use useFormAsync() to load the module before using useForm().');
24
+ }
25
+
19
26
  if (ele == null) {
20
27
  return new formElement(undefined, undefined, options);
21
28
  }
22
29
 
23
- const selector = typeof ele === 'string' ? ele : undefined;
24
- const el = selectOne<HTMLFormElement>(ele as string);
30
+ let selector: string | undefined = undefined;
31
+ let el: HTMLFormElement | undefined = undefined;
32
+
33
+ if (typeof ele === 'string') {
34
+ selector = ele;
35
+ el = selectOne<HTMLFormElement>(ele) ?? undefined;
36
+ } else {
37
+ el = ele as HTMLFormElement;
38
+ }
25
39
 
26
40
  if (!el) {
27
- throw new Error(`Form element of: ${selector} not found.`);
41
+ return new formElement(selector, el, options);
28
42
  }
29
43
 
30
44
  return module(
@@ -41,3 +55,19 @@ export async function useFormComponent(ele?: string | Element, options: Record<s
41
55
 
42
56
  return form;
43
57
  }
58
+
59
+ export interface FormSubmitOptions {
60
+ form?: string | Element;
61
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
62
+ url?: string;
63
+ data?: Record<string, any>;
64
+ }
65
+
66
+ export async function useFormSubmit(options: FormSubmitOptions = {}) {
67
+ const form = (await useFormAsync(options.form)) as UnicornFormElement;
68
+
69
+ // fun type should be method of form
70
+ const func = (options.method?.toLowerCase() || 'post') as 'get' | 'post' | 'put' | 'delete' | 'patch';
71
+
72
+ return form[func](options.url, options.data);
73
+ }
@@ -1,26 +1,50 @@
1
- import type { IFrameModalElement } from './iframe-modal';
2
- import { data } from '../data';
3
- import { __, highlight, html, selectOne, slideUp } from '../service';
4
1
  import { template } from 'lodash-es';
2
+ import { data } from '../data';
3
+ import { __, highlight, html, selectOne, simpleAlert, slideUp } from '../service';
4
+ import type { IFrameModalElement } from './iframe-modal';
5
5
 
6
- export type ModalSelectCallback = (item: any) => void;
6
+ export type ModalSelectCallback = (item: any, ...args: any[]) => void;
7
7
 
8
- export function createCallback(type: 'list' | 'single', selector: string, modalSelector: string): ModalSelectCallback {
8
+ export function createCallback(
9
+ type: 'list' | 'single',
10
+ selector: string,
11
+ modalSelector: string
12
+ ): ModalSelectCallback {
9
13
  switch (type) {
10
- // case 'tag':
11
- // return () => {
12
- //
13
- // };
14
14
  case 'list':
15
15
  return (item: any) => {
16
16
  const modalList = document.querySelector(selector) as any as ModalListSelectElement;
17
-
18
- if (!modalList.querySelector(`[data-value="${item.value}"]`)) {
19
- modalList.appendItem(item, true);
20
-
21
- selectOne<IFrameModalElement>(modalSelector)?.close();
22
- } else {
23
- alert(__('unicorn.field.modal.already.selected'));
17
+ const checked = item.checked;
18
+
19
+ if (checked === undefined) {
20
+ // Single selection mode
21
+ if (!modalList.querySelector(`[data-value="${item.value}"]`)) {
22
+ modalList.appendItem(item, true);
23
+
24
+ selectOne<IFrameModalElement>(modalSelector)?.close();
25
+ } else {
26
+ simpleAlert(__('unicorn.field.modal.already.selected'));
27
+ }
28
+ } else if (checked) {
29
+ // Multiple selection mode - add item
30
+ try {
31
+ modalList.appendIfNotExists(item, true);
32
+ } catch (e) {
33
+ window.postMessage({
34
+ task: 'remove-row',
35
+ value: item,
36
+ id: item.instanceId
37
+ });
38
+ simpleAlert((e as Error).message);
39
+ } finally {
40
+ modalList.updateSelected();
41
+ }
42
+ } else if (!checked) {
43
+ // Multiple selection mode - remove item
44
+ modalList.removeItem(item).then(() => {
45
+ console.log(modalList.items);
46
+ modalList.updateSelected();
47
+ });
24
48
  }
25
49
  };
26
50
 
@@ -55,12 +79,14 @@ interface ModalListOptions {
55
79
  sortable: boolean;
56
80
  dataKey: string;
57
81
  max: number;
82
+ multiCheck?: boolean;
58
83
  }
59
84
 
60
85
  export interface ReceivedItem {
61
86
  value: string | number;
62
87
  title?: string;
63
88
  image?: string;
89
+
64
90
  [key: string]: any;
65
91
  }
66
92
 
@@ -69,11 +95,16 @@ class ModalListSelectElement extends HTMLElement {
69
95
 
70
96
  itemTemplate!: ReturnType<typeof template>;
71
97
  options!: ModalListOptions;
98
+ isMultiCheck = false;
72
99
 
73
100
  get listContainer() {
74
101
  return this.querySelector<HTMLDivElement>('[data-role=list-container]')!;
75
102
  }
76
103
 
104
+ get selectButton() {
105
+ return this.querySelector<HTMLAnchorElement>('[data-role=select]')!;
106
+ }
107
+
77
108
  get modal() {
78
109
  return document.querySelector<IFrameModalElement>(this.options.modalSelector);
79
110
  }
@@ -82,6 +113,10 @@ class ModalListSelectElement extends HTMLElement {
82
113
  return Array.from(this.listContainer.querySelectorAll<HTMLElement>('[data-value]'));
83
114
  }
84
115
 
116
+ get count(): number {
117
+ return this.items.length;
118
+ }
119
+
85
120
  connectedCallback() {
86
121
  this.options = JSON.parse(this.getAttribute('options') || '{}');
87
122
  this.itemTemplate = template(document.querySelector(this.options.itemTemplate)!.innerHTML);
@@ -98,18 +133,23 @@ class ModalListSelectElement extends HTMLElement {
98
133
  });
99
134
  }
100
135
 
101
- const selectButton = this.querySelector<HTMLButtonElement>('[data-role=select]')!;
102
- selectButton.addEventListener('click', (e) => {
103
- this.open(e);
136
+ this.selectButton.addEventListener('click', (e) => {
137
+ try {
138
+ this.open(e);
139
+ } catch (e) {
140
+ simpleAlert((e as Error).message);
141
+ }
104
142
  });
105
143
 
106
144
  this.querySelector('[data-role=clear]')?.addEventListener('click', () => {
107
145
  this.removeAll();
108
146
  });
109
147
 
110
- selectButton.style.pointerEvents = '';
148
+ this.selectButton.style.pointerEvents = '';
111
149
 
112
150
  this.render();
151
+
152
+ this.enableMultiCheck(this.options.multiCheck || false);
113
153
  }
114
154
 
115
155
  render() {
@@ -121,6 +161,12 @@ class ModalListSelectElement extends HTMLElement {
121
161
  }
122
162
 
123
163
  appendItem(item: ReceivedItem, highlights = false) {
164
+ const max = this.options.max;
165
+
166
+ if (max && this.count >= max) {
167
+ throw new Error(__('unicorn.field.modal.max.selected', max));
168
+ }
169
+
124
170
  const itemHtml = html(this.itemTemplate({ item }));
125
171
 
126
172
  itemHtml.dataset.value = String(item.value);
@@ -134,6 +180,10 @@ class ModalListSelectElement extends HTMLElement {
134
180
  if (highlights) {
135
181
  highlight(itemHtml);
136
182
  }
183
+
184
+ if (this.isMultiCheck) {
185
+ this.updateSelected();
186
+ }
137
187
  }
138
188
 
139
189
  appendIfNotExists(item: ReceivedItem, highlights = false) {
@@ -162,7 +212,7 @@ class ModalListSelectElement extends HTMLElement {
162
212
  return this.items.map((item) => item.dataset.value);
163
213
  }
164
214
 
165
- removeItem(item: ReceivedItem | string | number) {
215
+ async removeItem(item: ReceivedItem | string | number) {
166
216
  if (typeof item === 'object') {
167
217
  item = item.value;
168
218
  }
@@ -170,9 +220,13 @@ class ModalListSelectElement extends HTMLElement {
170
220
  const element = this.listContainer.querySelector<HTMLElement>(`[data-value="${item}"]`);
171
221
 
172
222
  if (element) {
173
- slideUp(element).then(() => {
223
+ return slideUp(element).then(() => {
174
224
  element.remove();
175
225
  this.toggleRequired();
226
+
227
+ if (this.isMultiCheck) {
228
+ this.updateSelected();
229
+ }
176
230
  });
177
231
  }
178
232
  }
@@ -187,6 +241,10 @@ class ModalListSelectElement extends HTMLElement {
187
241
  await Promise.all(promises);
188
242
 
189
243
  this.toggleRequired();
244
+
245
+ if (this.isMultiCheck) {
246
+ this.updateSelected();
247
+ }
190
248
  }
191
249
 
192
250
  toggleRequired() {
@@ -210,16 +268,34 @@ class ModalListSelectElement extends HTMLElement {
210
268
  return;
211
269
  }
212
270
 
213
- if (this.listContainer.children.length >= max) {
214
- alert(
215
- __('unicorn.field.modal.max.selected', max)
216
- );
217
-
218
- return;
271
+ if (this.count >= max) {
272
+ throw new Error(__('unicorn.field.modal.max.selected', max));
219
273
  }
220
274
 
221
275
  this.modal?.open(target.href, { size: 'modal-xl' });
222
276
  }
277
+
278
+ enableMultiCheck(enable = true) {
279
+ this.isMultiCheck = enable;
280
+
281
+ if (enable) {
282
+ this.updateSelected();
283
+ } else {
284
+ this.clearSelected();
285
+ }
286
+ }
287
+
288
+ updateSelected() {
289
+ const url = new URL(this.selectButton.href);
290
+ url.searchParams.set('selected', this.items.map((i) => i.dataset.value).join(','));
291
+ this.selectButton.href = url.toString();
292
+ }
293
+
294
+ clearSelected() {
295
+ const url = new URL(this.selectButton.href);
296
+ url.searchParams.delete('selected');
297
+ this.selectButton.href = url.toString();
298
+ }
223
299
  }
224
300
 
225
301
  async function init() {
@@ -238,8 +314,23 @@ export function listenMessages(options: ModalListenMessagesOptions) {
238
314
  const callback = createCallback(options.type, options.selector, options.modalSelector);
239
315
 
240
316
  window.addEventListener('message', (e) => {
241
- if (e.origin === options.origin && Array.isArray(e.data) && e.data[0] === options.instanceId) {
242
- callback(e.data[1]);
317
+ if (e.origin === options.origin) {
318
+ if (Array.isArray(e.data) && e.data[0] === options.instanceId) {
319
+ callback(e.data[1]);
320
+ }
321
+
322
+ if (
323
+ typeof e.data === 'object'
324
+ && e.data !== null
325
+ && e.data.id === options.instanceId
326
+ && e.data.task === 'select-row'
327
+ ) {
328
+ const item = e.data.value;
329
+ item.checked = e.data.checked;
330
+ item.instanceId = e.data.id;
331
+
332
+ callback(e.data.value);
333
+ }
243
334
  }
244
335
  });
245
336