@dragcraft/renderer 0.0.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/index.mjs ADDED
@@ -0,0 +1,2590 @@
1
+ import { CommandType, DEFAULT_LAYOUT_REGION, DEFAULT_SORT_SCOPE, buildSchemaIndex, createContainerPlan, createLayoutPlan, getLockedIndices, getLockedIndicesFromNodes, getSortScopeEntries, isInsertAllowed, isMoveAllowed, isRemoveAllowed, normalizeStyleValueMap, resolveBehavior, resolveNodeLayout } from "@dragcraft/core";
2
+ import { IconArrowDown, IconArrowUp, IconComponent, IconCopy, IconDelete, IconDrag, IconPlus } from "@dragcraft/icons";
3
+ import { Teleport, computed, defineComponent, h, inject, mergeProps, nextTick, onBeforeUnmount, onMounted, onUpdated, provide, ref, watch } from "vue";
4
+ import { cloneDeep } from "@dragcraft/utils";
5
+ import { useI18n } from "@dragcraft/i18n";
6
+ import { autoUpdate } from "@floating-ui/dom";
7
+ //#region src/action-runtime.ts
8
+ function isPromiseLike$1(value) {
9
+ return value !== null && typeof value === "object" && typeof value.then === "function";
10
+ }
11
+ function resolveActionDecision(result) {
12
+ if (result === false) return { allowed: false };
13
+ if (result && typeof result === "object" && "allowed" in result) return result;
14
+ return { allowed: true };
15
+ }
16
+ function resolveText(value, invocation) {
17
+ return typeof value === "function" ? value(invocation) : value;
18
+ }
19
+ function reportActionError(interceptors, invocation, error) {
20
+ console.error(`[dragcraft] Action "${invocation.key}" error (action cancelled):`, error);
21
+ for (const interceptor of interceptors) try {
22
+ interceptor.onActionError?.(invocation, error);
23
+ } catch (err) {
24
+ console.error("[dragcraft] Action error handler failed:", err);
25
+ }
26
+ }
27
+ function fireAfterAction(interceptors, invocation) {
28
+ for (const interceptor of interceptors) {
29
+ if (!interceptor.afterAction) continue;
30
+ try {
31
+ const result = interceptor.afterAction(invocation);
32
+ if (isPromiseLike$1(result)) result.catch((err) => {
33
+ console.error("[dragcraft] Async after-action error:", err);
34
+ });
35
+ } catch (err) {
36
+ console.error("[dragcraft] After-action error:", err);
37
+ }
38
+ }
39
+ }
40
+ function runActionPipeline(invocation, execute, interceptors = [], pendingGuard) {
41
+ if (pendingGuard?.value) return;
42
+ const runExecute = () => {
43
+ const result = execute();
44
+ if (isPromiseLike$1(result)) return result.then(() => {
45
+ fireAfterAction(interceptors, invocation);
46
+ });
47
+ fireAfterAction(interceptors, invocation);
48
+ };
49
+ const runBeforeFrom = (startIndex) => {
50
+ for (let index = startIndex; index < interceptors.length; index++) {
51
+ const beforeAction = interceptors[index].beforeAction;
52
+ if (!beforeAction) continue;
53
+ const result = beforeAction(invocation);
54
+ if (isPromiseLike$1(result)) return result.then((resolved) => {
55
+ if (!resolveActionDecision(resolved).allowed) return;
56
+ return runBeforeFrom(index + 1);
57
+ });
58
+ if (!resolveActionDecision(result).allowed) return;
59
+ }
60
+ return runExecute();
61
+ };
62
+ try {
63
+ const result = runBeforeFrom(0);
64
+ if (!isPromiseLike$1(result)) return result;
65
+ if (pendingGuard) pendingGuard.value = true;
66
+ return result.catch((err) => {
67
+ reportActionError(interceptors, invocation, err);
68
+ }).finally(() => {
69
+ if (pendingGuard) pendingGuard.value = false;
70
+ });
71
+ } catch (err) {
72
+ reportActionError(interceptors, invocation, err);
73
+ }
74
+ }
75
+ function createConfirmActionInterceptor(options) {
76
+ const shouldConfirm = options.shouldConfirm ?? ((invocation) => invocation.risk === "destructive");
77
+ return { beforeAction(invocation) {
78
+ if (!shouldConfirm(invocation)) return;
79
+ return options.confirm({
80
+ invocation,
81
+ title: resolveText(options.title, invocation),
82
+ message: resolveText(options.message, invocation)
83
+ });
84
+ } };
85
+ }
86
+ //#endregion
87
+ //#region src/action-registry.ts
88
+ /**
89
+ * Builds an InstanceBehaviorContext from a NodeActionContext.
90
+ * Schema is already read reactively by the calling computed (in useNodeActions).
91
+ */
92
+ function toInstanceCtx(ctx) {
93
+ return {
94
+ node: ctx.node,
95
+ schema: ctx.schema
96
+ };
97
+ }
98
+ function canReorder(ctx) {
99
+ if (ctx.owner.kind === "root" && ctx.sortScope === false) return false;
100
+ const instanceCtx = toInstanceCtx(ctx);
101
+ return resolveBehavior(ctx.meta?.draggable, instanceCtx) && resolveBehavior(ctx.meta?.sortable, instanceCtx);
102
+ }
103
+ function getScopedLockedIndices(ctx) {
104
+ if (ctx.lockedIndices) return ctx.lockedIndices;
105
+ const schema = ctx.schema;
106
+ if (ctx.owner.kind === "container") {
107
+ const containerOwner = ctx.owner;
108
+ return getLockedIndicesFromNodes((schema.root.children?.find((node) => node.id === containerOwner.containerId))?.container?.regions[containerOwner.regionId] ?? [], ctx.engine.registry, schema);
109
+ }
110
+ return getLockedIndices(schema.root.children ?? [], ctx.engine.registry, schema, ctx.sortScope === false ? void 0 : ctx.sortScope);
111
+ }
112
+ const ActionKey = {
113
+ DRAG: "drag",
114
+ MOVE_UP: "move-up",
115
+ MOVE_DOWN: "move-down",
116
+ DUPLICATE: "duplicate",
117
+ DELETE: "delete"
118
+ };
119
+ function createDefaultActions(t) {
120
+ const _t = t ?? ((_, f) => f ?? "");
121
+ return [
122
+ {
123
+ key: ActionKey.DRAG,
124
+ label: _t("action.drag", "拖拽排序"),
125
+ icon: IconDrag,
126
+ type: "drag-handle",
127
+ order: 100,
128
+ available: canReorder
129
+ },
130
+ {
131
+ key: ActionKey.MOVE_UP,
132
+ label: _t("action.move-up", "上移"),
133
+ icon: IconArrowUp,
134
+ type: "button",
135
+ order: 200,
136
+ metadata: {
137
+ commandType: CommandType.MOVE_NODE,
138
+ direction: "up"
139
+ },
140
+ available: canReorder,
141
+ disabled: (ctx) => {
142
+ if (ctx.index === 0) return true;
143
+ const lockedIndices = getScopedLockedIndices(ctx);
144
+ if (lockedIndices.size === 0) return false;
145
+ return !isMoveAllowed(ctx.index, ctx.index - 1, lockedIndices);
146
+ },
147
+ command: (ctx) => {
148
+ if (ctx.index <= 0) return null;
149
+ return {
150
+ type: CommandType.MOVE_NODE,
151
+ payload: {
152
+ nodeId: ctx.node.id,
153
+ destination: {
154
+ ...ctx.owner,
155
+ index: ctx.index - 1
156
+ }
157
+ }
158
+ };
159
+ }
160
+ },
161
+ {
162
+ key: ActionKey.MOVE_DOWN,
163
+ label: _t("action.move-down", "下移"),
164
+ icon: IconArrowDown,
165
+ type: "button",
166
+ order: 300,
167
+ metadata: {
168
+ commandType: CommandType.MOVE_NODE,
169
+ direction: "down"
170
+ },
171
+ available: canReorder,
172
+ disabled: (ctx) => {
173
+ if (ctx.index >= ctx.siblingCount - 1) return true;
174
+ const lockedIndices = getScopedLockedIndices(ctx);
175
+ if (lockedIndices.size === 0) return false;
176
+ return !isMoveAllowed(ctx.index, ctx.index + 1, lockedIndices);
177
+ },
178
+ command: (ctx) => {
179
+ if (ctx.index >= ctx.siblingCount - 1) return null;
180
+ return {
181
+ type: CommandType.MOVE_NODE,
182
+ payload: {
183
+ nodeId: ctx.node.id,
184
+ destination: {
185
+ ...ctx.owner,
186
+ index: ctx.index + 2
187
+ }
188
+ }
189
+ };
190
+ }
191
+ },
192
+ {
193
+ key: ActionKey.DUPLICATE,
194
+ label: _t("action.duplicate", "复制"),
195
+ icon: IconCopy,
196
+ type: "button",
197
+ order: 350,
198
+ disabled: (ctx) => {
199
+ if (ctx.owner.kind === "root" && ctx.sortScope === false) return false;
200
+ const lockedIndices = getScopedLockedIndices(ctx);
201
+ return !isInsertAllowed(ctx.index + 1, lockedIndices);
202
+ },
203
+ command: (ctx) => ({
204
+ type: CommandType.DUPLICATE_NODE,
205
+ payload: { nodeId: ctx.node.id }
206
+ })
207
+ },
208
+ {
209
+ key: ActionKey.DELETE,
210
+ label: _t("action.delete", "删除"),
211
+ icon: IconDelete,
212
+ type: "button",
213
+ order: 400,
214
+ risk: "destructive",
215
+ metadata: { commandType: CommandType.REMOVE_NODE },
216
+ className: "dc-node__toolbar-btn--delete",
217
+ available: (ctx) => resolveBehavior(ctx.meta?.deletable, toInstanceCtx(ctx)),
218
+ disabled: (ctx) => {
219
+ if (ctx.owner.kind === "root" && ctx.sortScope === false) return false;
220
+ const lockedIndices = getScopedLockedIndices(ctx);
221
+ if (lockedIndices.size === 0) return false;
222
+ return !isRemoveAllowed(ctx.index, lockedIndices);
223
+ },
224
+ command: (ctx) => ({
225
+ type: CommandType.REMOVE_NODE,
226
+ payload: { nodeId: ctx.node.id }
227
+ })
228
+ }
229
+ ];
230
+ }
231
+ function createNodeActionRegistry(initialActions) {
232
+ const actions = /* @__PURE__ */ new Map();
233
+ const defaults = initialActions ?? createDefaultActions();
234
+ for (const action of defaults) actions.set(action.key, action);
235
+ return {
236
+ getActions() {
237
+ return Array.from(actions.values()).sort((a, b) => a.order - b.order);
238
+ },
239
+ register(action) {
240
+ actions.set(action.key, action);
241
+ },
242
+ unregister(key) {
243
+ actions.delete(key);
244
+ },
245
+ resolve(ctx, actionInterceptors = [], keys) {
246
+ const widgetActions = ctx.meta?.actions;
247
+ let actionDefs = this.getActions();
248
+ if (widgetActions) {
249
+ if (widgetActions.only) {
250
+ const allowedKeys = new Set(widgetActions.only);
251
+ actionDefs = actionDefs.filter((a) => allowedKeys.has(a.key));
252
+ }
253
+ if (widgetActions.exclude) {
254
+ const excludeKeys = new Set(widgetActions.exclude);
255
+ actionDefs = actionDefs.filter((a) => !excludeKeys.has(a.key));
256
+ }
257
+ if (widgetActions.extra) actionDefs = [...actionDefs, ...widgetActions.extra].sort((a, b) => a.order - b.order);
258
+ }
259
+ if (keys) {
260
+ const requestedKeys = new Set(keys);
261
+ actionDefs = actionDefs.filter((action) => requestedKeys.has(action.key));
262
+ }
263
+ return actionDefs.map((def) => {
264
+ if (!(def.visible ? def.visible(ctx) : true)) return null;
265
+ const disabled = !(def.available ? def.available(ctx) : true) || (def.disabled ? def.disabled(ctx) : false);
266
+ const pending = { value: false };
267
+ return {
268
+ key: def.key,
269
+ label: def.label,
270
+ icon: def.icon,
271
+ type: def.type,
272
+ order: def.order,
273
+ risk: def.risk ?? "normal",
274
+ metadata: def.metadata,
275
+ visible: true,
276
+ disabled,
277
+ handler: (event) => {
278
+ if (pending.value) return;
279
+ event.stopPropagation();
280
+ const command = def.command?.(ctx, event) ?? void 0;
281
+ const execute = () => {
282
+ if (command) ctx.engine.execute(command);
283
+ return def.handler?.(ctx, event);
284
+ };
285
+ return runActionPipeline({
286
+ key: def.key,
287
+ label: def.label,
288
+ ctx,
289
+ event,
290
+ risk: def.risk ?? "normal",
291
+ command,
292
+ metadata: def.metadata
293
+ }, execute, actionInterceptors, pending);
294
+ },
295
+ className: def.className
296
+ };
297
+ }).filter((a) => a !== null);
298
+ }
299
+ };
300
+ }
301
+ //#endregion
302
+ //#region src/container-runtime.ts
303
+ function deepFreeze(value) {
304
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
305
+ Object.freeze(value);
306
+ for (const child of Object.values(value)) deepFreeze(child);
307
+ }
308
+ return value;
309
+ }
310
+ function toReadonlySnapshot(value) {
311
+ return Object.isFrozen(value) ? value : deepFreeze(cloneDeep(value));
312
+ }
313
+ const CONTAINER_RUNTIME_CONTEXT_KEY = Symbol("dc-container-runtime");
314
+ function createContainerRuntime(getNode, ctx) {
315
+ const resolveNode = () => {
316
+ ctx.engine.store.schema.value;
317
+ return getNode();
318
+ };
319
+ const plan = computed(() => createContainerPlan(resolveNode(), ctx.engine.registry));
320
+ return {
321
+ nodeId: computed(() => resolveNode().id),
322
+ variant: computed(() => resolveNode().container?.variant ?? ""),
323
+ regionDefinitions: computed(() => deepFreeze(plan.value.ok ? plan.value.plan.variant.regions.map((region) => ({
324
+ ...region,
325
+ constraints: region.constraints ? {
326
+ ...region.constraints,
327
+ includeTypes: region.constraints.includeTypes ? [...region.constraints.includeTypes] : void 0,
328
+ excludeTypes: region.constraints.excludeTypes ? [...region.constraints.excludeTypes] : void 0
329
+ } : void 0
330
+ })) : [])),
331
+ getRegionNodes: (regionId) => toReadonlySnapshot(resolveNode().container?.regions[regionId] ?? []),
332
+ requestVariantChange: (variant) => ctx.engine.execute({
333
+ type: CommandType.CHANGE_CONTAINER_VARIANT,
334
+ payload: {
335
+ containerId: getNode().id,
336
+ variant
337
+ }
338
+ })
339
+ };
340
+ }
341
+ function useContainerRuntime() {
342
+ const runtime = inject(CONTAINER_RUNTIME_CONTEXT_KEY);
343
+ if (!runtime) throw new Error("[dragcraft/renderer] ContainerRuntime not found. ContainerRegionOutlet must be rendered inside a resolved container material.");
344
+ return runtime;
345
+ }
346
+ //#endregion
347
+ //#region src/event-hooks.ts
348
+ /**
349
+ * Returns empty event hooks (no-op). Used as the default.
350
+ */
351
+ function createDefaultEventHooks() {
352
+ return {};
353
+ }
354
+ /**
355
+ * Checks whether a value is a thenable (Promise-like).
356
+ */
357
+ function isPromiseLike(value) {
358
+ return value !== null && typeof value === "object" && typeof value.then === "function";
359
+ }
360
+ /**
361
+ * Resolves a before-hook result that may be sync or async.
362
+ * Returns `true` if the action should proceed, `false` if cancelled.
363
+ *
364
+ * Error handling: if the hook throws or the promise rejects,
365
+ * the action is CANCELLED (returns false) and the error is logged.
366
+ * Rationale: a gating hook that crashes should fail closed, not open.
367
+ */
368
+ async function resolveBeforeHook(result) {
369
+ try {
370
+ return await result !== false;
371
+ } catch (err) {
372
+ console.error("[dragcraft] Before-hook error (action cancelled):", err);
373
+ return false;
374
+ }
375
+ }
376
+ /**
377
+ * Runs an action through an optional before/after hook pair.
378
+ * Handles synchronous and asynchronous before-hooks consistently.
379
+ */
380
+ function runBeforeAfterHook(beforeHook, payload, execute, afterHook, pendingGuard) {
381
+ if (!beforeHook) {
382
+ execute();
383
+ fireAfterHook(afterHook, payload);
384
+ return;
385
+ }
386
+ let result;
387
+ try {
388
+ result = beforeHook(payload);
389
+ } catch (err) {
390
+ console.error("[dragcraft] Before-hook error (action cancelled):", err);
391
+ return;
392
+ }
393
+ if (!isPromiseLike(result)) {
394
+ if (result === false) return;
395
+ execute();
396
+ fireAfterHook(afterHook, payload);
397
+ return;
398
+ }
399
+ if (pendingGuard) pendingGuard.value = true;
400
+ return resolveBeforeHook(result).then((allowed) => {
401
+ if (allowed) {
402
+ execute();
403
+ fireAfterHook(afterHook, payload);
404
+ }
405
+ }).finally(() => {
406
+ if (pendingGuard) pendingGuard.value = false;
407
+ });
408
+ }
409
+ /**
410
+ * Fires an after-hook (fire-and-forget).
411
+ * If the hook returns a promise, any rejection is caught and logged.
412
+ * Errors never propagate to the caller.
413
+ */
414
+ function fireAfterHook(hook, payload) {
415
+ if (!hook) return;
416
+ try {
417
+ const result = hook(payload);
418
+ if (isPromiseLike(result)) result.catch((err) => {
419
+ console.error("[dragcraft] Async after-hook error:", err);
420
+ });
421
+ } catch (err) {
422
+ console.error("[dragcraft] After-hook error:", err);
423
+ }
424
+ }
425
+ //#endregion
426
+ //#region src/types.ts
427
+ /**
428
+ * Injection key for the renderer context.
429
+ */
430
+ const RENDERER_CONTEXT_KEY = Symbol("dc-renderer");
431
+ //#endregion
432
+ //#region src/context.ts
433
+ /**
434
+ * Creates a RendererContext from options.
435
+ * Called internally by RootRenderer.
436
+ */
437
+ function createRendererContext(options) {
438
+ const schema = computed(() => {
439
+ options.engine.store.schema.value;
440
+ return options.engine.state.getSchema();
441
+ });
442
+ const mutableSchema = () => schema.value;
443
+ const layoutPlan = computed(() => createLayoutPlan(mutableSchema(), options.engine.registry));
444
+ const schemaIndex = computed(() => buildSchemaIndex(mutableSchema()));
445
+ const rootActionPositions = computed(() => {
446
+ const positions = /* @__PURE__ */ new Map();
447
+ for (const [sortScope, entries] of layoutPlan.value.sortScopes) {
448
+ const lockedIndices = getLockedIndicesFromNodes(entries.map((entry) => entry.node), options.engine.registry, mutableSchema());
449
+ entries.forEach((entry, index) => positions.set(entry.node.id, {
450
+ index,
451
+ siblingCount: entries.length,
452
+ sortScope,
453
+ lockedIndices
454
+ }));
455
+ }
456
+ for (const entry of layoutPlan.value.entries) if (!positions.has(entry.node.id)) positions.set(entry.node.id, {
457
+ index: entry.arrayIndex,
458
+ siblingCount: layoutPlan.value.entries.length,
459
+ sortScope: false,
460
+ lockedIndices: /* @__PURE__ */ new Set()
461
+ });
462
+ return positions;
463
+ });
464
+ const containerLockedIndices = computed(() => {
465
+ const result = /* @__PURE__ */ new Map();
466
+ for (const container of mutableSchema().root.children ?? []) {
467
+ if (!container.container) continue;
468
+ const regions = /* @__PURE__ */ new Map();
469
+ for (const [regionId, nodes] of Object.entries(container.container.regions)) regions.set(regionId, getLockedIndicesFromNodes(nodes, options.engine.registry, mutableSchema()));
470
+ result.set(container.id, regions);
471
+ }
472
+ return result;
473
+ });
474
+ function resolveNodeActionPosition(node, owner) {
475
+ if (owner.kind === "container") {
476
+ const siblings = (schemaIndex.value.index.get(owner.containerId)?.node)?.container?.regions[owner.regionId] ?? [];
477
+ return {
478
+ owner,
479
+ index: schemaIndex.value.index.get(node.id)?.index ?? -1,
480
+ siblingCount: siblings.length,
481
+ sortScope: false,
482
+ lockedIndices: containerLockedIndices.value.get(owner.containerId)?.get(owner.regionId) ?? /* @__PURE__ */ new Set()
483
+ };
484
+ }
485
+ const position = rootActionPositions.value.get(node.id);
486
+ return {
487
+ owner: {
488
+ kind: "root",
489
+ sortScope: position?.sortScope === false ? void 0 : position?.sortScope
490
+ },
491
+ index: position?.index ?? -1,
492
+ siblingCount: position?.siblingCount ?? 0,
493
+ sortScope: position?.sortScope ?? false,
494
+ lockedIndices: position?.lockedIndices ?? /* @__PURE__ */ new Set()
495
+ };
496
+ }
497
+ return {
498
+ engine: options.engine,
499
+ schema,
500
+ layoutPlan,
501
+ schemaIndex,
502
+ resolveNodeActionPosition,
503
+ componentMap: options.componentMap,
504
+ extensions: options.extensions ?? {},
505
+ eventHooks: options.eventHooks ?? createDefaultEventHooks(),
506
+ actionInterceptors: options.actionInterceptors ?? [],
507
+ actionRegistry: options.actionRegistry ?? createNodeActionRegistry(),
508
+ dragOverNodeId: options.dragOverNodeId ?? ref(null),
509
+ activeDestination: options.activeDestination ?? ref(null),
510
+ containerDropDecision: options.containerDropDecision ?? ref(null),
511
+ onContainerDragOver: options.onContainerDragOver,
512
+ onContainerDragLeave: options.onContainerDragLeave,
513
+ onContainerDrop: options.onContainerDrop,
514
+ interactionBoundary: options.interactionBoundary
515
+ };
516
+ }
517
+ /**
518
+ * Injects the RendererContext from the nearest ancestor RootRenderer.
519
+ * Throws if called outside the renderer component tree.
520
+ */
521
+ function useRendererContext() {
522
+ const ctx = inject(RENDERER_CONTEXT_KEY);
523
+ if (!ctx) throw new Error("[dragcraft/renderer] RendererContext not found. Ensure this component is a descendant of RootRenderer.");
524
+ return ctx;
525
+ }
526
+ //#endregion
527
+ //#region src/components/DefaultDropIndicator.ts
528
+ /**
529
+ * Sort-scope drop indicator shown during drag-over.
530
+ * Renders a dashed-border rectangle placeholder indicating where
531
+ * the widget will be placed. Styled via CSS class `dc-drop-indicator`.
532
+ */
533
+ var DefaultDropIndicator_default = defineComponent({
534
+ name: "DcDefaultDropIndicator",
535
+ setup() {
536
+ return () => h("div", {
537
+ "class": "dc-drop-indicator",
538
+ "data-dc-component": "drop-indicator"
539
+ });
540
+ }
541
+ });
542
+ //#endregion
543
+ //#region src/components/DefaultEmptyState.ts
544
+ /**
545
+ * Default empty canvas state component.
546
+ * Displayed when the canvas has no widgets and no drag operation is active.
547
+ */
548
+ var DefaultEmptyState_default = defineComponent({
549
+ name: "DcDefaultEmptyState",
550
+ props: { isDragOver: {
551
+ type: Boolean,
552
+ default: false
553
+ } },
554
+ setup(props) {
555
+ const { t } = useI18n();
556
+ return () => h("div", {
557
+ "class": ["dc-empty-state", { "dc-empty-state--drag-over": props.isDragOver }],
558
+ "data-dc-component": "empty-state",
559
+ "data-dc-state": props.isDragOver ? "drag-over" : void 0
560
+ }, [h("div", {
561
+ "class": "dc-empty-state__icon",
562
+ "data-dc-part": "icon"
563
+ }, h(IconPlus, { size: 32 })), h("div", {
564
+ "class": "dc-empty-state__text",
565
+ "data-dc-part": "text"
566
+ }, t("canvas.empty", "拖拽组件到这里"))]);
567
+ }
568
+ });
569
+ //#endregion
570
+ //#region src/components/DefaultForbiddenOverlay.ts
571
+ /**
572
+ * Default forbidden overlay shown when a widget type cannot be dropped.
573
+ * Renders a red dashed drop zone with the blocked reason in the center.
574
+ */
575
+ var DefaultForbiddenOverlay_default = defineComponent({
576
+ name: "DcDefaultForbiddenOverlay",
577
+ props: {
578
+ widgetType: {
579
+ type: String,
580
+ required: true
581
+ },
582
+ reason: {
583
+ type: Object,
584
+ default: null
585
+ }
586
+ },
587
+ setup(props) {
588
+ const { t } = useI18n();
589
+ const getMessage = () => {
590
+ const fallback = props.reason?.message ?? t("forbidden.default", "当前物料不满足创建条件,无法添加到画布");
591
+ return props.reason?.messageKey ? t(props.reason.messageKey, fallback) : fallback;
592
+ };
593
+ return () => h("div", {
594
+ "class": "dc-forbidden-overlay",
595
+ "data-dc-component": "forbidden-overlay"
596
+ }, [h("span", {
597
+ "class": "dc-forbidden-overlay__text",
598
+ "data-dc-part": "text"
599
+ }, getMessage())]);
600
+ }
601
+ });
602
+ //#endregion
603
+ //#region src/composables/useNodeActions.ts
604
+ function resolveUncachedPosition(node, owner, ctx) {
605
+ const schema = ctx.schema.value;
606
+ if (owner.kind === "container") {
607
+ const siblings = (buildSchemaIndex(schema).index.get(owner.containerId)?.node)?.container?.regions[owner.regionId] ?? [];
608
+ return {
609
+ owner,
610
+ index: siblings.findIndex((item) => item.id === node.id),
611
+ siblingCount: siblings.length,
612
+ sortScope: false,
613
+ lockedIndices: getLockedIndicesFromNodes(siblings, ctx.engine.registry, schema)
614
+ };
615
+ }
616
+ const layout = resolveNodeLayout(node, ctx.engine.registry, schema);
617
+ const entries = layout.sortScope === false ? [] : getSortScopeEntries(createLayoutPlan(schema, ctx.engine.registry), layout.sortScope);
618
+ return {
619
+ owner: {
620
+ kind: "root",
621
+ sortScope: layout.sortScope === false ? void 0 : layout.sortScope
622
+ },
623
+ index: entries.findIndex((entry) => entry.node.id === node.id),
624
+ siblingCount: entries.length,
625
+ sortScope: layout.sortScope,
626
+ lockedIndices: layout.sortScope === false ? /* @__PURE__ */ new Set() : getLockedIndices(schema.root.children ?? [], ctx.engine.registry, schema, layout.sortScope)
627
+ };
628
+ }
629
+ /**
630
+ * Composable that resolves the action system for a specific node.
631
+ * Provides the list of visible, resolved actions with their handlers.
632
+ *
633
+ * @param getNode - Getter for the current schema node
634
+ * @param ctx - The renderer context
635
+ */
636
+ function useNodeActions(getNode, ctx, getOwner = () => ({ kind: "root" })) {
637
+ const { engine, actionRegistry, actionInterceptors } = ctx;
638
+ const actionContext = computed(() => {
639
+ const node = getNode();
640
+ const schema = ctx.schema.value;
641
+ const owner = getOwner();
642
+ const meta = engine.registry.getWidget(node.type);
643
+ return {
644
+ node,
645
+ ...ctx.resolveNodeActionPosition?.(node, owner) ?? resolveUncachedPosition(node, owner, ctx),
646
+ meta,
647
+ engine,
648
+ schema
649
+ };
650
+ });
651
+ return {
652
+ actions: computed(() => {
653
+ return actionRegistry.resolve(actionContext.value, actionInterceptors);
654
+ }),
655
+ actionContext
656
+ };
657
+ }
658
+ //#endregion
659
+ //#region src/drag-image.ts
660
+ let transparentDragImageEl = null;
661
+ function getTransparentDragImageElement() {
662
+ if (typeof document === "undefined") return null;
663
+ if (transparentDragImageEl && document.body.contains(transparentDragImageEl)) return transparentDragImageEl;
664
+ const element = document.createElement("div");
665
+ element.setAttribute("data-dc-transparent-drag-image", "");
666
+ element.style.position = "fixed";
667
+ element.style.left = "-10000px";
668
+ element.style.top = "-10000px";
669
+ element.style.width = "1px";
670
+ element.style.height = "1px";
671
+ element.style.opacity = "0";
672
+ element.style.pointerEvents = "none";
673
+ document.body.appendChild(element);
674
+ transparentDragImageEl = element;
675
+ return element;
676
+ }
677
+ function hideNativeDragImage(dataTransfer) {
678
+ if (!dataTransfer?.setDragImage) return;
679
+ const element = getTransparentDragImageElement();
680
+ if (element) dataTransfer.setDragImage(element, 0, 0);
681
+ }
682
+ //#endregion
683
+ //#region src/composables/useNodeDrag.ts
684
+ /**
685
+ * Composable that encapsulates drag handle behavior for a widget node.
686
+ * Integrates with event hooks for interceptable drag operations.
687
+ *
688
+ * @param getNode - Getter for the current schema node
689
+ * @param ctx - The renderer context (from useRendererContext)
690
+ */
691
+ function useNodeDrag(getNode, ctx) {
692
+ const { engine, eventHooks } = ctx;
693
+ const handleDragStart = (e) => {
694
+ e.stopPropagation();
695
+ const nodeId = getNode().id;
696
+ if (eventHooks.onBeforeDrag) {
697
+ if (eventHooks.onBeforeDrag({
698
+ nodeId,
699
+ event: e
700
+ }) === false) {
701
+ e.preventDefault();
702
+ return;
703
+ }
704
+ }
705
+ engine.store.setDragTarget({
706
+ sourceNodeId: nodeId,
707
+ widgetType: null
708
+ });
709
+ if (e.dataTransfer) {
710
+ e.dataTransfer.effectAllowed = "move";
711
+ e.dataTransfer.setData("text/plain", nodeId);
712
+ hideNativeDragImage(e.dataTransfer);
713
+ }
714
+ };
715
+ const handleDragEnd = (e) => {
716
+ e.stopPropagation();
717
+ engine.store.setDragTarget(null);
718
+ fireAfterHook(eventHooks.onAfterDrag, {
719
+ nodeId: getNode().id,
720
+ event: e
721
+ });
722
+ };
723
+ return {
724
+ handleDragStart,
725
+ handleDragEnd
726
+ };
727
+ }
728
+ //#endregion
729
+ //#region src/composables/useNodeInteractionGeometry.ts
730
+ const CLIP_OVERFLOW_RE = /auto|scroll|hidden|clip|overlay/;
731
+ const EMPTY_RECT = {
732
+ top: 0,
733
+ right: 0,
734
+ bottom: 0,
735
+ left: 0,
736
+ width: 0,
737
+ height: 0
738
+ };
739
+ function makeRect(top, right, bottom, left) {
740
+ return {
741
+ top,
742
+ right,
743
+ bottom,
744
+ left,
745
+ width: Math.max(0, right - left),
746
+ height: Math.max(0, bottom - top)
747
+ };
748
+ }
749
+ function getEffectiveClipRect(el) {
750
+ let top = 0;
751
+ let bottom = window.innerHeight;
752
+ let left = 0;
753
+ let right = window.innerWidth;
754
+ let current = el.parentElement;
755
+ while (current) {
756
+ const style = getComputedStyle(current);
757
+ const clipsY = CLIP_OVERFLOW_RE.test(style.overflowY);
758
+ const clipsX = CLIP_OVERFLOW_RE.test(style.overflowX);
759
+ if (clipsX || clipsY) {
760
+ const rect = current.getBoundingClientRect();
761
+ if (clipsY) {
762
+ top = Math.max(top, rect.top);
763
+ bottom = Math.min(bottom, rect.bottom);
764
+ }
765
+ if (clipsX) {
766
+ left = Math.max(left, rect.left);
767
+ right = Math.min(right, rect.right);
768
+ }
769
+ }
770
+ current = current.parentElement;
771
+ }
772
+ return {
773
+ top,
774
+ right,
775
+ bottom,
776
+ left
777
+ };
778
+ }
779
+ function resolveTargetElement$1(hostEl, selfTargetSelector) {
780
+ if (hostEl.dataset.dcLayerMode !== "self" || !selfTargetSelector) return hostEl;
781
+ const candidate = hostEl.querySelector(selfTargetSelector);
782
+ if (!candidate) return hostEl;
783
+ const rect = candidate.getBoundingClientRect();
784
+ return rect.width > 0 && rect.height > 0 ? candidate : hostEl;
785
+ }
786
+ function resolveBoundaryElement(hostEl, targetEl, boundarySelector) {
787
+ if (!boundarySelector) return null;
788
+ return hostEl.closest(boundarySelector) ?? targetEl.closest(boundarySelector);
789
+ }
790
+ function resolvePaintInset(size, requestedInset) {
791
+ return Math.min(requestedInset, Math.max(0, (size - 1) / 2));
792
+ }
793
+ function insetRect(rect, requestedInset) {
794
+ const inlineInset = resolvePaintInset(rect.width, requestedInset);
795
+ const blockInset = resolvePaintInset(rect.height, requestedInset);
796
+ return makeRect(rect.top + blockInset, rect.right - inlineInset, rect.bottom - blockInset, rect.left + inlineInset);
797
+ }
798
+ function isSameRect(left, right) {
799
+ return left.top === right.top && left.right === right.right && left.bottom === right.bottom && left.left === right.left;
800
+ }
801
+ function useNodeInteractionGeometry(elRef, isActive, options) {
802
+ const geometry = ref({
803
+ visibleRect: EMPTY_RECT,
804
+ paintRect: EMPTY_RECT,
805
+ visible: false
806
+ });
807
+ let cleanupAutoUpdate = null;
808
+ function applyGeometry(next) {
809
+ const previous = geometry.value;
810
+ if (previous.visible !== next.visible || !isSameRect(previous.visibleRect, next.visibleRect) || !isSameRect(previous.paintRect, next.paintRect)) geometry.value = next;
811
+ }
812
+ function hide() {
813
+ if (geometry.value.visible) applyGeometry({
814
+ ...geometry.value,
815
+ visible: false
816
+ });
817
+ }
818
+ function update() {
819
+ const hostEl = elRef.value;
820
+ if (!hostEl || !isActive.value) {
821
+ hide();
822
+ return;
823
+ }
824
+ const targetEl = resolveTargetElement$1(hostEl, options.selfTargetSelector);
825
+ const targetRect = targetEl.getBoundingClientRect();
826
+ if (targetRect.width <= 0 || targetRect.height <= 0) {
827
+ hide();
828
+ return;
829
+ }
830
+ const boundaryRect = resolveBoundaryElement(hostEl, targetEl, options.boundarySelector)?.getBoundingClientRect();
831
+ const clip = getEffectiveClipRect(targetEl);
832
+ const clipTop = Math.max(clip.top, boundaryRect?.top ?? -Infinity);
833
+ const clipRight = Math.min(clip.right, boundaryRect?.right ?? Infinity);
834
+ const clipBottom = Math.min(clip.bottom, boundaryRect?.bottom ?? Infinity);
835
+ const clipLeft = Math.max(clip.left, boundaryRect?.left ?? -Infinity);
836
+ const visibleTop = Math.max(targetRect.top, clipTop);
837
+ const visibleBottom = Math.min(targetRect.bottom, clipBottom);
838
+ const targetVisibleLeft = Math.max(targetRect.left, clipLeft);
839
+ const targetVisibleRight = Math.min(targetRect.right, clipRight);
840
+ const visibleLeft = options.mode === "root-band" && boundaryRect ? Math.max(boundaryRect.left, clipLeft) : targetVisibleLeft;
841
+ const visibleRect = makeRect(visibleTop, options.mode === "root-band" && boundaryRect ? Math.min(boundaryRect.right, clipRight) : targetVisibleRight, visibleBottom, visibleLeft);
842
+ if (!(visibleBottom > visibleTop && targetVisibleRight > targetVisibleLeft && visibleRect.width > 0 && visibleRect.height > 0)) {
843
+ hide();
844
+ return;
845
+ }
846
+ applyGeometry({
847
+ visibleRect,
848
+ paintRect: insetRect(visibleRect, Math.max(0, options.paintInset ?? 0)),
849
+ visible: true
850
+ });
851
+ }
852
+ function stopAutoUpdate() {
853
+ cleanupAutoUpdate?.();
854
+ cleanupAutoUpdate = null;
855
+ }
856
+ function startAutoUpdate() {
857
+ stopAutoUpdate();
858
+ const host = elRef.value;
859
+ if (!host || !isActive.value) return;
860
+ cleanupAutoUpdate = autoUpdate(resolveTargetElement$1(host, options.selfTargetSelector), host, update, {
861
+ ancestorScroll: true,
862
+ ancestorResize: true,
863
+ elementResize: true,
864
+ layoutShift: true,
865
+ animationFrame: false
866
+ });
867
+ }
868
+ watch([elRef, isActive], ([newEl, active]) => {
869
+ stopAutoUpdate();
870
+ if (newEl && active) nextTick(startAutoUpdate);
871
+ else hide();
872
+ }, {
873
+ immediate: true,
874
+ flush: "post"
875
+ });
876
+ onBeforeUnmount(stopAutoUpdate);
877
+ return {
878
+ geometry,
879
+ update
880
+ };
881
+ }
882
+ //#endregion
883
+ //#region src/selection-presentation.ts
884
+ const NODE_SELECTION_PRESENTATION_KEY = Symbol("dc-node-selection-presentation");
885
+ const NODE_SELECTION_PLANE_KEY = Symbol("dc-node-selection-plane");
886
+ const DETACHED_PLANE = computed(() => null);
887
+ const DETACHED_PRESENTATION = {
888
+ registerPlane: () => {},
889
+ registerFallback: () => {},
890
+ getPlane: () => DETACHED_PLANE
891
+ };
892
+ function createNodeSelectionPresentation() {
893
+ const planes = {
894
+ root: ref(null),
895
+ content: ref(null),
896
+ viewport: ref(null)
897
+ };
898
+ const fallback = ref(null);
899
+ const resolvedPlanes = {
900
+ root: computed(() => planes.root.value ?? fallback.value),
901
+ content: computed(() => planes.content.value ?? fallback.value),
902
+ viewport: computed(() => planes.viewport.value ?? fallback.value)
903
+ };
904
+ return {
905
+ registerPlane(plane, element) {
906
+ planes[plane].value = element;
907
+ },
908
+ registerFallback(element) {
909
+ fallback.value = element;
910
+ },
911
+ getPlane(plane) {
912
+ return resolvedPlanes[plane];
913
+ }
914
+ };
915
+ }
916
+ function useNodeSelectionPresentation() {
917
+ return inject(NODE_SELECTION_PRESENTATION_KEY, DETACHED_PRESENTATION);
918
+ }
919
+ //#endregion
920
+ //#region src/composables/useNodeSelectionProjection.ts
921
+ function resolveTargetElement(host, selfTargetSelector) {
922
+ if (host.dataset.dcLayerMode !== "self" || !selfTargetSelector) return host;
923
+ const candidate = host.querySelector(selfTargetSelector);
924
+ if (!candidate) return host;
925
+ const rect = candidate.getBoundingClientRect();
926
+ return rect.width > 0 && rect.height > 0 ? candidate : host;
927
+ }
928
+ function useNodeSelectionProjection(elRef, isSelected, options) {
929
+ const presentation = useNodeSelectionPresentation();
930
+ const projection = ref(null);
931
+ const target = computed(() => presentation.getPlane(options.plane.value).value);
932
+ let cleanupAutoUpdate = null;
933
+ function update() {
934
+ const host = elRef.value;
935
+ const plane = target.value;
936
+ if (!host || !plane || !isSelected.value) {
937
+ projection.value = null;
938
+ return;
939
+ }
940
+ const targetRect = resolveTargetElement(host, options.selfTargetSelector).getBoundingClientRect();
941
+ const planeRect = plane.getBoundingClientRect();
942
+ if (targetRect.width <= 0 || targetRect.height <= 0 || planeRect.width <= 0) {
943
+ projection.value = null;
944
+ return;
945
+ }
946
+ const materialBounds = {
947
+ top: targetRect.top - planeRect.top,
948
+ left: targetRect.left - planeRect.left,
949
+ width: targetRect.width,
950
+ height: targetRect.height
951
+ };
952
+ const bounds = options.kind === "root-segment" ? {
953
+ top: materialBounds.top,
954
+ left: 0,
955
+ width: planeRect.width,
956
+ height: materialBounds.height
957
+ } : { ...materialBounds };
958
+ projection.value = {
959
+ kind: options.kind,
960
+ plane: options.plane.value,
961
+ materialBounds,
962
+ bounds
963
+ };
964
+ }
965
+ function stopAutoUpdate() {
966
+ cleanupAutoUpdate?.();
967
+ cleanupAutoUpdate = null;
968
+ }
969
+ function startAutoUpdate() {
970
+ stopAutoUpdate();
971
+ const host = elRef.value;
972
+ const plane = target.value;
973
+ if (!host || !plane || !isSelected.value) {
974
+ projection.value = null;
975
+ return;
976
+ }
977
+ cleanupAutoUpdate = autoUpdate(resolveTargetElement(host, options.selfTargetSelector), plane, update, {
978
+ ancestorScroll: options.plane.value === "root",
979
+ ancestorResize: true,
980
+ elementResize: true,
981
+ layoutShift: true,
982
+ animationFrame: false
983
+ });
984
+ }
985
+ watch([
986
+ elRef,
987
+ isSelected,
988
+ target
989
+ ], () => {
990
+ if (!isSelected.value) {
991
+ stopAutoUpdate();
992
+ projection.value = null;
993
+ return;
994
+ }
995
+ nextTick(startAutoUpdate);
996
+ }, {
997
+ immediate: true,
998
+ flush: "post"
999
+ });
1000
+ onBeforeUnmount(stopAutoUpdate);
1001
+ return {
1002
+ projection,
1003
+ target,
1004
+ update
1005
+ };
1006
+ }
1007
+ //#endregion
1008
+ //#region src/composables/useToolbarPosition.ts
1009
+ function useToolbarPosition(referenceRef, floatingRef, isActive, options = {}) {
1010
+ const { gap = 8, interactionBoundary, targetSelector, selfTargetSelector, boundarySelector, padding = 8, interactionGeometry, interactionGeometryUpdate, placement = "left-start", orientation = "vertical" } = options;
1011
+ const position = ref({
1012
+ x: 0,
1013
+ y: 0,
1014
+ placement,
1015
+ orientation,
1016
+ strategy: "fixed",
1017
+ visible: false
1018
+ });
1019
+ let cleanupAutoUpdate = null;
1020
+ function resolveReference(host) {
1021
+ const selector = targetSelector ?? (host.dataset.dcLayerMode === "self" ? selfTargetSelector : void 0);
1022
+ if (!selector) return host;
1023
+ const candidate = host.querySelector(selector);
1024
+ if (!candidate) return host;
1025
+ const rect = candidate.getBoundingClientRect();
1026
+ return rect.width > 0 || rect.height > 0 ? candidate : host;
1027
+ }
1028
+ function isReferenceVisible(reference, boundary) {
1029
+ const rect = reference.getBoundingClientRect();
1030
+ const clip = boundary?.getBoundingClientRect() ?? {
1031
+ top: 0,
1032
+ right: window.innerWidth,
1033
+ bottom: window.innerHeight,
1034
+ left: 0
1035
+ };
1036
+ return rect.bottom > clip.top && rect.top < clip.bottom && rect.right > clip.left && rect.left < clip.right;
1037
+ }
1038
+ function resolveToolbarBoundary(host, reference) {
1039
+ if (!boundarySelector) return null;
1040
+ return host.closest(boundarySelector) ?? reference.closest(boundarySelector);
1041
+ }
1042
+ function applyPosition(next) {
1043
+ const current = position.value;
1044
+ if (current.x === next.x && current.y === next.y && current.placement === next.placement && current.orientation === next.orientation && current.strategy === next.strategy && current.visible === next.visible) return;
1045
+ position.value = next;
1046
+ }
1047
+ async function update() {
1048
+ const host = referenceRef.value;
1049
+ const floating = floatingRef.value;
1050
+ if (!host || !floating || !isActive.value) {
1051
+ applyPosition({
1052
+ ...position.value,
1053
+ visible: false
1054
+ });
1055
+ return;
1056
+ }
1057
+ const reference = resolveReference(host);
1058
+ interactionGeometryUpdate?.();
1059
+ const boundaryElement = interactionBoundary?.value ?? null;
1060
+ const interactionRect = interactionGeometry?.value.visibleRect;
1061
+ if (!(interactionGeometry ? interactionGeometry.value.visible : isReferenceVisible(reference, boundaryElement))) {
1062
+ applyPosition({
1063
+ ...position.value,
1064
+ visible: false
1065
+ });
1066
+ return;
1067
+ }
1068
+ const referenceRect = interactionRect ?? reference.getBoundingClientRect();
1069
+ const floatingRect = floating.getBoundingClientRect();
1070
+ const clipRect = boundaryElement?.getBoundingClientRect() ?? {
1071
+ top: 0,
1072
+ right: window.innerWidth,
1073
+ bottom: window.innerHeight,
1074
+ left: 0
1075
+ };
1076
+ const availableHeight = Math.max(0, clipRect.bottom - clipRect.top - padding * 2);
1077
+ const availableWidth = Math.max(0, clipRect.right - clipRect.left - padding * 2);
1078
+ const minY = clipRect.top + padding;
1079
+ const maxY = Math.max(minY, clipRect.bottom - padding - floatingRect.height);
1080
+ floating.style.maxHeight = `${availableHeight}px`;
1081
+ if (placement === "top-end") {
1082
+ floating.style.maxWidth = `${availableWidth}px`;
1083
+ const minX = clipRect.left + padding;
1084
+ const maxX = Math.max(minX, clipRect.right - padding - floatingRect.width);
1085
+ const topY = referenceRect.top - gap - floatingRect.height;
1086
+ const bottomY = referenceRect.bottom + gap;
1087
+ const resolvedPlacement = topY >= minY || !(bottomY <= maxY) ? "top-end" : "bottom-end";
1088
+ const requestedY = resolvedPlacement === "top-end" ? topY : bottomY;
1089
+ applyPosition({
1090
+ x: Math.min(Math.max(referenceRect.right - floatingRect.width, minX), maxX),
1091
+ y: Math.min(Math.max(requestedY, minY), maxY),
1092
+ placement: resolvedPlacement,
1093
+ orientation,
1094
+ strategy: "fixed",
1095
+ visible: true
1096
+ });
1097
+ return;
1098
+ }
1099
+ floating.style.maxWidth = "";
1100
+ applyPosition({
1101
+ x: (resolveToolbarBoundary(host, reference)?.getBoundingClientRect() ?? referenceRect).left - gap - floatingRect.width,
1102
+ y: Math.min(Math.max(referenceRect.top, minY), maxY),
1103
+ placement: "left-start",
1104
+ orientation,
1105
+ strategy: "fixed",
1106
+ visible: true
1107
+ });
1108
+ }
1109
+ function stopAutoUpdate() {
1110
+ cleanupAutoUpdate?.();
1111
+ cleanupAutoUpdate = null;
1112
+ }
1113
+ function startAutoUpdate() {
1114
+ stopAutoUpdate();
1115
+ const host = referenceRef.value;
1116
+ const floating = floatingRef.value;
1117
+ if (!host || !floating || !isActive.value) return;
1118
+ cleanupAutoUpdate = autoUpdate(resolveReference(host), floating, update, {
1119
+ animationFrame: false,
1120
+ ancestorResize: true,
1121
+ ancestorScroll: true,
1122
+ elementResize: true,
1123
+ layoutShift: true
1124
+ });
1125
+ }
1126
+ watch([
1127
+ referenceRef,
1128
+ floatingRef,
1129
+ isActive
1130
+ ], () => {
1131
+ if (isActive.value) startAutoUpdate();
1132
+ else {
1133
+ stopAutoUpdate();
1134
+ applyPosition({
1135
+ ...position.value,
1136
+ visible: false
1137
+ });
1138
+ }
1139
+ }, {
1140
+ immediate: true,
1141
+ flush: "post"
1142
+ });
1143
+ onBeforeUnmount(stopAutoUpdate);
1144
+ return {
1145
+ position,
1146
+ update
1147
+ };
1148
+ }
1149
+ //#endregion
1150
+ //#region src/composables/useNodeState.ts
1151
+ /**
1152
+ * Computes reactive interaction state for a single schema node.
1153
+ *
1154
+ * @param getNodeId - Getter function returning the node ID (for reactivity safety)
1155
+ * @param ctx - The renderer context (from useRendererContext)
1156
+ */
1157
+ function useNodeState(getNodeId, ctx) {
1158
+ const { engine, dragOverNodeId } = ctx;
1159
+ const isSelected = computed(() => engine.store.selectedNodeId.value === getNodeId());
1160
+ const isHovered = computed(() => engine.store.hoveredNodeId.value === getNodeId());
1161
+ const isDragOver = computed(() => dragOverNodeId.value === getNodeId());
1162
+ return {
1163
+ isSelected,
1164
+ isHovered,
1165
+ isDragOver,
1166
+ interactionClasses: computed(() => ({
1167
+ "dc-node--selected": isSelected.value,
1168
+ "dc-node--hovered": isHovered.value,
1169
+ "dc-node--drag-over": isDragOver.value
1170
+ }))
1171
+ };
1172
+ }
1173
+ //#endregion
1174
+ //#region src/composables/useWidgetNode.ts
1175
+ /**
1176
+ * Composable that extracts all widget node state and event handling logic.
1177
+ * This is the primary composable for building custom node renderers.
1178
+ *
1179
+ * @param getNode - Getter for the current schema node
1180
+ * @param ctx - The renderer context (from useRendererContext)
1181
+ */
1182
+ function useWidgetNode(getNode, ctx) {
1183
+ const { engine, componentMap, eventHooks } = ctx;
1184
+ const state = useNodeState(() => getNode().id, ctx);
1185
+ const meta = computed(() => engine.registry.getWidget(getNode().type));
1186
+ const resolvedComponent = computed(() => componentMap[getNode().type]);
1187
+ const layout = computed(() => resolveNodeLayout(getNode(), engine.registry, ctx.schema.value));
1188
+ const inSortScope = computed(() => layout.value.sortScope !== false);
1189
+ const isDragging = computed(() => engine.store.dragTarget.value?.sourceNodeId === getNode().id);
1190
+ const visible = computed(() => layout.value.visible);
1191
+ function readInstanceCtx() {
1192
+ return {
1193
+ node: getNode(),
1194
+ schema: ctx.schema.value
1195
+ };
1196
+ }
1197
+ function resolveMetaBehavior(field) {
1198
+ if (typeof field !== "function") return field !== false;
1199
+ return field(readInstanceCtx());
1200
+ }
1201
+ const useMask = computed(() => resolveMetaBehavior(meta.value?.mask));
1202
+ const selectable = computed(() => resolveMetaBehavior(meta.value?.selectable));
1203
+ const sortable = computed(() => resolveMetaBehavior(meta.value?.sortable));
1204
+ const draggable = computed(() => {
1205
+ if (!inSortScope.value || !sortable.value) return false;
1206
+ return resolveMetaBehavior(meta.value?.draggable);
1207
+ });
1208
+ const wrapperClasses = computed(() => [
1209
+ "dc-node",
1210
+ "dc-node--widget",
1211
+ {
1212
+ "dc-node--masked": useMask.value,
1213
+ "dc-node--unmasked": !useMask.value,
1214
+ "dc-node--non-selectable": !selectable.value,
1215
+ "dc-node--locked": inSortScope.value && !sortable.value,
1216
+ "dc-node--unsorted": !inSortScope.value,
1217
+ "dc-node--dragging": isDragging.value,
1218
+ "dc-node--hidden": !visible.value
1219
+ },
1220
+ state.interactionClasses.value
1221
+ ]);
1222
+ const selectPending = { value: false };
1223
+ const handleSelect = (e) => {
1224
+ if (!selectable.value || selectPending.value) return;
1225
+ e.stopPropagation();
1226
+ const nodeId = getNode().id;
1227
+ runBeforeAfterHook(eventHooks.onBeforeSelect, {
1228
+ nodeId,
1229
+ event: e
1230
+ }, () => engine.store.selectNode(nodeId), eventHooks.onAfterSelect ? () => eventHooks.onAfterSelect?.({ nodeId }) : void 0, selectPending);
1231
+ };
1232
+ const handleMouseEnter = () => {
1233
+ const nodeId = getNode().id;
1234
+ if (engine.store.hoveredNodeId.value === nodeId) return;
1235
+ engine.store.hoverNode(nodeId);
1236
+ eventHooks.onHoverChange?.({ nodeId });
1237
+ };
1238
+ const handleMouseLeave = () => {
1239
+ if (engine.store.hoveredNodeId.value !== getNode().id) return;
1240
+ engine.store.hoverNode(null);
1241
+ eventHooks.onHoverChange?.({ nodeId: null });
1242
+ };
1243
+ return {
1244
+ state,
1245
+ resolvedComponent,
1246
+ meta,
1247
+ useMask,
1248
+ selectable,
1249
+ draggable,
1250
+ sortable,
1251
+ inSortScope,
1252
+ isDragging,
1253
+ visible,
1254
+ layout,
1255
+ wrapperClasses,
1256
+ handleSelect,
1257
+ handleMouseEnter,
1258
+ handleMouseLeave
1259
+ };
1260
+ }
1261
+ //#endregion
1262
+ //#region src/node-interaction.ts
1263
+ function resolveNodeInteractionPresentation(owner) {
1264
+ return owner.kind === "container" ? {
1265
+ geometryMode: "node-box",
1266
+ selectionKind: "material-bounds",
1267
+ toolbarPlacement: "top-end",
1268
+ toolbarOrientation: "horizontal"
1269
+ } : {
1270
+ geometryMode: "root-band",
1271
+ selectionKind: "root-segment",
1272
+ toolbarPlacement: "left-start",
1273
+ toolbarOrientation: "vertical"
1274
+ };
1275
+ }
1276
+ //#endregion
1277
+ //#region src/widget-runtime.ts
1278
+ const WIDGET_RUNTIME_CONTEXT_KEY = Symbol("dc-widget-runtime");
1279
+ function createWidgetRuntimeContext(getNode) {
1280
+ const ctx = useRendererContext();
1281
+ const nodeId = computed(() => getNode().id);
1282
+ const nodeType = computed(() => getNode().type);
1283
+ function updateProps(patch) {
1284
+ ctx.engine.execute({
1285
+ type: CommandType.UPDATE_PROPS,
1286
+ payload: {
1287
+ nodeId: nodeId.value,
1288
+ props: patch
1289
+ }
1290
+ });
1291
+ }
1292
+ function updateStyle(patch) {
1293
+ ctx.engine.execute({
1294
+ type: CommandType.UPDATE_PROPS,
1295
+ payload: {
1296
+ nodeId: nodeId.value,
1297
+ props: {},
1298
+ style: patch
1299
+ }
1300
+ });
1301
+ }
1302
+ return {
1303
+ nodeId,
1304
+ nodeType,
1305
+ updateProps,
1306
+ updateStyle,
1307
+ updateContainerStyle: (patch) => updateStyle({ container: patch }),
1308
+ updateContentStyle: (patch) => updateStyle({ content: patch })
1309
+ };
1310
+ }
1311
+ function useWidgetRuntime() {
1312
+ const ctx = inject(WIDGET_RUNTIME_CONTEXT_KEY);
1313
+ if (!ctx) throw new Error("[dragcraft/renderer] WidgetRuntimeContext not found. Ensure this component is rendered by WidgetRenderer.");
1314
+ return ctx;
1315
+ }
1316
+ //#endregion
1317
+ //#region src/components/DefaultContainerFallback.ts
1318
+ var DefaultContainerFallback_default = defineComponent({
1319
+ name: "DcDefaultContainerFallback",
1320
+ props: { node: {
1321
+ type: Object,
1322
+ required: true
1323
+ } },
1324
+ setup(props) {
1325
+ return () => h("div", {
1326
+ "class": "dc-unresolved-container",
1327
+ "data-dc-component": "unresolved-container",
1328
+ "data-dc-unresolved-container": props.node.id
1329
+ }, Object.entries(props.node.container?.regions ?? {}).map(([regionId, nodes]) => {
1330
+ const owner = {
1331
+ kind: "container",
1332
+ containerId: props.node.id,
1333
+ regionId
1334
+ };
1335
+ return h("div", {
1336
+ "key": regionId,
1337
+ "class": "dc-container-region dc-container-region--unresolved",
1338
+ "data-dc-component": "container-region",
1339
+ "data-dc-state": "unresolved",
1340
+ "data-dc-container-id": props.node.id,
1341
+ "data-dc-container-region": regionId,
1342
+ "role": "group",
1343
+ "aria-label": regionId
1344
+ }, nodes.map((node) => h(WidgetRenderer_default, {
1345
+ key: node.id,
1346
+ node,
1347
+ owner
1348
+ })));
1349
+ }));
1350
+ }
1351
+ });
1352
+ //#endregion
1353
+ //#region src/components/DefaultNodeHandle.ts
1354
+ /**
1355
+ * Default semantic selection handle for unmasked widgets and resolved containers.
1356
+ */
1357
+ var DefaultNodeHandle_default = defineComponent({
1358
+ name: "DcDefaultNodeHandle",
1359
+ props: {
1360
+ nodeId: {
1361
+ type: String,
1362
+ required: true
1363
+ },
1364
+ nodeType: {
1365
+ type: String,
1366
+ required: true
1367
+ },
1368
+ owner: {
1369
+ type: Object,
1370
+ required: true
1371
+ },
1372
+ onSelect: {
1373
+ type: Function,
1374
+ required: true
1375
+ }
1376
+ },
1377
+ setup(props) {
1378
+ const { t } = useI18n();
1379
+ return () => {
1380
+ const label = t("canvas.node-handle", "选中组件");
1381
+ return h("button", {
1382
+ "type": "button",
1383
+ "class": "dc-node__handle",
1384
+ "data-dc-component": "node-handle",
1385
+ "onClick": props.onSelect,
1386
+ "title": label,
1387
+ "aria-label": label
1388
+ }, [h("span", {
1389
+ "class": "dc-node__handle-surface",
1390
+ "data-dc-part": "surface"
1391
+ }, [h("span", {
1392
+ "class": "dc-node__handle-icon",
1393
+ "data-dc-part": "icon",
1394
+ "aria-hidden": "true"
1395
+ }, [h(IconComponent, { size: 12 })])])]);
1396
+ };
1397
+ }
1398
+ });
1399
+ //#endregion
1400
+ //#region src/components/DefaultNodeMask.ts
1401
+ /**
1402
+ * Default mask overlay component for widgets with mask=true.
1403
+ * Renders a transparent overlay that blocks widget interaction
1404
+ * and captures clicks for selection.
1405
+ */
1406
+ var DefaultNodeMask_default = defineComponent({
1407
+ name: "DcDefaultNodeMask",
1408
+ props: {
1409
+ nodeId: {
1410
+ type: String,
1411
+ required: true
1412
+ },
1413
+ nodeType: {
1414
+ type: String,
1415
+ required: true
1416
+ },
1417
+ owner: {
1418
+ type: Object,
1419
+ required: true
1420
+ },
1421
+ onSelect: {
1422
+ type: Function,
1423
+ required: true
1424
+ }
1425
+ },
1426
+ setup(props) {
1427
+ return () => h("div", {
1428
+ class: "dc-node__mask",
1429
+ onClick: props.onSelect
1430
+ });
1431
+ }
1432
+ });
1433
+ //#endregion
1434
+ //#region src/components/DefaultNodeSelection.ts
1435
+ var DefaultNodeSelection_default = defineComponent({
1436
+ name: "DcDefaultNodeSelection",
1437
+ props: {
1438
+ nodeId: {
1439
+ type: String,
1440
+ required: true
1441
+ },
1442
+ nodeType: {
1443
+ type: String,
1444
+ required: true
1445
+ },
1446
+ owner: {
1447
+ type: Object,
1448
+ required: true
1449
+ },
1450
+ projection: {
1451
+ type: Object,
1452
+ required: true
1453
+ }
1454
+ },
1455
+ setup(props) {
1456
+ return () => h("div", {
1457
+ "class": "dc-node__selection-outline",
1458
+ "data-dc-component": "node-selection",
1459
+ "data-dc-state": props.projection.kind,
1460
+ "aria-hidden": "true"
1461
+ }, props.projection.kind === "root-segment" ? [
1462
+ h("span", {
1463
+ "class": "dc-node__selection-edge dc-node__selection-edge--block-start",
1464
+ "data-dc-part": "block-start-edge"
1465
+ }),
1466
+ h("span", {
1467
+ "class": "dc-node__selection-edge dc-node__selection-edge--inline-end",
1468
+ "data-dc-part": "inline-end-edge"
1469
+ }),
1470
+ h("span", {
1471
+ "class": "dc-node__selection-edge dc-node__selection-edge--block-end",
1472
+ "data-dc-part": "block-end-edge"
1473
+ }),
1474
+ h("span", {
1475
+ "class": "dc-node__selection-edge dc-node__selection-edge--inline-start",
1476
+ "data-dc-part": "inline-start-edge"
1477
+ })
1478
+ ] : void 0);
1479
+ }
1480
+ });
1481
+ //#endregion
1482
+ //#region src/components/DefaultNodeToolbar.ts
1483
+ /**
1484
+ * Default per-node floating toolbar component.
1485
+ * Renders actions based on the resolved action list from the action registry.
1486
+ * Supports both 'button' and 'drag-handle' action types.
1487
+ *
1488
+ * Positioning is owned by WidgetRenderer's measurable floating wrapper. The
1489
+ * resolved placement controls whether actions use a vertical or horizontal row.
1490
+ */
1491
+ var DefaultNodeToolbar_default = defineComponent({
1492
+ name: "DcDefaultNodeToolbar",
1493
+ props: {
1494
+ nodeId: {
1495
+ type: String,
1496
+ required: true
1497
+ },
1498
+ nodeType: {
1499
+ type: String,
1500
+ required: true
1501
+ },
1502
+ owner: {
1503
+ type: Object,
1504
+ required: true
1505
+ },
1506
+ actions: {
1507
+ type: Array,
1508
+ required: true
1509
+ },
1510
+ state: {
1511
+ type: Object,
1512
+ required: true
1513
+ },
1514
+ toolbarPosition: {
1515
+ type: Object,
1516
+ default: void 0
1517
+ },
1518
+ onDragStart: {
1519
+ type: Function,
1520
+ required: true
1521
+ },
1522
+ onDragEnd: {
1523
+ type: Function,
1524
+ required: true
1525
+ }
1526
+ },
1527
+ setup(props) {
1528
+ return () => {
1529
+ const pos = props.toolbarPosition;
1530
+ const useFixed = pos != null;
1531
+ const actionVNodes = props.actions.map((action) => {
1532
+ if (action.type === "drag-handle") return h("div", {
1533
+ "class": [
1534
+ "dc-node__toolbar-btn",
1535
+ "dc-node__toolbar-btn--drag",
1536
+ { "dc-node__toolbar-btn--disabled": action.disabled },
1537
+ action.className
1538
+ ],
1539
+ "data-dc-part": "action",
1540
+ "data-dc-state": ["drag", action.disabled ? "disabled" : null].filter(Boolean).join(" "),
1541
+ "title": action.label,
1542
+ "aria-disabled": action.disabled ? "true" : void 0,
1543
+ "draggable": !action.disabled,
1544
+ "onDragstart": action.disabled ? void 0 : props.onDragStart,
1545
+ "onDragend": action.disabled ? void 0 : props.onDragEnd
1546
+ }, typeof action.icon === "string" ? action.icon : action.icon ? h(action.icon) : void 0);
1547
+ return h("button", {
1548
+ "type": "button",
1549
+ "class": ["dc-node__toolbar-btn", action.className],
1550
+ "data-dc-part": "action",
1551
+ "data-dc-state": action.key === "delete" ? "danger" : void 0,
1552
+ "title": action.label,
1553
+ "disabled": action.disabled,
1554
+ "onClick": action.handler
1555
+ }, typeof action.icon === "string" ? action.icon : action.icon ? h(action.icon) : void 0);
1556
+ });
1557
+ return h("div", {
1558
+ "class": ["dc-node__toolbar", {
1559
+ "dc-node__toolbar--floating": useFixed,
1560
+ [`dc-node__toolbar--${pos?.orientation ?? "vertical"}`]: true
1561
+ }],
1562
+ "data-dc-component": "node-toolbar",
1563
+ "data-dc-state": [useFixed ? "floating" : null, pos?.orientation ?? "vertical"].filter(Boolean).join(" "),
1564
+ "data-placement": pos?.placement,
1565
+ "data-orientation": pos?.orientation
1566
+ }, actionVNodes);
1567
+ };
1568
+ }
1569
+ });
1570
+ //#endregion
1571
+ //#region src/components/DefaultWidgetFallback.ts
1572
+ var DefaultWidgetFallback_default = defineComponent({
1573
+ name: "DcDefaultWidgetFallback",
1574
+ props: {
1575
+ nodeId: {
1576
+ type: String,
1577
+ required: true
1578
+ },
1579
+ nodeType: {
1580
+ type: String,
1581
+ default: "unknown"
1582
+ }
1583
+ },
1584
+ setup(props) {
1585
+ return () => h("div", {
1586
+ "class": "dc-widget-fallback",
1587
+ "data-dc-component": "widget-fallback"
1588
+ }, `Unknown widget: ${props.nodeType}`);
1589
+ }
1590
+ });
1591
+ //#endregion
1592
+ //#region src/components/WidgetRenderer.ts
1593
+ const NODE_SURFACE_SELECTOR = "[data-dc-node-surface]";
1594
+ const TOOLBAR_BOUNDARY_SELECTOR = "[data-dc-toolbar-boundary]";
1595
+ const OVERLAY_BOUNDARY_SELECTOR = "[data-dc-overlay-boundary]";
1596
+ const CANVAS_INTERACTION_LAYER_SELECTOR = "[data-dc-canvas-interaction-layer]";
1597
+ const ContainerRuntimeProvider = defineComponent({
1598
+ name: "DcContainerRuntimeProvider",
1599
+ props: { runtime: {
1600
+ type: Object,
1601
+ required: true
1602
+ } },
1603
+ setup(props, { slots }) {
1604
+ provide(CONTAINER_RUNTIME_CONTEXT_KEY, props.runtime);
1605
+ return () => slots.default?.();
1606
+ }
1607
+ });
1608
+ function resolveInteractionLayerTarget(host) {
1609
+ if (typeof document === "undefined") return "body";
1610
+ return host?.closest(".dc-canvas")?.querySelector(CANVAS_INTERACTION_LAYER_SELECTOR) ?? "body";
1611
+ }
1612
+ /**
1613
+ * WidgetRenderer — thin orchestration layer.
1614
+ *
1615
+ * Delegates all logic to composables (useWidgetNode, useNodeActions, useNodeDrag)
1616
+ * and renders via configurable extension components (nodeMask, nodeHandle,
1617
+ * nodeToolbar, widgetFallback, nodeWrapper).
1618
+ */
1619
+ var WidgetRenderer_default = defineComponent({
1620
+ name: "DcWidgetRenderer",
1621
+ props: {
1622
+ node: {
1623
+ type: Object,
1624
+ required: true
1625
+ },
1626
+ owner: {
1627
+ type: Object,
1628
+ default: () => ({ kind: "root" })
1629
+ },
1630
+ selectionPlane: {
1631
+ type: String,
1632
+ default: void 0
1633
+ }
1634
+ },
1635
+ setup(props) {
1636
+ const ctx = useRendererContext();
1637
+ const { extensions } = ctx;
1638
+ provide(WIDGET_RUNTIME_CONTEXT_KEY, createWidgetRuntimeContext(() => props.node));
1639
+ const containerRuntime = createContainerRuntime(() => props.node, ctx);
1640
+ const widget = useWidgetNode(() => props.node, ctx);
1641
+ const { actions } = useNodeActions(() => props.node, ctx, () => props.owner);
1642
+ const drag = useNodeDrag(() => props.node, ctx);
1643
+ const interactionPresentation = resolveNodeInteractionPresentation(props.owner);
1644
+ const inheritedSelectionPlane = inject(NODE_SELECTION_PLANE_KEY, ref("content"));
1645
+ const subtreeSelectionPlane = computed(() => props.selectionPlane ?? inheritedSelectionPlane.value);
1646
+ const projectionPlane = computed(() => props.owner.kind === "root" ? "root" : subtreeSelectionPlane.value);
1647
+ provide(NODE_SELECTION_PLANE_KEY, subtreeSelectionPlane);
1648
+ const containerPlan = computed(() => props.node.container ? createContainerPlan(props.node, ctx.engine.registry) : null);
1649
+ const isResolvedContainer = computed(() => {
1650
+ const plan = containerPlan.value;
1651
+ if (plan?.ok !== true || !widget.resolvedComponent.value) return false;
1652
+ const declaredRegionIds = new Set(plan.plan.variant.regions.map((region) => region.id));
1653
+ return Object.keys(props.node.container?.regions ?? {}).every((regionId) => declaredRegionIds.has(regionId));
1654
+ });
1655
+ const isSelfPositionedLayer = computed(() => {
1656
+ if (props.owner.kind === "container") return false;
1657
+ const placement = widget.layout.value.placement;
1658
+ return placement.kind === "layer" && placement.mode === "self";
1659
+ });
1660
+ const usesBlockingMask = computed(() => widget.useMask.value && !isSelfPositionedLayer.value && !isResolvedContainer.value);
1661
+ const usesSelectionHandle = computed(() => !usesBlockingMask.value && widget.selectable.value && !isSelfPositionedLayer.value);
1662
+ const nodeElRef = ref(null);
1663
+ const toolbarElRef = ref(null);
1664
+ const handleAnchorElRef = ref(null);
1665
+ const isExternalHandleActive = computed(() => isResolvedContainer.value && usesSelectionHandle.value && !widget.state.isSelected.value);
1666
+ const { geometry: interactionGeometry, update: updateInteractionGeometry } = useNodeInteractionGeometry(nodeElRef, widget.state.isSelected, {
1667
+ mode: interactionPresentation.geometryMode,
1668
+ boundarySelector: OVERLAY_BOUNDARY_SELECTOR,
1669
+ selfTargetSelector: NODE_SURFACE_SELECTOR
1670
+ });
1671
+ const { projection: selectionProjection, target: selectionTarget } = useNodeSelectionProjection(nodeElRef, widget.state.isSelected, {
1672
+ kind: interactionPresentation.selectionKind,
1673
+ plane: projectionPlane,
1674
+ selfTargetSelector: NODE_SURFACE_SELECTOR
1675
+ });
1676
+ const { position: toolbarPosition } = useToolbarPosition(nodeElRef, toolbarElRef, widget.state.isSelected, {
1677
+ interactionBoundary: ctx.interactionBoundary,
1678
+ interactionGeometry,
1679
+ interactionGeometryUpdate: updateInteractionGeometry,
1680
+ selfTargetSelector: NODE_SURFACE_SELECTOR,
1681
+ boundarySelector: TOOLBAR_BOUNDARY_SELECTOR,
1682
+ placement: interactionPresentation.toolbarPlacement,
1683
+ orientation: interactionPresentation.toolbarOrientation
1684
+ });
1685
+ const { geometry: handleGeometry, update: updateHandleGeometry } = useNodeInteractionGeometry(nodeElRef, isExternalHandleActive, {
1686
+ mode: "node-box",
1687
+ boundarySelector: OVERLAY_BOUNDARY_SELECTOR
1688
+ });
1689
+ const { position: handlePosition } = useToolbarPosition(nodeElRef, handleAnchorElRef, isExternalHandleActive, {
1690
+ interactionBoundary: ctx.interactionBoundary,
1691
+ interactionGeometry: handleGeometry,
1692
+ interactionGeometryUpdate: updateHandleGeometry,
1693
+ boundarySelector: TOOLBAR_BOUNDARY_SELECTOR,
1694
+ placement: "left-start",
1695
+ orientation: "vertical"
1696
+ });
1697
+ function isDirectNodeHit(event) {
1698
+ const target = event.target;
1699
+ return target instanceof Element && target.closest("[data-node-id]") === event.currentTarget;
1700
+ }
1701
+ function handleDirectSelect(event) {
1702
+ if (isDirectNodeHit(event)) widget.handleSelect(event);
1703
+ }
1704
+ function handleMouseOver(event) {
1705
+ if (isDirectNodeHit(event)) widget.handleMouseEnter();
1706
+ }
1707
+ return () => {
1708
+ ctx.engine.store.schema.value;
1709
+ const node = props.node;
1710
+ const interactionLayerTarget = resolveInteractionLayerTarget(nodeElRef.value);
1711
+ const placement = widget.layout.value.placement;
1712
+ const isContainerOwned = props.owner.kind === "container";
1713
+ const ownerKind = isContainerOwned ? "container" : "root";
1714
+ const resolvedContainer = isResolvedContainer.value;
1715
+ const NodeMask = extensions.nodeMask ?? DefaultNodeMask_default;
1716
+ const NodeHandle = extensions.nodeHandle ?? DefaultNodeHandle_default;
1717
+ const NodeToolbar = extensions.nodeToolbar ?? DefaultNodeToolbar_default;
1718
+ const NodeSelection = extensions.nodeSelection ?? DefaultNodeSelection_default;
1719
+ const WidgetFallback = extensions.widgetFallback ?? DefaultWidgetFallback_default;
1720
+ const NodeWrapper = widget.meta.value?.wrapper ?? extensions.nodeWrapper;
1721
+ const widgetProps = { ...node.props };
1722
+ const wrapperStyle = normalizeStyleValueMap(node.style?.container);
1723
+ let contentStyle = normalizeStyleValueMap(node.style?.content);
1724
+ if (usesBlockingMask.value) {
1725
+ contentStyle = contentStyle ?? {};
1726
+ contentStyle.pointerEvents = "none";
1727
+ }
1728
+ let innerContent;
1729
+ if (node.container && !resolvedContainer) innerContent = h(DefaultContainerFallback_default, { node });
1730
+ else if (widget.resolvedComponent.value) {
1731
+ const material = h(widget.resolvedComponent.value, {
1732
+ ...widgetProps,
1733
+ "style": contentStyle,
1734
+ "data-dc-node-surface": ""
1735
+ });
1736
+ innerContent = resolvedContainer ? h(ContainerRuntimeProvider, { runtime: containerRuntime }, { default: () => material }) : material;
1737
+ } else innerContent = h(WidgetFallback, {
1738
+ "nodeId": node.id,
1739
+ "nodeType": node.type,
1740
+ "data-dc-node-surface": ""
1741
+ });
1742
+ const wrapperChildren = [innerContent];
1743
+ if (selectionProjection.value && selectionTarget.value) {
1744
+ const projection = selectionProjection.value;
1745
+ wrapperChildren.push(h(Teleport, { to: selectionTarget.value }, [h("div", {
1746
+ "class": [
1747
+ "dc-node__selection-projection",
1748
+ `dc-node__selection-projection--${projection.kind}`,
1749
+ `dc-node__selection-projection--${ownerKind}-owned`
1750
+ ],
1751
+ "data-node-id": node.id,
1752
+ "data-node-type": node.type,
1753
+ "data-dc-selection-plane": projection.plane,
1754
+ "style": {
1755
+ top: `${projection.bounds.top}px`,
1756
+ left: `${projection.bounds.left}px`,
1757
+ width: `${projection.bounds.width}px`,
1758
+ height: `${projection.bounds.height}px`
1759
+ }
1760
+ }, [h(NodeSelection, {
1761
+ nodeId: node.id,
1762
+ nodeType: node.type,
1763
+ owner: props.owner,
1764
+ projection
1765
+ })])]));
1766
+ }
1767
+ if (usesBlockingMask.value) wrapperChildren.push(h(NodeMask, {
1768
+ nodeId: node.id,
1769
+ nodeType: node.type,
1770
+ owner: props.owner,
1771
+ onSelect: widget.handleSelect
1772
+ }));
1773
+ if (usesSelectionHandle.value && !widget.state.isSelected.value) {
1774
+ const handleVNode = h(NodeHandle, {
1775
+ nodeId: node.id,
1776
+ nodeType: node.type,
1777
+ owner: props.owner,
1778
+ onSelect: widget.handleSelect
1779
+ });
1780
+ if (resolvedContainer) {
1781
+ const position = handlePosition.value;
1782
+ wrapperChildren.push(h(Teleport, { to: interactionLayerTarget }, [h("div", {
1783
+ "ref": handleAnchorElRef,
1784
+ "class": ["dc-node__handle-anchor", { "dc-node__handle-anchor--visible": position.visible }],
1785
+ "data-dc-component": "node-handle-anchor",
1786
+ "data-dc-state": position.visible ? "visible" : "hidden",
1787
+ "data-dc-node-handle-for": node.id,
1788
+ "style": {
1789
+ position: position.strategy,
1790
+ top: 0,
1791
+ left: 0,
1792
+ transform: `translate3d(${position.x}px, ${position.y}px, 0)`
1793
+ }
1794
+ }, [handleVNode])]));
1795
+ } else wrapperChildren.push(handleVNode);
1796
+ }
1797
+ if (widget.state.isSelected.value) {
1798
+ const toolbarVNode = h(NodeToolbar, {
1799
+ nodeId: node.id,
1800
+ nodeType: node.type,
1801
+ owner: props.owner,
1802
+ actions: actions.value,
1803
+ state: widget.state,
1804
+ toolbarPosition: toolbarPosition.value,
1805
+ onDragStart: drag.handleDragStart,
1806
+ onDragEnd: drag.handleDragEnd
1807
+ });
1808
+ wrapperChildren.push(h(Teleport, { to: interactionLayerTarget }, [h("div", {
1809
+ "ref": toolbarElRef,
1810
+ "class": "dc-node__toolbar-anchor",
1811
+ "data-placement": toolbarPosition.value.placement,
1812
+ "data-orientation": toolbarPosition.value.orientation,
1813
+ "style": {
1814
+ position: toolbarPosition.value.strategy,
1815
+ top: 0,
1816
+ left: 0,
1817
+ transform: `translate3d(${toolbarPosition.value.x}px, ${toolbarPosition.value.y}px, 0)`,
1818
+ visibility: toolbarPosition.value.visible ? "visible" : "hidden",
1819
+ pointerEvents: toolbarPosition.value.visible ? "auto" : "none"
1820
+ }
1821
+ }, [toolbarVNode])]));
1822
+ }
1823
+ const themeStates = [
1824
+ widget.useMask.value ? "masked" : "unmasked",
1825
+ !widget.selectable.value ? "non-selectable" : null,
1826
+ widget.inSortScope.value && !widget.sortable.value ? "locked" : null,
1827
+ !widget.inSortScope.value ? "unsorted" : null,
1828
+ widget.isDragging.value ? "dragging" : null,
1829
+ !widget.visible.value ? "hidden" : null,
1830
+ widget.state.isSelected.value ? "selected" : null,
1831
+ widget.state.isHovered.value ? "hovered" : null,
1832
+ widget.state.isDragOver.value ? "drag-over" : null,
1833
+ `${ownerKind}-owned`
1834
+ ].filter(Boolean).join(" ");
1835
+ const coreWrapper = h("div", {
1836
+ "ref": nodeElRef,
1837
+ "class": [widget.wrapperClasses.value, `dc-node--${ownerKind}-owned`],
1838
+ "data-dc-component": "node",
1839
+ "data-dc-state": themeStates,
1840
+ "data-dc-node-owner": ownerKind,
1841
+ "style": wrapperStyle,
1842
+ "data-node-id": node.id,
1843
+ "data-node-type": node.type,
1844
+ "data-dc-layout-placement": isContainerOwned ? void 0 : placement.kind,
1845
+ "data-dc-layer-mode": !isContainerOwned && placement.kind === "layer" ? placement.mode : void 0,
1846
+ "data-dc-layout-region": isContainerOwned ? void 0 : widget.layout.value.region,
1847
+ "data-dc-sort-scope": isContainerOwned || widget.layout.value.sortScope === false ? void 0 : widget.layout.value.sortScope,
1848
+ "data-dc-visible": widget.visible.value ? void 0 : "false",
1849
+ "onMouseover": resolvedContainer ? void 0 : handleMouseOver,
1850
+ "onMouseleave": resolvedContainer ? void 0 : widget.handleMouseLeave,
1851
+ "onClick": isSelfPositionedLayer.value && widget.selectable.value ? widget.handleSelect : resolvedContainer && widget.selectable.value ? handleDirectSelect : void 0
1852
+ }, wrapperChildren);
1853
+ if (NodeWrapper) return h(NodeWrapper, {
1854
+ nodeId: node.id,
1855
+ nodeType: node.type,
1856
+ owner: props.owner,
1857
+ state: widget.state,
1858
+ meta: widget.meta.value
1859
+ }, { default: () => coreWrapper });
1860
+ return coreWrapper;
1861
+ };
1862
+ }
1863
+ });
1864
+ //#endregion
1865
+ //#region src/components/ContainerRegionOutlet.ts
1866
+ function isNestedRegionEvent(event, regionElement) {
1867
+ return event.target instanceof Element && event.target.closest("[data-dc-container-region]") !== regionElement;
1868
+ }
1869
+ var ContainerRegionOutlet_default = defineComponent({
1870
+ name: "DcContainerRegionOutlet",
1871
+ inheritAttrs: false,
1872
+ props: {
1873
+ regionId: {
1874
+ type: String,
1875
+ required: true
1876
+ },
1877
+ as: {
1878
+ type: [String, Object],
1879
+ default: "div"
1880
+ },
1881
+ resolveDropIndex: {
1882
+ type: Function,
1883
+ default: void 0
1884
+ }
1885
+ },
1886
+ setup(props, { attrs }) {
1887
+ const ctx = useRendererContext();
1888
+ const runtime = useContainerRuntime();
1889
+ const definition = computed(() => runtime.regionDefinitions.value.find((item) => item.id === props.regionId));
1890
+ const regionNodes = computed(() => runtime.getRegionNodes(props.regionId));
1891
+ const containerMeta = computed(() => {
1892
+ const containerNode = ctx.schemaIndex.value.index.get(runtime.nodeId.value)?.node;
1893
+ return containerNode ? ctx.engine.registry.getWidget(containerNode.type) : void 0;
1894
+ });
1895
+ const isEmpty = computed(() => regionNodes.value.length === 0);
1896
+ const isActive = computed(() => {
1897
+ const destination = ctx.activeDestination.value;
1898
+ return destination?.kind === "container" && destination.containerId === runtime.nodeId.value && destination.regionId === props.regionId;
1899
+ });
1900
+ const isForbidden = computed(() => isActive.value && ctx.containerDropDecision.value?.allowed === false);
1901
+ function handleDragOver(event) {
1902
+ const regionElement = event.currentTarget;
1903
+ if (isNestedRegionEvent(event, regionElement)) return;
1904
+ event.preventDefault();
1905
+ event.stopPropagation();
1906
+ const resolver = props.resolveDropIndex ?? containerMeta.value?.containerAdapter?.resolveDropIndex;
1907
+ if (!resolver) {
1908
+ ctx.onContainerDragOver?.({
1909
+ event,
1910
+ containerId: runtime.nodeId.value,
1911
+ regionId: props.regionId,
1912
+ allowed: false,
1913
+ code: "CONTAINER_DROP_ADAPTER_MISSING"
1914
+ });
1915
+ return;
1916
+ }
1917
+ try {
1918
+ const nodes = regionNodes.value;
1919
+ const index = resolver({
1920
+ event,
1921
+ regionElement,
1922
+ itemElements: Array.from(regionElement.querySelectorAll(":scope > [data-node-id]")),
1923
+ nodes
1924
+ });
1925
+ if (index === null) {
1926
+ ctx.onContainerDragOver?.({
1927
+ event,
1928
+ containerId: runtime.nodeId.value,
1929
+ regionId: props.regionId,
1930
+ allowed: false,
1931
+ code: "CONTAINER_DROP_NO_TARGET"
1932
+ });
1933
+ return;
1934
+ }
1935
+ if (!Number.isInteger(index) || index < 0 || index > nodes.length) {
1936
+ ctx.onContainerDragOver?.({
1937
+ event,
1938
+ containerId: runtime.nodeId.value,
1939
+ regionId: props.regionId,
1940
+ allowed: false,
1941
+ code: "CONTAINER_DROP_ADAPTER_INVALID"
1942
+ });
1943
+ return;
1944
+ }
1945
+ ctx.onContainerDragOver?.({
1946
+ event,
1947
+ destination: {
1948
+ kind: "container",
1949
+ containerId: runtime.nodeId.value,
1950
+ regionId: props.regionId,
1951
+ index
1952
+ }
1953
+ });
1954
+ } catch (error) {
1955
+ ctx.onContainerDragOver?.({
1956
+ event,
1957
+ containerId: runtime.nodeId.value,
1958
+ regionId: props.regionId,
1959
+ allowed: false,
1960
+ code: "CONTAINER_DROP_ADAPTER_FAILED",
1961
+ message: error instanceof Error ? error.message : String(error)
1962
+ });
1963
+ }
1964
+ }
1965
+ function handleDragLeave(event) {
1966
+ const regionElement = event.currentTarget;
1967
+ if (isNestedRegionEvent(event, regionElement)) return;
1968
+ event.stopPropagation();
1969
+ ctx.onContainerDragLeave?.(event);
1970
+ }
1971
+ function handleDrop(event) {
1972
+ const regionElement = event.currentTarget;
1973
+ if (isNestedRegionEvent(event, regionElement)) return;
1974
+ event.preventDefault();
1975
+ event.stopPropagation();
1976
+ ctx.onContainerDrop?.(event);
1977
+ }
1978
+ return () => {
1979
+ const children = regionNodes.value.map((node) => h(WidgetRenderer_default, {
1980
+ key: node.id,
1981
+ node,
1982
+ owner: {
1983
+ kind: "container",
1984
+ containerId: runtime.nodeId.value,
1985
+ regionId: props.regionId
1986
+ }
1987
+ }));
1988
+ const DropIndicator = ctx.extensions.dropIndicator ?? DefaultDropIndicator_default;
1989
+ const EmptyState = ctx.extensions.emptyState ?? DefaultEmptyState_default;
1990
+ const ForbiddenOverlay = ctx.extensions.forbiddenOverlay ?? DefaultForbiddenOverlay_default;
1991
+ if (isEmpty.value) children.push(h(EmptyState, { isDragOver: isActive.value }));
1992
+ if (isActive.value && !isForbidden.value) {
1993
+ const index = ctx.activeDestination.value?.index;
1994
+ if (Number.isInteger(index) && index != null && index >= 0 && index <= regionNodes.value.length) children.splice(index, 0, h(DropIndicator, { key: "__container-drop-indicator__" }));
1995
+ }
1996
+ if (isForbidden.value) children.push(h(ForbiddenOverlay, {
1997
+ widgetType: ctx.engine.store.dragTarget.value?.widgetType ?? "",
1998
+ reason: ctx.containerDropDecision.value
1999
+ }));
2000
+ const themeStates = [
2001
+ isEmpty.value ? "empty" : null,
2002
+ isActive.value ? "active" : null,
2003
+ isForbidden.value ? "forbidden" : null
2004
+ ].filter(Boolean).join(" ") || void 0;
2005
+ return h(props.as, mergeProps(attrs, {
2006
+ "class": ["dc-container-region", {
2007
+ "dc-container-region--empty": isEmpty.value,
2008
+ "dc-container-region--active": isActive.value,
2009
+ "dc-container-region--forbidden": isForbidden.value
2010
+ }],
2011
+ "data-dc-component": "container-region",
2012
+ "data-dc-state": themeStates,
2013
+ "data-dc-container-id": runtime.nodeId.value,
2014
+ "data-dc-container-region": props.regionId,
2015
+ "role": attrs.role ?? "group",
2016
+ "aria-label": attrs["aria-label"] ?? definition.value?.title ?? props.regionId,
2017
+ "aria-disabled": isForbidden.value ? "true" : void 0,
2018
+ "onDragover": handleDragOver,
2019
+ "onDragleave": handleDragLeave,
2020
+ "onDrop": handleDrop
2021
+ }), children);
2022
+ };
2023
+ }
2024
+ });
2025
+ //#endregion
2026
+ //#region src/components/DefaultContainerShell.ts
2027
+ const CHROME_MEASURE_SELECTOR = "[data-dc-chrome-position=\"fixed\"][data-dc-reserve-mode=\"measure\"][data-dc-avoid-content=\"true\"]";
2028
+ function sizeToCss(value) {
2029
+ return typeof value === "number" ? `${value}px` : value ?? "0px";
2030
+ }
2031
+ function edgeStyle(edge) {
2032
+ switch (edge) {
2033
+ case "block-start": return {
2034
+ top: "0px",
2035
+ right: "0px",
2036
+ left: "0px"
2037
+ };
2038
+ case "block-end": return {
2039
+ right: "0px",
2040
+ bottom: "0px",
2041
+ left: "0px"
2042
+ };
2043
+ case "inline-start": return {
2044
+ top: "0px",
2045
+ bottom: "0px",
2046
+ left: "0px"
2047
+ };
2048
+ case "inline-end": return {
2049
+ top: "0px",
2050
+ right: "0px",
2051
+ bottom: "0px"
2052
+ };
2053
+ }
2054
+ }
2055
+ function chromeItemStyle(placement) {
2056
+ if (placement.position === "flow") return {
2057
+ position: "relative",
2058
+ pointerEvents: "auto"
2059
+ };
2060
+ if (placement.position === "fixed") return {
2061
+ position: "relative",
2062
+ zIndex: "20",
2063
+ pointerEvents: "auto"
2064
+ };
2065
+ return {
2066
+ position: "sticky",
2067
+ zIndex: "20",
2068
+ pointerEvents: "auto",
2069
+ ...edgeStyle(placement.edge)
2070
+ };
2071
+ }
2072
+ function fixedChromeStackStyle(edge) {
2073
+ const style = {
2074
+ position: "absolute",
2075
+ display: "flex",
2076
+ pointerEvents: "none"
2077
+ };
2078
+ if (edge === "block-start" || edge === "block-end") {
2079
+ style.flexDirection = edge === "block-start" ? "column" : "column-reverse";
2080
+ style.left = "0px";
2081
+ style.right = "0px";
2082
+ style[edge === "block-start" ? "top" : "bottom"] = "0px";
2083
+ } else {
2084
+ style.flexDirection = edge === "inline-start" ? "row" : "row-reverse";
2085
+ style.top = "var(--dc-inset-block-start)";
2086
+ style.bottom = "var(--dc-inset-block-end)";
2087
+ style[edge === "inline-start" ? "left" : "right"] = "0px";
2088
+ }
2089
+ return style;
2090
+ }
2091
+ function layerItemStyle(placement) {
2092
+ if (placement.mode === "self") return {
2093
+ position: "absolute",
2094
+ inset: "0px",
2095
+ pointerEvents: "none"
2096
+ };
2097
+ const style = {
2098
+ position: "absolute",
2099
+ pointerEvents: "auto"
2100
+ };
2101
+ const block = placement.anchor.block ?? "end";
2102
+ const inline = placement.anchor.inline ?? "end";
2103
+ if (block === "start") style.top = sizeToCss(placement.offset?.blockStart ?? "calc(var(--dc-inset-block-start) + 16px)");
2104
+ else if (block === "center") style.top = "50%";
2105
+ else style.bottom = sizeToCss(placement.offset?.blockEnd ?? "calc(var(--dc-inset-block-end) + 16px)");
2106
+ if (inline === "start") style.left = sizeToCss(placement.offset?.inlineStart ?? "calc(var(--dc-inset-inline-start) + 16px)");
2107
+ else if (inline === "center") style.left = "50%";
2108
+ else style.right = sizeToCss(placement.offset?.inlineEnd ?? "calc(var(--dc-inset-inline-end) + 16px)");
2109
+ if (block === "center" && inline === "center") style.transform = "translate(-50%, -50%)";
2110
+ else if (block === "center") style.transform = "translateY(-50%)";
2111
+ else if (inline === "center") style.transform = "translateX(-50%)";
2112
+ return style;
2113
+ }
2114
+ function insetVariables(plan) {
2115
+ const sized = {
2116
+ "block-start": [],
2117
+ "block-end": [],
2118
+ "inline-start": [],
2119
+ "inline-end": []
2120
+ };
2121
+ const measuredFallback = {
2122
+ "block-start": [],
2123
+ "block-end": [],
2124
+ "inline-start": [],
2125
+ "inline-end": []
2126
+ };
2127
+ for (const entry of plan?.chrome ?? []) {
2128
+ const placement = entry.layout.placement;
2129
+ if (placement.position !== "fixed" || !placement.avoidContent) continue;
2130
+ if (placement.reserve.mode === "size") sized[placement.edge].push(sizeToCss(placement.reserve.size));
2131
+ else if (placement.reserve.mode === "measure" && placement.reserve.size !== void 0) measuredFallback[placement.edge].push(sizeToCss(placement.reserve.size));
2132
+ }
2133
+ const result = {};
2134
+ for (const [edge, sizes] of Object.entries(sized)) result[`--dc-sized-inset-${edge}`] = sizes.length > 1 ? `calc(${sizes.join(" + ")})` : sizes[0] ?? "0px";
2135
+ for (const [edge, sizes] of Object.entries(measuredFallback)) result[`--dc-measured-inset-${edge}`] = sizes.length > 1 ? `calc(${sizes.join(" + ")})` : sizes[0] ?? "0px";
2136
+ return result;
2137
+ }
2138
+ const DefaultContainerShell = defineComponent({
2139
+ name: "DcDefaultContainerShell",
2140
+ props: {
2141
+ isEmpty: {
2142
+ type: Boolean,
2143
+ default: false
2144
+ },
2145
+ regionVNodes: {
2146
+ type: Object,
2147
+ default: () => ({})
2148
+ },
2149
+ chromeVNodes: {
2150
+ type: Array,
2151
+ default: () => []
2152
+ },
2153
+ layerVNodes: {
2154
+ type: Object,
2155
+ default: () => ({})
2156
+ },
2157
+ forbiddenOverlayVNode: {
2158
+ type: Object,
2159
+ default: null
2160
+ },
2161
+ layoutPlan: {
2162
+ type: Object,
2163
+ default: void 0
2164
+ },
2165
+ surfaceStyle: {
2166
+ type: Object,
2167
+ default: void 0
2168
+ },
2169
+ selectionPresentation: {
2170
+ type: Object,
2171
+ default: void 0
2172
+ }
2173
+ },
2174
+ setup(props, { slots }) {
2175
+ const shellRef = ref(null);
2176
+ let resizeObserver = null;
2177
+ function updateMeasuredInsets() {
2178
+ const shell = shellRef.value;
2179
+ if (!shell) return;
2180
+ const totals = {
2181
+ "block-start": 0,
2182
+ "block-end": 0,
2183
+ "inline-start": 0,
2184
+ "inline-end": 0
2185
+ };
2186
+ for (const item of Array.from(shell.querySelectorAll(CHROME_MEASURE_SELECTOR))) {
2187
+ const edge = item.dataset.dcChromeEdge;
2188
+ const rect = item.getBoundingClientRect();
2189
+ totals[edge] += edge === "block-start" || edge === "block-end" ? rect.height : rect.width;
2190
+ }
2191
+ for (const [edge, value] of Object.entries(totals)) shell.style.setProperty(`--dc-measured-inset-${edge}`, `${value}px`);
2192
+ }
2193
+ function refreshMeasuredInsets() {
2194
+ resizeObserver?.disconnect();
2195
+ if (typeof ResizeObserver !== "undefined") {
2196
+ resizeObserver = new ResizeObserver(updateMeasuredInsets);
2197
+ for (const item of Array.from(shellRef.value?.querySelectorAll(CHROME_MEASURE_SELECTOR) ?? [])) resizeObserver.observe(item);
2198
+ }
2199
+ nextTick(updateMeasuredInsets);
2200
+ }
2201
+ onMounted(refreshMeasuredInsets);
2202
+ onUpdated(refreshMeasuredInsets);
2203
+ onBeforeUnmount(() => resizeObserver?.disconnect());
2204
+ return () => {
2205
+ const additionalRegions = Object.entries(props.regionVNodes).filter(([region]) => region !== DEFAULT_LAYOUT_REGION).map(([region, nodes]) => h("div", {
2206
+ "key": region,
2207
+ "class": "dc-container-shell__region",
2208
+ "data-dc-layout-region": region
2209
+ }, nodes));
2210
+ const contentChrome = {
2211
+ "block-start": [],
2212
+ "block-end": [],
2213
+ "inline-start": [],
2214
+ "inline-end": []
2215
+ };
2216
+ const fixedChrome = {
2217
+ "block-start": {
2218
+ reserved: [],
2219
+ overlay: []
2220
+ },
2221
+ "block-end": {
2222
+ reserved: [],
2223
+ overlay: []
2224
+ },
2225
+ "inline-start": {
2226
+ reserved: [],
2227
+ overlay: []
2228
+ },
2229
+ "inline-end": {
2230
+ reserved: [],
2231
+ overlay: []
2232
+ }
2233
+ };
2234
+ for (const [index, entry] of (props.layoutPlan?.chrome ?? []).entries()) {
2235
+ const placement = entry.layout.placement;
2236
+ const item = h("div", {
2237
+ "key": entry.node.id,
2238
+ "class": ["dc-container-shell__chrome-item", `dc-container-shell__chrome-item--${placement.position}`],
2239
+ "data-dc-chrome-edge": placement.edge,
2240
+ "data-dc-chrome-position": placement.position,
2241
+ "data-dc-reserve-mode": placement.reserve.mode,
2242
+ "data-dc-avoid-content": String(placement.avoidContent),
2243
+ "style": chromeItemStyle(placement)
2244
+ }, [props.chromeVNodes[index]]);
2245
+ if (placement.position === "fixed") {
2246
+ const stack = placement.avoidContent ? "reserved" : "overlay";
2247
+ fixedChrome[placement.edge][stack].push(item);
2248
+ } else contentChrome[placement.edge].push(item);
2249
+ }
2250
+ const fixedChromeStacks = Object.entries(fixedChrome).flatMap(([edge, groups]) => ["reserved", "overlay"].flatMap((stack) => {
2251
+ const items = groups[stack];
2252
+ return items.length > 0 ? [h("div", {
2253
+ "key": `${edge}:${stack}`,
2254
+ "class": [
2255
+ "dc-container-shell__chrome-stack",
2256
+ `dc-container-shell__chrome-stack--${edge}`,
2257
+ `dc-container-shell__chrome-stack--${stack}`
2258
+ ],
2259
+ "data-dc-chrome-edge": edge,
2260
+ "data-dc-avoid-content-stack": String(stack === "reserved"),
2261
+ "style": fixedChromeStackStyle(edge)
2262
+ }, items)] : [];
2263
+ }));
2264
+ const layers = Array.from(props.layoutPlan?.layers.entries() ?? []).map(([layer, entries]) => {
2265
+ const vnodes = props.layerVNodes[layer] ?? [];
2266
+ return h("div", {
2267
+ "key": layer,
2268
+ "class": "dc-container-shell__layer",
2269
+ "data-dc-layer": layer,
2270
+ "style": {
2271
+ position: "absolute",
2272
+ inset: "0px",
2273
+ zIndex: "30",
2274
+ pointerEvents: "none"
2275
+ }
2276
+ }, entries.map((entry, index) => {
2277
+ const placement = entry.layout.placement;
2278
+ return h("div", {
2279
+ "key": entry.node.id,
2280
+ "class": ["dc-container-shell__layer-item", `dc-container-shell__layer-item--${placement.mode}`],
2281
+ "data-dc-layer-mode": placement.mode,
2282
+ "data-dc-layer-block": placement.anchor.block ?? "end",
2283
+ "data-dc-layer-inline": placement.anchor.inline ?? "end",
2284
+ "style": layerItemStyle(placement)
2285
+ }, [vnodes[index]]);
2286
+ }));
2287
+ });
2288
+ return h("div", {
2289
+ "ref": shellRef,
2290
+ "class": "dc-container-shell",
2291
+ "data-dc-component": "container-shell",
2292
+ "data-dc-state": props.isEmpty ? "empty" : void 0,
2293
+ "style": insetVariables(props.layoutPlan)
2294
+ }, [
2295
+ h("div", {
2296
+ class: "dc-container-shell__content",
2297
+ style: {
2298
+ position: "relative",
2299
+ boxSizing: "border-box"
2300
+ }
2301
+ }, [h("div", { class: "dc-container-shell__content-layout" }, [
2302
+ ...contentChrome["block-start"],
2303
+ h("div", { class: "dc-container-shell__content-row" }, [
2304
+ contentChrome["inline-start"].length > 0 ? h("div", { class: "dc-container-shell__content-edge dc-container-shell__content-edge--inline-start" }, contentChrome["inline-start"]) : null,
2305
+ h("div", {
2306
+ class: "dc-container-shell__content-surface",
2307
+ style: props.surfaceStyle
2308
+ }, [...slots.default?.() ?? [], ...additionalRegions]),
2309
+ contentChrome["inline-end"].length > 0 ? h("div", { class: "dc-container-shell__content-edge dc-container-shell__content-edge--inline-end" }, contentChrome["inline-end"]) : null
2310
+ ]),
2311
+ ...contentChrome["block-end"],
2312
+ h("div", {
2313
+ "ref": (element) => {
2314
+ props.selectionPresentation?.registerPlane("content", element instanceof HTMLElement ? element : null);
2315
+ },
2316
+ "class": "dc-node-selection-plane dc-node-selection-plane--content",
2317
+ "data-dc-selection-plane": "content",
2318
+ "aria-hidden": "true"
2319
+ })
2320
+ ])]),
2321
+ fixedChromeStacks.length > 0 ? h("div", {
2322
+ class: "dc-container-shell__chrome",
2323
+ style: {
2324
+ position: "absolute",
2325
+ inset: "0px",
2326
+ zIndex: "20",
2327
+ pointerEvents: "none"
2328
+ }
2329
+ }, fixedChromeStacks) : null,
2330
+ ...layers,
2331
+ h("div", {
2332
+ "ref": (element) => {
2333
+ props.selectionPresentation?.registerPlane("root", element instanceof HTMLElement ? element : null);
2334
+ },
2335
+ "class": "dc-node-selection-plane dc-node-selection-plane--root",
2336
+ "data-dc-selection-plane": "root",
2337
+ "aria-hidden": "true"
2338
+ }),
2339
+ h("div", {
2340
+ "ref": (element) => {
2341
+ props.selectionPresentation?.registerPlane("viewport", element instanceof HTMLElement ? element : null);
2342
+ },
2343
+ "class": "dc-node-selection-plane dc-node-selection-plane--viewport",
2344
+ "data-dc-selection-plane": "viewport",
2345
+ "aria-hidden": "true"
2346
+ }),
2347
+ props.forbiddenOverlayVNode
2348
+ ]);
2349
+ };
2350
+ }
2351
+ });
2352
+ const DefaultContainerShellWithOverlay = DefaultContainerShell;
2353
+ DefaultContainerShellWithOverlay.__dcHandlesForbiddenOverlay = true;
2354
+ //#endregion
2355
+ //#region src/components/RootRenderer.ts
2356
+ function handlesForbiddenOverlay(component) {
2357
+ return Boolean(component.__dcHandlesForbiddenOverlay);
2358
+ }
2359
+ function regionEntryIndex(plan, entry) {
2360
+ return (plan.regions.get(entry.layout.region ?? DEFAULT_LAYOUT_REGION) ?? []).findIndex((candidate) => candidate.node.id === entry.node.id);
2361
+ }
2362
+ function insertDropIndicator(regionVNodes, plan, schema, engine, destination, legacyIndex, indicator) {
2363
+ if (destination?.kind === "container") return;
2364
+ const sortScope = destination === void 0 ? DEFAULT_SORT_SCOPE : destination?.sortScope;
2365
+ if (!sortScope) return;
2366
+ const entries = plan.sortScopes.get(sortScope) ?? [];
2367
+ const requestedIndex = destination?.index ?? legacyIndex;
2368
+ const index = requestedIndex == null ? entries.length : Math.max(0, Math.min(requestedIndex, entries.length));
2369
+ const dragTarget = engine.store.dragTarget.value;
2370
+ const draggedEntry = dragTarget?.sourceNodeId ? plan.entries.find((entry) => entry.node.id === dragTarget.sourceNodeId) : void 0;
2371
+ const draggedLayout = !draggedEntry && dragTarget?.widgetType ? resolveNodeLayout({
2372
+ id: "__drop-indicator__",
2373
+ type: dragTarget.widgetType,
2374
+ props: {}
2375
+ }, engine.registry, schema) : void 0;
2376
+ const inferredRegion = draggedEntry?.layout.region ?? (draggedLayout?.placement.kind === "flow" ? draggedLayout.region : void 0);
2377
+ const adjacentEntry = index < entries.length ? entries[index] : entries.at(-1);
2378
+ const region = inferredRegion ?? adjacentEntry?.layout.region ?? DEFAULT_LAYOUT_REGION;
2379
+ const regionNodes = regionVNodes[region] ?? (regionVNodes[region] = []);
2380
+ const nextRegionEntry = entries.slice(index).find((entry) => (entry.layout.region ?? DEFAULT_LAYOUT_REGION) === region);
2381
+ const previousRegionEntry = entries.slice(0, index).findLast((entry) => (entry.layout.region ?? DEFAULT_LAYOUT_REGION) === region);
2382
+ if (!nextRegionEntry && !previousRegionEntry) {
2383
+ regionNodes.push(indicator);
2384
+ return;
2385
+ }
2386
+ const insertIndex = nextRegionEntry ? regionEntryIndex(plan, nextRegionEntry) : regionEntryIndex(plan, previousRegionEntry) + 1;
2387
+ regionNodes.splice(Math.max(0, insertIndex), 0, indicator);
2388
+ }
2389
+ var RootRenderer_default = defineComponent({
2390
+ name: "DcRootRenderer",
2391
+ props: {
2392
+ engine: {
2393
+ type: Object,
2394
+ required: true
2395
+ },
2396
+ componentMap: {
2397
+ type: Object,
2398
+ required: true
2399
+ },
2400
+ extensions: {
2401
+ type: Object,
2402
+ default: () => ({})
2403
+ },
2404
+ eventHooks: {
2405
+ type: Object,
2406
+ default: void 0
2407
+ },
2408
+ actionInterceptors: {
2409
+ type: Array,
2410
+ default: void 0
2411
+ },
2412
+ actionRegistry: {
2413
+ type: Object,
2414
+ default: void 0
2415
+ },
2416
+ dragOverNodeId: {
2417
+ type: Object,
2418
+ default: void 0
2419
+ },
2420
+ dragOverIndex: {
2421
+ type: Object,
2422
+ default: void 0
2423
+ },
2424
+ activeDestination: {
2425
+ type: Object,
2426
+ default: void 0
2427
+ },
2428
+ containerDropDecision: {
2429
+ type: Object,
2430
+ default: void 0
2431
+ },
2432
+ onContainerDragOver: {
2433
+ type: Function,
2434
+ default: void 0
2435
+ },
2436
+ onContainerDragLeave: {
2437
+ type: Function,
2438
+ default: void 0
2439
+ },
2440
+ onContainerDrop: {
2441
+ type: Function,
2442
+ default: void 0
2443
+ },
2444
+ interactionBoundary: {
2445
+ type: Object,
2446
+ default: void 0
2447
+ },
2448
+ isForbidden: {
2449
+ type: Object,
2450
+ default: void 0
2451
+ },
2452
+ forbiddenReason: {
2453
+ type: Object,
2454
+ default: void 0
2455
+ }
2456
+ },
2457
+ setup(props) {
2458
+ const ctx = createRendererContext({
2459
+ engine: props.engine,
2460
+ componentMap: props.componentMap,
2461
+ extensions: props.extensions,
2462
+ eventHooks: props.eventHooks,
2463
+ actionInterceptors: props.actionInterceptors,
2464
+ actionRegistry: props.actionRegistry,
2465
+ dragOverNodeId: props.dragOverNodeId,
2466
+ activeDestination: props.activeDestination,
2467
+ containerDropDecision: props.containerDropDecision,
2468
+ onContainerDragOver: props.onContainerDragOver,
2469
+ onContainerDragLeave: props.onContainerDragLeave,
2470
+ onContainerDrop: props.onContainerDrop,
2471
+ interactionBoundary: props.interactionBoundary
2472
+ });
2473
+ provide(RENDERER_CONTEXT_KEY, ctx);
2474
+ const selectionPresentation = createNodeSelectionPresentation();
2475
+ provide(NODE_SELECTION_PRESENTATION_KEY, selectionPresentation);
2476
+ const ContainerShell = computed(() => props.extensions?.containerShell ?? DefaultContainerShell);
2477
+ const ForbiddenOverlay = computed(() => props.extensions?.forbiddenOverlay ?? DefaultForbiddenOverlay_default);
2478
+ return () => {
2479
+ const schema = ctx.schema.value;
2480
+ const isDragOver = props.dragOverNodeId?.value === "root";
2481
+ const plan = ctx.layoutPlan.value;
2482
+ const DropIndicator = props.extensions?.dropIndicator ?? DefaultDropIndicator_default;
2483
+ const EmptyState = props.extensions?.emptyState ?? DefaultEmptyState_default;
2484
+ const regionVNodes = {};
2485
+ for (const [region, entries] of plan.regions) regionVNodes[region] = entries.map((entry) => h(WidgetRenderer_default, {
2486
+ "key": entry.node.id,
2487
+ "node": entry.node,
2488
+ "selectionPlane": "content",
2489
+ "data-dc-layout-region": entry.layout.region
2490
+ }));
2491
+ const chromeVNodes = plan.chrome.map((entry) => h(WidgetRenderer_default, {
2492
+ "key": entry.node.id,
2493
+ "node": entry.node,
2494
+ "selectionPlane": entry.layout.placement.kind === "chrome" && entry.layout.placement.position === "fixed" ? "viewport" : "content",
2495
+ "data-dc-layout-placement": "chrome"
2496
+ }));
2497
+ const layerVNodes = {};
2498
+ for (const [layer, entries] of plan.layers) layerVNodes[layer] = entries.map((entry) => h(WidgetRenderer_default, {
2499
+ "key": entry.node.id,
2500
+ "node": entry.node,
2501
+ "selectionPlane": "viewport",
2502
+ "data-dc-layout-placement": "layer"
2503
+ }));
2504
+ const isForbidden = props.isForbidden?.value ?? false;
2505
+ const createForbiddenOverlayVNode = () => h(ForbiddenOverlay.value, {
2506
+ key: "__forbidden__",
2507
+ widgetType: props.engine.store.dragTarget.value?.widgetType ?? "",
2508
+ reason: props.forbiddenReason?.value ?? null
2509
+ });
2510
+ const forbiddenOverlayVNode = isDragOver && isForbidden ? createForbiddenOverlayVNode() : null;
2511
+ if (isDragOver && !isForbidden) insertDropIndicator(regionVNodes, plan, schema, props.engine, props.activeDestination?.value, props.dragOverIndex?.value, h(DropIndicator, { key: "__drop-indicator__" }));
2512
+ const contentVNodes = regionVNodes[DEFAULT_LAYOUT_REGION] ?? [];
2513
+ const isEmpty = plan.entries.length === 0 && !isDragOver;
2514
+ const ContainerShellComponent = ContainerShell.value;
2515
+ const fallbackForbiddenOverlayVNode = forbiddenOverlayVNode && !handlesForbiddenOverlay(ContainerShellComponent) ? createForbiddenOverlayVNode() : null;
2516
+ return h("div", {
2517
+ "class": "dc-root-renderer",
2518
+ "data-dc-component": "root-renderer",
2519
+ "data-node-id": "root",
2520
+ "data-node-type": "root"
2521
+ }, [
2522
+ h(ContainerShellComponent, {
2523
+ class: { "dc-container-shell--empty": isEmpty },
2524
+ isEmpty,
2525
+ regionVNodes,
2526
+ chromeVNodes,
2527
+ layerVNodes,
2528
+ forbiddenOverlayVNode,
2529
+ layoutPlan: plan,
2530
+ surfaceStyle: normalizeStyleValueMap(schema.root.style?.surface),
2531
+ registry: props.engine.registry,
2532
+ selectionPresentation
2533
+ }, {
2534
+ default: () => {
2535
+ if (isEmpty) return [h(EmptyState, { isDragOver: false })];
2536
+ return contentVNodes;
2537
+ },
2538
+ ...Object.fromEntries(Object.entries(regionVNodes).map(([region, vnodes]) => [region, () => vnodes]))
2539
+ }),
2540
+ h("div", {
2541
+ "ref": (element) => {
2542
+ selectionPresentation.registerFallback(element instanceof HTMLElement ? element : null);
2543
+ },
2544
+ "class": "dc-node-selection-plane dc-node-selection-plane--fallback",
2545
+ "data-dc-selection-plane": "fallback",
2546
+ "aria-hidden": "true"
2547
+ }),
2548
+ fallbackForbiddenOverlayVNode
2549
+ ]);
2550
+ };
2551
+ }
2552
+ });
2553
+ //#endregion
2554
+ //#region src/messages.ts
2555
+ const rendererMessages = {
2556
+ "zh-CN": {
2557
+ action: {
2558
+ "drag": "拖拽排序",
2559
+ "move-up": "上移",
2560
+ "move-down": "下移",
2561
+ "delete": "删除"
2562
+ },
2563
+ canvas: {
2564
+ "empty": "拖拽组件到这里",
2565
+ "node-handle": "选中组件"
2566
+ },
2567
+ forbidden: {
2568
+ default: "当前物料不满足创建条件,无法添加到画布",
2569
+ alreadyExists: "无法添加 — 该类型已存在"
2570
+ }
2571
+ },
2572
+ "en": {
2573
+ action: {
2574
+ "drag": "Drag to sort",
2575
+ "move-up": "Move up",
2576
+ "move-down": "Move down",
2577
+ "delete": "Delete"
2578
+ },
2579
+ canvas: {
2580
+ "empty": "Drag components here",
2581
+ "node-handle": "Select component"
2582
+ },
2583
+ forbidden: {
2584
+ default: "This component cannot be added to the canvas",
2585
+ alreadyExists: "Cannot add — this type already exists"
2586
+ }
2587
+ }
2588
+ };
2589
+ //#endregion
2590
+ export { ActionKey, CONTAINER_RUNTIME_CONTEXT_KEY, ContainerRegionOutlet_default as ContainerRegionOutlet, DefaultContainerFallback_default as DefaultContainerFallback, DefaultContainerShell, DefaultDropIndicator_default as DefaultDropIndicator, DefaultEmptyState_default as DefaultEmptyState, DefaultForbiddenOverlay_default as DefaultForbiddenOverlay, DefaultNodeHandle_default as DefaultNodeHandle, DefaultNodeMask_default as DefaultNodeMask, DefaultNodeSelection_default as DefaultNodeSelection, DefaultNodeToolbar_default as DefaultNodeToolbar, DefaultWidgetFallback_default as DefaultWidgetFallback, NODE_SELECTION_PLANE_KEY, NODE_SELECTION_PRESENTATION_KEY, RENDERER_CONTEXT_KEY, RootRenderer_default as RootRenderer, WIDGET_RUNTIME_CONTEXT_KEY, WidgetRenderer_default as WidgetRenderer, createConfirmActionInterceptor, createContainerRuntime, createDefaultActions, createDefaultEventHooks, createNodeActionRegistry, createNodeSelectionPresentation, createRendererContext, fireAfterHook, hideNativeDragImage, rendererMessages, resolveBeforeHook, resolveNodeInteractionPresentation, runActionPipeline, useContainerRuntime, useNodeActions, useNodeDrag, useNodeInteractionGeometry, useNodeSelectionPresentation, useNodeSelectionProjection, useNodeState, useRendererContext, useToolbarPosition, useWidgetNode, useWidgetRuntime };