@vobs/ui 0.3.0 → 1.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 +1 -1
- package/README.md +60 -0
- package/package.json +52 -110
- package/src/activity-rail.ts +125 -0
- package/src/alert.ts +65 -0
- package/src/assets/icons/alert-circle.svg +1 -0
- package/src/assets/icons/alert-triangle.svg +1 -0
- package/src/assets/icons/alert.d27e24.svg +1 -0
- package/src/assets/icons/alert.svg +1 -0
- package/src/assets/icons/apple.svg +1 -0
- package/src/assets/icons/arrow-down.svg +1 -0
- package/src/assets/icons/arrow-expand.svg +1 -0
- package/src/assets/icons/arrow-left.svg +1 -0
- package/src/assets/icons/arrow-minimize.svg +1 -0
- package/src/assets/icons/arrow-right-to-line.svg +1 -0
- package/src/assets/icons/arrow-right.svg +1 -0
- package/src/assets/icons/arrow-up-right.svg +1 -0
- package/src/assets/icons/arrow-up.0c0c0d.svg +1 -0
- package/src/assets/icons/arrow-up.svg +1 -0
- package/src/assets/icons/at.svg +1 -0
- package/src/assets/icons/atom.0c0c0d.svg +1 -0
- package/src/assets/icons/atom.svg +1 -0
- package/src/assets/icons/bar.svg +1 -0
- package/src/assets/icons/bell.svg +1 -0
- package/src/assets/icons/box.svg +1 -0
- package/src/assets/icons/bug.svg +1 -0
- package/src/assets/icons/calendar.svg +1 -0
- package/src/assets/icons/check-circle.33c192.svg +1 -0
- package/src/assets/icons/check-circle.svg +1 -0
- package/src/assets/icons/check.d1d3db.svg +1 -0
- package/src/assets/icons/check.svg +1 -0
- package/src/assets/icons/chevron-down.svg +1 -0
- package/src/assets/icons/chevron-right.svg +1 -0
- package/src/assets/icons/chevron-up.svg +1 -0
- package/src/assets/icons/circle.svg +1 -0
- package/src/assets/icons/clock.svg +1 -0
- package/src/assets/icons/cmd.svg +1 -0
- package/src/assets/icons/code.svg +1 -0
- package/src/assets/icons/columns.svg +1 -0
- package/src/assets/icons/copy.svg +1 -0
- package/src/assets/icons/cpu.svg +1 -0
- package/src/assets/icons/dashboard.0c0c0d.svg +1 -0
- package/src/assets/icons/dashboard.svg +1 -0
- package/src/assets/icons/dollar.svg +1 -0
- package/src/assets/icons/download.svg +1 -0
- package/src/assets/icons/edit.svg +1 -0
- package/src/assets/icons/extensions.svg +1 -0
- package/src/assets/icons/external.svg +1 -0
- package/src/assets/icons/eye.svg +1 -0
- package/src/assets/icons/file-text.svg +1 -0
- package/src/assets/icons/file.svg +1 -0
- package/src/assets/icons/files.svg +1 -0
- package/src/assets/icons/filter.svg +1 -0
- package/src/assets/icons/folder.svg +1 -0
- package/src/assets/icons/gear.svg +1 -0
- package/src/assets/icons/git.svg +1 -0
- package/src/assets/icons/github.svg +1 -0
- package/src/assets/icons/globe.svg +1 -0
- package/src/assets/icons/grid-2x2.svg +1 -0
- package/src/assets/icons/hash.svg +1 -0
- package/src/assets/icons/heart.svg +1 -0
- package/src/assets/icons/help.svg +1 -0
- package/src/assets/icons/home.svg +1 -0
- package/src/assets/icons/image.svg +1 -0
- package/src/assets/icons/info-circle.svg +1 -0
- package/src/assets/icons/info-square.svg +1 -0
- package/src/assets/icons/info.387bff.svg +1 -0
- package/src/assets/icons/info.svg +1 -0
- package/src/assets/icons/key.svg +1 -0
- package/src/assets/icons/layers.svg +1 -0
- package/src/assets/icons/layout.svg +1 -0
- package/src/assets/icons/link.svg +1 -0
- package/src/assets/icons/list.svg +1 -0
- package/src/assets/icons/lock.svg +1 -0
- package/src/assets/icons/log-out.svg +1 -0
- package/src/assets/icons/logo.svg +1 -0
- package/src/assets/icons/mail.svg +1 -0
- package/src/assets/icons/menu.svg +1 -0
- package/src/assets/icons/message-circle.svg +1 -0
- package/src/assets/icons/message-plus.svg +1 -0
- package/src/assets/icons/message-square.svg +1 -0
- package/src/assets/icons/mic.svg +1 -0
- package/src/assets/icons/minus.svg +1 -0
- package/src/assets/icons/moon.svg +1 -0
- package/src/assets/icons/more-h.svg +1 -0
- package/src/assets/icons/mouse.svg +1 -0
- package/src/assets/icons/palette.svg +1 -0
- package/src/assets/icons/panel-bottom.svg +1 -0
- package/src/assets/icons/pause.svg +1 -0
- package/src/assets/icons/play.svg +1 -0
- package/src/assets/icons/plug.svg +1 -0
- package/src/assets/icons/plus.0c0c0d.svg +1 -0
- package/src/assets/icons/plus.svg +1 -0
- package/src/assets/icons/refresh.svg +1 -0
- package/src/assets/icons/ruler.svg +1 -0
- package/src/assets/icons/scroll-text.svg +1 -0
- package/src/assets/icons/search-menu.svg +1 -0
- package/src/assets/icons/search.svg +1 -0
- package/src/assets/icons/send.0c0c0d.svg +1 -0
- package/src/assets/icons/send.svg +1 -0
- package/src/assets/icons/settings.0c0c0d.svg +1 -0
- package/src/assets/icons/settings.svg +1 -0
- package/src/assets/icons/shield.svg +1 -0
- package/src/assets/icons/sidebar-left.svg +1 -0
- package/src/assets/icons/sidebar-right.svg +1 -0
- package/src/assets/icons/sliders.svg +1 -0
- package/src/assets/icons/sparkles.32f08c.svg +1 -0
- package/src/assets/icons/sparkles.svg +1 -0
- package/src/assets/icons/square.svg +1 -0
- package/src/assets/icons/star.svg +1 -0
- package/src/assets/icons/sun.svg +1 -0
- package/src/assets/icons/terminal.svg +1 -0
- package/src/assets/icons/trash.svg +1 -0
- package/src/assets/icons/trending-down.svg +1 -0
- package/src/assets/icons/trending-up.svg +1 -0
- package/src/assets/icons/type.svg +1 -0
- package/src/assets/icons/upload.svg +1 -0
- package/src/assets/icons/user-circle.svg +1 -0
- package/src/assets/icons/user.svg +1 -0
- package/src/assets/icons/users.svg +1 -0
- package/src/assets/icons/wrench.svg +1 -0
- package/src/assets/icons/x-circle.svg +1 -0
- package/src/assets/icons/x.svg +1 -0
- package/src/assets/icons/zap.0c0c0d.svg +1 -0
- package/src/assets/icons/zap.svg +1 -0
- package/src/avatar.ts +64 -0
- package/src/button.ts +88 -0
- package/src/card.ts +59 -0
- package/src/chat-composer.ts +156 -0
- package/src/dialog.ts +127 -0
- package/src/drawer.ts +131 -0
- package/src/editor-tabs.ts +184 -0
- package/src/file-tree.ts +214 -0
- package/src/forms.ts +270 -0
- package/src/icon.ts +118 -0
- package/src/icons.js +165 -0
- package/src/index.test.ts +213 -0
- package/src/index.ts +94 -0
- package/src/kbd.ts +72 -0
- package/src/menu.ts +119 -0
- package/src/nav-list.ts +157 -0
- package/src/overlay.test.ts +66 -0
- package/src/overlay.ts +205 -0
- package/src/page-header.ts +97 -0
- package/src/pagination.ts +163 -0
- package/src/second-batch.test.ts +205 -0
- package/src/stat-card.ts +92 -0
- package/src/status-bar.ts +108 -0
- package/src/styles/base.css +240 -0
- package/src/styles/components.css +1234 -0
- package/{dist → src/styles}/styles.css +2 -0
- package/src/styles/toast.css +57 -0
- package/src/styles/tokens.css +438 -0
- package/src/styles/vui.css +47 -0
- package/src/table.ts +191 -0
- package/src/tabs.ts +100 -0
- package/src/tag.ts +33 -0
- package/src/third-batch.test.ts +137 -0
- package/src/toast.test.ts +55 -0
- package/src/toast.ts +79 -0
- package/src/types.ts +25 -0
- package/src/utils.ts +221 -0
- package/src/workbench-titlebar.ts +137 -0
- package/dist/app-shell-dom.d.ts +0 -30
- package/dist/app-shell-dom.js +0 -913
- package/dist/app-shell.d.ts +0 -235
- package/dist/app-shell.js +0 -415
- package/dist/base.css +0 -51
- package/dist/calendar-picker.d.ts +0 -45
- package/dist/calendar-picker.js +0 -217
- package/dist/calendar.d.ts +0 -95
- package/dist/calendar.js +0 -194
- package/dist/complex-inputs.d.ts +0 -300
- package/dist/complex-inputs.js +0 -866
- package/dist/components.css +0 -4756
- package/dist/data-display.d.ts +0 -1094
- package/dist/data-display.js +0 -2341
- package/dist/dialog.d.ts +0 -26
- package/dist/dialog.js +0 -94
- package/dist/feedback.d.ts +0 -88
- package/dist/feedback.js +0 -281
- package/dist/forms.d.ts +0 -378
- package/dist/forms.js +0 -1019
- package/dist/icons-config.d.ts +0 -25
- package/dist/icons-config.js +0 -14
- package/dist/icons.d.ts +0 -41
- package/dist/icons.js +0 -185
- package/dist/index.d.ts +0 -31
- package/dist/index.js +0 -40
- package/dist/locale/en-US.d.ts +0 -31
- package/dist/locale/en-US.js +0 -20
- package/dist/locale/zh-CN.d.ts +0 -31
- package/dist/locale/zh-CN.js +0 -20
- package/dist/navigation.d.ts +0 -609
- package/dist/navigation.js +0 -1707
- package/dist/overlay.d.ts +0 -308
- package/dist/overlay.js +0 -974
- package/dist/page-state.d.ts +0 -23
- package/dist/page-state.js +0 -96
- package/dist/primitives.d.ts +0 -268
- package/dist/primitives.js +0 -764
- package/dist/theme-data.d.ts +0 -394
- package/dist/theme-data.js +0 -417
- package/dist/theme.d.ts +0 -97
- package/dist/theme.js +0 -301
- package/dist/tokens.css +0 -570
- package/dist/tree.d.ts +0 -144
- package/dist/tree.js +0 -475
- package/dist/virtual-list.d.ts +0 -190
- package/dist/virtual-list.js +0 -538
- package/dist/workbench.d.ts +0 -136
- package/dist/workbench.js +0 -156
package/dist/forms.js
DELETED
|
@@ -1,1019 +0,0 @@
|
|
|
1
|
-
/** @license MIT
|
|
2
|
-
* Copyright (c) 2026 vobsjs
|
|
3
|
-
* @vobs/ui
|
|
4
|
-
*/
|
|
5
|
-
import { createForm, createFormRenderer, createFormView, } from '@vobs/forms';
|
|
6
|
-
import { classNames, createChoicePrimitive, createInputPrimitive, createSelectPrimitive, createTextareaPrimitive, createUiDomPlan, } from './primitives.js';
|
|
7
|
-
export function createFormDrawerSubmissionContract(options) {
|
|
8
|
-
const mode = options.mode ?? 'edit';
|
|
9
|
-
const open = options.open === true;
|
|
10
|
-
const dirty = options.dirty === true;
|
|
11
|
-
const disabled = options.disabled === true;
|
|
12
|
-
const submitting = options.submitting === true || options.view.submitting.value;
|
|
13
|
-
const serverErrorCount = normalizeCount(options.serverErrorCount);
|
|
14
|
-
const errorCount = options.view.errorSummary.value.length;
|
|
15
|
-
const invalid = errorCount > 0;
|
|
16
|
-
const success = options.success === true && !invalid && !submitting;
|
|
17
|
-
const state = formDrawerSubmissionState({
|
|
18
|
-
dirty,
|
|
19
|
-
disabled,
|
|
20
|
-
invalid,
|
|
21
|
-
serverErrorCount,
|
|
22
|
-
submitting,
|
|
23
|
-
success,
|
|
24
|
-
});
|
|
25
|
-
const canSubmit = !disabled && !submitting && !invalid;
|
|
26
|
-
const focusTarget = readFirstErrorFocusTarget(options.view) ?? `#${options.drawerId}`;
|
|
27
|
-
return {
|
|
28
|
-
drawerId: options.drawerId,
|
|
29
|
-
mode,
|
|
30
|
-
state,
|
|
31
|
-
open,
|
|
32
|
-
dirty,
|
|
33
|
-
disabled,
|
|
34
|
-
submitting,
|
|
35
|
-
invalid,
|
|
36
|
-
success,
|
|
37
|
-
errorCount,
|
|
38
|
-
serverErrorCount,
|
|
39
|
-
canSubmit,
|
|
40
|
-
focusTarget,
|
|
41
|
-
attrs: {
|
|
42
|
-
'data-form-drawer-contract': 'true',
|
|
43
|
-
'data-drawer-id': options.drawerId,
|
|
44
|
-
'data-mode': mode,
|
|
45
|
-
'data-state': state,
|
|
46
|
-
'data-open': open ? 'true' : 'false',
|
|
47
|
-
'data-dirty': dirty ? 'true' : 'false',
|
|
48
|
-
'data-disabled': disabled ? 'true' : undefined,
|
|
49
|
-
'data-submitting': submitting ? 'true' : 'false',
|
|
50
|
-
'data-invalid': invalid ? 'true' : 'false',
|
|
51
|
-
'data-success': success ? 'true' : 'false',
|
|
52
|
-
'data-error-count': errorCount,
|
|
53
|
-
'data-server-error-count': serverErrorCount,
|
|
54
|
-
'data-can-submit': canSubmit ? 'true' : 'false',
|
|
55
|
-
'data-focus-target': focusTarget,
|
|
56
|
-
'aria-busy': submitting ? 'true' : undefined,
|
|
57
|
-
},
|
|
58
|
-
submitAttrs: {
|
|
59
|
-
type: 'submit',
|
|
60
|
-
disabled: !canSubmit,
|
|
61
|
-
'aria-disabled': canSubmit ? undefined : 'true',
|
|
62
|
-
'aria-busy': submitting ? 'true' : undefined,
|
|
63
|
-
'data-submit-intent': options.submitIntent ?? 'submit',
|
|
64
|
-
'data-state': submitting ? 'submitting' : canSubmit ? 'ready' : 'blocked',
|
|
65
|
-
'data-can-submit': canSubmit ? 'true' : 'false',
|
|
66
|
-
},
|
|
67
|
-
cancelAttrs: {
|
|
68
|
-
type: 'button',
|
|
69
|
-
'data-cancel-intent': options.cancelIntent ?? 'cancel',
|
|
70
|
-
'data-state': submitting ? 'blocked' : 'ready',
|
|
71
|
-
disabled: submitting ? true : undefined,
|
|
72
|
-
'aria-disabled': submitting ? 'true' : undefined,
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
export function createSliderPlans(options) {
|
|
77
|
-
const min = finiteOr(options.min, 0);
|
|
78
|
-
const max = Math.max(min, finiteOr(options.max, 100));
|
|
79
|
-
const step = Math.max(0, finiteOr(options.step, 1));
|
|
80
|
-
const values = sliderValues(options.value, min, max);
|
|
81
|
-
const mode = isSliderRangeValue(options.value) ? 'range' : 'single';
|
|
82
|
-
const thumbValues = mode === 'single' ? [values[0]] : values;
|
|
83
|
-
const startPercent = sliderPercent(values[0], min, max);
|
|
84
|
-
const endPercent = sliderPercent(values[1], min, max);
|
|
85
|
-
return {
|
|
86
|
-
root: createUiDomPlan('div', 'slider', {
|
|
87
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
88
|
-
...(options.readonly === undefined ? {} : { readonly: options.readonly }),
|
|
89
|
-
state: options.disabled === true ? 'disabled' : options.readonly === true ? 'readonly' : mode,
|
|
90
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
91
|
-
attrs: {
|
|
92
|
-
id: options.id,
|
|
93
|
-
role: 'group',
|
|
94
|
-
'aria-label': options.label,
|
|
95
|
-
...options.attrs,
|
|
96
|
-
'data-mode': mode,
|
|
97
|
-
'data-orientation': options.orientation ?? 'horizontal',
|
|
98
|
-
'data-min': min,
|
|
99
|
-
'data-max': max,
|
|
100
|
-
'data-step': step,
|
|
101
|
-
'data-value-start': values[0],
|
|
102
|
-
'data-value-end': values[1],
|
|
103
|
-
style: `--kui-slider-start: ${startPercent}%; --kui-slider-end: ${endPercent}%;${styleSuffix(options.attrs?.style)}`,
|
|
104
|
-
},
|
|
105
|
-
}),
|
|
106
|
-
track: createUiDomPlan('div', 'slider-track', { attrs: { 'aria-hidden': 'true' } }),
|
|
107
|
-
range: createUiDomPlan('span', 'slider-range', { attrs: { 'aria-hidden': 'true' } }),
|
|
108
|
-
thumbs: thumbValues.map((value, index) => createUiDomPlan('input', 'slider-thumb', {
|
|
109
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
110
|
-
...(options.readonly === undefined ? {} : { readonly: options.readonly }),
|
|
111
|
-
attrs: {
|
|
112
|
-
id: `${options.id}-${mode === 'range' ? (index === 0 ? 'start' : 'end') : 'thumb'}`,
|
|
113
|
-
type: 'range',
|
|
114
|
-
name: sliderThumbName(options.name, mode, index),
|
|
115
|
-
min,
|
|
116
|
-
max,
|
|
117
|
-
step,
|
|
118
|
-
value,
|
|
119
|
-
'aria-label': sliderThumbLabel(options.label, mode, index),
|
|
120
|
-
'aria-valuetext': sliderValueText(options.valueText, value, index),
|
|
121
|
-
},
|
|
122
|
-
})),
|
|
123
|
-
marks: (options.marks ?? []).map((mark) => createUiDomPlan('span', 'slider-mark', {
|
|
124
|
-
attrs: {
|
|
125
|
-
'aria-hidden': mark.label === undefined ? 'true' : undefined,
|
|
126
|
-
'data-value': clampNumber(mark.value, min, max),
|
|
127
|
-
'data-label': mark.label,
|
|
128
|
-
style: `--kui-slider-mark: ${sliderPercent(mark.value, min, max)}%;`,
|
|
129
|
-
},
|
|
130
|
-
})),
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
export function getNextSliderValue(value, key, options = {}) {
|
|
134
|
-
const min = finiteOr(options.min, 0);
|
|
135
|
-
const max = Math.max(min, finiteOr(options.max, 100));
|
|
136
|
-
const step = Math.max(1, finiteOr(options.step, 1));
|
|
137
|
-
if (key === 'Home')
|
|
138
|
-
return min;
|
|
139
|
-
if (key === 'End')
|
|
140
|
-
return max;
|
|
141
|
-
if (key === 'PageUp')
|
|
142
|
-
return clampNumber(value + step * 10, min, max);
|
|
143
|
-
if (key === 'PageDown')
|
|
144
|
-
return clampNumber(value - step * 10, min, max);
|
|
145
|
-
if (key === 'ArrowRight' || key === 'ArrowUp')
|
|
146
|
-
return clampNumber(value + step, min, max);
|
|
147
|
-
if (key === 'ArrowLeft' || key === 'ArrowDown')
|
|
148
|
-
return clampNumber(value - step, min, max);
|
|
149
|
-
return clampNumber(value, min, max);
|
|
150
|
-
}
|
|
151
|
-
export function createSliderTooltipFormatterStateContract(options) {
|
|
152
|
-
const min = finiteOr(options.min, 0);
|
|
153
|
-
const max = Math.max(min, finiteOr(options.max, 100));
|
|
154
|
-
const values = sliderValues(options.value, min, max);
|
|
155
|
-
const mode = isSliderRangeValue(options.value) ? 'range' : 'single';
|
|
156
|
-
const thumbValues = mode === 'single' ? [values[0]] : values;
|
|
157
|
-
const formattedValues = sliderContractFormattedValues(options.formattedValue, values);
|
|
158
|
-
const formatState = sliderHasFormattedValue(options.formattedValue)
|
|
159
|
-
? 'formatted'
|
|
160
|
-
: 'raw';
|
|
161
|
-
const tooltipVisibility = sliderContractTooltipVisibility(options.tooltipVisible, thumbValues.length);
|
|
162
|
-
const tooltipVisibleCount = tooltipVisibility.filter(Boolean).length;
|
|
163
|
-
const tooltipState = tooltipVisibleCount === 0
|
|
164
|
-
? 'hidden'
|
|
165
|
-
: tooltipVisibleCount === thumbValues.length
|
|
166
|
-
? 'visible'
|
|
167
|
-
: 'partial';
|
|
168
|
-
const filterSummary = options.filterSummary?.trim() ?? '';
|
|
169
|
-
const filterSummaryState = filterSummary === '' ? 'empty' : 'ready';
|
|
170
|
-
const thumbs = thumbValues.map((value, index) => {
|
|
171
|
-
const id = `${options.id}-${mode === 'range' ? (index === 0 ? 'start' : 'end') : 'thumb'}`;
|
|
172
|
-
const tooltipId = `${id}-tooltip`;
|
|
173
|
-
const formattedValue = formattedValues[index] ?? String(value);
|
|
174
|
-
const tooltipVisible = tooltipVisibility[index] === true;
|
|
175
|
-
return {
|
|
176
|
-
id,
|
|
177
|
-
value,
|
|
178
|
-
formattedValue,
|
|
179
|
-
tooltipVisible,
|
|
180
|
-
attrs: {
|
|
181
|
-
'data-tooltip-visible': tooltipVisible ? 'true' : undefined,
|
|
182
|
-
'data-tooltip-id': tooltipId,
|
|
183
|
-
'data-formatted-value': formattedValue,
|
|
184
|
-
'aria-valuetext': formattedValue,
|
|
185
|
-
'aria-describedby': tooltipVisible ? tooltipId : undefined,
|
|
186
|
-
},
|
|
187
|
-
tooltipAttrs: {
|
|
188
|
-
id: tooltipId,
|
|
189
|
-
role: 'tooltip',
|
|
190
|
-
hidden: tooltipVisible ? undefined : true,
|
|
191
|
-
'data-tooltip-visible': tooltipVisible ? 'true' : undefined,
|
|
192
|
-
'data-formatted-value': formattedValue,
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
return {
|
|
197
|
-
mode,
|
|
198
|
-
tooltipState,
|
|
199
|
-
formatState,
|
|
200
|
-
filterSummaryState,
|
|
201
|
-
tooltipVisibleCount,
|
|
202
|
-
formattedValues,
|
|
203
|
-
valueText: mode === 'single' ? formattedValues[0] : formattedValues,
|
|
204
|
-
filterSummary,
|
|
205
|
-
attrs: {
|
|
206
|
-
'data-slider-tooltip-formatter-contract': 'true',
|
|
207
|
-
'data-slider-tooltip-state': tooltipState,
|
|
208
|
-
'data-slider-format-state': formatState,
|
|
209
|
-
'data-slider-filter-summary-state': filterSummaryState,
|
|
210
|
-
'data-tooltip-visible-count': tooltipVisibleCount,
|
|
211
|
-
'data-formatted-value-start': formattedValues[0],
|
|
212
|
-
'data-formatted-value-end': formattedValues[1],
|
|
213
|
-
'data-filter-summary': filterSummary === '' ? undefined : filterSummary,
|
|
214
|
-
},
|
|
215
|
-
thumbs,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
export function createSegmentedPlans(options) {
|
|
219
|
-
const enabled = options.options.filter((option) => option.disabled !== true);
|
|
220
|
-
const value = enabled.some((option) => option.value === options.value)
|
|
221
|
-
? options.value
|
|
222
|
-
: enabled[0]?.value;
|
|
223
|
-
return {
|
|
224
|
-
root: createUiDomPlan('div', 'segmented', {
|
|
225
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
226
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
227
|
-
attrs: {
|
|
228
|
-
id: options.id,
|
|
229
|
-
role: 'radiogroup',
|
|
230
|
-
'aria-label': options.label,
|
|
231
|
-
...options.attrs,
|
|
232
|
-
'data-orientation': options.orientation ?? 'horizontal',
|
|
233
|
-
'data-value': value,
|
|
234
|
-
'data-count': options.options.length,
|
|
235
|
-
},
|
|
236
|
-
}),
|
|
237
|
-
items: options.options.map((option) => {
|
|
238
|
-
const checked = option.value === value;
|
|
239
|
-
const disabled = options.disabled === true || option.disabled === true;
|
|
240
|
-
return createUiDomPlan('button', 'segmented-item', {
|
|
241
|
-
disabled,
|
|
242
|
-
state: disabled ? 'disabled' : checked ? 'checked' : 'idle',
|
|
243
|
-
attrs: {
|
|
244
|
-
id: `${options.id}-${option.id}`,
|
|
245
|
-
type: 'button',
|
|
246
|
-
role: 'radio',
|
|
247
|
-
tabindex: checked && !disabled ? 0 : -1,
|
|
248
|
-
'aria-checked': checked ? 'true' : 'false',
|
|
249
|
-
'data-option-id': option.id,
|
|
250
|
-
'data-value': option.value,
|
|
251
|
-
'data-label': option.label,
|
|
252
|
-
'data-icon': option.icon === true ? 'true' : undefined,
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
}),
|
|
256
|
-
icons: options.options.map((option) => option.icon === true
|
|
257
|
-
? createUiDomPlan('span', 'segmented-icon', {
|
|
258
|
-
attrs: { 'aria-hidden': 'true', 'data-option-id': option.id },
|
|
259
|
-
})
|
|
260
|
-
: undefined),
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
export function getNextSegmentedValue(options, value, key, orientation = 'horizontal') {
|
|
264
|
-
return getNextSelectionValue(options, value, key, orientation);
|
|
265
|
-
}
|
|
266
|
-
export function createSegmentedResponsiveStateContract(options) {
|
|
267
|
-
const ids = options.options.map((option) => option.id);
|
|
268
|
-
const knownIds = new Set(ids);
|
|
269
|
-
const optionIconIds = options.options
|
|
270
|
-
.filter((option) => option.icon === true)
|
|
271
|
-
.map((option) => option.id);
|
|
272
|
-
const iconIds = normalizeKnownIds([...optionIconIds, ...(options.iconIds ?? [])], knownIds);
|
|
273
|
-
const overflowIds = normalizeKnownIds(options.overflowIds, knownIds);
|
|
274
|
-
const overflowSet = new Set(overflowIds);
|
|
275
|
-
const visibleInputIds = options.visibleIds === undefined ? ids : normalizeKnownIds(options.visibleIds, knownIds);
|
|
276
|
-
const visibleIds = visibleInputIds.filter((id) => !overflowSet.has(id));
|
|
277
|
-
const iconSet = new Set(iconIds);
|
|
278
|
-
const visibleSet = new Set(visibleIds);
|
|
279
|
-
const wrapState = options.wrapped === true ? 'wrapped' : 'nowrap';
|
|
280
|
-
const overflowState = options.options.length === 0 ? 'empty' : overflowIds.length > 0 ? 'overflow' : 'expanded';
|
|
281
|
-
const items = options.options.map((option) => {
|
|
282
|
-
const icon = iconSet.has(option.id);
|
|
283
|
-
const overflow = overflowSet.has(option.id);
|
|
284
|
-
const visible = visibleSet.has(option.id);
|
|
285
|
-
return {
|
|
286
|
-
id: option.id,
|
|
287
|
-
option: icon === option.icon ? option : { ...option, icon },
|
|
288
|
-
icon,
|
|
289
|
-
visible,
|
|
290
|
-
overflow,
|
|
291
|
-
attrs: {
|
|
292
|
-
'data-option-id': option.id,
|
|
293
|
-
'data-icon': icon ? 'true' : undefined,
|
|
294
|
-
'data-visible': visible ? 'true' : undefined,
|
|
295
|
-
'data-overflow': overflow ? 'true' : undefined,
|
|
296
|
-
hidden: visible ? undefined : true,
|
|
297
|
-
'aria-hidden': visible ? undefined : 'true',
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
});
|
|
301
|
-
return {
|
|
302
|
-
overflowState,
|
|
303
|
-
wrapState,
|
|
304
|
-
itemCount: options.options.length,
|
|
305
|
-
iconCount: iconIds.length,
|
|
306
|
-
visibleCount: visibleIds.length,
|
|
307
|
-
overflowCount: overflowIds.length,
|
|
308
|
-
iconIds,
|
|
309
|
-
visibleIds,
|
|
310
|
-
overflowIds,
|
|
311
|
-
attrs: {
|
|
312
|
-
'data-segmented-responsive-state-contract': 'true',
|
|
313
|
-
'data-segmented-overflow-state': overflowState,
|
|
314
|
-
'data-segmented-wrap-state': wrapState,
|
|
315
|
-
'data-icon-count': iconIds.length,
|
|
316
|
-
'data-visible-count': visibleIds.length,
|
|
317
|
-
'data-overflow-count': overflowIds.length,
|
|
318
|
-
'data-icon-ids': iconIds.join(','),
|
|
319
|
-
'data-visible-ids': visibleIds.join(','),
|
|
320
|
-
'data-overflow-ids': overflowIds.join(','),
|
|
321
|
-
},
|
|
322
|
-
items,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
export function createRadioGroupPlans(options) {
|
|
326
|
-
return createChoiceGroupPlans('radio', options);
|
|
327
|
-
}
|
|
328
|
-
export function createCheckboxGroupPlans(options) {
|
|
329
|
-
return createChoiceGroupPlans('checkbox', options);
|
|
330
|
-
}
|
|
331
|
-
export function getNextRadioGroupValue(options, value, key, orientation = 'vertical') {
|
|
332
|
-
return getNextSelectionValue(options, value, key, orientation);
|
|
333
|
-
}
|
|
334
|
-
export function toggleCheckboxGroupValue(values, value) {
|
|
335
|
-
const next = new Set(values);
|
|
336
|
-
if (next.has(value)) {
|
|
337
|
-
next.delete(value);
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
next.add(value);
|
|
341
|
-
}
|
|
342
|
-
return next;
|
|
343
|
-
}
|
|
344
|
-
export function createChoiceGroupCardOptionStateContract(options) {
|
|
345
|
-
const ids = options.options.map((option) => option.id);
|
|
346
|
-
const knownIds = new Set(ids);
|
|
347
|
-
const selectedValues = options.type === 'radio'
|
|
348
|
-
? new Set(options.value === undefined ? [] : [options.value])
|
|
349
|
-
: valueSet(options.values);
|
|
350
|
-
const selectedIds = options.options
|
|
351
|
-
.filter((option) => selectedValues.has(option.value))
|
|
352
|
-
.map((option) => option.id);
|
|
353
|
-
const disabledIds = options.options
|
|
354
|
-
.filter((option) => option.disabled === true)
|
|
355
|
-
.map((option) => option.id);
|
|
356
|
-
const selectedSet = new Set(selectedIds);
|
|
357
|
-
const disabledSet = new Set(disabledIds);
|
|
358
|
-
const cardIds = normalizeKnownIds(options.cardIds, knownIds);
|
|
359
|
-
const selectedEmphasisIds = normalizeKnownIds(options.selectedEmphasisIds ?? selectedIds, knownIds).filter((id) => selectedSet.has(id));
|
|
360
|
-
const disabledEmphasisIds = normalizeKnownIds(options.disabledEmphasisIds ?? disabledIds, knownIds).filter((id) => disabledSet.has(id));
|
|
361
|
-
const descriptionIds = normalizeKnownIds(options.descriptionIds ??
|
|
362
|
-
options.options
|
|
363
|
-
.filter((option) => option.description !== undefined)
|
|
364
|
-
.map((option) => option.id), knownIds);
|
|
365
|
-
const optionById = new Map(options.options.map((option) => [option.id, option]));
|
|
366
|
-
const filteredDescriptionIds = descriptionIds.filter((id) => optionById.get(id)?.description !== undefined);
|
|
367
|
-
const actionIds = normalizeKnownIds(options.actionIds, knownIds);
|
|
368
|
-
const actionSet = new Set(actionIds);
|
|
369
|
-
const actionDisabledIds = normalizeKnownIds(options.actionDisabledIds, knownIds).filter((id) => actionSet.has(id));
|
|
370
|
-
const cardSet = new Set(cardIds);
|
|
371
|
-
const selectedEmphasisSet = new Set(selectedEmphasisIds);
|
|
372
|
-
const disabledEmphasisSet = new Set(disabledEmphasisIds);
|
|
373
|
-
const descriptionSet = new Set(filteredDescriptionIds);
|
|
374
|
-
const actionDisabledSet = new Set(actionDisabledIds);
|
|
375
|
-
const state = options.options.length === 0
|
|
376
|
-
? 'empty'
|
|
377
|
-
: cardIds.length === 0
|
|
378
|
-
? 'plain'
|
|
379
|
-
: cardIds.length === options.options.length
|
|
380
|
-
? 'cards'
|
|
381
|
-
: 'partial';
|
|
382
|
-
const items = options.options.map((option) => {
|
|
383
|
-
const card = cardSet.has(option.id);
|
|
384
|
-
const selected = selectedSet.has(option.id);
|
|
385
|
-
const disabled = disabledSet.has(option.id);
|
|
386
|
-
const selectedEmphasis = selectedEmphasisSet.has(option.id);
|
|
387
|
-
const disabledEmphasis = disabledEmphasisSet.has(option.id);
|
|
388
|
-
const descriptionState = descriptionSet.has(option.id)
|
|
389
|
-
? 'visible'
|
|
390
|
-
: 'hidden';
|
|
391
|
-
const actionState = !actionSet.has(option.id)
|
|
392
|
-
? 'hidden'
|
|
393
|
-
: actionDisabledSet.has(option.id) || disabled
|
|
394
|
-
? 'disabled'
|
|
395
|
-
: 'ready';
|
|
396
|
-
const optionState = disabled
|
|
397
|
-
? 'disabled'
|
|
398
|
-
: selected
|
|
399
|
-
? 'selected'
|
|
400
|
-
: 'idle';
|
|
401
|
-
return {
|
|
402
|
-
id: option.id,
|
|
403
|
-
option,
|
|
404
|
-
state: optionState,
|
|
405
|
-
actionState,
|
|
406
|
-
descriptionState,
|
|
407
|
-
card,
|
|
408
|
-
selectedEmphasis,
|
|
409
|
-
disabledEmphasis,
|
|
410
|
-
attrs: {
|
|
411
|
-
'data-card-option': card ? 'true' : undefined,
|
|
412
|
-
'data-card-option-state': optionState,
|
|
413
|
-
'data-selected-emphasis': selectedEmphasis ? 'true' : undefined,
|
|
414
|
-
'data-disabled-emphasis': disabledEmphasis ? 'true' : undefined,
|
|
415
|
-
'data-description-state': descriptionState,
|
|
416
|
-
'data-action-state': actionState,
|
|
417
|
-
'aria-disabled': disabled ? 'true' : undefined,
|
|
418
|
-
},
|
|
419
|
-
actionAttrs: {
|
|
420
|
-
type: 'button',
|
|
421
|
-
'data-option-id': option.id,
|
|
422
|
-
'data-action-state': actionState,
|
|
423
|
-
hidden: actionState === 'hidden' ? true : undefined,
|
|
424
|
-
disabled: actionState === 'disabled' ? true : undefined,
|
|
425
|
-
'aria-disabled': actionState === 'disabled' ? 'true' : undefined,
|
|
426
|
-
},
|
|
427
|
-
};
|
|
428
|
-
});
|
|
429
|
-
return {
|
|
430
|
-
type: options.type,
|
|
431
|
-
state,
|
|
432
|
-
optionCount: options.options.length,
|
|
433
|
-
cardCount: cardIds.length,
|
|
434
|
-
selectedEmphasisCount: selectedEmphasisIds.length,
|
|
435
|
-
disabledEmphasisCount: disabledEmphasisIds.length,
|
|
436
|
-
descriptionCount: filteredDescriptionIds.length,
|
|
437
|
-
actionCount: actionIds.length,
|
|
438
|
-
actionDisabledCount: actionDisabledIds.length,
|
|
439
|
-
cardIds,
|
|
440
|
-
selectedEmphasisIds,
|
|
441
|
-
disabledEmphasisIds,
|
|
442
|
-
descriptionIds: filteredDescriptionIds,
|
|
443
|
-
actionIds,
|
|
444
|
-
actionDisabledIds,
|
|
445
|
-
attrs: {
|
|
446
|
-
'data-choice-group-card-option-contract': 'true',
|
|
447
|
-
'data-choice-type': options.type,
|
|
448
|
-
'data-card-state': state,
|
|
449
|
-
'data-card-count': cardIds.length,
|
|
450
|
-
'data-selected-emphasis-count': selectedEmphasisIds.length,
|
|
451
|
-
'data-disabled-emphasis-count': disabledEmphasisIds.length,
|
|
452
|
-
'data-description-count': descriptionIds.length,
|
|
453
|
-
'data-action-count': actionIds.length,
|
|
454
|
-
'data-action-disabled-count': actionDisabledIds.length,
|
|
455
|
-
'data-card-ids': cardIds.join(','),
|
|
456
|
-
'data-selected-emphasis-ids': selectedEmphasisIds.join(','),
|
|
457
|
-
'data-disabled-emphasis-ids': disabledEmphasisIds.join(','),
|
|
458
|
-
'data-description-ids': filteredDescriptionIds.join(','),
|
|
459
|
-
'data-action-ids': actionIds.join(','),
|
|
460
|
-
'data-action-disabled-ids': actionDisabledIds.join(','),
|
|
461
|
-
},
|
|
462
|
-
items,
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
export function createChoiceGroupBulkSelectionContract(options) {
|
|
466
|
-
const disabled = options.disabled === true;
|
|
467
|
-
const disabledReason = options.disabledReason?.trim() ?? '';
|
|
468
|
-
const selectedValues = options.type === 'radio'
|
|
469
|
-
? new Set(options.value === undefined ? [] : [options.value])
|
|
470
|
-
: valueSet(options.values);
|
|
471
|
-
const indeterminateValues = valueSet(options.indeterminateValues);
|
|
472
|
-
const enabledOptions = options.options.filter((option) => option.disabled !== true);
|
|
473
|
-
const enabledValues = new Set(enabledOptions.map((option) => option.value));
|
|
474
|
-
const checkedOptions = options.options.filter((option) => selectedValues.has(option.value));
|
|
475
|
-
const indeterminateOptions = options.options.filter((option) => indeterminateValues.has(option.value));
|
|
476
|
-
const checkedIds = checkedOptions.map((option) => option.id);
|
|
477
|
-
const indeterminateIds = indeterminateOptions.map((option) => option.id);
|
|
478
|
-
const checkedEnabledCount = checkedOptions.filter((option) => enabledValues.has(option.value)).length;
|
|
479
|
-
const indeterminateEnabledCount = indeterminateOptions.filter((option) => enabledValues.has(option.value)).length;
|
|
480
|
-
const state = choiceGroupBulkSelectionState({
|
|
481
|
-
checkedCount: checkedEnabledCount,
|
|
482
|
-
disabled,
|
|
483
|
-
enabledCount: enabledOptions.length,
|
|
484
|
-
indeterminateCount: indeterminateEnabledCount,
|
|
485
|
-
totalCount: options.options.length,
|
|
486
|
-
});
|
|
487
|
-
const selectAllState = choiceGroupBulkSelectAllState({
|
|
488
|
-
state,
|
|
489
|
-
type: options.type,
|
|
490
|
-
});
|
|
491
|
-
const canSelectAll = options.type === 'checkbox' && !disabled && enabledOptions.length > 0;
|
|
492
|
-
const nextCheckedIds = canSelectAll && selectAllState !== 'checked' ? enabledOptions.map((option) => option.id) : [];
|
|
493
|
-
const attrs = {
|
|
494
|
-
'data-choice-group-bulk-selection-contract': 'true',
|
|
495
|
-
'data-choice-type': options.type,
|
|
496
|
-
'data-selection-state': state,
|
|
497
|
-
'data-select-all-state': selectAllState,
|
|
498
|
-
'data-total-count': options.options.length,
|
|
499
|
-
'data-enabled-count': enabledOptions.length,
|
|
500
|
-
'data-checked-count': checkedIds.length,
|
|
501
|
-
'data-indeterminate-count': indeterminateIds.length,
|
|
502
|
-
'data-checked-ids': checkedIds.join(','),
|
|
503
|
-
'data-indeterminate-ids': indeterminateIds.join(','),
|
|
504
|
-
'data-next-checked-ids': nextCheckedIds.join(','),
|
|
505
|
-
'data-disabled-reason': disabledReason === '' ? undefined : disabledReason,
|
|
506
|
-
'data-can-select-all': canSelectAll ? 'true' : undefined,
|
|
507
|
-
'aria-disabled': disabled ? 'true' : undefined,
|
|
508
|
-
};
|
|
509
|
-
return {
|
|
510
|
-
type: options.type,
|
|
511
|
-
state,
|
|
512
|
-
selectAllState,
|
|
513
|
-
totalCount: options.options.length,
|
|
514
|
-
enabledCount: enabledOptions.length,
|
|
515
|
-
checkedCount: checkedIds.length,
|
|
516
|
-
indeterminateCount: indeterminateIds.length,
|
|
517
|
-
checkedIds,
|
|
518
|
-
indeterminateIds,
|
|
519
|
-
nextCheckedIds,
|
|
520
|
-
disabledReason,
|
|
521
|
-
canSelectAll,
|
|
522
|
-
attrs,
|
|
523
|
-
selectAllAttrs: {
|
|
524
|
-
id: `${options.id}-select-all`,
|
|
525
|
-
type: 'checkbox',
|
|
526
|
-
role: 'checkbox',
|
|
527
|
-
'aria-label': options.selectAllLabel ?? 'Select all options',
|
|
528
|
-
'aria-checked': selectAllState === 'mixed' ? 'mixed' : selectAllState === 'checked' ? 'true' : 'false',
|
|
529
|
-
disabled: canSelectAll ? undefined : true,
|
|
530
|
-
'aria-disabled': canSelectAll ? undefined : 'true',
|
|
531
|
-
'data-select-all-state': selectAllState,
|
|
532
|
-
'data-next-checked-ids': nextCheckedIds.join(','),
|
|
533
|
-
},
|
|
534
|
-
optionContracts: options.options.map((option) => {
|
|
535
|
-
const checked = selectedValues.has(option.value);
|
|
536
|
-
const indeterminate = options.type === 'checkbox' && indeterminateValues.has(option.value);
|
|
537
|
-
const optionDisabled = disabled || option.disabled === true;
|
|
538
|
-
return {
|
|
539
|
-
id: option.id,
|
|
540
|
-
value: option.value,
|
|
541
|
-
checked,
|
|
542
|
-
indeterminate,
|
|
543
|
-
disabled: optionDisabled,
|
|
544
|
-
attrs: {
|
|
545
|
-
'data-option-id': option.id,
|
|
546
|
-
'data-value': option.value,
|
|
547
|
-
'data-checked': checked ? 'true' : undefined,
|
|
548
|
-
'data-indeterminate': indeterminate ? 'true' : undefined,
|
|
549
|
-
'data-disabled-reason': optionDisabled && disabledReason !== '' ? disabledReason : undefined,
|
|
550
|
-
'aria-checked': indeterminate ? 'mixed' : checked ? 'true' : 'false',
|
|
551
|
-
'aria-disabled': optionDisabled ? 'true' : undefined,
|
|
552
|
-
},
|
|
553
|
-
};
|
|
554
|
-
}),
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
export function createFormUi(schema, options) {
|
|
558
|
-
const form = createForm(schema, options.initialValues);
|
|
559
|
-
const renderer = createFormRenderer(form, options.renderer);
|
|
560
|
-
const view = createFormView(renderer, { id: options.id });
|
|
561
|
-
return createFormUiFromView(view, options);
|
|
562
|
-
}
|
|
563
|
-
export function createFormUiFromView(view, options) {
|
|
564
|
-
return {
|
|
565
|
-
form: view.renderer.form,
|
|
566
|
-
renderer: view.renderer,
|
|
567
|
-
view,
|
|
568
|
-
root: createFormRootPlan(view),
|
|
569
|
-
fields: view.items.map((item) => createFormFieldPlan(item, options.descriptions?.[item.name])),
|
|
570
|
-
errorSummary: createErrorSummaryPlan(view),
|
|
571
|
-
submit: (callback) => view.submit(callback),
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
export function applyFormServerErrors(view, errors) {
|
|
575
|
-
for (const item of view.items) {
|
|
576
|
-
const value = errors[item.name];
|
|
577
|
-
if (value === undefined)
|
|
578
|
-
continue;
|
|
579
|
-
item.errors.value = Array.isArray(value) ? value : [value];
|
|
580
|
-
item.touched.value = true;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
export function readFirstErrorFocusTarget(view) {
|
|
584
|
-
return view.errorSummary.value[0]?.href;
|
|
585
|
-
}
|
|
586
|
-
export function createFormRootPlan(view) {
|
|
587
|
-
return createUiDomPlan('form', 'form', {
|
|
588
|
-
loading: view.submitting.value,
|
|
589
|
-
state: view.submitting.value ? 'submitting' : 'idle',
|
|
590
|
-
...(view.submitting.value ? { className: 'kui-form--submitting' } : {}),
|
|
591
|
-
attrs: { id: view.id, novalidate: true },
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
export function createErrorSummaryPlan(view) {
|
|
595
|
-
const items = view.errorSummary.value;
|
|
596
|
-
return {
|
|
597
|
-
root: createUiDomPlan('div', 'error-summary', {
|
|
598
|
-
invalid: items.length > 0,
|
|
599
|
-
state: items.length > 0 ? 'invalid' : 'idle',
|
|
600
|
-
attrs: {
|
|
601
|
-
id: view.errorSummaryId,
|
|
602
|
-
role: 'alert',
|
|
603
|
-
'aria-labelledby': view.errorSummaryLabelId,
|
|
604
|
-
hidden: items.length === 0 ? true : undefined,
|
|
605
|
-
},
|
|
606
|
-
}),
|
|
607
|
-
title: createUiDomPlan('h2', 'error-summary-title', {
|
|
608
|
-
attrs: { id: view.errorSummaryLabelId },
|
|
609
|
-
}),
|
|
610
|
-
items,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
export function createFormFieldPlan(item, description) {
|
|
614
|
-
const descriptionId = description === undefined ? undefined : `${item.controlId}-description`;
|
|
615
|
-
const invalid = item.errors.value.length > 0;
|
|
616
|
-
const disabled = item.disabled.value;
|
|
617
|
-
const describedBy = joinIds(descriptionId, item.ariaDescribedBy.value);
|
|
618
|
-
return {
|
|
619
|
-
name: item.name,
|
|
620
|
-
label: item.labelText.value,
|
|
621
|
-
control: item.control,
|
|
622
|
-
root: createUiDomPlan('div', 'form-field', {
|
|
623
|
-
invalid,
|
|
624
|
-
disabled,
|
|
625
|
-
state: invalid ? 'invalid' : disabled ? 'disabled' : 'idle',
|
|
626
|
-
attrs: { id: item.id },
|
|
627
|
-
}),
|
|
628
|
-
labelPlan: createUiDomPlan('label', 'form-label', {
|
|
629
|
-
attrs: {
|
|
630
|
-
id: item.labelId,
|
|
631
|
-
for: item.control === 'custom' ? undefined : item.controlId,
|
|
632
|
-
},
|
|
633
|
-
}),
|
|
634
|
-
...(description === undefined
|
|
635
|
-
? {}
|
|
636
|
-
: {
|
|
637
|
-
descriptionText: description,
|
|
638
|
-
description: createUiDomPlan('p', 'form-description', {
|
|
639
|
-
attrs: { id: descriptionId },
|
|
640
|
-
}),
|
|
641
|
-
}),
|
|
642
|
-
controlPlan: createControlPlan(item, describedBy),
|
|
643
|
-
error: createUiDomPlan('p', 'form-error', {
|
|
644
|
-
invalid,
|
|
645
|
-
state: invalid ? 'invalid' : 'idle',
|
|
646
|
-
attrs: {
|
|
647
|
-
id: item.errorId,
|
|
648
|
-
role: invalid ? 'alert' : undefined,
|
|
649
|
-
hidden: invalid ? undefined : true,
|
|
650
|
-
},
|
|
651
|
-
}),
|
|
652
|
-
errorText: item.errorText.value,
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
function createControlPlan(item, describedBy) {
|
|
656
|
-
const common = {
|
|
657
|
-
name: item.name,
|
|
658
|
-
required: item.required.value,
|
|
659
|
-
disabled: item.disabled.value,
|
|
660
|
-
invalid: item.errors.value.length > 0,
|
|
661
|
-
attrs: controlAttrs(item, describedBy),
|
|
662
|
-
};
|
|
663
|
-
switch (item.control) {
|
|
664
|
-
case 'checkbox':
|
|
665
|
-
return createChoicePrimitive({
|
|
666
|
-
type: 'checkbox',
|
|
667
|
-
checked: item.model.value === true,
|
|
668
|
-
value: 'true',
|
|
669
|
-
...common,
|
|
670
|
-
});
|
|
671
|
-
case 'select':
|
|
672
|
-
// value carries the current selection (schema defaultValue / form model) so templates can render option selected
|
|
673
|
-
return createSelectPrimitive({
|
|
674
|
-
...common,
|
|
675
|
-
value: item.model.value,
|
|
676
|
-
});
|
|
677
|
-
case 'textarea':
|
|
678
|
-
return createTextareaPrimitive({
|
|
679
|
-
...common,
|
|
680
|
-
...textValueOption(item.model.value),
|
|
681
|
-
});
|
|
682
|
-
case 'custom':
|
|
683
|
-
return createUiDomPlan('div', 'custom-field', {
|
|
684
|
-
invalid: common.invalid,
|
|
685
|
-
disabled: common.disabled,
|
|
686
|
-
state: common.invalid ? 'invalid' : common.disabled ? 'disabled' : 'idle',
|
|
687
|
-
attrs: {
|
|
688
|
-
id: item.controlId,
|
|
689
|
-
role: 'group',
|
|
690
|
-
'aria-labelledby': item.labelId,
|
|
691
|
-
'aria-describedby': describedBy,
|
|
692
|
-
},
|
|
693
|
-
});
|
|
694
|
-
default:
|
|
695
|
-
return createInputPrimitive({
|
|
696
|
-
type: normalizeInputType(item.inputType),
|
|
697
|
-
...common,
|
|
698
|
-
...textValueOption(item.model.value),
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
function controlAttrs(item, describedBy) {
|
|
703
|
-
return {
|
|
704
|
-
id: item.controlId,
|
|
705
|
-
'aria-labelledby': item.labelId,
|
|
706
|
-
'aria-describedby': describedBy,
|
|
707
|
-
'aria-required': item.ariaRequired.value,
|
|
708
|
-
'aria-invalid': item.ariaInvalid.value,
|
|
709
|
-
'aria-disabled': item.ariaDisabled.value,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
function normalizeInputType(value) {
|
|
713
|
-
switch (value) {
|
|
714
|
-
case 'date':
|
|
715
|
-
case 'email':
|
|
716
|
-
case 'number':
|
|
717
|
-
case 'password':
|
|
718
|
-
case 'search':
|
|
719
|
-
case 'url':
|
|
720
|
-
return value;
|
|
721
|
-
default:
|
|
722
|
-
return 'text';
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
function textValueOption(value) {
|
|
726
|
-
return typeof value === 'number' || typeof value === 'string' ? { value } : {};
|
|
727
|
-
}
|
|
728
|
-
function joinIds(...ids) {
|
|
729
|
-
return classNames(...ids);
|
|
730
|
-
}
|
|
731
|
-
function createChoiceGroupPlans(type, options) {
|
|
732
|
-
const selectedValues = type === 'radio'
|
|
733
|
-
? new Set(options.value === undefined ? [] : [options.value])
|
|
734
|
-
: valueSet(options.values);
|
|
735
|
-
const indeterminateValues = valueSet(options.indeterminateValues);
|
|
736
|
-
return {
|
|
737
|
-
root: createUiDomPlan('fieldset', `${type}-group`, {
|
|
738
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
739
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
740
|
-
attrs: {
|
|
741
|
-
id: options.id,
|
|
742
|
-
...options.attrs,
|
|
743
|
-
'data-orientation': options.orientation ?? 'vertical',
|
|
744
|
-
'data-count': options.options.length,
|
|
745
|
-
'data-selected-count': selectedValues.size,
|
|
746
|
-
},
|
|
747
|
-
}),
|
|
748
|
-
legend: createUiDomPlan('legend', `${type}-group-legend`, {
|
|
749
|
-
attrs: { id: `${options.id}-legend`, 'data-label': options.legend },
|
|
750
|
-
}),
|
|
751
|
-
options: options.options.map((option) => {
|
|
752
|
-
const disabled = options.disabled === true || option.disabled === true;
|
|
753
|
-
const checked = selectedValues.has(option.value);
|
|
754
|
-
return createUiDomPlan('label', `${type}-group-option`, {
|
|
755
|
-
disabled,
|
|
756
|
-
state: disabled ? 'disabled' : checked ? 'checked' : 'idle',
|
|
757
|
-
attrs: {
|
|
758
|
-
id: `${options.id}-${option.id}-option`,
|
|
759
|
-
for: `${options.id}-${option.id}`,
|
|
760
|
-
'data-option-id': option.id,
|
|
761
|
-
'data-value': option.value,
|
|
762
|
-
'data-label': option.label,
|
|
763
|
-
'data-description': option.description,
|
|
764
|
-
'data-indeterminate': type === 'checkbox' && indeterminateValues.has(option.value) ? 'true' : undefined,
|
|
765
|
-
},
|
|
766
|
-
});
|
|
767
|
-
}),
|
|
768
|
-
inputs: options.options.map((option) => {
|
|
769
|
-
const disabled = options.disabled === true || option.disabled === true;
|
|
770
|
-
const checked = selectedValues.has(option.value);
|
|
771
|
-
return createChoicePrimitive({
|
|
772
|
-
type,
|
|
773
|
-
name: options.name,
|
|
774
|
-
value: option.value,
|
|
775
|
-
checked,
|
|
776
|
-
disabled,
|
|
777
|
-
attrs: {
|
|
778
|
-
id: `${options.id}-${option.id}`,
|
|
779
|
-
'aria-describedby': option.description === undefined ? undefined : `${options.id}-${option.id}-description`,
|
|
780
|
-
'aria-checked': type === 'checkbox' && indeterminateValues.has(option.value)
|
|
781
|
-
? 'mixed'
|
|
782
|
-
: checked
|
|
783
|
-
? 'true'
|
|
784
|
-
: 'false',
|
|
785
|
-
'data-option-id': option.id,
|
|
786
|
-
'data-label': option.label,
|
|
787
|
-
},
|
|
788
|
-
});
|
|
789
|
-
}),
|
|
790
|
-
descriptions: options.options.map((option) => option.description === undefined
|
|
791
|
-
? undefined
|
|
792
|
-
: createUiDomPlan('span', `${type}-group-description`, {
|
|
793
|
-
attrs: {
|
|
794
|
-
id: `${options.id}-${option.id}-description`,
|
|
795
|
-
'data-description': option.description,
|
|
796
|
-
},
|
|
797
|
-
})),
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
function sliderValues(value, min, max) {
|
|
801
|
-
if (isSliderRangeValue(value)) {
|
|
802
|
-
const start = clampNumber(value[0] ?? min, min, max);
|
|
803
|
-
const end = clampNumber(value[1] ?? max, min, max);
|
|
804
|
-
return start <= end ? [start, end] : [end, start];
|
|
805
|
-
}
|
|
806
|
-
const current = clampNumber(value, min, max);
|
|
807
|
-
return [current, current];
|
|
808
|
-
}
|
|
809
|
-
function sliderPercent(value, min, max) {
|
|
810
|
-
if (max <= min)
|
|
811
|
-
return 0;
|
|
812
|
-
return Math.round(((clampNumber(value, min, max) - min) / (max - min)) * 10000) / 100;
|
|
813
|
-
}
|
|
814
|
-
function sliderThumbName(name, mode, index) {
|
|
815
|
-
if (name === undefined)
|
|
816
|
-
return undefined;
|
|
817
|
-
if (mode === 'single')
|
|
818
|
-
return name;
|
|
819
|
-
return `${name}-${index === 0 ? 'start' : 'end'}`;
|
|
820
|
-
}
|
|
821
|
-
function sliderThumbLabel(label, mode, index) {
|
|
822
|
-
if (mode === 'single')
|
|
823
|
-
return label ?? 'Slider value';
|
|
824
|
-
return `${label ?? 'Slider'} ${index === 0 ? 'start' : 'end'}`;
|
|
825
|
-
}
|
|
826
|
-
function sliderValueText(valueText, value, index) {
|
|
827
|
-
if (isSliderValueTextRange(valueText))
|
|
828
|
-
return valueText[index];
|
|
829
|
-
return valueText ?? String(value);
|
|
830
|
-
}
|
|
831
|
-
function sliderContractFormattedValues(value, values) {
|
|
832
|
-
if (value !== undefined && typeof value !== 'string') {
|
|
833
|
-
return [value[0].trim() || String(values[0]), value[1].trim() || String(values[1])];
|
|
834
|
-
}
|
|
835
|
-
const formatted = typeof value === 'string' ? value.trim() : '';
|
|
836
|
-
return [formatted || String(values[0]), formatted || String(values[1])];
|
|
837
|
-
}
|
|
838
|
-
function sliderContractTooltipVisibility(value, count) {
|
|
839
|
-
if (value !== undefined && typeof value !== 'boolean')
|
|
840
|
-
return value.slice(0, count);
|
|
841
|
-
return Array.from({ length: count }, () => value === true);
|
|
842
|
-
}
|
|
843
|
-
function sliderHasFormattedValue(value) {
|
|
844
|
-
if (value === undefined)
|
|
845
|
-
return false;
|
|
846
|
-
return typeof value === 'string' ? value.trim() !== '' : value.some((item) => item.trim() !== '');
|
|
847
|
-
}
|
|
848
|
-
function isSliderRangeValue(value) {
|
|
849
|
-
return Array.isArray(value);
|
|
850
|
-
}
|
|
851
|
-
function isSliderValueTextRange(value) {
|
|
852
|
-
return Array.isArray(value);
|
|
853
|
-
}
|
|
854
|
-
function getNextSelectionValue(options, value, key, orientation) {
|
|
855
|
-
const enabled = options.filter((option) => option.disabled !== true);
|
|
856
|
-
if (enabled.length === 0)
|
|
857
|
-
return value;
|
|
858
|
-
const currentIndex = Math.max(0, enabled.findIndex((option) => option.value === value));
|
|
859
|
-
const previousKey = orientation === 'vertical' ? 'ArrowUp' : 'ArrowLeft';
|
|
860
|
-
const nextKey = orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight';
|
|
861
|
-
if (key === 'Home')
|
|
862
|
-
return enabled[0]?.value;
|
|
863
|
-
if (key === 'End')
|
|
864
|
-
return enabled.at(-1)?.value;
|
|
865
|
-
if (key === previousKey)
|
|
866
|
-
return enabled.at(currentIndex - 1)?.value ?? enabled.at(-1)?.value;
|
|
867
|
-
if (key === nextKey)
|
|
868
|
-
return enabled[(currentIndex + 1) % enabled.length]?.value;
|
|
869
|
-
return value;
|
|
870
|
-
}
|
|
871
|
-
function valueSet(value) {
|
|
872
|
-
if (value === undefined)
|
|
873
|
-
return new Set();
|
|
874
|
-
return value instanceof Set ? value : new Set(value);
|
|
875
|
-
}
|
|
876
|
-
function normalizeKnownIds(value, knownIds) {
|
|
877
|
-
if (value === undefined)
|
|
878
|
-
return [];
|
|
879
|
-
const result = [];
|
|
880
|
-
const seen = new Set();
|
|
881
|
-
for (const id of value) {
|
|
882
|
-
if (!knownIds.has(id) || seen.has(id))
|
|
883
|
-
continue;
|
|
884
|
-
seen.add(id);
|
|
885
|
-
result.push(id);
|
|
886
|
-
}
|
|
887
|
-
return result;
|
|
888
|
-
}
|
|
889
|
-
function choiceGroupBulkSelectionState(options) {
|
|
890
|
-
if (options.disabled)
|
|
891
|
-
return 'disabled';
|
|
892
|
-
if (options.totalCount === 0)
|
|
893
|
-
return 'empty';
|
|
894
|
-
if (options.enabledCount > 0 &&
|
|
895
|
-
options.checkedCount >= options.enabledCount &&
|
|
896
|
-
options.indeterminateCount === 0) {
|
|
897
|
-
return 'all';
|
|
898
|
-
}
|
|
899
|
-
if (options.checkedCount > 0 || options.indeterminateCount > 0)
|
|
900
|
-
return 'partial';
|
|
901
|
-
return 'none';
|
|
902
|
-
}
|
|
903
|
-
function choiceGroupBulkSelectAllState(options) {
|
|
904
|
-
if (options.type === 'radio' || options.state === 'disabled' || options.state === 'empty') {
|
|
905
|
-
return 'disabled';
|
|
906
|
-
}
|
|
907
|
-
if (options.state === 'all')
|
|
908
|
-
return 'checked';
|
|
909
|
-
if (options.state === 'partial')
|
|
910
|
-
return 'mixed';
|
|
911
|
-
return 'unchecked';
|
|
912
|
-
}
|
|
913
|
-
function finiteOr(value, fallback) {
|
|
914
|
-
return value === undefined || !Number.isFinite(value) ? fallback : value;
|
|
915
|
-
}
|
|
916
|
-
function normalizeCount(value) {
|
|
917
|
-
if (value === undefined || !Number.isFinite(value))
|
|
918
|
-
return 0;
|
|
919
|
-
return Math.max(0, Math.trunc(value));
|
|
920
|
-
}
|
|
921
|
-
function formDrawerSubmissionState(options) {
|
|
922
|
-
if (options.submitting)
|
|
923
|
-
return 'submitting';
|
|
924
|
-
if (options.disabled)
|
|
925
|
-
return 'disabled';
|
|
926
|
-
if (options.serverErrorCount > 0)
|
|
927
|
-
return 'server-error';
|
|
928
|
-
if (options.invalid)
|
|
929
|
-
return 'invalid';
|
|
930
|
-
if (options.success)
|
|
931
|
-
return 'success';
|
|
932
|
-
if (options.dirty)
|
|
933
|
-
return 'dirty';
|
|
934
|
-
return 'idle';
|
|
935
|
-
}
|
|
936
|
-
function clampNumber(value, min, max) {
|
|
937
|
-
if (!Number.isFinite(value))
|
|
938
|
-
return min;
|
|
939
|
-
return Math.min(Math.max(value, min), max);
|
|
940
|
-
}
|
|
941
|
-
function styleSuffix(value) {
|
|
942
|
-
if (typeof value !== 'string' || value.trim() === '')
|
|
943
|
-
return '';
|
|
944
|
-
return value.trim().endsWith(';') ? value.trim() : `${value.trim()};`;
|
|
945
|
-
}
|
|
946
|
-
export function createSettingRowPlans(options) {
|
|
947
|
-
const kind = options.control ?? 'select';
|
|
948
|
-
const control = createUiDomPlan('button', 'setting-row-control', {
|
|
949
|
-
state: options.danger === true ? 'danger' : kind === 'icon-button' ? 'icon' : 'idle',
|
|
950
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
951
|
-
attrs: {
|
|
952
|
-
id: `${options.id}-control`,
|
|
953
|
-
type: 'button',
|
|
954
|
-
...(kind === 'select' ? { 'data-kind': 'select' } : {}),
|
|
955
|
-
...(options.controlLabel === undefined ? {} : { 'data-label': options.controlLabel }),
|
|
956
|
-
},
|
|
957
|
-
});
|
|
958
|
-
return {
|
|
959
|
-
root: createUiDomPlan('div', 'setting-row', {
|
|
960
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
961
|
-
attrs: { id: `${options.id}-row`, ...options.attrs },
|
|
962
|
-
}),
|
|
963
|
-
main: createUiDomPlan('div', 'setting-row-main', {
|
|
964
|
-
attrs: { id: `${options.id}-main` },
|
|
965
|
-
}),
|
|
966
|
-
title: createUiDomPlan('span', 'setting-row-title', {
|
|
967
|
-
attrs: { id: `${options.id}-title`, 'data-label': options.title },
|
|
968
|
-
}),
|
|
969
|
-
description: createUiDomPlan('span', 'setting-row-description', {
|
|
970
|
-
attrs: {
|
|
971
|
-
id: `${options.id}-desc`,
|
|
972
|
-
...(options.description === undefined ? {} : { 'data-label': options.description }),
|
|
973
|
-
},
|
|
974
|
-
}),
|
|
975
|
-
control,
|
|
976
|
-
};
|
|
977
|
-
}
|
|
978
|
-
export function createSettingRowGroupPlans(options) {
|
|
979
|
-
return {
|
|
980
|
-
...createUiDomPlan('div', 'setting-row-group', {
|
|
981
|
-
attrs: { id: `${options.id}-group` },
|
|
982
|
-
}),
|
|
983
|
-
groupId: options.id,
|
|
984
|
-
groupLabel: createUiDomPlan('span', 'setting-row-group-label', {
|
|
985
|
-
attrs: { id: `${options.id}-group-label`, 'data-label': options.label },
|
|
986
|
-
}),
|
|
987
|
-
panel: createUiDomPlan('div', 'setting-row-panel', {
|
|
988
|
-
attrs: { id: `${options.id}-panel` },
|
|
989
|
-
}),
|
|
990
|
-
rows: options.rows.map((row) => createSettingRowPlans(row)),
|
|
991
|
-
};
|
|
992
|
-
}
|
|
993
|
-
export function createRatePlans(options) {
|
|
994
|
-
const max = options.max ?? 5;
|
|
995
|
-
const value = Math.max(0, Math.min(max, options.value ?? 0));
|
|
996
|
-
const stars = [];
|
|
997
|
-
for (let index = 1; index <= max; index += 1) {
|
|
998
|
-
stars.push(createUiDomPlan('button', 'rate-star', {
|
|
999
|
-
state: index <= value ? 'active' : 'idle',
|
|
1000
|
-
...(options.disabled === undefined ? {} : { disabled: options.disabled }),
|
|
1001
|
-
attrs: {
|
|
1002
|
-
id: `${options.id}-star-${index}`,
|
|
1003
|
-
type: 'button',
|
|
1004
|
-
'data-index': String(index),
|
|
1005
|
-
'aria-label': `Rate ${index} of ${max}`,
|
|
1006
|
-
'aria-checked': index <= value ? 'true' : 'false',
|
|
1007
|
-
role: 'radio',
|
|
1008
|
-
},
|
|
1009
|
-
}));
|
|
1010
|
-
}
|
|
1011
|
-
return {
|
|
1012
|
-
root: createUiDomPlan('div', 'rate', {
|
|
1013
|
-
state: options.readonly === true ? 'readonly' : 'idle',
|
|
1014
|
-
...(options.className === undefined ? {} : { className: options.className }),
|
|
1015
|
-
attrs: { id: `${options.id}-rate`, role: 'radiogroup', ...options.attrs },
|
|
1016
|
-
}),
|
|
1017
|
-
stars,
|
|
1018
|
-
};
|
|
1019
|
-
}
|