@blocklet/pages-kit-block-studio 0.4.127 → 0.4.128
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.
|
@@ -5,6 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
// import BlockStudio from '@blocklet/pages-kit-block-studio/frontend';
|
|
6
6
|
// export default BlockStudio;
|
|
7
7
|
import { createAuthServiceSessionContext } from '@arcblock/did-connect/lib/Session';
|
|
8
|
+
import Empty from '@arcblock/ux/lib/Empty';
|
|
8
9
|
import { LocaleProvider, useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
9
10
|
import Toast, { ToastProvider } from '@arcblock/ux/lib/Toast';
|
|
10
11
|
import { createAxios } from '@blocklet/js-sdk';
|
|
@@ -20,15 +21,19 @@ import { parsePropertyValue } from '@blocklet/pages-kit/utils/property';
|
|
|
20
21
|
import { Dashboard } from '@blocklet/studio-ui';
|
|
21
22
|
import { BlockletStudio } from '@blocklet/ui-react';
|
|
22
23
|
import AddIcon from '@mui/icons-material/Add';
|
|
23
|
-
import
|
|
24
|
-
import
|
|
24
|
+
import LaptopMacIcon from '@mui/icons-material/LaptopMac';
|
|
25
|
+
import PhoneAndroidIcon from '@mui/icons-material/PhoneAndroid';
|
|
26
|
+
import { Alert, Box, Button, CircularProgress, Dialog, DialogContent, DialogTitle, List, ListItem, ListItemButton, Skeleton, Stack, StyledEngineProvider, TextField, ThemeProvider, ToggleButton, ToggleButtonGroup, Tooltip, Typography, backdropClasses, circularProgressClasses, styled, } from '@mui/material';
|
|
27
|
+
import { useDebounceFn, useLocalStorageState, useReactive } from 'ahooks';
|
|
25
28
|
import cloneDeep from 'lodash/cloneDeep';
|
|
26
29
|
import get from 'lodash/get';
|
|
27
30
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
28
|
-
import { Suspense, useCallback, useContext, useEffect, useMemo } from 'react';
|
|
31
|
+
import { Suspense, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
29
32
|
import { DndProvider } from 'react-dnd';
|
|
30
33
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
31
34
|
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
|
|
35
|
+
import SplitPane, { Pane } from 'split-pane-react';
|
|
36
|
+
import 'split-pane-react/esm/themes/default.css';
|
|
32
37
|
import { joinURL } from 'ufo';
|
|
33
38
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
34
39
|
import { useStaticData } from 'vite-plugin-react-pages/client';
|
|
@@ -45,17 +50,18 @@ function useSessionContext() {
|
|
|
45
50
|
const info = useContext(SessionContext);
|
|
46
51
|
return info;
|
|
47
52
|
}
|
|
48
|
-
const LEFT_DRAWER_WIDTH =
|
|
53
|
+
const LEFT_DRAWER_WIDTH = 300;
|
|
49
54
|
const RIGHT_DRAWER_WIDTH = 300;
|
|
50
55
|
const defaultLocale = 'en';
|
|
51
|
-
const ComparisonPreviewDialog = ({ open, title, leftTitle, leftContent, rightTitle, rightContent, description
|
|
56
|
+
const ComparisonPreviewDialog = ({ open, title, leftTitle, leftContent, rightTitle, rightContent, description, loading, onConfirm, onClose, }) => {
|
|
57
|
+
const { t } = useLocaleContext();
|
|
52
58
|
const handleConfirm = async () => {
|
|
53
59
|
try {
|
|
54
60
|
await onConfirm();
|
|
55
61
|
}
|
|
56
62
|
catch (error) {
|
|
57
63
|
console.error('执行操作失败:', error);
|
|
58
|
-
Toast.error('
|
|
64
|
+
Toast.error(t('themeTranslations.operationFailed'));
|
|
59
65
|
}
|
|
60
66
|
};
|
|
61
67
|
return (_jsxs(Dialog, { open: open, onClose: onClose, maxWidth: "md", fullWidth: true, children: [_jsx(DialogTitle, { children: title }), _jsx(DialogContent, { children: _jsxs(Box, { children: [_jsxs(Box, { sx: { display: 'flex', flexDirection: 'row', mb: 2 }, children: [_jsxs(Box, { sx: { flex: 1, mr: 1 }, children: [_jsx(Typography, { variant: "subtitle2", sx: { mb: 1 }, children: leftTitle }), _jsx(TextField, { multiline: true, fullWidth: true, rows: 10, InputProps: {
|
|
@@ -72,13 +78,10 @@ const ComparisonPreviewDialog = ({ open, title, leftTitle, leftContent, rightTit
|
|
|
72
78
|
fontFamily: 'monospace',
|
|
73
79
|
fontSize: '0.875rem',
|
|
74
80
|
},
|
|
75
|
-
} })] })] }), _jsx(Typography, { variant: "body2", sx: { mt: 2, color: 'text.secondary' }, children: description })] }) }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', p: 2 }, children: [_jsx(Button, { variant: "outlined", onClick: onClose, sx: { mr: 1 }, children:
|
|
81
|
+
} })] })] }), _jsx(Typography, { variant: "body2", sx: { mt: 2, color: 'text.secondary' }, children: description })] }) }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', p: 2 }, children: [_jsx(Button, { variant: "outlined", onClick: onClose, sx: { mr: 1 }, children: t('themeTranslations.cancel') }), _jsx(Button, { variant: "contained", onClick: handleConfirm, disabled: loading, children: loading ? _jsx(CircularProgress, { size: 24 }) : t('themeTranslations.confirmUpdate') })] })] }));
|
|
76
82
|
};
|
|
77
83
|
function Layout({ loadState, loadedData }) {
|
|
78
84
|
const state = useReactive({
|
|
79
|
-
injectBlocks: [],
|
|
80
|
-
selectingParam: null,
|
|
81
|
-
componentSelectOpen: false,
|
|
82
85
|
createResourceOpen: false,
|
|
83
86
|
createBlockOpen: false,
|
|
84
87
|
metadata: {
|
|
@@ -106,8 +109,16 @@ function Layout({ loadState, loadedData }) {
|
|
|
106
109
|
init: false,
|
|
107
110
|
allComponents: [],
|
|
108
111
|
propertiesValue: {},
|
|
112
|
+
draggingSplitPane: false,
|
|
113
|
+
simulatorType: 'pc',
|
|
114
|
+
iframeLoaded: false,
|
|
109
115
|
});
|
|
110
|
-
|
|
116
|
+
// 添加renderComponentTrigger状态,用于触发组件重新渲染
|
|
117
|
+
const [renderComponentTrigger, setRenderComponentTrigger] = useState(0);
|
|
118
|
+
const [hSizes, setHSizes] = useLocalStorageState('BlockStudioHorizontalSizes', {
|
|
119
|
+
defaultValue: [LEFT_DRAWER_WIDTH, 'auto', RIGHT_DRAWER_WIDTH],
|
|
120
|
+
});
|
|
121
|
+
const { t, locale } = useLocaleContext();
|
|
111
122
|
const { session } = useSessionContext();
|
|
112
123
|
const location = useLocation();
|
|
113
124
|
const navigate = useNavigate();
|
|
@@ -142,14 +153,6 @@ function Layout({ loadState, loadedData }) {
|
|
|
142
153
|
const response = await api.get('/api/blocks/components');
|
|
143
154
|
return response.data || [];
|
|
144
155
|
};
|
|
145
|
-
useEffect(() => {
|
|
146
|
-
if (currentPage && state.injectBlocks.length === 0) {
|
|
147
|
-
state.injectBlocks.push({
|
|
148
|
-
...currentPage,
|
|
149
|
-
level: 0,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}, [currentPage]);
|
|
153
156
|
// Add new effect to fetch metadata when page changes
|
|
154
157
|
useEffect(() => {
|
|
155
158
|
const fetchMetadata = async () => {
|
|
@@ -235,39 +238,402 @@ function Layout({ loadState, loadedData }) {
|
|
|
235
238
|
description: '',
|
|
236
239
|
};
|
|
237
240
|
};
|
|
238
|
-
let mergedPropertiesValues =
|
|
239
|
-
return
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
locale,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
241
|
+
let mergedPropertiesValues = useMemo(() => {
|
|
242
|
+
return Object.fromEntries(Object.values(state.metadata.properties ?? {}).map(({ data }) => {
|
|
243
|
+
return [
|
|
244
|
+
data.id,
|
|
245
|
+
{
|
|
246
|
+
value: cloneDeep(state.propertiesValue[data.id]?.value ??
|
|
247
|
+
parsePropertyValue(data, data.locales?.[locale]?.defaultValue ?? data.locales?.[defaultLocale]?.defaultValue, {
|
|
248
|
+
locale,
|
|
249
|
+
defaultLocale,
|
|
250
|
+
})),
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
}));
|
|
254
|
+
}, [JSON.stringify(state.metadata.properties), JSON.stringify(state.propertiesValue)]);
|
|
255
|
+
// 添加防抖函数,延迟渲染
|
|
256
|
+
const { run: triggerRerender } = useDebounceFn(() => {
|
|
257
|
+
// 发送消息到 iframe
|
|
258
|
+
try {
|
|
259
|
+
const iframe = document.querySelector('#preview-iframe');
|
|
260
|
+
if (iframe && iframe.contentWindow) {
|
|
261
|
+
// 创建精简版数据,只包含必要信息,并避免循环引用
|
|
262
|
+
const safeData = {
|
|
263
|
+
type: 'STATE_UPDATE',
|
|
264
|
+
data: {
|
|
265
|
+
componentId: state.metadata.id,
|
|
266
|
+
// 不传递整个 components 对象,只传递当前组件需要的最小数据集
|
|
267
|
+
currentComponent: {
|
|
268
|
+
id: state.metadata.id,
|
|
269
|
+
// 使用 JSON 序列化和反序列化清除循环引用和函数
|
|
270
|
+
mergedPropertiesValues: cloneDeep({
|
|
271
|
+
...mergedPropertiesValues,
|
|
272
|
+
updatedAt: Date.now(),
|
|
273
|
+
} || {}),
|
|
274
|
+
},
|
|
275
|
+
locale: locale,
|
|
276
|
+
defaultLocale: defaultLocale,
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
iframe.contentWindow.postMessage(safeData, '*');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
console.error('Failed to send message to iframe:', error);
|
|
284
|
+
}
|
|
285
|
+
}, { wait: 50 });
|
|
286
|
+
// 当属性变化时触发重新渲染
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
triggerRerender();
|
|
289
|
+
}, [JSON.stringify(mergedPropertiesValues), JSON.stringify(mergedAllBlocks), state.metadata.id]);
|
|
290
|
+
const DraggingSplitPlaceholder = useMemo(() => {
|
|
291
|
+
return (_jsx(Box, { p: 1.5, width: "100%", height: "100%", children: _jsx(Skeleton, { variant: "rectangular", height: "100%", sx: { borderRadius: 1 } }) }));
|
|
292
|
+
}, []);
|
|
293
|
+
// 修改 iframe URL 构建逻辑,添加初始状态参数
|
|
294
|
+
const iframeUrl = useMemo(() => {
|
|
295
|
+
const url = new URL(window.location.href);
|
|
296
|
+
url.searchParams.set('simulator', '1');
|
|
297
|
+
// 添加初始状态参数
|
|
298
|
+
url.searchParams.set('componentId', state.metadata.id || '');
|
|
299
|
+
url.searchParams.set('locale', locale || defaultLocale || 'en');
|
|
300
|
+
return url.toString();
|
|
301
|
+
}, [window.location.href, state.metadata.id, locale, defaultLocale]);
|
|
302
|
+
// 获取当前URL参数判断是否是在iframe内
|
|
303
|
+
const isInsideIframe = useMemo(() => {
|
|
304
|
+
const url = new URL(window.location.href);
|
|
305
|
+
return url.searchParams.get('simulator') === '1';
|
|
306
|
+
}, [window.location.href]);
|
|
307
|
+
// 添加消息事件监听
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
const handleMessage = (event) => {
|
|
310
|
+
if (event.data && event.data.type === 'STATE_UPDATE') {
|
|
311
|
+
// 接收并应用父窗口发来的状态更新
|
|
312
|
+
const { componentId, currentComponent, locale } = event.data.data;
|
|
313
|
+
// 安全地更新组件ID
|
|
314
|
+
if (componentId && componentId !== state.metadata.id) {
|
|
315
|
+
state.metadata.id = componentId;
|
|
316
|
+
}
|
|
317
|
+
// 只更新当前组件的属性值,而不是整个组件列表
|
|
318
|
+
if (currentComponent && currentComponent.mergedPropertiesValues) {
|
|
319
|
+
state.propertiesValue = cloneDeep(currentComponent.mergedPropertiesValues);
|
|
320
|
+
}
|
|
321
|
+
setRenderComponentTrigger((prev) => prev + 1);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
window.addEventListener('message', handleMessage);
|
|
325
|
+
return () => window.removeEventListener('message', handleMessage);
|
|
326
|
+
}, []);
|
|
327
|
+
const leftPanelContent = useMemo(() => {
|
|
328
|
+
if (state.draggingSplitPane) {
|
|
329
|
+
return DraggingSplitPlaceholder;
|
|
253
330
|
}
|
|
254
|
-
|
|
255
|
-
|
|
331
|
+
return (_jsxs(Stack, { height: "100%", children: [_jsxs(Stack, { gap: 1, direction: "row", alignItems: "center", sx: { py: 2, pr: 1, pl: 0.5, flex: 1 }, children: [_jsx(TextField, { placeholder: t('themeTranslations.search'), sx: { minWidth: 60, flex: 1 }, onChange: (e) => {
|
|
332
|
+
state.searchValue = e.target.value;
|
|
333
|
+
} }), _jsx(Button, { variant: "contained", sx: { minWidth: 40 }, onClick: () => {
|
|
334
|
+
state.createBlockOpen = true;
|
|
335
|
+
}, children: _jsx(AddIcon, { fontSize: "small" }) })] }), routes?.length > 0 ? (_jsx(List, { sx: { pr: 1, overflowY: 'auto', height: 'calc(100% - 60px)' }, children: routes
|
|
336
|
+
.map((route) => {
|
|
337
|
+
const routeName = route;
|
|
338
|
+
const staticDataInRoute = staticData[route]?.main;
|
|
339
|
+
if (state.searchValue && !routeName?.toLowerCase().includes(state.searchValue?.toLowerCase())) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
if (!state.allComponents?.find(({ blockName }) => `/${blockName}` === routeName)) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
return (_jsx(ListItem, { disablePadding: true, children: _jsx(ListItemButton, { selected: currentPage.pageId === route, onClick: () => {
|
|
346
|
+
navigate(route);
|
|
347
|
+
state.iframeLoaded = false;
|
|
348
|
+
}, sx: {
|
|
349
|
+
borderRadius: 1,
|
|
350
|
+
mb: 1,
|
|
351
|
+
width: '100%',
|
|
352
|
+
textOverflow: 'ellipsis',
|
|
353
|
+
whiteSpace: 'nowrap',
|
|
354
|
+
overflowX: 'hidden',
|
|
355
|
+
transition: 'all 0.3s ease',
|
|
356
|
+
'&.Mui-selected': {
|
|
357
|
+
backgroundColor: 'primary.main',
|
|
358
|
+
color: 'white',
|
|
359
|
+
'&:hover': {
|
|
360
|
+
backgroundColor: 'primary.main',
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
fontSize: '14px',
|
|
364
|
+
}, children: _jsx(Tooltip, { title: staticDataInRoute.blockName || routeName, children: _jsx("div", { style: {
|
|
365
|
+
width: '100%',
|
|
366
|
+
overflow: 'hidden',
|
|
367
|
+
textOverflow: 'ellipsis',
|
|
368
|
+
}, children: staticDataInRoute.blockName || routeName }) }) }) }, route));
|
|
369
|
+
})
|
|
370
|
+
.filter(Boolean) })) : (_jsx(Box, { display: "flex", justifyContent: "center", alignItems: "center", height: "100%", children: _jsx(Empty, { children: _jsx(Typography, { variant: "body2", color: "text.secondary", children: t('themeTranslations.noRoutesFound') }) }) }))] }));
|
|
371
|
+
}, [routes, staticData, state.allComponents, state.searchValue, state.draggingSplitPane, locale]);
|
|
372
|
+
// 修改 middlePanelContent - iframe 内部监听消息
|
|
373
|
+
const middlePanelContent = useMemo(() => {
|
|
374
|
+
// 如果在iframe内部,添加消息接收逻辑
|
|
375
|
+
if (isInsideIframe) {
|
|
376
|
+
if (loadState.type === '404' || loadState.type === 'loading') {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
if (loadState.type === 'load-error') {
|
|
380
|
+
return (_jsx(Box, { width: "100%", height: "100%", display: "flex", justifyContent: "center", alignItems: "center", children: _jsx(Alert, { severity: "error", variant: "filled", children: t('themeTranslations.failedLoadCode') }) }));
|
|
381
|
+
}
|
|
382
|
+
// 从 URL 获取初始组件 ID 和语言
|
|
383
|
+
const url = new URL(window.location.href);
|
|
384
|
+
const initialComponentId = url.searchParams.get('componentId') || state.metadata.id;
|
|
385
|
+
const initialLocale = url.searchParams.get('locale') || locale || 'en';
|
|
386
|
+
return (_jsx(Box, { className: "custom-component-root", sx: { height: '100%', width: '100%', overflow: 'auto' }, children: _jsx(ThemeProvider, { theme: pagesTheme, children: _jsx(Suspense, { fallback: _jsx(CircularProgress, {}), children: _jsx(CustomComponentRenderer, { locale: initialLocale, componentId: initialComponentId || state.metadata.id, dev: { mode: 'draft', components: mergedAllBlocks, defaultLocale }, properties: mergedPropertiesValues }, `custom-${renderComponentTrigger}`) }) }) }));
|
|
256
387
|
}
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
388
|
+
// 外部容器,包含设备切换按钮和iframe
|
|
389
|
+
return (_jsxs(Box, { sx: { height: '100%', display: 'flex', flexDirection: 'column' }, children: [_jsx(Box, { sx: { p: 1, display: 'flex', justifyContent: 'center', borderBottom: '1px solid #e0e0e0' }, children: _jsxs(ToggleButtonGroup, { size: "small", exclusive: true, color: "primary", value: state.simulatorType, onChange: (_, value) => (state.simulatorType = value ?? state.simulatorType), sx: {
|
|
390
|
+
'& > button': {
|
|
391
|
+
p: 0.75,
|
|
392
|
+
minWidth: 50,
|
|
393
|
+
},
|
|
394
|
+
}, children: [_jsx(ToggleButton, { value: "pc", children: _jsx(LaptopMacIcon, { fontSize: "small" }) }), _jsx(ToggleButton, { value: "mobile", children: _jsx(PhoneAndroidIcon, { fontSize: "small" }) })] }) }), _jsx(Box, { sx: {
|
|
395
|
+
height: '100%',
|
|
396
|
+
width: '100%',
|
|
397
|
+
display: 'flex',
|
|
398
|
+
justifyContent: 'center',
|
|
399
|
+
flex: 1,
|
|
400
|
+
overflowY: 'auto',
|
|
401
|
+
position: 'relative', // 为绝对定位的loading提供参考
|
|
402
|
+
p: 1.5,
|
|
403
|
+
alignItems: 'center',
|
|
404
|
+
}, children: _jsxs(Box, { sx: {
|
|
405
|
+
position: 'relative',
|
|
406
|
+
height: '100%',
|
|
407
|
+
width: state.simulatorType === 'mobile' ? '375px' : '100%',
|
|
408
|
+
maxWidth: '100%',
|
|
409
|
+
display: 'flex',
|
|
410
|
+
flexDirection: 'column',
|
|
411
|
+
}, children: [state.simulatorType === 'pc' && (_jsx(Box, { sx: {
|
|
412
|
+
height: '30px',
|
|
413
|
+
backgroundColor: '#f5f5f5',
|
|
414
|
+
borderTopLeftRadius: '6px',
|
|
415
|
+
borderTopRightRadius: '6px',
|
|
416
|
+
borderTop: '1px solid #e0e0e0',
|
|
417
|
+
borderLeft: '1px solid #e0e0e0',
|
|
418
|
+
borderRight: '1px solid #e0e0e0',
|
|
419
|
+
display: 'flex',
|
|
420
|
+
alignItems: 'center',
|
|
421
|
+
px: 1.5,
|
|
422
|
+
}, children: _jsxs(Box, { sx: { display: 'flex', gap: 0.7, alignItems: 'center' }, children: [_jsx(Box, { sx: { width: 12, height: 12, borderRadius: '50%', backgroundColor: '#ff5f57' } }), _jsx(Box, { sx: { width: 12, height: 12, borderRadius: '50%', backgroundColor: '#febc2e' } }), _jsx(Box, { sx: { width: 12, height: 12, borderRadius: '50%', backgroundColor: '#28c840' } })] }) })), state.simulatorType === 'mobile' && (_jsxs(Box, { sx: {
|
|
423
|
+
height: '30px',
|
|
424
|
+
backgroundColor: '#111',
|
|
425
|
+
borderTopLeftRadius: '20px',
|
|
426
|
+
borderTopRightRadius: '20px',
|
|
427
|
+
display: 'flex',
|
|
428
|
+
justifyContent: 'center',
|
|
429
|
+
alignItems: 'center',
|
|
430
|
+
boxShadow: '0 0 0 12px #111',
|
|
431
|
+
mt: 2,
|
|
432
|
+
}, children: [_jsx(Box, { sx: {
|
|
433
|
+
width: '60px',
|
|
434
|
+
height: '5px',
|
|
435
|
+
backgroundColor: '#333',
|
|
436
|
+
borderRadius: '3px',
|
|
437
|
+
} }), _jsx(Box, { sx: {
|
|
438
|
+
width: '40%',
|
|
439
|
+
height: '5px',
|
|
440
|
+
backgroundColor: '#333',
|
|
441
|
+
borderRadius: '3px',
|
|
442
|
+
position: 'absolute',
|
|
443
|
+
bottom: 20,
|
|
444
|
+
left: '50%',
|
|
445
|
+
transform: 'translateX(-50%)',
|
|
446
|
+
} })] })), !state.iframeLoaded && !isInsideIframe && (_jsx(Box, { sx: {
|
|
447
|
+
position: 'absolute',
|
|
448
|
+
top: 0,
|
|
449
|
+
left: 0,
|
|
450
|
+
right: 0,
|
|
451
|
+
bottom: 0,
|
|
452
|
+
display: 'flex',
|
|
453
|
+
justifyContent: 'center',
|
|
454
|
+
alignItems: 'center',
|
|
455
|
+
zIndex: 2,
|
|
456
|
+
}, children: _jsx(CircularProgress, {}) })), _jsxs(Box, { sx: {
|
|
457
|
+
border: 'none',
|
|
458
|
+
height: state.simulatorType === 'mobile' ? 'calc(100% - 60px)' : 'calc(100% - 30px)',
|
|
459
|
+
display: 'flex',
|
|
460
|
+
width: '100%',
|
|
461
|
+
backgroundColor: '#fff',
|
|
462
|
+
...(state.simulatorType === 'mobile'
|
|
463
|
+
? {
|
|
464
|
+
borderRadius: '0 0 20px 20px',
|
|
465
|
+
boxShadow: '0 0 0 12px #111',
|
|
466
|
+
}
|
|
467
|
+
: {
|
|
468
|
+
borderBottomLeftRadius: '6px',
|
|
469
|
+
borderBottomRightRadius: '6px',
|
|
470
|
+
border: '1px solid #e0e0e0',
|
|
471
|
+
}),
|
|
472
|
+
}, children: [state.draggingSplitPane && DraggingSplitPlaceholder, _jsx(Box, { id: "preview-iframe", component: "iframe", src: iframeUrl, sx: {
|
|
473
|
+
flex: 1,
|
|
474
|
+
border: 'none',
|
|
475
|
+
display: state.draggingSplitPane ? 'none' : 'flex',
|
|
476
|
+
}, onLoad: () => {
|
|
477
|
+
state.iframeLoaded = true;
|
|
478
|
+
}, title: "Component Preview" })] })] }) })] }, "middle-panel"));
|
|
479
|
+
}, [
|
|
480
|
+
loadedData,
|
|
481
|
+
loadState,
|
|
482
|
+
iframeUrl,
|
|
483
|
+
state.simulatorType,
|
|
484
|
+
state.draggingSplitPane,
|
|
485
|
+
renderComponentTrigger,
|
|
486
|
+
isInsideIframe,
|
|
487
|
+
state.iframeLoaded,
|
|
488
|
+
locale,
|
|
489
|
+
]);
|
|
490
|
+
const rightPanelContent = useMemo(() => {
|
|
491
|
+
if (state.draggingSplitPane) {
|
|
492
|
+
return DraggingSplitPlaceholder;
|
|
493
|
+
}
|
|
494
|
+
return (_jsxs(List, { sx: { height: '100%', overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 1 }, children: [_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) => {
|
|
495
|
+
updater(state.metadata);
|
|
496
|
+
}, useI18nEditor: false }), _jsxs(Stack, { direction: "column", spacing: 1, sx: { mt: 1 }, children: [_jsx(Button, { variant: "contained", size: "small", color: "primary", onClick: async () => {
|
|
497
|
+
try {
|
|
498
|
+
const { dataPath } = getStaticData() || {};
|
|
499
|
+
if (!dataPath) {
|
|
500
|
+
Toast.error(t('themeTranslations.componentPathNotFound'));
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
Toast.info(t('themeTranslations.analyzingInterface'));
|
|
504
|
+
const response = await api.post('/api/blocks/interface-to-properties', {
|
|
505
|
+
componentPath: dataPath,
|
|
506
|
+
});
|
|
507
|
+
if (response.data.success) {
|
|
508
|
+
const { currentProperties, newProperties } = response.data;
|
|
509
|
+
state.previewDialog = {
|
|
510
|
+
open: true,
|
|
511
|
+
title: t('themeTranslations.interfacePropertiesPreview'),
|
|
512
|
+
leftTitle: t('themeTranslations.currentProperties'),
|
|
513
|
+
leftContent: JSON.stringify(currentProperties, null, 2),
|
|
514
|
+
rightTitle: t('themeTranslations.newProperties'),
|
|
515
|
+
rightContent: JSON.stringify(newProperties, null, 2),
|
|
516
|
+
description: t('themeTranslations.confirmUpdateMetadata'),
|
|
517
|
+
loading: false,
|
|
518
|
+
onConfirm: async () => {
|
|
519
|
+
state.previewDialog.loading = true;
|
|
520
|
+
try {
|
|
521
|
+
const updateResponse = await api.post('/api/blocks/interface-to-properties', {
|
|
522
|
+
componentPath: dataPath,
|
|
523
|
+
write: true,
|
|
524
|
+
});
|
|
525
|
+
if (updateResponse.data.success) {
|
|
526
|
+
Toast.success(t('themeTranslations.metadataSuccess'));
|
|
527
|
+
// 更新当前的metadata状态
|
|
528
|
+
state.metadata = {
|
|
529
|
+
...updateResponse.data.metadata,
|
|
530
|
+
renderer: state.metadata.renderer,
|
|
531
|
+
};
|
|
532
|
+
state.previewDialog.open = false;
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
throw new Error(updateResponse.data.error || t('themeTranslations.generationFailed'));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
finally {
|
|
539
|
+
state.previewDialog.loading = false;
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
Toast.error(response.data.error || t('themeTranslations.previewFailed'));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
catch (error) {
|
|
549
|
+
console.error(error);
|
|
550
|
+
Toast.error(t('themeTranslations.previewFailed'));
|
|
551
|
+
}
|
|
552
|
+
}, children: t('themeTranslations.interfaceToProperties') }), _jsx(Button, { variant: "outlined", size: "small", color: "primary", onClick: async () => {
|
|
553
|
+
try {
|
|
554
|
+
const { dataPath } = getStaticData() || {};
|
|
555
|
+
if (!dataPath) {
|
|
556
|
+
Toast.error(t('themeTranslations.componentPathNotFound'));
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
Toast.info(t('themeTranslations.generatingInterface'));
|
|
560
|
+
const response = await api.post('/api/blocks/properties-to-interface', {
|
|
561
|
+
componentPath: dataPath,
|
|
562
|
+
});
|
|
563
|
+
if (response.data.success) {
|
|
564
|
+
const { currentInterface, newInterface } = response.data;
|
|
565
|
+
state.previewDialog = {
|
|
566
|
+
open: true,
|
|
567
|
+
title: t('themeTranslations.propertiesInterfacePreview'),
|
|
568
|
+
leftTitle: t('themeTranslations.currentInterface'),
|
|
569
|
+
leftContent: currentInterface,
|
|
570
|
+
rightTitle: t('themeTranslations.newInterface'),
|
|
571
|
+
rightContent: newInterface,
|
|
572
|
+
description: t('themeTranslations.confirmUpdateInterface'),
|
|
573
|
+
loading: false,
|
|
574
|
+
onConfirm: async () => {
|
|
575
|
+
state.previewDialog.loading = true;
|
|
576
|
+
try {
|
|
577
|
+
const updateResponse = await api.post('/api/blocks/properties-to-interface', {
|
|
578
|
+
componentPath: dataPath,
|
|
579
|
+
write: true,
|
|
580
|
+
});
|
|
581
|
+
if (updateResponse.data.success) {
|
|
582
|
+
Toast.success(t('themeTranslations.interfaceSuccess'));
|
|
583
|
+
state.previewDialog.open = false;
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
throw new Error(updateResponse.data.error || t('themeTranslations.generationFailed'));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
finally {
|
|
590
|
+
state.previewDialog.loading = false;
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
Toast.error(response.data.error || t('themeTranslations.previewGenerationFailed'));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
console.error(error);
|
|
601
|
+
Toast.error(t('themeTranslations.interfacePreviewFailed'));
|
|
602
|
+
}
|
|
603
|
+
}, children: t('themeTranslations.propertiesToInterface') })] })] }) }), state.metadata.id && (_jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(ParametersConfig, { config: state.metadata, allComponents: mergedAllBlocks, defaultLocale: defaultLocale, locale: locale, propertiesValue: mergedPropertiesValues, onChange: ({ key, value, id, path, ...rest }) => {
|
|
604
|
+
const realPath = [...path, 'data'];
|
|
605
|
+
const property = get(state.metadata, realPath);
|
|
606
|
+
// ensure property exist
|
|
607
|
+
if (!property) {
|
|
608
|
+
Toast.warning(`${t('themeTranslations.propertyNotFound')} ${realPath.join('.')}`);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const realValue = parsePropertyValue(property, value.value, {
|
|
612
|
+
locale,
|
|
613
|
+
defaultLocale,
|
|
614
|
+
});
|
|
615
|
+
state.propertiesValue[id] = {
|
|
616
|
+
value: realValue,
|
|
617
|
+
};
|
|
618
|
+
} }) }) }))] }));
|
|
619
|
+
}, [
|
|
620
|
+
JSON.stringify(state.metadata || {}),
|
|
621
|
+
locale,
|
|
622
|
+
defaultLocale,
|
|
623
|
+
JSON.stringify(mergedAllBlocks || []),
|
|
624
|
+
JSON.stringify(mergedPropertiesValues || {}),
|
|
625
|
+
getStaticData,
|
|
626
|
+
state.draggingSplitPane,
|
|
627
|
+
]);
|
|
265
628
|
// add auto redirect to first route
|
|
266
629
|
if (loadState.type === '404' &&
|
|
267
630
|
!routes.includes(location.pathname) &&
|
|
268
631
|
!location.search.includes('no-redirect=true')) {
|
|
269
632
|
return _jsx(Navigate, { to: `${firstRoute ?? '/'}`, replace: true });
|
|
270
633
|
}
|
|
634
|
+
if (isInsideIframe) {
|
|
635
|
+
return middlePanelContent;
|
|
636
|
+
}
|
|
271
637
|
return (_jsx(DndProvider, { backend: HTML5Backend, children: _jsxs(StyledDashboard, { HeaderProps: {
|
|
272
638
|
// @ts-ignore
|
|
273
639
|
homeLink: joinURL(basename),
|
|
@@ -275,7 +641,7 @@ function Layout({ loadState, loadedData }) {
|
|
|
275
641
|
return [
|
|
276
642
|
_jsx(Button, { onClick: async () => {
|
|
277
643
|
if (!session?.user?.did) {
|
|
278
|
-
Toast.warning('
|
|
644
|
+
Toast.warning(t('themeTranslations.connectWallet'));
|
|
279
645
|
await session.login();
|
|
280
646
|
setTimeout(() => {
|
|
281
647
|
state.createResourceOpen = true;
|
|
@@ -284,208 +650,35 @@ function Layout({ loadState, loadedData }) {
|
|
|
284
650
|
else {
|
|
285
651
|
state.createResourceOpen = true;
|
|
286
652
|
}
|
|
287
|
-
}, children:
|
|
653
|
+
}, children: t('themeTranslations.createResource') }, "logout"),
|
|
288
654
|
...addons,
|
|
289
655
|
];
|
|
290
656
|
},
|
|
291
|
-
}, MenusDrawerProps: { sx: { [`.${backdropClasses.root}`]: { top: 64 } } }, children: [_jsxs(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
position: 'relative',
|
|
299
|
-
height: '100%',
|
|
657
|
+
}, MenusDrawerProps: { sx: { [`.${backdropClasses.root}`]: { top: 64 } } }, children: [_jsxs(StyledSplitPane, { split: "vertical", sizes: hSizes, onChange: setHSizes, sashRender: SashRender, onDragStart: () => (state.draggingSplitPane = true), onDragEnd: () => (state.draggingSplitPane = false), sx: {
|
|
658
|
+
'&.react-split--dragging': {
|
|
659
|
+
'.react-split__pane': {
|
|
660
|
+
'*': {
|
|
661
|
+
userSelect: 'none',
|
|
662
|
+
},
|
|
663
|
+
},
|
|
300
664
|
},
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
} }), _jsx(Button, { variant: "contained", sx: { minWidth: 'auto' }, onClick: () => {
|
|
304
|
-
state.createBlockOpen = true;
|
|
305
|
-
}, children: _jsx(AddIcon, { fontSize: "small" }) })] }), _jsx(List, { sx: { pr: 1, overflowY: 'auto' }, children: routes
|
|
306
|
-
.map((route) => {
|
|
307
|
-
const routeName = route;
|
|
308
|
-
const staticDataInRoute = staticData[route]?.main;
|
|
309
|
-
if (state.searchValue && !routeName?.toLowerCase().includes(state.searchValue?.toLowerCase())) {
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
if (!state.allComponents?.find(({ blockName }) => `/${blockName}` === routeName)) {
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
return (_jsx(ListItem, { disablePadding: true, children: _jsx(ListItemButton, { selected: currentPage.pageId === route, onClick: () => {
|
|
316
|
-
navigate(route);
|
|
317
|
-
}, sx: {
|
|
318
|
-
borderRadius: 1,
|
|
319
|
-
mb: 1,
|
|
320
|
-
width: '100%',
|
|
321
|
-
textOverflow: 'ellipsis',
|
|
322
|
-
whiteSpace: 'nowrap',
|
|
323
|
-
overflowX: 'hidden',
|
|
324
|
-
transition: 'all 0.3s ease',
|
|
325
|
-
'&.Mui-selected': {
|
|
326
|
-
backgroundColor: 'primary.main',
|
|
327
|
-
color: 'white',
|
|
328
|
-
'&:hover': {
|
|
329
|
-
backgroundColor: 'primary.main',
|
|
330
|
-
},
|
|
331
|
-
},
|
|
332
|
-
fontSize: '14px',
|
|
333
|
-
}, children: _jsx(Tooltip, { title: staticDataInRoute.blockName || routeName, children: _jsx("div", { style: {
|
|
334
|
-
width: '100%',
|
|
335
|
-
overflow: 'hidden',
|
|
336
|
-
textOverflow: 'ellipsis',
|
|
337
|
-
}, children: staticDataInRoute.blockName || routeName }) }) }) }, route));
|
|
338
|
-
})
|
|
339
|
-
.filter(Boolean) })] }), _jsx(Box, { sx: { flex: 1, overflowX: 'hidden', overflowY: getStaticData()?.isHtml ? 'hidden' : 'auto' }, children: _jsx(ThemeProvider, { theme: pagesTheme, children: _jsx(Suspense, { children: getRenderContent() }) }) }), _jsx(Drawer, { variant: "permanent", anchor: "right", sx: {
|
|
340
|
-
width: RIGHT_DRAWER_WIDTH,
|
|
341
|
-
flexShrink: 0,
|
|
342
|
-
zIndex: 1000,
|
|
343
|
-
'& .MuiDrawer-paper': {
|
|
344
|
-
width: RIGHT_DRAWER_WIDTH,
|
|
345
|
-
boxSizing: 'border-box',
|
|
346
|
-
position: 'relative',
|
|
347
|
-
height: '100%',
|
|
348
|
-
},
|
|
349
|
-
}, children: _jsxs(List, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_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) => {
|
|
350
|
-
updater(state.metadata);
|
|
351
|
-
}, useI18nEditor: false }), _jsxs(Stack, { direction: "column", spacing: 1, sx: { mt: 1 }, children: [_jsx(Button, { variant: "contained", size: "small", color: "primary", onClick: async () => {
|
|
352
|
-
try {
|
|
353
|
-
const { dataPath } = getStaticData() || {};
|
|
354
|
-
if (!dataPath) {
|
|
355
|
-
Toast.error('无法找到组件路径');
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
Toast.info('正在分析组件接口...');
|
|
359
|
-
const response = await api.post('/api/blocks/interface-to-properties', {
|
|
360
|
-
componentPath: dataPath,
|
|
361
|
-
});
|
|
362
|
-
if (response.data.success) {
|
|
363
|
-
const { currentProperties, newProperties } = response.data;
|
|
364
|
-
state.previewDialog = {
|
|
365
|
-
open: true,
|
|
366
|
-
title: 'Interface → Properties 预览',
|
|
367
|
-
leftTitle: '当前 Properties',
|
|
368
|
-
leftContent: JSON.stringify(currentProperties, null, 2),
|
|
369
|
-
rightTitle: '新 Properties',
|
|
370
|
-
rightContent: JSON.stringify(newProperties, null, 2),
|
|
371
|
-
description: '确认后将更新metadata文件。这将保留现有配置值,但可能更改属性结构。',
|
|
372
|
-
loading: false,
|
|
373
|
-
onConfirm: async () => {
|
|
374
|
-
state.previewDialog.loading = true;
|
|
375
|
-
try {
|
|
376
|
-
const updateResponse = await api.post('/api/blocks/interface-to-properties', {
|
|
377
|
-
componentPath: dataPath,
|
|
378
|
-
write: true,
|
|
379
|
-
});
|
|
380
|
-
if (updateResponse.data.success) {
|
|
381
|
-
Toast.success('Metadata生成成功!');
|
|
382
|
-
// 更新当前的metadata状态
|
|
383
|
-
state.metadata = {
|
|
384
|
-
...updateResponse.data.metadata,
|
|
385
|
-
renderer: state.metadata.renderer,
|
|
386
|
-
};
|
|
387
|
-
state.previewDialog.open = false;
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
throw new Error(updateResponse.data.error || '生成失败');
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
finally {
|
|
394
|
-
state.previewDialog.loading = false;
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
Toast.error(response.data.error || '预览失败');
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
catch (error) {
|
|
404
|
-
console.error('生成预览失败:', error);
|
|
405
|
-
Toast.error('生成预览失败');
|
|
406
|
-
}
|
|
407
|
-
}, children: "Interface \u2192 Properties" }), _jsx(Button, { variant: "outlined", size: "small", color: "primary", onClick: async () => {
|
|
408
|
-
try {
|
|
409
|
-
const { dataPath } = getStaticData() || {};
|
|
410
|
-
if (!dataPath) {
|
|
411
|
-
Toast.error('无法找到组件路径');
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
Toast.info('正在生成TypeScript接口预览...');
|
|
415
|
-
const response = await api.post('/api/blocks/properties-to-interface', {
|
|
416
|
-
componentPath: dataPath,
|
|
417
|
-
});
|
|
418
|
-
if (response.data.success) {
|
|
419
|
-
const { currentInterface, newInterface } = response.data;
|
|
420
|
-
state.previewDialog = {
|
|
421
|
-
open: true,
|
|
422
|
-
title: 'Properties → Interface 预览',
|
|
423
|
-
leftTitle: '当前接口',
|
|
424
|
-
leftContent: currentInterface,
|
|
425
|
-
rightTitle: '新接口',
|
|
426
|
-
rightContent: newInterface,
|
|
427
|
-
description: '确认后将更新TypeScript接口。这将覆盖当前的接口定义。',
|
|
428
|
-
loading: false,
|
|
429
|
-
onConfirm: async () => {
|
|
430
|
-
state.previewDialog.loading = true;
|
|
431
|
-
try {
|
|
432
|
-
const updateResponse = await api.post('/api/blocks/properties-to-interface', {
|
|
433
|
-
componentPath: dataPath,
|
|
434
|
-
write: true,
|
|
435
|
-
});
|
|
436
|
-
if (updateResponse.data.success) {
|
|
437
|
-
Toast.success('TypeScript接口生成成功!');
|
|
438
|
-
state.previewDialog.open = false;
|
|
439
|
-
}
|
|
440
|
-
else {
|
|
441
|
-
throw new Error(updateResponse.data.error || '生成失败');
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
finally {
|
|
445
|
-
state.previewDialog.loading = false;
|
|
446
|
-
}
|
|
447
|
-
},
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
Toast.error(response.data.error || '预览失败');
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
console.error('生成接口预览失败:', error);
|
|
456
|
-
Toast.error('生成接口预览失败');
|
|
457
|
-
}
|
|
458
|
-
}, children: "Properties \u2192 Interface" })] })] }) }), state.metadata.id && (_jsx(ListItem, { children: _jsx(Box, { sx: { width: '100%' }, children: _jsx(ParametersConfig, { config: state.metadata, allComponents: mergedAllBlocks, defaultLocale: defaultLocale, locale: locale, propertiesValue: mergedPropertiesValues, onChange: ({ key, value, id, path, ...rest }) => {
|
|
459
|
-
const realPath = [...path, 'data'];
|
|
460
|
-
const property = get(state.metadata, realPath);
|
|
461
|
-
// ensure property exist
|
|
462
|
-
if (!property) {
|
|
463
|
-
Toast.warning(`无法找到属性,请检查在 @metadata.json 中,是否存在该属性: ${realPath.join('.')}`);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
const realValue = parsePropertyValue(property, value.value, {
|
|
467
|
-
locale,
|
|
468
|
-
defaultLocale,
|
|
469
|
-
});
|
|
470
|
-
state.propertiesValue[id] = {
|
|
471
|
-
value: realValue,
|
|
472
|
-
};
|
|
473
|
-
} }) }) }))] }) }), _jsx(CreateResource, { open: state.createResourceOpen, onClose: () => {
|
|
665
|
+
flex: 1,
|
|
666
|
+
}, children: [_jsx(Pane, { minSize: 100, maxSize: 400, children: leftPanelContent }), _jsx(Pane, { minSize: 400, children: middlePanelContent }), _jsx(Pane, { minSize: 100, maxSize: '30%', children: rightPanelContent })] }), _jsx(CreateResource, { open: state.createResourceOpen, onClose: () => {
|
|
474
667
|
state.createResourceOpen = false;
|
|
475
|
-
} }), _jsxs(Dialog, { open: state.createBlockOpen, onClose: onCloseCreateBlock, children: [_jsx(DialogTitle, { children:
|
|
668
|
+
} }), _jsxs(Dialog, { open: state.createBlockOpen, onClose: onCloseCreateBlock, children: [_jsx(DialogTitle, { children: t('themeTranslations.createNewBlock') }), _jsx(DialogContent, { children: _jsxs(Stack, { spacing: 2, sx: { pt: 1, minWidth: 300 }, children: [_jsx(TextField, { autoFocus: true, required: true, label: t('themeTranslations.name'), fullWidth: true, value: state.newBlockParams.name, onChange: (e) => {
|
|
476
669
|
state.newBlockParams.name = e.target.value.replace(/[^a-zA-Z0-9-]/g, '');
|
|
477
|
-
} }), _jsx(TextField, { label:
|
|
670
|
+
} }), _jsx(TextField, { label: t('themeTranslations.description'), fullWidth: true, multiline: true, rows: 3, value: state.newBlockParams.description, onChange: (e) => {
|
|
478
671
|
state.newBlockParams.description = e.target.value;
|
|
479
672
|
} }), _jsx(Button, { variant: "contained", fullWidth: true, onClick: async () => {
|
|
480
673
|
if (!state.newBlockParams.name) {
|
|
481
|
-
Toast.warning('
|
|
674
|
+
Toast.warning(t('themeTranslations.blockNameRequired'));
|
|
482
675
|
return;
|
|
483
676
|
}
|
|
484
677
|
if (routes.some((route) => {
|
|
485
678
|
const staticDataInRoute = staticData[route]?.main;
|
|
486
679
|
return staticDataInRoute?.blockName?.toLowerCase() === state.newBlockParams.name.toLowerCase();
|
|
487
680
|
})) {
|
|
488
|
-
Toast.warning('
|
|
681
|
+
Toast.warning(t('themeTranslations.blockNameExists'));
|
|
489
682
|
return;
|
|
490
683
|
}
|
|
491
684
|
try {
|
|
@@ -498,12 +691,29 @@ function Layout({ loadState, loadedData }) {
|
|
|
498
691
|
}
|
|
499
692
|
catch (error) {
|
|
500
693
|
console.error('Failed to create block:', error);
|
|
501
|
-
Toast.error('
|
|
694
|
+
Toast.error(t('themeTranslations.failedCreateBlock'));
|
|
502
695
|
}
|
|
503
|
-
}, children:
|
|
696
|
+
}, 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: () => {
|
|
504
697
|
state.previewDialog.open = false;
|
|
505
698
|
} })] }) }));
|
|
506
699
|
}
|
|
700
|
+
// Add SplitPane styling
|
|
701
|
+
const StyledSplitPane = styled(SplitPane) `
|
|
702
|
+
.react-split__sash {
|
|
703
|
+
z-index: 1000; // resolve the bug of uploader zIndex
|
|
704
|
+
}
|
|
705
|
+
`;
|
|
706
|
+
function SashRender() {
|
|
707
|
+
return _jsx(DragHandle, {});
|
|
708
|
+
}
|
|
709
|
+
const DragHandle = styled('div') `
|
|
710
|
+
height: 100%;
|
|
711
|
+
background-color: #f0f0f0;
|
|
712
|
+
|
|
713
|
+
&:hover {
|
|
714
|
+
background-color: #e3e3e3;
|
|
715
|
+
}
|
|
716
|
+
`;
|
|
507
717
|
const StyledDashboard = styled(Dashboard) `
|
|
508
718
|
.dashboard-content {
|
|
509
719
|
display: flex;
|
|
@@ -557,3 +767,85 @@ function CreateResource({ open, onClose }) {
|
|
|
557
767
|
// 默认选中的资源
|
|
558
768
|
resources: {} }) }));
|
|
559
769
|
}
|
|
770
|
+
// 添加 themeTranslations 到 translations 对象
|
|
771
|
+
// 这里我们在运行时扩展 translations 对象,而不是修改源文件
|
|
772
|
+
if (!translations.en.themeTranslations) {
|
|
773
|
+
translations.en.themeTranslations = {
|
|
774
|
+
search: 'Search Blocks...',
|
|
775
|
+
createNewBlock: 'Create New Block',
|
|
776
|
+
name: 'Name',
|
|
777
|
+
description: 'Description',
|
|
778
|
+
create: 'Create',
|
|
779
|
+
blockNameRequired: 'Block name is required',
|
|
780
|
+
blockNameExists: 'Block name already exists, please change it',
|
|
781
|
+
failedCreateBlock: 'Failed to create block',
|
|
782
|
+
createResource: 'Create Resource',
|
|
783
|
+
failedLoadCode: 'Failed to load component code',
|
|
784
|
+
interfaceToProperties: 'Interface → Properties',
|
|
785
|
+
propertiesToInterface: 'Properties → Interface',
|
|
786
|
+
componentPathNotFound: 'Component path not found',
|
|
787
|
+
analyzingInterface: 'Analyzing component interface...',
|
|
788
|
+
interfacePropertiesPreview: 'Interface → Properties Preview',
|
|
789
|
+
currentProperties: 'Current Properties',
|
|
790
|
+
newProperties: 'New Properties',
|
|
791
|
+
confirmUpdateMetadata: 'Confirm to update metadata file. This will preserve existing configuration values, but may change property structure.',
|
|
792
|
+
metadataSuccess: 'Metadata generated successfully!',
|
|
793
|
+
generationFailed: 'Generation failed',
|
|
794
|
+
previewFailed: 'Failed to generate preview',
|
|
795
|
+
generatingInterface: 'Generating TypeScript interface preview...',
|
|
796
|
+
propertiesInterfacePreview: 'Properties → Interface Preview',
|
|
797
|
+
currentInterface: 'Current Interface',
|
|
798
|
+
newInterface: 'New Interface',
|
|
799
|
+
confirmUpdateInterface: 'Confirm to update TypeScript interface. This will overwrite the current interface definition.',
|
|
800
|
+
interfaceSuccess: 'TypeScript interface generated successfully!',
|
|
801
|
+
previewGenerationFailed: 'Preview failed',
|
|
802
|
+
interfacePreviewFailed: 'Failed to generate interface preview',
|
|
803
|
+
propertyNotFound: 'Property not found, please check if it exists in @metadata.json:',
|
|
804
|
+
connectWallet: 'Please connect wallet first',
|
|
805
|
+
cancel: 'Cancel',
|
|
806
|
+
confirmUpdate: 'Confirm Update',
|
|
807
|
+
configUpdateConfirmation: 'Configuration will be updated after confirmation.',
|
|
808
|
+
operationFailed: 'Operation failed',
|
|
809
|
+
noRoutesFound: 'No Components Found',
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if (!translations.zh.themeTranslations) {
|
|
813
|
+
translations.zh.themeTranslations = {
|
|
814
|
+
search: '搜索组件...',
|
|
815
|
+
createNewBlock: '创建新组件',
|
|
816
|
+
name: '名称',
|
|
817
|
+
description: '描述',
|
|
818
|
+
create: '创建',
|
|
819
|
+
blockNameRequired: '组件名称必填',
|
|
820
|
+
blockNameExists: '组件名称已存在,请更改',
|
|
821
|
+
failedCreateBlock: '创建组件失败',
|
|
822
|
+
createResource: '创建资源',
|
|
823
|
+
failedLoadCode: '加载组件代码失败',
|
|
824
|
+
interfaceToProperties: '接口 → 属性',
|
|
825
|
+
propertiesToInterface: '属性 → 接口',
|
|
826
|
+
componentPathNotFound: '无法找到组件路径',
|
|
827
|
+
analyzingInterface: '正在分析组件接口...',
|
|
828
|
+
interfacePropertiesPreview: '接口 → 属性预览',
|
|
829
|
+
currentProperties: '当前属性',
|
|
830
|
+
newProperties: '新属性',
|
|
831
|
+
confirmUpdateMetadata: '确认后将更新metadata文件。这将保留现有配置值,但可能更改属性结构。',
|
|
832
|
+
metadataSuccess: 'Metadata生成成功!',
|
|
833
|
+
generationFailed: '生成失败',
|
|
834
|
+
previewFailed: '生成预览失败',
|
|
835
|
+
generatingInterface: '正在生成TypeScript接口预览...',
|
|
836
|
+
propertiesInterfacePreview: '属性 → 接口预览',
|
|
837
|
+
currentInterface: '当前接口',
|
|
838
|
+
newInterface: '新接口',
|
|
839
|
+
confirmUpdateInterface: '确认后将更新TypeScript接口。这将覆盖当前的接口定义。',
|
|
840
|
+
interfaceSuccess: 'TypeScript接口生成成功!',
|
|
841
|
+
previewGenerationFailed: '预览失败',
|
|
842
|
+
interfacePreviewFailed: '生成接口预览失败',
|
|
843
|
+
propertyNotFound: '无法找到属性,请检查在 @metadata.json 中,是否存在该属性:',
|
|
844
|
+
connectWallet: '请先连接钱包',
|
|
845
|
+
cancel: '取消',
|
|
846
|
+
confirmUpdate: '确认更新',
|
|
847
|
+
configUpdateConfirmation: '确认后将更新配置。',
|
|
848
|
+
operationFailed: '执行操作失败',
|
|
849
|
+
noRoutesFound: '未找到组件',
|
|
850
|
+
};
|
|
851
|
+
}
|