@windwalker-io/unicorn-next 0.1.0

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 (175) hide show
  1. package/.editorconfig +18 -0
  2. package/.gulp.json +7 -0
  3. package/bin/release.mjs +47 -0
  4. package/dist/chunks/_arrayPush.js +168 -0
  5. package/dist/chunks/_arrayPush.js.map +1 -0
  6. package/dist/chunks/_baseRest.js +73 -0
  7. package/dist/chunks/_baseRest.js.map +1 -0
  8. package/dist/chunks/_commonjsHelpers.js +7 -0
  9. package/dist/chunks/_commonjsHelpers.js.map +1 -0
  10. package/dist/chunks/_getPrototype.js +130 -0
  11. package/dist/chunks/_getPrototype.js.map +1 -0
  12. package/dist/chunks/button-radio.js +147 -0
  13. package/dist/chunks/button-radio.js.map +1 -0
  14. package/dist/chunks/checkboxes-multi-select.js +44 -0
  15. package/dist/chunks/checkboxes-multi-select.js.map +1 -0
  16. package/dist/chunks/cloneDeep.js +287 -0
  17. package/dist/chunks/cloneDeep.js.map +1 -0
  18. package/dist/chunks/cropper.min.js +5 -0
  19. package/dist/chunks/cropper.min.js.map +1 -0
  20. package/dist/chunks/field-cascade-select.js +256 -0
  21. package/dist/chunks/field-cascade-select.js.map +1 -0
  22. package/dist/chunks/field-file-drag.js +218 -0
  23. package/dist/chunks/field-file-drag.js.map +1 -0
  24. package/dist/chunks/field-flatpickr.js +893 -0
  25. package/dist/chunks/field-flatpickr.js.map +1 -0
  26. package/dist/chunks/field-modal-select.js +403 -0
  27. package/dist/chunks/field-modal-select.js.map +1 -0
  28. package/dist/chunks/field-modal-tree.js +790 -0
  29. package/dist/chunks/field-modal-tree.js.map +1 -0
  30. package/dist/chunks/field-multi-uploader.js +256 -0
  31. package/dist/chunks/field-multi-uploader.js.map +1 -0
  32. package/dist/chunks/field-repeatable.js +132 -0
  33. package/dist/chunks/field-repeatable.js.map +1 -0
  34. package/dist/chunks/field-single-image-drag.js +338 -0
  35. package/dist/chunks/field-single-image-drag.js.map +1 -0
  36. package/dist/chunks/form.js +154 -0
  37. package/dist/chunks/form.js.map +1 -0
  38. package/dist/chunks/grid.js +345 -0
  39. package/dist/chunks/grid.js.map +1 -0
  40. package/dist/chunks/http-client.js +229 -0
  41. package/dist/chunks/http-client.js.map +1 -0
  42. package/dist/chunks/iframe-modal.js +124 -0
  43. package/dist/chunks/iframe-modal.js.map +1 -0
  44. package/dist/chunks/index.js +309 -0
  45. package/dist/chunks/index.js.map +1 -0
  46. package/dist/chunks/isArguments.js +146 -0
  47. package/dist/chunks/isArguments.js.map +1 -0
  48. package/dist/chunks/keep-tab.js +101 -0
  49. package/dist/chunks/keep-tab.js.map +1 -0
  50. package/dist/chunks/legacy.js +210 -0
  51. package/dist/chunks/legacy.js.map +1 -0
  52. package/dist/chunks/list-dependent.js +231 -0
  53. package/dist/chunks/list-dependent.js.map +1 -0
  54. package/dist/chunks/s3-multipart-uploader.js +172 -0
  55. package/dist/chunks/s3-multipart-uploader.js.map +1 -0
  56. package/dist/chunks/s3-uploader.js +136 -0
  57. package/dist/chunks/s3-uploader.js.map +1 -0
  58. package/dist/chunks/show-on.js +237 -0
  59. package/dist/chunks/show-on.js.map +1 -0
  60. package/dist/chunks/tinymce.js +196 -0
  61. package/dist/chunks/tinymce.js.map +1 -0
  62. package/dist/chunks/ui-bootstrap5.js +71 -0
  63. package/dist/chunks/ui-bootstrap5.js.map +1 -0
  64. package/dist/chunks/unicorn.js +2202 -0
  65. package/dist/chunks/unicorn.js.map +1 -0
  66. package/dist/chunks/validation.js +854 -0
  67. package/dist/chunks/validation.js.map +1 -0
  68. package/dist/editor.css +1 -0
  69. package/dist/index.d.ts +1427 -0
  70. package/dist/multi-level-menu.css +1 -0
  71. package/dist/switcher.css +1 -0
  72. package/dist/unicorn-next.css +12 -0
  73. package/dist/unicorn.js +125 -0
  74. package/dist/unicorn.js.map +1 -0
  75. package/fusionfile.mjs +155 -0
  76. package/images/ajax-loader.gif +0 -0
  77. package/images/placeholder/avatar.png +0 -0
  78. package/images/placeholder/image-16x10.png +0 -0
  79. package/images/placeholder/image-16x9.png +0 -0
  80. package/images/placeholder/image-1x1.png +0 -0
  81. package/images/placeholder/image-4x3.png +0 -0
  82. package/package.json +102 -0
  83. package/scss/bootstrap/multi-level-menu.scss +121 -0
  84. package/scss/editor.scss +116 -0
  85. package/scss/field/file-drag.scss +102 -0
  86. package/scss/field/single-image-drag.scss +88 -0
  87. package/scss/field/vue-drag-uploader.scss +160 -0
  88. package/scss/switcher.scss +156 -0
  89. package/src/app.ts +128 -0
  90. package/src/bootstrap/button-radio.ts +208 -0
  91. package/src/bootstrap/keep-tab.ts +155 -0
  92. package/src/composable/index.ts +21 -0
  93. package/src/composable/useCheckboxesMultiSelect.ts +22 -0
  94. package/src/composable/useFieldCascadeSelect.ts +9 -0
  95. package/src/composable/useFieldFileDrag.ts +9 -0
  96. package/src/composable/useFieldFlatpickr.ts +3 -0
  97. package/src/composable/useFieldModalSelect.ts +6 -0
  98. package/src/composable/useFieldModalTree.ts +3 -0
  99. package/src/composable/useFieldMultiUploader.ts +3 -0
  100. package/src/composable/useFieldRepeatable.ts +9 -0
  101. package/src/composable/useFieldSingleImageDrag.ts +5 -0
  102. package/src/composable/useForm.ts +43 -0
  103. package/src/composable/useGrid.ts +57 -0
  104. package/src/composable/useHttp.ts +8 -0
  105. package/src/composable/useIframeModal.ts +9 -0
  106. package/src/composable/useListDependent.ts +26 -0
  107. package/src/composable/useQueue.ts +13 -0
  108. package/src/composable/useS3Uploader.ts +32 -0
  109. package/src/composable/useShowOn.ts +9 -0
  110. package/src/composable/useStack.ts +13 -0
  111. package/src/composable/useTinymce.ts +29 -0
  112. package/src/composable/useTomSelect.ts +72 -0
  113. package/src/composable/useUIBootstrap5.ts +48 -0
  114. package/src/composable/useUniDirective.ts +32 -0
  115. package/src/composable/useValidation.ts +39 -0
  116. package/src/data.ts +36 -0
  117. package/src/events.ts +73 -0
  118. package/src/legacy/legacy.ts +186 -0
  119. package/src/legacy/loader.ts +125 -0
  120. package/src/module/checkboxes-multi-select.ts +54 -0
  121. package/src/module/field-cascade-select.ts +292 -0
  122. package/src/module/field-file-drag.ts +292 -0
  123. package/src/module/field-flatpickr.ts +127 -0
  124. package/src/module/field-modal-select.ts +174 -0
  125. package/src/module/field-modal-tree.ts +27 -0
  126. package/src/module/field-multi-uploader.ts +361 -0
  127. package/src/module/field-repeatable.ts +202 -0
  128. package/src/module/field-single-image-drag.ts +468 -0
  129. package/src/module/form.ts +223 -0
  130. package/src/module/grid.ts +465 -0
  131. package/src/module/http-client.ts +243 -0
  132. package/src/module/iframe-modal.ts +167 -0
  133. package/src/module/list-dependent.ts +321 -0
  134. package/src/module/s3-multipart-uploader.ts +300 -0
  135. package/src/module/s3-uploader.ts +234 -0
  136. package/src/module/show-on.ts +173 -0
  137. package/src/module/tinymce.ts +263 -0
  138. package/src/module/ui-bootstrap5.ts +107 -0
  139. package/src/module/validation.ts +1019 -0
  140. package/src/plugin/index.ts +1 -0
  141. package/src/plugin/php-adapter.ts +65 -0
  142. package/src/polyfill/form-request-submit.ts +31 -0
  143. package/src/polyfill/index.ts +9 -0
  144. package/src/service/animate.ts +58 -0
  145. package/src/service/crypto.ts +27 -0
  146. package/src/service/dom-watcher.ts +62 -0
  147. package/src/service/dom.ts +265 -0
  148. package/src/service/helper.ts +48 -0
  149. package/src/service/index.ts +10 -0
  150. package/src/service/lang.ts +122 -0
  151. package/src/service/loader.ts +152 -0
  152. package/src/service/router.ts +118 -0
  153. package/src/service/ui.ts +497 -0
  154. package/src/service/uri.ts +106 -0
  155. package/src/types/base.ts +9 -0
  156. package/src/types/index.ts +4 -0
  157. package/src/types/modal-tree.ts +12 -0
  158. package/src/types/plugin.ts +6 -0
  159. package/src/types/shims.d.ts +18 -0
  160. package/src/types/ui.ts +6 -0
  161. package/src/unicorn.ts +63 -0
  162. package/src/utilities/arr.ts +25 -0
  163. package/src/utilities/base.ts +9 -0
  164. package/src/utilities/data.ts +48 -0
  165. package/src/utilities/index.ts +5 -0
  166. package/src/utilities/tree.ts +20 -0
  167. package/src/vue/components/ModalTree/ModalTreeApp.vue +175 -0
  168. package/src/vue/components/ModalTree/TreeItem.vue +262 -0
  169. package/src/vue/components/ModalTree/TreeModal.vue +225 -0
  170. package/tests/test.js +4 -0
  171. package/tsconfig.js.json +25 -0
  172. package/tsconfig.json +17 -0
  173. package/vite.assets.config.ts +61 -0
  174. package/vite.config.test.ts +36 -0
  175. package/vite.config.ts +112 -0
@@ -0,0 +1,223 @@
1
+ import { data } from '../data';
2
+ import { loadAlpine, useSystemUri } from '../service';
3
+ import type { Nullable } from '../types';
4
+
5
+ export class UnicornFormElement {
6
+ element: HTMLFormElement | undefined;
7
+ options: Record<string, any>;
8
+
9
+ constructor(
10
+ selector?: string | Element,
11
+ element?: HTMLFormElement,
12
+ options: Record<string, any> = {},
13
+ ) {
14
+ // If form not found, create one
15
+ if (!element) {
16
+ element = document.createElement('form');
17
+
18
+ if (typeof selector === 'string' && selector.startsWith('#')) {
19
+ element.setAttribute('id', selector.substring(1));
20
+ element.setAttribute('name', selector.substring(1));
21
+ }
22
+
23
+ element.setAttribute('method', 'post');
24
+ element.setAttribute('enctype', 'multipart/form-data');
25
+ element.setAttribute('novalidate', 'true');
26
+ element.setAttribute('action', useSystemUri('full'));
27
+ element.setAttribute('style', 'display: none;');
28
+
29
+ const csrf = document.createElement('input');
30
+ csrf.setAttribute('type', 'hidden');
31
+ csrf.setAttribute('name', data('csrf-token'));
32
+ csrf.setAttribute('value', '1');
33
+
34
+ element.appendChild(csrf);
35
+ document.body.appendChild(element);
36
+ }
37
+
38
+ this.element = element;
39
+ this.options = { ...options };
40
+ }
41
+
42
+ initComponent(store = 'form', custom = {}) {
43
+ return loadAlpine((Alpine) => {
44
+ Alpine.store(store, this.useState(custom));
45
+ });
46
+ }
47
+
48
+ useState(custom = {}) {
49
+ const state: Record<string, any> = {};
50
+ Object.getOwnPropertyNames(Object.getPrototypeOf(this))
51
+ .map(item => {
52
+ // @ts-ignore
53
+ return state[item] = this[item].bind(this);
54
+ });
55
+
56
+ return Object.assign(
57
+ state,
58
+ custom
59
+ );
60
+ }
61
+
62
+ getElement() {
63
+ return this.element;
64
+ }
65
+
66
+ submit(
67
+ url?: Nullable<string>,
68
+ data?: Nullable<Record<string, any>>,
69
+ method?: Nullable<string>,
70
+ customMethod?: Nullable<string>,
71
+ ): boolean {
72
+ const form = this.element!;
73
+
74
+ if (customMethod) {
75
+ let methodInput = form.querySelector<HTMLInputElement>('input[name="_method"]');
76
+
77
+ if (!methodInput) {
78
+ methodInput = document.createElement('input');
79
+ methodInput.setAttribute('name', '_method');
80
+ methodInput.setAttribute('type', 'hidden');
81
+ methodInput.value = customMethod;
82
+
83
+ form.appendChild(methodInput);
84
+ } else {
85
+ methodInput.value = customMethod;
86
+ }
87
+ }
88
+
89
+ // Set data into form.
90
+ if (data) {
91
+ const flatted = UnicornFormElement.flattenObject(data);
92
+
93
+ for (const key in flatted) {
94
+ const value = flatted[key];
95
+
96
+ const fieldName = UnicornFormElement.buildFieldName(key);
97
+ this.injectInput(fieldName, value);
98
+ }
99
+ }
100
+
101
+ if (url) {
102
+ form.setAttribute('action', url);
103
+ }
104
+
105
+ if (method) {
106
+ form.setAttribute('method', method);
107
+ }
108
+
109
+ // Use requestSubmit() to fire submit event.
110
+ form.requestSubmit();
111
+
112
+ return true;
113
+ }
114
+
115
+ injectInput(name: string, value: any) {
116
+ let input = this.element!.querySelector<HTMLInputElement>(`input[name="${name}"]`);
117
+
118
+ if (!input) {
119
+ input = document.createElement('input');
120
+ input.setAttribute('name', name);
121
+ input.setAttribute('type', 'hidden');
122
+ input.setAttribute('data-role', 'temp-input');
123
+
124
+ this.element!.appendChild(input);
125
+ }
126
+
127
+ input.value = value;
128
+ return input;
129
+ }
130
+
131
+ /**
132
+ * Make a GET request.
133
+ */
134
+ get(
135
+ url?: Nullable<string>,
136
+ data?: Nullable<Record<string, any>>,
137
+ customMethod?: Nullable<string>,
138
+ ): boolean {
139
+ return this.submit(url, data, 'GET', customMethod);
140
+ }
141
+
142
+ /**
143
+ * Post form.
144
+ */
145
+ post(
146
+ url?: Nullable<string>,
147
+ data?: Nullable<Record<string, any>>,
148
+ customMethod?: Nullable<string>,
149
+ ) {
150
+ customMethod = customMethod || 'POST';
151
+
152
+ return this.submit(url, data, 'POST', customMethod);
153
+ }
154
+
155
+ /**
156
+ * Make a PUT request.
157
+ */
158
+ put(
159
+ url?: Nullable<string>,
160
+ data?: Nullable<Record<string, any>>,
161
+ ) {
162
+ return this.post(url, data, 'PUT');
163
+ }
164
+
165
+ /**
166
+ * Make a PATCH request.
167
+ */
168
+ patch(
169
+ url?: Nullable<string>,
170
+ data?: Nullable<Record<string, any>>,
171
+ ) {
172
+ return this.post(url, data, 'PATCH');
173
+ }
174
+
175
+ /**
176
+ * Make a DELETE request.
177
+ */
178
+ delete(
179
+ url?: Nullable<string>,
180
+ data?: Nullable<Record<string, any>>,
181
+ ) {
182
+ return this.post(url, data, 'DELETE');
183
+ }
184
+
185
+ /**
186
+ * @see https://stackoverflow.com/a/53739792
187
+ *
188
+ * @param {Object} ob
189
+ * @returns {Object}
190
+ */
191
+ static flattenObject(ob: Record<string, any>): Record<string, any> {
192
+ const toReturn: Record<string, any> = {};
193
+
194
+ for (let i in ob) {
195
+ if (!ob.hasOwnProperty(i)) {
196
+ continue;
197
+ }
198
+
199
+ if ((typeof ob[i]) === 'object' && ob[i] != null) {
200
+ const flatObject = this.flattenObject(ob[i]);
201
+
202
+ for (let x in flatObject) {
203
+ if (!flatObject.hasOwnProperty(x)) {
204
+ continue;
205
+ }
206
+
207
+ toReturn[i + '/' + x] = flatObject[x];
208
+ }
209
+ } else {
210
+ toReturn[i] = ob[i];
211
+ }
212
+ }
213
+ return toReturn;
214
+ }
215
+
216
+ static buildFieldName(field: string) {
217
+ const names = field.split('/');
218
+
219
+ const first = names.shift();
220
+
221
+ return first + names.map(name => `[${name}]`).join('');
222
+ }
223
+ }
@@ -0,0 +1,465 @@
1
+ import { __, deleteConfirm, h, loadAlpine, simpleAlert, simpleConfirm, slideDown, slideUp } from '../service';
2
+ import { Nullable } from '../types';
3
+ import type { UnicornFormElement } from './form';
4
+
5
+ export class UnicornGridElement {
6
+ options: Record<string, any>;
7
+ ordering = '';
8
+ state = {};
9
+
10
+ constructor(
11
+ selector: string,
12
+ public element: HTMLElement,
13
+ public form: UnicornFormElement,
14
+ options: Record<string, any> = {}
15
+ ) {
16
+ this.options = { ...options };
17
+
18
+ this.bindEvents();
19
+ }
20
+
21
+ bindEvents() {
22
+ const inputs = this.element.querySelectorAll<HTMLInputElement>('input[data-role=grid-checkbox]');
23
+
24
+ for (const ch of inputs) {
25
+ ch.addEventListener('click', () => {
26
+ ch.dispatchEvent(new CustomEvent('change'));
27
+ });
28
+ ch.addEventListener('change', () => {
29
+ const event = new CustomEvent('unicorn:checked', {
30
+ detail: { grid: this }
31
+ });
32
+
33
+ this.form.element?.dispatchEvent(event);
34
+ });
35
+ }
36
+ }
37
+
38
+ initComponent(store = 'grid', custom: Record<string, string> = {}) {
39
+ this.ordering = this.element?.dataset?.ordering || '';
40
+
41
+ if (this.ordering) {
42
+ if (!this.ordering.toLowerCase().endsWith(' asc')
43
+ && !this.ordering.toLowerCase().endsWith(' desc')) {
44
+ this.ordering += ' ASC';
45
+ }
46
+ }
47
+
48
+ return loadAlpine((Alpine) => {
49
+ Alpine.store(store, this.useState(custom));
50
+ });
51
+ }
52
+
53
+ useState(this: any, custom: Record<string, any> = {}) {
54
+ const state: Partial<Record<string, any>> = {
55
+ form: this.form.useState(custom),
56
+ };
57
+
58
+ Object.getOwnPropertyNames(Object.getPrototypeOf(this))
59
+ .map(item => {
60
+ const prop = this[item];
61
+
62
+ if (typeof prop === 'function') {
63
+ return state[item] = this[item].bind(this);
64
+ }
65
+
66
+ return item;
67
+ });
68
+
69
+ return Object.assign(
70
+ state,
71
+ custom
72
+ );
73
+ }
74
+
75
+ getElement() {
76
+ return this.element;
77
+ }
78
+
79
+ sendFilter($event?: Event, method?: string) {
80
+ if ($event) {
81
+ $event.preventDefault();
82
+ }
83
+
84
+ this.form.submit(null, null, method);
85
+ }
86
+
87
+ clearFilters(element: HTMLElement, method?: Nullable<string>): void {
88
+ element.querySelectorAll('input, textarea, select').forEach((ele) => {
89
+ (ele as HTMLInputElement).value = '';
90
+ });
91
+
92
+ this.form.submit(null, null, method);
93
+ }
94
+
95
+ async toggleFilters(open: boolean, filterForm: HTMLElement) {
96
+ if (open) {
97
+ await slideDown(filterForm);
98
+ } else {
99
+ await slideUp(filterForm);
100
+ }
101
+ }
102
+
103
+ sort($el: HTMLElement): boolean {
104
+ const dir = this.getDirection($el);
105
+
106
+ const field = $el.dataset.field;
107
+ let asc = $el.dataset.asc;
108
+ let desc = $el.dataset.desc;
109
+
110
+ if (field) {
111
+ asc = field + ' ASC';
112
+ desc = field + ' DESC';
113
+ }
114
+
115
+ if (dir === 'ASC') {
116
+ return this.sortBy(desc);
117
+ }
118
+
119
+ return this.sortBy(asc);
120
+ }
121
+
122
+ /**
123
+ * Sort two items.
124
+ */
125
+ sortBy(ordering: Nullable<string>): boolean {
126
+ if (!ordering) {
127
+ return false;
128
+ }
129
+
130
+ let orderingInput = this.element.querySelector<HTMLInputElement>('input[name=list_ordering]');
131
+
132
+ if (!orderingInput) {
133
+ orderingInput = h('input', { name: 'list_ordering', type: 'hidden', value: '' });
134
+
135
+ this.element.appendChild(orderingInput);
136
+ }
137
+
138
+ orderingInput.value = ordering;
139
+
140
+ return this.form.put();
141
+ }
142
+
143
+ isSortActive($el: HTMLElement): boolean {
144
+ return this.getDirection($el) != null;
145
+ }
146
+
147
+ getDirection($el: HTMLElement): "ASC" | "DESC" | null {
148
+ const field = $el.dataset.field;
149
+ let asc = $el.dataset.asc;
150
+ let desc = $el.dataset.desc;
151
+
152
+ if (field) {
153
+ asc = field + ' ASC';
154
+ desc = field + ' DESC';
155
+ }
156
+
157
+ if (this.orderingEquals(asc, this.ordering)) {
158
+ return 'ASC';
159
+ } else if (this.orderingEquals(desc, this.ordering)) {
160
+ return 'DESC';
161
+ }
162
+
163
+ return null;
164
+ }
165
+
166
+ orderingEquals(a: Nullable<string>, b: Nullable<string>): boolean {
167
+ a = a || '';
168
+ b = b || '';
169
+
170
+ a = a.replace(/\s+/g, ' ').trim().toLowerCase();
171
+ b = b.replace(/\s+/g, ' ').trim().toLowerCase();
172
+
173
+ return a === b;
174
+ }
175
+
176
+ /**
177
+ * Check a row's checkbox.
178
+ */
179
+ checkRow(row: number, value = true): void {
180
+ const ch = this.getCheckboxByRow(row);
181
+
182
+ if (!ch) {
183
+ throw new Error('Checkbox of row: ' + row + ' not found.');
184
+ }
185
+
186
+ ch.checked = value;
187
+ ch.dispatchEvent(new Event('input'));
188
+ ch.dispatchEvent(new Event('change'));
189
+ }
190
+
191
+ getCheckboxByRow(row: number): Nullable<HTMLInputElement> {
192
+ return this.form.element?.querySelector<HTMLInputElement>(
193
+ `input[data-role=grid-checkbox][data-row-number="${row}"]`
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Update a row.
199
+ */
200
+ updateRow(row: number, url?: Nullable<string>, data?: Nullable<Record<string, any>>) {
201
+ const ch = this.getCheckboxByRow(row);
202
+
203
+ if (!ch) {
204
+ return false;
205
+ }
206
+
207
+ return this.updateItem(ch.value, url, data);
208
+ }
209
+
210
+ /**
211
+ * Update an item by id.
212
+ */
213
+ updateItem(id: string | number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
214
+ this.toggleAll(false);
215
+
216
+ this.disableAllCheckboxes();
217
+
218
+ this.form.injectInput('id[]', id);
219
+
220
+ return this.form.patch(url, data);
221
+ }
222
+
223
+ /**
224
+ * Update a item with batch task.
225
+ */
226
+ updateItemByTask(
227
+ task: string,
228
+ id: string | number,
229
+ url?: Nullable<string>,
230
+ data?: Nullable<Record<string, any>>
231
+ ): boolean {
232
+ data = data || {};
233
+ data.task = task;
234
+
235
+ return this.updateItem(id, url, data);
236
+ }
237
+
238
+ /**
239
+ * @deprecated Use updateItemByTask() instead.
240
+ */
241
+ doTask(task: string, id: number | string, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
242
+ return this.updateItemByTask(task, id, url, data);
243
+ }
244
+
245
+ /**
246
+ * Update a row with batch task.
247
+ */
248
+ updateRowByTask(task: string, row: number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
249
+ const ch = this.getCheckboxByRow(row);
250
+
251
+ if (!ch) {
252
+ return false;
253
+ }
254
+
255
+ return this.updateItemByTask(task, ch.value, url, data);
256
+ }
257
+
258
+ /**
259
+ * Batch update items.
260
+ */
261
+ updateListByTask(task: string, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
262
+ data = data || {};
263
+ data.task = task;
264
+
265
+ return this.form.patch(url, data);
266
+ }
267
+
268
+ /**
269
+ * Copy a row.
270
+ */
271
+ copyItem(id: string | number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
272
+ this.toggleAll(false);
273
+
274
+ this.disableAllCheckboxes();
275
+
276
+ this.form.injectInput('id[]', id);
277
+
278
+ return this.form.post(url, data);
279
+ }
280
+
281
+ /**
282
+ * Copy a row.
283
+ */
284
+ copyRow(row: number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
285
+ const ch = this.getCheckboxByRow(row);
286
+
287
+ if (!ch) {
288
+ return false;
289
+ }
290
+
291
+ return this.copyItem(ch.value, url, data);
292
+ }
293
+
294
+ /**
295
+ * Delete checked items.
296
+ */
297
+ deleteList(
298
+ message?: Nullable<string> | false,
299
+ url?: Nullable<string>,
300
+ data?: Nullable<Record<string, any>>
301
+ ): boolean {
302
+ if (!this.validateChecked()) {
303
+ return false;
304
+ }
305
+
306
+ message = message == null ? __('unicorn.message.delete.confirm') : message;
307
+
308
+ if (message !== false) {
309
+ simpleConfirm(message).then(isConfirm => {
310
+ if (isConfirm) {
311
+ this.form.delete(url, data);
312
+ }
313
+ });
314
+ } else {
315
+ this.form.delete(url, data);
316
+ }
317
+
318
+ return true;
319
+ }
320
+
321
+ /**
322
+ * Delete an item by row.
323
+ */
324
+ async deleteRow(row: number,
325
+ msg?: Nullable<string>,
326
+ url?: Nullable<string>,
327
+ data?: Nullable<Record<string, any>>): Promise<boolean> {
328
+ const ch = this.getCheckboxByRow(row);
329
+
330
+ if (!ch) {
331
+ return false;
332
+ }
333
+
334
+ return this.deleteItem(ch.value, msg, url, data);
335
+ }
336
+
337
+ /**
338
+ * Delete an item.
339
+ */
340
+ async deleteItem(id: string,
341
+ msg?: Nullable<string>,
342
+ url?: Nullable<string>,
343
+ data?: Nullable<Record<string, any>>): Promise<boolean> {
344
+ msg = msg || __('unicorn.message.delete.confirm');
345
+
346
+ const isConfirm = await deleteConfirm(msg);
347
+
348
+ if (isConfirm) {
349
+ // this.toggleAll(false);
350
+ // this.checkRow(row);
351
+ data = data || {};
352
+
353
+ data.id = id;
354
+
355
+ this.form.delete(url, data);
356
+ }
357
+
358
+ return isConfirm;
359
+ }
360
+
361
+ /**
362
+ * Toggle all checkboxes.
363
+ */
364
+ toggleAll(value: boolean) {
365
+ Array.from(
366
+ this.element.querySelectorAll<HTMLInputElement>('input[data-role=grid-checkbox][type=checkbox]')
367
+ )
368
+ .forEach((input) => {
369
+ input.checked = value;
370
+
371
+ input.dispatchEvent(new CustomEvent('input'));
372
+ input.dispatchEvent(new CustomEvent('change'));
373
+ });
374
+
375
+ return this;
376
+ }
377
+
378
+ disableAllCheckboxes() {
379
+ Array.from(
380
+ this.element.querySelectorAll<HTMLInputElement>('input[data-role=grid-checkbox][type=checkbox]')
381
+ )
382
+ .forEach((input) => {
383
+ input.disabled = true;
384
+ });
385
+ }
386
+
387
+ /**
388
+ * Count checked checkboxes.
389
+ */
390
+ countChecked(): number {
391
+ return this.getChecked().length;
392
+ }
393
+
394
+ /**
395
+ * Get Checked boxes.
396
+ */
397
+ getChecked(): HTMLInputElement[] {
398
+ return Array.from(
399
+ this.element.querySelectorAll<HTMLInputElement>('input[data-role=grid-checkbox][type=checkbox]:checked')
400
+ );
401
+ }
402
+
403
+ getCheckedValues(): string[] {
404
+ return this.getChecked().map(input => input.value);
405
+ }
406
+
407
+ /**
408
+ * Validate there has one or more checked boxes.
409
+ */
410
+ validateChecked(event?: Event, callback?: (grid: UnicornGridElement) => any, msg?: string): this {
411
+ msg = msg || __('unicorn.message.grid.checked');
412
+
413
+ if (!this.hasChecked()) {
414
+ if (msg !== '') {
415
+ simpleAlert(msg);
416
+ }
417
+
418
+ if (event) {
419
+ event.stopPropagation();
420
+ event.preventDefault();
421
+ }
422
+
423
+ return this;
424
+ }
425
+
426
+ if (callback) {
427
+ callback(this);
428
+ }
429
+
430
+ return this;
431
+ }
432
+
433
+ hasChecked(): boolean {
434
+ return this.countChecked() > 0;
435
+ }
436
+
437
+ /**
438
+ * Reorder all.
439
+ */
440
+ reorderAll(url?: Nullable<string>, data?: Nullable<Record<string, any>>) {
441
+ return this.updateListByTask('reorder', url, data);
442
+ }
443
+
444
+ /**
445
+ * Reorder items.
446
+ */
447
+ moveItem(id: number | string, delta: number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
448
+ data = data || {};
449
+ data.delta = delta;
450
+
451
+ return this.updateItemByTask('move', id, url, data);
452
+ }
453
+
454
+ moveUp(id: string | number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
455
+ return this.moveItem(id, -1, url, data);
456
+ }
457
+
458
+ moveDown(id: string | number, url?: Nullable<string>, data?: Nullable<Record<string, any>>): boolean {
459
+ return this.moveItem(id, 1, url, data);
460
+ }
461
+
462
+ getId(suffix = '') {
463
+ return this.form.element?.id + suffix;
464
+ }
465
+ }