@panphora/bevel 0.2.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.
|
@@ -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", "src/lib/ephemeral.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",
|
|
@@ -1786,13 +1786,16 @@ var RichClayBundle = (() => {
|
|
|
1786
1786
|
return clone.innerHTML;
|
|
1787
1787
|
}
|
|
1788
1788
|
var instances = /* @__PURE__ */ new WeakMap();
|
|
1789
|
-
var autoInitWindows = /* @__PURE__ */ new
|
|
1790
|
-
var watchedWindows = /* @__PURE__ */ new
|
|
1789
|
+
var autoInitWindows = /* @__PURE__ */ new WeakMap();
|
|
1790
|
+
var watchedWindows = /* @__PURE__ */ new WeakMap();
|
|
1791
1791
|
var globalRegistry = createDefaultRegistry();
|
|
1792
1792
|
var KEEP_FOCUS = Symbol("richclay-keep-focus");
|
|
1793
1793
|
var ROOT_TRANSFER_EVENTS = ["cut", "paste", "drop"];
|
|
1794
1794
|
var MODIFIER_ORDER = ["Alt", "Ctrl", "Meta", "Shift"];
|
|
1795
1795
|
var dialogSeq = 0;
|
|
1796
|
+
function isManagedByClayJS(win) {
|
|
1797
|
+
return typeof win.clay?.mode === "string";
|
|
1798
|
+
}
|
|
1796
1799
|
var defaultOptions = {
|
|
1797
1800
|
toolbar: "standard",
|
|
1798
1801
|
toolbarContainer: null,
|
|
@@ -1886,23 +1889,36 @@ var RichClayBundle = (() => {
|
|
|
1886
1889
|
return elements.map((element) => new _RichClay(element, options));
|
|
1887
1890
|
}
|
|
1888
1891
|
static autoInit(win = typeof window !== "undefined" ? window : void 0) {
|
|
1889
|
-
if (!win || !win.document
|
|
1890
|
-
|
|
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;
|
|
1891
1898
|
const run = () => {
|
|
1899
|
+
if (cancelled || isManagedByClayJS(win)) return;
|
|
1892
1900
|
if (shouldUseHyperclay({}, win) && isHyperclayEditMode(win)) {
|
|
1893
1901
|
_RichClay.init();
|
|
1894
|
-
_RichClay.watch(win);
|
|
1902
|
+
stopWatching = _RichClay.watch(win);
|
|
1895
1903
|
}
|
|
1896
1904
|
};
|
|
1905
|
+
const stop = () => {
|
|
1906
|
+
cancelled = true;
|
|
1907
|
+
stopWatching?.();
|
|
1908
|
+
};
|
|
1909
|
+
autoInitWindows.set(win, stop);
|
|
1897
1910
|
if (win.document.readyState === "loading") {
|
|
1898
1911
|
win.document.addEventListener("DOMContentLoaded", run, { once: true });
|
|
1899
1912
|
} else {
|
|
1900
1913
|
run();
|
|
1901
1914
|
}
|
|
1915
|
+
return stop;
|
|
1902
1916
|
}
|
|
1903
1917
|
static watch(win = typeof window !== "undefined" ? window : void 0, options = {}) {
|
|
1904
|
-
if (!win || !win.document
|
|
1905
|
-
|
|
1918
|
+
if (!win || !win.document) return () => {
|
|
1919
|
+
};
|
|
1920
|
+
const existing = watchedWindows.get(win);
|
|
1921
|
+
if (existing) return existing;
|
|
1906
1922
|
const mount = (element) => {
|
|
1907
1923
|
if (!isMountable(element)) return;
|
|
1908
1924
|
if (!instances.has(element) && !conflictsWithExistingEditor(element)) new _RichClay(element, options);
|
|
@@ -1931,12 +1947,21 @@ var RichClayBundle = (() => {
|
|
|
1931
1947
|
});
|
|
1932
1948
|
});
|
|
1933
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
|
+
};
|
|
1934
1957
|
observer.observe(win.document.documentElement || win.document, {
|
|
1935
1958
|
childList: true,
|
|
1936
1959
|
subtree: true,
|
|
1937
1960
|
attributes: true,
|
|
1938
1961
|
attributeFilter: ["editable", "clay-editable", "data-richclay", "richclay"]
|
|
1939
1962
|
});
|
|
1963
|
+
watchedWindows.set(win, stop);
|
|
1964
|
+
return stop;
|
|
1940
1965
|
}
|
|
1941
1966
|
static registerButton(def) {
|
|
1942
1967
|
validateButton(def);
|