diff --git a/ivette/src/dome/renderer/text/richtext.tsx b/ivette/src/dome/renderer/text/richtext.tsx index ecea1ee55b44f991b4588ab5d2f004ca99cd2a50..057c71993768491f35e04e2b1fbc4734c872c37a 100644 --- a/ivette/src/dome/renderer/text/richtext.tsx +++ b/ivette/src/dome/renderer/text/richtext.tsx @@ -38,14 +38,54 @@ export function byDepth(a : Range, b : Range): number return (a.length - b.length) || (b.offset - a.offset); } +/* -------------------------------------------------------------------------- */ +/* --- Code Mirror Utils --- */ +/* -------------------------------------------------------------------------- */ + +type View = CM.EditorView | null; + +class Field<A> { + readonly extension : CS.Extension; + readonly field : CS.StateField<A>; + private readonly annot : CS.AnnotationType<A>; + + constructor(init: A) { + const annot = CS.Annotation.define<A>(); + const field = CS.StateField.define<A>({ + create: () => init, + update: (fd: A, tr: CS.Transaction) => tr.annotation(annot) ?? fd, + }); + this.annot = annot; + this.field = field; + this.extension = [ field ]; + } + + get(view: View) : A | undefined { + return view?.state.field(this.field); + } + + set(view: View, value?: A): void { + if (view && value !== undefined) { + view.dispatch({ annotations: this.annot.of(value) }); + } + } + +} + +/* -------------------------------------------------------------------------- */ +/* --- Read Only --- */ +/* -------------------------------------------------------------------------- */ + +const ReadOnly = new Field(false); + /* -------------------------------------------------------------------------- */ /* --- Editor View --- */ /* -------------------------------------------------------------------------- */ function createView(parent: Element): CM.EditorView { const extensions : CS.Extension[] = [ - //CS.EditorState.readOnly.of(false), - ] + ReadOnly, CS.EditorState.readOnly.from(ReadOnly.field) + ]; const state = CS.EditorState.create({ extensions }); return new CM.EditorView({ state, parent }); } @@ -55,29 +95,33 @@ function createView(parent: Element): CM.EditorView { /* -------------------------------------------------------------------------- */ export interface RichTextProps { + readOnly?: boolean; className?: string; style?: CSSProperties; } export function RichText(props: RichTextProps) : JSX.Element { - type View = CM.EditorView | null; - const parent = React.useRef(null); - const editor = React.useRef<View>(null); + const [view, setView] = React.useState<View>(null); + + // ---- Updates + const { readOnly } = props; + React.useEffect(() => ReadOnly.set(view, readOnly), [view, readOnly]); + + // ---- Mount & Unmount Editor + const [nodeRef, setRef] = React.useState<Element | null>(null); React.useEffect(() => { - const div = parent.current; - if (!div) return; - const view = createView(div); - editor.current = view; - return () => { - editor.current = null; - view.destroy(); - }; - }, []); + if (!nodeRef) return; + const view = createView(nodeRef); + setView(view); + return () => { setView(null); view.destroy(); }; + }, [nodeRef]); + + // ---- Editor DIV const className = classes( 'cm-global-box', props.className ); - return <div className={className} style={props.style} ref={parent} />; + return <div className={className} style={props.style} ref={setRef} />; } /* -------------------------------------------------------------------------- */ diff --git a/ivette/src/sandbox/text.tsx b/ivette/src/sandbox/text.tsx index 95453ba7f21ef64437ac716d85d1e0a6ac5e12b0..521cdabb2121f2ded66e40fe48314b6463b5ba6a 100644 --- a/ivette/src/sandbox/text.tsx +++ b/ivette/src/sandbox/text.tsx @@ -26,6 +26,8 @@ /* -------------------------------------------------------------------------- */ import React from 'react'; +import { Hbox } from 'dome/layout/boxes'; +import { Checkbox } from 'dome/controls/buttons'; import { RichText } from 'dome/text/richtext'; import { registerSandbox } from 'ivette'; @@ -34,7 +36,15 @@ import { registerSandbox } from 'ivette'; /* -------------------------------------------------------------------------- */ function UseText(): JSX.Element { - return <RichText />; + const [readOnly, setReadOnly] = React.useState(false); + return ( + <> + <Hbox> + <Checkbox label="Read Only" value={readOnly} onChange={setReadOnly} /> + </Hbox> + <RichText readOnly={readOnly}/> + </> + ); } /* -------------------------------------------------------------------------- */