@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.
- package/README.md +22 -5
- package/dist/NOTICE +19 -0
- package/dist/components/composites/list-row/index.d.ts +1 -0
- package/dist/components/composites/list-row/index.d.ts.map +1 -1
- package/dist/components/composites/list-row/index.js +2 -1
- package/dist/components/composites/list-row/list-row-control.d.ts +20 -0
- package/dist/components/composites/list-row/list-row-control.d.ts.map +1 -0
- package/dist/components/composites/list-row/list-row-control.js +96 -0
- package/dist/components/composites/list-row/list-row-control.js.map +1 -0
- package/dist/components/composites/list-row/list-row.d.ts +15 -35
- package/dist/components/composites/list-row/list-row.d.ts.map +1 -1
- package/dist/components/composites/list-row/list-row.js +88 -135
- package/dist/components/composites/list-row/list-row.js.map +1 -1
- package/dist/components/composites/tree/index.d.ts +4 -0
- package/dist/components/composites/tree/index.d.ts.map +1 -0
- package/dist/components/composites/tree/index.js +2 -0
- package/dist/components/composites/tree/tree-navigation.d.ts +46 -0
- package/dist/components/composites/tree/tree-navigation.d.ts.map +1 -0
- package/dist/components/composites/tree/tree-navigation.js +95 -0
- package/dist/components/composites/tree/tree-navigation.js.map +1 -0
- package/dist/components/composites/tree/tree.d.ts +64 -0
- package/dist/components/composites/tree/tree.d.ts.map +1 -0
- package/dist/components/composites/tree/tree.js +311 -0
- package/dist/components/composites/tree/tree.js.map +1 -0
- package/dist/components/conversation/message-composer.d.ts +18 -3
- package/dist/components/conversation/message-composer.d.ts.map +1 -1
- package/dist/components/conversation/message-composer.js +93 -27
- package/dist/components/conversation/message-composer.js.map +1 -1
- package/dist/components/layout/sidebar-nav/index.d.ts +1 -1
- package/dist/components/layout/sidebar-nav/index.d.ts.map +1 -1
- package/dist/components/layout/sidebar-nav/index.js +2 -2
- package/dist/components/layout/sidebar-nav/sidebar-nav.d.ts +19 -1
- package/dist/components/layout/sidebar-nav/sidebar-nav.d.ts.map +1 -1
- package/dist/components/layout/sidebar-nav/sidebar-nav.js +83 -17
- package/dist/components/layout/sidebar-nav/sidebar-nav.js.map +1 -1
- package/dist/components/layout/top-bar/top-bar.d.ts +6 -5
- package/dist/components/layout/top-bar/top-bar.d.ts.map +1 -1
- package/dist/components/layout/top-bar/top-bar.js +30 -7
- package/dist/components/layout/top-bar/top-bar.js.map +1 -1
- package/dist/components/ui/dialog/dialog.d.ts +2 -0
- package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog/dialog.js +4 -3
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/modal-dialog/modal-dialog.d.ts +5 -2
- package/dist/components/ui/modal-dialog/modal-dialog.d.ts.map +1 -1
- package/dist/components/ui/modal-dialog/modal-dialog.js +33 -24
- package/dist/components/ui/modal-dialog/modal-dialog.js.map +1 -1
- package/dist/components/ui/textarea/index.d.ts +1 -1
- package/dist/components/ui/textarea/index.d.ts.map +1 -1
- package/dist/components/ui/textarea/index.js +2 -2
- package/dist/components/ui/textarea/textarea.d.ts +15 -2
- package/dist/components/ui/textarea/textarea.d.ts.map +1 -1
- package/dist/components/ui/textarea/textarea.js +47 -3
- package/dist/components/ui/textarea/textarea.js.map +1 -1
- package/dist/components/ui/tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip/tooltip.js +239 -17
- package/dist/components/ui/tooltip/tooltip.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/lib/variants.js +2 -2
- package/dist/r/conversation.json +1 -0
- package/dist/r/list-row.json +5 -0
- package/dist/r/registry.json +41 -2
- package/dist/r/src/components/composites/list-row/index.ts +1 -0
- package/dist/r/src/components/composites/list-row/list-row-control.tsx +108 -0
- package/dist/r/src/components/composites/list-row/list-row.tsx +60 -127
- package/dist/r/src/components/composites/tree/index.ts +3 -0
- package/dist/r/src/components/composites/tree/tree-navigation.ts +114 -0
- package/dist/r/src/components/composites/tree/tree.tsx +405 -0
- package/dist/r/src/components/conversation/message-composer.tsx +103 -22
- package/dist/r/src/components/layout/sidebar-nav/index.ts +3 -0
- package/dist/r/src/components/layout/sidebar-nav/sidebar-nav.tsx +75 -6
- package/dist/r/src/components/layout/top-bar/top-bar.tsx +34 -11
- package/dist/r/src/components/ui/dialog/dialog.tsx +4 -2
- package/dist/r/src/components/ui/modal-dialog/modal-dialog.tsx +20 -4
- package/dist/r/src/components/ui/textarea/index.ts +1 -1
- package/dist/r/src/components/ui/textarea/textarea.tsx +52 -10
- package/dist/r/src/components/ui/tooltip/tooltip.tsx +305 -10
- package/dist/r/src/styles/base.css +40 -0
- package/dist/r/textarea.json +3 -1
- package/dist/r/top-bar.json +3 -1
- package/dist/r/tree.json +29 -0
- package/package.json +4 -1
- package/registry.json +41 -2
- package/src/components/composites/list-row/index.ts +1 -0
- package/src/components/composites/list-row/list-row-control.tsx +108 -0
- package/src/components/composites/list-row/list-row.tsx +60 -127
- package/src/components/composites/tree/index.ts +3 -0
- package/src/components/composites/tree/tree-navigation.ts +114 -0
- package/src/components/composites/tree/tree.tsx +405 -0
- package/src/components/conversation/message-composer.tsx +103 -22
- package/src/components/layout/sidebar-nav/index.ts +3 -0
- package/src/components/layout/sidebar-nav/sidebar-nav.tsx +75 -6
- package/src/components/layout/top-bar/top-bar.tsx +34 -11
- package/src/components/ui/dialog/dialog.tsx +4 -2
- package/src/components/ui/modal-dialog/modal-dialog.tsx +20 -4
- package/src/components/ui/textarea/index.ts +1 -1
- package/src/components/ui/textarea/textarea.tsx +52 -10
- package/src/components/ui/tooltip/tooltip.tsx +305 -10
- package/src/index.ts +1 -0
- package/src/styles/base.css +40 -0
package/dist/lib/variants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cva } from "class-variance-authority";
|
|
1
|
+
import { cva as cva$1 } from "class-variance-authority";
|
|
2
2
|
//#region src/lib/variants.ts
|
|
3
3
|
/**
|
|
4
4
|
* The shared control-size ladder, in Tailwind height utilities.
|
|
@@ -44,6 +44,6 @@ var controlInteractive = "transition-colors ease-out outline-none focus-visible:
|
|
|
44
44
|
/** The shared focus treatment for surfaces that are not focusable controls. */
|
|
45
45
|
var surfaceInteractive = "transition-colors ease-out outline-none";
|
|
46
46
|
//#endregion
|
|
47
|
-
export { controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, surfaceInteractive };
|
|
47
|
+
export { controlInteractive, controlSize, controlSizeIcon, controlSizes, cva$1 as cva, surfaceInteractive };
|
|
48
48
|
|
|
49
49
|
//# sourceMappingURL=variants.js.map
|
package/dist/r/conversation.json
CHANGED
package/dist/r/list-row.json
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"type": "registry:ui",
|
|
17
17
|
"target": "components/composites/list-row/index.ts"
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
"path": "src/components/composites/list-row/list-row-control.tsx",
|
|
21
|
+
"type": "registry:ui",
|
|
22
|
+
"target": "components/composites/list-row/list-row-control.tsx"
|
|
23
|
+
},
|
|
19
24
|
{
|
|
20
25
|
"path": "src/components/composites/list-row/list-row.tsx",
|
|
21
26
|
"type": "registry:ui",
|
package/dist/r/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",
|
|
@@ -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
|
+
}
|
|
@@ -1,161 +1,93 @@
|
|
|
1
1
|
import type { PolymorphicProps } from '@kobalte/core/polymorphic'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
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
|
|
47
|
-
|
|
48
|
-
|
|
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, [
|
|
135
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
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(
|
|
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,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
|
+
}
|