@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.
- package/package.json +58 -59
- package/src/api/categories.ts +0 -43
- package/src/api/check-title.ts +0 -60
- package/src/api/config-handler.ts +0 -76
- package/src/api/handler.ts +0 -418
- package/src/api/index.ts +0 -33
- package/src/api/route.ts +0 -116
- package/src/api/router.ts +0 -128
- package/src/api-server.ts +0 -11
- package/src/config.ts +0 -161
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.d.ts.map +0 -1
- package/src/hooks/index.js +0 -7
- package/src/hooks/index.ts +0 -9
- package/src/hooks/useBlog.d.ts +0 -31
- package/src/hooks/useBlog.d.ts.map +0 -1
- package/src/hooks/useBlog.js +0 -57
- package/src/hooks/useBlog.ts +0 -85
- package/src/hooks/useBlogs.d.ts +0 -39
- package/src/hooks/useBlogs.d.ts.map +0 -1
- package/src/hooks/useBlogs.js +0 -82
- package/src/hooks/useBlogs.ts +0 -123
- package/src/hooks/useCategories.d.ts +0 -9
- package/src/hooks/useCategories.d.ts.map +0 -1
- package/src/hooks/useCategories.js +0 -70
- package/src/hooks/useCategories.ts +0 -76
- package/src/index.d.ts +0 -55
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -228
- package/src/index.server.ts +0 -14
- package/src/index.tsx +0 -335
- package/src/init.d.ts +0 -40
- package/src/init.d.ts.map +0 -1
- package/src/init.js +0 -41
- package/src/init.tsx +0 -63
- package/src/lib/blocks/BlockRenderer.d.ts +0 -54
- package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
- package/src/lib/blocks/BlockRenderer.js +0 -54
- package/src/lib/blocks/BlockRenderer.tsx +0 -141
- package/src/lib/blocks/index.ts +0 -6
- package/src/lib/config-storage.d.ts +0 -30
- package/src/lib/config-storage.d.ts.map +0 -1
- package/src/lib/config-storage.js +0 -31
- package/src/lib/config-storage.ts +0 -65
- package/src/lib/index.ts +0 -9
- package/src/lib/layouts/blocks/ColumnsBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +0 -1
- package/src/lib/layouts/blocks/ColumnsBlock.js +0 -182
- package/src/lib/layouts/blocks/ColumnsBlock.tsx +0 -298
- package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +0 -81
- package/src/lib/layouts/blocks/SectionBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
- package/src/lib/layouts/blocks/SectionBlock.js +0 -44
- package/src/lib/layouts/blocks/SectionBlock.tsx +0 -104
- package/src/lib/layouts/blocks/index.ts +0 -8
- package/src/lib/layouts/index.d.ts +0 -23
- package/src/lib/layouts/index.d.ts.map +0 -1
- package/src/lib/layouts/index.js +0 -45
- package/src/lib/layouts/index.ts +0 -52
- package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
- package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
- package/src/lib/layouts/registerLayoutBlocks.js +0 -60
- package/src/lib/layouts/registerLayoutBlocks.ts +0 -64
- package/src/lib/mappers/apiMapper.d.ts +0 -66
- package/src/lib/mappers/apiMapper.d.ts.map +0 -1
- package/src/lib/mappers/apiMapper.js +0 -191
- package/src/lib/mappers/apiMapper.ts +0 -254
- package/src/lib/migration/index.ts +0 -6
- package/src/lib/migration/mapper.ts +0 -140
- package/src/lib/rich-text/RichTextEditor.d.ts +0 -45
- package/src/lib/rich-text/RichTextEditor.d.ts.map +0 -1
- package/src/lib/rich-text/RichTextEditor.js +0 -564
- package/src/lib/rich-text/RichTextEditor.tsx +0 -826
- package/src/lib/rich-text/RichTextPreview.d.ts +0 -16
- package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
- package/src/lib/rich-text/RichTextPreview.js +0 -144
- package/src/lib/rich-text/RichTextPreview.tsx +0 -210
- package/src/lib/rich-text/index.d.ts +0 -9
- package/src/lib/rich-text/index.d.ts.map +0 -1
- package/src/lib/rich-text/index.js +0 -6
- package/src/lib/rich-text/index.ts +0 -10
- package/src/lib/utils/blockHelpers.d.ts +0 -23
- package/src/lib/utils/blockHelpers.d.ts.map +0 -1
- package/src/lib/utils/blockHelpers.js +0 -65
- package/src/lib/utils/blockHelpers.ts +0 -72
- package/src/lib/utils/configValidation.d.ts +0 -23
- package/src/lib/utils/configValidation.d.ts.map +0 -1
- package/src/lib/utils/configValidation.js +0 -113
- package/src/lib/utils/configValidation.ts +0 -137
- package/src/lib/utils/index.ts +0 -8
- package/src/lib/utils/slugify.ts +0 -79
- package/src/registry/BlockRegistry.d.ts +0 -62
- package/src/registry/BlockRegistry.d.ts.map +0 -1
- package/src/registry/BlockRegistry.js +0 -112
- package/src/registry/BlockRegistry.ts +0 -139
- package/src/registry/index.d.ts +0 -6
- package/src/registry/index.d.ts.map +0 -1
- package/src/registry/index.js +0 -4
- package/src/registry/index.ts +0 -11
- package/src/state/EditorContext.d.ts +0 -45
- package/src/state/EditorContext.d.ts.map +0 -1
- package/src/state/EditorContext.js +0 -215
- package/src/state/EditorContext.tsx +0 -283
- package/src/state/index.d.ts +0 -7
- package/src/state/index.d.ts.map +0 -1
- package/src/state/index.js +0 -6
- package/src/state/index.ts +0 -8
- package/src/state/reducer.d.ts +0 -11
- package/src/state/reducer.d.ts.map +0 -1
- package/src/state/reducer.js +0 -443
- package/src/state/reducer.ts +0 -694
- package/src/state/types.d.ts +0 -162
- package/src/state/types.d.ts.map +0 -1
- package/src/state/types.js +0 -27
- package/src/state/types.ts +0 -160
- package/src/types/block.d.ts +0 -221
- package/src/types/block.d.ts.map +0 -1
- package/src/types/block.js +0 -6
- package/src/types/block.ts +0 -269
- package/src/types/index.d.ts +0 -8
- package/src/types/index.d.ts.map +0 -1
- package/src/types/index.js +0 -5
- package/src/types/index.ts +0 -17
- package/src/types/post.d.ts +0 -136
- package/src/types/post.d.ts.map +0 -1
- package/src/types/post.js +0 -5
- package/src/types/post.ts +0 -169
- package/src/utils/client.d.ts +0 -48
- package/src/utils/client.d.ts.map +0 -1
- package/src/utils/client.js +0 -77
- package/src/utils/client.ts +0 -122
- package/src/utils/index.ts +0 -7
- package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
- package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
- package/src/views/CanvasEditor/BlockWrapper.js +0 -276
- package/src/views/CanvasEditor/BlockWrapper.tsx +0 -522
- package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
- package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
- package/src/views/CanvasEditor/CanvasEditorView.js +0 -209
- package/src/views/CanvasEditor/CanvasEditorView.tsx +0 -337
- package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
- package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorBody.js +0 -505
- package/src/views/CanvasEditor/EditorBody.tsx +0 -665
- package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
- package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorHeader.js +0 -101
- package/src/views/CanvasEditor/EditorHeader.tsx +0 -268
- package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
- package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
- package/src/views/CanvasEditor/LayoutContainer.js +0 -222
- package/src/views/CanvasEditor/LayoutContainer.tsx +0 -322
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
- package/src/views/CanvasEditor/SaveConfirmationModal.js +0 -78
- package/src/views/CanvasEditor/SaveConfirmationModal.tsx +0 -233
- package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +0 -14
- package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/CustomBlockItem.js +0 -44
- package/src/views/CanvasEditor/components/CustomBlockItem.tsx +0 -92
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts +0 -29
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorCanvas.js +0 -32
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +0 -160
- package/src/views/CanvasEditor/components/EditorLibrary.d.ts +0 -7
- package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorLibrary.js +0 -25
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +0 -122
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts +0 -13
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorSidebar.js +0 -20
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +0 -181
- package/src/views/CanvasEditor/components/ErrorBanner.d.ts +0 -6
- package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/ErrorBanner.js +0 -8
- package/src/views/CanvasEditor/components/ErrorBanner.tsx +0 -31
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -25
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/FeaturedMediaSection.js +0 -182
- package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +0 -341
- package/src/views/CanvasEditor/components/LibraryItem.d.ts +0 -14
- package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/LibraryItem.js +0 -43
- package/src/views/CanvasEditor/components/LibraryItem.tsx +0 -80
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -15
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/PrivacySettingsSection.js +0 -63
- package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +0 -212
- package/src/views/CanvasEditor/components/index.d.ts +0 -21
- package/src/views/CanvasEditor/components/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/index.js +0 -12
- package/src/views/CanvasEditor/components/index.ts +0 -28
- package/src/views/CanvasEditor/hooks/index.d.ts +0 -10
- package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/index.js +0 -9
- package/src/views/CanvasEditor/hooks/index.ts +0 -10
- package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +0 -8
- package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useHeroBlock.js +0 -79
- package/src/views/CanvasEditor/hooks/useHeroBlock.ts +0 -103
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +0 -3
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.js +0 -114
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +0 -142
- package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +0 -5
- package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/usePostLoader.js +0 -32
- package/src/views/CanvasEditor/hooks/usePostLoader.ts +0 -39
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +0 -2
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.js +0 -47
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +0 -55
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +0 -25
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -285
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +0 -339
- package/src/views/CanvasEditor/index.d.ts +0 -16
- package/src/views/CanvasEditor/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/index.js +0 -9
- package/src/views/CanvasEditor/index.ts +0 -16
- package/src/views/PostManager/EmptyState.d.ts +0 -10
- package/src/views/PostManager/EmptyState.d.ts.map +0 -1
- package/src/views/PostManager/EmptyState.js +0 -12
- package/src/views/PostManager/EmptyState.tsx +0 -42
- package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
- package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
- package/src/views/PostManager/PostActionsMenu.js +0 -58
- package/src/views/PostManager/PostActionsMenu.tsx +0 -112
- package/src/views/PostManager/PostCards.d.ts +0 -15
- package/src/views/PostManager/PostCards.d.ts.map +0 -1
- package/src/views/PostManager/PostCards.js +0 -79
- package/src/views/PostManager/PostCards.tsx +0 -197
- package/src/views/PostManager/PostFilters.d.ts +0 -16
- package/src/views/PostManager/PostFilters.d.ts.map +0 -1
- package/src/views/PostManager/PostFilters.js +0 -10
- package/src/views/PostManager/PostFilters.tsx +0 -95
- package/src/views/PostManager/PostManagerView.d.ts +0 -11
- package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
- package/src/views/PostManager/PostManagerView.js +0 -174
- package/src/views/PostManager/PostManagerView.tsx +0 -289
- package/src/views/PostManager/PostStats.d.ts +0 -11
- package/src/views/PostManager/PostStats.d.ts.map +0 -1
- package/src/views/PostManager/PostStats.js +0 -46
- package/src/views/PostManager/PostStats.tsx +0 -81
- package/src/views/PostManager/PostTable.d.ts +0 -15
- package/src/views/PostManager/PostTable.d.ts.map +0 -1
- package/src/views/PostManager/PostTable.js +0 -79
- package/src/views/PostManager/PostTable.tsx +0 -230
- package/src/views/PostManager/index.d.ts +0 -12
- package/src/views/PostManager/index.d.ts.map +0 -1
- package/src/views/PostManager/index.js +0 -11
- package/src/views/PostManager/index.ts +0 -15
- package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
- package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
- package/src/views/Preview/PreviewBridgeView.js +0 -11
- package/src/views/Preview/PreviewBridgeView.tsx +0 -64
- package/src/views/Preview/index.d.ts +0 -6
- package/src/views/Preview/index.d.ts.map +0 -1
- package/src/views/Preview/index.js +0 -4
- package/src/views/Preview/index.ts +0 -7
- package/src/views/Settings/SettingsView.d.ts +0 -10
- package/src/views/Settings/SettingsView.d.ts.map +0 -1
- package/src/views/Settings/SettingsView.js +0 -111
- package/src/views/Settings/SettingsView.tsx +0 -298
- package/src/views/Settings/index.d.ts +0 -6
- package/src/views/Settings/index.d.ts.map +0 -1
- package/src/views/Settings/index.js +0 -4
- package/src/views/Settings/index.ts +0 -7
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
- package/src/views/SlugSEO/SlugSEOManagerView.js +0 -11
- package/src/views/SlugSEO/SlugSEOManagerView.tsx +0 -94
- package/src/views/SlugSEO/index.d.ts +0 -6
- package/src/views/SlugSEO/index.d.ts.map +0 -1
- package/src/views/SlugSEO/index.js +0 -4
- package/src/views/SlugSEO/index.ts +0 -7
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Configuration Validation Utilities
|
|
3
|
-
* Validates plugin configuration and custom blocks
|
|
4
|
-
*/
|
|
5
|
-
import { useEffect, useState, useMemo } from 'react';
|
|
6
|
-
/**
|
|
7
|
-
* Routes that require the hero block to be defined
|
|
8
|
-
*/
|
|
9
|
-
const ROUTES_REQUIRING_HERO_BLOCK = ['editor', 'new', 'preview'];
|
|
10
|
-
/**
|
|
11
|
-
* Check if a route requires the hero block
|
|
12
|
-
*/
|
|
13
|
-
export function routeRequiresHeroBlock(route) {
|
|
14
|
-
return ROUTES_REQUIRING_HERO_BLOCK.includes(route);
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Find hero block definition in custom blocks
|
|
18
|
-
*/
|
|
19
|
-
export function findHeroBlock(customBlocks) {
|
|
20
|
-
return customBlocks.find(block => block.type === 'hero');
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Hook to validate hero block configuration
|
|
24
|
-
* Only validates for routes that require it, and waits for customBlocks to load
|
|
25
|
-
*
|
|
26
|
-
* @param route - Current route (e.g., 'posts', 'editor', 'new')
|
|
27
|
-
* @param customBlocks - Array of custom block definitions
|
|
28
|
-
* @param propsCustomBlocks - Custom blocks from props (for immediate validation)
|
|
29
|
-
*/
|
|
30
|
-
export function useHeroBlockValidation(route, customBlocks, propsCustomBlocks) {
|
|
31
|
-
const needsHeroBlock = useMemo(() => routeRequiresHeroBlock(route), [route]);
|
|
32
|
-
const heroBlockDefinition = useMemo(() => {
|
|
33
|
-
// Only search for hero block if route needs it
|
|
34
|
-
return needsHeroBlock ? findHeroBlock(customBlocks) : undefined;
|
|
35
|
-
}, [customBlocks, needsHeroBlock]);
|
|
36
|
-
// Track if we've given customBlocks time to load from window global
|
|
37
|
-
// This prevents false errors when customBlocks are loaded asynchronously
|
|
38
|
-
// Only track this if we actually need the hero block
|
|
39
|
-
const [hasCheckedForBlocks, setHasCheckedForBlocks] = useState(false);
|
|
40
|
-
// Give window global a chance to load before validating
|
|
41
|
-
// Only do this if the route needs the hero block
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
var _a, _b;
|
|
44
|
-
// Early return: Skip all validation logic if route doesn't need hero block
|
|
45
|
-
if (!needsHeroBlock) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (typeof window === 'undefined') {
|
|
49
|
-
setHasCheckedForBlocks(true); // Server-side, no need to wait
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
// If props are provided, we can validate immediately
|
|
53
|
-
if (propsCustomBlocks && propsCustomBlocks.length > 0) {
|
|
54
|
-
setHasCheckedForBlocks(true);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
// Check if window global is already available
|
|
58
|
-
if ((_b = (_a = window.__JHITS_PLUGIN_PROPS__) === null || _a === void 0 ? void 0 : _a['plugin-blog']) === null || _b === void 0 ? void 0 : _b.customBlocks) {
|
|
59
|
-
setHasCheckedForBlocks(true);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// Retry mechanism: check multiple times with increasing delays
|
|
63
|
-
// This handles the case where BlogPluginInit's useEffect runs after BlogPlugin mounts
|
|
64
|
-
// BlogPluginInit runs initBlogPlugin in useEffect, which may execute after BlogPlugin's first render
|
|
65
|
-
let attempt = 0;
|
|
66
|
-
const maxAttempts = 6;
|
|
67
|
-
const delays = [50, 100, 200, 300, 500, 1000]; // Progressive delays: total ~2.15s
|
|
68
|
-
const timers = [];
|
|
69
|
-
const checkWithRetry = () => {
|
|
70
|
-
var _a, _b;
|
|
71
|
-
const windowGlobal = (_b = (_a = window.__JHITS_PLUGIN_PROPS__) === null || _a === void 0 ? void 0 : _a['plugin-blog']) === null || _b === void 0 ? void 0 : _b.customBlocks;
|
|
72
|
-
if (windowGlobal && windowGlobal.length > 0) {
|
|
73
|
-
// Found it! Mark as checked
|
|
74
|
-
setHasCheckedForBlocks(true);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (attempt < maxAttempts) {
|
|
78
|
-
const timer = setTimeout(() => {
|
|
79
|
-
attempt++;
|
|
80
|
-
checkWithRetry();
|
|
81
|
-
}, delays[attempt]);
|
|
82
|
-
timers.push(timer);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// After all retries, mark as checked (even if not found)
|
|
86
|
-
// This prevents infinite waiting and allows validation to proceed
|
|
87
|
-
setHasCheckedForBlocks(true);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
// Start checking
|
|
91
|
-
checkWithRetry();
|
|
92
|
-
// Cleanup all timers
|
|
93
|
-
return () => {
|
|
94
|
-
timers.forEach(timer => clearTimeout(timer));
|
|
95
|
-
};
|
|
96
|
-
}, [propsCustomBlocks, needsHeroBlock]);
|
|
97
|
-
// Validate that Hero block is required (only for editor routes, and only after we've checked for blocks)
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
// Early return if route doesn't need hero block - no validation needed
|
|
100
|
-
if (!needsHeroBlock) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
// Only validate if:
|
|
104
|
-
// 1. We're in a route that needs the hero block (already checked above)
|
|
105
|
-
// 2. We've given customBlocks time to load (hasCheckedForBlocks is true)
|
|
106
|
-
// 3. The hero block is still not found
|
|
107
|
-
if (hasCheckedForBlocks && !heroBlockDefinition) {
|
|
108
|
-
console.error('[BlogPlugin] Hero block is REQUIRED but not found in customBlocks. ' +
|
|
109
|
-
'Please define a block with type: "hero" in your blog config. ' +
|
|
110
|
-
`Current route: "${route}"`);
|
|
111
|
-
}
|
|
112
|
-
}, [heroBlockDefinition, needsHeroBlock, hasCheckedForBlocks, route]);
|
|
113
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugin Configuration Validation Utilities
|
|
3
|
-
* Validates plugin configuration and custom blocks
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useEffect, useState, useMemo } from 'react';
|
|
7
|
-
import { ClientBlockDefinition } from '../../types/block';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Routes that require the hero block to be defined
|
|
11
|
-
*/
|
|
12
|
-
const ROUTES_REQUIRING_HERO_BLOCK = ['editor', 'new', 'preview'] as const;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Check if a route requires the hero block
|
|
16
|
-
*/
|
|
17
|
-
export function routeRequiresHeroBlock(route: string): boolean {
|
|
18
|
-
return ROUTES_REQUIRING_HERO_BLOCK.includes(route as any);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Find hero block definition in custom blocks
|
|
23
|
-
*/
|
|
24
|
-
export function findHeroBlock(customBlocks: ClientBlockDefinition[]): ClientBlockDefinition | undefined {
|
|
25
|
-
return customBlocks.find(block => block.type === 'hero');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Hook to validate hero block configuration
|
|
30
|
-
* Only validates for routes that require it, and waits for customBlocks to load
|
|
31
|
-
*
|
|
32
|
-
* @param route - Current route (e.g., 'posts', 'editor', 'new')
|
|
33
|
-
* @param customBlocks - Array of custom block definitions
|
|
34
|
-
* @param propsCustomBlocks - Custom blocks from props (for immediate validation)
|
|
35
|
-
*/
|
|
36
|
-
export function useHeroBlockValidation(
|
|
37
|
-
route: string,
|
|
38
|
-
customBlocks: ClientBlockDefinition[],
|
|
39
|
-
propsCustomBlocks?: ClientBlockDefinition[]
|
|
40
|
-
): void {
|
|
41
|
-
const needsHeroBlock = useMemo(() => routeRequiresHeroBlock(route), [route]);
|
|
42
|
-
|
|
43
|
-
const heroBlockDefinition = useMemo(() => {
|
|
44
|
-
// Only search for hero block if route needs it
|
|
45
|
-
return needsHeroBlock ? findHeroBlock(customBlocks) : undefined;
|
|
46
|
-
}, [customBlocks, needsHeroBlock]);
|
|
47
|
-
|
|
48
|
-
// Track if we've given customBlocks time to load from window global
|
|
49
|
-
// This prevents false errors when customBlocks are loaded asynchronously
|
|
50
|
-
// Only track this if we actually need the hero block
|
|
51
|
-
const [hasCheckedForBlocks, setHasCheckedForBlocks] = useState(false);
|
|
52
|
-
|
|
53
|
-
// Give window global a chance to load before validating
|
|
54
|
-
// Only do this if the route needs the hero block
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
// Early return: Skip all validation logic if route doesn't need hero block
|
|
57
|
-
if (!needsHeroBlock) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (typeof window === 'undefined') {
|
|
62
|
-
setHasCheckedForBlocks(true); // Server-side, no need to wait
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// If props are provided, we can validate immediately
|
|
67
|
-
if (propsCustomBlocks && propsCustomBlocks.length > 0) {
|
|
68
|
-
setHasCheckedForBlocks(true);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Check if window global is already available
|
|
73
|
-
if ((window as any).__JHITS_PLUGIN_PROPS__?.['plugin-blog']?.customBlocks) {
|
|
74
|
-
setHasCheckedForBlocks(true);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Retry mechanism: check multiple times with increasing delays
|
|
79
|
-
// This handles the case where BlogPluginInit's useEffect runs after BlogPlugin mounts
|
|
80
|
-
// BlogPluginInit runs initBlogPlugin in useEffect, which may execute after BlogPlugin's first render
|
|
81
|
-
let attempt = 0;
|
|
82
|
-
const maxAttempts = 6;
|
|
83
|
-
const delays = [50, 100, 200, 300, 500, 1000]; // Progressive delays: total ~2.15s
|
|
84
|
-
|
|
85
|
-
const timers: NodeJS.Timeout[] = [];
|
|
86
|
-
|
|
87
|
-
const checkWithRetry = () => {
|
|
88
|
-
const windowGlobal = (window as any).__JHITS_PLUGIN_PROPS__?.['plugin-blog']?.customBlocks;
|
|
89
|
-
if (windowGlobal && windowGlobal.length > 0) {
|
|
90
|
-
// Found it! Mark as checked
|
|
91
|
-
setHasCheckedForBlocks(true);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (attempt < maxAttempts) {
|
|
96
|
-
const timer = setTimeout(() => {
|
|
97
|
-
attempt++;
|
|
98
|
-
checkWithRetry();
|
|
99
|
-
}, delays[attempt]);
|
|
100
|
-
timers.push(timer);
|
|
101
|
-
} else {
|
|
102
|
-
// After all retries, mark as checked (even if not found)
|
|
103
|
-
// This prevents infinite waiting and allows validation to proceed
|
|
104
|
-
setHasCheckedForBlocks(true);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
// Start checking
|
|
109
|
-
checkWithRetry();
|
|
110
|
-
|
|
111
|
-
// Cleanup all timers
|
|
112
|
-
return () => {
|
|
113
|
-
timers.forEach(timer => clearTimeout(timer));
|
|
114
|
-
};
|
|
115
|
-
}, [propsCustomBlocks, needsHeroBlock]);
|
|
116
|
-
|
|
117
|
-
// Validate that Hero block is required (only for editor routes, and only after we've checked for blocks)
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
// Early return if route doesn't need hero block - no validation needed
|
|
120
|
-
if (!needsHeroBlock) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Only validate if:
|
|
125
|
-
// 1. We're in a route that needs the hero block (already checked above)
|
|
126
|
-
// 2. We've given customBlocks time to load (hasCheckedForBlocks is true)
|
|
127
|
-
// 3. The hero block is still not found
|
|
128
|
-
if (hasCheckedForBlocks && !heroBlockDefinition) {
|
|
129
|
-
console.error(
|
|
130
|
-
'[BlogPlugin] Hero block is REQUIRED but not found in customBlocks. ' +
|
|
131
|
-
'Please define a block with type: "hero" in your blog config. ' +
|
|
132
|
-
`Current route: "${route}"`
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}, [heroBlockDefinition, needsHeroBlock, hasCheckedForBlocks, route]);
|
|
136
|
-
}
|
|
137
|
-
|
package/src/lib/utils/index.ts
DELETED
package/src/lib/utils/slugify.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slug Utilities
|
|
3
|
-
* Functions for generating and validating URL slugs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Convert a string to a URL-friendly slug
|
|
8
|
-
*/
|
|
9
|
-
export function slugify(text: string): string {
|
|
10
|
-
return text
|
|
11
|
-
.toString()
|
|
12
|
-
.toLowerCase()
|
|
13
|
-
.trim()
|
|
14
|
-
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
15
|
-
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
|
16
|
-
.replace(/\-\-+/g, '-') // Replace multiple hyphens with single hyphen
|
|
17
|
-
.replace(/^-+/, '') // Trim hyphens from start
|
|
18
|
-
.replace(/-+$/, ''); // Trim hyphens from end
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate a slug from a title
|
|
23
|
-
* Automatically handles edge cases and ensures uniqueness
|
|
24
|
-
*/
|
|
25
|
-
export function generateSlugFromTitle(title: string, existingSlugs: string[] = []): string {
|
|
26
|
-
let baseSlug = slugify(title);
|
|
27
|
-
|
|
28
|
-
// If slug is empty after processing, use a fallback
|
|
29
|
-
if (!baseSlug) {
|
|
30
|
-
baseSlug = 'untitled-post';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Check for collisions and append number if needed
|
|
34
|
-
let finalSlug = baseSlug;
|
|
35
|
-
let counter = 1;
|
|
36
|
-
|
|
37
|
-
while (existingSlugs.includes(finalSlug)) {
|
|
38
|
-
finalSlug = `${baseSlug}-${counter}`;
|
|
39
|
-
counter++;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return finalSlug;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Validate a slug format
|
|
47
|
-
*/
|
|
48
|
-
export function isValidSlug(slug: string): boolean {
|
|
49
|
-
// Slug should be lowercase, alphanumeric with hyphens
|
|
50
|
-
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
51
|
-
return slugRegex.test(slug) && slug.length > 0 && slug.length <= 200;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Check if a slug collides with existing slugs
|
|
56
|
-
*/
|
|
57
|
-
export function checkSlugCollision(slug: string, existingSlugs: string[], excludeId?: string): {
|
|
58
|
-
isCollision: boolean;
|
|
59
|
-
conflictingId?: string;
|
|
60
|
-
} {
|
|
61
|
-
// Normalize the slug for comparison
|
|
62
|
-
const normalizedSlug = slug.toLowerCase().trim();
|
|
63
|
-
|
|
64
|
-
// Check against existing slugs (excluding current post if editing)
|
|
65
|
-
for (const existing of existingSlugs) {
|
|
66
|
-
if (existing.toLowerCase().trim() === normalizedSlug) {
|
|
67
|
-
// If we have an excludeId, we need to check if this is the same post
|
|
68
|
-
// This would require additional context (like a post ID map)
|
|
69
|
-
// For now, we'll return collision if it matches
|
|
70
|
-
return {
|
|
71
|
-
isCollision: true,
|
|
72
|
-
conflictingId: existing,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return { isCollision: false };
|
|
78
|
-
}
|
|
79
|
-
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Block Registry
|
|
3
|
-
* Dynamic registry for all block types in the system
|
|
4
|
-
* Multi-Tenant Architecture: Blocks are provided by client applications
|
|
5
|
-
*
|
|
6
|
-
* The registry is a singleton that starts empty and is populated by client apps
|
|
7
|
-
*/
|
|
8
|
-
import { BlockTypeDefinition, BlockRegistry as IBlockRegistry, ClientBlockDefinition } from '../types/block';
|
|
9
|
-
/**
|
|
10
|
-
* Dynamic Block Registry Implementation
|
|
11
|
-
* No default blocks - all blocks must be registered by client applications
|
|
12
|
-
*/
|
|
13
|
-
declare class BlockRegistryImpl implements IBlockRegistry {
|
|
14
|
-
private _types;
|
|
15
|
-
get types(): Map<string, BlockTypeDefinition>;
|
|
16
|
-
/**
|
|
17
|
-
* Register a single block type
|
|
18
|
-
*/
|
|
19
|
-
register(definition: BlockTypeDefinition): void;
|
|
20
|
-
/**
|
|
21
|
-
* Register multiple client blocks at once
|
|
22
|
-
* This is the primary method for client applications to register their blocks
|
|
23
|
-
*/
|
|
24
|
-
registerClientBlocks(definitions: ClientBlockDefinition[]): void;
|
|
25
|
-
/**
|
|
26
|
-
* Get block type definition by type string
|
|
27
|
-
*/
|
|
28
|
-
get(type: string): BlockTypeDefinition | undefined;
|
|
29
|
-
/**
|
|
30
|
-
* Get all registered block types
|
|
31
|
-
*/
|
|
32
|
-
getAll(): BlockTypeDefinition[];
|
|
33
|
-
/**
|
|
34
|
-
* Get block types filtered by category
|
|
35
|
-
*/
|
|
36
|
-
getByCategory(category: BlockTypeDefinition['category']): BlockTypeDefinition[];
|
|
37
|
-
/**
|
|
38
|
-
* Check if a block type is registered
|
|
39
|
-
*/
|
|
40
|
-
has(type: string): boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Unregister a block type (useful for testing or dynamic loading)
|
|
43
|
-
*/
|
|
44
|
-
unregister(type: string): boolean;
|
|
45
|
-
/**
|
|
46
|
-
* Clear all registered block types
|
|
47
|
-
* Useful for testing or resetting the registry
|
|
48
|
-
*/
|
|
49
|
-
clear(): void;
|
|
50
|
-
/**
|
|
51
|
-
* Get count of registered blocks
|
|
52
|
-
*/
|
|
53
|
-
getCount(): number;
|
|
54
|
-
}
|
|
55
|
-
export declare const blockRegistry: BlockRegistryImpl;
|
|
56
|
-
export {};
|
|
57
|
-
/**
|
|
58
|
-
* NOTE: No default blocks are registered automatically.
|
|
59
|
-
* Client applications must call registerClientBlocks() to populate the registry.
|
|
60
|
-
* This ensures the editor is a true "shell" without hardcoded blocks.
|
|
61
|
-
*/
|
|
62
|
-
//# sourceMappingURL=BlockRegistry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BlockRegistry.d.ts","sourceRoot":"","sources":["BlockRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACH,mBAAmB,EACnB,aAAa,IAAI,cAAc,EAC/B,qBAAqB,EACxB,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,cAAM,iBAAkB,YAAW,cAAc;IAC7C,OAAO,CAAC,MAAM,CAA+C;IAE7D,IAAI,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAE5C;IAED;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI;IAa/C;;;OAGG;IACH,oBAAoB,CAAC,WAAW,EAAE,qBAAqB,EAAE,GAAG,IAAI;IAmChE;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIlD;;OAEG;IACH,MAAM,IAAI,mBAAmB,EAAE;IAI/B;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,mBAAmB,EAAE;IAI/E;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC;;;OAGG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,QAAQ,IAAI,MAAM;CAGrB;AAGD,eAAO,MAAM,aAAa,mBAA0B,CAAC;;AAErD;;;;GAIG"}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Block Registry
|
|
3
|
-
* Dynamic registry for all block types in the system
|
|
4
|
-
* Multi-Tenant Architecture: Blocks are provided by client applications
|
|
5
|
-
*
|
|
6
|
-
* The registry is a singleton that starts empty and is populated by client apps
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Dynamic Block Registry Implementation
|
|
10
|
-
* No default blocks - all blocks must be registered by client applications
|
|
11
|
-
*/
|
|
12
|
-
class BlockRegistryImpl {
|
|
13
|
-
constructor() {
|
|
14
|
-
this._types = new Map();
|
|
15
|
-
}
|
|
16
|
-
get types() {
|
|
17
|
-
return this._types;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Register a single block type
|
|
21
|
-
*/
|
|
22
|
-
register(definition) {
|
|
23
|
-
// Validate that components are provided
|
|
24
|
-
if (!definition.components || !definition.components.Edit || !definition.components.Preview) {
|
|
25
|
-
throw new Error(`Block type "${definition.type}" must provide both Edit and Preview components. ` +
|
|
26
|
-
`Use registerClientBlocks() for client-provided blocks.`);
|
|
27
|
-
}
|
|
28
|
-
// Silently overwrite if already registered (expected in React 18 Strict Mode)
|
|
29
|
-
this._types.set(definition.type, definition);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Register multiple client blocks at once
|
|
33
|
-
* This is the primary method for client applications to register their blocks
|
|
34
|
-
*/
|
|
35
|
-
registerClientBlocks(definitions) {
|
|
36
|
-
for (const def of definitions) {
|
|
37
|
-
// Validate required fields
|
|
38
|
-
if (!def.type || !def.name || !def.components) {
|
|
39
|
-
console.error('Invalid block definition:', def);
|
|
40
|
-
throw new Error('Block definition must have: type, name, and components (with Edit and Preview)');
|
|
41
|
-
}
|
|
42
|
-
// Validate components
|
|
43
|
-
if (!def.components.Edit || !def.components.Preview) {
|
|
44
|
-
throw new Error(`Block "${def.type}" must provide both Edit and Preview components in the components object`);
|
|
45
|
-
}
|
|
46
|
-
// Convert ClientBlockDefinition to BlockTypeDefinition
|
|
47
|
-
const blockDefinition = {
|
|
48
|
-
type: def.type,
|
|
49
|
-
name: def.name,
|
|
50
|
-
description: def.description,
|
|
51
|
-
icon: def.icon || def.components.Icon,
|
|
52
|
-
defaultData: def.defaultData,
|
|
53
|
-
validate: def.validate,
|
|
54
|
-
isContainer: def.isContainer,
|
|
55
|
-
allowedChildren: def.allowedChildren,
|
|
56
|
-
category: def.category || 'custom',
|
|
57
|
-
components: def.components,
|
|
58
|
-
};
|
|
59
|
-
this.register(blockDefinition);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Get block type definition by type string
|
|
64
|
-
*/
|
|
65
|
-
get(type) {
|
|
66
|
-
return this._types.get(type);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Get all registered block types
|
|
70
|
-
*/
|
|
71
|
-
getAll() {
|
|
72
|
-
return Array.from(this._types.values());
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Get block types filtered by category
|
|
76
|
-
*/
|
|
77
|
-
getByCategory(category) {
|
|
78
|
-
return this.getAll().filter(block => block.category === category);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Check if a block type is registered
|
|
82
|
-
*/
|
|
83
|
-
has(type) {
|
|
84
|
-
return this._types.has(type);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Unregister a block type (useful for testing or dynamic loading)
|
|
88
|
-
*/
|
|
89
|
-
unregister(type) {
|
|
90
|
-
return this._types.delete(type);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Clear all registered block types
|
|
94
|
-
* Useful for testing or resetting the registry
|
|
95
|
-
*/
|
|
96
|
-
clear() {
|
|
97
|
-
this._types.clear();
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Get count of registered blocks
|
|
101
|
-
*/
|
|
102
|
-
getCount() {
|
|
103
|
-
return this._types.size;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// Singleton instance - starts empty, populated by client apps
|
|
107
|
-
export const blockRegistry = new BlockRegistryImpl();
|
|
108
|
-
/**
|
|
109
|
-
* NOTE: No default blocks are registered automatically.
|
|
110
|
-
* Client applications must call registerClientBlocks() to populate the registry.
|
|
111
|
-
* This ensures the editor is a true "shell" without hardcoded blocks.
|
|
112
|
-
*/
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Block Registry
|
|
3
|
-
* Dynamic registry for all block types in the system
|
|
4
|
-
* Multi-Tenant Architecture: Blocks are provided by client applications
|
|
5
|
-
*
|
|
6
|
-
* The registry is a singleton that starts empty and is populated by client apps
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
BlockTypeDefinition,
|
|
11
|
-
BlockRegistry as IBlockRegistry,
|
|
12
|
-
ClientBlockDefinition
|
|
13
|
-
} from '../types/block';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Dynamic Block Registry Implementation
|
|
17
|
-
* No default blocks - all blocks must be registered by client applications
|
|
18
|
-
*/
|
|
19
|
-
class BlockRegistryImpl implements IBlockRegistry {
|
|
20
|
-
private _types: Map<string, BlockTypeDefinition> = new Map();
|
|
21
|
-
|
|
22
|
-
get types(): Map<string, BlockTypeDefinition> {
|
|
23
|
-
return this._types;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Register a single block type
|
|
28
|
-
*/
|
|
29
|
-
register(definition: BlockTypeDefinition): void {
|
|
30
|
-
// Validate that components are provided
|
|
31
|
-
if (!definition.components || !definition.components.Edit || !definition.components.Preview) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Block type "${definition.type}" must provide both Edit and Preview components. ` +
|
|
34
|
-
`Use registerClientBlocks() for client-provided blocks.`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Silently overwrite if already registered (expected in React 18 Strict Mode)
|
|
39
|
-
this._types.set(definition.type, definition);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Register multiple client blocks at once
|
|
44
|
-
* This is the primary method for client applications to register their blocks
|
|
45
|
-
*/
|
|
46
|
-
registerClientBlocks(definitions: ClientBlockDefinition[]): void {
|
|
47
|
-
for (const def of definitions) {
|
|
48
|
-
// Validate required fields
|
|
49
|
-
if (!def.type || !def.name || !def.components) {
|
|
50
|
-
console.error('Invalid block definition:', def);
|
|
51
|
-
throw new Error(
|
|
52
|
-
'Block definition must have: type, name, and components (with Edit and Preview)'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Validate components
|
|
57
|
-
if (!def.components.Edit || !def.components.Preview) {
|
|
58
|
-
throw new Error(
|
|
59
|
-
`Block "${def.type}" must provide both Edit and Preview components in the components object`
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Convert ClientBlockDefinition to BlockTypeDefinition
|
|
64
|
-
const blockDefinition: BlockTypeDefinition = {
|
|
65
|
-
type: def.type,
|
|
66
|
-
name: def.name,
|
|
67
|
-
description: def.description,
|
|
68
|
-
icon: def.icon || def.components.Icon,
|
|
69
|
-
defaultData: def.defaultData,
|
|
70
|
-
validate: def.validate,
|
|
71
|
-
isContainer: def.isContainer,
|
|
72
|
-
allowedChildren: def.allowedChildren,
|
|
73
|
-
category: def.category || 'custom',
|
|
74
|
-
components: def.components,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
this.register(blockDefinition);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get block type definition by type string
|
|
83
|
-
*/
|
|
84
|
-
get(type: string): BlockTypeDefinition | undefined {
|
|
85
|
-
return this._types.get(type);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get all registered block types
|
|
90
|
-
*/
|
|
91
|
-
getAll(): BlockTypeDefinition[] {
|
|
92
|
-
return Array.from(this._types.values());
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Get block types filtered by category
|
|
97
|
-
*/
|
|
98
|
-
getByCategory(category: BlockTypeDefinition['category']): BlockTypeDefinition[] {
|
|
99
|
-
return this.getAll().filter(block => block.category === category);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Check if a block type is registered
|
|
104
|
-
*/
|
|
105
|
-
has(type: string): boolean {
|
|
106
|
-
return this._types.has(type);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Unregister a block type (useful for testing or dynamic loading)
|
|
111
|
-
*/
|
|
112
|
-
unregister(type: string): boolean {
|
|
113
|
-
return this._types.delete(type);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Clear all registered block types
|
|
118
|
-
* Useful for testing or resetting the registry
|
|
119
|
-
*/
|
|
120
|
-
clear(): void {
|
|
121
|
-
this._types.clear();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Get count of registered blocks
|
|
126
|
-
*/
|
|
127
|
-
getCount(): number {
|
|
128
|
-
return this._types.size;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Singleton instance - starts empty, populated by client apps
|
|
133
|
-
export const blockRegistry = new BlockRegistryImpl();
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* NOTE: No default blocks are registered automatically.
|
|
137
|
-
* Client applications must call registerClientBlocks() to populate the registry.
|
|
138
|
-
* This ensures the editor is a true "shell" without hardcoded blocks.
|
|
139
|
-
*/
|
package/src/registry/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACR,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,GAChB,MAAM,gBAAgB,CAAC"}
|
package/src/registry/index.js
DELETED