@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.
- 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 +39 -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 +39 -0
|
@@ -44,7 +44,7 @@ export function SidebarNav<T extends ValidComponent = 'nav'>(props: SidebarNavPr
|
|
|
44
44
|
as="nav"
|
|
45
45
|
aria-label={local['aria-label'] ?? 'Section'}
|
|
46
46
|
class={cn(
|
|
47
|
-
'bg-sidebar text-sidebar-foreground flex h-full w-sidebar shrink-0 flex-col border-e border-sidebar-border',
|
|
47
|
+
'bg-sidebar text-sidebar-foreground flex h-full w-sidebar min-w-0 max-w-full shrink-0 flex-col border-e border-sidebar-border',
|
|
48
48
|
local.class
|
|
49
49
|
)}
|
|
50
50
|
{...(rest as ComponentProps<'nav'>)}
|
|
@@ -128,6 +128,8 @@ export type SidebarNavSectionProps = ComponentProps<'div'> & {
|
|
|
128
128
|
/** Render a disclosure control instead of a plain heading. */
|
|
129
129
|
collapsible?: boolean
|
|
130
130
|
defaultOpen?: boolean
|
|
131
|
+
/** Mark the current project/group without changing its disclosure state. */
|
|
132
|
+
active?: boolean
|
|
131
133
|
/** Controlled disclosure state. When supplied, the host owns changes. */
|
|
132
134
|
open?: boolean
|
|
133
135
|
/** Called for user-initiated disclosure changes. */
|
|
@@ -148,6 +150,7 @@ export function SidebarNavSection(props: SidebarNavSectionProps) {
|
|
|
148
150
|
'label',
|
|
149
151
|
'collapsible',
|
|
150
152
|
'defaultOpen',
|
|
153
|
+
'active',
|
|
151
154
|
'open',
|
|
152
155
|
'onOpenChange',
|
|
153
156
|
'triggerProps',
|
|
@@ -188,17 +191,29 @@ export function SidebarNavSection(props: SidebarNavSectionProps) {
|
|
|
188
191
|
|
|
189
192
|
return (
|
|
190
193
|
<div data-slot="sidebar-nav-section" class={cn('flex flex-col gap-0.5', local.class)} {...rest}>
|
|
191
|
-
<div
|
|
194
|
+
<div
|
|
195
|
+
data-slot="sidebar-nav-section-header"
|
|
196
|
+
data-active={local.active ? '' : undefined}
|
|
197
|
+
class={cn(
|
|
198
|
+
'group/section-header flex items-center gap-1 rounded-md px-2 py-1.5',
|
|
199
|
+
{ '[@media(pointer:coarse)]:py-0': local.collapsible },
|
|
200
|
+
local.active ? 'bg-primary-subtle text-foreground' : 'hover:bg-sidebar-accent/60'
|
|
201
|
+
)}
|
|
202
|
+
>
|
|
192
203
|
<Polymorphic
|
|
193
204
|
as={local.headingAs ?? 'div'}
|
|
194
205
|
aria-label={local.headingAs ? local.label : undefined}
|
|
195
|
-
class=
|
|
206
|
+
class={cn(
|
|
207
|
+
'flex min-w-0 flex-1 items-center gap-1.5 text-2xs font-medium tracking-wide uppercase',
|
|
208
|
+
local.active ? 'text-foreground' : 'text-sidebar-muted-foreground'
|
|
209
|
+
)}
|
|
196
210
|
>
|
|
197
211
|
<Show when={local.collapsible} fallback={heading}>
|
|
198
212
|
<button
|
|
199
213
|
{...(triggerRest as ComponentProps<'button'>)}
|
|
200
214
|
type="button"
|
|
201
215
|
aria-expanded={open()}
|
|
216
|
+
aria-current={local.active ? 'page' : triggerRest['aria-current']}
|
|
202
217
|
onClick={(event) => {
|
|
203
218
|
invokeEventHandler(trigger.onClick, event)
|
|
204
219
|
if (event.defaultPrevented) return
|
|
@@ -212,7 +227,10 @@ export function SidebarNavSection(props: SidebarNavSectionProps) {
|
|
|
212
227
|
event.stopPropagation()
|
|
213
228
|
trigger.onReorder?.(event.key === 'ArrowUp' ? 'up' : 'down')
|
|
214
229
|
}}
|
|
215
|
-
class=
|
|
230
|
+
class={cn(
|
|
231
|
+
'flex min-w-0 flex-1 items-center gap-1.5 rounded-sm text-2xs font-medium tracking-wide uppercase outline-none transition-colors ease-out hover:text-sidebar-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle [@media(pointer:coarse)]:min-h-11',
|
|
232
|
+
local.active ? 'text-foreground' : 'text-sidebar-muted-foreground'
|
|
233
|
+
)}
|
|
216
234
|
>
|
|
217
235
|
{heading}
|
|
218
236
|
</button>
|
|
@@ -231,6 +249,53 @@ export function SidebarNavSection(props: SidebarNavSectionProps) {
|
|
|
231
249
|
)
|
|
232
250
|
}
|
|
233
251
|
|
|
252
|
+
export type SidebarNavRowProps = ComponentProps<'div'> & {
|
|
253
|
+
/** Adjacent controls, outside the navigation item's interactive element. */
|
|
254
|
+
actions?: JSX.Element
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* A navigation item and its adjacent controls. Actions stay outside the item
|
|
259
|
+
* to avoid nested buttons; touch, keyboard focus, and expanded menus expose
|
|
260
|
+
* them without requiring a pointer hover. A narrow lane or enlarged text
|
|
261
|
+
* may move actions onto a second line rather than clipping the label or controls.
|
|
262
|
+
*/
|
|
263
|
+
export function SidebarNavRow(props: SidebarNavRowProps) {
|
|
264
|
+
const [local, rest] = splitProps(props, ['class', 'children', 'actions'])
|
|
265
|
+
return (
|
|
266
|
+
<div
|
|
267
|
+
data-slot="sidebar-nav-row"
|
|
268
|
+
class={cn(
|
|
269
|
+
'group/sidebar-nav-row flex min-w-0 flex-wrap items-center gap-0.5 [&>[data-slot=sidebar-nav-item]]:flex-1 [&>[data-slot=sidebar-nav-item]]:basis-36',
|
|
270
|
+
local.class
|
|
271
|
+
)}
|
|
272
|
+
{...rest}
|
|
273
|
+
>
|
|
274
|
+
{local.children}
|
|
275
|
+
<Show when={local.actions}>
|
|
276
|
+
<span
|
|
277
|
+
data-slot="sidebar-nav-row-actions"
|
|
278
|
+
class="ms-auto flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity ease-out group-hover/sidebar-nav-row:opacity-100 group-focus-within/sidebar-nav-row:opacity-100 has-[[aria-haspopup=menu][aria-expanded=true]]:opacity-100 has-[[aria-haspopup=true][aria-expanded=true]]:opacity-100 [@media(hover:none)]:opacity-100"
|
|
279
|
+
>
|
|
280
|
+
{local.actions}
|
|
281
|
+
</span>
|
|
282
|
+
</Show>
|
|
283
|
+
</div>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Row text may truncate visually; its complete content remains the accessible name. */
|
|
288
|
+
export function SidebarNavLabel(props: ComponentProps<'span'>) {
|
|
289
|
+
const [local, rest] = splitProps(props, ['class'])
|
|
290
|
+
return (
|
|
291
|
+
<span
|
|
292
|
+
data-slot="sidebar-nav-label"
|
|
293
|
+
class={cn('min-w-0 flex-1 truncate', local.class)}
|
|
294
|
+
{...rest}
|
|
295
|
+
/>
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
234
299
|
export type SidebarNavItemProps<T extends ValidComponent = 'a'> = PolymorphicProps<
|
|
235
300
|
T,
|
|
236
301
|
{
|
|
@@ -243,10 +308,13 @@ export type SidebarNavItemProps<T extends ValidComponent = 'a'> = PolymorphicPro
|
|
|
243
308
|
}
|
|
244
309
|
>
|
|
245
310
|
|
|
246
|
-
/**
|
|
311
|
+
/**
|
|
312
|
+
* A sidebar row. Exported classes, so a caller's own element can match it.
|
|
313
|
+
* Coarse pointers get a 44px target without changing Kiro's desktop density.
|
|
314
|
+
*/
|
|
247
315
|
export const sidebarNavItemClass = [
|
|
248
316
|
'group/nav-item relative flex min-w-0 items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium',
|
|
249
|
-
'whitespace-nowrap transition-colors ease-out outline-none select-none',
|
|
317
|
+
'whitespace-nowrap transition-colors ease-out outline-none select-none [@media(pointer:coarse)]:min-h-11',
|
|
250
318
|
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
|
|
251
319
|
'[&_svg]:size-4 [&_svg]:shrink-0',
|
|
252
320
|
].join(' ')
|
|
@@ -268,6 +336,7 @@ export function SidebarNavItem<T extends ValidComponent = 'a'>(props: SidebarNav
|
|
|
268
336
|
return (
|
|
269
337
|
<Polymorphic
|
|
270
338
|
as="a"
|
|
339
|
+
data-slot="sidebar-nav-item"
|
|
271
340
|
aria-current={local.active ? 'page' : undefined}
|
|
272
341
|
data-active={local.active ? '' : undefined}
|
|
273
342
|
class={cn(
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { ComponentProps } from 'solid-js'
|
|
2
2
|
import { Show, splitProps } from 'solid-js'
|
|
3
|
+
import { Search } from 'lucide-solid'
|
|
3
4
|
import { cn } from '../../../lib/utils'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* TopBar.
|
|
7
8
|
*
|
|
8
9
|
* The window's title row and primary toolbar. Three tracks: a leading group, a
|
|
9
|
-
* centred search, and a trailing group.
|
|
10
|
-
* window width and the
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* centred search, and a trailing group. On wide screens the search is a fixed
|
|
11
|
+
* function of the window width and the side groups are equal remainders. On
|
|
12
|
+
* narrow screens the middle track yields space to the controls, and each side
|
|
13
|
+
* group scrolls when necessary so keyboard focus can reveal every action.
|
|
13
14
|
*
|
|
14
15
|
* On a frameless desktop window this row is also the drag region, so it carries
|
|
15
16
|
* `window-drag` and the controls inside it opt back out. `macos-inset` and
|
|
@@ -41,7 +42,7 @@ export function TopBar(props: TopBarProps) {
|
|
|
41
42
|
<header
|
|
42
43
|
data-slot="top-bar"
|
|
43
44
|
class={cn(
|
|
44
|
-
'h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[
|
|
45
|
+
'h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground px-3 md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]',
|
|
45
46
|
local.glass && 'glass-panel',
|
|
46
47
|
local.draggable && 'window-drag',
|
|
47
48
|
local.macosInset && 'window-inset-macos',
|
|
@@ -53,25 +54,46 @@ export function TopBar(props: TopBarProps) {
|
|
|
53
54
|
)
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
function invokeFocusHandler(handler: unknown, event: Event) {
|
|
58
|
+
if (Array.isArray(handler)) {
|
|
59
|
+
const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]
|
|
60
|
+
callback(data, event)
|
|
61
|
+
} else if (typeof handler === 'function') {
|
|
62
|
+
const callback = handler as (event: Event) => void
|
|
63
|
+
callback(event)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A scrollable side group; equal wide-screen tracks centre the search. */
|
|
57
68
|
export function TopBarSection(props: ComponentProps<'div'> & { align?: 'start' | 'end' }) {
|
|
58
|
-
const [local, rest] = splitProps(props, ['class', 'align'])
|
|
69
|
+
const [local, rest] = splitProps(props, ['class', 'align', 'onFocusIn'])
|
|
59
70
|
|
|
60
71
|
return (
|
|
61
72
|
<div
|
|
62
73
|
data-slot="top-bar-section"
|
|
63
74
|
class={cn(
|
|
64
75
|
'window-no-drag flex min-w-0 items-center gap-2 overflow-x-auto overflow-y-hidden',
|
|
65
|
-
local.align === 'end' && 'justify-end',
|
|
76
|
+
local.align === 'end' && 'md:justify-end',
|
|
66
77
|
local.align !== 'end' && 'justify-start',
|
|
67
78
|
local.class
|
|
68
79
|
)}
|
|
80
|
+
onFocusIn={(event) => {
|
|
81
|
+
if (event.target instanceof HTMLElement) {
|
|
82
|
+
event.target.scrollIntoView({ block: 'nearest', inline: 'nearest' })
|
|
83
|
+
}
|
|
84
|
+
invokeFocusHandler(local.onFocusIn, event)
|
|
85
|
+
}}
|
|
69
86
|
{...rest}
|
|
70
87
|
/>
|
|
71
88
|
)
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
export function TopBarTitle(
|
|
91
|
+
export function TopBarTitle(
|
|
92
|
+
props: ComponentProps<'div'> & {
|
|
93
|
+
/** Aligns text within this slot; it does not position the slot. */
|
|
94
|
+
align?: 'start' | 'center'
|
|
95
|
+
}
|
|
96
|
+
) {
|
|
75
97
|
const [local, rest] = splitProps(props, ['class', 'align'])
|
|
76
98
|
return (
|
|
77
99
|
<div
|
|
@@ -122,7 +144,7 @@ export function TopBarSearch(props: TopBarSearchProps) {
|
|
|
122
144
|
type="button"
|
|
123
145
|
data-slot="top-bar-search"
|
|
124
146
|
class={cn(
|
|
125
|
-
'text-muted-foreground flex h-control-md w-full items-center gap-2 rounded-md border border-input px-3 text-sm',
|
|
147
|
+
'text-muted-foreground flex h-control-md w-full min-w-(--control-height-md) items-center justify-center gap-2 rounded-md border border-input px-3 text-sm sm:justify-start',
|
|
126
148
|
'transition-colors ease-out outline-none',
|
|
127
149
|
'hover:bg-surface-hover hover:text-foreground',
|
|
128
150
|
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
|
|
@@ -131,7 +153,8 @@ export function TopBarSearch(props: TopBarSearchProps) {
|
|
|
131
153
|
)}
|
|
132
154
|
{...rest}
|
|
133
155
|
>
|
|
134
|
-
<
|
|
156
|
+
<Search aria-hidden="true" class="size-4 shrink-0 sm:hidden" />
|
|
157
|
+
<span class="sr-only sm:not-sr-only sm:truncate">{local.placeholder ?? 'Search'}</span>
|
|
135
158
|
<Show when={local.shortcut}>
|
|
136
159
|
<kbd class="bg-muted ms-auto hidden h-5 shrink-0 items-center rounded-sm border border-border px-1.5 font-mono text-2xs sm:flex">
|
|
137
160
|
{local.shortcut}
|
|
@@ -257,15 +257,17 @@ export type DialogContentProps = ComponentProps<typeof KobalteDialogContent> & {
|
|
|
257
257
|
* user can escape from needs the button.
|
|
258
258
|
*/
|
|
259
259
|
closeButton?: JSX.Element | false
|
|
260
|
+
/** Use a 1rem inset as the positioner bounds for tall, viewport-filling surfaces. */
|
|
261
|
+
positioner?: 'default' | 'inset'
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
export function DialogContent(props: DialogContentProps) {
|
|
263
|
-
const [local, rest] = splitProps(props, ['class', 'children', 'closeButton'])
|
|
265
|
+
const [local, rest] = splitProps(props, ['class', 'children', 'closeButton', 'positioner'])
|
|
264
266
|
|
|
265
267
|
return (
|
|
266
268
|
<KobalteDialog.Portal>
|
|
267
269
|
<DialogOverlay />
|
|
268
|
-
<div class={overlayPositioner}>
|
|
270
|
+
<div class={cn(overlayPositioner, local.positioner === 'inset' && 'inset-4 grid-rows-1 p-0')}>
|
|
269
271
|
<KobalteDialogContent
|
|
270
272
|
class={cn(
|
|
271
273
|
overlaySurface,
|
|
@@ -35,12 +35,15 @@ import {
|
|
|
35
35
|
* cannot be inferred from the focused element.
|
|
36
36
|
*
|
|
37
37
|
* Use `Dialog` directly when you need its composition; use this when you want a
|
|
38
|
-
* dialog that is correct by default.
|
|
38
|
+
* dialog that is correct by default. `size="settings"` supplies a wide, bounded
|
|
39
|
+
* shell for a shared settings layout; ordinary dialogs keep their compact default.
|
|
39
40
|
*/
|
|
40
41
|
export type ModalDialogProps = Omit<
|
|
41
42
|
ComponentProps<typeof DialogContent>,
|
|
42
|
-
'children' | 'aria-label' | 'aria-labelledby'
|
|
43
|
+
'children' | 'aria-label' | 'aria-labelledby' | 'positioner'
|
|
43
44
|
> & {
|
|
45
|
+
/** Selects the bounded, scrollable shell used for grouped workspace settings. */
|
|
46
|
+
size?: 'default' | 'settings'
|
|
44
47
|
open: boolean
|
|
45
48
|
onClose: () => void
|
|
46
49
|
title: string
|
|
@@ -70,6 +73,7 @@ export function ModalDialog(props: ModalDialogProps) {
|
|
|
70
73
|
'onClose',
|
|
71
74
|
'title',
|
|
72
75
|
'modal',
|
|
76
|
+
'size',
|
|
73
77
|
'description',
|
|
74
78
|
'headerLeading',
|
|
75
79
|
'class',
|
|
@@ -114,7 +118,13 @@ export function ModalDialog(props: ModalDialogProps) {
|
|
|
114
118
|
<Dialog open modal={local.modal ?? true} onOpenChange={(next) => !next && local.onClose()}>
|
|
115
119
|
<DialogContent
|
|
116
120
|
ref={setContent}
|
|
117
|
-
|
|
121
|
+
positioner={local.size === 'settings' ? 'inset' : 'default'}
|
|
122
|
+
class={cn(
|
|
123
|
+
'gap-4',
|
|
124
|
+
local.size === 'settings' &&
|
|
125
|
+
'flex h-208 max-h-full min-h-0 max-w-6xl flex-col gap-0 overflow-hidden p-0',
|
|
126
|
+
local.class
|
|
127
|
+
)}
|
|
118
128
|
aria-label={local['aria-label'] ?? local.title}
|
|
119
129
|
aria-labelledby={
|
|
120
130
|
local['aria-labelledby'] ?? (local['aria-label'] !== undefined ? '' : undefined)
|
|
@@ -123,7 +133,13 @@ export function ModalDialog(props: ModalDialogProps) {
|
|
|
123
133
|
onOpenAutoFocus={focusRestoration.onOpenAutoFocus}
|
|
124
134
|
onCloseAutoFocus={focusRestoration.onCloseAutoFocus}
|
|
125
135
|
>
|
|
126
|
-
<DialogHeader
|
|
136
|
+
<DialogHeader
|
|
137
|
+
class={
|
|
138
|
+
local.size === 'settings'
|
|
139
|
+
? 'shrink-0 gap-1 border-b border-border px-5 pt-4 pb-3 pe-14'
|
|
140
|
+
: undefined
|
|
141
|
+
}
|
|
142
|
+
>
|
|
127
143
|
<DialogTitle class="flex items-center gap-2">
|
|
128
144
|
<Show when={local.headerLeading}>{local.headerLeading}</Show>
|
|
129
145
|
{local.title}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Textarea } from './textarea'
|
|
1
|
+
export { Textarea, textareaVariants, type TextareaProps } from './textarea'
|
|
@@ -1,8 +1,49 @@
|
|
|
1
|
-
import type { ComponentProps } from 'solid-js'
|
|
1
|
+
import type { ComponentProps, Ref } from 'solid-js'
|
|
2
2
|
import { splitProps } from 'solid-js'
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
4
|
import { useFormFieldControl } from '../../../lib/form-field'
|
|
4
5
|
import { cn } from '../../../lib/utils'
|
|
5
6
|
|
|
7
|
+
export const textareaVariants = cva(
|
|
8
|
+
[
|
|
9
|
+
'flex field-sizing-content w-full rounded-md border border-input bg-transparent px-control-md py-2 text-sm',
|
|
10
|
+
'transition-[color,box-shadow,border-color] ease-out outline-none',
|
|
11
|
+
'placeholder:text-muted-foreground',
|
|
12
|
+
'selection:bg-primary selection:text-primary-foreground',
|
|
13
|
+
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
|
|
14
|
+
'aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive-subtle',
|
|
15
|
+
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
16
|
+
],
|
|
17
|
+
{
|
|
18
|
+
variants: {
|
|
19
|
+
variant: {
|
|
20
|
+
default: '',
|
|
21
|
+
composer: 'border-0 bg-transparent p-0 shadow-none focus-visible:ring-0',
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: 'min-h-16',
|
|
25
|
+
comfortable: 'min-h-16 max-h-48',
|
|
26
|
+
},
|
|
27
|
+
resize: {
|
|
28
|
+
vertical: 'resize-y',
|
|
29
|
+
none: 'resize-none',
|
|
30
|
+
both: 'resize',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultVariants: {
|
|
34
|
+
variant: 'default',
|
|
35
|
+
size: 'default',
|
|
36
|
+
resize: 'vertical',
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export type TextareaProps = Omit<ComponentProps<'textarea'>, 'size'> &
|
|
42
|
+
VariantProps<typeof textareaVariants> & {
|
|
43
|
+
/** Solid's actual textarea element, rather than its containing component or form. */
|
|
44
|
+
ref?: Ref<HTMLTextAreaElement>
|
|
45
|
+
}
|
|
46
|
+
|
|
6
47
|
/**
|
|
7
48
|
* Textarea.
|
|
8
49
|
*
|
|
@@ -13,10 +54,17 @@ import { cn } from '../../../lib/utils'
|
|
|
13
54
|
* `field-sizing: content` lets the element grow with its content where the
|
|
14
55
|
* engine supports it, which is the behaviour a chat composer wants; `rows`
|
|
15
56
|
* remains the fallback so an unsupported engine still gets a usable height.
|
|
57
|
+
* `variant="composer"` removes the standalone field surface when nested in a
|
|
58
|
+
* composer shell. `size="comfortable"` caps growth at the shared 12rem rung, and
|
|
59
|
+
* the finite `resize` variants keep browser resize behavior explicit.
|
|
16
60
|
*/
|
|
17
|
-
export function Textarea(props:
|
|
61
|
+
export function Textarea(props: TextareaProps) {
|
|
18
62
|
const [local, rest] = splitProps(props, [
|
|
19
63
|
'class',
|
|
64
|
+
'variant',
|
|
65
|
+
'size',
|
|
66
|
+
'resize',
|
|
67
|
+
'ref',
|
|
20
68
|
'id',
|
|
21
69
|
'aria-label',
|
|
22
70
|
'aria-labelledby',
|
|
@@ -42,15 +90,9 @@ export function Textarea(props: ComponentProps<'textarea'>) {
|
|
|
42
90
|
aria-describedby={field?.['aria-describedby'] ?? local['aria-describedby']}
|
|
43
91
|
aria-invalid={field?.['aria-invalid'] ?? local['aria-invalid']}
|
|
44
92
|
aria-errormessage={field?.['aria-errormessage'] ?? local['aria-errormessage']}
|
|
93
|
+
ref={local.ref}
|
|
45
94
|
class={cn(
|
|
46
|
-
|
|
47
|
-
'transition-[color,box-shadow,border-color] ease-out outline-none',
|
|
48
|
-
'placeholder:text-muted-foreground',
|
|
49
|
-
'selection:bg-primary selection:text-primary-foreground',
|
|
50
|
-
'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
|
|
51
|
-
'aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive-subtle',
|
|
52
|
-
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
53
|
-
'resize-y',
|
|
95
|
+
textareaVariants({ variant: local.variant, size: local.size, resize: local.resize }),
|
|
54
96
|
local.class
|
|
55
97
|
)}
|
|
56
98
|
{...rest}
|