@jackuait/blok 0.4.1-beta.11 → 0.4.1-beta.13
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/blok.mjs +2 -2
- package/dist/chunks/{blok-oNSQ3HA6.mjs → blok-Xfgk2kCJ.mjs} +665 -637
- package/dist/chunks/{i18next-loader-BdNRw4n4.mjs → i18next-loader-BMO6Rg_l.mjs} +1 -1
- package/dist/chunks/{index-DHgXmfki.mjs → index-DyPp5v5e.mjs} +1 -1
- package/dist/chunks/{inline-tool-convert-CRqgjRim.mjs → inline-tool-convert-DhHW7EYl.mjs} +2 -1
- package/dist/full.mjs +2 -2
- package/dist/tools.mjs +57 -31
- package/package.json +25 -7
- package/src/components/inline-tools/inline-tool-convert.ts +1 -0
- package/src/components/inline-tools/inline-tool-link.ts +1 -0
- package/src/components/modules/toolbar/blockSettings.ts +2 -1
- package/src/components/modules/toolbar/index.ts +97 -116
- package/src/components/modules/ui.ts +11 -7
- package/src/components/ui/toolbox.ts +14 -5
- package/src/components/utils/data-model-transform.ts +38 -21
- package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +1 -1
- package/src/components/utils/popover/components/popover-item/popover-item.ts +11 -0
- package/src/components/utils/popover/popover-abstract.ts +1 -1
- package/src/components/utils/popover/popover-desktop.ts +8 -2
- package/src/stories/Popover.stories.ts +0 -85
- package/src/styles/main.css +7 -4
- package/src/tools/header/index.ts +1 -0
- package/src/tools/list/index.ts +34 -4
- package/types/configs/sanitizer-config.d.ts +25 -1
- package/types/index.d.ts +1 -0
- package/types/tools/block-tool.d.ts +2 -2
- package/types/tools/tool-settings.d.ts +7 -0
- package/types/utils/popover/popover-item.d.ts +6 -0
- package/types/utils/popover/popover.d.ts +6 -0
|
@@ -18,7 +18,7 @@ let nt = (o = 21) => {
|
|
|
18
18
|
return t;
|
|
19
19
|
};
|
|
20
20
|
var ot = /* @__PURE__ */ ((o) => (o.VERBOSE = "VERBOSE", o.INFO = "INFO", o.WARN = "WARN", o.ERROR = "ERROR", o))(ot || {});
|
|
21
|
-
const rt = () => "0.4.1-beta.
|
|
21
|
+
const rt = () => "0.4.1-beta.13", Ct = {
|
|
22
22
|
BACKSPACE: 8,
|
|
23
23
|
TAB: 9,
|
|
24
24
|
ENTER: 13,
|
|
@@ -1895,6 +1895,7 @@ const te = `
|
|
|
1895
1895
|
},
|
|
1896
1896
|
children: {
|
|
1897
1897
|
items: s,
|
|
1898
|
+
width: "auto",
|
|
1898
1899
|
onOpen: () => {
|
|
1899
1900
|
c && (this.selectionAPI.setFakeBackground(), this.selectionAPI.save());
|
|
1900
1901
|
},
|
package/dist/full.mjs
CHANGED
|
@@ -10,10 +10,10 @@ var e = (a, l, o) => l in a ? n(a, l, { enumerable: !0, configurable: !0, writab
|
|
|
10
10
|
d.call(l, o) && e(a, o, l[o]);
|
|
11
11
|
return a;
|
|
12
12
|
}, r = (a, l) => t(a, c(l));
|
|
13
|
-
import { B as v, v as A } from "./chunks/blok-
|
|
13
|
+
import { B as v, v as A } from "./chunks/blok-Xfgk2kCJ.mjs";
|
|
14
14
|
import { List as p, Header as f, Paragraph as I, Link as k, Italic as u, Bold as B } from "./tools.mjs";
|
|
15
15
|
import { defaultBlockTools as H, defaultInlineTools as P } from "./tools.mjs";
|
|
16
|
-
import { D as _ } from "./chunks/inline-tool-convert-
|
|
16
|
+
import { D as _ } from "./chunks/inline-tool-convert-DhHW7EYl.mjs";
|
|
17
17
|
const m = {
|
|
18
18
|
paragraph: {
|
|
19
19
|
class: I,
|
package/dist/tools.mjs
CHANGED
|
@@ -2,7 +2,7 @@ var nt = Object.defineProperty, rt = Object.defineProperties;
|
|
|
2
2
|
var st = Object.getOwnPropertyDescriptors;
|
|
3
3
|
var K = Object.getOwnPropertySymbols;
|
|
4
4
|
var ot = Object.prototype.hasOwnProperty, it = Object.prototype.propertyIsEnumerable;
|
|
5
|
-
var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : f[t] = e,
|
|
5
|
+
var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : f[t] = e, O = (f, t) => {
|
|
6
6
|
for (var e in t || (t = {}))
|
|
7
7
|
ot.call(t, e) && U(f, e, t[e]);
|
|
8
8
|
if (K)
|
|
@@ -10,8 +10,8 @@ var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writa
|
|
|
10
10
|
it.call(t, e) && U(f, e, t[e]);
|
|
11
11
|
return f;
|
|
12
12
|
}, P = (f, t) => rt(f, st(t));
|
|
13
|
-
import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-
|
|
14
|
-
import { a0 as Dt } from "./chunks/inline-tool-convert-
|
|
13
|
+
import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-DhHW7EYl.mjs";
|
|
14
|
+
import { a0 as Dt } from "./chunks/inline-tool-convert-DhHW7EYl.mjs";
|
|
15
15
|
const W = [
|
|
16
16
|
"empty:before:pointer-events-none",
|
|
17
17
|
"empty:before:text-gray-text",
|
|
@@ -531,7 +531,8 @@ const L = class L {
|
|
|
531
531
|
titleKey: t.nameKey,
|
|
532
532
|
name: `header-${t.number}`,
|
|
533
533
|
data: { level: t.number },
|
|
534
|
-
searchTerms: [`h${t.number}`, "title", "header", "heading"]
|
|
534
|
+
searchTerms: [`h${t.number}`, "title", "header", "heading"],
|
|
535
|
+
shortcut: "#".repeat(t.number)
|
|
535
536
|
}));
|
|
536
537
|
}
|
|
537
538
|
};
|
|
@@ -554,15 +555,32 @@ const u = class u {
|
|
|
554
555
|
})));
|
|
555
556
|
}, this.api = n, this.readOnly = r, this._settings = e || {}, this._data = this.normalizeData(t), s && (this.blockId = s.id), this._data.style === "ordered" && this.api.events.on("block changed", this.handleBlockChanged);
|
|
556
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Legacy list item structure for backward compatibility
|
|
560
|
+
*/
|
|
561
|
+
static isLegacyFormat(t) {
|
|
562
|
+
return typeof t == "object" && t !== null && "items" in t && Array.isArray(t.items);
|
|
563
|
+
}
|
|
557
564
|
normalizeData(t) {
|
|
558
565
|
var n;
|
|
559
566
|
const e = this._settings.defaultStyle || "unordered";
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
567
|
+
if (!t || typeof t != "object")
|
|
568
|
+
return {
|
|
569
|
+
text: "",
|
|
570
|
+
style: e,
|
|
571
|
+
checked: !1,
|
|
572
|
+
depth: 0
|
|
573
|
+
};
|
|
574
|
+
if (u.isLegacyFormat(t)) {
|
|
575
|
+
const r = t.items[0], s = (r == null ? void 0 : r.content) || "", o = (r == null ? void 0 : r.checked) || !1;
|
|
576
|
+
return O({
|
|
577
|
+
text: s,
|
|
578
|
+
style: t.style || e,
|
|
579
|
+
checked: !!o,
|
|
580
|
+
depth: 0
|
|
581
|
+
}, t.start !== void 0 && t.start !== 1 ? { start: t.start } : {});
|
|
582
|
+
}
|
|
583
|
+
return O({
|
|
566
584
|
text: t.text || "",
|
|
567
585
|
style: t.style || e,
|
|
568
586
|
checked: !!t.checked,
|
|
@@ -1154,7 +1172,7 @@ const u = class u {
|
|
|
1154
1172
|
charCount: r.charCount,
|
|
1155
1173
|
node: s,
|
|
1156
1174
|
offset: e - r.charCount
|
|
1157
|
-
} : P(
|
|
1175
|
+
} : P(O({}, r), {
|
|
1158
1176
|
charCount: r.charCount + o
|
|
1159
1177
|
});
|
|
1160
1178
|
},
|
|
@@ -1201,7 +1219,7 @@ const u = class u {
|
|
|
1201
1219
|
this.syncContentFromDOM();
|
|
1202
1220
|
const s = n + 1;
|
|
1203
1221
|
this._data.depth = s;
|
|
1204
|
-
const o = await this.api.blocks.update(this.blockId || "", P(
|
|
1222
|
+
const o = await this.api.blocks.update(this.blockId || "", P(O({}, this._data), {
|
|
1205
1223
|
depth: s
|
|
1206
1224
|
}));
|
|
1207
1225
|
this.setCaretToBlockContent(o);
|
|
@@ -1212,7 +1230,7 @@ const u = class u {
|
|
|
1212
1230
|
this.syncContentFromDOM();
|
|
1213
1231
|
const e = t - 1;
|
|
1214
1232
|
this._data.depth = e;
|
|
1215
|
-
const n = await this.api.blocks.update(this.blockId || "", P(
|
|
1233
|
+
const n = await this.api.blocks.update(this.blockId || "", P(O({}, this._data), {
|
|
1216
1234
|
depth: e
|
|
1217
1235
|
}));
|
|
1218
1236
|
this.setCaretToBlockContent(n);
|
|
@@ -1342,7 +1360,11 @@ const u = class u {
|
|
|
1342
1360
|
return {
|
|
1343
1361
|
text: {
|
|
1344
1362
|
br: !0,
|
|
1345
|
-
a:
|
|
1363
|
+
a: {
|
|
1364
|
+
href: !0,
|
|
1365
|
+
target: "_blank",
|
|
1366
|
+
rel: "nofollow"
|
|
1367
|
+
},
|
|
1346
1368
|
b: !0,
|
|
1347
1369
|
i: !0,
|
|
1348
1370
|
mark: !0
|
|
@@ -1421,7 +1443,8 @@ const u = class u {
|
|
|
1421
1443
|
titleKey: "bulletedList",
|
|
1422
1444
|
data: { style: "unordered" },
|
|
1423
1445
|
name: "bulleted-list",
|
|
1424
|
-
searchTerms: ["ul", "bullet", "unordered", "list"]
|
|
1446
|
+
searchTerms: ["ul", "bullet", "unordered", "list"],
|
|
1447
|
+
shortcut: "-"
|
|
1425
1448
|
},
|
|
1426
1449
|
{
|
|
1427
1450
|
icon: j,
|
|
@@ -1429,7 +1452,8 @@ const u = class u {
|
|
|
1429
1452
|
titleKey: "numberedList",
|
|
1430
1453
|
data: { style: "ordered" },
|
|
1431
1454
|
name: "numbered-list",
|
|
1432
|
-
searchTerms: ["ol", "ordered", "number", "list"]
|
|
1455
|
+
searchTerms: ["ol", "ordered", "number", "list"],
|
|
1456
|
+
shortcut: "1."
|
|
1433
1457
|
},
|
|
1434
1458
|
{
|
|
1435
1459
|
icon: $,
|
|
@@ -1437,7 +1461,8 @@ const u = class u {
|
|
|
1437
1461
|
titleKey: "todoList",
|
|
1438
1462
|
data: { style: "checklist" },
|
|
1439
1463
|
name: "check-list",
|
|
1440
|
-
searchTerms: ["checkbox", "task", "todo", "check", "list"]
|
|
1464
|
+
searchTerms: ["checkbox", "task", "todo", "check", "list"],
|
|
1465
|
+
shortcut: "[]"
|
|
1441
1466
|
}
|
|
1442
1467
|
];
|
|
1443
1468
|
}
|
|
@@ -2082,7 +2107,7 @@ const i = class i {
|
|
|
2082
2107
|
return;
|
|
2083
2108
|
const s = `strong[${i.DATA_ATTR_COLLAPSED_ACTIVE}="true"]`;
|
|
2084
2109
|
r.querySelectorAll(s).forEach((l) => {
|
|
2085
|
-
var k, I,
|
|
2110
|
+
var k, I, _, z, q;
|
|
2086
2111
|
const d = l.getAttribute(i.DATA_ATTR_PREV_LENGTH), c = l.previousSibling;
|
|
2087
2112
|
if (!d || !c || c.nodeType !== Node.TEXT_NODE)
|
|
2088
2113
|
return;
|
|
@@ -2097,13 +2122,13 @@ const i = class i {
|
|
|
2097
2122
|
const C = E.match(/^[\u00A0\s]+/);
|
|
2098
2123
|
if (C && !l.hasAttribute(i.DATA_ATTR_LEADING_WHITESPACE) && l.setAttribute(i.DATA_ATTR_LEADING_WHITESPACE, C[0]), E.length === 0)
|
|
2099
2124
|
return;
|
|
2100
|
-
const T = (I = l.textContent) != null ? I : "", N = T + E, B = (
|
|
2125
|
+
const T = (I = l.textContent) != null ? I : "", N = T + E, B = (_ = l.getAttribute(i.DATA_ATTR_LEADING_WHITESPACE)) != null ? _ : "", R = B.length > 0 && T.length === 0 && !N.startsWith(B) ? B + N : N, b = document.createTextNode(R);
|
|
2101
2126
|
for (; l.firstChild; )
|
|
2102
2127
|
l.removeChild(l.firstChild);
|
|
2103
2128
|
if (l.appendChild(b), !(t != null && t.isCollapsed) || !i.isNodeWithin(t.focusNode, p))
|
|
2104
2129
|
return;
|
|
2105
|
-
const S = document.createRange(),
|
|
2106
|
-
S.setStart(b,
|
|
2130
|
+
const S = document.createRange(), F = (q = (z = b.textContent) == null ? void 0 : z.length) != null ? q : 0;
|
|
2131
|
+
S.setStart(b, F), S.collapse(!0), t.removeAllRanges(), t.addRange(S);
|
|
2107
2132
|
});
|
|
2108
2133
|
}
|
|
2109
2134
|
/**
|
|
@@ -2312,7 +2337,7 @@ const i = class i {
|
|
|
2312
2337
|
if (!r)
|
|
2313
2338
|
return;
|
|
2314
2339
|
r.querySelectorAll(`strong[${i.DATA_ATTR_COLLAPSED_LENGTH}]`).forEach((a) => {
|
|
2315
|
-
var v, R, b, S,
|
|
2340
|
+
var v, R, b, S, F;
|
|
2316
2341
|
const l = a, d = l.getAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
|
|
2317
2342
|
if (!d)
|
|
2318
2343
|
return;
|
|
@@ -2323,11 +2348,11 @@ const i = class i {
|
|
|
2323
2348
|
if (B && T) {
|
|
2324
2349
|
const k = N.slice(0, E), I = N.slice(E);
|
|
2325
2350
|
T.textContent = k;
|
|
2326
|
-
const
|
|
2327
|
-
(b = l.parentNode) == null || b.insertBefore(
|
|
2351
|
+
const _ = document.createTextNode(I);
|
|
2352
|
+
(b = l.parentNode) == null || b.insertBefore(_, l.nextSibling);
|
|
2328
2353
|
}
|
|
2329
2354
|
if (B && l.removeAttribute(i.DATA_ATTR_PREV_LENGTH), t != null && t.isCollapsed && g && i.isNodeWithin(t.focusNode, l)) {
|
|
2330
|
-
const k = document.createRange(), I = (
|
|
2355
|
+
const k = document.createRange(), I = (F = (S = g.textContent) == null ? void 0 : S.length) != null ? F : 0;
|
|
2331
2356
|
k.setStart(g, I), k.collapse(!0), t.removeAllRanges(), t.addRange(k);
|
|
2332
2357
|
}
|
|
2333
2358
|
p && l.removeAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
|
|
@@ -2594,7 +2619,7 @@ const i = class i {
|
|
|
2594
2619
|
};
|
|
2595
2620
|
i.isInline = !0, i.title = "Bold", i.titleKey = "bold", i.shortcutListenerRegistered = !1, i.selectionListenerRegistered = !1, i.inputListenerRegistered = !1, i.beforeInputListenerRegistered = !1, i.globalListenersInitialized = i.initializeGlobalListeners(), i.collapsedExitRecords = /* @__PURE__ */ new Set(), i.markerSequence = 0, i.isProcessingMutation = !1, i.DATA_ATTR_COLLAPSED_LENGTH = "data-blok-bold-collapsed-length", i.DATA_ATTR_COLLAPSED_ACTIVE = "data-blok-bold-collapsed-active", i.DATA_ATTR_PREV_LENGTH = "data-blok-bold-prev-length", i.DATA_ATTR_LEADING_WHITESPACE = "data-blok-bold-leading-ws", i.instances = /* @__PURE__ */ new Set(), i.pendingBoundaryCaretAdjustments = /* @__PURE__ */ new WeakSet(), i.shortcut = "CMD+B";
|
|
2596
2621
|
let Z = i;
|
|
2597
|
-
const
|
|
2622
|
+
const D = class D {
|
|
2598
2623
|
/**
|
|
2599
2624
|
* Sanitizer Rule
|
|
2600
2625
|
* Leave <i> and <em> tags
|
|
@@ -2865,9 +2890,9 @@ const O = class O {
|
|
|
2865
2890
|
n.insertBefore(a, e.nextSibling), n.insertBefore(t, a);
|
|
2866
2891
|
}
|
|
2867
2892
|
};
|
|
2868
|
-
|
|
2869
|
-
let Q =
|
|
2870
|
-
const
|
|
2893
|
+
D.isInline = !0, D.title = "Italic", D.titleKey = "italic", D.shortcut = "CMD+I";
|
|
2894
|
+
let Q = D;
|
|
2895
|
+
const w = class w {
|
|
2871
2896
|
/**
|
|
2872
2897
|
* @param api - Blok API
|
|
2873
2898
|
*/
|
|
@@ -2907,6 +2932,7 @@ const D = class D {
|
|
|
2907
2932
|
isActive: () => !!this.selection.findParentTag("A"),
|
|
2908
2933
|
children: {
|
|
2909
2934
|
hideChevron: !0,
|
|
2935
|
+
width: "200px",
|
|
2910
2936
|
items: [
|
|
2911
2937
|
{
|
|
2912
2938
|
type: Tt.Html,
|
|
@@ -3093,8 +3119,8 @@ const D = class D {
|
|
|
3093
3119
|
t && t.setAttribute(e, n ? "true" : "false");
|
|
3094
3120
|
}
|
|
3095
3121
|
};
|
|
3096
|
-
|
|
3097
|
-
let tt =
|
|
3122
|
+
w.isInline = !0, w.title = "Link", w.titleKey = "link", w.shortcut = "CMD+K";
|
|
3123
|
+
let tt = w;
|
|
3098
3124
|
const vt = {
|
|
3099
3125
|
paragraph: { preserveBlank: !0 },
|
|
3100
3126
|
header: {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackuait/blok",
|
|
3
|
-
"version": "0.4.1-beta.
|
|
3
|
+
"version": "0.4.1-beta.13",
|
|
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
|
"types": "./types/index.d.ts",
|
|
@@ -96,14 +96,12 @@
|
|
|
96
96
|
"verify:package:local": "npm pack && node scripts/verify-published-package.mjs --local",
|
|
97
97
|
"verify:version": "node scripts/verify-version.mjs",
|
|
98
98
|
"unpublish": "node scripts/unpublish-package.mjs",
|
|
99
|
-
"bundle:track": "node scripts/track-bundle-size.mjs --verbose",
|
|
100
|
-
"bundle:variants": "node scripts/build-bundle-variants.mjs --verbose",
|
|
101
|
-
"bundle:trends": "node scripts/view-bundle-trends.mjs --trends",
|
|
102
|
-
"bundle:history": "node scripts/view-bundle-trends.mjs",
|
|
103
99
|
"perf:analyze": "node scripts/analyze-performance.mjs",
|
|
104
100
|
"perf:compare": "node scripts/analyze-performance.mjs --baseline",
|
|
105
101
|
"perf:dashboard": "node scripts/generate-performance-dashboard.mjs",
|
|
106
|
-
"e2e:validate-categories": "node scripts/validate-test-categories.mjs"
|
|
102
|
+
"e2e:validate-categories": "node scripts/validate-test-categories.mjs",
|
|
103
|
+
"size": "size-limit",
|
|
104
|
+
"size:why": "size-limit --why"
|
|
107
105
|
},
|
|
108
106
|
"author": "JackUait",
|
|
109
107
|
"contributors": [
|
|
@@ -123,6 +121,8 @@
|
|
|
123
121
|
"@playwright/test": "1.57.0",
|
|
124
122
|
"@semantic-release/changelog": "^6.0.3",
|
|
125
123
|
"@semantic-release/git": "^10.0.1",
|
|
124
|
+
"@size-limit/esbuild-why": "^12.0.0",
|
|
125
|
+
"@size-limit/preset-small-lib": "^12.0.0",
|
|
126
126
|
"@storybook/addon-a11y": "10.1.1",
|
|
127
127
|
"@storybook/addon-vitest": "10.1.1",
|
|
128
128
|
"@storybook/global": "5.0.0",
|
|
@@ -150,6 +150,7 @@
|
|
|
150
150
|
"postcss": "8.5.6",
|
|
151
151
|
"rollup-plugin-license": "3.6.0",
|
|
152
152
|
"semantic-release": "^25.0.2",
|
|
153
|
+
"size-limit": "^12.0.0",
|
|
153
154
|
"storybook": "10.1.1",
|
|
154
155
|
"tailwindcss": "3",
|
|
155
156
|
"tslint": "6.1.3",
|
|
@@ -163,5 +164,22 @@
|
|
|
163
164
|
"dependencies": {
|
|
164
165
|
"i18next": "^25.7.3",
|
|
165
166
|
"nanoid": "^5.1.6"
|
|
166
|
-
}
|
|
167
|
+
},
|
|
168
|
+
"size-limit": [
|
|
169
|
+
{
|
|
170
|
+
"name": "Minimum (core only)",
|
|
171
|
+
"path": "src/variants/blok-minimum.ts",
|
|
172
|
+
"limit": "300 KB"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"name": "Normal (with tools)",
|
|
176
|
+
"path": "src/blok.ts",
|
|
177
|
+
"limit": "300 KB"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"name": "Maximum (all locales)",
|
|
181
|
+
"path": "src/variants/blok-maximum.ts",
|
|
182
|
+
"limit": "300 KB"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
167
185
|
}
|
|
@@ -164,10 +164,11 @@ export class BlockSettings extends Module<BlockSettingsNodes> {
|
|
|
164
164
|
|
|
165
165
|
const PopoverClass = isMobileScreen() ? PopoverMobile : PopoverDesktop;
|
|
166
166
|
const popoverParams: PopoverParams & { flipper?: Flipper } = {
|
|
167
|
-
searchable:
|
|
167
|
+
searchable: false,
|
|
168
168
|
trigger: trigger || this.nodes.wrapper,
|
|
169
169
|
items: await this.getTunesItems(block, commonTunes, toolTunes),
|
|
170
170
|
scopeElement: this.Blok.API.methods.ui.nodes.redactor,
|
|
171
|
+
width: 'auto',
|
|
171
172
|
messages: {
|
|
172
173
|
nothingFound: this.Blok.I18n.t('popover.nothingFound'),
|
|
173
174
|
search: this.Blok.I18n.t('popover.search'),
|
|
@@ -109,18 +109,6 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
109
109
|
*/
|
|
110
110
|
private toolboxInstance: Toolbox | null = null;
|
|
111
111
|
|
|
112
|
-
/**
|
|
113
|
-
* Mouse position when mousedown occurred on settings toggler
|
|
114
|
-
* Used to distinguish between click and drag
|
|
115
|
-
*/
|
|
116
|
-
private settingsTogglerMouseDownPosition: { x: number; y: number } | null = null;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Mouse position when mousedown occurred on plus button
|
|
120
|
-
* Used to distinguish between click and drag
|
|
121
|
-
*/
|
|
122
|
-
private plusButtonMouseDownPosition: { x: number; y: number } | null = null;
|
|
123
|
-
|
|
124
112
|
/**
|
|
125
113
|
* Last calculated toolbar Y position
|
|
126
114
|
* Used to avoid unnecessary repositioning when the position hasn't changed
|
|
@@ -133,6 +121,12 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
133
121
|
*/
|
|
134
122
|
private ignoreNextSettingsMouseUp = false;
|
|
135
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Set of pending document-level mouseup listeners that need cleanup on destroy.
|
|
126
|
+
* Each listener is added on mousedown and removed after mouseup fires.
|
|
127
|
+
*/
|
|
128
|
+
private pendingMouseUpListeners: Set<(e: MouseEvent) => void> = new Set();
|
|
129
|
+
|
|
136
130
|
/**
|
|
137
131
|
* @class
|
|
138
132
|
* @param moduleConfiguration - Module Configuration
|
|
@@ -218,7 +212,6 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
218
212
|
'not-mobile:w-6'
|
|
219
213
|
),
|
|
220
214
|
settingsTogglerHidden: 'hidden',
|
|
221
|
-
settingsTogglerOpened: '',
|
|
222
215
|
};
|
|
223
216
|
}
|
|
224
217
|
|
|
@@ -660,60 +653,25 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
660
653
|
|
|
661
654
|
/**
|
|
662
655
|
* Plus button mousedown handler
|
|
663
|
-
*
|
|
664
|
-
* Using document-level mouseup ensures we catch the event even if the mouse
|
|
665
|
-
* moves slightly off the button element during the click.
|
|
656
|
+
* Uses click-vs-drag detection to distinguish clicks from drags.
|
|
666
657
|
*/
|
|
667
658
|
this.readOnlyMutableListeners.on(plusButton, 'mousedown', (e) => {
|
|
668
659
|
hide();
|
|
669
660
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Add document-level mouseup listener to catch the event even if mouse
|
|
683
|
-
* moves slightly off the button. This is removed after firing once.
|
|
684
|
-
*/
|
|
685
|
-
const onMouseUp = (mouseUpEvent: MouseEvent): void => {
|
|
686
|
-
document.removeEventListener('mouseup', onMouseUp, true);
|
|
687
|
-
|
|
688
|
-
const mouseDownPos = this.plusButtonMouseDownPosition;
|
|
689
|
-
|
|
690
|
-
this.plusButtonMouseDownPosition = null;
|
|
691
|
-
|
|
692
|
-
if (mouseDownPos === null) {
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const wasDragged = (
|
|
697
|
-
Math.abs(mouseUpEvent.clientX - mouseDownPos.x) > DRAG_THRESHOLD ||
|
|
698
|
-
Math.abs(mouseUpEvent.clientY - mouseDownPos.y) > DRAG_THRESHOLD
|
|
699
|
-
);
|
|
661
|
+
this.setupClickVsDrag(
|
|
662
|
+
e as MouseEvent,
|
|
663
|
+
(mouseUpEvent) => {
|
|
664
|
+
/**
|
|
665
|
+
* Check for modifier key to determine insert direction:
|
|
666
|
+
* - Option/Alt on Mac, Ctrl on Windows → insert above
|
|
667
|
+
* - No modifier → insert below (default)
|
|
668
|
+
*/
|
|
669
|
+
const userOS = getUserOS();
|
|
670
|
+
const insertAbove = userOS.win ? mouseUpEvent.ctrlKey : mouseUpEvent.altKey;
|
|
700
671
|
|
|
701
|
-
|
|
702
|
-
return;
|
|
672
|
+
this.plusButtonClicked(insertAbove);
|
|
703
673
|
}
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* Check for modifier key to determine insert direction:
|
|
707
|
-
* - Option/Alt on Mac, Ctrl on Windows → insert above
|
|
708
|
-
* - No modifier → insert below (default)
|
|
709
|
-
*/
|
|
710
|
-
const userOS = getUserOS();
|
|
711
|
-
const insertAbove = userOS.win ? mouseUpEvent.ctrlKey : mouseUpEvent.altKey;
|
|
712
|
-
|
|
713
|
-
this.plusButtonClicked(insertAbove);
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
document.addEventListener('mouseup', onMouseUp, true);
|
|
674
|
+
);
|
|
717
675
|
}, true);
|
|
718
676
|
|
|
719
677
|
/**
|
|
@@ -931,66 +889,35 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
931
889
|
if (settingsToggler) {
|
|
932
890
|
/**
|
|
933
891
|
* Settings toggler mousedown handler
|
|
934
|
-
*
|
|
935
|
-
* Using document-level mouseup ensures we catch the event even if the mouse
|
|
936
|
-
* moves slightly off the toggler element during the click.
|
|
892
|
+
* Uses click-vs-drag detection to distinguish clicks from drags.
|
|
937
893
|
*/
|
|
938
894
|
this.readOnlyMutableListeners.on(settingsToggler, 'mousedown', (e) => {
|
|
939
895
|
hide();
|
|
940
896
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
this.ignoreNextSettingsMouseUp = false;
|
|
964
|
-
this.settingsTogglerMouseDownPosition = null;
|
|
965
|
-
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const mouseDownPos = this.settingsTogglerMouseDownPosition;
|
|
970
|
-
|
|
971
|
-
this.settingsTogglerMouseDownPosition = null;
|
|
972
|
-
|
|
973
|
-
if (mouseDownPos === null) {
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
const wasDragged = (
|
|
978
|
-
Math.abs(mouseUpEvent.clientX - mouseDownPos.x) > DRAG_THRESHOLD ||
|
|
979
|
-
Math.abs(mouseUpEvent.clientY - mouseDownPos.y) > DRAG_THRESHOLD
|
|
980
|
-
);
|
|
981
|
-
|
|
982
|
-
if (wasDragged) {
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
this.settingsTogglerClicked();
|
|
987
|
-
|
|
988
|
-
if (this.toolboxInstance?.opened) {
|
|
989
|
-
this.toolboxInstance.close();
|
|
897
|
+
this.setupClickVsDrag(
|
|
898
|
+
e as MouseEvent,
|
|
899
|
+
() => {
|
|
900
|
+
this.settingsTogglerClicked();
|
|
901
|
+
|
|
902
|
+
if (this.toolboxInstance?.opened) {
|
|
903
|
+
this.toolboxInstance.close();
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
/**
|
|
908
|
+
* Check if we should ignore this mouseup (e.g., after a block drop)
|
|
909
|
+
*/
|
|
910
|
+
beforeCallback: () => {
|
|
911
|
+
if (this.ignoreNextSettingsMouseUp) {
|
|
912
|
+
this.ignoreNextSettingsMouseUp = false;
|
|
913
|
+
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return true;
|
|
918
|
+
},
|
|
990
919
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
document.addEventListener('mouseup', onMouseUp, true);
|
|
920
|
+
);
|
|
994
921
|
}, true);
|
|
995
922
|
}
|
|
996
923
|
|
|
@@ -1302,6 +1229,50 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
1302
1229
|
return container;
|
|
1303
1230
|
}
|
|
1304
1231
|
|
|
1232
|
+
/**
|
|
1233
|
+
* Sets up a click-vs-drag detection pattern on an element.
|
|
1234
|
+
* Tracks mousedown position and fires callback only if mouse didn't move beyond threshold.
|
|
1235
|
+
* Uses document-level mouseup to catch events even if mouse moves off element.
|
|
1236
|
+
* @param element - Element to attach mousedown listener to
|
|
1237
|
+
* @param mouseEvent - The mousedown event
|
|
1238
|
+
* @param onClickCallback - Callback to fire if it was a click (not a drag)
|
|
1239
|
+
* @param options - Optional configuration
|
|
1240
|
+
* @param options.beforeCallback - Function called before click callback, return false to abort
|
|
1241
|
+
*/
|
|
1242
|
+
private setupClickVsDrag(
|
|
1243
|
+
mouseEvent: MouseEvent,
|
|
1244
|
+
onClickCallback: (mouseUpEvent: MouseEvent) => void,
|
|
1245
|
+
options?: { beforeCallback?: () => boolean }
|
|
1246
|
+
): void {
|
|
1247
|
+
const startPosition = {
|
|
1248
|
+
x: mouseEvent.clientX,
|
|
1249
|
+
y: mouseEvent.clientY,
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1252
|
+
const onMouseUp = (mouseUpEvent: MouseEvent): void => {
|
|
1253
|
+
document.removeEventListener('mouseup', onMouseUp, true);
|
|
1254
|
+
this.pendingMouseUpListeners.delete(onMouseUp);
|
|
1255
|
+
|
|
1256
|
+
if (options?.beforeCallback && !options.beforeCallback()) {
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
const wasDragged = (
|
|
1261
|
+
Math.abs(mouseUpEvent.clientX - startPosition.x) > DRAG_THRESHOLD ||
|
|
1262
|
+
Math.abs(mouseUpEvent.clientY - startPosition.y) > DRAG_THRESHOLD
|
|
1263
|
+
);
|
|
1264
|
+
|
|
1265
|
+
if (wasDragged) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
onClickCallback(mouseUpEvent);
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
this.pendingMouseUpListeners.add(onMouseUp);
|
|
1273
|
+
document.addEventListener('mouseup', onMouseUp, true);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1305
1276
|
/**
|
|
1306
1277
|
* Removes all created and saved HTMLElements
|
|
1307
1278
|
* It is used in Read-Only mode
|
|
@@ -1311,5 +1282,15 @@ export class Toolbar extends Module<ToolbarNodes> {
|
|
|
1311
1282
|
if (this.toolboxInstance) {
|
|
1312
1283
|
this.toolboxInstance.destroy();
|
|
1313
1284
|
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Clean up any pending document-level mouseup listeners.
|
|
1288
|
+
* These are added on mousedown and normally removed on mouseup,
|
|
1289
|
+
* but if the component is destroyed mid-click, they need manual cleanup.
|
|
1290
|
+
*/
|
|
1291
|
+
for (const listener of this.pendingMouseUpListeners) {
|
|
1292
|
+
document.removeEventListener('mouseup', listener, true);
|
|
1293
|
+
}
|
|
1294
|
+
this.pendingMouseUpListeners.clear();
|
|
1314
1295
|
}
|
|
1315
1296
|
}
|