import FormRender, { useForm } from 'form-render'; import React, { useContext, useEffect, useState } from 'react'; import type { Dict, SchemaForUI, PropPanelWidgetProps, PropPanelSchema, } from '@pdfme-tables/common'; import type { SidebarProps } from '../../../../types'; import { MenuOutlined } from '@ant-design/icons'; import { I18nContext, PluginsRegistry, OptionsContext } from '../../../../contexts'; import { getSidebarContentHeight } from '../../../../helper'; import { theme, Typography, Button, Divider } from 'antd'; import AlignWidget from './AlignWidget'; import WidgetRenderer from './WidgetRenderer'; const { Text } = Typography; const DetailView = ( props: Pick< SidebarProps, 'size' | 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements' | 'deselectSchema' > & { activeSchema: SchemaForUI; } ) => { const { token } = theme.useToken(); const { size, changeSchemas, deselectSchema, activeSchema, activeElements } = props; const form = useForm(); const i18n = useContext(I18nContext); const pluginsRegistry = useContext(PluginsRegistry); const options = useContext(OptionsContext); const [widgets, setWidgets] = useState<{ [key: string]: (props: PropPanelWidgetProps) => React.JSX.Element; }>({}); useEffect(() => { const newWidgets: typeof widgets = { AlignWidget: (p) => , Divider: () => ( ), }; for (const plugin of Object.values(pluginsRegistry)) { const widgets = plugin?.propPanel.widgets || {}; Object.entries(widgets).forEach(([widgetKey, widgetValue]) => { newWidgets[widgetKey] = (p) => ( string} widget={widgetValue} /> ); }); } setWidgets(newWidgets); }, [activeSchema, activeElements, pluginsRegistry, JSON.stringify(options)]); useEffect(() => { const values: any = { ...activeSchema }; // [position] Change the nested position object into a flat, as a three-column layout is difficult to implement values.x = values.position.x; values.y = values.position.y; delete values.position; if (values.key !== (form.getValues() || {}).key) { form.resetFields(); } form.setValues(values); }, [form, activeSchema]); const handleWatch = (newSchema: any) => { const changes = []; for (let key in newSchema) { if (['id', 'content'].includes(key)) continue; if (newSchema[key] !== (activeSchema as any)[key]) { let value = newSchema[key]; // FIXME memo: https://github.com/pdfme/pdfme/pull/367#issuecomment-1857468274 if (value === null && ['rotate', 'opacity'].includes(key)) value = undefined; // [position] Return the flattened position to its original form. if (key === 'x') key = 'position.x'; if (key === 'y') key = 'position.y'; changes.push({ key, value, schemaId: activeSchema.id }); } } if (changes.length) { changeSchemas(changes); } }; const activePlugin = Object.values(pluginsRegistry).find( (plugin) => plugin?.propPanel.defaultSchema.type === activeSchema.type )!; const activePropPanelSchema = activePlugin?.propPanel.schema; if (!activePropPanelSchema) { console.error(`[@pdfme/ui] No propPanel.schema for ${activeSchema.type}. Check this document: https://pdfme.com/docs/custom-schemas`); } const typeOptions = Object.entries(pluginsRegistry).map(([label, value]) => ({ label, value: value?.propPanel.defaultSchema.type, })); const defaultSchema = activePlugin.propPanel.defaultSchema; const propPanelSchema: PropPanelSchema = { type: 'object', column: 2, properties: { type: { title: i18n('type'), type: 'string', widget: 'select', props: { options: typeOptions }, required: true, span: 10, }, key: { title: i18n('fieldName'), type: 'string', required: true, span: 14 }, '-': { type: 'void', widget: 'Divider' }, align: { title: i18n('align'), type: 'void', widget: 'AlignWidget' }, x: { title: 'X', type: 'number', widget: 'inputNumber', required: true, span: 8, min: 0 }, y: { title: 'Y', type: 'number', widget: 'inputNumber', required: true, span: 8, min: 0 }, rotate: { title: i18n('rotate'), type: 'number', widget: 'inputNumber', disabled: defaultSchema?.rotate === undefined, max: 360, props: { min: 0 }, span: 8, }, width: { title: i18n('width'), type: 'number', widget: 'inputNumber', required: true, span: 8, props: { min: 0 }, }, height: { title: i18n('height'), type: 'number', widget: 'inputNumber', required: true, span: 8, props: { min: 0 }, }, opacity: { title: i18n('opacity'), type: 'number', widget: 'inputNumber', disabled: defaultSchema?.opacity === undefined, props: { step: 0.1, min: 0, max: 1 }, span: 8, }, }, }; if (typeof activePropPanelSchema === 'function') { const apps = activePropPanelSchema({ ...props, options, theme: token, i18n: i18n as (key: keyof Dict | string) => string, }) || {}; propPanelSchema.properties = { ...propPanelSchema.properties, ...(Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } }), ...apps, }; } else { const apps = activePropPanelSchema || {}; propPanelSchema.properties = { ...propPanelSchema.properties, ...(Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } }), ...apps, }; } return (
); }; export default DetailView;