@jhits/plugin-blog 0.0.9 → 0.0.11

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 +58 -59
  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,339 +0,0 @@
1
- /**
2
- * Hook for managing unsaved changes warnings and auto-save
3
- */
4
-
5
- 'use client';
6
-
7
- import { useEffect, useRef, useCallback, useState } from 'react';
8
- import type { EditorState } from '../../../state/types';
9
- import type { Block } from '../../../types/block';
10
-
11
- interface UseUnsavedChangesOptions {
12
- state: EditorState;
13
- isDirty: boolean;
14
- onSave: (heroBlock?: Block | null) => Promise<void>;
15
- heroBlock?: Block | null;
16
- autoSaveEnabled?: boolean;
17
- autoSaveDelay?: number; // in milliseconds
18
- postId?: string | null; // Post ID to detect when a post is loaded
19
- }
20
-
21
- const AUTO_SAVE_STORAGE_KEY = 'blog-editor-autosave-enabled';
22
- const DEFAULT_AUTO_SAVE_DELAY = 10000; // 10 seconds
23
-
24
- /**
25
- * Hook to manage unsaved changes warnings and auto-save
26
- */
27
- export function useUnsavedChanges({
28
- state,
29
- isDirty,
30
- onSave,
31
- heroBlock,
32
- autoSaveEnabled: propAutoSaveEnabled,
33
- autoSaveDelay = DEFAULT_AUTO_SAVE_DELAY,
34
- postId,
35
- }: UseUnsavedChangesOptions) {
36
- const autoSaveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
37
- const isSavingRef = useRef(false);
38
- const lastSavedStateRef = useRef<string>('');
39
- const countdownIntervalRef = useRef<NodeJS.Timeout | null>(null);
40
- const [countdown, setCountdown] = useState<number | null>(null);
41
- const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
42
- const saveStatusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
43
- const previousIsDirtyRef = useRef<boolean>(false);
44
- const countdownStartTimeRef = useRef<number | null>(null);
45
- const performAutoSaveRef = useRef<(() => Promise<void>) | null>(null);
46
-
47
- // Get auto-save preference from localStorage
48
- const getAutoSavePreference = useCallback((): boolean => {
49
- if (typeof window === 'undefined') return false;
50
- try {
51
- const stored = localStorage.getItem(AUTO_SAVE_STORAGE_KEY);
52
- if (stored !== null) {
53
- return stored === 'true';
54
- }
55
- } catch (error) {
56
- console.error('[useUnsavedChanges] Failed to read auto-save preference:', error);
57
- }
58
- // Default to false if not set
59
- return false;
60
- }, []);
61
-
62
- // State to track auto-save enabled status (reactive)
63
- const [autoSaveEnabledState, setAutoSaveEnabledState] = useState<boolean>(() => {
64
- // Initialize from prop or localStorage
65
- if (propAutoSaveEnabled !== undefined) {
66
- return propAutoSaveEnabled;
67
- }
68
- return getAutoSavePreference();
69
- });
70
-
71
- // Sync with prop changes
72
- useEffect(() => {
73
- if (propAutoSaveEnabled !== undefined) {
74
- setAutoSaveEnabledState(propAutoSaveEnabled);
75
- }
76
- }, [propAutoSaveEnabled]);
77
-
78
- // Initialize from localStorage on mount (if no prop provided)
79
- useEffect(() => {
80
- if (propAutoSaveEnabled === undefined && typeof window !== 'undefined') {
81
- const stored = getAutoSavePreference();
82
- setAutoSaveEnabledState(stored);
83
- }
84
- }, []); // Only run on mount
85
-
86
- // Set auto-save preference in localStorage and state
87
- const setAutoSavePreference = useCallback((enabled: boolean) => {
88
- if (typeof window === 'undefined') return;
89
- try {
90
- localStorage.setItem(AUTO_SAVE_STORAGE_KEY, enabled.toString());
91
- setAutoSaveEnabledState(enabled);
92
- console.log('[useUnsavedChanges] Auto-save preference updated:', enabled);
93
- } catch (error) {
94
- console.error('[useUnsavedChanges] Failed to save auto-save preference:', error);
95
- }
96
- }, []);
97
-
98
- // Determine if auto-save is enabled (prop takes precedence over state)
99
- const autoSaveEnabled = propAutoSaveEnabled !== undefined
100
- ? propAutoSaveEnabled
101
- : autoSaveEnabledState;
102
-
103
- // Create a stable reference of the current state for comparison
104
- const getStateSnapshot = useCallback(() => {
105
- return JSON.stringify({
106
- title: state.title,
107
- slug: state.slug,
108
- blocks: state.blocks,
109
- seo: state.seo,
110
- metadata: state.metadata,
111
- status: state.status,
112
- });
113
- }, [state]);
114
-
115
- // Initialize lastSavedStateRef when a post is loaded and marked as clean
116
- useEffect(() => {
117
- // When a post is loaded (postId exists) and isDirty is false, update the saved state reference
118
- if (postId && !isDirty && lastSavedStateRef.current === '') {
119
- lastSavedStateRef.current = getStateSnapshot();
120
- console.log('[useUnsavedChanges] Initialized saved state reference after post load');
121
- }
122
- // Also update if isDirty becomes false after being true (e.g., after save or MARK_CLEAN)
123
- if (!isDirty && lastSavedStateRef.current !== getStateSnapshot()) {
124
- lastSavedStateRef.current = getStateSnapshot();
125
- }
126
- }, [postId, isDirty, getStateSnapshot]);
127
-
128
- // Auto-save function
129
- const performAutoSave = useCallback(async () => {
130
- if (isSavingRef.current || !isDirty) {
131
- return;
132
- }
133
-
134
- try {
135
- isSavingRef.current = true;
136
- setSaveStatus('saving');
137
- setCountdown(null);
138
- countdownStartTimeRef.current = null;
139
- console.log('[useUnsavedChanges] Auto-saving...');
140
- await onSave(heroBlock);
141
- lastSavedStateRef.current = getStateSnapshot();
142
- setSaveStatus('saved');
143
- console.log('[useUnsavedChanges] Auto-save completed');
144
-
145
- // Clear save status after 2 seconds
146
- if (saveStatusTimeoutRef.current) {
147
- clearTimeout(saveStatusTimeoutRef.current);
148
- }
149
- saveStatusTimeoutRef.current = setTimeout(() => {
150
- setSaveStatus('idle');
151
- }, 2000);
152
- } catch (error) {
153
- console.error('[useUnsavedChanges] Auto-save failed:', error);
154
- setSaveStatus('error');
155
- // Clear error status after 3 seconds
156
- if (saveStatusTimeoutRef.current) {
157
- clearTimeout(saveStatusTimeoutRef.current);
158
- }
159
- saveStatusTimeoutRef.current = setTimeout(() => {
160
- setSaveStatus('idle');
161
- }, 3000);
162
- } finally {
163
- isSavingRef.current = false;
164
- }
165
- }, [isDirty, onSave, heroBlock, getStateSnapshot]);
166
-
167
- // Keep ref updated with latest function
168
- useEffect(() => {
169
- performAutoSaveRef.current = performAutoSave;
170
- }, [performAutoSave]);
171
-
172
- // Set up auto-save timer and countdown when state changes
173
- useEffect(() => {
174
- // Only reset countdown if isDirty changed from false to true, or if auto-save was just disabled
175
- const isDirtyChanged = previousIsDirtyRef.current !== isDirty;
176
- const becameDirty = !previousIsDirtyRef.current && isDirty;
177
-
178
- // Update previous isDirty ref
179
- previousIsDirtyRef.current = isDirty;
180
-
181
- // Clear existing countdown interval
182
- if (countdownIntervalRef.current) {
183
- clearInterval(countdownIntervalRef.current);
184
- countdownIntervalRef.current = null;
185
- }
186
-
187
- if (!autoSaveEnabled || !isDirty || isSavingRef.current) {
188
- setCountdown(null);
189
- countdownStartTimeRef.current = null;
190
- // Clear timeout if auto-save is disabled or not dirty
191
- if (autoSaveTimeoutRef.current) {
192
- clearTimeout(autoSaveTimeoutRef.current);
193
- autoSaveTimeoutRef.current = null;
194
- }
195
- return;
196
- }
197
-
198
- // Only reset countdown if:
199
- // 1. isDirty just became true (new changes)
200
- // 2. Or if there's no active countdown
201
- const shouldResetCountdown = becameDirty || countdownStartTimeRef.current === null;
202
-
203
- if (shouldResetCountdown) {
204
- // Clear existing timeout
205
- if (autoSaveTimeoutRef.current) {
206
- clearTimeout(autoSaveTimeoutRef.current);
207
- }
208
-
209
- // Initialize countdown
210
- const secondsRemaining = Math.ceil(autoSaveDelay / 1000);
211
- setCountdown(secondsRemaining);
212
- countdownStartTimeRef.current = Date.now();
213
-
214
- // Set new timeout for auto-save
215
- autoSaveTimeoutRef.current = setTimeout(() => {
216
- if (performAutoSaveRef.current) {
217
- performAutoSaveRef.current();
218
- }
219
- }, autoSaveDelay);
220
- } else {
221
- // Countdown is already running, just update it based on elapsed time
222
- if (countdownStartTimeRef.current && autoSaveTimeoutRef.current) {
223
- const elapsed = Date.now() - countdownStartTimeRef.current;
224
- const remaining = Math.max(0, autoSaveDelay - elapsed);
225
- const secondsRemaining = Math.ceil(remaining / 1000);
226
- setCountdown(secondsRemaining);
227
- }
228
- }
229
-
230
- // Update countdown every second (only if we have an active countdown)
231
- if (shouldResetCountdown || countdownIntervalRef.current === null) {
232
- countdownIntervalRef.current = setInterval(() => {
233
- if (countdownStartTimeRef.current && autoSaveTimeoutRef.current) {
234
- const elapsed = Date.now() - countdownStartTimeRef.current;
235
- const remaining = Math.max(0, autoSaveDelay - elapsed);
236
- const secondsRemaining = Math.ceil(remaining / 1000);
237
-
238
- if (secondsRemaining <= 0) {
239
- setCountdown(null);
240
- if (countdownIntervalRef.current) {
241
- clearInterval(countdownIntervalRef.current);
242
- countdownIntervalRef.current = null;
243
- }
244
- } else {
245
- setCountdown(secondsRemaining);
246
- }
247
- }
248
- }, 1000);
249
- }
250
-
251
- return () => {
252
- if (autoSaveTimeoutRef.current) {
253
- clearTimeout(autoSaveTimeoutRef.current);
254
- }
255
- if (countdownIntervalRef.current) {
256
- clearInterval(countdownIntervalRef.current);
257
- }
258
- };
259
- }, [autoSaveEnabled, isDirty, autoSaveDelay]);
260
-
261
- // Handle browser beforeunload event (page refresh/close)
262
- useEffect(() => {
263
- if (!isDirty) {
264
- return;
265
- }
266
-
267
- const handleBeforeUnload = (e: BeforeUnloadEvent) => {
268
- e.preventDefault();
269
- // Modern browsers ignore custom messages, but we still need to set returnValue
270
- e.returnValue = '';
271
- return '';
272
- };
273
-
274
- window.addEventListener('beforeunload', handleBeforeUnload);
275
-
276
- return () => {
277
- window.removeEventListener('beforeunload', handleBeforeUnload);
278
- };
279
- }, [isDirty]);
280
-
281
- // Handle link clicks (for Next.js App Router)
282
- useEffect(() => {
283
- if (!isDirty) {
284
- return;
285
- }
286
-
287
- const handleLinkClick = (e: MouseEvent) => {
288
- const target = e.target as HTMLElement;
289
- const link = target.closest('a');
290
-
291
- if (link && link.href) {
292
- const url = new URL(link.href);
293
- const currentPath = window.location.pathname;
294
-
295
- // Check if navigating away from editor
296
- if (!url.pathname.includes('/blog/editor') && !url.pathname.includes('/blog/new')) {
297
- const confirmed = window.confirm(
298
- 'You have unsaved changes. Are you sure you want to leave? Your changes will be lost.'
299
- );
300
-
301
- if (!confirmed) {
302
- e.preventDefault();
303
- e.stopPropagation();
304
- return false;
305
- }
306
- }
307
- }
308
- };
309
-
310
- // Listen for clicks on links
311
- document.addEventListener('click', handleLinkClick, true);
312
-
313
- return () => {
314
- document.removeEventListener('click', handleLinkClick, true);
315
- };
316
- }, [isDirty]);
317
-
318
- // Cleanup on unmount
319
- useEffect(() => {
320
- return () => {
321
- if (autoSaveTimeoutRef.current) {
322
- clearTimeout(autoSaveTimeoutRef.current);
323
- }
324
- if (countdownIntervalRef.current) {
325
- clearInterval(countdownIntervalRef.current);
326
- }
327
- if (saveStatusTimeoutRef.current) {
328
- clearTimeout(saveStatusTimeoutRef.current);
329
- }
330
- };
331
- }, []);
332
-
333
- return {
334
- autoSaveEnabled,
335
- setAutoSaveEnabled: setAutoSavePreference,
336
- countdown,
337
- saveStatus,
338
- };
339
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Canvas Editor View Exports
3
- */
4
- export { CanvasEditorView } from './CanvasEditorView';
5
- export type { CanvasEditorViewProps } from './CanvasEditorView';
6
- export { EditorBody } from './EditorBody';
7
- export { LayoutContainer } from './LayoutContainer';
8
- export type { LayoutContainerProps } from './LayoutContainer';
9
- export type { EditorBodyProps } from './EditorBody';
10
- export { BlockWrapper } from './BlockWrapper';
11
- export type { BlockWrapperProps } from './BlockWrapper';
12
- export { EditorHeader } from './EditorHeader';
13
- export type { EditorHeaderProps } from './EditorHeader';
14
- export { SaveConfirmationModal } from './SaveConfirmationModal';
15
- export type { SaveConfirmationModalProps } from './SaveConfirmationModal';
16
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC"}
@@ -1,9 +0,0 @@
1
- /**
2
- * Canvas Editor View Exports
3
- */
4
- export { CanvasEditorView } from './CanvasEditorView';
5
- export { EditorBody } from './EditorBody';
6
- export { LayoutContainer } from './LayoutContainer';
7
- export { BlockWrapper } from './BlockWrapper';
8
- export { EditorHeader } from './EditorHeader';
9
- export { SaveConfirmationModal } from './SaveConfirmationModal';
@@ -1,16 +0,0 @@
1
- /**
2
- * Canvas Editor View Exports
3
- */
4
-
5
- export { CanvasEditorView } from './CanvasEditorView';
6
- export type { CanvasEditorViewProps } from './CanvasEditorView';
7
- export { EditorBody } from './EditorBody';
8
- export { LayoutContainer } from './LayoutContainer';
9
- export type { LayoutContainerProps } from './LayoutContainer';
10
- export type { EditorBodyProps } from './EditorBody';
11
- export { BlockWrapper } from './BlockWrapper';
12
- export type { BlockWrapperProps } from './BlockWrapper';
13
- export { EditorHeader } from './EditorHeader';
14
- export type { EditorHeaderProps } from './EditorHeader';
15
- export { SaveConfirmationModal } from './SaveConfirmationModal';
16
- export type { SaveConfirmationModalProps } from './SaveConfirmationModal';
@@ -1,10 +0,0 @@
1
- /**
2
- * Empty State Component
3
- * Botanical-themed empty state for when no posts are found
4
- */
5
- export interface EmptyStateProps {
6
- hasFilters: boolean;
7
- onCreatePost: () => void;
8
- }
9
- export declare function EmptyState({ hasFilters, onCreatePost }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
10
- //# sourceMappingURL=EmptyState.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["EmptyState.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,eAAe,2CAyBvE"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Empty State Component
3
- * Botanical-themed empty state for when no posts are found
4
- */
5
- 'use client';
6
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
- import { Sprout, Plus } from 'lucide-react';
8
- export function EmptyState({ hasFilters, onCreatePost }) {
9
- return (_jsxs("div", { className: "flex flex-col items-center justify-center py-20 px-8 bg-neutral-100 dark:bg-neutral-800/50 rounded-[2.5rem] border-2 border-dashed border-neutral-300 dark:border-neutral-700", children: [_jsx("div", { className: "w-24 h-24 rounded-full bg-green-500/10 dark:bg-green-500/20 flex items-center justify-center mb-6", children: _jsx(Sprout, { className: "text-green-600 dark:text-green-400 size-12" }) }), _jsx("h3", { className: "text-xl font-black text-neutral-950 dark:text-white uppercase tracking-tight mb-2", children: hasFilters ? 'No Posts Found' : 'No Posts Yet' }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400 text-center mb-6 max-w-md", children: hasFilters
10
- ? 'Try adjusting your search or filter criteria to find what you\'re looking for.'
11
- : 'Start growing your content garden. Create your first blog post to share your botanical knowledge with the world.' }), !hasFilters && (_jsxs("button", { onClick: onCreatePost, className: "inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20", children: [_jsx(Plus, { size: 16 }), "Create Your First Post"] }))] }));
12
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * Empty State Component
3
- * Botanical-themed empty state for when no posts are found
4
- */
5
-
6
- 'use client';
7
-
8
- import React from 'react';
9
- import { Sprout, Plus } from 'lucide-react';
10
-
11
- export interface EmptyStateProps {
12
- hasFilters: boolean;
13
- onCreatePost: () => void;
14
- }
15
-
16
- export function EmptyState({ hasFilters, onCreatePost }: EmptyStateProps) {
17
- return (
18
- <div className="flex flex-col items-center justify-center py-20 px-8 bg-neutral-100 dark:bg-neutral-800/50 rounded-[2.5rem] border-2 border-dashed border-neutral-300 dark:border-neutral-700">
19
- <div className="w-24 h-24 rounded-full bg-green-500/10 dark:bg-green-500/20 flex items-center justify-center mb-6">
20
- <Sprout className="text-green-600 dark:text-green-400 size-12" />
21
- </div>
22
- <h3 className="text-xl font-black text-neutral-950 dark:text-white uppercase tracking-tight mb-2">
23
- {hasFilters ? 'No Posts Found' : 'No Posts Yet'}
24
- </h3>
25
- <p className="text-sm text-neutral-500 dark:text-neutral-400 text-center mb-6 max-w-md">
26
- {hasFilters
27
- ? 'Try adjusting your search or filter criteria to find what you\'re looking for.'
28
- : 'Start growing your content garden. Create your first blog post to share your botanical knowledge with the world.'}
29
- </p>
30
- {!hasFilters && (
31
- <button
32
- onClick={onCreatePost}
33
- className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20"
34
- >
35
- <Plus size={16} />
36
- Create Your First Post
37
- </button>
38
- )}
39
- </div>
40
- );
41
- }
42
-
@@ -1,12 +0,0 @@
1
- /**
2
- * Post Actions Menu Component
3
- * Three-dot menu with actions for each post
4
- */
5
- export interface PostActionsMenuProps {
6
- onEdit: () => void;
7
- onPreview: () => void;
8
- onDuplicate: () => void;
9
- onDelete: () => void;
10
- }
11
- export declare function PostActionsMenu({ onEdit, onPreview, onDuplicate, onDelete, }: PostActionsMenuProps): import("react/jsx-runtime").JSX.Element;
12
- //# sourceMappingURL=PostActionsMenu.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PostActionsMenu.d.ts","sourceRoot":"","sources":["PostActionsMenu.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,SAAS,EACT,WAAW,EACX,QAAQ,GACX,EAAE,oBAAoB,2CAuFtB"}
@@ -1,58 +0,0 @@
1
- /**
2
- * Post Actions Menu Component
3
- * Three-dot menu with actions for each post
4
- */
5
- 'use client';
6
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
7
- import { useState, useRef, useEffect } from 'react';
8
- import { createPortal } from 'react-dom';
9
- import { MoreVertical, Edit, Copy, Trash2 } from 'lucide-react';
10
- export function PostActionsMenu({ onEdit, onPreview, onDuplicate, onDelete, }) {
11
- const [isOpen, setIsOpen] = useState(false);
12
- const [menuPosition, setMenuPosition] = useState({ top: 0, right: 0 });
13
- const buttonRef = useRef(null);
14
- const menuRef = useRef(null);
15
- // Calculate menu position when opening
16
- useEffect(() => {
17
- if (isOpen && buttonRef.current) {
18
- const buttonRect = buttonRef.current.getBoundingClientRect();
19
- setMenuPosition({
20
- top: buttonRect.bottom + 8, // 8px = mt-2 equivalent
21
- right: window.innerWidth - buttonRect.right,
22
- });
23
- }
24
- }, [isOpen]);
25
- useEffect(() => {
26
- function handleClickOutside(event) {
27
- if (menuRef.current &&
28
- !menuRef.current.contains(event.target) &&
29
- buttonRef.current &&
30
- !buttonRef.current.contains(event.target)) {
31
- setIsOpen(false);
32
- }
33
- }
34
- if (isOpen) {
35
- document.addEventListener('mousedown', handleClickOutside);
36
- }
37
- return () => {
38
- document.removeEventListener('mousedown', handleClickOutside);
39
- };
40
- }, [isOpen]);
41
- const actions = [
42
- { label: 'Edit', icon: Edit, onClick: onEdit, color: 'text-neutral-600 dark:text-neutral-400' },
43
- // { label: 'Preview', icon: Eye, onClick: onPreview, color: 'text-blue-600 dark:text-blue-400' },
44
- { label: 'Duplicate', icon: Copy, onClick: onDuplicate, color: 'text-neutral-600 dark:text-neutral-400' },
45
- { label: 'Delete', icon: Trash2, onClick: onDelete, color: 'text-red-600 dark:text-red-400' },
46
- ];
47
- const menuContent = isOpen && (_jsx("div", { ref: menuRef, className: "fixed w-48 bg-dashboard-card border border-dashboard-border rounded-2xl shadow-xl z-9999 overflow-hidden", style: {
48
- top: `${menuPosition.top}px`,
49
- right: `${menuPosition.right}px`,
50
- }, children: actions.map((action) => {
51
- const Icon = action.icon;
52
- return (_jsxs("button", { onClick: () => {
53
- action.onClick();
54
- setIsOpen(false);
55
- }, className: `w-full flex items-center font-sans gap-3 px-4 py-3 text-sm hover:bg-dashboard-bg transition-colors ${action.color}`, children: [_jsx(Icon, { size: 16 }), _jsx("span", { children: action.label })] }, action.label));
56
- }) }));
57
- return (_jsxs(_Fragment, { children: [_jsx("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "p-2 text-neutral-400 hover:text-dashboard-text hover:bg-dashboard-bg rounded-full transition-colors", title: "Actions", children: _jsx(MoreVertical, { size: 18 }) }), typeof window !== 'undefined' && isOpen && createPortal(menuContent, document.body)] }));
58
- }
@@ -1,112 +0,0 @@
1
- /**
2
- * Post Actions Menu Component
3
- * Three-dot menu with actions for each post
4
- */
5
-
6
- 'use client';
7
-
8
- import React, { useState, useRef, useEffect } from 'react';
9
- import { createPortal } from 'react-dom';
10
- import { MoreVertical, Edit, Eye, Copy, Trash2 } from 'lucide-react';
11
-
12
- export interface PostActionsMenuProps {
13
- onEdit: () => void;
14
- onPreview: () => void;
15
- onDuplicate: () => void;
16
- onDelete: () => void;
17
- }
18
-
19
- export function PostActionsMenu({
20
- onEdit,
21
- onPreview,
22
- onDuplicate,
23
- onDelete,
24
- }: PostActionsMenuProps) {
25
- const [isOpen, setIsOpen] = useState(false);
26
- const [menuPosition, setMenuPosition] = useState({ top: 0, right: 0 });
27
- const buttonRef = useRef<HTMLButtonElement>(null);
28
- const menuRef = useRef<HTMLDivElement>(null);
29
-
30
- // Calculate menu position when opening
31
- useEffect(() => {
32
- if (isOpen && buttonRef.current) {
33
- const buttonRect = buttonRef.current.getBoundingClientRect();
34
- setMenuPosition({
35
- top: buttonRect.bottom + 8, // 8px = mt-2 equivalent
36
- right: window.innerWidth - buttonRect.right,
37
- });
38
- }
39
- }, [isOpen]);
40
-
41
- useEffect(() => {
42
- function handleClickOutside(event: MouseEvent) {
43
- if (
44
- menuRef.current &&
45
- !menuRef.current.contains(event.target as Node) &&
46
- buttonRef.current &&
47
- !buttonRef.current.contains(event.target as Node)
48
- ) {
49
- setIsOpen(false);
50
- }
51
- }
52
-
53
- if (isOpen) {
54
- document.addEventListener('mousedown', handleClickOutside);
55
- }
56
-
57
- return () => {
58
- document.removeEventListener('mousedown', handleClickOutside);
59
- };
60
- }, [isOpen]);
61
-
62
- const actions = [
63
- { label: 'Edit', icon: Edit, onClick: onEdit, color: 'text-neutral-600 dark:text-neutral-400' },
64
- // { label: 'Preview', icon: Eye, onClick: onPreview, color: 'text-blue-600 dark:text-blue-400' },
65
- { label: 'Duplicate', icon: Copy, onClick: onDuplicate, color: 'text-neutral-600 dark:text-neutral-400' },
66
- { label: 'Delete', icon: Trash2, onClick: onDelete, color: 'text-red-600 dark:text-red-400' },
67
- ];
68
-
69
- const menuContent = isOpen && (
70
- <div
71
- ref={menuRef}
72
- className="fixed w-48 bg-dashboard-card border border-dashboard-border rounded-2xl shadow-xl z-9999 overflow-hidden"
73
- style={{
74
- top: `${menuPosition.top}px`,
75
- right: `${menuPosition.right}px`,
76
- }}
77
- >
78
- {actions.map((action) => {
79
- const Icon = action.icon;
80
- return (
81
- <button
82
- key={action.label}
83
- onClick={() => {
84
- action.onClick();
85
- setIsOpen(false);
86
- }}
87
- className={`w-full flex items-center font-sans gap-3 px-4 py-3 text-sm hover:bg-dashboard-bg transition-colors ${action.color}`}
88
- >
89
- <Icon size={16} />
90
- <span>{action.label}</span>
91
- </button>
92
- );
93
- })}
94
- </div>
95
- );
96
-
97
- return (
98
- <>
99
- <button
100
- ref={buttonRef}
101
- onClick={() => setIsOpen(!isOpen)}
102
- className="p-2 text-neutral-400 hover:text-dashboard-text hover:bg-dashboard-bg rounded-full transition-colors"
103
- title="Actions"
104
- >
105
- <MoreVertical size={18} />
106
- </button>
107
-
108
- {typeof window !== 'undefined' && isOpen && createPortal(menuContent, document.body)}
109
- </>
110
- );
111
- }
112
-
@@ -1,15 +0,0 @@
1
- /**
2
- * Post Cards Component
3
- * Card-based layout for displaying posts
4
- */
5
- import { PostListItem } from '../../types/post';
6
- export interface PostCardsProps {
7
- posts: PostListItem[];
8
- locale: string;
9
- onEdit: (postId: string) => void;
10
- onPreview: (postId: string) => void;
11
- onDuplicate: (postId: string) => void;
12
- onDelete: (postId: string) => void;
13
- }
14
- export declare function PostCards({ posts, locale, onEdit, onPreview, onDuplicate, onDelete, }: PostCardsProps): import("react/jsx-runtime").JSX.Element;
15
- //# sourceMappingURL=PostCards.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PostCards.d.ts","sourceRoot":"","sources":["PostCards.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAc,MAAM,kBAAkB,CAAC;AAI5D,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AA0BD,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,MAAM,EACN,MAAM,EACN,SAAS,EACT,WAAW,EACX,QAAQ,GACX,EAAE,cAAc,2CA6IhB"}