@adea-ai/ui 0.89.1 → 0.93.2

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 (102) hide show
  1. package/README.md +22 -5
  2. package/dist/NOTICE +19 -0
  3. package/dist/components/composites/list-row/index.d.ts +1 -0
  4. package/dist/components/composites/list-row/index.d.ts.map +1 -1
  5. package/dist/components/composites/list-row/index.js +2 -1
  6. package/dist/components/composites/list-row/list-row-control.d.ts +20 -0
  7. package/dist/components/composites/list-row/list-row-control.d.ts.map +1 -0
  8. package/dist/components/composites/list-row/list-row-control.js +96 -0
  9. package/dist/components/composites/list-row/list-row-control.js.map +1 -0
  10. package/dist/components/composites/list-row/list-row.d.ts +15 -35
  11. package/dist/components/composites/list-row/list-row.d.ts.map +1 -1
  12. package/dist/components/composites/list-row/list-row.js +88 -135
  13. package/dist/components/composites/list-row/list-row.js.map +1 -1
  14. package/dist/components/composites/tree/index.d.ts +4 -0
  15. package/dist/components/composites/tree/index.d.ts.map +1 -0
  16. package/dist/components/composites/tree/index.js +2 -0
  17. package/dist/components/composites/tree/tree-navigation.d.ts +46 -0
  18. package/dist/components/composites/tree/tree-navigation.d.ts.map +1 -0
  19. package/dist/components/composites/tree/tree-navigation.js +95 -0
  20. package/dist/components/composites/tree/tree-navigation.js.map +1 -0
  21. package/dist/components/composites/tree/tree.d.ts +64 -0
  22. package/dist/components/composites/tree/tree.d.ts.map +1 -0
  23. package/dist/components/composites/tree/tree.js +311 -0
  24. package/dist/components/composites/tree/tree.js.map +1 -0
  25. package/dist/components/conversation/message-composer.d.ts +18 -3
  26. package/dist/components/conversation/message-composer.d.ts.map +1 -1
  27. package/dist/components/conversation/message-composer.js +93 -27
  28. package/dist/components/conversation/message-composer.js.map +1 -1
  29. package/dist/components/layout/sidebar-nav/index.d.ts +1 -1
  30. package/dist/components/layout/sidebar-nav/index.d.ts.map +1 -1
  31. package/dist/components/layout/sidebar-nav/index.js +2 -2
  32. package/dist/components/layout/sidebar-nav/sidebar-nav.d.ts +19 -1
  33. package/dist/components/layout/sidebar-nav/sidebar-nav.d.ts.map +1 -1
  34. package/dist/components/layout/sidebar-nav/sidebar-nav.js +83 -17
  35. package/dist/components/layout/sidebar-nav/sidebar-nav.js.map +1 -1
  36. package/dist/components/layout/top-bar/top-bar.d.ts +6 -5
  37. package/dist/components/layout/top-bar/top-bar.d.ts.map +1 -1
  38. package/dist/components/layout/top-bar/top-bar.js +30 -7
  39. package/dist/components/layout/top-bar/top-bar.js.map +1 -1
  40. package/dist/components/ui/dialog/dialog.d.ts +2 -0
  41. package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
  42. package/dist/components/ui/dialog/dialog.js +4 -3
  43. package/dist/components/ui/dialog/dialog.js.map +1 -1
  44. package/dist/components/ui/modal-dialog/modal-dialog.d.ts +5 -2
  45. package/dist/components/ui/modal-dialog/modal-dialog.d.ts.map +1 -1
  46. package/dist/components/ui/modal-dialog/modal-dialog.js +33 -24
  47. package/dist/components/ui/modal-dialog/modal-dialog.js.map +1 -1
  48. package/dist/components/ui/textarea/index.d.ts +1 -1
  49. package/dist/components/ui/textarea/index.d.ts.map +1 -1
  50. package/dist/components/ui/textarea/index.js +2 -2
  51. package/dist/components/ui/textarea/textarea.d.ts +15 -2
  52. package/dist/components/ui/textarea/textarea.d.ts.map +1 -1
  53. package/dist/components/ui/textarea/textarea.js +47 -3
  54. package/dist/components/ui/textarea/textarea.js.map +1 -1
  55. package/dist/components/ui/tooltip/tooltip.d.ts.map +1 -1
  56. package/dist/components/ui/tooltip/tooltip.js +239 -17
  57. package/dist/components/ui/tooltip/tooltip.js.map +1 -1
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +5 -3
  61. package/dist/lib/variants.js +2 -2
  62. package/dist/r/conversation.json +1 -0
  63. package/dist/r/list-row.json +5 -0
  64. package/dist/r/registry.json +41 -2
  65. package/dist/r/src/components/composites/list-row/index.ts +1 -0
  66. package/dist/r/src/components/composites/list-row/list-row-control.tsx +108 -0
  67. package/dist/r/src/components/composites/list-row/list-row.tsx +60 -127
  68. package/dist/r/src/components/composites/tree/index.ts +3 -0
  69. package/dist/r/src/components/composites/tree/tree-navigation.ts +114 -0
  70. package/dist/r/src/components/composites/tree/tree.tsx +405 -0
  71. package/dist/r/src/components/conversation/message-composer.tsx +103 -22
  72. package/dist/r/src/components/layout/sidebar-nav/index.ts +3 -0
  73. package/dist/r/src/components/layout/sidebar-nav/sidebar-nav.tsx +75 -6
  74. package/dist/r/src/components/layout/top-bar/top-bar.tsx +34 -11
  75. package/dist/r/src/components/ui/dialog/dialog.tsx +4 -2
  76. package/dist/r/src/components/ui/modal-dialog/modal-dialog.tsx +20 -4
  77. package/dist/r/src/components/ui/textarea/index.ts +1 -1
  78. package/dist/r/src/components/ui/textarea/textarea.tsx +52 -10
  79. package/dist/r/src/components/ui/tooltip/tooltip.tsx +305 -10
  80. package/dist/r/src/styles/base.css +40 -0
  81. package/dist/r/textarea.json +3 -1
  82. package/dist/r/top-bar.json +3 -1
  83. package/dist/r/tree.json +29 -0
  84. package/package.json +4 -1
  85. package/registry.json +41 -2
  86. package/src/components/composites/list-row/index.ts +1 -0
  87. package/src/components/composites/list-row/list-row-control.tsx +108 -0
  88. package/src/components/composites/list-row/list-row.tsx +60 -127
  89. package/src/components/composites/tree/index.ts +3 -0
  90. package/src/components/composites/tree/tree-navigation.ts +114 -0
  91. package/src/components/composites/tree/tree.tsx +405 -0
  92. package/src/components/conversation/message-composer.tsx +103 -22
  93. package/src/components/layout/sidebar-nav/index.ts +3 -0
  94. package/src/components/layout/sidebar-nav/sidebar-nav.tsx +75 -6
  95. package/src/components/layout/top-bar/top-bar.tsx +34 -11
  96. package/src/components/ui/dialog/dialog.tsx +4 -2
  97. package/src/components/ui/modal-dialog/modal-dialog.tsx +20 -4
  98. package/src/components/ui/textarea/index.ts +1 -1
  99. package/src/components/ui/textarea/textarea.tsx +52 -10
  100. package/src/components/ui/tooltip/tooltip.tsx +305 -10
  101. package/src/index.ts +1 -0
  102. package/src/styles/base.css +40 -0
@@ -1,6 +1,15 @@
1
- import { Tooltip as KobalteTooltip } from '@kobalte/core/tooltip'
1
+ import { Tooltip as KobalteTooltip, useTooltipContext } from '@kobalte/core/tooltip'
2
2
  import type { ComponentProps } from 'solid-js'
3
- import { createContext, splitProps, useContext } from 'solid-js'
3
+ import {
4
+ createContext,
5
+ createEffect,
6
+ createSignal,
7
+ createUniqueId,
8
+ onCleanup,
9
+ splitProps,
10
+ useContext,
11
+ } from 'solid-js'
12
+ import { isServer } from 'solid-js/web'
4
13
  import { cn } from '../../../lib/utils'
5
14
 
6
15
  /**
@@ -33,6 +42,99 @@ const tooltipDefaults: TooltipTiming = {
33
42
 
34
43
  const TooltipTimingContext = createContext<TooltipTiming>(tooltipDefaults)
35
44
 
45
+ type TooltipInteraction = {
46
+ id: string
47
+ trigger: () => HTMLElement | undefined
48
+ setTrigger: (element: HTMLElement) => void
49
+ isOpen: () => boolean
50
+ isDisabled: () => boolean
51
+ isTriggerFocused: () => boolean
52
+ markOpenIntent: () => void
53
+ clearOpenIntent: () => void
54
+ requestCloseFromAnotherTooltip: () => void
55
+ markPointerCloseIntent: () => void
56
+ clearPointerCloseIntent: () => void
57
+ markCloseIntent: () => void
58
+ }
59
+
60
+ const TooltipInteractionContext = createContext<TooltipInteraction>()
61
+ const openTooltipInteractions = new Set<TooltipInteraction>()
62
+ let pendingTooltipOpenId: string | undefined
63
+ let tooltipScrollListenerAttached = false
64
+
65
+ function clearPendingTooltipOpen(id: string) {
66
+ if (pendingTooltipOpenId === id) pendingTooltipOpenId = undefined
67
+ }
68
+
69
+ function tooltipScrollContainsTrigger(event: Event, trigger: HTMLElement) {
70
+ const target = event.target
71
+ return target === window || (target instanceof Node && target.contains(trigger))
72
+ }
73
+
74
+ function handleTooltipScroll(event: Event) {
75
+ for (const interaction of openTooltipInteractions) {
76
+ const trigger = interaction.trigger()
77
+ if (interaction.isOpen() && trigger && tooltipScrollContainsTrigger(event, trigger)) {
78
+ interaction.markCloseIntent()
79
+ }
80
+ }
81
+ }
82
+
83
+ function handleTooltipPointerMove(event: PointerEvent) {
84
+ for (const interaction of openTooltipInteractions) {
85
+ const trigger = interaction.trigger()
86
+ const target = event.target
87
+ if (
88
+ interaction.isOpen() &&
89
+ interaction.isTriggerFocused() &&
90
+ trigger &&
91
+ !(target instanceof Node && trigger.contains(target))
92
+ ) {
93
+ interaction.markPointerCloseIntent()
94
+ }
95
+ }
96
+ }
97
+
98
+ function setTooltipInteractionOpen(interaction: TooltipInteraction, open: boolean) {
99
+ if (isServer) return
100
+
101
+ if (open) {
102
+ if (openTooltipInteractions.has(interaction)) return
103
+ openTooltipInteractions.add(interaction)
104
+ if (!tooltipScrollListenerAttached) {
105
+ tooltipScrollListenerAttached = true
106
+ // Register before Kobalte's global listeners so intentional scroll
107
+ // dismissal can be distinguished from its delayed pointer dismissal.
108
+ window.addEventListener('scroll', handleTooltipScroll, true)
109
+ document.addEventListener('pointermove', handleTooltipPointerMove, true)
110
+ }
111
+ } else {
112
+ openTooltipInteractions.delete(interaction)
113
+ if (openTooltipInteractions.size === 0 && tooltipScrollListenerAttached) {
114
+ tooltipScrollListenerAttached = false
115
+ window.removeEventListener('scroll', handleTooltipScroll, true)
116
+ document.removeEventListener('pointermove', handleTooltipPointerMove, true)
117
+ }
118
+ }
119
+ }
120
+
121
+ function registerTooltipInteraction(interaction: TooltipInteraction) {
122
+ onCleanup(() => {
123
+ setTooltipInteractionOpen(interaction, false)
124
+ clearPendingTooltipOpen(interaction.id)
125
+ })
126
+ }
127
+
128
+ function invokeEventHandler(handler: unknown, event: Event) {
129
+ if (Array.isArray(handler)) {
130
+ const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]
131
+ callback(data, event)
132
+ } else if (typeof handler === 'function') {
133
+ const callback = handler as (event: Event) => void
134
+ callback(event)
135
+ }
136
+ }
137
+
36
138
  export type TooltipProviderProps = {
37
139
  children?: ComponentProps<'div'>['children']
38
140
  openDelay?: number
@@ -58,19 +160,192 @@ export type TooltipRootProps = ComponentProps<typeof KobalteTooltip>
58
160
 
59
161
  export function Tooltip(props: TooltipRootProps) {
60
162
  const timing = useContext(TooltipTimingContext)
163
+ const [local, rest] = splitProps(props, ['open', 'defaultOpen', 'onOpenChange', 'disabled'])
164
+ const id = createUniqueId()
165
+ const controlled = () => local.open !== undefined
166
+ const [uncontrolledOpen, setUncontrolledOpen] = createSignal(local.defaultOpen ?? false)
167
+ const isOpen = () => (controlled() ? (local.open ?? false) : uncontrolledOpen())
168
+ const [trigger, setTrigger] = createSignal<HTMLElement>()
169
+ let closeIntent = false
170
+ let ignoreDuplicateClose = false
171
+ let onOpenChange: (next: boolean, source?: 'handoff') => void
172
+ let pointerCloseIntentUntil = 0
173
+ let pointerCloseIntentTimeout: ReturnType<typeof setTimeout> | undefined
174
+ const clearPointerCloseIntent = () => {
175
+ pointerCloseIntentUntil = 0
176
+ if (pointerCloseIntentTimeout) clearTimeout(pointerCloseIntentTimeout)
177
+ pointerCloseIntentTimeout = undefined
178
+ }
179
+ const markPointerCloseIntent = () => {
180
+ if (!isOpen()) return
181
+ clearPointerCloseIntent()
182
+ const closeDelay = props.closeDelay ?? timing.closeDelay
183
+ pointerCloseIntentUntil = Date.now() + closeDelay + 50
184
+ pointerCloseIntentTimeout = setTimeout(clearPointerCloseIntent, closeDelay + 50)
185
+ }
186
+ const markCloseIntent = () => {
187
+ closeIntent = true
188
+ clearPointerCloseIntent()
189
+ queueMicrotask(() => {
190
+ closeIntent = false
191
+ })
192
+ }
193
+ const requestCloseFromAnotherTooltip = () => {
194
+ if (!isOpen()) return
195
+
196
+ // Another trigger is an explicit handoff, even when a focused tooltip has
197
+ // a pending pointer-close intent that would otherwise suppress Kobalte's
198
+ // close callback. Route the request through the same controlled/uncontrolled
199
+ // state contract as Kobalte and ignore its duplicate callback for this turn.
200
+ ignoreDuplicateClose = true
201
+ queueMicrotask(() => {
202
+ ignoreDuplicateClose = false
203
+ })
204
+ onOpenChange(false, 'handoff')
205
+ }
206
+ const interaction: TooltipInteraction = {
207
+ id,
208
+ trigger,
209
+ setTrigger,
210
+ isOpen,
211
+ isDisabled: () => local.disabled ?? false,
212
+ isTriggerFocused: () =>
213
+ Boolean(trigger() && typeof document !== 'undefined' && trigger() === document.activeElement),
214
+ markOpenIntent: () => {
215
+ clearPointerCloseIntent()
216
+ pendingTooltipOpenId = id
217
+ // A focused tooltip ignores ordinary pointer movement, but entering or
218
+ // focusing another tooltip is an explicit transfer.
219
+ for (const openInteraction of openTooltipInteractions) {
220
+ if (openInteraction.id !== id) openInteraction.requestCloseFromAnotherTooltip()
221
+ }
222
+ },
223
+ clearOpenIntent: () => clearPendingTooltipOpen(id),
224
+ requestCloseFromAnotherTooltip,
225
+ markPointerCloseIntent,
226
+ clearPointerCloseIntent,
227
+ markCloseIntent,
228
+ }
229
+ registerTooltipInteraction(interaction)
230
+ onCleanup(() => {
231
+ clearPointerCloseIntent()
232
+ ignoreDuplicateClose = false
233
+ })
234
+ if (isOpen()) setTooltipInteractionOpen(interaction, true)
235
+ createEffect(() => setTooltipInteractionOpen(interaction, isOpen()))
236
+ onOpenChange = (next: boolean, source) => {
237
+ if (!next && source !== 'handoff' && ignoreDuplicateClose) return
238
+
239
+ if (next) {
240
+ clearPendingTooltipOpen(id)
241
+ clearPointerCloseIntent()
242
+ // Install this capture listener before Kobalte's open-state effect adds
243
+ // its scroll listener later in the same update.
244
+ setTooltipInteractionOpen(interaction, true)
245
+ } else {
246
+ const focusedTrigger = interaction.isTriggerFocused()
247
+ const anotherTooltipOpening =
248
+ pendingTooltipOpenId !== undefined && pendingTooltipOpenId !== id
249
+ const pointerClosePending = Date.now() <= pointerCloseIntentUntil
250
+ if (
251
+ source !== 'handoff' &&
252
+ focusedTrigger &&
253
+ pointerClosePending &&
254
+ !closeIntent &&
255
+ !anotherTooltipOpening
256
+ ) {
257
+ clearPointerCloseIntent()
258
+ return
259
+ }
260
+ closeIntent = false
261
+ clearPointerCloseIntent()
262
+ clearPendingTooltipOpen(id)
263
+ }
264
+
265
+ if (!controlled()) setUncontrolledOpen(next)
266
+ local.onOpenChange?.(next)
267
+ if (next) {
268
+ // A controlled parent can reject the open request without changing its
269
+ // signal, so the effect above would not rerun to release this listener.
270
+ queueMicrotask(() => {
271
+ if (!isOpen()) setTooltipInteractionOpen(interaction, false)
272
+ })
273
+ }
274
+ }
275
+
276
+ // Kobalte dismisses on Escape only when its content owns the top-most
277
+ // layer, and a handoff keeps the previous tooltip's content mounted while
278
+ // it exits — swallowing the key for the tooltip that just took over.
279
+ // Listening on document (capture) closes this tooltip first; the layer's
280
+ // own handler still runs for anything stacked above it.
281
+ const handleKeyDown = (event: KeyboardEvent) => {
282
+ if (event.key !== 'Escape') return
283
+ markCloseIntent()
284
+ onOpenChange(false)
285
+ }
286
+ createEffect(() => {
287
+ if (isServer || !isOpen()) return
288
+ document.addEventListener('keydown', handleKeyDown, true)
289
+ onCleanup(() => document.removeEventListener('keydown', handleKeyDown, true))
290
+ })
61
291
 
62
292
  return (
63
- <KobalteTooltip
64
- openDelay={props.openDelay ?? timing.openDelay}
65
- closeDelay={props.closeDelay ?? timing.closeDelay}
66
- skipDelayDuration={props.skipDelayDuration ?? timing.skipDelayDuration}
67
- {...props}
68
- />
293
+ <TooltipInteractionContext.Provider value={interaction}>
294
+ <KobalteTooltip
295
+ open={isOpen()}
296
+ onOpenChange={onOpenChange}
297
+ disabled={local.disabled}
298
+ openDelay={props.openDelay ?? timing.openDelay}
299
+ closeDelay={props.closeDelay ?? timing.closeDelay}
300
+ skipDelayDuration={props.skipDelayDuration ?? timing.skipDelayDuration}
301
+ {...rest}
302
+ />
303
+ </TooltipInteractionContext.Provider>
69
304
  )
70
305
  }
71
306
 
72
307
  export function TooltipTrigger(props: ComponentProps<typeof KobalteTooltip.Trigger>) {
73
- return <KobalteTooltip.Trigger {...props} />
308
+ const interaction = useContext(TooltipInteractionContext)
309
+ const [local, rest] = splitProps(props, [
310
+ 'ref',
311
+ 'onFocus',
312
+ 'onBlur',
313
+ 'onPointerEnter',
314
+ 'onPointerLeave',
315
+ 'onClick',
316
+ ])
317
+
318
+ return (
319
+ <KobalteTooltip.Trigger
320
+ {...rest}
321
+ ref={(element) => {
322
+ interaction?.setTrigger(element)
323
+ if (typeof local.ref === 'function') local.ref(element)
324
+ }}
325
+ onFocus={(event) => {
326
+ invokeEventHandler(local.onFocus, event)
327
+ if (!event.defaultPrevented && !interaction?.isDisabled()) interaction?.markOpenIntent()
328
+ }}
329
+ onBlur={(event) => {
330
+ invokeEventHandler(local.onBlur, event)
331
+ interaction?.clearOpenIntent()
332
+ interaction?.markCloseIntent()
333
+ }}
334
+ onPointerEnter={(event) => {
335
+ invokeEventHandler(local.onPointerEnter, event)
336
+ if (!event.defaultPrevented && !interaction?.isDisabled()) interaction?.markOpenIntent()
337
+ }}
338
+ onPointerLeave={(event) => {
339
+ invokeEventHandler(local.onPointerLeave, event)
340
+ interaction?.clearOpenIntent()
341
+ interaction?.markPointerCloseIntent()
342
+ }}
343
+ onClick={(event) => {
344
+ invokeEventHandler(local.onClick, event)
345
+ interaction?.markCloseIntent()
346
+ }}
347
+ />
348
+ )
74
349
  }
75
350
 
76
351
  export type TooltipContentProps = ComponentProps<typeof KobalteTooltip.Content> & {
@@ -79,8 +354,19 @@ export type TooltipContentProps = ComponentProps<typeof KobalteTooltip.Content>
79
354
  }
80
355
 
81
356
  export function TooltipContent(props: TooltipContentProps) {
82
- const [local, rest] = splitProps(props, ['class', 'hideArrow', 'children'])
357
+ const interaction = useContext(TooltipInteractionContext)
358
+ const [local, rest] = splitProps(props, [
359
+ 'class',
360
+ 'hideArrow',
361
+ 'children',
362
+ 'aria-hidden',
363
+ 'onEscapeKeyDown',
364
+ 'onPointerDownOutside',
365
+ ])
366
+ const context = useTooltipContext()
83
367
 
368
+ // Kobalte keeps force-mounted and exiting content present after close. Hide
369
+ // that retained tooltip from assistive technology until it opens again.
84
370
  return (
85
371
  <KobalteTooltip.Portal>
86
372
  <KobalteTooltip.Content
@@ -92,6 +378,15 @@ export function TooltipContent(props: TooltipContentProps) {
92
378
  'data-expanded:duration-150 data-closed:duration-100',
93
379
  local.class
94
380
  )}
381
+ aria-hidden={context.isOpen() ? local['aria-hidden'] : true}
382
+ onEscapeKeyDown={(event) => {
383
+ invokeEventHandler(local.onEscapeKeyDown, event)
384
+ if (!event.defaultPrevented) interaction?.markCloseIntent()
385
+ }}
386
+ onPointerDownOutside={(event) => {
387
+ invokeEventHandler(local.onPointerDownOutside, event)
388
+ if (!event.defaultPrevented) interaction?.markCloseIntent()
389
+ }}
95
390
  {...rest}
96
391
  >
97
392
  {local.children}
@@ -92,6 +92,45 @@
92
92
  }
93
93
  }
94
94
 
95
+ /* Emitted only when ListRowControl is used. A described row changes its own
96
+ height and stacks trailing actions when its available width gets tight. */
97
+ @utility list-row-description {
98
+ &[data-description] {
99
+ align-items: flex-start;
100
+ container: list-row / inline-size;
101
+ flex-wrap: wrap;
102
+ min-height: var(--row-height-lg);
103
+ padding-block: calc(var(--spacing) * 1.5);
104
+ }
105
+
106
+ &[data-description] [data-slot='list-row-content'] {
107
+ flex: 1 1 14rem;
108
+ }
109
+
110
+ @container list-row (width < 28rem) {
111
+ &[data-description] [data-slot='list-row-content'] {
112
+ align-items: stretch;
113
+ flex-direction: column;
114
+ }
115
+
116
+ &[data-description] [data-slot='list-row-label'],
117
+ &[data-description] [data-slot='list-row-trailing'] {
118
+ flex: none;
119
+ width: 100%;
120
+ }
121
+
122
+ &[data-description] [data-slot='list-row-trailing'] {
123
+ flex-wrap: wrap;
124
+ justify-content: flex-end;
125
+ }
126
+
127
+ &[data-description] [data-slot='list-row-trailing'] button {
128
+ max-width: 100%;
129
+ white-space: normal;
130
+ }
131
+ }
132
+ }
133
+
95
134
  @custom-variant data-horizontal {
96
135
  &:where([data-orientation='horizontal']) {
97
136
  @slot;
@@ -550,6 +589,7 @@
550
589
  [role='button']:not([aria-disabled='true']),
551
590
  [role='tab']:not([aria-disabled='true']),
552
591
  [role='menuitem']:not([aria-disabled='true']),
592
+ [role='menuitemradio']:not([aria-disabled='true']),
553
593
  [role='option']:not([aria-disabled='true']) {
554
594
  cursor: pointer;
555
595
  }
@@ -3,7 +3,9 @@
3
3
  "type": "registry:ui",
4
4
  "title": "Textarea",
5
5
  "description": "",
6
- "dependencies": [],
6
+ "dependencies": [
7
+ "class-variance-authority@^0.7.1"
8
+ ],
7
9
  "registryDependencies": [
8
10
  "lib"
9
11
  ],
@@ -3,7 +3,9 @@
3
3
  "type": "registry:ui",
4
4
  "title": "Top Bar",
5
5
  "description": "",
6
- "dependencies": [],
6
+ "dependencies": [
7
+ "lucide-solid@^1.48.0"
8
+ ],
7
9
  "registryDependencies": [
8
10
  "lib"
9
11
  ],
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "tree",
3
+ "type": "registry:ui",
4
+ "title": "Tree",
5
+ "description": "Keyboard-accessible tree rows with virtualization-aware focus handoff.",
6
+ "dependencies": [
7
+ "lucide-solid@^1.48.0"
8
+ ],
9
+ "registryDependencies": [
10
+ "lib"
11
+ ],
12
+ "files": [
13
+ {
14
+ "path": "src/components/composites/tree/index.ts",
15
+ "type": "registry:ui",
16
+ "target": "components/composites/tree/index.ts"
17
+ },
18
+ {
19
+ "path": "src/components/composites/tree/tree-navigation.ts",
20
+ "type": "registry:ui",
21
+ "target": "components/composites/tree/tree-navigation.ts"
22
+ },
23
+ {
24
+ "path": "src/components/composites/tree/tree.tsx",
25
+ "type": "registry:ui",
26
+ "target": "components/composites/tree/tree.tsx"
27
+ }
28
+ ]
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.89.1",
3
+ "version": "0.93.2",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
@@ -102,12 +102,15 @@
102
102
  "theme:check": "bun scripts/build-default-theme.ts --check",
103
103
  "check:packed-layout": "node --test scripts/check-packed-layout-process.test.mjs && node scripts/check-packed-layout.mjs",
104
104
  "check:packed-consumer": "bun scripts/check-packed-consumer.ts",
105
+ "check:packed-list-row": "bun scripts/check-packed-list-row.ts",
105
106
  "check:packed-conversation": "bun scripts/check-packed-conversation.ts",
106
107
  "check:packed-transcript": "bun scripts/check-packed-transcript.ts",
107
108
  "check:packed-layout-renderer": "bun scripts/check-packed-layout-renderer.ts",
108
109
  "check:packed-appearance": "bun scripts/check-packed-appearance.ts",
110
+ "check:packed-modal-dialog": "bun scripts/check-packed-modal-dialog.ts",
109
111
  "check:packed-native-select": "bun scripts/check-packed-native-select.ts",
110
112
  "check:packed-settings-navigation": "bun scripts/check-packed-settings-navigation.ts",
113
+ "check:packed-tree": "bun scripts/check-packed-tree.ts",
111
114
  "check:packed-paste-model": "node scripts/check-packed-paste-model.mjs"
112
115
  },
113
116
  "dependencies": {
package/registry.json CHANGED
@@ -1374,7 +1374,9 @@
1374
1374
  "type": "registry:ui",
1375
1375
  "title": "Textarea",
1376
1376
  "description": "",
1377
- "dependencies": [],
1377
+ "dependencies": [
1378
+ "class-variance-authority@^0.7.1"
1379
+ ],
1378
1380
  "registryDependencies": [
1379
1381
  "lib"
1380
1382
  ],
@@ -1740,7 +1742,9 @@
1740
1742
  "type": "registry:ui",
1741
1743
  "title": "Top Bar",
1742
1744
  "description": "",
1743
- "dependencies": [],
1745
+ "dependencies": [
1746
+ "lucide-solid@^1.48.0"
1747
+ ],
1744
1748
  "registryDependencies": [
1745
1749
  "lib"
1746
1750
  ],
@@ -1946,6 +1950,11 @@
1946
1950
  "type": "registry:ui",
1947
1951
  "target": "components/composites/list-row/index.ts"
1948
1952
  },
1953
+ {
1954
+ "path": "src/components/composites/list-row/list-row-control.tsx",
1955
+ "type": "registry:ui",
1956
+ "target": "components/composites/list-row/list-row-control.tsx"
1957
+ },
1949
1958
  {
1950
1959
  "path": "src/components/composites/list-row/list-row.tsx",
1951
1960
  "type": "registry:ui",
@@ -2011,6 +2020,35 @@
2011
2020
  }
2012
2021
  ]
2013
2022
  },
2023
+ {
2024
+ "name": "tree",
2025
+ "type": "registry:ui",
2026
+ "title": "Tree",
2027
+ "description": "Keyboard-accessible tree rows with virtualization-aware focus handoff.",
2028
+ "dependencies": [
2029
+ "lucide-solid@^1.48.0"
2030
+ ],
2031
+ "registryDependencies": [
2032
+ "lib"
2033
+ ],
2034
+ "files": [
2035
+ {
2036
+ "path": "src/components/composites/tree/index.ts",
2037
+ "type": "registry:ui",
2038
+ "target": "components/composites/tree/index.ts"
2039
+ },
2040
+ {
2041
+ "path": "src/components/composites/tree/tree-navigation.ts",
2042
+ "type": "registry:ui",
2043
+ "target": "components/composites/tree/tree-navigation.ts"
2044
+ },
2045
+ {
2046
+ "path": "src/components/composites/tree/tree.tsx",
2047
+ "type": "registry:ui",
2048
+ "target": "components/composites/tree/tree.tsx"
2049
+ }
2050
+ ]
2051
+ },
2014
2052
  {
2015
2053
  "name": "update-dialog",
2016
2054
  "type": "registry:ui",
@@ -2072,6 +2110,7 @@
2072
2110
  "lucide-solid@^1.48.0"
2073
2111
  ],
2074
2112
  "registryDependencies": [
2113
+ "@adea-ai/ui/action-button",
2075
2114
  "@adea-ai/ui/button",
2076
2115
  "@adea-ai/ui/button-group",
2077
2116
  "@adea-ai/ui/code-block",
@@ -1 +1,2 @@
1
1
  export { ListGroup, ListRow, type ListRowProps } from './list-row'
2
+ export { ListRowControl, type ListRowControlProps } from './list-row-control'
@@ -0,0 +1,108 @@
1
+ import type { PolymorphicProps } from '@kobalte/core/polymorphic'
2
+ import { Polymorphic } from '@kobalte/core/polymorphic'
3
+ import type { ComponentProps, JSX, ValidComponent } from 'solid-js'
4
+ import { Show, splitProps } from 'solid-js'
5
+ import { cn } from '#lib/utils'
6
+
7
+ export type ListRowControlProps<T extends ValidComponent = 'div'> = PolymorphicProps<
8
+ T,
9
+ {
10
+ class?: string
11
+ /** Marks the row as current. Sets `aria-current` for a link row. */
12
+ selected?: boolean
13
+ /** Draw the row at the compact height. */
14
+ dense?: boolean
15
+ /** Leading slot: an avatar, an icon, a checkbox. */
16
+ leading?: JSX.Element
17
+ /** Trailing slot: a count, a menu trigger, a timestamp. */
18
+ trailing?: JSX.Element
19
+ /** A second line that wraps and grows the row to fit its content. */
20
+ description?: string
21
+ /** Native button type; defaults to `button` for button rows. */
22
+ type?: ComponentProps<'button'>['type']
23
+ }
24
+ >
25
+
26
+ type ListRowControlBaseProps<T extends ValidComponent = 'div'> = ListRowControlProps<T> & {
27
+ as?: T
28
+ }
29
+
30
+ /** A tree-shakeable ListRow renderer without optional tooltip behavior. */
31
+ export function ListRowControl<T extends ValidComponent = 'div'>(props: ListRowControlProps<T>) {
32
+ const [local, rest] = splitProps(props as ListRowControlBaseProps, [
33
+ 'as',
34
+ 'class',
35
+ 'selected',
36
+ 'dense',
37
+ 'leading',
38
+ 'trailing',
39
+ 'description',
40
+ 'type',
41
+ 'tabIndex',
42
+ 'children',
43
+ ])
44
+ const hasClickHandler = () => Boolean(rest.onClick || rest['on:click'])
45
+ const rowAs = () => (local.as ?? (hasClickHandler() ? 'button' : 'div')) as T
46
+
47
+ return (
48
+ <Polymorphic
49
+ as={rowAs()}
50
+ type={rowAs() === 'button' ? (local.type ?? 'button') : local.type}
51
+ tabIndex={local.tabIndex ?? (rowAs() === 'button' || rowAs() === 'a' ? 0 : undefined)}
52
+ aria-current={local.selected ? 'true' : undefined}
53
+ data-selected={local.selected ? '' : undefined}
54
+ data-description={local.description ? '' : undefined}
55
+ class={cn(
56
+ 'group/row list-row-description flex min-w-0 items-center gap-2.5 rounded-md px-2 text-sm',
57
+ 'transition-colors ease-out outline-none',
58
+ 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
59
+ {
60
+ 'h-row-sm': local.dense && !local.description,
61
+ 'h-row-md': !local.dense && !local.description,
62
+ },
63
+ {
64
+ 'bg-primary-subtle text-foreground': local.selected,
65
+ 'hover:bg-surface-hover': !local.selected,
66
+ },
67
+ local.class
68
+ )}
69
+ {...(rest as ComponentProps<'div'>)}
70
+ >
71
+ <Show when={local.leading}>
72
+ <span
73
+ data-slot="list-row-leading"
74
+ class="flex shrink-0 items-center justify-center [&_svg]:size-4"
75
+ >
76
+ {local.leading}
77
+ </span>
78
+ </Show>
79
+ <div data-slot="list-row-content" class="flex min-w-0 flex-1 items-center gap-2.5">
80
+ <span data-slot="list-row-label" class="flex min-w-0 flex-1 flex-col">
81
+ <span class="truncate">{local.children}</span>
82
+ <Show when={local.description}>
83
+ <span
84
+ data-slot="list-row-description"
85
+ class={cn(
86
+ 'text-xs break-words',
87
+ local.selected ? 'text-foreground' : 'text-muted-foreground'
88
+ )}
89
+ >
90
+ {local.description}
91
+ </span>
92
+ </Show>
93
+ </span>
94
+ <Show when={local.trailing}>
95
+ <span
96
+ data-slot="list-row-trailing"
97
+ class={cn(
98
+ 'ms-auto flex shrink-0 items-center gap-1',
99
+ local.selected ? 'text-foreground' : 'text-muted-foreground'
100
+ )}
101
+ >
102
+ {local.trailing}
103
+ </span>
104
+ </Show>
105
+ </div>
106
+ </Polymorphic>
107
+ )
108
+ }