torch-glare 2.4.0 → 2.4.2

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 (318) hide show
  1. package/README.md +8 -0
  2. package/apps/lib/components/ActionButton.tsx +7 -12
  3. package/apps/lib/components/ActionsGroup.tsx +4 -15
  4. package/apps/lib/components/AlertDialog.tsx +173 -191
  5. package/apps/lib/components/Avatar.tsx +13 -16
  6. package/apps/lib/components/Badge.tsx +1 -3
  7. package/apps/lib/components/BadgeField.tsx +76 -71
  8. package/apps/lib/components/Breadcrumb.tsx +49 -85
  9. package/apps/lib/components/Button.tsx +21 -20
  10. package/apps/lib/components/ButtonGroup.tsx +31 -41
  11. package/apps/lib/components/Calendar.tsx +51 -29
  12. package/apps/lib/components/Card.tsx +40 -24
  13. package/apps/lib/components/Checkbox.tsx +9 -16
  14. package/apps/lib/components/ColorPicker.tsx +441 -0
  15. package/apps/lib/components/ConclusionHeader.tsx +148 -0
  16. package/apps/lib/components/ContextMenu.tsx +97 -96
  17. package/apps/lib/components/CountBadge.tsx +4 -6
  18. package/apps/lib/components/DataTable.tsx +196 -216
  19. package/apps/lib/components/DataViews/DataViewRadio.tsx +1 -6
  20. package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +13 -47
  21. package/apps/lib/components/DataViews/DataViewsHeader.tsx +1 -3
  22. package/apps/lib/components/DataViews/DataViewsLayout.tsx +5 -12
  23. package/apps/lib/components/DataViews/FilterPanel.tsx +155 -149
  24. package/apps/lib/components/DataViews/HeaderSearch.tsx +1 -5
  25. package/apps/lib/components/DataViews/InboxView.tsx +22 -68
  26. package/apps/lib/components/DataViews/InboxViewCard.tsx +68 -77
  27. package/apps/lib/components/DataViews/KanbanView.tsx +8 -25
  28. package/apps/lib/components/DataViews/PanelControls.tsx +3 -13
  29. package/apps/lib/components/DataViews/SettingsPanel.tsx +83 -89
  30. package/apps/lib/components/DataViews/TableView.tsx +8 -28
  31. package/apps/lib/components/DataViews/TreeView.tsx +12 -40
  32. package/apps/lib/components/DataViews/badgeAdapter.ts +32 -28
  33. package/apps/lib/components/DataViews/fieldRenderers.tsx +106 -141
  34. package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +30 -23
  35. package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +39 -32
  36. package/apps/lib/components/DataViews/filters/PresetChips.tsx +15 -15
  37. package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +69 -58
  38. package/apps/lib/components/DataViews/index.ts +26 -26
  39. package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +12 -16
  40. package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +28 -31
  41. package/apps/lib/components/DataViews/types.ts +104 -115
  42. package/apps/lib/components/DatePicker.tsx +200 -196
  43. package/apps/lib/components/Dialog.tsx +92 -105
  44. package/apps/lib/components/Divider.tsx +21 -24
  45. package/apps/lib/components/Drawer.tsx +124 -142
  46. package/apps/lib/components/DropdownMenu.tsx +131 -171
  47. package/apps/lib/components/FieldHint.tsx +4 -11
  48. package/apps/lib/components/Form.tsx +146 -155
  49. package/apps/lib/components/FormBuilder/DisplayField.tsx +40 -0
  50. package/apps/lib/components/FormBuilder/context.ts +71 -0
  51. package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +65 -0
  52. package/apps/lib/components/FormBuilder/fields/ColorField.tsx +76 -0
  53. package/apps/lib/components/FormBuilder/fields/CustomField.tsx +16 -0
  54. package/apps/lib/components/FormBuilder/fields/DateField.tsx +34 -0
  55. package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +79 -0
  56. package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +174 -0
  57. package/apps/lib/components/FormBuilder/fields/FileField.tsx +40 -0
  58. package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +139 -0
  59. package/apps/lib/components/FormBuilder/fields/OtpField.tsx +32 -0
  60. package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +82 -0
  61. package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +37 -0
  62. package/apps/lib/components/FormBuilder/fields/SelectField.tsx +101 -0
  63. package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +175 -0
  64. package/apps/lib/components/FormBuilder/fields/SliderField.tsx +72 -0
  65. package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +42 -0
  66. package/apps/lib/components/FormBuilder/fields/TableField.tsx +312 -0
  67. package/apps/lib/components/FormBuilder/fields/TextField.tsx +222 -0
  68. package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +49 -0
  69. package/apps/lib/components/FormBuilder/fields/countries.ts +303 -0
  70. package/apps/lib/components/FormBuilder/fields/index.ts +25 -0
  71. package/apps/lib/components/FormBuilder/form-builder.tsx +292 -0
  72. package/apps/lib/components/FormBuilder/header.tsx +105 -0
  73. package/apps/lib/components/FormBuilder/index.ts +37 -0
  74. package/apps/lib/components/FormBuilder/numberFormat.ts +16 -0
  75. package/apps/lib/components/FormBuilder/stepper.tsx +217 -0
  76. package/apps/lib/components/FormBuilder/submit.tsx +41 -0
  77. package/apps/lib/components/FormBuilder/types.ts +264 -0
  78. package/apps/lib/components/FormBuilder/viewFormat.tsx +137 -0
  79. package/apps/lib/components/FormRenderer/FormDrawer.tsx +121 -0
  80. package/apps/lib/components/FormRenderer/form-renderer.tsx +113 -0
  81. package/apps/lib/components/FormRenderer/index.ts +9 -0
  82. package/apps/lib/components/FormRenderer/types.ts +79 -0
  83. package/apps/lib/components/FormStepper.tsx +9 -39
  84. package/apps/lib/components/FormSummary.tsx +282 -0
  85. package/apps/lib/components/HeaderBar.tsx +1 -9
  86. package/apps/lib/components/ImageAttachment.tsx +106 -93
  87. package/apps/lib/components/InnerLabelField.tsx +20 -16
  88. package/apps/lib/components/Input.tsx +171 -172
  89. package/apps/lib/components/InputField.tsx +44 -45
  90. package/apps/lib/components/InputOTP.tsx +68 -69
  91. package/apps/lib/components/Label.tsx +61 -58
  92. package/apps/lib/components/LabelField.tsx +4 -5
  93. package/apps/lib/components/LabeledCheckBox.tsx +8 -12
  94. package/apps/lib/components/LabeledRadio.tsx +7 -5
  95. package/apps/lib/components/LinkButton.tsx +2 -9
  96. package/apps/lib/components/LoginButton.tsx +4 -9
  97. package/apps/lib/components/PasswordLevel.tsx +5 -5
  98. package/apps/lib/components/Popover.tsx +15 -33
  99. package/apps/lib/components/ProfileMenu.tsx +67 -72
  100. package/apps/lib/components/Radio.tsx +1 -5
  101. package/apps/lib/components/RadioCard.tsx +11 -28
  102. package/apps/lib/components/ScrollArea.tsx +45 -43
  103. package/apps/lib/components/SearchField.tsx +49 -39
  104. package/apps/lib/components/SearchableSelect.tsx +16 -16
  105. package/apps/lib/components/SearchableTable.tsx +108 -118
  106. package/apps/lib/components/SearchableTree.tsx +469 -0
  107. package/apps/lib/components/SearchableTreeDialog.tsx +493 -0
  108. package/apps/lib/components/SectionBlock.tsx +19 -23
  109. package/apps/lib/components/Select.tsx +80 -186
  110. package/apps/lib/components/SimpleSelect.tsx +196 -187
  111. package/apps/lib/components/Skeleton.tsx +9 -12
  112. package/apps/lib/components/SlideDatePicker.tsx +215 -166
  113. package/apps/lib/components/SpinLoading.tsx +33 -49
  114. package/apps/lib/components/Stepper.tsx +13 -56
  115. package/apps/lib/components/Switch.tsx +8 -10
  116. package/apps/lib/components/TabFormItem.tsx +3 -12
  117. package/apps/lib/components/TabSwitch.tsx +7 -34
  118. package/apps/lib/components/Table.tsx +60 -75
  119. package/apps/lib/components/{ChartBlockTool.ts → TextEditor/ChartBlockTool.ts} +17 -52
  120. package/apps/lib/components/TextEditor/RichTextField.tsx +46 -0
  121. package/apps/lib/components/{TableDnDWrapper.ts → TextEditor/TableDnDWrapper.ts} +15 -48
  122. package/apps/lib/components/{TextEditor.tsx → TextEditor/TextEditor.tsx} +156 -113
  123. package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +429 -0
  124. package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +70 -0
  125. package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +50 -0
  126. package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +48 -0
  127. package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +98 -0
  128. package/apps/lib/{types → components/TextEditor}/editorjs.d.ts +19 -0
  129. package/apps/lib/components/TextEditor/index.ts +7 -0
  130. package/apps/lib/{utils → components/TextEditor}/markdownParser.ts +2 -7
  131. package/apps/lib/components/Textarea.tsx +9 -13
  132. package/apps/lib/components/Timeline.tsx +21 -41
  133. package/apps/lib/components/Toast.tsx +83 -83
  134. package/apps/lib/components/Toggle.tsx +112 -114
  135. package/apps/lib/components/ToggleButton.tsx +1 -1
  136. package/apps/lib/components/Tooltip.tsx +24 -19
  137. package/apps/lib/components/TransparentLabel.tsx +61 -62
  138. package/apps/lib/components/TreeDropDown.tsx +86 -52
  139. package/apps/lib/components/TreeFolder/TreeFolder.tsx +15 -48
  140. package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +16 -21
  141. package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +9 -20
  142. package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +3 -3
  143. package/apps/lib/components/TreeFolder/icons.tsx +14 -17
  144. package/apps/lib/components/TreeFolder/index.ts +15 -8
  145. package/apps/lib/components/TreeFolder/treeFolderUtils.ts +47 -52
  146. package/apps/lib/components/TreeFolder/types.ts +32 -32
  147. package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +117 -123
  148. package/apps/lib/hooks/useActiveTreeItem.ts +52 -55
  149. package/apps/lib/hooks/useClickOutside.ts +21 -20
  150. package/apps/lib/hooks/useDataViewsState.ts +86 -80
  151. package/apps/lib/hooks/useIsMobile.ts +12 -12
  152. package/apps/lib/hooks/useResize.ts +54 -65
  153. package/apps/lib/hooks/useTagSelection.ts +201 -201
  154. package/apps/lib/layouts/CNLayout.tsx +181 -140
  155. package/apps/lib/layouts/DataViewCard.tsx +4 -18
  156. package/apps/lib/layouts/FieldSection.tsx +2 -6
  157. package/apps/lib/layouts/TreeSubLayout.tsx +24 -38
  158. package/apps/lib/providers/ThemeProvider.tsx +8 -23
  159. package/apps/lib/registry.json +997 -0
  160. package/apps/lib/tsconfig.json +9 -0
  161. package/apps/lib/tsconfig.tsbuildinfo +1 -0
  162. package/apps/lib/utils/cn.ts +1 -1
  163. package/apps/lib/utils/color.ts +175 -0
  164. package/apps/lib/utils/dataViews/columnUtils.ts +46 -44
  165. package/apps/lib/utils/dataViews/fieldUtils.ts +70 -71
  166. package/apps/lib/utils/dataViews/nestedDataUtils.tsx +129 -122
  167. package/apps/lib/utils/dataViews/pathUtils.ts +74 -67
  168. package/apps/lib/utils/dataViews/rangeUtils.ts +121 -112
  169. package/apps/lib/utils/dataViews/treeUtils.ts +204 -211
  170. package/apps/lib/utils/dateFormat.ts +64 -54
  171. package/apps/lib/utils/resize.ts +21 -25
  172. package/apps/lib/utils/types.ts +13 -13
  173. package/dist/bin/index.js +8 -2
  174. package/dist/bin/index.js.map +1 -1
  175. package/dist/src/shared/getAvailableFiles.d.ts.map +1 -1
  176. package/dist/src/shared/getAvailableFiles.js +6 -1
  177. package/dist/src/shared/getAvailableFiles.js.map +1 -1
  178. package/dist/src/shared/loadRegistry.d.ts +7 -0
  179. package/dist/src/shared/loadRegistry.d.ts.map +1 -0
  180. package/dist/src/shared/loadRegistry.js +31 -0
  181. package/dist/src/shared/loadRegistry.js.map +1 -0
  182. package/dist/src/shared/resolveInstallPlan.d.ts +12 -0
  183. package/dist/src/shared/resolveInstallPlan.d.ts.map +1 -0
  184. package/dist/src/shared/resolveInstallPlan.js +39 -0
  185. package/dist/src/shared/resolveInstallPlan.js.map +1 -0
  186. package/dist/src/types/main.d.ts +24 -0
  187. package/dist/src/types/main.d.ts.map +1 -1
  188. package/docs/README.md +10 -2
  189. package/docs/components/action-button.md +30 -24
  190. package/docs/components/actions-group.md +26 -20
  191. package/docs/components/alert-dialog.md +45 -45
  192. package/docs/components/avatar.md +6 -2
  193. package/docs/components/badge-field.md +6 -2
  194. package/docs/components/badge.md +7 -1
  195. package/docs/components/breadcrumb.md +15 -40
  196. package/docs/components/button-group.md +13 -10
  197. package/docs/components/button.md +58 -33
  198. package/docs/components/calendar.md +26 -19
  199. package/docs/components/card.md +23 -18
  200. package/docs/components/chart-block-tool.md +129 -0
  201. package/docs/components/checkbox.md +16 -8
  202. package/docs/components/cn-layout.md +7 -1
  203. package/docs/components/color-picker.md +101 -0
  204. package/docs/components/conclusion-header.md +80 -0
  205. package/docs/components/context-menu.md +18 -25
  206. package/docs/components/count-badge.md +7 -1
  207. package/docs/components/data-table.md +45 -33
  208. package/docs/components/data-views-config-panel.md +15 -11
  209. package/docs/components/data-views-layout.md +13 -16
  210. package/docs/components/date-picker.md +30 -23
  211. package/docs/components/dialog.md +43 -42
  212. package/docs/components/divider.md +10 -4
  213. package/docs/components/drawer.md +161 -135
  214. package/docs/components/dropdown-menu.md +25 -34
  215. package/docs/components/field-hint.md +37 -25
  216. package/docs/components/field-section.md +9 -3
  217. package/docs/components/form-builder.md +270 -0
  218. package/docs/components/form-renderer.md +228 -0
  219. package/docs/components/form-stepper.md +6 -0
  220. package/docs/components/form-summary.md +123 -0
  221. package/docs/components/form.md +17 -20
  222. package/docs/components/header-bar.md +6 -0
  223. package/docs/components/image-attachment.md +43 -45
  224. package/docs/components/inbox-view.md +16 -5
  225. package/docs/components/inner-label-field.md +34 -24
  226. package/docs/components/input-field.md +18 -10
  227. package/docs/components/input-otp.md +14 -11
  228. package/docs/components/input.md +22 -16
  229. package/docs/components/kanban-view.md +15 -5
  230. package/docs/components/label-field.md +32 -22
  231. package/docs/components/label.md +43 -32
  232. package/docs/components/labeled-check-box.md +16 -8
  233. package/docs/components/labeled-radio.md +18 -11
  234. package/docs/components/link-button.md +7 -1
  235. package/docs/components/login-button.md +8 -2
  236. package/docs/components/password-level.md +37 -24
  237. package/docs/components/popover.md +26 -23
  238. package/docs/components/profile-menu.md +38 -27
  239. package/docs/components/radio-card.md +16 -9
  240. package/docs/components/radio.md +16 -8
  241. package/docs/components/scroll-area.md +6 -2
  242. package/docs/components/search-field.md +14 -6
  243. package/docs/components/searchable-select.md +19 -14
  244. package/docs/components/searchable-table.md +18 -13
  245. package/docs/components/searchable-tree-dialog.md +190 -0
  246. package/docs/components/searchable-tree.md +200 -0
  247. package/docs/components/section-block.md +12 -4
  248. package/docs/components/select.md +23 -18
  249. package/docs/components/simple-select.md +14 -10
  250. package/docs/components/skeleton.md +43 -33
  251. package/docs/components/slide-date-picker.md +37 -29
  252. package/docs/components/spin-loading.md +32 -23
  253. package/docs/components/stepper.md +6 -0
  254. package/docs/components/switch.md +14 -6
  255. package/docs/components/tab-form-item.md +7 -1
  256. package/docs/components/tab-switch.md +13 -6
  257. package/docs/components/table-dnd-wrapper.md +107 -0
  258. package/docs/components/table-view.md +15 -5
  259. package/docs/components/table.md +28 -28
  260. package/docs/components/text-editor.md +37 -13
  261. package/docs/components/textarea.md +15 -7
  262. package/docs/components/timeline.md +6 -0
  263. package/docs/components/toast.md +35 -26
  264. package/docs/components/toggle-button.md +24 -21
  265. package/docs/components/toggle.md +23 -15
  266. package/docs/components/tooltip.md +42 -30
  267. package/docs/components/transparent-label.md +29 -20
  268. package/docs/components/tree-drop-down.md +31 -23
  269. package/docs/components/tree-sub-layout.md +6 -2
  270. package/docs/components/tree-view.md +16 -5
  271. package/docs/explanation/architecture.md +54 -0
  272. package/docs/explanation/design-system.md +52 -0
  273. package/docs/how-to/data-views-from-backend-response.md +6 -4
  274. package/docs/how-to/form-and-list-recipes.md +26 -19
  275. package/docs/how-to/forms-with-form-builder.md +408 -0
  276. package/docs/how-to/guides.md +177 -197
  277. package/docs/migration/changelog.md +38 -0
  278. package/docs/reference/cli.md +87 -0
  279. package/docs/reference/components.md +11 -12
  280. package/docs/reference/hooks.md +8 -8
  281. package/docs/reference/providers.md +22 -22
  282. package/docs/reference/tailwind-plugins.md +2 -2
  283. package/docs/reference/theme.md +85 -0
  284. package/docs/reference/types.md +4 -4
  285. package/docs/reference/utilities.md +19 -26
  286. package/docs/tutorials/building-first-form.md +170 -172
  287. package/docs/tutorials/component-composition.md +34 -50
  288. package/docs/tutorials/getting-started.md +2 -2
  289. package/docs/tutorials/theming-basics.md +13 -10
  290. package/package.json +31 -3
  291. package/dist/src/commands/block.d.ts +0 -5
  292. package/dist/src/commands/block.d.ts.map +0 -1
  293. package/dist/src/commands/block.js +0 -255
  294. package/dist/src/commands/block.js.map +0 -1
  295. package/dist/src/shared/configureFonts.d.ts +0 -6
  296. package/dist/src/shared/configureFonts.d.ts.map +0 -1
  297. package/dist/src/shared/configureFonts.js +0 -106
  298. package/dist/src/shared/configureFonts.js.map +0 -1
  299. package/dist/src/shared/configureGlobalCss.d.ts +0 -6
  300. package/dist/src/shared/configureGlobalCss.d.ts.map +0 -1
  301. package/dist/src/shared/configureGlobalCss.js +0 -154
  302. package/dist/src/shared/configureGlobalCss.js.map +0 -1
  303. package/dist/src/shared/configureTailwind.d.ts +0 -7
  304. package/dist/src/shared/configureTailwind.d.ts.map +0 -1
  305. package/dist/src/shared/configureTailwind.js +0 -163
  306. package/dist/src/shared/configureTailwind.js.map +0 -1
  307. package/dist/src/shared/detectFramework.d.ts +0 -23
  308. package/dist/src/shared/detectFramework.d.ts.map +0 -1
  309. package/dist/src/shared/detectFramework.js +0 -119
  310. package/dist/src/shared/detectFramework.js.map +0 -1
  311. package/dist/src/shared/installBaseUtils.d.ts +0 -5
  312. package/dist/src/shared/installBaseUtils.d.ts.map +0 -1
  313. package/dist/src/shared/installBaseUtils.js +0 -79
  314. package/dist/src/shared/installBaseUtils.js.map +0 -1
  315. package/dist/src/shared/resolveAliases.d.ts +0 -24
  316. package/dist/src/shared/resolveAliases.d.ts.map +0 -1
  317. package/dist/src/shared/resolveAliases.js +0 -98
  318. package/dist/src/shared/resolveAliases.js.map +0 -1
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import { lazy, Suspense, useEffect, useState } from "react";
4
+ import type { OutputData } from "@editorjs/editorjs";
5
+
6
+ /**
7
+ * Client-only, lazily-loaded wrapper around the EditorJS `TextEditor`.
8
+ *
9
+ * EditorJS references DOM globals (`Element`) at module-load time, which crashes
10
+ * server-side rendering. Statically importing `TextEditor` in the field registry
11
+ * would pull it into the SSR bundle; loading it lazily + only after mount keeps
12
+ * the builder SSR-safe — the editor is imported on the client, on demand.
13
+ */
14
+ const TextEditorLazy = lazy(() => import("./TextEditor").then((m) => ({ default: m.TextEditor })));
15
+
16
+ export interface RichTextFieldProps {
17
+ data?: OutputData;
18
+ onChange?: (data: OutputData) => void;
19
+ readOnly?: boolean;
20
+ placeholder?: string;
21
+ /** Editor size — controls min-height + padding (the toolbar gutter). Default "M". */
22
+ size?: "S" | "M" | "L" | "XL";
23
+ }
24
+
25
+ const placeholderBox = (
26
+ <div className="min-h-[200px] w-full rounded-[6px] border border-border-presentation-action-primary bg-background-presentation-form-field-primary" />
27
+ );
28
+
29
+ export function RichTextField({ size = "M", ...props }: RichTextFieldProps) {
30
+ const [mounted, setMounted] = useState(false);
31
+ useEffect(() => setMounted(true), []);
32
+
33
+ if (!mounted) return placeholderBox;
34
+
35
+ return (
36
+ <Suspense fallback={placeholderBox}>
37
+ <TextEditorLazy
38
+ size={size}
39
+ className="border border-border-presentation-action-primary"
40
+ {...props}
41
+ />
42
+ </Suspense>
43
+ );
44
+ }
45
+
46
+ RichTextField.displayName = "RichTextField";
@@ -27,9 +27,7 @@ interface DragState {
27
27
  startY: number;
28
28
  }
29
29
 
30
- export default function createTableDnDClass(
31
- OriginalTable: TableClass,
32
- ): TableClass {
30
+ export default function createTableDnDClass(OriginalTable: TableClass): TableClass {
33
31
  return class TableDnD extends OriginalTable {
34
32
  private _drag: DragState = {
35
33
  active: false,
@@ -101,9 +99,7 @@ export default function createTableDnDClass(
101
99
 
102
100
  // Proximity-based toolbox visibility:
103
101
  // Only show row handle near left edge, column handle near top edge
104
- wrapper.addEventListener("mousemove", (e) =>
105
- this._onWrapperMouseMove(e),
106
- );
102
+ wrapper.addEventListener("mousemove", (e) => this._onWrapperMouseMove(e));
107
103
  wrapper.addEventListener("mouseleave", () => {
108
104
  wrapper.classList.remove("tc-dnd--show-row", "tc-dnd--show-col");
109
105
  });
@@ -140,8 +136,7 @@ export default function createTableDnDClass(
140
136
  // ─── Proximity-based toolbox visibility ──────────────────────────────
141
137
 
142
138
  private _onWrapperMouseMove(e: MouseEvent): void {
143
- const tableEl =
144
- this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
139
+ const tableEl = this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
145
140
  if (!tableEl || !this._wrapperRef) return;
146
141
 
147
142
  const tableRect = tableEl.getBoundingClientRect();
@@ -172,20 +167,13 @@ export default function createTableDnDClass(
172
167
  this._wrapperRef.classList.add("tc-dnd--show-col");
173
168
  this._wrapperRef.classList.remove("tc-dnd--show-row");
174
169
  } else {
175
- this._wrapperRef.classList.remove(
176
- "tc-dnd--show-row",
177
- "tc-dnd--show-col",
178
- );
170
+ this._wrapperRef.classList.remove("tc-dnd--show-row", "tc-dnd--show-col");
179
171
  }
180
172
  }
181
173
 
182
174
  // ─── Toggler pointerdown (start pending drag) ───────────────────────
183
175
 
184
- private _onTogglerDown(
185
- type: "row" | "col",
186
- wrapper: HTMLElement,
187
- e: PointerEvent,
188
- ): void {
176
+ private _onTogglerDown(type: "row" | "col", wrapper: HTMLElement, e: PointerEvent): void {
189
177
  // Detect which row/col is currently hovered by the table plugin
190
178
  const index = this._detectHoveredIndex(type, wrapper);
191
179
  if (index < 0) return;
@@ -208,10 +196,7 @@ export default function createTableDnDClass(
208
196
 
209
197
  // ─── Detect hovered row/col index ───────────────────────────────────
210
198
 
211
- private _detectHoveredIndex(
212
- type: "row" | "col",
213
- wrapper: HTMLElement,
214
- ): number {
199
+ private _detectHoveredIndex(type: "row" | "col", wrapper: HTMLElement): number {
215
200
  const tableEl = wrapper.querySelector<HTMLElement>(".tc-table");
216
201
  if (!tableEl) return -1;
217
202
 
@@ -230,9 +215,7 @@ export default function createTableDnDClass(
230
215
  }
231
216
  return -1;
232
217
  } else {
233
- const toolbox = wrapper.querySelector<HTMLElement>(
234
- ".tc-toolbox--column",
235
- );
218
+ const toolbox = wrapper.querySelector<HTMLElement>(".tc-toolbox--column");
236
219
  if (!toolbox) return -1;
237
220
  const toolboxRect = toolbox.getBoundingClientRect();
238
221
  const toolboxMid = toolboxRect.left + toolboxRect.width / 2;
@@ -265,8 +248,7 @@ export default function createTableDnDClass(
265
248
  d.active = true;
266
249
 
267
250
  // Close any open popover from the table plugin
268
- const openPopover =
269
- this._wrapperRef?.querySelector<HTMLElement>(".tc-popover--opened");
251
+ const openPopover = this._wrapperRef?.querySelector<HTMLElement>(".tc-popover--opened");
270
252
  openPopover?.classList.remove("tc-popover--opened");
271
253
 
272
254
  this._highlight(d.type!, d.index, true);
@@ -338,13 +320,10 @@ export default function createTableDnDClass(
338
320
  // ─── Move Row / Column ──────────────────────────────────────────────
339
321
 
340
322
  private _moveRow(from: number, to: number): void {
341
- const tableEl =
342
- this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
323
+ const tableEl = this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
343
324
  if (!tableEl) return;
344
325
 
345
- const rows = Array.from(
346
- tableEl.querySelectorAll<HTMLElement>(".tc-row"),
347
- );
326
+ const rows = Array.from(tableEl.querySelectorAll<HTMLElement>(".tc-row"));
348
327
  if (from < 0 || from >= rows.length) return;
349
328
 
350
329
  const target = to > from ? to - 1 : to;
@@ -360,17 +339,14 @@ export default function createTableDnDClass(
360
339
  }
361
340
 
362
341
  private _moveCol(from: number, to: number): void {
363
- const tableEl =
364
- this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
342
+ const tableEl = this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
365
343
  if (!tableEl) return;
366
344
 
367
345
  const target = to > from ? to - 1 : to;
368
346
  if (target === from) return;
369
347
 
370
348
  tableEl.querySelectorAll<HTMLElement>(".tc-row").forEach((row) => {
371
- const cells = Array.from(
372
- row.querySelectorAll<HTMLElement>(".tc-cell"),
373
- );
349
+ const cells = Array.from(row.querySelectorAll<HTMLElement>(".tc-cell"));
374
350
  if (from >= cells.length) return;
375
351
 
376
352
  const cell = cells[from];
@@ -385,11 +361,7 @@ export default function createTableDnDClass(
385
361
 
386
362
  // ─── Drop Index Calc ────────────────────────────────────────────────
387
363
 
388
- private _calcDrop(
389
- wrapper: HTMLElement,
390
- cx: number,
391
- cy: number,
392
- ): number {
364
+ private _calcDrop(wrapper: HTMLElement, cx: number, cy: number): number {
393
365
  const tableEl = wrapper.querySelector<HTMLElement>(".tc-table");
394
366
  if (!tableEl) return this._drag.index;
395
367
 
@@ -414,13 +386,8 @@ export default function createTableDnDClass(
414
386
 
415
387
  // ─── Visual Feedback ────────────────────────────────────────────────
416
388
 
417
- private _highlight(
418
- type: "row" | "col",
419
- idx: number,
420
- on: boolean,
421
- ): void {
422
- const tableEl =
423
- this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
389
+ private _highlight(type: "row" | "col", idx: number, on: boolean): void {
390
+ const tableEl = this._wrapperRef?.querySelector<HTMLElement>(".tc-table");
424
391
  if (!tableEl) return;
425
392
 
426
393
  if (type === "row") {
@@ -10,10 +10,10 @@ import React, {
10
10
  useState,
11
11
  } from "react";
12
12
  import { cva, type VariantProps } from "class-variance-authority";
13
- import { cn } from "../utils/cn";
14
- import { Themes } from "../utils/types";
13
+ import { cn } from "../../utils/cn";
14
+ import { Themes } from "../../utils/types";
15
15
  import EditorJS, { type OutputData } from "@editorjs/editorjs";
16
- import { isMarkdown, parseMarkdownToBlocks } from "../utils/markdownParser";
16
+ import { isMarkdown, parseMarkdownToBlocks } from "./markdownParser";
17
17
 
18
18
  // ─── Tool Imports ────────────────────────────────────────────────────────────
19
19
  import Header from "@editorjs/header";
@@ -34,7 +34,14 @@ import Marker from "@editorjs/marker";
34
34
  import InlineCode from "@editorjs/inline-code";
35
35
  import Underline from "@editorjs/underline";
36
36
  import TextVariantTune from "@editorjs/text-variant-tune";
37
+ // `editorjs-undo` touches DOM globals at module-eval, so it is imported dynamically
38
+ // (browser-only) in `onReady`; keep only the type here.
39
+ import type Undo from "editorjs-undo";
37
40
  import ChartBlockTool from "./ChartBlockTool";
41
+ import StrikethroughInlineTool from "./editor-tools/StrikethroughInlineTool";
42
+ import ColorInlineTool from "./editor-tools/ColorInlineTool";
43
+ import AlignmentTune from "./editor-tools/AlignmentTune";
44
+ import { TextEditorToolbar } from "./TextEditorToolbar";
38
45
 
39
46
  // ─── RTL Detection ───────────────────────────────────────────────────────────
40
47
  const RTL_REGEX = /[\u0591-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]/;
@@ -66,7 +73,10 @@ function setDirIfChanged(el: HTMLElement, dir: "rtl" | "ltr") {
66
73
  }
67
74
  }
68
75
 
69
- function applyDirectionToBlock(block: HTMLElement, fallbackDir: "rtl" | "ltr" = "ltr"): "rtl" | "ltr" {
76
+ function applyDirectionToBlock(
77
+ block: HTMLElement,
78
+ fallbackDir: "rtl" | "ltr" = "ltr",
79
+ ): "rtl" | "ltr" {
70
80
  // Skip non-text blocks (delimiters, embeds, code)
71
81
  if (block.querySelector(".ce-delimiter, .cdx-embed, .embed-tool")) return fallbackDir;
72
82
 
@@ -78,9 +88,7 @@ function applyDirectionToBlock(block: HTMLElement, fallbackDir: "rtl" | "ltr" =
78
88
  if (listItems.length > 0) {
79
89
  let prevDir: "rtl" | "ltr" = fallbackDir;
80
90
  listItems.forEach((item) => {
81
- const editable = item.querySelector<HTMLElement>(
82
- '[contenteditable="true"]',
83
- );
91
+ const editable = item.querySelector<HTMLElement>('[contenteditable="true"]');
84
92
  if (!editable) return;
85
93
  const text = (editable.textContent || "").trim();
86
94
  const dir = text ? getTextDirection(text) : prevDir;
@@ -93,9 +101,7 @@ function applyDirectionToBlock(block: HTMLElement, fallbackDir: "rtl" | "ltr" =
93
101
 
94
102
  // Handle regular blocks (paragraph, header, quote, warning, etc.)
95
103
  let lastDir = fallbackDir;
96
- const editables = block.querySelectorAll<HTMLElement>(
97
- '[contenteditable="true"]',
98
- );
104
+ const editables = block.querySelectorAll<HTMLElement>('[contenteditable="true"]');
99
105
  editables.forEach((el) => {
100
106
  const text = (el.textContent || "").trim();
101
107
  const dir = text ? getTextDirection(text) : fallbackDir;
@@ -169,15 +175,25 @@ const getDefaultTools = (): Record<string, any> => ({
169
175
  linkTool: { class: LinkTool },
170
176
  image: { class: SimpleImage },
171
177
  raw: { class: RawTool },
172
- chart: { class: ChartBlockTool as any },
178
+ chart: { class: ChartBlockTool },
173
179
  marker: { class: Marker, shortcut: "CMD+SHIFT+M" },
174
180
  inlineCode: { class: InlineCode, shortcut: "CMD+SHIFT+I" },
175
181
  underline: { class: Underline },
182
+ // Registered so their `sanitize` unions into every block's save config (strike/color
183
+ // markup persists) and they appear in the inline toolbar; the fixed toolbar drives them too.
184
+ strikethrough: { class: StrikethroughInlineTool, shortcut: "CMD+SHIFT+X" },
185
+ color: { class: ColorInlineTool },
176
186
  textVariant: { class: TextVariantTune },
187
+ // Per-block text alignment; wired into `tunes` below so every block gets it.
188
+ align: { class: AlignmentTune },
177
189
  });
178
190
 
179
191
  // ─── Auto-direction CSS ──────────────────────────────────────────────────────
180
192
  const AUTO_DIR_STYLES = `
193
+ /* Block alignment (AlignmentTune sets data-align on the block content wrapper). */
194
+ .torch-text-editor .cdx-align-wrap[data-align="center"] { text-align: center; }
195
+ .torch-text-editor .cdx-align-wrap[data-align="right"] { text-align: right; }
196
+ .torch-text-editor .cdx-align-wrap[data-align="left"] { text-align: left; }
181
197
  .torch-text-editor [contenteditable="true"][dir="rtl"] {
182
198
  unicode-bidi: plaintext;
183
199
  }
@@ -692,7 +708,8 @@ const textEditorStyles = cva(
692
708
  "relative w-full rounded-[6px]",
693
709
  "border-0 transition-all duration-200 ease-in-out",
694
710
  "[&_.codex-editor]:min-h-[inherit]",
695
- "[&_.codex-editor__redactor]:min-h-[inherit] [&_.codex-editor__redactor]:pb-[100px]",
711
+ // Content padding lives on the redactor (not the container) so the toolbar stays full-bleed.
712
+ "[&_.codex-editor__redactor]:min-h-[inherit] [&_.codex-editor__redactor]:px-[16px] [&_.codex-editor__redactor]:pt-[12px] [&_.codex-editor__redactor]:pb-[100px]",
696
713
  "[&_.ce-paragraph]:typography-body-medium-regular",
697
714
  ],
698
715
  {
@@ -728,11 +745,13 @@ const textEditorStyles = cva(
728
745
  "[&_.ce-delimiter]:border-border-presentation-action-primary",
729
746
  ],
730
747
  },
748
+ // No padding on the container — the fixed toolbar must go edge-to-edge. Content
749
+ // padding is applied to the EditorJS redactor below instead.
731
750
  size: {
732
- S: "min-h-[200px] p-2",
733
- M: "min-h-[300px] p-3",
734
- L: "min-h-[400px] p-4",
735
- XL: "min-h-[500px] p-5",
751
+ S: "min-h-[200px]",
752
+ M: "min-h-[300px]",
753
+ L: "min-h-[400px]",
754
+ XL: "min-h-[500px]",
736
755
  },
737
756
  disabled: {
738
757
  true: "cursor-not-allowed opacity-60 pointer-events-none",
@@ -754,9 +773,7 @@ export interface TextEditorRef {
754
773
  }
755
774
 
756
775
  interface TextEditorProps
757
- extends
758
- Omit<HTMLAttributes<HTMLDivElement>, "onChange">,
759
- VariantProps<typeof textEditorStyles> {
776
+ extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">, VariantProps<typeof textEditorStyles> {
760
777
  theme?: Themes;
761
778
  data?: OutputData;
762
779
  onChange?: (data: OutputData) => void;
@@ -767,6 +784,8 @@ interface TextEditorProps
767
784
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
768
785
  tools?: Record<string, any>;
769
786
  minHeight?: number;
787
+ /** Show the fixed formatting toolbar (default `true`; hidden when `readOnly`/`disabled`). */
788
+ toolbar?: boolean;
770
789
  }
771
790
 
772
791
  // ─── Component ───────────────────────────────────────────────────────────────
@@ -786,16 +805,17 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
786
805
  autofocus = false,
787
806
  tools,
788
807
  minHeight,
808
+ toolbar = true,
789
809
  ...props
790
810
  },
791
811
  ref,
792
812
  ) => {
793
813
  const editorRef = useRef<EditorJS | null>(null);
814
+ const undoRef = useRef<Undo | null>(null);
815
+ const [toolbarReady, setToolbarReady] = useState(false);
794
816
  const holderRef = useRef<HTMLDivElement>(null);
795
817
  const observerRef = useRef<MutationObserver | null>(null);
796
- const [holderId] = useState(
797
- () => `torch-editor-${Math.random().toString(36).substring(2, 9)}`,
798
- );
818
+ const [holderId] = useState(() => `torch-editor-${Math.random().toString(36).substring(2, 9)}`);
799
819
  const isInitializing = useRef(false);
800
820
  const pasteHandlerRef = useRef<((e: ClipboardEvent) => void) | null>(null);
801
821
  const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -839,107 +859,104 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
839
859
  }, []);
840
860
 
841
861
  // ── Markdown paste handler ──
842
- const setupMarkdownPaste = useCallback(
843
- (holderEl: HTMLElement) => {
844
- const handler = async (e: ClipboardEvent) => {
845
- if (readOnlyRef.current || !editorRef.current) return;
862
+ const setupMarkdownPaste = useCallback((holderEl: HTMLElement) => {
863
+ const handler = async (e: ClipboardEvent) => {
864
+ if (readOnlyRef.current || !editorRef.current) return;
846
865
 
847
- const plain = e.clipboardData?.getData("text/plain") || "";
866
+ const plain = e.clipboardData?.getData("text/plain") || "";
848
867
 
849
- // Only intercept if the plain text is markdown
850
- if (!isMarkdown(plain)) return;
868
+ // Only intercept if the plain text is markdown
869
+ if (!isMarkdown(plain)) return;
851
870
 
852
- e.preventDefault();
853
- e.stopPropagation();
871
+ e.preventDefault();
872
+ e.stopPropagation();
854
873
 
855
- const parsedBlocks = parseMarkdownToBlocks(plain);
856
- if (parsedBlocks.length === 0) return;
874
+ const parsedBlocks = parseMarkdownToBlocks(plain);
875
+ if (parsedBlocks.length === 0) return;
857
876
 
858
- const editor = editorRef.current;
877
+ const editor = editorRef.current;
878
+
879
+ // Save existing content, merge new blocks at cursor, render once
880
+ // This avoids per-block insert which freezes the editor on large pastes
881
+ try {
882
+ const existingData = await editor.save();
883
+ const existingBlocks = existingData.blocks || [];
859
884
 
860
- // Save existing content, merge new blocks at cursor, render once
861
- // This avoids per-block insert which freezes the editor on large pastes
885
+ // Determine insertion point
886
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
887
+ const blocks = (editor as any).blocks;
888
+ let insertIdx = existingBlocks.length;
862
889
  try {
863
- const existingData = await editor.save();
864
- const existingBlocks = existingData.blocks || [];
865
-
866
- // Determine insertion point
867
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
868
- const blocks = (editor as any).blocks;
869
- let insertIdx = existingBlocks.length;
870
- try {
871
- if (blocks?.getCurrentBlockIndex) {
872
- insertIdx = blocks.getCurrentBlockIndex() + 1;
873
- }
874
- } catch {
875
- // Use end of document
890
+ if (blocks?.getCurrentBlockIndex) {
891
+ insertIdx = blocks.getCurrentBlockIndex() + 1;
876
892
  }
893
+ } catch {
894
+ // Use end of document
895
+ }
877
896
 
878
- // Convert parsed blocks to OutputData block format
879
- const newBlocks = parsedBlocks.map((block) => ({
880
- type: block.type,
881
- data: block.data,
882
- }));
883
-
884
- // Remove empty paragraph at cursor if it's the only content there
885
- const filteredExisting = [...existingBlocks];
886
- if (insertIdx > 0) {
887
- const cursorBlock = filteredExisting[insertIdx - 1];
888
- if (
889
- cursorBlock &&
890
- cursorBlock.type === "paragraph" &&
891
- (!cursorBlock.data.text ||
892
- cursorBlock.data.text.trim() === "" ||
893
- cursorBlock.data.text === "<br>")
894
- ) {
895
- filteredExisting.splice(insertIdx - 1, 1);
896
- insertIdx = Math.max(0, insertIdx - 1);
897
- }
897
+ // Convert parsed blocks to OutputData block format
898
+ const newBlocks = parsedBlocks.map((block) => ({
899
+ type: block.type,
900
+ data: block.data,
901
+ }));
902
+
903
+ // Remove empty paragraph at cursor if it's the only content there
904
+ const filteredExisting = [...existingBlocks];
905
+ if (insertIdx > 0) {
906
+ const cursorBlock = filteredExisting[insertIdx - 1];
907
+ if (
908
+ cursorBlock &&
909
+ cursorBlock.type === "paragraph" &&
910
+ (!cursorBlock.data.text ||
911
+ cursorBlock.data.text.trim() === "" ||
912
+ cursorBlock.data.text === "<br>")
913
+ ) {
914
+ filteredExisting.splice(insertIdx - 1, 1);
915
+ insertIdx = Math.max(0, insertIdx - 1);
898
916
  }
917
+ }
899
918
 
900
- // Merge: existing blocks before cursor + new blocks + existing blocks after cursor
901
- const mergedBlocks = [
902
- ...filteredExisting.slice(0, insertIdx),
903
- ...newBlocks,
904
- ...filteredExisting.slice(insertIdx),
905
- ];
906
-
907
- // Pause MutationObserver during bulk render to prevent O(n^2) direction checks
908
- observerRef.current?.disconnect();
909
-
910
- // Single render pass — no per-block DOM thrashing
911
- await editor.render({
912
- time: Date.now(),
913
- version: existingData.version,
914
- blocks: mergedBlocks,
915
- });
919
+ // Merge: existing blocks before cursor + new blocks + existing blocks after cursor
920
+ const mergedBlocks = [
921
+ ...filteredExisting.slice(0, insertIdx),
922
+ ...newBlocks,
923
+ ...filteredExisting.slice(insertIdx),
924
+ ];
925
+
926
+ // Pause MutationObserver during bulk render to prevent O(n^2) direction checks
927
+ observerRef.current?.disconnect();
928
+
929
+ // Single render pass — no per-block DOM thrashing
930
+ await editor.render({
931
+ time: Date.now(),
932
+ version: existingData.version,
933
+ blocks: mergedBlocks,
934
+ });
916
935
 
917
- // Reconnect observer on the redactor element
918
- if (observerRef.current) {
919
- const redactor = holderEl.querySelector(".codex-editor__redactor") || holderEl;
920
- observerRef.current.observe(redactor, {
921
- childList: true,
922
- subtree: false,
923
- });
924
- }
925
- // Defer direction scan to next frame — let the browser paint first
926
- requestAnimationFrame(() => applyAutoDirection(holderEl));
936
+ // Reconnect observer on the redactor element
937
+ if (observerRef.current) {
938
+ const redactor = holderEl.querySelector(".codex-editor__redactor") || holderEl;
939
+ observerRef.current.observe(redactor, {
940
+ childList: true,
941
+ subtree: false,
942
+ });
943
+ }
944
+ // Defer direction scan to next frame — let the browser paint first
945
+ requestAnimationFrame(() => applyAutoDirection(holderEl));
927
946
 
928
- // Trigger onChange with merged data
929
- if (onChangeRef.current) {
930
- const savedData = await editor.save();
931
- onChangeRef.current(savedData);
932
- }
933
- } catch {
934
- // Fallback: if render approach fails, do nothing rather than freeze
947
+ // Trigger onChange with merged data
948
+ if (onChangeRef.current) {
949
+ const savedData = await editor.save();
950
+ onChangeRef.current(savedData);
935
951
  }
936
- };
952
+ } catch {
953
+ // Fallback: if render approach fails, do nothing rather than freeze
954
+ }
955
+ };
937
956
 
938
- holderEl.addEventListener("paste", handler as unknown as EventListener, true);
939
- pasteHandlerRef.current = handler;
940
- },
941
- [],
942
- );
957
+ holderEl.addEventListener("paste", handler as unknown as EventListener, true);
958
+ pasteHandlerRef.current = handler;
959
+ }, []);
943
960
 
944
961
  // ── Auto-direction: watch for DOM changes & input ──
945
962
  const inputHandlerRef = useRef<((e: Event) => void) | null>(null);
@@ -989,10 +1006,13 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
989
1006
  if (isInitializing.current || editorRef.current) return;
990
1007
  isInitializing.current = true;
991
1008
 
1009
+ // Only enable the `align` tune when using the default tools (a caller-supplied `tools`
1010
+ // map may not register it).
1011
+ const usingDefaultTools = !toolsRef.current;
992
1012
  const editor = new EditorJS({
993
1013
  holder: holderId,
994
1014
  tools: toolsRef.current ?? getDefaultTools(),
995
- tunes: ["textVariant"],
1015
+ tunes: usingDefaultTools ? ["textVariant", "align"] : ["textVariant"],
996
1016
  data: initialDataRef.current || undefined,
997
1017
  readOnly: readOnlyRef.current,
998
1018
  placeholder: placeholderRef.current,
@@ -1007,6 +1027,21 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
1007
1027
  setupMarkdownPaste(holderEl);
1008
1028
  }
1009
1029
 
1030
+ // History (undo/redo) for the fixed toolbar + Ctrl+Z/Y. Not for readOnly.
1031
+ // Dynamically imported because the package evaluates DOM globals at module load.
1032
+ if (!readOnlyRef.current) {
1033
+ import("editorjs-undo")
1034
+ .then(({ default: UndoCls }) => {
1035
+ if (!editorRef.current) return;
1036
+ undoRef.current = new UndoCls({ editor: editorRef.current });
1037
+ if (initialDataRef.current) undoRef.current.initialize(initialDataRef.current);
1038
+ })
1039
+ .catch(() => {
1040
+ undoRef.current = null;
1041
+ });
1042
+ }
1043
+ setToolbarReady(true);
1044
+
1010
1045
  onReadyRef.current?.();
1011
1046
  },
1012
1047
  onChange: () => {
@@ -1052,13 +1087,12 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
1052
1087
  (window.cancelIdleCallback || clearTimeout)(idleCallbackRef.current);
1053
1088
  }
1054
1089
 
1055
- if (
1056
- editorRef.current &&
1057
- typeof editorRef.current.destroy === "function"
1058
- ) {
1090
+ if (editorRef.current && typeof editorRef.current.destroy === "function") {
1059
1091
  editorRef.current.destroy();
1060
1092
  editorRef.current = null;
1061
1093
  }
1094
+ undoRef.current = null;
1095
+ setToolbarReady(false);
1062
1096
  isInitializing.current = false;
1063
1097
  };
1064
1098
  }, [initEditor]);
@@ -1109,6 +1143,15 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
1109
1143
  style={minHeight ? { minHeight: `${minHeight}px` } : undefined}
1110
1144
  {...props}
1111
1145
  >
1146
+ {toolbar && !readOnly && !disabled && (
1147
+ <TextEditorToolbar
1148
+ editorRef={editorRef}
1149
+ undoRef={undoRef}
1150
+ holderRef={holderRef}
1151
+ ready={toolbarReady}
1152
+ theme={theme}
1153
+ />
1154
+ )}
1112
1155
  <div id={holderId} />
1113
1156
  </div>
1114
1157
  </>