@nuforge/editor 0.2.0 → 0.3.0
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/index.cjs +190 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +252 -3
- package/dist/index.mjs +186 -1
- package/dist/index.mjs.map +1 -0
- package/dist/react.cjs +325 -3
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.ts +205 -4
- package/dist/react.mjs +325 -4
- package/dist/react.mjs.map +1 -0
- package/package.json +5 -5
package/dist/react.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ReactNode } from 'react';
|
|
|
3
3
|
import * as t from '@nuforge/types';
|
|
4
4
|
import { MutationEntry } from '@nuforge/reactivity';
|
|
5
5
|
|
|
6
|
+
/** A control hint for auto-generating inspector fields from a block's props. */
|
|
6
7
|
interface PropSchemaField {
|
|
7
8
|
name: string;
|
|
8
9
|
type: 'string' | 'number' | 'boolean' | 'expression' | 'enum';
|
|
@@ -10,6 +11,11 @@ interface PropSchemaField {
|
|
|
10
11
|
default?: string | number | boolean;
|
|
11
12
|
options?: string[];
|
|
12
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* A reusable element/block in the palette. `create()` returns a fresh template
|
|
16
|
+
* subtree to insert. `icon` is a name (not a component) so the registry stays
|
|
17
|
+
* framework-agnostic; the UI maps the name to its own icon set.
|
|
18
|
+
*/
|
|
13
19
|
interface Block {
|
|
14
20
|
id: string;
|
|
15
21
|
label: string;
|
|
@@ -27,6 +33,35 @@ declare class BlockRegistry {
|
|
|
27
33
|
byCategory(): Record<string, Block[]>;
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
/** Where a dragged node is dropped relative to a target. */
|
|
37
|
+
type DropPosition = 'before' | 'after' | 'inside';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Wire protocol for `RemoteCanvasHost` <-> `createCanvasReceiver`: a genuinely
|
|
41
|
+
* navigated `/canvas/[id]`-style iframe has its own JS realm, so state and
|
|
42
|
+
* interaction can't cross as direct object references the way `IframeCanvas`'s
|
|
43
|
+
* portal allows — everything here is a structured-clone-safe plain message.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately generic: only the shapes the editor primitive itself needs
|
|
46
|
+
* (state sync, ready handshake, the core interaction events) are typed. Host
|
|
47
|
+
* concerns like theme/preview-mode/overrides ride through `CanvasUiMessage`'s
|
|
48
|
+
* opaque `payload` — this package relays it, never interprets it.
|
|
49
|
+
*/
|
|
50
|
+
declare const CANVAS_CHANNEL = "nuforge-canvas";
|
|
51
|
+
declare const CANVAS_PROTOCOL_VERSION = 1;
|
|
52
|
+
interface CanvasKeydownMessage {
|
|
53
|
+
channel: typeof CANVAS_CHANNEL;
|
|
54
|
+
v: typeof CANVAS_PROTOCOL_VERSION;
|
|
55
|
+
type: 'keydown';
|
|
56
|
+
key: string;
|
|
57
|
+
code: string;
|
|
58
|
+
ctrlKey: boolean;
|
|
59
|
+
metaKey: boolean;
|
|
60
|
+
shiftKey: boolean;
|
|
61
|
+
altKey: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A named action a UI can discover and bind to (toolbar buttons, shortcuts). */
|
|
30
65
|
interface Command {
|
|
31
66
|
id: string;
|
|
32
67
|
label: string;
|
|
@@ -43,40 +78,68 @@ declare class CommandRegistry {
|
|
|
43
78
|
run(id: string): void;
|
|
44
79
|
}
|
|
45
80
|
|
|
46
|
-
type DropPosition = 'before' | 'after' | 'inside';
|
|
47
|
-
|
|
48
81
|
interface CreateEditorOptions {
|
|
82
|
+
/** Extra blocks to add to the palette. */
|
|
49
83
|
blocks?: Block[];
|
|
84
|
+
/** Include the built-in DEFAULT_BLOCKS (default true). */
|
|
50
85
|
defaultBlocks?: boolean;
|
|
51
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* A high-level facade over a {@link Nu} runtime for building visual page
|
|
89
|
+
* editors. Every mutation goes through `nu.change`, so it is transactional and
|
|
90
|
+
* undoable. Reach for these instead of hand-splicing the AST.
|
|
91
|
+
*/
|
|
52
92
|
declare class Editor {
|
|
53
93
|
readonly nu: Nu;
|
|
54
94
|
readonly blocks: BlockRegistry;
|
|
55
95
|
readonly commands: CommandRegistry;
|
|
56
96
|
constructor(nu: Nu, opts?: CreateEditorOptions);
|
|
97
|
+
/** Append (or insert at `index`) `child` into `parent`'s children. */
|
|
57
98
|
insertNode(parent: t.Template, child: t.Template, index?: number): void;
|
|
99
|
+
/** Remove a node from its parent. Returns false if it has no parent. */
|
|
58
100
|
removeNode(node: t.Template): boolean;
|
|
101
|
+
/** Move `dragged` before/after/inside `target`. Refuses to nest into self. */
|
|
59
102
|
moveNode(dragged: t.Template, target: t.Template, position: DropPosition): boolean;
|
|
60
103
|
private clipboardNode;
|
|
104
|
+
/** Put a node on the editor clipboard (a clone is made on paste). */
|
|
61
105
|
copyNode(node: t.Template): void;
|
|
106
|
+
/** Copy a node, then remove it. */
|
|
62
107
|
cutNode(node: t.Template): boolean;
|
|
108
|
+
/** Whether there is something on the clipboard to paste. */
|
|
63
109
|
canPaste(): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Paste a fresh copy (new ids) of the clipboard node before/after/inside
|
|
112
|
+
* `target`. Returns the inserted node, or null if nothing was pasted.
|
|
113
|
+
*/
|
|
64
114
|
pasteNode(target: t.Template, position?: DropPosition): t.Template | null;
|
|
115
|
+
/** Duplicate a node in place (inserted right after it). Returns the copy. */
|
|
65
116
|
duplicateNode(node: t.Template): t.Template | null;
|
|
117
|
+
/**
|
|
118
|
+
* Insert a detached node before/after/inside a target (undoable). The core of
|
|
119
|
+
* canvas drag-and-drop: a drop computes a target + position, then calls this.
|
|
120
|
+
* Returns the inserted node.
|
|
121
|
+
*/
|
|
66
122
|
insertNodeAt(node: t.Template, target: t.Template, position: DropPosition): t.Template | null;
|
|
123
|
+
/** Create a block and insert it before/after/inside a target. */
|
|
67
124
|
insertBlockAt(blockId: string, target: t.Template, position: DropPosition): t.Template | null;
|
|
68
125
|
private insertCloneRelativeTo;
|
|
126
|
+
/** Set several props at once. */
|
|
69
127
|
updateProps(node: t.Template, props: Record<string, t.Expression>): void;
|
|
70
128
|
setProp(node: t.Template, key: string, expr: t.Expression): void;
|
|
71
129
|
removeProp(node: t.Template, key: string): void;
|
|
130
|
+
/** Parse `source` as a nuforge expression and assign it to `key`. */
|
|
72
131
|
setExpression(node: t.Template, key: string, source: string): boolean;
|
|
132
|
+
/** Update a text node's literal value. */
|
|
73
133
|
setText(node: t.Template, value: string): boolean;
|
|
74
134
|
setTag(node: t.TagTemplate, tag: string): void;
|
|
75
135
|
addClass(node: t.Template, name: string): void;
|
|
136
|
+
/** Parse `source` and set the condition under which class `name` applies. */
|
|
76
137
|
setClassCondition(node: t.Template, name: string, source: string): boolean;
|
|
77
138
|
removeClass(node: t.Template, name: string): void;
|
|
139
|
+
/** Set the `@if` condition from source. */
|
|
78
140
|
setCondition(node: t.Template, source: string): boolean;
|
|
79
141
|
clearCondition(node: t.Template): void;
|
|
142
|
+
/** Repeat a node over `iterator` (a list expression), binding each `alias`. */
|
|
80
143
|
setEach(node: t.Template, iterator: string, alias?: string, indexName?: string): boolean;
|
|
81
144
|
clearEach(node: t.Template): void;
|
|
82
145
|
addState(component: t.NuComponent, name?: string, init?: string): t.Val;
|
|
@@ -85,9 +148,12 @@ declare class Editor {
|
|
|
85
148
|
addComponentProp(component: t.NuComponent, name?: string, init?: string): t.ComponentProp;
|
|
86
149
|
removeComponentProp(component: t.NuComponent, prop: t.ComponentProp): void;
|
|
87
150
|
setComponentPropInit(prop: t.ComponentProp, source: string): boolean;
|
|
151
|
+
/** Create a block's template and insert it into `parent`. */
|
|
88
152
|
insertBlock(blockId: string, parent: t.Template, index?: number): t.Template | null;
|
|
89
153
|
private uniqueName;
|
|
154
|
+
/** Add a new component (a "page"). */
|
|
90
155
|
addComponent(name?: string): t.NuComponent;
|
|
156
|
+
/** Rename a component, updating `<Name/>` references across the program. */
|
|
91
157
|
renameComponent(component: t.NuComponent, name: string): boolean;
|
|
92
158
|
removeComponent(component: t.NuComponent): boolean;
|
|
93
159
|
duplicateComponent(component: t.NuComponent): t.NuComponent;
|
|
@@ -98,11 +164,15 @@ declare class Editor {
|
|
|
98
164
|
getChildren(node: t.Template): t.Template[];
|
|
99
165
|
getSiblings(node: t.Template): t.Template[];
|
|
100
166
|
getNodeIndex(node: t.Template): number;
|
|
167
|
+
/** Full path from the State root to this node (for selections/serialization). */
|
|
101
168
|
getNodePath(node: t.Type): Array<string | number>;
|
|
169
|
+
/** Fires with the raw field-level writes of every change (incl. undo/redo). */
|
|
102
170
|
onChange(cb: (entries: MutationEntry[]) => void): () => void;
|
|
171
|
+
/** Fires only when the tree shape changes (a child array was mutated). */
|
|
103
172
|
onStructureChange(cb: () => void): () => void;
|
|
104
173
|
private spliceFromParent;
|
|
105
174
|
private applyMove;
|
|
175
|
+
/** Insert an already-detached node relative to `target` (no removal). */
|
|
106
176
|
private insertRelativeTo;
|
|
107
177
|
}
|
|
108
178
|
|
|
@@ -113,31 +183,76 @@ interface Rect {
|
|
|
113
183
|
height: number;
|
|
114
184
|
}
|
|
115
185
|
interface CanvasWindow {
|
|
186
|
+
/** The canvas's own document — the iframe's, inside `IframeCanvas`. */
|
|
116
187
|
doc: Document | null;
|
|
188
|
+
/** The canvas's own window — the iframe's, inside `IframeCanvas`. */
|
|
117
189
|
window: Window | null;
|
|
118
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* The real `window`/`document` a canvas-rendered element visually lives in.
|
|
193
|
+
* `IframeCanvas` portals its content into an `<iframe>`, but a portal only
|
|
194
|
+
* relocates DOM nodes — the JS building them still runs in the host page's
|
|
195
|
+
* realm. Any library that reads a bare `window`/`document` global (scroll
|
|
196
|
+
* listeners, `matchMedia`, `ResizeObserver`/`IntersectionObserver` defaults)
|
|
197
|
+
* silently ends up watching the HOST page, not the iframe the user actually
|
|
198
|
+
* sees and scrolls. Build refs/options from `useCanvasWindow()` instead of the
|
|
199
|
+
* ambient global so they resolve to the right realm — this works identically
|
|
200
|
+
* outside `IframeCanvas` too (e.g. `NuFrame`'s direct, non-iframe render),
|
|
201
|
+
* where it's just the ambient `window`/`document`.
|
|
202
|
+
*/
|
|
119
203
|
declare function useCanvasWindow(): CanvasWindow;
|
|
204
|
+
/** Reactive renderer for a frame, decorating each element with `data-nu-id`. */
|
|
120
205
|
declare const EditorFrame: (props: {
|
|
121
206
|
frame: Frame;
|
|
122
207
|
}) => ReactNode;
|
|
208
|
+
/** Map a DOM node (or event target) back to its template id. */
|
|
123
209
|
declare function templateIdFromElement(target: EventTarget | null): string | null;
|
|
210
|
+
/** Sensible default styling for the isolated canvas document. */
|
|
124
211
|
declare const DEFAULT_CANVAS_CSS = "\n *, *::before, *::after { box-sizing: border-box; }\n html, body { margin: 0; }\n body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; color: #111; line-height: 1.5; padding: 28px; }\n h1 { font-size: 2rem; font-weight: 700; margin: .5em 0; }\n h2 { font-size: 1.5rem; font-weight: 600; margin: .6em 0; }\n h3 { font-size: 1.2rem; font-weight: 600; margin: .7em 0; }\n p { margin: .8em 0; }\n ul { list-style: disc; padding-left: 1.5em; margin: .8em 0; }\n ol { list-style: decimal; padding-left: 1.5em; margin: .8em 0; }\n a { color: #2563eb; text-decoration: underline; }\n button { border: 1px solid #d4d4d8; border-radius: 6px; padding: 4px 12px; background: #fafafa; cursor: pointer; font: inherit; }\n button:hover { background: #f4f4f5; }\n input, textarea, select { border: 1px solid #d4d4d8; border-radius: 6px; padding: 4px 8px; font: inherit; }\n section { display: flex; align-items: center; gap: .75rem; margin: .8em 0; }\n img { max-width: 100%; }\n [data-nu-id] { cursor: default; }\n";
|
|
125
212
|
interface IframeCanvasProps {
|
|
126
213
|
frame: Frame;
|
|
127
214
|
selectedId?: string | null;
|
|
128
215
|
hoveredId?: string | null;
|
|
216
|
+
/** Bump to force the overlay to recompute (e.g. on every committed change). */
|
|
129
217
|
revision?: number;
|
|
218
|
+
/**
|
|
219
|
+
* Turn the canvas into a live, interactive preview. When `true`, the editing
|
|
220
|
+
* affordances are switched off: clicks and hovers pass through to the real
|
|
221
|
+
* elements (buttons fire, links work, inputs focus), drag-and-drop editing is
|
|
222
|
+
* disabled, and no overlay is drawn. When `false` (the default) the canvas is
|
|
223
|
+
* in edit mode — clicks select instead of activating, hover highlights, and
|
|
224
|
+
* `onDropOnNode` / `renderBox` are live. The same reactive `frame` is shown in
|
|
225
|
+
* both modes, so toggling is instant and state is preserved.
|
|
226
|
+
*/
|
|
130
227
|
previewMode?: boolean;
|
|
131
228
|
onSelect?: (id: string | null) => void;
|
|
132
229
|
onHover?: (id: string | null) => void;
|
|
230
|
+
/**
|
|
231
|
+
* Enable drop-on-canvas: fired when something is dropped onto a node. Compute
|
|
232
|
+
* a block to insert and call `editor.insertBlockAt(blockId, target, position)`.
|
|
233
|
+
*/
|
|
133
234
|
onDropOnNode?: (targetId: string, position: DropPosition, event: DragEvent) => void;
|
|
235
|
+
/** CSS injected into the isolated document (defaults to DEFAULT_CANVAS_CSS). */
|
|
134
236
|
baseCss?: string;
|
|
237
|
+
/**
|
|
238
|
+
* Initial HTML for the iframe document — put anything in the `<head>` (CDN
|
|
239
|
+
* stylesheets, web fonts, `<script>` tags, a `<base>`, meta). The frame is
|
|
240
|
+
* portaled into its `<body>`. `baseCss` is still appended on top; pass
|
|
241
|
+
* `baseCss=""` to fully control styling from here.
|
|
242
|
+
*/
|
|
135
243
|
srcDoc?: string;
|
|
136
244
|
className?: string;
|
|
137
245
|
containerClassName?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Build the overlay from the computed selection / hover / drop rects and the
|
|
248
|
+
* selected element's tag. Omit it for no overlay. The overlay container is
|
|
249
|
+
* pointer-events:none — set `pointerEvents: 'auto'` on interactive bits (e.g.
|
|
250
|
+
* an action toolbar) so they receive clicks.
|
|
251
|
+
*/
|
|
138
252
|
renderBox?: (rects: {
|
|
139
253
|
selected: Rect | null;
|
|
140
254
|
hovered: Rect | null;
|
|
255
|
+
/** Tag name of the selected element, e.g. `"div"`. */
|
|
141
256
|
selectedLabel: string | null;
|
|
142
257
|
drop: {
|
|
143
258
|
rect: Rect;
|
|
@@ -145,17 +260,103 @@ interface IframeCanvasProps {
|
|
|
145
260
|
} | null;
|
|
146
261
|
}) => ReactNode;
|
|
147
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* A style-isolated editor canvas: the frame is portaled into an `<iframe>` so
|
|
265
|
+
* the host's CSS can't leak into the preview (and vice-versa). Selection/hover
|
|
266
|
+
* use native capture listeners on the iframe document, so element handlers stay
|
|
267
|
+
* inert while editing. Selection/hover rects are computed (in a layout effect,
|
|
268
|
+
* after the iframe commits) and handed to `renderBox` for the overlay.
|
|
269
|
+
*/
|
|
148
270
|
declare function IframeCanvas({ frame, selectedId, hoveredId, revision, previewMode, onSelect, onHover, onDropOnNode, baseCss, srcDoc, className, containerClassName, renderBox, }: IframeCanvasProps): ReactNode;
|
|
271
|
+
interface RemoteCanvasHostProps {
|
|
272
|
+
/** The host's `Nu` instance — flattened and pushed to the canvas realm on
|
|
273
|
+
* every committed change. Never referenced directly by the canvas realm: a
|
|
274
|
+
* real `src` navigation puts it in a separate JS realm with no shared
|
|
275
|
+
* object identity, unlike `IframeCanvas`'s portal. */
|
|
276
|
+
nu: Nu;
|
|
277
|
+
/** Which component the canvas should render (`Nu.createFrame`'s `component.name`). */
|
|
278
|
+
activeComponent: string;
|
|
279
|
+
/** The canvas route's URL, e.g. `/canvas/my-project`. The `BroadcastChannel`
|
|
280
|
+
* name is derived from it, so `createCanvasReceiver` on the other end just
|
|
281
|
+
* needs the SAME `src` — no separate id to keep in sync. */
|
|
282
|
+
src: string;
|
|
283
|
+
selectedId?: string | null;
|
|
284
|
+
hoveredId?: string | null;
|
|
285
|
+
/** Bump to force the overlay to recompute (e.g. on every committed change). */
|
|
286
|
+
revision?: number;
|
|
287
|
+
/**
|
|
288
|
+
* Mirrors `IframeCanvas`'s `previewMode`: when `true`, the canvas is a live,
|
|
289
|
+
* interactive preview (its own DSL `onClick` handlers run against the
|
|
290
|
+
* canvas's local `Nu`, since that instance is genuinely loaded and mutable —
|
|
291
|
+
* see `createCanvasReceiver`'s docs). Toggling this (either direction) forces
|
|
292
|
+
* a fresh `init` instead of an incremental `patch`, so any preview-only
|
|
293
|
+
* local mutation never lingers once you're back in edit mode.
|
|
294
|
+
*/
|
|
295
|
+
previewMode?: boolean;
|
|
296
|
+
/** Opaque host-defined payload (theme CSS, hide/lock overrides, ...) pushed
|
|
297
|
+
* to the canvas via a `ui` message whenever this reference changes. */
|
|
298
|
+
uiPayload?: unknown;
|
|
299
|
+
onSelect?: (id: string | null) => void;
|
|
300
|
+
onHover?: (id: string | null) => void;
|
|
301
|
+
onDropOnNode?: (targetId: string, position: DropPosition) => void;
|
|
302
|
+
onKeydownRelay?: (e: Omit<CanvasKeydownMessage, 'channel' | 'v' | 'type'>) => void;
|
|
303
|
+
onLinkClick?: (href: string) => void;
|
|
304
|
+
className?: string;
|
|
305
|
+
containerClassName?: string;
|
|
306
|
+
/**
|
|
307
|
+
* Build the overlay from the computed selection / hover rects and the
|
|
308
|
+
* selected element's tag — same contract as `IframeCanvas`'s `renderBox`
|
|
309
|
+
* (no `drop` rect: the canvas realm renders its own drop indicator locally,
|
|
310
|
+
* since it already has the DOM geometry and relaying it every `dragover`
|
|
311
|
+
* tick would add a message-per-frame the local realm doesn't need). Reading
|
|
312
|
+
* these rects is a same-origin DOM read (`iframe.contentDocument`), which a
|
|
313
|
+
* real `src` navigation doesn't block — only shared JS object identity is
|
|
314
|
+
* blocked, not DOM geometry reads.
|
|
315
|
+
*/
|
|
316
|
+
renderBox?: (rects: {
|
|
317
|
+
selected: Rect | null;
|
|
318
|
+
hovered: Rect | null;
|
|
319
|
+
selectedLabel: string | null;
|
|
320
|
+
}) => ReactNode;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The `IframeCanvas` alternative for when a canvas needs its OWN genuine JS
|
|
324
|
+
* realm — e.g. so a third-party library reading bare `window`/`document` at
|
|
325
|
+
* module scope (an animation library's scroll-linked internals, a gesture
|
|
326
|
+
* library, ...) resolves against the canvas's real globals instead of
|
|
327
|
+
* silently binding to the host page's. A portal can't fix this (see
|
|
328
|
+
* `useCanvasWindow`'s docs for that half of the problem) because it only
|
|
329
|
+
* relocates DOM nodes, not the JS module graph building them.
|
|
330
|
+
*
|
|
331
|
+
* Renders a genuinely-`src`-navigated `<iframe>` — no `createPortal`, so
|
|
332
|
+
* nothing here shares object identity with the canvas's own React tree. State
|
|
333
|
+
* flows one-way, host -> canvas, over a `BroadcastChannel` as `t.flatten`'d
|
|
334
|
+
* JSON (see `./canvas-protocol`); interaction (select/hover/drop/keydown/
|
|
335
|
+
* link-click) relays back the other way as small typed messages. Pair with
|
|
336
|
+
* `createCanvasReceiver` (`./canvas-client`) on the canvas route's own end.
|
|
337
|
+
*/
|
|
338
|
+
declare function RemoteCanvasHost({ nu, activeComponent, src, selectedId, hoveredId, revision, previewMode, uiPayload, onSelect, onHover, onDropOnNode, onKeydownRelay, onLinkClick, className, containerClassName, renderBox, }: RemoteCanvasHostProps): ReactNode;
|
|
339
|
+
/** Re-render only when the tree shape changes (add/remove/move). */
|
|
149
340
|
declare function useStructureRevision(editor: Pick<Editor, 'onStructureChange'>): number;
|
|
341
|
+
/** Re-render on every committed change (for undo state, inspectors, …). */
|
|
150
342
|
declare function useEditorRevision(editor: Pick<Editor, 'onChange'>): number;
|
|
151
343
|
type PropFieldValue = string | number | boolean | undefined;
|
|
152
344
|
interface PropFieldsProps {
|
|
345
|
+
/** The control schema (e.g. a block's `props`). */
|
|
153
346
|
fields: PropSchemaField[];
|
|
347
|
+
/** Current value per field name. */
|
|
154
348
|
values: Record<string, PropFieldValue>;
|
|
349
|
+
/** Called when a control changes. Wire to `editor.setProp`/`setExpression`. */
|
|
155
350
|
onChange: (name: string, value: string | number | boolean) => void;
|
|
156
351
|
className?: string;
|
|
157
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* A headless inspector renderer: builds a control per {@link PropSchemaField}
|
|
355
|
+
* (text / number / checkbox / select / expression) so you don't hand-write a
|
|
356
|
+
* form per block. Style via `className` and your own CSS; wire `onChange` to the
|
|
357
|
+
* editor's mutation methods.
|
|
358
|
+
*/
|
|
158
359
|
declare function PropFields({ fields, values, onChange, className, }: PropFieldsProps): ReactNode;
|
|
159
360
|
|
|
160
|
-
export { DEFAULT_CANVAS_CSS, EditorFrame, IframeCanvas, PropFields, templateIdFromElement, useCanvasWindow, useEditorRevision, useStructureRevision };
|
|
161
|
-
export type { CanvasWindow, IframeCanvasProps, PropFieldValue, PropFieldsProps, Rect };
|
|
361
|
+
export { DEFAULT_CANVAS_CSS, EditorFrame, IframeCanvas, PropFields, RemoteCanvasHost, templateIdFromElement, useCanvasWindow, useEditorRevision, useStructureRevision };
|
|
362
|
+
export type { CanvasWindow, IframeCanvasProps, PropFieldValue, PropFieldsProps, Rect, RemoteCanvasHostProps };
|