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;