@panphora/bevel 0.2.0 → 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.
|
@@ -6,12 +6,15 @@ import { type ChangeDetails } from '../internal/controllable-state.js';
|
|
|
6
6
|
import { Button } from './button.js';
|
|
7
7
|
export declare const wrapCompartment: Compartment;
|
|
8
8
|
export declare const darkCompartment: Compartment;
|
|
9
|
+
export declare const readOnlyCompartment: Compartment;
|
|
9
10
|
/** iOS zooms the page when a focused editable is under 16px, contenteditable included. */
|
|
10
11
|
export declare function coarsePointer(): boolean;
|
|
11
12
|
/** Wrapping is remembered per device. A blocked or absent store falls back to the pointer. */
|
|
12
13
|
export declare function storedWrap(coarse: boolean): boolean;
|
|
13
14
|
export declare function rememberWrap(on: boolean): boolean;
|
|
14
15
|
export declare const wrapExtension: (on: boolean) => Extension;
|
|
16
|
+
/** Read-only still takes focus, so the keyboard can move, select and search; only edits are refused. */
|
|
17
|
+
export declare const readOnlyExtension: (on: boolean) => Extension;
|
|
15
18
|
export declare const bevelHighlight: HighlightStyle;
|
|
16
19
|
/** One theme serves both modes: the tokens are light-dark() and the dark flag is a facet. */
|
|
17
20
|
export declare function bevelEditorTheme(coarse: boolean): Extension;
|
|
@@ -22,8 +25,10 @@ export interface CodeEditorExtensionOptions {
|
|
|
22
25
|
onSave: () => void;
|
|
23
26
|
onDocChange: () => void;
|
|
24
27
|
onFindChange: (open: boolean) => void;
|
|
28
|
+
readOnly: boolean;
|
|
29
|
+
onFind: () => void;
|
|
25
30
|
}
|
|
26
|
-
export declare function codeEditorExtensions({ coarse, wrapped, dark, onSave, onDocChange, onFindChange }: CodeEditorExtensionOptions): Extension[];
|
|
31
|
+
export declare function codeEditorExtensions({ coarse, wrapped, dark, onSave, onDocChange, onFindChange, readOnly, onFind }: CodeEditorExtensionOptions): Extension[];
|
|
27
32
|
export type CodeEditorAction = 'undo' | 'redo' | 'find' | 'outdent' | 'indent' | 'wrap' | 'fold' | 'suggest';
|
|
28
33
|
interface CodeEditorState {
|
|
29
34
|
view: EditorView | null;
|
|
@@ -36,6 +41,8 @@ interface CodeEditorState {
|
|
|
36
41
|
/** The host clears the flag through this once its own write has landed. `save()` never clears it. */
|
|
37
42
|
setDirty: (next: boolean | ((previous: boolean) => boolean), details: ChangeDetails) => void;
|
|
38
43
|
findOpen: boolean;
|
|
44
|
+
findRequest: number;
|
|
45
|
+
readOnly: boolean;
|
|
39
46
|
extensions: Extension[];
|
|
40
47
|
root: RefObject<HTMLDivElement | null>;
|
|
41
48
|
save: () => void;
|
|
@@ -52,11 +59,13 @@ export interface CodeEditorProps extends Omit<ComponentProps<'div'>, 'onChange'>
|
|
|
52
59
|
wrap?: boolean;
|
|
53
60
|
defaultWrap?: boolean;
|
|
54
61
|
onWrapChange?: (wrap: boolean, details: ChangeDetails) => void;
|
|
62
|
+
/** Keeps the editor focusable and selectable, and refuses edits. */
|
|
63
|
+
readOnly?: boolean;
|
|
55
64
|
/** The mounted view, and null when it is torn down. The tab drives live sync through it. */
|
|
56
65
|
onViewChange?: (view: EditorView | null) => void;
|
|
57
66
|
extensions?: Extension[];
|
|
58
67
|
}
|
|
59
|
-
declare function Root({ doc, onSave, dirty, defaultDirty, onDirtyChange, wrap, defaultWrap, onWrapChange, onViewChange, extensions, ref, children, ...props }: CodeEditorProps): import("react").JSX.Element;
|
|
68
|
+
declare function Root({ doc, onSave, dirty, defaultDirty, onDirtyChange, wrap, defaultWrap, onWrapChange, readOnly, onViewChange, extensions, ref, onKeyDown, children, ...props }: CodeEditorProps): import("react").JSX.Element;
|
|
60
69
|
declare function Dirty({ children, hidden, ...props }: ComponentProps<'i'>): import("react").JSX.Element;
|
|
61
70
|
export interface CodeEditorToolProps extends ComponentProps<'button'> {
|
|
62
71
|
action?: CodeEditorAction;
|
|
@@ -64,6 +64,7 @@ const WRAP_STORAGE_KEY = "bevel.code-editor.wrap";
|
|
|
64
64
|
const FONT = "var(--bevel-font-mono)";
|
|
65
65
|
const wrapCompartment = new Compartment();
|
|
66
66
|
const darkCompartment = new Compartment();
|
|
67
|
+
const readOnlyCompartment = new Compartment();
|
|
67
68
|
function coarsePointer() {
|
|
68
69
|
if (typeof window === "undefined") return false;
|
|
69
70
|
return window.matchMedia("(pointer: coarse)").matches || navigator.maxTouchPoints > 0;
|
|
@@ -122,6 +123,7 @@ const hangingIndent = ViewPlugin.fromClass(class {
|
|
|
122
123
|
}
|
|
123
124
|
}, { decorations: (plugin) => plugin.decorations });
|
|
124
125
|
const wrapExtension = (on) => on ? [EditorView.lineWrapping, hangingIndent] : [];
|
|
126
|
+
const readOnlyExtension = (on) => on ? [EditorState.readOnly.of(true), EditorView.contentAttributes.of({ inputmode: "none" })] : [];
|
|
125
127
|
const bevelHighlight = HighlightStyle.define([
|
|
126
128
|
{ tag: [t.comment, t.lineComment, t.blockComment, t.docComment], color: "var(--bevel-syn-comment)", fontStyle: "italic" },
|
|
127
129
|
{ tag: [t.tagName, t.standard(t.tagName)], color: "var(--bevel-syn-tag)", fontWeight: "500" },
|
|
@@ -382,7 +384,7 @@ function findPanel(coarse) {
|
|
|
382
384
|
};
|
|
383
385
|
};
|
|
384
386
|
}
|
|
385
|
-
function codeEditorExtensions({ coarse, wrapped, dark, onSave, onDocChange, onFindChange }) {
|
|
387
|
+
function codeEditorExtensions({ coarse, wrapped, dark, onSave, onDocChange, onFindChange, readOnly, onFind }) {
|
|
386
388
|
return [
|
|
387
389
|
lineNumbers(),
|
|
388
390
|
highlightActiveLineGutter(),
|
|
@@ -415,6 +417,7 @@ function codeEditorExtensions({ coarse, wrapped, dark, onSave, onDocChange, onFi
|
|
|
415
417
|
...coarse ? [] : [rectangularSelection(), crosshairCursor()],
|
|
416
418
|
wrapCompartment.of(wrapExtension(wrapped)),
|
|
417
419
|
darkCompartment.of(EditorView.darkTheme.of(dark)),
|
|
420
|
+
readOnlyCompartment.of(readOnlyExtension(readOnly)),
|
|
418
421
|
bevelEditorTheme(coarse),
|
|
419
422
|
EditorView.contentAttributes.of({
|
|
420
423
|
autocorrect: "off",
|
|
@@ -424,6 +427,7 @@ function codeEditorExtensions({ coarse, wrapped, dark, onSave, onDocChange, onFi
|
|
|
424
427
|
"aria-label": "Source"
|
|
425
428
|
}),
|
|
426
429
|
Prec.highest(keymap.of([
|
|
430
|
+
{ key: "Mod-f", preventDefault: true, run: () => (onFind(), true) },
|
|
427
431
|
{ key: "Mod-s", preventDefault: true, run: () => (onSave(), true) },
|
|
428
432
|
{ key: "Mod-Shift-s", preventDefault: true, run: () => (onSave(), true) },
|
|
429
433
|
{ key: "Mod-]", preventDefault: true, run: indentMore },
|
|
@@ -502,7 +506,7 @@ const StatusPart = nativePart("div", "bevel-r-code-editor__status");
|
|
|
502
506
|
const MetaPart = nativePart("span", "bevel-r-code-editor__meta");
|
|
503
507
|
const BarPart = nativePart("div", "bevel-r-code-editor__bar");
|
|
504
508
|
const BarToolsPart = nativePart("div", "bevel-r-code-editor__bar-tools");
|
|
505
|
-
function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, defaultWrap, onWrapChange, onViewChange, extensions, ref, children, ...props }) {
|
|
509
|
+
function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, defaultWrap, onWrapChange, readOnly = false, onViewChange, extensions, ref, onKeyDown, children, ...props }) {
|
|
506
510
|
const [view, setView] = useState(null);
|
|
507
511
|
const viewRef = useRef(null);
|
|
508
512
|
const root = useRef(null);
|
|
@@ -512,6 +516,7 @@ function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, d
|
|
|
512
516
|
const [isDirty, setDirty] = useControllableState({ value: dirty, defaultValue: defaultDirty, onChange: onDirtyChange });
|
|
513
517
|
const [wrapped, setWrap] = useControllableState({ value: wrap, defaultValue: initialWrap, onChange: onWrapChange });
|
|
514
518
|
const [findOpen, setFindOpen] = useState(false);
|
|
519
|
+
const [findRequest, setFindRequest] = useState(0);
|
|
515
520
|
const { mode } = useBevel();
|
|
516
521
|
const dark = useResolvedDark(mode);
|
|
517
522
|
const latest = useRef({ onSave, onViewChange, wrapped });
|
|
@@ -527,6 +532,12 @@ function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, d
|
|
|
527
532
|
setView(next);
|
|
528
533
|
latest.current.onViewChange?.(next);
|
|
529
534
|
}, []);
|
|
535
|
+
const requestFind = useCallback(() => {
|
|
536
|
+
const current = viewRef.current;
|
|
537
|
+
if (!current) return;
|
|
538
|
+
openSearchPanel(current);
|
|
539
|
+
setFindRequest((count) => count + 1);
|
|
540
|
+
}, []);
|
|
530
541
|
const run = useCallback((action) => {
|
|
531
542
|
const current = viewRef.current;
|
|
532
543
|
if (!current) return;
|
|
@@ -540,20 +551,22 @@ function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, d
|
|
|
540
551
|
if (searchPanelOpen(current.state)) {
|
|
541
552
|
closeSearchPanel(current);
|
|
542
553
|
current.focus();
|
|
543
|
-
} else
|
|
554
|
+
} else requestFind();
|
|
544
555
|
return;
|
|
545
556
|
} else if (action === "wrap") {
|
|
546
557
|
setWrap(!latest.current.wrapped, { reason: "toolbar" });
|
|
547
558
|
}
|
|
548
559
|
current.focus();
|
|
549
|
-
}, [setWrap]);
|
|
560
|
+
}, [setWrap, requestFind]);
|
|
550
561
|
const mounted = useMemo(() => codeEditorExtensions({
|
|
551
562
|
coarse,
|
|
552
563
|
wrapped: initialWrap,
|
|
553
564
|
dark,
|
|
565
|
+
readOnly,
|
|
554
566
|
onSave: save,
|
|
555
567
|
onDocChange: () => setDirty(true, { reason: "edit" }),
|
|
556
|
-
onFindChange: setFindOpen
|
|
568
|
+
onFindChange: setFindOpen,
|
|
569
|
+
onFind: requestFind
|
|
557
570
|
}), []);
|
|
558
571
|
useEffect(() => {
|
|
559
572
|
view?.dispatch({ effects: wrapCompartment.reconfigure(wrapExtension(wrapped)) });
|
|
@@ -561,6 +574,9 @@ function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, d
|
|
|
561
574
|
useEffect(() => {
|
|
562
575
|
view?.dispatch({ effects: darkCompartment.reconfigure(EditorView.darkTheme.of(dark)) });
|
|
563
576
|
}, [view, dark]);
|
|
577
|
+
useEffect(() => {
|
|
578
|
+
view?.dispatch({ effects: readOnlyCompartment.reconfigure(readOnlyExtension(readOnly)) });
|
|
579
|
+
}, [view, readOnly]);
|
|
564
580
|
const settled = useRef(false);
|
|
565
581
|
useEffect(() => {
|
|
566
582
|
if (!settled.current) {
|
|
@@ -579,12 +595,20 @@ function Root({ doc, onSave, dirty, defaultDirty = false, onDirtyChange, wrap, d
|
|
|
579
595
|
dirty: isDirty,
|
|
580
596
|
setDirty,
|
|
581
597
|
findOpen,
|
|
598
|
+
findRequest,
|
|
599
|
+
readOnly,
|
|
582
600
|
extensions: extensions ? [...mounted, ...extensions] : mounted,
|
|
583
601
|
root,
|
|
584
602
|
save,
|
|
585
603
|
run
|
|
586
|
-
}), [view, register, doc, coarse, dark, wrapped, isDirty, setDirty, findOpen, extensions, mounted, save, run]);
|
|
587
|
-
return /* @__PURE__ */ jsx(CodeEditorContext, { value: context, children: /* @__PURE__ */ jsx(RootPart, { ...props, ref: merged,
|
|
604
|
+
}), [view, register, doc, coarse, dark, wrapped, isDirty, setDirty, findOpen, findRequest, readOnly, extensions, mounted, save, run]);
|
|
605
|
+
return /* @__PURE__ */ jsx(CodeEditorContext, { value: context, children: /* @__PURE__ */ jsx(RootPart, { ...props, ref: merged, onKeyDown: (event) => {
|
|
606
|
+
onKeyDown?.(event);
|
|
607
|
+
if (event.defaultPrevented || event.altKey || event.shiftKey || !(event.metaKey || event.ctrlKey)) return;
|
|
608
|
+
if (event.key.toLowerCase() !== "f") return;
|
|
609
|
+
event.preventDefault();
|
|
610
|
+
requestFind();
|
|
611
|
+
}, children }) });
|
|
588
612
|
}
|
|
589
613
|
function Dirty({ children, hidden, ...props }) {
|
|
590
614
|
const { dirty } = useCodeEditor();
|
|
@@ -687,6 +711,8 @@ export {
|
|
|
687
711
|
coarsePointer,
|
|
688
712
|
codeEditorExtensions,
|
|
689
713
|
darkCompartment,
|
|
714
|
+
readOnlyCompartment,
|
|
715
|
+
readOnlyExtension,
|
|
690
716
|
rememberWrap,
|
|
691
717
|
storedWrap,
|
|
692
718
|
useCodeEditor,
|
package/examples/setup-peers.mjs
CHANGED
|
@@ -7,7 +7,7 @@ const distributions = {
|
|
|
7
7
|
richclay: ["richclay.js", "richclay.css", "LICENSE", "THIRD-PARTY-NOTICES.md", "vendor/squire.js", "vendor/squire.provenance.json", "vendor/SQUIRE-LICENSE", "vendor/purify.min.js", "vendor/DOMPURIFY-LICENSE", "vendor/DOMPURIFY-LICENSE-MPL"],
|
|
8
8
|
quickcrop: ["quickcrop.js", "quickcrop.css", "LICENSE"],
|
|
9
9
|
hypercms: ["dist/hypercms.min.js", "LICENSE"],
|
|
10
|
-
"clay-mutation": ["src/lib/mutation.js", "src/lib/extension-noise.js", "src/lib/region-policy.js", "src/lib/region-capabilities.js", "src/lib/root-attrs.js", "src/lib/user-gesture.js", "LICENSE"]
|
|
10
|
+
"clay-mutation": ["src/lib/mutation.js", "src/lib/extension-noise.js", "src/lib/region-policy.js", "src/lib/region-capabilities.js", "src/lib/root-attrs.js", "src/lib/user-gesture.js", "src/lib/ephemeral.js", "src/lib/derived.js", "src/vendor/sap-ownership.vendor.js", "src/core/boot-phase.js", "LICENSE"]
|
|
11
11
|
};
|
|
12
12
|
const packageNames = { hypercms: "@panphora/hyper-cms", "clay-mutation": "@panphora/clayjs" };
|
|
13
13
|
const [name, source] = process.argv.slice(2);
|
package/package.json
CHANGED
package/react/README.md
CHANGED
|
@@ -225,7 +225,7 @@ Each row links to a real example compiled against the installed package. Each co
|
|
|
225
225
|
| persistent-action-banner | `@panphora/bevel/react/persistent-action-banner` | ActionBanner (Root, Dismiss, Body, Icon, Actions, Observation, Meta, Head, Count, Progress, Status) | [Example](../tests/react/consumer/catalog-cases.tsx#L105) |
|
|
226
226
|
| save-status-indicator | `@panphora/bevel/react/save-status-indicator` | SaveStatus; SavedNotification | [Example](../tests/react/consumer/catalog-cases.tsx#L106) |
|
|
227
227
|
| code-block | `@panphora/bevel/react/code-block` | CodeBlock (Root, Code, Copy, Wrap, Feedback, Header, Name, Language, Meta) | [Example](../tests/react/consumer/catalog-cases.tsx#L108) |
|
|
228
|
-
| code-editor | `@panphora/bevel/react/code-editor` | CodeEditor (Root, Tab, Name, Host, Dirty, Spacer, Toolbar, Tool, Save, Surface, Status, Meta, KeyboardBar, BarTools). CodeMirror 6 and its grammars are optional peers, so this entry is not re-exported from `@panphora/bevel/react` and a consumer that never opens it pays nothing. Give the root a definite height. | [Example](../tests/react/consumer/catalog-cases.tsx#L107) |
|
|
228
|
+
| code-editor | `@panphora/bevel/react/code-editor` | CodeEditor (Root, Tab, Name, Host, Dirty, Spacer, Toolbar, Tool, Save, Surface, Status, Meta, KeyboardBar, BarTools). CodeMirror 6 and its grammars are optional peers, so this entry is not re-exported from `@panphora/bevel/react` and a consumer that never opens it pays nothing. Give the root a definite height. `readOnly` keeps the editor focusable and selectable; `findRequest` in `useCodeEditor()` increases on every find request, for hosts that draw their own find field. | [Example](../tests/react/consumer/catalog-cases.tsx#L107) |
|
|
229
229
|
| diff-view | `@panphora/bevel/react/diff-view` | DiffView (Root, Body, Line, Count, Layout, Hunk, Header, Controls, Footer) | [Example](../tests/react/consumer/catalog-cases.tsx#L109) |
|
|
230
230
|
| activity-feed | `@panphora/bevel/react/activity-feed` | ActivityFeed (Root, Groups, More, Day, List, Item, Head, Detail, Avatar) | [Example](../tests/react/consumer/catalog-cases.tsx#L110) |
|
|
231
231
|
| stat-tiles | `@panphora/bevel/react/stat-tiles` | StatTile (Root, Label, Value, Delta, Spark, Description); StatTiles | [Example](../tests/react/consumer/catalog-cases.tsx#L111) |
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "richclay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"source": "Explicitly supplied local distribution; hashes identify the exact tested bytes, not a published release claim.",
|
|
5
5
|
"files": {
|
|
6
|
-
"richclay.js": "
|
|
6
|
+
"richclay.js": "5434545ce62960fc27611edae1ef808f29dfe31f4e8bd8c8553c4eb118d83c0c",
|
|
7
7
|
"richclay.css": "fc44ab9e71f8b66950fbda5a535774c7acb97d7e54aff2521ea4122d21422d47",
|
|
8
8
|
"LICENSE": "5312a8ef6ff9a844697b8b112612467efd12957e8afba098ce600dec0f8396c4",
|
|
9
9
|
"THIRD-PARTY-NOTICES.md": "3659529614b62297b8336ac7f6189ef9f67425911265bff1cde8d136df21562b",
|
|
@@ -1568,7 +1568,8 @@ var RichClayBundle = (() => {
|
|
|
1568
1568
|
"no-undo",
|
|
1569
1569
|
"no-data",
|
|
1570
1570
|
"freeze",
|
|
1571
|
-
"editor-ui"
|
|
1571
|
+
"editor-ui",
|
|
1572
|
+
"live"
|
|
1572
1573
|
]);
|
|
1573
1574
|
function hasPolicyToken(element, token) {
|
|
1574
1575
|
if (!element || element.nodeType !== 1) return false;
|
|
@@ -1609,9 +1610,6 @@ var RichClayBundle = (() => {
|
|
|
1609
1610
|
if ("value" in source && "value" in copy && source.tagName !== "OPTION" && type !== "checkbox" && type !== "radio" && type !== "file") copy.value = source.value;
|
|
1610
1611
|
if (source.tagName === "INPUT" && (type === "checkbox" || type === "radio")) copy.checked = source.checked;
|
|
1611
1612
|
if (source.tagName === "OPTION") copy.selected = source.selected;
|
|
1612
|
-
if (source.tagName === "SELECT") {
|
|
1613
|
-
for (let i = 0; i < source.options.length; i++) copy.options[i].selected = source.options[i].selected;
|
|
1614
|
-
}
|
|
1615
1613
|
if ("indeterminate" in source && "indeterminate" in copy) copy.indeterminate = source.indeterminate;
|
|
1616
1614
|
}
|
|
1617
1615
|
function matchesWithin(node, selector, boundary, inherit) {
|
|
@@ -1624,40 +1622,60 @@ var RichClayBundle = (() => {
|
|
|
1624
1622
|
}
|
|
1625
1623
|
return false;
|
|
1626
1624
|
}
|
|
1627
|
-
function
|
|
1628
|
-
|
|
1629
|
-
|
|
1625
|
+
function excluded(source, capability, capabilityMatch, exclude, boundary, inherit) {
|
|
1626
|
+
return (inherit ? hasCapability(source, capability) : matchesWithin(source, capabilityMatch, boundary, false)) || !!(exclude && (inherit && source !== boundary ? source.matches(exclude) : matchesWithin(source, exclude, boundary, inherit)));
|
|
1627
|
+
}
|
|
1628
|
+
function hasOwnCapability(element, capability) {
|
|
1629
|
+
const definition = CAPABILITIES[capability];
|
|
1630
|
+
return definition.tokens.some((token) => expandsTo(element, token)) || definition.bundles.some((bundle) => hasPolicyToken(element, bundle));
|
|
1631
|
+
}
|
|
1632
|
+
var CONTROL_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT", "OPTION"]);
|
|
1633
|
+
function pairTree(source, copy, maps, drop) {
|
|
1634
|
+
if (source.nodeType === 1 && drop(source)) {
|
|
1635
|
+
maps.dropped.push(copy);
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1630
1638
|
maps.cloneToLive.set(copy, source);
|
|
1631
1639
|
maps.liveToClone.set(source, copy);
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
if (
|
|
1635
|
-
maps.
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1640
|
+
let sourceChildren = source;
|
|
1641
|
+
let copyChildren = copy;
|
|
1642
|
+
if (source.nodeType === 1) {
|
|
1643
|
+
if (CONTROL_TAGS.has(source.tagName)) maps.controls.push(source, copy);
|
|
1644
|
+
if (source.tagName === "TEMPLATE") {
|
|
1645
|
+
while (copy.firstChild) copy.removeChild(copy.firstChild);
|
|
1646
|
+
sourceChildren = source.content;
|
|
1647
|
+
copyChildren = copy.content;
|
|
1648
|
+
maps.cloneToLive.set(copyChildren, sourceChildren);
|
|
1649
|
+
maps.liveToClone.set(sourceChildren, copyChildren);
|
|
1650
|
+
}
|
|
1651
|
+
} else if (source.nodeType !== 11) return;
|
|
1652
|
+
let c = copyChildren.firstChild;
|
|
1653
|
+
for (let s = sourceChildren.firstChild; s && c; s = s.nextSibling) {
|
|
1654
|
+
pairTree(s, c, maps, drop);
|
|
1655
|
+
c = c.nextSibling;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
function createContentView(context, { capability = "data", exclude = null, inherit = true, provenance = null } = {}) {
|
|
1649
1659
|
if (!context) throw new TypeError("createContentView requires a DOM context");
|
|
1650
1660
|
const sourceDocument = context.nodeType === 9 ? context : context.ownerDocument;
|
|
1651
1661
|
if (!sourceDocument?.implementation?.createHTMLDocument) {
|
|
1652
1662
|
throw new TypeError("createContentView requires an HTML DOM implementation");
|
|
1653
1663
|
}
|
|
1654
1664
|
const inertDocument = sourceDocument.implementation.createHTMLDocument("");
|
|
1655
|
-
const cloneToLive = /* @__PURE__ */ new WeakMap();
|
|
1665
|
+
const cloneToLive = provenance || /* @__PURE__ */ new WeakMap();
|
|
1656
1666
|
const liveToClone = /* @__PURE__ */ new WeakMap();
|
|
1657
1667
|
const sourceRoot = context.nodeType === 9 ? context.documentElement : context;
|
|
1658
1668
|
if (exclude) inertDocument.documentElement.matches(exclude);
|
|
1659
1669
|
const capabilityMatch = capabilitySelector(capability);
|
|
1660
|
-
const
|
|
1670
|
+
const drop = inherit ? (el) => hasOwnCapability(el, capability) || !!(exclude && el.matches(exclude)) : (el) => excluded(el, capability, capabilityMatch, exclude, sourceRoot, false);
|
|
1671
|
+
let root = null;
|
|
1672
|
+
if (sourceRoot.nodeType !== 1 || !excluded(sourceRoot, capability, capabilityMatch, exclude, sourceRoot, inherit)) {
|
|
1673
|
+
root = inertDocument.importNode(sourceRoot, true);
|
|
1674
|
+
const maps = { cloneToLive, liveToClone, dropped: [], controls: [] };
|
|
1675
|
+
pairTree(sourceRoot, root, maps, drop);
|
|
1676
|
+
for (const node of maps.dropped) node.remove();
|
|
1677
|
+
for (let i = 0; i < maps.controls.length; i += 2) copyControlState(maps.controls[i], maps.controls[i + 1]);
|
|
1678
|
+
}
|
|
1661
1679
|
if (context.nodeType === 9 && root) {
|
|
1662
1680
|
inertDocument.replaceChild(root, inertDocument.documentElement);
|
|
1663
1681
|
liveToClone.set(context, inertDocument);
|
|
@@ -1768,13 +1786,16 @@ var RichClayBundle = (() => {
|
|
|
1768
1786
|
return clone.innerHTML;
|
|
1769
1787
|
}
|
|
1770
1788
|
var instances = /* @__PURE__ */ new WeakMap();
|
|
1771
|
-
var autoInitWindows = /* @__PURE__ */ new
|
|
1772
|
-
var watchedWindows = /* @__PURE__ */ new
|
|
1789
|
+
var autoInitWindows = /* @__PURE__ */ new WeakMap();
|
|
1790
|
+
var watchedWindows = /* @__PURE__ */ new WeakMap();
|
|
1773
1791
|
var globalRegistry = createDefaultRegistry();
|
|
1774
1792
|
var KEEP_FOCUS = Symbol("richclay-keep-focus");
|
|
1775
1793
|
var ROOT_TRANSFER_EVENTS = ["cut", "paste", "drop"];
|
|
1776
1794
|
var MODIFIER_ORDER = ["Alt", "Ctrl", "Meta", "Shift"];
|
|
1777
1795
|
var dialogSeq = 0;
|
|
1796
|
+
function isManagedByClayJS(win) {
|
|
1797
|
+
return typeof win.clay?.mode === "string";
|
|
1798
|
+
}
|
|
1778
1799
|
var defaultOptions = {
|
|
1779
1800
|
toolbar: "standard",
|
|
1780
1801
|
toolbarContainer: null,
|
|
@@ -1868,23 +1889,36 @@ var RichClayBundle = (() => {
|
|
|
1868
1889
|
return elements.map((element) => new _RichClay(element, options));
|
|
1869
1890
|
}
|
|
1870
1891
|
static autoInit(win = typeof window !== "undefined" ? window : void 0) {
|
|
1871
|
-
if (!win || !win.document
|
|
1872
|
-
|
|
1892
|
+
if (!win || !win.document) return () => {
|
|
1893
|
+
};
|
|
1894
|
+
const existing = autoInitWindows.get(win);
|
|
1895
|
+
if (existing) return existing;
|
|
1896
|
+
let cancelled = false;
|
|
1897
|
+
let stopWatching = null;
|
|
1873
1898
|
const run = () => {
|
|
1899
|
+
if (cancelled || isManagedByClayJS(win)) return;
|
|
1874
1900
|
if (shouldUseHyperclay({}, win) && isHyperclayEditMode(win)) {
|
|
1875
1901
|
_RichClay.init();
|
|
1876
|
-
_RichClay.watch(win);
|
|
1902
|
+
stopWatching = _RichClay.watch(win);
|
|
1877
1903
|
}
|
|
1878
1904
|
};
|
|
1905
|
+
const stop = () => {
|
|
1906
|
+
cancelled = true;
|
|
1907
|
+
stopWatching?.();
|
|
1908
|
+
};
|
|
1909
|
+
autoInitWindows.set(win, stop);
|
|
1879
1910
|
if (win.document.readyState === "loading") {
|
|
1880
1911
|
win.document.addEventListener("DOMContentLoaded", run, { once: true });
|
|
1881
1912
|
} else {
|
|
1882
1913
|
run();
|
|
1883
1914
|
}
|
|
1915
|
+
return stop;
|
|
1884
1916
|
}
|
|
1885
1917
|
static watch(win = typeof window !== "undefined" ? window : void 0, options = {}) {
|
|
1886
|
-
if (!win || !win.document
|
|
1887
|
-
|
|
1918
|
+
if (!win || !win.document) return () => {
|
|
1919
|
+
};
|
|
1920
|
+
const existing = watchedWindows.get(win);
|
|
1921
|
+
if (existing) return existing;
|
|
1888
1922
|
const mount = (element) => {
|
|
1889
1923
|
if (!isMountable(element)) return;
|
|
1890
1924
|
if (!instances.has(element) && !conflictsWithExistingEditor(element)) new _RichClay(element, options);
|
|
@@ -1913,12 +1947,21 @@ var RichClayBundle = (() => {
|
|
|
1913
1947
|
});
|
|
1914
1948
|
});
|
|
1915
1949
|
});
|
|
1950
|
+
let stopped = false;
|
|
1951
|
+
const stop = () => {
|
|
1952
|
+
if (stopped) return;
|
|
1953
|
+
stopped = true;
|
|
1954
|
+
observer.disconnect();
|
|
1955
|
+
if (watchedWindows.get(win) === stop) watchedWindows.delete(win);
|
|
1956
|
+
};
|
|
1916
1957
|
observer.observe(win.document.documentElement || win.document, {
|
|
1917
1958
|
childList: true,
|
|
1918
1959
|
subtree: true,
|
|
1919
1960
|
attributes: true,
|
|
1920
1961
|
attributeFilter: ["editable", "clay-editable", "data-richclay", "richclay"]
|
|
1921
1962
|
});
|
|
1963
|
+
watchedWindows.set(win, stop);
|
|
1964
|
+
return stop;
|
|
1922
1965
|
}
|
|
1923
1966
|
static registerButton(def) {
|
|
1924
1967
|
validateButton(def);
|
|
@@ -2001,18 +2044,18 @@ var RichClayBundle = (() => {
|
|
|
2001
2044
|
editor._mutation?.takeRecords();
|
|
2002
2045
|
},
|
|
2003
2046
|
onMutations: (records, editor) => {
|
|
2004
|
-
const
|
|
2047
|
+
const excluded2 = (node, capability) => {
|
|
2005
2048
|
const boundary = closestWithCapability(node, capability);
|
|
2006
2049
|
return !!boundary && boundary !== this.element && this.element.contains(boundary);
|
|
2007
2050
|
};
|
|
2008
2051
|
let authored = false;
|
|
2009
2052
|
let local = false;
|
|
2010
2053
|
for (const record of records) {
|
|
2011
|
-
if (
|
|
2054
|
+
if (excluded2(record.target, "history")) continue;
|
|
2012
2055
|
const nodes = record.type === "childList" ? [...record.addedNodes, ...record.removedNodes] : [record.target];
|
|
2013
2056
|
for (const node of nodes) {
|
|
2014
|
-
if (
|
|
2015
|
-
if (
|
|
2057
|
+
if (excluded2(node, "history")) continue;
|
|
2058
|
+
if (excluded2(record.target, "undo") || excluded2(node, "undo")) local = true;
|
|
2016
2059
|
else authored = true;
|
|
2017
2060
|
}
|
|
2018
2061
|
}
|