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
@@ -0,0 +1,186 @@
1
+ import type { IntrinsicElements, Stateful, Stateless, WithChildren } from 'ajo'
2
+ import { callHandler, callRef, dom, listen, statefulRootAttrs as rootAttrs } from 'ajo-cloves'
3
+ import { context } from 'ajo/context'
4
+ import { ariaChecked, bool, flag, syncCheckedState } from './utils'
5
+
6
+ /** Layout and keyboard-navigation axis of a radio group. */
7
+ export type RadioGroupOrientation = 'horizontal' | 'vertical'
8
+
9
+ /** Props for a radio-group fieldset and its controlled selection. */
10
+ export type RadioGroupArgs = WithChildren<IntrinsicElements['fieldset'] & {
11
+ /** Shared radio input name. */
12
+ name?: string
13
+ /** Controlled selected value. */
14
+ value?: string
15
+ /** Initial selected value for uncontrolled groups. */
16
+ defaultValue?: string
17
+ /** Layout orientation. */
18
+ orientation?: RadioGroupOrientation
19
+ /** Called when a radio item becomes checked. */
20
+ onValueChange?: (value: string, event: Event) => void
21
+ }>
22
+
23
+ /** Props for a radio item and its rendered indicator. */
24
+ export type RadioGroupItemArgs = IntrinsicElements['input'] & {
25
+ /** Classes for the indicator element. */
26
+ indicatorClass?: string
27
+ /** Classes for the native input element. */
28
+ inputClass?: string
29
+ }
30
+
31
+ type RadioGroupContextValue = {
32
+ defaultValue?: string
33
+ disabled?: boolean
34
+ name?: string
35
+ onValueChange?: (value: string, event: Event) => void
36
+ required?: boolean
37
+ value?: string
38
+ }
39
+
40
+ type RadioGroupRootArgs = WithChildren<RadioGroupContextValue>
41
+
42
+ const RadioGroupContext = context<RadioGroupContextValue | null>(null)
43
+
44
+ const syncRadioInput = (input: HTMLInputElement) =>
45
+ syncCheckedState(input, input.closest<HTMLElement>('[data-slot="radio-group-item"]'))
46
+
47
+ const syncRadioGroup = (root: HTMLElement) => {
48
+ for (const input of root.querySelectorAll<HTMLInputElement>('input[data-slot="radio-group-input"]')) {
49
+ syncRadioInput(input)
50
+ }
51
+ }
52
+
53
+ const itemState = (active: boolean | undefined, attrs: IntrinsicElements['input']) =>
54
+ bool(active ?? attrs['set:checked'] ?? attrs.checked ?? attrs.defaultChecked) ? 'checked' : 'unchecked'
55
+
56
+ const change = (
57
+ value: string | undefined,
58
+ previous: unknown,
59
+ onValueChange: RadioGroupContextValue['onValueChange'],
60
+ ) => (event: Event) => {
61
+ callHandler(previous, event)
62
+
63
+ const input = event.currentTarget as HTMLInputElement
64
+ if (input.checked && value != null) onValueChange?.(value, event)
65
+ queueMicrotask(() => {
66
+ const root = input.closest<HTMLElement>('[data-slot="radio-group"]')
67
+ if (root) syncRadioGroup(root)
68
+ else syncRadioInput(input)
69
+ })
70
+ }
71
+
72
+ const RadioGroupRoot: Stateful<RadioGroupRootArgs, 'fieldset'> = function* () {
73
+ listen(this, 'change', () => {
74
+ queueMicrotask(() => syncRadioGroup(this))
75
+ })
76
+
77
+ for (const { children, defaultValue, disabled, name, onValueChange, required, value } of this) {
78
+ RadioGroupContext({ defaultValue, disabled, name, onValueChange, required, value })
79
+ if (dom(this)) queueMicrotask(() => syncRadioGroup(this))
80
+ yield <>{children}</>
81
+ }
82
+ }
83
+
84
+ RadioGroupRoot.is = 'fieldset'
85
+
86
+ /** Unstyled radio group with native fieldset semantics. */
87
+ const RadioGroup: Stateless<RadioGroupArgs> = ({
88
+ children,
89
+ defaultValue,
90
+ disabled,
91
+ name,
92
+ onValueChange,
93
+ orientation = 'vertical',
94
+ required,
95
+ value,
96
+ ...attrs
97
+ }) => {
98
+ const disabledFlag = disabled ? true : undefined
99
+ const requiredFlag = required ? true : undefined
100
+
101
+ return (
102
+ <RadioGroupRoot
103
+ {...rootAttrs(attrs)}
104
+ defaultValue={defaultValue}
105
+ disabled={disabledFlag}
106
+ name={name}
107
+ onValueChange={onValueChange}
108
+ required={requiredFlag}
109
+ value={value}
110
+ attr:aria-orientation={orientation}
111
+ attr:data-disabled={flag(disabled)}
112
+ attr:data-orientation={orientation}
113
+ attr:data-slot="radio-group"
114
+ attr:disabled={disabledFlag}
115
+ >
116
+ {children}
117
+ </RadioGroupRoot>
118
+ )
119
+ }
120
+
121
+ /** Unstyled native radio item with indicator slot. */
122
+ const RadioGroupItem: Stateless<RadioGroupItemArgs> = ({
123
+ checked,
124
+ class: classes,
125
+ disabled,
126
+ indicatorClass,
127
+ inputClass,
128
+ name,
129
+ ref,
130
+ required,
131
+ 'set:checked': setChecked,
132
+ 'set:onchange': setOnChange,
133
+ type: _type,
134
+ value,
135
+ ...attrs
136
+ }) => {
137
+ const group = RadioGroupContext()
138
+ const itemValue = value == null ? undefined : String(value)
139
+ const ownChecked = checked == null ? undefined : Boolean(checked)
140
+ const groupChecked = group?.value != null
141
+ ? itemValue === group.value
142
+ : group?.defaultValue != null
143
+ ? itemValue === group.defaultValue
144
+ : undefined
145
+ const active = ownChecked ?? groupChecked
146
+ const syncChecked = active ?? setChecked as boolean | undefined
147
+ const state = itemState(active, { ...attrs, checked, defaultChecked: attrs.defaultChecked, 'set:checked': setChecked })
148
+ const disabledValue = disabled ?? group?.disabled
149
+
150
+ return (
151
+ <span
152
+ class={classes}
153
+ data-disabled={flag(disabledValue)}
154
+ data-slot="radio-group-item"
155
+ data-state={state}
156
+ >
157
+ <input
158
+ {...attrs}
159
+ aria-checked={ariaChecked(state)}
160
+ checked={active}
161
+ class={inputClass}
162
+ data-slot="radio-group-input"
163
+ data-state={state}
164
+ disabled={disabledValue}
165
+ name={name ?? group?.name}
166
+ ref={element => {
167
+ callRef(ref, element)
168
+ if (element) queueMicrotask(() => syncRadioInput(element))
169
+ }}
170
+ required={required ?? group?.required}
171
+ set:checked={syncChecked}
172
+ set:onchange={change(itemValue, setOnChange, group?.onValueChange)}
173
+ type="radio"
174
+ value={value}
175
+ />
176
+ <span
177
+ aria-hidden="true"
178
+ class={indicatorClass}
179
+ data-slot="radio-group-indicator"
180
+ data-state={state}
181
+ />
182
+ </span>
183
+ )
184
+ }
185
+
186
+ export { RadioGroup, RadioGroupItem }
@@ -0,0 +1,310 @@
1
+ import type { IntrinsicElements, Stateful, Stateless, WithChildren } from 'ajo'
2
+ import { clamp, listen, move, statefulRootAttrs as rootAttrs } from 'ajo-cloves'
3
+ import { context } from 'ajo/context'
4
+ import { stlx } from './utils'
5
+
6
+ /** Axis along which adjacent panels are resized. */
7
+ export type ResizableOrientation = 'horizontal' | 'vertical'
8
+ /** Panel size expressed as a percentage number or CSS-like string. */
9
+ export type ResizableSize = number | string
10
+
11
+ /** Props for a group of adjacent resizable panels. */
12
+ export type ResizablePanelGroupArgs = WithChildren<IntrinsicElements['div'] & {
13
+ /** Resize direction for adjacent panels. */
14
+ orientation?: ResizableOrientation
15
+ }>
16
+
17
+ /** Props for a panel with initial and bounded sizes. */
18
+ export type ResizablePanelArgs = WithChildren<IntrinsicElements['div'] & {
19
+ /** Initial panel size as a percentage number or string such as `50%`. */
20
+ defaultSize?: ResizableSize
21
+ /** Minimum panel size as a percentage. */
22
+ minSize?: ResizableSize
23
+ /** Maximum panel size as a percentage. */
24
+ maxSize?: ResizableSize
25
+ }>
26
+
27
+ /** Props for the pointer- and keyboard-operated resize handle. */
28
+ export type ResizableHandleArgs = WithChildren<IntrinsicElements['div'] & {
29
+ /** Disable pointer and keyboard resizing for this handle. */
30
+ disabled?: boolean
31
+ }>
32
+
33
+ /** Layout direction inherited by Resizable parts. */
34
+ export type ResizableContextValue = {
35
+ orientation: ResizableOrientation
36
+ }
37
+
38
+ type ResizeSession = {
39
+ groupSize: number
40
+ handle: HTMLElement
41
+ next: HTMLElement
42
+ nextMax: number
43
+ nextMin: number
44
+ prev: HTMLElement
45
+ prevMax: number
46
+ prevMin: number
47
+ prevStart: number
48
+ total: number
49
+ }
50
+
51
+ /** Layout direction shared by a ResizablePanelGroup with its parts. */
52
+ export const ResizableContext = context<ResizableContextValue>({ orientation: 'horizontal' })
53
+
54
+ const percent = (size: ResizableSize | undefined, fallback: number) => {
55
+ if (typeof size === 'number' && Number.isFinite(size)) return size
56
+ if (typeof size === 'string') {
57
+ const parsed = Number.parseFloat(size)
58
+ if (Number.isFinite(parsed)) return parsed
59
+ }
60
+ return fallback
61
+ }
62
+
63
+ const limit = (panel: HTMLElement, name: 'maxSize' | 'minSize', fallback: number) =>
64
+ percent(panel.dataset[name], fallback)
65
+
66
+ const style = (
67
+ orientation: ResizableOrientation,
68
+ defaultSize: ResizableSize | undefined,
69
+ minSize: ResizableSize | undefined,
70
+ maxSize: ResizableSize | undefined,
71
+ styles: unknown,
72
+ ) => {
73
+ const basis = percent(defaultSize, 0)
74
+
75
+ return stlx(
76
+ typeof styles === 'string' ? styles : undefined,
77
+ { flex: defaultSize == null ? '1 1 0' : `0 0 ${basis}%` },
78
+ minSize == null ? undefined : {
79
+ [orientation === 'horizontal' ? 'minWidth' : 'minHeight']: `${percent(minSize, 0)}%`,
80
+ },
81
+ maxSize == null ? undefined : {
82
+ [orientation === 'horizontal' ? 'maxWidth' : 'maxHeight']: `${percent(maxSize, 100)}%`,
83
+ },
84
+ )
85
+ }
86
+
87
+ const mainSize = (element: HTMLElement, orientation: ResizableOrientation) => {
88
+ const box = element.getBoundingClientRect()
89
+ return orientation === 'horizontal' ? box.width : box.height
90
+ }
91
+
92
+ const previousPanel = (handle: HTMLElement) => {
93
+ let node = handle.previousElementSibling
94
+ while (node && !(node instanceof HTMLElement && node.matches('[data-slot="resizable-panel"]'))) {
95
+ node = node.previousElementSibling
96
+ }
97
+ return node instanceof HTMLElement ? node : null
98
+ }
99
+
100
+ const nextPanel = (handle: HTMLElement) => {
101
+ let node = handle.nextElementSibling
102
+ while (node && !(node instanceof HTMLElement && node.matches('[data-slot="resizable-panel"]'))) {
103
+ node = node.nextElementSibling
104
+ }
105
+ return node instanceof HTMLElement ? node : null
106
+ }
107
+
108
+ const owningGroup = (handle: HTMLElement) =>
109
+ handle.closest<HTMLElement>('[data-slot="resizable-panel-group"]')
110
+
111
+ const setPanelSize = (panel: HTMLElement, px: number, groupSize: number) => {
112
+ const pct = groupSize > 0 ? px / groupSize * 100 : 0
113
+ panel.dataset.size = String(pct)
114
+ panel.style.flex = `0 0 ${px}px`
115
+ panel.dispatchEvent(new CustomEvent('resize', { detail: { size: pct } }))
116
+ }
117
+
118
+ const setHandleValue = (session: ResizeSession, prev: number) => {
119
+ const pct = session.groupSize > 0 ? prev / session.groupSize * 100 : 0
120
+ session.handle.setAttribute('aria-valuenow', String(Math.round(clamp(pct, 0, 100))))
121
+ }
122
+
123
+ const applyResize = (session: ResizeSession, delta: number) => {
124
+ // Conflicting panel constraints can invert the bounds; the lower bound wins.
125
+ const min = Math.max(session.prevMin, session.total - session.nextMax)
126
+ const max = Math.min(session.prevMax, session.total - session.nextMin)
127
+ const prev = clamp(session.prevStart + delta, min, Math.max(min, max))
128
+ const next = session.total - prev
129
+
130
+ setPanelSize(session.prev, prev, session.groupSize)
131
+ setPanelSize(session.next, next, session.groupSize)
132
+ setHandleValue(session, prev)
133
+ }
134
+
135
+ const session = (
136
+ group: HTMLElement,
137
+ handle: HTMLElement,
138
+ orientation: ResizableOrientation,
139
+ ): ResizeSession | null => {
140
+ const prev = previousPanel(handle)
141
+ const next = nextPanel(handle)
142
+ const groupSize = mainSize(group, orientation)
143
+ if (!prev || !next || groupSize <= 0) return null
144
+
145
+ const prevStart = mainSize(prev, orientation)
146
+ const nextStart = mainSize(next, orientation)
147
+ const total = prevStart + nextStart
148
+
149
+ const active = {
150
+ groupSize,
151
+ handle,
152
+ next,
153
+ nextMax: groupSize * limit(next, 'maxSize', 100) / 100,
154
+ nextMin: groupSize * limit(next, 'minSize', 0) / 100,
155
+ prev,
156
+ prevMax: groupSize * limit(prev, 'maxSize', 100) / 100,
157
+ prevMin: groupSize * limit(prev, 'minSize', 0) / 100,
158
+ prevStart,
159
+ total,
160
+ }
161
+ setHandleValue(active, prevStart)
162
+ return active
163
+ }
164
+
165
+ const ResizablePanelGroupRoot: Stateful<ResizablePanelGroupArgs> = function* () {
166
+ let orientation: ResizableOrientation = 'horizontal'
167
+ let active: ResizeSession | null = null
168
+ let cursor = ''
169
+ let select = ''
170
+
171
+ const restore = () => {
172
+ if (!active) return
173
+ document.body.style.cursor = cursor
174
+ document.body.style.userSelect = select
175
+ active = null
176
+ }
177
+ this.signal.addEventListener('abort', restore, { once: true })
178
+
179
+ const drag = move(this, {
180
+ onMove: data => {
181
+ if (active) applyResize(active, orientation === 'horizontal' ? data.dx : data.dy)
182
+ },
183
+ onEnd: () => restore(),
184
+ })
185
+
186
+ const startPointer = (event: PointerEvent) => {
187
+ if (active || event.button !== 0) return
188
+
189
+ const handle = (event.target as HTMLElement | null)?.closest<HTMLElement>('[data-slot="resizable-handle"]')
190
+ if (!handle || owningGroup(handle) !== this || handle.getAttribute('aria-disabled') === 'true') return
191
+
192
+ const next = session(this, handle, orientation)
193
+ if (!next) return
194
+
195
+ event.preventDefault()
196
+ event.stopPropagation()
197
+
198
+ cursor = document.body.style.cursor
199
+ select = document.body.style.userSelect
200
+ document.body.style.cursor = orientation === 'horizontal' ? 'col-resize' : 'row-resize'
201
+ document.body.style.userSelect = 'none'
202
+
203
+ active = next
204
+ if (!drag.start(event)) restore()
205
+ }
206
+
207
+ const keyResize = (event: KeyboardEvent) => {
208
+ const handle = (event.target as HTMLElement | null)?.closest<HTMLElement>('[data-slot="resizable-handle"]')
209
+ if (!handle || owningGroup(handle) !== this || handle.getAttribute('aria-disabled') === 'true') return
210
+
211
+ const step = event.shiftKey ? 50 : 10
212
+ const delta = orientation === 'horizontal'
213
+ ? event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0
214
+ : event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0
215
+
216
+ if (!delta) return
217
+
218
+ const active = session(this, handle, orientation)
219
+ if (!active) return
220
+
221
+ event.preventDefault()
222
+ applyResize(active, delta)
223
+ }
224
+
225
+ listen(this, 'pointerdown', startPointer)
226
+ listen(this, 'keydown', keyResize)
227
+
228
+ for (const args of this) {
229
+ orientation = args.orientation ?? 'horizontal'
230
+ ResizableContext({ orientation })
231
+ yield <>{args.children}</>
232
+ }
233
+ }
234
+
235
+ /** Unstyled resizable panel group for split layouts. */
236
+ const ResizablePanelGroup: Stateless<ResizablePanelGroupArgs> = ({
237
+ children,
238
+ orientation = 'horizontal',
239
+ ...attrs
240
+ }) => (
241
+ <ResizablePanelGroupRoot
242
+ {...rootAttrs(attrs)}
243
+ orientation={orientation}
244
+ attr:aria-orientation={orientation}
245
+ attr:data-orientation={orientation}
246
+ attr:data-panel-group-direction={orientation}
247
+ attr:data-slot="resizable-panel-group"
248
+ >
249
+ {children}
250
+ </ResizablePanelGroupRoot>
251
+ )
252
+
253
+ /** Unstyled resizable flex panel. */
254
+ const ResizablePanel: Stateless<ResizablePanelArgs> = ({
255
+ children,
256
+ defaultSize,
257
+ maxSize,
258
+ minSize,
259
+ style: styles,
260
+ ...attrs
261
+ }) => {
262
+ const { orientation } = ResizableContext()
263
+ const initial = percent(defaultSize, 0)
264
+
265
+ return (
266
+ <div
267
+ {...attrs}
268
+ data-max-size={maxSize == null ? undefined : percent(maxSize, 100)}
269
+ data-min-size={minSize == null ? undefined : percent(minSize, 0)}
270
+ data-size={defaultSize == null ? undefined : initial}
271
+ data-slot="resizable-panel"
272
+ style={style(orientation, defaultSize, minSize, maxSize, styles)}
273
+ >
274
+ {children}
275
+ </div>
276
+ )
277
+ }
278
+
279
+ /** Unstyled resize separator between adjacent panels. */
280
+ const ResizableHandle: Stateless<ResizableHandleArgs> = ({
281
+ children,
282
+ disabled,
283
+ ...attrs
284
+ }) => {
285
+ const { orientation } = ResizableContext()
286
+ const separator = orientation === 'vertical' ? 'horizontal' : 'vertical'
287
+
288
+ return (
289
+ <div
290
+ {...attrs}
291
+ aria-disabled={disabled ? 'true' : undefined}
292
+ aria-orientation={separator}
293
+ aria-valuemax="100"
294
+ aria-valuemin="0"
295
+ data-orientation={separator}
296
+ data-panel-group-direction={orientation}
297
+ data-slot="resizable-handle"
298
+ role="separator"
299
+ tabindex={disabled ? -1 : 0}
300
+ >
301
+ {children}
302
+ </div>
303
+ )
304
+ }
305
+
306
+ export {
307
+ ResizableHandle,
308
+ ResizablePanel,
309
+ ResizablePanelGroup,
310
+ }