ajo-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +126 -0
  3. package/dist/accordion.js +130 -0
  4. package/dist/avatar.js +54 -0
  5. package/dist/calendar.js +2 -0
  6. package/dist/carousel.js +239 -0
  7. package/dist/chart.js +790 -0
  8. package/dist/checkbox-group.js +70 -0
  9. package/dist/checkbox.js +77 -0
  10. package/dist/chunks/bar-CVafh6C1.js +99 -0
  11. package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
  12. package/dist/chunks/collection-DtRB63U4.js +111 -0
  13. package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
  14. package/dist/chunks/menu-B45IyHHC.js +704 -0
  15. package/dist/chunks/native-BJdhd9XJ.js +20 -0
  16. package/dist/chunks/popup-C8Bb3l_g.js +459 -0
  17. package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
  18. package/dist/chunks/position-D6_i_SRn.js +434 -0
  19. package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
  20. package/dist/collapsible.js +106 -0
  21. package/dist/command.js +263 -0
  22. package/dist/context-menu.js +112 -0
  23. package/dist/data-table.js +5 -0
  24. package/dist/dialog.js +207 -0
  25. package/dist/direction.js +22 -0
  26. package/dist/drawer.js +139 -0
  27. package/dist/field.js +26 -0
  28. package/dist/index.js +38 -0
  29. package/dist/input-date.js +994 -0
  30. package/dist/input-group.js +52 -0
  31. package/dist/input-otp.js +179 -0
  32. package/dist/menu.js +2 -0
  33. package/dist/menubar.js +236 -0
  34. package/dist/message-scroller.js +446 -0
  35. package/dist/navigation-menu.js +330 -0
  36. package/dist/popover.js +307 -0
  37. package/dist/progress.js +39 -0
  38. package/dist/radio-group.js +107 -0
  39. package/dist/resizable.js +172 -0
  40. package/dist/select.js +961 -0
  41. package/dist/sidebar.js +343 -0
  42. package/dist/slider.js +259 -0
  43. package/dist/switch.js +53 -0
  44. package/dist/tabs.js +182 -0
  45. package/dist/toast.js +492 -0
  46. package/dist/toggle-group.js +111 -0
  47. package/dist/toggle.js +52 -0
  48. package/dist/toolbar.js +127 -0
  49. package/dist/tooltip.js +196 -0
  50. package/dist/utils.js +104 -0
  51. package/dist/virtual-list.js +2 -0
  52. package/package.json +250 -0
  53. package/src/accordion.tsx +261 -0
  54. package/src/availability.ts +261 -0
  55. package/src/avatar.tsx +99 -0
  56. package/src/bar.ts +156 -0
  57. package/src/calendar.tsx +1441 -0
  58. package/src/carousel.tsx +424 -0
  59. package/src/chart.tsx +1194 -0
  60. package/src/checkbox-group.tsx +132 -0
  61. package/src/checkbox.tsx +130 -0
  62. package/src/collapsible.tsx +188 -0
  63. package/src/collection.ts +154 -0
  64. package/src/command.tsx +511 -0
  65. package/src/context-menu.tsx +233 -0
  66. package/src/data-table-contract.ts +143 -0
  67. package/src/data-table-model.ts +760 -0
  68. package/src/data-table.tsx +475 -0
  69. package/src/dialog.tsx +393 -0
  70. package/src/direction.tsx +45 -0
  71. package/src/drawer.tsx +251 -0
  72. package/src/field.tsx +61 -0
  73. package/src/index.ts +37 -0
  74. package/src/input-date.tsx +1539 -0
  75. package/src/input-group.tsx +142 -0
  76. package/src/input-otp.tsx +324 -0
  77. package/src/menu-cluster.ts +124 -0
  78. package/src/menu.tsx +1095 -0
  79. package/src/menubar.tsx +459 -0
  80. package/src/message-scroller.tsx +732 -0
  81. package/src/native.ts +26 -0
  82. package/src/navigation-menu.tsx +578 -0
  83. package/src/popover.tsx +519 -0
  84. package/src/popup-surface.tsx +31 -0
  85. package/src/popup.ts +569 -0
  86. package/src/position.ts +523 -0
  87. package/src/progress.tsx +70 -0
  88. package/src/radio-group.tsx +186 -0
  89. package/src/resizable.tsx +310 -0
  90. package/src/segments.ts +922 -0
  91. package/src/select.tsx +1501 -0
  92. package/src/sidebar.tsx +683 -0
  93. package/src/slider.tsx +424 -0
  94. package/src/switch.tsx +104 -0
  95. package/src/tabs.tsx +314 -0
  96. package/src/toast.tsx +923 -0
  97. package/src/toggle-group.tsx +249 -0
  98. package/src/toggle.tsx +91 -0
  99. package/src/toolbar.tsx +212 -0
  100. package/src/tooltip.tsx +359 -0
  101. package/src/utils.ts +204 -0
  102. package/src/virtual-list.tsx +205 -0
  103. package/src/virtual.ts +385 -0
package/src/slider.tsx ADDED
@@ -0,0 +1,424 @@
1
+ import type { IntrinsicElements, Stateful, Stateless } from 'ajo'
2
+ import { callHandler, clamp, controlled, listen, move } from 'ajo-cloves'
3
+ import { FieldContext } from './field'
4
+ import type { FixedArgs, OmitArg } from './utils'
5
+ import { clx, flag, stlx, toNumber } from './utils'
6
+
7
+ /** Axis along which slider values increase. */
8
+ export type SliderOrientation = 'horizontal' | 'vertical'
9
+
10
+ /** Props for a single- or multi-thumb range slider. */
11
+ export type SliderArgs = OmitArg<IntrinsicElements['input'], 'children' | 'defaultValue' | 'type' | 'value'> & {
12
+ /** Controlled slider values. Use one value for a single thumb, multiple values for ranges. */
13
+ value?: number[]
14
+ /** Initial values for uncontrolled usage. */
15
+ defaultValue?: number[]
16
+ /** Lowest allowed value. */
17
+ min?: number
18
+ /** Highest allowed value. */
19
+ max?: number
20
+ /** Value granularity. */
21
+ step?: number | 'any'
22
+ /** Minimum distance, in steps, allowed between adjacent thumbs. */
23
+ minStepsBetweenThumbs?: number
24
+ /** Layout orientation. */
25
+ orientation?: SliderOrientation
26
+ /** Reverse the value direction. */
27
+ inverted?: boolean
28
+ /** Called whenever the value changes. */
29
+ onValueChange?: (value: number[], event: Event) => void
30
+ /** Called when an input change is committed. */
31
+ onValueCommit?: (value: number[], event: Event) => void
32
+ /** Classes for the native range inputs. */
33
+ inputClass?: string
34
+ /** Classes for the range fill element. */
35
+ rangeClass?: string
36
+ /** Classes for the thumb elements. */
37
+ thumbClass?: string
38
+ /** Classes for the track element. */
39
+ trackClass?: string
40
+ /** Extra input classes applied only in vertical orientation. */
41
+ verticalInputClass?: string
42
+ } & FixedArgs<'children' | 'type'>
43
+
44
+ type SliderRootArgs = OmitArg<SliderArgs, 'class' | 'inputClass' | 'rangeClass' | 'thumbClass' | 'trackClass' | 'verticalInputClass'> & {
45
+ inputAttrs: Record<string, unknown>
46
+ inputClass?: string
47
+ inputOnChange?: unknown
48
+ inputOnInput?: unknown
49
+ rangeClass?: string
50
+ thumbClass?: string
51
+ trackClass?: string
52
+ verticalInputClass?: string
53
+ } & FixedArgs<'class'>
54
+
55
+ type ValueCallback = (value: number[], event: Event) => void
56
+
57
+ type Runtime = {
58
+ disabled: boolean
59
+ inputs: Array<HTMLInputElement | null>
60
+ inverted: boolean
61
+ max: number
62
+ min: number
63
+ minStepsBetweenThumbs: number
64
+ onValueChange?: ValueCallback
65
+ onValueCommit?: ValueCallback
66
+ orientation: SliderOrientation
67
+ step?: number | 'any'
68
+ values: number[]
69
+ }
70
+
71
+ const sliderStep = (value: unknown): number | 'any' | undefined =>
72
+ value === 'any' ? 'any' : toNumber(value ?? 1, 1)
73
+
74
+ const sliderOrientation = (value: unknown): SliderOrientation =>
75
+ value === 'vertical' ? 'vertical' : 'horizontal'
76
+
77
+ const valueCallback = (value: unknown): ValueCallback | undefined =>
78
+ typeof value === 'function' ? value as ValueCallback : undefined
79
+
80
+ const percent = (value: number, min: number, max: number, inverted: boolean) => {
81
+ if (max <= min) return 0
82
+ const raw = clamp(((value - min) / (max - min)) * 100, 0, 100)
83
+ return inverted ? 100 - raw : raw
84
+ }
85
+
86
+ const precision = (value: number) => {
87
+ const match = String(value).match(/\.(\d+)/)
88
+ return match ? match[1].length : 0
89
+ }
90
+
91
+ const snap = (value: number, min: number, step: number | 'any' | undefined) => {
92
+ if (step === 'any' || step == null) return value
93
+ const amount = Number(step)
94
+ if (!Number.isFinite(amount) || amount <= 0) return value
95
+ const places = Math.max(precision(min), precision(amount))
96
+ const next = min + Math.round((value - min) / amount) * amount
97
+ return Number(next.toFixed(places + 2))
98
+ }
99
+
100
+ const valuesFrom = (
101
+ value: unknown,
102
+ min: number,
103
+ max: number,
104
+ step: number | 'any' | undefined,
105
+ minStepsBetweenThumbs = 0,
106
+ ) => {
107
+ const raw = Array.isArray(value) && value.length
108
+ ? value.map(item => toNumber(item, min))
109
+ : [min]
110
+ const gapStep = step === 'any' ? 0 : toNumber(step ?? 1, 1)
111
+ const gap = Math.max(0, minStepsBetweenThumbs) * Math.max(0, gapStep)
112
+ const next = raw
113
+ .map(item => snap(clamp(item, min, max), min, step))
114
+ .sort((a, b) => a - b)
115
+
116
+ for (let index = 0; index < next.length; index++) {
117
+ const lower = index > 0 ? next[index - 1] + gap : min
118
+ next[index] = clamp(next[index], lower, max)
119
+ }
120
+
121
+ for (let index = next.length - 1; index >= 0; index--) {
122
+ const upper = index < next.length - 1 ? next[index + 1] - gap : max
123
+ next[index] = clamp(next[index], min, upper)
124
+ }
125
+
126
+ return next
127
+ }
128
+
129
+ const sameValues = (first: number[], second: number[]) =>
130
+ first.length === second.length && first.every((value, index) => value === second[index])
131
+
132
+ const replaceValue = (runtime: Runtime, index: number, value: number) => {
133
+ const next = [...runtime.values]
134
+ const gapStep = runtime.step === 'any' ? 0 : toNumber(runtime.step ?? 1, 1)
135
+ const gap = Math.max(0, runtime.minStepsBetweenThumbs) * Math.max(0, gapStep)
136
+ const lower = index > 0 ? next[index - 1] + gap : runtime.min
137
+ const upper = index < next.length - 1 ? next[index + 1] - gap : runtime.max
138
+ next[index] = snap(clamp(value, lower, upper), runtime.min, runtime.step)
139
+ return next
140
+ }
141
+
142
+ const closestThumb = (runtime: Runtime, value: number) => {
143
+ let nearest = 0
144
+ let distance = Number.POSITIVE_INFINITY
145
+
146
+ for (let index = 0; index < runtime.values.length; index++) {
147
+ const next = Math.abs(runtime.values[index] - value)
148
+ if (next < distance) {
149
+ nearest = index
150
+ distance = next
151
+ }
152
+ }
153
+
154
+ return nearest
155
+ }
156
+
157
+ const pointerValue = (element: HTMLElement, runtime: Runtime, event: PointerEvent) => {
158
+ const rect = element.getBoundingClientRect()
159
+ const distance = runtime.orientation === 'vertical'
160
+ ? rect.bottom - event.clientY
161
+ : event.clientX - rect.left
162
+ const size = runtime.orientation === 'vertical' ? rect.height : rect.width
163
+ const raw = size > 0 ? clamp(distance / size, 0, 1) : 0
164
+ const ratio = runtime.inverted ? 1 - raw : raw
165
+ return runtime.min + ratio * (runtime.max - runtime.min)
166
+ }
167
+
168
+ const rangeStyle = (orientation: SliderOrientation, start: number, end: number) =>
169
+ orientation === 'vertical'
170
+ ? stlx({ bottom: `${start}%`, height: `${Math.max(0, end - start)}%` })
171
+ : stlx({ left: `${start}%`, width: `${Math.max(0, end - start)}%` })
172
+
173
+ const thumbStyle = (orientation: SliderOrientation, position: number) =>
174
+ orientation === 'vertical'
175
+ ? stlx({ bottom: `${position}%`, left: '50%', transform: 'translate(-50%, 50%)' })
176
+ : stlx({ left: `${position}%`, top: '50%', transform: 'translate(-50%, -50%)' })
177
+
178
+ const inputId = (id: unknown, index: number, total: number) => {
179
+ if (id == null) return undefined
180
+ const value = String(id)
181
+ return total === 1 ? value : `${value}-${index + 1}`
182
+ }
183
+
184
+ const inputLabel = (label: unknown, index: number, total: number) => {
185
+ if (label == null) return total === 1 ? 'Slider' : `Slider thumb ${index + 1}`
186
+ const value = String(label)
187
+ return total === 1 ? value : `${value} ${index + 1}`
188
+ }
189
+
190
+ const SliderRoot: Stateful<SliderRootArgs, 'span'> = function* ({ defaultValue, max = 100, min = 0, step = 1, value }) {
191
+ const initialMin = toNumber(min, 0)
192
+ const initialMax = toNumber(max, 100)
193
+ const initialStep = sliderStep(step)
194
+ const initial = valuesFrom(value ?? defaultValue, initialMin, initialMax, initialStep, 0)
195
+ let runtime: Runtime = {
196
+ disabled: false,
197
+ inputs: [],
198
+ inverted: false,
199
+ max: initialMax,
200
+ min: initialMin,
201
+ minStepsBetweenThumbs: 0,
202
+ orientation: 'horizontal',
203
+ step: initialStep,
204
+ values: initial,
205
+ }
206
+ const state = controlled<number[]>(this, {
207
+ fallback: initial,
208
+ onChange: (next, event) => runtime.onValueChange?.(next, event as Event),
209
+ })
210
+
211
+ const commit = (event: Event) => runtime.onValueCommit?.(runtime.values, event)
212
+
213
+ const update = (index: number, raw: number, event: Event) => {
214
+ const next = replaceValue(runtime, index, raw)
215
+ if (sameValues(next, runtime.values)) return
216
+
217
+ runtime.values = next
218
+ state.set(next, event)
219
+ }
220
+
221
+ let dragIndex = 0
222
+ const drag = move(this, {
223
+ onMove: (_data, event) => update(dragIndex, pointerValue(this, runtime, event), event),
224
+ onEnd: (_data, event) => commit(event as Event),
225
+ })
226
+
227
+ listen(this, 'pointerdown', (event: PointerEvent) => {
228
+ if (runtime.disabled || event.button !== 0) return
229
+
230
+ const raw = pointerValue(this, runtime, event)
231
+ const index = closestThumb(runtime, raw)
232
+ dragIndex = index
233
+ runtime.inputs[index]?.focus()
234
+ update(index, raw, event)
235
+ event.preventDefault()
236
+ drag.start(event)
237
+ })
238
+
239
+ for (const {
240
+ disabled,
241
+ inputAttrs,
242
+ inputClass,
243
+ inputOnChange,
244
+ inputOnInput,
245
+ inverted,
246
+ max = 100,
247
+ min = 0,
248
+ minStepsBetweenThumbs = 0,
249
+ onValueChange,
250
+ onValueCommit,
251
+ orientation = 'horizontal',
252
+ rangeClass,
253
+ step = 1,
254
+ thumbClass,
255
+ trackClass,
256
+ value,
257
+ verticalInputClass,
258
+ } of this) {
259
+ const minValue = toNumber(min, 0)
260
+ const maxValue = toNumber(max, 100)
261
+ const stepValue = sliderStep(step)
262
+ const gapValue = toNumber(minStepsBetweenThumbs, 0)
263
+ const orientationValue = sliderOrientation(orientation)
264
+ const raw = state.sync(value != null ? value as number[] : undefined)
265
+ const values = valuesFrom(raw, minValue, maxValue, stepValue, gapValue)
266
+ if (!state.controlled && !sameValues(values, raw)) state.init(values)
267
+
268
+ runtime = {
269
+ disabled: Boolean(disabled),
270
+ inputs: runtime.inputs,
271
+ inverted: Boolean(inverted),
272
+ max: maxValue,
273
+ min: minValue,
274
+ minStepsBetweenThumbs: gapValue,
275
+ onValueChange: valueCallback(onValueChange),
276
+ onValueCommit: valueCallback(onValueCommit),
277
+ orientation: orientationValue,
278
+ step: stepValue,
279
+ values,
280
+ }
281
+ runtime.inputs.length = values.length
282
+
283
+ const positions = values.map(item => percent(item, minValue, maxValue, runtime.inverted))
284
+ const start = values.length > 1 ? Math.min(...positions) : 0
285
+ const end = values.length > 1 ? Math.max(...positions) : positions[0] ?? 0
286
+ const disabledFlag = disabled ? true : undefined
287
+
288
+ yield (
289
+ <>
290
+ <span
291
+ class={trackClass}
292
+ data-orientation={orientationValue}
293
+ data-slot="slider-track"
294
+ >
295
+ <span
296
+ class={rangeClass}
297
+ data-orientation={orientationValue}
298
+ data-slot="slider-range"
299
+ style={rangeStyle(orientationValue, start, end)}
300
+ />
301
+ </span>
302
+ {values.map((item, index) => {
303
+ const handleInput = (event: Event) => {
304
+ callHandler(inputOnInput, event)
305
+ update(index, (event.currentTarget as HTMLInputElement).valueAsNumber, event)
306
+ }
307
+ const handleChange = (event: Event) => {
308
+ callHandler(inputOnChange, event)
309
+ commit(event)
310
+ }
311
+
312
+ return (
313
+ <input
314
+ key={`slider-input-${index}`}
315
+ {...inputAttrs}
316
+ aria-label={inputLabel(inputAttrs['aria-label'], index, values.length)}
317
+ aria-orientation={orientationValue}
318
+ class={clx(inputClass, orientationValue === 'vertical' && verticalInputClass)}
319
+ data-orientation={orientationValue}
320
+ data-slot="slider-input"
321
+ disabled={disabledFlag}
322
+ id={inputId(inputAttrs.id, index, values.length)}
323
+ max={maxValue}
324
+ min={minValue}
325
+ ref={element => runtime.inputs[index] = element}
326
+ set:onchange={handleChange}
327
+ set:oninput={handleInput}
328
+ set:value={String(item)}
329
+ step={stepValue}
330
+ type="range"
331
+ value={item}
332
+ />
333
+ )
334
+ })}
335
+ {positions.map((item, index) => (
336
+ <span
337
+ aria-hidden="true"
338
+ class={thumbClass}
339
+ data-index={index}
340
+ data-orientation={orientationValue}
341
+ data-slot="slider-thumb"
342
+ key={`slider-thumb-${index}`}
343
+ style={thumbStyle(orientationValue, item)}
344
+ />
345
+ ))}
346
+ </>
347
+ )
348
+ }
349
+ }
350
+
351
+ SliderRoot.is = 'span'
352
+
353
+ /** Unstyled range slider with native range inputs and pointer behavior. */
354
+ const Slider: Stateless<SliderArgs> = ({
355
+ class: classes,
356
+ defaultValue,
357
+ disabled,
358
+ inputClass,
359
+ inverted,
360
+ max = 100,
361
+ min = 0,
362
+ minStepsBetweenThumbs,
363
+ onValueChange,
364
+ onValueCommit,
365
+ orientation = 'horizontal',
366
+ rangeClass,
367
+ 'set:onchange': inputOnChange,
368
+ 'set:oninput': inputOnInput,
369
+ step = 1,
370
+ thumbClass,
371
+ trackClass,
372
+ type: _type,
373
+ value,
374
+ verticalInputClass,
375
+ ...inputAttrs
376
+ }) => {
377
+ const disabledFlag = disabled ? true : undefined
378
+ const preview = valuesFrom(
379
+ value ?? defaultValue,
380
+ toNumber(min, 0),
381
+ toNumber(max, 100),
382
+ sliderStep(step),
383
+ toNumber(minStepsBetweenThumbs, 0),
384
+ )
385
+ const field = FieldContext()
386
+ const groupAttrs = field && preview.length > 1 ? field.groupAttrs : undefined
387
+ const controlAttrs = field && preview.length === 1 ? field.controlAttrs : undefined
388
+ const effectiveInputAttrs = controlAttrs ? { ...controlAttrs, ...inputAttrs } : inputAttrs
389
+
390
+ return (
391
+ <SliderRoot
392
+ defaultValue={defaultValue}
393
+ disabled={disabledFlag}
394
+ inputAttrs={effectiveInputAttrs}
395
+ inputClass={inputClass}
396
+ inputOnChange={inputOnChange}
397
+ inputOnInput={inputOnInput}
398
+ inverted={inverted}
399
+ max={max}
400
+ min={min}
401
+ minStepsBetweenThumbs={minStepsBetweenThumbs}
402
+ onValueChange={onValueChange}
403
+ onValueCommit={onValueCommit}
404
+ orientation={orientation}
405
+ rangeClass={rangeClass}
406
+ step={step}
407
+ thumbClass={thumbClass}
408
+ trackClass={trackClass}
409
+ value={value}
410
+ verticalInputClass={verticalInputClass}
411
+ attr:aria-describedby={groupAttrs?.['aria-describedby']}
412
+ attr:aria-labelledby={groupAttrs?.['aria-labelledby']}
413
+ attr:aria-disabled={flag(disabled)}
414
+ attr:class={classes}
415
+ attr:data-disabled={flag(disabled)}
416
+ attr:data-inverted={flag(inverted)}
417
+ attr:data-orientation={orientation}
418
+ attr:data-slot="slider"
419
+ attr:role={groupAttrs ? 'group' : undefined}
420
+ />
421
+ )
422
+ }
423
+
424
+ export { Slider }
package/src/switch.tsx ADDED
@@ -0,0 +1,104 @@
1
+ import type { IntrinsicElements, Stateful, Stateless } from 'ajo'
2
+ import { callHandler, controlled } from 'ajo-cloves'
3
+
4
+ /** Props for a checkbox-backed binary switch and its visual thumb. */
5
+ export type SwitchArgs = IntrinsicElements['input'] & {
6
+ /** Controlled checked state. */
7
+ checked?: boolean
8
+ /** Initial checked state for uncontrolled usage. */
9
+ defaultChecked?: boolean
10
+ /** Called when the native checkbox state changes. */
11
+ onCheckedChange?: (checked: boolean, event: Event) => void
12
+ /** Classes for the hidden input element. */
13
+ inputClass?: string
14
+ /** Size marker mirrored as `data-size`. */
15
+ size?: string
16
+ /** Classes for the thumb element. */
17
+ thumbClass?: string
18
+ }
19
+
20
+ type SwitchRootArgs = {
21
+ checked?: boolean
22
+ defaultChecked?: boolean
23
+ inputAttrs: Record<string, unknown>
24
+ inputClass?: string
25
+ onCheckedChange?: SwitchArgs['onCheckedChange']
26
+ role: unknown
27
+ setChecked?: unknown
28
+ setOnChange?: unknown
29
+ size?: string
30
+ thumbClass?: string
31
+ value: unknown
32
+ }
33
+
34
+ const SwitchRoot: Stateful<SwitchRootArgs, 'span'> = function* ({ checked, defaultChecked, setChecked }) {
35
+ let onCheckedChange: SwitchArgs['onCheckedChange']
36
+ const state = controlled<boolean>(this, {
37
+ fallback: Boolean(checked ?? defaultChecked ?? setChecked),
38
+ onChange: (next, event) => onCheckedChange?.(next, event as Event),
39
+ })
40
+
41
+ for (const args of this) {
42
+ const initialChecked = args.checked ?? args.defaultChecked
43
+ onCheckedChange = args.onCheckedChange
44
+ state.sync(args.checked != null ? Boolean(args.checked) : undefined)
45
+
46
+ yield (
47
+ <>
48
+ <input
49
+ {...args.inputAttrs}
50
+ checked={initialChecked}
51
+ class={args.inputClass}
52
+ data-slot="switch-input"
53
+ role={args.role as string | undefined}
54
+ set:checked={state.value}
55
+ set:onchange={(event: Event) => {
56
+ callHandler(args.setOnChange, event)
57
+ state.set((event.currentTarget as HTMLInputElement).checked, event)
58
+ }}
59
+ type="checkbox"
60
+ value={args.value}
61
+ />
62
+ <span aria-hidden="true" class={args.thumbClass} data-slot="switch-thumb" />
63
+ </>
64
+ )
65
+ }
66
+ }
67
+
68
+ SwitchRoot.is = 'span'
69
+
70
+ /** Unstyled native switch control with form behavior. */
71
+ const Switch: Stateless<SwitchArgs> = ({
72
+ checked,
73
+ class: classes,
74
+ defaultChecked,
75
+ inputClass,
76
+ onCheckedChange,
77
+ role = 'switch',
78
+ size,
79
+ thumbClass,
80
+ 'set:checked': setChecked,
81
+ 'set:onchange': setOnChange,
82
+ type: _type,
83
+ value = 'on',
84
+ ...attrs
85
+ }) => (
86
+ <SwitchRoot
87
+ checked={checked}
88
+ defaultChecked={defaultChecked}
89
+ inputAttrs={attrs}
90
+ inputClass={inputClass}
91
+ onCheckedChange={onCheckedChange}
92
+ role={role}
93
+ setChecked={setChecked}
94
+ setOnChange={setOnChange}
95
+ size={size}
96
+ thumbClass={thumbClass}
97
+ value={value}
98
+ attr:class={classes}
99
+ attr:data-size={size}
100
+ attr:data-slot="switch"
101
+ />
102
+ )
103
+
104
+ export { Switch }