@vobs/ui 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.
- package/LICENSE +21 -0
- package/dist/app-shell-dom.d.ts +24 -0
- package/dist/app-shell-dom.js +573 -0
- package/dist/app-shell.d.ts +199 -0
- package/dist/app-shell.js +370 -0
- package/dist/base.css +52 -0
- package/dist/calendar-picker.d.ts +45 -0
- package/dist/calendar-picker.js +217 -0
- package/dist/calendar.d.ts +95 -0
- package/dist/calendar.js +194 -0
- package/dist/complex-inputs.d.ts +300 -0
- package/dist/complex-inputs.js +862 -0
- package/dist/components.css +4568 -0
- package/dist/data-display.d.ts +1063 -0
- package/dist/data-display.js +2298 -0
- package/dist/feedback.d.ts +88 -0
- package/dist/feedback.js +281 -0
- package/dist/forms.d.ts +378 -0
- package/dist/forms.js +1015 -0
- package/dist/icons-config.d.ts +25 -0
- package/dist/icons-config.js +14 -0
- package/dist/icons.d.ts +41 -0
- package/dist/icons.js +185 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +32 -0
- package/dist/locale/en-US.d.ts +31 -0
- package/dist/locale/en-US.js +20 -0
- package/dist/locale/zh-CN.d.ts +31 -0
- package/dist/locale/zh-CN.js +20 -0
- package/dist/navigation.d.ts +609 -0
- package/dist/navigation.js +1707 -0
- package/dist/overlay.d.ts +304 -0
- package/dist/overlay.js +969 -0
- package/dist/primitives.d.ts +268 -0
- package/dist/primitives.js +764 -0
- package/dist/styles.css +3 -0
- package/dist/theme-data.d.ts +363 -0
- package/dist/theme-data.js +374 -0
- package/dist/theme.d.ts +79 -0
- package/dist/theme.js +262 -0
- package/dist/tokens.css +449 -0
- package/dist/tree.d.ts +144 -0
- package/dist/tree.js +469 -0
- package/dist/virtual-list.d.ts +190 -0
- package/dist/virtual-list.js +521 -0
- package/dist/workbench.d.ts +136 -0
- package/dist/workbench.js +156 -0
- package/package.json +113 -0
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
/** @license MIT
|
|
2
|
+
* Copyright (c) 2026 vobsjs
|
|
3
|
+
* @vobs/ui
|
|
4
|
+
*/
|
|
5
|
+
import { createInputPrimitive, createUiDomPlan, } from './primitives.js';
|
|
6
|
+
export function createComboboxPlans(options) {
|
|
7
|
+
const open = options.open === true;
|
|
8
|
+
const activeId = open ? options.activeId : undefined;
|
|
9
|
+
return {
|
|
10
|
+
root: createUiDomPlan('div', 'combobox', {
|
|
11
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
12
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
13
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
14
|
+
state: options.disabled === true ? 'disabled' : open ? 'open' : 'closed',
|
|
15
|
+
attrs: {
|
|
16
|
+
id: options.id,
|
|
17
|
+
...options.attrs,
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
input: createUiDomPlan('input', 'combobox-input', {
|
|
21
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
22
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
23
|
+
attrs: {
|
|
24
|
+
id: `${options.id}-input`,
|
|
25
|
+
type: 'text',
|
|
26
|
+
role: 'combobox',
|
|
27
|
+
autocomplete: 'off',
|
|
28
|
+
'aria-autocomplete': 'list',
|
|
29
|
+
'aria-expanded': open ? 'true' : 'false',
|
|
30
|
+
'aria-controls': `${options.id}-listbox`,
|
|
31
|
+
'aria-activedescendant': activeId === undefined ? undefined : optionDomId(options.id, activeId),
|
|
32
|
+
'aria-labelledby': options.labelId,
|
|
33
|
+
'aria-describedby': options.describedBy,
|
|
34
|
+
'aria-required': options.required === true ? 'true' : undefined,
|
|
35
|
+
value: options.inputValue,
|
|
36
|
+
placeholder: options.placeholder,
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
listbox: createUiDomPlan('ul', 'combobox-listbox', {
|
|
40
|
+
state: open ? 'open' : 'closed',
|
|
41
|
+
attrs: {
|
|
42
|
+
id: `${options.id}-listbox`,
|
|
43
|
+
role: 'listbox',
|
|
44
|
+
hidden: open ? undefined : true,
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
options: options.options.map((option) => {
|
|
48
|
+
const active = option.id === activeId;
|
|
49
|
+
const selected = option.value === options.selectedValue;
|
|
50
|
+
return createUiDomPlan('li', 'combobox-option', {
|
|
51
|
+
...(option.disabled === undefined ? {} : { disabled: option.disabled }),
|
|
52
|
+
state: option.disabled === true
|
|
53
|
+
? 'disabled'
|
|
54
|
+
: active
|
|
55
|
+
? 'active'
|
|
56
|
+
: selected
|
|
57
|
+
? 'selected'
|
|
58
|
+
: 'idle',
|
|
59
|
+
attrs: {
|
|
60
|
+
id: optionDomId(options.id, option.id),
|
|
61
|
+
role: 'option',
|
|
62
|
+
'aria-selected': selected ? 'true' : 'false',
|
|
63
|
+
'data-option-id': option.id,
|
|
64
|
+
'data-value': option.value,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function createUploadPlans(options) {
|
|
71
|
+
const files = options.files ?? [];
|
|
72
|
+
const disabled = options.disabled === true;
|
|
73
|
+
return {
|
|
74
|
+
root: createUiDomPlan('div', 'upload', {
|
|
75
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
76
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
77
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
78
|
+
state: stateFromUpload(options, files.length),
|
|
79
|
+
attrs: {
|
|
80
|
+
id: `${options.id}-upload`,
|
|
81
|
+
...options.attrs,
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
input: createUiDomPlan('input', 'upload-input', {
|
|
85
|
+
disabled,
|
|
86
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
87
|
+
attrs: {
|
|
88
|
+
id: options.id,
|
|
89
|
+
type: 'file',
|
|
90
|
+
name: options.name,
|
|
91
|
+
accept: options.accept,
|
|
92
|
+
multiple: options.multiple,
|
|
93
|
+
required: options.required,
|
|
94
|
+
disabled,
|
|
95
|
+
'aria-labelledby': options.labelId,
|
|
96
|
+
'aria-describedby': options.describedBy,
|
|
97
|
+
'aria-invalid': options.invalid === true ? 'true' : undefined,
|
|
98
|
+
},
|
|
99
|
+
}),
|
|
100
|
+
dropzone: createUiDomPlan('label', 'upload-dropzone', {
|
|
101
|
+
disabled,
|
|
102
|
+
state: options.dragActive === true ? 'drag-active' : disabled ? 'disabled' : 'idle',
|
|
103
|
+
attrs: {
|
|
104
|
+
for: options.id,
|
|
105
|
+
'data-label': options.label,
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
fileList: createUiDomPlan('ul', 'upload-list', {
|
|
109
|
+
state: files.length === 0 ? 'empty' : 'filled',
|
|
110
|
+
attrs: {
|
|
111
|
+
id: `${options.id}-list`,
|
|
112
|
+
role: 'list',
|
|
113
|
+
'aria-live': 'polite',
|
|
114
|
+
hidden: files.length === 0 ? true : undefined,
|
|
115
|
+
},
|
|
116
|
+
}),
|
|
117
|
+
files: files.map((file) => createUiDomPlan('li', 'upload-file', {
|
|
118
|
+
state: file.state ?? 'done',
|
|
119
|
+
attrs: {
|
|
120
|
+
id: uploadFileDomId(options.id, file.id),
|
|
121
|
+
role: 'listitem',
|
|
122
|
+
'data-file-id': file.id,
|
|
123
|
+
'data-name': file.name,
|
|
124
|
+
'data-size': file.size,
|
|
125
|
+
'data-type': file.type,
|
|
126
|
+
'data-size-label': file.size === undefined ? undefined : formatUploadFileSize(file.size),
|
|
127
|
+
'data-progress': file.progress,
|
|
128
|
+
'data-retry-count': file.retryCount,
|
|
129
|
+
'data-retryable': file.retryable === true ? 'true' : undefined,
|
|
130
|
+
'data-server-error': file.serverError,
|
|
131
|
+
'data-message': file.message,
|
|
132
|
+
},
|
|
133
|
+
})),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export function createUploadTransportStateContract(options = {}) {
|
|
137
|
+
const disabled = options.disabled === true;
|
|
138
|
+
const maxRetries = normalizeRetryCount(options.maxRetries);
|
|
139
|
+
const retryingIds = normalizeIds(options.retryingIds);
|
|
140
|
+
const files = (options.files ?? []).map((file) => uploadTransportFileContract(file, {
|
|
141
|
+
disabled,
|
|
142
|
+
maxRetries,
|
|
143
|
+
retrying: retryingIds.includes(file.id),
|
|
144
|
+
serverErrors: normalizeServerErrors(options.serverErrors?.[file.id], file.serverError),
|
|
145
|
+
}));
|
|
146
|
+
const fileCount = files.length;
|
|
147
|
+
const pendingCount = files.filter((file) => file.state === 'pending').length;
|
|
148
|
+
const uploadingCount = files.filter((file) => file.state === 'uploading').length;
|
|
149
|
+
const doneCount = files.filter((file) => file.state === 'done').length;
|
|
150
|
+
const errorCount = files.filter((file) => file.state === 'error').length;
|
|
151
|
+
const retryableCount = files.filter((file) => file.canRetry).length;
|
|
152
|
+
const serverErrorCount = files.reduce((count, file) => count + file.serverErrors.length, 0);
|
|
153
|
+
const progress = fileCount === 0
|
|
154
|
+
? 0
|
|
155
|
+
: Math.round(files.reduce((total, file) => total + file.progress, 0) / fileCount);
|
|
156
|
+
const state = uploadTransportState({
|
|
157
|
+
disabled,
|
|
158
|
+
doneCount,
|
|
159
|
+
errorCount,
|
|
160
|
+
fileCount,
|
|
161
|
+
pendingCount,
|
|
162
|
+
retryingCount: retryingIds.length,
|
|
163
|
+
uploadingCount,
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
state,
|
|
167
|
+
disabled,
|
|
168
|
+
fileCount,
|
|
169
|
+
pendingCount,
|
|
170
|
+
uploadingCount,
|
|
171
|
+
doneCount,
|
|
172
|
+
errorCount,
|
|
173
|
+
retryableCount,
|
|
174
|
+
serverErrorCount,
|
|
175
|
+
retryingIds,
|
|
176
|
+
progress,
|
|
177
|
+
files,
|
|
178
|
+
attrs: {
|
|
179
|
+
'data-upload-transport-contract': 'true',
|
|
180
|
+
'data-state': state,
|
|
181
|
+
'data-disabled': disabled ? 'true' : undefined,
|
|
182
|
+
'data-file-count': fileCount,
|
|
183
|
+
'data-pending-count': pendingCount,
|
|
184
|
+
'data-uploading-count': uploadingCount,
|
|
185
|
+
'data-done-count': doneCount,
|
|
186
|
+
'data-error-count': errorCount,
|
|
187
|
+
'data-retryable-count': retryableCount,
|
|
188
|
+
'data-server-error-count': serverErrorCount,
|
|
189
|
+
'data-retrying-ids': retryingIds.join(','),
|
|
190
|
+
'data-progress': progress,
|
|
191
|
+
'aria-busy': state === 'uploading' || state === 'retrying' ? 'true' : undefined,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export function createCascaderPlans(options) {
|
|
196
|
+
const open = options.open === true;
|
|
197
|
+
const selectedPath = findCascaderPathByValue(options.options, options.selectedValue);
|
|
198
|
+
const activePath = options.activePath === undefined || options.activePath.length === 0
|
|
199
|
+
? selectedPath.map((item) => item.id)
|
|
200
|
+
: [...options.activePath];
|
|
201
|
+
const columns = getCascaderColumns(options.options, activePath);
|
|
202
|
+
const activeId = activePath.at(-1);
|
|
203
|
+
const selectedId = selectedPath.at(-1)?.id;
|
|
204
|
+
const selectedLabel = selectedPath.map((item) => item.label).join(' / ');
|
|
205
|
+
const valueLabel = selectedLabel === '' ? (options.placeholder ?? 'Select option') : selectedLabel;
|
|
206
|
+
const disabled = options.disabled === true;
|
|
207
|
+
return {
|
|
208
|
+
root: createUiDomPlan('div', 'cascader', {
|
|
209
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
210
|
+
disabled,
|
|
211
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
212
|
+
state: disabled ? 'disabled' : open ? 'open' : 'closed',
|
|
213
|
+
attrs: {
|
|
214
|
+
id: `${options.id}-cascader`,
|
|
215
|
+
...options.attrs,
|
|
216
|
+
'data-value': options.selectedValue,
|
|
217
|
+
'data-label': valueLabel,
|
|
218
|
+
'data-levels': columns.length,
|
|
219
|
+
},
|
|
220
|
+
}),
|
|
221
|
+
trigger: createUiDomPlan('button', 'cascader-trigger', {
|
|
222
|
+
disabled,
|
|
223
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
224
|
+
attrs: {
|
|
225
|
+
id: `${options.id}-trigger`,
|
|
226
|
+
type: 'button',
|
|
227
|
+
role: 'combobox',
|
|
228
|
+
'aria-haspopup': 'tree',
|
|
229
|
+
'aria-expanded': open ? 'true' : 'false',
|
|
230
|
+
'aria-controls': `${options.id}-popup`,
|
|
231
|
+
'aria-labelledby': options.labelId,
|
|
232
|
+
'aria-describedby': options.describedBy,
|
|
233
|
+
'aria-required': options.required === true ? 'true' : undefined,
|
|
234
|
+
'data-value': options.selectedValue,
|
|
235
|
+
'data-label': valueLabel,
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
popup: createUiDomPlan('div', 'cascader-popup', {
|
|
239
|
+
state: open ? 'open' : 'closed',
|
|
240
|
+
attrs: {
|
|
241
|
+
id: `${options.id}-popup`,
|
|
242
|
+
role: 'tree',
|
|
243
|
+
hidden: open ? undefined : true,
|
|
244
|
+
'aria-labelledby': options.labelId,
|
|
245
|
+
'data-levels': columns.length,
|
|
246
|
+
},
|
|
247
|
+
}),
|
|
248
|
+
columns: columns.map((column, index) => createUiDomPlan('div', 'cascader-column', {
|
|
249
|
+
attrs: {
|
|
250
|
+
id: cascaderColumnDomId(options.id, index),
|
|
251
|
+
role: 'group',
|
|
252
|
+
'data-level': index + 1,
|
|
253
|
+
'data-count': column.length,
|
|
254
|
+
},
|
|
255
|
+
})),
|
|
256
|
+
options: columns.flatMap((column, columnIndex) => column.map((item) => {
|
|
257
|
+
const active = item.id === activeId;
|
|
258
|
+
const selected = item.id === selectedId;
|
|
259
|
+
const expandable = (item.children?.length ?? 0) > 0;
|
|
260
|
+
return createUiDomPlan('button', 'cascader-option', {
|
|
261
|
+
...(item.disabled === undefined ? {} : { disabled: item.disabled }),
|
|
262
|
+
state: item.disabled === true
|
|
263
|
+
? 'disabled'
|
|
264
|
+
: active
|
|
265
|
+
? 'active'
|
|
266
|
+
: selected
|
|
267
|
+
? 'selected'
|
|
268
|
+
: 'idle',
|
|
269
|
+
attrs: {
|
|
270
|
+
id: cascaderOptionDomId(options.id, item.id),
|
|
271
|
+
type: 'button',
|
|
272
|
+
role: 'treeitem',
|
|
273
|
+
'aria-level': columnIndex + 1,
|
|
274
|
+
'aria-expanded': expandable ? String(active) : undefined,
|
|
275
|
+
'aria-selected': selected ? 'true' : 'false',
|
|
276
|
+
'data-column': columnIndex,
|
|
277
|
+
'data-option-id': item.id,
|
|
278
|
+
'data-value': item.value,
|
|
279
|
+
'data-label': item.label,
|
|
280
|
+
'data-has-children': expandable ? 'true' : undefined,
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
})),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
export function createCascaderAdvancedStateContract(options) {
|
|
287
|
+
const flattenedOptions = flattenCascaderOptions(options.options);
|
|
288
|
+
const query = (options.query ?? '').trim();
|
|
289
|
+
const resultCount = normalizeCount(options.resultCount);
|
|
290
|
+
const totalNodeCount = normalizeCount(options.totalNodeCount) ?? flattenedOptions.length;
|
|
291
|
+
const visibleIds = options.visibleIds === undefined
|
|
292
|
+
? flattenedOptions.map((option) => option.id)
|
|
293
|
+
: normalizeCascaderIds(options.visibleIds, new Set(flattenedOptions.map((option) => option.id)));
|
|
294
|
+
const allowedSet = options.allowedIds === undefined
|
|
295
|
+
? undefined
|
|
296
|
+
: new Set(normalizeCascaderIds(options.allowedIds, new Set(flattenedOptions.map((option) => option.id))));
|
|
297
|
+
const deniedIds = normalizeCascaderIds(options.deniedIds, new Set(flattenedOptions.map((option) => option.id)));
|
|
298
|
+
const deniedSet = new Set(deniedIds);
|
|
299
|
+
const optionContracts = flattenedOptions.map((option) => {
|
|
300
|
+
const visible = visibleIds.includes(option.id);
|
|
301
|
+
const allowed = !deniedSet.has(option.id) && (allowedSet === undefined || allowedSet.has(option.id));
|
|
302
|
+
const reason = allowed ? '' : (options.disabledReasons?.[option.id] ?? 'Permission denied');
|
|
303
|
+
return {
|
|
304
|
+
id: option.id,
|
|
305
|
+
visible,
|
|
306
|
+
allowed,
|
|
307
|
+
reason,
|
|
308
|
+
attrs: {
|
|
309
|
+
'data-option-id': option.id,
|
|
310
|
+
'data-visible': visible ? 'true' : 'false',
|
|
311
|
+
'data-permission': allowed ? 'allowed' : 'denied',
|
|
312
|
+
'data-disabled-reason': reason === '' ? undefined : reason,
|
|
313
|
+
'aria-disabled': allowed ? undefined : 'true',
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
const deniedOptionIds = optionContracts
|
|
318
|
+
.filter((option) => !option.allowed)
|
|
319
|
+
.map((option) => option.id);
|
|
320
|
+
const virtualStart = normalizeVirtualIndex(options.virtualStart, 0, totalNodeCount);
|
|
321
|
+
const virtualEnd = normalizeVirtualIndex(options.virtualEnd, totalNodeCount - 1, totalNodeCount);
|
|
322
|
+
const virtualState = virtualStart > 0 || virtualEnd < Math.max(totalNodeCount - 1, 0) ? 'windowed' : 'full';
|
|
323
|
+
const remoteState = cascaderRemoteState({
|
|
324
|
+
error: options.error,
|
|
325
|
+
loading: options.loading === true,
|
|
326
|
+
query,
|
|
327
|
+
resultCount,
|
|
328
|
+
});
|
|
329
|
+
const persistenceState = cascaderPersistenceState({
|
|
330
|
+
currentValue: options.currentValue,
|
|
331
|
+
dirty: options.dirty === true,
|
|
332
|
+
persistedValue: options.persistedValue,
|
|
333
|
+
restored: options.restored === true,
|
|
334
|
+
});
|
|
335
|
+
const permissionState = deniedOptionIds.length > 0 ? 'restricted' : 'allowed';
|
|
336
|
+
const currentValue = options.currentValue ?? '';
|
|
337
|
+
const persistedValue = options.persistedValue ?? '';
|
|
338
|
+
return {
|
|
339
|
+
remoteState,
|
|
340
|
+
permissionState,
|
|
341
|
+
virtualState,
|
|
342
|
+
persistenceState,
|
|
343
|
+
query,
|
|
344
|
+
resultCount,
|
|
345
|
+
deniedIds: deniedOptionIds,
|
|
346
|
+
visibleIds,
|
|
347
|
+
totalNodeCount,
|
|
348
|
+
virtualStart,
|
|
349
|
+
virtualEnd,
|
|
350
|
+
currentValue,
|
|
351
|
+
persistedValue,
|
|
352
|
+
optionContracts,
|
|
353
|
+
attrs: {
|
|
354
|
+
'data-cascader-advanced-contract': 'true',
|
|
355
|
+
'data-remote-state': remoteState,
|
|
356
|
+
'data-query': query,
|
|
357
|
+
'data-result-count': resultCount,
|
|
358
|
+
'data-error-message': options.error,
|
|
359
|
+
'data-permission-state': permissionState,
|
|
360
|
+
'data-denied-option-ids': deniedOptionIds.join(','),
|
|
361
|
+
'data-denied-count': deniedOptionIds.length,
|
|
362
|
+
'data-visible-option-ids': visibleIds.join(','),
|
|
363
|
+
'data-visible-count': visibleIds.length,
|
|
364
|
+
'data-virtual-state': virtualState,
|
|
365
|
+
'data-total-node-count': totalNodeCount,
|
|
366
|
+
'data-window-start': virtualStart,
|
|
367
|
+
'data-window-end': virtualEnd,
|
|
368
|
+
'data-window-size': Math.max(virtualEnd - virtualStart + 1, 0),
|
|
369
|
+
'data-estimated-row-height': normalizeCount(options.estimatedRowHeight),
|
|
370
|
+
'data-persistence-state': persistenceState,
|
|
371
|
+
'data-current-value': currentValue,
|
|
372
|
+
'data-persisted-value': persistedValue,
|
|
373
|
+
'data-dirty': options.dirty === true ? 'true' : undefined,
|
|
374
|
+
'data-restored': options.restored === true ? 'true' : undefined,
|
|
375
|
+
'aria-busy': options.loading === true ? 'true' : undefined,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
export function createDateTimeFieldPlans(options) {
|
|
380
|
+
const inputType = options.kind === 'datetime' ? 'datetime-local' : options.kind === 'time' ? 'time' : 'date';
|
|
381
|
+
return {
|
|
382
|
+
root: createUiDomPlan('div', `${options.kind}-field`, {
|
|
383
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
384
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
385
|
+
...(options.readonly === undefined ? {} : { readonly: options.readonly }),
|
|
386
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
387
|
+
...(options.density === undefined ? {} : { density: options.density }),
|
|
388
|
+
state: stateFromDateTimeField(options),
|
|
389
|
+
attrs: {
|
|
390
|
+
id: `${options.id}-field`,
|
|
391
|
+
},
|
|
392
|
+
}),
|
|
393
|
+
input: createInputPrimitive({
|
|
394
|
+
type: 'text',
|
|
395
|
+
...(options.name === undefined ? {} : { name: options.name }),
|
|
396
|
+
...(options.value === undefined ? {} : { value: options.value }),
|
|
397
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
398
|
+
...(options.readonly === undefined ? {} : { readonly: options.readonly }),
|
|
399
|
+
...(options.required === undefined ? {} : { required: options.required }),
|
|
400
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
401
|
+
...(options.density === undefined ? {} : { density: options.density }),
|
|
402
|
+
...(options.size === undefined ? {} : { className: `kui-input--${options.size}` }),
|
|
403
|
+
attrs: {
|
|
404
|
+
id: options.id,
|
|
405
|
+
type: inputType,
|
|
406
|
+
min: options.min,
|
|
407
|
+
max: options.max,
|
|
408
|
+
step: options.step,
|
|
409
|
+
placeholder: options.placeholder,
|
|
410
|
+
'aria-labelledby': options.labelId,
|
|
411
|
+
'aria-describedby': options.describedBy,
|
|
412
|
+
'aria-invalid': options.invalid === true ? 'true' : undefined,
|
|
413
|
+
...options.attrs,
|
|
414
|
+
},
|
|
415
|
+
}),
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
export function formatDateInputValue(value) {
|
|
419
|
+
return [
|
|
420
|
+
value.getFullYear().toString().padStart(4, '0'),
|
|
421
|
+
(value.getMonth() + 1).toString().padStart(2, '0'),
|
|
422
|
+
value.getDate().toString().padStart(2, '0'),
|
|
423
|
+
].join('-');
|
|
424
|
+
}
|
|
425
|
+
export function formatTimeInputValue(value, includeSeconds = false) {
|
|
426
|
+
const formatted = [
|
|
427
|
+
value.getHours().toString().padStart(2, '0'),
|
|
428
|
+
value.getMinutes().toString().padStart(2, '0'),
|
|
429
|
+
];
|
|
430
|
+
if (includeSeconds)
|
|
431
|
+
formatted.push(value.getSeconds().toString().padStart(2, '0'));
|
|
432
|
+
return formatted.join(':');
|
|
433
|
+
}
|
|
434
|
+
export function formatDateTimeInputValue(value, includeSeconds = false) {
|
|
435
|
+
return `${formatDateInputValue(value)}T${formatTimeInputValue(value, includeSeconds)}`;
|
|
436
|
+
}
|
|
437
|
+
export function parseDateInputValue(value) {
|
|
438
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(value);
|
|
439
|
+
if (match === null)
|
|
440
|
+
return undefined;
|
|
441
|
+
return createValidDate(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
|
442
|
+
}
|
|
443
|
+
export function parseTimeInputValue(value) {
|
|
444
|
+
const match = /^(\d{2}):(\d{2})(?::(\d{2}))?$/u.exec(value);
|
|
445
|
+
if (match === null)
|
|
446
|
+
return undefined;
|
|
447
|
+
const hours = Number(match[1]);
|
|
448
|
+
const minutes = Number(match[2]);
|
|
449
|
+
const seconds = Number(match[3] ?? '0');
|
|
450
|
+
if (hours > 23 || minutes > 59 || seconds > 59)
|
|
451
|
+
return undefined;
|
|
452
|
+
const date = new Date(0);
|
|
453
|
+
date.setHours(hours, minutes, seconds, 0);
|
|
454
|
+
return date;
|
|
455
|
+
}
|
|
456
|
+
export function parseDateTimeInputValue(value) {
|
|
457
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}(?::\d{2})?)$/u.exec(value);
|
|
458
|
+
if (match === null)
|
|
459
|
+
return undefined;
|
|
460
|
+
const date = parseDateInputValue(match[1] ?? '');
|
|
461
|
+
const time = parseTimeInputValue(match[2] ?? '');
|
|
462
|
+
if (date === undefined || time === undefined)
|
|
463
|
+
return undefined;
|
|
464
|
+
date.setHours(time.getHours(), time.getMinutes(), time.getSeconds(), 0);
|
|
465
|
+
return date;
|
|
466
|
+
}
|
|
467
|
+
export function clampDateTimeInputValue(value, min, max) {
|
|
468
|
+
if (min !== undefined && value < min)
|
|
469
|
+
return min;
|
|
470
|
+
if (max !== undefined && value > max)
|
|
471
|
+
return max;
|
|
472
|
+
return value;
|
|
473
|
+
}
|
|
474
|
+
export function formatUploadFileSize(bytes) {
|
|
475
|
+
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
476
|
+
return '0 B';
|
|
477
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
478
|
+
let value = bytes;
|
|
479
|
+
let unitIndex = 0;
|
|
480
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
481
|
+
value /= 1024;
|
|
482
|
+
unitIndex += 1;
|
|
483
|
+
}
|
|
484
|
+
const rounded = unitIndex === 0 || value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
485
|
+
return `${rounded} ${units[unitIndex]}`;
|
|
486
|
+
}
|
|
487
|
+
export function isUploadFileAccepted(file, accept) {
|
|
488
|
+
if (accept === undefined || accept.trim() === '')
|
|
489
|
+
return true;
|
|
490
|
+
const rules = accept
|
|
491
|
+
.split(',')
|
|
492
|
+
.map((rule) => rule.trim().toLowerCase())
|
|
493
|
+
.filter((rule) => rule !== '');
|
|
494
|
+
const fileName = file.name.toLowerCase();
|
|
495
|
+
const fileType = file.type?.toLowerCase() ?? '';
|
|
496
|
+
return rules.some((rule) => {
|
|
497
|
+
if (rule.startsWith('.'))
|
|
498
|
+
return fileName.endsWith(rule);
|
|
499
|
+
if (rule.endsWith('/*'))
|
|
500
|
+
return fileType.startsWith(rule.slice(0, -1));
|
|
501
|
+
return fileType === rule;
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
export function filterAutocompleteOptions(options, query, limit = options.length) {
|
|
505
|
+
const keyword = query.trim().toLowerCase();
|
|
506
|
+
const result = [];
|
|
507
|
+
for (const option of options) {
|
|
508
|
+
if (keyword === '' ||
|
|
509
|
+
option.label.toLowerCase().includes(keyword) ||
|
|
510
|
+
option.value.toLowerCase().includes(keyword)) {
|
|
511
|
+
result.push(option);
|
|
512
|
+
if (result.length >= limit)
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
518
|
+
export function getNextComboboxOptionId(options, activeId, key) {
|
|
519
|
+
const enabled = options.filter((option) => option.disabled !== true);
|
|
520
|
+
if (enabled.length === 0)
|
|
521
|
+
return activeId;
|
|
522
|
+
const currentIndex = Math.max(-1, enabled.findIndex((option) => option.id === activeId));
|
|
523
|
+
if (key === 'Home')
|
|
524
|
+
return enabled[0]?.id;
|
|
525
|
+
if (key === 'End')
|
|
526
|
+
return enabled.at(-1)?.id;
|
|
527
|
+
if (key === 'ArrowUp')
|
|
528
|
+
return enabled.at(currentIndex - 1)?.id ?? enabled.at(-1)?.id;
|
|
529
|
+
if (key === 'ArrowDown')
|
|
530
|
+
return enabled[(currentIndex + 1) % enabled.length]?.id;
|
|
531
|
+
return activeId;
|
|
532
|
+
}
|
|
533
|
+
export function getCascaderColumns(options, activePath = []) {
|
|
534
|
+
const columns = [[...options]];
|
|
535
|
+
let currentOptions = options;
|
|
536
|
+
for (const activeId of activePath) {
|
|
537
|
+
const active = currentOptions.find((option) => option.id === activeId);
|
|
538
|
+
if (active?.children === undefined || active.children.length === 0)
|
|
539
|
+
break;
|
|
540
|
+
columns.push([...active.children]);
|
|
541
|
+
currentOptions = active.children;
|
|
542
|
+
}
|
|
543
|
+
return columns;
|
|
544
|
+
}
|
|
545
|
+
export function findCascaderPathByValue(options, value) {
|
|
546
|
+
if (value === undefined)
|
|
547
|
+
return [];
|
|
548
|
+
return findCascaderPath(options, (option) => option.value === value);
|
|
549
|
+
}
|
|
550
|
+
export function findCascaderPathById(options, id) {
|
|
551
|
+
if (id === undefined)
|
|
552
|
+
return [];
|
|
553
|
+
return findCascaderPath(options, (option) => option.id === id);
|
|
554
|
+
}
|
|
555
|
+
export function getNextCascaderOptionId(options, activeId, key) {
|
|
556
|
+
const enabled = options.filter((option) => option.disabled !== true);
|
|
557
|
+
if (enabled.length === 0)
|
|
558
|
+
return activeId;
|
|
559
|
+
const currentIndex = Math.max(-1, enabled.findIndex((option) => option.id === activeId));
|
|
560
|
+
if (key === 'Home')
|
|
561
|
+
return enabled[0]?.id;
|
|
562
|
+
if (key === 'End')
|
|
563
|
+
return enabled.at(-1)?.id;
|
|
564
|
+
if (key === 'ArrowUp')
|
|
565
|
+
return enabled.at(currentIndex - 1)?.id ?? enabled.at(-1)?.id;
|
|
566
|
+
if (key === 'ArrowDown')
|
|
567
|
+
return enabled[(currentIndex + 1) % enabled.length]?.id;
|
|
568
|
+
return activeId;
|
|
569
|
+
}
|
|
570
|
+
export function shouldOpenCascaderForKey(key) {
|
|
571
|
+
return key === 'ArrowDown' || key === 'ArrowUp' || key === 'Enter' || key === ' ';
|
|
572
|
+
}
|
|
573
|
+
export function shouldSelectCascaderOption(key) {
|
|
574
|
+
return key === 'Enter' || key === ' ';
|
|
575
|
+
}
|
|
576
|
+
export function shouldCloseCascaderForKey(key) {
|
|
577
|
+
return key === 'Escape' || key === 'Tab';
|
|
578
|
+
}
|
|
579
|
+
export function shouldOpenComboboxForKey(key) {
|
|
580
|
+
return key === 'ArrowDown' || key === 'ArrowUp';
|
|
581
|
+
}
|
|
582
|
+
export function shouldSelectComboboxOption(key) {
|
|
583
|
+
return key === 'Enter';
|
|
584
|
+
}
|
|
585
|
+
export function shouldCloseComboboxForKey(key) {
|
|
586
|
+
return key === 'Escape' || key === 'Tab';
|
|
587
|
+
}
|
|
588
|
+
function optionDomId(comboboxId, optionId) {
|
|
589
|
+
return `${comboboxId}-${optionId}-option`;
|
|
590
|
+
}
|
|
591
|
+
function cascaderColumnDomId(cascaderId, columnIndex) {
|
|
592
|
+
return `${cascaderId}-column-${columnIndex + 1}`;
|
|
593
|
+
}
|
|
594
|
+
function cascaderOptionDomId(cascaderId, optionId) {
|
|
595
|
+
return `${cascaderId}-${optionId}-cascader-option`;
|
|
596
|
+
}
|
|
597
|
+
function uploadFileDomId(uploadId, fileId) {
|
|
598
|
+
return `${uploadId}-${fileId}-file`;
|
|
599
|
+
}
|
|
600
|
+
function uploadTransportFileContract(file, options) {
|
|
601
|
+
const state = file.state ?? 'done';
|
|
602
|
+
const progress = normalizeProgress(file.progress, state);
|
|
603
|
+
const retryCount = normalizeRetryCount(file.retryCount);
|
|
604
|
+
const retryable = file.retryable === true || options.serverErrors.length > 0;
|
|
605
|
+
const canRetry = !options.disabled &&
|
|
606
|
+
state === 'error' &&
|
|
607
|
+
retryable &&
|
|
608
|
+
retryCount < options.maxRetries &&
|
|
609
|
+
!options.retrying;
|
|
610
|
+
return {
|
|
611
|
+
id: file.id,
|
|
612
|
+
state,
|
|
613
|
+
progress,
|
|
614
|
+
retryCount,
|
|
615
|
+
retrying: options.retrying,
|
|
616
|
+
retryable,
|
|
617
|
+
canRetry,
|
|
618
|
+
serverErrors: options.serverErrors,
|
|
619
|
+
attrs: {
|
|
620
|
+
'data-file-id': file.id,
|
|
621
|
+
'data-state': options.retrying ? 'uploading' : state,
|
|
622
|
+
'data-progress': progress,
|
|
623
|
+
'data-retry-count': retryCount,
|
|
624
|
+
'data-retryable': retryable ? 'true' : 'false',
|
|
625
|
+
'data-can-retry': canRetry ? 'true' : 'false',
|
|
626
|
+
'data-server-error-count': options.serverErrors.length,
|
|
627
|
+
'data-server-errors': options.serverErrors.join('|'),
|
|
628
|
+
'aria-busy': options.retrying || state === 'uploading' ? 'true' : undefined,
|
|
629
|
+
},
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function findCascaderPath(options, predicate) {
|
|
633
|
+
for (const option of options) {
|
|
634
|
+
if (predicate(option))
|
|
635
|
+
return [option];
|
|
636
|
+
const childPath = option.children === undefined ? [] : findCascaderPath(option.children, predicate);
|
|
637
|
+
if (childPath.length > 0)
|
|
638
|
+
return [option, ...childPath];
|
|
639
|
+
}
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
function flattenCascaderOptions(options) {
|
|
643
|
+
return options.flatMap((option) => [
|
|
644
|
+
option,
|
|
645
|
+
...(option.children === undefined ? [] : flattenCascaderOptions(option.children)),
|
|
646
|
+
]);
|
|
647
|
+
}
|
|
648
|
+
function normalizeCount(value) {
|
|
649
|
+
if (value === undefined || !Number.isFinite(value))
|
|
650
|
+
return undefined;
|
|
651
|
+
return Math.max(0, Math.trunc(value));
|
|
652
|
+
}
|
|
653
|
+
function normalizeVirtualIndex(value, fallback, total) {
|
|
654
|
+
const normalized = normalizeCount(value) ?? fallback;
|
|
655
|
+
return Math.min(Math.max(normalized, 0), Math.max(total - 1, 0));
|
|
656
|
+
}
|
|
657
|
+
function normalizeCascaderIds(ids, knownIds) {
|
|
658
|
+
if (ids === undefined)
|
|
659
|
+
return [];
|
|
660
|
+
const result = [];
|
|
661
|
+
for (const id of ids) {
|
|
662
|
+
if (!knownIds.has(id) || result.includes(id))
|
|
663
|
+
continue;
|
|
664
|
+
result.push(id);
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
function cascaderRemoteState(options) {
|
|
669
|
+
if (options.loading)
|
|
670
|
+
return 'loading';
|
|
671
|
+
if (options.error !== undefined && options.error.length > 0)
|
|
672
|
+
return 'error';
|
|
673
|
+
if (options.resultCount === 0)
|
|
674
|
+
return 'empty';
|
|
675
|
+
if (options.query.length > 0)
|
|
676
|
+
return 'filtered';
|
|
677
|
+
return 'ready';
|
|
678
|
+
}
|
|
679
|
+
function cascaderPersistenceState(options) {
|
|
680
|
+
if (options.restored)
|
|
681
|
+
return 'restored';
|
|
682
|
+
if (options.dirty)
|
|
683
|
+
return 'dirty';
|
|
684
|
+
if (options.persistedValue === undefined)
|
|
685
|
+
return 'unbound';
|
|
686
|
+
return options.currentValue === options.persistedValue ? 'synced' : 'dirty';
|
|
687
|
+
}
|
|
688
|
+
function stateFromDateTimeField(options) {
|
|
689
|
+
if (options.disabled === true)
|
|
690
|
+
return 'disabled';
|
|
691
|
+
if (options.readonly === true)
|
|
692
|
+
return 'readonly';
|
|
693
|
+
if (options.invalid === true)
|
|
694
|
+
return 'invalid';
|
|
695
|
+
return options.value === undefined || options.value === '' ? 'empty' : 'filled';
|
|
696
|
+
}
|
|
697
|
+
function stateFromUpload(options, fileCount) {
|
|
698
|
+
if (options.disabled === true)
|
|
699
|
+
return 'disabled';
|
|
700
|
+
if (options.invalid === true)
|
|
701
|
+
return 'invalid';
|
|
702
|
+
if (options.dragActive === true)
|
|
703
|
+
return 'drag-active';
|
|
704
|
+
return fileCount === 0 ? 'empty' : 'filled';
|
|
705
|
+
}
|
|
706
|
+
function uploadTransportState(options) {
|
|
707
|
+
if (options.disabled)
|
|
708
|
+
return 'disabled';
|
|
709
|
+
if (options.fileCount === 0)
|
|
710
|
+
return 'empty';
|
|
711
|
+
if (options.retryingCount > 0)
|
|
712
|
+
return 'retrying';
|
|
713
|
+
if (options.uploadingCount > 0)
|
|
714
|
+
return 'uploading';
|
|
715
|
+
if (options.errorCount > 0)
|
|
716
|
+
return 'error';
|
|
717
|
+
if (options.doneCount === options.fileCount)
|
|
718
|
+
return 'complete';
|
|
719
|
+
if (options.doneCount > 0)
|
|
720
|
+
return 'partial';
|
|
721
|
+
if (options.pendingCount > 0)
|
|
722
|
+
return 'pending';
|
|
723
|
+
return 'empty';
|
|
724
|
+
}
|
|
725
|
+
function normalizeProgress(value, state) {
|
|
726
|
+
if (state === 'done')
|
|
727
|
+
return 100;
|
|
728
|
+
if (value === undefined || !Number.isFinite(value))
|
|
729
|
+
return 0;
|
|
730
|
+
return Math.min(100, Math.max(0, Math.trunc(value)));
|
|
731
|
+
}
|
|
732
|
+
function normalizeRetryCount(value) {
|
|
733
|
+
if (value === undefined || !Number.isFinite(value))
|
|
734
|
+
return 0;
|
|
735
|
+
return Math.max(0, Math.trunc(value));
|
|
736
|
+
}
|
|
737
|
+
function normalizeIds(ids) {
|
|
738
|
+
if (ids === undefined)
|
|
739
|
+
return [];
|
|
740
|
+
const values = Array.isArray(ids) ? ids : [...ids];
|
|
741
|
+
return [...new Set(values.filter((id) => id.length > 0))];
|
|
742
|
+
}
|
|
743
|
+
function normalizeServerErrors(value, fallback) {
|
|
744
|
+
const values = value === undefined
|
|
745
|
+
? fallback === undefined
|
|
746
|
+
? []
|
|
747
|
+
: [fallback]
|
|
748
|
+
: typeof value === 'string'
|
|
749
|
+
? [value]
|
|
750
|
+
: value;
|
|
751
|
+
return values.map((item) => item.trim()).filter((item) => item.length > 0);
|
|
752
|
+
}
|
|
753
|
+
function createValidDate(year, month, day) {
|
|
754
|
+
const date = new Date(year, month, day);
|
|
755
|
+
if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
return date;
|
|
759
|
+
}
|
|
760
|
+
export function createColorPickerPlans(options) {
|
|
761
|
+
return {
|
|
762
|
+
root: createUiDomPlan('div', 'color-picker', {
|
|
763
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
764
|
+
attrs: { id: `${options.id}-colorpicker`, ...options.attrs },
|
|
765
|
+
}),
|
|
766
|
+
input: createUiDomPlan('input', 'color-picker-input', {
|
|
767
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
768
|
+
attrs: {
|
|
769
|
+
id: `${options.id}-input`,
|
|
770
|
+
type: 'color',
|
|
771
|
+
...(options.value === undefined ? {} : { value: options.value }),
|
|
772
|
+
},
|
|
773
|
+
}),
|
|
774
|
+
preview: createUiDomPlan('span', 'color-picker-preview', {
|
|
775
|
+
attrs: {
|
|
776
|
+
id: `${options.id}-preview`,
|
|
777
|
+
'aria-hidden': 'true',
|
|
778
|
+
...(options.value === undefined ? {} : { 'data-value': options.value }),
|
|
779
|
+
},
|
|
780
|
+
}),
|
|
781
|
+
value: createUiDomPlan('span', 'color-picker-value', {
|
|
782
|
+
attrs: {
|
|
783
|
+
id: `${options.id}-value`,
|
|
784
|
+
...(options.value === undefined ? {} : { 'data-label': options.value }),
|
|
785
|
+
},
|
|
786
|
+
}),
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
export function createTransferPlans(options) {
|
|
790
|
+
return {
|
|
791
|
+
root: createUiDomPlan('div', 'transfer', {
|
|
792
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
793
|
+
attrs: { id: `${options.id}-transfer`, ...options.attrs },
|
|
794
|
+
}),
|
|
795
|
+
source: createUiDomPlan('div', 'transfer-pane', {
|
|
796
|
+
attrs: { id: `${options.id}-source` },
|
|
797
|
+
}),
|
|
798
|
+
sourceList: createUiDomPlan('ul', 'transfer-list', {
|
|
799
|
+
attrs: { id: `${options.id}-source-list` },
|
|
800
|
+
}),
|
|
801
|
+
actions: createUiDomPlan('div', 'transfer-actions', {
|
|
802
|
+
attrs: { id: `${options.id}-actions` },
|
|
803
|
+
}),
|
|
804
|
+
toRight: createUiDomPlan('button', 'transfer-action', {
|
|
805
|
+
attrs: {
|
|
806
|
+
id: `${options.id}-to-right`,
|
|
807
|
+
type: 'button',
|
|
808
|
+
'data-direction': 'right',
|
|
809
|
+
'aria-label': 'Move to target',
|
|
810
|
+
},
|
|
811
|
+
}),
|
|
812
|
+
toLeft: createUiDomPlan('button', 'transfer-action', {
|
|
813
|
+
attrs: {
|
|
814
|
+
id: `${options.id}-to-left`,
|
|
815
|
+
type: 'button',
|
|
816
|
+
'data-direction': 'left',
|
|
817
|
+
'aria-label': 'Move to source',
|
|
818
|
+
},
|
|
819
|
+
}),
|
|
820
|
+
target: createUiDomPlan('div', 'transfer-pane', {
|
|
821
|
+
attrs: { id: `${options.id}-target` },
|
|
822
|
+
}),
|
|
823
|
+
targetList: createUiDomPlan('ul', 'transfer-list', {
|
|
824
|
+
attrs: { id: `${options.id}-target-list` },
|
|
825
|
+
}),
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const MASK_PRESETS = {
|
|
829
|
+
amount: '###,###.##',
|
|
830
|
+
date: '####-##-##',
|
|
831
|
+
phone: '###-####-####',
|
|
832
|
+
};
|
|
833
|
+
export function resolveMaskInputMask(kind, mask) {
|
|
834
|
+
if (mask !== undefined && mask !== '')
|
|
835
|
+
return mask;
|
|
836
|
+
return (kind === undefined ? 'phone' : kind === 'custom' ? '' : MASK_PRESETS[kind]) ?? '';
|
|
837
|
+
}
|
|
838
|
+
export function createMaskInputPlans(options) {
|
|
839
|
+
const kind = options.kind ?? 'phone';
|
|
840
|
+
const mask = resolveMaskInputMask(kind, options.mask);
|
|
841
|
+
return {
|
|
842
|
+
root: createUiDomPlan('div', 'mask-input', {
|
|
843
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
844
|
+
...(options.className === undefined ? {} : { className: options.className }),
|
|
845
|
+
attrs: { id: `${options.id}-mask-input`, ...options.attrs },
|
|
846
|
+
}),
|
|
847
|
+
input: createUiDomPlan('input', 'mask-input-field', {
|
|
848
|
+
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
849
|
+
...(options.readonly === undefined ? {} : { readonly: options.readonly }),
|
|
850
|
+
...(options.invalid === undefined ? {} : { invalid: options.invalid }),
|
|
851
|
+
attrs: {
|
|
852
|
+
id: `${options.id}-input`,
|
|
853
|
+
type: 'text',
|
|
854
|
+
inputmode: kind === 'phone' || kind === 'date' ? 'numeric' : 'decimal',
|
|
855
|
+
'data-kind': kind,
|
|
856
|
+
...(mask === '' ? {} : { 'data-mask': mask }),
|
|
857
|
+
...(options.value === undefined ? {} : { value: options.value }),
|
|
858
|
+
...(options.placeholder === undefined ? {} : { placeholder: options.placeholder ?? mask }),
|
|
859
|
+
},
|
|
860
|
+
}),
|
|
861
|
+
};
|
|
862
|
+
}
|