@stonecrop/aform 0.30.0 → 0.32.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 (92) hide show
  1. package/dist/aform.js +3024 -2256
  2. package/dist/aform.js.map +1 -1
  3. package/dist/assets/index.css +1170 -1
  4. package/dist/src/components/AForm.vue.d.ts +20 -0
  5. package/dist/src/components/AForm.vue.d.ts.map +1 -0
  6. package/dist/src/components/AFormLoading.vue.d.ts +14 -0
  7. package/dist/src/components/AFormLoading.vue.d.ts.map +1 -0
  8. package/dist/src/components/base/CollapseButton.vue.d.ts +7 -0
  9. package/dist/src/components/base/CollapseButton.vue.d.ts.map +1 -0
  10. package/dist/src/components/base/ExpandButton.vue.d.ts +7 -0
  11. package/dist/src/components/base/ExpandButton.vue.d.ts.map +1 -0
  12. package/dist/src/components/form/ABadge.vue.d.ts +14 -0
  13. package/dist/src/components/form/ABadge.vue.d.ts.map +1 -0
  14. package/dist/src/components/form/ACheckbox.vue.d.ts +15 -0
  15. package/dist/src/components/form/ACheckbox.vue.d.ts.map +1 -0
  16. package/dist/src/components/form/ACurrencyInput.vue.d.ts +20 -0
  17. package/dist/src/components/form/ACurrencyInput.vue.d.ts.map +1 -0
  18. package/dist/src/components/form/ADate.vue.d.ts +14 -0
  19. package/dist/src/components/form/ADate.vue.d.ts.map +1 -0
  20. package/dist/src/components/form/ADatePicker.vue.d.ts +28 -0
  21. package/dist/src/components/form/ADatePicker.vue.d.ts.map +1 -0
  22. package/dist/src/components/form/ADateRange.vue.d.ts +18 -0
  23. package/dist/src/components/form/ADateRange.vue.d.ts.map +1 -0
  24. package/dist/src/components/form/ADateSelection.vue.d.ts +52 -0
  25. package/dist/src/components/form/ADateSelection.vue.d.ts.map +1 -0
  26. package/dist/src/components/form/ADateTime.vue.d.ts +17 -0
  27. package/dist/src/components/form/ADateTime.vue.d.ts.map +1 -0
  28. package/dist/src/components/form/ADateTimeInput.vue.d.ts +30 -0
  29. package/dist/src/components/form/ADateTimeInput.vue.d.ts.map +1 -0
  30. package/dist/src/components/form/ADropdown.vue.d.ts +20 -0
  31. package/dist/src/components/form/ADropdown.vue.d.ts.map +1 -0
  32. package/dist/src/components/form/ADuration.vue.d.ts +18 -0
  33. package/dist/src/components/form/ADuration.vue.d.ts.map +1 -0
  34. package/dist/src/components/form/AFieldset.vue.d.ts +28 -0
  35. package/dist/src/components/form/AFieldset.vue.d.ts.map +1 -0
  36. package/dist/src/components/form/AFileAttach.vue.d.ts +5 -0
  37. package/dist/src/components/form/AFileAttach.vue.d.ts.map +1 -0
  38. package/dist/src/components/form/AFormLink.vue.d.ts +40 -0
  39. package/dist/src/components/form/AFormLink.vue.d.ts.map +1 -0
  40. package/dist/src/components/form/ANumericInput.vue.d.ts +14 -0
  41. package/dist/src/components/form/ANumericInput.vue.d.ts.map +1 -0
  42. package/dist/src/components/form/AQuantityInput.vue.d.ts +20 -0
  43. package/dist/src/components/form/AQuantityInput.vue.d.ts.map +1 -0
  44. package/dist/src/components/form/ASegmentedControl.vue.d.ts +23 -0
  45. package/dist/src/components/form/ASegmentedControl.vue.d.ts.map +1 -0
  46. package/dist/src/components/form/ATextInput.vue.d.ts +14 -0
  47. package/dist/src/components/form/ATextInput.vue.d.ts.map +1 -0
  48. package/dist/src/components/form/ATextboxInput.vue.d.ts +21 -0
  49. package/dist/src/components/form/ATextboxInput.vue.d.ts.map +1 -0
  50. package/dist/src/components/utilities/Login.vue.d.ts +14 -0
  51. package/dist/src/components/utilities/Login.vue.d.ts.map +1 -0
  52. package/dist/{tsdoc-metadata.json → src/tsdoc-metadata.json} +1 -1
  53. package/package.json +35 -30
  54. package/dist/aform.d.ts +0 -441
  55. package/dist/aform.tsbuildinfo +0 -1
  56. package/dist/src/directives/mask.js +0 -107
  57. package/dist/src/index.js +0 -58
  58. package/dist/src/types/index.js +0 -0
  59. package/dist/src/utils/badge.js +0 -71
  60. package/dist/src/utils/columns.js +0 -33
  61. package/dist/src/utils/deserialize.js +0 -20
  62. package/src/components/AForm.vue +0 -314
  63. package/src/components/AFormLoading.vue +0 -76
  64. package/src/components/base/CollapseButton.vue +0 -36
  65. package/src/components/base/ExpandButton.vue +0 -34
  66. package/src/components/form/ABadge.vue +0 -135
  67. package/src/components/form/ACheckbox.vue +0 -62
  68. package/src/components/form/ACurrencyInput.vue +0 -357
  69. package/src/components/form/ADate.vue +0 -93
  70. package/src/components/form/ADatePicker.vue +0 -470
  71. package/src/components/form/ADateRange.vue +0 -133
  72. package/src/components/form/ADateSelection.vue +0 -174
  73. package/src/components/form/ADateTime.vue +0 -149
  74. package/src/components/form/ADateTimeInput.vue +0 -299
  75. package/src/components/form/ADropdown.vue +0 -222
  76. package/src/components/form/ADuration.vue +0 -124
  77. package/src/components/form/AFieldset.vue +0 -72
  78. package/src/components/form/AFileAttach.vue +0 -97
  79. package/src/components/form/AFormLink.vue +0 -368
  80. package/src/components/form/ANumericInput.vue +0 -32
  81. package/src/components/form/AQuantityInput.vue +0 -349
  82. package/src/components/form/ASegmentedControl.vue +0 -342
  83. package/src/components/form/ATextInput.vue +0 -40
  84. package/src/components/form/ATextboxInput.vue +0 -66
  85. package/src/components/utilities/Login.vue +0 -189
  86. package/src/directives/mask.ts +0 -122
  87. package/src/index.ts +0 -92
  88. package/src/shims-vue.d.ts +0 -5
  89. package/src/types/index.ts +0 -333
  90. package/src/utils/badge.ts +0 -94
  91. package/src/utils/columns.ts +0 -38
  92. package/src/utils/deserialize.ts +0 -20
@@ -1,349 +0,0 @@
1
- <template>
2
- <div class="aform_form-element aquantity">
3
- <template v-if="mode === 'display'">
4
- <span class="aform_display-value">{{ displayText }}</span>
5
- <label class="aform_field-label">{{ label }}</label>
6
- </template>
7
- <template v-else>
8
- <div class="aquantity__row">
9
- <div class="aquantity__field aquantity__field--qty">
10
- <div class="aquantity__group">
11
- <input
12
- :id="uuid"
13
- v-model.number="qty"
14
- class="aquantity__qty"
15
- type="number"
16
- :disabled="mode === 'read'"
17
- :required="required"
18
- @keydown="onQtyKeydown"
19
- @paste="onQtyPaste" />
20
- <div v-on-click-outside="closeDropdown" class="aquantity__uom">
21
- <button
22
- :id="`${uuid}-uom`"
23
- type="button"
24
- class="aquantity__uom-toggle"
25
- :disabled="mode === 'read'"
26
- aria-haspopup="listbox"
27
- :aria-expanded="dropdown.open"
28
- :aria-activedescendant="
29
- dropdown.open && dropdown.activeIndex >= 0 ? `${uuid}-uom-opt-${dropdown.activeIndex}` : undefined
30
- "
31
- @click="toggleDropdown"
32
- @keydown.down.prevent="moveActive(1)"
33
- @keydown.up.prevent="moveActive(-1)"
34
- @keydown.enter.prevent="selectActive"
35
- @keydown.esc="closeDropdown">
36
- <span class="aquantity__uom-value">{{ uom || uomLabel }}</span>
37
- <span class="aquantity__caret" aria-hidden="true"></span>
38
- </button>
39
- <ul v-show="dropdown.open" class="aquantity__uom-menu" role="listbox" :aria-label="uomLabel">
40
- <li
41
- v-for="(option, i) in uoms"
42
- :id="`${uuid}-uom-opt-${i}`"
43
- :key="option"
44
- role="option"
45
- :aria-selected="option === uom"
46
- class="aquantity__uom-option"
47
- :class="{ 'is-active': i === dropdown.activeIndex }"
48
- @mouseenter="dropdown.activeIndex = i"
49
- @click="selectUom(option)">
50
- {{ option }}
51
- </li>
52
- </ul>
53
- </div>
54
- </div>
55
- <label class="aform_field-label" :for="uuid">{{ label }}</label>
56
- </div>
57
- </div>
58
- <div class="aquantity__row aquantity__row--stock">
59
- <div class="aquantity__field aquantity__field--stock-uom">
60
- <input :value="modelValue.stockUom" class="aform_input-field aquantity__stock-field" type="text" disabled />
61
- <label class="aform_field-label">{{ stockUomLabel }}</label>
62
- </div>
63
- <div class="aquantity__field aquantity__field--stock-qty">
64
- <input :value="modelValue.stockQty" class="aform_input-field aquantity__stock-field" type="number" disabled />
65
- <label class="aform_field-label">{{ stockQtyLabel }}</label>
66
- </div>
67
- <div class="aquantity__field aquantity__field--conversion">
68
- <input
69
- :value="modelValue.conversionFactor"
70
- class="aform_input-field aquantity__stock-field"
71
- type="number"
72
- disabled />
73
- <label class="aform_field-label">{{ conversionFactorLabel }}</label>
74
- </div>
75
- </div>
76
- <p v-show="validation.errorMessage" class="aform_error" v-html="validation.errorMessage"></p>
77
- </template>
78
- </div>
79
- </template>
80
-
81
- <script setup lang="ts">
82
- import { vOnClickOutside } from '@vueuse/components'
83
- import { computed, reactive } from 'vue'
84
-
85
- import type { ComponentProps, QuantityOptions, QuantityValue } from '../../types'
86
-
87
- const {
88
- label,
89
- required,
90
- mode,
91
- uuid,
92
- validation = { errorMessage: '&nbsp;' },
93
- options = {},
94
- uomLabel = 'UOM',
95
- stockUomLabel = 'Stock UOM',
96
- stockQtyLabel = 'Stock Qty',
97
- conversionFactorLabel = 'Conversion Factor',
98
- } = defineProps<
99
- ComponentProps & {
100
- options?: QuantityOptions
101
- uomLabel?: string
102
- stockUomLabel?: string
103
- stockQtyLabel?: string
104
- conversionFactorLabel?: string
105
- }
106
- >()
107
-
108
- const modelValue = defineModel<QuantityValue>({
109
- default: () => ({ qty: 0, uom: '', stockQty: 0, stockUom: '', conversionFactor: 1 }),
110
- })
111
-
112
- const uoms = computed(() => options.uoms ?? [])
113
-
114
- // Round to shed binary floating-point noise (e.g. 0.1 * 3 → 0.30000000000000004) while
115
- // preserving any legitimate decimal places.
116
- const roundQty = (value: number): number => Number(value.toFixed(6))
117
-
118
- const resolveConversionFactor = (uom: string): number => {
119
- const stockUom = options.stockUom ?? modelValue.value.stockUom
120
- if (!uom || uom === stockUom) return 1
121
- const mapped = options.conversionFactors?.[uom]
122
- if (mapped !== undefined) return mapped
123
- // UOM absent from the conversion map: keep the stored factor only when the unit is
124
- // unchanged (e.g. editing qty on a loaded value, so the factor round-trips). Switching
125
- // to a new, unmapped unit resets to 1 rather than silently reusing the previous factor.
126
- if (uom === modelValue.value.uom) return modelValue.value.conversionFactor ?? 1
127
- return 1
128
- }
129
-
130
- const recompute = (qty: number, uom: string) => {
131
- const conversionFactor = resolveConversionFactor(uom)
132
- modelValue.value = {
133
- qty,
134
- uom,
135
- conversionFactor,
136
- stockUom: options.stockUom ?? modelValue.value.stockUom,
137
- stockQty: roundQty(qty * conversionFactor),
138
- }
139
- }
140
-
141
- const qty = computed({
142
- get: () => modelValue.value?.qty ?? 0,
143
- set: (value: number) => recompute(value, modelValue.value?.uom ?? ''),
144
- })
145
-
146
- const uom = computed({
147
- get: () => modelValue.value?.uom ?? '',
148
- set: (value: string) => recompute(modelValue.value?.qty ?? 0, value),
149
- })
150
-
151
- const qtyNavigationKeys = new Set([
152
- 'Backspace',
153
- 'Delete',
154
- 'Tab',
155
- 'Escape',
156
- 'Enter',
157
- 'ArrowLeft',
158
- 'ArrowRight',
159
- 'ArrowUp',
160
- 'ArrowDown',
161
- 'Home',
162
- 'End',
163
- ])
164
-
165
- const onQtyKeydown = (event: KeyboardEvent) => {
166
- if (event.ctrlKey || event.metaKey || event.altKey) return
167
- if (qtyNavigationKeys.has(event.key)) return
168
- if (/^[0-9]$/.test(event.key)) return
169
- const input = event.target as HTMLInputElement
170
- if (event.key === '.' && !input.value.includes('.')) return
171
- event.preventDefault()
172
- }
173
-
174
- const onQtyPaste = (event: ClipboardEvent) => {
175
- const pasted = event.clipboardData?.getData('text') ?? ''
176
- if (!/^\d*\.?\d*$/.test(pasted)) event.preventDefault()
177
- }
178
-
179
- const dropdown = reactive({ open: false, activeIndex: -1 })
180
-
181
- const openDropdown = () => {
182
- dropdown.activeIndex = Math.max(uoms.value.indexOf(uom.value), 0)
183
- dropdown.open = true
184
- }
185
-
186
- const closeDropdown = () => {
187
- dropdown.open = false
188
- }
189
-
190
- const toggleDropdown = () => {
191
- if (dropdown.open) closeDropdown()
192
- else openDropdown()
193
- }
194
-
195
- const selectUom = (value: string) => {
196
- uom.value = value
197
- closeDropdown()
198
- }
199
-
200
- const moveActive = (delta: number) => {
201
- if (!dropdown.open) {
202
- openDropdown()
203
- return
204
- }
205
- const length = uoms.value.length
206
- if (!length) return
207
- dropdown.activeIndex = (dropdown.activeIndex + delta + length) % length
208
- }
209
-
210
- const selectActive = () => {
211
- if (!dropdown.open) {
212
- openDropdown()
213
- return
214
- }
215
- const option = uoms.value[dropdown.activeIndex]
216
- if (option !== undefined) selectUom(option)
217
- }
218
-
219
- const showStock = computed(() => {
220
- const v = modelValue.value
221
- return !!v?.stockUom && (v.uom !== v.stockUom || v.qty !== v.stockQty)
222
- })
223
-
224
- const displayText = computed(() => {
225
- const v = modelValue.value
226
- if (!v || !v.uom) return '—'
227
- const base = `${v.qty} ${v.uom}`
228
- return showStock.value ? `${base} (${v.stockQty} ${v.stockUom})` : base
229
- })
230
- </script>
231
-
232
- <style scoped>
233
- .aquantity__row {
234
- display: flex;
235
- gap: 1ch;
236
- }
237
-
238
- .aquantity__row--stock {
239
- margin-top: 1.5rem;
240
- }
241
-
242
- .aquantity__field {
243
- position: relative;
244
- flex: 1;
245
- min-width: 0;
246
- }
247
-
248
- .aquantity__group {
249
- display: flex;
250
- align-items: stretch;
251
- width: 100%;
252
- border: 1px solid var(--sc-input-border-color);
253
- border-radius: 0.25rem;
254
- }
255
-
256
- .aquantity__group:focus-within {
257
- border-color: var(--sc-input-active-border-color);
258
- }
259
-
260
- .aquantity__qty {
261
- flex: 1;
262
- min-width: 0;
263
- border: none;
264
- outline: none;
265
- padding: 0.5ch 1ch;
266
- background: transparent;
267
- border-radius: 0.25rem 0 0 0.25rem;
268
- appearance: textfield;
269
- -moz-appearance: textfield;
270
- }
271
-
272
- .aquantity__qty::-webkit-outer-spin-button,
273
- .aquantity__qty::-webkit-inner-spin-button {
274
- appearance: none;
275
- -webkit-appearance: none;
276
- margin: 0;
277
- }
278
-
279
- .aquantity__uom {
280
- position: relative;
281
- flex: 0 0 auto;
282
- border-left: 1px solid var(--sc-input-border-color);
283
- }
284
-
285
- .aquantity__uom-toggle {
286
- display: flex;
287
- align-items: center;
288
- gap: 0.75ch;
289
- height: 100%;
290
- padding: 0.5ch 1ch;
291
- background: var(--sc-gray-5);
292
- border: none;
293
- border-radius: 0 0.25rem 0.25rem 0;
294
- white-space: nowrap;
295
- cursor: pointer;
296
- }
297
-
298
- .aquantity__uom-toggle:disabled {
299
- cursor: not-allowed;
300
- color: var(--sc-gray-50);
301
- }
302
-
303
- .aquantity__caret {
304
- display: inline-block;
305
- width: 0;
306
- height: 0;
307
- border-left: 0.3em solid transparent;
308
- border-right: 0.3em solid transparent;
309
- border-top: 0.3em solid currentColor;
310
- }
311
-
312
- .aquantity__uom-menu {
313
- position: absolute;
314
- top: 100%;
315
- right: 0;
316
- z-index: 100;
317
- min-width: 100%;
318
- margin: 0.15rem 0 0 0;
319
- padding: 0.25rem 0;
320
- list-style: none;
321
- background: var(--sc-input-field-background);
322
- border: 1px solid var(--sc-input-active-border-color);
323
- border-radius: 0.25rem;
324
- }
325
-
326
- .aquantity__uom-option {
327
- padding: 0.4ch 1ch;
328
- white-space: nowrap;
329
- cursor: pointer;
330
- }
331
-
332
- .aquantity__uom-option.is-active,
333
- .aquantity__uom-option:hover {
334
- background-color: var(--sc-row-color-zebra-light);
335
- }
336
-
337
- .aquantity__stock-field {
338
- width: 100%;
339
- font-size: 1rem;
340
- padding: 0.5ch 1ch;
341
- border: 1px solid var(--sc-input-border-color);
342
- border-radius: 0.25rem;
343
- outline: none;
344
- }
345
-
346
- .aquantity__stock-field:disabled {
347
- color: var(--sc-gray-50);
348
- }
349
- </style>
@@ -1,342 +0,0 @@
1
- <template>
2
- <div
3
- class="aform_form-element aform_segmented-control"
4
- :class="{
5
- 'aform_segmented-control--xs': size === 'xs',
6
- 'aform_segmented-control--sm': size === 'sm',
7
- 'aform_segmented-control--equal': equal,
8
- }">
9
- <template v-if="mode === 'display'">
10
- <label class="aform_field-label" :class="{ 'aform_segmented-sr-label': hideLabel }">{{ label }}</label>
11
- <ABadge
12
- v-if="displayBadge"
13
- class="aform_display-value aform_segmented-display-badge"
14
- :value="displayBadgeValue"
15
- :options="options"
16
- presentation="input-accent" />
17
- <span v-else class="aform_display-value">{{ displayText }}</span>
18
- </template>
19
- <template v-else>
20
- <label :id="labelId" class="aform_field-label" :class="{ 'aform_segmented-sr-label': hideLabel }">
21
- {{ label }}
22
- </label>
23
- <div
24
- :role="multiple ? 'group' : 'radiogroup'"
25
- class="aform_segmented-track"
26
- :aria-labelledby="groupAriaLabel ? undefined : labelId"
27
- :aria-label="groupAriaLabel"
28
- :aria-busy="busy || undefined">
29
- <label
30
- v-for="choice in choices"
31
- :key="choice"
32
- class="aform_segmented-segment"
33
- :class="segmentClasses(choice)"
34
- :style="segmentStyle(choice)"
35
- :data-segment-value="choice">
36
- <input
37
- v-if="multiple"
38
- v-model="selectedMany"
39
- type="checkbox"
40
- class="aform_segmented-input"
41
- :name="groupName"
42
- :value="choice"
43
- :disabled="mode === 'read'" />
44
- <input
45
- v-else
46
- v-model="selected"
47
- type="radio"
48
- class="aform_segmented-input"
49
- :name="groupName"
50
- :value="choice"
51
- :disabled="mode === 'read'" />
52
- <span class="aform_segmented-label">{{ segmentLabel(choice) }}</span>
53
- </label>
54
- </div>
55
- <p v-show="errorText" class="aform_error" v-html="errorText"></p>
56
- </template>
57
- </div>
58
- </template>
59
-
60
- <script setup lang="ts">
61
- import type { BadgeVariant, FieldOptions } from '@stonecrop/schema'
62
- import { hasBadgeOptions, lookupBadge, selectChoices } from '@stonecrop/schema'
63
- import { computed, useId, type CSSProperties } from 'vue'
64
-
65
- import type { ComponentProps } from '../../types'
66
- import ABadge from './ABadge.vue'
67
-
68
- const {
69
- label,
70
- options = undefined,
71
- mode,
72
- uuid,
73
- errors,
74
- validation = { errorMessage: '' },
75
- size = 'sm',
76
- equal = false,
77
- hideLabel = false,
78
- ariaLabel = undefined,
79
- busy = false,
80
- multiple = false,
81
- } = defineProps<
82
- ComponentProps & {
83
- options?: FieldOptions
84
- size?: 'xs' | 'sm'
85
- equal?: boolean
86
- hideLabel?: boolean
87
- ariaLabel?: string
88
- busy?: boolean
89
- multiple?: boolean
90
- }
91
- >()
92
-
93
- const selected = defineModel<string | string[]>({ default: '' })
94
-
95
- const selectedMany = computed({
96
- get: () => (Array.isArray(selected.value) ? selected.value : []),
97
- set: (value: string[]) => {
98
- selected.value = value
99
- },
100
- })
101
-
102
- const errorText = computed(() => (errors?.length ? errors.join('; ') : (validation.errorMessage ?? '')))
103
-
104
- const fallbackId = useId()
105
- const groupName = computed(() => uuid ?? `aform-segmented-${fallbackId}`)
106
- const labelId = computed(() => `${groupName.value}-label`)
107
-
108
- // aria-labelledby outranks aria-label in the accessible-name algorithm, so the two are mutually
109
- // exclusive here: emitting both would silently leave `ariaLabel` inert. It only applies while the
110
- // label element is visually hidden — overriding a *visible* label would break WCAG 2.5.3.
111
- const groupAriaLabel = computed(() => (hideLabel ? ariaLabel : undefined))
112
-
113
- const choices = computed(() => selectChoices(options))
114
- const badgeOptions = computed(() => hasBadgeOptions(options))
115
-
116
- function segmentLabel(choice: string): string {
117
- return lookupBadge(options, choice)?.label ?? choice
118
- }
119
-
120
- function segmentVariant(choice: string): BadgeVariant {
121
- return lookupBadge(options, choice)?.variant ?? 'neutral'
122
- }
123
-
124
- function isChoiceSelected(choice: string): boolean {
125
- if (multiple) return selectedMany.value.includes(choice)
126
- return selected.value === choice
127
- }
128
-
129
- function segmentClasses(choice: string): Record<string, boolean> {
130
- if (!badgeOptions.value) return {}
131
- return {
132
- 'aform_segmented-segment--badge': true,
133
- [`aform_segmented-segment--${segmentVariant(choice)}`]: isChoiceSelected(choice),
134
- }
135
- }
136
-
137
- function segmentStyle(choice: string): CSSProperties {
138
- if (!badgeOptions.value || !isChoiceSelected(choice)) return {}
139
- const color = lookupBadge(options, choice)?.color
140
- if (!color) return {}
141
- return {
142
- '--sc-badge-bg': color,
143
- '--sc-badge-text': color,
144
- } as CSSProperties
145
- }
146
-
147
- const displayText = computed(() => {
148
- if (multiple) {
149
- return selectedMany.value.map(segmentLabel).filter(Boolean).join(', ')
150
- }
151
- const value = selected.value
152
- if (typeof value !== 'string' || value === '') return ''
153
- return segmentLabel(value)
154
- })
155
-
156
- const displayBadge = computed(() => {
157
- if (!badgeOptions.value) return false
158
- if (multiple) return selectedMany.value.length === 1
159
- return typeof selected.value === 'string' && selected.value !== ''
160
- })
161
-
162
- const displayBadgeValue = computed(() => {
163
- if (multiple) return selectedMany.value[0] ?? ''
164
- return typeof selected.value === 'string' ? selected.value : ''
165
- })
166
- </script>
167
-
168
- <style scoped>
169
- .aform_segmented-control--xs.aform_form-element {
170
- min-width: 0;
171
- padding-top: 0;
172
- max-width: none;
173
- }
174
-
175
- .aform_segmented-control--xs:not(:has(.aform_segmented-sr-label)) {
176
- padding-top: 0.5rem;
177
- }
178
-
179
- .aform_segmented-sr-label {
180
- position: absolute;
181
- width: 1px;
182
- height: 1px;
183
- padding: 0;
184
- margin: -1px;
185
- overflow: hidden;
186
- clip: rect(0, 0, 0, 0);
187
- white-space: nowrap;
188
- border: 0;
189
- }
190
-
191
- .aform_segmented-track {
192
- display: inline-flex;
193
- flex-wrap: wrap;
194
- align-items: stretch;
195
- gap: 0.125rem;
196
- padding: 0.125rem;
197
- width: auto;
198
- min-width: 0;
199
- min-height: 2rem;
200
- outline: 1px solid var(--sc-input-border-color);
201
- outline-offset: -1px;
202
- border-radius: 0;
203
- box-sizing: border-box;
204
- background: var(--sc-input-field-disabled-background);
205
- font-family: var(--sc-font-family);
206
- }
207
-
208
- .aform_segmented-control--xs .aform_segmented-track {
209
- min-height: 1.5rem;
210
- font-size: 0.875rem;
211
- }
212
-
213
- .aform_segmented-control--equal .aform_segmented-track {
214
- display: flex;
215
- width: 100%;
216
- }
217
-
218
- .aform_segmented-segment {
219
- position: relative;
220
- display: inline-flex;
221
- align-items: center;
222
- justify-content: center;
223
- flex: none;
224
- margin: 0;
225
- padding: 0.125rem 0.5rem;
226
- min-height: 1.5rem;
227
- border-radius: 0;
228
- color: var(--sc-input-label-color);
229
- font-family: inherit;
230
- font-size: inherit;
231
- font-weight: 400;
232
- line-height: 1.25;
233
- cursor: pointer;
234
- user-select: none;
235
- box-sizing: border-box;
236
- }
237
-
238
- .aform_segmented-control--sm .aform_segmented-segment {
239
- min-height: 1.75rem;
240
- padding: 0.125rem 0.75rem;
241
- font-size: var(--sc-font-size);
242
- }
243
-
244
- .aform_segmented-control--equal .aform_segmented-segment {
245
- flex: 1 1 0;
246
- }
247
-
248
- .aform_segmented-input {
249
- position: absolute;
250
- inline-size: 1px;
251
- block-size: 1px;
252
- padding: 0;
253
- margin: -1px;
254
- overflow: hidden;
255
- clip: rect(0, 0, 0, 0);
256
- white-space: nowrap;
257
- border: 0;
258
- }
259
-
260
- .aform_segmented-label {
261
- pointer-events: none;
262
- }
263
-
264
- .aform_segmented-segment:not(.aform_segmented-segment--badge):has(.aform_segmented-input:checked) {
265
- background: var(--sc-input-field-background);
266
- color: var(--sc-cell-text-color);
267
- font-weight: 500;
268
- }
269
-
270
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked) {
271
- font-weight: 500;
272
- }
273
-
274
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked).aform_segmented-segment--neutral {
275
- background: var(--sc-badge-neutral-bg);
276
- color: var(--sc-badge-neutral-text);
277
- }
278
-
279
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked).aform_segmented-segment--success {
280
- background: var(--sc-badge-success-bg);
281
- color: var(--sc-badge-success-text);
282
- }
283
-
284
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked).aform_segmented-segment--warning {
285
- background: var(--sc-badge-warning-bg);
286
- color: var(--sc-badge-warning-text);
287
- }
288
-
289
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked).aform_segmented-segment--danger {
290
- background: var(--sc-badge-danger-bg);
291
- color: var(--sc-badge-danger-text);
292
- }
293
-
294
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked).aform_segmented-segment--brand {
295
- background: var(--sc-badge-brand-bg);
296
- color: var(--sc-badge-brand-text);
297
- }
298
-
299
- .aform_segmented-segment--badge:has(.aform_segmented-input:checked)[style*='--sc-badge-bg'] {
300
- background: var(--sc-badge-bg);
301
- color: var(--sc-badge-text);
302
- }
303
-
304
- .aform_segmented-segment:has(.aform_segmented-input:focus-visible) {
305
- outline: 1px solid var(--sc-input-active-border-color);
306
- outline-offset: -1px;
307
- }
308
-
309
- .aform_segmented-segment:hover:has(.aform_segmented-input:not(:disabled):not(:checked)) {
310
- background: color-mix(in srgb, var(--sc-input-field-background) 50%, transparent);
311
- }
312
-
313
- .aform_segmented-segment:has(.aform_segmented-input:disabled) {
314
- cursor: not-allowed;
315
- opacity: 0.5;
316
- }
317
-
318
- .aform_segmented-track:has(.aform_segmented-input:focus-visible) {
319
- outline-color: var(--sc-input-active-border-color);
320
- }
321
-
322
- .aform_segmented-display-badge {
323
- display: block;
324
- padding: 0.5rem;
325
- min-height: 2rem;
326
- box-sizing: border-box;
327
- }
328
-
329
- @media (prefers-reduced-motion: no-preference) {
330
- .aform_segmented-segment {
331
- transition:
332
- background-color 0.1s ease-out,
333
- color 0.1s ease-out;
334
- }
335
- }
336
-
337
- @media (prefers-reduced-motion: reduce) {
338
- .aform_segmented-segment {
339
- transition: none;
340
- }
341
- }
342
- </style>