@adea-ai/ui 0.109.0 → 0.110.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 (56) hide show
  1. package/README.md +14 -0
  2. package/dist/components/composites/update-dialog/update-dialog.js +1 -1
  3. package/dist/components/layout/contextual-sidebar/contextual-sidebar.d.ts.map +1 -1
  4. package/dist/components/layout/contextual-sidebar/contextual-sidebar.js +45 -35
  5. package/dist/components/layout/contextual-sidebar/contextual-sidebar.js.map +1 -1
  6. package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.d.ts +12 -0
  7. package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.d.ts.map +1 -0
  8. package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.js +36 -0
  9. package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.js.map +1 -0
  10. package/dist/components/layout/split-layout/index.js +2 -2
  11. package/dist/components/layout/split-layout/model.d.ts +13 -0
  12. package/dist/components/layout/split-layout/model.d.ts.map +1 -1
  13. package/dist/components/layout/split-layout/model.js +55 -18
  14. package/dist/components/layout/split-layout/model.js.map +1 -1
  15. package/dist/components/ui/alert-dialog/alert-dialog.d.ts.map +1 -1
  16. package/dist/components/ui/alert-dialog/alert-dialog.js +2 -6
  17. package/dist/components/ui/alert-dialog/alert-dialog.js.map +1 -1
  18. package/dist/components/ui/board/board.js +1 -1
  19. package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
  20. package/dist/components/ui/dialog/dialog.js +2 -6
  21. package/dist/components/ui/dialog/dialog.js.map +1 -1
  22. package/dist/components/ui/item/item.d.ts +1 -1
  23. package/dist/components/ui/sheet/sheet.d.ts.map +1 -1
  24. package/dist/components/ui/sheet/sheet.js +2 -5
  25. package/dist/components/ui/sheet/sheet.js.map +1 -1
  26. package/dist/components/ui/typography/typography.d.ts.map +1 -1
  27. package/dist/components/ui/typography/typography.js +2 -2
  28. package/dist/components/ui/typography/typography.js.map +1 -1
  29. package/dist/index.js +3 -3
  30. package/dist/lib/overlay.d.ts +5 -0
  31. package/dist/lib/overlay.d.ts.map +1 -1
  32. package/dist/lib/overlay.js +6 -1
  33. package/dist/lib/overlay.js.map +1 -1
  34. package/dist/r/alert-dialog.json +0 -1
  35. package/dist/r/contextual-sidebar.json +5 -0
  36. package/dist/r/dialog.json +0 -1
  37. package/dist/r/registry.json +5 -3
  38. package/dist/r/sheet.json +0 -1
  39. package/dist/r/src/components/layout/contextual-sidebar/contextual-sidebar.tsx +41 -16
  40. package/dist/r/src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx +28 -0
  41. package/dist/r/src/components/layout/split-layout/model.ts +62 -10
  42. package/dist/r/src/components/ui/alert-dialog/alert-dialog.tsx +8 -8
  43. package/dist/r/src/components/ui/dialog/dialog.tsx +8 -8
  44. package/dist/r/src/components/ui/sheet/sheet.tsx +2 -7
  45. package/dist/r/src/components/ui/typography/typography.tsx +3 -2
  46. package/dist/r/src/lib/overlay.ts +6 -0
  47. package/package.json +1 -1
  48. package/registry.json +5 -3
  49. package/src/components/layout/contextual-sidebar/contextual-sidebar.tsx +41 -16
  50. package/src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx +28 -0
  51. package/src/components/layout/split-layout/model.ts +62 -10
  52. package/src/components/ui/alert-dialog/alert-dialog.tsx +8 -8
  53. package/src/components/ui/dialog/dialog.tsx +8 -8
  54. package/src/components/ui/sheet/sheet.tsx +2 -7
  55. package/src/components/ui/typography/typography.tsx +3 -2
  56. package/src/lib/overlay.ts +6 -0
@@ -41,6 +41,10 @@ export type SplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readon
41
41
  splitId: string
42
42
  }>
43
43
 
44
+ export type BalancedSplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<
45
+ Pick<SplitPaneInput<L>, 'placement' | 'leaf' | 'splitId'>
46
+ >
47
+
44
48
  function replaceLeaf<L extends SplitLayoutLeaf>(
45
49
  node: SplitLayoutNode<L>,
46
50
  leafId: string,
@@ -86,14 +90,19 @@ function assertUniqueInput<L extends SplitLayoutLeaf>(
86
90
  if (typeof splitId !== 'string' || splitId.length === 0)
87
91
  throw new Error('invalid_state: split identity must be nonempty')
88
92
  const ids = new Set<string>()
93
+ const splitIds: string[] = []
89
94
  const visit = (node: SplitLayoutNode<L>) => {
90
95
  if (ids.has(node.id)) throw new Error('invalid_state: duplicate pane id')
91
96
  ids.add(node.id)
92
- if (node.kind === 'split') node.children.forEach(visit)
97
+ if (node.kind === 'split') {
98
+ splitIds.push(node.id)
99
+ node.children.forEach(visit)
100
+ }
93
101
  }
94
102
  visit(state.center)
95
103
  if (ids.has(leaf.id) || ids.has(splitId) || leaf.id === splitId)
96
104
  throw new Error('invalid_state: duplicate pane id')
105
+ return splitIds
97
106
  }
98
107
 
99
108
  /** Visual-tree validation only; applications still decode and scope persisted preferences. */
@@ -163,6 +172,51 @@ export function splitPane<L extends SplitLayoutLeaf>(
163
172
  return { ...state, center, focusedLeafId: input.leaf.id, closed: [] }
164
173
  }
165
174
 
175
+ /**
176
+ * Add a pane beside the selected leaf and reflow the visual tree into a
177
+ * balanced row-major grid. One or two leaves share one row; from three leaves
178
+ * onward, leaves fill a first row and then a second row, with no more than four
179
+ * columns in either. Each row gives its leaves equal width and the two rows
180
+ * equal height. Existing opaque leaf objects are preserved. Split IDs are
181
+ * recycled across rebuilt branches, whose axis and descendants can change;
182
+ * resize actions must resolve against the current tree. Ratios are recomputed.
183
+ * This is opt-in: callers
184
+ * that need an explicit axis and ratio should keep using `splitPane`.
185
+ */
186
+ export function splitPaneBalanced<L extends SplitLayoutLeaf>(
187
+ state: SplitLayoutState<L>,
188
+ targetLeafId: string,
189
+ input: BalancedSplitPaneInput<L>
190
+ ): SplitLayoutState<L> {
191
+ const leaves = [...listLeaves(state.center)]
192
+ const targetIndex = leaves.findIndex((leaf) => leaf.id === targetLeafId)
193
+ if (targetIndex < 0) return state
194
+ if (leaves.length >= MAX_LAYOUT_LEAVES)
195
+ throw new Error('limit_exceeded: center layout has eight leaves')
196
+ const splitIds = assertUniqueInput(state, input.leaf, input.splitId)
197
+
198
+ const insertionIndex = targetIndex + (input.placement === 'before' ? 0 : 1)
199
+ leaves.splice(insertionIndex, 0, input.leaf)
200
+
201
+ const build = (start: number, end: number, vertical = false): SplitLayoutNode<L> => {
202
+ if (end - start === 1) return leaves[start]!
203
+ const middle = start + Math.ceil((end - start) / 2)
204
+ // The new identity belongs to the lowest branch separating the selected
205
+ // pane from its new neighbour. Other branches reuse the existing ID pool.
206
+ const id = middle === targetIndex + 1 ? input.splitId : splitIds.shift()!
207
+ return {
208
+ kind: 'split',
209
+ id,
210
+ direction: vertical ? 'column' : 'row',
211
+ ratio: vertical ? 0.5 : (middle - start) / (end - start),
212
+ children: [build(start, middle), build(middle, end)],
213
+ }
214
+ }
215
+ const center = build(0, leaves.length, leaves.length > 2)
216
+ assertLayoutTree(center)
217
+ return { ...state, center, focusedLeafId: input.leaf.id, closed: [] }
218
+ }
219
+
166
220
  export function closePane<L extends SplitLayoutLeaf>(
167
221
  state: SplitLayoutState<L>,
168
222
  leafId: string,
@@ -287,21 +341,19 @@ export function resizeSplit<L extends SplitLayoutLeaf>(
287
341
  export function normalizeLayout<L extends SplitLayoutLeaf>(
288
342
  state: SplitLayoutState<L>
289
343
  ): SplitLayoutState<L> {
290
- const visit = (node: SplitLayoutNode<L>): { node: SplitLayoutNode<L>; changed: boolean } => {
291
- if (node.kind === 'leaf') return { node, changed: false }
344
+ const visit = (node: SplitLayoutNode<L>): SplitLayoutNode<L> => {
345
+ if (node.kind === 'leaf') return node
292
346
  const first = visit(node.children[0])
293
347
  const second = visit(node.children[1])
294
348
  const ratio = Number.isFinite(node.ratio)
295
349
  ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, node.ratio))
296
350
  : (MIN_SPLIT_RATIO + MAX_SPLIT_RATIO) / 2
297
- const changed = first.changed || second.changed || ratio !== node.ratio
298
- return {
299
- node: changed ? { ...node, ratio, children: [first.node, second.node] } : node,
300
- changed,
301
- }
351
+ return first === node.children[0] && second === node.children[1] && ratio === node.ratio
352
+ ? node
353
+ : { ...node, ratio, children: [first, second] }
302
354
  }
303
- const result = visit(state.center)
304
- return result.changed ? { ...state, center: result.node } : state
355
+ const center = visit(state.center)
356
+ return center === state.center ? state : { ...state, center }
305
357
  }
306
358
 
307
359
  /** The leaf immediately after (`1`) or before (`-1`) the given leaf in reading order. */
@@ -5,9 +5,14 @@ import {
5
5
  // Use the alert-specific export rather than DialogRoot.Content, which other imports can replace.
6
6
  import type { ComponentProps } from 'solid-js'
7
7
  import { createEffect, createSignal, splitProps } from 'solid-js'
8
- import { overlayMotion, overlayPositioner, overlayScrim, overlaySurface } from '#lib/overlay'
8
+ import {
9
+ overlayMotion,
10
+ overlayPositioner,
11
+ overlayScrim,
12
+ overlaySurface,
13
+ overlayTitle,
14
+ } from '#lib/overlay'
9
15
  import { cn } from '#lib/utils'
10
- import { headingVariants } from '../typography'
11
16
 
12
17
  /**
13
18
  * AlertDialog.
@@ -116,12 +121,7 @@ export function AlertDialogFooter(props: ComponentProps<'div'>) {
116
121
 
117
122
  export function AlertDialogTitle(props: ComponentProps<typeof KobalteAlertDialog.Title>) {
118
123
  const [local, rest] = splitProps(props, ['class'])
119
- return (
120
- <KobalteAlertDialog.Title
121
- class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
122
- {...rest}
123
- />
124
- )
124
+ return <KobalteAlertDialog.Title class={cn(overlayTitle, local.class)} {...rest} />
125
125
  }
126
126
 
127
127
  export function AlertDialogDescription(
@@ -7,9 +7,14 @@ import {
7
7
  import { X } from 'lucide-solid'
8
8
  import type { Accessor, ComponentProps, JSX } from 'solid-js'
9
9
  import { createRenderEffect, onCleanup, Show, splitProps } from 'solid-js'
10
- import { overlayMotion, overlayPositioner, overlayScrim, overlaySurface } from '#lib/overlay'
10
+ import {
11
+ overlayMotion,
12
+ overlayPositioner,
13
+ overlayScrim,
14
+ overlaySurface,
15
+ overlayTitle,
16
+ } from '#lib/overlay'
11
17
  import { cn } from '#lib/utils'
12
- import { headingVariants } from '../typography'
13
18
 
14
19
  type FocusCycle = {
15
20
  content: HTMLElement
@@ -326,12 +331,7 @@ export function DialogFooter(props: ComponentProps<'div'>) {
326
331
 
327
332
  export function DialogTitle(props: ComponentProps<typeof KobalteDialog.Title>) {
328
333
  const [local, rest] = splitProps(props, ['class'])
329
- return (
330
- <KobalteDialog.Title
331
- class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
332
- {...rest}
333
- />
334
- )
334
+ return <KobalteDialog.Title class={cn(overlayTitle, local.class)} {...rest} />
335
335
  }
336
336
 
337
337
  export function DialogDescription(props: ComponentProps<typeof KobalteDialog.Description>) {
@@ -8,9 +8,9 @@ import { X } from 'lucide-solid'
8
8
  import type { Accessor, ComponentProps, JSX } from 'solid-js'
9
9
  import { Show, splitProps } from 'solid-js'
10
10
  import { cva, type VariantProps } from '#lib/variants'
11
+ import { overlayTitle } from '#lib/overlay'
11
12
  import { cn } from '#lib/utils'
12
13
  import { createDialogFocusRestoration, DialogOverlay } from '../dialog/dialog'
13
- import { headingVariants } from '../typography'
14
14
 
15
15
  /**
16
16
  * Sheet.
@@ -215,12 +215,7 @@ export function SheetFooter(props: ComponentProps<'div'>) {
215
215
 
216
216
  export function SheetTitle(props: ComponentProps<typeof KobalteDialog.Title>) {
217
217
  const [local, rest] = splitProps(props, ['class'])
218
- return (
219
- <KobalteDialog.Title
220
- class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
221
- {...rest}
222
- />
223
- )
218
+ return <KobalteDialog.Title class={cn(overlayTitle, local.class)} {...rest} />
224
219
  }
225
220
 
226
221
  export function SheetDescription(props: ComponentProps<typeof KobalteDialog.Description>) {
@@ -25,7 +25,8 @@ import { cn } from '#lib/utils'
25
25
  * stack already sets the rhythm (a card or dialog header), and is the only other
26
26
  * option.
27
27
  */
28
- export const headingVariants = cva('font-semibold', {
28
+ // cva only returns a closure. A heading-only consumer must discard Text's recipe.
29
+ export const headingVariants = /* @__PURE__ */ cva('font-semibold', {
29
30
  variants: {
30
31
  size: {
31
32
  /** 30px. An app-level headline — a first-run or empty-app screen. Once per app. */
@@ -130,7 +131,7 @@ export function Heading<T extends ValidComponent = 'h2'>(props: HeadingProps<T>)
130
131
  *
131
132
  * Defaults: `body` renders a `p`, `code` a `code`, every other variant a `span`.
132
133
  */
133
- export const textVariants = cva('', {
134
+ export const textVariants = /* @__PURE__ */ cva('', {
134
135
  variants: {
135
136
  variant: {
136
137
  /** 14px / 400. Body copy and descriptions. */
@@ -14,6 +14,12 @@
14
14
  export const overlaySurface =
15
15
  'bg-popover text-popover-foreground rounded-xl border border-border shadow-lg'
16
16
 
17
+ /**
18
+ * Overlay titles have one fixed typography role. Keep this small recipe shared
19
+ * without loading the configurable Heading component into every dialog.
20
+ */
21
+ export const overlayTitle = 'font-semibold text-base tracking-tight leading-none'
22
+
17
23
  /** The span of the portal wrapper that centres a dialog-like overlay. */
18
24
  export const overlayPositioner = 'fixed inset-0 z-(--z-dialog) grid place-items-center p-4'
19
25