@blocklet/pages-kit-block-studio 0.5.41 → 0.5.42
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/lib/cjs/middlewares/init-block-studio-router.js +120 -0
- package/lib/cjs/middlewares/init-resource-router.js +54 -5
- package/lib/cjs/plugins/_theme.js +347 -54
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/middlewares/init-block-studio-router.js +120 -0
- package/lib/esm/middlewares/init-resource-router.js +55 -6
- package/lib/esm/plugins/_theme.js +348 -55
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -4,9 +4,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// NOTICE: This file is very important, please do not change it!
|
|
5
5
|
// import BlockStudio from '@blocklet/pages-kit-block-studio/frontend';
|
|
6
6
|
// export default BlockStudio;
|
|
7
|
+
// add tailwindcss css to the head
|
|
7
8
|
import { createAuthServiceSessionContext } from '@arcblock/did-connect/lib/Session';
|
|
8
9
|
import { useConfig } from '@arcblock/ux/lib/Config';
|
|
9
|
-
import Empty from '@arcblock/ux/lib/Empty';
|
|
10
10
|
import { LocaleProvider, useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
11
11
|
import { ThemeProvider } from '@arcblock/ux/lib/Theme';
|
|
12
12
|
import Toast, { ToastProvider } from '@arcblock/ux/lib/Toast';
|
|
@@ -24,16 +24,21 @@ import { parsePropertyValue } from '@blocklet/pages-kit/utils/property';
|
|
|
24
24
|
import { Dashboard } from '@blocklet/studio-ui';
|
|
25
25
|
import { BlockletStudio } from '@blocklet/ui-react';
|
|
26
26
|
import AddIcon from '@mui/icons-material/Add';
|
|
27
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
28
|
+
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
|
|
27
29
|
import LaptopMacIcon from '@mui/icons-material/LaptopMac';
|
|
28
30
|
import PhoneAndroidIcon from '@mui/icons-material/PhoneAndroid';
|
|
29
|
-
import
|
|
31
|
+
import SettingsIcon from '@mui/icons-material/Settings';
|
|
32
|
+
import SortByAlphaIcon from '@mui/icons-material/SortByAlpha';
|
|
33
|
+
import { Alert, Box, Button, CircularProgress, Dialog, DialogContent, DialogTitle, IconButton, List, ListItem, ListItemButton, Skeleton, Stack, StyledEngineProvider, TextField, ToggleButton, ToggleButtonGroup, Tooltip, Typography, backdropClasses, circularProgressClasses, styled, } from '@mui/material';
|
|
30
34
|
import { useDebounceFn, useLocalStorageState, useReactive } from 'ahooks';
|
|
31
35
|
import { useUpdateEffect } from 'ahooks';
|
|
32
36
|
import cloneDeep from 'lodash/cloneDeep';
|
|
33
37
|
import get from 'lodash/get';
|
|
34
38
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
35
|
-
import {
|
|
39
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
36
40
|
import { DndProvider } from 'react-dnd';
|
|
41
|
+
import { useDrag, useDrop } from 'react-dnd';
|
|
37
42
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
38
43
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
39
44
|
import SplitPane, { Pane } from 'split-pane-react';
|
|
@@ -57,6 +62,136 @@ function useSessionContext() {
|
|
|
57
62
|
const LEFT_DRAWER_WIDTH = 300;
|
|
58
63
|
const RIGHT_DRAWER_WIDTH = 300;
|
|
59
64
|
const defaultLocale = 'en';
|
|
65
|
+
// Drag and drop types
|
|
66
|
+
const ItemTypes = {
|
|
67
|
+
ROUTE_ITEM: 'route-item',
|
|
68
|
+
};
|
|
69
|
+
const DraggableRouteItem = ({ route, staticDataInRoute, currentPageId, onNavigate, onDelete, index, moveItem, showDragHandle, routes, }) => {
|
|
70
|
+
const ref = React.useRef(null);
|
|
71
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
72
|
+
const [{ handlerId }, drop] = useDrop({
|
|
73
|
+
accept: ItemTypes.ROUTE_ITEM,
|
|
74
|
+
collect(monitor) {
|
|
75
|
+
return {
|
|
76
|
+
handlerId: monitor.getHandlerId(),
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
hover(item, monitor) {
|
|
80
|
+
if (!ref.current || !showDragHandle) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Get the real dragIndex from routes array using route as identifier
|
|
84
|
+
// This prevents issues when dragging too fast and item.index becomes stale
|
|
85
|
+
const dragIndex = routes.findIndex((r) => r === item.route);
|
|
86
|
+
const hoverIndex = index;
|
|
87
|
+
// Don't replace items with themselves
|
|
88
|
+
if (dragIndex === hoverIndex || dragIndex === -1) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Determine rectangle on screen
|
|
92
|
+
const hoverBoundingRect = ref.current?.getBoundingClientRect();
|
|
93
|
+
// Get vertical middle
|
|
94
|
+
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
95
|
+
// Determine mouse position
|
|
96
|
+
const clientOffset = monitor.getClientOffset();
|
|
97
|
+
// Get pixels to the top
|
|
98
|
+
const hoverClientY = (clientOffset?.y ?? 0) - hoverBoundingRect.top;
|
|
99
|
+
// Only perform the move when the mouse has crossed half of the items height
|
|
100
|
+
// When dragging downwards, only move when the cursor is below 50%
|
|
101
|
+
// When dragging upwards, only move when the cursor is above 50%
|
|
102
|
+
// Dragging downwards
|
|
103
|
+
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Dragging upwards
|
|
107
|
+
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Time to actually perform the action
|
|
111
|
+
moveItem(dragIndex, hoverIndex);
|
|
112
|
+
// Note: we're mutating the monitor item here!
|
|
113
|
+
// Generally it's better to avoid mutations,
|
|
114
|
+
// but it's good here for the sake of performance
|
|
115
|
+
// to avoid expensive index searches.
|
|
116
|
+
item.index = hoverIndex;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
const [{ isDragging }, drag, preview] = useDrag({
|
|
120
|
+
type: ItemTypes.ROUTE_ITEM,
|
|
121
|
+
item: () => {
|
|
122
|
+
return { route, index };
|
|
123
|
+
},
|
|
124
|
+
collect: (monitor) => ({
|
|
125
|
+
isDragging: monitor.isDragging(),
|
|
126
|
+
}),
|
|
127
|
+
canDrag: showDragHandle,
|
|
128
|
+
});
|
|
129
|
+
const opacity = isDragging ? 0.5 : 1;
|
|
130
|
+
// Separate drag handle from the clickable area
|
|
131
|
+
const dragHandleRef = React.useRef(null);
|
|
132
|
+
// Only attach drag to the handle when in custom sort mode
|
|
133
|
+
React.useEffect(() => {
|
|
134
|
+
if (showDragHandle && dragHandleRef.current) {
|
|
135
|
+
drag(dragHandleRef.current);
|
|
136
|
+
}
|
|
137
|
+
}, [drag, showDragHandle]);
|
|
138
|
+
drop(ref);
|
|
139
|
+
return (_jsx(ListItem, { ref: ref, disablePadding: true, className: isDragging ? 'drag-shaking' : '', sx: {
|
|
140
|
+
opacity,
|
|
141
|
+
transition: isDragging ? 'opacity 0.2s ease' : 'opacity 0.2s ease, transform 0.2s ease',
|
|
142
|
+
zIndex: isDragging ? 1000 : 'auto',
|
|
143
|
+
}, "data-handler-id": handlerId, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: _jsxs(ListItemButton, { selected: currentPageId === route, onClick: () => onNavigate(route), sx: {
|
|
144
|
+
borderRadius: 1,
|
|
145
|
+
width: '100%',
|
|
146
|
+
textOverflow: 'ellipsis',
|
|
147
|
+
whiteSpace: 'nowrap',
|
|
148
|
+
overflowX: 'hidden',
|
|
149
|
+
transition: 'all 0.3s ease',
|
|
150
|
+
'&.Mui-selected': {
|
|
151
|
+
backgroundColor: 'primary.main',
|
|
152
|
+
color: 'white',
|
|
153
|
+
'&:hover': {
|
|
154
|
+
backgroundColor: 'primary.main',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
fontSize: '14px',
|
|
158
|
+
display: 'flex',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
gap: 1,
|
|
161
|
+
border: (theme) => (isDragging ? `1px solid ${theme.palette.primary.main}` : '1px solid transparent'),
|
|
162
|
+
backgroundColor: isDragging ? 'grey.100' : 'inherit',
|
|
163
|
+
pl: 1,
|
|
164
|
+
pr: 1,
|
|
165
|
+
}, children: [showDragHandle && (_jsx(Box, { ref: dragHandleRef, sx: {
|
|
166
|
+
display: 'flex',
|
|
167
|
+
alignItems: 'center',
|
|
168
|
+
justifyContent: 'center',
|
|
169
|
+
cursor: 'grab',
|
|
170
|
+
borderRadius: 1,
|
|
171
|
+
'&:active': {
|
|
172
|
+
cursor: 'grabbing',
|
|
173
|
+
},
|
|
174
|
+
}, children: _jsx(DragIndicatorIcon, { sx: {
|
|
175
|
+
fontSize: '14px',
|
|
176
|
+
} }) })), _jsx(Tooltip, { title: staticDataInRoute.blockName || route, children: _jsx(Box, { sx: {
|
|
177
|
+
flex: 1,
|
|
178
|
+
overflow: 'hidden',
|
|
179
|
+
textOverflow: 'ellipsis',
|
|
180
|
+
whiteSpace: 'nowrap',
|
|
181
|
+
}, children: staticDataInRoute.blockName || route }) }), _jsx(IconButton, { size: "small", onClick: (e) => {
|
|
182
|
+
e.stopPropagation();
|
|
183
|
+
onDelete(route, staticDataInRoute.blockName || route);
|
|
184
|
+
}, sx: {
|
|
185
|
+
ml: 1,
|
|
186
|
+
opacity: isHovered ? 0.7 : 0,
|
|
187
|
+
visibility: isHovered ? 'visible' : 'hidden',
|
|
188
|
+
transition: 'opacity 0.2s ease',
|
|
189
|
+
'&:hover': {
|
|
190
|
+
bgcolor: 'error.main',
|
|
191
|
+
color: 'error.contrastText',
|
|
192
|
+
},
|
|
193
|
+
}, children: _jsx(DeleteIcon, { sx: { fontSize: '16px' } }) })] }) }));
|
|
194
|
+
};
|
|
60
195
|
const ComparisonPreviewDialog = ({ open, title, leftTitle, leftContent, rightTitle, rightContent, description, loading, onConfirm, onClose, }) => {
|
|
61
196
|
const { t } = useLocaleContext();
|
|
62
197
|
const handleConfirm = async () => {
|
|
@@ -110,6 +245,12 @@ function Layout({ loadState, loadedData }) {
|
|
|
110
245
|
loading: false,
|
|
111
246
|
onConfirm: async () => { },
|
|
112
247
|
},
|
|
248
|
+
deleteConfirmDialog: {
|
|
249
|
+
open: false,
|
|
250
|
+
blockName: '',
|
|
251
|
+
route: '',
|
|
252
|
+
loading: false,
|
|
253
|
+
},
|
|
113
254
|
init: false,
|
|
114
255
|
allComponents: [],
|
|
115
256
|
propertiesValue: {},
|
|
@@ -123,6 +264,10 @@ function Layout({ loadState, loadedData }) {
|
|
|
123
264
|
const [hSizes, setHSizes] = useLocalStorageState('BlockStudioHorizontalSizes', {
|
|
124
265
|
defaultValue: [LEFT_DRAWER_WIDTH, 'auto', RIGHT_DRAWER_WIDTH],
|
|
125
266
|
});
|
|
267
|
+
// Custom sorting order storage
|
|
268
|
+
const [customSortOrder, setCustomSortOrder] = useLocalStorageState('BlockStudioCustomSortOrder', {
|
|
269
|
+
defaultValue: [],
|
|
270
|
+
});
|
|
126
271
|
const { t, locale } = useLocaleContext();
|
|
127
272
|
const { session } = useSessionContext();
|
|
128
273
|
const location = useLocation();
|
|
@@ -146,18 +291,37 @@ function Layout({ loadState, loadedData }) {
|
|
|
146
291
|
iframe.contentWindow.postMessage(safeData, '*');
|
|
147
292
|
}
|
|
148
293
|
}, [mode]);
|
|
149
|
-
const { routes, currentPage } = useMemo(() => {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
294
|
+
const { routes, currentPage, hasCustomSort } = useMemo(() => {
|
|
295
|
+
const allRoutes = Object.keys(staticData).filter((route) => staticData[route]?.main?.isDeleted !== true);
|
|
296
|
+
let sortedRoutes;
|
|
297
|
+
let hasCustomSort = false;
|
|
298
|
+
if (customSortOrder && customSortOrder.length > 0) {
|
|
299
|
+
// Use custom sort order if available
|
|
300
|
+
hasCustomSort = true;
|
|
301
|
+
const sortOrderMap = new Map(customSortOrder.map((route, index) => [route, index]));
|
|
302
|
+
sortedRoutes = allRoutes.sort((a, b) => {
|
|
303
|
+
const indexA = sortOrderMap.get(a) ?? Number.MAX_SAFE_INTEGER;
|
|
304
|
+
const indexB = sortOrderMap.get(b) ?? Number.MAX_SAFE_INTEGER;
|
|
305
|
+
if (indexA === indexB) {
|
|
306
|
+
return a.localeCompare(b); // Fallback to alphabetical for new items
|
|
307
|
+
}
|
|
308
|
+
return indexA - indexB;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
// Use alphabetical sort as default
|
|
313
|
+
hasCustomSort = false;
|
|
314
|
+
sortedRoutes = allRoutes.sort((a, b) => a.localeCompare(b));
|
|
315
|
+
}
|
|
153
316
|
return {
|
|
154
|
-
routes,
|
|
317
|
+
routes: sortedRoutes,
|
|
318
|
+
hasCustomSort,
|
|
155
319
|
currentPage: {
|
|
156
320
|
...staticData[location.pathname]?.main,
|
|
157
321
|
pageId: location.pathname,
|
|
158
322
|
},
|
|
159
323
|
};
|
|
160
|
-
}, [staticData, location.pathname]);
|
|
324
|
+
}, [staticData, location.pathname, customSortOrder]);
|
|
161
325
|
const notSelectedBlock = useMemo(() => {
|
|
162
326
|
return !currentPage?.blockName || !currentPage?.code;
|
|
163
327
|
}, [currentPage]);
|
|
@@ -226,6 +390,9 @@ function Layout({ loadState, loadedData }) {
|
|
|
226
390
|
}
|
|
227
391
|
catch (error) {
|
|
228
392
|
console.error('Failed to write metadata:', error);
|
|
393
|
+
Toast.error(t('themeTranslations.saveMetadataFailed', {
|
|
394
|
+
error: error.message,
|
|
395
|
+
}));
|
|
229
396
|
}
|
|
230
397
|
}
|
|
231
398
|
}, { wait: 500 });
|
|
@@ -316,6 +483,16 @@ function Layout({ loadState, loadedData }) {
|
|
|
316
483
|
const DraggingSplitPlaceholder = useMemo(() => {
|
|
317
484
|
return (_jsx(Box, { p: 1.5, width: "100%", height: "100%", children: _jsx(Skeleton, { variant: "rectangular", height: "100%", sx: { borderRadius: 1 } }) }));
|
|
318
485
|
}, []);
|
|
486
|
+
// Move item function for drag and drop
|
|
487
|
+
const moveItem = useCallback((dragIndex, hoverIndex) => {
|
|
488
|
+
if (dragIndex === hoverIndex)
|
|
489
|
+
return;
|
|
490
|
+
const newRoutes = [...routes];
|
|
491
|
+
const [draggedItem] = newRoutes.splice(dragIndex, 1);
|
|
492
|
+
newRoutes.splice(hoverIndex, 0, draggedItem);
|
|
493
|
+
// Save the new order to localStorage (automatically enables custom sort)
|
|
494
|
+
setCustomSortOrder(newRoutes);
|
|
495
|
+
}, [routes, setCustomSortOrder]);
|
|
319
496
|
// 修改 iframe URL 构建逻辑,添加初始状态参数
|
|
320
497
|
const iframeUrl = useMemo(() => {
|
|
321
498
|
const url = new URL(window.location.href);
|
|
@@ -350,16 +527,67 @@ function Layout({ loadState, loadedData }) {
|
|
|
350
527
|
window.addEventListener('message', handleMessage);
|
|
351
528
|
return () => window.removeEventListener('message', handleMessage);
|
|
352
529
|
}, [changeMode, mode]);
|
|
530
|
+
// Add delete block function
|
|
531
|
+
const handleDeleteBlock = async (route, blockName) => {
|
|
532
|
+
state.deleteConfirmDialog = {
|
|
533
|
+
open: true,
|
|
534
|
+
blockName,
|
|
535
|
+
route,
|
|
536
|
+
loading: false,
|
|
537
|
+
};
|
|
538
|
+
};
|
|
539
|
+
const confirmDeleteBlock = async () => {
|
|
540
|
+
state.deleteConfirmDialog.loading = true;
|
|
541
|
+
try {
|
|
542
|
+
// Call delete API
|
|
543
|
+
const response = await api.delete(`/api/blocks/delete`, {
|
|
544
|
+
data: { route: state.deleteConfirmDialog.route },
|
|
545
|
+
});
|
|
546
|
+
if (response.data.success) {
|
|
547
|
+
Toast.success(t('themeTranslations.deleteSuccess'));
|
|
548
|
+
// Navigate away if currently viewing the deleted component
|
|
549
|
+
if (currentPage.pageId === state.deleteConfirmDialog.route) {
|
|
550
|
+
const remainingComponents = routes.filter((r) => r !== state.deleteConfirmDialog.route);
|
|
551
|
+
navigate('/');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
throw new Error(response.data.error || t('themeTranslations.deleteFailed'));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
catch (error) {
|
|
559
|
+
console.error('Failed to delete block:', error);
|
|
560
|
+
Toast.error(t('themeTranslations.deleteFailed'));
|
|
561
|
+
}
|
|
562
|
+
finally {
|
|
563
|
+
state.deleteConfirmDialog.loading = false;
|
|
564
|
+
state.deleteConfirmDialog.open = false;
|
|
565
|
+
}
|
|
566
|
+
};
|
|
353
567
|
const leftPanelContent = useMemo(() => {
|
|
354
568
|
if (state.draggingSplitPane) {
|
|
355
569
|
return DraggingSplitPlaceholder;
|
|
356
570
|
}
|
|
357
|
-
return (_jsxs(Stack, { height: "100%", children: [_jsxs(Stack, { gap: 1, direction: "row", alignItems: "center", sx: {
|
|
571
|
+
return (_jsxs(Stack, { height: "100%", children: [_jsxs(Stack, { gap: 1, direction: "row", alignItems: "center", sx: { pt: 2, pr: 1, pl: 0.5, pb: 1 }, children: [_jsx(TextField, { placeholder: t('themeTranslations.search'), sx: { minWidth: 60, flex: 1 }, onChange: (e) => {
|
|
358
572
|
state.searchValue = e.target.value;
|
|
359
|
-
} }), _jsx(
|
|
573
|
+
} }), hasCustomSort && (_jsx(Tooltip, { title: t('themeTranslations.resetToAlphabeticalSort'), children: _jsx(IconButton, { size: "small", onClick: () => {
|
|
574
|
+
setCustomSortOrder([]);
|
|
575
|
+
}, sx: {
|
|
576
|
+
transition: 'all 0.2s ease',
|
|
577
|
+
'&:hover': {
|
|
578
|
+
backgroundColor: 'action.hover',
|
|
579
|
+
transform: 'scale(1.1)',
|
|
580
|
+
},
|
|
581
|
+
}, children: _jsx(SortByAlphaIcon, { fontSize: "small" }) }) })), _jsx(Button, { variant: "contained", sx: { minWidth: 40 }, onClick: () => {
|
|
360
582
|
state.createBlockOpen = true;
|
|
361
|
-
}, children: _jsx(AddIcon, { fontSize: "small" }) })] }), routes?.length > 0 ? (_jsx(List, { sx: {
|
|
362
|
-
|
|
583
|
+
}, children: _jsx(AddIcon, { fontSize: "small" }) })] }), routes?.length > 0 ? (_jsx(List, { sx: {
|
|
584
|
+
pr: 1,
|
|
585
|
+
overflowY: 'auto',
|
|
586
|
+
height: hasCustomSort ? 'calc(100% - 80px)' : 'calc(100% - 60px)',
|
|
587
|
+
// Prevent text selection during drag
|
|
588
|
+
userSelect: 'none',
|
|
589
|
+
}, children: routes
|
|
590
|
+
.map((route, index) => {
|
|
363
591
|
const routeName = route;
|
|
364
592
|
const staticDataInRoute = staticData[route]?.main;
|
|
365
593
|
if (state.searchValue && !routeName?.toLowerCase().includes(state.searchValue?.toLowerCase())) {
|
|
@@ -368,35 +596,35 @@ function Layout({ loadState, loadedData }) {
|
|
|
368
596
|
if (!state.allComponents?.find(({ blockName }) => `/${blockName}` === routeName)) {
|
|
369
597
|
return null;
|
|
370
598
|
}
|
|
371
|
-
return (_jsx(
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
borderRadius: 1,
|
|
378
|
-
mb: 1,
|
|
379
|
-
width: '100%',
|
|
380
|
-
textOverflow: 'ellipsis',
|
|
381
|
-
whiteSpace: 'nowrap',
|
|
382
|
-
overflowX: 'hidden',
|
|
383
|
-
transition: 'all 0.3s ease',
|
|
384
|
-
'&.Mui-selected': {
|
|
385
|
-
backgroundColor: 'primary.main',
|
|
386
|
-
color: 'white',
|
|
387
|
-
'&:hover': {
|
|
388
|
-
backgroundColor: 'primary.main',
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
fontSize: '14px',
|
|
392
|
-
}, children: _jsx(Tooltip, { title: staticDataInRoute.blockName || routeName, children: _jsx("div", { style: {
|
|
393
|
-
width: '100%',
|
|
394
|
-
overflow: 'hidden',
|
|
395
|
-
textOverflow: 'ellipsis',
|
|
396
|
-
}, children: staticDataInRoute.blockName || routeName }) }) }) }, route));
|
|
599
|
+
return (_jsx(DraggableRouteItem, { route: route, staticDataInRoute: staticDataInRoute, currentPageId: currentPage.pageId, onNavigate: (route) => {
|
|
600
|
+
if (route !== currentPage.pageId) {
|
|
601
|
+
navigate(route);
|
|
602
|
+
state.iframeLoaded = false;
|
|
603
|
+
}
|
|
604
|
+
}, onDelete: handleDeleteBlock, index: index, moveItem: moveItem, showDragHandle: true, routes: routes }, route));
|
|
397
605
|
})
|
|
398
|
-
.filter(Boolean) })) : (_jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", children:
|
|
399
|
-
|
|
606
|
+
.filter(Boolean) })) : (_jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", sx: { p: 3 }, children: _jsxs(Stack, { alignItems: "center", spacing: 3, sx: { textAlign: 'center' }, children: [_jsx(Box, { component: "img", src: "https://api.iconify.design/material-symbols:folder-open-outline.svg", sx: {
|
|
607
|
+
width: 48,
|
|
608
|
+
height: 48,
|
|
609
|
+
opacity: 0.5,
|
|
610
|
+
filter: 'grayscale(100%)',
|
|
611
|
+
}, alt: "No components" }), _jsx(Typography, { variant: "h6", color: "text.primary", sx: { fontWeight: 600 }, children: t('themeTranslations.noComponentsFound') }), _jsx(Typography, { variant: "body2", color: "text.secondary", sx: { maxWidth: 200 }, children: t('themeTranslations.createFirstComponent') }), _jsx(Button, { variant: "contained", startIcon: _jsx(AddIcon, {}), onClick: () => {
|
|
612
|
+
state.createBlockOpen = true;
|
|
613
|
+
}, sx: { mt: 2 }, children: t('themeTranslations.createComponent') })] }) }))] }));
|
|
614
|
+
}, [
|
|
615
|
+
routes,
|
|
616
|
+
staticData,
|
|
617
|
+
state.allComponents,
|
|
618
|
+
state.searchValue,
|
|
619
|
+
state.draggingSplitPane,
|
|
620
|
+
hasCustomSort,
|
|
621
|
+
moveItem,
|
|
622
|
+
currentPage.pageId,
|
|
623
|
+
navigate,
|
|
624
|
+
locale,
|
|
625
|
+
mode,
|
|
626
|
+
handleDeleteBlock,
|
|
627
|
+
]);
|
|
400
628
|
// 修改 middlePanelContent - iframe 内部监听消息
|
|
401
629
|
const middlePanelContent = useMemo(() => {
|
|
402
630
|
// 如果在iframe内部,添加消息接收逻辑
|
|
@@ -411,11 +639,11 @@ function Layout({ loadState, loadedData }) {
|
|
|
411
639
|
const url = new URL(window.location.href);
|
|
412
640
|
const initialComponentId = url.searchParams.get('componentId') || state.metadata.id;
|
|
413
641
|
const initialLocale = url.searchParams.get('locale') || locale || 'en';
|
|
414
|
-
return (_jsx(Box, { className: "custom-component-root", sx: { height: '100vh', width: '100vw', overflow: 'auto' }, children: _jsx(ThemeProvider, { theme: theme, children: _jsx(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
642
|
+
return (_jsx(Box, { className: "custom-component-root", sx: { height: '100vh', width: '100vw', overflow: 'auto' }, children: _jsx(ThemeProvider, { theme: theme, children: _jsx(CustomComponentRenderer, { locale: initialLocale, componentId: initialComponentId || state.metadata.id, dev: {
|
|
643
|
+
mode: 'draft',
|
|
644
|
+
components: mergedAllBlocks,
|
|
645
|
+
defaultLocale,
|
|
646
|
+
}, properties: mergedPropertiesValues }, `custom-${renderComponentTrigger}`) }) }));
|
|
419
647
|
}
|
|
420
648
|
// 没有匹配到路由,使用欢迎页面
|
|
421
649
|
if (notSelectedBlock) {
|
|
@@ -582,6 +810,7 @@ function Layout({ loadState, loadedData }) {
|
|
|
582
810
|
JSON.stringify(state.metadata || {}),
|
|
583
811
|
JSON.stringify(mergedAllBlocks || []),
|
|
584
812
|
JSON.stringify(mergedPropertiesValues || {}),
|
|
813
|
+
handleDeleteBlock,
|
|
585
814
|
]);
|
|
586
815
|
const rightPanelContent = useMemo(() => {
|
|
587
816
|
if (state.draggingSplitPane) {
|
|
@@ -596,7 +825,7 @@ function Layout({ loadState, loadedData }) {
|
|
|
596
825
|
// add disabled backdrop if not selected block
|
|
597
826
|
'& .MuiListItem-root': {
|
|
598
827
|
pointerEvents: notSelectedBlock ? 'none' : 'auto',
|
|
599
|
-
opacity: notSelectedBlock ? 0.
|
|
828
|
+
opacity: notSelectedBlock ? 0.4 : 1,
|
|
600
829
|
userSelect: notSelectedBlock ? 'none' : 'auto',
|
|
601
830
|
filter: notSelectedBlock ? 'blur(5px)' : 'none',
|
|
602
831
|
},
|
|
@@ -609,7 +838,13 @@ function Layout({ loadState, loadedData }) {
|
|
|
609
838
|
display: 'flex',
|
|
610
839
|
justifyContent: 'center',
|
|
611
840
|
alignItems: 'center',
|
|
612
|
-
|
|
841
|
+
p: 3,
|
|
842
|
+
}, children: _jsxs(Stack, { alignItems: "center", spacing: 2, sx: {
|
|
843
|
+
textAlign: 'center',
|
|
844
|
+
bgcolor: 'background.default',
|
|
845
|
+
borderRadius: 2,
|
|
846
|
+
p: 3,
|
|
847
|
+
}, children: [_jsx(SettingsIcon, { sx: { fontSize: 48, color: 'primary.main' } }), _jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { variant: "h6", color: "primary.main", sx: { fontWeight: 600 }, children: t('themeTranslations.componentPropertiesTitle') }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: t('themeTranslations.selectComponentToConfigureProperties') })] })] }) })), _jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(BasicInfo, { config: state.metadata }) }) }), _jsx(ListItem, { children: _jsxs(Box, { sx: { width: '100%' }, children: [_jsx(PropertiesConfig, { config: state.metadata, currentLocale: locale, defaultLocale: defaultLocale, allComponents: mergedAllBlocks, onUpdateConfig: (updater) => {
|
|
613
848
|
updater(state.metadata);
|
|
614
849
|
}, useI18nEditor: false }), _jsxs(Stack, { direction: "column", spacing: 1, sx: { mt: 1 }, children: [_jsx(Button, { variant: "contained", size: "small", color: "primary", onClick: async () => {
|
|
615
850
|
try {
|
|
@@ -743,6 +978,7 @@ function Layout({ loadState, loadedData }) {
|
|
|
743
978
|
getStaticData,
|
|
744
979
|
state.draggingSplitPane,
|
|
745
980
|
mode,
|
|
981
|
+
handleDeleteBlock,
|
|
746
982
|
]);
|
|
747
983
|
if (isInsideIframe) {
|
|
748
984
|
return middlePanelContent;
|
|
@@ -808,7 +1044,11 @@ function Layout({ loadState, loadedData }) {
|
|
|
808
1044
|
}
|
|
809
1045
|
}, children: t('themeTranslations.create') })] }) })] }), _jsx(ComparisonPreviewDialog, { open: state.previewDialog.open, title: state.previewDialog.title, leftTitle: state.previewDialog.leftTitle, leftContent: state.previewDialog.leftContent, rightTitle: state.previewDialog.rightTitle, rightContent: state.previewDialog.rightContent, description: state.previewDialog.description, loading: state.previewDialog.loading, onConfirm: state.previewDialog.onConfirm, onClose: () => {
|
|
810
1046
|
state.previewDialog.open = false;
|
|
811
|
-
} })
|
|
1047
|
+
} }), _jsxs(Dialog, { open: state.deleteConfirmDialog.open, onClose: () => (state.deleteConfirmDialog.open = false), maxWidth: "sm", fullWidth: true, children: [_jsx(DialogTitle, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: t('themeTranslations.deleteComponent') }), _jsxs(DialogContent, { children: [_jsx(Alert, { severity: "warning", sx: { mb: 2 }, children: t('themeTranslations.deleteWarning') }), _jsx(Typography, { children: t('themeTranslations.deleteConfirmMessage', {
|
|
1048
|
+
componentName: state.deleteConfirmDialog.blockName,
|
|
1049
|
+
}) })] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', p: 2, gap: 1 }, children: [_jsx(Button, { variant: "outlined", onClick: () => (state.deleteConfirmDialog.open = false), disabled: state.deleteConfirmDialog.loading, children: t('themeTranslations.cancel') }), _jsx(Button, { variant: "contained", color: "error", onClick: confirmDeleteBlock, disabled: state.deleteConfirmDialog.loading, startIcon: state.deleteConfirmDialog.loading ? _jsx(CircularProgress, { size: 20 }) : _jsx(DeleteIcon, {}), children: state.deleteConfirmDialog.loading
|
|
1050
|
+
? t('themeTranslations.deleting')
|
|
1051
|
+
: t('themeTranslations.deleteConfirm') })] })] })] }) }));
|
|
812
1052
|
}
|
|
813
1053
|
// Add SplitPane styling
|
|
814
1054
|
const StyledSplitPane = styled(SplitPane) `
|
|
@@ -828,6 +1068,29 @@ const DragHandle = styled('div') `
|
|
|
828
1068
|
}
|
|
829
1069
|
`;
|
|
830
1070
|
const StyledDashboard = styled(Dashboard) `
|
|
1071
|
+
@keyframes dragShake {
|
|
1072
|
+
0%,
|
|
1073
|
+
100% {
|
|
1074
|
+
transform: translateY(0px) rotate(0deg);
|
|
1075
|
+
}
|
|
1076
|
+
25% {
|
|
1077
|
+
transform: translateY(-2px) rotate(-2deg);
|
|
1078
|
+
}
|
|
1079
|
+
50% {
|
|
1080
|
+
transform: translateY(2px) rotate(0deg);
|
|
1081
|
+
}
|
|
1082
|
+
75% {
|
|
1083
|
+
transform: translateY(-2px) rotate(2deg);
|
|
1084
|
+
}
|
|
1085
|
+
100% {
|
|
1086
|
+
transform: translateY(0px) rotate(0deg);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
.drag-shaking {
|
|
1091
|
+
animation: dragShake 0.3s ease-in-out infinite;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
831
1094
|
.dashboard-content {
|
|
832
1095
|
display: flex;
|
|
833
1096
|
flex-direction: row;
|
|
@@ -873,12 +1136,12 @@ function CreateResource({ open, onClose }) {
|
|
|
873
1136
|
return null;
|
|
874
1137
|
}
|
|
875
1138
|
const tenantScope = 'pages-kit-block-studio';
|
|
876
|
-
return (_jsx(
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1139
|
+
return (_jsx(BlockletStudio, { mode: "dialog", tenantScope: tenantScope, title: "Pages Kit Blocks", description: "", note: "", introduction: "", logo: "", componentDid: PAGES_KIT_BLOCK_STUDIO_BLOCKLET_DID,
|
|
1140
|
+
// 透传到 get blocklet resource 的参数
|
|
1141
|
+
resourcesParams: {}, dependentComponentsMode: "readonly", open: true, setOpen: () => onClose(), onConnected: () => { }, onUploaded: () => { }, onReleased: () => { },
|
|
1142
|
+
// onOpened={() => onOpened?.()}
|
|
1143
|
+
// 默认选中的资源
|
|
1144
|
+
resources: {} }));
|
|
882
1145
|
}
|
|
883
1146
|
// 添加 themeTranslations 到 translations 对象
|
|
884
1147
|
// 这里我们在运行时扩展 translations 对象,而不是修改源文件
|
|
@@ -929,6 +1192,21 @@ if (!translations.en.themeTranslations) {
|
|
|
929
1192
|
step1: 'Browse and select components from the left menu, or create your own',
|
|
930
1193
|
step2: 'Preview your component in the center area with responsive design views',
|
|
931
1194
|
step3: 'Customize properties and configurations in the right panel to bring your ideas to life',
|
|
1195
|
+
// New translations for sorting and improved empty states
|
|
1196
|
+
resetToAlphabeticalSort: 'Reset to Alphabetical Sort',
|
|
1197
|
+
noComponentsFound: 'No Components Found',
|
|
1198
|
+
createFirstComponent: 'Create your first component to get started',
|
|
1199
|
+
createComponent: 'Create Component',
|
|
1200
|
+
componentPropertiesTitle: 'Configure Component',
|
|
1201
|
+
selectComponentToConfigureProperties: 'Select a component from the left panel to configure its properties',
|
|
1202
|
+
saveMetadataFailed: 'Failed to write @metadata.json: {error}',
|
|
1203
|
+
deleteComponent: 'Delete Component',
|
|
1204
|
+
deleteWarning: 'This action cannot be undone. All files in the component directory will be permanently deleted.',
|
|
1205
|
+
deleteConfirmMessage: "Are you sure you want to delete the component '{componentName}'?",
|
|
1206
|
+
deleteConfirm: 'Delete',
|
|
1207
|
+
deleting: 'Deleting...',
|
|
1208
|
+
deleteSuccess: 'Component deleted successfully',
|
|
1209
|
+
deleteFailed: 'Failed to delete component',
|
|
932
1210
|
};
|
|
933
1211
|
}
|
|
934
1212
|
if (!translations.zh.themeTranslations) {
|
|
@@ -978,5 +1256,20 @@ if (!translations.zh.themeTranslations) {
|
|
|
978
1256
|
step1: '从左侧菜单浏览并选择组件,或创建您自己的组件',
|
|
979
1257
|
step2: '在中央区域预览您的组件,支持响应式设计视图',
|
|
980
1258
|
step3: '在右侧面板自定义属性和配置,让您的创意变为现实',
|
|
1259
|
+
// New translations for sorting and improved empty states
|
|
1260
|
+
resetToAlphabeticalSort: '重置为字母排序',
|
|
1261
|
+
noComponentsFound: '未找到组件',
|
|
1262
|
+
createFirstComponent: '创建您的第一个组件来开始',
|
|
1263
|
+
createComponent: '创建组件',
|
|
1264
|
+
componentPropertiesTitle: '配置组件',
|
|
1265
|
+
selectComponentToConfigureProperties: '从左侧面板选择一个组件来配置其属性',
|
|
1266
|
+
saveMetadataFailed: '修改 @metadata.json 失败: {error}',
|
|
1267
|
+
deleteComponent: '删除组件',
|
|
1268
|
+
deleteWarning: '此操作无法撤销。组件目录中的所有文件将被永久删除。',
|
|
1269
|
+
deleteConfirmMessage: "确定要删除组件 '{componentName}' 吗?",
|
|
1270
|
+
deleteConfirm: '删除',
|
|
1271
|
+
deleting: '删除中...',
|
|
1272
|
+
deleteSuccess: '组件删除成功',
|
|
1273
|
+
deleteFailed: '删除组件失败',
|
|
981
1274
|
};
|
|
982
1275
|
}
|