@open-wysiwyg-editor/react 0.0.0-stage → 1.0.1

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.
@@ -0,0 +1,43 @@
1
+ import { ReactElement, RefObject } from 'react';
2
+ import { EditorOptions, Content, Editor } from 'open-wysiwyg-editor';
3
+ export * from 'open-wysiwyg-editor';
4
+
5
+ interface UseEditorResult {
6
+ /** Attach to the element the editor mounts in: `<div ref={ref} />`. */
7
+ ref: RefObject<HTMLDivElement | null>;
8
+ /** The live editor, or `null` before mount and during server rendering. */
9
+ editor: Editor | null;
10
+ }
11
+ /**
12
+ * Creates an editor in the element behind `ref` and destroys it on unmount.
13
+ * Callbacks and the runtime options (`editable`, `placeholder`, `aria*`, `dir`, `contentLang`)
14
+ * follow every render; everything else (`extensions`, `ui`, `language`…) is read once at mount.
15
+ * Give the component a new `key` to apply those.
16
+ */
17
+ declare function useEditor(options?: EditorOptions): UseEditorResult;
18
+ interface RichTextEditorProps extends Omit<EditorOptions, "element" | "content"> {
19
+ /** Controlled HTML. The editor loads it whenever it differs from what the editor last produced. */
20
+ value?: string;
21
+ /** Initial content (HTML or JSON) for an uncontrolled editor. */
22
+ defaultValue?: Content;
23
+ /** Called with the new HTML on every change. */
24
+ onChange?: (html: string, editor: Editor) => void;
25
+ /**
26
+ * Form field name. The HTML is kept in a hidden input, so `<form action={serverAction}>`
27
+ * (Next.js, React 19) and plain form posts receive it.
28
+ */
29
+ name?: string;
30
+ className?: string;
31
+ id?: string;
32
+ }
33
+ /**
34
+ * The full editor (toolbar, dialogs, status bar) as a component.
35
+ *
36
+ * ```tsx
37
+ * const [html, setHtml] = useState("<p>Hello</p>");
38
+ * <RichTextEditor value={html} onChange={setHtml} />
39
+ * ```
40
+ */
41
+ declare function RichTextEditor(props: RichTextEditorProps): ReactElement;
42
+
43
+ export { RichTextEditor, type RichTextEditorProps, type UseEditorResult, useEditor };
@@ -0,0 +1,43 @@
1
+ import { ReactElement, RefObject } from 'react';
2
+ import { EditorOptions, Content, Editor } from 'open-wysiwyg-editor';
3
+ export * from 'open-wysiwyg-editor';
4
+
5
+ interface UseEditorResult {
6
+ /** Attach to the element the editor mounts in: `<div ref={ref} />`. */
7
+ ref: RefObject<HTMLDivElement | null>;
8
+ /** The live editor, or `null` before mount and during server rendering. */
9
+ editor: Editor | null;
10
+ }
11
+ /**
12
+ * Creates an editor in the element behind `ref` and destroys it on unmount.
13
+ * Callbacks and the runtime options (`editable`, `placeholder`, `aria*`, `dir`, `contentLang`)
14
+ * follow every render; everything else (`extensions`, `ui`, `language`…) is read once at mount.
15
+ * Give the component a new `key` to apply those.
16
+ */
17
+ declare function useEditor(options?: EditorOptions): UseEditorResult;
18
+ interface RichTextEditorProps extends Omit<EditorOptions, "element" | "content"> {
19
+ /** Controlled HTML. The editor loads it whenever it differs from what the editor last produced. */
20
+ value?: string;
21
+ /** Initial content (HTML or JSON) for an uncontrolled editor. */
22
+ defaultValue?: Content;
23
+ /** Called with the new HTML on every change. */
24
+ onChange?: (html: string, editor: Editor) => void;
25
+ /**
26
+ * Form field name. The HTML is kept in a hidden input, so `<form action={serverAction}>`
27
+ * (Next.js, React 19) and plain form posts receive it.
28
+ */
29
+ name?: string;
30
+ className?: string;
31
+ id?: string;
32
+ }
33
+ /**
34
+ * The full editor (toolbar, dialogs, status bar) as a component.
35
+ *
36
+ * ```tsx
37
+ * const [html, setHtml] = useState("<p>Hello</p>");
38
+ * <RichTextEditor value={html} onChange={setHtml} />
39
+ * ```
40
+ */
41
+ declare function RichTextEditor(props: RichTextEditorProps): ReactElement;
42
+
43
+ export { RichTextEditor, type RichTextEditorProps, type UseEditorResult, useEditor };
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ "use client";
2
+
3
+ // src/index.ts
4
+ import { createElement, useEffect, useRef, useState } from "react";
5
+ import {
6
+ RUNTIME_OPTIONS,
7
+ createEditor,
8
+ forwardCallbacks,
9
+ pickRuntimeOptions,
10
+ syncContent
11
+ } from "open-wysiwyg-editor";
12
+ export * from "open-wysiwyg-editor";
13
+ function useEditor(options = {}) {
14
+ const ref = useRef(null);
15
+ const [editor, setEditor] = useState(null);
16
+ const latest = useRef(options);
17
+ useEffect(() => {
18
+ latest.current = options;
19
+ });
20
+ useEffect(() => {
21
+ const instance = createEditor({
22
+ ...latest.current,
23
+ element: ref.current,
24
+ ...forwardCallbacks(() => latest.current)
25
+ });
26
+ setEditor(instance);
27
+ return () => {
28
+ instance.destroy();
29
+ setEditor(null);
30
+ };
31
+ }, []);
32
+ const runtime = RUNTIME_OPTIONS.map((key) => options[key]);
33
+ useEffect(() => {
34
+ editor?.setOptions(pickRuntimeOptions(latest.current));
35
+ }, [editor, ...runtime]);
36
+ return { ref, editor };
37
+ }
38
+ function RichTextEditor(props) {
39
+ const { value, defaultValue, onChange, name, className, id, ...options } = props;
40
+ const applied = useRef(value);
41
+ const field = useRef(null);
42
+ const { ref, editor } = useEditor({
43
+ ...options,
44
+ content: value ?? defaultValue,
45
+ onCreate: (instance) => {
46
+ if (field.current) field.current.value = instance.getHTML();
47
+ options.onCreate?.(instance);
48
+ },
49
+ onUpdate: (instance) => {
50
+ const html = instance.getHTML();
51
+ applied.current = html;
52
+ if (field.current) field.current.value = html;
53
+ options.onUpdate?.(instance);
54
+ onChange?.(html, instance);
55
+ }
56
+ });
57
+ useEffect(() => {
58
+ if (!editor || value === void 0 || value === applied.current) return;
59
+ applied.current = value;
60
+ syncContent(editor, value);
61
+ if (field.current) field.current.value = editor.getHTML();
62
+ }, [editor, value]);
63
+ const initial = typeof (value ?? defaultValue) === "string" ? value ?? defaultValue : "";
64
+ return createElement(
65
+ "div",
66
+ { className, id },
67
+ createElement("div", { ref }),
68
+ name ? createElement("input", { ref: field, type: "hidden", name, defaultValue: initial }) : null
69
+ );
70
+ }
71
+ export {
72
+ RichTextEditor,
73
+ useEditor
74
+ };
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// React adapter (React 18+, Next.js App and Pages Router, Remix, Gatsby, Vite). The build marks\n// this file \"use client\", so Server Components can render <RichTextEditor> directly.\nimport { createElement, useEffect, useRef, useState, type ReactElement, type RefObject } from \"react\";\nimport {\n RUNTIME_OPTIONS,\n createEditor,\n forwardCallbacks,\n pickRuntimeOptions,\n syncContent,\n type Content,\n type Editor,\n type EditorOptions,\n} from \"open-wysiwyg-editor\";\n\n// One install gives the whole API: StarterKit, getUI, types…\nexport * from \"open-wysiwyg-editor\";\n\nexport interface UseEditorResult {\n /** Attach to the element the editor mounts in: `<div ref={ref} />`. */\n ref: RefObject<HTMLDivElement | null>;\n /** The live editor, or `null` before mount and during server rendering. */\n editor: Editor | null;\n}\n\n/**\n * Creates an editor in the element behind `ref` and destroys it on unmount.\n * Callbacks and the runtime options (`editable`, `placeholder`, `aria*`, `dir`, `contentLang`)\n * follow every render; everything else (`extensions`, `ui`, `language`…) is read once at mount.\n * Give the component a new `key` to apply those.\n */\nexport function useEditor(options: EditorOptions = {}): UseEditorResult {\n const ref = useRef<HTMLDivElement>(null);\n const [editor, setEditor] = useState<Editor | null>(null);\n const latest = useRef(options);\n\n useEffect(() => {\n latest.current = options;\n });\n\n useEffect(() => {\n const instance = createEditor({\n ...latest.current,\n element: ref.current,\n ...forwardCallbacks(() => latest.current),\n });\n setEditor(instance);\n return () => {\n instance.destroy();\n setEditor(null);\n };\n }, []);\n\n const runtime = RUNTIME_OPTIONS.map((key) => options[key]);\n useEffect(() => {\n editor?.setOptions(pickRuntimeOptions(latest.current));\n }, [editor, ...runtime]);\n\n return { ref, editor };\n}\n\nexport interface RichTextEditorProps extends Omit<EditorOptions, \"element\" | \"content\"> {\n /** Controlled HTML. The editor loads it whenever it differs from what the editor last produced. */\n value?: string;\n /** Initial content (HTML or JSON) for an uncontrolled editor. */\n defaultValue?: Content;\n /** Called with the new HTML on every change. */\n onChange?: (html: string, editor: Editor) => void;\n /**\n * Form field name. The HTML is kept in a hidden input, so `<form action={serverAction}>`\n * (Next.js, React 19) and plain form posts receive it.\n */\n name?: string;\n className?: string;\n id?: string;\n}\n\n/**\n * The full editor (toolbar, dialogs, status bar) as a component.\n *\n * ```tsx\n * const [html, setHtml] = useState(\"<p>Hello</p>\");\n * <RichTextEditor value={html} onChange={setHtml} />\n * ```\n */\nexport function RichTextEditor(props: RichTextEditorProps): ReactElement {\n const { value, defaultValue, onChange, name, className, id, ...options } = props;\n const applied = useRef<Content>(value);\n const field = useRef<HTMLInputElement>(null);\n const { ref, editor } = useEditor({\n ...options,\n content: value ?? defaultValue,\n onCreate: (instance) => {\n if (field.current) field.current.value = instance.getHTML();\n options.onCreate?.(instance);\n },\n onUpdate: (instance) => {\n const html = instance.getHTML();\n applied.current = html;\n if (field.current) field.current.value = html;\n options.onUpdate?.(instance);\n onChange?.(html, instance);\n },\n });\n\n useEffect(() => {\n if (!editor || value === undefined || value === applied.current) return;\n applied.current = value;\n syncContent(editor, value);\n if (field.current) field.current.value = editor.getHTML();\n }, [editor, value]);\n\n // Uncontrolled hidden input: the server renders the initial HTML, the editor keeps it current.\n const initial = typeof (value ?? defaultValue) === \"string\" ? ((value ?? defaultValue) as string) : \"\";\n return createElement(\n \"div\",\n { className, id },\n createElement(\"div\", { ref }),\n name ? createElement(\"input\", { ref: field, type: \"hidden\", name, defaultValue: initial }) : null,\n );\n}\n"],"mappings":";;;AAEA,SAAS,eAAe,WAAW,QAAQ,gBAAmD;AAC9F;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP,cAAc;AAeP,SAAS,UAAU,UAAyB,CAAC,GAAoB;AACtE,QAAM,MAAM,OAAuB,IAAI;AACvC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,IAAI;AACxD,QAAM,SAAS,OAAO,OAAO;AAE7B,YAAU,MAAM;AACd,WAAO,UAAU;AAAA,EACnB,CAAC;AAED,YAAU,MAAM;AACd,UAAM,WAAW,aAAa;AAAA,MAC5B,GAAG,OAAO;AAAA,MACV,SAAS,IAAI;AAAA,MACb,GAAG,iBAAiB,MAAM,OAAO,OAAO;AAAA,IAC1C,CAAC;AACD,cAAU,QAAQ;AAClB,WAAO,MAAM;AACX,eAAS,QAAQ;AACjB,gBAAU,IAAI;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,gBAAgB,IAAI,CAAC,QAAQ,QAAQ,GAAG,CAAC;AACzD,YAAU,MAAM;AACd,YAAQ,WAAW,mBAAmB,OAAO,OAAO,CAAC;AAAA,EACvD,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC;AAEvB,SAAO,EAAE,KAAK,OAAO;AACvB;AA0BO,SAAS,eAAe,OAA0C;AACvE,QAAM,EAAE,OAAO,cAAc,UAAU,MAAM,WAAW,IAAI,GAAG,QAAQ,IAAI;AAC3E,QAAM,UAAU,OAAgB,KAAK;AACrC,QAAM,QAAQ,OAAyB,IAAI;AAC3C,QAAM,EAAE,KAAK,OAAO,IAAI,UAAU;AAAA,IAChC,GAAG;AAAA,IACH,SAAS,SAAS;AAAA,IAClB,UAAU,CAAC,aAAa;AACtB,UAAI,MAAM,QAAS,OAAM,QAAQ,QAAQ,SAAS,QAAQ;AAC1D,cAAQ,WAAW,QAAQ;AAAA,IAC7B;AAAA,IACA,UAAU,CAAC,aAAa;AACtB,YAAM,OAAO,SAAS,QAAQ;AAC9B,cAAQ,UAAU;AAClB,UAAI,MAAM,QAAS,OAAM,QAAQ,QAAQ;AACzC,cAAQ,WAAW,QAAQ;AAC3B,iBAAW,MAAM,QAAQ;AAAA,IAC3B;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,UAAU,UAAa,UAAU,QAAQ,QAAS;AACjE,YAAQ,UAAU;AAClB,gBAAY,QAAQ,KAAK;AACzB,QAAI,MAAM,QAAS,OAAM,QAAQ,QAAQ,OAAO,QAAQ;AAAA,EAC1D,GAAG,CAAC,QAAQ,KAAK,CAAC;AAGlB,QAAM,UAAU,QAAQ,SAAS,kBAAkB,WAAa,SAAS,eAA2B;AACpG,SAAO;AAAA,IACL;AAAA,IACA,EAAE,WAAW,GAAG;AAAA,IAChB,cAAc,OAAO,EAAE,IAAI,CAAC;AAAA,IAC5B,OAAO,cAAc,SAAS,EAAE,KAAK,OAAO,MAAM,UAAU,MAAM,cAAc,QAAQ,CAAC,IAAI;AAAA,EAC/F;AACF;","names":[]}