@bloklabs/react 1.2.2 → 1.2.4

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 CHANGED
@@ -1 +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`),i=require(`react-dom`);var a=new WeakMap;function o(e,t){a.set(e,t)}function s(e){return a.get(e)}function c(e){a.delete(e)}function l(e){"@babel/helpers - typeof";return l=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},l(e)}function u(e,t){if(l(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(l(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function d(e){var t=u(e,`string`);return l(t)==`symbol`?t:t+``}function f(e,t,n){return(t=d(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function p(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 m(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?p(Object(n),!0).forEach(function(t){f(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):p(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}var h=(0,e.createContext)({});function g({defaults:e,children:t}){return(0,r.jsx)(h.Provider,{value:e,children:t})}function _(){return(0,e.useContext)(h)}function v(e,t){if(Object.keys(e).length===0)return t;let n=m({},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=m(m({},e.tools),t.tools)),n}var y=`__blokPortalRegistry`,b=()=>{let e={snapshot:new Map},t=new Set,n=n=>{e.snapshot=n,t.forEach(e=>e())};return{register(t,r){let i=new Map(e.snapshot);i.set(t,r),n(i)},unregister(t){if(!e.snapshot.has(t))return;let r=new Map(e.snapshot);r.delete(t),n(r)},setProps(t,r){let i=e.snapshot.get(t);if(i===void 0)return;let a=new Map(e.snapshot);a.set(t,m(m({},i),{},{props:m(m({},i.props),r)})),n(a)},subscribe(e){return t.add(e),()=>{t.delete(e)}},getSnapshot(){return e.snapshot}}},x=new WeakMap;function S(e,t){x.set(e,t)}function C(e){return x.get(e)}function w(e){x.delete(e)}var T=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},E=(e,t)=>t.reduce((e,t)=>{if(Array.isArray(e))return typeof t==`number`?e[t]:void 0;if(T(e))return e[t]},e),D=(e,t)=>{if(!T(e))return e;let n={};for(let[r,i]of Object.entries(e)){if(!T(i)||!T(i.config)){n[r]=i;continue}let e=new WeakSet,a=(n,i)=>{if(typeof n==`function`){let e=n;return function(...n){let a=t(),o=T(a)?a[r]:void 0,s=E(T(o)?o.config:void 0,i);return Reflect.apply(typeof s==`function`?s:e,this,n)}}if(Array.isArray(n))return e.has(n)?n:(e.add(n),n.map((e,t)=>a(e,[...i,t])));if(T(n)){if(e.has(n))return n;e.add(n);let t={};for(let[e,r]of Object.entries(n))t[e]=a(r,[...i,e]);return t}return n};n[r]=m(m({},i),{},{config:a(i.config,[])})}return n},O=(e,t)=>{if(typeof e!=`object`||!e)return e;let n={};for(let[i,a]of Object.entries(e)){var r;let e=typeof a==`function`?a:a==null?void 0:a.class;if(!(typeof e==`function`&&e.__isBlokReactBlock===!0)){n[i]=a;continue}let o=typeof a==`function`?{class:a}:m({},a);o.config=m(m({},(r=o.config)==null?{}:r),{},{[y]:t}),n[i]=o}return n};function k(r,i){let a=v((0,e.useContext)(h),r),[s,l]=(0,e.useState)(null),u=(0,e.useRef)(a);u.current=a;let d=(0,e.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,isEditorReady:!1,depsToken:null}),f=(0,e.useMemo)(()=>({}),i==null?[]:i),p=(0,e.useRef)(a.data),g=(0,e.useRef)(null),_=(0,e.useRef)(a.data),y=(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),()=>{A(e,l)};if(e.editor!==null&&!e.isDestroyed){c(e.editor),w(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=b(),a=r.tools===void 0?void 0:O(D(r.tools,()=>u.current.tools),i),s=m(m(m({},r),a===void 0?{}:{tools:a}),{},{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&&(s.onSave=(...e)=>{var t,n;p.current=e[0],(t=(n=u.current).onSave)==null||t.call(n,...e)}),r.onBeforeRender&&(s.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&&(s.onAfterRender=(...e)=>{var t,n;(t=(n=u.current).onAfterRender)==null||t.call(n,...e)});let h=new t.Blok(s);return e.editor=h,_.current=r.data,o(h,n),S(h,i),h.isReady.then(()=>{e.editor===h&&!e.isDestroyed&&(e.isEditorReady=!0,l(h))}).catch(()=>{if(e.editor===h&&!e.isDestroyed){c(h),w(h);try{h.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.isEditorReady=!1,l(null)}}),()=>{A(e,l)}},[f]);let C=(0,n.normalizeReadOnlyConfig)(a.readOnly).enabled;(0,e.useEffect)(()=>{s!==null&&s.readOnly.set(C)},[s,C]);let{autofocus:T}=a;(0,e.useEffect)(()=>{s===null||!T||s.focus()},[s,T]);let{theme:E}=a;(0,e.useEffect)(()=>{s===null||E===void 0||s.theme.set(E)},[s,E]);let{width:k}=a;(0,e.useEffect)(()=>{s===null||k===void 0||s.width.set(k)},[s,k]);let{placeholder:j}=a;(0,e.useEffect)(()=>{s===null||j===void 0||s.placeholder.set(j)},[s,j]);let{data:M}=a;return(0,e.useEffect)(()=>{if(s===null||M===void 0)return;let e=d.current;if(!(e.editor!==s||e.isDestroyed)){if(g.current!==s&&(g.current=s,x.current=void 0,(0,n.deepEqual)(M,_.current))){p.current=M;return}(0,n.deepEqual)(M,p.current)||(0,n.deepEqual)(M,x.current)||(x.current=M,y.current=y.current.catch(()=>void 0).then(()=>s.render(M)).then(()=>{p.current=M},()=>{}).then(()=>{x.current===M&&(x.current=void 0)}))}},[s,M]),s}function A(e,t){e.destroyTimeout=setTimeout(()=>{if(e.editor!==null){c(e.editor),w(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)}var j=class extends e.Component{constructor(...e){super(...e),this.state={failed:!1}}static getDerivedStateFromError(){return{failed:!0}}componentDidCatch(){}render(){return this.state.failed?null:this.props.children}};function M({registry:t}){let n=(0,e.useSyncExternalStore)(t.subscribe,t.getSnapshot,t.getSnapshot);return(0,r.jsx)(r.Fragment,{children:Array.from(n.entries()).map(([e,t])=>{let n=t.component;return(0,i.createPortal)((0,r.jsx)(j,{children:(0,r.jsx)(n,m({},t.props))}),t.hostEl,e)})})}function N(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 P(e,t){if(e==null)return{};var n,r,i=N(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 F=[`editor`],I=(0,e.forwardRef)(function(t,n){let{editor:i}=t,a=P(t,F),o=(0,e.useRef)(null),c=e=>{if(o.current=e,typeof n==`function`)n(e);else if(n!=null){let t=n;t.current=e}};(0,e.useEffect)(()=>{if(i===null||o.current===null)return;let e=s(i);if(e!==void 0)return o.current.appendChild(e),()=>{e.remove()}},[i]);let l=i===null?void 0:C(i);return(0,r.jsx)(`div`,m(m({ref:c},a),{},{children:l===void 0?null:(0,r.jsx)(M,{registry:l})}))}),L=`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(`.`),R=[`deps`,`onReady`,`onBlocksRendered`,`onBlockRendered`],z=new Set(L),B=(0,e.forwardRef)(function(t,n){let{deps:i,onReady:a,onBlocksRendered:o,onBlockRendered:s}=t,c=P(t,R),l={},u={};for(let e of Object.keys(c)){let t=c[e];z.has(e)?l[e]=t:u[e]=t}let d=k(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 p=(0,e.useRef)(o);p.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=p.current)==null?void 0:t.call(p,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)(I,m({editor:d},u))}),V=`block changed`;function H(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(V,n),()=>t.off(V,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])}function U(t){var a;return a=class{static get toolbox(){return t.toolbox}static get isReadOnlySupported(){return!0}constructor(i){var a,o;this.hostEl=null,this.pendingDispatch=!1,this.commit=e=>{var r;let i=(0,n.fillDefaults)(t.propSchema,m(m({},this.mirror),e));this.mirror=i,this.lastRendered=i,(r=this.registry)==null||r.setProps(this.blockApi.id,{data:i}),this.flushDispatch()},this.blockApi=i.block;let s=(a=i.config)==null?{}:a;this.registry=s[y];let c=i.api;this.pointerDrag=()=>{var e;return(c==null||(e=c.blocks)==null?void 0:e.isPointerDragActive)===!0},this.mirror=(0,n.fillDefaults)(t.propSchema,(o=i.data)==null?{}:o),this.lastRendered=this.mirror,this.readOnly=i.readOnly;let l=this.blockApi;this.childrenComponent=function(){let t=(0,e.useRef)(null);return(0,e.useEffect)(()=>{t.current!==null&&(0,n.mountChildBlocks)(t.current,l.getChildren())}),(0,r.jsx)(`div`,{ref:t,[n.DATA_ATTR.nestedBlocks]:``})}}render(){var e;let n=document.createElement(`div`);return n.setAttribute(`data-blok-mutation-free`,`true`),this.hostEl=n,(e=this.registry)==null||e.register(this.blockApi.id,{hostEl:n,component:t.component,props:this.buildProps()}),n}rendered(){var e;(e=t.onRendered)==null||e.call(t,this.blockApi)}save(){return this.mirror}async setData(e){var r=this;let i=(0,n.fillDefaults)(t.propSchema,e==null?{}:e);return(0,n.deepEqual)(i,r.lastRendered)?!0:(r.mirror=i,r.lastRendered=i,r.pushProps({data:i}),!0)}setReadOnly(e){this.readOnly=e,this.pushProps({readOnly:e})}moved(){var e;(e=t.onMoved)==null||e.call(t,this.blockApi)}removed(){var e,n;(e=this.registry)==null||e.unregister(this.blockApi.id),(n=t.onRemoved)==null||n.call(t,this.blockApi)}destroy(){var e;(e=this.registry)==null||e.unregister(this.blockApi.id)}buildProps(){return{data:this.mirror,commit:this.commit,block:this.blockApi,readOnly:this.readOnly,BlockChildren:this.childrenComponent}}pushProps(e){let t=this.registry;t!==void 0&&(0,i.flushSync)(()=>{t.setProps(this.blockApi.id,e)})}flushDispatch(){if(!this.pointerDrag()){this.pendingDispatch=!1,this.blockApi.dispatchChange();return}if(this.pendingDispatch)return;this.pendingDispatch=!0;let e=()=>{if(this.pointerDrag()){requestAnimationFrame(e);return}this.pendingDispatch=!1,this.blockApi.dispatchChange()};requestAnimationFrame(e)}},a.__isBlokReactBlock=!0,a}exports.BLOK_PORTAL_REGISTRY_CONFIG_KEY=y,exports.BlockPortalHost=M,exports.BlokContent=I,exports.BlokEditor=B,exports.BlokProvider=g,exports.createBlockPortalRegistry=b,exports.createReactBlock=U,exports.useBlocks=H,exports.useBlok=k,exports.useBlokDefaults=_;
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`),i=require(`react-dom`);var a=new WeakMap;function o(e,t){a.set(e,t)}function s(e){return a.get(e)}function c(e){a.delete(e)}function l(e){"@babel/helpers - typeof";return l=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},l(e)}function u(e,t){if(l(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(l(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function d(e){var t=u(e,`string`);return l(t)==`symbol`?t:t+``}function f(e,t,n){return(t=d(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function p(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 m(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?p(Object(n),!0).forEach(function(t){f(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):p(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}var h=(0,e.createContext)({});function g({defaults:e,children:t}){return(0,r.jsx)(h.Provider,{value:e,children:t})}function _(){return(0,e.useContext)(h)}function v(e,t){if(Object.keys(e).length===0)return t;let n=m({},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=m(m({},e.tools),t.tools)),n}var y=`__blokPortalRegistry`,b=`__blokToolName`,x=()=>{let e={snapshot:new Map},t=new Set,n=new Map,r=n=>{e.snapshot=n,t.forEach(e=>e())};return{register(t,i){let a=new Map(e.snapshot),o=i.toolName===void 0?void 0:n.get(i.toolName);a.set(t,o===void 0?i:m(m({},i),{},{props:m(m({},i.props),{},{config:o})})),r(a)},unregister(t){if(!e.snapshot.has(t))return;let n=new Map(e.snapshot);n.delete(t),r(n)},setProps(t,n){let i=e.snapshot.get(t);if(i===void 0)return;let a=new Map(e.snapshot);a.set(t,m(m({},i),{},{props:m(m({},i.props),n)})),r(a)},setToolConfig(t,i){n.set(t,i);let a=[...e.snapshot].filter(([,e])=>e.toolName===t);if(a.length===0)return;let o=new Map(e.snapshot);for(let[e,t]of a)o.set(e,m(m({},t),{},{props:m(m({},t.props),{},{config:i})}));r(o)},subscribe(e){return t.add(e),()=>{t.delete(e)}},getSnapshot(){return e.snapshot}}},S=new WeakMap;function C(e,t){S.set(e,t)}function w(e){return S.get(e)}function T(e){S.delete(e)}var E=e=>{if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},D=(e,t)=>t.reduce((e,t)=>{if(Array.isArray(e))return typeof t==`number`?e[t]:void 0;if(E(e))return e[t]},e),O=(e,t)=>{let n=(e,t)=>{if(typeof e==`function`)return typeof t==`function`?t:e;if(Array.isArray(e)){let r=Array.isArray(t)?t:[];return e.map((e,t)=>n(e,r[t]))}if(E(e)){let r=E(t)?t:{},i={};for(let[t,a]of Object.entries(e))i[t]=n(a,r[t]);return i}return e},r=n(t,e);return E(r)?r:{}},k=(e,t)=>{if(!E(e))return e;let n={};for(let[r,i]of Object.entries(e)){if(!E(i)||!E(i.config)){n[r]=i;continue}let e=new WeakSet,a=(n,i)=>{if(typeof n==`function`){let e=n;return function(...n){let a=t(),o=E(a)?a[r]:void 0,s=D(E(o)?o.config:void 0,i);return Reflect.apply(typeof s==`function`?s:e,this,n)}}if(Array.isArray(n))return e.has(n)?n:(e.add(n),n.map((e,t)=>a(e,[...i,t])));if(E(n)){if(e.has(n))return n;e.add(n);let t={};for(let[e,r]of Object.entries(n))t[e]=a(r,[...i,e]);return t}return n};n[r]=m(m({},i),{},{config:a(i.config,[])})}return n},A=e=>{let t=typeof e==`function`?e:e==null?void 0:e.class;return typeof t==`function`&&t.__isBlokReactBlock===!0},j=(e,t)=>{if(typeof e!=`object`||!e)return e;let n={};for(let[i,a]of Object.entries(e)){var r;if(!A(a)){n[i]=a;continue}let e=typeof a==`function`?{class:a}:m({},a);e.config=m(m({},(r=e.config)==null?{}:r),{},{[y]:t,[b]:i}),n[i]=e}return n};function M(r,i){let a=v((0,e.useContext)(h),r),[s,l]=(0,e.useState)(null),u=(0,e.useRef)(a);u.current=a;let d=(0,e.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,isEditorReady:!1,depsToken:null}),f=(0,e.useMemo)(()=>({}),i==null?[]:i),p=(0,e.useRef)(a.data),g=(0,e.useRef)(null),_=(0,e.useRef)(a.data),y=(0,e.useRef)(Promise.resolve()),b=(0,e.useRef)(void 0),S=(0,e.useRef)(null),w=(0,e.useRef)(void 0),E=(0,e.useRef)(new Map);(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),()=>{N(e,l)};if(e.editor!==null&&!e.isDestroyed){c(e.editor),T(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=x(),a=r.tools===void 0?void 0:k(r.tools,()=>u.current.tools),s=a===void 0?void 0:j(a,i);S.current=i,w.current=a,E.current=new Map;let h=m(m(m({},r),s===void 0?{}:{tools:s}),{},{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&&(h.onSave=(...e)=>{var t,n;p.current=e[0],(t=(n=u.current).onSave)==null||t.call(n,...e)}),r.onBeforeRender&&(h.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&&(h.onAfterRender=(...e)=>{var t,n;(t=(n=u.current).onAfterRender)==null||t.call(n,...e)});let g=new t.Blok(h);return e.editor=g,_.current=r.data,o(g,n),C(g,i),g.isReady.then(()=>{e.editor===g&&!e.isDestroyed&&(e.isEditorReady=!0,l(g))}).catch(()=>{if(e.editor===g&&!e.isDestroyed){c(g),T(g);try{g.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.isEditorReady=!1,l(null)}}),()=>{N(e,l)}},[f]),(0,e.useEffect)(()=>{let e=S.current,t=d.current;if(e===null||t.editor===null||t.isDestroyed)return;let r=u.current.tools;if(typeof r!=`object`||!r)return;let i=w.current;for(let[t,o]of Object.entries(r)){var a;if(!A(o))continue;let r=typeof o==`function`||(a=o.config)==null?{}:a,s=i==null?void 0:i[t],c=O(typeof s==`object`&&s?s.config:void 0,r),l=E.current.get(t);if(l!==void 0&&(0,n.deepEqual)(c,l))continue;let u=Object.freeze(c);E.current.set(t,u),e.setToolConfig(t,u)}});let D=(0,n.normalizeReadOnlyConfig)(a.readOnly).enabled;(0,e.useEffect)(()=>{s!==null&&s.readOnly.set(D)},[s,D]);let{autofocus:M}=a;(0,e.useEffect)(()=>{s===null||!M||s.focus()},[s,M]);let{theme:P}=a;(0,e.useEffect)(()=>{s===null||P===void 0||s.theme.set(P)},[s,P]);let{width:F}=a;(0,e.useEffect)(()=>{s===null||F===void 0||s.width.set(F)},[s,F]);let{placeholder:I}=a;(0,e.useEffect)(()=>{s===null||I===void 0||s.placeholder.set(I)},[s,I]);let{data:L}=a;return(0,e.useEffect)(()=>{if(s===null||L===void 0)return;let e=d.current;if(!(e.editor!==s||e.isDestroyed)){if(g.current!==s&&(g.current=s,b.current=void 0,(0,n.deepEqual)(L,_.current))){p.current=L;return}(0,n.deepEqual)(L,p.current)||(0,n.deepEqual)(L,b.current)||(b.current=L,y.current=y.current.catch(()=>void 0).then(()=>s.render(L)).then(()=>{p.current=L},()=>{}).then(()=>{b.current===L&&(b.current=void 0)}))}},[s,L]),s}function N(e,t){e.destroyTimeout=setTimeout(()=>{if(e.editor!==null){c(e.editor),T(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)}var P=class extends e.Component{constructor(...e){super(...e),this.state={failed:!1}}static getDerivedStateFromError(){return{failed:!0}}componentDidCatch(){}render(){return this.state.failed?null:this.props.children}};function F({registry:t}){let n=(0,e.useSyncExternalStore)(t.subscribe,t.getSnapshot,t.getSnapshot);return(0,r.jsx)(r.Fragment,{children:Array.from(n.entries()).map(([e,t])=>{let n=t.component;return(0,i.createPortal)((0,r.jsx)(P,{children:(0,r.jsx)(n,m({},t.props))}),t.hostEl,e)})})}function I(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 L(e,t){if(e==null)return{};var n,r,i=I(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 R=[`editor`],z=(0,e.forwardRef)(function(t,n){let{editor:i}=t,a=L(t,R),o=(0,e.useRef)(null),c=e=>{if(o.current=e,typeof n==`function`)n(e);else if(n!=null){let t=n;t.current=e}};(0,e.useEffect)(()=>{if(i===null||o.current===null)return;let e=s(i);if(e!==void 0)return o.current.appendChild(e),()=>{e.remove()}},[i]);let l=i===null?void 0:w(i);return(0,r.jsx)(`div`,m(m({ref:c},a),{},{children:l===void 0?null:(0,r.jsx)(F,{registry:l})}))}),B=`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(`.`),V=[`deps`,`onReady`,`onBlocksRendered`,`onBlockRendered`],H=new Set(B),U=(0,e.forwardRef)(function(t,n){let{deps:i,onReady:a,onBlocksRendered:o,onBlockRendered:s}=t,c=L(t,V),l={},u={};for(let e of Object.keys(c)){let t=c[e];H.has(e)?l[e]=t:u[e]=t}let d=M(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 p=(0,e.useRef)(o);p.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=p.current)==null?void 0:t.call(p,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)(z,m({editor:d},u))}),W=`block changed`;function G(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(W,n),()=>t.off(W,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])}var K=[y,b];function q(t){var a;return a=class{static get toolbox(){return t.toolbox}static get isReadOnlySupported(){return!0}constructor(i){var a,o;this.hostEl=null,this.pendingDispatch=!1,this.commit=e=>{var r;let i=(0,n.fillDefaults)(t.propSchema,m(m({},this.mirror),e));(0,n.deepEqual)(i,this.mirror)||(this.mirror=i,this.lastRendered=i,(r=this.registry)==null||r.setProps(this.blockApi.id,{data:i}),this.flushDispatch())},this.blockApi=i.block;let s=(a=i.config)==null?{}:a;this.registry=s[y];let c=s[b];this.toolName=typeof c==`string`?c:void 0;let l={};for(let[e,t]of Object.entries(s))K.includes(e)||(l[e]=t);this.toolConfig=Object.freeze(l);let u=i.api;this.pointerDrag=()=>{var e;return(u==null||(e=u.blocks)==null?void 0:e.isPointerDragActive)===!0},this.mirror=(0,n.fillDefaults)(t.propSchema,(o=i.data)==null?{}:o),this.lastRendered=this.mirror,this.readOnly=i.readOnly;let d=this.blockApi;this.childrenComponent=function(){let t=(0,e.useRef)(null);return(0,e.useEffect)(()=>{t.current!==null&&(0,n.mountChildBlocks)(t.current,d.getChildren())}),(0,r.jsx)(`div`,{ref:t,[n.DATA_ATTR.nestedBlocks]:``})}}render(){var e;let n=document.createElement(`div`);return n.setAttribute(`data-blok-mutation-free`,`true`),this.hostEl=n,(e=this.registry)==null||e.register(this.blockApi.id,{hostEl:n,component:t.component,props:this.buildProps(),toolName:this.toolName}),n}rendered(){var e;(e=t.onRendered)==null||e.call(t,this.blockApi)}save(){return this.mirror}async setData(e){var r=this;let i=(0,n.fillDefaults)(t.propSchema,e==null?{}:e);return(0,n.deepEqual)(i,r.lastRendered)?!0:(r.mirror=i,r.lastRendered=i,r.pushProps({data:i}),!0)}setReadOnly(e){this.readOnly=e,this.pushProps({readOnly:e})}moved(){var e;(e=t.onMoved)==null||e.call(t,this.blockApi)}removed(){var e,n;(e=this.registry)==null||e.unregister(this.blockApi.id),(n=t.onRemoved)==null||n.call(t,this.blockApi)}destroy(){var e;(e=this.registry)==null||e.unregister(this.blockApi.id)}buildProps(){return{data:this.mirror,commit:this.commit,block:this.blockApi,readOnly:this.readOnly,config:this.toolConfig,BlockChildren:this.childrenComponent}}pushProps(e){let t=this.registry;t!==void 0&&(0,i.flushSync)(()=>{t.setProps(this.blockApi.id,e)})}flushDispatch(){if(!this.pointerDrag()){this.pendingDispatch=!1,this.blockApi.dispatchChange();return}if(this.pendingDispatch)return;this.pendingDispatch=!0;let e=()=>{if(this.pointerDrag()){requestAnimationFrame(e);return}this.pendingDispatch=!1,this.blockApi.dispatchChange()};requestAnimationFrame(e)}},a.__isBlokReactBlock=!0,a}exports.BLOK_PORTAL_REGISTRY_CONFIG_KEY=y,exports.BLOK_TOOL_NAME_CONFIG_KEY=b,exports.BlockPortalHost=F,exports.BlokContent=z,exports.BlokEditor=U,exports.BlokProvider=g,exports.createBlockPortalRegistry=x,exports.createReactBlock=q,exports.useBlocks=G,exports.useBlok=M,exports.useBlokDefaults=_;
package/dist/index.mjs CHANGED
@@ -1,51 +1,51 @@
1
1
  import { Component as e, createContext as t, forwardRef as n, useCallback as r, useContext as i, useEffect as a, useImperativeHandle as o, useMemo as s, useRef as c, useState as l, useSyncExternalStore as u } from "react";
2
2
  import { Blok as d } from "@bloklabs/core";
3
- import { DATA_ATTR as f, EMPTY_API as p, createBlocksApiForEditor as m, deepEqual as h, deepEqual as g, fillDefaults as _, mountChildBlocks as v, normalizeReadOnlyConfig as ee } from "@bloklabs/core/adapters";
4
- import { Fragment as y, jsx as b } from "react/jsx-runtime";
5
- import { createPortal as x, flushSync as S } from "react-dom";
3
+ import { DATA_ATTR as f, EMPTY_API as p, createBlocksApiForEditor as m, deepEqual as h, deepEqual as g, fillDefaults as _, mountChildBlocks as v, normalizeReadOnlyConfig as y } from "@bloklabs/core/adapters";
4
+ import { Fragment as b, jsx as x } from "react/jsx-runtime";
5
+ import { createPortal as S, flushSync as C } from "react-dom";
6
6
  //#region src/holder-map.ts
7
- var C = /* @__PURE__ */ new WeakMap();
8
- function te(e, t) {
9
- C.set(e, t);
7
+ var w = /* @__PURE__ */ new WeakMap();
8
+ function T(e, t) {
9
+ w.set(e, t);
10
10
  }
11
- function w(e) {
12
- return C.get(e);
11
+ function E(e) {
12
+ return w.get(e);
13
13
  }
14
- function T(e) {
15
- C.delete(e);
14
+ function D(e) {
15
+ w.delete(e);
16
16
  }
17
17
  //#endregion
18
18
  //#region \0@oxc-project+runtime@0.126.0/helpers/typeof.js
19
- function E(e) {
19
+ function O(e) {
20
20
  "@babel/helpers - typeof";
21
- return E = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) {
21
+ return O = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) {
22
22
  return typeof e;
23
23
  } : function(e) {
24
24
  return e && typeof Symbol == "function" && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e;
25
- }, E(e);
25
+ }, O(e);
26
26
  }
27
27
  //#endregion
28
28
  //#region \0@oxc-project+runtime@0.126.0/helpers/toPrimitive.js
29
- function D(e, t) {
30
- if (E(e) != "object" || !e) return e;
29
+ function k(e, t) {
30
+ if (O(e) != "object" || !e) return e;
31
31
  var n = e[Symbol.toPrimitive];
32
32
  if (n !== void 0) {
33
33
  var r = n.call(e, t || "default");
34
- if (E(r) != "object") return r;
34
+ if (O(r) != "object") return r;
35
35
  throw TypeError("@@toPrimitive must return a primitive value.");
36
36
  }
37
37
  return (t === "string" ? String : Number)(e);
38
38
  }
39
39
  //#endregion
40
40
  //#region \0@oxc-project+runtime@0.126.0/helpers/toPropertyKey.js
41
- function O(e) {
42
- var t = D(e, "string");
43
- return E(t) == "symbol" ? t : t + "";
41
+ function A(e) {
42
+ var t = k(e, "string");
43
+ return O(t) == "symbol" ? t : t + "";
44
44
  }
45
45
  //#endregion
46
46
  //#region \0@oxc-project+runtime@0.126.0/helpers/defineProperty.js
47
- function k(e, t, n) {
48
- return (t = O(t)) in e ? Object.defineProperty(e, t, {
47
+ function j(e, t, n) {
48
+ return (t = A(t)) in e ? Object.defineProperty(e, t, {
49
49
  value: n,
50
50
  enumerable: !0,
51
51
  configurable: !0,
@@ -54,7 +54,7 @@ function k(e, t, n) {
54
54
  }
55
55
  //#endregion
56
56
  //#region \0@oxc-project+runtime@0.126.0/helpers/objectSpread2.js
57
- function A(e, t) {
57
+ function M(e, t) {
58
58
  var n = Object.keys(e);
59
59
  if (Object.getOwnPropertySymbols) {
60
60
  var r = Object.getOwnPropertySymbols(e);
@@ -64,12 +64,12 @@ function A(e, t) {
64
64
  }
65
65
  return n;
66
66
  }
67
- function j(e) {
67
+ function N(e) {
68
68
  for (var t = 1; t < arguments.length; t++) {
69
69
  var n = arguments[t] == null ? {} : arguments[t];
70
- t % 2 ? A(Object(n), !0).forEach(function(t) {
71
- k(e, t, n[t]);
72
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : A(Object(n)).forEach(function(t) {
70
+ t % 2 ? M(Object(n), !0).forEach(function(t) {
71
+ j(e, t, n[t]);
72
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : M(Object(n)).forEach(function(t) {
73
73
  Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t));
74
74
  });
75
75
  }
@@ -77,46 +77,54 @@ function j(e) {
77
77
  }
78
78
  //#endregion
79
79
  //#region src/provide-blok.tsx
80
- var M = t({});
81
- function N({ defaults: e, children: t }) {
82
- return /* @__PURE__ */ b(M.Provider, {
80
+ var P = t({});
81
+ function F({ defaults: e, children: t }) {
82
+ return /* @__PURE__ */ x(P.Provider, {
83
83
  value: e,
84
84
  children: t
85
85
  });
86
86
  }
87
- function P() {
88
- return i(M);
87
+ function I() {
88
+ return i(P);
89
89
  }
90
- function F(e, t) {
90
+ function ee(e, t) {
91
91
  if (Object.keys(e).length === 0) return t;
92
- let n = j({}, e);
92
+ let n = N({}, e);
93
93
  for (let e of Object.keys(t)) {
94
94
  let r = t[e];
95
95
  r !== void 0 && (n[e] = r);
96
96
  }
97
- return (e.tools !== void 0 || t.tools !== void 0) && (n.tools = j(j({}, e.tools), t.tools)), n;
97
+ return (e.tools !== void 0 || t.tools !== void 0) && (n.tools = N(N({}, e.tools), t.tools)), n;
98
98
  }
99
99
  //#endregion
100
100
  //#region src/block-portal-registry.ts
101
- var I = "__blokPortalRegistry", L = () => {
102
- let e = { snapshot: /* @__PURE__ */ new Map() }, t = /* @__PURE__ */ new Set(), n = (n) => {
101
+ var L = "__blokPortalRegistry", R = "__blokToolName", z = () => {
102
+ let e = { snapshot: /* @__PURE__ */ new Map() }, t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Map(), r = (n) => {
103
103
  e.snapshot = n, t.forEach((e) => e());
104
104
  };
105
105
  return {
106
- register(t, r) {
107
- let i = new Map(e.snapshot);
108
- i.set(t, r), n(i);
106
+ register(t, i) {
107
+ let a = new Map(e.snapshot), o = i.toolName === void 0 ? void 0 : n.get(i.toolName);
108
+ a.set(t, o === void 0 ? i : N(N({}, i), {}, { props: N(N({}, i.props), {}, { config: o }) })), r(a);
109
109
  },
110
110
  unregister(t) {
111
111
  if (!e.snapshot.has(t)) return;
112
- let r = new Map(e.snapshot);
113
- r.delete(t), n(r);
112
+ let n = new Map(e.snapshot);
113
+ n.delete(t), r(n);
114
114
  },
115
- setProps(t, r) {
115
+ setProps(t, n) {
116
116
  let i = e.snapshot.get(t);
117
117
  if (i === void 0) return;
118
118
  let a = new Map(e.snapshot);
119
- a.set(t, j(j({}, i), {}, { props: j(j({}, i.props), r) })), n(a);
119
+ a.set(t, N(N({}, i), {}, { props: N(N({}, i.props), n) })), r(a);
120
+ },
121
+ setToolConfig(t, i) {
122
+ n.set(t, i);
123
+ let a = [...e.snapshot].filter(([, e]) => e.toolName === t);
124
+ if (a.length === 0) return;
125
+ let o = new Map(e.snapshot);
126
+ for (let [e, t] of a) o.set(e, N(N({}, t), {}, { props: N(N({}, t.props), {}, { config: i }) }));
127
+ r(o);
120
128
  },
121
129
  subscribe(e) {
122
130
  return t.add(e), () => {
@@ -127,30 +135,45 @@ var I = "__blokPortalRegistry", L = () => {
127
135
  return e.snapshot;
128
136
  }
129
137
  };
130
- }, R = /* @__PURE__ */ new WeakMap();
131
- function z(e, t) {
132
- R.set(e, t);
138
+ }, B = /* @__PURE__ */ new WeakMap();
139
+ function V(e, t) {
140
+ B.set(e, t);
133
141
  }
134
- function B(e) {
135
- return R.get(e);
142
+ function H(e) {
143
+ return B.get(e);
136
144
  }
137
- function V(e) {
138
- R.delete(e);
145
+ function U(e) {
146
+ B.delete(e);
139
147
  }
140
148
  //#endregion
141
149
  //#region src/live-tool-config.ts
142
- var H = (e) => {
150
+ var W = (e) => {
143
151
  if (typeof e != "object" || !e) return !1;
144
152
  let t = Object.getPrototypeOf(e);
145
153
  return t === Object.prototype || t === null;
146
- }, U = (e, t) => t.reduce((e, t) => {
154
+ }, G = (e, t) => t.reduce((e, t) => {
147
155
  if (Array.isArray(e)) return typeof t == "number" ? e[t] : void 0;
148
- if (H(e)) return e[t];
149
- }, e), W = (e, t) => {
150
- if (!H(e)) return e;
156
+ if (W(e)) return e[t];
157
+ }, e), K = (e, t) => {
158
+ let n = (e, t) => {
159
+ if (typeof e == "function") return typeof t == "function" ? t : e;
160
+ if (Array.isArray(e)) {
161
+ let r = Array.isArray(t) ? t : [];
162
+ return e.map((e, t) => n(e, r[t]));
163
+ }
164
+ if (W(e)) {
165
+ let r = W(t) ? t : {}, i = {};
166
+ for (let [t, a] of Object.entries(e)) i[t] = n(a, r[t]);
167
+ return i;
168
+ }
169
+ return e;
170
+ }, r = n(t, e);
171
+ return W(r) ? r : {};
172
+ }, te = (e, t) => {
173
+ if (!W(e)) return e;
151
174
  let n = {};
152
175
  for (let [r, i] of Object.entries(e)) {
153
- if (!H(i) || !H(i.config)) {
176
+ if (!W(i) || !W(i.config)) {
154
177
  n[r] = i;
155
178
  continue;
156
179
  }
@@ -158,12 +181,12 @@ var H = (e) => {
158
181
  if (typeof n == "function") {
159
182
  let e = n;
160
183
  return function(...n) {
161
- let a = t(), o = H(a) ? a[r] : void 0, s = U(H(o) ? o.config : void 0, i);
184
+ let a = t(), o = W(a) ? a[r] : void 0, s = G(W(o) ? o.config : void 0, i);
162
185
  return Reflect.apply(typeof s == "function" ? s : e, this, n);
163
186
  };
164
187
  }
165
188
  if (Array.isArray(n)) return e.has(n) ? n : (e.add(n), n.map((e, t) => a(e, [...i, t])));
166
- if (H(n)) {
189
+ if (W(n)) {
167
190
  if (e.has(n)) return n;
168
191
  e.add(n);
169
192
  let t = {};
@@ -172,26 +195,31 @@ var H = (e) => {
172
195
  }
173
196
  return n;
174
197
  };
175
- n[r] = j(j({}, i), {}, { config: a(i.config, []) });
198
+ n[r] = N(N({}, i), {}, { config: a(i.config, []) });
176
199
  }
177
200
  return n;
178
- }, G = (e, t) => {
201
+ }, q = (e) => {
202
+ let t = typeof e == "function" ? e : e == null ? void 0 : e.class;
203
+ return typeof t == "function" && t.__isBlokReactBlock === !0;
204
+ }, ne = (e, t) => {
179
205
  if (typeof e != "object" || !e) return e;
180
206
  let n = {};
181
207
  for (let [i, a] of Object.entries(e)) {
182
208
  var r;
183
- let e = typeof a == "function" ? a : a == null ? void 0 : a.class;
184
- if (!(typeof e == "function" && e.__isBlokReactBlock === !0)) {
209
+ if (!q(a)) {
185
210
  n[i] = a;
186
211
  continue;
187
212
  }
188
- let o = typeof a == "function" ? { class: a } : j({}, a);
189
- o.config = j(j({}, (r = o.config) == null ? {} : r), {}, { [I]: t }), n[i] = o;
213
+ let e = typeof a == "function" ? { class: a } : N({}, a);
214
+ e.config = N(N({}, (r = e.config) == null ? {} : r), {}, {
215
+ [L]: t,
216
+ [R]: i
217
+ }), n[i] = e;
190
218
  }
191
219
  return n;
192
220
  };
193
- function K(e, t) {
194
- let n = F(i(M), e), [r, o] = l(null), u = c(n);
221
+ function J(e, t) {
222
+ let n = ee(i(P), e), [r, o] = l(null), u = c(n);
195
223
  u.current = n;
196
224
  let f = c({
197
225
  editor: null,
@@ -200,15 +228,15 @@ function K(e, t) {
200
228
  isDestroyed: !1,
201
229
  isEditorReady: !1,
202
230
  depsToken: null
203
- }), p = s(() => ({}), t == null ? [] : t), m = c(n.data), h = c(null), _ = c(n.data), v = c(Promise.resolve()), y = c(void 0);
231
+ }), p = s(() => ({}), t == null ? [] : t), m = c(n.data), h = c(null), _ = c(n.data), v = c(Promise.resolve()), b = c(void 0), x = c(null), S = c(void 0), C = c(/* @__PURE__ */ new Map());
204
232
  a(() => {
205
233
  if (typeof window > "u") return;
206
234
  let e = f.current;
207
235
  if (e.destroyTimeout !== null && (clearTimeout(e.destroyTimeout), e.destroyTimeout = null), e.editor !== null && !e.isDestroyed && e.depsToken === p) return e.isEditorReady && o(e.editor), () => {
208
- q(e, o);
236
+ Y(e, o);
209
237
  };
210
238
  if (e.editor !== null && !e.isDestroyed) {
211
- T(e.editor), V(e.editor);
239
+ D(e.editor), U(e.editor);
212
240
  try {
213
241
  e.editor.destroy();
214
242
  } catch (e) {}
@@ -216,7 +244,9 @@ function K(e, t) {
216
244
  }
217
245
  let t = document.createElement("div");
218
246
  e.holder = t, e.isDestroyed = !1, e.isEditorReady = !1, e.depsToken = p;
219
- let n = u.current, r = L(), i = n.tools === void 0 ? void 0 : G(W(n.tools, () => u.current.tools), r), a = j(j(j({}, n), i === void 0 ? {} : { tools: i }), {}, {
247
+ let n = u.current, r = z(), i = n.tools === void 0 ? void 0 : te(n.tools, () => u.current.tools), a = i === void 0 ? void 0 : ne(i, r);
248
+ x.current = r, S.current = i, C.current = /* @__PURE__ */ new Map();
249
+ let s = N(N(N({}, n), a === void 0 ? {} : { tools: a }), {}, {
220
250
  holder: t,
221
251
  onReady: (...e) => {
222
252
  var t, n;
@@ -227,72 +257,86 @@ function K(e, t) {
227
257
  (t = (n = u.current).onChange) == null || t.call(n, ...e);
228
258
  }
229
259
  });
230
- n.onSave && (a.onSave = (...e) => {
260
+ n.onSave && (s.onSave = (...e) => {
231
261
  var t, n;
232
262
  m.current = e[0], (t = (n = u.current).onSave) == null || t.call(n, ...e);
233
- }), n.onBeforeRender && (a.onBeforeRender = (...e) => {
263
+ }), n.onBeforeRender && (s.onBeforeRender = (...e) => {
234
264
  var t, n, r;
235
265
  return (t = (n = (r = u.current).onBeforeRender) == null ? void 0 : n.call(r, ...e)) == null ? e[0] : t;
236
- }), n.onAfterRender && (a.onAfterRender = (...e) => {
266
+ }), n.onAfterRender && (s.onAfterRender = (...e) => {
237
267
  var t, n;
238
268
  (t = (n = u.current).onAfterRender) == null || t.call(n, ...e);
239
269
  });
240
- let s = new d(a);
241
- return e.editor = s, _.current = n.data, te(s, t), z(s, r), s.isReady.then(() => {
242
- e.editor === s && !e.isDestroyed && (e.isEditorReady = !0, o(s));
270
+ let c = new d(s);
271
+ return e.editor = c, _.current = n.data, T(c, t), V(c, r), c.isReady.then(() => {
272
+ e.editor === c && !e.isDestroyed && (e.isEditorReady = !0, o(c));
243
273
  }).catch(() => {
244
- if (e.editor === s && !e.isDestroyed) {
245
- T(s), V(s);
274
+ if (e.editor === c && !e.isDestroyed) {
275
+ D(c), U(c);
246
276
  try {
247
- s.destroy();
277
+ c.destroy();
248
278
  } catch (e) {}
249
279
  e.editor = null, e.holder = null, e.isDestroyed = !0, e.isEditorReady = !1, o(null);
250
280
  }
251
281
  }), () => {
252
- q(e, o);
282
+ Y(e, o);
253
283
  };
254
- }, [p]);
255
- let b = ee(n.readOnly).enabled;
284
+ }, [p]), a(() => {
285
+ let e = x.current, t = f.current;
286
+ if (e === null || t.editor === null || t.isDestroyed) return;
287
+ let n = u.current.tools;
288
+ if (typeof n != "object" || !n) return;
289
+ let r = S.current;
290
+ for (let [t, a] of Object.entries(n)) {
291
+ var i;
292
+ if (!q(a)) continue;
293
+ let n = typeof a == "function" || (i = a.config) == null ? {} : i, o = r == null ? void 0 : r[t], s = K(typeof o == "object" && o ? o.config : void 0, n), c = C.current.get(t);
294
+ if (c !== void 0 && g(s, c)) continue;
295
+ let l = Object.freeze(s);
296
+ C.current.set(t, l), e.setToolConfig(t, l);
297
+ }
298
+ });
299
+ let w = y(n.readOnly).enabled;
256
300
  a(() => {
257
- r !== null && r.readOnly.set(b);
258
- }, [r, b]);
259
- let { autofocus: x } = n;
301
+ r !== null && r.readOnly.set(w);
302
+ }, [r, w]);
303
+ let { autofocus: E } = n;
260
304
  a(() => {
261
- r === null || !x || r.focus();
262
- }, [r, x]);
263
- let { theme: S } = n;
305
+ r === null || !E || r.focus();
306
+ }, [r, E]);
307
+ let { theme: O } = n;
264
308
  a(() => {
265
- r === null || S === void 0 || r.theme.set(S);
266
- }, [r, S]);
267
- let { width: C } = n;
309
+ r === null || O === void 0 || r.theme.set(O);
310
+ }, [r, O]);
311
+ let { width: k } = n;
268
312
  a(() => {
269
- r === null || C === void 0 || r.width.set(C);
270
- }, [r, C]);
271
- let { placeholder: w } = n;
313
+ r === null || k === void 0 || r.width.set(k);
314
+ }, [r, k]);
315
+ let { placeholder: A } = n;
272
316
  a(() => {
273
- r === null || w === void 0 || r.placeholder.set(w);
274
- }, [r, w]);
275
- let { data: E } = n;
317
+ r === null || A === void 0 || r.placeholder.set(A);
318
+ }, [r, A]);
319
+ let { data: j } = n;
276
320
  return a(() => {
277
- if (r === null || E === void 0) return;
321
+ if (r === null || j === void 0) return;
278
322
  let e = f.current;
279
323
  if (!(e.editor !== r || e.isDestroyed)) {
280
- if (h.current !== r && (h.current = r, y.current = void 0, g(E, _.current))) {
281
- m.current = E;
324
+ if (h.current !== r && (h.current = r, b.current = void 0, g(j, _.current))) {
325
+ m.current = j;
282
326
  return;
283
327
  }
284
- g(E, m.current) || g(E, y.current) || (y.current = E, v.current = v.current.catch(() => void 0).then(() => r.render(E)).then(() => {
285
- m.current = E;
328
+ g(j, m.current) || g(j, b.current) || (b.current = j, v.current = v.current.catch(() => void 0).then(() => r.render(j)).then(() => {
329
+ m.current = j;
286
330
  }, () => {}).then(() => {
287
- y.current === E && (y.current = void 0);
331
+ b.current === j && (b.current = void 0);
288
332
  }));
289
333
  }
290
- }, [r, E]), r;
334
+ }, [r, j]), r;
291
335
  }
292
- function q(e, t) {
336
+ function Y(e, t) {
293
337
  e.destroyTimeout = setTimeout(() => {
294
338
  if (e.editor !== null) {
295
- T(e.editor), V(e.editor);
339
+ D(e.editor), U(e.editor);
296
340
  try {
297
341
  e.editor.destroy();
298
342
  } catch (e) {}
@@ -302,7 +346,7 @@ function q(e, t) {
302
346
  }
303
347
  //#endregion
304
348
  //#region src/BlockPortalHost.tsx
305
- var J = class extends e {
349
+ var re = class extends e {
306
350
  constructor(...e) {
307
351
  super(...e), this.state = { failed: !1 };
308
352
  }
@@ -314,16 +358,16 @@ var J = class extends e {
314
358
  return this.state.failed ? null : this.props.children;
315
359
  }
316
360
  };
317
- function Y({ registry: e }) {
361
+ function X({ registry: e }) {
318
362
  let t = u(e.subscribe, e.getSnapshot, e.getSnapshot);
319
- return /* @__PURE__ */ b(y, { children: Array.from(t.entries()).map(([e, t]) => {
363
+ return /* @__PURE__ */ x(b, { children: Array.from(t.entries()).map(([e, t]) => {
320
364
  let n = t.component;
321
- return x(/* @__PURE__ */ b(J, { children: /* @__PURE__ */ b(n, j({}, t.props)) }), t.hostEl, e);
365
+ return S(/* @__PURE__ */ x(re, { children: /* @__PURE__ */ x(n, N({}, t.props)) }), t.hostEl, e);
322
366
  }) });
323
367
  }
324
368
  //#endregion
325
369
  //#region \0@oxc-project+runtime@0.126.0/helpers/objectWithoutPropertiesLoose.js
326
- function X(e, t) {
370
+ function ie(e, t) {
327
371
  if (e == null) return {};
328
372
  var n = {};
329
373
  for (var r in e) if ({}.hasOwnProperty.call(e, r)) {
@@ -336,7 +380,7 @@ function X(e, t) {
336
380
  //#region \0@oxc-project+runtime@0.126.0/helpers/objectWithoutProperties.js
337
381
  function Z(e, t) {
338
382
  if (e == null) return {};
339
- var n, r, i = X(e, t);
383
+ var n, r, i = ie(e, t);
340
384
  if (Object.getOwnPropertySymbols) {
341
385
  var a = Object.getOwnPropertySymbols(e);
342
386
  for (r = 0; r < a.length; r++) n = a[r], t.includes(n) || {}.propertyIsEnumerable.call(e, n) && (i[n] = e[n]);
@@ -345,8 +389,8 @@ function Z(e, t) {
345
389
  }
346
390
  //#endregion
347
391
  //#region src/BlokContent.tsx
348
- var ne = ["editor"], Q = n(function(e, t) {
349
- let { editor: n } = e, r = Z(e, ne), i = c(null), o = (e) => {
392
+ var ae = ["editor"], Q = n(function(e, t) {
393
+ let { editor: n } = e, r = Z(e, ae), i = c(null), o = (e) => {
350
394
  if (i.current = e, typeof t == "function") t(e);
351
395
  else if (t != null) {
352
396
  let n = t;
@@ -355,25 +399,25 @@ var ne = ["editor"], Q = n(function(e, t) {
355
399
  };
356
400
  a(() => {
357
401
  if (n === null || i.current === null) return;
358
- let e = w(n);
402
+ let e = E(n);
359
403
  if (e !== void 0) return i.current.appendChild(e), () => {
360
404
  e.remove();
361
405
  };
362
406
  }, [n]);
363
- let s = n === null ? void 0 : B(n);
364
- return /* @__PURE__ */ b("div", j(j({ ref: o }, r), {}, { children: s === void 0 ? null : /* @__PURE__ */ b(Y, { registry: s }) }));
365
- }), re = /* @__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("."), ie = [
407
+ let s = n === null ? void 0 : H(n);
408
+ return /* @__PURE__ */ x("div", N(N({ ref: o }, r), {}, { children: s === void 0 ? null : /* @__PURE__ */ x(X, { registry: s }) }));
409
+ }), oe = /* @__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("."), se = [
366
410
  "deps",
367
411
  "onReady",
368
412
  "onBlocksRendered",
369
413
  "onBlockRendered"
370
- ], ae = new Set(re), oe = n(function(e, t) {
371
- let { deps: n, onReady: r, onBlocksRendered: i, onBlockRendered: s } = e, l = Z(e, ie), u = {}, d = {};
414
+ ], ce = new Set(oe), le = n(function(e, t) {
415
+ let { deps: n, onReady: r, onBlocksRendered: i, onBlockRendered: s } = e, l = Z(e, se), u = {}, d = {};
372
416
  for (let e of Object.keys(l)) {
373
417
  let t = l[e];
374
- ae.has(e) ? u[e] = t : d[e] = t;
418
+ ce.has(e) ? u[e] = t : d[e] = t;
375
419
  }
376
- let f = K(u, n);
420
+ let f = J(u, n);
377
421
  o(t, () => f, [f]);
378
422
  let p = c(r);
379
423
  p.current = r, a(() => {
@@ -411,9 +455,9 @@ var ne = ["editor"], Q = n(function(e, t) {
411
455
  f,
412
456
  g,
413
457
  _
414
- ]), /* @__PURE__ */ b(Q, j({ editor: f }, d));
458
+ ]), /* @__PURE__ */ x(Q, N({ editor: f }, d));
415
459
  }), $ = "block changed";
416
- function se(e) {
460
+ function ue(e) {
417
461
  let t = c(0), n = c(e);
418
462
  n.current !== e && (n.current = e, t.current = 0);
419
463
  let i = r((n) => {
@@ -427,7 +471,8 @@ function se(e) {
427
471
  }
428
472
  //#endregion
429
473
  //#region src/createReactBlock.tsx
430
- function ce(e) {
474
+ var de = [L, R];
475
+ function fe(e) {
431
476
  var t;
432
477
  return t = class {
433
478
  static get toolbox() {
@@ -440,22 +485,27 @@ function ce(e) {
440
485
  var n, r;
441
486
  this.hostEl = null, this.pendingDispatch = !1, this.commit = (t) => {
442
487
  var n;
443
- let r = _(e.propSchema, j(j({}, this.mirror), t));
444
- this.mirror = r, this.lastRendered = r, (n = this.registry) == null || n.setProps(this.blockApi.id, { data: r }), this.flushDispatch();
488
+ let r = _(e.propSchema, N(N({}, this.mirror), t));
489
+ h(r, this.mirror) || (this.mirror = r, this.lastRendered = r, (n = this.registry) == null || n.setProps(this.blockApi.id, { data: r }), this.flushDispatch());
445
490
  }, this.blockApi = t.block;
446
491
  let i = (n = t.config) == null ? {} : n;
447
- this.registry = i[I];
448
- let o = t.api;
492
+ this.registry = i[L];
493
+ let o = i[R];
494
+ this.toolName = typeof o == "string" ? o : void 0;
495
+ let s = {};
496
+ for (let [e, t] of Object.entries(i)) de.includes(e) || (s[e] = t);
497
+ this.toolConfig = Object.freeze(s);
498
+ let l = t.api;
449
499
  this.pointerDrag = () => {
450
500
  var e;
451
- return (o == null || (e = o.blocks) == null ? void 0 : e.isPointerDragActive) === !0;
501
+ return (l == null || (e = l.blocks) == null ? void 0 : e.isPointerDragActive) === !0;
452
502
  }, this.mirror = _(e.propSchema, (r = t.data) == null ? {} : r), this.lastRendered = this.mirror, this.readOnly = t.readOnly;
453
- let s = this.blockApi;
503
+ let u = this.blockApi;
454
504
  this.childrenComponent = function() {
455
505
  let e = c(null);
456
506
  return a(() => {
457
- e.current !== null && v(e.current, s.getChildren());
458
- }), /* @__PURE__ */ b("div", {
507
+ e.current !== null && v(e.current, u.getChildren());
508
+ }), /* @__PURE__ */ x("div", {
459
509
  ref: e,
460
510
  [f.nestedBlocks]: ""
461
511
  });
@@ -467,7 +517,8 @@ function ce(e) {
467
517
  return n.setAttribute("data-blok-mutation-free", "true"), this.hostEl = n, (t = this.registry) == null || t.register(this.blockApi.id, {
468
518
  hostEl: n,
469
519
  component: e.component,
470
- props: this.buildProps()
520
+ props: this.buildProps(),
521
+ toolName: this.toolName
471
522
  }), n;
472
523
  }
473
524
  rendered() {
@@ -503,12 +554,13 @@ function ce(e) {
503
554
  commit: this.commit,
504
555
  block: this.blockApi,
505
556
  readOnly: this.readOnly,
557
+ config: this.toolConfig,
506
558
  BlockChildren: this.childrenComponent
507
559
  };
508
560
  }
509
561
  pushProps(e) {
510
562
  let t = this.registry;
511
- t !== void 0 && S(() => {
563
+ t !== void 0 && C(() => {
512
564
  t.setProps(this.blockApi.id, e);
513
565
  });
514
566
  }
@@ -531,4 +583,4 @@ function ce(e) {
531
583
  }, t.__isBlokReactBlock = !0, t;
532
584
  }
533
585
  //#endregion
534
- export { I as BLOK_PORTAL_REGISTRY_CONFIG_KEY, Y as BlockPortalHost, Q as BlokContent, oe as BlokEditor, N as BlokProvider, L as createBlockPortalRegistry, ce as createReactBlock, se as useBlocks, K as useBlok, P as useBlokDefaults };
586
+ export { L as BLOK_PORTAL_REGISTRY_CONFIG_KEY, R as BLOK_TOOL_NAME_CONFIG_KEY, X as BlockPortalHost, Q as BlokContent, le as BlokEditor, F as BlokProvider, z as createBlockPortalRegistry, fe as createReactBlock, ue as useBlocks, J as useBlok, I as useBlokDefaults };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloklabs/react",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "React bindings for the Blok editor",
5
5
  "license": "Apache-2.0",
6
6
  "author": "JackUait",
@@ -33,7 +33,7 @@
33
33
  "build": "yarn run -T vite build --mode production"
34
34
  },
35
35
  "peerDependencies": {
36
- "@bloklabs/core": "^1.2.2",
36
+ "@bloklabs/core": "^1.2.4",
37
37
  "react": "^18.0.0 || ^19.0.0",
38
38
  "react-dom": "^18.0.0 || ^19.0.0"
39
39
  }
@@ -44,11 +44,13 @@ export interface BlokEditorProps
44
44
  * value referentially stable (primitives or useMemo-stable objects) — a dep
45
45
  * whose identity changes every render recreates the editor each time.
46
46
  *
47
- * FUNCTIONS inside tool configs do NOT belong here: the adapter re-binds
48
- * every tool-config function to the latest render's closure automatically,
49
- * so an inline `uploadByFile`/callback with a fresh identity each render
50
- * reaches the editor live, without recreation and without freezing its
51
- * identity in a `useState` initializer.
47
+ * Tool configs do NOT belong here — reserve deps for true identity changes
48
+ * (e.g. the document id). FUNCTIONS inside tool configs are re-bound to the
49
+ * latest render's closure automatically (an inline `uploadByFile` with a
50
+ * fresh identity each render reaches the editor live), and non-function
51
+ * config VALUES of `createReactBlock` tools (permissions, URLs, locale…) are
52
+ * pushed to mounted blocks in place when they change. Neither requires
53
+ * recreation or `useState` identity-freezing.
52
54
  */
53
55
  deps?: DependencyList;
54
56
  /** Test id forwarded to the editor container element (via data-testid). */
@@ -8,6 +8,15 @@ import type { ComponentType } from 'react';
8
8
  */
9
9
  export const BLOK_PORTAL_REGISTRY_CONFIG_KEY = '__blokPortalRegistry';
10
10
 
11
+ /**
12
+ * Tool-config key carrying the name the tool was registered under in the
13
+ * consumer's `tools` map (injected next to the registry). The factory needs it
14
+ * to associate portal entries with their tool so live config updates can be
15
+ * routed; it is stripped (with the registry key) before the sanitized config
16
+ * reaches the component.
17
+ */
18
+ export const BLOK_TOOL_NAME_CONFIG_KEY = '__blokToolName';
19
+
11
20
  /**
12
21
  * One mounted React block in the portal registry. `hostEl` is the Blok-owned
13
22
  * `toolRenderedElement` (tagged `data-blok-mutation-free`) the component is
@@ -18,6 +27,8 @@ export interface BlockPortalEntry {
18
27
  hostEl: HTMLElement;
19
28
  component: ComponentType<Record<string, unknown>>;
20
29
  props: Record<string, unknown>;
30
+ /** Name the tool is registered under — routes per-tool live config updates. */
31
+ toolName?: string;
21
32
  }
22
33
 
23
34
  /**
@@ -40,6 +51,12 @@ export interface BlockPortalRegistry {
40
51
  unregister(id: string): void;
41
52
  /** Merge `props` into the entry's props (in-place update, no remount). */
42
53
  setProps(id: string, props: Record<string, unknown>): void;
54
+ /**
55
+ * Replace the `config` prop of every entry registered under `toolName`, and
56
+ * remember it so entries registered LATER start from this latest config
57
+ * instead of their construction-time snapshot.
58
+ */
59
+ setToolConfig(toolName: string, config: Record<string, unknown>): void;
43
60
  /** Subscribe to snapshot changes; returns the unsubscribe function. */
44
61
  subscribe(listener: () => void): () => void;
45
62
  /** Current immutable snapshot (stable identity until the next mutation). */
@@ -56,6 +73,8 @@ export const createBlockPortalRegistry = (): BlockPortalRegistry => {
56
73
  snapshot: new Map<string, BlockPortalEntry>() as ReadonlyMap<string, BlockPortalEntry>,
57
74
  };
58
75
  const listeners = new Set<() => void>();
76
+ /** Latest per-tool config, applied to mounted entries and future registers. */
77
+ const toolConfigs = new Map<string, Record<string, unknown>>();
59
78
 
60
79
  const commit = (next: Map<string, BlockPortalEntry>): void => {
61
80
  state.snapshot = next;
@@ -65,8 +84,12 @@ export const createBlockPortalRegistry = (): BlockPortalRegistry => {
65
84
  return {
66
85
  register(id: string, entry: BlockPortalEntry): void {
67
86
  const next = new Map(state.snapshot);
87
+ const liveConfig = entry.toolName === undefined ? undefined : toolConfigs.get(entry.toolName);
68
88
 
69
- next.set(id, entry);
89
+ next.set(
90
+ id,
91
+ liveConfig === undefined ? entry : { ...entry, props: { ...entry.props, config: liveConfig } }
92
+ );
70
93
  commit(next);
71
94
  },
72
95
  unregister(id: string): void {
@@ -91,6 +114,23 @@ export const createBlockPortalRegistry = (): BlockPortalRegistry => {
91
114
  next.set(id, { ...current, props: { ...current.props, ...props } });
92
115
  commit(next);
93
116
  },
117
+ setToolConfig(toolName: string, config: Record<string, unknown>): void {
118
+ toolConfigs.set(toolName, config);
119
+
120
+ const matching = [...state.snapshot].filter(([, entry]) => entry.toolName === toolName);
121
+
122
+ if (matching.length === 0) {
123
+ return;
124
+ }
125
+
126
+ const next = new Map(state.snapshot);
127
+
128
+ for (const [id, entry] of matching) {
129
+ next.set(id, { ...entry, props: { ...entry.props, config } });
130
+ }
131
+
132
+ commit(next);
133
+ },
94
134
  subscribe(listener: () => void): () => void {
95
135
  listeners.add(listener);
96
136
 
@@ -3,6 +3,7 @@ import { flushSync } from 'react-dom';
3
3
  import { DATA_ATTR, deepEqual, fillDefaults, mountChildBlocks, type PropSchema } from '@bloklabs/core/adapters';
4
4
  import {
5
5
  BLOK_PORTAL_REGISTRY_CONFIG_KEY,
6
+ BLOK_TOOL_NAME_CONFIG_KEY,
6
7
  type BlockPortalRegistry,
7
8
  } from './block-portal-registry';
8
9
  import type { BlockAPI } from '@/types/api';
@@ -10,8 +11,14 @@ import type { BlockToolConstructorOptions, BlockToolData, ToolboxConfig } from '
10
11
 
11
12
  export type { PropSchema, PropSchemaEntry } from '@bloklabs/core/adapters';
12
13
 
14
+ /** Adapter-internal config keys, stripped before the config reaches the component. */
15
+ const INTERNAL_CONFIG_KEYS: readonly string[] = [
16
+ BLOK_PORTAL_REGISTRY_CONFIG_KEY,
17
+ BLOK_TOOL_NAME_CONFIG_KEY,
18
+ ];
19
+
13
20
  /** Props handed to a React block's `component` (the only data write path is `commit`). */
14
- export interface ReactBlockRenderProps<Data> {
21
+ export interface ReactBlockRenderProps<Data, Config = Record<string, unknown>> {
15
22
  /** Frozen snapshot of the block data. Re-rendered with a new value on change; never mutate. */
16
23
  data: Readonly<Data>;
17
24
  /** The ONLY data write path: merge a partial patch and sync once. */
@@ -26,12 +33,21 @@ export interface ReactBlockRenderProps<Data> {
26
33
  * the editor is read-only (same contract as a vanilla tool's `setReadOnly`).
27
34
  */
28
35
  readOnly: boolean;
36
+ /**
37
+ * The tool's `config` from the consumer's `tools` map (adapter-internal keys
38
+ * stripped) — the first-class channel for host props (permissions, CDN/upload
39
+ * URLs, locale…). No context provider needed. Under `useBlok`/`BlokEditor`
40
+ * this prop is LIVE: functions always call the latest render's closure, and
41
+ * changed non-function values are pushed to mounted blocks in place — so
42
+ * config values never belong in `deps`.
43
+ */
44
+ config: Readonly<Partial<Config>>;
29
45
  /** Engine-owned child slot — render `<BlockChildren />` for a container block. */
30
46
  BlockChildren: ComponentType;
31
47
  }
32
48
 
33
49
  /** Spec for {@link createReactBlock}. */
34
- export interface CreateReactBlockSpec<Data = BlockToolData> {
50
+ export interface CreateReactBlockSpec<Data = BlockToolData, Config = Record<string, unknown>> {
35
51
  /** Tool type name (registered key). */
36
52
  type: string;
37
53
  /** Optional toolbox entry. */
@@ -39,7 +55,7 @@ export interface CreateReactBlockSpec<Data = BlockToolData> {
39
55
  /** Declarative defaults that also define the exact `save()` key set. */
40
56
  propSchema: PropSchema;
41
57
  /** The component rendered for each block instance. */
42
- component: ComponentType<ReactBlockRenderProps<Data>>;
58
+ component: ComponentType<ReactBlockRenderProps<Data, Config>>;
43
59
  /** Optional lifecycle callbacks mapped from Blok's block hooks. */
44
60
  onRendered?: (block: BlockAPI) => void;
45
61
  onMoved?: (block: BlockAPI) => void;
@@ -64,8 +80,8 @@ export interface CreateReactBlockSpec<Data = BlockToolData> {
64
80
  * - `commit()` merges a patch and fires `dispatchChange` exactly once.
65
81
  * - `removed()`/`destroy()` unregister the portal (deterministic unmount).
66
82
  */
67
- export function createReactBlock<Data = BlockToolData>(
68
- spec: CreateReactBlockSpec<Data>
83
+ export function createReactBlock<Data = BlockToolData, Config = Record<string, unknown>>(
84
+ spec: CreateReactBlockSpec<Data, Config>
69
85
  ): (new (options: BlockToolConstructorOptions) => {
70
86
  render(): HTMLElement;
71
87
  save(): BlockToolData;
@@ -100,6 +116,10 @@ export function createReactBlock<Data = BlockToolData>(
100
116
 
101
117
  private readonly blockApi: BlockAPI;
102
118
  private readonly registry: BlockPortalRegistry | undefined;
119
+ /** Name this tool is registered under in the consumer's `tools` map. */
120
+ private readonly toolName: string | undefined;
121
+ /** The consumer's tool config with adapter-internal keys stripped. */
122
+ private readonly toolConfig: Readonly<Partial<Config>>;
103
123
  private readonly pointerDrag: () => boolean;
104
124
  private readonly childrenComponent: ComponentType;
105
125
  private mirror: Readonly<Data>;
@@ -117,6 +137,18 @@ export function createReactBlock<Data = BlockToolData>(
117
137
  const config = (options.config ?? {}) as Record<string, unknown>;
118
138
 
119
139
  this.registry = config[BLOK_PORTAL_REGISTRY_CONFIG_KEY] as BlockPortalRegistry | undefined;
140
+ const toolName = config[BLOK_TOOL_NAME_CONFIG_KEY];
141
+
142
+ this.toolName = typeof toolName === 'string' ? toolName : undefined;
143
+
144
+ const publicConfig: Record<string, unknown> = {};
145
+
146
+ for (const [key, value] of Object.entries(config)) {
147
+ if (!INTERNAL_CONFIG_KEYS.includes(key)) {
148
+ publicConfig[key] = value;
149
+ }
150
+ }
151
+ this.toolConfig = Object.freeze(publicConfig) as Readonly<Partial<Config>>;
120
152
 
121
153
  // Read the LIVE pointer-drag flag so a mid-drag commit can be deferred
122
154
  // (core silently drops a dispatchChange while a drag is active).
@@ -160,6 +192,7 @@ export function createReactBlock<Data = BlockToolData>(
160
192
  hostEl: host,
161
193
  component: spec.component as unknown as ComponentType<Record<string, unknown>>,
162
194
  props: this.buildProps(),
195
+ toolName: this.toolName,
163
196
  });
164
197
 
165
198
  // Returned synchronously; React flushes the portal on its next commit.
@@ -224,11 +257,12 @@ export function createReactBlock<Data = BlockToolData>(
224
257
 
225
258
  /** The full entry props handed to the component on (re-)register. */
226
259
  private buildProps(): Record<string, unknown> {
227
- const props: ReactBlockRenderProps<Data> = {
260
+ const props: ReactBlockRenderProps<Data, Config> = {
228
261
  data: this.mirror,
229
262
  commit: this.commit,
230
263
  block: this.blockApi,
231
264
  readOnly: this.readOnly,
265
+ config: this.toolConfig,
232
266
  BlockChildren: this.childrenComponent,
233
267
  };
234
268
 
@@ -262,6 +296,14 @@ export function createReactBlock<Data = BlockToolData>(
262
296
  ...(patch as Record<string, unknown>),
263
297
  });
264
298
 
299
+ // Idempotent: a patch that changes nothing is a full no-op — no props
300
+ // push, no dispatchChange. This is what makes an effect that echoes the
301
+ // current value back through commit safe without a consumer-side guard
302
+ // (an unguarded echo would otherwise loop commit → render → effect).
303
+ if (deepEqual(next, this.mirror)) {
304
+ return;
305
+ }
306
+
265
307
  this.mirror = next;
266
308
  this.lastRendered = next;
267
309
  // No flushSync here: commit is called from React event handlers (inside a
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export type {
15
15
  export {
16
16
  createBlockPortalRegistry,
17
17
  BLOK_PORTAL_REGISTRY_CONFIG_KEY,
18
+ BLOK_TOOL_NAME_CONFIG_KEY,
18
19
  } from './block-portal-registry';
19
20
  export type { BlockPortalRegistry, BlockPortalEntry } from './block-portal-registry';
20
21
  export { BlockPortalHost } from './BlockPortalHost';
@@ -38,6 +38,48 @@ const resolveAtPath = (root: unknown, path: readonly (string | number)[]): unkno
38
38
  return undefined;
39
39
  }, root);
40
40
 
41
+ /**
42
+ * Build the component-facing `config` for a react block from the LATEST render's
43
+ * raw config, preferring the stable live wrapper from `wrappedConfig` wherever
44
+ * both sides hold a function at the same path. The result carries the newest
45
+ * non-function VALUES while keeping function identities stable across renders
46
+ * (wrappers already delegate to the latest closure), so a deep-equal dedupe of
47
+ * successive results only fires when a value genuinely changed.
48
+ */
49
+ export const buildLiveBlockConfig = (
50
+ wrappedConfig: unknown,
51
+ latestConfig: unknown
52
+ ): Record<string, unknown> => {
53
+ const merge = (latest: unknown, wrapped: unknown): unknown => {
54
+ if (typeof latest === 'function') {
55
+ return typeof wrapped === 'function' ? wrapped : latest;
56
+ }
57
+
58
+ if (Array.isArray(latest)) {
59
+ const wrappedArray = Array.isArray(wrapped) ? wrapped : [];
60
+
61
+ return latest.map((item, index) => merge(item, wrappedArray[index]));
62
+ }
63
+
64
+ if (isPlainObject(latest)) {
65
+ const wrappedObject = isPlainObject(wrapped) ? wrapped : {};
66
+ const result: Record<string, unknown> = {};
67
+
68
+ for (const [key, value] of Object.entries(latest)) {
69
+ result[key] = merge(value, wrappedObject[key]);
70
+ }
71
+
72
+ return result;
73
+ }
74
+
75
+ return latest;
76
+ };
77
+
78
+ const merged = merge(latestConfig, wrappedConfig);
79
+
80
+ return isPlainObject(merged) ? merged : {};
81
+ };
82
+
41
83
  /**
42
84
  * Return a copy of `tools` where every function inside a tool's `config` is
43
85
  * replaced by a stable wrapper that calls the function found at the same path
package/src/useBlok.ts CHANGED
@@ -6,10 +6,11 @@ import { BlokDefaultsContext, mergeBlokDefaults } from './provide-blok';
6
6
  import {
7
7
  createBlockPortalRegistry,
8
8
  BLOK_PORTAL_REGISTRY_CONFIG_KEY,
9
+ BLOK_TOOL_NAME_CONFIG_KEY,
9
10
  type BlockPortalRegistry,
10
11
  } from './block-portal-registry';
11
12
  import { setRegistry, removeRegistry } from './registry-map';
12
- import { bindLiveToolConfigFunctions } from './live-tool-config';
13
+ import { bindLiveToolConfigFunctions, buildLiveBlockConfig } from './live-tool-config';
13
14
  import { normalizeReadOnlyConfig } from '@bloklabs/core/adapters';
14
15
  import type { Blok } from '@/types';
15
16
  import type { UseBlokConfig } from './types';
@@ -32,6 +33,15 @@ interface EditorInstanceState {
32
33
  * CORE, outside any React render, so it cannot read context — this config
33
34
  * bridge is how the tool reaches its editor-scoped registry.
34
35
  */
36
+ const isReactBlockEntry = (entry: unknown): boolean => {
37
+ const toolClass = typeof entry === 'function' ? entry : (entry as { class?: unknown })?.class;
38
+
39
+ return (
40
+ typeof toolClass === 'function' &&
41
+ (toolClass as { __isBlokReactBlock?: boolean }).__isBlokReactBlock === true
42
+ );
43
+ };
44
+
35
45
  const injectPortalRegistry = (tools: unknown, registry: BlockPortalRegistry): unknown => {
36
46
  if (tools === null || typeof tools !== 'object') {
37
47
  return tools;
@@ -40,12 +50,7 @@ const injectPortalRegistry = (tools: unknown, registry: BlockPortalRegistry): un
40
50
  const result: Record<string, unknown> = {};
41
51
 
42
52
  for (const [name, entry] of Object.entries(tools as Record<string, unknown>)) {
43
- const toolClass = typeof entry === 'function' ? entry : (entry as { class?: unknown })?.class;
44
- const isReactBlock =
45
- typeof toolClass === 'function' &&
46
- (toolClass as { __isBlokReactBlock?: boolean }).__isBlokReactBlock === true;
47
-
48
- if (!isReactBlock) {
53
+ if (!isReactBlockEntry(entry)) {
49
54
  result[name] = entry;
50
55
 
51
56
  continue;
@@ -57,6 +62,7 @@ const injectPortalRegistry = (tools: unknown, registry: BlockPortalRegistry): un
57
62
  base.config = {
58
63
  ...((base.config as Record<string, unknown> | undefined) ?? {}),
59
64
  [BLOK_PORTAL_REGISTRY_CONFIG_KEY]: registry,
65
+ [BLOK_TOOL_NAME_CONFIG_KEY]: name,
60
66
  };
61
67
  result[name] = base;
62
68
  }
@@ -122,6 +128,12 @@ export function useBlok(configInput: UseBlokConfig, deps?: DependencyList): Blok
122
128
  // success baseline — so a failed render can't strand the baseline on content
123
129
  // the editor never actually showed.
124
130
  const pendingDataRef = useRef<UseBlokConfig['data']>(undefined);
131
+ // Live block-config channel (see the sync effect below): the current editor's
132
+ // portal registry, its construction-time wrapped tools (stable function
133
+ // wrappers), and the last config pushed per tool name (dedupe baseline).
134
+ const liveRegistryRef = useRef<BlockPortalRegistry | null>(null);
135
+ const wrappedToolsRef = useRef<unknown>(undefined);
136
+ const pushedBlockConfigsRef = useRef<Map<string, Record<string, unknown>>>(new Map());
125
137
 
126
138
  // Main lifecycle effect
127
139
  useEffect(() => {
@@ -191,13 +203,17 @@ export function useBlok(configInput: UseBlokConfig, deps?: DependencyList): Blok
191
203
  // wrappers that always call the latest render's closure — so consumers
192
204
  // never need to freeze identities or recreate the editor to update them.
193
205
  const registry = createBlockPortalRegistry();
194
- const liveTools =
206
+ const wrappedTools =
195
207
  currentConfig.tools === undefined
196
208
  ? undefined
197
- : injectPortalRegistry(
198
- bindLiveToolConfigFunctions(currentConfig.tools, () => configRef.current.tools),
199
- registry
200
- );
209
+ : bindLiveToolConfigFunctions(currentConfig.tools, () => configRef.current.tools);
210
+ const liveTools = wrappedTools === undefined ? undefined : injectPortalRegistry(wrappedTools, registry);
211
+
212
+ // Reset the live block-config channel for this editor: the sync effect below
213
+ // reads these to push changed non-function config VALUES to mounted blocks.
214
+ liveRegistryRef.current = registry;
215
+ wrappedToolsRef.current = wrappedTools;
216
+ pushedBlockConfigsRef.current = new Map();
201
217
 
202
218
  const blokConfig = {
203
219
  ...currentConfig,
@@ -277,6 +293,58 @@ export function useBlok(configInput: UseBlokConfig, deps?: DependencyList): Blok
277
293
  };
278
294
  }, [depsToken]);
279
295
 
296
+ // Live tool-config VALUES for react blocks. Function slots are already live
297
+ // via the stable wrappers, but non-function values (permissions, URLs,
298
+ // locale…) were snapshotted at construction — which forced consumers to put
299
+ // them in `deps` and recreate the whole editor to change them. Instead, this
300
+ // effect runs on every render, rebuilds each react-block tool's
301
+ // component-facing config from the LATEST render's values (keeping wrapper
302
+ // identities for functions), and pushes it through the portal registry when
303
+ // it genuinely changed — mounted blocks re-render in place, future blocks
304
+ // start from the latest config.
305
+ useEffect(() => {
306
+ const registry = liveRegistryRef.current;
307
+ const state = stateRef.current;
308
+
309
+ if (registry === null || state.editor === null || state.isDestroyed) {
310
+ return;
311
+ }
312
+
313
+ const latestTools = configRef.current.tools as Record<string, unknown> | undefined;
314
+
315
+ if (latestTools === null || typeof latestTools !== 'object') {
316
+ return;
317
+ }
318
+
319
+ const wrappedTools = wrappedToolsRef.current as Record<string, unknown> | undefined;
320
+
321
+ for (const [name, entry] of Object.entries(latestTools)) {
322
+ if (!isReactBlockEntry(entry)) {
323
+ continue;
324
+ }
325
+
326
+ const latestConfig =
327
+ typeof entry === 'function' ? {} : ((entry as { config?: unknown }).config ?? {});
328
+ const wrappedEntry = wrappedTools?.[name];
329
+ const wrappedConfig =
330
+ wrappedEntry !== null && typeof wrappedEntry === 'object'
331
+ ? (wrappedEntry as { config?: unknown }).config
332
+ : undefined;
333
+
334
+ const candidate = buildLiveBlockConfig(wrappedConfig, latestConfig);
335
+ const pushed = pushedBlockConfigsRef.current.get(name);
336
+
337
+ if (pushed !== undefined && deepEqual(candidate, pushed)) {
338
+ continue;
339
+ }
340
+
341
+ const frozen = Object.freeze(candidate);
342
+
343
+ pushedBlockConfigsRef.current.set(name, frozen);
344
+ registry.setToolConfig(name, frozen);
345
+ }
346
+ });
347
+
280
348
  // Reactive: readOnly (object form normalized so the effect dep is a stable boolean)
281
349
  const readOnlyEnabled = normalizeReadOnlyConfig(config.readOnly).enabled;
282
350
  useEffect(() => {
package/types/index.d.ts CHANGED
@@ -39,7 +39,7 @@ export interface BlokContentProps extends React.HTMLAttributes<HTMLDivElement> {
39
39
  * React hook that creates and manages a Blok editor instance.
40
40
  *
41
41
  * @param config - Editor configuration (all BlokConfig props except `holder`)
42
- * @param deps - Optional dependency array. When any dep changes, the editor is destroyed and recreated. Keep each value referentially stable (primitives or useMemo-stable objects) so the editor isn't recreated every render.
42
+ * @param deps - Optional dependency array. When any dep changes, the editor is destroyed and recreated. Keep each value referentially stable (primitives or useMemo-stable objects) so the editor isn't recreated every render. Reserve it for true identity changes (e.g. the document id): tool-config functions are re-bound to the latest render's closure automatically, and non-function config values of `createReactBlock` tools are pushed to mounted blocks in place — neither belongs in deps.
43
43
  * @returns The Blok editor instance, or null during SSR / before initialization.
44
44
  *
45
45
  * @example
@@ -81,6 +81,8 @@ export interface BlokEditorProps
81
81
  * When any value changes, the editor is destroyed and recreated. Keep each
82
82
  * value referentially stable (primitives or useMemo-stable objects) — a dep
83
83
  * whose identity changes every render recreates the editor each time.
84
+ * Reserve it for true identity changes (e.g. the document id): tool-config
85
+ * functions and `createReactBlock` config values stay live without it.
84
86
  */
85
87
  deps?: React.DependencyList;
86
88
  /** Test id forwarded to the editor container element (via data-testid). */
@@ -555,10 +557,14 @@ export interface PropSchemaEntry {
555
557
  export type PropSchema = Record<string, PropSchemaEntry>;
556
558
 
557
559
  /** Props handed to a React block's `component` (the only data write path is `commit`). */
558
- export interface ReactBlockRenderProps<Data> {
560
+ export interface ReactBlockRenderProps<Data, Config = Record<string, unknown>> {
559
561
  /** Frozen snapshot of the block data. Re-rendered with a new value on change; never mutate. */
560
562
  data: Readonly<Data>;
561
- /** The ONLY data write path: merge a partial patch and sync once. */
563
+ /**
564
+ * The ONLY data write path: merge a partial patch and sync once. Idempotent —
565
+ * a patch that changes nothing is a full no-op (no re-render, no change
566
+ * event), so effects may safely echo current values without a guard.
567
+ */
562
568
  commit: (patch: Partial<Data>) => void;
563
569
  /** This block's per-block API. */
564
570
  block: BlockAPI;
@@ -568,16 +574,27 @@ export interface ReactBlockRenderProps<Data> {
568
574
  * interactive when the editor is read-only.
569
575
  */
570
576
  readOnly: boolean;
577
+ /**
578
+ * The tool's `config` from the consumer's `tools` map (adapter-internal keys
579
+ * stripped) — the first-class channel for host props (permissions, CDN/upload
580
+ * URLs, locale…). LIVE under `useBlok`/`BlokEditor`: functions always call
581
+ * the latest render's closure, and changed non-function values are pushed to
582
+ * mounted blocks in place — config values never belong in `deps`.
583
+ */
584
+ config: Readonly<Partial<Config>>;
571
585
  /** Engine-owned child slot — render `<BlockChildren />` for a container block. */
572
586
  BlockChildren: React.ComponentType;
573
587
  }
574
588
 
575
589
  /** Spec for {@link createReactBlock}. */
576
- export interface CreateReactBlockSpec<Data extends BlockToolData = BlockToolData> {
590
+ export interface CreateReactBlockSpec<
591
+ Data extends BlockToolData = BlockToolData,
592
+ Config = Record<string, unknown>
593
+ > {
577
594
  type: string;
578
595
  toolbox?: ToolboxConfig;
579
596
  propSchema: PropSchema;
580
- component: React.ComponentType<ReactBlockRenderProps<Data>>;
597
+ component: React.ComponentType<ReactBlockRenderProps<Data, Config>>;
581
598
  onRendered?: (block: BlockAPI) => void;
582
599
  onMoved?: (block: BlockAPI) => void;
583
600
  onRemoved?: (block: BlockAPI) => void;
@@ -602,9 +619,10 @@ export interface CreateReactBlockSpec<Data extends BlockToolData = BlockToolData
602
619
  * });
603
620
  * ```
604
621
  */
605
- export declare function createReactBlock<Data extends BlockToolData = BlockToolData>(
606
- spec: CreateReactBlockSpec<Data>
607
- ): BlockToolConstructable;
622
+ export declare function createReactBlock<
623
+ Data extends BlockToolData = BlockToolData,
624
+ Config = Record<string, unknown>
625
+ >(spec: CreateReactBlockSpec<Data, Config>): BlockToolConstructable;
608
626
 
609
627
  /**
610
628
  * Tool-config key carrying the editor's portal registry into a
@@ -613,11 +631,20 @@ export declare function createReactBlock<Data extends BlockToolData = BlockToolD
613
631
  */
614
632
  export declare const BLOK_PORTAL_REGISTRY_CONFIG_KEY: '__blokPortalRegistry';
615
633
 
634
+ /**
635
+ * Tool-config key carrying the name a react-block tool is registered under in
636
+ * the consumer's `tools` map. Injected automatically by `useBlok`; routes live
637
+ * tool-config updates to the tool's mounted blocks.
638
+ */
639
+ export declare const BLOK_TOOL_NAME_CONFIG_KEY: '__blokToolName';
640
+
616
641
  /** One mounted React block in the portal registry. */
617
642
  export interface BlockPortalEntry {
618
643
  hostEl: HTMLElement;
619
644
  component: React.ComponentType<Record<string, unknown>>;
620
645
  props: Record<string, unknown>;
646
+ /** Name the tool is registered under — routes per-tool live config updates. */
647
+ toolName?: string;
621
648
  }
622
649
 
623
650
  /**
@@ -630,6 +657,11 @@ export interface BlockPortalRegistry {
630
657
  register(id: string, entry: BlockPortalEntry): void;
631
658
  unregister(id: string): void;
632
659
  setProps(id: string, props: Record<string, unknown>): void;
660
+ /**
661
+ * Replace the `config` prop of every entry registered under `toolName`, and
662
+ * remember it so entries registered later start from the latest config.
663
+ */
664
+ setToolConfig(toolName: string, config: Record<string, unknown>): void;
633
665
  subscribe(listener: () => void): () => void;
634
666
  getSnapshot(): ReadonlyMap<string, BlockPortalEntry>;
635
667
  }