@bettercms-ai/kit 2.0.0 → 2.0.2
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/adapters/lossy.d.ts +14 -6
- package/dist/adapters/lossy.js +23 -11
- package/dist/adapters/lossy.js.map +1 -1
- package/dist/adapters/prod-types.d.ts +46 -14
- package/dist/adapters/prod-types.js +37 -35
- package/dist/adapters/prod-types.js.map +1 -1
- package/dist/adapters/schema.d.ts +39 -0
- package/dist/adapters/schema.js +281 -82
- package/dist/adapters/schema.js.map +1 -1
- package/dist/context.d.ts +39 -0
- package/dist/context.js +40 -1
- package/dist/context.js.map +1 -1
- package/dist/controls/form-picker-register.d.ts +7 -1
- package/dist/controls/form-picker-register.js +1 -5
- package/dist/controls/form-picker-register.js.map +1 -1
- package/dist/controls/icon.d.ts +1 -1
- package/dist/controls/icon.js +1 -1
- package/dist/controls/icon.js.map +1 -1
- package/dist/controls/link.d.ts +11 -4
- package/dist/controls/link.js +120 -202
- package/dist/controls/link.js.map +1 -1
- package/dist/controls/location.js +3 -8
- package/dist/controls/location.js.map +1 -1
- package/dist/controls/media.d.ts +0 -14
- package/dist/controls/media.js +52 -95
- package/dist/controls/media.js.map +1 -1
- package/dist/controls/reference-services.d.ts +23 -2
- package/dist/controls/reference.d.ts +2 -2
- package/dist/controls/reference.js +79 -28
- package/dist/controls/reference.js.map +1 -1
- package/dist/controls/rich-text/article-shell.d.ts +145 -0
- package/dist/controls/rich-text/article-shell.js +514 -0
- package/dist/controls/rich-text/article-shell.js.map +1 -0
- package/dist/controls/rich-text/author.d.ts +25 -0
- package/dist/controls/rich-text/author.js +107 -0
- package/dist/controls/rich-text/author.js.map +1 -0
- package/dist/controls/rich-text/blocks.d.ts +19 -2
- package/dist/controls/rich-text/blocks.js +275 -638
- package/dist/controls/rich-text/blocks.js.map +1 -1
- package/dist/controls/rich-text/button.d.ts +23 -0
- package/dist/controls/rich-text/button.js +81 -0
- package/dist/controls/rich-text/button.js.map +1 -0
- package/dist/controls/rich-text/catalog.d.ts +2 -5
- package/dist/controls/rich-text/catalog.js +121 -7
- package/dist/controls/rich-text/catalog.js.map +1 -1
- package/dist/controls/rich-text/checklist.d.ts +24 -0
- package/dist/controls/rich-text/checklist.js +115 -0
- package/dist/controls/rich-text/checklist.js.map +1 -0
- package/dist/controls/rich-text/codec.d.ts +15 -1
- package/dist/controls/rich-text/codec.js +11 -1
- package/dist/controls/rich-text/codec.js.map +1 -1
- package/dist/controls/rich-text/columns.d.ts +41 -0
- package/dist/controls/rich-text/columns.js +181 -0
- package/dist/controls/rich-text/columns.js.map +1 -0
- package/dist/controls/rich-text/control.js +1 -0
- package/dist/controls/rich-text/control.js.map +1 -1
- package/dist/controls/rich-text/dom.d.ts +84 -4
- package/dist/controls/rich-text/dom.js +171 -10
- package/dist/controls/rich-text/dom.js.map +1 -1
- package/dist/controls/rich-text/editor.d.ts +4 -2
- package/dist/controls/rich-text/editor.js +199 -26
- package/dist/controls/rich-text/editor.js.map +1 -1
- package/dist/controls/rich-text/embed.d.ts +28 -0
- package/dist/controls/rich-text/embed.js +210 -0
- package/dist/controls/rich-text/embed.js.map +1 -0
- package/dist/controls/rich-text/footnote.d.ts +41 -0
- package/dist/controls/rich-text/footnote.js +185 -0
- package/dist/controls/rich-text/footnote.js.map +1 -0
- package/dist/controls/rich-text/gallery.d.ts +28 -0
- package/dist/controls/rich-text/gallery.js +167 -0
- package/dist/controls/rich-text/gallery.js.map +1 -0
- package/dist/controls/rich-text/link-form.d.ts +13 -1
- package/dist/controls/rich-text/link-form.js +41 -7
- package/dist/controls/rich-text/link-form.js.map +1 -1
- package/dist/controls/rich-text/ops.js +6 -0
- package/dist/controls/rich-text/ops.js.map +1 -1
- package/dist/controls/rich-text/related.d.ts +49 -0
- package/dist/controls/rich-text/related.js +233 -0
- package/dist/controls/rich-text/related.js.map +1 -0
- package/dist/controls/rich-text/toc.d.ts +47 -0
- package/dist/controls/rich-text/toc.js +160 -0
- package/dist/controls/rich-text/toc.js.map +1 -0
- package/dist/controls/slot-register.d.ts +6 -1
- package/dist/controls/slot-register.js +1 -4
- package/dist/controls/slot-register.js.map +1 -1
- package/dist/core/core.d.ts +11 -3
- package/dist/core/core.js +24 -6
- package/dist/core/core.js.map +1 -1
- package/dist/core/facets.d.ts +16 -0
- package/dist/core/facets.js +17 -1
- package/dist/core/facets.js.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/ledgers/exclusions.v1.js +141 -0
- package/dist/ledgers/exclusions.v1.js.map +1 -0
- package/dist/library/coverage.d.ts +13 -1
- package/dist/library/coverage.js +12 -1
- package/dist/library/coverage.js.map +1 -1
- package/dist/library/kit-catalog.json +158 -154
- package/dist/schema/types.d.ts +17 -0
- package/dist/schema/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing/coverage-extras.d.ts +8 -0
- package/dist/testing/coverage-extras.js +44 -0
- package/dist/testing/coverage-extras.js.map +1 -0
- package/dist/testing/exclusions.d.ts +30 -0
- package/dist/testing/exclusions.js +13 -0
- package/dist/testing/exclusions.js.map +1 -0
- package/dist/ui/autocomplete.d.ts +29 -1
- package/dist/ui/autocomplete.js +102 -47
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/button.js +3 -5
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/overlay.d.ts +4 -1
- package/dist/ui/overlay.js +2 -1
- package/dist/ui/overlay.js.map +1 -1
- package/dist/ui/toast.js +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { useKitEnv } from "../../context.js";
|
|
3
|
+
import { Button, IconButton } from "../../ui/button.js";
|
|
4
|
+
import { Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Popover, PopoverAnchor, PopoverContent, Tooltip } from "../../ui/overlay.js";
|
|
5
|
+
import { SchemaForm } from "../../engine/schema-form.js";
|
|
6
|
+
import { eventKey } from "../../lib/key-event.js";
|
|
7
|
+
import { FloatingPanel } from "../../ui/floating-panel.js";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { IconArrowsMove, IconCopy, IconDimensions, IconDots, IconLayoutSidebar, IconLayoutSidebarRight, IconSettings, IconTrash, IconX } from "@tabler/icons-react";
|
|
11
|
+
//#region src/controls/rich-text/article-shell.tsx
|
|
12
|
+
/**
|
|
13
|
+
* RICH TEXT · the article-block shell (V2 article-blocks.tsx ArticleBlock), shared by every article block: the one-open
|
|
14
|
+
* store (one panel or popover open at a time), the card the block draws in (a focusable group: Enter opens it, Escape
|
|
15
|
+
* closes it then leaves, ⌘Z / ⌘⇧Z, ⌘D, ⌥↑/↓, Backspace), the edit / remove chip on its top edge, the empty state, the
|
|
16
|
+
* schema form a panel or popover holds, and the floating properties panel (D54).
|
|
17
|
+
*
|
|
18
|
+
* surface "panel" ArticlePanelBlock: ArticleCard + useArticleOpen + ArticlePanel(ArticleForm) (pull quote, gallery,
|
|
19
|
+
* related posts)
|
|
20
|
+
* surface "popover" ArticlePopoverBlock: ArticleCard + useArticleOpen + a Popover of ArticleForm (embed, table of
|
|
21
|
+
* contents, author bio)
|
|
22
|
+
* surface "inline" ArticleCard without onOpen: the block edits in the text itself (key takeaways)
|
|
23
|
+
*/
|
|
24
|
+
var openArticle = null;
|
|
25
|
+
var articleSubs = /* @__PURE__ */ new Set();
|
|
26
|
+
var setOpenArticle = (next) => {
|
|
27
|
+
openArticle = next;
|
|
28
|
+
articleSubs.forEach((f) => f());
|
|
29
|
+
};
|
|
30
|
+
/** The article block open now, if any. */
|
|
31
|
+
var currentOpenArticle = () => openArticle;
|
|
32
|
+
function useOpenArticle() {
|
|
33
|
+
return React.useSyncExternalStore((cb) => (articleSubs.add(cb), () => void articleSubs.delete(cb)), () => openArticle, () => openArticle);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One block's place in the one-open store: `open(field?)` opens it (closing any other), `close()` closes it if it is
|
|
37
|
+
* the open one. It closes when the block unmounts, and a fresh insert (`autoFocus`) opens its form.
|
|
38
|
+
*/
|
|
39
|
+
function useArticleOpen({ ro, autoFocus }) {
|
|
40
|
+
const uid = React.useId();
|
|
41
|
+
const open = useOpenArticle();
|
|
42
|
+
const editing = open?.id === uid;
|
|
43
|
+
const openPanel = React.useCallback((k) => {
|
|
44
|
+
if (!ro) setOpenArticle({
|
|
45
|
+
id: uid,
|
|
46
|
+
focusKey: k ?? null,
|
|
47
|
+
nonce: (openArticle?.nonce ?? 0) + 1
|
|
48
|
+
});
|
|
49
|
+
}, [uid, ro]);
|
|
50
|
+
const close = React.useCallback(() => {
|
|
51
|
+
if (openArticle?.id === uid) setOpenArticle(null);
|
|
52
|
+
}, [uid]);
|
|
53
|
+
React.useEffect(() => () => {
|
|
54
|
+
if (openArticle?.id === uid) setOpenArticle(null);
|
|
55
|
+
}, [uid]);
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
if (autoFocus && !ro) openPanel();
|
|
58
|
+
}, []);
|
|
59
|
+
return {
|
|
60
|
+
open,
|
|
61
|
+
editing,
|
|
62
|
+
openPanel,
|
|
63
|
+
close
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
var controlOf = (el) => Array.from(el.querySelectorAll("textarea, input:not([type=hidden]), [contenteditable=true], [role=combobox]")).find((n) => n.getClientRects().length) ?? Array.from(el.querySelectorAll(":scope > :not(:first-child) button")).find((n) => n.getClientRects().length);
|
|
67
|
+
/** Focus the form row `key` names (`data-field-key`), else the first row. */
|
|
68
|
+
function focusField(root, key) {
|
|
69
|
+
if (!root) return;
|
|
70
|
+
const rows = Array.from(root.querySelectorAll("[data-field-key]"));
|
|
71
|
+
const row = key && rows.find((r) => r.getAttribute("data-field-key") === key) || rows[0];
|
|
72
|
+
if (!row) return;
|
|
73
|
+
row.scrollIntoView({ block: "nearest" });
|
|
74
|
+
controlOf(row)?.focus({ preventScroll: true });
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* V2 ArticleBlock onKeyDown, on the card itself (its own fields handle their own keys). With `onOpen` (a panel or
|
|
78
|
+
* popover block) Enter / Space open it and Escape closes it first; without (an inline block) Escape leaves the block.
|
|
79
|
+
*/
|
|
80
|
+
function articleCardKeyDown({ card, ctx, el, ro, editing = false, onOpen, onClose, focusCard }) {
|
|
81
|
+
return (e) => {
|
|
82
|
+
if (e.defaultPrevented || e.target !== card.current) return;
|
|
83
|
+
const mod = e.metaKey || e.ctrlKey;
|
|
84
|
+
const k = eventKey(e);
|
|
85
|
+
if (mod && k === "z") {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
e.stopPropagation();
|
|
88
|
+
if (e.shiftKey) ctx.redo?.();
|
|
89
|
+
else ctx.undo?.();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (onOpen && (e.key === "Enter" || e.key === " ")) {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
onOpen();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (e.key === "Escape") {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
if (editing) onClose?.();
|
|
101
|
+
else ctx.exit(el);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (ro) return;
|
|
105
|
+
if (mod && k === "d" && ctx.duplicate) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
e.stopPropagation();
|
|
108
|
+
ctx.duplicate(el);
|
|
109
|
+
} else if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown") && ctx.move) {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
ctx.move(el, e.key === "ArrowUp" ? -1 : 1);
|
|
112
|
+
focusCard();
|
|
113
|
+
} else if (e.key === "Backspace" || e.key === "Delete") {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
ctx.remove(el);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** The chip on the block's top edge (so it never covers the block's own text): Edit (with `onEdit`) and Remove. */
|
|
120
|
+
function ArticleActions({ label, ctx, el, editing = false, onEdit, tooltips = false }) {
|
|
121
|
+
if (ctx.readOnly) return null;
|
|
122
|
+
const tip = (content, button) => tooltips ? /* @__PURE__ */ jsx(Tooltip, {
|
|
123
|
+
content,
|
|
124
|
+
children: button
|
|
125
|
+
}) : button;
|
|
126
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
127
|
+
className: cn("absolute -top-3.5 right-2 z-[2] flex items-center gap-0.5 rounded-kit-md border border-kit-line bg-kit-surface-raised p-0.5 opacity-0 shadow-kit-sm transition-opacity", "group-hover/art:opacity-100 group-focus-within/art:opacity-100 pointer-coarse:opacity-100", editing && "opacity-100"),
|
|
128
|
+
children: [onEdit ? tip(`Edit ${label.toLowerCase()}`, /* @__PURE__ */ jsx(IconButton, {
|
|
129
|
+
size: "xs",
|
|
130
|
+
label: `Edit ${label.toLowerCase()}`,
|
|
131
|
+
onClick: onEdit,
|
|
132
|
+
children: /* @__PURE__ */ jsx(IconSettings, { stroke: 1.5 })
|
|
133
|
+
})) : null, tip(`Remove ${label.toLowerCase()}`, /* @__PURE__ */ jsx(IconButton, {
|
|
134
|
+
size: "xs",
|
|
135
|
+
label: `Remove ${label.toLowerCase()}`,
|
|
136
|
+
onClick: () => ctx.remove(el),
|
|
137
|
+
className: "hover:text-kit-danger-fg",
|
|
138
|
+
children: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 })
|
|
139
|
+
}))]
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The card an article block draws in. With `onOpen` (panel / popover) it is pressable: a click opens it on the field
|
|
144
|
+
* clicked (`data-field`), and it shows the open ring while `editing`. Without, it is the inline block's quiet group.
|
|
145
|
+
*/
|
|
146
|
+
var ArticleCard = React.forwardRef(function ArticleCard({ label, kind, ro, editing = false, onOpen, onKeyDown, children }, ref) {
|
|
147
|
+
if (!onOpen) return /* @__PURE__ */ jsx("div", {
|
|
148
|
+
ref,
|
|
149
|
+
role: "group",
|
|
150
|
+
tabIndex: 0,
|
|
151
|
+
"aria-label": label,
|
|
152
|
+
"aria-roledescription": "block",
|
|
153
|
+
"data-rt-isolate": "",
|
|
154
|
+
"data-article-block": kind,
|
|
155
|
+
onKeyDown,
|
|
156
|
+
className: "group/art relative rounded-kit-lg outline-none transition-shadow duration-150 focus-visible:shadow-[0_0_0_1px_var(--kit-line-strong)]",
|
|
157
|
+
children
|
|
158
|
+
});
|
|
159
|
+
return /* @__PURE__ */ jsx("div", {
|
|
160
|
+
ref,
|
|
161
|
+
role: "group",
|
|
162
|
+
tabIndex: 0,
|
|
163
|
+
"aria-label": `${label}${editing ? ", editing" : ""}`,
|
|
164
|
+
"aria-roledescription": "block",
|
|
165
|
+
"aria-haspopup": "dialog",
|
|
166
|
+
"aria-expanded": editing,
|
|
167
|
+
"data-rt-isolate": "",
|
|
168
|
+
"data-article-block": kind,
|
|
169
|
+
onKeyDown,
|
|
170
|
+
onClick: (e) => {
|
|
171
|
+
if (ro || !e.currentTarget.contains(e.target)) return;
|
|
172
|
+
const t = e.target;
|
|
173
|
+
if (t.closest("button,a,[role=menuitem]")) return;
|
|
174
|
+
onOpen(t.closest("[data-field]")?.getAttribute("data-field") ?? null);
|
|
175
|
+
},
|
|
176
|
+
className: cn("group/art relative rounded-kit-lg outline-none transition-shadow duration-150", !ro && "cursor-pointer", "focus-visible:shadow-[0_0_0_1px_var(--kit-line-strong)]", editing ? "shadow-[0_0_0_2px_var(--kit-accent)] focus-visible:shadow-[0_0_0_2px_var(--kit-accent)]" : !ro && "hover:shadow-[0_0_0_1px_var(--kit-line)]"),
|
|
177
|
+
children
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
/** The block's settings, built from its schema (V2 ArticleForm: the kit SchemaForm, flush to the panel's top). */
|
|
181
|
+
function ArticleForm({ schema, label, value, onChange, ro }) {
|
|
182
|
+
const { onOpenReference } = useKitEnv();
|
|
183
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
184
|
+
"data-article-form": "",
|
|
185
|
+
className: "min-w-0",
|
|
186
|
+
children: [/* @__PURE__ */ jsx(SchemaForm, {
|
|
187
|
+
schema,
|
|
188
|
+
value,
|
|
189
|
+
onChange,
|
|
190
|
+
variant: "panel",
|
|
191
|
+
scroll: false,
|
|
192
|
+
readOnly: ro,
|
|
193
|
+
rootLabel: label,
|
|
194
|
+
env: {
|
|
195
|
+
docId: `article:${schema.name}`,
|
|
196
|
+
docTitle: label
|
|
197
|
+
},
|
|
198
|
+
onOpenReference
|
|
199
|
+
}), /* @__PURE__ */ jsx("style", { children: `[data-article-form] [data-kit-form] section:first-of-type { border-top-width: 0; margin-top: 0; padding-top: 0; }` })]
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/** V2 article-blocks.tsx Empty: a block with nothing to show yet. */
|
|
203
|
+
var ArticleEmpty = ({ icon, title, action, onAction, ro }) => /* @__PURE__ */ jsxs("div", {
|
|
204
|
+
className: "flex items-center gap-3 rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-4 py-3.5",
|
|
205
|
+
children: [
|
|
206
|
+
/* @__PURE__ */ jsx("span", {
|
|
207
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-kit-md border border-kit-line bg-kit-control-bg text-kit-fg-2 [&_svg]:size-4",
|
|
208
|
+
children: icon
|
|
209
|
+
}),
|
|
210
|
+
/* @__PURE__ */ jsx("span", {
|
|
211
|
+
className: "min-w-0 flex-1 text-kit-sm font-medium text-kit-fg",
|
|
212
|
+
children: title
|
|
213
|
+
}),
|
|
214
|
+
ro ? null : /* @__PURE__ */ jsx(Button, {
|
|
215
|
+
size: "sm",
|
|
216
|
+
onClick: onAction,
|
|
217
|
+
children: action
|
|
218
|
+
})
|
|
219
|
+
]
|
|
220
|
+
});
|
|
221
|
+
function ArticlePanel({ label, anchor, nonce, focusKey, onClose, focusBack, ctx, el, children }) {
|
|
222
|
+
const panel = React.useRef(null);
|
|
223
|
+
const titleId = React.useId();
|
|
224
|
+
const container = anchor?.closest("[data-expanded-editor]") ?? null;
|
|
225
|
+
React.useEffect(() => {
|
|
226
|
+
const timers = [];
|
|
227
|
+
let landed = false;
|
|
228
|
+
for (const ms of [
|
|
229
|
+
40,
|
|
230
|
+
90,
|
|
231
|
+
160,
|
|
232
|
+
260,
|
|
233
|
+
420,
|
|
234
|
+
640
|
|
235
|
+
]) timers.push(window.setTimeout(() => {
|
|
236
|
+
const root = panel.current?.el() ?? null;
|
|
237
|
+
if (!root?.querySelector("[data-field-key]")) return;
|
|
238
|
+
const a = document.activeElement;
|
|
239
|
+
const lost = !a || a === document.body || a.hasAttribute("data-expanded-editor") || a.hasAttribute("data-article-block");
|
|
240
|
+
if (root.contains(a)) return void (landed = true);
|
|
241
|
+
if (!landed || lost) focusField(root, focusKey);
|
|
242
|
+
if (root.contains(document.activeElement)) landed = true;
|
|
243
|
+
}, ms));
|
|
244
|
+
return () => timers.forEach((t) => window.clearTimeout(t));
|
|
245
|
+
}, [nonce]);
|
|
246
|
+
React.useEffect(() => panel.current?.announce(`${label} properties.`), [label]);
|
|
247
|
+
const header = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
248
|
+
/* @__PURE__ */ jsx("span", {
|
|
249
|
+
id: titleId,
|
|
250
|
+
className: "min-w-0 flex-1 truncate text-kit-sm font-medium text-kit-fg",
|
|
251
|
+
children: label
|
|
252
|
+
}),
|
|
253
|
+
/* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsx(MenuTrigger, { render: /* @__PURE__ */ jsx(IconButton, {
|
|
254
|
+
size: "sm",
|
|
255
|
+
label: `${label} actions`,
|
|
256
|
+
children: /* @__PURE__ */ jsx(IconDots, { stroke: 1.5 })
|
|
257
|
+
}) }), /* @__PURE__ */ jsxs(MenuContent, {
|
|
258
|
+
align: "end",
|
|
259
|
+
className: "w-52",
|
|
260
|
+
children: [
|
|
261
|
+
ctx.readOnly || !ctx.duplicate ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
262
|
+
icon: /* @__PURE__ */ jsx(IconCopy, { stroke: 1.5 }),
|
|
263
|
+
onClick: () => ctx.duplicate?.(el),
|
|
264
|
+
children: "Duplicate"
|
|
265
|
+
}), /* @__PURE__ */ jsx(MenuSeparator, {})] }),
|
|
266
|
+
/* @__PURE__ */ jsx(MenuItem, {
|
|
267
|
+
icon: /* @__PURE__ */ jsx(IconArrowsMove, { stroke: 1.5 }),
|
|
268
|
+
onClick: () => panel.current?.keyboard("move"),
|
|
269
|
+
children: "Move panel"
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ jsx(MenuItem, {
|
|
272
|
+
icon: /* @__PURE__ */ jsx(IconDimensions, { stroke: 1.5 }),
|
|
273
|
+
onClick: () => panel.current?.keyboard("resize"),
|
|
274
|
+
children: "Resize panel"
|
|
275
|
+
}),
|
|
276
|
+
/* @__PURE__ */ jsx(MenuItem, {
|
|
277
|
+
icon: /* @__PURE__ */ jsx(IconLayoutSidebar, { stroke: 1.5 }),
|
|
278
|
+
onClick: () => panel.current?.dock("left"),
|
|
279
|
+
children: "Dock left"
|
|
280
|
+
}),
|
|
281
|
+
/* @__PURE__ */ jsx(MenuItem, {
|
|
282
|
+
icon: /* @__PURE__ */ jsx(IconLayoutSidebarRight, { stroke: 1.5 }),
|
|
283
|
+
onClick: () => panel.current?.dock("right"),
|
|
284
|
+
children: "Dock right"
|
|
285
|
+
}),
|
|
286
|
+
ctx.readOnly ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuSeparator, {}), /* @__PURE__ */ jsx(MenuItem, {
|
|
287
|
+
danger: true,
|
|
288
|
+
icon: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 }),
|
|
289
|
+
onClick: () => ctx.remove(el),
|
|
290
|
+
children: "Remove"
|
|
291
|
+
})] })
|
|
292
|
+
]
|
|
293
|
+
})] }),
|
|
294
|
+
/* @__PURE__ */ jsx(IconButton, {
|
|
295
|
+
size: "sm",
|
|
296
|
+
label: "Close properties",
|
|
297
|
+
tooltip: "Close",
|
|
298
|
+
onClick: () => (onClose(), focusBack()),
|
|
299
|
+
children: /* @__PURE__ */ jsx(IconX, { stroke: 1.5 })
|
|
300
|
+
})
|
|
301
|
+
] });
|
|
302
|
+
return /* @__PURE__ */ jsx(FloatingPanel, {
|
|
303
|
+
ref: panel,
|
|
304
|
+
id: "article-properties",
|
|
305
|
+
labelledBy: titleId,
|
|
306
|
+
anchor,
|
|
307
|
+
container,
|
|
308
|
+
header,
|
|
309
|
+
onClose,
|
|
310
|
+
focusBack,
|
|
311
|
+
onPanelKeyDown: (e) => {
|
|
312
|
+
if ((e.metaKey || e.ctrlKey) && !e.altKey && eventKey(e) === "z" && !e.defaultPrevented) {
|
|
313
|
+
e.preventDefault();
|
|
314
|
+
if (e.shiftKey) ctx.redo?.();
|
|
315
|
+
else ctx.undo?.();
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
sheetFooter: /* @__PURE__ */ jsx(Button, {
|
|
319
|
+
size: "sm",
|
|
320
|
+
variant: "primary",
|
|
321
|
+
onClick: () => (onClose(), focusBack()),
|
|
322
|
+
children: "Done"
|
|
323
|
+
}),
|
|
324
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
325
|
+
className: "pb-3 pt-1",
|
|
326
|
+
children
|
|
327
|
+
})
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* A panel article block (V2 ArticleBlock surface "panel": Gallery, Related posts): the shell's card + chip, its
|
|
332
|
+
* settings in the floating properties panel, one undo step per field edit.
|
|
333
|
+
*/
|
|
334
|
+
function ArticlePanelBlock({ label, kind, schema, m, set, el, ctx, autoFocus, openOnInsert = true, toForm, fromForm, children }) {
|
|
335
|
+
const card = React.useRef(null);
|
|
336
|
+
const [cardEl, setCardEl] = React.useState(null);
|
|
337
|
+
const ro = !!ctx.readOnly;
|
|
338
|
+
const { open, editing, openPanel, close } = useArticleOpen({
|
|
339
|
+
ro,
|
|
340
|
+
autoFocus: autoFocus && openOnInsert
|
|
341
|
+
});
|
|
342
|
+
const cardRef = React.useCallback((n) => {
|
|
343
|
+
card.current = n;
|
|
344
|
+
if (n) setCardEl(n);
|
|
345
|
+
}, []);
|
|
346
|
+
const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);
|
|
347
|
+
React.useEffect(() => {
|
|
348
|
+
if (autoFocus && !openOnInsert && !ro) focusCard();
|
|
349
|
+
}, []);
|
|
350
|
+
const modelRef = React.useRef(m);
|
|
351
|
+
modelRef.current = m;
|
|
352
|
+
/** a form edit joins the body's one undo history (D54); typing coalesces per field */
|
|
353
|
+
const commit = (v) => {
|
|
354
|
+
const prev = modelRef.current;
|
|
355
|
+
const before = toForm(prev);
|
|
356
|
+
const field = Object.keys(v).find((k) => JSON.stringify(v[k]) !== JSON.stringify(before[k])) ?? "";
|
|
357
|
+
set(() => fromForm(v, prev), `Edit ${label.toLowerCase()} ${field}`);
|
|
358
|
+
};
|
|
359
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(ArticleCard, {
|
|
360
|
+
ref: cardRef,
|
|
361
|
+
label,
|
|
362
|
+
kind,
|
|
363
|
+
ro,
|
|
364
|
+
editing,
|
|
365
|
+
onOpen: openPanel,
|
|
366
|
+
onKeyDown: articleCardKeyDown({
|
|
367
|
+
card,
|
|
368
|
+
ctx,
|
|
369
|
+
el,
|
|
370
|
+
ro,
|
|
371
|
+
editing,
|
|
372
|
+
onOpen: openPanel,
|
|
373
|
+
onClose: close,
|
|
374
|
+
focusCard
|
|
375
|
+
}),
|
|
376
|
+
children: [children(openPanel, ro), /* @__PURE__ */ jsx(ArticleActions, {
|
|
377
|
+
label,
|
|
378
|
+
ctx,
|
|
379
|
+
el,
|
|
380
|
+
editing,
|
|
381
|
+
onEdit: () => editing ? close() : openPanel(),
|
|
382
|
+
tooltips: true
|
|
383
|
+
})]
|
|
384
|
+
}), editing ? /* @__PURE__ */ jsx(ArticlePanel, {
|
|
385
|
+
label,
|
|
386
|
+
anchor: cardEl,
|
|
387
|
+
nonce: open.nonce,
|
|
388
|
+
focusKey: open.focusKey,
|
|
389
|
+
onClose: close,
|
|
390
|
+
focusBack: focusCard,
|
|
391
|
+
ctx,
|
|
392
|
+
el,
|
|
393
|
+
children: /* @__PURE__ */ jsx(ArticleForm, {
|
|
394
|
+
schema,
|
|
395
|
+
label,
|
|
396
|
+
value: toForm(m),
|
|
397
|
+
onChange: commit,
|
|
398
|
+
ro
|
|
399
|
+
})
|
|
400
|
+
}) : null] });
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* A popover article block (V2 ArticleBlock surface "popover": TOC, Embed, Author bio): the shell's card + chip, its
|
|
404
|
+
* settings in a popover from the card's top-right corner, one undo step per field edit.
|
|
405
|
+
*/
|
|
406
|
+
function ArticlePopoverBlock({ label, kind, schema, m, set, el, ctx, autoFocus, openOnInsert = true, toForm, fromForm, children }) {
|
|
407
|
+
const card = React.useRef(null);
|
|
408
|
+
const form = React.useRef(null);
|
|
409
|
+
const ro = !!ctx.readOnly;
|
|
410
|
+
const { open, editing, openPanel: openPopover, close } = useArticleOpen({
|
|
411
|
+
ro,
|
|
412
|
+
autoFocus: autoFocus && openOnInsert
|
|
413
|
+
});
|
|
414
|
+
const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);
|
|
415
|
+
React.useEffect(() => {
|
|
416
|
+
if (autoFocus && !openOnInsert && !ro) focusCard();
|
|
417
|
+
}, []);
|
|
418
|
+
const modelRef = React.useRef(m);
|
|
419
|
+
modelRef.current = m;
|
|
420
|
+
const nonce = editing ? open.nonce : 0;
|
|
421
|
+
React.useEffect(() => {
|
|
422
|
+
if (!nonce) return;
|
|
423
|
+
const id = requestAnimationFrame(() => focusField(form.current, currentOpenArticle()?.focusKey ?? null));
|
|
424
|
+
return () => cancelAnimationFrame(id);
|
|
425
|
+
}, [nonce]);
|
|
426
|
+
/** a form edit joins the body's one undo history (D54); typing coalesces per field */
|
|
427
|
+
const commit = (v) => {
|
|
428
|
+
const prev = modelRef.current;
|
|
429
|
+
const before = toForm(prev);
|
|
430
|
+
const field = Object.keys(v).find((k) => JSON.stringify(v[k]) !== JSON.stringify(before[k])) ?? "";
|
|
431
|
+
set(() => fromForm(v, prev), `Edit ${label.toLowerCase()} ${field}`);
|
|
432
|
+
};
|
|
433
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
434
|
+
open: editing,
|
|
435
|
+
onOpenChange: (o, d) => {
|
|
436
|
+
if (o) return;
|
|
437
|
+
close();
|
|
438
|
+
if (d?.reason !== "outside-press") focusCard();
|
|
439
|
+
},
|
|
440
|
+
children: [/* @__PURE__ */ jsxs(ArticleCard, {
|
|
441
|
+
ref: card,
|
|
442
|
+
label,
|
|
443
|
+
kind,
|
|
444
|
+
ro,
|
|
445
|
+
editing,
|
|
446
|
+
onOpen: openPopover,
|
|
447
|
+
onKeyDown: articleCardKeyDown({
|
|
448
|
+
card,
|
|
449
|
+
ctx,
|
|
450
|
+
el,
|
|
451
|
+
ro,
|
|
452
|
+
editing,
|
|
453
|
+
onOpen: openPopover,
|
|
454
|
+
onClose: close,
|
|
455
|
+
focusCard
|
|
456
|
+
}),
|
|
457
|
+
children: [
|
|
458
|
+
children(openPopover, ro),
|
|
459
|
+
/* @__PURE__ */ jsx(ArticleActions, {
|
|
460
|
+
label,
|
|
461
|
+
ctx,
|
|
462
|
+
el,
|
|
463
|
+
editing,
|
|
464
|
+
onEdit: () => editing ? close() : openPopover(),
|
|
465
|
+
tooltips: true
|
|
466
|
+
}),
|
|
467
|
+
/* @__PURE__ */ jsx(PopoverAnchor, { render: /* @__PURE__ */ jsx("span", {
|
|
468
|
+
"aria-hidden": true,
|
|
469
|
+
className: "pointer-events-none absolute right-2 top-2 size-px"
|
|
470
|
+
}) })
|
|
471
|
+
]
|
|
472
|
+
}), editing ? /* @__PURE__ */ jsxs(PopoverContent, {
|
|
473
|
+
align: "end",
|
|
474
|
+
side: "bottom",
|
|
475
|
+
arrowPadding: 0,
|
|
476
|
+
className: "w-[340px] p-0",
|
|
477
|
+
initialFocus: false,
|
|
478
|
+
finalFocus: false,
|
|
479
|
+
onKeyDown: (e) => {
|
|
480
|
+
if ((e.metaKey || e.ctrlKey) && !e.altKey && eventKey(e) === "z" && !e.defaultPrevented) {
|
|
481
|
+
e.preventDefault();
|
|
482
|
+
if (e.shiftKey) ctx.redo?.();
|
|
483
|
+
else ctx.undo?.();
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
487
|
+
className: "flex h-10 items-center gap-2 border-b border-kit-line-subtle pl-4 pr-1.5",
|
|
488
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
489
|
+
className: "flex-1 truncate text-kit-sm font-medium text-kit-fg",
|
|
490
|
+
children: label
|
|
491
|
+
}), /* @__PURE__ */ jsx(IconButton, {
|
|
492
|
+
size: "sm",
|
|
493
|
+
label: "Close",
|
|
494
|
+
onClick: () => (close(), focusCard()),
|
|
495
|
+
children: /* @__PURE__ */ jsx(IconX, { stroke: 1.5 })
|
|
496
|
+
})]
|
|
497
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
498
|
+
ref: form,
|
|
499
|
+
className: "max-h-[min(480px,70vh)] overflow-y-auto pb-2",
|
|
500
|
+
children: /* @__PURE__ */ jsx(ArticleForm, {
|
|
501
|
+
schema,
|
|
502
|
+
label,
|
|
503
|
+
value: toForm(m),
|
|
504
|
+
onChange: commit,
|
|
505
|
+
ro
|
|
506
|
+
})
|
|
507
|
+
})]
|
|
508
|
+
}) : null]
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
//#endregion
|
|
512
|
+
export { ArticleActions, ArticleCard, ArticleEmpty, ArticleForm, ArticlePanel, ArticlePanelBlock, ArticlePopoverBlock, articleCardKeyDown, currentOpenArticle, focusField, useArticleOpen, useOpenArticle };
|
|
513
|
+
|
|
514
|
+
//# sourceMappingURL=article-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article-shell.js","names":[],"sources":["../../../src/controls/rich-text/article-shell.tsx"],"sourcesContent":["/**\n * RICH TEXT · the article-block shell (V2 article-blocks.tsx ArticleBlock), shared by every article block: the one-open\n * store (one panel or popover open at a time), the card the block draws in (a focusable group: Enter opens it, Escape\n * closes it then leaves, ⌘Z / ⌘⇧Z, ⌘D, ⌥↑/↓, Backspace), the edit / remove chip on its top edge, the empty state, the\n * schema form a panel or popover holds, and the floating properties panel (D54).\n *\n * surface \"panel\" ArticlePanelBlock: ArticleCard + useArticleOpen + ArticlePanel(ArticleForm) (pull quote, gallery,\n * related posts)\n * surface \"popover\" ArticlePopoverBlock: ArticleCard + useArticleOpen + a Popover of ArticleForm (embed, table of\n * contents, author bio)\n * surface \"inline\" ArticleCard without onOpen: the block edits in the text itself (key takeaways)\n */\nimport * as React from \"react\";\nimport { IconArrowsMove, IconCopy, IconDimensions, IconDots, IconLayoutSidebar, IconLayoutSidebarRight, IconSettings, IconTrash, IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../lib/cn\";\nimport { eventKey } from \"../../lib/key-event\";\nimport { Button, IconButton } from \"../../ui/button\";\nimport { Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Popover, PopoverAnchor, PopoverContent, Tooltip } from \"../../ui/overlay\";\nimport { FloatingPanel, type FloatingPanelHandle } from \"../../ui/floating-panel\";\nimport { SchemaForm } from \"../../engine/schema-form\";\nimport { useKitEnv } from \"../../context\";\nimport type { TypeSchema } from \"../../schema/types\";\nimport type { IslandEl } from \"./dom\";\nimport type { IslandCtx } from \"./blocks\";\n\n/* ── one article panel open at a time (V2 useOpenArticle) ── */\nexport interface OpenArticle {\n id: string;\n /** the field the panel focuses when it opens (a click on the preview's `data-field`), null = the first */\n focusKey: string | null;\n /** bumps on every open, so re-opening the open block refocuses */\n nonce: number;\n}\nlet openArticle: OpenArticle | null = null;\nconst articleSubs = new Set<() => void>();\nconst setOpenArticle = (next: OpenArticle | null) => {\n openArticle = next;\n articleSubs.forEach((f) => f());\n};\n/** The article block open now, if any. */\nexport const currentOpenArticle = () => openArticle;\nexport function useOpenArticle() {\n return React.useSyncExternalStore(\n (cb) => (articleSubs.add(cb), () => void articleSubs.delete(cb)),\n () => openArticle,\n () => openArticle,\n );\n}\n\n/**\n * One block's place in the one-open store: `open(field?)` opens it (closing any other), `close()` closes it if it is\n * the open one. It closes when the block unmounts, and a fresh insert (`autoFocus`) opens its form.\n */\nexport function useArticleOpen({ ro, autoFocus }: { ro: boolean; autoFocus: boolean }) {\n const uid = React.useId();\n const open = useOpenArticle();\n const editing = open?.id === uid;\n const openPanel = React.useCallback((k?: string | null) => {\n if (!ro) setOpenArticle({ id: uid, focusKey: k ?? null, nonce: (openArticle?.nonce ?? 0) + 1 });\n }, [uid, ro]);\n const close = React.useCallback(() => {\n if (openArticle?.id === uid) setOpenArticle(null);\n }, [uid]);\n React.useEffect(() => () => {\n if (openArticle?.id === uid) setOpenArticle(null);\n }, [uid]);\n React.useEffect(() => {\n // a fresh insert opens its form\n if (autoFocus && !ro) openPanel();\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n return { open, editing, openPanel, close };\n}\n\nconst controlOf = (el: HTMLElement) =>\n Array.from(el.querySelectorAll<HTMLElement>(\"textarea, input:not([type=hidden]), [contenteditable=true], [role=combobox]\")).find((n) => n.getClientRects().length) ??\n Array.from(el.querySelectorAll<HTMLElement>(\":scope > :not(:first-child) button\")).find((n) => n.getClientRects().length);\n/** Focus the form row `key` names (`data-field-key`), else the first row. */\nexport function focusField(root: HTMLElement | null, key: string | null) {\n if (!root) return;\n const rows = Array.from(root.querySelectorAll<HTMLElement>(\"[data-field-key]\"));\n const row = (key && rows.find((r) => r.getAttribute(\"data-field-key\") === key)) || rows[0];\n if (!row) return;\n row.scrollIntoView({ block: \"nearest\" });\n controlOf(row)?.focus({ preventScroll: true });\n}\n\n/**\n * V2 ArticleBlock onKeyDown, on the card itself (its own fields handle their own keys). With `onOpen` (a panel or\n * popover block) Enter / Space open it and Escape closes it first; without (an inline block) Escape leaves the block.\n */\nexport function articleCardKeyDown({\n card, ctx, el, ro, editing = false, onOpen, onClose, focusCard,\n}: {\n card: React.RefObject<HTMLElement | null>;\n ctx: IslandCtx;\n el: IslandEl;\n ro: boolean;\n editing?: boolean;\n onOpen?: () => void;\n onClose?: () => void;\n focusCard: () => void;\n}) {\n return (e: React.KeyboardEvent) => {\n if (e.defaultPrevented || e.target !== card.current) return;\n const mod = e.metaKey || e.ctrlKey;\n const k = eventKey(e);\n if (mod && k === \"z\") {\n e.preventDefault();\n e.stopPropagation();\n if (e.shiftKey) ctx.redo?.();\n else ctx.undo?.();\n return;\n }\n if (onOpen && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault();\n onOpen();\n return;\n }\n if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n if (editing) onClose?.();\n else ctx.exit(el);\n return;\n }\n if (ro) return;\n if (mod && k === \"d\" && ctx.duplicate) {\n e.preventDefault();\n e.stopPropagation();\n ctx.duplicate(el);\n } else if (e.altKey && (e.key === \"ArrowUp\" || e.key === \"ArrowDown\") && ctx.move) {\n e.preventDefault();\n ctx.move(el, e.key === \"ArrowUp\" ? -1 : 1);\n focusCard();\n } else if (e.key === \"Backspace\" || e.key === \"Delete\") {\n e.preventDefault();\n ctx.remove(el);\n }\n };\n}\n\n/** The chip on the block's top edge (so it never covers the block's own text): Edit (with `onEdit`) and Remove. */\nexport function ArticleActions({\n label, ctx, el, editing = false, onEdit, tooltips = false,\n}: {\n label: string;\n ctx: IslandCtx;\n el: IslandEl;\n editing?: boolean;\n /** toggles the block's panel or popover; absent on an inline block */\n onEdit?: () => void;\n /** each button also says what it does in a tooltip */\n tooltips?: boolean;\n}) {\n if (ctx.readOnly) return null;\n const tip = (content: string, button: React.ReactElement) => (tooltips ? <Tooltip content={content}>{button}</Tooltip> : button);\n return (\n <div\n className={cn(\n \"absolute -top-3.5 right-2 z-[2] flex items-center gap-0.5 rounded-kit-md border border-kit-line bg-kit-surface-raised p-0.5 opacity-0 shadow-kit-sm transition-opacity\",\n \"group-hover/art:opacity-100 group-focus-within/art:opacity-100 pointer-coarse:opacity-100\",\n editing && \"opacity-100\",\n )}\n >\n {onEdit\n ? tip(\n `Edit ${label.toLowerCase()}`,\n <IconButton size=\"xs\" label={`Edit ${label.toLowerCase()}`} onClick={onEdit}>\n <IconSettings stroke={1.5} />\n </IconButton>,\n )\n : null}\n {tip(\n `Remove ${label.toLowerCase()}`,\n <IconButton size=\"xs\" label={`Remove ${label.toLowerCase()}`} onClick={() => ctx.remove(el)} className=\"hover:text-kit-danger-fg\">\n <IconTrash stroke={1.5} />\n </IconButton>,\n )}\n </div>\n );\n}\n\n/**\n * The card an article block draws in. With `onOpen` (panel / popover) it is pressable: a click opens it on the field\n * clicked (`data-field`), and it shows the open ring while `editing`. Without, it is the inline block's quiet group.\n */\nexport const ArticleCard = React.forwardRef<\n HTMLDivElement,\n { label: string; kind: string; ro: boolean; editing?: boolean; onOpen?: (field: string | null) => void; onKeyDown: (e: React.KeyboardEvent) => void; children: React.ReactNode }\n>(function ArticleCard({ label, kind, ro, editing = false, onOpen, onKeyDown, children }, ref) {\n if (!onOpen)\n return (\n <div\n ref={ref}\n role=\"group\"\n tabIndex={0}\n aria-label={label}\n aria-roledescription=\"block\"\n data-rt-isolate=\"\"\n data-article-block={kind}\n onKeyDown={onKeyDown}\n className=\"group/art relative rounded-kit-lg outline-none transition-shadow duration-150 focus-visible:shadow-[0_0_0_1px_var(--kit-line-strong)]\"\n >\n {children}\n </div>\n );\n return (\n <div\n ref={ref}\n role=\"group\"\n tabIndex={0}\n aria-label={`${label}${editing ? \", editing\" : \"\"}`}\n aria-roledescription=\"block\"\n aria-haspopup=\"dialog\"\n aria-expanded={editing}\n data-rt-isolate=\"\"\n data-article-block={kind}\n onKeyDown={onKeyDown}\n onClick={(e) => {\n if (ro || !e.currentTarget.contains(e.target as Node)) return;\n const t = e.target as HTMLElement;\n if (t.closest(\"button,a,[role=menuitem]\")) return;\n onOpen(t.closest<HTMLElement>(\"[data-field]\")?.getAttribute(\"data-field\") ?? null);\n }}\n className={cn(\n \"group/art relative rounded-kit-lg outline-none transition-shadow duration-150\",\n !ro && \"cursor-pointer\",\n \"focus-visible:shadow-[0_0_0_1px_var(--kit-line-strong)]\",\n editing ? \"shadow-[0_0_0_2px_var(--kit-accent)] focus-visible:shadow-[0_0_0_2px_var(--kit-accent)]\" : !ro && \"hover:shadow-[0_0_0_1px_var(--kit-line)]\",\n )}\n >\n {children}\n </div>\n );\n});\n\n/** The block's settings, built from its schema (V2 ArticleForm: the kit SchemaForm, flush to the panel's top). */\nexport function ArticleForm({\n schema, label, value, onChange, ro,\n}: {\n schema: TypeSchema;\n label: string;\n value: Record<string, unknown>;\n onChange: (v: Record<string, unknown>) => void;\n ro: boolean;\n}) {\n // a form of its own (its docId keys its expand and full-screen state), but the host's way to open a reference is the\n // outer form's: Open on a picked author or story, and on a picker-made draft's toast\n const { onOpenReference } = useKitEnv();\n return (\n <div data-article-form=\"\" className=\"min-w-0\">\n <SchemaForm schema={schema} value={value} onChange={onChange} variant=\"panel\" scroll={false} readOnly={ro} rootLabel={label} env={{ docId: `article:${schema.name}`, docTitle: label }} onOpenReference={onOpenReference} />\n <style>{`[data-article-form] [data-kit-form] section:first-of-type { border-top-width: 0; margin-top: 0; padding-top: 0; }`}</style>\n </div>\n );\n}\n\n/** V2 article-blocks.tsx Empty: a block with nothing to show yet. */\nexport const ArticleEmpty = ({ icon, title, action, onAction, ro }: { icon: React.ReactNode; title: string; action: string; onAction: () => void; ro: boolean }) => (\n <div className=\"flex items-center gap-3 rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-4 py-3.5\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-kit-md border border-kit-line bg-kit-control-bg text-kit-fg-2 [&_svg]:size-4\">{icon}</span>\n <span className=\"min-w-0 flex-1 text-kit-sm font-medium text-kit-fg\">{title}</span>\n {ro ? null : (\n <Button size=\"sm\" onClick={onAction}>\n {action}\n </Button>\n )}\n </div>\n);\n\n/* ── Floating properties panel (D54 primitive; V2 ArticlePanel) ─────────────── */\nexport function ArticlePanel({\n label, anchor, nonce, focusKey, onClose, focusBack, ctx, el, children,\n}: {\n label: string;\n anchor: HTMLElement | null;\n nonce: number;\n focusKey: string | null;\n onClose: () => void;\n focusBack: () => void;\n ctx: IslandCtx;\n el: IslandEl;\n children: React.ReactNode;\n}) {\n const panel = React.useRef<FloatingPanelHandle>(null);\n const titleId = React.useId();\n const container = anchor?.closest<HTMLElement>(\"[data-expanded-editor]\") ?? null;\n React.useEffect(() => {\n // the panel mounts (and, inside the expanded editor, docks) over a few frames; focus lands on the field asked for,\n // and is put back while it is lost (never taken from somewhere the person went)\n const timers: number[] = [];\n let landed = false;\n for (const ms of [40, 90, 160, 260, 420, 640]) {\n timers.push(\n window.setTimeout(() => {\n const root = panel.current?.el() ?? null;\n if (!root?.querySelector(\"[data-field-key]\")) return;\n const a = document.activeElement as HTMLElement | null;\n const lost = !a || a === document.body || a.hasAttribute(\"data-expanded-editor\") || a.hasAttribute(\"data-article-block\");\n if (root.contains(a)) return void (landed = true);\n if (!landed || lost) focusField(root, focusKey);\n if (root.contains(document.activeElement)) landed = true;\n }, ms),\n );\n }\n return () => timers.forEach((t) => window.clearTimeout(t));\n }, [nonce]); // eslint-disable-line react-hooks/exhaustive-deps\n React.useEffect(() => panel.current?.announce(`${label} properties.`), [label]);\n const header = (\n <>\n <span id={titleId} className=\"min-w-0 flex-1 truncate text-kit-sm font-medium text-kit-fg\">{label}</span>\n <Menu>\n <MenuTrigger\n render={\n <IconButton size=\"sm\" label={`${label} actions`}>\n <IconDots stroke={1.5} />\n </IconButton>\n }\n />\n <MenuContent align=\"end\" className=\"w-52\">\n {ctx.readOnly || !ctx.duplicate ? null : (\n <>\n <MenuItem icon={<IconCopy stroke={1.5} />} onClick={() => ctx.duplicate?.(el)}>Duplicate</MenuItem>\n <MenuSeparator />\n </>\n )}\n <MenuItem icon={<IconArrowsMove stroke={1.5} />} onClick={() => panel.current?.keyboard(\"move\")}>Move panel</MenuItem>\n <MenuItem icon={<IconDimensions stroke={1.5} />} onClick={() => panel.current?.keyboard(\"resize\")}>Resize panel</MenuItem>\n <MenuItem icon={<IconLayoutSidebar stroke={1.5} />} onClick={() => panel.current?.dock(\"left\")}>Dock left</MenuItem>\n <MenuItem icon={<IconLayoutSidebarRight stroke={1.5} />} onClick={() => panel.current?.dock(\"right\")}>Dock right</MenuItem>\n {ctx.readOnly ? null : (\n <>\n <MenuSeparator />\n <MenuItem danger icon={<IconTrash stroke={1.5} />} onClick={() => ctx.remove(el)}>Remove</MenuItem>\n </>\n )}\n </MenuContent>\n </Menu>\n <IconButton size=\"sm\" label=\"Close properties\" tooltip=\"Close\" onClick={() => (onClose(), focusBack())}>\n <IconX stroke={1.5} />\n </IconButton>\n </>\n );\n return (\n <FloatingPanel\n ref={panel}\n id=\"article-properties\"\n labelledBy={titleId}\n anchor={anchor}\n container={container}\n header={header}\n onClose={onClose}\n focusBack={focusBack}\n onPanelKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && !e.altKey && eventKey(e) === \"z\" && !e.defaultPrevented) {\n e.preventDefault();\n if (e.shiftKey) ctx.redo?.();\n else ctx.undo?.();\n }\n }}\n sheetFooter={\n <Button size=\"sm\" variant=\"primary\" onClick={() => (onClose(), focusBack())}>\n Done\n </Button>\n }\n >\n <div className=\"pb-3 pt-1\">{children}</div>\n </FloatingPanel>\n );\n}\n\n/* ── The two article block surfaces ────────────────────────────────── */\n/**\n * A panel article block (V2 ArticleBlock surface \"panel\": Gallery, Related posts): the shell's card + chip, its\n * settings in the floating properties panel, one undo step per field edit.\n */\nexport function ArticlePanelBlock<M extends object>({\n label, kind, schema, m, set, el, ctx, autoFocus, openOnInsert = true, toForm, fromForm, children,\n}: {\n label: string;\n kind: string;\n schema: TypeSchema;\n m: M;\n set: (fn: (m: M) => M, label?: string) => void;\n el: IslandEl;\n ctx: IslandCtx;\n autoFocus: boolean;\n /** V2 article-blocks.tsx:127-133: a fresh insert opens the panel, except Related posts, which focuses the card */\n openOnInsert?: boolean;\n toForm: (m: M) => Record<string, unknown>;\n fromForm: (v: Record<string, unknown>, prev: M) => M;\n children: (open: (field?: string | null) => void, ro: boolean) => React.ReactNode;\n}) {\n const card = React.useRef<HTMLDivElement | null>(null);\n const [cardEl, setCardEl] = React.useState<HTMLDivElement | null>(null);\n const ro = !!ctx.readOnly;\n const { open, editing, openPanel, close } = useArticleOpen({ ro, autoFocus: autoFocus && openOnInsert });\n // a stable ref: a new callback each render would pass null for a moment and remount the panel\n const cardRef = React.useCallback((n: HTMLDivElement | null) => {\n card.current = n;\n if (n) setCardEl(n);\n }, []);\n const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);\n React.useEffect(() => {\n if (autoFocus && !openOnInsert && !ro) focusCard();\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n const modelRef = React.useRef(m);\n modelRef.current = m;\n\n /** a form edit joins the body's one undo history (D54); typing coalesces per field */\n const commit = (v: Record<string, unknown>) => {\n const prev = modelRef.current;\n const before = toForm(prev);\n const field = Object.keys(v).find((k) => JSON.stringify(v[k]) !== JSON.stringify(before[k])) ?? \"\";\n set(() => fromForm(v, prev), `Edit ${label.toLowerCase()} ${field}`);\n };\n\n return (\n <>\n <ArticleCard\n ref={cardRef}\n label={label}\n kind={kind}\n ro={ro}\n editing={editing}\n onOpen={openPanel}\n onKeyDown={articleCardKeyDown({ card, ctx, el, ro, editing, onOpen: openPanel, onClose: close, focusCard })}\n >\n {children(openPanel, ro)}\n <ArticleActions label={label} ctx={ctx} el={el} editing={editing} onEdit={() => (editing ? close() : openPanel())} tooltips />\n </ArticleCard>\n {editing ? (\n <ArticlePanel label={label} anchor={cardEl} nonce={open!.nonce} focusKey={open!.focusKey} onClose={close} focusBack={focusCard} ctx={ctx} el={el}>\n <ArticleForm schema={schema} label={label} value={toForm(m)} onChange={commit} ro={ro} />\n </ArticlePanel>\n ) : null}\n </>\n );\n}\n\n/**\n * A popover article block (V2 ArticleBlock surface \"popover\": TOC, Embed, Author bio): the shell's card + chip, its\n * settings in a popover from the card's top-right corner, one undo step per field edit.\n */\nexport function ArticlePopoverBlock<M extends object>({\n label, kind, schema, m, set, el, ctx, autoFocus, openOnInsert = true, toForm, fromForm, children,\n}: {\n label: string;\n kind: string;\n schema: TypeSchema;\n m: M;\n set: (fn: (m: M) => M, label?: string) => void;\n el: IslandEl;\n ctx: IslandCtx;\n autoFocus: boolean;\n /** V2 article-blocks.tsx:127-133: a fresh insert opens the form, except TOC and Related, which focus the card */\n openOnInsert?: boolean;\n toForm: (m: M) => Record<string, unknown>;\n fromForm: (v: Record<string, unknown>, prev: M) => M;\n /** the preview; `open(field)` opens the popover on that field (the empty state's action) */\n children: (open: (field?: string | null) => void, ro: boolean) => React.ReactNode;\n}) {\n const card = React.useRef<HTMLDivElement | null>(null);\n const form = React.useRef<HTMLDivElement | null>(null);\n const ro = !!ctx.readOnly;\n const { open, editing, openPanel: openPopover, close } = useArticleOpen({ ro, autoFocus: autoFocus && openOnInsert });\n const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);\n React.useEffect(() => {\n if (autoFocus && !openOnInsert && !ro) focusCard();\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n const modelRef = React.useRef(m);\n modelRef.current = m;\n const nonce = editing ? open!.nonce : 0;\n React.useEffect(() => {\n if (!nonce) return;\n const id = requestAnimationFrame(() => focusField(form.current, currentOpenArticle()?.focusKey ?? null));\n return () => cancelAnimationFrame(id);\n }, [nonce]);\n\n /** a form edit joins the body's one undo history (D54); typing coalesces per field */\n const commit = (v: Record<string, unknown>) => {\n const prev = modelRef.current;\n const before = toForm(prev);\n const field = Object.keys(v).find((k) => JSON.stringify(v[k]) !== JSON.stringify(before[k])) ?? \"\";\n set(() => fromForm(v, prev), `Edit ${label.toLowerCase()} ${field}`);\n };\n\n return (\n <Popover\n open={editing}\n onOpenChange={(o, d) => {\n if (o) return;\n close();\n // V2 onCloseAutoFocus: back on the block, unless the press that closed it went somewhere else\n if (d?.reason !== \"outside-press\") focusCard();\n }}\n >\n <ArticleCard\n ref={card}\n label={label}\n kind={kind}\n ro={ro}\n editing={editing}\n onOpen={openPopover}\n onKeyDown={articleCardKeyDown({ card, ctx, el, ro, editing, onOpen: openPopover, onClose: close, focusCard })}\n >\n {children(openPopover, ro)}\n <ArticleActions label={label} ctx={ctx} el={el} editing={editing} onEdit={() => (editing ? close() : openPopover())} tooltips />\n {/* the popover opens from the block's top-right corner, over the block, wherever the block sits */}\n <PopoverAnchor render={<span aria-hidden className=\"pointer-events-none absolute right-2 top-2 size-px\" />} />\n </ArticleCard>\n {editing ? (\n <PopoverContent\n align=\"end\"\n side=\"bottom\"\n // the anchor is a 1px dot at the card's top-right: no arrow inset, so the popup's edge sits on it (V2 AB:259-300)\n arrowPadding={0}\n className=\"w-[340px] p-0\"\n initialFocus={false}\n finalFocus={false}\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && !e.altKey && eventKey(e) === \"z\" && !e.defaultPrevented) {\n e.preventDefault();\n if (e.shiftKey) ctx.redo?.();\n else ctx.undo?.();\n }\n }}\n >\n <div className=\"flex h-10 items-center gap-2 border-b border-kit-line-subtle pl-4 pr-1.5\">\n <span className=\"flex-1 truncate text-kit-sm font-medium text-kit-fg\">{label}</span>\n <IconButton size=\"sm\" label=\"Close\" onClick={() => (close(), focusCard())}>\n <IconX stroke={1.5} />\n </IconButton>\n </div>\n <div ref={form} className=\"max-h-[min(480px,70vh)] overflow-y-auto pb-2\">\n <ArticleForm schema={schema} label={label} value={toForm(m)} onChange={commit} ro={ro} />\n </div>\n </PopoverContent>\n ) : null}\n </Popover>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { TypeSchema } from "../../schema/types";
|
|
2
|
+
import type { IslandCtx } from "./blocks";
|
|
3
|
+
import type { IslandEl } from "./dom";
|
|
4
|
+
/** The editor's model: the referenced entry's id ("" = none picked). The PT form wraps it as `{ _ref }`. */
|
|
5
|
+
export interface AuthorModel {
|
|
6
|
+
author: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const AUTHOR_LABEL = "Author bio";
|
|
9
|
+
export declare const authorDefault: () => AuthorModel;
|
|
10
|
+
/** The popover form; `to` is the authors collection from the field's config. */
|
|
11
|
+
export declare const authorSchema: (to: string[]) => TypeSchema;
|
|
12
|
+
export declare function AuthorPreview({ m, onPick, ro }: {
|
|
13
|
+
m: AuthorModel;
|
|
14
|
+
onPick: () => void;
|
|
15
|
+
ro: boolean;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function AuthorBlock({ m, set, el, ctx, autoFocus, collection, }: {
|
|
18
|
+
m: AuthorModel;
|
|
19
|
+
set: (fn: (m: AuthorModel) => AuthorModel, label?: string) => void;
|
|
20
|
+
el: IslandEl;
|
|
21
|
+
ctx: IslandCtx;
|
|
22
|
+
autoFocus: boolean;
|
|
23
|
+
/** the authors collection the reference picks from (the field's config) */
|
|
24
|
+
collection: string;
|
|
25
|
+
}): import("react/jsx-runtime").JSX.Element;
|