@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
|
@@ -5,9 +5,14 @@ import {
|
|
|
5
5
|
// Use the alert-specific export rather than DialogRoot.Content, which other imports can replace.
|
|
6
6
|
import type { ComponentProps } from 'solid-js'
|
|
7
7
|
import { createEffect, createSignal, splitProps } from 'solid-js'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
overlayMotion,
|
|
10
|
+
overlayPositioner,
|
|
11
|
+
overlayScrim,
|
|
12
|
+
overlaySurface,
|
|
13
|
+
overlayTitle,
|
|
14
|
+
} from '#lib/overlay'
|
|
9
15
|
import { cn } from '#lib/utils'
|
|
10
|
-
import { headingVariants } from '../typography'
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* AlertDialog.
|
|
@@ -116,12 +121,7 @@ export function AlertDialogFooter(props: ComponentProps<'div'>) {
|
|
|
116
121
|
|
|
117
122
|
export function AlertDialogTitle(props: ComponentProps<typeof KobalteAlertDialog.Title>) {
|
|
118
123
|
const [local, rest] = splitProps(props, ['class'])
|
|
119
|
-
return (
|
|
120
|
-
<KobalteAlertDialog.Title
|
|
121
|
-
class={cn(headingVariants({ size: 'card', leading: 'none' }), local.class)}
|
|
122
|
-
{...rest}
|
|
123
|
-
/>
|
|
124
|
-
)
|
|
124
|
+
return <KobalteAlertDialog.Title class={cn(overlayTitle, local.class)} {...rest} />
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export function AlertDialogDescription(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.dev-annotation-surface {
|
|
2
|
+
touch-action: none;
|
|
3
|
+
cursor: crosshair;
|
|
4
|
+
background-image: repeating-linear-gradient(
|
|
5
|
+
45deg,
|
|
6
|
+
color-mix(in oklch, var(--muted) 55%, transparent) 0 calc(var(--spacing) * 2),
|
|
7
|
+
transparent calc(var(--spacing) * 2) calc(var(--spacing) * 4)
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.dev-annotation-surface[data-tool='point'] {
|
|
12
|
+
cursor: cell;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.dev-annotation-surface[data-dragging='true'] {
|
|
16
|
+
cursor: grabbing;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.dev-annotation-surface[aria-disabled='true'] {
|
|
20
|
+
touch-action: auto;
|
|
21
|
+
cursor: default;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dev-annotation-surface__region,
|
|
25
|
+
.dev-annotation-surface__hint {
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
}
|
|
@@ -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. */
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
export * from './components/ui/accordion'
|
|
19
19
|
export * from './components/ui/alert'
|
|
20
20
|
export * from './components/ui/alert-dialog'
|
|
21
|
+
export * from './components/ui/annotation-surface'
|
|
21
22
|
export * from './components/ui/aspect-ratio'
|
|
22
23
|
export * from './components/ui/avatar'
|
|
23
24
|
export * from './components/ui/board'
|
package/src/lib/overlay.ts
CHANGED
|
@@ -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
|
|