@adea-ai/ui 0.89.2 → 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 +39 -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 +39 -0
@@ -1,161 +1,93 @@
1
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'
2
+ import type { ComponentProps, ValidComponent } from 'solid-js'
3
+ import { createSignal, createUniqueId, Show, splitProps } from 'solid-js'
5
4
  import { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'
6
5
  import { cn } from '#lib/utils'
6
+ import { ListRowControl, type ListRowControlProps } from './list-row-control'
7
7
 
8
- /**
9
- * ListRow.
10
- *
11
- * One row in a list of like things: a project, a session, a file, a person. It
12
- * is the highest-frequency component in an application of this shape, and the
13
- * one where a half-pixel of drift is most visible — twenty rows of a sidebar
14
- * make any inconsistency in height or padding obvious.
15
- *
16
- * The structure is fixed at four slots — `leading`, `children`, `meta`,
17
- * `trailing` — so rows that show an avatar, an icon, a status dot, a timestamp
18
- * and an overflow menu all sit at the same height without the caller doing
19
- * arithmetic.
20
- *
21
- * A row is only interactive when it is given an `as` that can be activated
22
- * (an anchor, a button) or an `onClick`. Click handlers default to a native
23
- * button; a row without either remains a `div` with no hover treatment.
24
- */
25
8
  export type ListRowProps<T extends ValidComponent = 'div'> = PolymorphicProps<
26
9
  T,
27
- {
28
- class?: string
29
- /** Marks the row as current. Sets `aria-current` for a link row. */
30
- selected?: boolean
31
- /** Draw the row at the compact height. */
32
- dense?: boolean
33
- /** Leading slot: an avatar, an icon, a checkbox. */
34
- leading?: JSX.Element
35
- /** Trailing slot: a count, a menu trigger, a timestamp. */
36
- trailing?: JSX.Element
37
- /** A dimmed second line under the main content. */
38
- description?: string
10
+ ListRowControlProps<T> & {
39
11
  /** A short explanation shown on pointer hover and keyboard focus. */
40
12
  tooltip?: string
41
13
  /** Native button type; defaults to `button` for button rows. */
42
14
  type?: ComponentProps<'button'>['type']
15
+ /** Additional accessible description preserved on plain and rich rows. */
16
+ 'aria-describedby'?: string
43
17
  }
44
18
  >
45
19
 
46
- type ListRowBaseProps<T extends ValidComponent = 'div'> = ListRowProps<T> & {
47
- as?: T
48
- type?: ComponentProps<'button'>['type']
49
- }
50
-
51
- function ListRowBase<T extends ValidComponent = 'div'>(props: ListRowBaseProps<T>) {
52
- const [local, rest] = splitProps(props as ListRowProps, [
53
- 'as',
54
- 'class',
55
- 'selected',
56
- 'dense',
57
- 'leading',
58
- 'trailing',
59
- 'description',
60
- 'tooltip',
61
- 'type',
62
- 'tabIndex',
63
- 'children',
64
- ])
65
-
66
- return (
67
- <Polymorphic
68
- as={(local.as ?? 'div') as T}
69
- type={local.as === 'button' ? (local.type ?? 'button') : local.type}
70
- tabIndex={local.tabIndex ?? (local.as === 'button' || local.as === 'a' ? 0 : undefined)}
71
- aria-current={local.selected ? 'true' : undefined}
72
- data-selected={local.selected ? '' : undefined}
73
- class={cn(
74
- 'group/row flex min-w-0 items-center gap-2.5 rounded-md px-2 text-sm',
75
- 'transition-colors ease-out outline-none',
76
- 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
77
- {
78
- 'h-row-sm': local.dense && !local.description,
79
- 'h-row-md': !local.dense && !local.description,
80
- 'min-h-row-sm py-1': local.dense && local.description,
81
- 'min-h-row-md py-1.5': !local.dense && local.description,
82
- },
83
- {
84
- 'bg-primary-subtle text-foreground': local.selected,
85
- 'hover:bg-surface-hover': !local.selected,
86
- },
87
- local.class
88
- )}
89
- {...(rest as ComponentProps<'div'>)}
90
- >
91
- <Show when={local.leading}>
92
- <span class="flex size-4 shrink-0 items-center justify-center [&_svg]:size-4">
93
- {local.leading}
94
- </span>
95
- </Show>
96
- <span class="flex min-w-0 flex-1 flex-col">
97
- <span class="truncate">{local.children}</span>
98
- <Show when={local.description}>
99
- <span
100
- data-slot="list-row-description"
101
- class={cn(
102
- 'truncate text-xs',
103
- local.selected ? 'text-foreground' : 'text-muted-foreground'
104
- )}
105
- >
106
- {local.description}
107
- </span>
108
- </Show>
109
- </span>
110
- <Show when={local.trailing}>
111
- <span
112
- data-slot="list-row-trailing"
113
- class={cn(
114
- 'ms-auto flex shrink-0 items-center gap-1',
115
- local.selected ? 'text-foreground' : 'text-muted-foreground'
116
- )}
117
- >
118
- {local.trailing}
119
- </span>
120
- </Show>
121
- </Polymorphic>
122
- )
20
+ type ListRowTooltipTargetProps<T extends ValidComponent = 'div'> = Omit<ListRowProps<T>, 'as'> & {
21
+ rowAs?: T
22
+ externalDescribedBy?: string
23
+ tooltipId?: string
24
+ tooltipOpen?: () => boolean
123
25
  }
124
26
 
125
- type ListRowTooltipTargetProps<T extends ValidComponent = 'div'> = Omit<
126
- ListRowBaseProps<T>,
127
- 'as'
128
- > & { rowAs?: T }
129
-
130
27
  /** Adapts TooltipTrigger's polymorphic target to ListRow's semantic element. */
131
28
  function ListRowTooltipTarget<T extends ValidComponent = 'div'>(
132
29
  props: ListRowTooltipTargetProps<T>
133
30
  ) {
134
- const [local, rest] = splitProps(props as ListRowTooltipTargetProps, ['rowAs'])
135
- return <ListRowBase as={local.rowAs} {...(rest as ListRowBaseProps<T>)} />
31
+ const [local, rest] = splitProps(props as ListRowTooltipTargetProps, [
32
+ 'rowAs',
33
+ 'externalDescribedBy',
34
+ 'tooltipId',
35
+ 'tooltipOpen',
36
+ ])
37
+ const describedBy = () => {
38
+ const ids = [
39
+ rest['aria-describedby'],
40
+ local.externalDescribedBy,
41
+ local.tooltipOpen?.() ? local.tooltipId : undefined,
42
+ ]
43
+ .flatMap((value) => value?.split(/\s+/) ?? [])
44
+ .filter(Boolean)
45
+ return [...new Set(ids)].join(' ') || undefined
46
+ }
47
+
48
+ return (
49
+ <ListRowControl
50
+ as={local.rowAs}
51
+ {...(rest as unknown as ListRowControlProps<T>)}
52
+ aria-describedby={describedBy()}
53
+ />
54
+ )
136
55
  }
137
56
 
57
+ /**
58
+ * Rich row composition with optional hover and keyboard tooltip behavior.
59
+ * Import ListRowControl when the tooltip API is not needed and the consumer
60
+ * wants the plain renderer to tree-shake independently.
61
+ */
138
62
  export function ListRow<T extends ValidComponent = 'div'>(props: ListRowProps<T>) {
139
- const [local, rest] = splitProps(props as ListRowProps, ['as', 'tooltip'])
63
+ const [local, rest] = splitProps(props as ListRowProps<T>, ['as', 'tooltip', 'aria-describedby'])
64
+ const [tooltipOpen, setTooltipOpen] = createSignal(false)
65
+ const tooltipId = `list-row-tooltip-${createUniqueId()}`
140
66
  const hasClickHandler = () => Boolean(rest.onClick || rest['on:click'])
141
67
  const rowAs = () => (local.as ?? (hasClickHandler() ? 'button' : 'div')) as T
142
68
  const rowType = () => (rowAs() === 'button' ? (rest.type ?? 'button') : rest.type)
69
+ const rowProps = () =>
70
+ ({
71
+ ...rest,
72
+ type: rowType(),
73
+ 'aria-describedby': local['aria-describedby'],
74
+ }) as unknown as ListRowControlProps<T>
143
75
 
144
76
  return (
145
- <Show
146
- when={local.tooltip}
147
- fallback={<ListRowBase as={rowAs()} type={rowType()} {...(rest as ListRowBaseProps<T>)} />}
148
- >
149
- <Tooltip>
77
+ <Show when={local.tooltip} fallback={<ListRowControl as={rowAs()} {...rowProps()} />}>
78
+ <Tooltip open={tooltipOpen()} onOpenChange={setTooltipOpen}>
150
79
  <TooltipTrigger
151
80
  as={ListRowTooltipTarget}
152
81
  rowAs={rowAs()}
153
82
  type={rowType()}
83
+ externalDescribedBy={local['aria-describedby']}
84
+ tooltipId={tooltipId}
85
+ tooltipOpen={tooltipOpen}
154
86
  {...(rest as ListRowTooltipTargetProps<T>)}
155
87
  >
156
88
  {rest.children}
157
89
  </TooltipTrigger>
158
- <TooltipContent>{local.tooltip}</TooltipContent>
90
+ <TooltipContent id={tooltipId}>{local.tooltip}</TooltipContent>
159
91
  </Tooltip>
160
92
  </Show>
161
93
  )
@@ -163,12 +95,13 @@ export function ListRow<T extends ValidComponent = 'div'>(props: ListRowProps<T>
163
95
 
164
96
  /**
165
97
  * A group of rows with an optional heading and a scrolling body. The heading is
166
- * a plain label rather than a control: grouping is for reading, and a
167
- * disclosure on a list of five rows costs more attention than it saves.
98
+ * a plain label rather than a control: grouping is for reading, and a disclosure
99
+ * on a list of five rows costs more attention than it saves.
168
100
  */
169
- export function ListGroup(props: ComponentProps<'div'> & { label?: string; action?: JSX.Element }) {
101
+ export function ListGroup(
102
+ props: ComponentProps<'div'> & { label?: string; action?: import('solid-js').JSX.Element }
103
+ ) {
170
104
  const [local, rest] = splitProps(props, ['class', 'label', 'action', 'children'])
171
-
172
105
  return (
173
106
  <div data-slot="list-group" class={cn('flex flex-col gap-0.5', local.class)} {...rest}>
174
107
  <Show when={local.label}>
@@ -0,0 +1,3 @@
1
+ /** Keyboard-accessible tree rows with virtualization-aware focus handoff. */
2
+ export { Tree, TreeRow, type TreeProps, type TreeRowProps } from './tree'
3
+ export type { TreeItemDescriptor } from './tree-navigation'
@@ -0,0 +1,114 @@
1
+ /** A visible tree row's host-owned projection metadata. */
2
+ export type TreeItemDescriptor = {
3
+ /** Stable identity shared with the host's domain item. */
4
+ id: string
5
+ /** Stable parent identity, or null for a root item. */
6
+ parentId: string | null
7
+ /** One-based depth exposed to assistive technology. */
8
+ level: number
9
+ /** Whether this item can be expanded. */
10
+ expandable: boolean
11
+ /** Whether this item is currently expanded. */
12
+ expanded: boolean
13
+ }
14
+
15
+ export type TreeNavigationIndex = {
16
+ ids: readonly string[]
17
+ byId: ReadonlyMap<string, TreeItemDescriptor>
18
+ indexById: ReadonlyMap<string, number>
19
+ firstChildByParent: ReadonlyMap<string, string>
20
+ positionById: ReadonlyMap<string, { position: number; setSize: number }>
21
+ }
22
+
23
+ export type TreeNavigationAction =
24
+ | { type: 'focus'; id: string }
25
+ | { type: 'expand'; id: string; expanded: boolean }
26
+ | { type: 'activate'; id: string }
27
+ | { type: 'select'; id: string }
28
+
29
+ /**
30
+ * Indexes the complete expanded projection once per host update. The DOM may
31
+ * contain only a window of these rows; this index keeps keyboard movement and
32
+ * aria-setsize independent of the mounted window.
33
+ */
34
+ export function indexTreeItems(items: readonly TreeItemDescriptor[]): TreeNavigationIndex {
35
+ const byId = new Map<string, TreeItemDescriptor>()
36
+ const indexById = new Map<string, number>()
37
+ const firstChildByParent = new Map<string, string>()
38
+ const siblingsByParent = new Map<string | null, string[]>()
39
+
40
+ items.forEach((item, index) => {
41
+ if (byId.has(item.id)) {
42
+ throw new Error(`[adea-ui]: Tree visibleItems contains duplicate id "${item.id}".`)
43
+ }
44
+ byId.set(item.id, item)
45
+ indexById.set(item.id, index)
46
+
47
+ const siblings = siblingsByParent.get(item.parentId) ?? []
48
+ siblings.push(item.id)
49
+ siblingsByParent.set(item.parentId, siblings)
50
+
51
+ if (item.parentId !== null && !firstChildByParent.has(item.parentId)) {
52
+ firstChildByParent.set(item.parentId, item.id)
53
+ }
54
+ })
55
+
56
+ const positionById = new Map<string, { position: number; setSize: number }>()
57
+ for (const siblings of siblingsByParent.values()) {
58
+ siblings.forEach((id, index) => {
59
+ positionById.set(id, { position: index + 1, setSize: siblings.length })
60
+ })
61
+ }
62
+
63
+ return {
64
+ ids: items.map((item) => item.id),
65
+ byId,
66
+ indexById,
67
+ firstChildByParent,
68
+ positionById,
69
+ }
70
+ }
71
+
72
+ /** Resolves one keyboard event without owning tree data or host actions. */
73
+ export function treeNavigationAction(
74
+ index: TreeNavigationIndex,
75
+ activeId: string | null,
76
+ key: string
77
+ ): TreeNavigationAction | null {
78
+ const { ids, byId, indexById, firstChildByParent } = index
79
+ if (ids.length === 0) return null
80
+
81
+ const activeIndex = activeId === null ? undefined : indexById.get(activeId)
82
+ const active = activeId === null ? undefined : byId.get(activeId)
83
+
84
+ if (key === 'ArrowDown' || key === 'ArrowUp') {
85
+ const step = key === 'ArrowDown' ? 1 : -1
86
+ const start = activeIndex ?? (step > 0 ? -1 : 0)
87
+ const nextIndex = (start + step + ids.length) % ids.length
88
+ const id = ids[nextIndex]
89
+ return id === undefined ? null : { type: 'focus', id }
90
+ }
91
+
92
+ if (key === 'Home') return { type: 'focus', id: ids[0]! }
93
+ if (key === 'End') return { type: 'focus', id: ids[ids.length - 1]! }
94
+ if (!active) return null
95
+
96
+ if (key === 'ArrowRight' && active.expandable) {
97
+ if (!active.expanded) return { type: 'expand', id: active.id, expanded: true }
98
+ const childId = firstChildByParent.get(active.id)
99
+ return childId ? { type: 'focus', id: childId } : null
100
+ }
101
+
102
+ if (key === 'ArrowLeft') {
103
+ if (active.expandable && active.expanded) {
104
+ return { type: 'expand', id: active.id, expanded: false }
105
+ }
106
+ if (active.parentId !== null && byId.has(active.parentId)) {
107
+ return { type: 'focus', id: active.parentId }
108
+ }
109
+ }
110
+
111
+ if (key === 'Enter') return { type: 'activate', id: active.id }
112
+ if (key === ' ' || key === 'Spacebar') return { type: 'select', id: active.id }
113
+ return null
114
+ }