@adea-ai/ui 0.71.1 → 0.71.3
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/appearance-editor/appearance-editor.js +1 -1
- package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
- package/dist/components/composites/stat/stat.d.ts.map +1 -1
- package/dist/components/composites/stat/stat.js +8 -8
- package/dist/components/composites/stat/stat.js.map +1 -1
- package/dist/components/composites/update-dialog/update-dialog.d.ts +7 -0
- package/dist/components/composites/update-dialog/update-dialog.d.ts.map +1 -1
- package/dist/components/composites/update-dialog/update-dialog.js +71 -26
- package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
- package/dist/components/conversation/internal/chat-composer-shell.js +1 -1
- package/dist/components/conversation/internal/chat-composer-shell.js.map +1 -1
- package/dist/components/conversation/message-composer.d.ts.map +1 -1
- package/dist/components/conversation/message-composer.js +1 -1
- package/dist/components/conversation/message-composer.js.map +1 -1
- package/dist/components/ui/alert/alert.d.ts +3 -1
- package/dist/components/ui/alert/alert.d.ts.map +1 -1
- package/dist/components/ui/alert/alert.js +18 -8
- package/dist/components/ui/alert/alert.js.map +1 -1
- package/dist/components/ui/badge/badge.js +4 -4
- package/dist/components/ui/badge/badge.js.map +1 -1
- package/dist/components/ui/context-menu/context-menu.d.ts.map +1 -1
- package/dist/components/ui/context-menu/context-menu.js +2 -2
- package/dist/components/ui/context-menu/context-menu.js.map +1 -1
- package/dist/components/ui/dialog/dialog.d.ts +17 -1
- package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog/dialog.js +120 -2
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/diff-block/diff-block.js +2 -2
- package/dist/components/ui/diff-block/diff-block.js.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts +3 -2
- package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js +5 -4
- package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/ui/entity-icon/entity-icon.d.ts +3 -3
- package/dist/components/ui/entity-icon/entity-icon.js +7 -7
- package/dist/components/ui/entity-icon/entity-icon.js.map +1 -1
- package/dist/components/ui/field/field.js +1 -1
- package/dist/components/ui/field/field.js.map +1 -1
- package/dist/components/ui/menubar/menubar.d.ts.map +1 -1
- package/dist/components/ui/menubar/menubar.js +2 -2
- package/dist/components/ui/menubar/menubar.js.map +1 -1
- package/dist/components/ui/modal-dialog/modal-dialog.d.ts +12 -2
- package/dist/components/ui/modal-dialog/modal-dialog.d.ts.map +1 -1
- package/dist/components/ui/modal-dialog/modal-dialog.js +44 -27
- package/dist/components/ui/modal-dialog/modal-dialog.js.map +1 -1
- package/dist/components/ui/status-chip/status-chip.js +4 -4
- package/dist/components/ui/status-chip/status-chip.js.map +1 -1
- package/dist/lib/overlay.d.ts +2 -0
- package/dist/lib/overlay.d.ts.map +1 -1
- package/dist/lib/overlay.js +3 -1
- package/dist/lib/overlay.js.map +1 -1
- package/dist/lib/themes.d.ts +7 -0
- package/dist/lib/themes.d.ts.map +1 -1
- package/dist/lib/themes.js +22 -1
- package/dist/lib/themes.js.map +1 -1
- package/dist/lib/tokens.d.ts.map +1 -1
- package/dist/lib/tokens.js +4 -4
- package/dist/lib/tokens.js.map +1 -1
- package/dist/r/src/components/composites/appearance-editor/appearance-editor.tsx +1 -1
- package/dist/r/src/components/composites/stat/stat.tsx +5 -8
- package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +98 -26
- package/dist/r/src/components/conversation/internal/chat-composer-shell.tsx +1 -1
- package/dist/r/src/components/conversation/message-composer.tsx +3 -1
- package/dist/r/src/components/ui/alert/alert.tsx +20 -7
- package/dist/r/src/components/ui/badge/badge.tsx +4 -4
- package/dist/r/src/components/ui/context-menu/context-menu.tsx +2 -2
- package/dist/r/src/components/ui/dialog/dialog.tsx +204 -2
- package/dist/r/src/components/ui/diff-block/diff-block.tsx +4 -4
- package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +5 -4
- package/dist/r/src/components/ui/entity-icon/entity-icon.tsx +7 -7
- package/dist/r/src/components/ui/field/field.tsx +1 -1
- package/dist/r/src/components/ui/menubar/menubar.tsx +2 -2
- package/dist/r/src/components/ui/modal-dialog/modal-dialog.tsx +24 -2
- package/dist/r/src/components/ui/status-chip/status-chip.tsx +4 -4
- package/dist/r/src/lib/overlay.ts +4 -0
- package/dist/r/src/lib/themes.ts +29 -0
- package/dist/r/src/lib/tokens.ts +19 -4
- package/dist/r/src/styles/theme.css +10 -10
- package/package.json +2 -2
- package/src/components/composites/appearance-editor/appearance-editor.tsx +1 -1
- package/src/components/composites/stat/stat.tsx +5 -8
- package/src/components/composites/update-dialog/update-dialog.tsx +98 -26
- package/src/components/conversation/internal/chat-composer-shell.tsx +1 -1
- package/src/components/conversation/message-composer.tsx +3 -1
- package/src/components/ui/alert/alert.tsx +20 -7
- package/src/components/ui/badge/badge.tsx +4 -4
- package/src/components/ui/context-menu/context-menu.tsx +2 -2
- package/src/components/ui/dialog/dialog.tsx +204 -2
- package/src/components/ui/diff-block/diff-block.tsx +4 -4
- package/src/components/ui/dropdown-menu/dropdown-menu.tsx +5 -4
- package/src/components/ui/entity-icon/entity-icon.tsx +7 -7
- package/src/components/ui/field/field.tsx +1 -1
- package/src/components/ui/menubar/menubar.tsx +2 -2
- package/src/components/ui/modal-dialog/modal-dialog.tsx +24 -2
- package/src/components/ui/status-chip/status-chip.tsx +4 -4
- package/src/lib/overlay.ts +4 -0
- package/src/lib/themes.ts +29 -0
- package/src/lib/tokens.ts +19 -4
- package/src/styles/theme.css +10 -10
|
@@ -1,10 +1,212 @@
|
|
|
1
1
|
import { Dialog as KobalteDialog } from '@kobalte/core/dialog'
|
|
2
2
|
import { X } from 'lucide-solid'
|
|
3
|
-
import type { ComponentProps, JSX } from 'solid-js'
|
|
4
|
-
import { Show, splitProps } from 'solid-js'
|
|
3
|
+
import type { Accessor, ComponentProps, JSX } from 'solid-js'
|
|
4
|
+
import { createRenderEffect, onCleanup, Show, splitProps } from 'solid-js'
|
|
5
5
|
import { overlayMotion, overlayPositioner, overlayScrim, overlaySurface } from '../../../lib/overlay'
|
|
6
6
|
import { cn } from '../../../lib/utils'
|
|
7
7
|
|
|
8
|
+
type FocusCycle = {
|
|
9
|
+
content: HTMLElement
|
|
10
|
+
opener: HTMLElement | null
|
|
11
|
+
openerEligible: boolean
|
|
12
|
+
closed: boolean
|
|
13
|
+
focusMovedOutside: boolean
|
|
14
|
+
closeAutoFocusScheduled: boolean
|
|
15
|
+
frame?: number
|
|
16
|
+
fallbackTimer?: number
|
|
17
|
+
observer?: MutationObserver
|
|
18
|
+
onFocusIn: (event: FocusEvent) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function canRestoreFocus(target: HTMLElement): boolean {
|
|
22
|
+
return (
|
|
23
|
+
target.isConnected &&
|
|
24
|
+
target !== document.body &&
|
|
25
|
+
target !== document.documentElement &&
|
|
26
|
+
!target.closest('[inert], [aria-hidden="true"]') &&
|
|
27
|
+
!target.matches(':disabled') &&
|
|
28
|
+
target.getClientRects().length > 0
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hasOtherOverlay(content: HTMLElement, opener: HTMLElement): boolean {
|
|
33
|
+
return [...document.querySelectorAll<HTMLElement>('[role="dialog"], [role="alertdialog"]')].some(
|
|
34
|
+
(overlay) =>
|
|
35
|
+
overlay !== content &&
|
|
36
|
+
!content.contains(overlay) &&
|
|
37
|
+
!overlay.contains(opener) &&
|
|
38
|
+
!overlay.closest('[aria-hidden="true"], [inert]') &&
|
|
39
|
+
overlay.getClientRects().length > 0
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type DialogFocusRestorationOptions = {
|
|
44
|
+
open: Accessor<boolean>
|
|
45
|
+
restoreFocusRef?: Accessor<HTMLElement | undefined>
|
|
46
|
+
onOpenAutoFocus?: ComponentProps<typeof KobalteDialog.Content>['onOpenAutoFocus']
|
|
47
|
+
onCloseAutoFocus?: ComponentProps<typeof KobalteDialog.Content>['onCloseAutoFocus']
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Adds guarded opener capture and restoration to a Kobalte dialog content.
|
|
52
|
+
* Composed surfaces use this internal helper; it is omitted from the public
|
|
53
|
+
* dialog barrel so the restoration policy stays with the dialog components.
|
|
54
|
+
*/
|
|
55
|
+
export function createDialogFocusRestoration({
|
|
56
|
+
open,
|
|
57
|
+
restoreFocusRef,
|
|
58
|
+
onOpenAutoFocus: callerOnOpenAutoFocus,
|
|
59
|
+
onCloseAutoFocus: callerOnCloseAutoFocus,
|
|
60
|
+
}: DialogFocusRestorationOptions) {
|
|
61
|
+
const focusCycles = new WeakMap<HTMLElement, FocusCycle>()
|
|
62
|
+
let activeFocusCycle: FocusCycle | undefined
|
|
63
|
+
let pendingOpener: HTMLElement | null = null
|
|
64
|
+
let pendingOpenerEligible = false
|
|
65
|
+
|
|
66
|
+
createRenderEffect(() => {
|
|
67
|
+
if (!open() || typeof document === 'undefined') {
|
|
68
|
+
pendingOpener = null
|
|
69
|
+
pendingOpenerEligible = false
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
const active = restoreFocusRef?.() ?? document.activeElement
|
|
73
|
+
pendingOpener = active instanceof HTMLElement ? active : null
|
|
74
|
+
pendingOpenerEligible = active instanceof HTMLElement && canRestoreFocus(active)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const finishFocusCycle = (cycle: FocusCycle) => {
|
|
78
|
+
if (cycle.frame !== undefined) window.cancelAnimationFrame(cycle.frame)
|
|
79
|
+
if (cycle.fallbackTimer !== undefined) window.clearTimeout(cycle.fallbackTimer)
|
|
80
|
+
cycle.observer?.disconnect()
|
|
81
|
+
document.removeEventListener('focusin', cycle.onFocusIn)
|
|
82
|
+
if (activeFocusCycle === cycle) activeFocusCycle = undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const scheduleFocusRestore = (cycle: FocusCycle) => {
|
|
86
|
+
if (cycle.frame !== undefined) return
|
|
87
|
+
cycle.frame = window.requestAnimationFrame(() => {
|
|
88
|
+
cycle.frame = undefined
|
|
89
|
+
const opener = cycle.opener
|
|
90
|
+
const active = document.activeElement
|
|
91
|
+
const focusIsAvailable =
|
|
92
|
+
active === document.body ||
|
|
93
|
+
active === document.documentElement ||
|
|
94
|
+
active === cycle.content ||
|
|
95
|
+
(active instanceof Node && cycle.content.contains(active))
|
|
96
|
+
if (
|
|
97
|
+
activeFocusCycle !== cycle ||
|
|
98
|
+
!cycle.closed ||
|
|
99
|
+
cycle.focusMovedOutside ||
|
|
100
|
+
!opener ||
|
|
101
|
+
!opener.isConnected ||
|
|
102
|
+
opener.matches(':disabled') ||
|
|
103
|
+
opener.getClientRects().length === 0 ||
|
|
104
|
+
!focusIsAvailable ||
|
|
105
|
+
hasOtherOverlay(cycle.content, opener)
|
|
106
|
+
) {
|
|
107
|
+
finishFocusCycle(cycle)
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Inert and aria-hidden are removed by the dialog's containment cleanup.
|
|
112
|
+
// Wait for that cleanup before returning focus to the original control.
|
|
113
|
+
if (opener.closest('[inert], [aria-hidden="true"]')) return
|
|
114
|
+
if (cycle.openerEligible && canRestoreFocus(opener)) {
|
|
115
|
+
opener.focus({ preventScroll: true })
|
|
116
|
+
}
|
|
117
|
+
finishFocusCycle(cycle)
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const onOpenAutoFocus = (event: Event) => {
|
|
122
|
+
const element = event.currentTarget
|
|
123
|
+
if (element instanceof HTMLElement) {
|
|
124
|
+
if (activeFocusCycle) finishFocusCycle(activeFocusCycle)
|
|
125
|
+
|
|
126
|
+
const openerWasCaptured = pendingOpener !== null
|
|
127
|
+
const opener = pendingOpener ?? document.activeElement
|
|
128
|
+
const openerEligible = openerWasCaptured
|
|
129
|
+
? pendingOpenerEligible
|
|
130
|
+
: opener instanceof HTMLElement && canRestoreFocus(opener)
|
|
131
|
+
pendingOpener = null
|
|
132
|
+
pendingOpenerEligible = false
|
|
133
|
+
const cycle: FocusCycle = {
|
|
134
|
+
content: element,
|
|
135
|
+
opener: opener instanceof HTMLElement ? opener : null,
|
|
136
|
+
openerEligible,
|
|
137
|
+
closed: false,
|
|
138
|
+
focusMovedOutside: false,
|
|
139
|
+
closeAutoFocusScheduled: false,
|
|
140
|
+
onFocusIn: (focusEvent) => {
|
|
141
|
+
const target = focusEvent.target
|
|
142
|
+
if (
|
|
143
|
+
!(target instanceof HTMLElement) ||
|
|
144
|
+
target === document.body ||
|
|
145
|
+
target === document.documentElement ||
|
|
146
|
+
target === element ||
|
|
147
|
+
element.contains(target) ||
|
|
148
|
+
target === (opener instanceof HTMLElement ? opener : null)
|
|
149
|
+
) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
cycle.focusMovedOutside = true
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
focusCycles.set(element, cycle)
|
|
156
|
+
activeFocusCycle = cycle
|
|
157
|
+
document.addEventListener('focusin', cycle.onFocusIn)
|
|
158
|
+
}
|
|
159
|
+
callerOnOpenAutoFocus?.(event)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const onCloseAutoFocus = (event: Event) => {
|
|
163
|
+
const element = event.currentTarget
|
|
164
|
+
const cycle = element instanceof HTMLElement ? focusCycles.get(element) : undefined
|
|
165
|
+
callerOnCloseAutoFocus?.(event)
|
|
166
|
+
|
|
167
|
+
if (!cycle) return
|
|
168
|
+
cycle.closed = true
|
|
169
|
+
|
|
170
|
+
// A caller that prevents Kobalte's close autofocus owns the focus decision.
|
|
171
|
+
if (event.defaultPrevented) {
|
|
172
|
+
finishFocusCycle(cycle)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (cycle.fallbackTimer !== undefined) {
|
|
177
|
+
window.clearTimeout(cycle.fallbackTimer)
|
|
178
|
+
cycle.fallbackTimer = undefined
|
|
179
|
+
}
|
|
180
|
+
cycle.closeAutoFocusScheduled = true
|
|
181
|
+
cycle.observer = new MutationObserver(() => scheduleFocusRestore(cycle))
|
|
182
|
+
cycle.observer.observe(document.body, {
|
|
183
|
+
attributes: true,
|
|
184
|
+
attributeFilter: ['aria-hidden', 'inert'],
|
|
185
|
+
childList: true,
|
|
186
|
+
subtree: true,
|
|
187
|
+
})
|
|
188
|
+
scheduleFocusRestore(cycle)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
onCleanup(() => {
|
|
192
|
+
const cycle = activeFocusCycle
|
|
193
|
+
if (!cycle) return
|
|
194
|
+
cycle.closed = true
|
|
195
|
+
|
|
196
|
+
// Kobalte dispatches unmount autofocus from a timer. This fallback only
|
|
197
|
+
// releases the scoped focus listener if the content did not dispatch it.
|
|
198
|
+
queueMicrotask(() => {
|
|
199
|
+
if (cycle.closeAutoFocusScheduled) return
|
|
200
|
+
cycle.fallbackTimer = window.setTimeout(() => {
|
|
201
|
+
cycle.fallbackTimer = undefined
|
|
202
|
+
if (!cycle.closeAutoFocusScheduled) finishFocusCycle(cycle)
|
|
203
|
+
}, 0)
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
return { onOpenAutoFocus, onCloseAutoFocus }
|
|
208
|
+
}
|
|
209
|
+
|
|
8
210
|
/**
|
|
9
211
|
* Dialog.
|
|
10
212
|
*
|
|
@@ -205,8 +205,8 @@ export function DiffBlock(props: DiffBlockProps) {
|
|
|
205
205
|
{/* The counts are text, so the summary survives greyscale and a reader
|
|
206
206
|
who cannot separate the two line tones still gets the shape of it. */}
|
|
207
207
|
<span class="shrink-0 font-mono text-2xs text-muted-foreground">
|
|
208
|
-
<span class="text-
|
|
209
|
-
<span class="text-
|
|
208
|
+
<span class="text-foreground">+{stats().added}</span>{' '}
|
|
209
|
+
<span class="text-foreground">−{stats().removed}</span>
|
|
210
210
|
</span>
|
|
211
211
|
</span>
|
|
212
212
|
{local.actions}
|
|
@@ -278,8 +278,8 @@ export function DiffSummary(props: { patch: string; class?: string; onSelect?: (
|
|
|
278
278
|
>
|
|
279
279
|
<span class="truncate text-foreground">{name()}</span>
|
|
280
280
|
<span class="shrink-0 text-2xs">
|
|
281
|
-
<span class="text-
|
|
282
|
-
<span class="text-
|
|
281
|
+
<span class="text-foreground">+{stats().added}</span>{' '}
|
|
282
|
+
<span class="text-foreground">−{stats().removed}</span>
|
|
283
283
|
</span>
|
|
284
284
|
</button>
|
|
285
285
|
)
|
|
@@ -4,6 +4,7 @@ import type { ComponentProps } from 'solid-js'
|
|
|
4
4
|
import { splitProps } from 'solid-js'
|
|
5
5
|
import {
|
|
6
6
|
menuContentPadding,
|
|
7
|
+
destructiveMenuItem,
|
|
7
8
|
menuItem,
|
|
8
9
|
menuLabel,
|
|
9
10
|
menuSeparator,
|
|
@@ -25,8 +26,9 @@ import { cn } from '../../../lib/utils'
|
|
|
25
26
|
* highlight, Escape to close and return focus — which is exactly what a
|
|
26
27
|
* hand-rolled `div` with click handlers gets wrong.
|
|
27
28
|
*
|
|
28
|
-
* The `destructive` variant
|
|
29
|
-
*
|
|
29
|
+
* The `destructive` variant marks the row with a status-coloured edge and
|
|
30
|
+
* highlighted tint while keeping its label on the body foreground. Pairing it
|
|
31
|
+
* with an AlertDialog is the caller's job.
|
|
30
32
|
*/
|
|
31
33
|
export function DropdownMenu(props: ComponentProps<typeof KobalteDropdownMenu>) {
|
|
32
34
|
return <KobalteDropdownMenu {...props} />
|
|
@@ -83,8 +85,7 @@ export function DropdownMenuItem(
|
|
|
83
85
|
class={cn(
|
|
84
86
|
menuItem,
|
|
85
87
|
{
|
|
86
|
-
|
|
87
|
-
local.variant === 'destructive',
|
|
88
|
+
[destructiveMenuItem]: local.variant === 'destructive',
|
|
88
89
|
},
|
|
89
90
|
local.class
|
|
90
91
|
)}
|
|
@@ -17,9 +17,9 @@ import { cn } from '../../../lib/utils'
|
|
|
17
17
|
* 1. **A glyph is always present.** A brand with no logo gets a monogram, not an
|
|
18
18
|
* empty box. `fallback` takes the name and returns the glyph, so a caller can
|
|
19
19
|
* choose initials, a domain letter, or a kind icon.
|
|
20
|
-
* 2. **The tile owns its own contrast.** `tone` tints the background
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* 2. **The tile owns its own contrast.** `tone` tints the background while
|
|
21
|
+
* monogram text keeps the body foreground; vector glyphs retain the status
|
|
22
|
+
* colour. A caller never picks a colour pair or ships a glyph that disappears.
|
|
23
23
|
* 3. **Size is on the control ladder**, not arbitrary. A tile in a rail and a
|
|
24
24
|
* tile in a detail header are the same two or three sizes every other
|
|
25
25
|
* interactive element uses.
|
|
@@ -51,10 +51,10 @@ export const entityIconVariants = cva(
|
|
|
51
51
|
primary: 'border-transparent bg-primary-subtle text-primary',
|
|
52
52
|
/** Bare: no fill and no edge, for a tile that already sits on a surface. */
|
|
53
53
|
bare: 'border-transparent bg-transparent text-foreground',
|
|
54
|
-
success: 'border-transparent bg-success-subtle text-success',
|
|
55
|
-
warning: 'border-transparent bg-warning-subtle text-warning',
|
|
56
|
-
danger: 'border-transparent bg-destructive-subtle text-destructive',
|
|
57
|
-
info: 'border-transparent bg-info-subtle text-info',
|
|
54
|
+
success: 'border-transparent bg-success-subtle text-foreground [&_svg]:text-success',
|
|
55
|
+
warning: 'border-transparent bg-warning-subtle text-foreground [&_svg]:text-warning',
|
|
56
|
+
danger: 'border-transparent bg-destructive-subtle text-foreground [&_svg]:text-destructive',
|
|
57
|
+
info: 'border-transparent bg-info-subtle text-foreground [&_svg]:text-info',
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
60
|
defaultVariants: { shape: 'rounded', size: 'md', tone: 'neutral' },
|
|
@@ -44,7 +44,7 @@ export function FieldDescription(props: ComponentProps<typeof KobalteTextField.D
|
|
|
44
44
|
export function FieldError(props: ComponentProps<typeof KobalteTextField.ErrorMessage>) {
|
|
45
45
|
const [local, rest] = splitProps(props, ['class'])
|
|
46
46
|
return (
|
|
47
|
-
<KobalteTextField.ErrorMessage class={cn('text-
|
|
47
|
+
<KobalteTextField.ErrorMessage class={cn('text-foreground text-sm', local.class)} {...rest} />
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -4,6 +4,7 @@ import type { ComponentProps } from 'solid-js'
|
|
|
4
4
|
import { splitProps } from 'solid-js'
|
|
5
5
|
import {
|
|
6
6
|
menuContentPadding,
|
|
7
|
+
destructiveMenuItem,
|
|
7
8
|
menuItem,
|
|
8
9
|
menuLabel,
|
|
9
10
|
menuSeparator,
|
|
@@ -98,8 +99,7 @@ export function MenubarItem(
|
|
|
98
99
|
class={cn(
|
|
99
100
|
menuItem,
|
|
100
101
|
{
|
|
101
|
-
|
|
102
|
-
local.variant === 'destructive',
|
|
102
|
+
[destructiveMenuItem]: local.variant === 'destructive',
|
|
103
103
|
},
|
|
104
104
|
local.class
|
|
105
105
|
)}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ComponentProps, JSX } from 'solid-js'
|
|
1
|
+
import type { Accessor, ComponentProps, JSX } from 'solid-js'
|
|
2
2
|
import { Show, createEffect, createSignal, onCleanup, splitProps } from 'solid-js'
|
|
3
3
|
import { cn } from '../../../lib/utils'
|
|
4
4
|
import {
|
|
5
|
+
createDialogFocusRestoration,
|
|
5
6
|
Dialog,
|
|
6
7
|
DialogContent,
|
|
7
8
|
DialogDescription,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
/**
|
|
13
14
|
* ModalDialog.
|
|
14
15
|
*
|
|
15
|
-
* A dialog that owns
|
|
16
|
+
* A dialog that owns four things `Dialog` leaves to its caller, each of which is a
|
|
16
17
|
* real defect when it is missed:
|
|
17
18
|
*
|
|
18
19
|
* 1. **It unmounts while closed.** An always-mounted dialog that closes as a side
|
|
@@ -27,6 +28,11 @@ import {
|
|
|
27
28
|
* from `title`, so its name does not depend on the title id registration effect.
|
|
28
29
|
* Pass `aria-label` to use a different name while keeping the visible title;
|
|
29
30
|
* pass `aria-labelledby` when another visible label should take precedence.
|
|
31
|
+
* 4. **It returns focus to the opener.** The opener is captured before the dialog
|
|
32
|
+
* autofocuses and restored after the owner closes, unless a close-autofocus
|
|
33
|
+
* handler takes control or focus has moved to another layer. For controlled
|
|
34
|
+
* dialogs without a composed trigger, pass `restoreFocusRef` when the opener
|
|
35
|
+
* cannot be inferred from the focused element.
|
|
30
36
|
*
|
|
31
37
|
* Use `Dialog` directly when you need its composition; use this when you want a
|
|
32
38
|
* dialog that is correct by default.
|
|
@@ -50,6 +56,11 @@ export type ModalDialogProps = Omit<
|
|
|
50
56
|
'aria-label'?: string
|
|
51
57
|
/** Names the dialog from a visible element; ARIA gives it precedence over `aria-label`. */
|
|
52
58
|
'aria-labelledby'?: string
|
|
59
|
+
/**
|
|
60
|
+
* Supplies the external control to focus when this controlled dialog closes.
|
|
61
|
+
* Defaults to the element focused immediately before the dialog opens.
|
|
62
|
+
*/
|
|
63
|
+
restoreFocusRef?: Accessor<HTMLElement | undefined>
|
|
53
64
|
children?: JSX.Element
|
|
54
65
|
}
|
|
55
66
|
|
|
@@ -65,9 +76,18 @@ export function ModalDialog(props: ModalDialogProps) {
|
|
|
65
76
|
'children',
|
|
66
77
|
'aria-label',
|
|
67
78
|
'aria-labelledby',
|
|
79
|
+
'restoreFocusRef',
|
|
80
|
+
'onOpenAutoFocus',
|
|
81
|
+
'onCloseAutoFocus',
|
|
68
82
|
])
|
|
69
83
|
|
|
70
84
|
const [content, setContent] = createSignal<HTMLElement>()
|
|
85
|
+
const focusRestoration = createDialogFocusRestoration({
|
|
86
|
+
open: () => local.open,
|
|
87
|
+
restoreFocusRef: local.restoreFocusRef,
|
|
88
|
+
onOpenAutoFocus: local.onOpenAutoFocus,
|
|
89
|
+
onCloseAutoFocus: local.onCloseAutoFocus,
|
|
90
|
+
})
|
|
71
91
|
|
|
72
92
|
createEffect(() => {
|
|
73
93
|
if (!local.open) return
|
|
@@ -100,6 +120,8 @@ export function ModalDialog(props: ModalDialogProps) {
|
|
|
100
120
|
local['aria-labelledby'] ?? (local['aria-label'] !== undefined ? '' : undefined)
|
|
101
121
|
}
|
|
102
122
|
{...rest}
|
|
123
|
+
onOpenAutoFocus={focusRestoration.onOpenAutoFocus}
|
|
124
|
+
onCloseAutoFocus={focusRestoration.onCloseAutoFocus}
|
|
103
125
|
>
|
|
104
126
|
<DialogHeader>
|
|
105
127
|
<DialogTitle class="flex items-center gap-2">
|
|
@@ -47,10 +47,10 @@ export const statusDotVariants = cva('size-2 shrink-0 rounded-full', {
|
|
|
47
47
|
|
|
48
48
|
const labelTone: Record<StatusTone, string> = {
|
|
49
49
|
neutral: 'text-foreground',
|
|
50
|
-
success: 'text-
|
|
51
|
-
warning: 'text-
|
|
52
|
-
danger: 'text-
|
|
53
|
-
info: 'text-
|
|
50
|
+
success: 'text-foreground',
|
|
51
|
+
warning: 'text-foreground',
|
|
52
|
+
danger: 'text-foreground',
|
|
53
|
+
info: 'text-foreground',
|
|
54
54
|
unknown: 'text-muted-foreground',
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -59,6 +59,10 @@ export const menuItem = [
|
|
|
59
59
|
'[&_svg:not([class*=text-])]:text-muted-foreground',
|
|
60
60
|
].join(' ')
|
|
61
61
|
|
|
62
|
+
/** A destructive action keeps readable text and marks its row with a status edge. */
|
|
63
|
+
export const destructiveMenuItem =
|
|
64
|
+
'border-s-2 border-destructive ps-1.5 text-foreground data-[highlighted]:bg-destructive-subtle'
|
|
65
|
+
|
|
62
66
|
/** The label above a group of menu rows. */
|
|
63
67
|
export const menuLabel =
|
|
64
68
|
'px-2 py-1.5 text-2xs font-medium tracking-wide text-muted-foreground uppercase'
|
package/dist/r/src/lib/themes.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
getAccent,
|
|
41
41
|
hasTheme,
|
|
42
42
|
statusForeground,
|
|
43
|
+
tint,
|
|
43
44
|
primaryHover,
|
|
44
45
|
primarySubtleCss,
|
|
45
46
|
syntaxRoles,
|
|
@@ -76,9 +77,16 @@ export type ThemeColors = {
|
|
|
76
77
|
accentForeground: string
|
|
77
78
|
destructive: string
|
|
78
79
|
destructiveForeground: string
|
|
80
|
+
destructiveSubtle: string
|
|
79
81
|
success: string
|
|
82
|
+
successForeground: string
|
|
83
|
+
successSubtle: string
|
|
80
84
|
warning: string
|
|
85
|
+
warningForeground: string
|
|
86
|
+
warningSubtle: string
|
|
81
87
|
info: string
|
|
88
|
+
infoForeground: string
|
|
89
|
+
infoSubtle: string
|
|
82
90
|
border: string
|
|
83
91
|
input: string
|
|
84
92
|
ring: string
|
|
@@ -186,9 +194,16 @@ const ROLE_FOR: Readonly<Record<keyof ThemeColors, keyof AdeaThemeRecord['colors
|
|
|
186
194
|
destructive: 'error',
|
|
187
195
|
// Overwritten below from the measured pairing; nominal here so the table is total.
|
|
188
196
|
destructiveForeground: 'error',
|
|
197
|
+
destructiveSubtle: 'error',
|
|
189
198
|
success: 'success',
|
|
199
|
+
successForeground: 'success',
|
|
200
|
+
successSubtle: 'success',
|
|
190
201
|
warning: 'warning',
|
|
202
|
+
warningForeground: 'warning',
|
|
203
|
+
warningSubtle: 'warning',
|
|
191
204
|
info: 'info',
|
|
205
|
+
infoForeground: 'info',
|
|
206
|
+
infoSubtle: 'info',
|
|
192
207
|
border: 'border',
|
|
193
208
|
input: 'border',
|
|
194
209
|
ring: 'accent',
|
|
@@ -208,6 +223,13 @@ function toVariant(theme: AdeaThemeRecord): ThemeVariant {
|
|
|
208
223
|
// red it is black and on a deep red it is white, and using the body text would put
|
|
209
224
|
// white on a mid-tone red at about 3:1.
|
|
210
225
|
colors.destructiveForeground = statusForeground(theme, 'error')
|
|
226
|
+
colors.destructiveSubtle = tint(theme.colors.error, theme.colors.background)
|
|
227
|
+
colors.successForeground = statusForeground(theme, 'success')
|
|
228
|
+
colors.successSubtle = tint(theme.colors.success, theme.colors.background)
|
|
229
|
+
colors.warningForeground = statusForeground(theme, 'warning')
|
|
230
|
+
colors.warningSubtle = tint(theme.colors.warning, theme.colors.background)
|
|
231
|
+
colors.infoForeground = statusForeground(theme, 'info')
|
|
232
|
+
colors.infoSubtle = tint(theme.colors.info, theme.colors.background)
|
|
211
233
|
|
|
212
234
|
const syntax = syntaxRoles(theme)
|
|
213
235
|
|
|
@@ -374,9 +396,16 @@ export function themeCssVariables(theme: ThemeVariant): Record<string, string> {
|
|
|
374
396
|
'--accent-foreground': c.accentForeground,
|
|
375
397
|
'--destructive': c.destructive,
|
|
376
398
|
'--destructive-foreground': c.destructiveForeground,
|
|
399
|
+
'--destructive-subtle': c.destructiveSubtle,
|
|
377
400
|
'--success': c.success,
|
|
401
|
+
'--success-foreground': c.successForeground,
|
|
402
|
+
'--success-subtle': c.successSubtle,
|
|
378
403
|
'--warning': c.warning,
|
|
404
|
+
'--warning-foreground': c.warningForeground,
|
|
405
|
+
'--warning-subtle': c.warningSubtle,
|
|
379
406
|
'--info': c.info,
|
|
407
|
+
'--info-foreground': c.infoForeground,
|
|
408
|
+
'--info-subtle': c.infoSubtle,
|
|
380
409
|
'--border': c.border,
|
|
381
410
|
'--input': c.input,
|
|
382
411
|
'--ring': c.ring,
|
package/dist/r/src/lib/tokens.ts
CHANGED
|
@@ -196,11 +196,16 @@ export const colorTokens: TokenDefinition[] = [
|
|
|
196
196
|
name: 'destructive-subtle',
|
|
197
197
|
kind: 'color',
|
|
198
198
|
themed: true,
|
|
199
|
-
description: '
|
|
199
|
+
description: 'Opaque destructive tint against the canvas; subtle labels use foreground.',
|
|
200
200
|
},
|
|
201
201
|
{ name: 'success', kind: 'color', themed: true, description: 'A completed or healthy state.' },
|
|
202
202
|
{ name: 'success-foreground', kind: 'color', themed: true, description: 'Label on `success`.' },
|
|
203
|
-
{
|
|
203
|
+
{
|
|
204
|
+
name: 'success-subtle',
|
|
205
|
+
kind: 'color',
|
|
206
|
+
themed: true,
|
|
207
|
+
description: 'Opaque success tint against the canvas; subtle labels use foreground.',
|
|
208
|
+
},
|
|
204
209
|
{
|
|
205
210
|
name: 'warning',
|
|
206
211
|
kind: 'color',
|
|
@@ -208,10 +213,20 @@ export const colorTokens: TokenDefinition[] = [
|
|
|
208
213
|
description: 'Something needs attention but is not broken.',
|
|
209
214
|
},
|
|
210
215
|
{ name: 'warning-foreground', kind: 'color', themed: true, description: 'Label on `warning`.' },
|
|
211
|
-
{
|
|
216
|
+
{
|
|
217
|
+
name: 'warning-subtle',
|
|
218
|
+
kind: 'color',
|
|
219
|
+
themed: true,
|
|
220
|
+
description: 'Opaque warning tint against the canvas; subtle labels use foreground.',
|
|
221
|
+
},
|
|
212
222
|
{ name: 'info', kind: 'color', themed: true, description: 'Neutral information.' },
|
|
213
223
|
{ name: 'info-foreground', kind: 'color', themed: true, description: 'Label on `info`.' },
|
|
214
|
-
{
|
|
224
|
+
{
|
|
225
|
+
name: 'info-subtle',
|
|
226
|
+
kind: 'color',
|
|
227
|
+
themed: true,
|
|
228
|
+
description: 'Opaque info tint against the canvas; subtle labels use foreground.',
|
|
229
|
+
},
|
|
215
230
|
|
|
216
231
|
// Lines
|
|
217
232
|
{
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
--muted: oklch(0.898 0.0108 261.79);
|
|
79
79
|
--muted-foreground: oklch(0.474 0.0245 265.01);
|
|
80
80
|
|
|
81
|
-
/* Status
|
|
81
|
+
/* Status fills; solid labels and subtle surfaces come from the catalogue. */
|
|
82
82
|
--destructive: oklch(0.4705 0.1626 25.78);
|
|
83
83
|
--destructive-foreground: oklch(0.933 0.016 261.79);
|
|
84
84
|
--success: oklch(0.4311 0.1809 145.62);
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
--warning-foreground: oklch(0.933 0.016 261.79);
|
|
88
88
|
--info: oklch(0.4915 0.1155 202.14);
|
|
89
89
|
--info-foreground: oklch(0.933 0.016 261.79);
|
|
90
|
-
--destructive-subtle:
|
|
91
|
-
--success-subtle:
|
|
92
|
-
--warning-subtle:
|
|
93
|
-
--info-subtle:
|
|
90
|
+
--destructive-subtle: oklch(0.8683 0.0365 279.15);
|
|
91
|
+
--success-subtle: oklch(0.8627 0.0391 245.53);
|
|
92
|
+
--warning-subtle: oklch(0.8662 0.0334 286.73);
|
|
93
|
+
--info-subtle: oklch(0.8712 0.0299 253.44);
|
|
94
94
|
|
|
95
95
|
/* Lines */
|
|
96
96
|
--border: oklch(0.783 0.0144 261.79);
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
--muted: oklch(0.2268 0.0288 265.37);
|
|
214
214
|
--muted-foreground: oklch(0.6308 0.0224 262.91);
|
|
215
215
|
|
|
216
|
-
/* Status
|
|
216
|
+
/* Status fills; solid labels and subtle surfaces come from the catalogue. */
|
|
217
217
|
--destructive: oklch(0.7345 0.1626 25.78);
|
|
218
218
|
--destructive-foreground: oklch(0.1918 0.0238 265.37);
|
|
219
219
|
--success: oklch(0.6951 0.1809 145.62);
|
|
@@ -222,10 +222,10 @@
|
|
|
222
222
|
--warning-foreground: oklch(0.1918 0.0238 265.37);
|
|
223
223
|
--info: oklch(0.7555 0.1155 202.14);
|
|
224
224
|
--info-foreground: oklch(0.1918 0.0238 265.37);
|
|
225
|
-
--destructive-subtle:
|
|
226
|
-
--success-subtle:
|
|
227
|
-
--warning-subtle:
|
|
228
|
-
--info-subtle:
|
|
225
|
+
--destructive-subtle: oklch(0.2678 0.0432 282.23);
|
|
226
|
+
--success-subtle: oklch(0.2623 0.0458 248.61);
|
|
227
|
+
--warning-subtle: oklch(0.2657 0.0401 289.81);
|
|
228
|
+
--info-subtle: oklch(0.2707 0.0366 256.52);
|
|
229
229
|
|
|
230
230
|
/* Lines */
|
|
231
231
|
--border: oklch(0.3418 0.0214 265.37);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adea-ai/ui",
|
|
3
|
-
"version": "0.71.
|
|
3
|
+
"version": "0.71.3",
|
|
4
4
|
"description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adea",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"check:tree-shaking": "bun scripts/check-tree-shaking.ts",
|
|
97
97
|
"theme:build": "bun scripts/build-default-theme.ts",
|
|
98
98
|
"theme:check": "bun scripts/build-default-theme.ts --check",
|
|
99
|
-
"check:packed-layout": "node scripts/check-packed-layout.mjs",
|
|
99
|
+
"check:packed-layout": "node --test scripts/check-packed-layout-process.test.mjs && node scripts/check-packed-layout.mjs",
|
|
100
100
|
"check:packed-consumer": "bun scripts/check-packed-consumer.ts",
|
|
101
101
|
"check:packed-conversation": "bun scripts/check-packed-conversation.ts",
|
|
102
102
|
"check:packed-transcript": "bun scripts/check-packed-transcript.ts",
|
|
@@ -121,7 +121,7 @@ export function AppearanceEditor(props: AppearanceEditorProps) {
|
|
|
121
121
|
onInput={(event) => props.onChange({ accent: event.currentTarget.value })}
|
|
122
122
|
/>
|
|
123
123
|
<Show when={props.customAccentError}>
|
|
124
|
-
<p id={errorId} role="alert" class="mt-1 text-xs text-
|
|
124
|
+
<p id={errorId} role="alert" class="mt-1 text-xs text-foreground">
|
|
125
125
|
{props.customAccentError}
|
|
126
126
|
</p>
|
|
127
127
|
</Show>
|
|
@@ -59,18 +59,15 @@ export function Stat(props: StatProps) {
|
|
|
59
59
|
</Show>
|
|
60
60
|
</div>
|
|
61
61
|
<Show when={local.delta}>
|
|
62
|
-
<div
|
|
63
|
-
class={cn('flex items-center gap-1 text-xs', {
|
|
64
|
-
'text-success': local.deltaTone === 'up',
|
|
65
|
-
'text-destructive': local.deltaTone === 'down',
|
|
66
|
-
'text-muted-foreground': local.deltaTone === undefined || local.deltaTone === 'neutral',
|
|
67
|
-
})}
|
|
68
|
-
>
|
|
62
|
+
<div class="text-foreground flex items-center gap-1 text-xs">
|
|
69
63
|
<Show when={local.deltaTone === 'up' || local.deltaTone === 'down'}>
|
|
70
64
|
<Dynamic
|
|
71
65
|
component={local.deltaTone === 'down' ? ArrowDownRight : ArrowUpRight}
|
|
72
66
|
aria-hidden="true"
|
|
73
|
-
class=
|
|
67
|
+
class={cn('size-3', {
|
|
68
|
+
'text-success': local.deltaTone === 'up',
|
|
69
|
+
'text-destructive': local.deltaTone === 'down',
|
|
70
|
+
})}
|
|
74
71
|
/>
|
|
75
72
|
</Show>
|
|
76
73
|
<span class="tabular-nums">{local.delta}</span>
|