@jackuait/blok 0.22.0 → 0.23.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/dist/full.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/blok-Blnd71Ww.cjs`),t=require(`./chunks/constants-BIqDMhRj.cjs`),n=require(`./chunks/objectSpread2-iNkE6aJh.cjs`),r=require(`./chunks/tools-DTjC0C5o.cjs`);var i={paragraph:{class:r.k,inlineToolbar:!0,config:{preserveBlank:!0}},header:{class:r.O,inlineToolbar:!0},list:{class:r.D,inlineToolbar:!0}},a=n.t(n.t({},i),{},{bold:{class:r.l},italic:{class:r.c},marker:{class:r.o},underline:{class:r.a},strikethrough:{class:r.i},link:{class:r.s}});exports.Blok=e.t,exports.Bold=r.l,exports.DATA_ATTR=t.ja,exports.Header=r.O,exports.Italic=r.c,exports.Link=r.s,exports.List=r.D,exports.Marker=r.o,exports.Paragraph=r.k,exports.Strikethrough=r.i,exports.Toggle=r.T,exports.Underline=r.a,exports.allTools=a,exports.defaultBlockTools=r.t,exports.defaultInlineTools=r.n,exports.defaultTools=i,exports.version=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/blok-D4WXeg3t.cjs`),t=require(`./chunks/constants-BbCnmzAr.cjs`),n=require(`./chunks/objectSpread2-iNkE6aJh.cjs`),r=require(`./chunks/tools-tG5_UaWz.cjs`);var i={paragraph:{class:r.k,inlineToolbar:!0,config:{preserveBlank:!0}},header:{class:r.O,inlineToolbar:!0},list:{class:r.D,inlineToolbar:!0}},a=n.t(n.t({},i),{},{bold:{class:r.l},italic:{class:r.c},marker:{class:r.o},underline:{class:r.a},strikethrough:{class:r.i},link:{class:r.s}});exports.Blok=e.t,exports.Bold=r.l,exports.DATA_ATTR=t.ja,exports.Header=r.O,exports.Italic=r.c,exports.Link=r.s,exports.List=r.D,exports.Marker=r.o,exports.Paragraph=r.k,exports.Strikethrough=r.i,exports.Toggle=r.T,exports.Underline=r.a,exports.allTools=a,exports.defaultBlockTools=r.t,exports.defaultInlineTools=r.n,exports.defaultTools=i,exports.version=e.n;
package/dist/full.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { n as e, t } from "./chunks/blok-S6c68iVJ.mjs";
2
- import { ja as n } from "./chunks/constants-BvxJIgSQ.mjs";
1
+ import { n as e, t } from "./chunks/blok-BmckXjmW.mjs";
2
+ import { ja as n } from "./chunks/constants-tXKHkDAG.mjs";
3
3
  import { t as r } from "./chunks/objectSpread2-jlBcdYeb.mjs";
4
- import { D as i, O as a, T as o, a as s, c, i as l, k as u, l as d, n as f, o as p, s as m, t as h } from "./chunks/tools-B89FUeNz.mjs";
4
+ import { D as i, O as a, T as o, a as s, c, i as l, k as u, l as d, n as f, o as p, s as m, t as h } from "./chunks/tools-oXMAa9zD.mjs";
5
5
  //#region src/full.ts
6
6
  var g = {
7
7
  paragraph: {
package/dist/react.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunks/chunk-BL1yB9zm.cjs`);const e=require(`./chunks/blok-Blnd71Ww.cjs`),t=require(`./chunks/objectSpread2-iNkE6aJh.cjs`);let n=require(`react`),r=require(`react/jsx-runtime`);var i=new WeakMap;function a(e,t){i.set(e,t)}function o(e){return i.get(e)}function s(e){i.delete(e)}function c(r,i){let[o,c]=(0,n.useState)(null),u=(0,n.useRef)(r);u.current=r;let d=(0,n.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,depsToken:null}),f=(0,n.useMemo)(()=>({}),i==null?[]:i);(0,n.useEffect)(()=>{if(typeof window>`u`)return;let n=d.current;if(n.destroyTimeout!==null&&(clearTimeout(n.destroyTimeout),n.destroyTimeout=null),n.editor!==null&&!n.isDestroyed&&n.depsToken===f)return c(n.editor),()=>{l(n,c)};if(n.editor!==null&&!n.isDestroyed){s(n.editor);try{n.editor.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,c(null)}let r=document.createElement(`div`);n.holder=r,n.isDestroyed=!1,n.depsToken=f;let i=u.current,o=new e.t(t.t(t.t({},i),{},{holder:r,onReady:()=>{var e,t;(e=(t=u.current).onReady)==null||e.call(t)},onChange:(...e)=>{var t,n;(t=(n=u.current).onChange)==null||t.call(n,...e)}}));return n.editor=o,a(o,r),o.isReady.then(()=>{n.editor===o&&!n.isDestroyed&&c(o)}).catch(()=>{if(n.editor===o&&!n.isDestroyed){s(o);try{o.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,c(null)}}),()=>{l(n,c)}},[f]);let{readOnly:p}=r;(0,n.useEffect)(()=>{o!==null&&o.readOnly.set(p==null?!1:p)},[o,p]);let{autofocus:m}=r;return(0,n.useEffect)(()=>{o===null||!m||o.focus()},[o,m]),o}function l(e,t){e.destroyTimeout=setTimeout(()=>{if(e.editor!==null){s(e.editor);try{e.editor.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.destroyTimeout=null,t(null)}},0)}var u=[`editor`],d=(0,n.forwardRef)(function(i,a){let{editor:s}=i,c=e.i(i,u),l=(0,n.useRef)(null);return(0,n.useEffect)(()=>{if(s===null||l.current===null)return;let e=o(s);if(e!==void 0)return l.current.appendChild(e),()=>{e.remove()}},[s]),(0,r.jsx)(`div`,t.t({ref:e=>{if(l.current=e,typeof a==`function`)a(e);else if(a!=null){let t=a;t.current=e}}},c))});exports.BlokContent=d,exports.useBlok=c;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunks/chunk-BL1yB9zm.cjs`);const e=require(`./chunks/blok-D4WXeg3t.cjs`),t=require(`./chunks/objectSpread2-iNkE6aJh.cjs`);let n=require(`react`),r=require(`react/jsx-runtime`);var i=new WeakMap;function a(e,t){i.set(e,t)}function o(e){return i.get(e)}function s(e){i.delete(e)}function c(r,i){let[o,c]=(0,n.useState)(null),u=(0,n.useRef)(r);u.current=r;let d=(0,n.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,depsToken:null}),f=(0,n.useMemo)(()=>({}),i==null?[]:i);(0,n.useEffect)(()=>{if(typeof window>`u`)return;let n=d.current;if(n.destroyTimeout!==null&&(clearTimeout(n.destroyTimeout),n.destroyTimeout=null),n.editor!==null&&!n.isDestroyed&&n.depsToken===f)return c(n.editor),()=>{l(n,c)};if(n.editor!==null&&!n.isDestroyed){s(n.editor);try{n.editor.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,c(null)}let r=document.createElement(`div`);n.holder=r,n.isDestroyed=!1,n.depsToken=f;let i=u.current,o=new e.t(t.t(t.t({},i),{},{holder:r,onReady:()=>{var e,t;(e=(t=u.current).onReady)==null||e.call(t)},onChange:(...e)=>{var t,n;(t=(n=u.current).onChange)==null||t.call(n,...e)}}));return n.editor=o,a(o,r),o.isReady.then(()=>{n.editor===o&&!n.isDestroyed&&c(o)}).catch(()=>{if(n.editor===o&&!n.isDestroyed){s(o);try{o.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,c(null)}}),()=>{l(n,c)}},[f]);let{readOnly:p}=r;(0,n.useEffect)(()=>{o!==null&&o.readOnly.set(p==null?!1:p)},[o,p]);let{autofocus:m}=r;(0,n.useEffect)(()=>{o===null||!m||o.focus()},[o,m]);let{theme:h}=r;(0,n.useEffect)(()=>{o===null||h===void 0||o.theme.set(h)},[o,h]);let{width:g}=r;return(0,n.useEffect)(()=>{o===null||g===void 0||o.width.set(g)},[o,g]),o}function l(e,t){e.destroyTimeout=setTimeout(()=>{if(e.editor!==null){s(e.editor);try{e.editor.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.destroyTimeout=null,t(null)}},0)}var u=[`editor`],d=(0,n.forwardRef)(function(i,a){let{editor:s}=i,c=e.i(i,u),l=(0,n.useRef)(null);return(0,n.useEffect)(()=>{if(s===null||l.current===null)return;let e=o(s);if(e!==void 0)return l.current.appendChild(e),()=>{e.remove()}},[s]),(0,r.jsx)(`div`,t.t({ref:e=>{if(l.current=e,typeof a==`function`)a(e);else if(a!=null){let t=a;t.current=e}}},c))}),f=[`deps`,`className`,`onReady`,`data-testid`],p=(0,n.forwardRef)(function(t,i){let{deps:a,className:o,onReady:s,"data-testid":l}=t,u=c(e.i(t,f),a);(0,n.useImperativeHandle)(i,()=>u,[u]);let p=(0,n.useRef)(s);return p.current=s,(0,n.useEffect)(()=>{if(u!==null){var e;(e=p.current)==null||e.call(p,u)}},[u]),(0,r.jsx)(d,{editor:u,className:o,"data-testid":l})});exports.BlokContent=d,exports.BlokEditor=p,exports.useBlok=c;
package/dist/react.mjs CHANGED
@@ -1,83 +1,91 @@
1
- import { i as e, t } from "./chunks/blok-S6c68iVJ.mjs";
1
+ import { i as e, t } from "./chunks/blok-BmckXjmW.mjs";
2
2
  import { t as n } from "./chunks/objectSpread2-jlBcdYeb.mjs";
3
- import { forwardRef as r, useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
4
- import { jsx as c } from "react/jsx-runtime";
3
+ import { forwardRef as r, useEffect as i, useImperativeHandle as a, useMemo as o, useRef as s, useState as c } from "react";
4
+ import { jsx as l } from "react/jsx-runtime";
5
5
  //#region src/react/holder-map.ts
6
- var l = /* @__PURE__ */ new WeakMap();
7
- function u(e, t) {
8
- l.set(e, t);
9
- }
10
- function d(e) {
11
- return l.get(e);
6
+ var u = /* @__PURE__ */ new WeakMap();
7
+ function d(e, t) {
8
+ u.set(e, t);
12
9
  }
13
10
  function f(e) {
14
- l.delete(e);
11
+ return u.get(e);
12
+ }
13
+ function p(e) {
14
+ u.delete(e);
15
15
  }
16
16
  //#endregion
17
17
  //#region src/react/useBlok.ts
18
- function p(e, r) {
19
- let [c, l] = s(null), d = o(e);
20
- d.current = e;
21
- let p = o({
18
+ function m(e, r) {
19
+ let [a, l] = c(null), u = s(e);
20
+ u.current = e;
21
+ let f = s({
22
22
  editor: null,
23
23
  holder: null,
24
24
  destroyTimeout: null,
25
25
  isDestroyed: !1,
26
26
  depsToken: null
27
- }), h = a(() => ({}), r == null ? [] : r);
27
+ }), m = o(() => ({}), r == null ? [] : r);
28
28
  i(() => {
29
29
  if (typeof window > "u") return;
30
- let e = p.current;
31
- if (e.destroyTimeout !== null && (clearTimeout(e.destroyTimeout), e.destroyTimeout = null), e.editor !== null && !e.isDestroyed && e.depsToken === h) return l(e.editor), () => {
32
- m(e, l);
30
+ let e = f.current;
31
+ if (e.destroyTimeout !== null && (clearTimeout(e.destroyTimeout), e.destroyTimeout = null), e.editor !== null && !e.isDestroyed && e.depsToken === m) return l(e.editor), () => {
32
+ h(e, l);
33
33
  };
34
34
  if (e.editor !== null && !e.isDestroyed) {
35
- f(e.editor);
35
+ p(e.editor);
36
36
  try {
37
37
  e.editor.destroy();
38
38
  } catch (e) {}
39
39
  e.editor = null, e.holder = null, e.isDestroyed = !0, l(null);
40
40
  }
41
41
  let r = document.createElement("div");
42
- e.holder = r, e.isDestroyed = !1, e.depsToken = h;
43
- let i = d.current, a = new t(n(n({}, i), {}, {
42
+ e.holder = r, e.isDestroyed = !1, e.depsToken = m;
43
+ let i = u.current, a = new t(n(n({}, i), {}, {
44
44
  holder: r,
45
45
  onReady: () => {
46
46
  var e, t;
47
- (e = (t = d.current).onReady) == null || e.call(t);
47
+ (e = (t = u.current).onReady) == null || e.call(t);
48
48
  },
49
49
  onChange: (...e) => {
50
50
  var t, n;
51
- (t = (n = d.current).onChange) == null || t.call(n, ...e);
51
+ (t = (n = u.current).onChange) == null || t.call(n, ...e);
52
52
  }
53
53
  }));
54
- return e.editor = a, u(a, r), a.isReady.then(() => {
54
+ return e.editor = a, d(a, r), a.isReady.then(() => {
55
55
  e.editor === a && !e.isDestroyed && l(a);
56
56
  }).catch(() => {
57
57
  if (e.editor === a && !e.isDestroyed) {
58
- f(a);
58
+ p(a);
59
59
  try {
60
60
  a.destroy();
61
61
  } catch (e) {}
62
62
  e.editor = null, e.holder = null, e.isDestroyed = !0, l(null);
63
63
  }
64
64
  }), () => {
65
- m(e, l);
65
+ h(e, l);
66
66
  };
67
- }, [h]);
67
+ }, [m]);
68
68
  let { readOnly: g } = e;
69
69
  i(() => {
70
- c !== null && c.readOnly.set(g == null ? !1 : g);
71
- }, [c, g]);
70
+ a !== null && a.readOnly.set(g == null ? !1 : g);
71
+ }, [a, g]);
72
72
  let { autofocus: _ } = e;
73
+ i(() => {
74
+ a === null || !_ || a.focus();
75
+ }, [a, _]);
76
+ let { theme: v } = e;
77
+ i(() => {
78
+ a === null || v === void 0 || a.theme.set(v);
79
+ }, [a, v]);
80
+ let { width: y } = e;
73
81
  return i(() => {
74
- c === null || !_ || c.focus();
75
- }, [c, _]), c;
82
+ a === null || y === void 0 || a.width.set(y);
83
+ }, [a, y]), a;
76
84
  }
77
- function m(e, t) {
85
+ function h(e, t) {
78
86
  e.destroyTimeout = setTimeout(() => {
79
87
  if (e.editor !== null) {
80
- f(e.editor);
88
+ p(e.editor);
81
89
  try {
82
90
  e.editor.destroy();
83
91
  } catch (e) {}
@@ -87,21 +95,40 @@ function m(e, t) {
87
95
  }
88
96
  //#endregion
89
97
  //#region src/react/BlokContent.tsx
90
- var h = ["editor"], g = r(function(t, r) {
91
- let { editor: a } = t, s = e(t, h), l = o(null);
98
+ var g = ["editor"], _ = r(function(t, r) {
99
+ let { editor: a } = t, o = e(t, g), c = s(null);
92
100
  return i(() => {
93
- if (a === null || l.current === null) return;
94
- let e = d(a);
95
- if (e !== void 0) return l.current.appendChild(e), () => {
101
+ if (a === null || c.current === null) return;
102
+ let e = f(a);
103
+ if (e !== void 0) return c.current.appendChild(e), () => {
96
104
  e.remove();
97
105
  };
98
- }, [a]), /* @__PURE__ */ c("div", n({ ref: (e) => {
99
- if (l.current = e, typeof r == "function") r(e);
106
+ }, [a]), /* @__PURE__ */ l("div", n({ ref: (e) => {
107
+ if (c.current = e, typeof r == "function") r(e);
100
108
  else if (r != null) {
101
109
  let t = r;
102
110
  t.current = e;
103
111
  }
104
- } }, s));
112
+ } }, o));
113
+ }), v = [
114
+ "deps",
115
+ "className",
116
+ "onReady",
117
+ "data-testid"
118
+ ], y = r(function(t, n) {
119
+ let { deps: r, className: o, onReady: c, "data-testid": u } = t, d = m(e(t, v), r);
120
+ a(n, () => d, [d]);
121
+ let f = s(c);
122
+ return f.current = c, i(() => {
123
+ if (d !== null) {
124
+ var e;
125
+ (e = f.current) == null || e.call(f, d);
126
+ }
127
+ }, [d]), /* @__PURE__ */ l(_, {
128
+ editor: d,
129
+ className: o,
130
+ "data-testid": u
131
+ });
105
132
  });
106
133
  //#endregion
107
- export { g as BlokContent, p as useBlok };
134
+ export { _ as BlokContent, y as BlokEditor, m as useBlok };
package/dist/tools.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/constants-BIqDMhRj.cjs`),t=require(`./chunks/tools-DTjC0C5o.cjs`);exports.Audio=t.g,exports.Bold=t.l,exports.Bookmark=t.u,exports.Callout=t.w,exports.Code=t.y,exports.Column=t.p,exports.ColumnList=t.m,exports.Columns=t.f,exports.Convert=e.m,exports.Database=t.C,exports.DatabaseRow=t.S,exports.Divider=t.x,exports.Embed=t.d,exports.File=t._,exports.Header=t.O,exports.Image=t.v,exports.InlineCode=t.r,exports.Italic=t.c,exports.Link=t.s,exports.List=t.D,exports.Marker=t.o,exports.Paragraph=t.k,exports.Quote=t.b,exports.Strikethrough=t.i,exports.Table=t.E,exports.Toggle=t.T,exports.Underline=t.a,exports.Video=t.h,exports.defaultBlockTools=t.t,exports.defaultInlineTools=t.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/constants-BbCnmzAr.cjs`),t=require(`./chunks/tools-tG5_UaWz.cjs`);exports.Audio=t.g,exports.Bold=t.l,exports.Bookmark=t.u,exports.Callout=t.w,exports.Code=t.y,exports.Column=t.p,exports.ColumnList=t.m,exports.Columns=t.f,exports.Convert=e.m,exports.Database=t.C,exports.DatabaseRow=t.S,exports.Divider=t.x,exports.Embed=t.d,exports.File=t._,exports.Header=t.O,exports.Image=t.v,exports.InlineCode=t.r,exports.Italic=t.c,exports.Link=t.s,exports.List=t.D,exports.Marker=t.o,exports.Paragraph=t.k,exports.Quote=t.b,exports.Strikethrough=t.i,exports.Table=t.E,exports.Toggle=t.T,exports.Underline=t.a,exports.Video=t.h,exports.defaultBlockTools=t.t,exports.defaultInlineTools=t.n;
package/dist/tools.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { m as e } from "./chunks/constants-BvxJIgSQ.mjs";
2
- import { C as t, D as n, E as r, O as i, S as a, T as o, _ as s, a as c, b as l, c as u, d, f, g as p, h as m, i as h, k as g, l as _, m as v, n as y, o as b, p as x, r as S, s as C, t as w, u as T, v as E, w as D, x as O, y as k } from "./chunks/tools-B89FUeNz.mjs";
1
+ import { m as e } from "./chunks/constants-tXKHkDAG.mjs";
2
+ import { C as t, D as n, E as r, O as i, S as a, T as o, _ as s, a as c, b as l, c as u, d, f, g as p, h as m, i as h, k as g, l as _, m as v, n as y, o as b, p as x, r as S, s as C, t as w, u as T, v as E, w as D, x as O, y as k } from "./chunks/tools-oXMAa9zD.mjs";
3
3
  export { p as Audio, _ as Bold, T as Bookmark, D as Callout, k as Code, x as Column, v as ColumnList, f as Columns, e as Convert, t as Database, a as DatabaseRow, O as Divider, d as Embed, s as File, i as Header, E as Image, S as InlineCode, u as Italic, C as Link, n as List, b as Marker, g as Paragraph, l as Quote, h as Strikethrough, r as Table, o as Toggle, c as Underline, m as Video, w as defaultBlockTools, y as defaultInlineTools };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackuait/blok",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Blok — headless, highly extensible rich text editor built for developers who need to implement a block-based editing experience (similar to Notion) without building it from scratch",
5
5
  "module": "dist/blok.mjs",
6
6
  "main": "./dist/blok.cjs",
@@ -0,0 +1,63 @@
1
+ import { forwardRef, useImperativeHandle, useEffect, useRef, type DependencyList } from 'react';
2
+ import { useBlok } from './useBlok';
3
+ import { BlokContent } from './BlokContent';
4
+ import type { Blok } from '@/types';
5
+ import type { UseBlokConfig } from './types';
6
+
7
+ /**
8
+ * Props for the all-in-one BlokEditor component.
9
+ * Accepts every useBlok config prop (except `onReady`, re-typed below to receive
10
+ * the ready instance), plus container `className`/`data-testid` and an optional
11
+ * `deps` list. When any value in `deps` changes, the editor is destroyed and
12
+ * recreated (use this when `tools` or other structural config changes).
13
+ *
14
+ * `data` is uncontrolled (seed-only): it sets the INITIAL content. After mount the
15
+ * editor owns the document — passing a new `data` reference does NOT reload it.
16
+ * Read content via `onChange` or the ref (`ref.current.save()`); replace it
17
+ * imperatively via `ref.current.render(newData)`.
18
+ */
19
+ export interface BlokEditorProps extends Omit<UseBlokConfig, 'onReady'> {
20
+ /** When any value changes, the editor is destroyed and recreated. */
21
+ deps?: DependencyList;
22
+ /** Class name applied to the editor container element. */
23
+ className?: string;
24
+ /** Test id forwarded to the editor container element (via data-testid). */
25
+ 'data-testid'?: string;
26
+ /**
27
+ * Called once the editor is ready, with the live Blok instance. Fires after the
28
+ * forwarded ref is committed, so `ref.current` is also populated at this point.
29
+ */
30
+ onReady?: (editor: Blok) => void;
31
+ }
32
+
33
+ /**
34
+ * The recommended way to embed Blok in React. Internally wires `useBlok` and
35
+ * `BlokContent`, and forwards a ref to the live `Blok` instance.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * const ref = useRef<Blok | null>(null);
40
+ * <BlokEditor ref={ref} tools={tools} data={data} theme={theme} onReady={(e) => e.focus()} />;
41
+ * ```
42
+ */
43
+ export const BlokEditor = forwardRef<Blok | null, BlokEditorProps>(
44
+ function BlokEditor({ deps, className, onReady, 'data-testid': dataTestId, ...config }, ref) {
45
+ const editor = useBlok(config, deps);
46
+
47
+ useImperativeHandle<Blok | null, Blok | null>(ref, () => editor, [editor]);
48
+
49
+ // Own onReady here instead of forwarding it to the core config: the core
50
+ // onReady fires before useImperativeHandle commits the ref. This passive
51
+ // effect runs AFTER the commit, so both the instance argument and ref.current
52
+ // are reliably populated.
53
+ const onReadyRef = useRef(onReady);
54
+ onReadyRef.current = onReady;
55
+ useEffect(() => {
56
+ if (editor !== null) {
57
+ onReadyRef.current?.(editor);
58
+ }
59
+ }, [editor]);
60
+
61
+ return <BlokContent editor={editor} className={className} data-testid={dataTestId} />;
62
+ }
63
+ );
@@ -1,3 +1,5 @@
1
1
  export { useBlok } from './useBlok';
2
2
  export { BlokContent } from './BlokContent';
3
+ export { BlokEditor } from './BlokEditor';
3
4
  export type { UseBlokConfig, BlokContentProps } from './types';
5
+ export type { BlokEditorProps } from './BlokEditor';
@@ -1,10 +1,20 @@
1
- import type { BlokConfig, Blok } from '@/types';
1
+ import type { BlokConfig, Blok, EditorWidth } from '@/types';
2
2
 
3
3
  /**
4
4
  * Configuration for useBlok hook.
5
- * Same as BlokConfig but without `holder` — the holder is managed by BlokContent.
5
+ * Same as BlokConfig but without `holder` (managed by BlokContent), plus a
6
+ * React-only reactive `width` prop.
7
+ *
8
+ * Reactive props (sync after mount without recreation):
9
+ * - `readOnly` — calls `editor.readOnly.set(value)`
10
+ * - `autofocus` — calls `editor.focus()` when changed to true
11
+ * - `theme` — calls `editor.theme.set(value)`
12
+ * - `width` — calls `editor.width.set(value)`
6
13
  */
7
- export interface UseBlokConfig extends Omit<BlokConfig, 'holder'> {}
14
+ export interface UseBlokConfig extends Omit<BlokConfig, 'holder'> {
15
+ /** Editor content width mode. Synced reactively after mount via `editor.width.set()`. */
16
+ width?: EditorWidth;
17
+ }
8
18
 
9
19
  /**
10
20
  * Props for the BlokContent component.
@@ -146,6 +146,24 @@ export function useBlok(config: UseBlokConfig, deps?: DependencyList): Blok | nu
146
146
  editor.focus();
147
147
  }, [editor, autofocus]);
148
148
 
149
+ // Reactive: theme
150
+ const { theme } = config;
151
+ useEffect(() => {
152
+ if (editor === null || theme === undefined) {
153
+ return;
154
+ }
155
+ editor.theme.set(theme);
156
+ }, [editor, theme]);
157
+
158
+ // Reactive: width
159
+ const { width } = config;
160
+ useEffect(() => {
161
+ if (editor === null || width === undefined) {
162
+ return;
163
+ }
164
+ editor.width.set(width);
165
+ }, [editor, width]);
166
+
149
167
  return editor;
150
168
  }
151
169
 
package/types/react.d.ts CHANGED
@@ -1,18 +1,23 @@
1
1
  import type { BlokConfig } from './index';
2
- import type { Blok } from './index';
2
+ import type { Blok, EditorWidth } from './index';
3
3
  import type React from 'react';
4
4
 
5
5
  /**
6
6
  * Configuration for the useBlok hook.
7
- * Accepts all BlokConfig properties except `holder`, which is managed by BlokContent.
7
+ * Accepts all BlokConfig properties except `holder`, plus a React-only `width` prop.
8
8
  *
9
9
  * Reactive props (sync after mount without recreation):
10
10
  * - `readOnly` — calls `editor.readOnly.set(value)`
11
11
  * - `autofocus` — calls `editor.focus()` when changed to true
12
+ * - `theme` — calls `editor.theme.set(value)`
13
+ * - `width` — calls `editor.width.set(value)`
12
14
  *
13
15
  * All other config is consumed once at editor creation.
14
16
  */
15
- export interface UseBlokConfig extends Omit<BlokConfig, 'holder'> {}
17
+ export interface UseBlokConfig extends Omit<BlokConfig, 'holder'> {
18
+ /** Editor content width mode. Synced reactively after mount. */
19
+ width?: EditorWidth;
20
+ }
16
21
 
17
22
  /**
18
23
  * Props for the BlokContent component.
@@ -56,3 +61,33 @@ export function useBlok(
56
61
  * ```
57
62
  */
58
63
  export declare const BlokContent: React.ForwardRefExoticComponent<BlokContentProps & React.RefAttributes<HTMLDivElement>>;
64
+
65
+ /**
66
+ * Props for the BlokEditor component — all useBlok config (except `onReady`,
67
+ * re-typed to receive the ready instance) plus container `className`/`data-testid`
68
+ * and an optional `deps` list that forces recreation when changed.
69
+ */
70
+ export interface BlokEditorProps extends Omit<UseBlokConfig, 'onReady'> {
71
+ /** When any value changes, the editor is destroyed and recreated. */
72
+ deps?: React.DependencyList;
73
+ /** Class name applied to the editor container element. */
74
+ className?: string;
75
+ /** Test id forwarded to the editor container element (via data-testid). */
76
+ 'data-testid'?: string;
77
+ /** Called once the editor is ready, with the live Blok instance (ref is also committed). */
78
+ onReady?: (editor: Blok) => void;
79
+ }
80
+
81
+ /**
82
+ * The recommended all-in-one React component. Wires useBlok + BlokContent and
83
+ * forwards a ref to the live Blok instance (null before the editor is ready).
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * const ref = useRef<Blok | null>(null);
88
+ * <BlokEditor ref={ref} tools={tools} data={data} theme="dark" />;
89
+ * ```
90
+ */
91
+ export declare const BlokEditor: React.ForwardRefExoticComponent<
92
+ BlokEditorProps & React.RefAttributes<Blok | null>
93
+ >;