@vobs/ui 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +60 -0
  3. package/package.json +52 -110
  4. package/src/activity-rail.ts +125 -0
  5. package/src/alert.ts +65 -0
  6. package/src/assets/icons/alert-circle.svg +1 -0
  7. package/src/assets/icons/alert-triangle.svg +1 -0
  8. package/src/assets/icons/alert.d27e24.svg +1 -0
  9. package/src/assets/icons/alert.svg +1 -0
  10. package/src/assets/icons/apple.svg +1 -0
  11. package/src/assets/icons/arrow-down.svg +1 -0
  12. package/src/assets/icons/arrow-expand.svg +1 -0
  13. package/src/assets/icons/arrow-left.svg +1 -0
  14. package/src/assets/icons/arrow-minimize.svg +1 -0
  15. package/src/assets/icons/arrow-right-to-line.svg +1 -0
  16. package/src/assets/icons/arrow-right.svg +1 -0
  17. package/src/assets/icons/arrow-up-right.svg +1 -0
  18. package/src/assets/icons/arrow-up.0c0c0d.svg +1 -0
  19. package/src/assets/icons/arrow-up.svg +1 -0
  20. package/src/assets/icons/at.svg +1 -0
  21. package/src/assets/icons/atom.0c0c0d.svg +1 -0
  22. package/src/assets/icons/atom.svg +1 -0
  23. package/src/assets/icons/bar.svg +1 -0
  24. package/src/assets/icons/bell.svg +1 -0
  25. package/src/assets/icons/box.svg +1 -0
  26. package/src/assets/icons/bug.svg +1 -0
  27. package/src/assets/icons/calendar.svg +1 -0
  28. package/src/assets/icons/check-circle.33c192.svg +1 -0
  29. package/src/assets/icons/check-circle.svg +1 -0
  30. package/src/assets/icons/check.d1d3db.svg +1 -0
  31. package/src/assets/icons/check.svg +1 -0
  32. package/src/assets/icons/chevron-down.svg +1 -0
  33. package/src/assets/icons/chevron-right.svg +1 -0
  34. package/src/assets/icons/chevron-up.svg +1 -0
  35. package/src/assets/icons/circle.svg +1 -0
  36. package/src/assets/icons/clock.svg +1 -0
  37. package/src/assets/icons/cmd.svg +1 -0
  38. package/src/assets/icons/code.svg +1 -0
  39. package/src/assets/icons/columns.svg +1 -0
  40. package/src/assets/icons/copy.svg +1 -0
  41. package/src/assets/icons/cpu.svg +1 -0
  42. package/src/assets/icons/dashboard.0c0c0d.svg +1 -0
  43. package/src/assets/icons/dashboard.svg +1 -0
  44. package/src/assets/icons/dollar.svg +1 -0
  45. package/src/assets/icons/download.svg +1 -0
  46. package/src/assets/icons/edit.svg +1 -0
  47. package/src/assets/icons/extensions.svg +1 -0
  48. package/src/assets/icons/external.svg +1 -0
  49. package/src/assets/icons/eye.svg +1 -0
  50. package/src/assets/icons/file-text.svg +1 -0
  51. package/src/assets/icons/file.svg +1 -0
  52. package/src/assets/icons/files.svg +1 -0
  53. package/src/assets/icons/filter.svg +1 -0
  54. package/src/assets/icons/folder.svg +1 -0
  55. package/src/assets/icons/gear.svg +1 -0
  56. package/src/assets/icons/git.svg +1 -0
  57. package/src/assets/icons/github.svg +1 -0
  58. package/src/assets/icons/globe.svg +1 -0
  59. package/src/assets/icons/grid-2x2.svg +1 -0
  60. package/src/assets/icons/hash.svg +1 -0
  61. package/src/assets/icons/heart.svg +1 -0
  62. package/src/assets/icons/help.svg +1 -0
  63. package/src/assets/icons/home.svg +1 -0
  64. package/src/assets/icons/image.svg +1 -0
  65. package/src/assets/icons/info-circle.svg +1 -0
  66. package/src/assets/icons/info-square.svg +1 -0
  67. package/src/assets/icons/info.387bff.svg +1 -0
  68. package/src/assets/icons/info.svg +1 -0
  69. package/src/assets/icons/key.svg +1 -0
  70. package/src/assets/icons/layers.svg +1 -0
  71. package/src/assets/icons/layout.svg +1 -0
  72. package/src/assets/icons/link.svg +1 -0
  73. package/src/assets/icons/list.svg +1 -0
  74. package/src/assets/icons/lock.svg +1 -0
  75. package/src/assets/icons/log-out.svg +1 -0
  76. package/src/assets/icons/logo.svg +1 -0
  77. package/src/assets/icons/mail.svg +1 -0
  78. package/src/assets/icons/menu.svg +1 -0
  79. package/src/assets/icons/message-circle.svg +1 -0
  80. package/src/assets/icons/message-plus.svg +1 -0
  81. package/src/assets/icons/message-square.svg +1 -0
  82. package/src/assets/icons/mic.svg +1 -0
  83. package/src/assets/icons/minus.svg +1 -0
  84. package/src/assets/icons/moon.svg +1 -0
  85. package/src/assets/icons/more-h.svg +1 -0
  86. package/src/assets/icons/mouse.svg +1 -0
  87. package/src/assets/icons/palette.svg +1 -0
  88. package/src/assets/icons/panel-bottom.svg +1 -0
  89. package/src/assets/icons/pause.svg +1 -0
  90. package/src/assets/icons/play.svg +1 -0
  91. package/src/assets/icons/plug.svg +1 -0
  92. package/src/assets/icons/plus.0c0c0d.svg +1 -0
  93. package/src/assets/icons/plus.svg +1 -0
  94. package/src/assets/icons/refresh.svg +1 -0
  95. package/src/assets/icons/ruler.svg +1 -0
  96. package/src/assets/icons/scroll-text.svg +1 -0
  97. package/src/assets/icons/search-menu.svg +1 -0
  98. package/src/assets/icons/search.svg +1 -0
  99. package/src/assets/icons/send.0c0c0d.svg +1 -0
  100. package/src/assets/icons/send.svg +1 -0
  101. package/src/assets/icons/settings.0c0c0d.svg +1 -0
  102. package/src/assets/icons/settings.svg +1 -0
  103. package/src/assets/icons/shield.svg +1 -0
  104. package/src/assets/icons/sidebar-left.svg +1 -0
  105. package/src/assets/icons/sidebar-right.svg +1 -0
  106. package/src/assets/icons/sliders.svg +1 -0
  107. package/src/assets/icons/sparkles.32f08c.svg +1 -0
  108. package/src/assets/icons/sparkles.svg +1 -0
  109. package/src/assets/icons/square.svg +1 -0
  110. package/src/assets/icons/star.svg +1 -0
  111. package/src/assets/icons/sun.svg +1 -0
  112. package/src/assets/icons/terminal.svg +1 -0
  113. package/src/assets/icons/trash.svg +1 -0
  114. package/src/assets/icons/trending-down.svg +1 -0
  115. package/src/assets/icons/trending-up.svg +1 -0
  116. package/src/assets/icons/type.svg +1 -0
  117. package/src/assets/icons/upload.svg +1 -0
  118. package/src/assets/icons/user-circle.svg +1 -0
  119. package/src/assets/icons/user.svg +1 -0
  120. package/src/assets/icons/users.svg +1 -0
  121. package/src/assets/icons/wrench.svg +1 -0
  122. package/src/assets/icons/x-circle.svg +1 -0
  123. package/src/assets/icons/x.svg +1 -0
  124. package/src/assets/icons/zap.0c0c0d.svg +1 -0
  125. package/src/assets/icons/zap.svg +1 -0
  126. package/src/avatar.ts +64 -0
  127. package/src/button.ts +88 -0
  128. package/src/card.ts +59 -0
  129. package/src/chat-composer.ts +156 -0
  130. package/src/dialog.ts +127 -0
  131. package/src/drawer.ts +131 -0
  132. package/src/editor-tabs.ts +184 -0
  133. package/src/file-tree.ts +214 -0
  134. package/src/forms.ts +270 -0
  135. package/src/icon.ts +117 -0
  136. package/src/icons.js +165 -0
  137. package/src/index.test.ts +213 -0
  138. package/src/index.ts +94 -0
  139. package/src/kbd.ts +72 -0
  140. package/src/menu.ts +119 -0
  141. package/src/nav-list.ts +157 -0
  142. package/src/overlay.test.ts +66 -0
  143. package/src/overlay.ts +205 -0
  144. package/src/page-header.ts +97 -0
  145. package/src/pagination.ts +163 -0
  146. package/src/second-batch.test.ts +205 -0
  147. package/src/stat-card.ts +92 -0
  148. package/src/status-bar.ts +108 -0
  149. package/src/styles/base.css +240 -0
  150. package/src/styles/components.css +1234 -0
  151. package/{dist → src/styles}/styles.css +2 -0
  152. package/src/styles/toast.css +57 -0
  153. package/src/styles/tokens.css +438 -0
  154. package/src/styles/vui.css +47 -0
  155. package/src/table.ts +191 -0
  156. package/src/tabs.ts +100 -0
  157. package/src/tag.ts +33 -0
  158. package/src/third-batch.test.ts +137 -0
  159. package/src/toast.test.ts +55 -0
  160. package/src/toast.ts +79 -0
  161. package/src/types.ts +25 -0
  162. package/src/utils.ts +221 -0
  163. package/src/workbench-titlebar.ts +137 -0
  164. package/dist/app-shell-dom.d.ts +0 -30
  165. package/dist/app-shell-dom.js +0 -913
  166. package/dist/app-shell.d.ts +0 -235
  167. package/dist/app-shell.js +0 -415
  168. package/dist/base.css +0 -51
  169. package/dist/calendar-picker.d.ts +0 -45
  170. package/dist/calendar-picker.js +0 -217
  171. package/dist/calendar.d.ts +0 -95
  172. package/dist/calendar.js +0 -194
  173. package/dist/complex-inputs.d.ts +0 -300
  174. package/dist/complex-inputs.js +0 -866
  175. package/dist/components.css +0 -4756
  176. package/dist/data-display.d.ts +0 -1094
  177. package/dist/data-display.js +0 -2341
  178. package/dist/dialog.d.ts +0 -26
  179. package/dist/dialog.js +0 -94
  180. package/dist/feedback.d.ts +0 -88
  181. package/dist/feedback.js +0 -281
  182. package/dist/forms.d.ts +0 -378
  183. package/dist/forms.js +0 -1019
  184. package/dist/icons-config.d.ts +0 -25
  185. package/dist/icons-config.js +0 -14
  186. package/dist/icons.d.ts +0 -41
  187. package/dist/icons.js +0 -185
  188. package/dist/index.d.ts +0 -31
  189. package/dist/index.js +0 -40
  190. package/dist/locale/en-US.d.ts +0 -31
  191. package/dist/locale/en-US.js +0 -20
  192. package/dist/locale/zh-CN.d.ts +0 -31
  193. package/dist/locale/zh-CN.js +0 -20
  194. package/dist/navigation.d.ts +0 -609
  195. package/dist/navigation.js +0 -1707
  196. package/dist/overlay.d.ts +0 -308
  197. package/dist/overlay.js +0 -974
  198. package/dist/page-state.d.ts +0 -23
  199. package/dist/page-state.js +0 -96
  200. package/dist/primitives.d.ts +0 -268
  201. package/dist/primitives.js +0 -764
  202. package/dist/theme-data.d.ts +0 -394
  203. package/dist/theme-data.js +0 -417
  204. package/dist/theme.d.ts +0 -97
  205. package/dist/theme.js +0 -301
  206. package/dist/tokens.css +0 -570
  207. package/dist/tree.d.ts +0 -144
  208. package/dist/tree.js +0 -475
  209. package/dist/virtual-list.d.ts +0 -190
  210. package/dist/virtual-list.js +0 -538
  211. package/dist/workbench.d.ts +0 -136
  212. package/dist/workbench.js +0 -156
@@ -1,764 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/ui
4
- */
5
- export function createButtonPrimitive(options = {}) {
6
- const disabled = options.disabled === true || options.loading === true;
7
- return createUiDomPlan('button', 'button', {
8
- ...options,
9
- disabled,
10
- state: options.state ?? stateFromFlags({ ...options, disabled }),
11
- className: classNames(`kui-button--${options.variant ?? 'solid'}`, `kui-button--${options.size ?? 'md'}`, options.iconOnly === true ? 'kui-button--icon-only' : undefined, options.className),
12
- attrs: {
13
- type: options.type ?? 'button',
14
- ...options.attrs,
15
- ...(options.label === undefined ? {} : { 'aria-label': options.label }),
16
- },
17
- });
18
- }
19
- export function createIconPrimitive(options = {}) {
20
- const semantic = options.decorative === false || options.label !== undefined;
21
- return createUiDomPlan('span', 'icon', {
22
- ...options,
23
- state: options.state ?? 'idle',
24
- attrs: {
25
- ...options.attrs,
26
- ...(semantic ? { role: 'img', 'aria-label': options.label } : { 'aria-hidden': 'true' }),
27
- },
28
- });
29
- }
30
- export function createInputPrimitive(options = {}) {
31
- return createTextControlPrimitive('input', options);
32
- }
33
- export function createTextareaPrimitive(options = {}) {
34
- return createTextControlPrimitive('textarea', options);
35
- }
36
- export function createNumberInputPrimitive(options = {}) {
37
- return createTextControlPrimitive('input', { ...options, type: 'number' });
38
- }
39
- export function createNumberInputStepperFormattingContract(options) {
40
- const rawValue = options.value === undefined ? '' : String(options.value);
41
- const parsedValue = numberInputNumericValue(options.value);
42
- const parseError = rawValue.trim() !== '' && parsedValue === undefined
43
- ? (options.parseError?.trim() ?? 'Invalid number')
44
- : '';
45
- const min = finiteNumber(options.min);
46
- const max = finiteNumber(options.max);
47
- const step = finiteNumber(options.step) ?? 1;
48
- const clampedValue = parsedValue === undefined
49
- ? undefined
50
- : min !== undefined && parsedValue < min
51
- ? min
52
- : max !== undefined && parsedValue > max
53
- ? max
54
- : parsedValue;
55
- const clampReason = parsedValue === undefined
56
- ? 'none'
57
- : min !== undefined && parsedValue < min
58
- ? 'min'
59
- : max !== undefined && parsedValue > max
60
- ? 'max'
61
- : 'none';
62
- const displayValue = options.formattedValue !== undefined && options.formattedValue !== ''
63
- ? options.formattedValue
64
- : rawValue;
65
- const formatState = numberInputFormatState({
66
- displayValue,
67
- formattedValue: options.formattedValue,
68
- parseError,
69
- rawValue,
70
- });
71
- const disabled = options.disabled === true;
72
- const readonly = options.readonly === true;
73
- const canDecrement = parsedValue !== undefined &&
74
- !disabled &&
75
- !readonly &&
76
- parseError === '' &&
77
- (min === undefined || parsedValue > min);
78
- const canIncrement = parsedValue !== undefined &&
79
- !disabled &&
80
- !readonly &&
81
- parseError === '' &&
82
- (max === undefined || parsedValue < max);
83
- const stepperState = numberInputStepperState({
84
- canDecrement,
85
- canIncrement,
86
- disabled,
87
- readonly,
88
- numericValue: parsedValue,
89
- });
90
- return {
91
- stepperState,
92
- formatState,
93
- clampReason,
94
- rawValue,
95
- displayValue,
96
- numericValue: parsedValue,
97
- clampedValue,
98
- min,
99
- max,
100
- step,
101
- parseError,
102
- canDecrement,
103
- canIncrement,
104
- attrs: {
105
- 'data-number-input-stepper-format-contract': 'true',
106
- 'data-stepper-state': stepperState,
107
- 'data-format-state': formatState,
108
- 'data-clamp-reason': clampReason,
109
- 'data-control-id': options.id,
110
- 'data-raw-value': rawValue,
111
- 'data-display-value': displayValue,
112
- 'data-numeric-value': parsedValue,
113
- 'data-clamped-value': clampedValue,
114
- 'data-min': min,
115
- 'data-max': max,
116
- 'data-step': step,
117
- 'data-parse-error': parseError,
118
- 'data-can-decrement': canDecrement ? 'true' : undefined,
119
- 'data-can-increment': canIncrement ? 'true' : undefined,
120
- 'aria-disabled': disabled ? 'true' : undefined,
121
- 'aria-readonly': readonly ? 'true' : undefined,
122
- 'aria-invalid': parseError !== '' ? 'true' : undefined,
123
- },
124
- controlAttrs: {
125
- id: options.id,
126
- 'data-number-format-control': 'true',
127
- 'data-stepper-state': stepperState,
128
- 'data-format-state': formatState,
129
- 'data-clamp-reason': clampReason,
130
- 'data-display-value': displayValue,
131
- 'data-parse-error': parseError,
132
- min,
133
- max,
134
- step,
135
- 'aria-invalid': parseError !== '' ? 'true' : undefined,
136
- },
137
- decrementButtonAttrs: {
138
- type: 'button',
139
- 'aria-label': options.decrementLabel ?? 'Decrement value',
140
- 'data-stepper-action': 'decrement',
141
- 'data-can-step': canDecrement ? 'true' : undefined,
142
- disabled: canDecrement ? undefined : true,
143
- 'aria-disabled': canDecrement ? undefined : 'true',
144
- },
145
- incrementButtonAttrs: {
146
- type: 'button',
147
- 'aria-label': options.incrementLabel ?? 'Increment value',
148
- 'data-stepper-action': 'increment',
149
- 'data-can-step': canIncrement ? 'true' : undefined,
150
- disabled: canIncrement ? undefined : true,
151
- 'aria-disabled': canIncrement ? undefined : 'true',
152
- },
153
- };
154
- }
155
- export function createTextInputAffixStateContract(options) {
156
- const kind = options.kind ?? 'input';
157
- const value = options.value === undefined ? '' : String(options.value);
158
- const prefix = options.prefix?.trim() ?? '';
159
- const suffix = options.suffix?.trim() ?? '';
160
- const hasPrefix = prefix !== '';
161
- const hasSuffix = suffix !== '';
162
- const clearable = options.clearable === true;
163
- const disabled = options.disabled === true;
164
- const readonly = options.readonly === true;
165
- const loading = options.loading === true;
166
- const invalid = options.invalid === true;
167
- const canClear = clearable && value !== '' && !disabled && !readonly && !loading;
168
- const state = textInputAffixState({
169
- canClear,
170
- disabled,
171
- invalid,
172
- loading,
173
- readonly,
174
- value,
175
- });
176
- const describedBy = [
177
- options.describedBy,
178
- hasPrefix ? `${options.id}-prefix` : undefined,
179
- hasSuffix ? `${options.id}-suffix` : undefined,
180
- ]
181
- .filter((id) => id !== undefined && id !== '')
182
- .join(' ');
183
- return {
184
- state,
185
- kind,
186
- value,
187
- prefix,
188
- suffix,
189
- hasPrefix,
190
- hasSuffix,
191
- clearable,
192
- canClear,
193
- attrs: {
194
- 'data-text-input-affix-contract': 'true',
195
- 'data-affix-state': state,
196
- 'data-control-id': options.id,
197
- 'data-control-kind': kind,
198
- 'data-has-prefix': hasPrefix ? 'true' : undefined,
199
- 'data-has-suffix': hasSuffix ? 'true' : undefined,
200
- 'data-clearable': clearable ? 'true' : undefined,
201
- 'data-can-clear': canClear ? 'true' : undefined,
202
- 'data-value-empty': value === '' ? 'true' : undefined,
203
- 'data-prefix': prefix,
204
- 'data-suffix': suffix,
205
- 'aria-disabled': disabled ? 'true' : undefined,
206
- 'aria-readonly': readonly ? 'true' : undefined,
207
- 'aria-busy': loading ? 'true' : undefined,
208
- 'aria-invalid': invalid ? 'true' : undefined,
209
- },
210
- controlAttrs: {
211
- id: options.id,
212
- 'data-affix-control': 'true',
213
- 'data-affix-state': state,
214
- 'data-has-prefix': hasPrefix ? 'true' : undefined,
215
- 'data-has-suffix': hasSuffix ? 'true' : undefined,
216
- 'data-clearable': clearable ? 'true' : undefined,
217
- 'data-can-clear': canClear ? 'true' : undefined,
218
- 'aria-describedby': describedBy === '' ? undefined : describedBy,
219
- },
220
- prefixAttrs: {
221
- id: `${options.id}-prefix`,
222
- 'data-affix-part': 'prefix',
223
- 'data-label': prefix,
224
- 'aria-hidden': 'true',
225
- },
226
- suffixAttrs: {
227
- id: `${options.id}-suffix`,
228
- 'data-affix-part': 'suffix',
229
- 'data-label': suffix,
230
- 'aria-hidden': 'true',
231
- },
232
- clearButtonAttrs: {
233
- type: 'button',
234
- 'aria-label': options.clearLabel ?? 'Clear input',
235
- 'data-affix-part': 'clear',
236
- 'data-can-clear': canClear ? 'true' : undefined,
237
- disabled: canClear ? undefined : true,
238
- 'aria-disabled': canClear ? undefined : 'true',
239
- },
240
- };
241
- }
242
- export function createChoicePrimitive(options) {
243
- return createUiDomPlan('input', options.type, {
244
- ...options,
245
- state: options.state ?? (options.checked === true ? 'checked' : 'unchecked'),
246
- attrs: {
247
- type: options.type,
248
- name: options.name,
249
- value: options.value,
250
- checked: options.checked,
251
- required: options.required,
252
- ...options.attrs,
253
- },
254
- });
255
- }
256
- export function createSwitchPrimitive(options = {}) {
257
- return createUiDomPlan('button', 'switch', {
258
- ...options,
259
- state: options.state ?? (options.checked === true ? 'checked' : 'unchecked'),
260
- attrs: {
261
- type: 'button',
262
- role: 'switch',
263
- 'aria-checked': options.checked === true ? 'true' : 'false',
264
- ...options.attrs,
265
- ...(options.label === undefined ? {} : { 'aria-label': options.label }),
266
- },
267
- });
268
- }
269
- export function createSwitchAsyncConfirmationContract(options) {
270
- const checked = options.checked === true;
271
- const pendingChecked = options.pendingChecked;
272
- const loading = options.loading === true;
273
- const confirmRequired = options.confirmRequired === true;
274
- const disabled = options.disabled === true;
275
- const readonly = options.readonly === true;
276
- const disabledReason = options.disabledReason?.trim() ?? '';
277
- const rollback = options.rollback === true;
278
- const error = options.error?.trim() ?? '';
279
- const pending = pendingChecked !== undefined && pendingChecked !== checked;
280
- const canToggle = !disabled && !readonly && !loading;
281
- const state = switchAsyncConfirmationState({
282
- checked,
283
- confirmRequired,
284
- disabled,
285
- error,
286
- loading,
287
- pending,
288
- readonly,
289
- rollback,
290
- });
291
- const attrs = {
292
- 'data-switch-async-confirmation-contract': 'true',
293
- 'data-switch-state': state,
294
- 'data-checked': checked ? 'true' : 'false',
295
- 'data-pending-checked': pendingChecked === undefined ? undefined : String(pendingChecked),
296
- 'data-loading': loading ? 'true' : undefined,
297
- 'data-confirm-required': confirmRequired ? 'true' : undefined,
298
- 'data-disabled-reason': disabledReason === '' ? undefined : disabledReason,
299
- 'data-rollback': rollback ? 'true' : undefined,
300
- 'data-error': error === '' ? undefined : error,
301
- 'data-can-toggle': canToggle ? 'true' : undefined,
302
- 'aria-busy': loading ? 'true' : undefined,
303
- 'aria-disabled': disabled || readonly ? 'true' : undefined,
304
- };
305
- return {
306
- state,
307
- checked,
308
- pendingChecked,
309
- loading,
310
- confirmRequired,
311
- disabled,
312
- readonly,
313
- disabledReason,
314
- rollback,
315
- error,
316
- canToggle,
317
- attrs,
318
- controlAttrs: {
319
- id: options.id,
320
- role: 'switch',
321
- type: 'button',
322
- 'aria-checked': checked ? 'true' : 'false',
323
- 'aria-label': options.label ?? options.id,
324
- ...attrs,
325
- disabled: disabled ? true : undefined,
326
- },
327
- confirmButtonAttrs: {
328
- type: 'button',
329
- 'aria-label': options.confirmLabel ?? 'Confirm switch change',
330
- 'data-confirm-required': confirmRequired ? 'true' : undefined,
331
- 'data-pending-checked': pendingChecked === undefined ? undefined : String(pendingChecked),
332
- disabled: confirmRequired && canToggle ? undefined : true,
333
- 'aria-disabled': confirmRequired && canToggle ? undefined : 'true',
334
- },
335
- };
336
- }
337
- export function createSelectPrimitive(options = {}) {
338
- return createUiDomPlan('select', 'select', {
339
- ...options,
340
- state: options.state ?? stateFromFlags(options),
341
- attrs: {
342
- name: options.name,
343
- required: options.required,
344
- multiple: options.multiple === true ? true : undefined,
345
- size: options.size,
346
- value: options.value,
347
- 'data-multiple': options.multiple === true ? 'true' : undefined,
348
- 'data-size': options.size,
349
- ...options.attrs,
350
- },
351
- });
352
- }
353
- export function createSelectSearchMultiselectContract(options) {
354
- const query = options.query?.trim() ?? '';
355
- const knownIds = new Set(options.items.map((item) => item.id));
356
- const selectedInputIds = options.selectedIds ?? options.items.filter((item) => item.selected).map((item) => item.id);
357
- const selectedIds = selectUniqueKnownIds(selectedInputIds, knownIds);
358
- const selectedSet = new Set(selectedIds);
359
- const normalizedMaxSelected = options.maxSelected === undefined || !Number.isFinite(options.maxSelected)
360
- ? undefined
361
- : Math.max(0, Math.trunc(options.maxSelected));
362
- const filteredItems = options.items.filter((item) => selectItemMatchesQuery(item, query));
363
- const visibleIds = filteredItems.map((item) => item.id);
364
- const visibleSet = new Set(visibleIds);
365
- const maxed = normalizedMaxSelected !== undefined &&
366
- selectedIds.length >= normalizedMaxSelected &&
367
- normalizedMaxSelected > 0;
368
- const clearable = options.clearable === true;
369
- const disabled = options.disabled === true;
370
- const readonly = options.readonly === true;
371
- const loading = options.loading === true;
372
- const canClear = clearable && selectedIds.length > 0 && !disabled && !readonly && !loading;
373
- const filtered = query !== '' && visibleIds.length < options.items.length;
374
- const state = options.items.length === 0
375
- ? 'empty'
376
- : filtered
377
- ? 'filtered'
378
- : maxed
379
- ? 'maxed'
380
- : selectedIds.length > 1
381
- ? 'multiple'
382
- : 'ready';
383
- const items = options.items.map((item) => selectSearchMultiselectOptionContract(item, {
384
- matched: visibleSet.has(item.id),
385
- selected: selectedSet.has(item.id),
386
- }));
387
- const searchAttrs = {
388
- id: `${options.id}-search`,
389
- type: 'search',
390
- role: 'searchbox',
391
- value: query,
392
- placeholder: options.searchPlaceholder ?? 'Search options',
393
- 'aria-label': options.searchLabel ?? 'Search options',
394
- 'aria-controls': options.id,
395
- 'aria-busy': loading ? 'true' : undefined,
396
- 'data-search-query': query,
397
- 'data-search-result-count': visibleIds.length,
398
- 'data-search-filtered': filtered ? 'true' : undefined,
399
- };
400
- const selectAttrs = {
401
- id: options.id,
402
- name: options.id,
403
- multiple: true,
404
- size: Math.max(4, Math.min(filteredItems.length === 0 ? options.items.length : filteredItems.length, 8)),
405
- 'data-select-search-multiselect-contract': 'true',
406
- 'data-select-state': state,
407
- 'data-query': query,
408
- 'data-total-count': options.items.length,
409
- 'data-visible-count': visibleIds.length,
410
- 'data-selected-count': selectedIds.length,
411
- 'data-max-selected': normalizedMaxSelected,
412
- 'data-clearable': clearable ? 'true' : undefined,
413
- 'data-can-clear': canClear ? 'true' : undefined,
414
- 'data-selected-ids': selectedIds.join(','),
415
- 'data-visible-ids': visibleIds.join(','),
416
- 'data-filtered': filtered ? 'true' : undefined,
417
- 'data-maxed': maxed ? 'true' : undefined,
418
- 'aria-busy': loading ? 'true' : undefined,
419
- 'aria-disabled': disabled ? 'true' : undefined,
420
- 'aria-readonly': readonly ? 'true' : undefined,
421
- };
422
- return {
423
- state,
424
- query,
425
- totalCount: options.items.length,
426
- visibleCount: visibleIds.length,
427
- selectedCount: selectedIds.length,
428
- maxSelected: normalizedMaxSelected,
429
- clearable,
430
- canClear,
431
- filtered,
432
- maxed,
433
- selectedIds,
434
- visibleIds,
435
- items,
436
- attrs: {
437
- 'data-select-search-multiselect-contract': 'true',
438
- 'data-select-state': state,
439
- 'data-query': query,
440
- 'data-total-count': options.items.length,
441
- 'data-visible-count': visibleIds.length,
442
- 'data-selected-count': selectedIds.length,
443
- 'data-max-selected': normalizedMaxSelected,
444
- 'data-clearable': clearable ? 'true' : undefined,
445
- 'data-can-clear': canClear ? 'true' : undefined,
446
- 'data-selected-ids': selectedIds.join(','),
447
- 'data-visible-ids': visibleIds.join(','),
448
- 'data-filtered': filtered ? 'true' : undefined,
449
- 'data-maxed': maxed ? 'true' : undefined,
450
- 'aria-busy': loading ? 'true' : undefined,
451
- 'aria-disabled': disabled ? 'true' : undefined,
452
- 'aria-readonly': readonly ? 'true' : undefined,
453
- },
454
- searchAttrs,
455
- selectAttrs,
456
- clearButtonAttrs: {
457
- type: 'button',
458
- 'aria-label': options.clearLabel ?? 'Clear selection',
459
- 'data-clearable': clearable ? 'true' : undefined,
460
- 'data-can-clear': canClear ? 'true' : undefined,
461
- disabled: canClear ? undefined : true,
462
- 'aria-disabled': canClear ? undefined : 'true',
463
- },
464
- };
465
- }
466
- export function createSkeletonPrimitive(options = {}) {
467
- return createUiDomPlan('span', 'skeleton', {
468
- ...options,
469
- loading: true,
470
- state: options.state ?? 'loading',
471
- attrs: {
472
- 'aria-hidden': 'true',
473
- ...options.attrs,
474
- },
475
- });
476
- }
477
- export function createSpinPrimitive(options = {}) {
478
- return createUiDomPlan('span', 'spin', {
479
- ...options,
480
- loading: true,
481
- state: options.state ?? 'loading',
482
- attrs: {
483
- role: 'status',
484
- 'aria-label': options.label ?? 'Loading',
485
- ...options.attrs,
486
- },
487
- });
488
- }
489
- export function createDashboardLayoutPrimitive(options = {}) {
490
- return createLayoutPrimitive('div', 'dashboard-layout', {
491
- density: 'compact',
492
- ...options,
493
- });
494
- }
495
- export function createAuthLayoutPrimitive(options = {}) {
496
- return createLayoutPrimitive('main', 'auth-layout', options);
497
- }
498
- export function createPageFramePrimitive(options = {}) {
499
- return createLayoutPrimitive('main', 'page-frame', options);
500
- }
501
- export function createPageHeaderPrimitive(options = {}) {
502
- return createLayoutPrimitive('header', 'page-header', options);
503
- }
504
- export function createPageToolbarPrimitive(options = {}) {
505
- return createLayoutPrimitive('div', 'page-toolbar', {
506
- ...options,
507
- attrs: { role: 'toolbar', ...options.attrs },
508
- });
509
- }
510
- export function createPageBodyPrimitive(options = {}) {
511
- const mode = options.mode ?? 'contained';
512
- return createLayoutPrimitive('section', 'page-body', {
513
- ...options,
514
- attrs: { 'data-mode': mode, ...options.attrs },
515
- });
516
- }
517
- export function createAdaptiveGridPrimitive(options = {}) {
518
- return createLayoutPrimitive('div', 'adaptive-grid', {
519
- ...options,
520
- attrs: {
521
- style: `--kui-grid-min: ${options.minColumnWidth ?? '240px'};${styleSuffix(options.attrs?.style)}`,
522
- ...options.attrs,
523
- },
524
- });
525
- }
526
- export function createUiDomPlan(tag, component, options = {}) {
527
- const attrs = { ...options.attrs };
528
- setDefined(attrs, 'data-state', options.state ?? stateFromFlags(options));
529
- setDefined(attrs, 'data-density', options.density);
530
- setFlag(attrs, 'data-disabled', options.disabled);
531
- setFlag(attrs, 'data-readonly', options.readonly);
532
- setFlag(attrs, 'data-loading', options.loading);
533
- setFlag(attrs, 'data-invalid', options.invalid);
534
- setDefined(attrs, 'aria-disabled', options.disabled === true ? 'true' : undefined);
535
- setDefined(attrs, 'aria-readonly', options.readonly === true ? 'true' : undefined);
536
- setDefined(attrs, 'aria-busy', options.loading === true ? 'true' : undefined);
537
- setDefined(attrs, 'aria-invalid', options.invalid === true ? 'true' : undefined);
538
- setDefined(attrs, 'disabled', options.disabled === true ? true : undefined);
539
- setDefined(attrs, 'readonly', options.readonly === true ? true : undefined);
540
- return {
541
- tag,
542
- className: classNames(`kui-${component}`, options.density === undefined ? undefined : `kui-density-${options.density}`, options.className),
543
- attrs: removeUndefined(attrs),
544
- };
545
- }
546
- export function classNames(...values) {
547
- return values
548
- .filter((value) => typeof value === 'string' && value !== '')
549
- .join(' ');
550
- }
551
- function createTextControlPrimitive(tag, options) {
552
- return createUiDomPlan(tag, tag, {
553
- ...options,
554
- state: options.state ?? stateFromFlags(options),
555
- attrs: {
556
- type: tag === 'input' ? (options.type ?? 'text') : undefined,
557
- name: options.name,
558
- value: options.value,
559
- required: options.required,
560
- placeholder: options.placeholder,
561
- ...options.attrs,
562
- },
563
- });
564
- }
565
- function createLayoutPrimitive(tag, component, options) {
566
- return createUiDomPlan((options.as ?? tag), component, {
567
- ...options,
568
- state: options.state ?? 'idle',
569
- });
570
- }
571
- function stateFromFlags(options) {
572
- if (options.loading === true)
573
- return 'loading';
574
- if (options.disabled === true)
575
- return 'disabled';
576
- if (options.readonly === true)
577
- return 'readonly';
578
- if (options.invalid === true)
579
- return 'invalid';
580
- return 'idle';
581
- }
582
- function textInputAffixState(options) {
583
- if (options.loading)
584
- return 'loading';
585
- if (options.disabled)
586
- return 'disabled';
587
- if (options.readonly)
588
- return 'readonly';
589
- if (options.invalid)
590
- return 'invalid';
591
- if (options.canClear)
592
- return 'clearable';
593
- if (options.value === '')
594
- return 'empty';
595
- return 'ready';
596
- }
597
- function numberInputNumericValue(value) {
598
- if (value === undefined || value === '')
599
- return undefined;
600
- if (typeof value === 'number')
601
- return Number.isFinite(value) ? value : undefined;
602
- const trimmed = value.trim();
603
- if (trimmed === '')
604
- return undefined;
605
- const parsed = Number(trimmed);
606
- return Number.isFinite(parsed) ? parsed : undefined;
607
- }
608
- function finiteNumber(value) {
609
- return value === undefined || !Number.isFinite(value) ? undefined : value;
610
- }
611
- function numberInputFormatState(options) {
612
- if (options.parseError !== '')
613
- return 'invalid';
614
- if (options.rawValue.trim() === '')
615
- return 'empty';
616
- if (options.formattedValue !== undefined && options.formattedValue !== options.rawValue) {
617
- return 'formatted';
618
- }
619
- return options.displayValue === options.rawValue ? 'raw' : 'formatted';
620
- }
621
- function numberInputStepperState(options) {
622
- if (options.disabled)
623
- return 'disabled';
624
- if (options.readonly)
625
- return 'readonly';
626
- if (options.numericValue === undefined)
627
- return 'unavailable';
628
- if (options.canDecrement && options.canIncrement)
629
- return 'available';
630
- if (!options.canDecrement && options.canIncrement)
631
- return 'decrement-disabled';
632
- if (options.canDecrement && !options.canIncrement)
633
- return 'increment-disabled';
634
- return 'unavailable';
635
- }
636
- function switchAsyncConfirmationState(options) {
637
- if (options.disabled)
638
- return 'disabled';
639
- if (options.readonly)
640
- return 'readonly';
641
- if (options.loading)
642
- return 'loading';
643
- if (options.error !== '')
644
- return 'error';
645
- if (options.rollback)
646
- return 'rollback';
647
- if (options.confirmRequired)
648
- return 'confirm-required';
649
- if (options.pending)
650
- return 'pending';
651
- return options.checked ? 'checked' : 'unchecked';
652
- }
653
- function selectUniqueKnownIds(value, knownIds) {
654
- const result = [];
655
- for (const id of value) {
656
- if (!knownIds.has(id) || result.includes(id))
657
- continue;
658
- result.push(id);
659
- }
660
- return result;
661
- }
662
- function selectItemMatchesQuery(item, query) {
663
- if (query === '')
664
- return true;
665
- const keywordPool = [item.label, ...(item.keywords ?? [])].join(' ').trim().toLowerCase();
666
- return keywordPool.includes(query.toLowerCase());
667
- }
668
- function selectSearchMultiselectOptionContract(item, options) {
669
- const visible = options.matched;
670
- return {
671
- id: item.id,
672
- option: item,
673
- selected: options.selected,
674
- visible,
675
- matched: options.matched,
676
- attrs: {
677
- 'data-option-id': item.id,
678
- 'data-label': item.label,
679
- 'data-visible': visible ? 'true' : undefined,
680
- 'data-matched': options.matched ? 'true' : undefined,
681
- 'data-selected': options.selected ? 'true' : undefined,
682
- 'aria-selected': options.selected ? 'true' : 'false',
683
- 'aria-hidden': visible ? undefined : 'true',
684
- disabled: item.disabled === true ? true : undefined,
685
- },
686
- };
687
- }
688
- function setFlag(attrs, name, value) {
689
- if (value === true)
690
- attrs[name] = '';
691
- }
692
- function setDefined(attrs, name, value) {
693
- if (value !== undefined)
694
- attrs[name] = value;
695
- }
696
- function removeUndefined(attrs) {
697
- const result = {};
698
- for (const [name, value] of Object.entries(attrs)) {
699
- if (value !== undefined)
700
- result[name] = value;
701
- }
702
- return result;
703
- }
704
- function styleSuffix(value) {
705
- if (typeof value !== 'string' || value.trim() === '')
706
- return '';
707
- return value.trim().endsWith(';') ? value.trim() : `${value.trim()};`;
708
- }
709
- export function createInputNumberPlans(options) {
710
- const value = options.value;
711
- const canDecrement = options.min === undefined || value === undefined || value > options.min;
712
- const canIncrement = options.max === undefined || value === undefined || value < options.max;
713
- const stepState = options.disabled === true
714
- ? 'disabled'
715
- : options.readonly === true
716
- ? 'readonly'
717
- : canDecrement && canIncrement
718
- ? 'available'
719
- : !canDecrement && canIncrement
720
- ? 'decrement-disabled'
721
- : 'increment-disabled';
722
- return {
723
- root: createUiDomPlan('div', 'number-input', {
724
- ...(options.invalid === undefined ? {} : { invalid: options.invalid }),
725
- ...(options.className === undefined ? {} : { className: options.className }),
726
- attrs: { id: `${options.id}-number-input`, ...options.attrs },
727
- }),
728
- input: createUiDomPlan('input', 'number-input-field', {
729
- ...(options.disabled === undefined ? {} : { disabled: options.disabled }),
730
- ...(options.readonly === undefined ? {} : { readonly: options.readonly }),
731
- ...(options.invalid === undefined ? {} : { invalid: options.invalid }),
732
- attrs: {
733
- id: `${options.id}-input`,
734
- type: 'text',
735
- inputmode: 'numeric',
736
- ...(options.value === undefined ? {} : { value: String(options.value) }),
737
- ...(options.min === undefined ? {} : { 'data-min': String(options.min) }),
738
- ...(options.max === undefined ? {} : { 'data-max': String(options.max) }),
739
- ...(options.step === undefined ? {} : { 'data-step': String(options.step) }),
740
- ...(options.placeholder === undefined ? {} : { placeholder: options.placeholder }),
741
- },
742
- }),
743
- decrement: createUiDomPlan('button', 'number-input-step', {
744
- state: stepState === 'decrement-disabled' || stepState === 'disabled' ? 'disabled' : 'idle',
745
- ...(canDecrement && options.disabled !== true ? {} : { disabled: true }),
746
- attrs: {
747
- id: `${options.id}-decrement`,
748
- type: 'button',
749
- 'data-direction': 'decrement',
750
- 'aria-label': 'Decrease',
751
- },
752
- }),
753
- increment: createUiDomPlan('button', 'number-input-step', {
754
- state: stepState === 'increment-disabled' || stepState === 'disabled' ? 'disabled' : 'idle',
755
- ...(canIncrement && options.disabled !== true ? {} : { disabled: true }),
756
- attrs: {
757
- id: `${options.id}-increment`,
758
- type: 'button',
759
- 'data-direction': 'increment',
760
- 'aria-label': 'Increase',
761
- },
762
- }),
763
- };
764
- }