@pdfme/ui 4.2.2 → 4.2.3-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,6 @@ export declare abstract class BaseUIClass {
21
21
  getTemplate(): import("zod").objectOutputType<{
22
22
  schemas: import("zod").ZodArray<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
23
23
  type: import("zod").ZodString;
24
- icon: import("zod").ZodOptional<import("zod").ZodString>;
25
24
  content: import("zod").ZodOptional<import("zod").ZodString>;
26
25
  position: import("zod").ZodObject<{
27
26
  x: import("zod").ZodNumber;
@@ -40,7 +39,6 @@ export declare abstract class BaseUIClass {
40
39
  readOnly: import("zod").ZodOptional<import("zod").ZodBoolean>;
41
40
  }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
42
41
  type: import("zod").ZodString;
43
- icon: import("zod").ZodOptional<import("zod").ZodString>;
44
42
  content: import("zod").ZodOptional<import("zod").ZodString>;
45
43
  position: import("zod").ZodObject<{
46
44
  x: import("zod").ZodNumber;
@@ -59,7 +57,6 @@ export declare abstract class BaseUIClass {
59
57
  readOnly: import("zod").ZodOptional<import("zod").ZodBoolean>;
60
58
  }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
61
59
  type: import("zod").ZodString;
62
- icon: import("zod").ZodOptional<import("zod").ZodString>;
63
60
  content: import("zod").ZodOptional<import("zod").ZodString>;
64
61
  position: import("zod").ZodObject<{
65
62
  x: import("zod").ZodNumber;
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import type { SchemaForUI } from '@pdfme/common';
3
3
  import type { SidebarProps } from '../../../../types';
4
- declare const DetailView: (props: Pick<SidebarProps, 'size' | 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements' | 'deselectSchema'> & {
4
+ type DetailViewProps = Pick<SidebarProps, 'size' | 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements' | 'deselectSchema'> & {
5
5
  activeSchema: SchemaForUI;
6
- }) => React.JSX.Element;
7
- export default DetailView;
6
+ };
7
+ declare const _default: React.MemoExoticComponent<(props: DetailViewProps) => React.JSX.Element>;
8
+ export default _default;
@@ -36,7 +36,6 @@ export declare const template2SchemasList: (_template: Template) => Promise<{
36
36
  key: string;
37
37
  opacity?: number | undefined;
38
38
  rotate?: number | undefined;
39
- icon?: string | undefined;
40
39
  content?: string | undefined;
41
40
  readOnly?: boolean | undefined;
42
41
  }[][]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "4.2.2",
3
+ "version": "4.2.3-dev.4",
4
4
  "sideEffects": false,
5
5
  "author": "hand-dot",
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@ import { theme, Button } from 'antd';
8
8
  import { useDraggable } from '@dnd-kit/core';
9
9
  import { CSS } from "@dnd-kit/utilities";
10
10
  import Renderer from '../Renderer';
11
- import { PluginsRegistry } from '../../contexts';
11
+ import { PluginsRegistry, OptionsContext } from '../../contexts';
12
12
 
13
13
  const Draggable = (props: { plugin: Plugin<any>, scale: number, basePdf: BasePdf, children: React.ReactNode }) => {
14
14
  const { scale, basePdf, plugin } = props;
@@ -44,6 +44,7 @@ const Draggable = (props: { plugin: Plugin<any>, scale: number, basePdf: BasePdf
44
44
  const LeftSidebar = ({ height, scale, basePdf }: { height: number, scale: number, basePdf: BasePdf }) => {
45
45
  const { token } = theme.useToken();
46
46
  const pluginsRegistry = useContext(PluginsRegistry);
47
+ const options = useContext(OptionsContext);
47
48
 
48
49
  const [isDragging, setIsDragging] = useState(false);
49
50
 
@@ -76,6 +77,8 @@ const LeftSidebar = ({ height, scale, basePdf }: { height: number, scale: number
76
77
  >
77
78
  {Object.entries(pluginsRegistry).map(([label, plugin]) => {
78
79
  if (!plugin?.propPanel.defaultSchema) return null;
80
+ const icon = options.icons?.[plugin.propPanel.defaultSchema.type] ?? plugin.icon;
81
+
79
82
  return <Draggable
80
83
  key={label}
81
84
  scale={scale}
@@ -87,8 +90,8 @@ const LeftSidebar = ({ height, scale, basePdf }: { height: number, scale: number
87
90
  setIsDragging(true);
88
91
  }}
89
92
  style={{ width: 35, height: 35, marginTop: '0.25rem', padding: '0.25rem' }}>
90
- {plugin.propPanel.defaultSchema.icon ?
91
- <div dangerouslySetInnerHTML={{ __html: plugin.propPanel.defaultSchema.icon }} />
93
+ {icon ?
94
+ <div dangerouslySetInnerHTML={{ __html: icon }} />
92
95
  :
93
96
  <div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{label}</div>
94
97
  }
@@ -1,5 +1,5 @@
1
1
  import FormRender, { useForm } from 'form-render';
2
- import React, { useContext, useEffect, useState } from 'react';
2
+ import React, { useContext, useEffect } from 'react';
3
3
  import type { ChangeSchemaItem, Dict, SchemaForUI, PropPanelWidgetProps, PropPanelSchema } from '@pdfme/common';
4
4
  import type { SidebarProps } from '../../../../types';
5
5
  import { MenuOutlined } from '@ant-design/icons';
@@ -13,79 +13,57 @@ import { InternalNamePath, ValidateErrorEntity } from "rc-field-form/es/interfac
13
13
 
14
14
  const { Text } = Typography;
15
15
 
16
- const DetailView = (
17
- props: Pick<
18
- SidebarProps,
19
- 'size' | 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements' | 'deselectSchema'
20
- > & {
21
- activeSchema: SchemaForUI;
22
- }
23
- ) => {
16
+ type DetailViewProps = Pick<SidebarProps,
17
+ 'size' | 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements' | 'deselectSchema'
18
+ > & {
19
+ activeSchema: SchemaForUI;
20
+ };
21
+
22
+ const DetailView = (props: DetailViewProps) => {
24
23
  const { token } = theme.useToken();
25
24
 
26
- const { size, changeSchemas, deselectSchema, activeSchema, activeElements } = props;
25
+ const { size, changeSchemas, deselectSchema, activeSchema } = props;
27
26
  const form = useForm();
28
27
 
29
28
  const i18n = useContext(I18nContext);
30
29
  const pluginsRegistry = useContext(PluginsRegistry);
31
30
  const options = useContext(OptionsContext);
32
31
 
33
- const [widgets, setWidgets] = useState<{
34
- [key: string]: (props: PropPanelWidgetProps) => React.JSX.Element;
35
- }>({});
36
-
37
- useEffect(() => {
38
- const newWidgets: typeof widgets = {
39
- AlignWidget: (p) => <AlignWidget {...p} {...props} options={options} />,
40
- Divider: () => (
41
- <Divider style={{ marginTop: token.marginXS, marginBottom: token.marginXS }} />
42
- ),
43
- ButtonGroup: (p) => <ButtonGroupWidget {...p} {...props} options={options} />,
44
- };
45
- for (const plugin of Object.values(pluginsRegistry)) {
46
- const widgets = plugin?.propPanel.widgets || {};
47
- Object.entries(widgets).forEach(([widgetKey, widgetValue]) => {
48
- newWidgets[widgetKey] = (p) => (
49
- <WidgetRenderer
50
- {...p}
51
- {...props}
52
- options={options}
53
- theme={token}
54
- i18n={i18n as (key: keyof Dict | string) => string}
55
- widget={widgetValue}
56
- />
57
- );
58
- });
59
- }
60
- setWidgets(newWidgets);
61
- }, [activeSchema, activeElements, pluginsRegistry, JSON.stringify(options)]);
62
-
63
32
  useEffect(() => {
64
33
  const values: any = { ...activeSchema };
65
-
66
34
  // [position] Change the nested position object into a flat, as a three-column layout is difficult to implement
67
35
  values.x = values.position.x;
68
36
  values.y = values.position.y;
69
37
  delete values.position;
38
+ form.setValues(values);
70
39
 
71
- if (values.key !== (form.getValues() || {}).key) {
72
- form.resetFields();
73
- }
40
+ }, [activeSchema, form]);
74
41
 
75
- form.setValues(values);
76
- }, [form, activeSchema]);
77
42
 
78
- const handleWatch = (newSchema: any) => {
43
+ const handleWatch = (formSchema: any) => {
44
+ const formAndSchemaValuesDiffer = (formValue: any, schemaValue: any): boolean => {
45
+ if (typeof formValue === 'object') {
46
+ return JSON.stringify(formValue) !== JSON.stringify(schemaValue);
47
+ }
48
+ return formValue !== schemaValue;
49
+ }
50
+
79
51
  let changes: ChangeSchemaItem[] = [];
80
- for (let key in newSchema) {
52
+ for (let key in formSchema) {
81
53
  if (['id', 'content'].includes(key)) continue;
82
54
 
83
- // [position] Return the flattened position to its original form.
84
- if (key === 'x') key = 'position.x';
85
- if (key === 'y') key = 'position.y';
55
+ let value = formSchema[key];
56
+ let changed = false;
86
57
 
87
- if (newSchema[key] !== (activeSchema as any)[key]) {
88
- let value = newSchema[key];
58
+ if (['x', 'y'].includes(key)) {
59
+ // [position] Return the flattened position to its original form.
60
+ changed = value !== (activeSchema as any)['position'][key];
61
+ key = 'position.' + key;
62
+ } else {
63
+ changed = formAndSchemaValuesDiffer(value, (activeSchema as any)[key]);
64
+ }
65
+
66
+ if (changed) {
89
67
  // FIXME memo: https://github.com/pdfme/pdfme/pull/367#issuecomment-1857468274
90
68
  if (value === null && ['rotate', 'opacity'].includes(key)) value = undefined;
91
69
 
@@ -98,11 +76,11 @@ const DetailView = (
98
76
  form.validateFields()
99
77
  .then(() => changeSchemas(changes))
100
78
  .catch((reason: ValidateErrorEntity) => {
101
- if (reason.errorFields.length) {
79
+ if (reason.errorFields.length) {
102
80
  changes = changes.filter((change: ChangeSchemaItem) => !reason.errorFields.find((field: {
103
- name: InternalNamePath;
104
- errors: string[];
105
- }) => field.name.includes(change.key)
81
+ name: InternalNamePath;
82
+ errors: string[];
83
+ }) => field.name.includes(change.key)
106
84
  ));
107
85
  }
108
86
  if (changes.length) {
@@ -203,6 +181,31 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
203
181
  };
204
182
  }
205
183
 
184
+ const allWidgets: {
185
+ [key: string]: (props: PropPanelWidgetProps) => React.JSX.Element;
186
+ } = {
187
+ AlignWidget: (p) => <AlignWidget {...p} {...props} options={options} />,
188
+ Divider: () => (
189
+ <Divider style={{ marginTop: token.marginXS, marginBottom: token.marginXS }} />
190
+ ),
191
+ ButtonGroup: (p) => <ButtonGroupWidget {...p} {...props} options={options} />,
192
+ };
193
+ for (const plugin of Object.values(pluginsRegistry)) {
194
+ const widgets = plugin?.propPanel.widgets || {};
195
+ Object.entries(widgets).forEach(([widgetKey, widgetValue]) => {
196
+ allWidgets[widgetKey] = (p) => (
197
+ <WidgetRenderer
198
+ {...p}
199
+ {...props}
200
+ options={options}
201
+ theme={token}
202
+ i18n={i18n as (key: keyof Dict | string) => string}
203
+ widget={widgetValue}
204
+ />
205
+ );
206
+ });
207
+ }
208
+
206
209
  return (
207
210
  <div>
208
211
  <div style={{ height: 40, display: 'flex', alignItems: 'center' }}>
@@ -232,7 +235,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
232
235
  <FormRender
233
236
  form={form}
234
237
  schema={propPanelSchema}
235
- widgets={widgets}
238
+ widgets={allWidgets}
236
239
  watch={{ '#': handleWatch }}
237
240
  locale="en-US"
238
241
  />
@@ -241,4 +244,8 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
241
244
  );
242
245
  };
243
246
 
244
- export default DetailView;
247
+ const propsAreUnchanged = (prevProps: DetailViewProps, nextProps: DetailViewProps) => {
248
+ return JSON.stringify(prevProps.activeSchema) == JSON.stringify(nextProps.activeSchema)
249
+ };
250
+
251
+ export default React.memo(DetailView, propsAreUnchanged);
@@ -19,6 +19,7 @@ import { I18nContext, PluginsRegistry } from '../../contexts';
19
19
  import {
20
20
  schemasList2template,
21
21
  uuid,
22
+ round,
22
23
  cloneDeep,
23
24
  template2SchemasList,
24
25
  getPagesScrollTopByIndex,
@@ -51,7 +52,7 @@ const TemplateEditor = ({
51
52
  onSaveTemplate: (t: Template) => void;
52
53
  onChangeTemplate: (t: Template) => void;
53
54
  } & {
54
- onChangeTemplate: (t: Template) => void
55
+ onChangeTemplate: (t: Template) => void
55
56
  onPageCursorChange: (newPageCursor: number) => void
56
57
  }) => {
57
58
  const past = useRef<SchemaForUI[][]>([]);
@@ -253,7 +254,7 @@ const TemplateEditor = ({
253
254
  const moveY = (event.delta.y - canvasTopOffsetFromPageCorner) / scale;
254
255
  const moveX = (event.delta.x - canvasLeftOffsetFromPageCorner) / scale;
255
256
 
256
- const position = { x: px2mm(Math.max(0, moveX)), y: px2mm(Math.max(0, moveY)) }
257
+ const position = { x: round(px2mm(Math.max(0, moveX)), 2), y: round(px2mm(Math.max(0, moveY)), 2) }
257
258
 
258
259
  addSchema({ ...(active.data.current as Schema), position });
259
260
  }}
package/src/helper.ts CHANGED
@@ -454,10 +454,15 @@ const handleTypeChange = (
454
454
  delete schema[key as keyof typeof schema];
455
455
  }
456
456
  });
457
+ // Apply attributes from new defaultSchema
457
458
  const propPanel = Object.values(pluginsRegistry).find(
458
459
  (plugin) => plugin?.propPanel.defaultSchema.type === value
459
460
  )?.propPanel;
460
- Object.assign(schema, propPanel?.defaultSchema || {});
461
+ Object.keys(propPanel?.defaultSchema || {}).forEach((key) => {
462
+ if (!schema.hasOwnProperty(key)) {
463
+ (schema as any)[key] = propPanel?.defaultSchema[key];
464
+ }
465
+ });
461
466
  };
462
467
 
463
468
  export const changeSchemas = (args: {
@@ -484,4 +489,4 @@ export const changeSchemas = (args: {
484
489
  return acc;
485
490
  }, cloneDeep(schemas));
486
491
  commitSchemas(newSchemas);
487
- };
492
+ };