@bloklabs/react 1.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`react`),t=require(`@bloklabs/core`),n=require(`@bloklabs/core/adapters`),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(e){"@babel/helpers - typeof";return c=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},c(e)}function l(e,t){if(c(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(c(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function u(e){var t=l(e,`string`);return c(t)==`symbol`?t:t+``}function d(e,t,n){return(t=u(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function f(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function p(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?f(Object(n),!0).forEach(function(t){d(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):f(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}var m=(0,e.createContext)({});function h({defaults:e,children:t}){return(0,r.jsx)(m.Provider,{value:e,children:t})}function g(){return(0,e.useContext)(m)}function _(e,t){if(Object.keys(e).length===0)return t;let n=p({},e);for(let e of Object.keys(t)){let r=t[e];r!==void 0&&(n[e]=r)}return(e.tools!==void 0||t.tools!==void 0)&&(n.tools=p(p({},e.tools),t.tools)),n}function v(r,i){let o=_((0,e.useContext)(m),r),[c,l]=(0,e.useState)(null),u=(0,e.useRef)(o);u.current=o;let d=(0,e.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,isEditorReady:!1,depsToken:null}),f=(0,e.useMemo)(()=>({}),i==null?[]:i),h=(0,e.useRef)(o.data),g=(0,e.useRef)(null),v=(0,e.useRef)(o.data),b=(0,e.useRef)(Promise.resolve()),x=(0,e.useRef)(void 0);(0,e.useEffect)(()=>{if(typeof window>`u`)return;let e=d.current;if(e.destroyTimeout!==null&&(clearTimeout(e.destroyTimeout),e.destroyTimeout=null),e.editor!==null&&!e.isDestroyed&&e.depsToken===f)return e.isEditorReady&&l(e.editor),()=>{y(e,l)};if(e.editor!==null&&!e.isDestroyed){s(e.editor);try{e.editor.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.isEditorReady=!1,l(null)}let n=document.createElement(`div`);e.holder=n,e.isDestroyed=!1,e.isEditorReady=!1,e.depsToken=f;let r=u.current,i=p(p({},r),{},{holder:n,onReady:(...e)=>{var t,n;(t=(n=u.current).onReady)==null||t.call(n,...e)},onChange:(...e)=>{var t,n;(t=(n=u.current).onChange)==null||t.call(n,...e)}});r.onSave&&(i.onSave=(...e)=>{var t,n;h.current=e[0],(t=(n=u.current).onSave)==null||t.call(n,...e)}),r.onBeforeRender&&(i.onBeforeRender=(...e)=>{var t,n,r;return(t=(n=(r=u.current).onBeforeRender)==null?void 0:n.call(r,...e))==null?e[0]:t}),r.onAfterRender&&(i.onAfterRender=(...e)=>{var t,n;(t=(n=u.current).onAfterRender)==null||t.call(n,...e)});let o=new t.Blok(i);return e.editor=o,v.current=r.data,a(o,n),o.isReady.then(()=>{e.editor===o&&!e.isDestroyed&&(e.isEditorReady=!0,l(o))}).catch(()=>{if(e.editor===o&&!e.isDestroyed){s(o);try{o.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.isEditorReady=!1,l(null)}}),()=>{y(e,l)}},[f]);let S=(0,n.normalizeReadOnlyConfig)(o.readOnly).enabled;(0,e.useEffect)(()=>{c!==null&&c.readOnly.set(S)},[c,S]);let{autofocus:C}=o;(0,e.useEffect)(()=>{c===null||!C||c.focus()},[c,C]);let{theme:w}=o;(0,e.useEffect)(()=>{c===null||w===void 0||c.theme.set(w)},[c,w]);let{width:T}=o;(0,e.useEffect)(()=>{c===null||T===void 0||c.width.set(T)},[c,T]);let{placeholder:E}=o;(0,e.useEffect)(()=>{c===null||E===void 0||c.placeholder.set(E)},[c,E]);let{data:D}=o;return(0,e.useEffect)(()=>{if(c===null||D===void 0)return;let e=d.current;if(!(e.editor!==c||e.isDestroyed)){if(g.current!==c&&(g.current=c,x.current=void 0,(0,n.deepEqual)(D,v.current))){h.current=D;return}(0,n.deepEqual)(D,h.current)||(0,n.deepEqual)(D,x.current)||(x.current=D,b.current=b.current.catch(()=>void 0).then(()=>c.render(D)).then(()=>{h.current=D},()=>{}).then(()=>{x.current===D&&(x.current=void 0)}))}},[c,D]),c}function y(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.isEditorReady=!1,e.destroyTimeout=null,t(null)}},0)}function b(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.includes(r))continue;n[r]=e[r]}return n}function x(e,t){if(e==null)return{};var n,r,i=b(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)n=a[r],t.includes(n)||{}.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var S=[`editor`],C=(0,e.forwardRef)(function(t,n){let{editor:i}=t,a=x(t,S),s=(0,e.useRef)(null);return(0,e.useEffect)(()=>{if(i===null||s.current===null)return;let e=o(i);if(e!==void 0)return s.current.appendChild(e),()=>{e.remove()}},[i]),(0,r.jsx)(`div`,p({ref:e=>{if(s.current=e,typeof n==`function`)n(e);else if(n!=null){let t=n;t.current=e}}},a))}),w=`autofocus.defaultBlock.dataModel.placeholder.sanitizer.hideToolbar.maxHistoryLength.historyDebounceTime.newGroupDelay.globalUndoRedo.tools.data.minHeight.logLevel.readOnly.i18n.link.linkPaste.onBeforePaste.onReady.onChange.onSave.onBeforeRender.onAfterRender.inlineToolbar.tunes.style.theme.onThemeChange.scrollToBlock.user.resolveUser.notifierPosition.notifier.width`.split(`.`),T=[`deps`,`onReady`,`onBlocksRendered`,`onBlockRendered`],E=new Set(w),D=(0,e.forwardRef)(function(t,n){let{deps:i,onReady:a,onBlocksRendered:o,onBlockRendered:s}=t,c=x(t,T),l={},u={};for(let e of Object.keys(c)){let t=c[e];E.has(e)?l[e]=t:u[e]=t}let d=v(l,i);(0,e.useImperativeHandle)(n,()=>d,[d]);let f=(0,e.useRef)(a);f.current=a,(0,e.useEffect)(()=>{if(d!==null){var e;(e=f.current)==null||e.call(f,d)}},[d]);let m=(0,e.useRef)(o);m.current=o;let h=(0,e.useRef)(s);h.current=s;let g=!!o,_=!!s;return(0,e.useEffect)(()=>{if(d===null)return;let e=[];if(g){let t=e=>{var t;return(t=m.current)==null?void 0:t.call(m,e)};d.on(`blocks:rendered`,t),e.push([`blocks:rendered`,t])}if(_){let t=e=>{var t;return(t=h.current)==null?void 0:t.call(h,e)};d.on(`block:rendered`,t),e.push([`block:rendered`,t])}return()=>{for(let[t,n]of e)d.off(t,n)}},[d,g,_]),(0,r.jsx)(C,p({editor:d},u))}),O=`block changed`;function k(t){let r=(0,e.useRef)(0),i=(0,e.useRef)(t);i.current!==t&&(i.current=t,r.current=0);let a=(0,e.useCallback)(e=>{if(t===null)return()=>void 0;let n=()=>{r.current+=1,e()};return t.on(O,n),()=>t.off(O,n)},[t]),o=(0,e.useCallback)(()=>r.current,[]);return(0,e.useSyncExternalStore)(a,o,o),(0,e.useMemo)(()=>t===null?n.EMPTY_API:(0,n.createBlocksApiForEditor)(t),[t])}exports.BlokContent=C,exports.BlokEditor=D,exports.BlokProvider=h,exports.useBlocks=k,exports.useBlok=v,exports.useBlokDefaults=g;
package/dist/index.mjs ADDED
@@ -0,0 +1,313 @@
1
+ import { createContext as e, forwardRef as t, useCallback as n, useContext as r, useEffect as i, useImperativeHandle as a, useMemo as o, useRef as s, useState as c, useSyncExternalStore as l } from "react";
2
+ import { Blok as u } from "@bloklabs/core";
3
+ import { EMPTY_API as d, createBlocksApiForEditor as f, deepEqual as p, normalizeReadOnlyConfig as m } from "@bloklabs/core/adapters";
4
+ import { jsx as h } from "react/jsx-runtime";
5
+ //#region src/holder-map.ts
6
+ var g = /* @__PURE__ */ new WeakMap();
7
+ function _(e, t) {
8
+ g.set(e, t);
9
+ }
10
+ function v(e) {
11
+ return g.get(e);
12
+ }
13
+ function y(e) {
14
+ g.delete(e);
15
+ }
16
+ //#endregion
17
+ //#region \0@oxc-project+runtime@0.126.0/helpers/typeof.js
18
+ function b(e) {
19
+ "@babel/helpers - typeof";
20
+ return b = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) {
21
+ return typeof e;
22
+ } : function(e) {
23
+ return e && typeof Symbol == "function" && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e;
24
+ }, b(e);
25
+ }
26
+ //#endregion
27
+ //#region \0@oxc-project+runtime@0.126.0/helpers/toPrimitive.js
28
+ function x(e, t) {
29
+ if (b(e) != "object" || !e) return e;
30
+ var n = e[Symbol.toPrimitive];
31
+ if (n !== void 0) {
32
+ var r = n.call(e, t || "default");
33
+ if (b(r) != "object") return r;
34
+ throw TypeError("@@toPrimitive must return a primitive value.");
35
+ }
36
+ return (t === "string" ? String : Number)(e);
37
+ }
38
+ //#endregion
39
+ //#region \0@oxc-project+runtime@0.126.0/helpers/toPropertyKey.js
40
+ function S(e) {
41
+ var t = x(e, "string");
42
+ return b(t) == "symbol" ? t : t + "";
43
+ }
44
+ //#endregion
45
+ //#region \0@oxc-project+runtime@0.126.0/helpers/defineProperty.js
46
+ function C(e, t, n) {
47
+ return (t = S(t)) in e ? Object.defineProperty(e, t, {
48
+ value: n,
49
+ enumerable: !0,
50
+ configurable: !0,
51
+ writable: !0
52
+ }) : e[t] = n, e;
53
+ }
54
+ //#endregion
55
+ //#region \0@oxc-project+runtime@0.126.0/helpers/objectSpread2.js
56
+ function w(e, t) {
57
+ var n = Object.keys(e);
58
+ if (Object.getOwnPropertySymbols) {
59
+ var r = Object.getOwnPropertySymbols(e);
60
+ t && (r = r.filter(function(t) {
61
+ return Object.getOwnPropertyDescriptor(e, t).enumerable;
62
+ })), n.push.apply(n, r);
63
+ }
64
+ return n;
65
+ }
66
+ function T(e) {
67
+ for (var t = 1; t < arguments.length; t++) {
68
+ var n = arguments[t] == null ? {} : arguments[t];
69
+ t % 2 ? w(Object(n), !0).forEach(function(t) {
70
+ C(e, t, n[t]);
71
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : w(Object(n)).forEach(function(t) {
72
+ Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t));
73
+ });
74
+ }
75
+ return e;
76
+ }
77
+ //#endregion
78
+ //#region src/provide-blok.tsx
79
+ var E = e({});
80
+ function D({ defaults: e, children: t }) {
81
+ return /* @__PURE__ */ h(E.Provider, {
82
+ value: e,
83
+ children: t
84
+ });
85
+ }
86
+ function O() {
87
+ return r(E);
88
+ }
89
+ function k(e, t) {
90
+ if (Object.keys(e).length === 0) return t;
91
+ let n = T({}, e);
92
+ for (let e of Object.keys(t)) {
93
+ let r = t[e];
94
+ r !== void 0 && (n[e] = r);
95
+ }
96
+ return (e.tools !== void 0 || t.tools !== void 0) && (n.tools = T(T({}, e.tools), t.tools)), n;
97
+ }
98
+ //#endregion
99
+ //#region src/useBlok.ts
100
+ function A(e, t) {
101
+ let n = k(r(E), e), [a, l] = c(null), d = s(n);
102
+ d.current = n;
103
+ let f = s({
104
+ editor: null,
105
+ holder: null,
106
+ destroyTimeout: null,
107
+ isDestroyed: !1,
108
+ isEditorReady: !1,
109
+ depsToken: null
110
+ }), h = o(() => ({}), t == null ? [] : t), g = s(n.data), v = s(null), b = s(n.data), x = s(Promise.resolve()), S = s(void 0);
111
+ i(() => {
112
+ if (typeof window > "u") return;
113
+ let e = f.current;
114
+ if (e.destroyTimeout !== null && (clearTimeout(e.destroyTimeout), e.destroyTimeout = null), e.editor !== null && !e.isDestroyed && e.depsToken === h) return e.isEditorReady && l(e.editor), () => {
115
+ j(e, l);
116
+ };
117
+ if (e.editor !== null && !e.isDestroyed) {
118
+ y(e.editor);
119
+ try {
120
+ e.editor.destroy();
121
+ } catch (e) {}
122
+ e.editor = null, e.holder = null, e.isDestroyed = !0, e.isEditorReady = !1, l(null);
123
+ }
124
+ let t = document.createElement("div");
125
+ e.holder = t, e.isDestroyed = !1, e.isEditorReady = !1, e.depsToken = h;
126
+ let n = d.current, r = T(T({}, n), {}, {
127
+ holder: t,
128
+ onReady: (...e) => {
129
+ var t, n;
130
+ (t = (n = d.current).onReady) == null || t.call(n, ...e);
131
+ },
132
+ onChange: (...e) => {
133
+ var t, n;
134
+ (t = (n = d.current).onChange) == null || t.call(n, ...e);
135
+ }
136
+ });
137
+ n.onSave && (r.onSave = (...e) => {
138
+ var t, n;
139
+ g.current = e[0], (t = (n = d.current).onSave) == null || t.call(n, ...e);
140
+ }), n.onBeforeRender && (r.onBeforeRender = (...e) => {
141
+ var t, n, r;
142
+ return (t = (n = (r = d.current).onBeforeRender) == null ? void 0 : n.call(r, ...e)) == null ? e[0] : t;
143
+ }), n.onAfterRender && (r.onAfterRender = (...e) => {
144
+ var t, n;
145
+ (t = (n = d.current).onAfterRender) == null || t.call(n, ...e);
146
+ });
147
+ let i = new u(r);
148
+ return e.editor = i, b.current = n.data, _(i, t), i.isReady.then(() => {
149
+ e.editor === i && !e.isDestroyed && (e.isEditorReady = !0, l(i));
150
+ }).catch(() => {
151
+ if (e.editor === i && !e.isDestroyed) {
152
+ y(i);
153
+ try {
154
+ i.destroy();
155
+ } catch (e) {}
156
+ e.editor = null, e.holder = null, e.isDestroyed = !0, e.isEditorReady = !1, l(null);
157
+ }
158
+ }), () => {
159
+ j(e, l);
160
+ };
161
+ }, [h]);
162
+ let C = m(n.readOnly).enabled;
163
+ i(() => {
164
+ a !== null && a.readOnly.set(C);
165
+ }, [a, C]);
166
+ let { autofocus: w } = n;
167
+ i(() => {
168
+ a === null || !w || a.focus();
169
+ }, [a, w]);
170
+ let { theme: D } = n;
171
+ i(() => {
172
+ a === null || D === void 0 || a.theme.set(D);
173
+ }, [a, D]);
174
+ let { width: O } = n;
175
+ i(() => {
176
+ a === null || O === void 0 || a.width.set(O);
177
+ }, [a, O]);
178
+ let { placeholder: A } = n;
179
+ i(() => {
180
+ a === null || A === void 0 || a.placeholder.set(A);
181
+ }, [a, A]);
182
+ let { data: M } = n;
183
+ return i(() => {
184
+ if (a === null || M === void 0) return;
185
+ let e = f.current;
186
+ if (!(e.editor !== a || e.isDestroyed)) {
187
+ if (v.current !== a && (v.current = a, S.current = void 0, p(M, b.current))) {
188
+ g.current = M;
189
+ return;
190
+ }
191
+ p(M, g.current) || p(M, S.current) || (S.current = M, x.current = x.current.catch(() => void 0).then(() => a.render(M)).then(() => {
192
+ g.current = M;
193
+ }, () => {}).then(() => {
194
+ S.current === M && (S.current = void 0);
195
+ }));
196
+ }
197
+ }, [a, M]), a;
198
+ }
199
+ function j(e, t) {
200
+ e.destroyTimeout = setTimeout(() => {
201
+ if (e.editor !== null) {
202
+ y(e.editor);
203
+ try {
204
+ e.editor.destroy();
205
+ } catch (e) {}
206
+ e.editor = null, e.holder = null, e.isDestroyed = !0, e.isEditorReady = !1, e.destroyTimeout = null, t(null);
207
+ }
208
+ }, 0);
209
+ }
210
+ //#endregion
211
+ //#region \0@oxc-project+runtime@0.126.0/helpers/objectWithoutPropertiesLoose.js
212
+ function M(e, t) {
213
+ if (e == null) return {};
214
+ var n = {};
215
+ for (var r in e) if ({}.hasOwnProperty.call(e, r)) {
216
+ if (t.includes(r)) continue;
217
+ n[r] = e[r];
218
+ }
219
+ return n;
220
+ }
221
+ //#endregion
222
+ //#region \0@oxc-project+runtime@0.126.0/helpers/objectWithoutProperties.js
223
+ function N(e, t) {
224
+ if (e == null) return {};
225
+ var n, r, i = M(e, t);
226
+ if (Object.getOwnPropertySymbols) {
227
+ var a = Object.getOwnPropertySymbols(e);
228
+ for (r = 0; r < a.length; r++) n = a[r], t.includes(n) || {}.propertyIsEnumerable.call(e, n) && (i[n] = e[n]);
229
+ }
230
+ return i;
231
+ }
232
+ //#endregion
233
+ //#region src/BlokContent.tsx
234
+ var P = ["editor"], F = t(function(e, t) {
235
+ let { editor: n } = e, r = N(e, P), a = s(null);
236
+ return i(() => {
237
+ if (n === null || a.current === null) return;
238
+ let e = v(n);
239
+ if (e !== void 0) return a.current.appendChild(e), () => {
240
+ e.remove();
241
+ };
242
+ }, [n]), /* @__PURE__ */ h("div", T({ ref: (e) => {
243
+ if (a.current = e, typeof t == "function") t(e);
244
+ else if (t != null) {
245
+ let n = t;
246
+ n.current = e;
247
+ }
248
+ } }, r));
249
+ }), I = /* @__PURE__ */ "autofocus.defaultBlock.dataModel.placeholder.sanitizer.hideToolbar.maxHistoryLength.historyDebounceTime.newGroupDelay.globalUndoRedo.tools.data.minHeight.logLevel.readOnly.i18n.link.linkPaste.onBeforePaste.onReady.onChange.onSave.onBeforeRender.onAfterRender.inlineToolbar.tunes.style.theme.onThemeChange.scrollToBlock.user.resolveUser.notifierPosition.notifier.width".split("."), L = [
250
+ "deps",
251
+ "onReady",
252
+ "onBlocksRendered",
253
+ "onBlockRendered"
254
+ ], R = new Set(I), z = t(function(e, t) {
255
+ let { deps: n, onReady: r, onBlocksRendered: o, onBlockRendered: c } = e, l = N(e, L), u = {}, d = {};
256
+ for (let e of Object.keys(l)) {
257
+ let t = l[e];
258
+ R.has(e) ? u[e] = t : d[e] = t;
259
+ }
260
+ let f = A(u, n);
261
+ a(t, () => f, [f]);
262
+ let p = s(r);
263
+ p.current = r, i(() => {
264
+ if (f !== null) {
265
+ var e;
266
+ (e = p.current) == null || e.call(p, f);
267
+ }
268
+ }, [f]);
269
+ let m = s(o);
270
+ m.current = o;
271
+ let g = s(c);
272
+ g.current = c;
273
+ let _ = !!o, v = !!c;
274
+ return i(() => {
275
+ if (f === null) return;
276
+ let e = [];
277
+ if (_) {
278
+ let t = (e) => {
279
+ var t;
280
+ return (t = m.current) == null ? void 0 : t.call(m, e);
281
+ };
282
+ f.on("blocks:rendered", t), e.push(["blocks:rendered", t]);
283
+ }
284
+ if (v) {
285
+ let t = (e) => {
286
+ var t;
287
+ return (t = g.current) == null ? void 0 : t.call(g, e);
288
+ };
289
+ f.on("block:rendered", t), e.push(["block:rendered", t]);
290
+ }
291
+ return () => {
292
+ for (let [t, n] of e) f.off(t, n);
293
+ };
294
+ }, [
295
+ f,
296
+ _,
297
+ v
298
+ ]), /* @__PURE__ */ h(F, T({ editor: f }, d));
299
+ }), B = "block changed";
300
+ function V(e) {
301
+ let t = s(0), r = s(e);
302
+ r.current !== e && (r.current = e, t.current = 0);
303
+ let i = n((n) => {
304
+ if (e === null) return () => void 0;
305
+ let r = () => {
306
+ t.current += 1, n();
307
+ };
308
+ return e.on(B, r), () => e.off(B, r);
309
+ }, [e]), a = n(() => t.current, []);
310
+ return l(i, a, a), o(() => e === null ? d : f(e), [e]);
311
+ }
312
+ //#endregion
313
+ export { F as BlokContent, z as BlokEditor, D as BlokProvider, V as useBlocks, A as useBlok, O as useBlokDefaults };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@bloklabs/react",
3
+ "version": "1.1.1",
4
+ "description": "React bindings for the Blok editor",
5
+ "license": "Apache-2.0",
6
+ "author": "JackUait",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/JackUait/blok.git",
10
+ "directory": "packages/react"
11
+ },
12
+ "type": "module",
13
+ "sideEffects": false,
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.mjs",
16
+ "types": "./types/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./types/index.d.ts",
20
+ "import": "./dist/index.mjs",
21
+ "require": "./dist/index.cjs"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "types",
27
+ "src"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "build": "yarn run -T vite build --mode production"
34
+ },
35
+ "peerDependencies": {
36
+ "@bloklabs/core": "^1.1.1",
37
+ "react": "^18.0.0 || ^19.0.0",
38
+ "react-dom": "^18.0.0 || ^19.0.0"
39
+ }
40
+ }
@@ -0,0 +1,46 @@
1
+ import { useEffect, useRef, forwardRef } from 'react';
2
+ import { getHolder } from './holder-map';
3
+ import type { BlokContentProps } from './types';
4
+
5
+ /**
6
+ * Component that provides the DOM mount point for a Blok editor.
7
+ * Renders a <div> and adopts the editor's detached holder DOM into it.
8
+ * When editor is null, renders an empty div (for layout stability).
9
+ * Passes through all standard HTML div attributes (className, style, etc.).
10
+ */
11
+ export const BlokContent = forwardRef<HTMLDivElement, BlokContentProps>(
12
+ function BlokContent({ editor, ...divProps }, ref) {
13
+ const containerRef = useRef<HTMLDivElement | null>(null);
14
+
15
+ const setRefs = (node: HTMLDivElement | null): void => {
16
+ containerRef.current = node;
17
+ if (typeof ref === 'function') {
18
+ ref(node);
19
+ } else if (ref !== null && ref !== undefined) {
20
+ const mutableRef = ref;
21
+
22
+ mutableRef.current = node;
23
+ }
24
+ };
25
+
26
+ useEffect(() => {
27
+ if (editor === null || containerRef.current === null) {
28
+ return;
29
+ }
30
+
31
+ const holder = getHolder(editor);
32
+
33
+ if (holder === undefined) {
34
+ return;
35
+ }
36
+
37
+ containerRef.current.appendChild(holder);
38
+
39
+ return (): void => {
40
+ holder.remove();
41
+ };
42
+ }, [editor]);
43
+
44
+ return <div ref={setRefs} {...divProps} />;
45
+ }
46
+ );
@@ -0,0 +1,157 @@
1
+ import {
2
+ forwardRef,
3
+ useImperativeHandle,
4
+ useEffect,
5
+ useRef,
6
+ type DependencyList,
7
+ type HTMLAttributes,
8
+ } from 'react';
9
+ import { useBlok } from './useBlok';
10
+ import { BlokContent } from './BlokContent';
11
+ import { USE_BLOK_CONFIG_KEYS } from './config-keys';
12
+ import type { Blok, BlockRenderedPayload, BlocksRenderedPayload } from '@/types';
13
+ import type { UseBlokConfig } from './types';
14
+
15
+ /**
16
+ * Props for the all-in-one BlokEditor component.
17
+ * Accepts every useBlok config prop (except `onReady`, re-typed below to receive
18
+ * the ready instance) AND every standard HTML div attribute, which is forwarded
19
+ * to the editor container element (mirrors `BlokContent`). `style` and `onChange`
20
+ * keep their editor-config meaning (they collide with div attributes of the same
21
+ * name), so the container is styled via `className` rather than an inline `style`.
22
+ *
23
+ * `data` is reactive (controlled-ish): it seeds the INITIAL content and, after
24
+ * mount, changing it to new *content* re-renders the editor via `render()` — no
25
+ * recreation. Updates are deep-equal–deduped (a new reference with identical
26
+ * content is a no-op, so it won't clobber the caret) and serialized (rapid
27
+ * changes can't overlap). For ad-hoc reloads you can still call
28
+ * `ref.current.render(newData)`.
29
+ *
30
+ * Pair `data` with `onSave` for a true controlled component: `onSave` is the
31
+ * output half, firing (debounced) with the full serialized `OutputData` on every
32
+ * content change — no manual `ref.current.save()` polling. Echoing that payload
33
+ * straight back via `onSave={setData}` is safe and caret-stable: the adapter
34
+ * records the editor's own emitted output as the content baseline, so the
35
+ * `onSave → setState → data` round-trip deep-equals that baseline and is deduped
36
+ * to a no-op (no re-render, no caret reset, no recursion). Genuine external `data`
37
+ * changes still re-render in place.
38
+ */
39
+ export interface BlokEditorProps
40
+ extends Omit<UseBlokConfig, 'onReady'>,
41
+ Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'onChange'> {
42
+ /**
43
+ * When any value changes, the editor is destroyed and recreated. Keep each
44
+ * value referentially stable (primitives or useMemo-stable objects) — a dep
45
+ * whose identity changes every render recreates the editor each time.
46
+ */
47
+ deps?: DependencyList;
48
+ /** Test id forwarded to the editor container element (via data-testid). */
49
+ 'data-testid'?: string;
50
+ /**
51
+ * Called once the editor is ready, with the live Blok instance. Fires after the
52
+ * forwarded ref is committed, so `ref.current` is also populated at this point.
53
+ */
54
+ onReady?: (editor: Blok) => void;
55
+ /**
56
+ * Called after a batch render completes (core `blocks:rendered` event). The
57
+ * declarative analog of `ref.current.on('blocks:rendered', …)` — mirrors the
58
+ * Vue/Angular adapters' rendered-lifecycle outputs.
59
+ */
60
+ onBlocksRendered?: (payload: BlocksRenderedPayload) => void;
61
+ /** Called for each block rendered into the DOM (core `block:rendered` event). */
62
+ onBlockRendered?: (payload: BlockRenderedPayload) => void;
63
+ }
64
+
65
+ /** Fast membership test for partitioning props into editor config vs. div attributes. */
66
+ const CONFIG_KEY_SET = new Set<string>(USE_BLOK_CONFIG_KEYS);
67
+
68
+ /**
69
+ * The recommended way to embed Blok in React. Internally wires `useBlok` and
70
+ * `BlokContent`, and forwards a ref to the live `Blok` instance.
71
+ *
72
+ * Don't wrap this component in `styled()` or any HOC that reserves the `theme`
73
+ * prop — styled-components claims `theme` for its own `ThemeProvider`, so it
74
+ * never reaches the editor and theme sync silently breaks. Render it directly
75
+ * and style the container via `className`.
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * const ref = useRef<Blok | null>(null);
80
+ * <BlokEditor ref={ref} id="editor" tools={tools} data={data} theme={theme} onReady={(e) => e.focus()} />;
81
+ * ```
82
+ */
83
+ export const BlokEditor = forwardRef<Blok | null, BlokEditorProps>(
84
+ function BlokEditor({ deps, onReady, onBlocksRendered, onBlockRendered, ...rest }, ref) {
85
+ const config: Record<string, unknown> = {};
86
+ const divProps: Record<string, unknown> = {};
87
+
88
+ for (const key of Object.keys(rest)) {
89
+ const value = (rest as Record<string, unknown>)[key];
90
+
91
+ if (CONFIG_KEY_SET.has(key)) {
92
+ config[key] = value;
93
+ } else {
94
+ divProps[key] = value;
95
+ }
96
+ }
97
+
98
+ const editor = useBlok(config as UseBlokConfig, deps);
99
+
100
+ useImperativeHandle<Blok | null, Blok | null>(ref, () => editor, [editor]);
101
+
102
+ // Own onReady here instead of forwarding it to the core config: the core
103
+ // onReady fires before useImperativeHandle commits the ref. This passive
104
+ // effect runs AFTER the commit, so both the instance argument and ref.current
105
+ // are reliably populated.
106
+ const onReadyRef = useRef(onReady);
107
+ onReadyRef.current = onReady;
108
+ useEffect(() => {
109
+ if (editor !== null) {
110
+ onReadyRef.current?.(editor);
111
+ }
112
+ }, [editor]);
113
+
114
+ // Subscribe to the rendered-lifecycle events (the declarative analog of the
115
+ // Vue/Angular outputs). Latest handlers are read through refs so a new
116
+ // callback identity never resubscribes; presence booleans in the deps add or
117
+ // drop subscriptions when a handler appears/disappears.
118
+ const onBlocksRenderedRef = useRef(onBlocksRendered);
119
+ onBlocksRenderedRef.current = onBlocksRendered;
120
+ const onBlockRenderedRef = useRef(onBlockRendered);
121
+ onBlockRenderedRef.current = onBlockRendered;
122
+
123
+ const hasBlocksRendered = Boolean(onBlocksRendered);
124
+ const hasBlockRendered = Boolean(onBlockRendered);
125
+ useEffect(() => {
126
+ if (editor === null) {
127
+ return;
128
+ }
129
+
130
+ const subscriptions: Array<[string, (payload?: unknown) => void]> = [];
131
+
132
+ if (hasBlocksRendered) {
133
+ const handler = (payload?: unknown): void =>
134
+ onBlocksRenderedRef.current?.(payload as BlocksRenderedPayload);
135
+
136
+ editor.on('blocks:rendered', handler);
137
+ subscriptions.push(['blocks:rendered', handler]);
138
+ }
139
+
140
+ if (hasBlockRendered) {
141
+ const handler = (payload?: unknown): void =>
142
+ onBlockRenderedRef.current?.(payload as BlockRenderedPayload);
143
+
144
+ editor.on('block:rendered', handler);
145
+ subscriptions.push(['block:rendered', handler]);
146
+ }
147
+
148
+ return (): void => {
149
+ for (const [name, handler] of subscriptions) {
150
+ editor.off(name, handler);
151
+ }
152
+ };
153
+ }, [editor, hasBlocksRendered, hasBlockRendered]);
154
+
155
+ return <BlokContent editor={editor} {...(divProps as HTMLAttributes<HTMLDivElement>)} />;
156
+ }
157
+ );
@@ -0,0 +1,27 @@
1
+ // packages/react/src/blocks-snapshot.ts
2
+ //
3
+ // The block-tree connection types and pure resolution helpers are
4
+ // framework-agnostic and now live in a shared core module so the React, Vue,
5
+ // and Angular adapters — and core's own per-block `BlockAPI` — resolve flat
6
+ // insert/move indices through ONE battle-tested implementation (the parity
7
+ // requirement: author once, reuse everywhere). This file re-exports them so
8
+ // existing React imports (`./blocks-snapshot`) keep working unchanged.
9
+ export {
10
+ snapshotNodes,
11
+ resolveInsertIndex,
12
+ resolveMoveIndex,
13
+ parentMap,
14
+ isDescendantOf,
15
+ } from '@bloklabs/core/adapters';
16
+
17
+ export type {
18
+ BlockNode,
19
+ CaretTarget,
20
+ InsertPosition,
21
+ InsertSpec,
22
+ TreeInsertSpec,
23
+ MoveTarget,
24
+ BlocksReader,
25
+ IndexReader,
26
+ UseBlocksApi,
27
+ } from '@bloklabs/core/adapters';
@@ -0,0 +1,56 @@
1
+ import type { UseBlokConfig } from './types';
2
+
3
+ /**
4
+ * Keys that belong to the editor config (consumed by `useBlok`). Any prop on
5
+ * `BlokEditor` whose key is NOT in this set is treated as a container div
6
+ * attribute and spread onto the underlying element (mirrors `BlokContent`).
7
+ *
8
+ * Keep in sync with `UseBlokConfig`. The two compile-time guards below fail the
9
+ * `yarn lint:types` typecheck if this list drifts.
10
+ */
11
+ export const USE_BLOK_CONFIG_KEYS = [
12
+ 'autofocus',
13
+ 'defaultBlock',
14
+ 'dataModel',
15
+ 'placeholder',
16
+ 'sanitizer',
17
+ 'hideToolbar',
18
+ 'maxHistoryLength',
19
+ 'historyDebounceTime',
20
+ 'newGroupDelay',
21
+ 'globalUndoRedo',
22
+ 'tools',
23
+ 'data',
24
+ 'minHeight',
25
+ 'logLevel',
26
+ 'readOnly',
27
+ 'i18n',
28
+ 'link',
29
+ 'linkPaste',
30
+ 'onBeforePaste',
31
+ 'onReady',
32
+ 'onChange',
33
+ 'onSave',
34
+ 'onBeforeRender',
35
+ 'onAfterRender',
36
+ 'inlineToolbar',
37
+ 'tunes',
38
+ 'style',
39
+ 'theme',
40
+ 'onThemeChange',
41
+ 'scrollToBlock',
42
+ 'user',
43
+ 'resolveUser',
44
+ 'notifierPosition',
45
+ 'notifier',
46
+ 'width',
47
+ ] as const satisfies readonly (keyof UseBlokConfig)[];
48
+
49
+ /**
50
+ * Compile error if a `UseBlokConfig` key is missing from the list above.
51
+ * (`satisfies` only proves each entry is a valid key; this proves coverage.)
52
+ */
53
+ type _MissingConfigKey = Exclude<keyof UseBlokConfig, (typeof USE_BLOK_CONFIG_KEYS)[number]>;
54
+ const _exhaustive: _MissingConfigKey extends never ? true : never = true;
55
+
56
+ void _exhaustive;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Re-export of the framework-agnostic deep equality used to dedupe reactive
3
+ * `data` updates. The implementation now lives in `src/shared/deep-equal.ts`
4
+ * (shared with the Angular adapter); this module is kept for back-compat with
5
+ * existing `./deep-equal` importers in the React adapter.
6
+ */
7
+ export { deepEqual } from '@bloklabs/core/adapters';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Module-level WeakMap associating editor instances with their detached holder divs.
3
+ * WeakMap ensures no memory leaks — when the editor is garbage collected, the holder reference is released.
4
+ */
5
+ const holders = new WeakMap<WeakKey, HTMLDivElement>();
6
+
7
+ export function setHolder(editor: WeakKey, holder: HTMLDivElement): void {
8
+ holders.set(editor, holder);
9
+ }
10
+
11
+ export function getHolder(editor: WeakKey): HTMLDivElement | undefined {
12
+ return holders.get(editor);
13
+ }
14
+
15
+ export function removeHolder(editor: WeakKey): void {
16
+ holders.delete(editor);
17
+ }