@adea-ai/ui 0.143.0 → 0.144.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.
- package/dist/components/composites/accent-picker/accent-picker.d.ts +1 -1
- package/dist/components/composites/accent-picker/accent-picker.d.ts.map +1 -1
- package/dist/components/composites/accent-picker/accent-picker.js +31 -21
- package/dist/components/composites/accent-picker/accent-picker.js.map +1 -1
- package/dist/components/composites/icon-picker/icon-picker.d.ts +16 -1
- package/dist/components/composites/icon-picker/icon-picker.d.ts.map +1 -1
- package/dist/components/composites/icon-picker/icon-picker.js +83 -5
- package/dist/components/composites/icon-picker/icon-picker.js.map +1 -1
- package/dist/components/composites/icon-picker/index.d.ts +1 -1
- package/dist/components/composites/icon-picker/index.d.ts.map +1 -1
- package/dist/components/composites/icon-picker/index.js +2 -2
- package/dist/components/composites/settings/settings-navigation.js +1 -1
- package/dist/components/composites/settings/settings-navigation.js.map +1 -1
- package/dist/components/composites/settings/settings.d.ts +8 -14
- package/dist/components/composites/settings/settings.d.ts.map +1 -1
- package/dist/components/composites/settings/settings.js +88 -37
- package/dist/components/composites/settings/settings.js.map +1 -1
- package/dist/components/composites/tree/tree.d.ts +6 -0
- package/dist/components/composites/tree/tree.d.ts.map +1 -1
- package/dist/components/composites/tree/tree.js +35 -14
- package/dist/components/composites/tree/tree.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/r/accent-picker.json +2 -2
- package/dist/r/icon-picker.json +1 -0
- package/dist/r/registry.json +6 -3
- package/dist/r/settings.json +3 -1
- package/dist/r/src/components/composites/accent-picker/accent-picker.tsx +37 -16
- package/dist/r/src/components/composites/icon-picker/icon-picker.tsx +65 -3
- package/dist/r/src/components/composites/icon-picker/index.ts +6 -1
- package/dist/r/src/components/composites/settings/settings-navigation.tsx +1 -1
- package/dist/r/src/components/composites/settings/settings.tsx +40 -5
- package/dist/r/src/components/composites/tree/tree.tsx +25 -0
- package/package.json +1 -1
- package/registry.json +6 -3
- package/src/components/composites/accent-picker/accent-picker.tsx +37 -16
- package/src/components/composites/icon-picker/icon-picker.tsx +65 -3
- package/src/components/composites/icon-picker/index.ts +6 -1
- package/src/components/composites/settings/settings-navigation.tsx +1 -1
- package/src/components/composites/settings/settings.tsx +40 -5
- package/src/components/composites/tree/tree.tsx +25 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { RadioGroup as Radio } from '@kobalte/core/radio-group'
|
|
2
|
-
import { ImageUp, Trash2 } from 'lucide-solid'
|
|
3
|
-
import {
|
|
2
|
+
import { ChevronDown, ImageUp, Trash2 } from 'lucide-solid'
|
|
3
|
+
import type { JSX } from 'solid-js'
|
|
4
|
+
import { For, Show, createSignal, createUniqueId, splitProps } from 'solid-js'
|
|
5
|
+
import { Dynamic } from 'solid-js/web'
|
|
4
6
|
import { cn } from '#lib/utils'
|
|
5
7
|
import { Button } from '../../ui/button'
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover'
|
|
6
9
|
import {
|
|
7
10
|
MarkImageError,
|
|
8
11
|
markImageFromFile,
|
|
9
12
|
markImageProblemCopy,
|
|
10
13
|
MARK_IMAGE_MAX_SOURCE_BYTES,
|
|
11
14
|
} from './image-data-url'
|
|
12
|
-
import { resolveShellIcon, shellIconEntries } from './shell-icons'
|
|
15
|
+
import { resolveShellIcon, shellIconEntries, shellIconLabel } from './shell-icons'
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* IconPicker.
|
|
@@ -182,3 +185,62 @@ export function IconPicker(props: IconPickerProps) {
|
|
|
182
185
|
</div>
|
|
183
186
|
)
|
|
184
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* IconPickerDropdown.
|
|
191
|
+
*
|
|
192
|
+
* The compact form of the picker for a settings row: one button showing the
|
|
193
|
+
* current mark, opening the full grid (and the upload row, when the host offers
|
|
194
|
+
* it) in a popover. Choosing a glyph applies it and closes the popover; an
|
|
195
|
+
* upload or Remove leaves it open so the result is visible. The host owns the
|
|
196
|
+
* value exactly as it does for `IconPicker`.
|
|
197
|
+
*/
|
|
198
|
+
export type IconPickerDropdownProps = IconPickerProps & {
|
|
199
|
+
/** The mark shown on the trigger; defaults to the checked glyph or the uploaded image. */
|
|
200
|
+
trigger?: JSX.Element
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function IconPickerDropdown(props: IconPickerDropdownProps) {
|
|
204
|
+
const [open, setOpen] = createSignal(false)
|
|
205
|
+
const [local, picker] = splitProps(props, ['trigger', 'class'])
|
|
206
|
+
const Current = () => resolveShellIcon(props.value ?? props.defaultKey ?? '')
|
|
207
|
+
return (
|
|
208
|
+
<Popover open={open()} onOpenChange={setOpen} placement="bottom-start">
|
|
209
|
+
<PopoverTrigger
|
|
210
|
+
as={Button}
|
|
211
|
+
type="button"
|
|
212
|
+
variant="outline"
|
|
213
|
+
data-slot="icon-picker-trigger"
|
|
214
|
+
aria-label={`${props.label}: ${props.image ? 'uploaded image' : shellIconLabel(props.value ?? props.defaultKey ?? '')}`}
|
|
215
|
+
disabled={props.disabled}
|
|
216
|
+
class={cn('gap-2', local.class)}
|
|
217
|
+
>
|
|
218
|
+
<span aria-hidden="true" class="flex size-5 items-center justify-center overflow-hidden">
|
|
219
|
+
<Show
|
|
220
|
+
when={local.trigger}
|
|
221
|
+
fallback={
|
|
222
|
+
<Show
|
|
223
|
+
when={props.image}
|
|
224
|
+
fallback={<Dynamic component={Current()} class="size-4" aria-hidden="true" />}
|
|
225
|
+
>
|
|
226
|
+
{(src) => <img src={src()} alt="" class="size-full rounded-sm object-cover" />}
|
|
227
|
+
</Show>
|
|
228
|
+
}
|
|
229
|
+
>
|
|
230
|
+
{local.trigger}
|
|
231
|
+
</Show>
|
|
232
|
+
</span>
|
|
233
|
+
<ChevronDown class="size-4 text-muted-foreground" aria-hidden="true" />
|
|
234
|
+
</PopoverTrigger>
|
|
235
|
+
<PopoverContent class="w-[min(22rem,var(--kb-popper-available-width))]">
|
|
236
|
+
<IconPicker
|
|
237
|
+
{...picker}
|
|
238
|
+
onValueChange={(key) => {
|
|
239
|
+
picker.onValueChange(key)
|
|
240
|
+
setOpen(false)
|
|
241
|
+
}}
|
|
242
|
+
/>
|
|
243
|
+
</PopoverContent>
|
|
244
|
+
</Popover>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
IconPicker,
|
|
3
|
+
IconPickerDropdown,
|
|
4
|
+
type IconPickerDropdownProps,
|
|
5
|
+
type IconPickerProps,
|
|
6
|
+
} from './icon-picker'
|
|
2
7
|
export {
|
|
3
8
|
MARK_IMAGE_MAX_DATA_URL_LENGTH,
|
|
4
9
|
MARK_IMAGE_MAX_SOURCE_BYTES,
|
|
@@ -182,7 +182,7 @@ export function SettingsNavigation(props: SettingsNavigationProps) {
|
|
|
182
182
|
class={cn(
|
|
183
183
|
sidebarNavItemClass,
|
|
184
184
|
sidebarNavItemStateClass.idle,
|
|
185
|
-
'h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle
|
|
185
|
+
'h-row-lg justify-start border-0 px-3 py-2 text-left text-foreground data-[selected]:bg-primary-subtle',
|
|
186
186
|
strip() ? 'w-auto shrink-0' : 'w-full'
|
|
187
187
|
)}
|
|
188
188
|
>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { CircleHelp } from 'lucide-solid'
|
|
1
2
|
import type { ComponentProps, JSX } from 'solid-js'
|
|
2
3
|
import { createUniqueId, Show, splitProps } from 'solid-js'
|
|
3
4
|
import { createFormFieldContext, FormFieldContext } from '#lib/form-field'
|
|
4
5
|
import { cn } from '#lib/utils'
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'
|
|
5
7
|
import { Heading } from '../../ui/typography'
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -18,10 +20,29 @@ import { Heading } from '../../ui/typography'
|
|
|
18
20
|
* the first control inside it. `SettingsField` is the visual stacked layout;
|
|
19
21
|
* use `FormField` when the control needs label, help, or validation wiring.
|
|
20
22
|
*/
|
|
23
|
+
function HelpTip(props: { label: string; help: string }) {
|
|
24
|
+
return (
|
|
25
|
+
<Tooltip>
|
|
26
|
+
<TooltipTrigger
|
|
27
|
+
as="button"
|
|
28
|
+
type="button"
|
|
29
|
+
data-slot="settings-row-help"
|
|
30
|
+
aria-label={`About ${props.label}`}
|
|
31
|
+
class="inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none"
|
|
32
|
+
>
|
|
33
|
+
<CircleHelp class="size-3.5" aria-hidden="true" />
|
|
34
|
+
</TooltipTrigger>
|
|
35
|
+
<TooltipContent>{props.help}</TooltipContent>
|
|
36
|
+
</Tooltip>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
export function SettingsSection(
|
|
22
41
|
props: ComponentProps<'section'> & {
|
|
23
42
|
title: string
|
|
24
43
|
description?: string
|
|
44
|
+
/** A short explanation behind a help icon beside the title; prefer it to a long description. */
|
|
45
|
+
help?: string
|
|
25
46
|
/** A control for the section as a whole, e.g. a master toggle. */
|
|
26
47
|
action?: JSX.Element
|
|
27
48
|
/** Use a spaced content stack for fields, alerts, and cards instead of divided rows. */
|
|
@@ -32,6 +53,7 @@ export function SettingsSection(
|
|
|
32
53
|
'class',
|
|
33
54
|
'title',
|
|
34
55
|
'description',
|
|
56
|
+
'help',
|
|
35
57
|
'action',
|
|
36
58
|
'bodyLayout',
|
|
37
59
|
'children',
|
|
@@ -45,9 +67,12 @@ export function SettingsSection(
|
|
|
45
67
|
>
|
|
46
68
|
<div class="flex items-start justify-between gap-4">
|
|
47
69
|
<div class="flex min-w-0 flex-col gap-0.5">
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
70
|
+
<div class="flex items-center gap-1.5">
|
|
71
|
+
<Heading as="h2" size="card">
|
|
72
|
+
{local.title}
|
|
73
|
+
</Heading>
|
|
74
|
+
<Show when={local.help}>{(help) => <HelpTip label={local.title} help={help()} />}</Show>
|
|
75
|
+
</div>
|
|
51
76
|
<Show when={local.description}>
|
|
52
77
|
<p class="max-w-prose text-sm text-muted-foreground text-pretty">{local.description}</p>
|
|
53
78
|
</Show>
|
|
@@ -82,6 +107,12 @@ export function SettingsRow(
|
|
|
82
107
|
props: ComponentProps<'div'> & {
|
|
83
108
|
label: string
|
|
84
109
|
description?: string
|
|
110
|
+
/**
|
|
111
|
+
* A short explanation behind a help icon beside the label. Prefer it to a
|
|
112
|
+
* long description: the row stays quiet and the explanation is one hover or
|
|
113
|
+
* focus away.
|
|
114
|
+
*/
|
|
115
|
+
help?: string
|
|
85
116
|
/** Render a vertical stack when the control needs the full width. */
|
|
86
117
|
orientation?: 'horizontal' | 'vertical'
|
|
87
118
|
/** Hide the divider this row would otherwise contribute. */
|
|
@@ -100,6 +131,7 @@ export function SettingsRow(
|
|
|
100
131
|
'class',
|
|
101
132
|
'label',
|
|
102
133
|
'description',
|
|
134
|
+
'help',
|
|
103
135
|
'orientation',
|
|
104
136
|
'bare',
|
|
105
137
|
'flush',
|
|
@@ -132,8 +164,11 @@ export function SettingsRow(
|
|
|
132
164
|
{...rest}
|
|
133
165
|
>
|
|
134
166
|
<div class="flex min-w-0 flex-col gap-0.5">
|
|
135
|
-
<div
|
|
136
|
-
{
|
|
167
|
+
<div class="flex items-center gap-1.5">
|
|
168
|
+
<div id={labelId} data-slot="settings-row-label" class="text-sm font-medium">
|
|
169
|
+
{local.label}
|
|
170
|
+
</div>
|
|
171
|
+
<Show when={local.help}>{(help) => <HelpTip label={local.label} help={help()} />}</Show>
|
|
137
172
|
</div>
|
|
138
173
|
<Show when={local.description}>
|
|
139
174
|
<p id={descriptionId} class="max-w-prose text-sm text-muted-foreground text-pretty">
|
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
createEffect,
|
|
6
6
|
createMemo,
|
|
7
7
|
createUniqueId,
|
|
8
|
+
For,
|
|
8
9
|
onCleanup,
|
|
10
|
+
Show,
|
|
9
11
|
splitProps,
|
|
10
12
|
useContext,
|
|
11
13
|
} from 'solid-js'
|
|
@@ -29,6 +31,7 @@ type TreeContextValue = {
|
|
|
29
31
|
activate: (id: string, event: Event) => void
|
|
30
32
|
expand: (id: string, expanded: boolean) => void
|
|
31
33
|
disclosure: () => TreeDisclosure
|
|
34
|
+
guides: () => boolean
|
|
32
35
|
onActiveIdChange: (id: string) => void
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -79,6 +82,12 @@ export type TreeProps = Omit<
|
|
|
79
82
|
* Keyboard behaviour is identical in both modes: Left/Right expand and collapse.
|
|
80
83
|
*/
|
|
81
84
|
disclosure?: TreeDisclosure
|
|
85
|
+
/**
|
|
86
|
+
* Draw a vertical guide line down each ancestor level of a nested row, so a
|
|
87
|
+
* group of children reads as belonging to its parent. Off by default: a flat
|
|
88
|
+
* list or a dense file explorer carries its own rhythm.
|
|
89
|
+
*/
|
|
90
|
+
guides?: boolean
|
|
82
91
|
/** Called for Enter or pointer activation. Domain action stays host-owned. */
|
|
83
92
|
onActivate?: (id: string, event: Event) => void
|
|
84
93
|
/** Called for Space selection. Selection state stays host-owned. */
|
|
@@ -132,6 +141,7 @@ export function Tree(props: TreeProps) {
|
|
|
132
141
|
'onActiveIdChange',
|
|
133
142
|
'onExpand',
|
|
134
143
|
'disclosure',
|
|
144
|
+
'guides',
|
|
135
145
|
'onActivate',
|
|
136
146
|
'onSelectItem',
|
|
137
147
|
'onRequestReveal',
|
|
@@ -296,6 +306,7 @@ export function Tree(props: TreeProps) {
|
|
|
296
306
|
activate: (id, event) => local.onActivate?.(id, event),
|
|
297
307
|
expand: (id, expanded) => local.onExpand?.(id, expanded),
|
|
298
308
|
disclosure: () => local.disclosure ?? 'row',
|
|
309
|
+
guides: () => local.guides ?? false,
|
|
299
310
|
onActiveIdChange: local.onActiveIdChange,
|
|
300
311
|
}
|
|
301
312
|
|
|
@@ -495,6 +506,8 @@ export function TreeRow(props: TreeRowProps) {
|
|
|
495
506
|
'group/tree-row flex min-h-row-sm w-full min-w-0 items-center gap-1.5 rounded-md pe-1.5 text-left text-sm outline-none transition-colors',
|
|
496
507
|
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
|
|
497
508
|
{
|
|
509
|
+
// Guide lines are positioned against the row; without them it keeps its original classes.
|
|
510
|
+
relative: tree.guides(),
|
|
498
511
|
'bg-primary-subtle text-foreground': local.selected,
|
|
499
512
|
'hover:bg-surface-hover': !local.selected,
|
|
500
513
|
},
|
|
@@ -505,6 +518,18 @@ export function TreeRow(props: TreeRowProps) {
|
|
|
505
518
|
onClick={handleClick}
|
|
506
519
|
onDblClick={handleDoubleClick}
|
|
507
520
|
>
|
|
521
|
+
<Show when={tree.guides() && local.item.level > 1}>
|
|
522
|
+
<For each={Array.from({ length: local.item.level - 1 }, (_, index) => index)}>
|
|
523
|
+
{(index) => (
|
|
524
|
+
<span
|
|
525
|
+
aria-hidden="true"
|
|
526
|
+
data-slot="tree-row-guide"
|
|
527
|
+
class="pointer-events-none absolute inset-y-0 w-px bg-border"
|
|
528
|
+
style={{ 'inset-inline-start': `${6 + index * 12 + 7}px` }}
|
|
529
|
+
/>
|
|
530
|
+
)}
|
|
531
|
+
</For>
|
|
532
|
+
</Show>
|
|
508
533
|
<span
|
|
509
534
|
data-slot={markerTarget() ? 'tree-row-disclosure' : undefined}
|
|
510
535
|
class={cn(
|