@jackuait/blok 0.23.1 → 0.23.3

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.
Files changed (49) hide show
  1. package/README.md +3 -1
  2. package/dist/blok.cjs +1 -1
  3. package/dist/blok.iife.js +73 -73
  4. package/dist/blok.mjs +3 -3
  5. package/dist/blok.umd.js +53 -53
  6. package/dist/chunks/{blok-BPeM6wKv.mjs → blok-1H9roBGf.mjs} +2271 -2249
  7. package/dist/chunks/{blok-Ci1zCOUg.cjs → blok-J3Lfa_bK.cjs} +13 -13
  8. package/dist/chunks/{constants-DR3sya6U.mjs → constants-BJ79cqMa.mjs} +288 -284
  9. package/dist/chunks/constants-BZUc1kAY.cjs +606 -0
  10. package/dist/chunks/{tools-B72rczVw.mjs → tools-ByOjWADT.mjs} +420 -418
  11. package/dist/chunks/{tools-DlOZpcVU.cjs → tools-OHDv2C7w.cjs} +2 -2
  12. package/dist/full.cjs +1 -1
  13. package/dist/full.mjs +3 -3
  14. package/dist/react.cjs +1 -1
  15. package/dist/react.mjs +54 -33
  16. package/dist/tools.cjs +1 -1
  17. package/dist/tools.mjs +2 -2
  18. package/package.json +1 -1
  19. package/src/blok.ts +12 -0
  20. package/src/components/block/tool-renderer.ts +2 -2
  21. package/src/components/blocks.ts +32 -10
  22. package/src/components/constants/test-ids.ts +17 -0
  23. package/src/components/constants.ts +5 -0
  24. package/src/components/events/BlockRendered.ts +9 -0
  25. package/src/components/events/BlocksRendered.ts +13 -0
  26. package/src/components/events/index.ts +8 -0
  27. package/src/components/inline-tools/inline-tool-link.ts +23 -9
  28. package/src/components/modules/api/events.ts +10 -10
  29. package/src/components/modules/api/index.ts +3 -0
  30. package/src/components/modules/api/tools.ts +3 -0
  31. package/src/components/modules/blockManager/blockManager.ts +1 -1
  32. package/src/components/modules/paste/index.ts +10 -1
  33. package/src/components/modules/renderer.ts +146 -131
  34. package/src/components/modules/toolbar/plus-button.ts +2 -1
  35. package/src/components/modules/toolbar/settings-toggler.ts +2 -2
  36. package/src/components/modules/tools.ts +18 -0
  37. package/src/components/tools/factory.ts +28 -1
  38. package/src/react/BlokEditor.tsx +17 -5
  39. package/src/react/config-keys.ts +2 -0
  40. package/src/react/deep-equal.ts +48 -0
  41. package/src/react/types.ts +2 -0
  42. package/src/react/useBlok.ts +37 -0
  43. package/types/api/events.d.ts +35 -5
  44. package/types/api/tools.d.ts +16 -1
  45. package/types/configs/blok-config.d.ts +37 -0
  46. package/types/events/editor-events.ts +41 -0
  47. package/types/index.d.ts +5 -1
  48. package/types/react.d.ts +13 -2
  49. package/dist/chunks/constants-Bkncy0y5.cjs +0 -606
package/dist/full.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/blok-Ci1zCOUg.cjs`),t=require(`./chunks/constants-Bkncy0y5.cjs`),n=require(`./chunks/objectSpread2-iNkE6aJh.cjs`),r=require(`./chunks/tools-DlOZpcVU.cjs`);var i={paragraph:{class:r.k,inlineToolbar:!0,config:{preserveBlank:!0}},header:{class:r.O,inlineToolbar:!0},list:{class:r.D,inlineToolbar:!0}},a=n.t(n.t({},i),{},{bold:{class:r.l},italic:{class:r.c},marker:{class:r.o},underline:{class:r.a},strikethrough:{class:r.i},link:{class:r.s}});exports.Blok=e.t,exports.Bold=r.l,exports.DATA_ATTR=t.ja,exports.Header=r.O,exports.Italic=r.c,exports.Link=r.s,exports.List=r.D,exports.Marker=r.o,exports.Paragraph=r.k,exports.Strikethrough=r.i,exports.Toggle=r.T,exports.Underline=r.a,exports.allTools=a,exports.defaultBlockTools=r.t,exports.defaultInlineTools=r.n,exports.defaultTools=i,exports.version=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/blok-J3Lfa_bK.cjs`),t=require(`./chunks/constants-BZUc1kAY.cjs`),n=require(`./chunks/objectSpread2-iNkE6aJh.cjs`),r=require(`./chunks/tools-OHDv2C7w.cjs`);var i={paragraph:{class:r.k,inlineToolbar:!0,config:{preserveBlank:!0}},header:{class:r.O,inlineToolbar:!0},list:{class:r.D,inlineToolbar:!0}},a=n.t(n.t({},i),{},{bold:{class:r.l},italic:{class:r.c},marker:{class:r.o},underline:{class:r.a},strikethrough:{class:r.i},link:{class:r.s}});exports.Blok=e.t,exports.Bold=r.l,exports.DATA_ATTR=t.Ma,exports.Header=r.O,exports.Italic=r.c,exports.Link=r.s,exports.List=r.D,exports.Marker=r.o,exports.Paragraph=r.k,exports.Strikethrough=r.i,exports.Toggle=r.T,exports.Underline=r.a,exports.allTools=a,exports.defaultBlockTools=r.t,exports.defaultInlineTools=r.n,exports.defaultTools=i,exports.version=e.n;
package/dist/full.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { n as e, t } from "./chunks/blok-BPeM6wKv.mjs";
2
- import { ja as n } from "./chunks/constants-DR3sya6U.mjs";
1
+ import { n as e, t } from "./chunks/blok-1H9roBGf.mjs";
2
+ import { Ma as n } from "./chunks/constants-BJ79cqMa.mjs";
3
3
  import { t as r } from "./chunks/objectSpread2-jlBcdYeb.mjs";
4
- import { D as i, O as a, T as o, a as s, c, i as l, k as u, l as d, n as f, o as p, s as m, t as h } from "./chunks/tools-B72rczVw.mjs";
4
+ import { D as i, O as a, T as o, a as s, c, i as l, k as u, l as d, n as f, o as p, s as m, t as h } from "./chunks/tools-ByOjWADT.mjs";
5
5
  //#region src/full.ts
6
6
  var g = {
7
7
  paragraph: {
package/dist/react.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunks/chunk-BL1yB9zm.cjs`);const e=require(`./chunks/blok-Ci1zCOUg.cjs`),t=require(`./chunks/objectSpread2-iNkE6aJh.cjs`);let n=require(`react`),r=require(`react/jsx-runtime`);var i=new WeakMap;function a(e,t){i.set(e,t)}function o(e){return i.get(e)}function s(e){i.delete(e)}function c(r,i){let[o,c]=(0,n.useState)(null),u=(0,n.useRef)(r);u.current=r;let d=(0,n.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,depsToken:null}),f=(0,n.useMemo)(()=>({}),i==null?[]:i);(0,n.useEffect)(()=>{if(typeof window>`u`)return;let n=d.current;if(n.destroyTimeout!==null&&(clearTimeout(n.destroyTimeout),n.destroyTimeout=null),n.editor!==null&&!n.isDestroyed&&n.depsToken===f)return c(n.editor),()=>{l(n,c)};if(n.editor!==null&&!n.isDestroyed){s(n.editor);try{n.editor.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,c(null)}let r=document.createElement(`div`);n.holder=r,n.isDestroyed=!1,n.depsToken=f;let i=u.current,o=new e.t(t.t(t.t({},i),{},{holder:r,onReady:()=>{var e,t;(e=(t=u.current).onReady)==null||e.call(t)},onChange:(...e)=>{var t,n;(t=(n=u.current).onChange)==null||t.call(n,...e)}}));return n.editor=o,a(o,r),o.isReady.then(()=>{n.editor===o&&!n.isDestroyed&&c(o)}).catch(()=>{if(n.editor===o&&!n.isDestroyed){s(o);try{o.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,c(null)}}),()=>{l(n,c)}},[f]);let{readOnly:p}=r;(0,n.useEffect)(()=>{o!==null&&o.readOnly.set(p==null?!1:p)},[o,p]);let{autofocus:m}=r;(0,n.useEffect)(()=>{o===null||!m||o.focus()},[o,m]);let{theme:h}=r;(0,n.useEffect)(()=>{o===null||h===void 0||o.theme.set(h)},[o,h]);let{width:g}=r;(0,n.useEffect)(()=>{o===null||g===void 0||o.width.set(g)},[o,g]);let{placeholder:_}=r;return(0,n.useEffect)(()=>{o===null||_===void 0||o.placeholder.set(_)},[o,_]),o}function l(e,t){e.destroyTimeout=setTimeout(()=>{if(e.editor!==null){s(e.editor);try{e.editor.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.destroyTimeout=null,t(null)}},0)}var u=[`editor`],d=(0,n.forwardRef)(function(i,a){let{editor:s}=i,c=e.i(i,u),l=(0,n.useRef)(null);return(0,n.useEffect)(()=>{if(s===null||l.current===null)return;let e=o(s);if(e!==void 0)return l.current.appendChild(e),()=>{e.remove()}},[s]),(0,r.jsx)(`div`,t.t({ref:e=>{if(l.current=e,typeof a==`function`)a(e);else if(a!=null){let t=a;t.current=e}}},c))}),f=`autofocus.defaultBlock.dataModel.placeholder.sanitizer.hideToolbar.maxHistoryLength.historyDebounceTime.newGroupDelay.globalUndoRedo.tools.data.minHeight.logLevel.readOnly.i18n.linkPaste.onReady.onChange.inlineToolbar.tunes.style.theme.onThemeChange.scrollToBlock.user.resolveUser.notifierPosition.notifier.width`.split(`.`),p=[`deps`,`onReady`],m=new Set(f),h=(0,n.forwardRef)(function(i,a){let{deps:o,onReady:s}=i,l=e.i(i,p),u={},f={};for(let e of Object.keys(l)){let t=l[e];m.has(e)?u[e]=t:f[e]=t}let h=c(u,o);(0,n.useImperativeHandle)(a,()=>h,[h]);let g=(0,n.useRef)(s);return g.current=s,(0,n.useEffect)(()=>{if(h!==null){var e;(e=g.current)==null||e.call(g,h)}},[h]),(0,r.jsx)(d,t.t({editor:h},f))});exports.BlokContent=d,exports.BlokEditor=h,exports.useBlok=c;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunks/chunk-BL1yB9zm.cjs`);const e=require(`./chunks/blok-J3Lfa_bK.cjs`),t=require(`./chunks/objectSpread2-iNkE6aJh.cjs`);let n=require(`react`),r=require(`react/jsx-runtime`);var i=new WeakMap;function a(e,t){i.set(e,t)}function o(e){return i.get(e)}function s(e){i.delete(e)}function c(e,t){if(e===t)return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;let n=Array.isArray(e),r=Array.isArray(t);if(n!==r)return!1;if(n&&r)return e.length===t.length?e.every((e,n)=>c(e,t[n])):!1;let i=e,a=t,o=Object.keys(i),s=Object.keys(a);return o.length===s.length?o.every(e=>Object.prototype.hasOwnProperty.call(a,e)&&c(i[e],a[e])):!1}function l(r,i){let[o,l]=(0,n.useState)(null),d=(0,n.useRef)(r);d.current=r;let f=(0,n.useRef)({editor:null,holder:null,destroyTimeout:null,isDestroyed:!1,depsToken:null}),p=(0,n.useMemo)(()=>({}),i==null?[]:i);(0,n.useEffect)(()=>{if(typeof window>`u`)return;let n=f.current;if(n.destroyTimeout!==null&&(clearTimeout(n.destroyTimeout),n.destroyTimeout=null),n.editor!==null&&!n.isDestroyed&&n.depsToken===p)return l(n.editor),()=>{u(n,l)};if(n.editor!==null&&!n.isDestroyed){s(n.editor);try{n.editor.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,l(null)}let r=document.createElement(`div`);n.holder=r,n.isDestroyed=!1,n.depsToken=p;let i=d.current,o=new e.t(t.t(t.t({},i),{},{holder:r,onReady:()=>{var e,t;(e=(t=d.current).onReady)==null||e.call(t)},onChange:(...e)=>{var t,n;(t=(n=d.current).onChange)==null||t.call(n,...e)}}));return n.editor=o,a(o,r),o.isReady.then(()=>{n.editor===o&&!n.isDestroyed&&l(o)}).catch(()=>{if(n.editor===o&&!n.isDestroyed){s(o);try{o.destroy()}catch(e){}n.editor=null,n.holder=null,n.isDestroyed=!0,l(null)}}),()=>{u(n,l)}},[p]);let{readOnly:m}=r;(0,n.useEffect)(()=>{o!==null&&o.readOnly.set(m==null?!1:m)},[o,m]);let{autofocus:h}=r;(0,n.useEffect)(()=>{o===null||!h||o.focus()},[o,h]);let{theme:g}=r;(0,n.useEffect)(()=>{o===null||g===void 0||o.theme.set(g)},[o,g]);let{width:_}=r;(0,n.useEffect)(()=>{o===null||_===void 0||o.width.set(_)},[o,_]);let{placeholder:v}=r;(0,n.useEffect)(()=>{o===null||v===void 0||o.placeholder.set(v)},[o,v]);let{data:y}=r,b=(0,n.useRef)(r.data),x=(0,n.useRef)(null),S=(0,n.useRef)(Promise.resolve());return(0,n.useEffect)(()=>{if(!(o===null||y===void 0)){if(x.current!==o){x.current=o,b.current=y;return}c(y,b.current)||(b.current=y,S.current=S.current.catch(()=>void 0).then(()=>o.render(y)))}},[o,y]),o}function u(e,t){e.destroyTimeout=setTimeout(()=>{if(e.editor!==null){s(e.editor);try{e.editor.destroy()}catch(e){}e.editor=null,e.holder=null,e.isDestroyed=!0,e.destroyTimeout=null,t(null)}},0)}var d=[`editor`],f=(0,n.forwardRef)(function(i,a){let{editor:s}=i,c=e.i(i,d),l=(0,n.useRef)(null);return(0,n.useEffect)(()=>{if(s===null||l.current===null)return;let e=o(s);if(e!==void 0)return l.current.appendChild(e),()=>{e.remove()}},[s]),(0,r.jsx)(`div`,t.t({ref:e=>{if(l.current=e,typeof a==`function`)a(e);else if(a!=null){let t=a;t.current=e}}},c))}),p=`autofocus.defaultBlock.dataModel.placeholder.sanitizer.hideToolbar.maxHistoryLength.historyDebounceTime.newGroupDelay.globalUndoRedo.tools.data.minHeight.logLevel.readOnly.i18n.link.linkPaste.onBeforePaste.onReady.onChange.inlineToolbar.tunes.style.theme.onThemeChange.scrollToBlock.user.resolveUser.notifierPosition.notifier.width`.split(`.`),m=[`deps`,`onReady`],h=new Set(p),g=(0,n.forwardRef)(function(i,a){let{deps:o,onReady:s}=i,c=e.i(i,m),u={},d={};for(let e of Object.keys(c)){let t=c[e];h.has(e)?u[e]=t:d[e]=t}let p=l(u,o);(0,n.useImperativeHandle)(a,()=>p,[p]);let g=(0,n.useRef)(s);return g.current=s,(0,n.useEffect)(()=>{if(p!==null){var e;(e=g.current)==null||e.call(g,p)}},[p]),(0,r.jsx)(f,t.t({editor:p},d))});exports.BlokContent=f,exports.BlokEditor=g,exports.useBlok=l;
package/dist/react.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { i as e, t } from "./chunks/blok-BPeM6wKv.mjs";
1
+ import { i as e, t } from "./chunks/blok-1H9roBGf.mjs";
2
2
  import { t as n } from "./chunks/objectSpread2-jlBcdYeb.mjs";
3
3
  import { forwardRef as r, useEffect as i, useImperativeHandle as a, useMemo as o, useRef as s, useState as c } from "react";
4
4
  import { jsx as l } from "react/jsx-runtime";
@@ -14,8 +14,19 @@ function p(e) {
14
14
  u.delete(e);
15
15
  }
16
16
  //#endregion
17
+ //#region src/react/deep-equal.ts
18
+ function m(e, t) {
19
+ if (e === t) return !0;
20
+ if (typeof e != "object" || !e || typeof t != "object" || !t) return !1;
21
+ let n = Array.isArray(e), r = Array.isArray(t);
22
+ if (n !== r) return !1;
23
+ if (n && r) return e.length === t.length ? e.every((e, n) => m(e, t[n])) : !1;
24
+ let i = e, a = t, o = Object.keys(i), s = Object.keys(a);
25
+ return o.length === s.length ? o.every((e) => Object.prototype.hasOwnProperty.call(a, e) && m(i[e], a[e])) : !1;
26
+ }
27
+ //#endregion
17
28
  //#region src/react/useBlok.ts
18
- function m(e, r) {
29
+ function h(e, r) {
19
30
  let [a, l] = c(null), u = s(e);
20
31
  u.current = e;
21
32
  let f = s({
@@ -24,12 +35,12 @@ function m(e, r) {
24
35
  destroyTimeout: null,
25
36
  isDestroyed: !1,
26
37
  depsToken: null
27
- }), m = o(() => ({}), r == null ? [] : r);
38
+ }), h = o(() => ({}), r == null ? [] : r);
28
39
  i(() => {
29
40
  if (typeof window > "u") return;
30
41
  let e = f.current;
31
- if (e.destroyTimeout !== null && (clearTimeout(e.destroyTimeout), e.destroyTimeout = null), e.editor !== null && !e.isDestroyed && e.depsToken === m) return l(e.editor), () => {
32
- h(e, l);
42
+ if (e.destroyTimeout !== null && (clearTimeout(e.destroyTimeout), e.destroyTimeout = null), e.editor !== null && !e.isDestroyed && e.depsToken === h) return l(e.editor), () => {
43
+ g(e, l);
33
44
  };
34
45
  if (e.editor !== null && !e.isDestroyed) {
35
46
  p(e.editor);
@@ -39,7 +50,7 @@ function m(e, r) {
39
50
  e.editor = null, e.holder = null, e.isDestroyed = !0, l(null);
40
51
  }
41
52
  let r = document.createElement("div");
42
- e.holder = r, e.isDestroyed = !1, e.depsToken = m;
53
+ e.holder = r, e.isDestroyed = !1, e.depsToken = h;
43
54
  let i = u.current, a = new t(n(n({}, i), {}, {
44
55
  holder: r,
45
56
  onReady: () => {
@@ -62,31 +73,41 @@ function m(e, r) {
62
73
  e.editor = null, e.holder = null, e.isDestroyed = !0, l(null);
63
74
  }
64
75
  }), () => {
65
- h(e, l);
76
+ g(e, l);
66
77
  };
67
- }, [m]);
68
- let { readOnly: g } = e;
78
+ }, [h]);
79
+ let { readOnly: _ } = e;
69
80
  i(() => {
70
- a !== null && a.readOnly.set(g == null ? !1 : g);
71
- }, [a, g]);
72
- let { autofocus: _ } = e;
73
- i(() => {
74
- a === null || !_ || a.focus();
81
+ a !== null && a.readOnly.set(_ == null ? !1 : _);
75
82
  }, [a, _]);
76
- let { theme: v } = e;
83
+ let { autofocus: v } = e;
77
84
  i(() => {
78
- a === null || v === void 0 || a.theme.set(v);
85
+ a === null || !v || a.focus();
79
86
  }, [a, v]);
80
- let { width: y } = e;
87
+ let { theme: y } = e;
81
88
  i(() => {
82
- a === null || y === void 0 || a.width.set(y);
89
+ a === null || y === void 0 || a.theme.set(y);
83
90
  }, [a, y]);
84
- let { placeholder: b } = e;
91
+ let { width: b } = e;
92
+ i(() => {
93
+ a === null || b === void 0 || a.width.set(b);
94
+ }, [a, b]);
95
+ let { placeholder: x } = e;
96
+ i(() => {
97
+ a === null || x === void 0 || a.placeholder.set(x);
98
+ }, [a, x]);
99
+ let { data: S } = e, C = s(e.data), w = s(null), T = s(Promise.resolve());
85
100
  return i(() => {
86
- a === null || b === void 0 || a.placeholder.set(b);
87
- }, [a, b]), a;
101
+ if (!(a === null || S === void 0)) {
102
+ if (w.current !== a) {
103
+ w.current = a, C.current = S;
104
+ return;
105
+ }
106
+ m(S, C.current) || (C.current = S, T.current = T.current.catch(() => void 0).then(() => a.render(S)));
107
+ }
108
+ }, [a, S]), a;
88
109
  }
89
- function h(e, t) {
110
+ function g(e, t) {
90
111
  e.destroyTimeout = setTimeout(() => {
91
112
  if (e.editor !== null) {
92
113
  p(e.editor);
@@ -99,8 +120,8 @@ function h(e, t) {
99
120
  }
100
121
  //#endregion
101
122
  //#region src/react/BlokContent.tsx
102
- var g = ["editor"], _ = r(function(t, r) {
103
- let { editor: a } = t, o = e(t, g), c = s(null);
123
+ var _ = ["editor"], v = r(function(t, r) {
124
+ let { editor: a } = t, o = e(t, _), c = s(null);
104
125
  return i(() => {
105
126
  if (a === null || c.current === null) return;
106
127
  let e = f(a);
@@ -114,21 +135,21 @@ var g = ["editor"], _ = r(function(t, r) {
114
135
  t.current = e;
115
136
  }
116
137
  } }, o));
117
- }), v = /* @__PURE__ */ "autofocus.defaultBlock.dataModel.placeholder.sanitizer.hideToolbar.maxHistoryLength.historyDebounceTime.newGroupDelay.globalUndoRedo.tools.data.minHeight.logLevel.readOnly.i18n.linkPaste.onReady.onChange.inlineToolbar.tunes.style.theme.onThemeChange.scrollToBlock.user.resolveUser.notifierPosition.notifier.width".split("."), y = ["deps", "onReady"], b = new Set(v), x = r(function(t, r) {
118
- let { deps: o, onReady: c } = t, u = e(t, y), d = {}, f = {};
138
+ }), y = /* @__PURE__ */ "autofocus.defaultBlock.dataModel.placeholder.sanitizer.hideToolbar.maxHistoryLength.historyDebounceTime.newGroupDelay.globalUndoRedo.tools.data.minHeight.logLevel.readOnly.i18n.link.linkPaste.onBeforePaste.onReady.onChange.inlineToolbar.tunes.style.theme.onThemeChange.scrollToBlock.user.resolveUser.notifierPosition.notifier.width".split("."), b = ["deps", "onReady"], x = new Set(y), S = r(function(t, r) {
139
+ let { deps: o, onReady: c } = t, u = e(t, b), d = {}, f = {};
119
140
  for (let e of Object.keys(u)) {
120
141
  let t = u[e];
121
- b.has(e) ? d[e] = t : f[e] = t;
142
+ x.has(e) ? d[e] = t : f[e] = t;
122
143
  }
123
- let p = m(d, o);
144
+ let p = h(d, o);
124
145
  a(r, () => p, [p]);
125
- let h = s(c);
126
- return h.current = c, i(() => {
146
+ let m = s(c);
147
+ return m.current = c, i(() => {
127
148
  if (p !== null) {
128
149
  var e;
129
- (e = h.current) == null || e.call(h, p);
150
+ (e = m.current) == null || e.call(m, p);
130
151
  }
131
- }, [p]), /* @__PURE__ */ l(_, n({ editor: p }, f));
152
+ }, [p]), /* @__PURE__ */ l(v, n({ editor: p }, f));
132
153
  });
133
154
  //#endregion
134
- export { _ as BlokContent, x as BlokEditor, m as useBlok };
155
+ export { v as BlokContent, S as BlokEditor, h as useBlok };
package/dist/tools.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/constants-Bkncy0y5.cjs`),t=require(`./chunks/tools-DlOZpcVU.cjs`);exports.Audio=t.g,exports.Bold=t.l,exports.Bookmark=t.u,exports.Callout=t.w,exports.Code=t.y,exports.Column=t.p,exports.ColumnList=t.m,exports.Columns=t.f,exports.Convert=e.m,exports.Database=t.C,exports.DatabaseRow=t.S,exports.Divider=t.x,exports.Embed=t.d,exports.File=t._,exports.Header=t.O,exports.Image=t.v,exports.InlineCode=t.r,exports.Italic=t.c,exports.Link=t.s,exports.List=t.D,exports.Marker=t.o,exports.Paragraph=t.k,exports.Quote=t.b,exports.Strikethrough=t.i,exports.Table=t.E,exports.Toggle=t.T,exports.Underline=t.a,exports.Video=t.h,exports.defaultBlockTools=t.t,exports.defaultInlineTools=t.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunks/constants-BZUc1kAY.cjs`),t=require(`./chunks/tools-OHDv2C7w.cjs`);exports.Audio=t.g,exports.Bold=t.l,exports.Bookmark=t.u,exports.Callout=t.w,exports.Code=t.y,exports.Column=t.p,exports.ColumnList=t.m,exports.Columns=t.f,exports.Convert=e.m,exports.Database=t.C,exports.DatabaseRow=t.S,exports.Divider=t.x,exports.Embed=t.d,exports.File=t._,exports.Header=t.O,exports.Image=t.v,exports.InlineCode=t.r,exports.Italic=t.c,exports.Link=t.s,exports.List=t.D,exports.Marker=t.o,exports.Paragraph=t.k,exports.Quote=t.b,exports.Strikethrough=t.i,exports.Table=t.E,exports.Toggle=t.T,exports.Underline=t.a,exports.Video=t.h,exports.defaultBlockTools=t.t,exports.defaultInlineTools=t.n;
package/dist/tools.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { m as e } from "./chunks/constants-DR3sya6U.mjs";
2
- import { C as t, D as n, E as r, O as i, S as a, T as o, _ as s, a as c, b as l, c as u, d, f, g as p, h as m, i as h, k as g, l as _, m as v, n as y, o as b, p as x, r as S, s as C, t as w, u as T, v as E, w as D, x as O, y as k } from "./chunks/tools-B72rczVw.mjs";
1
+ import { m as e } from "./chunks/constants-BJ79cqMa.mjs";
2
+ import { C as t, D as n, E as r, O as i, S as a, T as o, _ as s, a as c, b as l, c as u, d, f, g as p, h as m, i as h, k as g, l as _, m as v, n as y, o as b, p as x, r as S, s as C, t as w, u as T, v as E, w as D, x as O, y as k } from "./chunks/tools-ByOjWADT.mjs";
3
3
  export { p as Audio, _ as Bold, T as Bookmark, D as Callout, k as Code, x as Column, v as ColumnList, f as Columns, e as Convert, t as Database, a as DatabaseRow, O as Divider, d as Embed, s as File, i as Header, E as Image, S as InlineCode, u as Italic, C as Link, n as List, b as Marker, g as Paragraph, l as Quote, h as Strikethrough, r as Table, o as Toggle, c as Underline, m as Video, w as defaultBlockTools, y as defaultInlineTools };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackuait/blok",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
4
4
  "description": "Blok — headless, highly extensible rich text editor built for developers who need to implement a block-based editing experience (similar to Notion) without building it from scratch",
5
5
  "module": "dist/blok.mjs",
6
6
  "main": "./dist/blok.cjs",
package/src/blok.ts CHANGED
@@ -25,6 +25,11 @@ export const version = getBlokVersion();
25
25
  */
26
26
  export { DATA_ATTR } from './components/constants/data-attributes';
27
27
 
28
+ /**
29
+ * Re-export the stable test-id hooks for targeting editor chrome in tests
30
+ */
31
+ export { TEST_ID } from './components/constants/test-ids';
32
+
28
33
  /**
29
34
  * Blok
30
35
  * @license Apache-2.0
@@ -542,3 +547,10 @@ export default Blok;
542
547
  * into Blok's `render()→MenuConfig` inline tool contract.
543
548
  */
544
549
  export { wrapLegacyInlineTool } from './components/inline-tools/wrap-legacy-inline-tool';
550
+
551
+ /**
552
+ * Typed event-name constants for editor lifecycle events observable via
553
+ * `blok.events.on(...)`. Prefer these over raw strings to avoid typos and
554
+ * get typed payloads (see {@link BlokEditorEventMap}).
555
+ */
556
+ export { BlockRendered, BlocksRendered } from './components/events';
@@ -1,5 +1,5 @@
1
1
  import type { BlockTool as IBlockTool , ToolConfig } from '../../../types';
2
- import { DATA_ATTR } from '../constants';
2
+ import { DATA_ATTR, TEST_ID } from '../constants';
3
3
  import { Dom as $ } from '../dom';
4
4
  import { log } from '../utils';
5
5
 
@@ -107,7 +107,7 @@ export class ToolRenderer {
107
107
  contentNode.setAttribute(DATA_ATTR.elementContent, '');
108
108
  contentNode.setAttribute('data-blok-testid', 'block-content');
109
109
 
110
- wrapper.setAttribute('data-blok-testid', 'block-wrapper');
110
+ wrapper.setAttribute(DATA_ATTR.testid, TEST_ID.blockWrapper);
111
111
 
112
112
  if (this.name && !wrapper.hasAttribute('data-blok-component')) {
113
113
  wrapper.setAttribute('data-blok-component', this.name);
@@ -1,5 +1,8 @@
1
1
  import type { Block } from './block';
2
2
  import { BlockToolAPI } from './block';
3
+ import { BlockRendered } from './events';
4
+ import type { BlokEventMap } from './events';
5
+ import type { EventsDispatcher } from './utils/events';
3
6
  import { moveElementBefore, moveElementToEnd } from './utils/html';
4
7
 
5
8
 
@@ -20,13 +23,32 @@ export class Blocks {
20
23
  */
21
24
  public workingArea: HTMLElement;
22
25
 
26
+ /**
27
+ * Editor-wide event bus used to broadcast block lifecycle events
28
+ * (e.g. `block:rendered`). Optional so the class can be used and
29
+ * unit-tested standalone without an event bus.
30
+ */
31
+ private readonly eventsDispatcher?: EventsDispatcher<BlokEventMap>;
32
+
23
33
  /**
24
34
  * @class
25
35
  * @param {HTMLElement} workingArea — blok`s working node
36
+ * @param [eventsDispatcher] - editor-wide event bus for lifecycle events
26
37
  */
27
- constructor(workingArea: HTMLElement) {
38
+ constructor(workingArea: HTMLElement, eventsDispatcher?: EventsDispatcher<BlokEventMap>) {
28
39
  this.blocks = [];
29
40
  this.workingArea = workingArea;
41
+ this.eventsDispatcher = eventsDispatcher;
42
+ }
43
+
44
+ /**
45
+ * Invokes the tool's `rendered()` lifecycle hook and broadcasts the
46
+ * `block:rendered` event for the freshly rendered block.
47
+ * @param block - block that has just been inserted into the DOM
48
+ */
49
+ private callRenderedHook(block: Block): void {
50
+ block.call(BlockToolAPI.RENDERED);
51
+ this.eventsDispatcher?.emit(BlockRendered, { blockId: block.id });
30
52
  }
31
53
 
32
54
  /**
@@ -226,7 +248,7 @@ export class Blocks {
226
248
  blockToReplace.holder.replaceWith(block.holder);
227
249
 
228
250
  this.blocks.splice(insertIndex, 1, block);
229
- block.call(BlockToolAPI.RENDERED);
251
+ this.callRenderedHook(block);
230
252
 
231
253
  return;
232
254
  }
@@ -358,7 +380,7 @@ export class Blocks {
358
380
 
359
381
  this.blocks[index] = block;
360
382
 
361
- block.call(BlockToolAPI.RENDERED);
383
+ this.callRenderedHook(block);
362
384
  }
363
385
 
364
386
  /**
@@ -388,7 +410,7 @@ export class Blocks {
388
410
  this.blocks.push(...blocks);
389
411
  this.workingArea.appendChild(fragment);
390
412
 
391
- blocks.forEach((block) => block.call(BlockToolAPI.RENDERED));
413
+ blocks.forEach((block) => this.callRenderedHook(block));
392
414
 
393
415
  return;
394
416
  }
@@ -417,7 +439,7 @@ export class Blocks {
417
439
  /**
418
440
  * Call Rendered event for each block
419
441
  */
420
- blocks.forEach((block) => block.call(BlockToolAPI.RENDERED));
442
+ blocks.forEach((block) => this.callRenderedHook(block));
421
443
  }
422
444
 
423
445
  /**
@@ -549,7 +571,7 @@ export class Blocks {
549
571
  */
550
572
  public activateBlock(block: Block): void {
551
573
  if (block.holder.parentElement !== null) {
552
- block.call(BlockToolAPI.RENDERED);
574
+ this.callRenderedHook(block);
553
575
 
554
576
  return;
555
577
  }
@@ -588,13 +610,13 @@ export class Blocks {
588
610
  private insertToDOM(block: Block, position?: InsertPosition, target?: Block): void {
589
611
  if (!position || target === undefined) {
590
612
  this.workingArea.appendChild(block.holder);
591
- block.call(BlockToolAPI.RENDERED);
613
+ this.callRenderedHook(block);
592
614
 
593
615
  return;
594
616
  }
595
617
 
596
618
  target.holder.insertAdjacentElement(position, block.holder);
597
- block.call(BlockToolAPI.RENDERED);
619
+ this.callRenderedHook(block);
598
620
  }
599
621
 
600
622
  /**
@@ -627,7 +649,7 @@ export class Blocks {
627
649
  );
628
650
  }
629
651
 
630
- block.call(BlockToolAPI.RENDERED);
652
+ this.callRenderedHook(block);
631
653
  }
632
654
 
633
655
  /**
@@ -678,7 +700,7 @@ export class Blocks {
678
700
  moveElementBefore(block.holder, nextBlock.holder);
679
701
  }
680
702
 
681
- block.call(BlockToolAPI.RENDERED);
703
+ this.callRenderedHook(block);
682
704
  }
683
705
 
684
706
  /**
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Stable, documented test-id values used with the `data-blok-testid`
3
+ * attribute ({@link DATA_ATTR.testid}).
4
+ *
5
+ * Exported as `Blok.TEST_ID` so consumers (and their test suites) can target
6
+ * editor chrome without reverse-engineering internal selectors. These values
7
+ * are part of the public contract — query them as
8
+ * `[${DATA_ATTR.testid}="${TEST_ID.plusButton}"]`.
9
+ */
10
+ export const TEST_ID = {
11
+ /** The "+" (add block) toolbar button. */
12
+ plusButton: 'plus-button',
13
+ /** The block settings / drag handle toggler (☰). */
14
+ settingsToggler: 'settings-toggler',
15
+ /** A single block's wrapper element. */
16
+ blockWrapper: 'block-wrapper',
17
+ } as const;
@@ -3,6 +3,11 @@
3
3
  */
4
4
  export { DATA_ATTR, createSelector } from './constants/data-attributes';
5
5
 
6
+ /**
7
+ * Re-export the stable public test-id hooks
8
+ */
9
+ export { TEST_ID } from './constants/test-ids';
10
+
6
11
  /**
7
12
  * Debounce timeout for selection change event
8
13
  * {@link modules/ui.ts}
@@ -0,0 +1,9 @@
1
+ export type { BlockRenderedPayload } from '../../../types/events/editor-events';
2
+
3
+ /**
4
+ * Fired when a single block has been rendered into the DOM
5
+ * (its `rendered()` lifecycle hook has been invoked).
6
+ *
7
+ * Public event name: `block:rendered`.
8
+ */
9
+ export const BlockRendered = 'block:rendered';
@@ -0,0 +1,13 @@
1
+ export type { BlocksRenderedPayload } from '../../../types/events/editor-events';
2
+
3
+ /**
4
+ * Fired when a batch of blocks has finished rendering into the DOM,
5
+ * e.g. on editor initialization or after `blok.blocks.render()`.
6
+ *
7
+ * Emitted once the browser has painted the inserted blocks, so subscribers
8
+ * can safely read the rendered DOM (build a table of contents, mark blocks,
9
+ * etc.) instead of polling.
10
+ *
11
+ * Public event name: `blocks:rendered`.
12
+ */
13
+ export const BlocksRendered = 'blocks:rendered';
@@ -1,6 +1,10 @@
1
1
  import type { BlockChangedPayload } from './BlockChanged';
2
2
  import { BlockChanged } from './BlockChanged';
3
3
  import type { BlockHovered, BlockHoveredPayload } from './BlockHovered';
4
+ import type { BlockRenderedPayload } from './BlockRendered';
5
+ import { BlockRendered } from './BlockRendered';
6
+ import type { BlocksRenderedPayload } from './BlocksRendered';
7
+ import { BlocksRendered } from './BlocksRendered';
4
8
  import type { BlockSettingsClosedPayload } from './BlockSettingsClosed';
5
9
  import { BlockSettingsClosed } from './BlockSettingsClosed';
6
10
  import type { BlockSettingsOpenedPayload } from './BlockSettingsOpened';
@@ -20,6 +24,8 @@ import type { RedactorDomChangedPayload } from './RedactorDomChanged';
20
24
  export {
21
25
  RedactorDomChanged,
22
26
  BlockChanged,
27
+ BlockRendered,
28
+ BlocksRendered,
23
29
  FakeCursorAboutToBeToggled,
24
30
  FakeCursorHaveBeenSet,
25
31
  BlokMobileLayoutToggled,
@@ -34,6 +40,8 @@ export interface BlokEventMap {
34
40
  [BlockHovered]: BlockHoveredPayload;
35
41
  [RedactorDomChanged]: RedactorDomChangedPayload;
36
42
  [BlockChanged]: BlockChangedPayload;
43
+ [BlockRendered]: BlockRenderedPayload;
44
+ [BlocksRendered]: BlocksRenderedPayload;
37
45
  [FakeCursorAboutToBeToggled]: FakeCursorAboutToBeToggledPayload;
38
46
  [FakeCursorHaveBeenSet]: FakeCursorHaveBeenSetPayload;
39
47
  [BlokMobileLayoutToggled]: BlokMobileLayoutToggledPayload;
@@ -3,6 +3,7 @@ import type {
3
3
  InlineToolConstructorOptions,
4
4
  SanitizerConfig
5
5
  } from '../../../types';
6
+ import type { BlokConfig } from '../../../types/configs/blok-config';
6
7
  import type { Notifier, Toolbar, I18n, InlineToolbar } from '../../../types/api';
7
8
  import type { MenuConfig } from '../../../types/tools';
8
9
  import { DATA_ATTR, createSelector, INLINE_TOOLBAR_INTERFACE_VALUE } from '../constants';
@@ -42,15 +43,18 @@ export class LinkInlineTool implements InlineTool {
42
43
 
43
44
  /**
44
45
  * Sanitizer Rule
45
- * Leave <a> tags
46
+ * Leave <a> tags. target/rel pass through (instead of being forced to the
47
+ * defaults) so consumer-configured BlokConfig.link.target / .rel values
48
+ * survive save-time sanitization. Created anchors still default to
49
+ * '_blank'/'nofollow' via insertLink().
46
50
  * @returns {object}
47
51
  */
48
52
  public static get sanitize(): SanitizerConfig {
49
53
  return {
50
54
  a: {
51
55
  href: true,
52
- target: '_blank',
53
- rel: 'nofollow',
56
+ target: true,
57
+ rel: true,
54
58
  },
55
59
  } as SanitizerConfig;
56
60
  }
@@ -119,6 +123,11 @@ export class LinkInlineTool implements InlineTool {
119
123
  */
120
124
  private i18n: I18n;
121
125
 
126
+ /**
127
+ * Global anchor-building config (target / rel / transformHref)
128
+ */
129
+ private linkConfig: NonNullable<BlokConfig['link']>;
130
+
122
131
  /**
123
132
  * @param api - Blok API
124
133
  */
@@ -127,6 +136,7 @@ export class LinkInlineTool implements InlineTool {
127
136
  this.inlineToolbar = api.inlineToolbar;
128
137
  this.notifier = api.notifier;
129
138
  this.i18n = api.i18n;
139
+ this.linkConfig = api.config.link ?? {};
130
140
  this.selection = new SelectionUtils();
131
141
  this.nodes.input = this.createInput();
132
142
  this.nodes.suggestion = this.createSuggestion();
@@ -676,6 +686,10 @@ export class LinkInlineTool implements InlineTool {
676
686
  * @param {string} link - "href" value
677
687
  */
678
688
  private insertLink(link: string): void {
689
+ const href = this.linkConfig.transformHref ? this.linkConfig.transformHref(link) : link;
690
+ const target = this.linkConfig.target ?? '_blank';
691
+ const rel = this.linkConfig.rel ?? 'nofollow';
692
+
679
693
  /**
680
694
  * Edit all link, not selected part
681
695
  */
@@ -684,9 +698,9 @@ export class LinkInlineTool implements InlineTool {
684
698
  if (anchorTag instanceof HTMLAnchorElement) {
685
699
  this.selection.expandToTag(anchorTag);
686
700
 
687
- anchorTag.href = link;
688
- anchorTag.target = '_blank';
689
- anchorTag.rel = 'nofollow';
701
+ anchorTag.href = href;
702
+ anchorTag.target = target;
703
+ anchorTag.rel = rel;
690
704
 
691
705
  return;
692
706
  }
@@ -699,9 +713,9 @@ export class LinkInlineTool implements InlineTool {
699
713
 
700
714
  const anchor = document.createElement('a');
701
715
 
702
- anchor.href = link;
703
- anchor.target = '_blank';
704
- anchor.rel = 'nofollow';
716
+ anchor.href = href;
717
+ anchor.target = target;
718
+ anchor.rel = rel;
705
719
 
706
720
  anchor.appendChild(range.extractContents());
707
721
 
@@ -13,9 +13,9 @@ export class EventsAPI extends Module {
13
13
  */
14
14
  public get methods(): Events {
15
15
  return {
16
- emit: (eventName: keyof BlokEventMap, data: BlokEventMap[keyof BlokEventMap] | undefined): void => this.emit(eventName, data),
17
- off: (eventName: keyof BlokEventMap, callback: (data?: unknown) => void): void => this.off(eventName, callback),
18
- on: (eventName: keyof BlokEventMap, callback: () => void): void => this.on(eventName, callback),
16
+ emit: (eventName: string, data?: unknown): void => this.emit(eventName, data),
17
+ off: (eventName: string, callback: (data?: unknown) => void): void => this.off(eventName, callback),
18
+ on: (eventName: string, callback: (data?: unknown) => void): void => this.on(eventName, callback),
19
19
  };
20
20
  }
21
21
 
@@ -24,8 +24,8 @@ export class EventsAPI extends Module {
24
24
  * @param {string} eventName - event name to subscribe
25
25
  * @param {Function} callback - event handler
26
26
  */
27
- public on(eventName: keyof BlokEventMap, callback: (data?: unknown) => void): void {
28
- this.eventsDispatcher.on(eventName, callback);
27
+ public on(eventName: string, callback: (data?: unknown) => void): void {
28
+ this.eventsDispatcher.on(eventName as keyof BlokEventMap, callback);
29
29
  }
30
30
 
31
31
  /**
@@ -33,10 +33,10 @@ export class EventsAPI extends Module {
33
33
  * @param {string} eventName - event to emit
34
34
  * @param {object} data - event's data
35
35
  */
36
- public emit(eventName: keyof BlokEventMap, data: BlokEventMap[keyof BlokEventMap] | undefined): void {
36
+ public emit(eventName: string, data?: unknown): void {
37
37
  this.eventsDispatcher.emit(
38
- eventName,
39
- data
38
+ eventName as keyof BlokEventMap,
39
+ data as BlokEventMap[keyof BlokEventMap]
40
40
  );
41
41
  }
42
42
 
@@ -45,7 +45,7 @@ export class EventsAPI extends Module {
45
45
  * @param {string} eventName - event to unsubscribe
46
46
  * @param {Function} callback - event handler
47
47
  */
48
- public off(eventName: keyof BlokEventMap, callback: (data?: unknown) => void): void {
49
- this.eventsDispatcher.off(eventName, callback);
48
+ public off(eventName: string, callback: (data?: unknown) => void): void {
49
+ this.eventsDispatcher.off(eventName as keyof BlokEventMap, callback);
50
50
  }
51
51
  }
@@ -41,6 +41,9 @@ export class API extends Module {
41
41
  get linkPaste() {
42
42
  return apiConfig.linkPaste;
43
43
  },
44
+ get link() {
45
+ return apiConfig.link;
46
+ },
44
47
  },
45
48
  rectangleSelection: this.Blok.RectangleSelection,
46
49
  };
@@ -30,6 +30,9 @@ export class ToolsAPI extends Module {
30
30
 
31
31
  return result;
32
32
  },
33
+ update: (name, config) => {
34
+ this.Blok.Tools.updateToolConfig(name, config);
35
+ },
33
36
  };
34
37
  }
35
38
  }
@@ -269,7 +269,7 @@ export class BlockManager extends Module {
269
269
  * Define this._blocks property
270
270
  */
271
271
  public prepare(): void {
272
- const blocks = new Blocks(this.Blok.UI.nodes.redactor);
272
+ const blocks = new Blocks(this.Blok.UI.nodes.redactor, this.eventsDispatcher);
273
273
 
274
274
  /**
275
275
  * We need to use Proxy to overload set/get [] operator.
@@ -113,7 +113,16 @@ export class Paste extends Module {
113
113
  */
114
114
  public async processDataTransfer(dataTransfer: DataTransfer, pasteTarget?: Element): Promise<void> {
115
115
  const plainData = dataTransfer.getData('text/plain');
116
- const rawHtmlData = dataTransfer.getData('text/html');
116
+
117
+ // Give consumers a chance to transform or drop the raw clipboard HTML
118
+ // before any Blok preprocessing/sanitization runs. Returning a string
119
+ // replaces the HTML for the rest of the pipeline; returning null aborts the
120
+ // HTML paste path entirely (an empty string makes HtmlHandler bail, so the
121
+ // paste falls through to the plain-text handler).
122
+ const clipboardHtml = dataTransfer.getData('text/html');
123
+ const rawHtmlData = clipboardHtml && this.config.onBeforePaste
124
+ ? this.config.onBeforePaste(clipboardHtml) ?? ''
125
+ : clipboardHtml;
117
126
 
118
127
  // Native Blok clipboard data always wins. When absent, fall back to the
119
128
  // lossless proprietary flavours of other editors (web→web paste): they