@windwalker-io/unicorn-next 0.1.15 → 0.1.17

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.
@@ -1,4 +1,4 @@
1
- import { __, deleteConfirm, h, loadAlpine, simpleAlert, simpleConfirm, slideDown, slideUp } from '../service';
1
+ import { __, delegate, deleteConfirm, h, loadAlpine, simpleAlert, simpleConfirm, slideDown, slideUp } from '../service';
2
2
  import { Nullable } from '../types';
3
3
  import type { UnicornFormElement } from './form';
4
4
 
@@ -22,9 +22,11 @@ export class UnicornGridElement {
22
22
  const inputs = this.element.querySelectorAll<HTMLInputElement>('input[data-role=grid-checkbox]');
23
23
 
24
24
  for (const ch of inputs) {
25
- ch.addEventListener('click', () => {
26
- ch.dispatchEvent(new CustomEvent('change'));
27
- });
25
+ // No-longer need this since browsers will trigger change event when checkbox is checked/unchecked
26
+ // ch.addEventListener('click', () => {
27
+ // ch.dispatchEvent(new CustomEvent('change'));
28
+ // });
29
+
28
30
  ch.addEventListener('change', () => {
29
31
  const event = new CustomEvent('unicorn:checked', {
30
32
  detail: { grid: this }
@@ -33,6 +35,66 @@ export class UnicornGridElement {
33
35
  this.form.element?.dispatchEvent(event);
34
36
  });
35
37
  }
38
+
39
+ if (this.form.element) {
40
+ this.bindMustCheckedEvent(this.form.element);
41
+ }
42
+ }
43
+
44
+ private bindMustCheckedEvent(form: HTMLFormElement) {
45
+ delegate(document, '[data-must-checked]', 'mousedown', (e) => {
46
+ const target = e.currentTarget as HTMLElement;
47
+
48
+ const selector = target.dataset.mustChecked!;
49
+
50
+ if (!selector || !form.matches(selector)) {
51
+ return;
52
+ }
53
+
54
+ // Prevent modal
55
+ const toggle = target.dataset.bsToggle;
56
+
57
+ if (toggle === 'modal') {
58
+ const modalTarget = target.dataset.bsTarget;
59
+
60
+ if (modalTarget) {
61
+ this.preventBSModal(modalTarget, this.getMustCheckedMessage());
62
+ }
63
+ }
64
+ });
65
+ }
66
+
67
+ preventBSModal(selector?: string | HTMLElement, msg?: string | boolean) {
68
+ let modalElement: HTMLElement | null = null;
69
+
70
+ if (typeof selector === 'string') {
71
+ modalElement = document.querySelector(selector);
72
+ } else if (selector instanceof HTMLElement) {
73
+ const modalTarget = selector.dataset.bsTarget;
74
+
75
+ if (modalTarget) {
76
+ modalElement = document.querySelector(modalTarget);
77
+ } else {
78
+ modalElement = selector;
79
+ }
80
+ }
81
+
82
+ if (modalElement) {
83
+ modalElement?.addEventListener('show.bs.modal', (e) => {
84
+ if (!this.hasChecked()) {
85
+ e.preventDefault();
86
+ e.stopPropagation();
87
+
88
+ if (msg) {
89
+ if (msg === true) {
90
+ msg = this.getMustCheckedMessage();
91
+ }
92
+
93
+ simpleAlert(msg);
94
+ }
95
+ }
96
+ }, { once: true });
97
+ }
36
98
  }
37
99
 
38
100
  initComponent(store = 'grid', custom: Record<string, string> = {}) {
@@ -335,10 +397,12 @@ export class UnicornGridElement {
335
397
  /**
336
398
  * Delete an item by row.
337
399
  */
338
- async deleteRow(row: number,
339
- msg?: Nullable<string>,
340
- url?: Nullable<string>,
341
- data?: Nullable<Record<string, any>>): Promise<boolean> {
400
+ async deleteRow(
401
+ row: number,
402
+ msg?: Nullable<string>,
403
+ url?: Nullable<string>,
404
+ data?: Nullable<Record<string, any>>
405
+ ): Promise<boolean> {
342
406
  const ch = this.getCheckboxByRow(row);
343
407
 
344
408
  if (!ch) {
@@ -421,17 +485,33 @@ export class UnicornGridElement {
421
485
  /**
422
486
  * Validate there has one or more checked boxes.
423
487
  */
424
- validateChecked(event?: Event, callback?: (grid: UnicornGridElement) => any, msg?: string): this {
425
- msg = msg || __('unicorn.message.grid.checked');
426
-
488
+ validateChecked(
489
+ event?: Event,
490
+ callback?: (grid: UnicornGridElement) => any,
491
+ errorMsg: string | boolean | null | ((grid: UnicornGridElement) => any) = true
492
+ ): this {
427
493
  if (!this.hasChecked()) {
428
- if (msg !== '') {
429
- simpleAlert(msg);
494
+ if (errorMsg === true) {
495
+ errorMsg = this.getMustCheckedMessage();
496
+ }
497
+
498
+ if (typeof errorMsg === 'string' && errorMsg !== '') {
499
+ simpleAlert(errorMsg);
500
+ } else if (typeof errorMsg === 'function') {
501
+ errorMsg(this);
430
502
  }
431
503
 
432
504
  if (event) {
433
505
  event.stopPropagation();
434
506
  event.preventDefault();
507
+
508
+ const target = event.currentTarget as HTMLElement;
509
+
510
+ if (target.dataset.bsToggle === 'modal') {
511
+ if (target.dataset.bsTarget) {
512
+ this.preventBSModal(target.dataset.bsTarget);
513
+ }
514
+ }
435
515
  }
436
516
 
437
517
  return this;
@@ -444,6 +524,18 @@ export class UnicornGridElement {
444
524
  return this;
445
525
  }
446
526
 
527
+ async validateCheckedPromise() {
528
+ if (!this.hasChecked()) {
529
+ throw new Error('No items checked.');
530
+ }
531
+
532
+ return this;
533
+ }
534
+
535
+ getMustCheckedMessage(): string {
536
+ return __('unicorn.message.grid.checked');
537
+ }
538
+
447
539
  hasChecked(): boolean {
448
540
  return this.countChecked() > 0;
449
541
  }
@@ -60,7 +60,7 @@ export class ShowOn {
60
60
  if (matched) {
61
61
  setTimeout(() => {
62
62
  fadeIn(this.el, duration, this.initialDisplay);
63
- }, duration + 30);
63
+ }, duration);
64
64
  } else {
65
65
  if (this.input) {
66
66
  this.defaultReadonly ??= this.input.hasAttribute('readonly');
@@ -96,7 +96,7 @@ export class UnicornFormValidation {
96
96
  options = {};
97
97
  }
98
98
 
99
- return this.options = mergeDeep({}, defaultOptions, this.options, options);
99
+ return this.options = mergeDeep({}, defaultOptions, this.options || {}, options);
100
100
  }
101
101
 
102
102
  get scrollEnabled() {
@@ -350,7 +350,7 @@ export class UnicornFormValidation {
350
350
 
351
351
  export class UnicornFieldValidation {
352
352
  $input: InputElements | undefined;
353
- options: Partial<FieldValidationOptions>;
353
+ options: Partial<FieldValidationOptions> = {};
354
354
 
355
355
  static is = 'uni-field-validate';
356
356
 
@@ -504,6 +504,11 @@ export class UnicornFieldValidation {
504
504
  return true;
505
505
  }
506
506
 
507
+ if (this.hasChildDirectives()) {
508
+ // If has child field validation directives, let them handle the validation.
509
+ return true;
510
+ }
511
+
507
512
  this.$input.setCustomValidity('');
508
513
  let valid = this.$input.checkValidity();
509
514
 
@@ -519,7 +524,7 @@ export class UnicornFieldValidation {
519
524
  return valid;
520
525
  }
521
526
 
522
- runCustomValidity() {
527
+ protected runCustomValidity() {
523
528
  if (!this.$input) {
524
529
  return true;
525
530
  }
@@ -572,6 +577,15 @@ export class UnicornFieldValidation {
572
577
  return true;
573
578
  }
574
579
 
580
+ if (this.$input.hasAttribute('[data-novalidate]')) {
581
+ return true;
582
+ }
583
+
584
+ if (this.hasChildDirectives()) {
585
+ // If has child field validation directives, let them handle the validation.
586
+ return true;
587
+ }
588
+
575
589
  this.$input.setCustomValidity('');
576
590
  let valid = this.$input.checkValidity();
577
591
 
@@ -584,7 +598,7 @@ export class UnicornFieldValidation {
584
598
  return valid;
585
599
  }
586
600
 
587
- async runCustomValidityAsync(): Promise<boolean> {
601
+ protected async runCustomValidityAsync(): Promise<boolean> {
588
602
  if (!this.$input) {
589
603
  return true;
590
604
  }
@@ -631,13 +645,13 @@ export class UnicornFieldValidation {
631
645
  return true;
632
646
  }
633
647
 
634
- checkCustomDataAttributeValidity(): boolean {
648
+ protected checkCustomDataAttributeValidity(): boolean {
635
649
  const error = this.$input?.dataset.validationFail;
636
650
 
637
651
  return this.handleCustomResult(error);
638
652
  }
639
653
 
640
- checkInputOptionsValidity(): boolean {
654
+ protected checkInputOptionsValidity(): boolean {
641
655
  if (!this.$input) {
642
656
  return true;
643
657
  }
@@ -828,6 +842,11 @@ export class UnicornFieldValidation {
828
842
  }
829
843
 
830
844
  showInvalidResponse() {
845
+ if (this.hasChildDirectives()) {
846
+ // If has child field validation directives, let them handle the error message display.
847
+ return;
848
+ }
849
+
831
850
  /** @type ValidityState */
832
851
  const state = this.$input?.validity;
833
852
  let message: string = this.$input?.validationMessage || '';
@@ -949,6 +968,10 @@ export class UnicornFieldValidation {
949
968
 
950
969
  return label;
951
970
  }
971
+
972
+ protected hasChildDirectives() {
973
+ return this.el.querySelector('[uni-field-validate]') != null;
974
+ }
952
975
  }
953
976
 
954
977
  function camelTo(str: string, sep: string) {
@@ -191,7 +191,7 @@ export function html<T extends Element = HTMLElement>(html: string): T {
191
191
  * @see https://gist.github.com/iagobruno/4db2ed62dc40fa841bb9a5c7de92f5f8
192
192
  */
193
193
  export function delegate(
194
- wrapper: Element | string,
194
+ wrapper: Element | Document | string,
195
195
  selector: string,
196
196
  eventName: string,
197
197
  callback: (e: Event) => void
package/src/service/ui.ts CHANGED
@@ -433,6 +433,7 @@ export function useDisableOnSubmit(
433
433
 
434
434
  const event = options.event || 'submit';
435
435
  const spinnerClass = options.spinnerClass || 'spinner-border spinner-border-sm';
436
+ const loadingClass = options.loadingCass || 'is-loading';
436
437
 
437
438
  selectAll<HTMLElement>(buttonSelector, (button) => {
438
439
  button.addEventListener('click', (e) => {
@@ -458,6 +459,7 @@ export function useDisableOnSubmit(
458
459
 
459
460
  if (button.dataset.clicked) {
460
461
  let icon = button.querySelector(iconSelector);
462
+ button.classList.add(loadingClass);
461
463
 
462
464
  if (icon) {
463
465
  const i = html('<i></i>');