quoin-editor 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -48
- package/dist/index.cjs +80 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -16
- package/dist/index.d.ts +125 -16
- package/dist/index.js +75 -45
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,12 +52,12 @@ function blockByType(definitions, type) {
|
|
|
52
52
|
return void 0;
|
|
53
53
|
}
|
|
54
54
|
function paletteBlocks(definitions) {
|
|
55
|
-
const
|
|
55
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
56
56
|
const out = [];
|
|
57
57
|
for (let i = definitions.length - 1; i >= 0; i -= 1) {
|
|
58
58
|
const d = definitions[i];
|
|
59
|
-
if (
|
|
60
|
-
|
|
59
|
+
if (seen2.has(d.type)) continue;
|
|
60
|
+
seen2.add(d.type);
|
|
61
61
|
if (d.inSlashPalette === false) continue;
|
|
62
62
|
out.push(d);
|
|
63
63
|
}
|
|
@@ -143,12 +143,7 @@ function Button({
|
|
|
143
143
|
|
|
144
144
|
// src/upload/FilePickerButton.tsx
|
|
145
145
|
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
146
|
-
function FilePickerButton({
|
|
147
|
-
label,
|
|
148
|
-
accept,
|
|
149
|
-
disabled,
|
|
150
|
-
onPick
|
|
151
|
-
}) {
|
|
146
|
+
function FilePickerButton({ label, accept, disabled, onPick }) {
|
|
152
147
|
return /* @__PURE__ */ jsxs("span", { className: "relative inline-flex rounded-md has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-ring/50", children: [
|
|
153
148
|
/* @__PURE__ */ jsx3(Button, { asChild: true, variant: "outline", size: "sm", children: /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: disabled ? "pointer-events-none opacity-50" : void 0, children: [
|
|
154
149
|
/* @__PURE__ */ jsx3(Upload, { "aria-hidden": "true" }),
|
|
@@ -1366,12 +1361,7 @@ import { useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
|
1366
1361
|
// src/upload/UploadField.tsx
|
|
1367
1362
|
import * as React6 from "react";
|
|
1368
1363
|
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1369
|
-
function UploadField({
|
|
1370
|
-
category,
|
|
1371
|
-
label,
|
|
1372
|
-
accept,
|
|
1373
|
-
onUploaded
|
|
1374
|
-
}) {
|
|
1364
|
+
function UploadField({ category, label, accept, onUploaded }) {
|
|
1375
1365
|
const { upload, onError } = useQuoin();
|
|
1376
1366
|
const [busy, setBusy] = React6.useState(false);
|
|
1377
1367
|
const [error, setError] = React6.useState(null);
|
|
@@ -1835,9 +1825,8 @@ function BlockEditor({
|
|
|
1835
1825
|
onDone
|
|
1836
1826
|
}) {
|
|
1837
1827
|
const { blockTypes } = useQuoin();
|
|
1838
|
-
const
|
|
1839
|
-
if (!
|
|
1840
|
-
const Editor = definition.editor;
|
|
1828
|
+
const Editor = blockByType(blockTypes, type)?.editor;
|
|
1829
|
+
if (!Editor) return null;
|
|
1841
1830
|
return /* @__PURE__ */ jsx21(Editor, { data, onChange, onInsertBlock, onDone });
|
|
1842
1831
|
}
|
|
1843
1832
|
|
|
@@ -1886,6 +1875,15 @@ function sourceOffsetFor(source, probe) {
|
|
|
1886
1875
|
return search(source, probe);
|
|
1887
1876
|
}
|
|
1888
1877
|
|
|
1878
|
+
// src/dev-warn.ts
|
|
1879
|
+
var seen = /* @__PURE__ */ new Set();
|
|
1880
|
+
function devWarn(message) {
|
|
1881
|
+
if (typeof process !== "undefined" && process?.env?.NODE_ENV === "production") return;
|
|
1882
|
+
if (seen.has(message)) return;
|
|
1883
|
+
seen.add(message);
|
|
1884
|
+
console.warn(`Quoin: ${message}`);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1889
1887
|
// src/canvas/BlockCard.tsx
|
|
1890
1888
|
import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1891
1889
|
var INTERACTIVE_SELECTOR = 'a, button, input, textarea, select, [role="button"], [tabindex], iframe';
|
|
@@ -1907,7 +1905,8 @@ function BlockCardImpl({
|
|
|
1907
1905
|
const { blockTypes, onError } = useQuoin();
|
|
1908
1906
|
const definition = blockByType(blockTypes, block.type);
|
|
1909
1907
|
const Preview = definition?.preview;
|
|
1910
|
-
const
|
|
1908
|
+
const editable = Boolean(definition?.editor) && !readOnly;
|
|
1909
|
+
const [editing, setEditing] = React8.useState(editingByDefault && editable);
|
|
1911
1910
|
const editButtonRef = React8.useRef(null);
|
|
1912
1911
|
const editorContainerRef = React8.useRef(null);
|
|
1913
1912
|
const returnFocusOnClose = React8.useRef(false);
|
|
@@ -1987,7 +1986,13 @@ function BlockCardImpl({
|
|
|
1987
1986
|
reportedType.current = block.type;
|
|
1988
1987
|
onError(new Error(`No block type is registered for "${block.type}".`));
|
|
1989
1988
|
}, [definition, block.type, onError]);
|
|
1990
|
-
|
|
1989
|
+
React8.useEffect(() => {
|
|
1990
|
+
if (!readOnly || !definition || definition.preview) return;
|
|
1991
|
+
devWarn(
|
|
1992
|
+
`the block type "${block.type}" has no \`preview\`, so a read-only canvas can draw only its label. Give its definition a \`preview\` to render the block itself.`
|
|
1993
|
+
);
|
|
1994
|
+
}, [readOnly, definition, block.type]);
|
|
1995
|
+
if (definition?.alwaysEditing && editable) {
|
|
1991
1996
|
return /* @__PURE__ */ jsx22(
|
|
1992
1997
|
BlockEditor,
|
|
1993
1998
|
{
|
|
@@ -2027,7 +2032,7 @@ function BlockCardImpl({
|
|
|
2027
2032
|
);
|
|
2028
2033
|
}
|
|
2029
2034
|
const rest = Preview ? /* @__PURE__ */ jsx22(Preview, { data: block.data, blockId: block.id }) : /* @__PURE__ */ jsx22("p", { className: "py-1 text-sm text-muted-foreground", children: definition?.label ?? block.type });
|
|
2030
|
-
if (
|
|
2035
|
+
if (!editable) return /* @__PURE__ */ jsx22("div", { children: rest });
|
|
2031
2036
|
return /* @__PURE__ */ jsxs15("div", { className: "group/card relative", children: [
|
|
2032
2037
|
/* @__PURE__ */ jsx22(
|
|
2033
2038
|
"div",
|
|
@@ -2189,6 +2194,16 @@ function DocumentStarter({
|
|
|
2189
2194
|
] });
|
|
2190
2195
|
}
|
|
2191
2196
|
|
|
2197
|
+
// src/canvas/ReadOnlyCanvas.tsx
|
|
2198
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
2199
|
+
function ReadOnlyCanvas({
|
|
2200
|
+
blocks,
|
|
2201
|
+
onUpdate,
|
|
2202
|
+
root
|
|
2203
|
+
}) {
|
|
2204
|
+
return /* @__PURE__ */ jsx25("div", { ...root, children: /* @__PURE__ */ jsx25("ul", { className: "flex flex-col", children: blocks.map((b, i) => /* @__PURE__ */ jsx25("li", { children: /* @__PURE__ */ jsx25("div", { "data-block-index": i, className: "rounded-lg px-2 py-1", children: /* @__PURE__ */ jsx25(BlockCard, { block: b, index: i, editingByDefault: false, readOnly: true, onUpdate }) }) }, b.id)) }) });
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2192
2207
|
// src/canvas/reorder-indices.ts
|
|
2193
2208
|
function reorderIndices(ids, activeId, overId) {
|
|
2194
2209
|
if (overId === null || activeId === overId) return null;
|
|
@@ -2218,7 +2233,7 @@ function sameBlocks(a, b) {
|
|
|
2218
2233
|
import { useSortable } from "@dnd-kit/sortable";
|
|
2219
2234
|
import { CSS } from "@dnd-kit/utilities";
|
|
2220
2235
|
import { GripVertical } from "lucide-react";
|
|
2221
|
-
import { jsx as
|
|
2236
|
+
import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2222
2237
|
var GUTTER = [
|
|
2223
2238
|
"flex items-center justify-end gap-0.5 pb-1",
|
|
2224
2239
|
"lg:absolute lg:right-full lg:top-1 lg:justify-start lg:pb-0 lg:pr-1",
|
|
@@ -2241,7 +2256,7 @@ function SortableBlock({
|
|
|
2241
2256
|
children: [
|
|
2242
2257
|
/* @__PURE__ */ jsxs18("div", { className: GUTTER, children: [
|
|
2243
2258
|
actions,
|
|
2244
|
-
/* @__PURE__ */
|
|
2259
|
+
/* @__PURE__ */ jsx26(
|
|
2245
2260
|
Button,
|
|
2246
2261
|
{
|
|
2247
2262
|
type: "button",
|
|
@@ -2252,17 +2267,17 @@ function SortableBlock({
|
|
|
2252
2267
|
"aria-label": `Reorder block ${index + 1}`,
|
|
2253
2268
|
...attributes,
|
|
2254
2269
|
...listeners,
|
|
2255
|
-
children: /* @__PURE__ */
|
|
2270
|
+
children: /* @__PURE__ */ jsx26(GripVertical, { "aria-hidden": "true" })
|
|
2256
2271
|
}
|
|
2257
2272
|
)
|
|
2258
2273
|
] }),
|
|
2259
|
-
/* @__PURE__ */
|
|
2274
|
+
/* @__PURE__ */ jsx26(
|
|
2260
2275
|
"div",
|
|
2261
2276
|
{
|
|
2262
2277
|
"data-block-index": index,
|
|
2263
2278
|
className: cn(
|
|
2264
2279
|
"rounded-lg px-2 py-1 transition-colors",
|
|
2265
|
-
isDragging ? "bg-
|
|
2280
|
+
isDragging ? "bg-popover shadow-md" : "group-hover/block:bg-muted/40"
|
|
2266
2281
|
),
|
|
2267
2282
|
children
|
|
2268
2283
|
}
|
|
@@ -2272,8 +2287,7 @@ function SortableBlock({
|
|
|
2272
2287
|
);
|
|
2273
2288
|
}
|
|
2274
2289
|
|
|
2275
|
-
// src/canvas/
|
|
2276
|
-
import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2290
|
+
// src/canvas/unacknowledged.ts
|
|
2277
2291
|
var UNACKNOWLEDGED_LIMIT = 64;
|
|
2278
2292
|
function remember(outstanding, blocks) {
|
|
2279
2293
|
outstanding.add(blocks);
|
|
@@ -2283,9 +2297,13 @@ function remember(outstanding, blocks) {
|
|
|
2283
2297
|
outstanding.delete(oldest.value);
|
|
2284
2298
|
}
|
|
2285
2299
|
}
|
|
2300
|
+
|
|
2301
|
+
// src/canvas/BlockCanvas.tsx
|
|
2302
|
+
import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2286
2303
|
function Canvas({
|
|
2287
2304
|
blocks,
|
|
2288
2305
|
onChange,
|
|
2306
|
+
onDelete,
|
|
2289
2307
|
blockTypes,
|
|
2290
2308
|
readOnly,
|
|
2291
2309
|
className,
|
|
@@ -2303,6 +2321,7 @@ function Canvas({
|
|
|
2303
2321
|
const [seedSelection, setSeedSelection] = React11.useState(null);
|
|
2304
2322
|
const listRef = React11.useRef(null);
|
|
2305
2323
|
const pendingDeleteIndexRef = React11.useRef(null);
|
|
2324
|
+
const removedRef = React11.useRef(null);
|
|
2306
2325
|
const announcements = React11.useMemo(
|
|
2307
2326
|
() => buildAnnouncements(list.blocks, blockTypes),
|
|
2308
2327
|
[list.blocks, blockTypes]
|
|
@@ -2315,7 +2334,10 @@ function Canvas({
|
|
|
2315
2334
|
lastEmitted.current = list.blocks;
|
|
2316
2335
|
remember(unacknowledged.current, list.blocks);
|
|
2317
2336
|
onChange(list.blocks);
|
|
2318
|
-
|
|
2337
|
+
const removed = removedRef.current;
|
|
2338
|
+
removedRef.current = null;
|
|
2339
|
+
if (removed) onDelete?.(removed.block, removed.index);
|
|
2340
|
+
}, [list.blocks, onChange, onDelete]);
|
|
2319
2341
|
React11.useEffect(() => {
|
|
2320
2342
|
const previous = lastSupplied.current;
|
|
2321
2343
|
lastSupplied.current = blocks;
|
|
@@ -2337,7 +2359,10 @@ function Canvas({
|
|
|
2337
2359
|
[insertAt, blockTypes]
|
|
2338
2360
|
);
|
|
2339
2361
|
function handleDelete(index, id2) {
|
|
2362
|
+
const block = list.blocks[index];
|
|
2340
2363
|
pendingDeleteIndexRef.current = index;
|
|
2364
|
+
if (block) removedRef.current = { block, index };
|
|
2365
|
+
setNewBlockId((current) => current === id2 ? null : current);
|
|
2341
2366
|
remove(id2);
|
|
2342
2367
|
}
|
|
2343
2368
|
React11.useEffect(() => {
|
|
@@ -2364,12 +2389,10 @@ function Canvas({
|
|
|
2364
2389
|
role: ariaLabel ? "group" : void 0,
|
|
2365
2390
|
"aria-label": ariaLabel
|
|
2366
2391
|
};
|
|
2367
|
-
if (readOnly) {
|
|
2368
|
-
return /* @__PURE__ */ jsx26("div", { ...rootProps, children: /* @__PURE__ */ jsx26("ul", { className: "flex flex-col", children: list.blocks.map((b, i) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsx26("div", { "data-block-index": i, className: "rounded-lg px-2 py-1", children: /* @__PURE__ */ jsx26(BlockCard, { block: b, index: i, editingByDefault: false, readOnly: true, onUpdate: update }) }) }, b.id)) }) });
|
|
2369
|
-
}
|
|
2392
|
+
if (readOnly) return /* @__PURE__ */ jsx27(ReadOnlyCanvas, { blocks: list.blocks, onUpdate: update, root: rootProps });
|
|
2370
2393
|
return /* @__PURE__ */ jsxs19("div", { ...rootProps, children: [
|
|
2371
|
-
/* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2394
|
+
/* @__PURE__ */ jsx27(BlockInserter, { index: 0, onInsert: insertPlain, label: "Insert a block at the start" }),
|
|
2395
|
+
/* @__PURE__ */ jsx27(
|
|
2373
2396
|
DndContext,
|
|
2374
2397
|
{
|
|
2375
2398
|
id: dndId,
|
|
@@ -2378,13 +2401,13 @@ function Canvas({
|
|
|
2378
2401
|
modifiers: [restrictToVerticalAxis],
|
|
2379
2402
|
accessibility: { announcements },
|
|
2380
2403
|
onDragEnd: handleDragEnd,
|
|
2381
|
-
children: /* @__PURE__ */
|
|
2382
|
-
/* @__PURE__ */
|
|
2404
|
+
children: /* @__PURE__ */ jsx27(SortableContext, { items: list.blocks.map((b) => b.id), strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx27("ul", { className: "flex flex-col", children: list.blocks.map((b, i) => /* @__PURE__ */ jsxs19("li", { children: [
|
|
2405
|
+
/* @__PURE__ */ jsx27(
|
|
2383
2406
|
SortableBlock,
|
|
2384
2407
|
{
|
|
2385
2408
|
id: b.id,
|
|
2386
2409
|
index: i,
|
|
2387
|
-
actions: /* @__PURE__ */
|
|
2410
|
+
actions: /* @__PURE__ */ jsx27(
|
|
2388
2411
|
BlockActionsMenu,
|
|
2389
2412
|
{
|
|
2390
2413
|
index: i,
|
|
@@ -2394,7 +2417,7 @@ function Canvas({
|
|
|
2394
2417
|
onDelete: () => handleDelete(i, b.id)
|
|
2395
2418
|
}
|
|
2396
2419
|
),
|
|
2397
|
-
children: /* @__PURE__ */
|
|
2420
|
+
children: /* @__PURE__ */ jsx27(
|
|
2398
2421
|
BlockCard,
|
|
2399
2422
|
{
|
|
2400
2423
|
block: b,
|
|
@@ -2407,7 +2430,7 @@ function Canvas({
|
|
|
2407
2430
|
)
|
|
2408
2431
|
}
|
|
2409
2432
|
),
|
|
2410
|
-
/* @__PURE__ */
|
|
2433
|
+
/* @__PURE__ */ jsx27(
|
|
2411
2434
|
BlockInserter,
|
|
2412
2435
|
{
|
|
2413
2436
|
index: i + 1,
|
|
@@ -2418,7 +2441,7 @@ function Canvas({
|
|
|
2418
2441
|
] }, b.id)) }) })
|
|
2419
2442
|
}
|
|
2420
2443
|
),
|
|
2421
|
-
/* @__PURE__ */
|
|
2444
|
+
/* @__PURE__ */ jsx27(
|
|
2422
2445
|
DocumentStarter,
|
|
2423
2446
|
{
|
|
2424
2447
|
autoFocus: list.blocks.length === 0,
|
|
@@ -2434,6 +2457,7 @@ function Canvas({
|
|
|
2434
2457
|
function BlockCanvas({
|
|
2435
2458
|
blocks,
|
|
2436
2459
|
onChange,
|
|
2460
|
+
onDelete,
|
|
2437
2461
|
blockTypes = coreBlocks,
|
|
2438
2462
|
upload,
|
|
2439
2463
|
resolveAssetUrl,
|
|
@@ -2447,11 +2471,12 @@ function BlockCanvas({
|
|
|
2447
2471
|
() => ({ blockTypes, upload, resolveAssetUrl, onError }),
|
|
2448
2472
|
[blockTypes, upload, resolveAssetUrl, onError]
|
|
2449
2473
|
);
|
|
2450
|
-
return /* @__PURE__ */
|
|
2474
|
+
return /* @__PURE__ */ jsx27(QuoinProvider, { value: config, children: /* @__PURE__ */ jsx27(
|
|
2451
2475
|
Canvas,
|
|
2452
2476
|
{
|
|
2453
2477
|
blocks,
|
|
2454
2478
|
onChange,
|
|
2479
|
+
onDelete,
|
|
2455
2480
|
blockTypes,
|
|
2456
2481
|
readOnly,
|
|
2457
2482
|
className,
|
|
@@ -2463,6 +2488,9 @@ function BlockCanvas({
|
|
|
2463
2488
|
|
|
2464
2489
|
// src/hooks/useAutosave.ts
|
|
2465
2490
|
import * as React12 from "react";
|
|
2491
|
+
function reportedFailure(result) {
|
|
2492
|
+
return typeof result === "object" && result !== null && "ok" in result && result.ok === false;
|
|
2493
|
+
}
|
|
2466
2494
|
function useAutosave({
|
|
2467
2495
|
deps,
|
|
2468
2496
|
build,
|
|
@@ -2486,7 +2514,7 @@ function useAutosave({
|
|
|
2486
2514
|
setStatus("saving");
|
|
2487
2515
|
try {
|
|
2488
2516
|
const res = await saveRef.current(buildRef.current());
|
|
2489
|
-
if (res
|
|
2517
|
+
if (reportedFailure(res)) throw new Error("save rejected");
|
|
2490
2518
|
retryRef.current = 0;
|
|
2491
2519
|
dirtyRef.current = false;
|
|
2492
2520
|
setStatus("saved");
|
|
@@ -2502,10 +2530,10 @@ function useAutosave({
|
|
|
2502
2530
|
});
|
|
2503
2531
|
const seenRef = React12.useRef(null);
|
|
2504
2532
|
React12.useEffect(() => {
|
|
2505
|
-
const
|
|
2533
|
+
const seen2 = seenRef.current;
|
|
2506
2534
|
seenRef.current = deps;
|
|
2507
|
-
if (
|
|
2508
|
-
const unchanged =
|
|
2535
|
+
if (seen2 === null) return;
|
|
2536
|
+
const unchanged = seen2.length === deps.length && seen2.every((v, i) => v === deps[i]);
|
|
2509
2537
|
if (!unchanged) dirtyRef.current = true;
|
|
2510
2538
|
}, deps);
|
|
2511
2539
|
React12.useEffect(() => {
|
|
@@ -2527,8 +2555,10 @@ function useAutosave({
|
|
|
2527
2555
|
}
|
|
2528
2556
|
export {
|
|
2529
2557
|
BlockCanvas,
|
|
2558
|
+
FilePickerButton,
|
|
2530
2559
|
QuoinProvider,
|
|
2531
2560
|
TEXT_COMMANDS,
|
|
2561
|
+
UploadField,
|
|
2532
2562
|
applyEdit,
|
|
2533
2563
|
blockByType,
|
|
2534
2564
|
caretRect,
|