Merge pull request #669 from smartxworks/feat/event-widget-form

Editor support emit and listen module's event
This commit is contained in:
tanbowensg 2023-01-18 10:03:54 +08:00 committed by GitHub
commit a09b65955c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 70 additions and 8 deletions

View File

@ -14,6 +14,8 @@ import {
CoreWidgetName,
generateDefaultValueFromSpec,
MountEvents,
GLOBAL_MODULE_ID,
ModuleEventMethodSpec,
} from '@sunmao-ui/shared';
import { JSONSchema7Object } from 'json-schema';
import { PREVENT_POPOVER_WIDGET_CLOSE_CLASS } from '../../constants/widget';
@ -32,7 +34,7 @@ declare module '../../types/widget' {
export const EventWidget: React.FC<WidgetProps<EventWidgetType>> = observer(props => {
const { value, path, level, component, spec, services, onChange } = props;
const { registry, editorStore, appModelManager } = services;
const { components } = editorStore;
const { components, currentEditingTarget } = editorStore;
const utilMethods = useMemo(() => registry.getAllUtilMethods(), [registry]);
const [methods, setMethods] = useState<string[]>([]);
@ -62,9 +64,22 @@ export const EventWidget: React.FC<WidgetProps<EventWidgetType>> = observer(prop
},
[registry]
);
const eventTypes = useMemo(() => {
return [...registry.getComponentByType(component.type).spec.events, ...MountEvents];
}, [component.type, registry]);
let moduleEvents: string[] = [];
if (component.type === 'core/v1/moduleContainer') {
// if component is moduleContainer, add module events to it
const moduleType = component.properties.type as string;
const moduleSpec = registry.getModuleByType(moduleType);
moduleEvents = moduleSpec.spec.events;
}
return [
...registry.getComponentByType(component.type).spec.events,
...moduleEvents,
...MountEvents,
];
}, [component.properties.type, component.type, registry]);
const hasParams = useMemo(
() => Object.keys(formik.values.method.parameters ?? {}).length,
[formik.values.method.parameters]
@ -79,6 +94,8 @@ export const EventWidget: React.FC<WidgetProps<EventWidgetType>> = observer(prop
const targetMethod = registry.getUtilMethodByType(methodType)!;
spec = targetMethod.spec.parameters;
} else if (componentId === GLOBAL_MODULE_ID) {
spec = ModuleEventMethodSpec;
} else {
const targetComponent = appModelManager.appModel.getComponentById(componentId);
const targetMethod = (findMethodsByComponent(targetComponent) ?? []).find(
@ -140,19 +157,42 @@ export const EventWidget: React.FC<WidgetProps<EventWidgetType>> = observer(prop
utilMethod => `${utilMethod.version}/${utilMethod.metadata.name}`
)
);
} else if (
componentId === GLOBAL_MODULE_ID &&
currentEditingTarget.kind === 'module'
) {
// if user is editing module, show the events of module spec as method
const moduleType = `${currentEditingTarget.version}/${currentEditingTarget.name}`;
let methodNames: string[] = [];
if (moduleType) {
const moduleSpec = services.registry.getModuleByType(moduleType);
if (moduleSpec) {
methodNames = moduleSpec.spec.events;
}
}
setMethods(methodNames);
} else {
// if user is editing application, show methods of component
const component = components.find(c => c.id === componentId);
if (component) {
const methodNames: string[] = findMethodsByComponent(component).map(
({ name }) => name
);
setMethods(methodNames);
}
}
},
[components, utilMethods, findMethodsByComponent]
[
currentEditingTarget.kind,
currentEditingTarget.version,
currentEditingTarget.name,
utilMethods,
services.registry,
components,
findMethodsByComponent,
]
);
useEffect(() => {
@ -235,6 +275,11 @@ export const EventWidget: React.FC<WidgetProps<EventWidgetType>> = observer(prop
style={{ width: '100%' }}
value={formik.values.componentId === '' ? undefined : formik.values.componentId}
>
{currentEditingTarget.kind === 'module' ? (
<ComponentTargetSelect.Option key={GLOBAL_MODULE_ID} value={GLOBAL_MODULE_ID}>
{GLOBAL_MODULE_ID}
</ComponentTargetSelect.Option>
) : undefined}
{[{ id: GLOBAL_UTIL_METHOD_ID }].concat(components).map(c => (
<ComponentTargetSelect.Option key={c.id} value={c.id}>
{c.id}

View File

@ -7,6 +7,11 @@ export interface EditorServicesInterface extends UIServices {
registry: RegistryInterface;
editorStore: {
components: ComponentSchema[];
currentEditingTarget: {
kind: 'app' | 'module';
version: string;
name: string;
};
};
appModelManager: {
appModel: any;

View File

@ -18,7 +18,7 @@ dayjs.locale('zh-cn');
type EvalOptions = {
scopeObject?: Record<string, any>;
overrideScope?: boolean;
fallbackWhenError?: (exp: string) => any;
fallbackWhenError?: (exp: string, err: Error) => any;
// when ignoreEvalError is true, the eval process will continue after error happens in nests expression.
ignoreEvalError?: boolean;
slotKey?: string;
@ -128,7 +128,9 @@ export class StateManager {
consoleError(ConsoleType.Expression, raw, expressionError.message);
}
return fallbackWhenError ? fallbackWhenError(raw) : expressionError;
return fallbackWhenError
? fallbackWhenError(raw, expressionError)
: expressionError;
}
return undefined;
}

View File

@ -1,6 +1,6 @@
import { Static, Type } from '@sinclair/typebox';
import { debounce, throttle, delay } from 'lodash';
import { EventCallBackHandlerSpec } from '@sunmao-ui/shared';
import { EventCallBackHandlerSpec, MODULE_ID_EXP } from '@sunmao-ui/shared';
import { type PropsBeforeEvaled } from '@sunmao-ui/core';
import { UIServices } from '../types';
@ -18,6 +18,10 @@ export const runEventHandler = (
// Eval before sending event to assure the handler object is evaled from the latest state.
const evalOptions = {
slotKey,
// keep MODULE_ID_EXP when error
fallbackWhenError(exp: string, err: Error) {
return exp === MODULE_ID_EXP ? exp : err;
},
};
const evaledHandlers = stateManager.deepEval(rawHandlers, evalOptions) as Static<
typeof EventCallBackHandlerSpec

View File

@ -7,6 +7,7 @@ export const LIST_ITEM_INDEX_EXP = '$i';
export const SLOT_PROPS_EXP = '$slot';
export const GLOBAL_UTIL_METHOD_ID = '$utils';
export const GLOBAL_MODULE_ID = '$module';
export const MODULE_ID_EXP = '{{$moduleId}}';
export const ExpressionKeywords = [
LIST_ITEM_EXP,

View File

@ -1,6 +1,7 @@
import { EventHandlerSpec } from './event';
import { Type } from '@sinclair/typebox';
import { CORE_VERSION, CoreWidgetName } from '../constants/core';
import { MODULE_ID_EXP } from '../constants';
export const ModuleRenderSpec = Type.Object(
{
@ -27,3 +28,7 @@ export const ModuleRenderSpec = Type.Object(
widget: 'core/v1/module',
}
);
export const ModuleEventMethodSpec = Type.Object({
moduleId: Type.Literal(MODULE_ID_EXP),
});