Merge pull request #260 from webzard-io/feat/windlike-patch

feat(editor): auto-complete the parameters of the method
This commit is contained in:
tanbowensg 2022-02-08 11:00:34 +08:00 committed by GitHub
commit ed913afc94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 25 deletions

View File

@ -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];
}

View File

@ -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}

View File

@ -7,9 +7,11 @@ import React, { useState, useEffect } from 'react';
type Props = {
onChange: (json: Record<string, string>) => void;
value?: Record<string, string>;
onlySetValue?: boolean;
};
export const KeyValueEditor: React.FC<Props> = props => {
const { onlySetValue } = props;
const [rows, setRows] = useState<Array<[string, string]>>(() => {
return toPairs(props.value);
});
@ -54,6 +56,7 @@ export const KeyValueEditor: React.FC<Props> = props => {
size="sm"
onChange={onInputChange}
onBlur={onBlur}
isDisabled={onlySetValue}
/>
<Input
name="value"
@ -63,13 +66,15 @@ 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"
/>
{onlySetValue ? null : (
<IconButton
aria-label="remove row"
icon={<CloseIcon />}
size="xs"
onClick={() => onRemoveRow(i)}
variant="ghost"
/>
)}
</HStack>
);
});
@ -77,9 +82,11 @@ export const KeyValueEditor: React.FC<Props> = props => {
return (
<VStack spacing="1" alignItems="start">
{rowItems}
<Button onClick={onAddRow} size="xs">
+ Add
</Button>
{onlySetValue ? null : (
<Button onClick={onAddRow} size="xs">
+ Add
</Button>
)}
</VStack>
);
};

View File

@ -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);
}
}
});