mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
Merge branch 'main' of https://github.com/webzard-io/sunmao-ui into feat/datasource
This commit is contained in:
commit
5642fde8dc
@ -8,8 +8,8 @@
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"standard"
|
||||
"standard",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
@ -25,12 +25,15 @@
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"no-case-declarations": "off",
|
||||
"no-use-before-define": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error"],
|
||||
"comma-dangle": "off",
|
||||
"space-before-function-paren": "off",
|
||||
"multiline-ternary": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"react/jsx-uses-react": "off",
|
||||
@ -48,5 +51,13 @@
|
||||
"react/jsx-wrap-multilines": ["error"],
|
||||
"react/jsx-tag-spacing": ["error"]
|
||||
},
|
||||
"ignorePatterns": ["node_modules", "*.d.ts", "*.js", "package.json", "*.html", "*.spec.ts", "*.spec.tsx"]
|
||||
"ignorePatterns": [
|
||||
"node_modules",
|
||||
"*.d.ts",
|
||||
"*.js",
|
||||
"package.json",
|
||||
"*.html",
|
||||
"*.spec.ts",
|
||||
"*.spec.tsx"
|
||||
]
|
||||
}
|
||||
|
@ -283,6 +283,9 @@ export default implementRuntimeComponent({
|
||||
border: '1px solid black',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: StyleSchema,
|
||||
|
@ -32,12 +32,15 @@ export default implementRuntimeComponent({
|
||||
isLoading: false,
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {
|
||||
click: void 0,
|
||||
click: undefined,
|
||||
},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
|
@ -53,6 +53,9 @@ export default implementRuntimeComponent({
|
||||
size: 'md',
|
||||
},
|
||||
exampleSize: [3, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -28,6 +28,9 @@ export default implementRuntimeComponent({
|
||||
defaultValue: [],
|
||||
},
|
||||
exampleSize: [3, 3],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import {
|
||||
implementRuntimeComponent,
|
||||
DIALOG_CONTAINER_ID,
|
||||
} from '@sunmao-ui/runtime';
|
||||
import { implementRuntimeComponent, DIALOG_CONTAINER_ID } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
@ -44,6 +41,9 @@ export default implementRuntimeComponent({
|
||||
disableConfirm: false,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -52,8 +52,8 @@ export default implementRuntimeComponent({
|
||||
openDialog: Type.Object({
|
||||
title: Type.String(),
|
||||
}),
|
||||
confirmDialog: void 0,
|
||||
cancelDialog: void 0,
|
||||
confirmDialog: undefined,
|
||||
cancelDialog: undefined,
|
||||
},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
@ -131,9 +131,7 @@ export default implementRuntimeComponent({
|
||||
{...(containerRef.current ? dialogContentProps : {})}
|
||||
>
|
||||
<AlertDialogHeader>{title}</AlertDialogHeader>
|
||||
<AlertDialogBody>
|
||||
{slotsElements.content}
|
||||
</AlertDialogBody>
|
||||
<AlertDialogBody>{slotsElements.content}</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button
|
||||
|
@ -13,6 +13,9 @@ export default implementRuntimeComponent({
|
||||
isResizable: true,
|
||||
exampleProperties: {},
|
||||
exampleSize: [4, 1],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: Type.Object({}),
|
||||
|
@ -20,6 +20,9 @@ export default implementRuntimeComponent({
|
||||
hideSubmit: false,
|
||||
},
|
||||
exampleSize: [4, 6],
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -28,7 +31,7 @@ export default implementRuntimeComponent({
|
||||
disableSubmit: Type.Boolean(),
|
||||
}),
|
||||
methods: {
|
||||
resetForm: void 0,
|
||||
resetForm: undefined,
|
||||
},
|
||||
slots: ['content'],
|
||||
styleSlots: ['content'],
|
||||
@ -44,7 +47,7 @@ export default implementRuntimeComponent({
|
||||
customStyle,
|
||||
slotsElements,
|
||||
childrenMap,
|
||||
component
|
||||
component,
|
||||
}) => {
|
||||
const [invalidArray, setInvalidArray] = useState<boolean[]>([]);
|
||||
const [isFormInvalid, setIsFormInvalid] = useState<boolean>(false);
|
||||
|
@ -40,6 +40,9 @@ export default implementRuntimeComponent({
|
||||
helperText: '',
|
||||
},
|
||||
exampleSize: [4, 2],
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -70,7 +73,10 @@ export default implementRuntimeComponent({
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
// don't show Invalid state on component mount
|
||||
const [hideInvalid, setHideInvalid] = useState(true);
|
||||
const inputId = useMemo(() => first(childrenMap[component.id]?.content)?.id || '', [component.id, childrenMap]);
|
||||
const inputId = useMemo(
|
||||
() => first(childrenMap[component.id]?.content)?.id || '',
|
||||
[component.id, childrenMap]
|
||||
);
|
||||
const [validResult, setValidResult] = useState({
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
|
@ -30,6 +30,9 @@ export default implementRuntimeComponent({
|
||||
exampleSize: [6, 6],
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -103,6 +103,9 @@ export default implementRuntimeComponent({
|
||||
fallbackSrc: 'https://via.placeholder.com/150',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -151,7 +154,7 @@ export default implementRuntimeComponent({
|
||||
ignoreFallback={ignoreFallback}
|
||||
borderRadius={borderRadius}
|
||||
fallbackSrc={fallbackSrc}
|
||||
></BaseImage>
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -71,6 +71,9 @@ export default implementRuntimeComponent({
|
||||
defaultValue: '',
|
||||
},
|
||||
exampleSize: [4, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -79,7 +82,7 @@ export default implementRuntimeComponent({
|
||||
setInputValue: Type.Object({
|
||||
value: Type.String(),
|
||||
}),
|
||||
resetInputValue: void 0,
|
||||
resetInputValue: undefined,
|
||||
},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
|
@ -27,6 +27,9 @@ export default implementRuntimeComponent({
|
||||
},
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -25,6 +25,9 @@ export default implementRuntimeComponent({
|
||||
href: 'https://www.google.com',
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -44,6 +44,9 @@ export default implementRuntimeComponent({
|
||||
isResizable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -64,25 +64,29 @@ const exampleProperties = {
|
||||
};
|
||||
|
||||
export default implementRuntimeComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'multiSelect',
|
||||
displayName: 'MultiSelect',
|
||||
description: 'chakra-ui MultiSelect',
|
||||
isResizable: true,
|
||||
isDraggable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [4, 1],
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'multiSelect',
|
||||
displayName: 'MultiSelect',
|
||||
description: 'chakra-ui MultiSelect',
|
||||
isResizable: true,
|
||||
isDraggable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [4, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(({
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
options,
|
||||
placeholder,
|
||||
defaultValue,
|
||||
@ -97,12 +101,12 @@ export default implementRuntimeComponent({
|
||||
const newValue = (defaultValue || []).map(o => o.value);
|
||||
mergeState({ value: newValue });
|
||||
}, [defaultValue, mergeState]);
|
||||
|
||||
|
||||
const onChange = (options: Static<typeof OptionsSchema>) => {
|
||||
const newValue = options.map(o => o.value);
|
||||
mergeState({ value: newValue });
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box
|
||||
width="full"
|
||||
@ -123,4 +127,5 @@ export default implementRuntimeComponent({
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})
|
||||
}
|
||||
);
|
||||
|
@ -54,6 +54,9 @@ export default implementRuntimeComponent({
|
||||
defaultValue: 0,
|
||||
},
|
||||
exampleSize: [4, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -62,7 +65,7 @@ export default implementRuntimeComponent({
|
||||
setInputValue: Type.Object({
|
||||
value: Type.Number(),
|
||||
}),
|
||||
resetInputValue: void 0,
|
||||
resetInputValue: undefined,
|
||||
},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
|
@ -30,33 +30,37 @@ const PropsSchema = Type.Object({
|
||||
});
|
||||
|
||||
export default implementRuntimeComponent({
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'radio',
|
||||
displayName: 'Radio',
|
||||
description: 'chakra-ui radio',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'Radio',
|
||||
format: 'plain',
|
||||
},
|
||||
value: 'Radio 1',
|
||||
isDisabled: false,
|
||||
size: 'md',
|
||||
version: 'chakra_ui/v1',
|
||||
metadata: {
|
||||
name: 'radio',
|
||||
displayName: 'Radio',
|
||||
description: 'chakra-ui radio',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
text: {
|
||||
raw: 'Radio',
|
||||
format: 'plain',
|
||||
},
|
||||
exampleSize: [3, 1],
|
||||
value: 'Radio 1',
|
||||
isDisabled: false,
|
||||
size: 'md',
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
exampleSize: [3, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
})(({
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
state: StateSchema,
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: ['content'],
|
||||
events: [],
|
||||
},
|
||||
})(
|
||||
({
|
||||
text,
|
||||
value,
|
||||
isDisabled,
|
||||
@ -74,11 +78,11 @@ export default implementRuntimeComponent({
|
||||
useEffect(() => {
|
||||
mergeState({ value: text.raw });
|
||||
}, [mergeState, text.raw]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
mergeState({ value });
|
||||
}, [mergeState, value]);
|
||||
|
||||
|
||||
return (
|
||||
<BaseRadio
|
||||
height="10"
|
||||
@ -99,4 +103,5 @@ export default implementRuntimeComponent({
|
||||
<Text value={text} />
|
||||
</BaseRadio>
|
||||
);
|
||||
})
|
||||
}
|
||||
);
|
||||
|
@ -26,6 +26,9 @@ export default implementRuntimeComponent({
|
||||
isNumerical: true,
|
||||
},
|
||||
exampleSize: [3, 3],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -12,6 +12,9 @@ export default implementRuntimeComponent({
|
||||
isResizable: true,
|
||||
exampleProperties: {},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Advance',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: Type.Object({}),
|
||||
|
@ -68,6 +68,9 @@ export default implementRuntimeComponent({
|
||||
isDraggable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [4, 1],
|
||||
annotations: {
|
||||
category: 'Input',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -58,6 +58,9 @@ export default implementRuntimeComponent({
|
||||
spacing: 10,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -1,3 +1,3 @@
|
||||
import { TableImpl } from './Table';
|
||||
|
||||
export default TableImpl
|
||||
export default TableImpl;
|
||||
|
@ -49,6 +49,9 @@ export const implementTable = implementRuntimeComponent({
|
||||
isResizable: true,
|
||||
exampleProperties,
|
||||
exampleSize: [8, 6],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -58,4 +61,4 @@ export const implementTable = implementRuntimeComponent({
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import {
|
||||
Tabs as BaseTabs,
|
||||
@ -33,6 +33,9 @@ export default implementRuntimeComponent({
|
||||
initialSelectedTabIndex: 0,
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
@ -74,7 +77,9 @@ export default implementRuntimeComponent({
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
{tabNames.map((_, idx) => {
|
||||
const ele = slotsElements.content ? ([] as React.ReactElement[]).concat(slotsElements.content)[idx] : placeholder;
|
||||
const ele = slotsElements.content
|
||||
? ([] as React.ReactElement[]).concat(slotsElements.content)[idx]
|
||||
: placeholder;
|
||||
return (
|
||||
<TabPanel
|
||||
key={idx}
|
||||
|
@ -46,6 +46,9 @@ export default implementRuntimeComponent({
|
||||
text: 'tooltip',
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -10,7 +10,7 @@ const ToastPosition = Type.Union([
|
||||
Type.Literal('bottom-right'),
|
||||
Type.Literal('bottom-left'),
|
||||
]);
|
||||
export const ToastOpenParamterSchema = Type.Object({
|
||||
export const ToastOpenParameterSchema = Type.Object({
|
||||
position: Type.Optional(ToastPosition),
|
||||
duration: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
||||
title: Type.Optional(Type.String()),
|
||||
@ -40,7 +40,7 @@ export const ToastCloseParameterSchema = Type.Object({
|
||||
positions: Type.Optional(Type.Array(ToastPosition)),
|
||||
});
|
||||
|
||||
export type ToastOpenParameter = Static<typeof ToastOpenParamterSchema>;
|
||||
export type ToastOpenParameter = Static<typeof ToastOpenParameterSchema>;
|
||||
export type ToastCloseParameter = Static<typeof ToastCloseParameterSchema>;
|
||||
|
||||
const pickProperty = <T, U extends Record<string, any>>(
|
||||
@ -58,14 +58,15 @@ export default function ToastUtilMethodFactory() {
|
||||
let toast: ReturnType<typeof createStandaloneToast> | undefined;
|
||||
const toastOpen: UtilMethod = {
|
||||
name: 'toast.open',
|
||||
method(parameters: Static<typeof ToastOpenParamterSchema>) {
|
||||
method(parameters: Static<typeof ToastOpenParameterSchema>) {
|
||||
if (!toast) {
|
||||
toast = createStandaloneToast();
|
||||
}
|
||||
if (parameters) {
|
||||
toast(pickProperty(ToastOpenParamterSchema, parameters));
|
||||
toast(pickProperty(ToastOpenParameterSchema, parameters));
|
||||
}
|
||||
},
|
||||
parameters: ToastOpenParameterSchema,
|
||||
};
|
||||
|
||||
const toastClose: UtilMethod = {
|
||||
@ -85,6 +86,7 @@ export default function ToastUtilMethodFactory() {
|
||||
}
|
||||
}
|
||||
},
|
||||
parameters: ToastCloseParameterSchema,
|
||||
};
|
||||
return [toastOpen, toastClose];
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ export default implementRuntimeComponent({
|
||||
exampleSize: [6, 6],
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { parseVersion } from './version';
|
||||
import { parseVersion, Version } from './version';
|
||||
import { ComponentMetadata } from './metadata';
|
||||
import { MethodSchema } from './method';
|
||||
import { Version } from './version';
|
||||
|
||||
// TODO: (type-safe), rename version 2 to normal version
|
||||
|
||||
type ComponentSpec<
|
||||
KMethodName extends string,
|
||||
|
@ -1,9 +1,19 @@
|
||||
export type Metadata = {
|
||||
export type Metadata<TAnnotations = Record<string, unknown>> = {
|
||||
name: string;
|
||||
description?: string;
|
||||
annotations?: Record<string, any> & TAnnotations;
|
||||
};
|
||||
|
||||
export type ComponentMetadata = Metadata & {
|
||||
type ComponentCategory =
|
||||
| (string & {})
|
||||
| 'Layout'
|
||||
| 'Input'
|
||||
| 'Display'
|
||||
| 'Advance'
|
||||
| undefined;
|
||||
|
||||
export type ComponentMetadata = Metadata<{ category?: ComponentCategory }> & {
|
||||
// TODO:(yanzhen): move to annotations
|
||||
isDraggable: boolean;
|
||||
isResizable: boolean;
|
||||
displayName: string;
|
||||
|
@ -22,7 +22,12 @@ function installTern(cm: CodeMirror.Editor) {
|
||||
const t = new CodeMirror.TernServer({ defs: [ecma as unknown as Def] });
|
||||
cm.on('cursorActivity', cm => t.updateArgHints(cm));
|
||||
cm.on('change', (_instance, change) => {
|
||||
if (change.text.length === 1 && change.text[0] === '.') {
|
||||
if (
|
||||
// change happened
|
||||
change.text.length + (change.removed?.length || 0) > 0 &&
|
||||
// not changed by auto-complete
|
||||
change.origin !== 'complete'
|
||||
) {
|
||||
t.complete(cm);
|
||||
}
|
||||
});
|
||||
@ -69,6 +74,9 @@ export const ExpressionEditor: React.FC<{
|
||||
},
|
||||
theme: 'ayu-mirage',
|
||||
viewportMargin: Infinity,
|
||||
hintOptions: {
|
||||
completeSingle: false,
|
||||
},
|
||||
});
|
||||
const t = installTern(cm.current);
|
||||
tServer.current = t.server;
|
||||
@ -93,5 +101,14 @@ export const ExpressionEditor: React.FC<{
|
||||
}
|
||||
}, [defs]);
|
||||
|
||||
return <Box css={style} ref={wrapperEl} height="100%" width="100%" />;
|
||||
return (
|
||||
<Box
|
||||
css={style}
|
||||
ref={wrapperEl}
|
||||
height="100%"
|
||||
width="100%"
|
||||
borderRadius="2"
|
||||
overflow="hidden"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -6,24 +6,22 @@ import { pickBy } from 'lodash-es';
|
||||
import ErrorBoundary from '../ErrorBoundary';
|
||||
|
||||
const theme = {
|
||||
scheme: 'monokai',
|
||||
author: 'wimer hazenberg (http://www.monokai.nl)',
|
||||
base00: '#272822',
|
||||
base01: '#383830',
|
||||
base02: '#49483e',
|
||||
base03: '#75715e',
|
||||
base04: '#a59f85',
|
||||
base05: '#f8f8f2',
|
||||
base06: '#f5f4f1',
|
||||
base07: '#f9f8f5',
|
||||
base08: '#f92672',
|
||||
base09: '#fd971f',
|
||||
base0A: '#f4bf75',
|
||||
base0B: '#a6e22e',
|
||||
base0C: '#a1efe4',
|
||||
base0D: '#66d9ef',
|
||||
base0E: '#ae81ff',
|
||||
base0F: '#cc6633',
|
||||
base0A: '#fded02',
|
||||
base0B: '#01a252',
|
||||
base0C: '#b5e4f4',
|
||||
base0D: '#01a0e4',
|
||||
base0E: '#a16a94',
|
||||
base0F: '#cdab53',
|
||||
base00: '#090300',
|
||||
base01: '#3a3432',
|
||||
base02: '#4a4543',
|
||||
base03: '#5c5855',
|
||||
base04: '#807d7c',
|
||||
base05: '#a5a2a2',
|
||||
base06: '#d6d5d4',
|
||||
base07: '#f7f7f7',
|
||||
base08: '#db2d20',
|
||||
base09: '#e8bbd0',
|
||||
};
|
||||
|
||||
const style = css`
|
||||
@ -31,6 +29,7 @@ const style = css`
|
||||
padding-left: 0.25em !important;
|
||||
flex: 1;
|
||||
margin: 0 !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
@ -19,6 +19,7 @@ import { KeyValueEditor } from '../../KeyValueEditor';
|
||||
import { EditorServices } from '../../../types';
|
||||
import { ComponentModel } from '../../../AppModel/ComponentModel';
|
||||
import { AppModel } from '../../../AppModel/AppModel';
|
||||
import { ComponentId } from '../../../AppModel/IAppModel';
|
||||
|
||||
type Props = {
|
||||
eventTypes: readonly string[];
|
||||
@ -31,7 +32,7 @@ type Props = {
|
||||
|
||||
export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
const { handler, eventTypes, onChange, onRemove, hideEventType, services } = props;
|
||||
const { registry, editorStore } = services;
|
||||
const { registry, editorStore, appModelManager } = services;
|
||||
const { utilMethods } = registry;
|
||||
const { components } = editorStore;
|
||||
const [methods, setMethods] = useState<string[]>([]);
|
||||
@ -43,6 +44,41 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
},
|
||||
});
|
||||
|
||||
const hasParams = useMemo(
|
||||
() => Object.keys(formik.values.method.parameters ?? {}).length,
|
||||
[formik.values.method.parameters]
|
||||
);
|
||||
const params = useMemo(() => {
|
||||
const params: Record<string, string> = {};
|
||||
const { values } = formik;
|
||||
const methodName = values.method.name;
|
||||
|
||||
if (values.method.name) {
|
||||
let parameters = {};
|
||||
|
||||
if (handler.componentId === GLOBAL_UTILS_ID) {
|
||||
const targetMethod = utilMethods.get(methodName);
|
||||
|
||||
parameters = targetMethod?.parameters?.properties ?? {};
|
||||
} else {
|
||||
const targetComponent = appModelManager.appModel.getComponentById(
|
||||
handler.componentId as ComponentId
|
||||
);
|
||||
const targetMethod = (targetComponent?.methods ?? []).find(
|
||||
({ name }) => name === formik.values.method.name
|
||||
);
|
||||
|
||||
parameters = targetMethod?.parameters?.properties ?? {};
|
||||
}
|
||||
|
||||
for (const key in parameters) {
|
||||
params[key] = values.method.parameters?.[key] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}, [formik.values.method.name]);
|
||||
|
||||
const updateMethods = useCallback(
|
||||
(componentId: string) => {
|
||||
if (componentId === GLOBAL_UTILS_ID) {
|
||||
@ -68,6 +104,10 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
formik.setValues(handler);
|
||||
}, [handler]);
|
||||
|
||||
useEffect(() => {
|
||||
formik.setFieldValue('method.parameters', params);
|
||||
}, [params]);
|
||||
|
||||
useEffect(() => {
|
||||
if (handler.componentId) {
|
||||
updateMethods(handler.componentId);
|
||||
@ -76,6 +116,8 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
|
||||
const onTargetComponentChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
updateMethods(e.target.value);
|
||||
formik.handleChange(e);
|
||||
formik.setFieldValue('method', { name: '', parameters: {} });
|
||||
};
|
||||
|
||||
const typeField = (
|
||||
@ -102,10 +144,7 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<Select
|
||||
name="componentId"
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={e => {
|
||||
onTargetComponentChange(e);
|
||||
formik.handleChange(e);
|
||||
}}
|
||||
onChange={onTargetComponentChange}
|
||||
placeholder="Select Target Component"
|
||||
value={formik.values.componentId}
|
||||
>
|
||||
@ -145,6 +184,7 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
formik.setFieldValue('method.parameters', json);
|
||||
formik.submitForm();
|
||||
}}
|
||||
onlySetValue={true}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
@ -195,7 +235,7 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
{hideEventType ? null : typeField}
|
||||
{targetField}
|
||||
{methodField}
|
||||
{parametersField}
|
||||
{hasParams ? parametersField : null}
|
||||
{waitTypeField}
|
||||
{waitTimeField}
|
||||
{disabledField}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { FieldProps } from './fields';
|
||||
import { Switch } from '@chakra-ui/react';
|
||||
|
||||
@ -7,6 +7,12 @@ type Props = FieldProps;
|
||||
const BooleanField: React.FC<Props> = props => {
|
||||
const { formData, onChange } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof formData !== 'boolean') {
|
||||
onChange(false);
|
||||
}
|
||||
}, [formData, onChange]);
|
||||
|
||||
return (
|
||||
<Switch isChecked={formData} onChange={evt => onChange(evt.currentTarget.checked)} />
|
||||
);
|
||||
|
@ -18,6 +18,12 @@ const NumberField: React.FC<Props> = props => {
|
||||
setValue(String(formData));
|
||||
}, [formData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof formData !== 'number') {
|
||||
onChange(1);
|
||||
}
|
||||
}, [formData, onChange]);
|
||||
|
||||
return (
|
||||
<NumberInput
|
||||
value={value}
|
||||
|
@ -88,10 +88,7 @@ type Props = FieldProps & {
|
||||
|
||||
const SchemaField: React.FC<Props> = props => {
|
||||
const { schema, label, formData, onChange, registry, stateManager } = props;
|
||||
const [isExpression, setIsExpression] = useState(
|
||||
// FIXME: regexp copied from FieldModel.ts, is this a stable way to check expression?
|
||||
() => _isExpression(formData)
|
||||
);
|
||||
const [isExpression, setIsExpression] = useState(() => _isExpression(formData));
|
||||
|
||||
if (isEmpty(schema)) {
|
||||
return null;
|
||||
@ -134,6 +131,7 @@ const SchemaField: React.FC<Props> = props => {
|
||||
return (
|
||||
<DefaultTemplate
|
||||
label={label}
|
||||
description={schema.description}
|
||||
displayLabel={displayLabel}
|
||||
codeMode={codeMode}
|
||||
isExpression={isExpression}
|
||||
|
@ -4,6 +4,26 @@ import { Input, Select } from '@chakra-ui/react';
|
||||
|
||||
type Props = FieldProps;
|
||||
|
||||
const EnumField: React.FC<FieldProps> = props => {
|
||||
const { schema, formData, onChange } = props;
|
||||
|
||||
const options = (schema.enum || []).map(item => item?.toString() || '');
|
||||
useEffect(() => {
|
||||
// reset to valid enum
|
||||
if (options.length && !options.includes(formData)) {
|
||||
onChange(options[0]);
|
||||
}
|
||||
}, [options, formData, onChange]);
|
||||
|
||||
return (
|
||||
<Select value={formData} onChange={evt => onChange(evt.currentTarget.value)}>
|
||||
{options.map((value, idx) => {
|
||||
return <option key={idx}>{value}</option>;
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
const StringField: React.FC<Props> = props => {
|
||||
const { schema, formData, onChange } = props;
|
||||
const [value, setValue] = useState(formData);
|
||||
@ -14,14 +34,7 @@ const StringField: React.FC<Props> = props => {
|
||||
|
||||
// enum
|
||||
if (Array.isArray(schema.enum)) {
|
||||
return (
|
||||
<Select value={formData} onChange={evt => onChange(evt.currentTarget.value)}>
|
||||
{schema.enum.map((item, idx) => {
|
||||
const value = item?.toString() || '';
|
||||
return <option key={idx}>{value}</option>;
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
return <EnumField {...props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -8,6 +8,7 @@ import { HEADER_HEIGHT } from '../constants/layout';
|
||||
|
||||
import { genOperation } from '../operations';
|
||||
import { EditorServices } from '../types';
|
||||
import { ExplorerMenuTabs } from '../services/enum';
|
||||
|
||||
type ComponentEditorState = 'drag' | 'select' | 'hover' | 'idle';
|
||||
|
||||
@ -135,6 +136,7 @@ export function useComponentWrapper(services: EditorServices): ComponentWrapperT
|
||||
setHoverComponentId,
|
||||
dragOverComponentId,
|
||||
setDragOverComponentId,
|
||||
setExplorerMenuTab,
|
||||
} = editorStore;
|
||||
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
@ -251,6 +253,7 @@ export function useComponentWrapper(services: EditorServices): ComponentWrapperT
|
||||
e.preventDefault();
|
||||
setDragOverComponentId('');
|
||||
setCurrentSlot(undefined);
|
||||
setExplorerMenuTab(ExplorerMenuTabs.UI_TREE);
|
||||
const creatingComponent = e.dataTransfer?.getData('component') || '';
|
||||
eventBus.send(
|
||||
'operation',
|
||||
|
@ -1,101 +1,143 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
SimpleGrid,
|
||||
Flex,
|
||||
Box,
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
Input,
|
||||
Tag,
|
||||
} from '@chakra-ui/react';
|
||||
import { encodeDragDataTransfer, DROP_EXAMPLE_SIZE_PREFIX } from '@sunmao-ui/runtime';
|
||||
import { Registry } from '@sunmao-ui/runtime';
|
||||
import {
|
||||
encodeDragDataTransfer,
|
||||
DROP_EXAMPLE_SIZE_PREFIX,
|
||||
Registry,
|
||||
} from '@sunmao-ui/runtime';
|
||||
import { groupBy, sortBy } from 'lodash-es';
|
||||
|
||||
type Props = {
|
||||
registry: Registry;
|
||||
};
|
||||
|
||||
type Category = {
|
||||
name: string;
|
||||
components: ReturnType<Registry['getAllComponents']>;
|
||||
};
|
||||
|
||||
const PRESET_CATEGORY_ORDER = {
|
||||
Layout: 5,
|
||||
Input: 4,
|
||||
Display: 3,
|
||||
Advance: -Infinity,
|
||||
};
|
||||
|
||||
function getCategoryOrder(name: string): number {
|
||||
return name in PRESET_CATEGORY_ORDER
|
||||
? PRESET_CATEGORY_ORDER[name as keyof typeof PRESET_CATEGORY_ORDER]
|
||||
: 0;
|
||||
}
|
||||
|
||||
function getTagColor(version: string): string {
|
||||
if (version.startsWith('chakra_ui/')) {
|
||||
return 'teal';
|
||||
} else if (version.startsWith('core/v1')) {
|
||||
return 'yellow';
|
||||
} else {
|
||||
return 'blackAlpha';
|
||||
}
|
||||
}
|
||||
|
||||
export const ComponentList: React.FC<Props> = ({ registry }) => {
|
||||
const [filterText, setFilterText] = useState('');
|
||||
const categories = useMemo<Category[]>(() => {
|
||||
const grouped = groupBy(
|
||||
registry.getAllComponents().filter(c => {
|
||||
if (!filterText) {
|
||||
return true;
|
||||
}
|
||||
return new RegExp(filterText, 'i').test(c.metadata.displayName);
|
||||
}),
|
||||
c => c.metadata.annotations?.category || 'Advance'
|
||||
);
|
||||
return sortBy(
|
||||
Object.keys(grouped).map(name => ({
|
||||
name,
|
||||
components: sortBy(grouped[name], 'metadata.name'),
|
||||
})),
|
||||
c => -getCategoryOrder(c.name)
|
||||
);
|
||||
}, [filterText, registry]);
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<TabList overflow="auto">
|
||||
{Array.from(registry.components.keys()).map(version => (
|
||||
<Tab key={version}>{version}</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
{Array.from(registry.components.keys()).map(version => (
|
||||
<TabPanel key={version} overflow="auto">
|
||||
<SimpleGrid columns={4} spacing={1}>
|
||||
{Array.from(registry.components.get(version)!.values()).map(c => {
|
||||
const onDragStart = (e: any) => {
|
||||
e.dataTransfer.setData('component', `${c.version}/${c.metadata.name}`);
|
||||
// pass the exampleSize to gridlayout to render placeholder
|
||||
e.dataTransfer.setData(
|
||||
encodeDragDataTransfer(
|
||||
`${DROP_EXAMPLE_SIZE_PREFIX}${JSON.stringify(
|
||||
c.metadata.exampleSize
|
||||
)}`
|
||||
),
|
||||
''
|
||||
);
|
||||
};
|
||||
const cEle = (
|
||||
<Flex
|
||||
key={c.metadata.name}
|
||||
flexDirection="column"
|
||||
align="center"
|
||||
justify="start"
|
||||
>
|
||||
<>
|
||||
<Input
|
||||
placeholder="filter the components"
|
||||
value={filterText}
|
||||
onChange={evt => setFilterText(evt.currentTarget.value)}
|
||||
/>
|
||||
<Accordion allowMultiple defaultIndex={categories.map((_, idx) => idx)}>
|
||||
{categories.map(category => {
|
||||
return (
|
||||
<AccordionItem key={category.name}>
|
||||
<AccordionButton>
|
||||
<Box flex="1" textAlign="left">
|
||||
{category.name}
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel>
|
||||
{category.components.map(c => {
|
||||
const onDragStart = (e: any) => {
|
||||
e.dataTransfer.setData(
|
||||
'component',
|
||||
`${c.version}/${c.metadata.name}`
|
||||
);
|
||||
// pass the exampleSize to gridlayout to render placeholder
|
||||
e.dataTransfer.setData(
|
||||
encodeDragDataTransfer(
|
||||
`${DROP_EXAMPLE_SIZE_PREFIX}${JSON.stringify(
|
||||
c.metadata.exampleSize
|
||||
)}`
|
||||
),
|
||||
''
|
||||
);
|
||||
};
|
||||
const cEle = (
|
||||
<Flex
|
||||
key={`${c.version}/${c.metadata.name}`}
|
||||
className="droppable-element"
|
||||
cursor="move"
|
||||
background="gray.100"
|
||||
width="60px"
|
||||
height="60px"
|
||||
width="100%"
|
||||
borderRadius="md"
|
||||
align="center"
|
||||
justify="center"
|
||||
justify="space-between"
|
||||
transition="ease 0.2s"
|
||||
_hover={{
|
||||
transform: 'scale(1.05)',
|
||||
background: 'gray.200',
|
||||
}}
|
||||
p={2}
|
||||
mb={1}
|
||||
draggable
|
||||
unselectable="on"
|
||||
onDragStart={onDragStart}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2693"
|
||||
width="30"
|
||||
height="30"
|
||||
>
|
||||
<path
|
||||
d="M972.09863 1016.986301H259.506849c-19.638356 0-35.068493-15.430137-35.068493-35.068493V768.70137c-8.416438 5.610959-18.235616 9.819178-28.054794 12.624657h-1.40274c-12.624658 4.208219-26.652055 5.610959-39.276712 5.610959-77.150685 0-138.871233-63.123288-138.871233-138.871233 0-77.150685 63.123288-138.871233 138.871233-138.871232 14.027397 0 28.054795 2.805479 42.082191 7.013698 1.40274 0 2.805479 1.40274 4.20822 1.40274 7.013699 2.805479 14.027397 5.610959 19.638356 8.416438l2.805479 1.40274V269.326027c0-19.638356 15.430137-35.068493 35.068493-35.068493h228.646576l-4.20822-8.416438c-7.013699-11.221918-12.624658-22.443836-15.430137-35.068493v-1.40274c-4.208219-12.624658-5.610959-26.652055-5.610958-39.276712C462.90411 72.942466 526.027397 11.221918 601.775342 11.221918c77.150685 0 138.871233 63.123288 138.871233 138.871233 0 14.027397-2.805479 28.054795-7.013698 42.082191 0 1.40274-1.40274 2.805479-1.40274 4.20822-2.805479 7.013699-5.610959 14.027397-8.416438 19.638356l-7.013699 16.832877h255.29863c19.638356 0 35.068493 15.430137 35.068493 35.068493v314.213698c0 11.221918-5.610959 22.443836-15.430137 29.457535-9.819178 7.013699-22.443836 7.013699-33.665753 2.805479L897.753425 587.747945c-1.40274 0-1.40274-1.40274-2.80548-1.40274-2.805479-1.40274-7.013699-4.208219-11.221918-5.610958h-1.402739c-7.013699-2.805479-14.027397-2.805479-21.041096-2.80548-37.873973 0-68.734247 30.860274-68.734247 68.734247s30.860274 68.734247 68.734247 68.734246c7.013699 0 14.027397-1.40274 19.638356-2.805479 7.013699-1.40274 12.624658-5.610959 18.235616-8.416439 1.40274-1.40274 2.805479-1.40274 4.20822-2.805479l53.304109-25.249315c11.221918-5.610959 23.846575-4.208219 33.665754 1.40274s16.832877 18.235616 16.832876 29.457534V981.917808c0 19.638356-15.430137 35.068493-35.068493 35.068493z m-677.523288-70.136986h642.454795v-182.356164h-1.40274c-11.221918 7.013699-22.443836 12.624658-35.068493 16.832876h-1.40274c-12.624658 4.208219-26.652055 5.610959-39.276712 5.610959-77.150685 0-138.871233-63.123288-138.871233-138.871233 0-77.150685 63.123288-138.871233 138.871233-138.871232 14.027397 0 28.054795 2.805479 42.082192 7.013698 1.40274 0 2.805479 1.40274 4.208219 1.40274 7.013699 2.805479 14.027397 5.610959 19.638356 8.416438l9.819178 4.208219v-224.438356H662.093151c-11.221918 0-22.443836-5.610959-29.457535-15.430137-7.013699-9.819178-7.013699-22.443836-2.805479-33.665753l29.457534-67.331507c0-1.40274 1.40274-1.40274 1.40274-2.805479 1.40274-2.805479 4.208219-7.013699 5.610959-11.221918v-1.40274c2.805479-7.013699 2.805479-14.027397 2.805479-21.041096 0-37.873973-30.860274-68.734247-68.734246-68.734246s-68.734247 30.860274-68.734247 68.734246c0 7.013699 1.40274 14.027397 2.80548 19.638356 1.40274 7.013699 5.610959 12.624658 8.416438 18.235617 1.40274 1.40274 1.40274 2.805479 2.805479 4.208219l28.054795 60.317808c5.610959 11.221918 4.208219 23.846575-1.40274 33.665754s-18.235616 16.832877-29.457534 16.832876H294.575342v274.936987c0 11.221918-5.610959 22.443836-15.430137 29.457534-9.819178 7.013699-22.443836 7.013699-33.665753 2.805479L192.175342 589.150685c-1.40274 0-1.40274-1.40274-2.805479-1.40274-2.805479-1.40274-7.013699-4.208219-11.221918-5.610959h-1.40274c-7.013699-2.805479-14.027397-2.805479-21.041095-2.805479-37.873973 0-68.734247 30.860274-68.734247 68.734246s30.860274 68.734247 68.734247 68.734247c7.013699 0 14.027397-1.40274 19.638356-2.805479 7.013699-1.40274 12.624658-5.610959 18.235616-8.416439 1.40274-1.40274 2.805479-1.40274 4.208219-2.805479l46.290411-22.443836c11.221918-5.610959 23.846575-4.208219 33.665754 1.40274 9.819178 7.013699 16.832877 18.235616 16.832876 29.457534v235.660274z"
|
||||
p-id="2694"
|
||||
></path>
|
||||
</svg>
|
||||
</Flex>
|
||||
<Box
|
||||
p={2}
|
||||
whiteSpace="pre-wrap"
|
||||
textAlign="center"
|
||||
fontSize="x-small"
|
||||
>
|
||||
{c.metadata.displayName}
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
return cEle;
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<Tag colorScheme={getTagColor(c.version)} size="sm">
|
||||
{c.version}
|
||||
</Tag>
|
||||
</Flex>
|
||||
);
|
||||
return cEle;
|
||||
})}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
})}
|
||||
</Accordion>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -53,6 +53,10 @@ export const Editor: React.FC<Props> = observer(
|
||||
modules,
|
||||
activeDataSource,
|
||||
activeDataSourceType,
|
||||
toolMenuTab,
|
||||
explorerMenuTab,
|
||||
setToolMenuTab,
|
||||
setExplorerMenuTab,
|
||||
} = editorStore;
|
||||
|
||||
const [scale, setScale] = useState(100);
|
||||
@ -182,7 +186,18 @@ export const Editor: React.FC<Props> = observer(
|
||||
position="relative"
|
||||
zIndex="2"
|
||||
>
|
||||
<Tabs height="100%" display="flex" flexDirection="column" isLazy>
|
||||
<Tabs
|
||||
align="center"
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
textAlign="left"
|
||||
isLazy
|
||||
index={explorerMenuTab}
|
||||
onChange={activatedTab => {
|
||||
setExplorerMenuTab(activatedTab);
|
||||
}}
|
||||
>
|
||||
<TabList background="gray.50" overflow="auto" whiteSpace="nowrap">
|
||||
<Tab>Explorer</Tab>
|
||||
<Tab>UI Tree</Tab>
|
||||
@ -233,6 +248,10 @@ export const Editor: React.FC<Props> = observer(
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
index={toolMenuTab}
|
||||
onChange={activatedTab => {
|
||||
setToolMenuTab(activatedTab);
|
||||
}}
|
||||
>
|
||||
<TabList background="gray.50">
|
||||
<Tab>Inspect</Tab>
|
||||
|
@ -9,10 +9,11 @@ type Props = {
|
||||
value?: Record<string, string>;
|
||||
isShowHeader?: boolean;
|
||||
minNum?: number;
|
||||
onlySetValue?: boolean;
|
||||
};
|
||||
|
||||
export const KeyValueEditor: React.FC<Props> = props => {
|
||||
const { minNum = 0 } = props;
|
||||
const { minNum = 0, onlySetValue } = props;
|
||||
const generateRows = (currentRows: Array<[string, string]> = []) => {
|
||||
let newRows = toPairs(props.value);
|
||||
|
||||
@ -67,6 +68,7 @@ export const KeyValueEditor: React.FC<Props> = props => {
|
||||
placeholder="key"
|
||||
onChange={onInputChange}
|
||||
onBlur={onBlur}
|
||||
isDisabled={onlySetValue}
|
||||
/>
|
||||
<Input
|
||||
flex={1}
|
||||
@ -76,14 +78,16 @@ export const KeyValueEditor: React.FC<Props> = props => {
|
||||
onChange={onInputChange}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<IconButton
|
||||
aria-label="remove row"
|
||||
icon={<CloseIcon />}
|
||||
size="xs"
|
||||
onClick={() => onRemoveRow(i)}
|
||||
variant="ghost"
|
||||
isDisabled={minNum >= rows.length}
|
||||
/>
|
||||
{onlySetValue ? null : (
|
||||
<IconButton
|
||||
aria-label="remove row"
|
||||
icon={<CloseIcon />}
|
||||
size="xs"
|
||||
onClick={() => onRemoveRow(i)}
|
||||
variant="ghost"
|
||||
isDisabled={minNum >= rows.length}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
);
|
||||
});
|
||||
@ -97,9 +101,11 @@ export const KeyValueEditor: React.FC<Props> = props => {
|
||||
</HStack>
|
||||
) : null}
|
||||
{rowItems}
|
||||
<Button onClick={onAddRow} size="xs" alignSelf="start">
|
||||
+ Add
|
||||
</Button>
|
||||
{onlySetValue ? null : (
|
||||
<Button onClick={onAddRow} size="xs" alignSelf="start">
|
||||
+ Add
|
||||
</Button>
|
||||
)}
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ import { removeModuleId } from '../utils/addModuleId';
|
||||
import { DataSourceType } from '../components/DataSource';
|
||||
import { TSchema } from '@sinclair/typebox';
|
||||
import { genOperation } from '../operations';
|
||||
import { ExplorerMenuTabs, ToolMenuTabs } from './enum';
|
||||
|
||||
type EditingTarget = {
|
||||
kind: 'app' | 'module';
|
||||
@ -22,6 +23,8 @@ export class EditorStore {
|
||||
_selectedComponentId = '';
|
||||
_hoverComponentId = '';
|
||||
_dragOverComponentId = '';
|
||||
explorerMenuTab = ExplorerMenuTabs.EXPLORER;
|
||||
toolMenuTab = ToolMenuTabs.INSERT;
|
||||
// current editor editing target(app or module)
|
||||
currentEditingTarget: EditingTarget = {
|
||||
kind: 'app',
|
||||
@ -78,6 +81,12 @@ export class EditorStore {
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => this.selectedComponentId,
|
||||
() => {
|
||||
this.setToolMenuTab(ToolMenuTabs.INSPECT);
|
||||
});
|
||||
|
||||
this.updateCurrentEditingTarget('app', this.app.version, this.app.metadata.name);
|
||||
}
|
||||
|
||||
@ -279,4 +288,12 @@ export class EditorStore {
|
||||
|
||||
this.setActiveDataSource(component!);
|
||||
};
|
||||
|
||||
setExplorerMenuTab = (val: ExplorerMenuTabs) => {
|
||||
this.explorerMenuTab = val;
|
||||
}
|
||||
|
||||
setToolMenuTab = (val: ToolMenuTabs) => {
|
||||
this.toolMenuTab = val;
|
||||
}
|
||||
}
|
||||
|
12
packages/editor/src/services/enum.ts
Normal file
12
packages/editor/src/services/enum.ts
Normal file
@ -0,0 +1,12 @@
|
||||
enum ExplorerMenuTabs {
|
||||
EXPLORER = 0,
|
||||
UI_TREE = 1,
|
||||
STATE = 2
|
||||
}
|
||||
|
||||
enum ToolMenuTabs {
|
||||
INSPECT = 0,
|
||||
INSERT = 1
|
||||
}
|
||||
|
||||
export { ExplorerMenuTabs, ToolMenuTabs };
|
@ -12,6 +12,9 @@ export default implementRuntimeComponent({
|
||||
isResizable: false,
|
||||
exampleProperties: {},
|
||||
exampleSize: [1, 1],
|
||||
annotations: {
|
||||
category: 'Advance',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: Type.Object({}),
|
||||
|
@ -31,6 +31,9 @@ export default implementRuntimeComponent({
|
||||
layout: [],
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Layout',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -3,50 +3,46 @@ import { ModuleSchema } from '../../types';
|
||||
import { ModuleRenderer } from '../_internal/ModuleRenderer';
|
||||
|
||||
export default implementRuntimeComponent({
|
||||
version: 'core/v1',
|
||||
metadata: {
|
||||
name: 'moduleContainer',
|
||||
displayName: 'ModuleContainer',
|
||||
description: 'ModuleContainer component',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
id: 'myModule',
|
||||
type: 'custom/v1/module',
|
||||
},
|
||||
exampleSize: [6, 6],
|
||||
version: 'core/v1',
|
||||
metadata: {
|
||||
name: 'moduleContainer',
|
||||
displayName: 'ModuleContainer',
|
||||
description: 'ModuleContainer component',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
id: 'myModule',
|
||||
type: 'custom/v1/module',
|
||||
},
|
||||
spec: {
|
||||
properties: ModuleSchema,
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
exampleSize: [6, 6],
|
||||
annotations: {
|
||||
category: 'Advance',
|
||||
},
|
||||
})(({
|
||||
id,
|
||||
type,
|
||||
properties,
|
||||
handlers,
|
||||
services,
|
||||
app
|
||||
}) => {
|
||||
if (!type) {
|
||||
return <span>Please choose a module to render.</span>
|
||||
}
|
||||
if (!id) {
|
||||
return <span>Please set a id for module.</span>
|
||||
}
|
||||
|
||||
return (
|
||||
<ModuleRenderer
|
||||
id={id}
|
||||
type={type}
|
||||
properties={properties}
|
||||
handlers={handlers}
|
||||
services={services}
|
||||
app={app}
|
||||
/>
|
||||
);
|
||||
})
|
||||
},
|
||||
spec: {
|
||||
properties: ModuleSchema,
|
||||
state: {},
|
||||
methods: {},
|
||||
slots: [],
|
||||
styleSlots: [],
|
||||
events: [],
|
||||
},
|
||||
})(({ id, type, properties, handlers, services, app }) => {
|
||||
if (!type) {
|
||||
return <span>Please choose a module to render.</span>;
|
||||
}
|
||||
if (!id) {
|
||||
return <span>Please set a id for module.</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModuleRenderer
|
||||
id={id}
|
||||
type={type}
|
||||
properties={properties}
|
||||
handlers={handlers}
|
||||
services={services}
|
||||
app={app}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -25,6 +25,9 @@ export default implementRuntimeComponent({
|
||||
},
|
||||
},
|
||||
exampleSize: [4, 1],
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: PropsSchema,
|
||||
|
@ -28,6 +28,7 @@ import { ApiService } from './apiService';
|
||||
export type UtilMethod = {
|
||||
name: string;
|
||||
method: (parameters?: any) => void;
|
||||
parameters?: any;
|
||||
};
|
||||
|
||||
export type UtilMethodFactory = () => UtilMethod[];
|
||||
@ -50,7 +51,7 @@ export class Registry {
|
||||
components = new Map<string, Map<string, AnyImplementedRuntimeComponent>>();
|
||||
traits = new Map<string, Map<string, ImplementedRuntimeTrait>>();
|
||||
modules = new Map<string, Map<string, ImplementedRuntimeModule>>();
|
||||
utilMethods = new Map<string, UtilMethod['method']>();
|
||||
utilMethods = new Map<string, UtilMethod>();
|
||||
private apiService: ApiService;
|
||||
|
||||
constructor(apiService: ApiService) {
|
||||
@ -166,7 +167,7 @@ export class Registry {
|
||||
if (this.utilMethods.get(m.name)) {
|
||||
throw new Error(`Already has utilMethod ${m.name} in this registry.`);
|
||||
}
|
||||
this.utilMethods.set(m.name, m.method);
|
||||
this.utilMethods.set(m.name, m);
|
||||
}
|
||||
|
||||
installLib(lib: SunmaoLib) {
|
||||
@ -185,9 +186,19 @@ export class Registry {
|
||||
private mountUtilMethods() {
|
||||
this.apiService.on('uiMethod', ({ componentId, name, parameters }) => {
|
||||
if (componentId === GLOBAL_UTILS_ID) {
|
||||
const utilMethod = this.utilMethods.get(name);
|
||||
const utilMethod = this.utilMethods.get(name)?.method;
|
||||
if (utilMethod) {
|
||||
utilMethod(parameters);
|
||||
const params: Record<string, unknown> = {};
|
||||
|
||||
for (const key in parameters) {
|
||||
const value = parameters[key];
|
||||
|
||||
if (value !== undefined && value !== '') {
|
||||
params[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
utilMethod(params);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user