@adea-ai/ui 0.108.0 → 0.109.1
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/NOTICE +13 -0
- package/dist/components/composites/update-dialog/update-dialog.js +1 -1
- package/dist/components/layout/contextual-sidebar/contextual-sidebar.d.ts.map +1 -1
- package/dist/components/layout/contextual-sidebar/contextual-sidebar.js +45 -35
- package/dist/components/layout/contextual-sidebar/contextual-sidebar.js.map +1 -1
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.d.ts +12 -0
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.d.ts.map +1 -0
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.js +36 -0
- package/dist/components/layout/contextual-sidebar/mobile-contextual-sidebar.js.map +1 -0
- package/dist/components/ui/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog/alert-dialog.js +2 -6
- package/dist/components/ui/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/components/ui/annotation-surface/annotation-surface.css +27 -0
- package/dist/components/ui/annotation-surface/annotation-surface.d.ts +66 -0
- package/dist/components/ui/annotation-surface/annotation-surface.d.ts.map +1 -0
- package/dist/components/ui/annotation-surface/annotation-surface.js +244 -0
- package/dist/components/ui/annotation-surface/annotation-surface.js.map +1 -0
- package/dist/components/ui/annotation-surface/geometry.d.ts +10 -0
- package/dist/components/ui/annotation-surface/geometry.d.ts.map +1 -0
- package/dist/components/ui/annotation-surface/geometry.js +19 -0
- package/dist/components/ui/annotation-surface/geometry.js.map +1 -0
- package/dist/components/ui/annotation-surface/index.d.ts +4 -0
- package/dist/components/ui/annotation-surface/index.d.ts.map +1 -0
- package/dist/components/ui/annotation-surface/index.js +2 -0
- package/dist/components/ui/board/board.js +1 -1
- package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog/dialog.js +2 -6
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/item/item.d.ts +1 -1
- package/dist/components/ui/sheet/sheet.d.ts +1 -1
- package/dist/components/ui/sheet/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet/sheet.js +2 -5
- package/dist/components/ui/sheet/sheet.js.map +1 -1
- package/dist/components/ui/typography/typography.d.ts.map +1 -1
- package/dist/components/ui/typography/typography.js +2 -2
- package/dist/components/ui/typography/typography.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/lib/overlay.d.ts +5 -0
- package/dist/lib/overlay.d.ts.map +1 -1
- package/dist/lib/overlay.js +6 -1
- package/dist/lib/overlay.js.map +1 -1
- package/dist/r/alert-dialog.json +0 -1
- package/dist/r/annotation-surface.json +32 -0
- package/dist/r/contextual-sidebar.json +5 -0
- package/dist/r/dialog.json +0 -1
- package/dist/r/registry.json +37 -3
- package/dist/r/sheet.json +0 -1
- package/dist/r/src/components/layout/contextual-sidebar/contextual-sidebar.tsx +41 -16
- package/dist/r/src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx +28 -0
- package/dist/r/src/components/ui/alert-dialog/alert-dialog.tsx +8 -8
- package/dist/r/src/components/ui/annotation-surface/annotation-surface.css +27 -0
- package/dist/r/src/components/ui/annotation-surface/annotation-surface.tsx +316 -0
- package/dist/r/src/components/ui/annotation-surface/geometry.ts +31 -0
- package/dist/r/src/components/ui/annotation-surface/index.ts +13 -0
- package/dist/r/src/components/ui/dialog/dialog.tsx +8 -8
- package/dist/r/src/components/ui/sheet/sheet.tsx +2 -7
- package/dist/r/src/components/ui/typography/typography.tsx +3 -2
- package/dist/r/src/lib/overlay.ts +6 -0
- package/package.json +3 -2
- package/registry.json +37 -3
- package/src/components/layout/contextual-sidebar/contextual-sidebar.tsx +41 -16
- package/src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx +28 -0
- package/src/components/ui/alert-dialog/alert-dialog.tsx +8 -8
- package/src/components/ui/annotation-surface/annotation-surface.css +27 -0
- package/src/components/ui/annotation-surface/annotation-surface.tsx +316 -0
- package/src/components/ui/annotation-surface/geometry.ts +31 -0
- package/src/components/ui/annotation-surface/index.ts +13 -0
- package/src/components/ui/dialog/dialog.tsx +8 -8
- package/src/components/ui/sheet/sheet.tsx +2 -7
- package/src/components/ui/typography/typography.tsx +3 -2
- package/src/index.ts +1 -0
- package/src/lib/overlay.ts +6 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createEffect,
|
|
3
|
+
createSignal,
|
|
4
|
+
createUniqueId,
|
|
5
|
+
onCleanup,
|
|
6
|
+
Show,
|
|
7
|
+
splitProps,
|
|
8
|
+
type JSX,
|
|
9
|
+
} from 'solid-js'
|
|
10
|
+
import { cn } from '../../../lib/utils'
|
|
11
|
+
import './annotation-surface.css'
|
|
12
|
+
import {
|
|
13
|
+
clampSurfaceFraction,
|
|
14
|
+
normalizedSurfacePoint,
|
|
15
|
+
type AnnotationSurfacePoint,
|
|
16
|
+
} from './geometry'
|
|
17
|
+
|
|
18
|
+
export type AnnotationSurfaceRegion = Readonly<{
|
|
19
|
+
x: number
|
|
20
|
+
y: number
|
|
21
|
+
width: number
|
|
22
|
+
height: number
|
|
23
|
+
}>
|
|
24
|
+
|
|
25
|
+
export type AnnotationSurfaceDrag = Readonly<{
|
|
26
|
+
start: AnnotationSurfacePoint
|
|
27
|
+
current: AnnotationSurfacePoint
|
|
28
|
+
}>
|
|
29
|
+
|
|
30
|
+
export type AnnotationSurfaceTool = 'region' | 'point'
|
|
31
|
+
export type AnnotationSurfaceDragPhase = 'start' | 'move' | 'end' | 'cancel' | 'keyboard'
|
|
32
|
+
export type AnnotationSurfacePointerEvent = PointerEvent & { currentTarget: HTMLDivElement }
|
|
33
|
+
export type AnnotationSurfaceKeyboardEvent = KeyboardEvent & { currentTarget: HTMLDivElement }
|
|
34
|
+
export type AnnotationSurfaceInputEvent =
|
|
35
|
+
| AnnotationSurfacePointerEvent
|
|
36
|
+
| AnnotationSurfaceKeyboardEvent
|
|
37
|
+
|
|
38
|
+
type SurfaceElementProps = Pick<JSX.HTMLAttributes<HTMLDivElement>, 'id' | 'aria-describedby'>
|
|
39
|
+
|
|
40
|
+
export type AnnotationSurfaceProps = SurfaceElementProps & {
|
|
41
|
+
/** Accessible instructions for the keyboard-operated surface. */
|
|
42
|
+
label: string
|
|
43
|
+
/** Region draws a captured drag; point reports a single placement point. */
|
|
44
|
+
tool: AnnotationSurfaceTool
|
|
45
|
+
/** Normalized 0..1 geometry supplied by the host; absent means no region. */
|
|
46
|
+
region?: AnnotationSurfaceRegion
|
|
47
|
+
/** Visible idle guidance. It is hidden while a region is present. */
|
|
48
|
+
hint?: JSX.Element
|
|
49
|
+
/** Disable new drawing while the host is busy. Defaults to true. */
|
|
50
|
+
interactive?: boolean
|
|
51
|
+
/** Changing this value cancels an in-flight drag owned by the host context. */
|
|
52
|
+
resetKey?: string | number
|
|
53
|
+
/** Receives one normalized point in point mode. */
|
|
54
|
+
onPoint?: (point: AnnotationSurfacePoint, event: AnnotationSurfaceInputEvent) => void
|
|
55
|
+
/** Receives drag phases; cancel has no drag and fires at most once per capture. */
|
|
56
|
+
onDragChange?: (
|
|
57
|
+
drag: AnnotationSurfaceDrag | undefined,
|
|
58
|
+
phase: AnnotationSurfaceDragPhase,
|
|
59
|
+
event?: AnnotationSurfaceInputEvent
|
|
60
|
+
) => void
|
|
61
|
+
/** Domain keyboard policy stays with the host and receives the native event. */
|
|
62
|
+
onKeyDown?: (event: AnnotationSurfaceKeyboardEvent) => void
|
|
63
|
+
/** Ref to the actual focusable and pointer-capturing HTMLDivElement. */
|
|
64
|
+
ref?: (element: HTMLDivElement) => void
|
|
65
|
+
/** Layout only; appearance remains owned by the shared component. */
|
|
66
|
+
class?: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type ActiveDrag = AnnotationSurfaceDrag & {
|
|
70
|
+
pointerId: number
|
|
71
|
+
target: HTMLDivElement
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function pointFrom(event: PointerEvent, target: HTMLDivElement): AnnotationSurfacePoint {
|
|
75
|
+
const bounds = target.getBoundingClientRect()
|
|
76
|
+
return normalizedSurfacePoint(event.clientX, event.clientY, bounds)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* AnnotationSurface.
|
|
81
|
+
*
|
|
82
|
+
* A keyboard-focusable, viewport-shaped drawing surface for hosts that own an
|
|
83
|
+
* annotation domain. It renders normalized region geometry and idle guidance,
|
|
84
|
+
* captures region drags, and reports normalized points without reading or
|
|
85
|
+
* rendering screenshot pixels. The host keeps tool policy, domain geometry,
|
|
86
|
+
* persistence and capture commands.
|
|
87
|
+
*
|
|
88
|
+
* This bespoke application widget is necessary because a generic button or
|
|
89
|
+
* slider cannot represent a two-dimensional drawing viewport. Its keyboard
|
|
90
|
+
* behavior is supplied by the host and forwarded as a native key event. Space
|
|
91
|
+
* creates a centered region or places a point at the center; Escape is left to
|
|
92
|
+
* the host first, which can prevent the default gesture cancellation while it
|
|
93
|
+
* applies its own draft and mode rules.
|
|
94
|
+
*/
|
|
95
|
+
export function AnnotationSurface(props: AnnotationSurfaceProps) {
|
|
96
|
+
const [local] = splitProps(props, [
|
|
97
|
+
'id',
|
|
98
|
+
'aria-describedby',
|
|
99
|
+
'label',
|
|
100
|
+
'tool',
|
|
101
|
+
'region',
|
|
102
|
+
'hint',
|
|
103
|
+
'interactive',
|
|
104
|
+
'resetKey',
|
|
105
|
+
'onPoint',
|
|
106
|
+
'onDragChange',
|
|
107
|
+
'onKeyDown',
|
|
108
|
+
'ref',
|
|
109
|
+
'class',
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
const [dragging, setDragging] = createSignal(false)
|
|
113
|
+
const hintId = createUniqueId()
|
|
114
|
+
let activeDrag: ActiveDrag | undefined
|
|
115
|
+
let previousResetKey = local.resetKey
|
|
116
|
+
let previousTool = local.tool
|
|
117
|
+
|
|
118
|
+
const isInteractive = () => local.interactive ?? true
|
|
119
|
+
|
|
120
|
+
function releaseCapture(drag: ActiveDrag): void {
|
|
121
|
+
try {
|
|
122
|
+
if (drag.target.hasPointerCapture(drag.pointerId))
|
|
123
|
+
drag.target.releasePointerCapture(drag.pointerId)
|
|
124
|
+
} catch {
|
|
125
|
+
// The browser may release capture while a host reset or unmount is running.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function cancelDrag(event?: AnnotationSurfaceInputEvent, release = true): void {
|
|
130
|
+
const drag = activeDrag
|
|
131
|
+
if (!drag) return
|
|
132
|
+
|
|
133
|
+
activeDrag = undefined
|
|
134
|
+
setDragging(false)
|
|
135
|
+
if (release) releaseCapture(drag)
|
|
136
|
+
local.onDragChange?.(undefined, 'cancel', event)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
createEffect(() => {
|
|
140
|
+
const resetKey = local.resetKey
|
|
141
|
+
const tool = local.tool
|
|
142
|
+
const interactive = isInteractive()
|
|
143
|
+
if (activeDrag && (resetKey !== previousResetKey || tool !== previousTool || !interactive)) {
|
|
144
|
+
cancelDrag()
|
|
145
|
+
}
|
|
146
|
+
previousResetKey = resetKey
|
|
147
|
+
previousTool = tool
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
onCleanup(() => cancelDrag())
|
|
151
|
+
|
|
152
|
+
function handlePointerDown(event: AnnotationSurfacePointerEvent): void {
|
|
153
|
+
if (!isInteractive() || !event.isPrimary || event.button !== 0) return
|
|
154
|
+
event.preventDefault()
|
|
155
|
+
event.currentTarget.focus({ preventScroll: true })
|
|
156
|
+
const point = pointFrom(event, event.currentTarget)
|
|
157
|
+
if (activeDrag) return
|
|
158
|
+
|
|
159
|
+
if (local.tool === 'point') {
|
|
160
|
+
local.onPoint?.(point, event)
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
event.currentTarget.setPointerCapture(event.pointerId)
|
|
166
|
+
} catch {
|
|
167
|
+
// A drag without real pointer capture can lose its final coordinates.
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const drag: ActiveDrag = {
|
|
172
|
+
pointerId: event.pointerId,
|
|
173
|
+
target: event.currentTarget,
|
|
174
|
+
start: point,
|
|
175
|
+
current: point,
|
|
176
|
+
}
|
|
177
|
+
activeDrag = drag
|
|
178
|
+
setDragging(true)
|
|
179
|
+
local.onDragChange?.({ start: point, current: point }, 'start', event)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function handlePointerMove(event: AnnotationSurfacePointerEvent): void {
|
|
183
|
+
const drag = activeDrag
|
|
184
|
+
if (!drag || drag.pointerId !== event.pointerId) return
|
|
185
|
+
|
|
186
|
+
const current = pointFrom(event, event.currentTarget)
|
|
187
|
+
activeDrag = { ...drag, current }
|
|
188
|
+
local.onDragChange?.({ start: drag.start, current }, 'move', event)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function handlePointerUp(event: AnnotationSurfacePointerEvent): void {
|
|
192
|
+
const drag = activeDrag
|
|
193
|
+
if (!drag || drag.pointerId !== event.pointerId) return
|
|
194
|
+
|
|
195
|
+
const current = pointFrom(event, event.currentTarget)
|
|
196
|
+
activeDrag = undefined
|
|
197
|
+
setDragging(false)
|
|
198
|
+
releaseCapture(drag)
|
|
199
|
+
local.onDragChange?.({ start: drag.start, current }, 'end', event)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function handlePointerCancel(event: AnnotationSurfacePointerEvent): void {
|
|
203
|
+
if (activeDrag?.pointerId === event.pointerId) cancelDrag(event)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function handleLostPointerCapture(event: AnnotationSurfacePointerEvent): void {
|
|
207
|
+
if (activeDrag?.pointerId === event.pointerId) cancelDrag(event, false)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function handleKeyDown(event: AnnotationSurfaceKeyboardEvent): void {
|
|
211
|
+
local.onKeyDown?.(event)
|
|
212
|
+
if (event.key === 'Escape') {
|
|
213
|
+
if (!event.defaultPrevented) cancelDrag()
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
if (
|
|
217
|
+
event.defaultPrevented ||
|
|
218
|
+
!isInteractive() ||
|
|
219
|
+
event.key !== ' ' ||
|
|
220
|
+
event.repeat ||
|
|
221
|
+
event.isComposing ||
|
|
222
|
+
event.altKey ||
|
|
223
|
+
event.ctrlKey ||
|
|
224
|
+
event.metaKey ||
|
|
225
|
+
activeDrag
|
|
226
|
+
)
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
event.preventDefault()
|
|
230
|
+
if (local.tool === 'point') {
|
|
231
|
+
local.onPoint?.({ x: 0.5, y: 0.5 }, event)
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
local.onDragChange?.(
|
|
236
|
+
{
|
|
237
|
+
start: { x: 0.25, y: 0.25 },
|
|
238
|
+
current: { x: 0.75, y: 0.75 },
|
|
239
|
+
},
|
|
240
|
+
'keyboard',
|
|
241
|
+
event
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const visibleRegion = (): AnnotationSurfaceRegion | undefined => {
|
|
246
|
+
const region = local.region
|
|
247
|
+
if (!region) return undefined
|
|
248
|
+
const x = clampSurfaceFraction(region.x)
|
|
249
|
+
const y = clampSurfaceFraction(region.y)
|
|
250
|
+
return {
|
|
251
|
+
x,
|
|
252
|
+
y,
|
|
253
|
+
width: Math.min(clampSurfaceFraction(region.width), 1 - x),
|
|
254
|
+
height: Math.min(clampSurfaceFraction(region.height), 1 - y),
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const describedBy = () =>
|
|
259
|
+
[local['aria-describedby'], local.hint && !visibleRegion() ? hintId : undefined]
|
|
260
|
+
.filter(Boolean)
|
|
261
|
+
.join(' ') || undefined
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<div
|
|
265
|
+
id={local.id}
|
|
266
|
+
aria-describedby={describedBy()}
|
|
267
|
+
role="application"
|
|
268
|
+
aria-roledescription="annotation surface"
|
|
269
|
+
aria-label={local.label}
|
|
270
|
+
aria-disabled={isInteractive() ? undefined : 'true'}
|
|
271
|
+
tabIndex={0}
|
|
272
|
+
data-tool={local.tool}
|
|
273
|
+
data-dragging={dragging() ? 'true' : 'false'}
|
|
274
|
+
class={cn(
|
|
275
|
+
'dev-annotation-surface relative isolate block w-full aspect-video select-none overflow-hidden rounded-md border border-border bg-muted/20 text-muted-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',
|
|
276
|
+
local.class
|
|
277
|
+
)}
|
|
278
|
+
ref={(element) => local.ref?.(element)}
|
|
279
|
+
onPointerDown={handlePointerDown}
|
|
280
|
+
onPointerMove={handlePointerMove}
|
|
281
|
+
onPointerUp={handlePointerUp}
|
|
282
|
+
onPointerCancel={handlePointerCancel}
|
|
283
|
+
onLostPointerCapture={handleLostPointerCapture}
|
|
284
|
+
onKeyDown={handleKeyDown}
|
|
285
|
+
>
|
|
286
|
+
<Show when={visibleRegion()}>
|
|
287
|
+
{(region) => (
|
|
288
|
+
<svg
|
|
289
|
+
class="dev-annotation-surface__region pointer-events-none absolute inset-0 size-full"
|
|
290
|
+
viewBox="0 0 10000 10000"
|
|
291
|
+
preserveAspectRatio="none"
|
|
292
|
+
aria-hidden="true"
|
|
293
|
+
>
|
|
294
|
+
<rect
|
|
295
|
+
class="fill-primary-subtle stroke-primary"
|
|
296
|
+
x={region().x * 10_000}
|
|
297
|
+
y={region().y * 10_000}
|
|
298
|
+
width={region().width * 10_000}
|
|
299
|
+
height={region().height * 10_000}
|
|
300
|
+
stroke-width="1.5"
|
|
301
|
+
vector-effect="non-scaling-stroke"
|
|
302
|
+
/>
|
|
303
|
+
</svg>
|
|
304
|
+
)}
|
|
305
|
+
</Show>
|
|
306
|
+
<Show when={local.hint && !visibleRegion()}>
|
|
307
|
+
<span
|
|
308
|
+
id={hintId}
|
|
309
|
+
class="dev-annotation-surface__hint pointer-events-none absolute inset-0 grid place-items-center p-4 text-center text-xs text-muted-foreground"
|
|
310
|
+
>
|
|
311
|
+
{local.hint}
|
|
312
|
+
</span>
|
|
313
|
+
</Show>
|
|
314
|
+
</div>
|
|
315
|
+
)
|
|
316
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type AnnotationSurfacePoint = Readonly<{ x: number; y: number }>
|
|
2
|
+
|
|
3
|
+
type SurfaceBounds = Readonly<Pick<DOMRectReadOnly, 'left' | 'top' | 'width' | 'height'>>
|
|
4
|
+
|
|
5
|
+
function fraction(client: number, start: number, length: number): number {
|
|
6
|
+
if (
|
|
7
|
+
!Number.isFinite(client) ||
|
|
8
|
+
!Number.isFinite(start) ||
|
|
9
|
+
!Number.isFinite(length) ||
|
|
10
|
+
length <= 0
|
|
11
|
+
)
|
|
12
|
+
return 0
|
|
13
|
+
|
|
14
|
+
return Math.min(1, Math.max(0, (client - start) / length))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Pointer coordinates normalized to the surface; degenerate bounds resolve to the origin. */
|
|
18
|
+
export function normalizedSurfacePoint(
|
|
19
|
+
clientX: number,
|
|
20
|
+
clientY: number,
|
|
21
|
+
bounds: SurfaceBounds
|
|
22
|
+
): AnnotationSurfacePoint {
|
|
23
|
+
return {
|
|
24
|
+
x: fraction(clientX, bounds.left, bounds.width),
|
|
25
|
+
y: fraction(clientY, bounds.top, bounds.height),
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function clampSurfaceFraction(value: number): number {
|
|
30
|
+
return Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Keyboard-operable normalized drawing surface; hosts retain domain geometry and actions. */
|
|
2
|
+
export {
|
|
3
|
+
AnnotationSurface,
|
|
4
|
+
type AnnotationSurfaceDrag,
|
|
5
|
+
type AnnotationSurfaceDragPhase,
|
|
6
|
+
type AnnotationSurfaceInputEvent,
|
|
7
|
+
type AnnotationSurfaceKeyboardEvent,
|
|
8
|
+
type AnnotationSurfacePointerEvent,
|
|
9
|
+
type AnnotationSurfaceProps,
|
|
10
|
+
type AnnotationSurfaceRegion,
|
|
11
|
+
type AnnotationSurfaceTool,
|
|
12
|
+
} from './annotation-surface'
|
|
13
|
+
export type { AnnotationSurfacePoint } from './geometry'
|
|
@@ -7,9 +7,14 @@ import {
|
|
|
7
7
|
import { X } from 'lucide-solid'
|
|
8
8
|
import type { Accessor, ComponentProps, JSX } from 'solid-js'
|
|
9
9
|
import { createRenderEffect, onCleanup, Show, splitProps } from 'solid-js'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
overlayMotion,
|
|
12
|
+
overlayPositioner,
|
|
13
|
+
overlayScrim,
|
|
14
|
+
overlaySurface,
|
|
15
|
+
overlayTitle,
|
|
16
|
+
} from '../../../lib/overlay'
|
|
11
17
|
import { cn } from '../../../lib/utils'
|
|
12
|
-
import { headingVariants } from '../typography'
|
|
13
18
|
|
|
14
19
|
type FocusCycle = {
|
|
15
20
|
content: HTMLElement
|
|
@@ -326,12 +331,7 @@ export function DialogFooter(props: ComponentProps<'div'>) {
|
|
|
326
331
|
|
|
327
332
|
export function DialogTitle(props: ComponentProps<typeof KobalteDialog.Title>) {
|
|
328
333
|
const [local, rest] = splitProps(props, ['class'])
|
|
329
|
-
return (
|
|
330
|
-
<KobalteDialog.Title
|
|
331
|
-
class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
|
|
332
|
-
{...rest}
|
|
333
|
-
/>
|
|
334
|
-
)
|
|
334
|
+
return <KobalteDialog.Title class={cn(overlayTitle, local.class)} {...rest} />
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
export function DialogDescription(props: ComponentProps<typeof KobalteDialog.Description>) {
|
|
@@ -8,9 +8,9 @@ import { X } from 'lucide-solid'
|
|
|
8
8
|
import type { Accessor, ComponentProps, JSX } from 'solid-js'
|
|
9
9
|
import { Show, splitProps } from 'solid-js'
|
|
10
10
|
import { cva, type VariantProps } from '../../../lib/variants'
|
|
11
|
+
import { overlayTitle } from '../../../lib/overlay'
|
|
11
12
|
import { cn } from '../../../lib/utils'
|
|
12
13
|
import { createDialogFocusRestoration, DialogOverlay } from '../dialog/dialog'
|
|
13
|
-
import { headingVariants } from '../typography'
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Sheet.
|
|
@@ -215,12 +215,7 @@ export function SheetFooter(props: ComponentProps<'div'>) {
|
|
|
215
215
|
|
|
216
216
|
export function SheetTitle(props: ComponentProps<typeof KobalteDialog.Title>) {
|
|
217
217
|
const [local, rest] = splitProps(props, ['class'])
|
|
218
|
-
return (
|
|
219
|
-
<KobalteDialog.Title
|
|
220
|
-
class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
|
|
221
|
-
{...rest}
|
|
222
|
-
/>
|
|
223
|
-
)
|
|
218
|
+
return <KobalteDialog.Title class={cn(overlayTitle, local.class)} {...rest} />
|
|
224
219
|
}
|
|
225
220
|
|
|
226
221
|
export function SheetDescription(props: ComponentProps<typeof KobalteDialog.Description>) {
|
|
@@ -25,7 +25,8 @@ import { cn } from '../../../lib/utils'
|
|
|
25
25
|
* stack already sets the rhythm (a card or dialog header), and is the only other
|
|
26
26
|
* option.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
// cva only returns a closure. A heading-only consumer must discard Text's recipe.
|
|
29
|
+
export const headingVariants = /* @__PURE__ */ cva('font-semibold', {
|
|
29
30
|
variants: {
|
|
30
31
|
size: {
|
|
31
32
|
/** 30px. An app-level headline — a first-run or empty-app screen. Once per app. */
|
|
@@ -130,7 +131,7 @@ export function Heading<T extends ValidComponent = 'h2'>(props: HeadingProps<T>)
|
|
|
130
131
|
*
|
|
131
132
|
* Defaults: `body` renders a `p`, `code` a `code`, every other variant a `span`.
|
|
132
133
|
*/
|
|
133
|
-
export const textVariants = cva('', {
|
|
134
|
+
export const textVariants = /* @__PURE__ */ cva('', {
|
|
134
135
|
variants: {
|
|
135
136
|
variant: {
|
|
136
137
|
/** 14px / 400. Body copy and descriptions. */
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
export const overlaySurface =
|
|
15
15
|
'bg-popover text-popover-foreground rounded-xl border border-border shadow-lg'
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Overlay titles have one fixed typography role. Keep this small recipe shared
|
|
19
|
+
* without loading the configurable Heading component into every dialog.
|
|
20
|
+
*/
|
|
21
|
+
export const overlayTitle = 'font-semibold text-base tracking-tight leading-none'
|
|
22
|
+
|
|
17
23
|
/** The span of the portal wrapper that centres a dialog-like overlay. */
|
|
18
24
|
export const overlayPositioner = 'fixed inset-0 z-(--z-dialog) grid place-items-center p-4'
|
|
19
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adea-ai/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.109.1",
|
|
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",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
],
|
|
35
35
|
"type": "module",
|
|
36
36
|
"sideEffects": [
|
|
37
|
-
"*.css"
|
|
37
|
+
"*.css",
|
|
38
|
+
"**/*.css"
|
|
38
39
|
],
|
|
39
40
|
"main": "dist/index.js",
|
|
40
41
|
"types": "dist/index.d.ts",
|
package/registry.json
CHANGED
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
"@kobalte/core@^0.13.14"
|
|
62
62
|
],
|
|
63
63
|
"registryDependencies": [
|
|
64
|
-
"@adea-ai/ui/typography",
|
|
65
64
|
"lib"
|
|
66
65
|
],
|
|
67
66
|
"files": [
|
|
@@ -77,6 +76,38 @@
|
|
|
77
76
|
}
|
|
78
77
|
]
|
|
79
78
|
},
|
|
79
|
+
{
|
|
80
|
+
"name": "annotation-surface",
|
|
81
|
+
"type": "registry:ui",
|
|
82
|
+
"title": "Annotation Surface",
|
|
83
|
+
"description": "Keyboard-operable normalized drawing surface; hosts retain domain geometry and actions.",
|
|
84
|
+
"dependencies": [],
|
|
85
|
+
"registryDependencies": [
|
|
86
|
+
"lib"
|
|
87
|
+
],
|
|
88
|
+
"files": [
|
|
89
|
+
{
|
|
90
|
+
"path": "src/components/ui/annotation-surface/annotation-surface.css",
|
|
91
|
+
"type": "registry:ui",
|
|
92
|
+
"target": "components/ui/annotation-surface/annotation-surface.css"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"path": "src/components/ui/annotation-surface/annotation-surface.tsx",
|
|
96
|
+
"type": "registry:ui",
|
|
97
|
+
"target": "components/ui/annotation-surface/annotation-surface.tsx"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"path": "src/components/ui/annotation-surface/geometry.ts",
|
|
101
|
+
"type": "registry:ui",
|
|
102
|
+
"target": "components/ui/annotation-surface/geometry.ts"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"path": "src/components/ui/annotation-surface/index.ts",
|
|
106
|
+
"type": "registry:ui",
|
|
107
|
+
"target": "components/ui/annotation-surface/index.ts"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
80
111
|
{
|
|
81
112
|
"name": "aspect-ratio",
|
|
82
113
|
"type": "registry:ui",
|
|
@@ -537,7 +568,6 @@
|
|
|
537
568
|
"lucide-solid@^1.51.0"
|
|
538
569
|
],
|
|
539
570
|
"registryDependencies": [
|
|
540
|
-
"@adea-ai/ui/typography",
|
|
541
571
|
"lib"
|
|
542
572
|
],
|
|
543
573
|
"files": [
|
|
@@ -1181,7 +1211,6 @@
|
|
|
1181
1211
|
],
|
|
1182
1212
|
"registryDependencies": [
|
|
1183
1213
|
"@adea-ai/ui/dialog",
|
|
1184
|
-
"@adea-ai/ui/typography",
|
|
1185
1214
|
"lib"
|
|
1186
1215
|
],
|
|
1187
1216
|
"files": [
|
|
@@ -1577,6 +1606,11 @@
|
|
|
1577
1606
|
"path": "src/components/layout/contextual-sidebar/index.ts",
|
|
1578
1607
|
"type": "registry:ui",
|
|
1579
1608
|
"target": "components/layout/contextual-sidebar/index.ts"
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
"path": "src/components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx",
|
|
1612
|
+
"type": "registry:ui",
|
|
1613
|
+
"target": "components/layout/contextual-sidebar/mobile-contextual-sidebar.tsx"
|
|
1580
1614
|
}
|
|
1581
1615
|
]
|
|
1582
1616
|
},
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Accessor, JSX } from 'solid-js'
|
|
2
2
|
import { Show, createSignal, createUniqueId, onCleanup, onMount } from 'solid-js'
|
|
3
3
|
import { cn } from '#lib/utils'
|
|
4
|
-
import { Sheet, SheetContent, SheetTitle } from '../../ui/sheet'
|
|
5
4
|
import {
|
|
6
5
|
SidebarNav,
|
|
7
6
|
SidebarNavContent,
|
|
@@ -11,6 +10,9 @@ import {
|
|
|
11
10
|
} from '../sidebar-nav/sidebar-nav'
|
|
12
11
|
import { PixelResizeHandle } from '../pixel-resize-handle/pixel-resize-handle'
|
|
13
12
|
|
|
13
|
+
type MobileModule = typeof import('./mobile-contextual-sidebar')
|
|
14
|
+
type MobileLoad = MobileModule | { error: unknown }
|
|
15
|
+
|
|
14
16
|
export type ContextualSidebarRenderContext = {
|
|
15
17
|
/** Whether the slot is being rendered inside the modal mobile sheet. */
|
|
16
18
|
mobile: boolean
|
|
@@ -67,6 +69,22 @@ export type ContextualSidebarProps = {
|
|
|
67
69
|
*/
|
|
68
70
|
export function ContextualSidebar(props: ContextualSidebarProps) {
|
|
69
71
|
const [isNarrowViewport, setIsNarrowViewport] = createSignal(false)
|
|
72
|
+
const [mobileLoad, setMobileLoad] = createSignal<MobileLoad>()
|
|
73
|
+
let mobileRequested = false
|
|
74
|
+
// Keep the resolved component mounted across viewport changes for Sheet cleanup.
|
|
75
|
+
const loadMobile = () => {
|
|
76
|
+
if (mobileRequested) return
|
|
77
|
+
mobileRequested = true
|
|
78
|
+
// Native module caching shares successful loads without keeping a rejected promise.
|
|
79
|
+
void import('./mobile-contextual-sidebar').then(
|
|
80
|
+
(module) => {
|
|
81
|
+
if (mobileRequested) setMobileLoad(module)
|
|
82
|
+
},
|
|
83
|
+
(error: unknown) => {
|
|
84
|
+
if (mobileRequested) setMobileLoad({ error })
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
}
|
|
70
88
|
const [mobileMount, setMobileMount] = createSignal<
|
|
71
89
|
{ element: HTMLElement; connected: boolean } | undefined
|
|
72
90
|
>()
|
|
@@ -81,6 +99,7 @@ export function ContextualSidebar(props: ContextualSidebarProps) {
|
|
|
81
99
|
const media = window.matchMedia('(max-width: 48rem)')
|
|
82
100
|
const updateViewport = () => {
|
|
83
101
|
const wasNarrow = isNarrowViewport()
|
|
102
|
+
if (media.matches) loadMobile()
|
|
84
103
|
// Close before mounting a modal. A desktop-expanded host state is not
|
|
85
104
|
// mobile intent, and opening the Sheet for one frame can steal focus.
|
|
86
105
|
if (media.matches && !wasNarrow) props.onOpenChange(false)
|
|
@@ -90,10 +109,14 @@ export function ContextualSidebar(props: ContextualSidebarProps) {
|
|
|
90
109
|
// A route can mount after a wide-loaded host crosses into mobile. Clear
|
|
91
110
|
// that stale seed, but preserve a sidebar intentionally opened at a
|
|
92
111
|
// narrow boot viewport.
|
|
112
|
+
if (media.matches) loadMobile()
|
|
93
113
|
if (media.matches && props.wideViewportAtLoad && props.open) props.onOpenChange(false)
|
|
94
114
|
setIsNarrowViewport(media.matches)
|
|
95
115
|
media.addEventListener('change', updateViewport)
|
|
96
|
-
onCleanup(() =>
|
|
116
|
+
onCleanup(() => {
|
|
117
|
+
mobileRequested = false
|
|
118
|
+
media.removeEventListener('change', updateViewport)
|
|
119
|
+
})
|
|
97
120
|
})
|
|
98
121
|
|
|
99
122
|
const renderSidebar = (mobile: boolean) => {
|
|
@@ -160,21 +183,23 @@ export function ContextualSidebar(props: ContextualSidebarProps) {
|
|
|
160
183
|
}
|
|
161
184
|
|
|
162
185
|
return (
|
|
163
|
-
|
|
164
|
-
open={isNarrowViewport() && props.open}
|
|
165
|
-
onOpenChange={(open) => props.onOpenChange(open)}
|
|
166
|
-
>
|
|
186
|
+
<>
|
|
167
187
|
<Show when={!isNarrowViewport()}>{(_wide) => renderSidebar(false)}</Show>
|
|
168
|
-
<Show when={
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
<Show when={mobileLoad()} keyed>
|
|
189
|
+
{(loaded) => {
|
|
190
|
+
if ('error' in loaded) throw loaded.error
|
|
191
|
+
return (
|
|
192
|
+
<loaded.MobileSidebar
|
|
193
|
+
open={isNarrowViewport() && props.open}
|
|
194
|
+
onOpenChange={props.onOpenChange}
|
|
195
|
+
label={props.label}
|
|
196
|
+
sheetClass={props.sheetClass}
|
|
197
|
+
restoreFocusRef={props.restoreFocusRef}
|
|
198
|
+
renderSidebar={() => renderSidebar(true)}
|
|
199
|
+
/>
|
|
200
|
+
)
|
|
201
|
+
}}
|
|
177
202
|
</Show>
|
|
178
|
-
|
|
203
|
+
</>
|
|
179
204
|
)
|
|
180
205
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Accessor, JSX } from 'solid-js'
|
|
2
|
+
import { cn } from '#lib/utils'
|
|
3
|
+
import { Sheet, SheetContent } from '../../ui/sheet'
|
|
4
|
+
|
|
5
|
+
export type MobileContextualSidebarProps = {
|
|
6
|
+
open: boolean
|
|
7
|
+
onOpenChange(open: boolean): void
|
|
8
|
+
label: string
|
|
9
|
+
sheetClass?: string
|
|
10
|
+
restoreFocusRef?: Accessor<HTMLElement | undefined>
|
|
11
|
+
renderSidebar(): JSX.Element
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Sheet wrappers are loaded only when a contextual sidebar enters mobile mode. */
|
|
15
|
+
export function MobileSidebar(props: MobileContextualSidebarProps) {
|
|
16
|
+
return (
|
|
17
|
+
<Sheet open={props.open} onOpenChange={props.onOpenChange}>
|
|
18
|
+
<SheetContent
|
|
19
|
+
side="start"
|
|
20
|
+
aria-label={props.label}
|
|
21
|
+
class={cn('gap-0 p-0', props.sheetClass)}
|
|
22
|
+
restoreFocusRef={props.restoreFocusRef}
|
|
23
|
+
>
|
|
24
|
+
{props.renderSidebar()}
|
|
25
|
+
</SheetContent>
|
|
26
|
+
</Sheet>
|
|
27
|
+
)
|
|
28
|
+
}
|