@haklex/rich-plugin-floating-toolbar 0.0.65 → 0.0.67

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/README.md CHANGED
@@ -1,30 +1,59 @@
1
1
  # @haklex/rich-plugin-floating-toolbar
2
2
 
3
- 浮动工具栏插件,选中文本时显示。
3
+ Floating toolbar plugin that appears on text selection for inline formatting.
4
4
 
5
- ## 安装
5
+ ## Installation
6
6
 
7
7
  ```bash
8
- pnpm add @haklex/rich-plugin-floating-toolbar @haklex/rich-editor
8
+ pnpm add @haklex/rich-plugin-floating-toolbar
9
9
  ```
10
10
 
11
- ## 导出
11
+ ## Peer Dependencies
12
12
 
13
- ```ts
14
- export { FloatingToolbarPlugin } from './FloatingToolbarPlugin'
15
- ```
13
+ | Package | Version |
14
+ | --- | --- |
15
+ | `@lexical/link` | `^0.41.0` |
16
+ | `@lexical/react` | `^0.41.0` |
17
+ | `@lexical/selection` | `^0.41.0` |
18
+ | `lexical` | `^0.41.0` |
19
+ | `lucide-react` | `^0.574.0` |
20
+ | `react` | `>= 19` |
21
+ | `react-dom` | `>= 19` |
16
22
 
17
- ## 使用
23
+ ## Usage
18
24
 
19
25
  ```tsx
20
26
  import { FloatingToolbarPlugin } from '@haklex/rich-plugin-floating-toolbar'
21
- import { RichEditor } from '@haklex/rich-editor'
22
-
23
- <RichEditor>
24
- <FloatingToolbarPlugin />
25
- </RichEditor>
27
+ import '@haklex/rich-plugin-floating-toolbar/style.css'
28
+
29
+ function Editor() {
30
+ return (
31
+ <RichEditor>
32
+ <FloatingToolbarPlugin />
33
+ </RichEditor>
34
+ )
35
+ }
26
36
  ```
27
37
 
38
+ When the user selects text, a floating toolbar appears near the selection with inline formatting controls such as bold, italic, underline, strikethrough, code, and link insertion.
39
+
40
+ ## Exports
41
+
42
+ | Export | Type | Description |
43
+ | --- | --- | --- |
44
+ | `FloatingToolbarPlugin` | Component | Main plugin component to render inside `RichEditor` |
45
+
46
+ ## Sub-path Exports
47
+
48
+ | Path | Description |
49
+ | --- | --- |
50
+ | `@haklex/rich-plugin-floating-toolbar` | Plugin component |
51
+ | `@haklex/rich-plugin-floating-toolbar/style.css` | Stylesheet |
52
+
53
+ ## Part of Haklex
54
+
55
+ This package is part of the [Haklex](../../README.md) rich editor ecosystem.
56
+
28
57
  ## License
29
58
 
30
59
  MIT
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingToolbarPlugin.d.ts","sourceRoot":"","sources":["../src/FloatingToolbarPlugin.tsx"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AA0MzC,wBAAgB,qBAAqB,IAAI,YAAY,GAAG,IAAI,CA0hB3D"}
1
+ {"version":3,"file":"FloatingToolbarPlugin.d.ts","sourceRoot":"","sources":["../src/FloatingToolbarPlugin.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AA6L1C,wBAAgB,qBAAqB,IAAI,YAAY,GAAG,IAAI,CA6f3D"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
- import { $createRubyNode, $isRubyNode } from "@haklex/rich-editor";
2
+ import { $createRubyNode, $isRubyNode } from "@haklex/rich-editor/nodes";
3
3
  import { ColorPicker } from "@haklex/rich-editor-ui";
4
4
  import { usePortalTheme, usePortalContainer, vars } from "@haklex/rich-style-token";
5
5
  import { TOGGLE_LINK_COMMAND, $isLinkNode, $isAutoLinkNode } from "@lexical/link";
@@ -108,32 +108,24 @@ function computePosition(nativeSelection, toolbar2, container) {
108
108
  const offsetY = containerRect?.top ?? 0;
109
109
  const availableWidth = containerRect?.width ?? window.innerWidth;
110
110
  const rawLeft = rect.left - offsetX + rect.width / 2 - toolbarWidth / 2;
111
- const clampedLeft = Math.max(
112
- 8,
113
- Math.min(rawLeft, availableWidth - toolbarWidth - 8)
114
- );
111
+ const clampedLeft = Math.max(8, Math.min(rawLeft, availableWidth - toolbarWidth - 8));
115
112
  return {
116
113
  top: rect.top - offsetY - toolbarHeight - 10,
117
114
  left: clampedLeft
118
115
  };
119
116
  }
120
- function ToolbarButton({
121
- active,
122
- onClick,
123
- ariaLabel,
124
- children
125
- }) {
117
+ function ToolbarButton({ active, onClick, ariaLabel, children }) {
126
118
  return /* @__PURE__ */ jsxs(
127
119
  "button",
128
120
  {
129
- type: "button",
121
+ "aria-label": ariaLabel,
122
+ "aria-pressed": active,
130
123
  className: `${btn}${active ? ` ${btnActive}` : ""}`,
124
+ type: "button",
131
125
  onMouseDown: (e) => {
132
126
  e.preventDefault();
133
127
  onClick();
134
128
  },
135
- "aria-label": ariaLabel,
136
- "aria-pressed": active,
137
129
  children: [
138
130
  children,
139
131
  active && /* @__PURE__ */ jsx("span", { className: btnIndicator })
@@ -144,7 +136,7 @@ function ToolbarButton({
144
136
  const ICON_SIZE = 15;
145
137
  const ICON_STROKE = 2;
146
138
  function extractCssVarName(value) {
147
- const match = value.match(/^var\((--[^),\s]+)(?:,[^)]+)?\)$/);
139
+ const match = value.match(/^var\((--[^\s),]+)(?:,[^)]+)?\)$/);
148
140
  return match?.[1] ?? null;
149
141
  }
150
142
  function collectThemeVarNames(contract, output) {
@@ -205,13 +197,11 @@ function FloatingToolbarPlugin() {
205
197
  },
206
198
  COMMAND_PRIORITY_LOW
207
199
  );
208
- const unregisterUpdate = editor.registerUpdateListener(
209
- ({ editorState }) => {
210
- editorState.read(() => {
211
- updateToolbar();
212
- });
213
- }
214
- );
200
+ const unregisterUpdate = editor.registerUpdateListener(({ editorState }) => {
201
+ editorState.read(() => {
202
+ updateToolbar();
203
+ });
204
+ });
215
205
  return () => {
216
206
  unregisterCommand();
217
207
  unregisterUpdate();
@@ -269,9 +259,7 @@ function FloatingToolbarPlugin() {
269
259
  return isEffectiveLinkNode(parent) || isEffectiveLinkNode(node);
270
260
  });
271
261
  if (hasLink) {
272
- for (const autoLinkNode of collectSelectedActiveAutoLinkNodes(
273
- selection
274
- )) {
262
+ for (const autoLinkNode of collectSelectedActiveAutoLinkNodes(selection)) {
275
263
  autoLinkNode.setIsUnlinked(true);
276
264
  autoLinkNode.markDirty();
277
265
  }
@@ -430,10 +418,7 @@ function FloatingToolbarPlugin() {
430
418
  const availW = containerRect?.width ?? window.innerWidth;
431
419
  const editorWidth = editorEl.offsetWidth;
432
420
  const rawLeft = rect.left - oX + rect.width / 2 - editorWidth / 2;
433
- const clampedLeft = Math.max(
434
- 8,
435
- Math.min(rawLeft, availW - editorWidth - 8)
436
- );
421
+ const clampedLeft = Math.max(8, Math.min(rawLeft, availW - editorWidth - 8));
437
422
  editorEl.style.top = `${rect.bottom - oY + 8}px`;
438
423
  editorEl.style.left = `${clampedLeft}px`;
439
424
  };
@@ -450,18 +435,18 @@ function FloatingToolbarPlugin() {
450
435
  /* @__PURE__ */ jsxs(
451
436
  "div",
452
437
  {
453
- ref: toolbarRef,
438
+ "aria-label": "Text formatting",
454
439
  className: toolbarClassName,
440
+ ref: toolbarRef,
455
441
  role: "toolbar",
456
- "aria-label": "Text formatting",
457
442
  style: { position: "fixed", zIndex: 50 },
458
443
  children: [
459
444
  /* @__PURE__ */ jsx(
460
445
  ToolbarButton,
461
446
  {
462
447
  active: state.isBold,
463
- onClick: () => handleFormat("bold"),
464
448
  ariaLabel: "Bold",
449
+ onClick: () => handleFormat("bold"),
465
450
  children: /* @__PURE__ */ jsx(Bold, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
466
451
  }
467
452
  ),
@@ -469,8 +454,8 @@ function FloatingToolbarPlugin() {
469
454
  ToolbarButton,
470
455
  {
471
456
  active: state.isItalic,
472
- onClick: () => handleFormat("italic"),
473
457
  ariaLabel: "Italic",
458
+ onClick: () => handleFormat("italic"),
474
459
  children: /* @__PURE__ */ jsx(Italic, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
475
460
  }
476
461
  ),
@@ -478,8 +463,8 @@ function FloatingToolbarPlugin() {
478
463
  ToolbarButton,
479
464
  {
480
465
  active: state.isUnderline,
481
- onClick: () => handleFormat("underline"),
482
466
  ariaLabel: "Underline",
467
+ onClick: () => handleFormat("underline"),
483
468
  children: /* @__PURE__ */ jsx(Underline, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
484
469
  }
485
470
  ),
@@ -487,8 +472,8 @@ function FloatingToolbarPlugin() {
487
472
  ToolbarButton,
488
473
  {
489
474
  active: state.isStrikethrough,
490
- onClick: () => handleFormat("strikethrough"),
491
475
  ariaLabel: "Strikethrough",
476
+ onClick: () => handleFormat("strikethrough"),
492
477
  children: /* @__PURE__ */ jsx(Strikethrough, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
493
478
  }
494
479
  ),
@@ -496,8 +481,8 @@ function FloatingToolbarPlugin() {
496
481
  ToolbarButton,
497
482
  {
498
483
  active: state.isSuperscript,
499
- onClick: () => handleFormat("superscript"),
500
484
  ariaLabel: "Superscript",
485
+ onClick: () => handleFormat("superscript"),
501
486
  children: /* @__PURE__ */ jsx(Superscript, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
502
487
  }
503
488
  ),
@@ -505,8 +490,8 @@ function FloatingToolbarPlugin() {
505
490
  ToolbarButton,
506
491
  {
507
492
  active: state.isSubscript,
508
- onClick: () => handleFormat("subscript"),
509
493
  ariaLabel: "Subscript",
494
+ onClick: () => handleFormat("subscript"),
510
495
  children: /* @__PURE__ */ jsx(Subscript, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
511
496
  }
512
497
  ),
@@ -515,8 +500,8 @@ function FloatingToolbarPlugin() {
515
500
  ToolbarButton,
516
501
  {
517
502
  active: state.isCode,
518
- onClick: () => handleFormat("code"),
519
503
  ariaLabel: "Code",
504
+ onClick: () => handleFormat("code"),
520
505
  children: /* @__PURE__ */ jsx(Code, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
521
506
  }
522
507
  ),
@@ -524,37 +509,15 @@ function FloatingToolbarPlugin() {
524
509
  ToolbarButton,
525
510
  {
526
511
  active: state.isHighlight,
527
- onClick: () => handleFormat("highlight"),
528
512
  ariaLabel: "Highlight",
513
+ onClick: () => handleFormat("highlight"),
529
514
  children: /* @__PURE__ */ jsx(Highlighter, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
530
515
  }
531
516
  ),
532
- /* @__PURE__ */ jsx(
533
- ToolbarButton,
534
- {
535
- active: state.isLink,
536
- onClick: handleLink,
537
- ariaLabel: "Link",
538
- children: /* @__PURE__ */ jsx(Link, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
539
- }
540
- ),
541
- /* @__PURE__ */ jsx(
542
- ToolbarButton,
543
- {
544
- active: state.isRuby,
545
- onClick: handleRuby,
546
- ariaLabel: "Ruby annotation",
547
- children: /* @__PURE__ */ jsx(Languages, { size: ICON_SIZE, strokeWidth: ICON_STROKE })
548
- }
549
- ),
517
+ /* @__PURE__ */ jsx(ToolbarButton, { active: state.isLink, ariaLabel: "Link", onClick: handleLink, children: /* @__PURE__ */ jsx(Link, { size: ICON_SIZE, strokeWidth: ICON_STROKE }) }),
518
+ /* @__PURE__ */ jsx(ToolbarButton, { active: state.isRuby, ariaLabel: "Ruby annotation", onClick: handleRuby, children: /* @__PURE__ */ jsx(Languages, { size: ICON_SIZE, strokeWidth: ICON_STROKE }) }),
550
519
  /* @__PURE__ */ jsx("span", { className: separator }),
551
- /* @__PURE__ */ jsx(
552
- ColorPicker,
553
- {
554
- currentColor: state.fontColor || "inherit",
555
- onSelect: handleColor
556
- }
557
- )
520
+ /* @__PURE__ */ jsx(ColorPicker, { currentColor: state.fontColor || "inherit", onSelect: handleColor })
558
521
  ]
559
522
  }
560
523
  ),
@@ -564,8 +527,8 @@ function FloatingToolbarPlugin() {
564
527
  /* @__PURE__ */ jsxs(
565
528
  "div",
566
529
  {
567
- ref: rubyEditorRef,
568
530
  className: rubyEditorClassName,
531
+ ref: rubyEditorRef,
569
532
  style: { position: "fixed", zIndex: 51 },
570
533
  children: [
571
534
  /* @__PURE__ */ jsxs("div", { className: rubyPreview, children: [
@@ -576,12 +539,11 @@ function FloatingToolbarPlugin() {
576
539
  /* @__PURE__ */ jsx(
577
540
  "input",
578
541
  {
579
- ref: rubyInputRef,
580
542
  className: rubyInput,
543
+ placeholder: "读音",
544
+ ref: rubyInputRef,
581
545
  value: rubyEdit.reading,
582
- onChange: (e) => setRubyEdit(
583
- (prev) => prev ? { ...prev, reading: e.target.value } : null
584
- ),
546
+ onChange: (e) => setRubyEdit((prev) => prev ? { ...prev, reading: e.target.value } : null),
585
547
  onKeyDown: (e) => {
586
548
  if (e.key === "Enter") {
587
549
  e.preventDefault();
@@ -591,34 +553,33 @@ function FloatingToolbarPlugin() {
591
553
  e.preventDefault();
592
554
  handleRubyCancel();
593
555
  }
594
- },
595
- placeholder: "读音"
556
+ }
596
557
  }
597
558
  ),
598
559
  /* @__PURE__ */ jsx(
599
560
  "button",
600
561
  {
601
- type: "button",
562
+ "aria-label": "Confirm",
602
563
  className: rubyActionBtn,
564
+ style: { color: "#22c55e" },
565
+ type: "button",
603
566
  onMouseDown: (e) => {
604
567
  e.preventDefault();
605
568
  handleRubyConfirm();
606
569
  },
607
- style: { color: "#22c55e" },
608
- "aria-label": "Confirm",
609
570
  children: /* @__PURE__ */ jsx(Check, { size: 14, strokeWidth: ICON_STROKE })
610
571
  }
611
572
  ),
612
573
  /* @__PURE__ */ jsx(
613
574
  "button",
614
575
  {
615
- type: "button",
576
+ "aria-label": "Cancel",
616
577
  className: rubyActionBtn,
578
+ type: "button",
617
579
  onMouseDown: (e) => {
618
580
  e.preventDefault();
619
581
  handleRubyCancel();
620
582
  },
621
- "aria-label": "Cancel",
622
583
  children: /* @__PURE__ */ jsx(X, { size: 14, strokeWidth: ICON_STROKE })
623
584
  }
624
585
  ),
@@ -626,14 +587,14 @@ function FloatingToolbarPlugin() {
626
587
  /* @__PURE__ */ jsx(
627
588
  "button",
628
589
  {
629
- type: "button",
590
+ "aria-label": "Delete ruby",
630
591
  className: rubyActionBtn,
592
+ style: { color: "#ef4444" },
593
+ type: "button",
631
594
  onMouseDown: (e) => {
632
595
  e.preventDefault();
633
596
  handleRubyDelete();
634
597
  },
635
- style: { color: "#ef4444" },
636
- "aria-label": "Delete ruby",
637
598
  children: /* @__PURE__ */ jsx(Trash2, { size: 14, strokeWidth: ICON_STROKE })
638
599
  }
639
600
  )
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@haklex/rich-plugin-floating-toolbar",
3
- "type": "module",
4
- "version": "0.0.65",
3
+ "version": "0.0.67",
5
4
  "description": "Floating toolbar plugin",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Innei/haklex.git",
8
+ "directory": "packages/rich-plugin-floating-toolbar"
9
+ },
6
10
  "license": "MIT",
11
+ "type": "module",
7
12
  "exports": {
8
13
  ".": {
9
14
  "import": "./dist/index.mjs",
@@ -15,19 +20,10 @@
15
20
  "files": [
16
21
  "dist"
17
22
  ],
18
- "peerDependencies": {
19
- "@lexical/link": "^0.41.0",
20
- "@lexical/react": "^0.41.0",
21
- "@lexical/selection": "^0.41.0",
22
- "lexical": "^0.41.0",
23
- "lucide-react": "^0.574.0",
24
- "react": ">=19",
25
- "react-dom": ">=19"
26
- },
27
23
  "dependencies": {
28
- "@haklex/rich-editor-ui": "0.0.65",
29
- "@haklex/rich-editor": "0.0.65",
30
- "@haklex/rich-style-token": "0.0.65"
24
+ "@haklex/rich-editor": "0.0.67",
25
+ "@haklex/rich-editor-ui": "0.0.67",
26
+ "@haklex/rich-style-token": "0.0.67"
31
27
  },
32
28
  "devDependencies": {
33
29
  "@lexical/link": "^0.41.0",
@@ -45,6 +41,15 @@
45
41
  "vite": "^7.3.1",
46
42
  "vite-plugin-dts": "^4.5.4"
47
43
  },
44
+ "peerDependencies": {
45
+ "@lexical/link": "^0.41.0",
46
+ "@lexical/react": "^0.41.0",
47
+ "@lexical/selection": "^0.41.0",
48
+ "lexical": "^0.41.0",
49
+ "lucide-react": "^0.574.0",
50
+ "react": ">=19",
51
+ "react-dom": ">=19"
52
+ },
48
53
  "publishConfig": {
49
54
  "access": "public"
50
55
  },