@kywi-software/core 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT-PATTERNS.md +111 -23
- package/README.md +67 -0
- package/dist/admin/content-form.d.ts.map +1 -1
- package/dist/admin/content-form.js +97 -10
- package/dist/admin/content-form.js.map +1 -1
- package/dist/admin/layout-editor/component-actions.d.ts +219 -22
- package/dist/admin/layout-editor/component-actions.d.ts.map +1 -1
- package/dist/admin/layout-editor/component-actions.js +356 -36
- package/dist/admin/layout-editor/component-actions.js.map +1 -1
- package/dist/admin/layout-editor/component-fork.d.ts +224 -0
- package/dist/admin/layout-editor/component-fork.d.ts.map +1 -0
- package/dist/admin/layout-editor/component-fork.js +342 -0
- package/dist/admin/layout-editor/component-fork.js.map +1 -0
- package/dist/admin/layout-editor/editor-canvas.d.ts +28 -7
- package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
- package/dist/admin/layout-editor/editor-canvas.js +145 -32
- package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
- package/dist/admin/layout-editor/editor-dialog.d.ts +164 -0
- package/dist/admin/layout-editor/editor-dialog.d.ts.map +1 -0
- package/dist/admin/layout-editor/editor-dialog.js +195 -0
- package/dist/admin/layout-editor/editor-dialog.js.map +1 -0
- package/dist/admin/layout-editor/editor-state.d.ts +76 -0
- package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
- package/dist/admin/layout-editor/editor-state.js +47 -1
- package/dist/admin/layout-editor/editor-state.js.map +1 -1
- package/dist/admin/layout-editor/editor.css +366 -4
- package/dist/admin/layout-editor/index.d.ts +10 -1
- package/dist/admin/layout-editor/index.d.ts.map +1 -1
- package/dist/admin/layout-editor/index.js +11 -1
- package/dist/admin/layout-editor/index.js.map +1 -1
- package/dist/admin/layout-editor/inline-text.d.ts +56 -4
- package/dist/admin/layout-editor/inline-text.d.ts.map +1 -1
- package/dist/admin/layout-editor/inline-text.js +134 -11
- package/dist/admin/layout-editor/inline-text.js.map +1 -1
- package/dist/admin/layout-editor/layout-editor.d.ts +58 -1
- package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
- package/dist/admin/layout-editor/layout-editor.js +160 -190
- package/dist/admin/layout-editor/layout-editor.js.map +1 -1
- package/dist/admin/layout-editor/linked-component.d.ts +39 -0
- package/dist/admin/layout-editor/linked-component.d.ts.map +1 -0
- package/dist/admin/layout-editor/linked-component.js +42 -0
- package/dist/admin/layout-editor/linked-component.js.map +1 -0
- package/dist/admin/layout-editor/module-overlay.d.ts +36 -14
- package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
- package/dist/admin/layout-editor/module-overlay.js +233 -25
- package/dist/admin/layout-editor/module-overlay.js.map +1 -1
- package/dist/admin/layout-editor/node-lookup.d.ts +35 -0
- package/dist/admin/layout-editor/node-lookup.d.ts.map +1 -1
- package/dist/admin/layout-editor/node-lookup.js +91 -0
- package/dist/admin/layout-editor/node-lookup.js.map +1 -1
- package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
- package/dist/admin/layout-editor/overlay-shell.js +150 -32
- package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
- package/dist/admin/layout-editor/props-panel.d.ts +54 -1
- package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
- package/dist/admin/layout-editor/props-panel.js +119 -10
- package/dist/admin/layout-editor/props-panel.js.map +1 -1
- package/dist/admin/layout-editor/section-overlay.d.ts +16 -1
- package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
- package/dist/admin/layout-editor/section-overlay.js +21 -11
- package/dist/admin/layout-editor/section-overlay.js.map +1 -1
- package/dist/admin/layout-editor/section-template-picker.d.ts +27 -1
- package/dist/admin/layout-editor/section-template-picker.d.ts.map +1 -1
- package/dist/admin/layout-editor/section-template-picker.js +71 -17
- package/dist/admin/layout-editor/section-template-picker.js.map +1 -1
- package/dist/admin/layout-editor/section-variant-container-config.js +5 -5
- package/dist/admin/layout-editor/section-variant-container-config.js.map +1 -1
- package/dist/admin/layout-editor/use-chrome-flip.d.ts +111 -11
- package/dist/admin/layout-editor/use-chrome-flip.d.ts.map +1 -1
- package/dist/admin/layout-editor/use-chrome-flip.js +233 -33
- package/dist/admin/layout-editor/use-chrome-flip.js.map +1 -1
- package/dist/admin/layout-editor/use-component-connect.d.ts +61 -0
- package/dist/admin/layout-editor/use-component-connect.d.ts.map +1 -0
- package/dist/admin/layout-editor/use-component-connect.js +292 -0
- package/dist/admin/layout-editor/use-component-connect.js.map +1 -0
- package/dist/admin/layout-editor/use-component-fork.d.ts +123 -0
- package/dist/admin/layout-editor/use-component-fork.d.ts.map +1 -0
- package/dist/admin/layout-editor/use-component-fork.js +346 -0
- package/dist/admin/layout-editor/use-component-fork.js.map +1 -0
- package/dist/admin/layout-editor/use-component-library.d.ts +94 -0
- package/dist/admin/layout-editor/use-component-library.d.ts.map +1 -0
- package/dist/admin/layout-editor/use-component-library.js +55 -0
- package/dist/admin/layout-editor/use-component-library.js.map +1 -0
- package/dist/admin/registry.d.ts +11 -2
- package/dist/admin/registry.d.ts.map +1 -1
- package/dist/admin/registry.js +40 -4
- package/dist/admin/registry.js.map +1 -1
- package/dist/admin/server.d.ts +1 -0
- package/dist/admin/server.d.ts.map +1 -1
- package/dist/admin/server.js +5 -0
- package/dist/admin/server.js.map +1 -1
- package/dist/admin/styles/admin.css +57 -1
- package/dist/admin/surfaces/components/component-edit.d.ts +3 -0
- package/dist/admin/surfaces/components/component-edit.d.ts.map +1 -0
- package/dist/admin/surfaces/components/component-edit.js +235 -0
- package/dist/admin/surfaces/components/component-edit.js.map +1 -0
- package/dist/admin/surfaces/components/components-data.d.ts +153 -0
- package/dist/admin/surfaces/components/components-data.d.ts.map +1 -0
- package/dist/admin/surfaces/components/components-data.js +154 -0
- package/dist/admin/surfaces/components/components-data.js.map +1 -0
- package/dist/admin/surfaces/components/components-list.d.ts +3 -0
- package/dist/admin/surfaces/components/components-list.d.ts.map +1 -0
- package/dist/admin/surfaces/components/components-list.js +196 -0
- package/dist/admin/surfaces/components/components-list.js.map +1 -0
- package/dist/admin/surfaces/components/definition-canvas.d.ts +73 -0
- package/dist/admin/surfaces/components/definition-canvas.d.ts.map +1 -0
- package/dist/admin/surfaces/components/definition-canvas.js +207 -0
- package/dist/admin/surfaces/components/definition-canvas.js.map +1 -0
- package/dist/admin/surfaces/layouts/layout-editor-client.d.ts.map +1 -1
- package/dist/admin/surfaces/layouts/layout-editor-client.js +14 -2
- package/dist/admin/surfaces/layouts/layout-editor-client.js.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-data.d.ts +3 -14
- package/dist/admin/surfaces/layouts/layouts-data.d.ts.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-data.js +4 -7
- package/dist/admin/surfaces/layouts/layouts-data.js.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-list.d.ts.map +1 -1
- package/dist/admin/surfaces/layouts/layouts-list.js +16 -22
- package/dist/admin/surfaces/layouts/layouts-list.js.map +1 -1
- package/dist/admin/surfaces/settings/admin-features.d.ts.map +1 -1
- package/dist/admin/surfaces/settings/admin-features.js +1 -0
- package/dist/admin/surfaces/settings/admin-features.js.map +1 -1
- package/dist/api/bulk-routes.d.ts.map +1 -1
- package/dist/api/bulk-routes.js +25 -0
- package/dist/api/bulk-routes.js.map +1 -1
- package/dist/api/changeset-routes.d.ts.map +1 -1
- package/dist/api/changeset-routes.js +17 -3
- package/dist/api/changeset-routes.js.map +1 -1
- package/dist/api/component-routes.d.ts.map +1 -1
- package/dist/api/component-routes.js +228 -8
- package/dist/api/component-routes.js.map +1 -1
- package/dist/api/content-routes.d.ts.map +1 -1
- package/dist/api/content-routes.js +64 -0
- package/dist/api/content-routes.js.map +1 -1
- package/dist/api/feed-routes.d.ts +6 -6
- package/dist/api/graphql/resolvers.d.ts +4 -4
- package/dist/api/layout-routes.d.ts.map +1 -1
- package/dist/api/layout-routes.js +4 -17
- package/dist/api/layout-routes.js.map +1 -1
- package/dist/api/layout-write-guard.d.ts +80 -0
- package/dist/api/layout-write-guard.d.ts.map +1 -0
- package/dist/api/layout-write-guard.js +88 -0
- package/dist/api/layout-write-guard.js.map +1 -0
- package/dist/api/section-template-routes.d.ts.map +1 -1
- package/dist/api/section-template-routes.js +11 -0
- package/dist/api/section-template-routes.js.map +1 -1
- package/dist/api/translation-routes.d.ts.map +1 -1
- package/dist/api/translation-routes.js +8 -0
- package/dist/api/translation-routes.js.map +1 -1
- package/dist/api/version-routes.d.ts.map +1 -1
- package/dist/api/version-routes.js +20 -8
- package/dist/api/version-routes.js.map +1 -1
- package/dist/api/workflow-routes.d.ts.map +1 -1
- package/dist/api/workflow-routes.js +53 -8
- package/dist/api/workflow-routes.js.map +1 -1
- package/dist/db/analytics-queries.d.ts +3 -3
- package/dist/db/apply-snapshot.d.ts +75 -8
- package/dist/db/apply-snapshot.d.ts.map +1 -1
- package/dist/db/apply-snapshot.js +104 -8
- package/dist/db/apply-snapshot.js.map +1 -1
- package/dist/db/audit-queries.d.ts +12 -12
- package/dist/db/category-queries.d.ts +3 -3
- package/dist/db/changeset-audit-queries.d.ts +4 -4
- package/dist/db/changeset-queries.d.ts +7 -7
- package/dist/db/comment-queries.d.ts +8 -8
- package/dist/db/comment-subscription-queries.d.ts +2 -2
- package/dist/db/component-instance-queries.d.ts +197 -0
- package/dist/db/component-instance-queries.d.ts.map +1 -0
- package/dist/db/component-instance-queries.js +405 -0
- package/dist/db/component-instance-queries.js.map +1 -0
- package/dist/db/content-queries.d.ts.map +1 -1
- package/dist/db/content-queries.js +39 -2
- package/dist/db/content-queries.js.map +1 -1
- package/dist/db/extension-queries.d.ts +7 -7
- package/dist/db/feed-queries.d.ts +3 -3
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/layout-queries.d.ts +13 -1
- package/dist/db/layout-queries.d.ts.map +1 -1
- package/dist/db/layout-queries.js +52 -7
- package/dist/db/layout-queries.js.map +1 -1
- package/dist/db/locale-queries.d.ts.map +1 -1
- package/dist/db/locale-queries.js +0 -0
- package/dist/db/locale-queries.js.map +1 -1
- package/dist/db/media-queries.d.ts +12 -12
- package/dist/db/revision-queries.d.ts +3 -3
- package/dist/db/site-admin-queries.d.ts +2 -2
- package/dist/db/site-membership-queries.d.ts +3 -3
- package/dist/db/site-plugin-queries.d.ts +2 -2
- package/dist/db/site-queries.d.ts +6 -6
- package/dist/db/version-queries.d.ts +12 -12
- package/dist/db/webhook-queries.d.ts +11 -11
- package/dist/db/workflow-queries.d.ts +6 -6
- package/dist/db/workflow-queries.d.ts.map +1 -1
- package/dist/db/workflow-queries.js +11 -2
- package/dist/db/workflow-queries.js.map +1 -1
- package/dist/layout/comments-module.d.ts.map +1 -1
- package/dist/layout/comments-module.js +6 -2
- package/dist/layout/comments-module.js.map +1 -1
- package/dist/layout/component-refs.d.ts +465 -0
- package/dist/layout/component-refs.d.ts.map +1 -0
- package/dist/layout/component-refs.js +848 -0
- package/dist/layout/component-refs.js.map +1 -0
- package/dist/layout/index.d.ts +2 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +1 -0
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/module-permissions.d.ts +49 -1
- package/dist/layout/module-permissions.d.ts.map +1 -1
- package/dist/layout/module-permissions.js +117 -6
- package/dist/layout/module-permissions.js.map +1 -1
- package/dist/layout/module-render-context.d.ts +32 -0
- package/dist/layout/module-render-context.d.ts.map +1 -1
- package/dist/layout/module-render-context.js +32 -1
- package/dist/layout/module-render-context.js.map +1 -1
- package/dist/layout/modules-extended.d.ts.map +1 -1
- package/dist/layout/modules-extended.js +21 -11
- package/dist/layout/modules-extended.js.map +1 -1
- package/dist/layout/modules.d.ts.map +1 -1
- package/dist/layout/modules.js +15 -10
- package/dist/layout/modules.js.map +1 -1
- package/dist/layout/renderer.d.ts +51 -4
- package/dist/layout/renderer.d.ts.map +1 -1
- package/dist/layout/renderer.js +36 -38
- package/dist/layout/renderer.js.map +1 -1
- package/dist/layout/types.d.ts +42 -0
- package/dist/layout/types.d.ts.map +1 -1
- package/dist/layout/types.js +54 -11
- package/dist/layout/types.js.map +1 -1
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/components.d.ts +150 -0
- package/dist/schema/components.d.ts.map +1 -1
- package/dist/schema/components.js +50 -4
- package/dist/schema/components.js.map +1 -1
- package/dist/scope/inline-editing.d.ts.map +1 -1
- package/dist/scope/inline-editing.js +10 -0
- package/dist/scope/inline-editing.js.map +1 -1
- package/dist/scope/sticky-offset.d.ts +185 -0
- package/dist/scope/sticky-offset.d.ts.map +1 -0
- package/dist/scope/sticky-offset.js +432 -0
- package/dist/scope/sticky-offset.js.map +1 -0
- package/dist/site/styles.css +41 -3
- package/dist/versioning/scheduler.d.ts +8 -0
- package/dist/versioning/scheduler.d.ts.map +1 -1
- package/dist/versioning/scheduler.js +21 -7
- package/dist/versioning/scheduler.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { collectComponentInstances, componentDetachSource, componentRefKey, refreshComponentCopies, } from '../../layout/component-refs.js';
|
|
5
|
+
import { adminFetch } from '../../host-client/index.js';
|
|
6
|
+
import { applyInstanceReplacements, buildUpdateAll, detachInstanceKeepingEdits, diffLinkedInstances, isBaselineStale, linkedInstanceContentEquals, } from './component-fork.js';
|
|
7
|
+
import { ComponentForkDialog, EditorDialog, } from './editor-dialog.js';
|
|
8
|
+
import { componentErrorMessage } from './use-component-connect.js';
|
|
9
|
+
// ── The hook ─────────────────────────────────────────────────────────────────
|
|
10
|
+
export function useComponentFork({ layout, originalLayout, dispatch, library, inlineEditActive = false, inert = false, }) {
|
|
11
|
+
const [session, setSession] = React.useState(null);
|
|
12
|
+
const [phase, setPhase] = React.useState({ kind: 'asking', index: 0 });
|
|
13
|
+
const [usage, setUsage] = React.useState({});
|
|
14
|
+
// ── Session baselines ──────────────────────────────────────────────────────
|
|
15
|
+
//
|
|
16
|
+
// A link created during this session has no stored self to be compared with,
|
|
17
|
+
// and connecting/placing already wrote the definition — so its baseline is what
|
|
18
|
+
// it looked like the moment the link appeared. Recorded here, first sight
|
|
19
|
+
// winning, because otherwise "save as component, then fix the typo you just
|
|
20
|
+
// spotted" would write the fix into a fallback copy the page never renders.
|
|
21
|
+
// Entries for placements that later reach the server are simply ignored (the
|
|
22
|
+
// stored document wins in `diffLinkedInstances`), so this never goes stale in a
|
|
23
|
+
// way that matters.
|
|
24
|
+
const sessionBaselines = React.useRef(new Map());
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
const stored = new Set(collectComponentInstances(originalLayout).map(componentRefKey));
|
|
27
|
+
for (const instance of collectComponentInstances(layout)) {
|
|
28
|
+
const key = componentRefKey(instance);
|
|
29
|
+
if (stored.has(key) || sessionBaselines.current.has(key))
|
|
30
|
+
continue;
|
|
31
|
+
sessionBaselines.current.set(key, instance);
|
|
32
|
+
}
|
|
33
|
+
}, [layout, originalLayout]);
|
|
34
|
+
// ── Fallback-copy refresh (kywi-cms#147 T4 review, C1) ─────────────────────
|
|
35
|
+
//
|
|
36
|
+
// A linked section/container stores an inline copy of its definition and the
|
|
37
|
+
// editor edits THAT — so before anything can be edited, the copies are brought
|
|
38
|
+
// back in line with the definitions the library just loaded. Without it,
|
|
39
|
+
// update-all built the new definition out of a stale copy and reverted every
|
|
40
|
+
// newer edit of that component everywhere, silently, published pages included.
|
|
41
|
+
//
|
|
42
|
+
// Once per library SNAPSHOT: the components array is a fresh identity on load, on
|
|
43
|
+
// `add` (a component this session created) and on `update` (a definition this
|
|
44
|
+
// session wrote), and each of those genuinely changes what a copy should hold.
|
|
45
|
+
// The layout is a dependency because the effect reads it, not because a layout
|
|
46
|
+
// change should re-run this — the ref guard is what stops that, and it has to,
|
|
47
|
+
// since every edit changes the layout.
|
|
48
|
+
//
|
|
49
|
+
// The reducer refuses the dispatch on a document that is no longer pristine (see
|
|
50
|
+
// REFRESH_COMPONENT_COPIES), so an author already mid-edit when a slow library
|
|
51
|
+
// arrives keeps their work and is protected by `isBaselineStale` instead.
|
|
52
|
+
const refreshedForRef = React.useRef(null);
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
// A definition canvas has no placements to refresh — see `inert`.
|
|
55
|
+
if (inert)
|
|
56
|
+
return;
|
|
57
|
+
// Deliberately does NOT update `refreshedForRef` on this branch, so the
|
|
58
|
+
// effect retries against the SAME library snapshot once the session ends
|
|
59
|
+
// (`inlineEditActive` flipping back to false is itself a dependency below,
|
|
60
|
+
// which re-runs this effect) — see `inlineEditActive`'s doc for why landing
|
|
61
|
+
// mid-session is unsafe rather than merely late.
|
|
62
|
+
if (inlineEditActive)
|
|
63
|
+
return;
|
|
64
|
+
if (refreshedForRef.current === library.components)
|
|
65
|
+
return;
|
|
66
|
+
refreshedForRef.current = library.components;
|
|
67
|
+
const refreshed = refreshComponentCopies(layout, library.lookup);
|
|
68
|
+
if (refreshed === layout)
|
|
69
|
+
return;
|
|
70
|
+
dispatch({ type: 'REFRESH_COMPONENT_COPIES', layout: refreshed });
|
|
71
|
+
}, [library, layout, dispatch, inlineEditActive, inert]);
|
|
72
|
+
const close = React.useCallback(() => {
|
|
73
|
+
setSession(null);
|
|
74
|
+
setPhase({ kind: 'asking', index: 0 });
|
|
75
|
+
}, []);
|
|
76
|
+
// ── Opening ────────────────────────────────────────────────────────────────
|
|
77
|
+
const guardSave = React.useCallback((perform, mode = 'save') => {
|
|
78
|
+
// A definition save is the propagation; there is nothing to ask (see `inert`).
|
|
79
|
+
if (inert) {
|
|
80
|
+
void perform(layout);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// A save while the dialog is open would ask twice about the same edits.
|
|
84
|
+
if (session)
|
|
85
|
+
return;
|
|
86
|
+
const edits = diffLinkedInstances(originalLayout, layout, ref => sessionBaselines.current.get(componentRefKey(ref)));
|
|
87
|
+
if (edits.length === 0) {
|
|
88
|
+
void perform(layout);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const groups = groupEdits(edits, library);
|
|
92
|
+
setSession({ layout, groups, decisions: {}, perform, mode });
|
|
93
|
+
setPhase({ kind: 'asking', index: 0 });
|
|
94
|
+
// Blast radius, best-effort and in parallel: one usage read per component,
|
|
95
|
+
// fired now so the numbers are there by the time the owner reads them.
|
|
96
|
+
setUsage(Object.fromEntries(groups.map(g => [g.componentId, { state: 'pending' }])));
|
|
97
|
+
for (const group of groups) {
|
|
98
|
+
void fetchUsage(group.componentId).then(radius => {
|
|
99
|
+
setUsage(prev => ({ ...prev, [group.componentId]: radius }));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}, [session, layout, originalLayout, library, inert]);
|
|
103
|
+
// ── Executing ──────────────────────────────────────────────────────────────
|
|
104
|
+
const execute = React.useCallback(async (current) => {
|
|
105
|
+
setPhase({ kind: 'saving' });
|
|
106
|
+
const replacements = new Map();
|
|
107
|
+
const updated = [];
|
|
108
|
+
// Definitions this save has written, so the OTHER placements of the same
|
|
109
|
+
// component — the ones nobody edited — can be refreshed to it before the page
|
|
110
|
+
// is stored (kywi-cms#147 T4 review, C1). Without this, editing placement A,
|
|
111
|
+
// updating everywhere, then editing placement B and updating again rebuilt the
|
|
112
|
+
// definition from B's now-stale copy and destroyed A's edit inside one session.
|
|
113
|
+
const written = new Map();
|
|
114
|
+
for (const group of current.groups) {
|
|
115
|
+
const decision = current.decisions[group.componentId] ?? 'this-page-only';
|
|
116
|
+
if (decision === 'this-page-only') {
|
|
117
|
+
for (const edit of group.edits) {
|
|
118
|
+
replacements.set(componentRefKey(edit), detachInstanceKeepingEdits(edit, library.lookup(edit.componentId)));
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Every edit in the group agrees (that is what makes update-all
|
|
123
|
+
// available), so any of them defines the component; each placement still
|
|
124
|
+
// gets its own clean instance, since a clean module instance is built
|
|
125
|
+
// from the node it replaces.
|
|
126
|
+
const plans = group.edits.map(edit => buildUpdateAll(edit, {
|
|
127
|
+
source: library.lookup(edit.componentId),
|
|
128
|
+
resolveLink: library.lookup,
|
|
129
|
+
}));
|
|
130
|
+
const definition = plans[0]?.definition;
|
|
131
|
+
if (!definition || plans.some(plan => plan === null)) {
|
|
132
|
+
// Only reachable if the library changed under us between the question
|
|
133
|
+
// and the answer (`groupEdits` already withdrew update-all otherwise).
|
|
134
|
+
setPhase({
|
|
135
|
+
kind: 'failed',
|
|
136
|
+
message: `${alreadyUpdated(updated)}Could not work out what to save for ` +
|
|
137
|
+
`“${group.name ?? 'this component'}”, so the page was not saved.`,
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
let res;
|
|
142
|
+
try {
|
|
143
|
+
res = await adminFetch(`/api/v1/components/${group.componentId}`, {
|
|
144
|
+
method: 'PUT',
|
|
145
|
+
body: JSON.stringify({ definition }),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
setPhase({
|
|
150
|
+
kind: 'failed',
|
|
151
|
+
message: `${alreadyUpdated(updated)}Could not reach the server, so “${group.name ?? 'this component'}” was not updated and the page was not saved.`,
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
setPhase({
|
|
157
|
+
kind: 'failed',
|
|
158
|
+
message: `${alreadyUpdated(updated)}“${group.name ?? 'This component'}” was not updated: ${await componentErrorMessage(res)} The page was not saved.`,
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
updated.push(group.name ?? 'the component');
|
|
163
|
+
group.edits.forEach((edit, index) => {
|
|
164
|
+
replacements.set(componentRefKey(edit), plans[index].cleanInstance);
|
|
165
|
+
});
|
|
166
|
+
// The library is the editor's copy of what the server now holds — every
|
|
167
|
+
// later read of it (the rail's values, the canvas preview, the next
|
|
168
|
+
// staleness check) must see the definition this save just wrote.
|
|
169
|
+
const row = library.byId(group.componentId);
|
|
170
|
+
if (row) {
|
|
171
|
+
written.set(group.componentId, componentDetachSource({ ...row, definition }));
|
|
172
|
+
}
|
|
173
|
+
library.update(group.componentId, definition);
|
|
174
|
+
}
|
|
175
|
+
// One document, one history entry, handed to the reducer BY REFERENCE so
|
|
176
|
+
// the shell's MARK_SAVED can recognise it (see the action's doc).
|
|
177
|
+
//
|
|
178
|
+
// Two passes, in this order: the answers first (each edited placement becomes
|
|
179
|
+
// a clean instance of the new definition), then the copy refresh for the
|
|
180
|
+
// placements nobody edited. The refresh is a no-op on the ones just replaced —
|
|
181
|
+
// `buildUpdateAll` builds `cleanInstance` by applying the new definition to the
|
|
182
|
+
// placement, which is precisely what refreshing would compute.
|
|
183
|
+
const answered = applyInstanceReplacements(current.layout, replacements);
|
|
184
|
+
const resolved = written.size > 0
|
|
185
|
+
? refreshComponentCopies(answered, id => written.get(id) ?? null)
|
|
186
|
+
: answered;
|
|
187
|
+
dispatch({ type: 'APPLY_COMPONENT_FORK', layout: resolved });
|
|
188
|
+
let ok;
|
|
189
|
+
try {
|
|
190
|
+
ok = await Promise.resolve(current.perform(resolved));
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
ok = false;
|
|
194
|
+
}
|
|
195
|
+
if (ok === false) {
|
|
196
|
+
setPhase({
|
|
197
|
+
kind: 'failed',
|
|
198
|
+
message: `${alreadyUpdated(updated)}This page could not be saved, so it still has unsaved changes. ` +
|
|
199
|
+
'Try again — updating a component twice changes nothing.',
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
close();
|
|
204
|
+
}, [library, dispatch, close]);
|
|
205
|
+
const decide = React.useCallback((decision) => {
|
|
206
|
+
if (!session)
|
|
207
|
+
return;
|
|
208
|
+
if (phase.kind !== 'asking')
|
|
209
|
+
return;
|
|
210
|
+
const group = session.groups[phase.index];
|
|
211
|
+
if (!group)
|
|
212
|
+
return;
|
|
213
|
+
const next = {
|
|
214
|
+
...session,
|
|
215
|
+
decisions: { ...session.decisions, [group.componentId]: decision },
|
|
216
|
+
};
|
|
217
|
+
setSession(next);
|
|
218
|
+
if (phase.index + 1 < next.groups.length) {
|
|
219
|
+
setPhase({ kind: 'asking', index: phase.index + 1 });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
void execute(next);
|
|
223
|
+
}, [session, phase, execute]);
|
|
224
|
+
const retry = React.useCallback(() => {
|
|
225
|
+
if (session)
|
|
226
|
+
void execute(session);
|
|
227
|
+
}, [session, execute]);
|
|
228
|
+
// ── The dialog ─────────────────────────────────────────────────────────────
|
|
229
|
+
const dialog = React.useMemo(() => {
|
|
230
|
+
if (!session)
|
|
231
|
+
return null;
|
|
232
|
+
// A failure replaces the question rather than decorating it: the answers have
|
|
233
|
+
// already been given, and re-offering "Save to this page only" here would
|
|
234
|
+
// re-run whatever was decided instead of what the label says. One honest
|
|
235
|
+
// action — try the same thing again — plus a way to walk away.
|
|
236
|
+
if (phase.kind === 'failed') {
|
|
237
|
+
return (_jsx(EditorDialog, { title: "Could not finish saving", error: phase.message, cancelLabel: "Close", actions: [{ label: 'Try again', action: 'fork-retry', onClick: retry }], onCancel: close }));
|
|
238
|
+
}
|
|
239
|
+
// While saving, the last question stays on screen with its actions disabled —
|
|
240
|
+
// the owner keeps the context of what they just decided.
|
|
241
|
+
const index = phase.kind === 'asking' ? phase.index : session.groups.length - 1;
|
|
242
|
+
const group = session.groups[Math.min(index, session.groups.length - 1)];
|
|
243
|
+
if (!group)
|
|
244
|
+
return null;
|
|
245
|
+
return (_jsx(ComponentForkDialog, { componentName: group.name, blastRadius: usage[group.componentId] ?? { state: 'pending' }, saveMode: session.mode, step: index + 1, stepCount: session.groups.length, updateAllUnavailable: group.updateAllUnavailable, busy: phase.kind === 'saving', onUpdateAll: () => decide('update-all'), onThisPageOnly: () => decide('this-page-only'),
|
|
246
|
+
// Cancel/Escape aborts the WHOLE save: nothing is written, the document
|
|
247
|
+
// keeps every edit, and the page stays dirty. A half-applied fork would
|
|
248
|
+
// be worse than no save at all.
|
|
249
|
+
onCancel: close }));
|
|
250
|
+
}, [session, phase, usage, decide, retry, close]);
|
|
251
|
+
return React.useMemo(() => ({ dialog, guardSave }), [dialog, guardSave]);
|
|
252
|
+
}
|
|
253
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
254
|
+
function alreadyUpdated(updated) {
|
|
255
|
+
if (updated.length === 0)
|
|
256
|
+
return '';
|
|
257
|
+
const names = updated.map(name => `“${name}”`).join(', ');
|
|
258
|
+
return `${names} ${updated.length === 1 ? 'was' : 'were'} already updated everywhere. `;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Group the edits by component — one question per component, not per node.
|
|
262
|
+
*
|
|
263
|
+
* MULTIPLE COMPONENTS IN ONE SAVE are asked about one after another ("Component 1
|
|
264
|
+
* of 2"), because the answers are genuinely independent: updating the shared
|
|
265
|
+
* footer everywhere and keeping a hero tweak local is a perfectly ordinary pair
|
|
266
|
+
* of intentions, and a single combined dialog would either force one answer for
|
|
267
|
+
* both or turn into a form.
|
|
268
|
+
*
|
|
269
|
+
* TWO PLACEMENTS OF ONE COMPONENT edited DIFFERENTLY on the same page cannot both
|
|
270
|
+
* become the definition. Rather than picking one silently, "update all" is
|
|
271
|
+
* withdrawn with the reason; "this page only" still works and detaches both.
|
|
272
|
+
* Identical edits to both placements are fine and stay offered.
|
|
273
|
+
*
|
|
274
|
+
* A STALE COPY withdraws it too (kywi-cms#147 T4 review, C1). If what this page
|
|
275
|
+
* stored as the component's content is not what the component now holds, the
|
|
276
|
+
* definition built from it would revert whatever changed in between — everywhere,
|
|
277
|
+
* published pages included. `isBaselineStale` decides; the copies are normally
|
|
278
|
+
* refreshed long before this point, so reaching it means the library never loaded
|
|
279
|
+
* or arrived too late to be applied safely.
|
|
280
|
+
*/
|
|
281
|
+
function groupEdits(edits, library) {
|
|
282
|
+
const groups = new Map();
|
|
283
|
+
for (const edit of edits) {
|
|
284
|
+
const existing = groups.get(edit.componentId);
|
|
285
|
+
if (existing) {
|
|
286
|
+
existing.edits.push(edit);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
groups.set(edit.componentId, {
|
|
290
|
+
componentId: edit.componentId,
|
|
291
|
+
name: library.nameOf(edit.componentId),
|
|
292
|
+
edits: [edit],
|
|
293
|
+
updateAllUnavailable: null,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
for (const group of groups.values()) {
|
|
297
|
+
const first = group.edits[0];
|
|
298
|
+
const divergent = group.edits.some(edit => !linkedInstanceContentEquals(first.instance, edit.instance));
|
|
299
|
+
if (divergent) {
|
|
300
|
+
group.updateAllUnavailable =
|
|
301
|
+
`${group.edits.length} instances of this component on this page were edited differently, ` +
|
|
302
|
+
'so they cannot all become the component. Save them to this page only, or undo one of the edits.';
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const source = library.lookup(group.componentId);
|
|
306
|
+
const buildable = group.edits.every(edit => buildUpdateAll(edit, { source, resolveLink: library.lookup }) !== null);
|
|
307
|
+
if (!buildable) {
|
|
308
|
+
group.updateAllUnavailable =
|
|
309
|
+
'This component’s current content could not be loaded, so the editor cannot tell which of ' +
|
|
310
|
+
'its settings you changed. Save to this page only, or reload the page and try again.';
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
// The belt on C1: a definition built out of an out-of-date copy would revert
|
|
314
|
+
// whatever changed in between, everywhere.
|
|
315
|
+
if (group.edits.some(edit => isBaselineStale(edit, source))) {
|
|
316
|
+
group.updateAllUnavailable =
|
|
317
|
+
'This page’s copy of the component is out of date, so saving it to every instance would ' +
|
|
318
|
+
'undo newer changes. Save to this page only, then re-edit the component from an ' +
|
|
319
|
+
'up-to-date page.';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return [...groups.values()];
|
|
323
|
+
}
|
|
324
|
+
/** Where a component is placed — best-effort; a failure degrades the wording. */
|
|
325
|
+
async function fetchUsage(componentId) {
|
|
326
|
+
try {
|
|
327
|
+
const res = await adminFetch(`/api/v1/components/${componentId}/usage`);
|
|
328
|
+
if (!res.ok)
|
|
329
|
+
return { state: 'unknown' };
|
|
330
|
+
const body = (await res.json());
|
|
331
|
+
const data = body.data;
|
|
332
|
+
if (!data || typeof data.instances !== 'number' || typeof data.pages !== 'number') {
|
|
333
|
+
return { state: 'unknown' };
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
state: 'known',
|
|
337
|
+
instances: data.instances,
|
|
338
|
+
pages: data.pages,
|
|
339
|
+
trashedInstances: typeof data.trashedInstances === 'number' ? data.trashedInstances : 0,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return { state: 'unknown' };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=use-component-fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-fork.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-component-fork.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ComponentPlacementLookup } from '../../layout/component-refs.js';
|
|
2
|
+
import type { ModuleNode } from '../../layout/types.js';
|
|
3
|
+
import type { ComponentOption } from './component-picker-field.js';
|
|
4
|
+
/**
|
|
5
|
+
* use-component-library.ts
|
|
6
|
+
* The saved components an editor shell has loaded, and the projections both
|
|
7
|
+
* shells need from them (kywi-cms#147).
|
|
8
|
+
*
|
|
9
|
+
* WHY ONE HOOK FOR BOTH SHELLS. The admin Layout tab fetched module components
|
|
10
|
+
* and section components in two separate effects and kept them in two states; the
|
|
11
|
+
* front-of-site overlay fetched neither, which is why "Save as component" and the
|
|
12
|
+
* linked badge existed only in the admin editor even though the overlay is the
|
|
13
|
+
* PRIMARY editing surface (kywi-cms#94). One hook, one request, three types.
|
|
14
|
+
*
|
|
15
|
+
* The request is deliberately unfiltered (`GET /components`, no `?type=`): a
|
|
16
|
+
* document can hold links of all three kinds at once, every one of them needs a
|
|
17
|
+
* name for its pill and a definition for detach, and one round trip is cheaper
|
|
18
|
+
* than three. Best-effort throughout — a failed load leaves the library empty,
|
|
19
|
+
* which degrades pills to the generic "Component" and leaves save-as-component
|
|
20
|
+
* fully working (it only ever POSTs).
|
|
21
|
+
*/
|
|
22
|
+
/** A saved component as `GET /api/v1/components` returns it. */
|
|
23
|
+
export interface EditorComponent {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
moduleType: string | null;
|
|
28
|
+
definition: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface ComponentLibrary {
|
|
31
|
+
/** Every loaded component, all types. */
|
|
32
|
+
components: EditorComponent[];
|
|
33
|
+
byId: (componentId: string) => EditorComponent | null;
|
|
34
|
+
/** Display name for a linked instance's pill, or null when unknown. */
|
|
35
|
+
nameOf: (componentId: string) => string | null;
|
|
36
|
+
/** Picker options for one component type. */
|
|
37
|
+
optionsFor: (type: 'module' | 'section' | 'variantContainer') => ComponentOption[];
|
|
38
|
+
/**
|
|
39
|
+
* The canonical lookup materialisation reads (`componentDetachSource`), used by
|
|
40
|
+
* detach, by save-as-component's nested-link flattening, and by anything else
|
|
41
|
+
* that has to answer "what does this link render".
|
|
42
|
+
*/
|
|
43
|
+
lookup: ComponentPlacementLookup;
|
|
44
|
+
/** Module-component projection for the canvas preview (kywi-cms#46). */
|
|
45
|
+
resolveModule: (componentId: string) => {
|
|
46
|
+
props: Record<string, unknown>;
|
|
47
|
+
style: Record<string, string>;
|
|
48
|
+
moduleType: string;
|
|
49
|
+
} | null;
|
|
50
|
+
/**
|
|
51
|
+
* What a module node RENDERS: its own props/style for an ordinary node, and for
|
|
52
|
+
* a linked instance the definition read through the canonical placement rule
|
|
53
|
+
* (`resolveModulePlacement` — definition alone for a clean instance, definition
|
|
54
|
+
* then node for a legacy override one).
|
|
55
|
+
*
|
|
56
|
+
* The props rail needs exactly this (kywi-cms#147 T4): a clean instance carries
|
|
57
|
+
* no props of its own, so fields fed from `node.props` would show an empty form
|
|
58
|
+
* for a module full of content, and the first keystroke would look like it
|
|
59
|
+
* cleared everything. Same projection as the canvas preview and the public
|
|
60
|
+
* renderer, so what the rail shows is what the visitor sees.
|
|
61
|
+
*/
|
|
62
|
+
renderedModule: (node: Pick<ModuleNode, 'props' | 'style' | 'componentId'>) => {
|
|
63
|
+
props: Record<string, unknown>;
|
|
64
|
+
style: Record<string, string>;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Which module type's registry entry describes a node's editable FIELDS, or null
|
|
68
|
+
* when nothing honest can be shown (kywi-cms#147 T4 review, I2).
|
|
69
|
+
*
|
|
70
|
+
* For an ordinary node that is its own `type`. For a linked instance it is the
|
|
71
|
+
* COMPONENT's `moduleType`, because that is what the placement renders as
|
|
72
|
+
* (`materializeModuleNode` retypes the node) — and because a component placed
|
|
73
|
+
* from the palette picker is a `component` placeholder node, whose registry entry
|
|
74
|
+
* declares "Component Slug" and "Component ID" props. Those are link plumbing,
|
|
75
|
+
* not content: seeded from the resolved definition they read empty, and typing
|
|
76
|
+
* into "Component ID" writes a prop the fork deliberately strips.
|
|
77
|
+
*
|
|
78
|
+
* Null for a linked placeholder whose component does not resolve — there is no
|
|
79
|
+
* module type, so there are no fields, and inventing the placeholder's own two is
|
|
80
|
+
* how the rail came to show a form nobody could use.
|
|
81
|
+
*/
|
|
82
|
+
fieldModuleType: (node: Pick<ModuleNode, 'type' | 'componentId'>) => string | null;
|
|
83
|
+
/** Adopt a component the shell just created, so its name is available at once. */
|
|
84
|
+
add: (component: EditorComponent) => void;
|
|
85
|
+
/**
|
|
86
|
+
* Adopt a definition the shell just wrote (the save fork's `PUT`), so everything
|
|
87
|
+
* reading the library — the rail's field values, the canvas preview, the copy
|
|
88
|
+
* refresh, and the next fork's staleness check — sees the new content rather than
|
|
89
|
+
* the one the page loaded with (kywi-cms#147 T4 review, C1).
|
|
90
|
+
*/
|
|
91
|
+
update: (componentId: string, definition: unknown) => void;
|
|
92
|
+
}
|
|
93
|
+
export declare function useComponentLibrary(): ComponentLibrary;
|
|
94
|
+
//# sourceMappingURL=use-component-library.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-library.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-component-library.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAGvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAElE;;;;;;;;;;;;;;;;;GAiBG;AAEH,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,eAAe,GAAG,IAAI,CAAA;IACrD,uEAAuE;IACvE,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IAC9C,6CAA6C;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,kBAAkB,KAAK,eAAe,EAAE,CAAA;IAClF;;;;OAIG;IACH,MAAM,EAAE,wBAAwB,CAAA;IAChC,wEAAwE;IACxE,aAAa,EAAE,CACb,WAAW,EAAE,MAAM,KAChB;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACjG;;;;;;;;;;;OAWG;IACH,cAAc,EAAE,CACd,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,aAAa,CAAC,KACtD;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACtE;;;;;;;;;;;;;;;OAeG;IACH,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAClF,kFAAkF;IAClF,GAAG,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC;;;;;OAKG;IACH,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;CAC3D;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,CAkFtD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { componentDetachSource, resolveModulePlacement } from '../../layout/component-refs.js';
|
|
4
|
+
import { adminFetch } from '../../host-client/index.js';
|
|
5
|
+
import { resolveComponentDefinition } from './component-actions.js';
|
|
6
|
+
export function useComponentLibrary() {
|
|
7
|
+
const [components, setComponents] = useState([]);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
let cancelled = false;
|
|
10
|
+
adminFetch('/api/v1/components')
|
|
11
|
+
.then(r => (r.ok ? r.json() : Promise.reject(new Error(String(r.status)))))
|
|
12
|
+
.then((body) => {
|
|
13
|
+
if (!cancelled)
|
|
14
|
+
setComponents(body.data ?? []);
|
|
15
|
+
})
|
|
16
|
+
.catch(() => { });
|
|
17
|
+
return () => { cancelled = true; };
|
|
18
|
+
}, []);
|
|
19
|
+
const byId = useCallback((componentId) => components.find(c => c.id === componentId) ?? null, [components]);
|
|
20
|
+
const nameOf = useCallback((componentId) => byId(componentId)?.name ?? null, [byId]);
|
|
21
|
+
const optionsFor = useCallback((type) => components
|
|
22
|
+
.filter(c => c.type === type)
|
|
23
|
+
.map(c => ({ id: c.id, name: c.name, moduleType: c.moduleType })), [components]);
|
|
24
|
+
const lookup = useCallback((componentId) => componentDetachSource(byId(componentId)), [byId]);
|
|
25
|
+
const resolveModule = useCallback((componentId) => resolveComponentDefinition(byId(componentId)), [byId]);
|
|
26
|
+
const renderedModule = useCallback((node) => {
|
|
27
|
+
const resolved = node.componentId ? resolveComponentDefinition(byId(node.componentId)) : null;
|
|
28
|
+
if (!resolved)
|
|
29
|
+
return { props: node.props ?? {}, style: node.style ?? {} };
|
|
30
|
+
return resolveModulePlacement(node, resolved);
|
|
31
|
+
}, [byId]);
|
|
32
|
+
const fieldModuleType = useCallback((node) => {
|
|
33
|
+
if (!node.componentId)
|
|
34
|
+
return node.type;
|
|
35
|
+
const resolved = resolveComponentDefinition(byId(node.componentId));
|
|
36
|
+
if (resolved)
|
|
37
|
+
return resolved.moduleType;
|
|
38
|
+
// An unresolvable link on a placeholder node has no fields to offer.
|
|
39
|
+
return node.type === 'component' ? null : node.type;
|
|
40
|
+
}, [byId]);
|
|
41
|
+
const add = useCallback((component) => {
|
|
42
|
+
setComponents(prev => (prev.some(c => c.id === component.id) ? prev : [...prev, component]));
|
|
43
|
+
}, []);
|
|
44
|
+
const update = useCallback((componentId, definition) => {
|
|
45
|
+
setComponents(prev => prev.map(c => (c.id === componentId ? { ...c, definition } : c)));
|
|
46
|
+
}, []);
|
|
47
|
+
return useMemo(() => ({
|
|
48
|
+
components, byId, nameOf, optionsFor, lookup, resolveModule, renderedModule,
|
|
49
|
+
fieldModuleType, add, update,
|
|
50
|
+
}), [
|
|
51
|
+
components, byId, nameOf, optionsFor, lookup, resolveModule, renderedModule,
|
|
52
|
+
fieldModuleType, add, update,
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=use-component-library.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-component-library.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-component-library.ts"],"names":[],"mappings":"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"}
|
package/dist/admin/registry.d.ts
CHANGED
|
@@ -12,9 +12,18 @@ import type { KywiRole } from '../auth/roles.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* Feature-area taxonomy — the granularity at which config (`admin.features`) and
|
|
14
14
|
* runtime flags (`admin.features` site-settings key, T16) enable/disable admin.
|
|
15
|
-
*
|
|
15
|
+
* 24 areas.
|
|
16
16
|
*/
|
|
17
|
-
export type AdminArea = 'dashboard' | 'contentTree' | 'menus' | 'content' | 'approvals' | 'changesets' | 'search' | 'media' | 'users' | 'forms' | 'taxonomy' | 'trash' | 'comments' | 'translations' | 'feeds' | 'audiences' | 'experiments' | 'layouts'
|
|
17
|
+
export type AdminArea = 'dashboard' | 'contentTree' | 'menus' | 'content' | 'approvals' | 'changesets' | 'search' | 'media' | 'users' | 'forms' | 'taxonomy' | 'trash' | 'comments' | 'translations' | 'feeds' | 'audiences' | 'experiments' | 'layouts'
|
|
18
|
+
/**
|
|
19
|
+
* The reusable-components library (kywi-cms#147). Its own area, not a slice of
|
|
20
|
+
* `layouts`: a saved component is a live source that pages LINK to (editing one
|
|
21
|
+
* changes every page it is placed on), whereas a layout is a blueprint that is
|
|
22
|
+
* copied on use. A site that wants one and not the other — components without
|
|
23
|
+
* the standalone layout builder, say — can now say so, and disabling `layouts`
|
|
24
|
+
* no longer takes the components library with it.
|
|
25
|
+
*/
|
|
26
|
+
| 'components' | 'extensions' | 'plugins' | 'webServices' | 'settings' | 'contentTypes';
|
|
18
27
|
/** Runtime-checkable list of every area, in declaration order. */
|
|
19
28
|
export declare const ADMIN_AREAS: readonly AdminArea[];
|
|
20
29
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/admin/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhD;;;;;;;;GAQG;AAIH;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB,WAAW,GACX,aAAa,GACb,OAAO,GACP,SAAS,GACT,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,OAAO,GACP,OAAO,GACP,OAAO,GACP,UAAU,GACV,OAAO,GACP,UAAU,GACV,cAAc,GACd,OAAO,GACP,WAAW,GACX,aAAa,GACb,SAAS,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/admin/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhD;;;;;;;;GAQG;AAIH;;;;GAIG;AACH,MAAM,MAAM,SAAS,GACjB,WAAW,GACX,aAAa,GACb,OAAO,GACP,SAAS,GACT,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,OAAO,GACP,OAAO,GACP,OAAO,GACP,UAAU,GACV,OAAO,GACP,UAAU,GACV,cAAc,GACd,OAAO,GACP,WAAW,GACX,aAAa,GACb,SAAS;AACX;;;;;;;GAOG;GACD,YAAY,GACZ,YAAY,GACZ,SAAS,GACT,aAAa,GACb,UAAU,GACV,cAAc,CAAA;AAElB,kEAAkE;AAClE,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAyBlC,CAAA;AAIV;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,EAAE,EAAE,MAAM,CAAA;IACV,oDAAoD;IACpD,IAAI,EAAE,SAAS,CAAA;IACf;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,+FAA+F;IAC/F,GAAG,CAAC,EAAE,eAAe,CAAA;IACrB,gFAAgF;IAChF,OAAO,EAAE,QAAQ,CAAA;IACjB;;;;;;;;OAQG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAA;KAAE,CAAC,CAAA;CAC9D;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAA;IACV,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;IAC9C,iEAAiE;IACjE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAIvD,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAQ3C,CAAA;AAQV,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAkfxC,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,oBAAoB,CAAA"}
|
package/dist/admin/registry.js
CHANGED
|
@@ -18,6 +18,7 @@ export const ADMIN_AREAS = [
|
|
|
18
18
|
'audiences',
|
|
19
19
|
'experiments',
|
|
20
20
|
'layouts',
|
|
21
|
+
'components',
|
|
21
22
|
'extensions',
|
|
22
23
|
'plugins',
|
|
23
24
|
'webServices',
|
|
@@ -30,9 +31,9 @@ export const ADMIN_NAV_GROUPS = [
|
|
|
30
31
|
{ id: 'users', label: 'Users', href: '/admin/users', icon: 'users', order: 8 },
|
|
31
32
|
{ id: 'forms', label: 'Forms', href: '/admin/forms', icon: 'clipboard', order: 9 },
|
|
32
33
|
{ id: 'audiences', label: 'Audiences', href: '/admin/audiences', icon: 'target', order: 16 },
|
|
33
|
-
{ id: 'extensions', label: 'Extensions', href: '/admin/extensions', icon: 'blocks', order:
|
|
34
|
-
{ id: 'webServices', label: 'Web Services', href: '/admin/web-services', icon: 'globe', order:
|
|
35
|
-
{ id: 'settings', label: 'Settings', href: '/admin/settings', icon: 'settings', order:
|
|
34
|
+
{ id: 'extensions', label: 'Extensions', href: '/admin/extensions', icon: 'blocks', order: 20 },
|
|
35
|
+
{ id: 'webServices', label: 'Web Services', href: '/admin/web-services', icon: 'globe', order: 22 },
|
|
36
|
+
{ id: 'settings', label: 'Settings', href: '/admin/settings', icon: 'settings', order: 23 },
|
|
36
37
|
];
|
|
37
38
|
// ─── Surfaces ───────────────────────────────────────────────────────────────────
|
|
38
39
|
// Sourced by cross-referencing packages/core/src/admin/nav.ts (nav-visible set)
|
|
@@ -321,6 +322,41 @@ export const ADMIN_SURFACES = [
|
|
|
321
322
|
},
|
|
322
323
|
{ id: 'layouts.new', area: 'layouts', pattern: 'layouts/new', label: 'New Layout', minRole: 'editor', load: () => import('./surfaces/layouts/layouts-new.js') },
|
|
323
324
|
{ id: 'layouts.detail', area: 'layouts', pattern: 'layouts/:id', label: 'Layout', minRole: 'editor', load: () => import('./surfaces/layouts/layouts-detail.js') },
|
|
325
|
+
// components (kywi-cms#147) — the reusable-components library, promoted out of
|
|
326
|
+
// the read-only sub-panel it used to occupy on the Layouts page. Sits directly
|
|
327
|
+
// after Layouts: both are "reusable building blocks", and inserting here shifted
|
|
328
|
+
// every later top-level `order` by one (the #112 precedent).
|
|
329
|
+
//
|
|
330
|
+
// ON minRole. `component-routes.ts` gates reading and writing the library at
|
|
331
|
+
// `write` (⇒ author) and DELETE at `delete` (⇒ siteAdmin). Only the second of
|
|
332
|
+
// those is reproduced in the UI (the list withholds the Delete action below
|
|
333
|
+
// siteAdmin) — the read floor here is `editor`, matching every other nav
|
|
334
|
+
// surface, because `editor` is not the admin's convention by accident: no
|
|
335
|
+
// surface in this registry is reachable below it, so an author-floored
|
|
336
|
+
// Components page would be a one-item admin (no Dashboard, no Site Tree, no
|
|
337
|
+
// content lists) rather than an unlocked capability. `editor` satisfies the
|
|
338
|
+
// route's `write` gate, so nobody who reaches this page gets a 403 from it. If
|
|
339
|
+
// the admin ever grows an author floor, this is the line to revisit.
|
|
340
|
+
{
|
|
341
|
+
id: 'components.list',
|
|
342
|
+
area: 'components',
|
|
343
|
+
pattern: 'components',
|
|
344
|
+
label: 'Components',
|
|
345
|
+
minRole: 'editor',
|
|
346
|
+
nav: { section: 'primary', icon: 'boxes', order: 19 },
|
|
347
|
+
load: () => import('./surfaces/components/components-list.js'),
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
// The definition-editing canvas. Its own surface (not a panel on the list) so
|
|
351
|
+
// the `LayoutEditor` it mounts for section/container components stays in its
|
|
352
|
+
// own chunk and the list page loads none of it — the same split Layouts uses.
|
|
353
|
+
id: 'components.edit',
|
|
354
|
+
area: 'components',
|
|
355
|
+
pattern: 'components/:id',
|
|
356
|
+
label: 'Component',
|
|
357
|
+
minRole: 'editor',
|
|
358
|
+
load: () => import('./surfaces/components/component-edit.js'),
|
|
359
|
+
},
|
|
324
360
|
// extensions (group) — the extensions ADMIN surface manages subtypes/sets/
|
|
325
361
|
// attributes and import/export, all siteAdmin-gated (extension-routes.ts). The
|
|
326
362
|
// editor-gated `/extensions/for-content|/data` endpoints belong to the content
|
|
@@ -350,7 +386,7 @@ export const ADMIN_SURFACES = [
|
|
|
350
386
|
pattern: 'plugins',
|
|
351
387
|
label: 'Plugins',
|
|
352
388
|
minRole: 'siteAdmin',
|
|
353
|
-
nav: { section: 'primary', icon: 'puzzle', order:
|
|
389
|
+
nav: { section: 'primary', icon: 'puzzle', order: 21 },
|
|
354
390
|
load: () => import('./surfaces/plugins/plugins-index.js'),
|
|
355
391
|
},
|
|
356
392
|
// webServices (group)
|