@jhits/plugin-blog 0.0.10 → 0.0.12

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 (276) hide show
  1. package/package.json +4 -5
  2. package/src/api/categories.ts +0 -43
  3. package/src/api/check-title.ts +0 -60
  4. package/src/api/config-handler.ts +0 -76
  5. package/src/api/handler.ts +0 -418
  6. package/src/api/index.ts +0 -33
  7. package/src/api/route.ts +0 -116
  8. package/src/api/router.ts +0 -128
  9. package/src/api-server.ts +0 -11
  10. package/src/config.ts +0 -161
  11. package/src/hooks/index.d.ts +0 -8
  12. package/src/hooks/index.d.ts.map +0 -1
  13. package/src/hooks/index.js +0 -7
  14. package/src/hooks/index.ts +0 -9
  15. package/src/hooks/useBlog.d.ts +0 -31
  16. package/src/hooks/useBlog.d.ts.map +0 -1
  17. package/src/hooks/useBlog.js +0 -57
  18. package/src/hooks/useBlog.ts +0 -85
  19. package/src/hooks/useBlogs.d.ts +0 -39
  20. package/src/hooks/useBlogs.d.ts.map +0 -1
  21. package/src/hooks/useBlogs.js +0 -82
  22. package/src/hooks/useBlogs.ts +0 -123
  23. package/src/hooks/useCategories.d.ts +0 -9
  24. package/src/hooks/useCategories.d.ts.map +0 -1
  25. package/src/hooks/useCategories.js +0 -70
  26. package/src/hooks/useCategories.ts +0 -76
  27. package/src/index.d.ts +0 -55
  28. package/src/index.d.ts.map +0 -1
  29. package/src/index.js +0 -228
  30. package/src/index.server.ts +0 -14
  31. package/src/index.tsx +0 -335
  32. package/src/init.d.ts +0 -40
  33. package/src/init.d.ts.map +0 -1
  34. package/src/init.js +0 -41
  35. package/src/init.tsx +0 -63
  36. package/src/lib/blocks/BlockRenderer.d.ts +0 -54
  37. package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
  38. package/src/lib/blocks/BlockRenderer.js +0 -54
  39. package/src/lib/blocks/BlockRenderer.tsx +0 -141
  40. package/src/lib/blocks/index.ts +0 -6
  41. package/src/lib/config-storage.d.ts +0 -30
  42. package/src/lib/config-storage.d.ts.map +0 -1
  43. package/src/lib/config-storage.js +0 -31
  44. package/src/lib/config-storage.ts +0 -65
  45. package/src/lib/index.ts +0 -9
  46. package/src/lib/layouts/blocks/ColumnsBlock.d.ts +0 -25
  47. package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +0 -1
  48. package/src/lib/layouts/blocks/ColumnsBlock.js +0 -182
  49. package/src/lib/layouts/blocks/ColumnsBlock.tsx +0 -298
  50. package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +0 -81
  51. package/src/lib/layouts/blocks/SectionBlock.d.ts +0 -25
  52. package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
  53. package/src/lib/layouts/blocks/SectionBlock.js +0 -44
  54. package/src/lib/layouts/blocks/SectionBlock.tsx +0 -104
  55. package/src/lib/layouts/blocks/index.ts +0 -8
  56. package/src/lib/layouts/index.d.ts +0 -23
  57. package/src/lib/layouts/index.d.ts.map +0 -1
  58. package/src/lib/layouts/index.js +0 -45
  59. package/src/lib/layouts/index.ts +0 -52
  60. package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
  61. package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
  62. package/src/lib/layouts/registerLayoutBlocks.js +0 -60
  63. package/src/lib/layouts/registerLayoutBlocks.ts +0 -64
  64. package/src/lib/mappers/apiMapper.d.ts +0 -66
  65. package/src/lib/mappers/apiMapper.d.ts.map +0 -1
  66. package/src/lib/mappers/apiMapper.js +0 -191
  67. package/src/lib/mappers/apiMapper.ts +0 -254
  68. package/src/lib/migration/index.ts +0 -6
  69. package/src/lib/migration/mapper.ts +0 -140
  70. package/src/lib/rich-text/RichTextEditor.d.ts +0 -45
  71. package/src/lib/rich-text/RichTextEditor.d.ts.map +0 -1
  72. package/src/lib/rich-text/RichTextEditor.js +0 -564
  73. package/src/lib/rich-text/RichTextEditor.tsx +0 -826
  74. package/src/lib/rich-text/RichTextPreview.d.ts +0 -16
  75. package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
  76. package/src/lib/rich-text/RichTextPreview.js +0 -144
  77. package/src/lib/rich-text/RichTextPreview.tsx +0 -210
  78. package/src/lib/rich-text/index.d.ts +0 -9
  79. package/src/lib/rich-text/index.d.ts.map +0 -1
  80. package/src/lib/rich-text/index.js +0 -6
  81. package/src/lib/rich-text/index.ts +0 -10
  82. package/src/lib/utils/blockHelpers.d.ts +0 -23
  83. package/src/lib/utils/blockHelpers.d.ts.map +0 -1
  84. package/src/lib/utils/blockHelpers.js +0 -65
  85. package/src/lib/utils/blockHelpers.ts +0 -72
  86. package/src/lib/utils/configValidation.d.ts +0 -23
  87. package/src/lib/utils/configValidation.d.ts.map +0 -1
  88. package/src/lib/utils/configValidation.js +0 -113
  89. package/src/lib/utils/configValidation.ts +0 -137
  90. package/src/lib/utils/index.ts +0 -8
  91. package/src/lib/utils/slugify.ts +0 -79
  92. package/src/registry/BlockRegistry.d.ts +0 -62
  93. package/src/registry/BlockRegistry.d.ts.map +0 -1
  94. package/src/registry/BlockRegistry.js +0 -112
  95. package/src/registry/BlockRegistry.ts +0 -139
  96. package/src/registry/index.d.ts +0 -6
  97. package/src/registry/index.d.ts.map +0 -1
  98. package/src/registry/index.js +0 -4
  99. package/src/registry/index.ts +0 -11
  100. package/src/state/EditorContext.d.ts +0 -45
  101. package/src/state/EditorContext.d.ts.map +0 -1
  102. package/src/state/EditorContext.js +0 -215
  103. package/src/state/EditorContext.tsx +0 -283
  104. package/src/state/index.d.ts +0 -7
  105. package/src/state/index.d.ts.map +0 -1
  106. package/src/state/index.js +0 -6
  107. package/src/state/index.ts +0 -8
  108. package/src/state/reducer.d.ts +0 -11
  109. package/src/state/reducer.d.ts.map +0 -1
  110. package/src/state/reducer.js +0 -443
  111. package/src/state/reducer.ts +0 -694
  112. package/src/state/types.d.ts +0 -162
  113. package/src/state/types.d.ts.map +0 -1
  114. package/src/state/types.js +0 -27
  115. package/src/state/types.ts +0 -160
  116. package/src/types/block.d.ts +0 -221
  117. package/src/types/block.d.ts.map +0 -1
  118. package/src/types/block.js +0 -6
  119. package/src/types/block.ts +0 -269
  120. package/src/types/index.d.ts +0 -8
  121. package/src/types/index.d.ts.map +0 -1
  122. package/src/types/index.js +0 -5
  123. package/src/types/index.ts +0 -17
  124. package/src/types/post.d.ts +0 -136
  125. package/src/types/post.d.ts.map +0 -1
  126. package/src/types/post.js +0 -5
  127. package/src/types/post.ts +0 -169
  128. package/src/utils/client.d.ts +0 -48
  129. package/src/utils/client.d.ts.map +0 -1
  130. package/src/utils/client.js +0 -77
  131. package/src/utils/client.ts +0 -122
  132. package/src/utils/index.ts +0 -7
  133. package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
  134. package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
  135. package/src/views/CanvasEditor/BlockWrapper.js +0 -276
  136. package/src/views/CanvasEditor/BlockWrapper.tsx +0 -522
  137. package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
  138. package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
  139. package/src/views/CanvasEditor/CanvasEditorView.js +0 -209
  140. package/src/views/CanvasEditor/CanvasEditorView.tsx +0 -337
  141. package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
  142. package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
  143. package/src/views/CanvasEditor/EditorBody.js +0 -505
  144. package/src/views/CanvasEditor/EditorBody.tsx +0 -665
  145. package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
  146. package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
  147. package/src/views/CanvasEditor/EditorHeader.js +0 -101
  148. package/src/views/CanvasEditor/EditorHeader.tsx +0 -268
  149. package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
  150. package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
  151. package/src/views/CanvasEditor/LayoutContainer.js +0 -222
  152. package/src/views/CanvasEditor/LayoutContainer.tsx +0 -322
  153. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
  154. package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
  155. package/src/views/CanvasEditor/SaveConfirmationModal.js +0 -78
  156. package/src/views/CanvasEditor/SaveConfirmationModal.tsx +0 -233
  157. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +0 -14
  158. package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +0 -1
  159. package/src/views/CanvasEditor/components/CustomBlockItem.js +0 -44
  160. package/src/views/CanvasEditor/components/CustomBlockItem.tsx +0 -92
  161. package/src/views/CanvasEditor/components/EditorCanvas.d.ts +0 -29
  162. package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
  163. package/src/views/CanvasEditor/components/EditorCanvas.js +0 -32
  164. package/src/views/CanvasEditor/components/EditorCanvas.tsx +0 -160
  165. package/src/views/CanvasEditor/components/EditorLibrary.d.ts +0 -7
  166. package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +0 -1
  167. package/src/views/CanvasEditor/components/EditorLibrary.js +0 -25
  168. package/src/views/CanvasEditor/components/EditorLibrary.tsx +0 -122
  169. package/src/views/CanvasEditor/components/EditorSidebar.d.ts +0 -13
  170. package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
  171. package/src/views/CanvasEditor/components/EditorSidebar.js +0 -20
  172. package/src/views/CanvasEditor/components/EditorSidebar.tsx +0 -181
  173. package/src/views/CanvasEditor/components/ErrorBanner.d.ts +0 -6
  174. package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +0 -1
  175. package/src/views/CanvasEditor/components/ErrorBanner.js +0 -8
  176. package/src/views/CanvasEditor/components/ErrorBanner.tsx +0 -31
  177. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -25
  178. package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
  179. package/src/views/CanvasEditor/components/FeaturedMediaSection.js +0 -182
  180. package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +0 -341
  181. package/src/views/CanvasEditor/components/LibraryItem.d.ts +0 -14
  182. package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +0 -1
  183. package/src/views/CanvasEditor/components/LibraryItem.js +0 -43
  184. package/src/views/CanvasEditor/components/LibraryItem.tsx +0 -80
  185. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -15
  186. package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
  187. package/src/views/CanvasEditor/components/PrivacySettingsSection.js +0 -63
  188. package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +0 -212
  189. package/src/views/CanvasEditor/components/index.d.ts +0 -21
  190. package/src/views/CanvasEditor/components/index.d.ts.map +0 -1
  191. package/src/views/CanvasEditor/components/index.js +0 -12
  192. package/src/views/CanvasEditor/components/index.ts +0 -28
  193. package/src/views/CanvasEditor/hooks/index.d.ts +0 -10
  194. package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
  195. package/src/views/CanvasEditor/hooks/index.js +0 -9
  196. package/src/views/CanvasEditor/hooks/index.ts +0 -10
  197. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +0 -8
  198. package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +0 -1
  199. package/src/views/CanvasEditor/hooks/useHeroBlock.js +0 -79
  200. package/src/views/CanvasEditor/hooks/useHeroBlock.ts +0 -103
  201. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +0 -3
  202. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
  203. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.js +0 -114
  204. package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +0 -142
  205. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +0 -5
  206. package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +0 -1
  207. package/src/views/CanvasEditor/hooks/usePostLoader.js +0 -32
  208. package/src/views/CanvasEditor/hooks/usePostLoader.ts +0 -39
  209. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +0 -2
  210. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
  211. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.js +0 -47
  212. package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +0 -55
  213. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +0 -25
  214. package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +0 -1
  215. package/src/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -285
  216. package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +0 -339
  217. package/src/views/CanvasEditor/index.d.ts +0 -16
  218. package/src/views/CanvasEditor/index.d.ts.map +0 -1
  219. package/src/views/CanvasEditor/index.js +0 -9
  220. package/src/views/CanvasEditor/index.ts +0 -16
  221. package/src/views/PostManager/EmptyState.d.ts +0 -10
  222. package/src/views/PostManager/EmptyState.d.ts.map +0 -1
  223. package/src/views/PostManager/EmptyState.js +0 -12
  224. package/src/views/PostManager/EmptyState.tsx +0 -42
  225. package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
  226. package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
  227. package/src/views/PostManager/PostActionsMenu.js +0 -58
  228. package/src/views/PostManager/PostActionsMenu.tsx +0 -112
  229. package/src/views/PostManager/PostCards.d.ts +0 -15
  230. package/src/views/PostManager/PostCards.d.ts.map +0 -1
  231. package/src/views/PostManager/PostCards.js +0 -79
  232. package/src/views/PostManager/PostCards.tsx +0 -197
  233. package/src/views/PostManager/PostFilters.d.ts +0 -16
  234. package/src/views/PostManager/PostFilters.d.ts.map +0 -1
  235. package/src/views/PostManager/PostFilters.js +0 -10
  236. package/src/views/PostManager/PostFilters.tsx +0 -95
  237. package/src/views/PostManager/PostManagerView.d.ts +0 -11
  238. package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
  239. package/src/views/PostManager/PostManagerView.js +0 -174
  240. package/src/views/PostManager/PostManagerView.tsx +0 -289
  241. package/src/views/PostManager/PostStats.d.ts +0 -11
  242. package/src/views/PostManager/PostStats.d.ts.map +0 -1
  243. package/src/views/PostManager/PostStats.js +0 -46
  244. package/src/views/PostManager/PostStats.tsx +0 -81
  245. package/src/views/PostManager/PostTable.d.ts +0 -15
  246. package/src/views/PostManager/PostTable.d.ts.map +0 -1
  247. package/src/views/PostManager/PostTable.js +0 -79
  248. package/src/views/PostManager/PostTable.tsx +0 -230
  249. package/src/views/PostManager/index.d.ts +0 -12
  250. package/src/views/PostManager/index.d.ts.map +0 -1
  251. package/src/views/PostManager/index.js +0 -11
  252. package/src/views/PostManager/index.ts +0 -15
  253. package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
  254. package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
  255. package/src/views/Preview/PreviewBridgeView.js +0 -11
  256. package/src/views/Preview/PreviewBridgeView.tsx +0 -64
  257. package/src/views/Preview/index.d.ts +0 -6
  258. package/src/views/Preview/index.d.ts.map +0 -1
  259. package/src/views/Preview/index.js +0 -4
  260. package/src/views/Preview/index.ts +0 -7
  261. package/src/views/Settings/SettingsView.d.ts +0 -10
  262. package/src/views/Settings/SettingsView.d.ts.map +0 -1
  263. package/src/views/Settings/SettingsView.js +0 -111
  264. package/src/views/Settings/SettingsView.tsx +0 -298
  265. package/src/views/Settings/index.d.ts +0 -6
  266. package/src/views/Settings/index.d.ts.map +0 -1
  267. package/src/views/Settings/index.js +0 -4
  268. package/src/views/Settings/index.ts +0 -7
  269. package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
  270. package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
  271. package/src/views/SlugSEO/SlugSEOManagerView.js +0 -11
  272. package/src/views/SlugSEO/SlugSEOManagerView.tsx +0 -94
  273. package/src/views/SlugSEO/index.d.ts +0 -6
  274. package/src/views/SlugSEO/index.d.ts.map +0 -1
  275. package/src/views/SlugSEO/index.js +0 -4
  276. package/src/views/SlugSEO/index.ts +0 -7
@@ -1,564 +0,0 @@
1
- /**
2
- * Rich Text Editor Component
3
- * Provides formatting toolbar (bold, italic, underline, links, colors)
4
- * Only shows options if client has provided styles for them
5
- */
6
- 'use client';
7
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
- import { useState, useRef, useEffect } from 'react';
9
- import { Bold, Italic, Underline, Link, Palette } from 'lucide-react';
10
- export function RichTextEditor({ value, onChange, placeholder = 'Enter text...', formatting, className = '', isFocused = false, onKeyDown: customOnKeyDown, }) {
11
- var _a;
12
- const editorRef = useRef(null);
13
- const [showToolbar, setShowToolbar] = useState(false);
14
- const [toolbarPosition, setToolbarPosition] = useState({ top: 0, left: 0 });
15
- const [showLinkDialog, setShowLinkDialog] = useState(false);
16
- const [linkUrl, setLinkUrl] = useState('');
17
- const [showColorPicker, setShowColorPicker] = useState(false);
18
- const [selectedText, setSelectedText] = useState('');
19
- const [currentColor, setCurrentColor] = useState(null);
20
- // Check which formatting options are available
21
- const hasBold = (formatting === null || formatting === void 0 ? void 0 : formatting.bold) !== false;
22
- const hasItalic = (formatting === null || formatting === void 0 ? void 0 : formatting.italic) !== false;
23
- const hasUnderline = (formatting === null || formatting === void 0 ? void 0 : formatting.underline) !== false;
24
- const hasLinks = (formatting === null || formatting === void 0 ? void 0 : formatting.links) !== false;
25
- const hasColors = (formatting === null || formatting === void 0 ? void 0 : formatting.colors) && formatting.colors.length > 0;
26
- // Initialize content when component mounts
27
- useEffect(() => {
28
- if (editorRef.current && !editorRef.current.innerHTML && value) {
29
- editorRef.current.innerHTML = value;
30
- }
31
- }, []); // Only run on mount
32
- // Update content when value prop changes (but avoid if user is editing)
33
- useEffect(() => {
34
- if (editorRef.current && document.activeElement !== editorRef.current) {
35
- if (editorRef.current.innerHTML !== value) {
36
- editorRef.current.innerHTML = value || '';
37
- }
38
- }
39
- }, [value]);
40
- // Handle focus prop
41
- useEffect(() => {
42
- if (isFocused && editorRef.current) {
43
- // Use requestAnimationFrame to ensure DOM is ready
44
- requestAnimationFrame(() => {
45
- if (editorRef.current) {
46
- editorRef.current.focus();
47
- // Place cursor at the end
48
- const range = document.createRange();
49
- range.selectNodeContents(editorRef.current);
50
- range.collapse(false);
51
- const selection = window.getSelection();
52
- if (selection) {
53
- selection.removeAllRanges();
54
- selection.addRange(range);
55
- }
56
- }
57
- });
58
- }
59
- }, [isFocused]);
60
- // Close color picker and link dialog when clicking outside, and hide toolbar when no selection
61
- useEffect(() => {
62
- const handleClickOutside = (e) => {
63
- const target = e.target;
64
- // Check if click is outside color picker
65
- if (showColorPicker) {
66
- const colorPicker = target.closest('[data-color-picker]');
67
- const colorPickerButton = target.closest('[data-color-picker-button]');
68
- if (!colorPicker && !colorPickerButton) {
69
- setShowColorPicker(false);
70
- }
71
- }
72
- // Check if click is outside link dialog
73
- if (showLinkDialog) {
74
- const linkDialog = target.closest('[data-link-dialog]');
75
- const linkButton = target.closest('[data-link-button]');
76
- if (!linkDialog && !linkButton) {
77
- setShowLinkDialog(false);
78
- }
79
- }
80
- // Check if click is outside editor and toolbar - hide toolbar if no selection
81
- if (editorRef.current && !editorRef.current.contains(target)) {
82
- const toolbar = target.closest('[class*="z-50"]');
83
- if (!toolbar) {
84
- // Check if there's actually a selection
85
- setTimeout(() => {
86
- const selection = window.getSelection();
87
- if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed) {
88
- setShowToolbar(false);
89
- setSelectedText('');
90
- }
91
- else {
92
- // Re-check selection to ensure it's still valid
93
- handleSelectionChange();
94
- }
95
- }, 0);
96
- }
97
- }
98
- else if (editorRef.current && editorRef.current.contains(target)) {
99
- // Clicked inside editor - check selection after a brief delay
100
- setTimeout(() => {
101
- handleSelectionChange();
102
- }, 10);
103
- }
104
- };
105
- document.addEventListener('mousedown', handleClickOutside);
106
- return () => {
107
- document.removeEventListener('mousedown', handleClickOutside);
108
- };
109
- }, [showColorPicker, showLinkDialog]);
110
- // Handle selection change to show/hide toolbar
111
- const handleSelectionChange = () => {
112
- var _a;
113
- // Don't hide toolbar if color picker or link dialog is open
114
- if (showColorPicker || showLinkDialog) {
115
- return;
116
- }
117
- const selection = window.getSelection();
118
- if (!selection || selection.rangeCount === 0) {
119
- setShowToolbar(false);
120
- setSelectedText('');
121
- return;
122
- }
123
- const range = selection.getRangeAt(0);
124
- // Check if selection is collapsed (no text selected)
125
- if (range.collapsed) {
126
- setShowToolbar(false);
127
- setSelectedText('');
128
- return;
129
- }
130
- // Check if the selection is within our editor
131
- if (!editorRef.current || !editorRef.current.contains(range.commonAncestorContainer)) {
132
- setShowToolbar(false);
133
- setSelectedText('');
134
- return;
135
- }
136
- // Get selected text and check if it's not empty
137
- const selectedText = selection.toString().trim();
138
- if (!selectedText) {
139
- setShowToolbar(false);
140
- setSelectedText('');
141
- return;
142
- }
143
- setSelectedText(selectedText);
144
- // Calculate toolbar position
145
- const rect = range.getBoundingClientRect();
146
- const editorRect = (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
147
- if (editorRect) {
148
- setToolbarPosition({
149
- top: rect.top - editorRect.top - 40,
150
- left: rect.left - editorRect.left + (rect.width / 2),
151
- });
152
- setShowToolbar(true);
153
- }
154
- };
155
- // Store selection range before button clicks
156
- const savedRangeRef = useRef(null);
157
- // Save selection before it's lost
158
- const saveSelection = () => {
159
- const selection = window.getSelection();
160
- if (selection && selection.rangeCount > 0) {
161
- savedRangeRef.current = selection.getRangeAt(0).cloneRange();
162
- }
163
- };
164
- // Restore saved selection
165
- const restoreSelection = () => {
166
- if (savedRangeRef.current && editorRef.current) {
167
- const selection = window.getSelection();
168
- if (selection) {
169
- selection.removeAllRanges();
170
- selection.addRange(savedRangeRef.current);
171
- }
172
- }
173
- };
174
- // Apply formatting command
175
- // Note: Client styles are applied in RichTextPreview component
176
- // The editor stores standard HTML tags, and preview applies client styles
177
- const applyFormat = (command, value) => {
178
- var _a;
179
- document.execCommand(command, false, value);
180
- (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.focus();
181
- updateContent();
182
- };
183
- // Update content from editor
184
- const updateContent = () => {
185
- if (editorRef.current) {
186
- onChange(editorRef.current.innerHTML);
187
- }
188
- };
189
- // Handle input
190
- const handleInput = () => {
191
- updateContent();
192
- };
193
- // Handle keyboard shortcuts
194
- const handleKeyDown = (e) => {
195
- var _a;
196
- // Call custom handler first (allows parent to intercept keys like Enter)
197
- if (customOnKeyDown) {
198
- customOnKeyDown(e);
199
- // If custom handler prevented default, don't process further
200
- if (e.defaultPrevented) {
201
- return;
202
- }
203
- }
204
- // Check for Ctrl/Cmd + key combinations
205
- const isModifierPressed = e.ctrlKey || e.metaKey;
206
- if (isModifierPressed) {
207
- switch (e.key.toLowerCase()) {
208
- case 'b':
209
- if (hasBold) {
210
- e.preventDefault();
211
- e.stopPropagation(); // Prevent event from bubbling to window-level listeners
212
- applyFormat('bold');
213
- }
214
- break;
215
- case 'i':
216
- if (hasItalic) {
217
- e.preventDefault();
218
- e.stopPropagation(); // Prevent event from bubbling to window-level listeners
219
- applyFormat('italic');
220
- }
221
- break;
222
- case 'u':
223
- if (hasUnderline) {
224
- e.preventDefault();
225
- e.stopPropagation(); // Prevent event from bubbling to window-level listeners
226
- applyFormat('underline');
227
- }
228
- break;
229
- case 'k':
230
- if (hasLinks) {
231
- e.preventDefault();
232
- e.stopPropagation(); // Prevent event from bubbling to window-level listeners
233
- // Get selected text and open link dialog
234
- const selection = window.getSelection();
235
- if (selection && selection.rangeCount > 0) {
236
- const range = selection.getRangeAt(0);
237
- if (!range.collapsed) {
238
- // Check if selection is already a link
239
- const linkElement = (_a = range.commonAncestorContainer.parentElement) === null || _a === void 0 ? void 0 : _a.closest('a');
240
- if (linkElement) {
241
- setLinkUrl(linkElement.href);
242
- }
243
- setShowLinkDialog(true);
244
- }
245
- }
246
- }
247
- break;
248
- }
249
- }
250
- };
251
- // Handle paste (clean HTML)
252
- const handlePaste = (e) => {
253
- e.preventDefault();
254
- const text = e.clipboardData.getData('text/plain');
255
- document.execCommand('insertText', false, text);
256
- updateContent();
257
- };
258
- // Handle link creation
259
- const handleCreateLink = () => {
260
- if (!linkUrl.trim())
261
- return;
262
- const selection = window.getSelection();
263
- if (selection && selection.rangeCount > 0) {
264
- const range = selection.getRangeAt(0);
265
- if (!range.collapsed) {
266
- applyFormat('createLink', linkUrl);
267
- setShowLinkDialog(false);
268
- setLinkUrl('');
269
- }
270
- }
271
- };
272
- // Check if selected text has a color applied
273
- const getSelectedColor = () => {
274
- var _a, _b, _c;
275
- // Use saved selection if available (when color picker is open)
276
- let range = null;
277
- if (savedRangeRef.current) {
278
- range = savedRangeRef.current;
279
- }
280
- else {
281
- const selection = window.getSelection();
282
- if (!selection || selection.rangeCount === 0)
283
- return null;
284
- range = selection.getRangeAt(0);
285
- }
286
- if (!range || range.collapsed)
287
- return null;
288
- // Check if the selection is within a span with a color class
289
- let node = range.commonAncestorContainer;
290
- // If the node is a text node, check its parent
291
- if (node.nodeType === Node.TEXT_NODE) {
292
- node = node.parentElement;
293
- }
294
- // Check if the node or any parent has a color class
295
- while (node && node !== editorRef.current) {
296
- if (node.nodeType === Node.ELEMENT_NODE) {
297
- const element = node;
298
- const className = element.className;
299
- // Check if this element has any of the configured color classes
300
- if ((_a = formatting === null || formatting === void 0 ? void 0 : formatting.styles) === null || _a === void 0 ? void 0 : _a.colorClasses) {
301
- for (const [colorKey, colorClass] of Object.entries(formatting.styles.colorClasses)) {
302
- if (typeof className === 'string' && className.includes(colorClass)) {
303
- return colorKey;
304
- }
305
- }
306
- }
307
- // Also check direct color classes
308
- if (formatting === null || formatting === void 0 ? void 0 : formatting.colors) {
309
- for (const color of formatting.colors) {
310
- const colorClass = ((_c = (_b = formatting === null || formatting === void 0 ? void 0 : formatting.styles) === null || _b === void 0 ? void 0 : _b.colorClasses) === null || _c === void 0 ? void 0 : _c[color]) || color;
311
- if (typeof className === 'string' && className.includes(colorClass)) {
312
- return color;
313
- }
314
- }
315
- }
316
- }
317
- node = node.parentElement;
318
- }
319
- return null;
320
- };
321
- // Remove color from selected text
322
- const handleRemoveColor = () => {
323
- var _a, _b, _c, _d;
324
- // Restore the saved selection first
325
- restoreSelection();
326
- const selection = window.getSelection();
327
- if (!selection || selection.rangeCount === 0)
328
- return;
329
- const range = selection.getRangeAt(0);
330
- if (range.collapsed)
331
- return;
332
- // Get all color classes to check for
333
- const colorClassesToRemove = [];
334
- if ((_a = formatting === null || formatting === void 0 ? void 0 : formatting.styles) === null || _a === void 0 ? void 0 : _a.colorClasses) {
335
- colorClassesToRemove.push(...Object.values(formatting.styles.colorClasses));
336
- }
337
- if (formatting === null || formatting === void 0 ? void 0 : formatting.colors) {
338
- for (const color of formatting.colors) {
339
- const colorClass = ((_c = (_b = formatting === null || formatting === void 0 ? void 0 : formatting.styles) === null || _b === void 0 ? void 0 : _b.colorClasses) === null || _c === void 0 ? void 0 : _c[color]) || color;
340
- if (!colorClassesToRemove.includes(colorClass)) {
341
- colorClassesToRemove.push(colorClass);
342
- }
343
- }
344
- }
345
- if (colorClassesToRemove.length === 0)
346
- return;
347
- // Get the editor element
348
- if (!editorRef.current)
349
- return;
350
- // Find all spans in the editor that intersect with the selection
351
- const allSpans = editorRef.current.querySelectorAll('span');
352
- const spansToProcess = [];
353
- allSpans.forEach(span => {
354
- // Check if span intersects with selection
355
- if (range.intersectsNode(span)) {
356
- const className = span.className || '';
357
- // Check if span has any color class
358
- for (const colorClass of colorClassesToRemove) {
359
- if (className.includes(colorClass)) {
360
- spansToProcess.push(span);
361
- break;
362
- }
363
- }
364
- }
365
- });
366
- // Process spans - remove color classes or unwrap
367
- spansToProcess.forEach(span => {
368
- const className = span.className || '';
369
- let newClassName = className;
370
- // Remove all color classes
371
- for (const colorClass of colorClassesToRemove) {
372
- const escapedClass = colorClass.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
373
- newClassName = newClassName
374
- .replace(new RegExp(`\\b${escapedClass}\\b`, 'g'), '')
375
- .replace(/\s+/g, ' ')
376
- .trim();
377
- }
378
- // Update or remove class
379
- if (!newClassName) {
380
- // Unwrap the span - move children to parent
381
- const parent = span.parentNode;
382
- if (parent) {
383
- const fragment = document.createDocumentFragment();
384
- while (span.firstChild) {
385
- fragment.appendChild(span.firstChild);
386
- }
387
- parent.replaceChild(fragment, span);
388
- }
389
- }
390
- else {
391
- span.className = newClassName;
392
- }
393
- });
394
- // Normalize to merge text nodes
395
- if (editorRef.current) {
396
- editorRef.current.normalize();
397
- }
398
- // Update content
399
- updateContent();
400
- setShowColorPicker(false);
401
- setCurrentColor(null);
402
- // Restore focus
403
- (_d = editorRef.current) === null || _d === void 0 ? void 0 : _d.focus();
404
- };
405
- // Apply color
406
- const handleApplyColor = (color) => {
407
- var _a, _b, _c;
408
- // Restore the saved selection first
409
- restoreSelection();
410
- const selection = window.getSelection();
411
- if (!selection || selection.rangeCount === 0) {
412
- return;
413
- }
414
- const range = selection.getRangeAt(0);
415
- if (!range.collapsed) {
416
- // Check if the selected text already has this color
417
- const currentColor = getSelectedColor();
418
- if (currentColor === color) {
419
- // If clicking the same color, remove it
420
- handleRemoveColor();
421
- return;
422
- }
423
- // Remove any existing color first
424
- if (currentColor) {
425
- handleRemoveColor();
426
- // Restore selection after removing color
427
- restoreSelection();
428
- }
429
- const colorClass = ((_b = (_a = formatting === null || formatting === void 0 ? void 0 : formatting.styles) === null || _a === void 0 ? void 0 : _a.colorClasses) === null || _b === void 0 ? void 0 : _b[color]) || color;
430
- // Create a span with the color class
431
- const span = document.createElement('span');
432
- span.className = colorClass;
433
- try {
434
- range.surroundContents(span);
435
- }
436
- catch (e) {
437
- // If surroundContents fails, try a different approach
438
- span.appendChild(range.extractContents());
439
- range.insertNode(span);
440
- }
441
- updateContent();
442
- setShowColorPicker(false);
443
- setCurrentColor(null);
444
- // Ensure the cursor stays active
445
- (_c = editorRef.current) === null || _c === void 0 ? void 0 : _c.focus();
446
- }
447
- };
448
- // Check if format is active
449
- const isFormatActive = (command) => {
450
- return document.queryCommandState(command);
451
- };
452
- return (_jsxs("div", { className: `relative ${className}`, children: [showToolbar && selectedText && selectedText.trim().length > 0 && (hasBold || hasItalic || hasUnderline || hasLinks || hasColors) && (_jsxs("div", { className: "absolute z-50 flex items-center gap-1 p-2 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg", style: {
453
- top: `${toolbarPosition.top}px`,
454
- left: `${toolbarPosition.left}px`,
455
- transform: 'translateX(-50%)',
456
- }, children: [hasBold && (_jsx("button", { type: "button", onMouseDown: (e) => e.preventDefault(), onClick: () => applyFormat('bold'), className: `p-1.5 rounded hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors ${isFormatActive('bold') ? 'bg-primary/10 text-primary' : 'text-neutral-600 dark:text-neutral-400'}`, title: "Bold", children: _jsx(Bold, { size: 14 }) })), hasItalic && (_jsx("button", { type: "button", onMouseDown: (e) => e.preventDefault(), onClick: () => applyFormat('italic'), className: `p-1.5 rounded hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors ${isFormatActive('italic') ? 'bg-primary/10 text-primary' : 'text-neutral-600 dark:text-neutral-400'}`, title: "Italic", children: _jsx(Italic, { size: 14 }) })), hasUnderline && (_jsx("button", { type: "button", onMouseDown: (e) => e.preventDefault(), onClick: () => applyFormat('underline'), className: `p-1.5 rounded hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors ${isFormatActive('underline') ? 'bg-primary/10 text-primary' : 'text-neutral-600 dark:text-neutral-400'}`, title: "Underline", children: _jsx(Underline, { size: 14 }) })), hasLinks && (_jsxs("div", { className: "relative", children: [_jsx("button", { type: "button", "data-link-button": true, onMouseDown: (e) => e.preventDefault(), onClick: (e) => {
457
- var _a;
458
- e.stopPropagation();
459
- const selection = window.getSelection();
460
- if (selection && selection.rangeCount > 0) {
461
- const range = selection.getRangeAt(0);
462
- const linkElement = (_a = range.commonAncestorContainer.parentElement) === null || _a === void 0 ? void 0 : _a.closest('a');
463
- if (linkElement) {
464
- setLinkUrl(linkElement.href);
465
- }
466
- }
467
- setShowLinkDialog(!showLinkDialog);
468
- }, className: `p-1.5 rounded hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors ${isFormatActive('unlink') ? 'bg-primary/10 text-primary' : 'text-neutral-600 dark:text-neutral-400'}`, title: "Link", children: _jsx(Link, { size: 14 }) }), showLinkDialog && (_jsxs("div", { "data-link-dialog": true, className: "absolute top-full left-0 mt-2 p-3 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-lg min-w-[300px] z-[60]", onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: [_jsx("input", { type: "url", value: linkUrl, onChange: (e) => setLinkUrl(e.target.value), placeholder: "Enter URL...", className: "w-full px-3 py-2 text-sm border border-neutral-300 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-100 outline-none focus:border-primary", onKeyDown: (e) => {
469
- if (e.key === 'Enter') {
470
- handleCreateLink();
471
- }
472
- else if (e.key === 'Escape') {
473
- setShowLinkDialog(false);
474
- }
475
- }, autoFocus: true }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx("button", { type: "button", onClick: handleCreateLink, className: "flex-1 px-3 py-1.5 text-xs font-bold bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors", children: "Apply" }), _jsx("button", { type: "button", onClick: () => {
476
- applyFormat('unlink');
477
- setShowLinkDialog(false);
478
- }, className: "px-3 py-1.5 text-xs font-bold border border-neutral-300 dark:border-neutral-700 rounded-lg hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors", children: "Remove" })] })] }))] })), hasColors && (_jsxs("div", { className: "relative", children: [_jsx("button", { type: "button", "data-color-picker-button": true, onMouseDown: (e) => {
479
- e.preventDefault();
480
- saveSelection(); // Save selection before opening color picker
481
- // Also check current color when saving selection
482
- setTimeout(() => {
483
- const color = getSelectedColor();
484
- setCurrentColor(color);
485
- }, 0);
486
- }, onClick: (e) => {
487
- e.stopPropagation();
488
- // Check current color before opening picker
489
- const color = getSelectedColor();
490
- setCurrentColor(color);
491
- setShowColorPicker(!showColorPicker);
492
- }, className: "p-1.5 rounded hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors text-neutral-600 dark:text-neutral-400", title: "Text Color", children: _jsx(Palette, { size: 14 }) }), showColorPicker && (_jsx("div", { "data-color-picker": true, className: "absolute top-full left-1/2 -translate-x-1/2 mt-2 p-2.5 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg shadow-xl z-[60]", style: {
493
- minWidth: 'fit-content',
494
- maxWidth: 'none',
495
- isolation: 'isolate'
496
- }, onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [currentColor && (_jsx("button", { type: "button", onMouseDown: (e) => {
497
- e.preventDefault();
498
- e.stopPropagation();
499
- }, onClick: (e) => {
500
- e.preventDefault();
501
- e.stopPropagation();
502
- handleRemoveColor();
503
- setCurrentColor(null);
504
- }, className: "flex-shrink-0 px-2.5 py-1.5 text-xs font-medium border border-neutral-300 dark:border-neutral-600 rounded-md hover:bg-neutral-100 dark:hover:bg-neutral-700 transition-colors text-neutral-700 dark:text-neutral-300", title: "Remove Color", children: "Remove" })), (_a = formatting === null || formatting === void 0 ? void 0 : formatting.colors) === null || _a === void 0 ? void 0 : _a.map((color) => {
505
- // Map text color classes to background colors for display
506
- const getBackgroundColor = (colorKey) => {
507
- // Remove 'text-' prefix if present
508
- const baseColor = colorKey.replace(/^text-/, '');
509
- // Map to actual color values
510
- const colorMap = {
511
- 'forest': '#6B7C5A',
512
- 'sage': '#9CAF88',
513
- 'primary': '#94b17b',
514
- 'soft-green': '#A8C09A',
515
- };
516
- return colorMap[baseColor] || '#6B7C5A'; // Default to forest
517
- };
518
- const bgColor = getBackgroundColor(color);
519
- const colorName = color.replace(/^text-/, '');
520
- return (_jsx("button", { type: "button", onMouseDown: (e) => {
521
- e.preventDefault();
522
- e.stopPropagation();
523
- }, onClick: (e) => {
524
- e.preventDefault();
525
- e.stopPropagation();
526
- handleApplyColor(color);
527
- }, className: "relative group flex-shrink-0 w-8 h-8 rounded-md border border-neutral-300 dark:border-neutral-600 hover:border-neutral-500 dark:hover:border-neutral-400 transition-all duration-150 hover:scale-110 active:scale-95", style: {
528
- backgroundColor: bgColor,
529
- boxShadow: 'inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 2px rgba(0, 0, 0, 0.1)'
530
- }, title: colorName.charAt(0).toUpperCase() + colorName.slice(1), children: _jsx("span", { className: "sr-only", children: colorName }) }, color));
531
- })] }) }))] }))] })), _jsx("div", { ref: editorRef, contentEditable: true, onInput: handleInput, onPaste: handlePaste, onKeyDown: handleKeyDown, onSelect: () => {
532
- // Small delay to ensure selection is updated
533
- setTimeout(() => {
534
- handleSelectionChange();
535
- }, 0);
536
- }, onMouseUp: (e) => {
537
- // Don't trigger selection change if clicking on toolbar/dropdowns
538
- const target = e.target;
539
- if (!target.closest('[class*="z-50"], [class*="z-[60]"]')) {
540
- // Small delay to ensure selection is updated
541
- setTimeout(() => {
542
- handleSelectionChange();
543
- }, 0);
544
- }
545
- }, onKeyUp: () => {
546
- // Small delay to ensure selection is updated
547
- setTimeout(() => {
548
- handleSelectionChange();
549
- }, 0);
550
- }, onBlur: () => {
551
- // Hide toolbar when editor loses focus (unless dropdowns are open)
552
- if (!showColorPicker && !showLinkDialog) {
553
- setTimeout(() => {
554
- const selection = window.getSelection();
555
- if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed) {
556
- setShowToolbar(false);
557
- setSelectedText('');
558
- }
559
- }, 100); // Small delay to allow dropdown clicks
560
- }
561
- }, "data-placeholder": placeholder, className: `outline-none min-h-[24px] ${!value || value === '<br>' || value === '<div><br></div>'
562
- ? 'before:content-[attr(data-placeholder)] before:text-neutral-400 dark:before:text-neutral-500 before:pointer-events-none'
563
- : ''}`, style: { userSelect: 'text' } })] }));
564
- }