@bettercms-ai/kit 2.0.0-next.0 → 2.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/lossy.d.ts +5 -1
- package/dist/adapters/lossy.js +10 -5
- package/dist/adapters/lossy.js.map +1 -1
- package/dist/adapters/prod-types.d.ts +9 -22
- package/dist/adapters/prod-types.js +36 -35
- package/dist/adapters/prod-types.js.map +1 -1
- package/dist/adapters/schema.js +56 -39
- 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/link.js +112 -196
- 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/rich-text/article-shell.d.ts +145 -0
- package/dist/controls/rich-text/article-shell.js +510 -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 +104 -0
- package/dist/controls/rich-text/author.js.map +1 -0
- package/dist/controls/rich-text/blocks.d.ts +13 -2
- package/dist/controls/rich-text/blocks.js +86 -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 +85 -0
- package/dist/controls/rich-text/button.js.map +1 -0
- package/dist/controls/rich-text/catalog.d.ts +3 -3
- package/dist/controls/rich-text/catalog.js +20 -4
- 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 +1 -1
- package/dist/controls/rich-text/codec.js +4 -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/dom.d.ts +5 -4
- package/dist/controls/rich-text/dom.js +13 -4
- package/dist/controls/rich-text/dom.js.map +1 -1
- package/dist/controls/rich-text/editor.js +92 -10
- 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 +3 -1
- package/dist/controls/rich-text/link-form.js +2 -2
- package/dist/controls/rich-text/link-form.js.map +1 -1
- package/dist/controls/rich-text/related.d.ts +36 -0
- package/dist/controls/rich-text/related.js +213 -0
- package/dist/controls/rich-text/related.js.map +1 -0
- package/dist/controls/rich-text/toc.d.ts +46 -0
- package/dist/controls/rich-text/toc.js +161 -0
- package/dist/controls/rich-text/toc.js.map +1 -0
- package/dist/core/core.d.ts +10 -3
- package/dist/core/core.js +22 -6
- package/dist/core/core.js.map +1 -1
- package/dist/core/facets.d.ts +8 -0
- package/dist/core/facets.js +9 -1
- package/dist/core/facets.js.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/index.js +4 -4
- package/dist/library/kit-catalog.json +45 -44
- package/dist/schema/types.d.ts +7 -0
- package/dist/schema/types.js.map +1 -1
- package/dist/styles.css +1 -1
- 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/toast.js +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { Button, IconButton } from "../../ui/button.js";
|
|
3
|
+
import { Segmented } from "../../ui/controls.js";
|
|
4
|
+
import { Tooltip } from "../../ui/overlay.js";
|
|
5
|
+
import { placeCaret, rid } from "./dom.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { IconMinus, IconPlus, IconTrash } from "@tabler/icons-react";
|
|
9
|
+
//#region src/controls/rich-text/columns.tsx
|
|
10
|
+
/**
|
|
11
|
+
* RICH TEXT · Columns (kit-richtext-ui-spec.md §4): V2's columns island, stored as the PT block object
|
|
12
|
+
* `{ _type: "columns", gap: "s" | "m" | "l", columns: { _key, content: PortableTextBlock[] }[] }`: 2–4 columns of
|
|
13
|
+
* text blocks only, depth 1 (plan R5). The editor holds each column as its HTML, as V2 does; the codec maps it.
|
|
14
|
+
*
|
|
15
|
+
* block V2 rich-text/blocks.tsx:536-570: a grid of dashed columns (gap s 8px · m 16px · l 28px), each a nested
|
|
16
|
+
* Editable (paragraphs and marks; paste is plain text), and the bar under it: Add column (≤4), Remove column
|
|
17
|
+
* (the last, ≥2), Gap S | M | L, Remove columns. Escape inside a column leaves the island (Island onKeyDown).
|
|
18
|
+
* editable V2 rich-text/blocks.tsx:54-120, ported here (the kit's own PlainEditable is plain text only).
|
|
19
|
+
*/
|
|
20
|
+
var COLUMNS_MIN = 2;
|
|
21
|
+
var COLUMNS_MAX = 4;
|
|
22
|
+
var columnsDefault = () => ({
|
|
23
|
+
gap: "m",
|
|
24
|
+
cols: [{
|
|
25
|
+
id: rid("col"),
|
|
26
|
+
html: ""
|
|
27
|
+
}, {
|
|
28
|
+
id: rid("col"),
|
|
29
|
+
html: ""
|
|
30
|
+
}]
|
|
31
|
+
});
|
|
32
|
+
var GAP = {
|
|
33
|
+
s: "8px",
|
|
34
|
+
m: "16px",
|
|
35
|
+
l: "28px"
|
|
36
|
+
};
|
|
37
|
+
/** V2 blocks.tsx:191: the controls row under a block, muted at rest, full strength on hover / focus */
|
|
38
|
+
var BAR = "mt-1.5 flex flex-wrap items-center gap-1 text-kit-fg-3 opacity-70 transition-opacity group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100";
|
|
39
|
+
var Sep = () => /* @__PURE__ */ jsx("span", {
|
|
40
|
+
"aria-hidden": true,
|
|
41
|
+
className: "mx-0.5 h-4 w-px shrink-0 bg-kit-line"
|
|
42
|
+
});
|
|
43
|
+
/** A small rich-text region inside a block (V2 Editable): paragraphs and marks, plain-text paste, a placeholder. */
|
|
44
|
+
function Editable({ html, onChange, placeholder, className, readOnly, label, autoFocus, ...rest }) {
|
|
45
|
+
const ref = React.useRef(null);
|
|
46
|
+
React.useLayoutEffect(() => {
|
|
47
|
+
const el = ref.current;
|
|
48
|
+
if (!el || document.activeElement === el) return;
|
|
49
|
+
if (el.innerHTML !== html) el.innerHTML = html;
|
|
50
|
+
}, [html]);
|
|
51
|
+
React.useEffect(() => {
|
|
52
|
+
if (!autoFocus || !ref.current) return;
|
|
53
|
+
ref.current.focus({ preventScroll: false });
|
|
54
|
+
placeCaret(ref.current);
|
|
55
|
+
}, []);
|
|
56
|
+
return /* @__PURE__ */ jsx("div", {
|
|
57
|
+
...rest,
|
|
58
|
+
ref,
|
|
59
|
+
role: "textbox",
|
|
60
|
+
"aria-label": label,
|
|
61
|
+
"aria-multiline": true,
|
|
62
|
+
tabIndex: readOnly ? -1 : 0,
|
|
63
|
+
contentEditable: !readOnly,
|
|
64
|
+
suppressContentEditableWarning: true,
|
|
65
|
+
spellCheck: true,
|
|
66
|
+
"data-placeholder": placeholder,
|
|
67
|
+
onInput: (e) => {
|
|
68
|
+
const el = e.currentTarget;
|
|
69
|
+
if (!el.textContent && !el.querySelector("img,br + br")) el.innerHTML = "";
|
|
70
|
+
onChange(el.innerHTML);
|
|
71
|
+
},
|
|
72
|
+
onPaste: (e) => {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
document.execCommand?.("insertText", false, e.clipboardData.getData("text/plain"));
|
|
75
|
+
},
|
|
76
|
+
className: cn("min-w-0 cursor-text whitespace-pre-wrap break-words outline-none focus-visible:shadow-none empty:before:pointer-events-none empty:before:text-kit-fg-4 empty:before:content-[attr(data-placeholder)]", className)
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function ColumnsBlock({ m, set, el, ctx, autoFocus }) {
|
|
80
|
+
const ro = !!ctx.readOnly;
|
|
81
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
82
|
+
className: "group/blk",
|
|
83
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
84
|
+
className: "grid",
|
|
85
|
+
style: {
|
|
86
|
+
gridTemplateColumns: `repeat(${m.cols.length}, minmax(0, 1fr))`,
|
|
87
|
+
gap: GAP[m.gap]
|
|
88
|
+
},
|
|
89
|
+
children: m.cols.map((c, i) => /* @__PURE__ */ jsx("div", {
|
|
90
|
+
className: "min-h-14 rounded-kit-md border border-dashed border-kit-line px-2.5 py-2 transition-colors focus-within:border-kit-line-strong hover:border-kit-line-strong",
|
|
91
|
+
children: /* @__PURE__ */ jsx(Editable, {
|
|
92
|
+
label: `Column ${i + 1}`,
|
|
93
|
+
html: c.html,
|
|
94
|
+
readOnly: ro,
|
|
95
|
+
autoFocus: autoFocus && i === 0,
|
|
96
|
+
placeholder: `Column ${i + 1}`,
|
|
97
|
+
onChange: (html) => set((x) => ({
|
|
98
|
+
...x,
|
|
99
|
+
cols: x.cols.map((y) => y.id === c.id ? {
|
|
100
|
+
...y,
|
|
101
|
+
html
|
|
102
|
+
} : y)
|
|
103
|
+
}), `Edit column ${i + 1}`),
|
|
104
|
+
className: "min-h-10 [&_p+p]:mt-2"
|
|
105
|
+
})
|
|
106
|
+
}, c.id))
|
|
107
|
+
}), ro ? null : /* @__PURE__ */ jsxs("div", {
|
|
108
|
+
className: BAR,
|
|
109
|
+
children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Button, {
|
|
111
|
+
size: "xs",
|
|
112
|
+
variant: "ghost",
|
|
113
|
+
leftIcon: /* @__PURE__ */ jsx(IconPlus, { stroke: 1.5 }),
|
|
114
|
+
disabled: m.cols.length >= 4,
|
|
115
|
+
onClick: () => set((x) => ({
|
|
116
|
+
...x,
|
|
117
|
+
cols: [...x.cols, {
|
|
118
|
+
id: rid("col"),
|
|
119
|
+
html: ""
|
|
120
|
+
}]
|
|
121
|
+
}), "Add column"),
|
|
122
|
+
children: "Add column"
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ jsx(Button, {
|
|
125
|
+
size: "xs",
|
|
126
|
+
variant: "ghost",
|
|
127
|
+
leftIcon: /* @__PURE__ */ jsx(IconMinus, { stroke: 1.5 }),
|
|
128
|
+
disabled: m.cols.length <= 2,
|
|
129
|
+
onClick: () => set((x) => ({
|
|
130
|
+
...x,
|
|
131
|
+
cols: x.cols.slice(0, -1)
|
|
132
|
+
}), "Remove column"),
|
|
133
|
+
children: "Remove column"
|
|
134
|
+
}),
|
|
135
|
+
/* @__PURE__ */ jsx(Sep, {}),
|
|
136
|
+
/* @__PURE__ */ jsx("span", {
|
|
137
|
+
className: "px-1 text-kit-xs text-kit-fg-3",
|
|
138
|
+
children: "Gap"
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ jsx(Segmented, {
|
|
141
|
+
"aria-label": "Gap",
|
|
142
|
+
size: "sm",
|
|
143
|
+
value: m.gap,
|
|
144
|
+
onChange: (gap) => set((x) => ({
|
|
145
|
+
...x,
|
|
146
|
+
gap
|
|
147
|
+
}), "Change column gap"),
|
|
148
|
+
items: [
|
|
149
|
+
{
|
|
150
|
+
value: "s",
|
|
151
|
+
label: "S"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
value: "m",
|
|
155
|
+
label: "M"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
value: "l",
|
|
159
|
+
label: "L"
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
164
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
165
|
+
content: "Remove columns",
|
|
166
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
167
|
+
size: "xs",
|
|
168
|
+
label: "Remove columns",
|
|
169
|
+
onClick: () => ctx.remove(el),
|
|
170
|
+
className: "hover:text-kit-danger-fg",
|
|
171
|
+
children: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 })
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
})]
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export { COLUMNS_MAX, COLUMNS_MIN, ColumnsBlock, Editable, columnsDefault };
|
|
180
|
+
|
|
181
|
+
//# sourceMappingURL=columns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"columns.js","names":[],"sources":["../../../src/controls/rich-text/columns.tsx"],"sourcesContent":["/**\n * RICH TEXT · Columns (kit-richtext-ui-spec.md §4): V2's columns island, stored as the PT block object\n * `{ _type: \"columns\", gap: \"s\" | \"m\" | \"l\", columns: { _key, content: PortableTextBlock[] }[] }`: 2–4 columns of\n * text blocks only, depth 1 (plan R5). The editor holds each column as its HTML, as V2 does; the codec maps it.\n *\n * block V2 rich-text/blocks.tsx:536-570: a grid of dashed columns (gap s 8px · m 16px · l 28px), each a nested\n * Editable (paragraphs and marks; paste is plain text), and the bar under it: Add column (≤4), Remove column\n * (the last, ≥2), Gap S | M | L, Remove columns. Escape inside a column leaves the island (Island onKeyDown).\n * editable V2 rich-text/blocks.tsx:54-120, ported here (the kit's own PlainEditable is plain text only).\n */\nimport * as React from \"react\";\nimport { IconMinus, IconPlus, IconTrash } from \"@tabler/icons-react\";\nimport { cn } from \"../../lib/cn\";\nimport { Button, IconButton } from \"../../ui/button\";\nimport { Segmented } from \"../../ui/controls\";\nimport { Tooltip } from \"../../ui/overlay\";\nimport type { IslandCtx } from \"./blocks\";\nimport { placeCaret, rid, type IslandEl } from \"./dom\";\n\nexport type ColumnsGap = \"s\" | \"m\" | \"l\";\nexport interface ColumnsModel {\n gap: ColumnsGap;\n cols: { id: string; html: string }[];\n}\nexport const COLUMNS_MIN = 2;\nexport const COLUMNS_MAX = 4;\nexport const columnsDefault = (): ColumnsModel => ({ gap: \"m\", cols: [{ id: rid(\"col\"), html: \"\" }, { id: rid(\"col\"), html: \"\" }] });\nconst GAP: Record<ColumnsGap, string> = { s: \"8px\", m: \"16px\", l: \"28px\" };\n/** V2 blocks.tsx:191: the controls row under a block, muted at rest, full strength on hover / focus */\nconst BAR = \"mt-1.5 flex flex-wrap items-center gap-1 text-kit-fg-3 opacity-70 transition-opacity group-hover/blk:opacity-100 group-focus-within/blk:opacity-100 pointer-coarse:opacity-100\";\nconst Sep = () => <span aria-hidden className=\"mx-0.5 h-4 w-px shrink-0 bg-kit-line\" />;\n\n/** A small rich-text region inside a block (V2 Editable): paragraphs and marks, plain-text paste, a placeholder. */\nexport function Editable({\n html, onChange, placeholder, className, readOnly, label, autoFocus, ...rest\n}: {\n html: string;\n onChange: (v: string) => void;\n placeholder?: string;\n className?: string;\n readOnly?: boolean;\n label: string;\n autoFocus?: boolean;\n} & Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\">) {\n const ref = React.useRef<HTMLDivElement>(null);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el || document.activeElement === el) return;\n if (el.innerHTML !== html) el.innerHTML = html;\n }, [html]);\n React.useEffect(() => {\n if (!autoFocus || !ref.current) return;\n ref.current.focus({ preventScroll: false });\n placeCaret(ref.current);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n return (\n <div\n {...rest}\n ref={ref}\n role=\"textbox\"\n aria-label={label}\n aria-multiline\n tabIndex={readOnly ? -1 : 0}\n contentEditable={!readOnly}\n suppressContentEditableWarning\n spellCheck\n data-placeholder={placeholder}\n onInput={(e) => {\n const el = e.currentTarget;\n if (!el.textContent && !el.querySelector(\"img,br + br\")) el.innerHTML = \"\";\n onChange(el.innerHTML);\n }}\n onPaste={(e) => {\n e.preventDefault();\n document.execCommand?.(\"insertText\", false, e.clipboardData.getData(\"text/plain\"));\n }}\n className={cn(\n \"min-w-0 cursor-text whitespace-pre-wrap break-words outline-none focus-visible:shadow-none empty:before:pointer-events-none empty:before:text-kit-fg-4 empty:before:content-[attr(data-placeholder)]\",\n className,\n )}\n />\n );\n}\n\nexport function ColumnsBlock({\n m, set, el, ctx, autoFocus,\n}: {\n m: ColumnsModel;\n set: (fn: (m: ColumnsModel) => ColumnsModel, label?: string) => void;\n el: IslandEl;\n ctx: IslandCtx;\n autoFocus: boolean;\n}) {\n const ro = !!ctx.readOnly;\n return (\n <div className=\"group/blk\">\n <div className=\"grid\" style={{ gridTemplateColumns: `repeat(${m.cols.length}, minmax(0, 1fr))`, gap: GAP[m.gap] }}>\n {m.cols.map((c, i) => (\n <div key={c.id} className=\"min-h-14 rounded-kit-md border border-dashed border-kit-line px-2.5 py-2 transition-colors focus-within:border-kit-line-strong hover:border-kit-line-strong\">\n <Editable\n label={`Column ${i + 1}`}\n html={c.html}\n readOnly={ro}\n autoFocus={autoFocus && i === 0}\n placeholder={`Column ${i + 1}`}\n onChange={(html) => set((x) => ({ ...x, cols: x.cols.map((y) => (y.id === c.id ? { ...y, html } : y)) }), `Edit column ${i + 1}`)}\n className=\"min-h-10 [&_p+p]:mt-2\"\n />\n </div>\n ))}\n </div>\n {ro ? null : (\n <div className={BAR}>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n leftIcon={<IconPlus stroke={1.5} />}\n disabled={m.cols.length >= COLUMNS_MAX}\n onClick={() => set((x) => ({ ...x, cols: [...x.cols, { id: rid(\"col\"), html: \"\" }] }), \"Add column\")}\n >\n Add column\n </Button>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n leftIcon={<IconMinus stroke={1.5} />}\n disabled={m.cols.length <= COLUMNS_MIN}\n onClick={() => set((x) => ({ ...x, cols: x.cols.slice(0, -1) }), \"Remove column\")}\n >\n Remove column\n </Button>\n <Sep />\n <span className=\"px-1 text-kit-xs text-kit-fg-3\">Gap</span>\n <Segmented<ColumnsGap>\n aria-label=\"Gap\"\n size=\"sm\"\n value={m.gap}\n onChange={(gap) => set((x) => ({ ...x, gap }), \"Change column gap\")}\n items={[{ value: \"s\", label: \"S\" }, { value: \"m\", label: \"M\" }, { value: \"l\", label: \"L\" }]}\n />\n <span className=\"flex-1\" />\n <Tooltip content=\"Remove columns\">\n <IconButton size=\"xs\" label=\"Remove columns\" onClick={() => ctx.remove(el)} className=\"hover:text-kit-danger-fg\">\n <IconTrash stroke={1.5} />\n </IconButton>\n </Tooltip>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
* <div data-body><p>…` (the message is plain text: paragraphs and line breaks)
|
|
29
29
|
* toggle `bcmsBlock` builtin:toggle {title, body}: `<details data-rt="toggle"><summary>…<div data-body><p>…`
|
|
30
30
|
* video the `video` block {src, title}: `<div data-rt="video" data-url data-title>`
|
|
31
|
-
* embed
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* embed V2's article Embed {url, title, aspect}: `<figure data-rt="embed" data-url data-title data-aspect><a href>…`
|
|
32
|
+
* (embed.tsx). Stored today as `bcmsBlock` builtin:video {url, title}, which keeps no aspect; the PT
|
|
33
|
+
* `embed` block (plan RT3) does.
|
|
34
34
|
* file the `file` block {src, name, size}: `<div data-rt="file" data-url data-name data-size>`
|
|
35
35
|
* component `bcmsBlock` "component" {componentId, overrides, variant} (CPO D28): `<div data-rt="component" data-ref
|
|
36
36
|
* data-id data-name data-overrides data-variant>` — a linked instance of a project component, shown as a card.
|
|
@@ -95,10 +95,11 @@ export interface VideoModel {
|
|
|
95
95
|
title?: string;
|
|
96
96
|
extra?: string;
|
|
97
97
|
}
|
|
98
|
-
/** V2 EmbedModel (article.ts)
|
|
98
|
+
/** V2 EmbedModel (article.ts); `aspect` absent draws 16:9 (embed.tsx). */
|
|
99
99
|
export interface EmbedModel {
|
|
100
100
|
url?: string;
|
|
101
101
|
title?: string;
|
|
102
|
+
aspect?: "16:9" | "4:3" | "1:1" | "9:16";
|
|
102
103
|
extra?: string;
|
|
103
104
|
}
|
|
104
105
|
export type EmbedProvider = "youtube" | "vimeo" | "x" | "codepen" | "figma" | "maps" | "spotify" | "podcast" | "audio" | "link";
|
|
@@ -30,9 +30,9 @@ import { boolLabels, cellText, cellValue, emptyTable, formatNumber, isCellObject
|
|
|
30
30
|
* <div data-body><p>…` (the message is plain text: paragraphs and line breaks)
|
|
31
31
|
* toggle `bcmsBlock` builtin:toggle {title, body}: `<details data-rt="toggle"><summary>…<div data-body><p>…`
|
|
32
32
|
* video the `video` block {src, title}: `<div data-rt="video" data-url data-title>`
|
|
33
|
-
* embed
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* embed V2's article Embed {url, title, aspect}: `<figure data-rt="embed" data-url data-title data-aspect><a href>…`
|
|
34
|
+
* (embed.tsx). Stored today as `bcmsBlock` builtin:video {url, title}, which keeps no aspect; the PT
|
|
35
|
+
* `embed` block (plan RT3) does.
|
|
36
36
|
* file the `file` block {src, name, size}: `<div data-rt="file" data-url data-name data-size>`
|
|
37
37
|
* component `bcmsBlock` "component" {componentId, overrides, variant} (CPO D28): `<div data-rt="component" data-ref
|
|
38
38
|
* data-id data-name data-overrides data-variant>` — a linked instance of a project component, shown as a card.
|
|
@@ -64,6 +64,12 @@ var CALLOUT_TONES = [
|
|
|
64
64
|
"tip",
|
|
65
65
|
"warning"
|
|
66
66
|
];
|
|
67
|
+
var EMBED_ASPECT_VALUES = [
|
|
68
|
+
"16:9",
|
|
69
|
+
"4:3",
|
|
70
|
+
"1:1",
|
|
71
|
+
"9:16"
|
|
72
|
+
];
|
|
67
73
|
var hostOf = (url) => {
|
|
68
74
|
try {
|
|
69
75
|
return new URL(url).hostname.replace(/^www\./, "");
|
|
@@ -260,7 +266,7 @@ function renderStatic(kind, model) {
|
|
|
260
266
|
}
|
|
261
267
|
case "embed": {
|
|
262
268
|
const m = model;
|
|
263
|
-
return `<figure data-rt="embed"${attr("data-url", m.url)}${attr("data-title", m.title)}${xAttr(m.extra)}>${m.url ? `<a href="${escAttr(m.url)}">${esc(m.title || m.url)}</a>` : ""}</figure>`;
|
|
269
|
+
return `<figure data-rt="embed"${attr("data-url", m.url)}${attr("data-title", m.title)}${attr("data-aspect", m.aspect)}${xAttr(m.extra)}>${m.url ? `<a href="${escAttr(m.url)}">${esc(m.title || m.url)}</a>` : ""}</figure>`;
|
|
264
270
|
}
|
|
265
271
|
case "file": {
|
|
266
272
|
const m = model;
|
|
@@ -810,8 +816,10 @@ function parseIsland(kind, el) {
|
|
|
810
816
|
const m = {};
|
|
811
817
|
const url = attrOf(el, "data-url");
|
|
812
818
|
const title = attrOf(el, "data-title");
|
|
819
|
+
const aspect = attrOf(el, "data-aspect");
|
|
813
820
|
if (url !== void 0) m.url = url;
|
|
814
821
|
if (title !== void 0) m.title = title;
|
|
822
|
+
if (aspect !== void 0 && EMBED_ASPECT_VALUES.includes(aspect)) m.aspect = aspect;
|
|
815
823
|
const extra = attrOf(el, "data-extra");
|
|
816
824
|
if (extra) m.extra = extra;
|
|
817
825
|
return m;
|
|
@@ -925,6 +933,7 @@ function serialize(root) {
|
|
|
925
933
|
c.replaceWith(tpl.content);
|
|
926
934
|
});
|
|
927
935
|
clone.querySelectorAll("[data-hint]").forEach((n) => n.removeAttribute("data-hint"));
|
|
936
|
+
clone.querySelectorAll("ul[data-task] > li").forEach((li) => (li.removeAttribute("role"), li.removeAttribute("aria-checked")));
|
|
928
937
|
while (clone.children.length > 1 && clone.lastElementChild && isEmptyP(clone.lastElementChild)) clone.lastElementChild.remove();
|
|
929
938
|
const html = clone.innerHTML.replace(//g, "");
|
|
930
939
|
return html === "<p><br></p>" || html === "<p></p>" ? "" : html;
|