@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
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
import { isCropRect } from "../../adapters/values.js";
|
|
1
2
|
import { cn } from "../../lib/cn.js";
|
|
2
3
|
import { useKitServices } from "../../context.js";
|
|
3
4
|
import { Button, IconButton } from "../../ui/button.js";
|
|
4
|
-
import { Menu, MenuContent, MenuItem,
|
|
5
|
+
import { Menu, MenuContent, MenuItem, MenuTrigger, Popover, PopoverContent, PopoverTrigger, Tooltip } from "../../ui/overlay.js";
|
|
5
6
|
import { Input, Textarea } from "../../ui/input.js";
|
|
6
7
|
import { TableEditor } from "../table/table-editor.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { CALLOUT_TONES,
|
|
8
|
+
import { VideoPreview } from "../media.js";
|
|
9
|
+
import { ArticleActions, ArticleCard, ArticleForm, ArticlePanel, articleCardKeyDown, useArticleOpen } from "./article-shell.js";
|
|
10
|
+
import { AuthorBlock } from "./author.js";
|
|
11
|
+
import { CALLOUT_TONES, placeCaret } from "./dom.js";
|
|
11
12
|
import { LinkForm } from "./link-form.js";
|
|
13
|
+
import { EmbedBlock } from "./embed.js";
|
|
14
|
+
import { GalleryBlock } from "./gallery.js";
|
|
15
|
+
import { TocBlock } from "./toc.js";
|
|
16
|
+
import { ColumnsBlock } from "./columns.js";
|
|
17
|
+
import { RelatedBlock } from "./related.js";
|
|
12
18
|
import { ComponentCard } from "./component-card.js";
|
|
13
19
|
import * as React from "react";
|
|
14
20
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
-
import { IconAlertTriangle, IconAlt, IconArrowsHorizontal,
|
|
21
|
+
import { IconAlertTriangle, IconAlt, IconArrowsHorizontal, IconBulb, IconCheck, IconChevronDown, IconChevronRight, IconComponents, IconCopy, IconFileText, IconFileUpload, IconInfoCircle, IconLayoutAlignCenter, IconLayoutAlignLeft, IconLayoutAlignRight, IconLibraryPhoto, IconLink, IconPhoto, IconQuote, IconReplace, IconTrash, IconWorld } from "@tabler/icons-react";
|
|
16
22
|
//#region src/controls/rich-text/blocks.tsx
|
|
17
23
|
/**
|
|
18
24
|
* RICH TEXT · rich blocks ("islands", V2 HEAD controls/rich-text/blocks.tsx). Each renders through a portal into a
|
|
@@ -29,8 +35,7 @@ import { IconAlertTriangle, IconAlt, IconArrowsHorizontal, IconArrowsMove, IconB
|
|
|
29
35
|
* block (article-blocks.tsx ArticleBlock + PullQuotePreview): drawn as the reader sees it, edited in the floating
|
|
30
36
|
* properties panel (4 settings → panel, CPO D54) through the kit SchemaForm. Key takeaways are V2's article block too,
|
|
31
37
|
* written in the text itself (article-blocks.tsx TakeawaysEditor, surface "inline"): a title line and a line per point.
|
|
32
|
-
* The embed
|
|
33
|
-
* provider, edited in a popover beside it (Link, Title; V2's Shape has no stored key, see dom.ts).
|
|
38
|
+
* The embed island draws embed.tsx's block (V2's article Embed: Link, Title, Shape), keeping the keys it lacks absent.
|
|
34
39
|
*/
|
|
35
40
|
function Island({ el, ctx }) {
|
|
36
41
|
const kind = el.getAttribute("data-rt-island");
|
|
@@ -114,7 +119,37 @@ function Island({ el, ctx }) {
|
|
|
114
119
|
autoFocus,
|
|
115
120
|
m: model,
|
|
116
121
|
set
|
|
117
|
-
}) : kind === "embed" ? /* @__PURE__ */ jsx(
|
|
122
|
+
}) : kind === "embed" ? /* @__PURE__ */ jsx(EmbedIsland, {
|
|
123
|
+
el,
|
|
124
|
+
ctx,
|
|
125
|
+
autoFocus,
|
|
126
|
+
m: model,
|
|
127
|
+
set
|
|
128
|
+
}) : kind === "gallery" ? /* @__PURE__ */ jsx(GalleryIsland, {
|
|
129
|
+
el,
|
|
130
|
+
ctx,
|
|
131
|
+
autoFocus,
|
|
132
|
+
m: model,
|
|
133
|
+
set
|
|
134
|
+
}) : kind === "toc" ? /* @__PURE__ */ jsx(TocIsland, {
|
|
135
|
+
el,
|
|
136
|
+
ctx,
|
|
137
|
+
autoFocus,
|
|
138
|
+
m: model,
|
|
139
|
+
set
|
|
140
|
+
}) : kind === "columns" ? /* @__PURE__ */ jsx(ColumnsIsland, {
|
|
141
|
+
el,
|
|
142
|
+
ctx,
|
|
143
|
+
autoFocus,
|
|
144
|
+
m: model,
|
|
145
|
+
set
|
|
146
|
+
}) : kind === "author" ? /* @__PURE__ */ jsx(AuthorIsland, {
|
|
147
|
+
el,
|
|
148
|
+
ctx,
|
|
149
|
+
autoFocus,
|
|
150
|
+
m: model,
|
|
151
|
+
set
|
|
152
|
+
}) : kind === "related" ? /* @__PURE__ */ jsx(RelatedIsland, {
|
|
118
153
|
el,
|
|
119
154
|
ctx,
|
|
120
155
|
autoFocus,
|
|
@@ -135,6 +170,185 @@ function Island({ el, ctx }) {
|
|
|
135
170
|
}) : null
|
|
136
171
|
});
|
|
137
172
|
}
|
|
173
|
+
/** The island's stored embed (keys absent when the block lacks them) through embed.tsx's block (V2's model). */
|
|
174
|
+
function EmbedIsland({ m, set, el, ctx, autoFocus }) {
|
|
175
|
+
return /* @__PURE__ */ jsx(EmbedBlock, {
|
|
176
|
+
m: {
|
|
177
|
+
url: m.url ?? "",
|
|
178
|
+
title: m.title ?? "",
|
|
179
|
+
aspect: m.aspect ?? "16:9"
|
|
180
|
+
},
|
|
181
|
+
set: (fn, label) => set((prev) => {
|
|
182
|
+
const next = fn({
|
|
183
|
+
url: prev.url ?? "",
|
|
184
|
+
title: prev.title ?? "",
|
|
185
|
+
aspect: prev.aspect ?? "16:9"
|
|
186
|
+
});
|
|
187
|
+
const out = { ...prev };
|
|
188
|
+
if (next.url !== (prev.url ?? "")) out.url = next.url;
|
|
189
|
+
if (next.title !== (prev.title ?? "")) out.title = next.title;
|
|
190
|
+
if (next.aspect !== (prev.aspect ?? "16:9")) out.aspect = next.aspect;
|
|
191
|
+
return out;
|
|
192
|
+
}, label),
|
|
193
|
+
el,
|
|
194
|
+
ctx,
|
|
195
|
+
autoFocus,
|
|
196
|
+
shape: ctx.allows?.("embedAspect") ?? false
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** The island's stored gallery (src, alt, caption per image) through gallery.tsx's block (V2's model, an image value each). */
|
|
200
|
+
/** RT9: a gallery image's framing, image-url's 0–1 focal point ⇄ the kit image value's percent */
|
|
201
|
+
var toGallery = (m) => ({
|
|
202
|
+
layout: m.layout ?? "grid",
|
|
203
|
+
images: m.images.map((x) => ({
|
|
204
|
+
_key: x._key,
|
|
205
|
+
image: x.src ? {
|
|
206
|
+
url: x.src,
|
|
207
|
+
...x.alt !== void 0 ? { alt: x.alt } : {},
|
|
208
|
+
...x.crop ? { crop: x.crop } : {},
|
|
209
|
+
...x.focal ? { focal: {
|
|
210
|
+
x: x.focal.x * 100,
|
|
211
|
+
y: x.focal.y * 100
|
|
212
|
+
} } : {}
|
|
213
|
+
} : null,
|
|
214
|
+
caption: x.caption ?? ""
|
|
215
|
+
}))
|
|
216
|
+
});
|
|
217
|
+
var cropOf = (c) => isCropRect(c) ? {
|
|
218
|
+
top: c.top,
|
|
219
|
+
right: c.right,
|
|
220
|
+
bottom: c.bottom,
|
|
221
|
+
left: c.left
|
|
222
|
+
} : void 0;
|
|
223
|
+
var focalOf = (f) => {
|
|
224
|
+
const p = f;
|
|
225
|
+
return typeof p?.x === "number" && typeof p?.y === "number" ? {
|
|
226
|
+
x: Math.round(p.x) / 100,
|
|
227
|
+
y: Math.round(p.y) / 100
|
|
228
|
+
} : void 0;
|
|
229
|
+
};
|
|
230
|
+
function GalleryIsland({ m, set, el, ctx, autoFocus }) {
|
|
231
|
+
return /* @__PURE__ */ jsx(GalleryBlock, {
|
|
232
|
+
m: toGallery(m),
|
|
233
|
+
set: (fn, label) => set((prev) => {
|
|
234
|
+
const next = fn(toGallery(prev));
|
|
235
|
+
const out = {
|
|
236
|
+
...prev,
|
|
237
|
+
images: next.images.map((x) => {
|
|
238
|
+
const crop = cropOf(x.image?.crop);
|
|
239
|
+
const focal = focalOf(x.image?.focal);
|
|
240
|
+
return {
|
|
241
|
+
_key: x._key,
|
|
242
|
+
src: x.image?.url ?? "",
|
|
243
|
+
...x.image?.alt !== void 0 ? { alt: x.image.alt } : {},
|
|
244
|
+
...x.caption ? { caption: x.caption } : {},
|
|
245
|
+
...crop ? { crop } : {},
|
|
246
|
+
...focal ? { focal } : {}
|
|
247
|
+
};
|
|
248
|
+
})
|
|
249
|
+
};
|
|
250
|
+
if (next.layout !== (prev.layout ?? "grid")) out.layout = next.layout;
|
|
251
|
+
return out;
|
|
252
|
+
}, label),
|
|
253
|
+
el,
|
|
254
|
+
ctx,
|
|
255
|
+
autoFocus
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/** The island's stored table of contents (title, depth) through toc.tsx's block, which lists the editor's live outline. */
|
|
259
|
+
function TocIsland({ m, set, el, ctx, autoFocus }) {
|
|
260
|
+
return /* @__PURE__ */ jsx(TocBlock, {
|
|
261
|
+
m: {
|
|
262
|
+
title: m.title ?? "",
|
|
263
|
+
depth: m.depth ?? "3"
|
|
264
|
+
},
|
|
265
|
+
set: (fn, label) => set((prev) => {
|
|
266
|
+
const next = fn({
|
|
267
|
+
title: prev.title ?? "",
|
|
268
|
+
depth: prev.depth ?? "3"
|
|
269
|
+
});
|
|
270
|
+
const out = { ...prev };
|
|
271
|
+
if (next.title !== (prev.title ?? "")) out.title = next.title;
|
|
272
|
+
if (next.depth !== (prev.depth ?? "3")) out.depth = next.depth;
|
|
273
|
+
return out;
|
|
274
|
+
}, label),
|
|
275
|
+
el,
|
|
276
|
+
ctx,
|
|
277
|
+
autoFocus
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/** The island's stored columns (gap, each column's HTML) through columns.tsx's block. */
|
|
281
|
+
function ColumnsIsland({ m, set, el, ctx, autoFocus }) {
|
|
282
|
+
return /* @__PURE__ */ jsx(ColumnsBlock, {
|
|
283
|
+
m: {
|
|
284
|
+
gap: m.gap ?? "m",
|
|
285
|
+
cols: m.cols
|
|
286
|
+
},
|
|
287
|
+
set: (fn, label) => set((prev) => {
|
|
288
|
+
const next = fn({
|
|
289
|
+
gap: prev.gap ?? "m",
|
|
290
|
+
cols: prev.cols
|
|
291
|
+
});
|
|
292
|
+
const out = {
|
|
293
|
+
...prev,
|
|
294
|
+
cols: next.cols
|
|
295
|
+
};
|
|
296
|
+
if (next.gap !== (prev.gap ?? "m")) out.gap = next.gap;
|
|
297
|
+
return out;
|
|
298
|
+
}, label),
|
|
299
|
+
el,
|
|
300
|
+
ctx,
|
|
301
|
+
autoFocus
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/** The island's stored author reference through author.tsx's block, picking from the field's authors collection. */
|
|
305
|
+
function AuthorIsland({ m, set, el, ctx, autoFocus }) {
|
|
306
|
+
return /* @__PURE__ */ jsx(AuthorBlock, {
|
|
307
|
+
m: { author: m.author ?? "" },
|
|
308
|
+
set: (fn, label) => set((prev) => {
|
|
309
|
+
const next = fn({ author: prev.author ?? "" });
|
|
310
|
+
return next.author === (prev.author ?? "") ? prev : {
|
|
311
|
+
...prev,
|
|
312
|
+
author: next.author
|
|
313
|
+
};
|
|
314
|
+
}, label),
|
|
315
|
+
el,
|
|
316
|
+
ctx,
|
|
317
|
+
autoFocus,
|
|
318
|
+
collection: ctx.references?.authors ?? ""
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
/** The island's stored related posts through related.tsx's block, over the field's posts collection. */
|
|
322
|
+
var toRelated = (m) => ({
|
|
323
|
+
title: m.title ?? "",
|
|
324
|
+
mode: m.mode ?? "topic",
|
|
325
|
+
count: m.count ?? 3,
|
|
326
|
+
posts: m.posts
|
|
327
|
+
});
|
|
328
|
+
function RelatedIsland({ m, set, el, ctx, autoFocus }) {
|
|
329
|
+
return /* @__PURE__ */ jsx(RelatedBlock, {
|
|
330
|
+
m: toRelated(m),
|
|
331
|
+
set: (fn, label) => set((prev) => {
|
|
332
|
+
const was = toRelated(prev);
|
|
333
|
+
const next = fn(was);
|
|
334
|
+
const out = {
|
|
335
|
+
...prev,
|
|
336
|
+
posts: next.posts
|
|
337
|
+
};
|
|
338
|
+
if (next.title !== was.title) out.title = next.title;
|
|
339
|
+
if (next.mode !== was.mode) out.mode = next.mode;
|
|
340
|
+
if (next.count !== was.count) out.count = next.count;
|
|
341
|
+
return out;
|
|
342
|
+
}, label),
|
|
343
|
+
el,
|
|
344
|
+
ctx,
|
|
345
|
+
autoFocus,
|
|
346
|
+
collection: ctx.references?.posts ?? "",
|
|
347
|
+
excludeId: ctx.references?.entryId,
|
|
348
|
+
topic: ctx.references?.topic !== false,
|
|
349
|
+
noun: ctx.references?.postsNoun
|
|
350
|
+
});
|
|
351
|
+
}
|
|
138
352
|
var Sep = () => /* @__PURE__ */ jsx("span", {
|
|
139
353
|
"aria-hidden": true,
|
|
140
354
|
className: "mx-0.5 h-4 w-px shrink-0 bg-kit-line"
|
|
@@ -744,13 +958,6 @@ function ToggleBlock({ m, set, el, ctx, autoFocus }) {
|
|
|
744
958
|
});
|
|
745
959
|
}
|
|
746
960
|
var videoUrlOk = (u) => /^https?:\/\/\S+$/i.test(u) || /^\/(?!\/)\S*$/.test(u);
|
|
747
|
-
function videoEmbedSrc(url) {
|
|
748
|
-
const yt = /(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/|shorts\/|live\/)|youtu\.be\/)([\w-]{6,})/i.exec(url);
|
|
749
|
-
if (yt) return `https://www.youtube-nocookie.com/embed/${yt[1]}`;
|
|
750
|
-
const vimeo = /vimeo\.com\/(?:video\/)?(\d+)/i.exec(url);
|
|
751
|
-
if (vimeo) return `https://player.vimeo.com/video/${vimeo[1]}`;
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
961
|
function VideoBlock({ m, set, el, ctx, autoFocus }) {
|
|
755
962
|
const [draft, setDraft] = React.useState(m.url ?? "");
|
|
756
963
|
React.useEffect(() => setDraft(m.url ?? ""), [m.url]);
|
|
@@ -764,7 +971,6 @@ function VideoBlock({ m, set, el, ctx, autoFocus }) {
|
|
|
764
971
|
set({ url: u }, "Set video link");
|
|
765
972
|
};
|
|
766
973
|
const ro = ctx.readOnly;
|
|
767
|
-
const frame = m.url ? videoEmbedSrc(m.url) : null;
|
|
768
974
|
return /* @__PURE__ */ jsxs("div", {
|
|
769
975
|
className: "group/blk rounded-kit-lg border border-kit-line bg-kit-subtle p-2.5",
|
|
770
976
|
"data-rt-isolate": "",
|
|
@@ -802,20 +1008,9 @@ function VideoBlock({ m, set, el, ctx, autoFocus }) {
|
|
|
802
1008
|
className: "mb-2 px-1 text-kit-xs text-kit-danger-fg",
|
|
803
1009
|
children: error
|
|
804
1010
|
}) : null,
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
title:
|
|
808
|
-
loading: "lazy",
|
|
809
|
-
tabIndex: -1,
|
|
810
|
-
sandbox: "allow-scripts allow-same-origin allow-presentation",
|
|
811
|
-
referrerPolicy: "strict-origin-when-cross-origin",
|
|
812
|
-
className: "pointer-events-none aspect-video w-full rounded-kit-md border-0 bg-kit-muted"
|
|
813
|
-
}) : m.url ? /* @__PURE__ */ jsx("video", {
|
|
814
|
-
src: m.url,
|
|
815
|
-
title: m.title || "Video preview",
|
|
816
|
-
controls: true,
|
|
817
|
-
preload: "metadata",
|
|
818
|
-
className: "aspect-video w-full rounded-kit-md bg-kit-muted"
|
|
1011
|
+
m.url ? /* @__PURE__ */ jsx(VideoPreview, {
|
|
1012
|
+
url: m.url,
|
|
1013
|
+
title: "Video"
|
|
819
1014
|
}) : /* @__PURE__ */ jsx("p", {
|
|
820
1015
|
className: "px-1 pb-0.5 text-kit-xs text-kit-fg-3",
|
|
821
1016
|
children: "No video yet."
|
|
@@ -1101,132 +1296,52 @@ var PULL_QUOTE_SCHEMA = {
|
|
|
1101
1296
|
}
|
|
1102
1297
|
]
|
|
1103
1298
|
};
|
|
1104
|
-
var openArticle = null;
|
|
1105
|
-
var articleSubs = /* @__PURE__ */ new Set();
|
|
1106
|
-
var setOpenArticle = (next) => {
|
|
1107
|
-
openArticle = next;
|
|
1108
|
-
articleSubs.forEach((f) => f());
|
|
1109
|
-
};
|
|
1110
|
-
function useOpenArticle() {
|
|
1111
|
-
return React.useSyncExternalStore((cb) => (articleSubs.add(cb), () => void articleSubs.delete(cb)), () => openArticle, () => openArticle);
|
|
1112
|
-
}
|
|
1113
|
-
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);
|
|
1114
|
-
function focusField(root, key) {
|
|
1115
|
-
if (!root) return;
|
|
1116
|
-
const rows = Array.from(root.querySelectorAll("[data-field-key]"));
|
|
1117
|
-
const row = key && rows.find((r) => r.getAttribute("data-field-key") === key) || rows[0];
|
|
1118
|
-
if (!row) return;
|
|
1119
|
-
row.scrollIntoView({ block: "nearest" });
|
|
1120
|
-
controlOf(row)?.focus({ preventScroll: true });
|
|
1121
|
-
}
|
|
1122
1299
|
function PullQuoteBlock({ m, set, el, ctx, autoFocus }) {
|
|
1123
1300
|
const label = PULL_QUOTE_LABEL;
|
|
1124
|
-
const uid = React.useId();
|
|
1125
1301
|
const card = React.useRef(null);
|
|
1126
1302
|
const [cardEl, setCardEl] = React.useState(null);
|
|
1127
|
-
const
|
|
1128
|
-
const editing
|
|
1303
|
+
const ro = !!ctx.readOnly;
|
|
1304
|
+
const { open, editing, openPanel, close } = useArticleOpen({
|
|
1305
|
+
ro,
|
|
1306
|
+
autoFocus
|
|
1307
|
+
});
|
|
1129
1308
|
const cardRef = React.useCallback((n) => {
|
|
1130
1309
|
card.current = n;
|
|
1131
1310
|
if (n) setCardEl(n);
|
|
1132
1311
|
}, []);
|
|
1133
|
-
const ro = !!ctx.readOnly;
|
|
1134
1312
|
const modelRef = React.useRef(m);
|
|
1135
1313
|
modelRef.current = m;
|
|
1136
|
-
const openPanel = React.useCallback((k) => {
|
|
1137
|
-
if (!ro) setOpenArticle({
|
|
1138
|
-
id: uid,
|
|
1139
|
-
focusKey: k ?? null,
|
|
1140
|
-
nonce: (openArticle?.nonce ?? 0) + 1
|
|
1141
|
-
});
|
|
1142
|
-
}, [uid, ro]);
|
|
1143
|
-
const close = React.useCallback(() => {
|
|
1144
|
-
if (openArticle?.id === uid) setOpenArticle(null);
|
|
1145
|
-
}, [uid]);
|
|
1146
1314
|
const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);
|
|
1147
|
-
React.useEffect(() => () => {
|
|
1148
|
-
if (openArticle?.id === uid) setOpenArticle(null);
|
|
1149
|
-
}, [uid]);
|
|
1150
|
-
React.useEffect(() => {
|
|
1151
|
-
if (autoFocus && !ro) openPanel();
|
|
1152
|
-
}, []);
|
|
1153
1315
|
/** a form edit joins the body's one undo history (D54); typing coalesces per field */
|
|
1154
1316
|
const commit = (next) => {
|
|
1155
1317
|
const prev = modelRef.current;
|
|
1156
1318
|
const field = Object.keys(next).find((k) => next[k] !== prev[k]) ?? "";
|
|
1157
1319
|
set(() => next, `Edit ${label.toLowerCase()} ${field}`);
|
|
1158
1320
|
};
|
|
1159
|
-
|
|
1160
|
-
if (e.defaultPrevented || e.target !== card.current) return;
|
|
1161
|
-
const mod = e.metaKey || e.ctrlKey;
|
|
1162
|
-
const k = eventKey(e);
|
|
1163
|
-
if (mod && k === "z") {
|
|
1164
|
-
e.preventDefault();
|
|
1165
|
-
e.stopPropagation();
|
|
1166
|
-
if (e.shiftKey) ctx.redo?.();
|
|
1167
|
-
else ctx.undo?.();
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
1171
|
-
e.preventDefault();
|
|
1172
|
-
openPanel();
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
if (e.key === "Escape") {
|
|
1176
|
-
e.preventDefault();
|
|
1177
|
-
e.stopPropagation();
|
|
1178
|
-
if (editing) close();
|
|
1179
|
-
else ctx.exit(el);
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
|
-
if (ro) return;
|
|
1183
|
-
if (mod && k === "d" && ctx.duplicate) {
|
|
1184
|
-
e.preventDefault();
|
|
1185
|
-
e.stopPropagation();
|
|
1186
|
-
ctx.duplicate(el);
|
|
1187
|
-
} else if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown") && ctx.move) {
|
|
1188
|
-
e.preventDefault();
|
|
1189
|
-
ctx.move(el, e.key === "ArrowUp" ? -1 : 1);
|
|
1190
|
-
focusCard();
|
|
1191
|
-
} else if (e.key === "Backspace" || e.key === "Delete") {
|
|
1192
|
-
e.preventDefault();
|
|
1193
|
-
ctx.remove(el);
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
const actions = ro ? null : /* @__PURE__ */ jsxs("div", {
|
|
1197
|
-
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"),
|
|
1198
|
-
children: [/* @__PURE__ */ jsx(IconButton, {
|
|
1199
|
-
size: "xs",
|
|
1200
|
-
label: `Edit ${label.toLowerCase()}`,
|
|
1201
|
-
onClick: () => editing ? close() : openPanel(),
|
|
1202
|
-
children: /* @__PURE__ */ jsx(IconSettings, { stroke: 1.5 })
|
|
1203
|
-
}), /* @__PURE__ */ jsx(IconButton, {
|
|
1204
|
-
size: "xs",
|
|
1205
|
-
label: `Remove ${label.toLowerCase()}`,
|
|
1206
|
-
onClick: () => ctx.remove(el),
|
|
1207
|
-
className: "hover:text-kit-danger-fg",
|
|
1208
|
-
children: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 })
|
|
1209
|
-
})]
|
|
1210
|
-
});
|
|
1211
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
1321
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(ArticleCard, {
|
|
1212
1322
|
ref: cardRef,
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
},
|
|
1228
|
-
|
|
1229
|
-
|
|
1323
|
+
label,
|
|
1324
|
+
kind: "pullquote",
|
|
1325
|
+
ro,
|
|
1326
|
+
editing,
|
|
1327
|
+
onOpen: openPanel,
|
|
1328
|
+
onKeyDown: articleCardKeyDown({
|
|
1329
|
+
card,
|
|
1330
|
+
ctx,
|
|
1331
|
+
el,
|
|
1332
|
+
ro,
|
|
1333
|
+
editing,
|
|
1334
|
+
onOpen: openPanel,
|
|
1335
|
+
onClose: close,
|
|
1336
|
+
focusCard
|
|
1337
|
+
}),
|
|
1338
|
+
children: [/* @__PURE__ */ jsx(PullQuotePreview, { m }), /* @__PURE__ */ jsx(ArticleActions, {
|
|
1339
|
+
label,
|
|
1340
|
+
ctx,
|
|
1341
|
+
el,
|
|
1342
|
+
editing,
|
|
1343
|
+
onEdit: () => editing ? close() : openPanel()
|
|
1344
|
+
})]
|
|
1230
1345
|
}), editing ? /* @__PURE__ */ jsx(ArticlePanel, {
|
|
1231
1346
|
label,
|
|
1232
1347
|
anchor: cardEl,
|
|
@@ -1236,22 +1351,12 @@ function PullQuoteBlock({ m, set, el, ctx, autoFocus }) {
|
|
|
1236
1351
|
focusBack: focusCard,
|
|
1237
1352
|
ctx,
|
|
1238
1353
|
el,
|
|
1239
|
-
children: /* @__PURE__ */
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
onChange: (v) => commit(v),
|
|
1246
|
-
variant: "panel",
|
|
1247
|
-
scroll: false,
|
|
1248
|
-
readOnly: ro,
|
|
1249
|
-
rootLabel: label,
|
|
1250
|
-
env: {
|
|
1251
|
-
docId: `article:${PULL_QUOTE_SCHEMA.name}`,
|
|
1252
|
-
docTitle: label
|
|
1253
|
-
}
|
|
1254
|
-
}), /* @__PURE__ */ jsx("style", { children: `[data-article-form] [data-kit-form] section:first-of-type { border-top-width: 0; margin-top: 0; padding-top: 0; }` })]
|
|
1354
|
+
children: /* @__PURE__ */ jsx(ArticleForm, {
|
|
1355
|
+
schema: PULL_QUOTE_SCHEMA,
|
|
1356
|
+
label,
|
|
1357
|
+
value: m,
|
|
1358
|
+
onChange: (v) => commit(v),
|
|
1359
|
+
ro
|
|
1255
1360
|
})
|
|
1256
1361
|
}) : null] });
|
|
1257
1362
|
}
|
|
@@ -1274,47 +1379,19 @@ function TakeawaysBlock({ m, set, el, ctx, autoFocus }) {
|
|
|
1274
1379
|
});
|
|
1275
1380
|
const setPoint = (i, v) => set({ points: m.points.map((p, j) => j === i ? v : p) }, `Edit key takeaways point ${i + 1}`);
|
|
1276
1381
|
/** V2 ArticleBlock onKeyDown, on the block itself (its lines handle their own keys) */
|
|
1277
|
-
const onKeyDown = (
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
else ctx.undo?.();
|
|
1286
|
-
return;
|
|
1287
|
-
}
|
|
1288
|
-
if (e.key === "Escape") {
|
|
1289
|
-
e.preventDefault();
|
|
1290
|
-
e.stopPropagation();
|
|
1291
|
-
ctx.exit(el);
|
|
1292
|
-
return;
|
|
1293
|
-
}
|
|
1294
|
-
if (ro) return;
|
|
1295
|
-
if (mod && k === "d" && ctx.duplicate) {
|
|
1296
|
-
e.preventDefault();
|
|
1297
|
-
e.stopPropagation();
|
|
1298
|
-
ctx.duplicate(el);
|
|
1299
|
-
} else if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown") && ctx.move) {
|
|
1300
|
-
e.preventDefault();
|
|
1301
|
-
ctx.move(el, e.key === "ArrowUp" ? -1 : 1);
|
|
1302
|
-
focusCard();
|
|
1303
|
-
} else if (e.key === "Backspace" || e.key === "Delete") {
|
|
1304
|
-
e.preventDefault();
|
|
1305
|
-
ctx.remove(el);
|
|
1306
|
-
}
|
|
1307
|
-
};
|
|
1308
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
1382
|
+
const onKeyDown = articleCardKeyDown({
|
|
1383
|
+
card,
|
|
1384
|
+
ctx,
|
|
1385
|
+
el,
|
|
1386
|
+
ro,
|
|
1387
|
+
focusCard
|
|
1388
|
+
});
|
|
1389
|
+
return /* @__PURE__ */ jsxs(ArticleCard, {
|
|
1309
1390
|
ref: card,
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
"aria-roledescription": "block",
|
|
1314
|
-
"data-rt-isolate": "",
|
|
1315
|
-
"data-article-block": "takeaways",
|
|
1391
|
+
label,
|
|
1392
|
+
kind: "takeaways",
|
|
1393
|
+
ro,
|
|
1316
1394
|
onKeyDown,
|
|
1317
|
-
className: "group/art relative rounded-kit-lg outline-none transition-shadow duration-150 focus-visible:shadow-[0_0_0_1px_var(--kit-line-strong)]",
|
|
1318
1395
|
children: [/* @__PURE__ */ jsxs("aside", {
|
|
1319
1396
|
className: "rounded-kit-lg border border-kit-line bg-kit-subtle px-4 py-3",
|
|
1320
1397
|
"aria-label": m.title || label,
|
|
@@ -1370,453 +1447,13 @@ function TakeawaysBlock({ m, set, el, ctx, autoFocus }) {
|
|
|
1370
1447
|
})]
|
|
1371
1448
|
}, i))
|
|
1372
1449
|
})]
|
|
1373
|
-
}),
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
label: `Remove ${label.toLowerCase()}`,
|
|
1378
|
-
onClick: () => ctx.remove(el),
|
|
1379
|
-
className: "hover:text-kit-danger-fg",
|
|
1380
|
-
children: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 })
|
|
1381
|
-
})
|
|
1382
|
-
})]
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
var EMBED_LABEL = "Embed";
|
|
1386
|
-
/** V2 article.ts ARTICLE_SCHEMAS.embed without `aspect` (the stored block keeps no shape): the popover is built from it. */
|
|
1387
|
-
var EMBED_SCHEMA = {
|
|
1388
|
-
name: "embed",
|
|
1389
|
-
title: EMBED_LABEL,
|
|
1390
|
-
kind: "object",
|
|
1391
|
-
icon: "code",
|
|
1392
|
-
fields: [{
|
|
1393
|
-
name: "url",
|
|
1394
|
-
title: "Link",
|
|
1395
|
-
type: "url",
|
|
1396
|
-
placeholder: "Paste a YouTube, X, CodePen, Figma or map link",
|
|
1397
|
-
validation: { required: true }
|
|
1398
|
-
}, {
|
|
1399
|
-
name: "title",
|
|
1400
|
-
title: "Title",
|
|
1401
|
-
type: "string",
|
|
1402
|
-
validation: {
|
|
1403
|
-
required: true,
|
|
1404
|
-
max: 120
|
|
1405
|
-
},
|
|
1406
|
-
description: "Not shown. Read by screen readers."
|
|
1407
|
-
}]
|
|
1408
|
-
};
|
|
1409
|
-
function EmbedBlock({ m, set, el, ctx, autoFocus }) {
|
|
1410
|
-
const label = EMBED_LABEL;
|
|
1411
|
-
const uid = React.useId();
|
|
1412
|
-
const card = React.useRef(null);
|
|
1413
|
-
const form = React.useRef(null);
|
|
1414
|
-
const open = useOpenArticle();
|
|
1415
|
-
const editing = open?.id === uid;
|
|
1416
|
-
const ro = !!ctx.readOnly;
|
|
1417
|
-
const modelRef = React.useRef(m);
|
|
1418
|
-
modelRef.current = m;
|
|
1419
|
-
const openPopover = React.useCallback((k) => {
|
|
1420
|
-
if (!ro) setOpenArticle({
|
|
1421
|
-
id: uid,
|
|
1422
|
-
focusKey: k ?? null,
|
|
1423
|
-
nonce: (openArticle?.nonce ?? 0) + 1
|
|
1424
|
-
});
|
|
1425
|
-
}, [uid, ro]);
|
|
1426
|
-
const close = React.useCallback(() => {
|
|
1427
|
-
if (openArticle?.id === uid) setOpenArticle(null);
|
|
1428
|
-
}, [uid]);
|
|
1429
|
-
const focusCard = React.useCallback(() => requestAnimationFrame(() => card.current?.focus({ preventScroll: true })), []);
|
|
1430
|
-
React.useEffect(() => () => {
|
|
1431
|
-
if (openArticle?.id === uid) setOpenArticle(null);
|
|
1432
|
-
}, [uid]);
|
|
1433
|
-
React.useEffect(() => {
|
|
1434
|
-
if (autoFocus && !ro) openPopover();
|
|
1435
|
-
}, []);
|
|
1436
|
-
const nonce = editing ? open.nonce : 0;
|
|
1437
|
-
React.useEffect(() => {
|
|
1438
|
-
if (!nonce) return;
|
|
1439
|
-
const id = requestAnimationFrame(() => focusField(form.current, openArticle?.focusKey ?? null));
|
|
1440
|
-
return () => cancelAnimationFrame(id);
|
|
1441
|
-
}, [nonce]);
|
|
1442
|
-
/** a form edit joins the body's one undo history (D54); typing coalesces per field */
|
|
1443
|
-
const commit = (next) => {
|
|
1444
|
-
const prev = modelRef.current;
|
|
1445
|
-
const field = ["url", "title"].find((k) => (next[k] ?? "") !== (prev[k] ?? "")) ?? "";
|
|
1446
|
-
set(() => next, `Edit ${label.toLowerCase()} ${field}`);
|
|
1447
|
-
};
|
|
1448
|
-
const onKeyDown = (e) => {
|
|
1449
|
-
if (e.defaultPrevented || e.target !== card.current) return;
|
|
1450
|
-
const mod = e.metaKey || e.ctrlKey;
|
|
1451
|
-
const k = eventKey(e);
|
|
1452
|
-
if (mod && k === "z") {
|
|
1453
|
-
e.preventDefault();
|
|
1454
|
-
e.stopPropagation();
|
|
1455
|
-
if (e.shiftKey) ctx.redo?.();
|
|
1456
|
-
else ctx.undo?.();
|
|
1457
|
-
return;
|
|
1458
|
-
}
|
|
1459
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
1460
|
-
e.preventDefault();
|
|
1461
|
-
openPopover();
|
|
1462
|
-
return;
|
|
1463
|
-
}
|
|
1464
|
-
if (e.key === "Escape") {
|
|
1465
|
-
e.preventDefault();
|
|
1466
|
-
e.stopPropagation();
|
|
1467
|
-
if (editing) close();
|
|
1468
|
-
else ctx.exit(el);
|
|
1469
|
-
return;
|
|
1470
|
-
}
|
|
1471
|
-
if (ro) return;
|
|
1472
|
-
if (mod && k === "d" && ctx.duplicate) {
|
|
1473
|
-
e.preventDefault();
|
|
1474
|
-
e.stopPropagation();
|
|
1475
|
-
ctx.duplicate(el);
|
|
1476
|
-
} else if (e.altKey && (e.key === "ArrowUp" || e.key === "ArrowDown") && ctx.move) {
|
|
1477
|
-
e.preventDefault();
|
|
1478
|
-
ctx.move(el, e.key === "ArrowUp" ? -1 : 1);
|
|
1479
|
-
focusCard();
|
|
1480
|
-
} else if (e.key === "Backspace" || e.key === "Delete") {
|
|
1481
|
-
e.preventDefault();
|
|
1482
|
-
ctx.remove(el);
|
|
1483
|
-
}
|
|
1484
|
-
};
|
|
1485
|
-
const actions = ro ? null : /* @__PURE__ */ jsxs("div", {
|
|
1486
|
-
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"),
|
|
1487
|
-
children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
1488
|
-
content: `Edit ${label.toLowerCase()}`,
|
|
1489
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
1490
|
-
size: "xs",
|
|
1491
|
-
label: `Edit ${label.toLowerCase()}`,
|
|
1492
|
-
onClick: () => editing ? close() : openPopover(),
|
|
1493
|
-
children: /* @__PURE__ */ jsx(IconSettings, { stroke: 1.5 })
|
|
1494
|
-
})
|
|
1495
|
-
}), /* @__PURE__ */ jsx(Tooltip, {
|
|
1496
|
-
content: `Remove ${label.toLowerCase()}`,
|
|
1497
|
-
children: /* @__PURE__ */ jsx(IconButton, {
|
|
1498
|
-
size: "xs",
|
|
1499
|
-
label: `Remove ${label.toLowerCase()}`,
|
|
1500
|
-
onClick: () => ctx.remove(el),
|
|
1501
|
-
className: "hover:text-kit-danger-fg",
|
|
1502
|
-
children: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 })
|
|
1503
|
-
})
|
|
1504
|
-
})]
|
|
1505
|
-
});
|
|
1506
|
-
return /* @__PURE__ */ jsxs(Popover, {
|
|
1507
|
-
open: editing,
|
|
1508
|
-
onOpenChange: (o, d) => {
|
|
1509
|
-
if (o) return;
|
|
1510
|
-
close();
|
|
1511
|
-
if (d?.reason !== "outside-press") focusCard();
|
|
1512
|
-
},
|
|
1513
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
1514
|
-
ref: card,
|
|
1515
|
-
role: "group",
|
|
1516
|
-
tabIndex: 0,
|
|
1517
|
-
"aria-label": `${label}${editing ? ", editing" : ""}`,
|
|
1518
|
-
"aria-roledescription": "block",
|
|
1519
|
-
"aria-haspopup": "dialog",
|
|
1520
|
-
"aria-expanded": editing,
|
|
1521
|
-
"data-rt-isolate": "",
|
|
1522
|
-
"data-article-block": "embed",
|
|
1523
|
-
onKeyDown,
|
|
1524
|
-
onClick: (e) => {
|
|
1525
|
-
if (ro || !e.currentTarget.contains(e.target)) return;
|
|
1526
|
-
const t = e.target;
|
|
1527
|
-
if (t.closest("button,a,[role=menuitem]")) return;
|
|
1528
|
-
openPopover(t.closest("[data-field]")?.getAttribute("data-field") ?? null);
|
|
1529
|
-
},
|
|
1530
|
-
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)]"),
|
|
1531
|
-
children: [
|
|
1532
|
-
/* @__PURE__ */ jsx(EmbedPreview, {
|
|
1533
|
-
m,
|
|
1534
|
-
onAdd: () => openPopover("url"),
|
|
1535
|
-
ro
|
|
1536
|
-
}),
|
|
1537
|
-
actions,
|
|
1538
|
-
/* @__PURE__ */ jsx(PopoverAnchor, { render: /* @__PURE__ */ jsx("span", {
|
|
1539
|
-
"aria-hidden": true,
|
|
1540
|
-
className: "pointer-events-none absolute right-2 top-2 size-px"
|
|
1541
|
-
}) })
|
|
1542
|
-
]
|
|
1543
|
-
}), editing ? /* @__PURE__ */ jsxs(PopoverContent, {
|
|
1544
|
-
align: "end",
|
|
1545
|
-
side: "bottom",
|
|
1546
|
-
className: "w-[340px] p-0",
|
|
1547
|
-
initialFocus: false,
|
|
1548
|
-
finalFocus: false,
|
|
1549
|
-
onKeyDown: (e) => {
|
|
1550
|
-
if ((e.metaKey || e.ctrlKey) && !e.altKey && eventKey(e) === "z" && !e.defaultPrevented) {
|
|
1551
|
-
e.preventDefault();
|
|
1552
|
-
if (e.shiftKey) ctx.redo?.();
|
|
1553
|
-
else ctx.undo?.();
|
|
1554
|
-
}
|
|
1555
|
-
},
|
|
1556
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
1557
|
-
className: "flex h-10 items-center gap-2 border-b border-kit-line-subtle pl-4 pr-1.5",
|
|
1558
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1559
|
-
className: "flex-1 truncate text-kit-sm font-medium text-kit-fg",
|
|
1560
|
-
children: label
|
|
1561
|
-
}), /* @__PURE__ */ jsx(IconButton, {
|
|
1562
|
-
size: "sm",
|
|
1563
|
-
label: "Close",
|
|
1564
|
-
onClick: () => (close(), focusCard()),
|
|
1565
|
-
children: /* @__PURE__ */ jsx(IconX, { stroke: 1.5 })
|
|
1566
|
-
})]
|
|
1567
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
1568
|
-
ref: form,
|
|
1569
|
-
className: "max-h-[min(480px,70vh)] overflow-y-auto pb-2",
|
|
1570
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
1571
|
-
"data-article-form": "",
|
|
1572
|
-
className: "min-w-0",
|
|
1573
|
-
children: [/* @__PURE__ */ jsx(SchemaForm, {
|
|
1574
|
-
schema: EMBED_SCHEMA,
|
|
1575
|
-
value: {
|
|
1576
|
-
url: m.url ?? "",
|
|
1577
|
-
title: m.title ?? ""
|
|
1578
|
-
},
|
|
1579
|
-
onChange: (v) => commit({
|
|
1580
|
-
...modelRef.current,
|
|
1581
|
-
url: String(v.url ?? ""),
|
|
1582
|
-
title: String(v.title ?? "")
|
|
1583
|
-
}),
|
|
1584
|
-
variant: "panel",
|
|
1585
|
-
scroll: false,
|
|
1586
|
-
readOnly: ro,
|
|
1587
|
-
rootLabel: label,
|
|
1588
|
-
env: {
|
|
1589
|
-
docId: `article:${EMBED_SCHEMA.name}`,
|
|
1590
|
-
docTitle: label
|
|
1591
|
-
}
|
|
1592
|
-
}), /* @__PURE__ */ jsx("style", { children: `[data-article-form] [data-kit-form] section:first-of-type { border-top-width: 0; margin-top: 0; padding-top: 0; }` })]
|
|
1593
|
-
})
|
|
1594
|
-
})]
|
|
1595
|
-
}) : null]
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
/** V2 article-blocks.tsx Empty: a block with nothing to show yet. */
|
|
1599
|
-
var ArticleEmpty = ({ icon, title, action, onAction, ro }) => /* @__PURE__ */ jsxs("div", {
|
|
1600
|
-
className: "flex items-center gap-3 rounded-kit-lg border border-dashed border-kit-line-strong bg-kit-subtle px-4 py-3.5",
|
|
1601
|
-
children: [
|
|
1602
|
-
/* @__PURE__ */ jsx("span", {
|
|
1603
|
-
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",
|
|
1604
|
-
children: icon
|
|
1605
|
-
}),
|
|
1606
|
-
/* @__PURE__ */ jsx("span", {
|
|
1607
|
-
className: "min-w-0 flex-1 text-kit-sm font-medium text-kit-fg",
|
|
1608
|
-
children: title
|
|
1609
|
-
}),
|
|
1610
|
-
ro ? null : /* @__PURE__ */ jsx(Button, {
|
|
1611
|
-
size: "sm",
|
|
1612
|
-
onClick: onAction,
|
|
1613
|
-
children: action
|
|
1614
|
-
})
|
|
1615
|
-
]
|
|
1616
|
-
});
|
|
1617
|
-
var PROVIDER_ICON = {
|
|
1618
|
-
youtube: /* @__PURE__ */ jsx(IconBrandYoutube, { stroke: 1.5 }),
|
|
1619
|
-
vimeo: /* @__PURE__ */ jsx(IconBrandVimeo, { stroke: 1.5 }),
|
|
1620
|
-
x: /* @__PURE__ */ jsx(IconBrandX, { stroke: 1.5 }),
|
|
1621
|
-
codepen: /* @__PURE__ */ jsx(IconBrandCodepen, { stroke: 1.5 }),
|
|
1622
|
-
figma: /* @__PURE__ */ jsx(IconBrandFigma, { stroke: 1.5 }),
|
|
1623
|
-
maps: /* @__PURE__ */ jsx(IconMap, { stroke: 1.5 }),
|
|
1624
|
-
spotify: /* @__PURE__ */ jsx(IconBrandSpotify, { stroke: 1.5 }),
|
|
1625
|
-
podcast: /* @__PURE__ */ jsx(IconMicrophone, { stroke: 1.5 }),
|
|
1626
|
-
audio: /* @__PURE__ */ jsx(IconHeadphones, { stroke: 1.5 }),
|
|
1627
|
-
link: /* @__PURE__ */ jsx(IconWorld, { stroke: 1.5 })
|
|
1628
|
-
};
|
|
1629
|
-
/** V2 EmbedPreview: a faithful placeholder (provider, title and shape), no third-party frame in the editor. Drawn 16:9. */
|
|
1630
|
-
function EmbedPreview({ m, onAdd, ro }) {
|
|
1631
|
-
if (!m.url) return /* @__PURE__ */ jsx(ArticleEmpty, {
|
|
1632
|
-
icon: /* @__PURE__ */ jsx(IconLink, { stroke: 1.5 }),
|
|
1633
|
-
title: "Embed",
|
|
1634
|
-
action: "Paste a link",
|
|
1635
|
-
onAction: onAdd,
|
|
1636
|
-
ro
|
|
1637
|
-
});
|
|
1638
|
-
const info = detectEmbed(m.url);
|
|
1639
|
-
const icon = PROVIDER_ICON[info.provider];
|
|
1640
|
-
const title = m.title || /* @__PURE__ */ jsx("span", {
|
|
1641
|
-
className: "text-kit-warning-fg",
|
|
1642
|
-
children: "Add a title"
|
|
1643
|
-
});
|
|
1644
|
-
if (info.shape === "frame") return /* @__PURE__ */ jsx("figure", {
|
|
1645
|
-
className: "m-0",
|
|
1646
|
-
"data-field": "url",
|
|
1647
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
1648
|
-
className: "relative mx-auto flex w-full flex-col items-center justify-center gap-2 overflow-hidden rounded-kit-lg border border-kit-line bg-kit-subtle text-center",
|
|
1649
|
-
style: { aspectRatio: "16 / 9" },
|
|
1650
|
-
children: [
|
|
1651
|
-
/* @__PURE__ */ jsx("span", {
|
|
1652
|
-
className: "flex size-11 items-center justify-center rounded-full bg-kit-inverse text-kit-fg-inverse [&_svg]:size-5",
|
|
1653
|
-
children: info.provider === "youtube" || info.provider === "vimeo" ? /* @__PURE__ */ jsx(IconPlayerPlayFilled, {}) : icon
|
|
1654
|
-
}),
|
|
1655
|
-
/* @__PURE__ */ jsx("span", {
|
|
1656
|
-
"data-field": "title",
|
|
1657
|
-
className: "max-w-[80%] truncate text-kit-sm font-medium text-kit-fg",
|
|
1658
|
-
children: title
|
|
1659
|
-
}),
|
|
1660
|
-
/* @__PURE__ */ jsxs("span", {
|
|
1661
|
-
className: "absolute left-3 top-2.5 flex items-center gap-1.5 text-kit-xs text-kit-fg-2 [&_svg]:size-3.5",
|
|
1662
|
-
children: [icon, info.label]
|
|
1663
|
-
})
|
|
1664
|
-
]
|
|
1665
|
-
})
|
|
1666
|
-
});
|
|
1667
|
-
if (info.shape === "audio") return /* @__PURE__ */ jsxs("figure", {
|
|
1668
|
-
className: "m-0 flex items-center gap-3 rounded-kit-lg border border-kit-line bg-kit-subtle px-3 py-2.5",
|
|
1669
|
-
"data-field": "url",
|
|
1670
|
-
children: [
|
|
1671
|
-
/* @__PURE__ */ jsx("span", {
|
|
1672
|
-
className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-kit-inverse text-kit-fg-inverse [&_svg]:size-4",
|
|
1673
|
-
children: /* @__PURE__ */ jsx(IconPlayerPlayFilled, {})
|
|
1674
|
-
}),
|
|
1675
|
-
/* @__PURE__ */ jsxs("span", {
|
|
1676
|
-
className: "min-w-0 flex-1",
|
|
1677
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1678
|
-
"data-field": "title",
|
|
1679
|
-
className: "block truncate text-kit-sm font-medium text-kit-fg",
|
|
1680
|
-
children: title
|
|
1681
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
1682
|
-
className: "flex items-center gap-1 text-kit-xs text-kit-fg-3 [&_svg]:size-3",
|
|
1683
|
-
children: [icon, info.label]
|
|
1684
|
-
})]
|
|
1685
|
-
}),
|
|
1686
|
-
/* @__PURE__ */ jsx("span", {
|
|
1687
|
-
"aria-hidden": true,
|
|
1688
|
-
className: "hidden h-1 w-28 rounded-full bg-kit-line-strong sm:block"
|
|
1689
|
-
})
|
|
1690
|
-
]
|
|
1691
|
-
});
|
|
1692
|
-
return /* @__PURE__ */ jsxs("figure", {
|
|
1693
|
-
className: "m-0 flex items-center gap-3 rounded-kit-lg border border-kit-line bg-kit-surface px-3 py-2.5",
|
|
1694
|
-
"data-field": "url",
|
|
1695
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1696
|
-
className: "flex size-9 shrink-0 items-center justify-center rounded-kit-md bg-kit-muted text-kit-fg-2 [&_svg]:size-4",
|
|
1697
|
-
children: icon
|
|
1698
|
-
}), /* @__PURE__ */ jsxs("span", {
|
|
1699
|
-
className: "min-w-0 flex-1",
|
|
1700
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1701
|
-
"data-field": "title",
|
|
1702
|
-
className: "block truncate text-kit-sm font-medium text-kit-fg",
|
|
1703
|
-
children: title
|
|
1704
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
1705
|
-
className: "block truncate text-kit-xs text-kit-fg-3",
|
|
1706
|
-
children: info.shape === "post" ? `${info.label} · ${m.url.replace(/^https?:\/\/(www\.)?/, "")}` : m.url.replace(/^https?:\/\/(www\.)?/, "")
|
|
1707
|
-
})]
|
|
1450
|
+
}), /* @__PURE__ */ jsx(ArticleActions, {
|
|
1451
|
+
label,
|
|
1452
|
+
ctx,
|
|
1453
|
+
el
|
|
1708
1454
|
})]
|
|
1709
1455
|
});
|
|
1710
1456
|
}
|
|
1711
|
-
function ArticlePanel({ label, anchor, nonce, focusKey, onClose, focusBack, ctx, el, children }) {
|
|
1712
|
-
const panel = React.useRef(null);
|
|
1713
|
-
const titleId = React.useId();
|
|
1714
|
-
const container = anchor?.closest("[data-expanded-editor]") ?? null;
|
|
1715
|
-
React.useEffect(() => {
|
|
1716
|
-
const timers = [];
|
|
1717
|
-
let landed = false;
|
|
1718
|
-
for (const ms of [
|
|
1719
|
-
40,
|
|
1720
|
-
90,
|
|
1721
|
-
160,
|
|
1722
|
-
260,
|
|
1723
|
-
420,
|
|
1724
|
-
640
|
|
1725
|
-
]) timers.push(window.setTimeout(() => {
|
|
1726
|
-
const root = panel.current?.el() ?? null;
|
|
1727
|
-
if (!root?.querySelector("[data-field-key]")) return;
|
|
1728
|
-
const a = document.activeElement;
|
|
1729
|
-
const lost = !a || a === document.body || a.hasAttribute("data-expanded-editor") || a.hasAttribute("data-article-block");
|
|
1730
|
-
if (root.contains(a)) return void (landed = true);
|
|
1731
|
-
if (!landed || lost) focusField(root, focusKey);
|
|
1732
|
-
if (root.contains(document.activeElement)) landed = true;
|
|
1733
|
-
}, ms));
|
|
1734
|
-
return () => timers.forEach((t) => window.clearTimeout(t));
|
|
1735
|
-
}, [nonce]);
|
|
1736
|
-
React.useEffect(() => panel.current?.announce(`${label} properties.`), [label]);
|
|
1737
|
-
const header = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1738
|
-
/* @__PURE__ */ jsx("span", {
|
|
1739
|
-
id: titleId,
|
|
1740
|
-
className: "min-w-0 flex-1 truncate text-kit-sm font-medium text-kit-fg",
|
|
1741
|
-
children: label
|
|
1742
|
-
}),
|
|
1743
|
-
/* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsx(MenuTrigger, { render: /* @__PURE__ */ jsx(IconButton, {
|
|
1744
|
-
size: "sm",
|
|
1745
|
-
label: `${label} actions`,
|
|
1746
|
-
children: /* @__PURE__ */ jsx(IconDots, { stroke: 1.5 })
|
|
1747
|
-
}) }), /* @__PURE__ */ jsxs(MenuContent, {
|
|
1748
|
-
align: "end",
|
|
1749
|
-
className: "w-52",
|
|
1750
|
-
children: [
|
|
1751
|
-
ctx.readOnly || !ctx.duplicate ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
1752
|
-
icon: /* @__PURE__ */ jsx(IconCopy, { stroke: 1.5 }),
|
|
1753
|
-
onClick: () => ctx.duplicate?.(el),
|
|
1754
|
-
children: "Duplicate"
|
|
1755
|
-
}), /* @__PURE__ */ jsx(MenuSeparator, {})] }),
|
|
1756
|
-
/* @__PURE__ */ jsx(MenuItem, {
|
|
1757
|
-
icon: /* @__PURE__ */ jsx(IconArrowsMove, { stroke: 1.5 }),
|
|
1758
|
-
onClick: () => panel.current?.keyboard("move"),
|
|
1759
|
-
children: "Move panel"
|
|
1760
|
-
}),
|
|
1761
|
-
/* @__PURE__ */ jsx(MenuItem, {
|
|
1762
|
-
icon: /* @__PURE__ */ jsx(IconDimensions, { stroke: 1.5 }),
|
|
1763
|
-
onClick: () => panel.current?.keyboard("resize"),
|
|
1764
|
-
children: "Resize panel"
|
|
1765
|
-
}),
|
|
1766
|
-
/* @__PURE__ */ jsx(MenuItem, {
|
|
1767
|
-
icon: /* @__PURE__ */ jsx(IconLayoutSidebar, { stroke: 1.5 }),
|
|
1768
|
-
onClick: () => panel.current?.dock("left"),
|
|
1769
|
-
children: "Dock left"
|
|
1770
|
-
}),
|
|
1771
|
-
/* @__PURE__ */ jsx(MenuItem, {
|
|
1772
|
-
icon: /* @__PURE__ */ jsx(IconLayoutSidebarRight, { stroke: 1.5 }),
|
|
1773
|
-
onClick: () => panel.current?.dock("right"),
|
|
1774
|
-
children: "Dock right"
|
|
1775
|
-
}),
|
|
1776
|
-
ctx.readOnly ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuSeparator, {}), /* @__PURE__ */ jsx(MenuItem, {
|
|
1777
|
-
danger: true,
|
|
1778
|
-
icon: /* @__PURE__ */ jsx(IconTrash, { stroke: 1.5 }),
|
|
1779
|
-
onClick: () => ctx.remove(el),
|
|
1780
|
-
children: "Remove"
|
|
1781
|
-
})] })
|
|
1782
|
-
]
|
|
1783
|
-
})] }),
|
|
1784
|
-
/* @__PURE__ */ jsx(IconButton, {
|
|
1785
|
-
size: "sm",
|
|
1786
|
-
label: "Close properties",
|
|
1787
|
-
tooltip: "Close",
|
|
1788
|
-
onClick: () => (onClose(), focusBack()),
|
|
1789
|
-
children: /* @__PURE__ */ jsx(IconX, { stroke: 1.5 })
|
|
1790
|
-
})
|
|
1791
|
-
] });
|
|
1792
|
-
return /* @__PURE__ */ jsx(FloatingPanel, {
|
|
1793
|
-
ref: panel,
|
|
1794
|
-
id: "article-properties",
|
|
1795
|
-
labelledBy: titleId,
|
|
1796
|
-
anchor,
|
|
1797
|
-
container,
|
|
1798
|
-
header,
|
|
1799
|
-
onClose,
|
|
1800
|
-
focusBack,
|
|
1801
|
-
onPanelKeyDown: (e) => {
|
|
1802
|
-
if ((e.metaKey || e.ctrlKey) && !e.altKey && eventKey(e) === "z" && !e.defaultPrevented) {
|
|
1803
|
-
e.preventDefault();
|
|
1804
|
-
if (e.shiftKey) ctx.redo?.();
|
|
1805
|
-
else ctx.undo?.();
|
|
1806
|
-
}
|
|
1807
|
-
},
|
|
1808
|
-
sheetFooter: /* @__PURE__ */ jsx(Button, {
|
|
1809
|
-
size: "sm",
|
|
1810
|
-
variant: "primary",
|
|
1811
|
-
onClick: () => (onClose(), focusBack()),
|
|
1812
|
-
children: "Done"
|
|
1813
|
-
}),
|
|
1814
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
1815
|
-
className: "pb-3 pt-1",
|
|
1816
|
-
children
|
|
1817
|
-
})
|
|
1818
|
-
});
|
|
1819
|
-
}
|
|
1820
1457
|
function PullQuotePreview({ m }) {
|
|
1821
1458
|
return /* @__PURE__ */ jsxs("figure", {
|
|
1822
1459
|
className: "m-0 py-2 pl-5 pr-10",
|