mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-02-23 17:49:49 +08:00
feat(extract): support generate stateMap
This commit is contained in:
parent
b7f46361e1
commit
3d2a090530
@ -1,7 +1,7 @@
|
||||
type OperationType =
|
||||
| 'createComponent'
|
||||
| 'removeComponent'
|
||||
| 'modifyComponentProperty'
|
||||
| 'modifyComponentProperties'
|
||||
| 'modifyComponentId'
|
||||
| 'adjustComponentOrder'
|
||||
| 'createTrait'
|
||||
|
@ -100,7 +100,7 @@ export const ComponentForm: React.FC<Props> = observer(props => {
|
||||
onChange={newFormData => {
|
||||
eventBus.send(
|
||||
'operation',
|
||||
genOperation(registry, 'modifyComponentProperty', {
|
||||
genOperation(registry, 'modifyComponentProperties', {
|
||||
componentId: selectedComponentId,
|
||||
properties: newFormData,
|
||||
})
|
||||
|
@ -27,19 +27,24 @@ import {
|
||||
import { Static } from '@sinclair/typebox';
|
||||
import { uniq } from 'lodash';
|
||||
import { RelationshipModal } from '../RelationshipModal';
|
||||
import { OutsideExpRelation } from './type';
|
||||
import { RenameStateForm } from './RenameStateForm';
|
||||
import { OutsideExpRelationWithState } from '.';
|
||||
|
||||
type Props = {
|
||||
componentId: string;
|
||||
services: EditorServices;
|
||||
};
|
||||
|
||||
type ExpressionRelation = {
|
||||
// 里面exp依赖外面
|
||||
type InsideExpRelation = {
|
||||
componentId: string;
|
||||
exp: string;
|
||||
key: string;
|
||||
};
|
||||
|
||||
export type MethodRelation = {
|
||||
// 里面的event调用外面的method
|
||||
export type InsideMethodRelation = {
|
||||
handler: Static<typeof EventHandlerSpec>;
|
||||
source: string;
|
||||
target: string;
|
||||
@ -48,8 +53,9 @@ export type MethodRelation = {
|
||||
};
|
||||
|
||||
type AllRelations = {
|
||||
exp: ExpressionRelation[];
|
||||
method: MethodRelation[];
|
||||
exp: InsideExpRelation[];
|
||||
method: InsideMethodRelation[];
|
||||
outsideExpRelations: OutsideExpRelation[];
|
||||
};
|
||||
|
||||
export const ExtractModuleView: React.FC<Props> = ({ componentId, services }) => {
|
||||
@ -57,25 +63,32 @@ export const ExtractModuleView: React.FC<Props> = ({ componentId, services }) =>
|
||||
const { appModel } = appModelManager;
|
||||
const [showRelationId, setShowRelationId] = useState('');
|
||||
const radioMapRef = useRef<RefTreatmentMap>({});
|
||||
const outsideExpRelationsValueRef = useRef<OutsideExpRelationWithState[]>([]);
|
||||
const [moduleName, setModuleName] = useState('myModule0');
|
||||
const [moduleVersion, setModuleVersion] = useState('custom/v1');
|
||||
|
||||
const { expressionRelations, methodRelations } = useMemo(() => {
|
||||
const { expressionRelations, methodRelations, outsideExpRelations } = useMemo(() => {
|
||||
const root = appModel.getComponentById(componentId as ComponentId)!;
|
||||
const components = root.allComponents;
|
||||
const allRelations = components.reduce(
|
||||
const moduleComponents = root.allComponents;
|
||||
const allRelations = moduleComponents.reduce(
|
||||
(res, c) => {
|
||||
const { expressionRelations, methodRelations } = getRelations(c, components);
|
||||
const { expressionRelations, methodRelations } = getRelations(
|
||||
c,
|
||||
moduleComponents
|
||||
);
|
||||
res.exp = res.exp.concat(expressionRelations);
|
||||
res.method = res.method.concat(methodRelations);
|
||||
return res;
|
||||
},
|
||||
{ exp: [], method: [] } as AllRelations
|
||||
{ exp: [], method: [], outsideExpRelations: [] } as AllRelations
|
||||
);
|
||||
const outsideExpRelations = getOutsideExpRelations(appModel.allComponents, root);
|
||||
console.log('outsideExpRelations', outsideExpRelations);
|
||||
console.log('allRelations', allRelations);
|
||||
return {
|
||||
expressionRelations: allRelations.exp,
|
||||
methodRelations: allRelations.method,
|
||||
outsideExpRelations,
|
||||
};
|
||||
}, [appModel, componentId]);
|
||||
|
||||
@ -111,26 +124,22 @@ export const ExtractModuleView: React.FC<Props> = ({ componentId, services }) =>
|
||||
}}
|
||||
onClickComponent={id => setShowRelationId(id)}
|
||||
/>
|
||||
// <Table size="sm" border="1px solid" borderColor="gray.100">
|
||||
// <Thead>
|
||||
// <Tr>
|
||||
// <Th>ComponentId</Th>
|
||||
// <Th>Move In to</Th>
|
||||
// <Th>Expression</Th>
|
||||
// </Tr>
|
||||
// </Thead>
|
||||
// <Tbody>
|
||||
// {uniqExpRelations.map((d, i) => {
|
||||
// return (
|
||||
// <Tr key={i}>
|
||||
// <Td>{idLink(d)}</Td>
|
||||
// {/* <Td>{d.key}</Td>
|
||||
// <Td>{d.exp}</Td> */}
|
||||
// </Tr>
|
||||
// );
|
||||
// })}
|
||||
// </Tbody>
|
||||
// </Table>
|
||||
);
|
||||
};
|
||||
|
||||
const renameStateForm = () => {
|
||||
if (!outsideExpRelations.length) {
|
||||
return <Placeholder />;
|
||||
}
|
||||
|
||||
return (
|
||||
<RenameStateForm
|
||||
outsideExpRelations={outsideExpRelations}
|
||||
onChange={v => {
|
||||
outsideExpRelationsValueRef.current = v;
|
||||
console.log('outsideExpRelationsValueRef', v);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -185,6 +194,7 @@ export const ExtractModuleView: React.FC<Props> = ({ componentId, services }) =>
|
||||
moduleName,
|
||||
moduleVersion,
|
||||
methodRelations,
|
||||
outsideExpRelations: outsideExpRelationsValueRef.current,
|
||||
});
|
||||
};
|
||||
|
||||
@ -207,6 +217,10 @@ export const ExtractModuleView: React.FC<Props> = ({ componentId, services }) =>
|
||||
</Heading>
|
||||
{expressionTable()}
|
||||
</VStack>
|
||||
<VStack width="full" alignItems="start">
|
||||
<Heading size="md">State map Config</Heading>
|
||||
{renameStateForm()}
|
||||
</VStack>
|
||||
<VStack width="full" alignItems="start">
|
||||
<Heading size="md">Who calls my methods?</Heading>
|
||||
{methodTable()}
|
||||
@ -217,9 +231,44 @@ export const ExtractModuleView: React.FC<Props> = ({ componentId, services }) =>
|
||||
);
|
||||
};
|
||||
|
||||
// 获取外部组件中依赖Module内组件的表达式;
|
||||
// TODO: 这里只检查了component的property,没检查trait里面的
|
||||
function getOutsideExpRelations(
|
||||
allComponents: IComponentModel[],
|
||||
moduleRoot: IComponentModel
|
||||
): OutsideExpRelation[] {
|
||||
const res: OutsideExpRelation[] = [];
|
||||
const clonedRoot = moduleRoot.clone();
|
||||
const ids = clonedRoot.allComponents.map(c => c.id);
|
||||
allComponents.forEach(c => {
|
||||
if (clonedRoot.appModel.getComponentById(c.id)) {
|
||||
// 是module内的,无视
|
||||
return;
|
||||
}
|
||||
c.properties.traverse((field, key) => {
|
||||
if (field.isDynamic) {
|
||||
const relyRefs = Object.keys(field.refComponentInfos).filter(refId =>
|
||||
ids.includes(refId as ComponentId)
|
||||
);
|
||||
relyRefs.forEach(refId => {
|
||||
res.push({
|
||||
componentId: c.id,
|
||||
exp: field.getValue() as string,
|
||||
key,
|
||||
valuePath:
|
||||
field.refComponentInfos[refId as ComponentId].refProperties.slice(-1)[0],
|
||||
relyOn: refId,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
function getRelations(component: IComponentModel, components: IComponentModel[]) {
|
||||
const expressionRelations: ExpressionRelation[] = [];
|
||||
const methodRelations: MethodRelation[] = [];
|
||||
const expressionRelations: InsideExpRelation[] = [];
|
||||
const methodRelations: InsideMethodRelation[] = [];
|
||||
const ids = components.map(c => c.id) as string[];
|
||||
// 获取到Module中用到的外部id
|
||||
component.properties.traverse((field, key) => {
|
||||
@ -273,6 +322,7 @@ function getRelations(component: IComponentModel, components: IComponentModel[])
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return { expressionRelations, methodRelations };
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,62 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Input, Table, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react';
|
||||
import { OutsideExpRelation, OutsideExpRelationWithState } from './type';
|
||||
import produce from 'immer';
|
||||
|
||||
type Props = {
|
||||
outsideExpRelations: OutsideExpRelation[];
|
||||
onChange: (value: OutsideExpRelationWithState[]) => void;
|
||||
};
|
||||
export const RenameStateForm: React.FC<Props> = ({ outsideExpRelations, onChange }) => {
|
||||
const [value, setValue] = useState<OutsideExpRelationWithState[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const newValue = outsideExpRelations.map(r => {
|
||||
return {
|
||||
...r,
|
||||
stateName: '',
|
||||
};
|
||||
});
|
||||
setValue(newValue);
|
||||
}, [outsideExpRelations]);
|
||||
|
||||
useEffect(() => {
|
||||
onChange(value);
|
||||
}, [onChange, value]);
|
||||
|
||||
return (
|
||||
<Table size="sm" border="1px solid" borderColor="gray.100">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Comoonent</Th>
|
||||
<Th>key</Th>
|
||||
<Th>Expression</Th>
|
||||
<Th>RawExp</Th>
|
||||
<Th>StateName</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{value.map((d, i) => {
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = produce(value, draft => {
|
||||
draft[i].stateName = e.target.value;
|
||||
return draft;
|
||||
});
|
||||
setValue(newValue);
|
||||
};
|
||||
return (
|
||||
<Tr key={i}>
|
||||
<Td>{d.componentId}</Td>
|
||||
<Td>{d.key}</Td>
|
||||
<Td>{d.exp}</Td>
|
||||
<Td>{`${d.relyOn}.${d.valuePath}`}</Td>
|
||||
<Td>
|
||||
<Input size="sm" value={d.stateName} onChange={onChange} />
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
@ -1 +1,2 @@
|
||||
export * from './ExtractModuleModal';
|
||||
export * from './type';
|
||||
|
12
packages/editor/src/components/ExtractModuleModal/type.ts
Normal file
12
packages/editor/src/components/ExtractModuleModal/type.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// 外面exp依赖里面
|
||||
export type OutsideExpRelation = {
|
||||
componentId: string;
|
||||
exp: string;
|
||||
key: string;
|
||||
valuePath: string;
|
||||
relyOn: string;
|
||||
};
|
||||
|
||||
export type OutsideExpRelationWithState = OutsideExpRelation & {
|
||||
stateName: string;
|
||||
};
|
@ -35,7 +35,7 @@ export const OperationConstructors: Record<
|
||||
> = {
|
||||
createComponent: CreateComponentBranchOperation,
|
||||
removeComponent: RemoveComponentBranchOperation,
|
||||
modifyComponentProperty: ModifyComponentPropertiesLeafOperation,
|
||||
modifyComponentProperties: ModifyComponentPropertiesLeafOperation,
|
||||
modifyComponentId: ModifyComponentIdBranchOperation,
|
||||
adjustComponentOrder: AdjustComponentOrderLeafOperation,
|
||||
createTrait: CreateTraitLeafOperation,
|
||||
@ -64,7 +64,7 @@ export type OperationConfigMaps = {
|
||||
RemoveComponentBranchOperation,
|
||||
RemoveComponentBranchOperationContext
|
||||
>;
|
||||
modifyComponentProperty: OperationConfigMap<
|
||||
modifyComponentProperties: OperationConfigMap<
|
||||
ModifyComponentPropertiesLeafOperation,
|
||||
ModifyComponentPropertiesLeafOperationContext
|
||||
>;
|
||||
|
@ -0,0 +1,53 @@
|
||||
import { BaseLeafOperation } from '../../type';
|
||||
import { AppModel } from '../../../AppModel/AppModel';
|
||||
import { ComponentId } from '../../../AppModel/IAppModel';
|
||||
export type ModifyComponentPropertyLeafOperationContext = {
|
||||
componentId: string;
|
||||
path: string;
|
||||
property: any;
|
||||
};
|
||||
|
||||
export class ModifyComponentPropertyLeafOperation extends BaseLeafOperation<ModifyComponentPropertyLeafOperationContext> {
|
||||
private previousValue: any = undefined;
|
||||
do(prev: AppModel): AppModel {
|
||||
const component = prev.getComponentById(this.context.componentId as ComponentId);
|
||||
if (component) {
|
||||
const oldValue = component.properties.getPropertyByPath(this.context.path);
|
||||
if (oldValue) {
|
||||
// assign previous data
|
||||
this.previousValue = oldValue;
|
||||
const newValue = this.context.property;
|
||||
oldValue.update(newValue);
|
||||
} else {
|
||||
console.warn('property not found');
|
||||
}
|
||||
} else {
|
||||
console.warn('component not found');
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
redo(prev: AppModel): AppModel {
|
||||
const component = prev.getComponentById(this.context.componentId as ComponentId);
|
||||
if (!component) {
|
||||
console.warn('component not found');
|
||||
return prev;
|
||||
}
|
||||
const newValue = this.context.property;
|
||||
component.properties.getPropertyByPath(this.context.path)!.update(newValue);
|
||||
return prev;
|
||||
}
|
||||
|
||||
undo(prev: AppModel): AppModel {
|
||||
const component = prev.getComponentById(this.context.componentId as ComponentId);
|
||||
if (!component) {
|
||||
console.warn('component not found');
|
||||
return prev;
|
||||
}
|
||||
|
||||
component.properties.getPropertyByPath(this.context.path)!.update(this.previousValue);
|
||||
|
||||
return prev;
|
||||
}
|
||||
}
|
@ -44,7 +44,8 @@ export class AppStorage {
|
||||
propertySpec?: JSONSchema7,
|
||||
events?: string[],
|
||||
moduleVersion?: string,
|
||||
moduleName?: string
|
||||
moduleName?: string,
|
||||
stateMap?: Record<string, string>
|
||||
): Module {
|
||||
let index = this.modules.length;
|
||||
|
||||
@ -77,6 +78,9 @@ export class AppStorage {
|
||||
if (events) {
|
||||
newModule.spec.events = events;
|
||||
}
|
||||
if (stateMap) {
|
||||
newModule.spec.stateMap = stateMap;
|
||||
}
|
||||
|
||||
this.setModules([...this.modules, newModule]);
|
||||
const rawModules = this.modules.map(addModuleId);
|
||||
|
@ -8,13 +8,14 @@ import { AppStorage } from './AppStorage';
|
||||
import type { SchemaValidator, ValidateErrorResult } from '../validator';
|
||||
import { ExplorerMenuTabs, ToolMenuTabs } from '../constants/enum';
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEqual, set } from 'lodash';
|
||||
import { AppModelManager } from '../operations/AppModelManager';
|
||||
import type { Metadata } from '@sunmao-ui/core';
|
||||
import { ComponentId } from '../AppModel/IAppModel';
|
||||
import { genOperation } from '../operations';
|
||||
import { MethodRelation } from '../components/ExtractModuleModal/ExtractModuleView';
|
||||
import { InsideMethodRelation } from '../components/ExtractModuleModal/ExtractModuleView';
|
||||
import { Static } from '@sinclair/typebox';
|
||||
import { OutsideExpRelationWithState } from '../components/ExtractModuleModal';
|
||||
|
||||
type EditingTarget = {
|
||||
kind: 'app' | 'module';
|
||||
@ -250,7 +251,8 @@ export class EditorStore {
|
||||
toMoveComponentIds: string[];
|
||||
moduleName: string;
|
||||
moduleVersion: string;
|
||||
methodRelations: MethodRelation[];
|
||||
methodRelations: InsideMethodRelation[];
|
||||
outsideExpRelations: OutsideExpRelationWithState[];
|
||||
}) {
|
||||
const {
|
||||
id,
|
||||
@ -259,12 +261,15 @@ export class EditorStore {
|
||||
moduleName,
|
||||
moduleVersion,
|
||||
methodRelations,
|
||||
outsideExpRelations,
|
||||
} = props;
|
||||
const root = this.appModelManager.appModel
|
||||
.getComponentById(id as ComponentId)!
|
||||
.clone();
|
||||
console.log('toMoveComponentIds', toMoveComponentIds);
|
||||
console.log('properties', properties);
|
||||
const newModuleContainerId = `${id}__module`;
|
||||
const newModuleId = `${id}Module`;
|
||||
const propertySpec: Record<string, any> = {
|
||||
type: 'object',
|
||||
properties: { ...properties },
|
||||
@ -335,6 +340,31 @@ export class EditorStore {
|
||||
};
|
||||
});
|
||||
|
||||
// 开始处理 State
|
||||
const stateMap: Record<string, string> = {};
|
||||
outsideExpRelations.forEach(r => {
|
||||
// 添加 StateMap
|
||||
if (r.stateName) {
|
||||
const origin = `${r.relyOn}.${r.valuePath}`;
|
||||
stateMap[r.stateName] = origin;
|
||||
// 然后一个个替换Exp里的字符串
|
||||
const newExp = r.exp.replaceAll(origin, `${newModuleId}.${r.stateName}`);
|
||||
const c = this.appModelManager.appModel.getComponentById(
|
||||
r.componentId as ComponentId
|
||||
)!;
|
||||
const fieldKey = r.key.startsWith('.') ? r.key.slice(1) : r.key;
|
||||
const newProperties = set(c.properties.rawValue, fieldKey, newExp);
|
||||
console.log('newProperties', newProperties);
|
||||
this.eventBus.send(
|
||||
'operation',
|
||||
genOperation(this.registry, 'modifyComponentProperties', {
|
||||
componentId: r.componentId,
|
||||
properties: newProperties,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('propertySpec', propertySpec);
|
||||
console.log('moduleComponents', moduleComponents);
|
||||
const rawModule = this.appStorage.createModule(
|
||||
@ -342,26 +372,26 @@ export class EditorStore {
|
||||
propertySpec,
|
||||
eventSpec,
|
||||
moduleVersion,
|
||||
moduleName
|
||||
moduleName,
|
||||
stateMap
|
||||
);
|
||||
|
||||
const module = createModule(rawModule);
|
||||
this.registry.registerModule(module);
|
||||
|
||||
const newId = `${id}__module`;
|
||||
this.eventBus.send(
|
||||
'operation',
|
||||
genOperation(this.registry, 'createComponent', {
|
||||
componentId: newId,
|
||||
componentId: newModuleContainerId,
|
||||
componentType: `core/v1/moduleContainer`,
|
||||
})
|
||||
);
|
||||
this.eventBus.send(
|
||||
'operation',
|
||||
genOperation(this.registry, 'modifyComponentProperty', {
|
||||
componentId: newId,
|
||||
genOperation(this.registry, 'modifyComponentProperties', {
|
||||
componentId: newModuleContainerId,
|
||||
properties: {
|
||||
id: `${id}Module`,
|
||||
id: newModuleId,
|
||||
type: `${moduleVersion}/${moduleName}`,
|
||||
properties,
|
||||
handlers: moduleHandlers,
|
||||
|
Loading…
Reference in New Issue
Block a user