@kyro-cms/admin 0.3.1 → 0.3.4
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/dist/EditorClient-XEUOVAAC.js +466 -0
- package/dist/EditorClient-XEUOVAAC.js.map +1 -0
- package/dist/EditorClient-YLCGVDXY.cjs +468 -0
- package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
- package/dist/chunk-7KPIUCGT.js +384 -0
- package/dist/chunk-7KPIUCGT.js.map +1 -0
- package/dist/chunk-GOACG6R7.cjs +473 -0
- package/dist/chunk-GOACG6R7.cjs.map +1 -0
- package/dist/index.cjs +14861 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +1661 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +563 -0
- package/dist/index.js +14784 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -19
- package/src/components/ActionBar.tsx +7 -43
- package/src/components/Admin.tsx +138 -277
- package/src/components/ApiKeysManager.tsx +428 -419
- package/src/components/AuditLogsPage.tsx +35 -39
- package/src/components/AuthBridge.tsx +51 -0
- package/src/components/AutoForm.tsx +495 -1230
- package/src/components/BrandingHub.tsx +18 -19
- package/src/components/BulkActionsBar.tsx +1 -1
- package/src/components/CreateView.tsx +22 -36
- package/src/components/Dashboard.tsx +60 -84
- package/src/components/DetailView.tsx +113 -91
- package/src/components/DeveloperCenter.tsx +200 -198
- package/src/components/FieldRenderer.tsx +206 -0
- package/src/components/GraphQLPlayground.tsx +340 -480
- package/src/components/ListView.tsx +828 -254
- package/src/components/LoginPage.tsx +3 -4
- package/src/components/MarketplaceManager.tsx +254 -0
- package/src/components/MediaGallery.tsx +856 -1192
- package/src/components/PluginsManager.tsx +277 -0
- package/src/components/RestPlayground.tsx +398 -560
- package/src/components/SessionsManager.tsx +211 -0
- package/src/components/Sidebar.astro +179 -151
- package/src/components/ThemeProvider.tsx +7 -161
- package/src/components/UserManagement.tsx +162 -146
- package/src/components/UserMenu.tsx +110 -0
- package/src/components/WebhookManager.tsx +305 -367
- package/src/components/blocks/AccordionBlock.tsx +4 -4
- package/src/components/blocks/ArrayBlock.tsx +3 -3
- package/src/components/blocks/BlockEditModal.tsx +8 -8
- package/src/components/blocks/BlockWrapper.tsx +61 -0
- package/src/components/blocks/ButtonBlock.tsx +4 -4
- package/src/components/blocks/ChildBlocksTree.tsx +23 -25
- package/src/components/blocks/CodeBlock.tsx +15 -15
- package/src/components/blocks/ColumnsBlock.tsx +6 -44
- package/src/components/blocks/DividerBlock.tsx +3 -3
- package/src/components/blocks/FileBlock.tsx +4 -4
- package/src/components/blocks/HeadingBlock.tsx +6 -38
- package/src/components/blocks/HeroBlock.tsx +4 -4
- package/src/components/blocks/ImageBlock.tsx +4 -4
- package/src/components/blocks/LinkBlock.tsx +4 -4
- package/src/components/blocks/ListBlock.tsx +3 -3
- package/src/components/blocks/ParagraphBlock.tsx +12 -42
- package/src/components/blocks/RelationshipBlock.tsx +4 -4
- package/src/components/blocks/RichTextBlock.tsx +4 -4
- package/src/components/blocks/VStackBlock.tsx +5 -37
- package/src/components/blocks/VideoBlock.tsx +4 -4
- package/src/components/blocks/types.ts +11 -0
- package/src/components/fields/AccordionField.tsx +1 -1
- package/src/components/fields/ArrayField.tsx +2 -2
- package/src/components/fields/ArrayLayout.tsx +93 -0
- package/src/components/fields/BlocksField.tsx +122 -111
- package/src/components/fields/ButtonField.tsx +1 -1
- package/src/components/fields/CheckboxField.tsx +14 -15
- package/src/components/fields/ChildrenField.tsx +2 -2
- package/src/components/fields/CodeField.tsx +3 -3
- package/src/components/fields/ColumnsField.tsx +2 -2
- package/src/components/fields/DateField.tsx +13 -26
- package/src/components/fields/EditorClient.tsx +26 -28
- package/src/components/fields/FieldLayout.tsx +52 -0
- package/src/components/fields/GroupLayout.tsx +35 -0
- package/src/components/fields/JSONField.tsx +7 -7
- package/src/components/fields/LinkField.tsx +1 -1
- package/src/components/fields/MarkdownField.tsx +1 -1
- package/src/components/fields/NumberField.tsx +13 -26
- package/src/components/fields/PortableTextField.tsx +4 -4
- package/src/components/fields/PortableTextRenderer.tsx +1 -1
- package/src/components/fields/RelationshipBlockField.tsx +31 -23
- package/src/components/fields/RelationshipField.tsx +14 -14
- package/src/components/fields/SelectField.tsx +17 -26
- package/src/components/fields/TabsLayout.tsx +69 -0
- package/src/components/fields/TextField.tsx +85 -38
- package/src/components/fields/UploadField.tsx +71 -41
- package/src/components/fields/VideoField.tsx +1 -1
- package/src/components/fields/extensions/blockComponents.tsx +2 -2
- package/src/components/fields/extensions/blocksStore.ts +207 -193
- package/src/components/fields/types.ts +22 -0
- package/src/components/layout/Layout.tsx +1 -1
- package/src/components/ui/ActionMenu.tsx +63 -0
- package/src/components/ui/Badge.tsx +59 -5
- package/src/components/ui/BlockDrawer.tsx +4 -5
- package/src/components/ui/CommandPalette.tsx +58 -36
- package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
- package/src/components/ui/Dropdown.tsx +18 -16
- package/src/components/ui/EmptyState.tsx +25 -0
- package/src/components/ui/GlobalModal.tsx +49 -0
- package/src/components/ui/IconButton.tsx +44 -0
- package/src/components/ui/Modal.tsx +19 -20
- package/src/components/ui/PageHeader.tsx +158 -0
- package/src/components/ui/Pagination.tsx +61 -0
- package/src/components/ui/PromptModal.tsx +1 -1
- package/src/components/ui/SearchInput.tsx +57 -0
- package/src/components/ui/SeoPreview.tsx +31 -0
- package/src/components/ui/SessionModal.tsx +0 -0
- package/src/components/ui/SlidePanel.tsx +2 -0
- package/src/components/ui/Toast.tsx +65 -122
- package/src/components/ui/Toaster.tsx +18 -0
- package/src/components/ui/icons.tsx +112 -0
- package/src/components/users/UserDetail.tsx +290 -0
- package/src/components/users/UserForm.tsx +242 -0
- package/src/components/users/UsersList.tsx +338 -0
- package/src/env.d.ts +13 -13
- package/src/fields/index.ts +2 -1
- package/src/global.d.ts +7 -0
- package/src/hooks/data.ts +2 -9
- package/src/hooks/useAsyncData.ts +36 -0
- package/src/hooks/useAutoFormState.ts +527 -0
- package/src/hooks/useSelection.ts +49 -0
- package/src/hooks/useSession.ts +0 -0
- package/src/index.ts +11 -1
- package/src/integration.ts +86 -11
- package/src/kyro-cms.d.ts +209 -0
- package/src/layouts/AdminLayout.astro +128 -11
- package/src/layouts/AuthLayout.astro +21 -5
- package/src/lib/api.ts +175 -55
- package/src/lib/autoform-store.ts +435 -0
- package/src/lib/config.ts +82 -34
- package/src/lib/createRegistry.ts +29 -0
- package/src/lib/default-kyro-config.ts +4 -0
- package/src/lib/globals.ts +50 -0
- package/src/lib/media-utils.ts +18 -0
- package/src/lib/object-utils.ts +77 -0
- package/src/lib/paths.ts +61 -0
- package/src/lib/stores/index.ts +370 -0
- package/src/lib/types.ts +43 -0
- package/src/lib/useResourceManager.ts +105 -0
- package/src/pages/403.astro +67 -0
- package/src/pages/[collection]/[id].astro +14 -180
- package/src/pages/[collection]/index.astro +11 -6
- package/src/pages/api-explorer.astro +173 -0
- package/src/pages/audit/index.astro +2 -0
- package/src/pages/auth/login.astro +122 -0
- package/src/pages/auth/register.astro +167 -0
- package/src/pages/graphql-explorer.astro +59 -0
- package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
- package/src/pages/index.astro +577 -0
- package/src/pages/index_ALT.astro +3 -0
- package/src/pages/keys.astro +11 -0
- package/src/pages/marketplace.astro +11 -0
- package/src/pages/media.astro +3 -0
- package/src/pages/plugins.astro +8 -0
- package/src/pages/preview/[collection]/[id].astro +188 -123
- package/src/pages/rest-playground.astro +62 -0
- package/src/pages/roles/index.astro +183 -76
- package/src/pages/sessions.astro +8 -0
- package/src/pages/settings/[slug].astro +92 -114
- package/src/pages/settings/index.astro +5 -3
- package/src/pages/users/[id].astro +25 -154
- package/src/pages/users/index.astro +19 -130
- package/src/pages/users/new.astro +9 -86
- package/src/pages/webhooks.astro +11 -0
- package/src/routes.ts +80 -0
- package/src/styles/main.css +119 -79
- package/src/theme/tokens.ts +1 -0
- package/src/vite-env.d.ts +14 -0
- package/src/collections/auth/index.ts +0 -155
- package/src/collections/portfolio/index.ts +0 -343
- package/src/components/ApiExplorer.tsx +0 -325
- package/src/components/EnhancedListView.tsx +0 -889
- package/src/components/GraphQLExplorer.tsx +0 -675
- package/src/components/Icons.tsx +0 -23
- package/src/components/StatusBadge.tsx +0 -76
- package/src/lib/MediaService.ts +0 -541
- package/src/lib/auth/sqlite-adapter.ts +0 -319
- package/src/lib/dataStore.ts +0 -226
- package/src/lib/db/adapter.ts +0 -54
- package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
- package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
- package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
- package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
- package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
- package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
- package/src/lib/db/index.ts +0 -449
- package/src/lib/db/mongodb-adapter.ts +0 -207
- package/src/lib/db/mongodb-auth-adapter.ts +0 -305
- package/src/lib/db/schema/mysql-auth.ts +0 -113
- package/src/lib/db/schema/mysql-content.ts +0 -20
- package/src/lib/db/schema/postgres-auth.ts +0 -116
- package/src/lib/db/schema/postgres-content.ts +0 -35
- package/src/lib/db/schema/postgres-media.ts +0 -52
- package/src/lib/db/schema/postgres-settings.ts +0 -11
- package/src/lib/db/schema/sqlite-auth.ts +0 -112
- package/src/lib/db/schema/sqlite-content.ts +0 -20
- package/src/lib/db/version-adapter.ts +0 -248
- package/src/lib/graphql/index.ts +0 -1
- package/src/lib/graphql/schema.ts +0 -443
- package/src/lib/rate-limit.ts +0 -267
- package/src/lib/storage.ts +0 -374
- package/src/lib/store.ts +0 -85
- package/src/middleware.ts +0 -177
- package/src/pages/admin/api-explorer.astro +0 -98
- package/src/pages/admin/graphql-explorer.astro +0 -40
- package/src/pages/admin/index.astro +0 -286
- package/src/pages/admin/keys.astro +0 -8
- package/src/pages/admin/rest-playground.astro +0 -44
- package/src/pages/admin/webhooks.astro +0 -8
- package/src/pages/api/[collection]/[id]/publish.ts +0 -52
- package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
- package/src/pages/api/[collection]/[id]/versions.ts +0 -66
- package/src/pages/api/[collection]/[id].ts +0 -213
- package/src/pages/api/[collection]/index.ts +0 -209
- package/src/pages/api/auth/[id].ts +0 -121
- package/src/pages/api/auth/audit-logs.ts +0 -57
- package/src/pages/api/auth/login.ts +0 -211
- package/src/pages/api/auth/logout.ts +0 -66
- package/src/pages/api/auth/me.ts +0 -36
- package/src/pages/api/auth/refresh.ts +0 -119
- package/src/pages/api/auth/register.ts +0 -188
- package/src/pages/api/auth/users.ts +0 -97
- package/src/pages/api/collections.ts +0 -59
- package/src/pages/api/globals/[slug].ts +0 -42
- package/src/pages/api/graphql.ts +0 -90
- package/src/pages/api/health.ts +0 -426
- package/src/pages/api/keys/[id].ts +0 -26
- package/src/pages/api/keys/index.ts +0 -75
- package/src/pages/api/media/[id].ts +0 -309
- package/src/pages/api/media/folders.ts +0 -609
- package/src/pages/api/media/index.ts +0 -146
- package/src/pages/api/media/resize.ts +0 -267
- package/src/pages/api/search.ts +0 -82
- package/src/pages/api/slug-availability.ts +0 -70
- package/src/pages/api/storage-config.ts +0 -20
- package/src/pages/api/storage-status.ts +0 -206
- package/src/pages/api/upload.ts +0 -334
- package/src/pages/api/webhooks/index.ts +0 -71
- package/src/pages/login.astro +0 -82
- package/src/pages/register.astro +0 -102
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useStore } from "zustand";
|
|
3
|
+
import { BlocksContext, createBlocksStore, createNewBlock, type BlocksStoreApi } from "./extensions/blocksStore";
|
|
3
4
|
import { BlockDrawer, DraggableBlockType } from "../ui/BlockDrawer";
|
|
4
|
-
import { Plus, Box } from "
|
|
5
|
+
import { Plus, Box } from "../ui/icons";
|
|
5
6
|
import {
|
|
6
7
|
BLOCK_COMPONENTS,
|
|
7
8
|
getBlockComponent,
|
|
@@ -27,9 +28,9 @@ import {
|
|
|
27
28
|
import { CSS } from "@dnd-kit/utilities";
|
|
28
29
|
|
|
29
30
|
interface BlocksFieldProps {
|
|
30
|
-
field:
|
|
31
|
-
value:
|
|
32
|
-
onChange?: (value:
|
|
31
|
+
field: Record<string, unknown>;
|
|
32
|
+
value: unknown[];
|
|
33
|
+
onChange?: (value: unknown[]) => void;
|
|
33
34
|
onBlocksChange?: () => void;
|
|
34
35
|
error?: string;
|
|
35
36
|
disabled?: boolean;
|
|
@@ -37,14 +38,14 @@ interface BlocksFieldProps {
|
|
|
37
38
|
justSaved?: boolean;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
import { GripVertical } from "
|
|
41
|
+
import { GripVertical } from "../ui/icons";
|
|
41
42
|
|
|
42
43
|
// Sortable block wrapper for drag-and-drop
|
|
43
44
|
const SortableBlockComponent = ({
|
|
44
45
|
block,
|
|
45
46
|
index,
|
|
46
47
|
}: {
|
|
47
|
-
block:
|
|
48
|
+
block: Record<string, unknown>;
|
|
48
49
|
index: number;
|
|
49
50
|
}) => {
|
|
50
51
|
const {
|
|
@@ -98,44 +99,53 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
|
|
|
98
99
|
justSaved,
|
|
99
100
|
}) => {
|
|
100
101
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
101
|
-
const
|
|
102
|
+
const storeRef = useRef<BlocksStoreApi | null>(null);
|
|
103
|
+
if (!storeRef.current) {
|
|
104
|
+
storeRef.current = createBlocksStore();
|
|
105
|
+
}
|
|
106
|
+
const store = storeRef.current;
|
|
107
|
+
|
|
108
|
+
const blocks = useStore(store, (s) => s.blocks);
|
|
102
109
|
const [activeDrag, setActiveDrag] = useState<Active | null>(null);
|
|
103
110
|
|
|
104
111
|
// Register blocks change callback
|
|
105
112
|
useEffect(() => {
|
|
106
113
|
if (onBlocksChange) {
|
|
107
|
-
setOnBlocksChange(onBlocksChange);
|
|
114
|
+
store.getState().setOnBlocksChange(onBlocksChange);
|
|
108
115
|
}
|
|
109
116
|
return () => {
|
|
110
|
-
setOnBlocksChange(() => {});
|
|
117
|
+
store.getState().setOnBlocksChange(() => { });
|
|
111
118
|
};
|
|
112
|
-
}, [onBlocksChange,
|
|
119
|
+
}, [onBlocksChange, store]);
|
|
113
120
|
|
|
114
121
|
// Sync external value changes (e.g., auto-save restore) to store
|
|
122
|
+
// Track last-synced value so we don't revert our own internal mutations
|
|
123
|
+
const lastValueRef = useRef<unknown[] | null>(null);
|
|
115
124
|
useEffect(() => {
|
|
116
|
-
const
|
|
117
|
-
const
|
|
125
|
+
const valueArray = Array.isArray(value) ? value : [];
|
|
126
|
+
const lastValueArray = lastValueRef.current || [];
|
|
127
|
+
const valueIds = valueArray.map((b: Record<string, unknown>) => b.id).join(",");
|
|
128
|
+
const lastValueIds = lastValueArray.map((b: Record<string, unknown>) => b.id).join(",");
|
|
118
129
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
} else if (!value || (Array.isArray(value) && value.length === 0)) {
|
|
124
|
-
setBlocks([]);
|
|
125
|
-
}
|
|
130
|
+
if (valueIds !== lastValueIds) {
|
|
131
|
+
console.log("BlocksField sync: value=", value, "valueIds=", valueIds, "lastValueIds=", lastValueIds);
|
|
132
|
+
store.getState().setBlocks(valueArray);
|
|
133
|
+
lastValueRef.current = [...valueArray];
|
|
126
134
|
}
|
|
127
|
-
}, [value]);
|
|
135
|
+
}, [value, field.name, store]);
|
|
128
136
|
|
|
129
137
|
// Debounced sync of store changes back to parent form to reduce re-renders
|
|
130
138
|
const onChangeTimer = useRef<number | null>(null);
|
|
139
|
+
const onChangeRef = useRef(onChange);
|
|
140
|
+
onChangeRef.current = onChange;
|
|
131
141
|
useEffect(() => {
|
|
132
|
-
if (!
|
|
142
|
+
if (!onChangeRef.current) return;
|
|
133
143
|
if (onChangeTimer.current) {
|
|
134
144
|
window.clearTimeout(onChangeTimer.current);
|
|
135
145
|
onChangeTimer.current = null;
|
|
136
146
|
}
|
|
137
147
|
onChangeTimer.current = window.setTimeout(() => {
|
|
138
|
-
|
|
148
|
+
onChangeRef.current?.(blocks);
|
|
139
149
|
}, 250);
|
|
140
150
|
return () => {
|
|
141
151
|
if (onChangeTimer.current) {
|
|
@@ -143,7 +153,7 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
|
|
|
143
153
|
onChangeTimer.current = null;
|
|
144
154
|
}
|
|
145
155
|
};
|
|
146
|
-
}, [blocks
|
|
156
|
+
}, [blocks]);
|
|
147
157
|
|
|
148
158
|
// Determine left border style based on document status
|
|
149
159
|
const getBorderClass = () => {
|
|
@@ -162,9 +172,9 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
|
|
|
162
172
|
|
|
163
173
|
const handleAddBlock = useCallback(
|
|
164
174
|
(blockType: string) => {
|
|
165
|
-
addBlock(blockType);
|
|
175
|
+
store.getState().addBlock(blockType);
|
|
166
176
|
},
|
|
167
|
-
[
|
|
177
|
+
[store],
|
|
168
178
|
);
|
|
169
179
|
|
|
170
180
|
// Set up dnd-kit sensors
|
|
@@ -196,15 +206,14 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
|
|
|
196
206
|
const containerId = over.id.toString().replace("container-", "");
|
|
197
207
|
const container = blocks.find((b) => b.id === containerId);
|
|
198
208
|
if (container) {
|
|
199
|
-
const { updateBlock } = useBlocksStore.getState();
|
|
200
209
|
const newBlock = createNewBlock(blockType);
|
|
201
|
-
updateBlock(containerId, {
|
|
210
|
+
store.getState().updateBlock(containerId, {
|
|
202
211
|
children: [...(container.children || []), newBlock],
|
|
203
212
|
});
|
|
204
213
|
}
|
|
205
214
|
} else {
|
|
206
215
|
// Dropped on root level - add as top-level block
|
|
207
|
-
|
|
216
|
+
handleAddBlock(blockType);
|
|
208
217
|
}
|
|
209
218
|
return;
|
|
210
219
|
}
|
|
@@ -218,7 +227,7 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
|
|
|
218
227
|
const newBlocks = [...blocks];
|
|
219
228
|
const [movedBlock] = newBlocks.splice(oldIndex, 1);
|
|
220
229
|
newBlocks.splice(newIndex, 0, movedBlock);
|
|
221
|
-
setBlocks(newBlocks);
|
|
230
|
+
store.getState().setBlocks(newBlocks);
|
|
222
231
|
}
|
|
223
232
|
}
|
|
224
233
|
};
|
|
@@ -226,98 +235,100 @@ export const BlocksField: React.FC<BlocksFieldProps> = ({
|
|
|
226
235
|
// Render active drag overlay
|
|
227
236
|
const activeBlock = activeDrag
|
|
228
237
|
? blockCategories
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
.flatMap((cat) => cat.blocks)
|
|
239
|
+
.find((b) => `drawer-${b.type}` === activeDrag.id) ||
|
|
240
|
+
blocks.find((b) => b.id === activeDrag.id)
|
|
232
241
|
: null;
|
|
233
242
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
const activeBlockLabel = activeBlock
|
|
244
|
+
? "label" in activeBlock
|
|
245
|
+
? (activeBlock as Record<string, unknown>).label
|
|
246
|
+
: activeBlock.type
|
|
247
|
+
: "Block";
|
|
239
248
|
|
|
240
249
|
const borderClass = getBorderClass();
|
|
241
250
|
|
|
242
251
|
return (
|
|
243
|
-
<
|
|
244
|
-
<
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
252
|
+
<BlocksContext.Provider value={store}>
|
|
253
|
+
<div className={`kyro-form-field ${borderClass}`}>
|
|
254
|
+
<DndContext
|
|
255
|
+
sensors={sensors}
|
|
256
|
+
collisionDetection={closestCenter}
|
|
257
|
+
onDragStart={handleDragStart}
|
|
258
|
+
onDragEnd={handleDragEnd}
|
|
259
|
+
>
|
|
260
|
+
{/* Block Builder Toolbar */}
|
|
261
|
+
<div className="flex items-center justify-between mb-2">
|
|
262
|
+
<label className="kyro-form-label">{field.label || field.name}</label>
|
|
263
|
+
<button
|
|
264
|
+
type="button"
|
|
265
|
+
onClick={() => setIsDrawerOpen(true)}
|
|
266
|
+
disabled={disabled}
|
|
267
|
+
className="flex items-center gap-2 px-3 py-2 text-sm text-[var(--kyro-primary)] hover:bg-[var(--kyro-surface-accent)]/30 rounded-md transition-colors disabled:opacity-50 font-semibold"
|
|
268
|
+
>
|
|
269
|
+
<Plus className="w-4 h-4" />
|
|
270
|
+
Add Block
|
|
271
|
+
</button>
|
|
272
|
+
<BlockDrawer
|
|
273
|
+
open={isDrawerOpen}
|
|
274
|
+
onClose={() => setIsDrawerOpen(false)}
|
|
275
|
+
onSelect={handleAddBlock}
|
|
276
|
+
>
|
|
277
|
+
<div className="space-y-4">
|
|
278
|
+
{blockCategories.map((category) => (
|
|
279
|
+
<div key={category.title}>
|
|
280
|
+
<h3 className="text-xs font-semibold text-[var(--kyro-text-muted)] mb-2 tracking-wider">
|
|
281
|
+
{category.title}
|
|
282
|
+
</h3>
|
|
283
|
+
<div className="grid grid-cols-3 gap-2">
|
|
284
|
+
{category.blocks.map((block) => (
|
|
285
|
+
<DraggableBlockType
|
|
286
|
+
key={block.type}
|
|
287
|
+
block={block}
|
|
288
|
+
onSelect={handleAddBlock}
|
|
289
|
+
>
|
|
290
|
+
<div className="w-6 h-6 flex items-center justify-center rounded group-hover:bg-[var(--kyro-primary)]/10 group-hover:text-[var(--kyro-primary)] transition-all duration-300">
|
|
291
|
+
<span className="text-[var(--kyro-text-muted)]">
|
|
292
|
+
{blockIcons[
|
|
293
|
+
block.icon as keyof typeof blockIcons
|
|
294
|
+
] || <Box className="w-4 h-4" />}
|
|
295
|
+
</span>
|
|
296
|
+
</div>
|
|
297
|
+
</DraggableBlockType>
|
|
298
|
+
))}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
))}
|
|
302
|
+
</div>
|
|
303
|
+
</BlockDrawer>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
{/* Block List with Drag-and-Drop */}
|
|
307
|
+
<SortableContext
|
|
308
|
+
items={blocks.map((b) => b.id)}
|
|
309
|
+
strategy={verticalListSortingStrategy}
|
|
266
310
|
>
|
|
267
311
|
<div className="space-y-4">
|
|
268
|
-
{
|
|
269
|
-
<
|
|
270
|
-
<h3 className="text-xs font-semibold text-[var(--kyro-text-muted)] mb-2 uppercase tracking-wider">
|
|
271
|
-
{category.title}
|
|
272
|
-
</h3>
|
|
273
|
-
<div className="grid grid-cols-3 gap-2">
|
|
274
|
-
{category.blocks.map((block) => (
|
|
275
|
-
<DraggableBlockType
|
|
276
|
-
key={block.type}
|
|
277
|
-
block={block}
|
|
278
|
-
onSelect={handleAddBlock}
|
|
279
|
-
>
|
|
280
|
-
<div className="w-6 h-6 flex items-center justify-center rounded group-hover:bg-[var(--kyro-primary)]/10 group-hover:text-[var(--kyro-primary)] transition-all duration-300">
|
|
281
|
-
<span className="text-[var(--kyro-text-muted)]">
|
|
282
|
-
{blockIcons[
|
|
283
|
-
block.icon as keyof typeof blockIcons
|
|
284
|
-
] || <Box className="w-4 h-4" />}
|
|
285
|
-
</span>
|
|
286
|
-
</div>
|
|
287
|
-
</DraggableBlockType>
|
|
288
|
-
))}
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
312
|
+
{blocks.map((block, index) => (
|
|
313
|
+
<SortableBlock key={block.id || index} block={block} index={index} />
|
|
291
314
|
))}
|
|
315
|
+
{blocks.length === 0 && (
|
|
316
|
+
<div className="text-center py-12 text-[var(--kyro-text-muted)] border-2 border-dashed border-[var(--kyro-border)] rounded-lg">
|
|
317
|
+
Click the button above to add your first block
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
292
320
|
</div>
|
|
293
|
-
</
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
items={blocks.map((b) => b.id)}
|
|
299
|
-
strategy={verticalListSortingStrategy}
|
|
300
|
-
>
|
|
301
|
-
<div className="space-y-4">
|
|
302
|
-
{blocks.map((block, index) => (
|
|
303
|
-
<SortableBlock key={block.id} block={block} index={index} />
|
|
304
|
-
))}
|
|
305
|
-
{blocks.length === 0 && (
|
|
306
|
-
<div className="text-center py-12 text-[var(--kyro-text-muted)] border-2 border-dashed border-[var(--kyro-border)] rounded-lg">
|
|
307
|
-
Click the button above to add your first block
|
|
321
|
+
</SortableContext>
|
|
322
|
+
<DragOverlay>
|
|
323
|
+
{activeDrag && activeBlock && (
|
|
324
|
+
<div className="bg-[var(--kyro-surface)] border border-[var(--kyro-primary)] rounded-md p-3 shadow-lg">
|
|
325
|
+
{(activeBlock as Record<string, unknown>).label || activeBlock.type || "Block"}
|
|
308
326
|
</div>
|
|
309
327
|
)}
|
|
310
|
-
</
|
|
311
|
-
</
|
|
312
|
-
<
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
{(activeBlock as any).label || activeBlock.type || "Block"}
|
|
316
|
-
</div>
|
|
317
|
-
)}
|
|
318
|
-
</DragOverlay>
|
|
319
|
-
</DndContext>
|
|
320
|
-
{error && <p className="kyro-form-error">{error}</p>}
|
|
321
|
-
</div>
|
|
328
|
+
</DragOverlay>
|
|
329
|
+
</DndContext>
|
|
330
|
+
{error && <p className="kyro-form-error">{error}</p>}
|
|
331
|
+
</div>
|
|
332
|
+
</BlocksContext.Provider>
|
|
322
333
|
);
|
|
323
334
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CheckboxField as CheckboxFieldType } from "@kyro-cms/core/client";
|
|
2
|
+
import FieldLayout from "./FieldLayout";
|
|
2
3
|
|
|
3
4
|
interface CheckboxFieldComponentProps {
|
|
4
5
|
field: CheckboxFieldType;
|
|
@@ -15,33 +16,31 @@ export default function CheckboxField({
|
|
|
15
16
|
error,
|
|
16
17
|
disabled,
|
|
17
18
|
}: CheckboxFieldComponentProps) {
|
|
19
|
+
const isReadOnly = field.admin?.readOnly;
|
|
20
|
+
|
|
18
21
|
return (
|
|
19
|
-
<
|
|
20
|
-
|
|
22
|
+
<FieldLayout
|
|
23
|
+
field={field}
|
|
24
|
+
error={error}
|
|
25
|
+
hideLabel={true}
|
|
26
|
+
>
|
|
27
|
+
<label className="flex items-center gap-2.5 cursor-pointer group py-0.5">
|
|
21
28
|
<input
|
|
22
29
|
type="checkbox"
|
|
23
30
|
checked={value}
|
|
24
31
|
onChange={(e) => onChange?.(e.target.checked)}
|
|
25
|
-
disabled={disabled ||
|
|
26
|
-
className={`w-4 h-4 rounded border-[var(--kyro-border)] text-[var(--kyro-
|
|
27
|
-
disabled ||
|
|
32
|
+
disabled={disabled || isReadOnly}
|
|
33
|
+
className={`w-4 h-4 rounded border-[var(--kyro-border)] text-[var(--kyro-primary)] focus:ring-[var(--kyro-primary)] transition-all ${
|
|
34
|
+
disabled || isReadOnly ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
|
|
28
35
|
}`}
|
|
29
36
|
/>
|
|
30
|
-
<span className="text-sm font-
|
|
37
|
+
<span className="text-sm font-semibold text-[var(--kyro-text-primary)] tracking-tight group-hover:text-[var(--kyro-primary)] transition-colors">
|
|
31
38
|
{field.label || field.name}
|
|
32
39
|
{field.required && (
|
|
33
40
|
<span className="text-[var(--kyro-error)] ml-1">*</span>
|
|
34
41
|
)}
|
|
35
42
|
</span>
|
|
36
43
|
</label>
|
|
37
|
-
|
|
38
|
-
<p className="text-xs text-[var(--kyro-text-secondary)] ml-6">
|
|
39
|
-
{field.admin.description}
|
|
40
|
-
</p>
|
|
41
|
-
)}
|
|
42
|
-
{error && (
|
|
43
|
-
<p className="text-xs text-[var(--kyro-error)] ml-6">{error}</p>
|
|
44
|
-
)}
|
|
45
|
-
</div>
|
|
44
|
+
</FieldLayout>
|
|
46
45
|
);
|
|
47
46
|
}
|
|
@@ -3,8 +3,8 @@ import { ChildBlocksTree } from "../blocks/ChildBlocksTree";
|
|
|
3
3
|
|
|
4
4
|
interface ChildrenFieldProps {
|
|
5
5
|
blockId: string;
|
|
6
|
-
children:
|
|
7
|
-
onUpdateChildren: (newChildren:
|
|
6
|
+
children: Record<string, unknown>[];
|
|
7
|
+
onUpdateChildren: (newChildren: Record<string, unknown>[]) => void;
|
|
8
8
|
label?: string;
|
|
9
9
|
compact?: boolean;
|
|
10
10
|
}
|
|
@@ -30,7 +30,7 @@ const LANGUAGES = [
|
|
|
30
30
|
{ value: "markdown", label: "Markdown" },
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
-
const languageExtensions: Record<string, () => Promise<
|
|
33
|
+
const languageExtensions: Record<string, () => Promise<unknown>> = {
|
|
34
34
|
javascript: () =>
|
|
35
35
|
import("@codemirror/lang-javascript").then((m) =>
|
|
36
36
|
m.javascript({ jsx: true, typescript: true }),
|
|
@@ -74,7 +74,7 @@ export const CodeField: React.FC<CodeFieldProps> = ({
|
|
|
74
74
|
}) => {
|
|
75
75
|
const [isMounted, setIsMounted] = useState(false);
|
|
76
76
|
const [isDark, setIsDark] = useState(false);
|
|
77
|
-
const [extensions, setExtensions] = useState<
|
|
77
|
+
const [extensions, setExtensions] = useState<unknown[]>([]);
|
|
78
78
|
const [loading, setLoading] = useState(false);
|
|
79
79
|
const [copied, setCopied] = useState(false);
|
|
80
80
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
@@ -243,7 +243,7 @@ export const CodeField: React.FC<CodeFieldProps> = ({
|
|
|
243
243
|
}
|
|
244
244
|
>
|
|
245
245
|
<CodeMirrorEditor
|
|
246
|
-
value={value}
|
|
246
|
+
value={value == null ? "" : value}
|
|
247
247
|
height={isFullScreen ? "calc(100vh - 100px)" : "280px"}
|
|
248
248
|
width="100%"
|
|
249
249
|
extensions={extensions}
|
|
@@ -3,14 +3,14 @@ import { ChildBlocksTree } from "../blocks/ChildBlocksTree";
|
|
|
3
3
|
|
|
4
4
|
interface ColumnData {
|
|
5
5
|
id: number;
|
|
6
|
-
children:
|
|
6
|
+
children: Record<string, unknown>[];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
interface ColumnsFieldProps {
|
|
10
10
|
columns?: number;
|
|
11
11
|
columnData?: ColumnData[];
|
|
12
12
|
onColumnsChange: (columns: number) => void;
|
|
13
|
-
onUpdateColumnChildren: (columnIndex: number, newChildren:
|
|
13
|
+
onUpdateColumnChildren: (columnIndex: number, newChildren: Record<string, unknown>[]) => void;
|
|
14
14
|
compact?: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DateField as DateFieldType } from "@kyro-cms/core/client";
|
|
2
|
+
import FieldLayout from "./FieldLayout";
|
|
2
3
|
|
|
3
4
|
interface DateFieldComponentProps {
|
|
4
5
|
field: DateFieldType;
|
|
@@ -15,40 +16,26 @@ export default function DateField({
|
|
|
15
16
|
error,
|
|
16
17
|
disabled,
|
|
17
18
|
}: DateFieldComponentProps) {
|
|
19
|
+
const isReadOnly = field.admin?.readOnly;
|
|
20
|
+
|
|
18
21
|
return (
|
|
19
|
-
<
|
|
20
|
-
{field
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{field.required && (
|
|
24
|
-
<span className="text-[var(--kyro-error)] ml-1">*</span>
|
|
25
|
-
)}
|
|
26
|
-
</label>
|
|
27
|
-
)}
|
|
22
|
+
<FieldLayout
|
|
23
|
+
field={field}
|
|
24
|
+
error={error}
|
|
25
|
+
>
|
|
28
26
|
<input
|
|
29
27
|
type={field.time ? "datetime-local" : "date"}
|
|
30
|
-
|
|
28
|
+
id={field.name}
|
|
29
|
+
value={value == null ? "" : value}
|
|
31
30
|
onChange={(e) => onChange?.(e.target.value)}
|
|
32
|
-
disabled={disabled ||
|
|
31
|
+
disabled={disabled || isReadOnly}
|
|
33
32
|
min={field.minDate}
|
|
34
33
|
max={field.maxDate}
|
|
35
34
|
required={field.required}
|
|
36
|
-
className={`
|
|
37
|
-
|
|
38
|
-
? "border-[var(--kyro-error)] focus:border-[var(--kyro-error)] focus:ring-[var(--kyro-error)]"
|
|
39
|
-
: "border-[var(--kyro-border)] focus:border-[var(--kyro-primary)] focus:ring-[var(--kyro-primary)]"
|
|
40
|
-
} ${
|
|
41
|
-
disabled || field.admin?.readOnly
|
|
42
|
-
? "bg-[var(--kyro-bg-secondary)] text-[var(--kyro-text-secondary)] opacity-50"
|
|
43
|
-
: "bg-[var(--kyro-surface)] text-[var(--kyro-text-primary)]"
|
|
35
|
+
className={`kyro-form-input ${
|
|
36
|
+
disabled || isReadOnly ? "opacity-50 cursor-not-allowed" : ""
|
|
44
37
|
}`}
|
|
45
38
|
/>
|
|
46
|
-
|
|
47
|
-
<p className="text-xs text-[var(--kyro-text-muted)]">
|
|
48
|
-
{field.admin.description}
|
|
49
|
-
</p>
|
|
50
|
-
)}
|
|
51
|
-
{error && <p className="text-xs text-[var(--kyro-error)]">{error}</p>}
|
|
52
|
-
</div>
|
|
39
|
+
</FieldLayout>
|
|
53
40
|
);
|
|
54
41
|
}
|
|
@@ -40,24 +40,24 @@ import {
|
|
|
40
40
|
ChevronDown,
|
|
41
41
|
X,
|
|
42
42
|
ExternalLink,
|
|
43
|
-
} from "
|
|
43
|
+
} from "../ui/icons";
|
|
44
44
|
|
|
45
45
|
interface EditorClientProps {
|
|
46
|
-
initialValue:
|
|
47
|
-
onChange: (blocks:
|
|
46
|
+
initialValue: Record<string, unknown>[];
|
|
47
|
+
onChange: (blocks: Record<string, unknown>[]) => void;
|
|
48
48
|
disabled?: boolean;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function sanitizeInitialValue(value:
|
|
51
|
+
function sanitizeInitialValue(value: unknown): Record<string, unknown>[] {
|
|
52
52
|
if (!value || !Array.isArray(value)) return [];
|
|
53
|
-
return value.filter((block) => {
|
|
53
|
+
return (value as Record<string, unknown>[]).filter((block) => {
|
|
54
54
|
if (!block || typeof block !== "object") return false;
|
|
55
|
-
if (!block
|
|
56
|
-
if (block._type === "block" && Array.isArray(block.children)) {
|
|
57
|
-
block.children = block.children.map((child
|
|
55
|
+
if (!("_type" in block)) return false;
|
|
56
|
+
if ((block as { _type?: string })._type === "block" && Array.isArray((block as { children?: unknown[] }).children)) {
|
|
57
|
+
(block as { children: Record<string, unknown>[] }).children = (block.children as Record<string, unknown>[]).map((child) => ({
|
|
58
58
|
...child,
|
|
59
|
-
_type: child._type || "span",
|
|
60
|
-
text: typeof child.text === "string" ? child.text : "",
|
|
59
|
+
_type: (child as { _type?: string })._type || "span",
|
|
60
|
+
text: typeof (child as { text?: unknown }).text === "string" ? child.text : "",
|
|
61
61
|
}));
|
|
62
62
|
}
|
|
63
63
|
return true;
|
|
@@ -333,7 +333,7 @@ const LinkDialog: React.FC = () => {
|
|
|
333
333
|
if (href.trim()) {
|
|
334
334
|
popover.send({
|
|
335
335
|
type: "edit",
|
|
336
|
-
at: [] as
|
|
336
|
+
at: [] as string[],
|
|
337
337
|
props: { href: href.trim() },
|
|
338
338
|
});
|
|
339
339
|
}
|
|
@@ -414,7 +414,7 @@ const StyleSelector: React.FC = () => {
|
|
|
414
414
|
<select
|
|
415
415
|
value={activeStyle}
|
|
416
416
|
onChange={(e) => {
|
|
417
|
-
send({ type: "toggle", style: e.target.value as
|
|
417
|
+
send({ type: "toggle", style: e.target.value as string });
|
|
418
418
|
editor.send({ type: "focus" });
|
|
419
419
|
}}
|
|
420
420
|
className="appearance-none bg-transparent text-sm pr-6 pl-2 py-1 rounded hover:bg-[var(--kyro-surface-accent)] cursor-pointer focus:outline-none focus:ring-1 focus:ring-[var(--kyro-primary)]"
|
|
@@ -469,7 +469,7 @@ const Toolbar: React.FC = () => {
|
|
|
469
469
|
};
|
|
470
470
|
|
|
471
471
|
const EditorInner: React.FC<{
|
|
472
|
-
onChange: (blocks:
|
|
472
|
+
onChange: (blocks: Record<string, unknown>[]) => void;
|
|
473
473
|
disabled?: boolean;
|
|
474
474
|
}> = ({ onChange, disabled }) => {
|
|
475
475
|
return (
|
|
@@ -506,32 +506,30 @@ export const EditorClient: React.FC<EditorClientProps> = ({
|
|
|
506
506
|
}
|
|
507
507
|
}, [initialValue]);
|
|
508
508
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
509
|
+
const handleChange = useCallback(
|
|
510
|
+
(newValue: Record<string, unknown>[]) => {
|
|
511
|
+
setValue(newValue);
|
|
512
|
+
onChange(newValue);
|
|
513
|
+
},
|
|
514
|
+
[onChange],
|
|
515
|
+
);
|
|
516
516
|
|
|
517
517
|
return (
|
|
518
518
|
<EditorProvider
|
|
519
519
|
key={JSON.stringify(value)}
|
|
520
520
|
initialConfig={{
|
|
521
|
-
schemaDefinition: schemaDefinition as
|
|
521
|
+
schemaDefinition: schemaDefinition as Record<string, unknown>,
|
|
522
522
|
initialValue: value,
|
|
523
523
|
}}
|
|
524
524
|
>
|
|
525
525
|
<EventListenerPlugin
|
|
526
|
-
on={(event:
|
|
526
|
+
on={(event: Record<string, unknown>) => {
|
|
527
527
|
if (event.type === "mutation" && event.value) {
|
|
528
|
-
handleChange(event.value);
|
|
528
|
+
handleChange(event.value as Record<string, unknown>);
|
|
529
529
|
}
|
|
530
530
|
}}
|
|
531
531
|
/>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
532
|
+
<EditorInner onChange={handleChange} disabled={disabled} />
|
|
533
|
+
</EditorProvider>
|
|
534
|
+
);
|
|
535
535
|
};
|
|
536
|
-
|
|
537
|
-
export default EditorClient;
|