mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-04-12 21:50:23 +08:00
wip init grid onLayoutChange
This commit is contained in:
parent
4e9e565bd7
commit
c1b1c65029
@ -1,4 +1,4 @@
|
||||
import { ChakraProvider, FormControl, FormLabel, Input } from '@chakra-ui/react';
|
||||
import { FormControl, FormLabel, Input } from '@chakra-ui/react';
|
||||
import { Application } from '@meta-ui/core';
|
||||
import React from 'react';
|
||||
import { eventBus } from '../../eventBus';
|
||||
@ -31,11 +31,9 @@ export const ComponentForm: React.FC<Props> = props => {
|
||||
});
|
||||
|
||||
return (
|
||||
<ChakraProvider>
|
||||
<div>
|
||||
<div>选中{selectedComponent?.id}</div>
|
||||
<form>{fields}</form>
|
||||
</div>
|
||||
</ChakraProvider>
|
||||
<div>
|
||||
<div>选中{selectedComponent?.id}</div>
|
||||
<form>{fields}</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,15 +1,19 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Application } from '@meta-ui/core';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { DialogFormSchema } from '../constants';
|
||||
import { DefaultAppSchema } from '../constants';
|
||||
import { App } from '../metaUI';
|
||||
import { StructureTree } from './StructureTree';
|
||||
import { OperationManager } from '../operations/OperationManager';
|
||||
import { CreateComponentOperation } from '../operations/Operations';
|
||||
import {
|
||||
CreateComponentOperation,
|
||||
ModifyComponentPropertyOperation,
|
||||
} from '../operations/Operations';
|
||||
import { eventBus } from '../eventBus';
|
||||
import { ComponentForm } from './ComponentForm';
|
||||
import { ComponentList } from './ComponentsList';
|
||||
|
||||
const operationManager = new OperationManager(DialogFormSchema);
|
||||
const operationManager = new OperationManager(DefaultAppSchema);
|
||||
|
||||
export const Editor = () => {
|
||||
const [selectedComponentId, setSelectedComponentId] = useState('');
|
||||
@ -45,7 +49,7 @@ export const Editor = () => {
|
||||
const onClickAdd = useCallback(() => {
|
||||
eventBus.send(
|
||||
'operation',
|
||||
new CreateComponentOperation('root', 'root', 'chakra_ui/v1/input')
|
||||
new CreateComponentOperation('root', 'container', 'chakra_ui/v1/input')
|
||||
);
|
||||
}, [app, setApp]);
|
||||
|
||||
@ -54,14 +58,31 @@ export const Editor = () => {
|
||||
}, [app, setApp]);
|
||||
|
||||
return (
|
||||
<Box display="flex" height="100vh">
|
||||
<Box display="flex" height="100vh" width="100vw">
|
||||
<Box flex="1">
|
||||
<div className="droppable-element" draggable={true} unselectable="on">
|
||||
hhhhh
|
||||
</div>
|
||||
<button onClick={onClickAdd}>添加</button>
|
||||
<button onClick={onClickUndo}>撤销</button>
|
||||
<StructureTree app={app} onSelectComponent={id => setSelectedComponentId(id)} />
|
||||
</Box>
|
||||
<Box flex="1">
|
||||
<ComponentList />{' '}
|
||||
</Box>
|
||||
<Box flex="3" borderRight="2px solid black">
|
||||
<App options={app} componentWrapper={Wrapper} />
|
||||
<App
|
||||
options={app}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
onLayoutChange={(id, layout) => {
|
||||
eventBus.send(
|
||||
'operation',
|
||||
new ModifyComponentPropertyOperation(id, 'layout', layout)
|
||||
);
|
||||
console.log('layout变啦!!', id, layout);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box flex="1" borderRight="2px solid black">
|
||||
<ComponentForm app={app} selectedId={selectedComponentId} />
|
||||
|
@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Application } from '@meta-ui/core';
|
||||
import { eventBus } from '../../eventBus';
|
||||
import { RemoveComponentOperation } from '../../operations/Operations';
|
||||
|
||||
type ChildrenMap = Map<string, SlotsMap>;
|
||||
type SlotsMap = Map<string, Array<Application['spec']['components'][0]>>;
|
||||
@ -46,6 +48,11 @@ export const StructureTree: React.FC<Props> = props => {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const onClickRemove = () => {
|
||||
eventBus.send('operation', new RemoveComponentOperation(component.id));
|
||||
};
|
||||
|
||||
return (
|
||||
<div key={component.id} style={{ paddingLeft: '16px' }}>
|
||||
<strong
|
||||
@ -55,6 +62,7 @@ export const StructureTree: React.FC<Props> = props => {
|
||||
>
|
||||
{component.id}
|
||||
</strong>
|
||||
<span onClick={onClickRemove}>删除</span>
|
||||
{slotsEle}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,538 +1,110 @@
|
||||
import { Application } from '@meta-ui/core';
|
||||
|
||||
export const DialogFormSchema: Application = {
|
||||
version: 'example/v1',
|
||||
export const DefaultAppSchema: Application = {
|
||||
kind: 'Application',
|
||||
version: 'example/v1',
|
||||
metadata: {
|
||||
name: 'dialog form',
|
||||
description: 'dialog form example',
|
||||
name: 'basic_grid_layout',
|
||||
description: 'basic grid layout example',
|
||||
},
|
||||
spec: {
|
||||
components: [
|
||||
{
|
||||
id: 'fetchVolumes',
|
||||
type: 'core/v1/dummy',
|
||||
properties: {},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/fetch',
|
||||
properties: {
|
||||
name: 'query',
|
||||
url: 'https://61373521eac1410017c18209.mockapi.io/Volume',
|
||||
method: 'get',
|
||||
lazy: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'createVolume',
|
||||
type: 'core/v1/dummy',
|
||||
properties: {},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/fetch',
|
||||
properties: {
|
||||
url: 'https://61373521eac1410017c18209.mockapi.io/Volume',
|
||||
method: 'post',
|
||||
lazy: true,
|
||||
headers: [{ key: 'Content-Type', value: 'application/json' }],
|
||||
body: '{{ form.data }}',
|
||||
onComplete: [
|
||||
{
|
||||
componentId: '$utils',
|
||||
method: {
|
||||
name: 'toast.open',
|
||||
parameters: {
|
||||
id: 'createSuccessToast',
|
||||
title: '恭喜',
|
||||
description: '创建虚拟卷成功',
|
||||
position: 'bottom-right',
|
||||
duration: null,
|
||||
isClosable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'editDialog',
|
||||
method: {
|
||||
name: 'cancelDialog',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'form',
|
||||
method: {
|
||||
name: 'resetForm',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentId: 'fetchVolumes',
|
||||
method: {
|
||||
name: 'triggerFetch',
|
||||
parameters: 'query',
|
||||
},
|
||||
wait: {},
|
||||
disabled: 'false',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'deleteVolume',
|
||||
type: 'core/v1/dummy',
|
||||
properties: {},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/fetch',
|
||||
properties: {
|
||||
url: 'https://61373521eac1410017c18209.mockapi.io/Volume/{{ table.selectedItem ? table.selectedItem.id : "" }}',
|
||||
method: 'delete',
|
||||
lazy: true,
|
||||
onComplete: [
|
||||
{
|
||||
componentId: 'fetchVolumes',
|
||||
method: {
|
||||
name: 'triggerFetch',
|
||||
parameters: 'query',
|
||||
},
|
||||
wait: {},
|
||||
disabled: 'false',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'root',
|
||||
type: 'chakra_ui/v1/root',
|
||||
properties: {},
|
||||
type: 'core/v1/grid_layout',
|
||||
properties: {
|
||||
layout: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 5,
|
||||
h: 2,
|
||||
i: 'input',
|
||||
isResizable: false,
|
||||
},
|
||||
{
|
||||
x: 4,
|
||||
y: 0,
|
||||
w: 4,
|
||||
h: 9,
|
||||
i: 'box1',
|
||||
},
|
||||
{
|
||||
x: 8,
|
||||
y: 0,
|
||||
w: 2,
|
||||
h: 12,
|
||||
i: 'box2',
|
||||
},
|
||||
],
|
||||
},
|
||||
traits: [],
|
||||
},
|
||||
{
|
||||
id: 'editDialog',
|
||||
type: 'chakra_ui/v1/dialog',
|
||||
properties: {
|
||||
title: 'This is a dialog',
|
||||
confirmButton: {
|
||||
text: '保存',
|
||||
colorScheme: 'purple',
|
||||
},
|
||||
cancelButton: {
|
||||
text: '取消',
|
||||
},
|
||||
disableConfirm: '{{ form.isFormInvalid }}',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'root',
|
||||
slot: 'root',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'core/v1/event',
|
||||
properties: {
|
||||
events: [
|
||||
{
|
||||
event: 'confirmDialog',
|
||||
componentId: 'createVolume',
|
||||
method: {
|
||||
name: 'triggerFetch',
|
||||
},
|
||||
wait: {},
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'table',
|
||||
type: 'chakra_ui/v1/table',
|
||||
properties: {
|
||||
data: '{{ fetchVolumes.fetch.data }}',
|
||||
majorKey: 'id',
|
||||
rowsPerPage: 5,
|
||||
columns: [
|
||||
{
|
||||
key: 'id',
|
||||
title: 'ID',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: '名称',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
title: '类别',
|
||||
type: 'text',
|
||||
displayValue:
|
||||
'{{$listItem.type === "sharing" ? "共享虚拟卷" : "虚拟卷"}}',
|
||||
},
|
||||
{
|
||||
key: 'size',
|
||||
title: '容量',
|
||||
type: 'text',
|
||||
displayValue: '{{$listItem.size}} GiB',
|
||||
},
|
||||
{
|
||||
key: 'policy',
|
||||
title: '存储策略',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
key: 'isActive',
|
||||
title: '是否激活',
|
||||
type: 'text',
|
||||
displayValue: '{{$listItem.isActive ? "是" : "否"}}',
|
||||
},
|
||||
{
|
||||
key: 'operation',
|
||||
title: '操作',
|
||||
type: 'button',
|
||||
buttonConfig: {
|
||||
text: '删除',
|
||||
events: [
|
||||
{
|
||||
componentId: 'deleteVolume',
|
||||
method: {
|
||||
name: 'triggerFetch',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'edit',
|
||||
title: '创建',
|
||||
type: 'button',
|
||||
buttonConfig: {
|
||||
text: '创建',
|
||||
events: [
|
||||
{
|
||||
componentId: 'editDialog',
|
||||
method: {
|
||||
name: 'openDialog',
|
||||
parameters: {
|
||||
title: '创建虚拟卷',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'root',
|
||||
slot: 'root',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'form',
|
||||
type: 'chakra_ui/v1/form',
|
||||
properties: {
|
||||
hideSubmit: true,
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'editDialog',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'nameFormControl',
|
||||
type: 'chakra_ui/v1/formControl',
|
||||
properties: {
|
||||
label: '名称',
|
||||
fieldName: 'name',
|
||||
isRequired: true,
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'form',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'nameInput',
|
||||
id: 'input',
|
||||
type: 'chakra_ui/v1/input',
|
||||
properties: {
|
||||
defaultValue:
|
||||
'{{ table.selectedItem ? table.selectedItem.name : "" }}',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'nameFormControl',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'core/v1/validation',
|
||||
properties: {
|
||||
value: '{{ nameInput.value || "" }}',
|
||||
maxLength: 10,
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'typeFormControl',
|
||||
type: 'chakra_ui/v1/formControl',
|
||||
properties: {
|
||||
label: '类型',
|
||||
fieldName: 'type',
|
||||
helperText:
|
||||
'共享虚拟卷支持被多台虚拟机同时挂载。类型创建后不可修改。',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'form',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'typeRadioGroup',
|
||||
type: 'chakra_ui/v1/radio_group',
|
||||
properties: {
|
||||
defaultValue:
|
||||
'{{ table.selectedItem ? table.selectedItem.type : "notSharing" }}',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'typeFormControl',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'radio1',
|
||||
type: 'chakra_ui/v1/radio',
|
||||
properties: {
|
||||
text: {
|
||||
raw: '虚拟卷',
|
||||
format: 'plain',
|
||||
},
|
||||
value: 'notSharing',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'typeRadioGroup',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'radio2',
|
||||
type: 'chakra_ui/v1/radio',
|
||||
properties: {
|
||||
text: {
|
||||
raw: '共享虚拟卷',
|
||||
format: 'plain',
|
||||
},
|
||||
value: 'sharing',
|
||||
size: 'md',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'typeRadioGroup',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'sizeFormControl',
|
||||
type: 'chakra_ui/v1/formControl',
|
||||
properties: {
|
||||
label: '容量',
|
||||
fieldName: 'size',
|
||||
variant: 'filled',
|
||||
placeholder: 'This a example',
|
||||
size: 'lg',
|
||||
colorScheme: 'pink',
|
||||
focusBorderColor: 'pink.500',
|
||||
isDisabled: false,
|
||||
isRequired: true,
|
||||
left: {
|
||||
type: 'addon',
|
||||
children: 'https://',
|
||||
},
|
||||
right: {
|
||||
type: 'element',
|
||||
children: '.com',
|
||||
color: 'red',
|
||||
fontSize: '16px',
|
||||
},
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'form',
|
||||
slot: 'content',
|
||||
id: 'root',
|
||||
slot: 'container',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'sizeInput',
|
||||
type: 'chakra_ui/v1/number_input',
|
||||
properties: {
|
||||
defaultValue:
|
||||
'{{ table.selectedItem ? table.selectedItem.size : 0 }}',
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 5,
|
||||
precision: 2,
|
||||
clampValueOnBlur: false,
|
||||
allowMouseWheel: true,
|
||||
},
|
||||
id: 'box1',
|
||||
type: 'chakra_ui/v1/box',
|
||||
properties: {},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'sizeFormControl',
|
||||
slot: 'content',
|
||||
id: 'root',
|
||||
slot: 'container',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'policyFormControl',
|
||||
type: 'chakra_ui/v1/formControl',
|
||||
id: 'box2',
|
||||
type: 'chakra_ui/v1/box',
|
||||
properties: {
|
||||
label: '存储策略',
|
||||
fieldName: 'policy',
|
||||
bgColor: 'pink',
|
||||
w: '100%',
|
||||
h: '100%',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'form',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'policySelect',
|
||||
type: 'chakra_ui/v1/select',
|
||||
properties: {
|
||||
defaultValue:
|
||||
'{{ table.selectedItem ? table.selectedItem.policy : "2thin" }}',
|
||||
options: [
|
||||
{
|
||||
value: '2thin',
|
||||
label: '2 副本,精简置备',
|
||||
},
|
||||
{
|
||||
value: '3thin',
|
||||
label: '3 副本,精简置备',
|
||||
},
|
||||
{
|
||||
value: '2thick',
|
||||
label: '2 副本,厚置备',
|
||||
},
|
||||
{
|
||||
value: '3thick',
|
||||
label: '3 副本,厚置备',
|
||||
},
|
||||
],
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'policyFormControl',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'isActiveFormControl',
|
||||
type: 'chakra_ui/v1/formControl',
|
||||
properties: {
|
||||
label: '激活',
|
||||
fieldName: 'isActive',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'form',
|
||||
slot: 'content',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'checkbox',
|
||||
type: 'chakra_ui/v1/checkbox',
|
||||
properties: {
|
||||
value: 'isActive',
|
||||
defaultIsChecked:
|
||||
'{{table.selectedItem ? !!table.selectedItem.isActive : false}}',
|
||||
text: {
|
||||
raw: '激活',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'isActiveFormControl',
|
||||
slot: 'content',
|
||||
id: 'root',
|
||||
slot: 'container',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/react';
|
||||
import { StrictMode } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
@ -5,13 +6,15 @@ import { Editor } from './components/Editor';
|
||||
export default function renderApp() {
|
||||
ReactDOM.render(
|
||||
<StrictMode>
|
||||
<div
|
||||
css={css`
|
||||
display: flex;
|
||||
`}
|
||||
>
|
||||
<Editor />
|
||||
</div>
|
||||
<ChakraProvider>
|
||||
<div
|
||||
css={css`
|
||||
display: flex;
|
||||
`}
|
||||
>
|
||||
<Editor />
|
||||
</div>
|
||||
</ChakraProvider>
|
||||
</StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
@ -41,8 +41,16 @@ function genComponent(
|
||||
|
||||
export class OperationManager {
|
||||
private undoStack: Operations[] = [];
|
||||
private app: Application;
|
||||
|
||||
constructor(app: Application) {
|
||||
const appFromLS = localStorage.getItem('schema');
|
||||
if (appFromLS) {
|
||||
this.app = JSON.parse(appFromLS);
|
||||
} else {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
constructor(private app: Application) {
|
||||
eventBus.on('undo', () => this.undo());
|
||||
eventBus.on('operation', o => this.apply(o));
|
||||
}
|
||||
@ -53,6 +61,7 @@ export class OperationManager {
|
||||
|
||||
updateApp(app: Application) {
|
||||
eventBus.send('appChange', app);
|
||||
localStorage.setItem('schema', JSON.stringify(app));
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ export const App: React.FC<AppProps> = props => {
|
||||
options,
|
||||
mModules,
|
||||
componentWrapper,
|
||||
onLayoutChange,
|
||||
debugStore = true,
|
||||
debugEvent = true,
|
||||
} = props;
|
||||
@ -26,7 +27,14 @@ export const App: React.FC<AppProps> = props => {
|
||||
initStateAndMethod(mModules.registry, mModules.stateManager, app.spec.components);
|
||||
|
||||
const { topLevelComponents, slotComponentsMap } = useMemo(
|
||||
() => resolveAppComponents(mModules, app.spec.components, componentWrapper, app),
|
||||
() =>
|
||||
resolveAppComponents(
|
||||
mModules,
|
||||
app.spec.components,
|
||||
app,
|
||||
componentWrapper,
|
||||
onLayoutChange
|
||||
),
|
||||
[app]
|
||||
);
|
||||
|
||||
@ -42,6 +50,7 @@ export const App: React.FC<AppProps> = props => {
|
||||
targetSlot={null}
|
||||
app={app}
|
||||
componentWrapper={componentWrapper}
|
||||
onLayoutChange={onLayoutChange}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@ -12,14 +12,29 @@ export const LayoutPropertySchema = Type.Array(
|
||||
w: Type.Number(),
|
||||
h: Type.Number(),
|
||||
i: Type.String(),
|
||||
isResizable: Type.Optional(Type.Boolean()),
|
||||
})
|
||||
);
|
||||
|
||||
const GridLayout: React.FC<{
|
||||
layout: Static<typeof LayoutPropertySchema>;
|
||||
}> = ({ children, layout }) => {
|
||||
onLayoutChange?: (layout: RGL.Layout[]) => void;
|
||||
}> = ({ children, layout, onLayoutChange }) => {
|
||||
return (
|
||||
<ReactGridLayout rowHeight={30} layout={layout}>
|
||||
<ReactGridLayout
|
||||
isDraggable={!!onLayoutChange}
|
||||
isResizable={!!onLayoutChange}
|
||||
compactType={null}
|
||||
preventCollision={true}
|
||||
rowHeight={30}
|
||||
layout={layout}
|
||||
onLayoutChange={onLayoutChange}
|
||||
onDragStart={() => {
|
||||
console.log('dragstart');
|
||||
}}
|
||||
// onDrop={onLayoutChange}
|
||||
isDroppable={true}
|
||||
>
|
||||
{children}
|
||||
</ReactGridLayout>
|
||||
);
|
||||
|
@ -60,7 +60,6 @@ const List: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
const { topLevelComponents, slotComponentsMap } = resolveAppComponents(
|
||||
mModules,
|
||||
evaledTemplate,
|
||||
undefined,
|
||||
app
|
||||
);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import RGL from 'react-grid-layout';
|
||||
import { ComponentImplementation } from '../../modules/registry';
|
||||
import { createComponent } from '@meta-ui/core';
|
||||
import { getSlots } from '../_internal/Slot';
|
||||
@ -10,10 +11,17 @@ const BaseGridLayout = React.lazy(() => import('../../components/_internal/GridL
|
||||
const GridLayout: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
slotsMap,
|
||||
layout = [],
|
||||
onLayoutChange,
|
||||
component,
|
||||
}) => {
|
||||
const _onLayoutChange = (layout: RGL.Layout[]) => {
|
||||
onLayoutChange && onLayoutChange(component.id, layout);
|
||||
};
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<BaseGridLayout layout={layout}>{getSlots(slotsMap, 'container')}</BaseGridLayout>
|
||||
<BaseGridLayout onLayoutChange={_onLayoutChange} layout={layout}>
|
||||
{getSlots(slotsMap, 'container')}
|
||||
</BaseGridLayout>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ type ImplWrapperProps = {
|
||||
mModules: MetaUIModules;
|
||||
app?: RuntimeApplication;
|
||||
componentWrapper?: ComponentWrapperType;
|
||||
onLayoutChange?: (id: string, layout: any) => void;
|
||||
};
|
||||
|
||||
export const ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
|
||||
@ -34,6 +35,7 @@ export const ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
|
||||
children,
|
||||
componentWrapper: ComponentWrapper,
|
||||
mModules,
|
||||
onLayoutChange,
|
||||
} = props;
|
||||
|
||||
const { registry, stateManager, globalHandlerMap, apiService } = props.mModules;
|
||||
@ -174,6 +176,7 @@ export const ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
|
||||
mModules={mModules}
|
||||
mergeState={mergeState}
|
||||
subscribeMethods={subscribeMethods}
|
||||
onLayoutChange={onLayoutChange}
|
||||
slotsMap={slotsMap}
|
||||
app={app}
|
||||
/>
|
||||
|
@ -66,6 +66,7 @@ export type ComponentMergedProps = {
|
||||
effects?: Array<() => void>;
|
||||
app?: RuntimeApplication;
|
||||
mModules: MetaUIModules;
|
||||
onLayoutChange?: (id: string, layout: any) => void;
|
||||
};
|
||||
|
||||
export type ComponentImplementation<T = any> = React.FC<T & ComponentMergedProps>;
|
||||
|
@ -12,8 +12,9 @@ import { ImplWrapper } from './ImplWrapper';
|
||||
export function resolveAppComponents(
|
||||
mModules: MetaUIModules,
|
||||
components: RuntimeApplication['spec']['components'],
|
||||
app?: RuntimeApplication,
|
||||
componentWrapper?: ComponentWrapperType,
|
||||
app?: RuntimeApplication
|
||||
onLayoutChange?: (id: string, layout: any) => void
|
||||
): {
|
||||
topLevelComponents: RuntimeApplication['spec']['components'];
|
||||
slotComponentsMap: SlotComponentMap;
|
||||
@ -44,6 +45,7 @@ export function resolveAppComponents(
|
||||
mModules={mModules}
|
||||
app={app}
|
||||
componentWrapper={componentWrapper}
|
||||
onLayoutChange={onLayoutChange}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
|
@ -16,6 +16,7 @@ export type MetaUIModules = {
|
||||
export type AppProps = {
|
||||
options: Application;
|
||||
mModules: MetaUIModules;
|
||||
onLayoutChange?: (id: string, layout: any) => void;
|
||||
componentWrapper?: ComponentWrapperType;
|
||||
debugStore?: boolean;
|
||||
debugEvent?: boolean;
|
||||
|
Loading…
x
Reference in New Issue
Block a user