import { css } from '@emotion/css'; import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd'; import { createForm, Field, GeneralField } from '@formily/core'; import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import { Alert, Button, Dropdown, Menu, MenuItemProps, Modal, Select, Space, Switch } from 'antd'; import classNames from 'classnames'; import { cloneDeep } from 'lodash'; import React, { createContext, useContext, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { ActionContext, CollectionManagerContext, createDesignable, Designable, FormProvider, RemoteSchemaComponent, SchemaComponent, SchemaComponentOptions, useActionContext, useAPIClient, useCollection, useCompile, useDesignable } from '..'; import { useSchemaTemplateManager } from '../schema-templates'; import { useBlockTemplateContext } from '../schema-templates/BlockTemplate'; interface SchemaSettingsProps { title?: any; dn?: Designable; field?: GeneralField; fieldSchema?: Schema; } interface SchemaSettingsContextProps { dn?: Designable; field?: GeneralField; fieldSchema?: Schema; setVisible?: any; visible?: any; template?: any; collectionName?: any; } const SchemaSettingsContext = createContext(null); export const useSchemaSettings = () => { return useContext(SchemaSettingsContext); }; interface RemoveProps { confirm?: any; removeParentsIfNoChildren?: boolean; breakRemoveOn?: ISchema | ((s: ISchema) => boolean); } type SchemaSettingsNested = { Remove?: React.FC; Item?: React.FC; Divider?: React.FC; Popup?: React.FC; SwitchItem?: React.FC; [key: string]: any; }; interface SchemaSettingsProviderProps { dn?: Designable; field?: GeneralField; fieldSchema?: Schema; setVisible?: any; visible?: any; template?: any; collectionName?: any; } export const SchemaSettingsProvider: React.FC = (props) => { const { children, fieldSchema, ...others } = props; const { getTemplateBySchema } = useSchemaTemplateManager(); const { name } = useCollection(); const template = getTemplateBySchema(fieldSchema); return ( {children} ); }; export const SchemaSettings: React.FC & SchemaSettingsNested = (props) => { const { title, dn, ...others } = props; const [visible, setVisible] = useState(false); const DropdownMenu = ( { setVisible(visible); }} overlay={{props.children}} overlayClassName={classNames( 'nb-schema-initializer-button-overlay', css` .ant-dropdown-menu-item-group-list { max-height: 40vh; overflow: auto; } `, )} > {typeof title === 'string' ? {title} : title} ); if (dn) { return ( {DropdownMenu} ); } return DropdownMenu; }; SchemaSettings.Template = (props) => { const { componentName, collectionName, resourceName } = props; const { t } = useTranslation(); const { dn, setVisible, template, fieldSchema } = useSchemaSettings(); const api = useAPIClient(); const { dn: tdn } = useBlockTemplateContext(); const { saveAsTemplate, copyTemplateSchema } = useSchemaTemplateManager(); if (!collectionName) { return null; } if (template) { return ( { const schema = await copyTemplateSchema(template); const removed = tdn.removeWithoutEmit(); tdn.insertAfterEnd(schema, { async onSuccess() { await api.request({ url: `/uiSchemas:remove/${removed['x-uid']}`, }); }, }); }} > {t('Convert reference to duplicate')} ); } return ( { setVisible(false); const values = await FormDialog(t('Save as template'), () => { return ( ); }).open({}); const sdn = createDesignable({ t, api, refresh: dn.refresh.bind(dn), current: fieldSchema.parent, }); sdn.loadAPIClientEvents(); const { key } = await saveAsTemplate({ collectionName, resourceName, componentName, name: values.name, uid: fieldSchema['x-uid'], }); sdn.removeWithoutEmit(fieldSchema); sdn.insertBeforeEnd({ type: 'void', 'x-component': 'BlockTemplate', 'x-component-props': { templateId: key, }, }); }} > {t('Save as template')} ); }; const findGridSchema = (fieldSchema) => { return fieldSchema.reduceProperties((buf, s) => { if (s['x-component'] === 'FormV2') { const f = s.reduceProperties((buf, s) => { if (s['x-component'] === 'Grid') { return s; } return buf; }, null); if (f) { return f; } } return buf; }, null); }; const findBlockTemplateSchema = (fieldSchema) => { return fieldSchema.reduceProperties((buf, s) => { if (s['x-component'] === 'FormV2') { const f = s.reduceProperties((buf, s) => { if (s['x-component'] === 'BlockTemplate') { return s; } return buf; }, null); if (f) { return f; } } return buf; }, null); }; SchemaSettings.FormItemTemplate = (props) => { const { insertAdjacentPosition = 'afterBegin', componentName, collectionName, resourceName } = props; const { t } = useTranslation(); const { dn, setVisible, template, fieldSchema } = useSchemaSettings(); const api = useAPIClient(); const { saveAsTemplate, copyTemplateSchema } = useSchemaTemplateManager(); if (!collectionName) { return null; } if (template) { return ( { const schema = await copyTemplateSchema(template); const templateSchema = findBlockTemplateSchema(fieldSchema); const sdn = createDesignable({ t, api, refresh: dn.refresh.bind(dn), current: templateSchema.parent, }); sdn.loadAPIClientEvents(); sdn.removeWithoutEmit(templateSchema); sdn.insertAdjacent(insertAdjacentPosition, schema, { async onSuccess() { await api.request({ url: `/uiSchemas:remove/${templateSchema['x-uid']}`, }); }, }); fieldSchema['x-template-key'] = null; await api.request({ url: `uiSchemas:patch`, method: 'post', data: { 'x-uid': fieldSchema['x-uid'], 'x-template-key': null, }, }); dn.refresh(); }} > {t('Convert reference to duplicate')} ); } return ( { setVisible(false); const gridSchema = findGridSchema(fieldSchema); const values = await FormDialog(t('Save as template'), () => { return ( ); }).open({}); const sdn = createDesignable({ t, api, refresh: dn.refresh.bind(dn), current: gridSchema.parent, }); sdn.loadAPIClientEvents(); const { key } = await saveAsTemplate({ collectionName, resourceName, componentName, name: values.name, uid: gridSchema['x-uid'], }); sdn.removeWithoutEmit(gridSchema); sdn.insertAdjacent(insertAdjacentPosition, { type: 'void', 'x-component': 'BlockTemplate', 'x-component-props': { templateId: key, }, }); fieldSchema['x-template-key'] = key; await api.request({ url: `uiSchemas:patch`, method: 'post', data: { 'x-uid': fieldSchema['x-uid'], 'x-template-key': key, }, }); }} > {t('Save as template')} ); }; SchemaSettings.Item = (props) => { let { eventKey } = props; return ( { info.domEvent.preventDefault(); info.domEvent.stopPropagation(); props?.onClick?.(info); }} style={{ minWidth: 120 }} > {props.children || props.title} ); }; SchemaSettings.ItemGroup = (props) => { return ; }; SchemaSettings.SubMenu = (props) => { return ; }; SchemaSettings.Divider = (props) => { return ; }; SchemaSettings.Remove = (props: any) => { const { confirm, removeParentsIfNoChildren, breakRemoveOn } = props; const { dn, template } = useSchemaSettings(); const { t } = useTranslation(); const field = useField(); const fieldSchema = useFieldSchema(); const ctx = useBlockTemplateContext(); const form = useForm(); return ( { Modal.confirm({ title: t('Delete block'), content: t('Are you sure you want to delete it?'), ...confirm, onOk() { const options = { removeParentsIfNoChildren, breakRemoveOn, }; if (field && field.required) { field.required = false; fieldSchema['required'] = false; } if (template && ctx?.dn) { ctx?.dn.remove(null, options); } else { dn.remove(null, options); } delete form.values[fieldSchema.name]; }, }); }} > {t('Delete')} ); }; SchemaSettings.SelectItem = (props) => { const { title, options, value, onChange, ...others } = props; return (
{title}