@haklex/rich-editor 0.0.42 → 0.0.43

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.
@@ -26,6 +26,7 @@ import { ListNode, ListItemNode } from "@lexical/list";
26
26
  import { HeadingNode, QuoteNode, $isQuoteNode, DRAG_DROP_PASTE, $createQuoteNode } from "@lexical/rich-text";
27
27
  import { TableNode, TableCellNode, TableRowNode } from "@lexical/table";
28
28
  import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
29
+ import { nanoid } from "nanoid";
29
30
  import { Dialog, DialogPopup, DialogTitle, SegmentedControl } from "@haklex/rich-editor-ui";
30
31
  import { b as clsx, g as getVariantClass } from "./utils-fpeaZV1R.js";
31
32
  import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
@@ -1330,42 +1331,104 @@ const blockIdState = createState("blockId", {
1330
1331
  parse: (v) => typeof v === "string" ? v : ""
1331
1332
  });
1332
1333
  const NORMALIZATION_TAG = "block-id-normalization";
1334
+ function buildPreviousIdIndex(editorState) {
1335
+ return editorState.read(() => {
1336
+ const map = /* @__PURE__ */ new Map();
1337
+ for (const child of $getRoot().getChildren()) {
1338
+ const id = $getState(child, blockIdState);
1339
+ if (!id) continue;
1340
+ const set = map.get(id) ?? /* @__PURE__ */ new Set();
1341
+ set.add(child.getKey());
1342
+ map.set(id, set);
1343
+ }
1344
+ return map;
1345
+ });
1346
+ }
1347
+ function collectRootChildren(editorState) {
1348
+ return editorState.read(
1349
+ () => $getRoot().getChildren().map((child) => $getState(child, blockIdState))
1350
+ );
1351
+ }
1352
+ function hasDuplicateOrMissingId(children) {
1353
+ const seen = /* @__PURE__ */ new Set();
1354
+ for (const id of children) {
1355
+ if (!id || seen.has(id)) {
1356
+ return true;
1357
+ }
1358
+ seen.add(id);
1359
+ }
1360
+ return false;
1361
+ }
1362
+ function generateBlockId(used) {
1363
+ let id = "";
1364
+ do {
1365
+ id = nanoid(8);
1366
+ } while (used.has(id));
1367
+ return id;
1368
+ }
1369
+ function pickKeeperKey(nodes, previousKeys) {
1370
+ if (!nodes.length) return null;
1371
+ if (previousKeys?.size) {
1372
+ for (const node of nodes) {
1373
+ if (previousKeys.has(node.getKey())) {
1374
+ return node.getKey();
1375
+ }
1376
+ }
1377
+ }
1378
+ return nodes[0]?.getKey() ?? null;
1379
+ }
1380
+ function normalizeRootBlockIds(editor, previousIdIndex) {
1381
+ editor.update(
1382
+ () => {
1383
+ $addUpdateTag("history-merge");
1384
+ const children = $getRoot().getChildren();
1385
+ const groupedById = /* @__PURE__ */ new Map();
1386
+ for (const child of children) {
1387
+ const id = $getState(child, blockIdState);
1388
+ if (!id) continue;
1389
+ const bucket = groupedById.get(id) ?? [];
1390
+ bucket.push(child);
1391
+ groupedById.set(id, bucket);
1392
+ }
1393
+ const keeperById = /* @__PURE__ */ new Map();
1394
+ for (const [id, nodes] of groupedById) {
1395
+ if (nodes.length <= 1) continue;
1396
+ const keeperKey = pickKeeperKey(nodes, previousIdIndex.get(id));
1397
+ if (keeperKey) {
1398
+ keeperById.set(id, keeperKey);
1399
+ }
1400
+ }
1401
+ const used = /* @__PURE__ */ new Set();
1402
+ for (const child of children) {
1403
+ let id = $getState(child, blockIdState);
1404
+ const keeperKey = id ? keeperById.get(id) : null;
1405
+ const shouldRegenerate = !id || used.has(id) || keeperKey !== void 0 && child.getKey() !== keeperKey;
1406
+ if (shouldRegenerate) {
1407
+ id = generateBlockId(used);
1408
+ $setState(child, blockIdState, id);
1409
+ }
1410
+ used.add(id);
1411
+ }
1412
+ },
1413
+ { tag: NORMALIZATION_TAG }
1414
+ );
1415
+ }
1333
1416
  function BlockIdPlugin() {
1334
1417
  const [editor] = useLexicalComposerContext();
1335
1418
  useEffect(() => {
1336
- return editor.registerUpdateListener(({ tags }) => {
1337
- if (tags.has(NORMALIZATION_TAG)) return;
1338
- editor.read(() => {
1339
- const children = $getRoot().getChildren();
1340
- let needsUpdate = false;
1341
- const seen = /* @__PURE__ */ new Set();
1342
- for (const child of children) {
1343
- const id = $getState(child, blockIdState);
1344
- if (id === "" || seen.has(id)) {
1345
- needsUpdate = true;
1346
- break;
1347
- }
1348
- seen.add(id);
1349
- }
1350
- if (!needsUpdate) return;
1351
- editor.update(
1352
- () => {
1353
- $addUpdateTag("history-merge");
1354
- const children2 = $getRoot().getChildren();
1355
- const seen2 = /* @__PURE__ */ new Set();
1356
- for (const child of children2) {
1357
- let id = $getState(child, blockIdState);
1358
- if (id === "" || seen2.has(id)) {
1359
- id = crypto.randomUUID();
1360
- $setState(child, blockIdState, id);
1361
- }
1362
- seen2.add(id);
1363
- }
1364
- },
1365
- { tag: NORMALIZATION_TAG }
1366
- );
1367
- });
1368
- });
1419
+ const initialChildren = collectRootChildren(editor.getEditorState());
1420
+ if (hasDuplicateOrMissingId(initialChildren)) {
1421
+ normalizeRootBlockIds(editor, /* @__PURE__ */ new Map());
1422
+ }
1423
+ return editor.registerUpdateListener(
1424
+ ({ tags, editorState, prevEditorState }) => {
1425
+ if (tags.has(NORMALIZATION_TAG)) return;
1426
+ const children = collectRootChildren(editorState);
1427
+ if (!hasDuplicateOrMissingId(children)) return;
1428
+ const previousIdIndex = buildPreviousIdIndex(prevEditorState);
1429
+ normalizeRootBlockIds(editor, previousIdIndex);
1430
+ }
1431
+ );
1369
1432
  }, [editor]);
1370
1433
  return null;
1371
1434
  }
@@ -2446,6 +2509,7 @@ function RichEditor({
2446
2509
  className: clsx("rich-editor", variantClass, className),
2447
2510
  style,
2448
2511
  "data-theme": theme,
2512
+ suppressHydrationWarning: true,
2449
2513
  children: [
2450
2514
  /* @__PURE__ */ jsx(
2451
2515
  RichTextPlugin,
@@ -1 +1 @@
1
- {"version":3,"file":"RichEditor.d.ts","sourceRoot":"","sources":["../../src/components/RichEditor.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAI/C,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,QAAQ,EACR,OAAmB,EACnB,KAAe,EACf,WAAkC,EAClC,QAAQ,EACR,SAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,KAAK,EACL,OAAO,EACP,aAAa,EACb,UAAU,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,GACT,EAAE,eAAe,+BA0EjB"}
1
+ {"version":3,"file":"RichEditor.d.ts","sourceRoot":"","sources":["../../src/components/RichEditor.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAI/C,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,QAAQ,EACR,OAAmB,EACnB,KAAe,EACf,WAAkC,EAClC,QAAQ,EACR,SAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,KAAK,EACL,OAAO,EACP,aAAa,EACb,UAAU,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,GACT,EAAE,eAAe,+BA2EjB"}
package/dist/editor.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { R, a, c, g, s } from "./RichEditor-DnJwe663.js";
1
+ import { R, a, c, g, s } from "./RichEditor-Cv399wxR.js";
2
2
  import { a as a2, b, c as c2 } from "./theme-gVNBI_ET.js";
3
3
  export {
4
4
  R as RichEditor,
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { b as blockIdState } from "./RichEditor-DnJwe663.js";
2
- import { A, F, d, I, e, f, h, i, N, R, a, c, j, g, s, u } from "./RichEditor-DnJwe663.js";
1
+ import { b as blockIdState } from "./RichEditor-Cv399wxR.js";
2
+ import { A, F, d, I, e, f, h, i, N, R, a, c, j, g, s, u } from "./RichEditor-Cv399wxR.js";
3
3
  import { h as useFootnoteContent, j as useFootnoteDisplayNumber } from "./theme-gVNBI_ET.js";
4
4
  import { $, o, p, q, r, s as s2, t, v, w, x, y, z, A as A2, B, D, E, G, H, C, F as F2, I as I2, J, K, L, M, O, P, Q, S, T, U, N as N2, V, a as a2, b, W, e as e2, c as c2, X, f as f2, g as g2, u as u2, i as i2, k, l, m, n } from "./theme-gVNBI_ET.js";
5
5
  import { a as a3, c as c3, g as g3, n as n2 } from "./utils-fpeaZV1R.js";
@@ -1 +1 @@
1
- {"version":3,"file":"BlockIdPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/BlockIdPlugin.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,kDAEvB,CAAA;AAIF,wBAAgB,aAAa,SA6C5B"}
1
+ {"version":3,"file":"BlockIdPlugin.d.ts","sourceRoot":"","sources":["../../src/plugins/BlockIdPlugin.tsx"],"names":[],"mappings":"AAaA,eAAO,MAAM,YAAY,kDAEvB,CAAA;AA8GF,wBAAgB,aAAa,SAwB5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-editor",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "description": "Core rich text editor based on Lexical",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,10 +28,11 @@
28
28
  "dist"
29
29
  ],
30
30
  "dependencies": {
31
+ "nanoid": "^5.1.6",
31
32
  "thumbhash": "^0.1.1",
32
- "@haklex/rich-editor-ui": "0.0.42",
33
- "@haklex/rich-headless": "0.0.42",
34
- "@haklex/rich-style-token": "0.0.42"
33
+ "@haklex/rich-editor-ui": "0.0.43",
34
+ "@haklex/rich-style-token": "0.0.43",
35
+ "@haklex/rich-headless": "0.0.43"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@base-ui/react": "^1.2.0",