panelui-web 0.9.1 → 0.10.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/components/choice-cards/index.d.ts +167 -0
- package/dist/components/choice-cards/index.js +132 -0
- package/dist/components/dialog/index.js +8 -1
- package/dist/components/inspector/index.d.ts +242 -0
- package/dist/components/inspector/index.js +230 -0
- package/dist/components/sheet/index.d.ts +16 -5
- package/dist/components/sheet/index.js +72 -15
- package/dist/components/slider/index.d.ts +293 -0
- package/dist/components/slider/index.js +342 -0
- package/dist/components/timeline/index.d.ts +328 -0
- package/dist/components/timeline/index.js +613 -0
- package/dist/components/toolbar/index.d.ts +185 -0
- package/dist/components/toolbar/index.js +270 -0
- package/dist/components/workbench/index.d.ts +224 -0
- package/dist/components/workbench/index.js +306 -0
- package/dist/components/xy-pad/index.d.ts +180 -0
- package/dist/components/xy-pad/index.js +278 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Workbench — the frame of an editor: a canvas with the work on it, a
|
|
5
|
+
* toolbar floating over it, an inspector beside it and a timeline under it.
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* <Workbench className="h-[640px]">
|
|
9
|
+
* <Workbench.Bar><Toolbar>…</Toolbar></Workbench.Bar>
|
|
10
|
+
* <Workbench.Canvas>
|
|
11
|
+
* <Workbench.Artboard aspect="16 / 9">…</Workbench.Artboard>
|
|
12
|
+
* </Workbench.Canvas>
|
|
13
|
+
* <Workbench.Inspector><Inspector>…</Inspector></Workbench.Inspector>
|
|
14
|
+
* <Workbench.Timeline><Timeline>…</Timeline></Workbench.Timeline>
|
|
15
|
+
* </Workbench>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ## The work is in the middle
|
|
19
|
+
*
|
|
20
|
+
* Everything else stands round the canvas as a panel of its own, floating
|
|
21
|
+
* a few pixels clear of its neighbours, so the work reads as the thing on
|
|
22
|
+
* the desk and the tools as things beside it. The artboard sits centred on
|
|
23
|
+
* a field of dots at the largest size its shape fits.
|
|
24
|
+
*
|
|
25
|
+
* ## Focus clears the desk
|
|
26
|
+
*
|
|
27
|
+
* In focus the sidebar, the inspector and the timeline slide away and the
|
|
28
|
+
* canvas takes the room they leave. `\` toggles it from the keyboard, and
|
|
29
|
+
* `useWorkbench()` gives a button of your own the same switch.
|
|
30
|
+
*
|
|
31
|
+
* ## On a narrow screen the inspector becomes a sheet
|
|
32
|
+
*
|
|
33
|
+
* Below `compactBelow` pixels wide there is no room for a column beside the
|
|
34
|
+
* canvas. The inspector then opens as a sheet from the side, and
|
|
35
|
+
* `Workbench.InspectorTrigger` is the button that opens it. The width that
|
|
36
|
+
* counts is the workbench's own, not the window's, so it works in a pane.
|
|
37
|
+
*/
|
|
38
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
39
|
+
import { tv } from 'tailwind-variants';
|
|
40
|
+
import { Sheet } from '../sheet/index.js';
|
|
41
|
+
const EASE = 'ease-[cubic-bezier(0.32,0.72,0,1)]';
|
|
42
|
+
const workbenchVariants = tv({
|
|
43
|
+
slots: {
|
|
44
|
+
root: [
|
|
45
|
+
'group/workbench relative grid w-full min-w-0 overflow-hidden rounded-[26px] bg-muted/60 p-2 text-foreground',
|
|
46
|
+
'ring-1 ring-foreground/[0.06] dark:bg-muted/40 dark:ring-foreground/10',
|
|
47
|
+
'[grid-template-areas:"sidebar_canvas_inspector""timeline_timeline_timeline"]',
|
|
48
|
+
'grid-cols-[var(--wb-sidebar)_minmax(0,1fr)_var(--wb-inspector)] grid-rows-[minmax(0,1fr)_var(--wb-timeline)]',
|
|
49
|
+
// No grid gap: each panel carries its own 8px on the canvas side, so a
|
|
50
|
+
// column that closes takes its gap with it.
|
|
51
|
+
`transition-[grid-template-columns,grid-template-rows] duration-500 ${EASE} motion-reduce:transition-none`,
|
|
52
|
+
'data-resizing:transition-none',
|
|
53
|
+
],
|
|
54
|
+
sidebar: [
|
|
55
|
+
'flex min-h-0 min-w-0 flex-col overflow-hidden pe-2 [grid-area:sidebar]',
|
|
56
|
+
`transition-[opacity,translate] duration-500 ${EASE} motion-reduce:transition-none`,
|
|
57
|
+
'group-data-focus/workbench:-translate-x-4 group-data-focus/workbench:opacity-0',
|
|
58
|
+
],
|
|
59
|
+
canvas: [
|
|
60
|
+
'relative isolate flex min-h-0 min-w-0 items-center justify-center overflow-hidden rounded-[20px] px-6 pt-[4.5rem] pb-6 [grid-area:canvas] [container-type:size]',
|
|
61
|
+
// The top padding is the floating bar's room, so the work is centred in what is left.
|
|
62
|
+
'bg-background ring-1 ring-foreground/[0.05] dark:bg-background/70',
|
|
63
|
+
'bg-[radial-gradient(color-mix(in_oklab,var(--foreground)_13%,transparent)_1px,transparent_1.5px)] bg-size-[20px_20px] bg-center',
|
|
64
|
+
],
|
|
65
|
+
artboard: [
|
|
66
|
+
'relative overflow-hidden rounded-xl bg-background shadow-[0_24px_60px_-24px_rgb(0_0_0/0.45)] ring-1 ring-foreground/10',
|
|
67
|
+
'aspect-(--wb-aspect) w-[min(100cqw,100cqh*var(--wb-ratio))]',
|
|
68
|
+
`transition-[width] duration-500 ${EASE} motion-reduce:transition-none`,
|
|
69
|
+
],
|
|
70
|
+
bar: 'pointer-events-none z-10 mt-3 flex justify-center self-start justify-self-center [grid-area:canvas] *:pointer-events-auto',
|
|
71
|
+
inspector: [
|
|
72
|
+
'flex min-h-0 min-w-0 justify-end overflow-hidden ps-2 [grid-area:inspector]',
|
|
73
|
+
`transition-[opacity,translate] duration-500 ${EASE} motion-reduce:transition-none`,
|
|
74
|
+
'group-data-focus/workbench:translate-x-4 group-data-focus/workbench:opacity-0',
|
|
75
|
+
// The panel inside fills the column's height.
|
|
76
|
+
'[&_[data-slot=inspector]]:h-full [&_[data-slot=inspector-panel]]:h-full',
|
|
77
|
+
],
|
|
78
|
+
timeline: [
|
|
79
|
+
'relative flex min-h-0 min-w-0 flex-col overflow-hidden pt-2 [grid-area:timeline]',
|
|
80
|
+
`transition-[opacity,translate] duration-500 ${EASE} motion-reduce:transition-none`,
|
|
81
|
+
'group-data-focus/workbench:translate-y-4 group-data-focus/workbench:opacity-0',
|
|
82
|
+
'[&>[data-slot=timeline]]:min-h-0 [&>[data-slot=timeline]]:flex-1',
|
|
83
|
+
],
|
|
84
|
+
resize: [
|
|
85
|
+
'absolute inset-x-0 top-0 z-20 flex h-2 cursor-row-resize items-center justify-center outline-none',
|
|
86
|
+
"after:h-1 after:w-10 after:rounded-full after:bg-foreground/20 after:transition-[background-color,width] after:duration-150 after:content-['']",
|
|
87
|
+
'hover:after:w-16 hover:after:bg-foreground/40 focus-visible:after:w-16 focus-visible:after:bg-ring data-active:after:w-16 data-active:after:bg-foreground/50',
|
|
88
|
+
],
|
|
89
|
+
trigger: [
|
|
90
|
+
'inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-xl text-muted-foreground outline-none',
|
|
91
|
+
'transition-[color,background-color,scale] duration-150 hover:bg-foreground/[0.06] hover:text-foreground active:scale-[0.94]',
|
|
92
|
+
'focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-[1.125rem] [&_svg]:shrink-0',
|
|
93
|
+
],
|
|
94
|
+
// In the sheet, the sheet is the surface: the inspector's own card gives
|
|
95
|
+
// up its fill, ring and shadow rather than stand as a box in a box.
|
|
96
|
+
sheetBody: [
|
|
97
|
+
'flex min-h-0 flex-1 px-2 pb-2 [&_[data-slot=inspector]]:h-full [&_[data-slot=inspector]]:w-full',
|
|
98
|
+
'[&_[data-slot=inspector-panel]]:h-full [&_[data-slot=inspector-panel]]:w-full [&_[data-slot=inspector-panel]]:bg-transparent',
|
|
99
|
+
'[&_[data-slot=inspector-panel]]:shadow-none [&_[data-slot=inspector-panel]]:ring-0',
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const WorkbenchContext = createContext(null);
|
|
104
|
+
function useWorkbenchContext(part) {
|
|
105
|
+
const context = useContext(WorkbenchContext);
|
|
106
|
+
if (!context)
|
|
107
|
+
throw new Error(`${part} must be used inside a <Workbench>.`);
|
|
108
|
+
return context;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The workbench's state, for controls of your own: a focus button in the
|
|
112
|
+
* toolbar, a button that opens the inspector on a narrow screen.
|
|
113
|
+
*/
|
|
114
|
+
export function useWorkbench() {
|
|
115
|
+
const { focus, setFocus, compact, inspectorOpen, setInspectorOpen } = useWorkbenchContext('useWorkbench');
|
|
116
|
+
return { focus, setFocus, compact, inspectorOpen, setInspectorOpen };
|
|
117
|
+
}
|
|
118
|
+
/** The editor's frame. Give it a height, such as `h-[640px]` or `h-dvh`. */
|
|
119
|
+
function WorkbenchRoot({ focus: focusProp, defaultFocus = false, onFocusChange, focusShortcut = true, compactBelow = 760, inspectorWidth = 348, sidebarWidth = 240, defaultTimelineHeight = 232, className, style, children, ...props }) {
|
|
120
|
+
const slots = workbenchVariants();
|
|
121
|
+
const [innerFocus, setInnerFocus] = useState(defaultFocus);
|
|
122
|
+
const focus = focusProp ?? innerFocus;
|
|
123
|
+
const [compact, setCompact] = useState(false);
|
|
124
|
+
const [inspectorOpen, setInspectorOpen] = useState(false);
|
|
125
|
+
const [parts, setParts] = useState({ sidebar: false, inspector: false, timeline: false });
|
|
126
|
+
const [timelineHeight, setTimelineHeight] = useState(defaultTimelineHeight);
|
|
127
|
+
const [resizing, setResizing] = useState(false);
|
|
128
|
+
const root = useRef(null);
|
|
129
|
+
const live = useRef({ focus, onFocusChange, controlled: focusProp !== undefined });
|
|
130
|
+
live.current = { focus, onFocusChange, controlled: focusProp !== undefined };
|
|
131
|
+
const setFocus = useCallback((next) => {
|
|
132
|
+
if (!live.current.controlled)
|
|
133
|
+
setInnerFocus(next);
|
|
134
|
+
live.current.onFocusChange?.(next);
|
|
135
|
+
}, []);
|
|
136
|
+
// The workbench's own width decides, so it works in a pane as on a page.
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
const box = root.current;
|
|
139
|
+
if (!box)
|
|
140
|
+
return;
|
|
141
|
+
const observer = new ResizeObserver(() => setCompact(box.clientWidth < compactBelow));
|
|
142
|
+
observer.observe(box);
|
|
143
|
+
return () => observer.disconnect();
|
|
144
|
+
}, [compactBelow]);
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (!compact)
|
|
147
|
+
setInspectorOpen(false);
|
|
148
|
+
}, [compact]);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (!focusShortcut)
|
|
151
|
+
return;
|
|
152
|
+
const onKey = (event) => {
|
|
153
|
+
if (event.key !== '\\' || event.metaKey || event.ctrlKey || event.altKey || event.defaultPrevented)
|
|
154
|
+
return;
|
|
155
|
+
const target = event.target;
|
|
156
|
+
if (target instanceof Element && target.closest('input, textarea, select, [contenteditable=""], [contenteditable="true"]')) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
setFocus(!live.current.focus);
|
|
161
|
+
};
|
|
162
|
+
document.addEventListener('keydown', onKey);
|
|
163
|
+
return () => document.removeEventListener('keydown', onKey);
|
|
164
|
+
}, [focusShortcut, setFocus]);
|
|
165
|
+
const mark = (part) => (present) => setParts((all) => (all[part] === present ? all : { ...all, [part]: present }));
|
|
166
|
+
const context = useMemo(() => ({
|
|
167
|
+
focus,
|
|
168
|
+
setFocus,
|
|
169
|
+
compact,
|
|
170
|
+
inspectorOpen,
|
|
171
|
+
setInspectorOpen,
|
|
172
|
+
hasSidebar: mark('sidebar'),
|
|
173
|
+
hasInspector: mark('inspector'),
|
|
174
|
+
hasTimeline: mark('timeline'),
|
|
175
|
+
timelineHeight,
|
|
176
|
+
setTimelineHeight,
|
|
177
|
+
setResizing,
|
|
178
|
+
rootHeight: () => root.current?.clientHeight ?? 0,
|
|
179
|
+
}), [focus, setFocus, compact, inspectorOpen, timelineHeight]);
|
|
180
|
+
const showSidebar = parts.sidebar && !focus && !compact;
|
|
181
|
+
const showInspector = parts.inspector && !focus && !compact;
|
|
182
|
+
const showTimeline = parts.timeline && !focus;
|
|
183
|
+
return (_jsx(WorkbenchContext.Provider, { value: context, children: _jsx("div", { ref: root, "data-slot": "workbench", "data-focus": focus ? '' : undefined, "data-compact": compact ? '' : undefined, "data-resizing": resizing ? '' : undefined, className: slots.root({ className }), style: {
|
|
184
|
+
...style,
|
|
185
|
+
'--wb-sidebar': showSidebar ? `${sidebarWidth}px` : '0px',
|
|
186
|
+
'--wb-inspector': showInspector ? `${inspectorWidth}px` : '0px',
|
|
187
|
+
'--wb-timeline': showTimeline ? `${timelineHeight}px` : '0px',
|
|
188
|
+
}, ...props, children: children }) }));
|
|
189
|
+
}
|
|
190
|
+
WorkbenchRoot.displayName = 'Workbench';
|
|
191
|
+
/** Tells the workbench a part is in it, so its column or row gets room. */
|
|
192
|
+
function usePresence(register) {
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
register(true);
|
|
195
|
+
return () => register(false);
|
|
196
|
+
}, []);
|
|
197
|
+
}
|
|
198
|
+
/** The strip floating at the top of the canvas, for a toolbar. */
|
|
199
|
+
function WorkbenchBar({ className, ...props }) {
|
|
200
|
+
useWorkbenchContext('Workbench.Bar');
|
|
201
|
+
const slots = workbenchVariants();
|
|
202
|
+
return _jsx("div", { "data-slot": "workbench-bar", className: slots.bar({ className }), ...props });
|
|
203
|
+
}
|
|
204
|
+
WorkbenchBar.displayName = 'Workbench.Bar';
|
|
205
|
+
/** A column before the canvas, for layers, pages or assets. It is hidden on a narrow screen and in focus. */
|
|
206
|
+
function WorkbenchSidebar({ className, ...props }) {
|
|
207
|
+
const workbench = useWorkbenchContext('Workbench.Sidebar');
|
|
208
|
+
usePresence(workbench.hasSidebar);
|
|
209
|
+
const slots = workbenchVariants();
|
|
210
|
+
return (_jsx("aside", { "data-slot": "workbench-sidebar", inert: workbench.focus || workbench.compact || undefined, className: slots.sidebar({ className }), ...props }));
|
|
211
|
+
}
|
|
212
|
+
WorkbenchSidebar.displayName = 'Workbench.Sidebar';
|
|
213
|
+
/** The stage in the middle: a field of dots with the work centred on it. */
|
|
214
|
+
function WorkbenchCanvas({ className, ...props }) {
|
|
215
|
+
useWorkbenchContext('Workbench.Canvas');
|
|
216
|
+
const slots = workbenchVariants();
|
|
217
|
+
return _jsx("div", { "data-slot": "workbench-canvas", className: slots.canvas({ className }), ...props });
|
|
218
|
+
}
|
|
219
|
+
WorkbenchCanvas.displayName = 'Workbench.Canvas';
|
|
220
|
+
/** The work itself: as large as its shape fits on the canvas, clear of the floating bar. */
|
|
221
|
+
function WorkbenchArtboard({ aspect = '16 / 9', className, style, ...props }) {
|
|
222
|
+
const slots = workbenchVariants();
|
|
223
|
+
const [wide, tall] = aspect.split('/').map((part) => Number(part.trim()) || 1);
|
|
224
|
+
return (_jsx("div", { "data-slot": "workbench-artboard", className: slots.artboard({ className }), style: { ...style, '--wb-aspect': aspect, '--wb-ratio': (wide ?? 1) / (tall ?? 1) }, ...props }));
|
|
225
|
+
}
|
|
226
|
+
WorkbenchArtboard.displayName = 'Workbench.Artboard';
|
|
227
|
+
/**
|
|
228
|
+
* The column after the canvas, for an `Inspector`. On a narrow screen the
|
|
229
|
+
* same children open in a sheet from the side instead.
|
|
230
|
+
*/
|
|
231
|
+
function WorkbenchInspector({ title = 'Inspector', className, children, ...props }) {
|
|
232
|
+
const workbench = useWorkbenchContext('Workbench.Inspector');
|
|
233
|
+
usePresence(workbench.hasInspector);
|
|
234
|
+
const slots = workbenchVariants();
|
|
235
|
+
if (workbench.compact) {
|
|
236
|
+
return (_jsx(Sheet, { side: "right", open: workbench.inspectorOpen, onOpenChange: workbench.setInspectorOpen, children: _jsxs(Sheet.Content, { size: "full", children: [_jsx(Sheet.Header, { title: title }), _jsx("div", { "data-slot": "workbench-inspector", className: slots.sheetBody(), children: children })] }) }));
|
|
237
|
+
}
|
|
238
|
+
return (_jsx("div", { "data-slot": "workbench-inspector", inert: workbench.focus || undefined, className: slots.inspector({ className }), ...props, children: children }));
|
|
239
|
+
}
|
|
240
|
+
WorkbenchInspector.displayName = 'Workbench.Inspector';
|
|
241
|
+
/**
|
|
242
|
+
* The button that opens the inspector's sheet on a narrow screen. It is
|
|
243
|
+
* only there when the inspector is a sheet, so put it where it belongs on
|
|
244
|
+
* either size: in the bar, beside the tools.
|
|
245
|
+
*/
|
|
246
|
+
function WorkbenchInspectorTrigger({ className, children, onClick, ...props }) {
|
|
247
|
+
const workbench = useWorkbenchContext('Workbench.InspectorTrigger');
|
|
248
|
+
const slots = workbenchVariants();
|
|
249
|
+
if (!workbench.compact)
|
|
250
|
+
return null;
|
|
251
|
+
return (_jsx("button", { type: "button", "data-slot": "workbench-inspector-trigger", "aria-label": "Open inspector", "aria-expanded": workbench.inspectorOpen, className: slots.trigger({ className }), onClick: (event) => {
|
|
252
|
+
onClick?.(event);
|
|
253
|
+
if (!event.defaultPrevented)
|
|
254
|
+
workbench.setInspectorOpen(true);
|
|
255
|
+
}, ...props, children: children ?? (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [_jsx("path", { d: "M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12M20 18h0" }), _jsx("circle", { cx: "16", cy: "6", r: "2" }), _jsx("circle", { cx: "10", cy: "12", r: "2" }), _jsx("circle", { cx: "18", cy: "18", r: "2" })] })) }));
|
|
256
|
+
}
|
|
257
|
+
WorkbenchInspectorTrigger.displayName = 'Workbench.InspectorTrigger';
|
|
258
|
+
/** The row under everything, for a `Timeline`. Its top edge drags to resize it. */
|
|
259
|
+
function WorkbenchTimeline({ resizable = true, minHeight = 140, className, children, ...props }) {
|
|
260
|
+
const workbench = useWorkbenchContext('Workbench.Timeline');
|
|
261
|
+
usePresence(workbench.hasTimeline);
|
|
262
|
+
const slots = workbenchVariants();
|
|
263
|
+
const [active, setActive] = useState(false);
|
|
264
|
+
const maxHeight = () => Math.max(minHeight, workbench.rootHeight() * 0.6);
|
|
265
|
+
const drag = (event) => {
|
|
266
|
+
if (event.button !== 0)
|
|
267
|
+
return;
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
270
|
+
const startY = event.clientY;
|
|
271
|
+
const startHeight = workbench.timelineHeight;
|
|
272
|
+
setActive(true);
|
|
273
|
+
workbench.setResizing(true);
|
|
274
|
+
const target = event.currentTarget;
|
|
275
|
+
const move = (next) => workbench.setTimelineHeight(Math.round(Math.min(Math.max(startHeight + startY - next.clientY, minHeight), maxHeight())));
|
|
276
|
+
const end = () => {
|
|
277
|
+
setActive(false);
|
|
278
|
+
workbench.setResizing(false);
|
|
279
|
+
target.removeEventListener('pointermove', move);
|
|
280
|
+
target.removeEventListener('pointerup', end);
|
|
281
|
+
target.removeEventListener('pointercancel', end);
|
|
282
|
+
};
|
|
283
|
+
target.addEventListener('pointermove', move);
|
|
284
|
+
target.addEventListener('pointerup', end);
|
|
285
|
+
target.addEventListener('pointercancel', end);
|
|
286
|
+
};
|
|
287
|
+
const keys = (event) => {
|
|
288
|
+
const by = event.key === 'ArrowUp' ? 16 : event.key === 'ArrowDown' ? -16 : 0;
|
|
289
|
+
if (!by)
|
|
290
|
+
return;
|
|
291
|
+
event.preventDefault();
|
|
292
|
+
workbench.setTimelineHeight(Math.min(Math.max(workbench.timelineHeight + by, minHeight), maxHeight()));
|
|
293
|
+
};
|
|
294
|
+
return (_jsxs("div", { "data-slot": "workbench-timeline", inert: workbench.focus || undefined, className: slots.timeline({ className }), ...props, children: [resizable ? (_jsx("div", { role: "separator", tabIndex: 0, "aria-orientation": "horizontal", "aria-label": "Timeline height", "aria-valuenow": workbench.timelineHeight, "aria-valuemin": minHeight, "data-slot": "workbench-resize", "data-active": active ? '' : undefined, className: slots.resize(), onPointerDown: drag, onKeyDown: keys })) : null, children] }));
|
|
295
|
+
}
|
|
296
|
+
WorkbenchTimeline.displayName = 'Workbench.Timeline';
|
|
297
|
+
export const Workbench = Object.assign(WorkbenchRoot, {
|
|
298
|
+
Bar: WorkbenchBar,
|
|
299
|
+
Sidebar: WorkbenchSidebar,
|
|
300
|
+
Canvas: WorkbenchCanvas,
|
|
301
|
+
Artboard: WorkbenchArtboard,
|
|
302
|
+
Inspector: WorkbenchInspector,
|
|
303
|
+
InspectorTrigger: WorkbenchInspectorTrigger,
|
|
304
|
+
Timeline: WorkbenchTimeline,
|
|
305
|
+
});
|
|
306
|
+
export { WorkbenchBar, WorkbenchSidebar, WorkbenchCanvas, WorkbenchArtboard, WorkbenchInspector, WorkbenchInspectorTrigger, WorkbenchTimeline, workbenchVariants, };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XYPad — two numbers set with one drag: a point moved across a field.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <XYPad label="Framing" defaultValue={{ x: 0.2, y: -0.1 }}>
|
|
6
|
+
* <XYPad.Value />
|
|
7
|
+
* </XYPad>
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* ## Two values that belong together
|
|
11
|
+
*
|
|
12
|
+
* A position, a pan, a balance between two pairs of things: values that the
|
|
13
|
+
* hand thinks of as one place are set as one place. Two sliders would make
|
|
14
|
+
* the reader find a point by turning two knobs in turn.
|
|
15
|
+
*
|
|
16
|
+
* ## The field reads like a chart
|
|
17
|
+
*
|
|
18
|
+
* A grid of dots marks the steps, two hairlines mark zero on each axis, and
|
|
19
|
+
* two fainter lines run through the handle to the edges, so where it stands
|
|
20
|
+
* can be read against both axes at once. While it is held, four brackets
|
|
21
|
+
* close round it.
|
|
22
|
+
*
|
|
23
|
+
* ## Joystick and trail
|
|
24
|
+
*
|
|
25
|
+
* `snapBack` makes it a joystick: it springs back to the centre when let go,
|
|
26
|
+
* for a value that only means something while it is held, such as a pan
|
|
27
|
+
* speed. `trail` draws the last moves as a fading line, for a value whose
|
|
28
|
+
* path matters as much as where it ends.
|
|
29
|
+
*/
|
|
30
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
31
|
+
export type XYPadColor = 'primary' | 'info' | 'success' | 'warning' | 'destructive';
|
|
32
|
+
export interface XYPadPoint {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
}
|
|
36
|
+
declare const xyPadVariants: import("tailwind-variants").TVReturnType<{
|
|
37
|
+
color: {
|
|
38
|
+
primary: {
|
|
39
|
+
root: string;
|
|
40
|
+
};
|
|
41
|
+
info: {
|
|
42
|
+
root: string;
|
|
43
|
+
};
|
|
44
|
+
success: {
|
|
45
|
+
root: string;
|
|
46
|
+
};
|
|
47
|
+
warning: {
|
|
48
|
+
root: string;
|
|
49
|
+
};
|
|
50
|
+
destructive: {
|
|
51
|
+
root: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}, {
|
|
55
|
+
root: string;
|
|
56
|
+
field: string[];
|
|
57
|
+
plane: string;
|
|
58
|
+
dots: string;
|
|
59
|
+
axis: string;
|
|
60
|
+
guide: string[];
|
|
61
|
+
trail: string;
|
|
62
|
+
handle: string[];
|
|
63
|
+
bracket: string[];
|
|
64
|
+
value: string;
|
|
65
|
+
axisName: string;
|
|
66
|
+
}, undefined, {
|
|
67
|
+
color: {
|
|
68
|
+
primary: {
|
|
69
|
+
root: string;
|
|
70
|
+
};
|
|
71
|
+
info: {
|
|
72
|
+
root: string;
|
|
73
|
+
};
|
|
74
|
+
success: {
|
|
75
|
+
root: string;
|
|
76
|
+
};
|
|
77
|
+
warning: {
|
|
78
|
+
root: string;
|
|
79
|
+
};
|
|
80
|
+
destructive: {
|
|
81
|
+
root: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}, {
|
|
85
|
+
root: string;
|
|
86
|
+
field: string[];
|
|
87
|
+
plane: string;
|
|
88
|
+
dots: string;
|
|
89
|
+
axis: string;
|
|
90
|
+
guide: string[];
|
|
91
|
+
trail: string;
|
|
92
|
+
handle: string[];
|
|
93
|
+
bracket: string[];
|
|
94
|
+
value: string;
|
|
95
|
+
axisName: string;
|
|
96
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
97
|
+
color: {
|
|
98
|
+
primary: {
|
|
99
|
+
root: string;
|
|
100
|
+
};
|
|
101
|
+
info: {
|
|
102
|
+
root: string;
|
|
103
|
+
};
|
|
104
|
+
success: {
|
|
105
|
+
root: string;
|
|
106
|
+
};
|
|
107
|
+
warning: {
|
|
108
|
+
root: string;
|
|
109
|
+
};
|
|
110
|
+
destructive: {
|
|
111
|
+
root: string;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
}, {
|
|
115
|
+
root: string;
|
|
116
|
+
field: string[];
|
|
117
|
+
plane: string;
|
|
118
|
+
dots: string;
|
|
119
|
+
axis: string;
|
|
120
|
+
guide: string[];
|
|
121
|
+
trail: string;
|
|
122
|
+
handle: string[];
|
|
123
|
+
bracket: string[];
|
|
124
|
+
value: string;
|
|
125
|
+
axisName: string;
|
|
126
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
127
|
+
export interface XYPadProps extends Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange' | 'children'> {
|
|
128
|
+
/** Where the point is, when the caller holds it. */
|
|
129
|
+
value?: XYPadPoint;
|
|
130
|
+
/** Where it starts, when the caller does not hold it. Default the centre. */
|
|
131
|
+
defaultValue?: XYPadPoint;
|
|
132
|
+
/** Called with the new point while it moves. */
|
|
133
|
+
onValueChange?: (value: XYPadPoint) => void;
|
|
134
|
+
/** Called once a move ends: on release, or after a key press. */
|
|
135
|
+
onValueCommitted?: (value: XYPadPoint) => void;
|
|
136
|
+
/** The range of `x`, left to right. Default `[-1, 1]`. */
|
|
137
|
+
x?: [number, number];
|
|
138
|
+
/** The range of `y`, bottom to top. Default `[-1, 1]`. */
|
|
139
|
+
y?: [number, number];
|
|
140
|
+
/** The granularity of both values. Default `0.01`. */
|
|
141
|
+
step?: number;
|
|
142
|
+
/** How many cells the dot grid has across. Default 8. */
|
|
143
|
+
grid?: number;
|
|
144
|
+
/** Snap the point to the dots of the grid. */
|
|
145
|
+
snapToGrid?: boolean;
|
|
146
|
+
/** Spring back to the centre on release, like a joystick. */
|
|
147
|
+
snapBack?: boolean;
|
|
148
|
+
/** Draw the last moves as a fading line behind the point. */
|
|
149
|
+
trail?: boolean;
|
|
150
|
+
/** The colour of the point, its guides and its trail. Default `info`. */
|
|
151
|
+
color?: XYPadColor;
|
|
152
|
+
/** The shape of the field, as a CSS aspect ratio. Default `16 / 10`. */
|
|
153
|
+
aspect?: string;
|
|
154
|
+
/** What it sets. Read out with its values. */
|
|
155
|
+
label?: string;
|
|
156
|
+
/** How each value is written, here and to a screen reader. Default two decimals. */
|
|
157
|
+
format?: (value: number, axis: 'x' | 'y') => string;
|
|
158
|
+
/** Turns it off. */
|
|
159
|
+
disabled?: boolean;
|
|
160
|
+
/** Submits `<name>.x` and `<name>.y` with a form. */
|
|
161
|
+
name?: string;
|
|
162
|
+
/** An `XYPad.Value` readout, set under the field. */
|
|
163
|
+
children?: ReactNode;
|
|
164
|
+
}
|
|
165
|
+
/** A field with a point in it, moved by a drag, a press or the arrow keys. */
|
|
166
|
+
declare function XYPadRoot({ value: valueProp, defaultValue, onValueChange, onValueCommitted, x: xRange, y: yRange, step, grid, snapToGrid, snapBack, trail, color, aspect, label, format, disabled, name, className, style, children, ...props }: XYPadProps): import("react").JSX.Element;
|
|
167
|
+
declare namespace XYPadRoot {
|
|
168
|
+
var displayName: string;
|
|
169
|
+
}
|
|
170
|
+
export interface XYPadValueProps extends ComponentProps<'div'> {
|
|
171
|
+
}
|
|
172
|
+
/** The two values written out, under the field. */
|
|
173
|
+
declare function XYPadValue({ className, ...props }: XYPadValueProps): import("react").JSX.Element;
|
|
174
|
+
declare namespace XYPadValue {
|
|
175
|
+
var displayName: string;
|
|
176
|
+
}
|
|
177
|
+
export declare const XYPad: typeof XYPadRoot & {
|
|
178
|
+
Value: typeof XYPadValue;
|
|
179
|
+
};
|
|
180
|
+
export { XYPadValue, xyPadVariants };
|