@pdfme/ui 1.0.0-beta.8 → 1.0.0-beta.9

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.
Files changed (44) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.LICENSE.txt +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/types/class.d.ts +1 -1
  5. package/dist/types/components/Designer/Sidebar/DetailView/ExampleInputEditor.d.ts +4 -1
  6. package/dist/types/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.d.ts +4 -1
  7. package/dist/types/components/Designer/Sidebar/DetailView/TextPropEditor.d.ts +4 -1
  8. package/dist/types/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.d.ts +4 -1
  9. package/dist/types/components/Designer/Sidebar/DetailView/index.d.ts +4 -1
  10. package/dist/types/components/Designer/Sidebar/ListView/Item.d.ts +25 -0
  11. package/dist/types/components/Designer/Sidebar/ListView/SelectableSortableContainer.d.ts +3 -0
  12. package/dist/types/components/Designer/Sidebar/ListView/SelectableSortableItem.d.ts +14 -0
  13. package/dist/types/components/Designer/Sidebar/ListView/index.d.ts +3 -0
  14. package/dist/types/components/Designer/Sidebar/index.d.ts +2 -7
  15. package/dist/types/contexts.d.ts +1 -1
  16. package/dist/types/helper.d.ts +1 -1
  17. package/dist/types/hooks.d.ts +1 -0
  18. package/dist/types/i18n.d.ts +5 -2
  19. package/package.json +4 -3
  20. package/src/assets/icons/align-horizontal-center.svg +1 -0
  21. package/src/assets/icons/align-horizontal-left.svg +1 -0
  22. package/src/assets/icons/align-horizontal-right.svg +1 -0
  23. package/src/assets/icons/align-vertical-bottom.svg +1 -0
  24. package/src/assets/icons/align-vertical-middle.svg +1 -0
  25. package/src/assets/icons/align-vertical-top.svg +1 -0
  26. package/src/assets/icons/horizontal-distribute.svg +1 -0
  27. package/src/assets/icons/vertical-distribute.svg +1 -0
  28. package/src/components/Designer/Main/index.tsx +15 -7
  29. package/src/components/Designer/Sidebar/DetailView/ExampleInputEditor.tsx +4 -1
  30. package/src/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.tsx +107 -24
  31. package/src/components/Designer/Sidebar/DetailView/TextPropEditor.tsx +4 -1
  32. package/src/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.tsx +2 -2
  33. package/src/components/Designer/Sidebar/DetailView/index.tsx +4 -1
  34. package/src/components/Designer/Sidebar/ListView/Item.tsx +113 -0
  35. package/src/components/Designer/Sidebar/ListView/SelectableSortableContainer.tsx +162 -0
  36. package/src/components/Designer/Sidebar/ListView/SelectableSortableItem.tsx +78 -0
  37. package/src/components/Designer/Sidebar/ListView/index.tsx +118 -0
  38. package/src/components/Designer/Sidebar/index.tsx +18 -6
  39. package/src/components/Designer/index.tsx +8 -22
  40. package/src/helper.ts +11 -10
  41. package/src/hooks.ts +11 -0
  42. package/src/i18n.ts +12 -7
  43. package/dist/types/components/Designer/Sidebar/ListView.d.ts +0 -3
  44. package/src/components/Designer/Sidebar/ListView.tsx +0 -202
@@ -7,7 +7,7 @@ export declare abstract class BaseUIClass {
7
7
  private readonly font;
8
8
  private readonly setSize;
9
9
  constructor(props: UIProps);
10
- protected getI18n(): (key: "type" | "field" | "fieldName" | "require" | "uniq" | "inputExample" | "edit" | "plsSelect" | "plsInputName" | "plsAddNewField" | "fieldMustUniq" | "notUniq" | "noKeyName" | "fieldsList" | "addNewField" | "editField" | "previewWarnMsg" | "previewErrMsg" | "goToFirst" | "goToPrevious" | "goToNext" | "goToEnd" | "errorOccurred") => string;
10
+ protected getI18n(): (key: "type" | "field" | "cancel" | "fieldName" | "require" | "uniq" | "inputExample" | "edit" | "plsSelect" | "plsInputName" | "plsAddNewField" | "fieldMustUniq" | "notUniq" | "noKeyName" | "fieldsList" | "addNewField" | "editField" | "goToFirst" | "goToPrevious" | "goToNext" | "goToEnd" | "select" | "errorOccurred" | "errorBulkUpdateFieldName" | "commitBulkUpdateFieldName" | "bulkUpdateFieldName") => string;
11
11
  protected getFont(): Record<string, {
12
12
  fallback?: boolean | undefined;
13
13
  subset?: boolean | undefined;
@@ -1,3 +1,6 @@
1
+ import { SchemaForUI } from '@pdfme/common';
1
2
  import { SidebarProps } from '..';
2
- declare const ExampleInputEditor: (props: Pick<SidebarProps, 'changeSchemas' | 'activeSchema'>) => JSX.Element;
3
+ declare const ExampleInputEditor: (props: Pick<SidebarProps, 'changeSchemas'> & {
4
+ activeSchema: SchemaForUI;
5
+ }) => JSX.Element;
3
6
  export default ExampleInputEditor;
@@ -1,3 +1,6 @@
1
+ import { SchemaForUI } from '@pdfme/common';
1
2
  import { SidebarProps } from '..';
2
- declare const PositionAndSizeEditor: (props: Pick<SidebarProps, 'pageSize' | 'changeSchemas' | 'activeSchema'>) => JSX.Element;
3
+ declare const PositionAndSizeEditor: (props: Pick<SidebarProps, 'pageSize' | 'schemas' | 'changeSchemas' | 'activeElements'> & {
4
+ activeSchema: SchemaForUI;
5
+ }) => JSX.Element;
3
6
  export default PositionAndSizeEditor;
@@ -1,3 +1,6 @@
1
+ import { SchemaForUI } from '@pdfme/common';
1
2
  import { SidebarProps } from '..';
2
- declare const TextPropEditor: (props: Pick<SidebarProps, 'changeSchemas' | 'activeSchema'>) => JSX.Element;
3
+ declare const TextPropEditor: (props: Pick<SidebarProps, 'changeSchemas'> & {
4
+ activeSchema: SchemaForUI;
5
+ }) => JSX.Element;
3
6
  export default TextPropEditor;
@@ -1,3 +1,6 @@
1
+ import { SchemaForUI } from '@pdfme/common';
1
2
  import { SidebarProps } from '..';
2
- declare const TypeAndKeyEditor: (props: Pick<SidebarProps, 'schemas' | 'changeSchemas' | 'activeSchema'>) => JSX.Element;
3
+ declare const TypeAndKeyEditor: (props: Pick<SidebarProps, 'schemas' | 'changeSchemas'> & {
4
+ activeSchema: SchemaForUI;
5
+ }) => JSX.Element;
3
6
  export default TypeAndKeyEditor;
@@ -1,3 +1,6 @@
1
+ import { SchemaForUI } from '@pdfme/common';
1
2
  import { SidebarProps } from '..';
2
- declare const DetailView: (props: Pick<SidebarProps, 'schemas' | 'pageSize' | 'changeSchemas' | 'activeSchema'>) => JSX.Element;
3
+ declare const DetailView: (props: Pick<SidebarProps, 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements'> & {
4
+ activeSchema: SchemaForUI;
5
+ }) => JSX.Element;
3
6
  export default DetailView;
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { DraggableSyntheticListeners } from '@dnd-kit/core';
3
+ interface Props {
4
+ value: React.ReactNode;
5
+ style?: React.CSSProperties;
6
+ status?: 'is-warning' | 'is-danger';
7
+ title?: string;
8
+ dragOverlay?: boolean;
9
+ onClick?: () => void;
10
+ onMouseEnter?: () => void;
11
+ onMouseLeave?: () => void;
12
+ dragging?: boolean;
13
+ sorting?: boolean;
14
+ transition?: string;
15
+ transform?: {
16
+ x: number;
17
+ y: number;
18
+ scaleX: number;
19
+ scaleY: number;
20
+ } | null;
21
+ fadeIn?: boolean;
22
+ listeners?: DraggableSyntheticListeners;
23
+ }
24
+ declare const Item: React.MemoExoticComponent<React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLLIElement>>>;
25
+ export default Item;
@@ -0,0 +1,3 @@
1
+ import { SidebarProps } from '../';
2
+ declare const SelectableSortableContainer: (props: Pick<SidebarProps, 'schemas' | 'onEdit' | 'onSortEnd' | 'height' | 'hoveringSchemaId' | 'onChangeHoveringSchemaId'>) => JSX.Element;
3
+ export default SelectableSortableContainer;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { SchemaForUI } from '@pdfme/common';
3
+ interface Props {
4
+ isSelected: boolean;
5
+ style?: React.CSSProperties;
6
+ onSelect: (id: string, isShiftSelect: boolean) => void;
7
+ onEdit: (id: string) => void;
8
+ schema: SchemaForUI;
9
+ schemas: SchemaForUI[];
10
+ onMouseEnter: () => void;
11
+ onMouseLeave: () => void;
12
+ }
13
+ declare const SelectableSortableItem: ({ isSelected, style, onSelect, onEdit, schema, schemas, onMouseEnter, onMouseLeave, }: Props) => JSX.Element;
14
+ export default SelectableSortableItem;
@@ -0,0 +1,3 @@
1
+ import { SidebarProps } from '..';
2
+ declare const ListView: (props: Pick<SidebarProps, 'schemas' | 'onSortEnd' | 'onEdit' | 'size' | 'hoveringSchemaId' | 'onChangeHoveringSchemaId' | 'changeSchemas'>) => JSX.Element;
3
+ export default ListView;
@@ -1,18 +1,13 @@
1
1
  import { SchemaForUI, Size } from '@pdfme/common';
2
2
  export declare type SidebarProps = {
3
- scale: number;
4
3
  height: number;
5
4
  hoveringSchemaId: string | null;
6
5
  onChangeHoveringSchemaId: (id: string | null) => void;
7
6
  size: Size;
8
7
  pageSize: Size;
9
- activeElement: HTMLElement | null;
10
- activeSchema: SchemaForUI;
8
+ activeElements: HTMLElement[];
11
9
  schemas: SchemaForUI[];
12
- onSortEnd: ({ oldIndex, newIndex }: {
13
- oldIndex: number;
14
- newIndex: number;
15
- }) => void;
10
+ onSortEnd: (sortedSchemas: SchemaForUI[]) => void;
16
11
  onEdit: (id: string) => void;
17
12
  onEditEnd: () => void;
18
13
  changeSchemas: (objs: {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- export declare const I18nContext: import("react").Context<(key: "type" | "field" | "fieldName" | "require" | "uniq" | "inputExample" | "edit" | "plsSelect" | "plsInputName" | "plsAddNewField" | "fieldMustUniq" | "notUniq" | "noKeyName" | "fieldsList" | "addNewField" | "editField" | "previewWarnMsg" | "previewErrMsg" | "goToFirst" | "goToPrevious" | "goToNext" | "goToEnd" | "errorOccurred") => string>;
2
+ export declare const I18nContext: import("react").Context<(key: "type" | "field" | "cancel" | "fieldName" | "require" | "uniq" | "inputExample" | "edit" | "plsSelect" | "plsInputName" | "plsAddNewField" | "fieldMustUniq" | "notUniq" | "noKeyName" | "fieldsList" | "addNewField" | "editField" | "goToFirst" | "goToPrevious" | "goToNext" | "goToEnd" | "select" | "errorOccurred" | "errorBulkUpdateFieldName" | "commitBulkUpdateFieldName" | "bulkUpdateFieldName") => string>;
3
3
  export declare const FontContext: import("react").Context<Record<string, {
4
4
  fallback?: boolean | undefined;
5
5
  subset?: boolean | undefined;
@@ -3,7 +3,6 @@ export declare const uuid: () => string;
3
3
  export declare const set: <T extends object>(obj: T, path: string | string[], value: any) => void;
4
4
  export declare const debounce: <T extends Function>(cb: T, wait?: number) => T;
5
5
  export declare const round: (number: number, precision: number) => number;
6
- export declare const arrayMove: <T>(array: T[], from: number, to: number) => T[];
7
6
  export declare const cloneDeep: <T>(value: T) => T;
8
7
  export declare const flatten: <T>(arr: T[][]) => T[];
9
8
  declare const esc = "esc";
@@ -16,6 +15,7 @@ export declare const initShortCuts: (arg: {
16
15
  redo: () => void;
17
16
  undo: () => void;
18
17
  save: () => void;
18
+ selectAll: () => void;
19
19
  }) => void;
20
20
  export declare const destroyShortCuts: () => void;
21
21
  export declare const readFiles: (files: FileList | null, type: 'text' | 'dataURL' | 'arrayBuffer') => Promise<string | ArrayBuffer>;
@@ -23,4 +23,5 @@ declare type ScrollPageCursorProps = {
23
23
  onChangePageCursor: (page: number) => void;
24
24
  };
25
25
  export declare const useScrollPageCursor: ({ rootRef, pageSizes, scale, pageCursor, onChangePageCursor, }: ScrollPageCursorProps) => void;
26
+ export declare const useMountStatus: () => boolean;
26
27
  export {};
@@ -1,6 +1,7 @@
1
1
  import { Lang } from '@pdfme/common';
2
2
  declare type DictEn = typeof dictEn;
3
3
  declare const dictEn: {
4
+ cancel: string;
4
5
  field: string;
5
6
  fieldName: string;
6
7
  require: string;
@@ -17,13 +18,15 @@ declare const dictEn: {
17
18
  addNewField: string;
18
19
  editField: string;
19
20
  type: string;
20
- previewWarnMsg: string;
21
- previewErrMsg: string;
22
21
  goToFirst: string;
23
22
  goToPrevious: string;
24
23
  goToNext: string;
25
24
  goToEnd: string;
25
+ select: string;
26
26
  errorOccurred: string;
27
+ errorBulkUpdateFieldName: string;
28
+ commitBulkUpdateFieldName: string;
29
+ bulkUpdateFieldName: string;
27
30
  };
28
31
  export declare const curriedI18n: (lang: Lang) => (key: keyof DictEn) => string;
29
32
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0-beta.9",
4
4
  "author": "hand-dot",
5
5
  "license": "MIT",
6
6
  "description": "TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!",
@@ -29,7 +29,9 @@
29
29
  "prune": "ts-prune src"
30
30
  },
31
31
  "dependencies": {
32
- "@pdfme/common": "^1.0.0-beta.8",
32
+ "@dnd-kit/core": "^5.0.1",
33
+ "@dnd-kit/sortable": "^6.0.0",
34
+ "@pdfme/common": "^1.0.0-beta.7",
33
35
  "@scena/react-guides": "^0.16.0",
34
36
  "hotkeys-js": "^3.8.7",
35
37
  "pdfjs-dist": "^2.12.313",
@@ -37,7 +39,6 @@
37
39
  "react-dom": "^17.0.2",
38
40
  "react-moveable": "^0.30.3",
39
41
  "react-selecto": "^1.12.0",
40
- "react-sortable-hoc": "^2.0.0",
41
42
  "worker-loader": "^3.0.8"
42
43
  },
43
44
  "devDependencies": {
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><polygon points="11,2 13,2 13,7 21,7 21,10 13,10 13,14 18,14 18,17 13,17 13,22 11,22 11,17 6,17 6,14 11,14 11,10 3,10 3,7 11,7"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M4,22H2V2h2V22z M22,7H6v3h16V7z M16,14H6v3h10V14z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M20,2h2v20h-2V2z M2,10h16V7H2V10z M8,17h10v-3H8V17z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M22,22H2v-2h20V22z M10,2H7v16h3V2z M17,8h-3v10h3V8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><polygon points="22,11 17,11 17,6 14,6 14,11 10,11 10,3 7,3 7,11 1.84,11 1.84,13 7,13 7,21 10,21 10,13 14,13 14,18 17,18 17,13 22,13"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M22,2v2H2V2H22z M7,22h3V6H7V22z M14,16h3V6h-3V16z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M4,22H2V2h2V22z M22,2h-2v20h2V2z M13.5,7h-3v10h3V7z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><rect fill="none" height="24" width="24"/><path d="M22,2v2H2V2H22z M7,10.5v3h10v-3H7z M2,20v2h20v-2H2z"/></svg>
@@ -121,16 +121,15 @@ const Main = (props: Props, ref: Ref<HTMLDivElement>) => {
121
121
  }, [initEvents, destroyEvents]);
122
122
 
123
123
  useEffect(() => {
124
+ moveable.current?.updateRect();
124
125
  if (prevSchemas === null) {
125
- moveable.current?.updateRect();
126
-
127
126
  return;
128
127
  }
129
128
 
130
- const prevSchemaKeys = Object.keys(prevSchemas[pageCursor] || {});
131
- const schemaKeys = Object.keys(schemasList[pageCursor] || {});
129
+ const prevSchemaKeys = JSON.stringify(prevSchemas[pageCursor] || {});
130
+ const schemaKeys = JSON.stringify(schemasList[pageCursor] || {});
132
131
 
133
- if (prevSchemaKeys.join() === schemaKeys.join()) {
132
+ if (prevSchemaKeys === schemaKeys) {
134
133
  moveable.current?.updateRect();
135
134
  }
136
135
  }, [pageCursor, schemasList, prevSchemas]);
@@ -239,8 +238,17 @@ const Main = (props: Props, ref: Ref<HTMLDivElement>) => {
239
238
  removeSchemas(activeElements.map((ae) => ae.id));
240
239
  }
241
240
  }}
242
- onSelect={(e) => {
243
- onEdit(e.selected as HTMLElement[]);
241
+ onSelect={({ added, removed, selected, inputEvent }) => {
242
+ const isClick = inputEvent.type === 'mousedown';
243
+ let newActiveElements: HTMLElement[] = isClick ? (selected as HTMLElement[]) : [];
244
+ if (!isClick && added.length > 0) {
245
+ newActiveElements = activeElements.concat(added as HTMLElement[]);
246
+ }
247
+ if (!isClick && removed.length > 0) {
248
+ newActiveElements = activeElements.filter((ae) => !removed.includes(ae));
249
+ }
250
+
251
+ onEdit(newActiveElements);
244
252
  }}
245
253
  />
246
254
  <Paper
@@ -1,10 +1,13 @@
1
1
  import React, { useContext } from 'react';
2
+ import { SchemaForUI } from '@pdfme/common';
2
3
  import { readFiles } from '../../../../helper';
3
4
  import { I18nContext } from '../../../../contexts';
4
5
  import { SidebarProps } from '..';
5
6
  import closeIcon from '../../../../assets/icons/close.svg';
6
7
 
7
- const ExampleInputEditor = (props: Pick<SidebarProps, 'changeSchemas' | 'activeSchema'>) => {
8
+ const ExampleInputEditor = (
9
+ props: Pick<SidebarProps, 'changeSchemas'> & { activeSchema: SchemaForUI }
10
+ ) => {
8
11
  const { changeSchemas, activeSchema } = props;
9
12
  const i18n = useContext(I18nContext);
10
13
 
@@ -1,13 +1,19 @@
1
- import React from 'react';
1
+ import React, { CSSProperties } from 'react';
2
+ import { SchemaForUI } from '@pdfme/common';
3
+ import { round } from '../../../../helper';
2
4
  import { SidebarProps } from '..';
5
+ import alignVerticalTop from '../../../../assets/icons/align-vertical-top.svg';
6
+ import alignVerticalMiddle from '../../../../assets/icons/align-vertical-middle.svg';
7
+ import alignVerticalBottom from '../../../../assets/icons/align-vertical-bottom.svg';
8
+ import alignHorizontalRight from '../../../../assets/icons/align-horizontal-right.svg';
9
+ import alignHorizontalLeft from '../../../../assets/icons/align-horizontal-left.svg';
10
+ import alignHorizontalCenter from '../../../../assets/icons/align-horizontal-center.svg';
11
+ import verticalDistribute from '../../../../assets/icons/vertical-distribute.svg';
12
+ import horizontalDistribute from '../../../../assets/icons/horizontal-distribute.svg';
3
13
 
4
- const inputSetStyle: React.CSSProperties = {
5
- marginRight: '1rem',
6
- display: 'flex',
7
- alignItems: 'center',
8
- };
14
+ const inputSetStyle: CSSProperties = { marginRight: '1rem', display: 'flex', alignItems: 'center' };
9
15
 
10
- const inputStyle: React.CSSProperties = {
16
+ const inputStyle: CSSProperties = {
11
17
  width: 70,
12
18
  border: '1px solid #767676',
13
19
  borderRadius: 2,
@@ -15,20 +21,104 @@ const inputStyle: React.CSSProperties = {
15
21
  background: 'none',
16
22
  };
17
23
 
24
+ const buttonStyle: CSSProperties = {
25
+ display: 'flex',
26
+ background: 'none',
27
+ alignItems: 'center',
28
+ borderRadius: 2,
29
+ border: '1px solid rgb(118, 118, 118)',
30
+ cursor: 'pointer',
31
+ };
32
+
18
33
  const PositionAndSizeEditor = (
19
- props: Pick<SidebarProps, 'pageSize' | 'changeSchemas' | 'activeSchema'>
34
+ props: Pick<SidebarProps, 'pageSize' | 'schemas' | 'changeSchemas' | 'activeElements'> & {
35
+ activeSchema: SchemaForUI;
36
+ }
20
37
  ) => {
21
- const { changeSchemas, activeSchema, pageSize } = props;
38
+ const { changeSchemas, schemas, activeSchema, activeElements, pageSize } = props;
39
+
40
+ const align = (type: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom') => {
41
+ const ids = activeElements.map((ae) => ae.id);
42
+ const ass = schemas.filter((s) => ids.includes(s.id));
43
+
44
+ const isVertical = ['left', 'center', 'right'].includes(type);
45
+ const tgtPos = isVertical ? 'x' : 'y';
46
+ const tgtSize = isVertical ? 'width' : 'height';
47
+ const isSingle = ass.length === 1;
48
+ const root = pageSize[tgtSize];
49
+
50
+ const min = isSingle ? 0 : Math.min(...ass.map((as) => as.position[tgtPos]));
51
+ const max = isSingle ? root : Math.max(...ass.map((as) => as.position[tgtPos] + as[tgtSize]));
52
+
53
+ let basePos = min;
54
+ let adjust = (_: number) => 0;
55
+
56
+ if (['center', 'middle'].includes(type)) {
57
+ basePos = (min + max) / 2;
58
+ adjust = (num: number) => num / 2;
59
+ } else if (['right', 'bottom'].includes(type)) {
60
+ basePos = max;
61
+ adjust = (num: number) => num;
62
+ }
63
+
64
+ changeSchemas(
65
+ ass.map((as) => ({
66
+ key: `position.${tgtPos}`,
67
+ value: round(basePos - adjust(as[tgtSize]), 2),
68
+ schemaId: as.id,
69
+ }))
70
+ );
71
+ };
72
+
73
+ const distribute = (type: 'vertical' | 'horizontal') => {
74
+ const ids = activeElements.map((ae) => ae.id);
75
+ const ass = schemas.filter((s) => ids.includes(s.id));
76
+
77
+ const isVertical = type === 'vertical';
78
+ const tgtPos = isVertical ? 'y' : 'x';
79
+ const tgtSize = isVertical ? 'height' : 'width';
80
+ const min = Math.min(...ass.map((as) => as.position[tgtPos]));
81
+ const max = Math.max(...ass.map((as) => as.position[tgtPos] + as[tgtSize]));
82
+
83
+ if (ass.length < 3) return;
84
+
85
+ const boxPos = min;
86
+ const boxSize = max - min;
87
+ const sum = ass.reduce((acc, cur) => acc + cur[tgtSize], 0);
88
+ const remain = boxSize - sum;
89
+ const unit = remain / (ass.length - 1);
90
+
91
+ let prev = 0;
92
+ changeSchemas(
93
+ ass.map((as, index) => {
94
+ prev += index === 0 ? 0 : ass[index - 1][tgtSize] + unit;
95
+ const value = round(boxPos + prev, 2);
96
+ return { key: `position.${tgtPos}`, value, schemaId: as.id };
97
+ })
98
+ );
99
+ };
100
+
101
+ const layoutBtns: { id: string; icon: any; action: () => void }[] = [
102
+ { id: 'left', icon: alignHorizontalLeft, action: () => align('left') },
103
+ { id: 'center', icon: alignHorizontalCenter, action: () => align('center') },
104
+ { id: 'right', icon: alignHorizontalRight, action: () => align('right') },
105
+ { id: 'top', icon: alignVerticalTop, action: () => align('top') },
106
+ { id: 'middle', icon: alignVerticalMiddle, action: () => align('middle') },
107
+ { id: 'bottom', icon: alignVerticalBottom, action: () => align('bottom') },
108
+ { id: 'vertical', icon: verticalDistribute, action: () => distribute('vertical') },
109
+ { id: 'horizontal', icon: horizontalDistribute, action: () => distribute('horizontal') },
110
+ ];
22
111
 
23
112
  return (
24
113
  <div>
25
- <div
26
- style={{
27
- display: 'flex',
28
- alignItems: 'center',
29
- justifyContent: 'space-between',
30
- }}
31
- >
114
+ <div style={{ display: 'flex', alignItems: 'center', marginBottom: '0.5rem' }}>
115
+ {layoutBtns.map((b) => (
116
+ <button key={b.id} title={b.id} onClick={b.action} style={buttonStyle}>
117
+ <img width={15} src={b.icon} />
118
+ </button>
119
+ ))}
120
+ </div>
121
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
32
122
  <div style={inputSetStyle}>
33
123
  <label style={{ width: 17 }}>X</label>
34
124
  <input
@@ -60,14 +150,7 @@ const PositionAndSizeEditor = (
60
150
  <span style={{ fontSize: '0.6rem' }}>mm</span>
61
151
  </div>
62
152
  </div>
63
- <div
64
- style={{
65
- display: 'flex',
66
- alignItems: 'center',
67
- justifyContent: 'space-between',
68
- marginTop: '0.25rem',
69
- }}
70
- >
153
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
71
154
  <div style={inputSetStyle}>
72
155
  <label style={{ width: 17 }}>W</label>
73
156
  <input
@@ -1,5 +1,6 @@
1
1
  import React, { useContext } from 'react';
2
2
  import {
3
+ SchemaForUI,
3
4
  getFallbackFontName,
4
5
  DEFAULT_FONT_SIZE,
5
6
  DEFAULT_LINE_HEIGHT,
@@ -88,7 +89,9 @@ const SelectSet = (props: {
88
89
  );
89
90
  };
90
91
 
91
- const TextPropEditor = (props: Pick<SidebarProps, 'changeSchemas' | 'activeSchema'>) => {
92
+ const TextPropEditor = (
93
+ props: Pick<SidebarProps, 'changeSchemas'> & { activeSchema: SchemaForUI }
94
+ ) => {
92
95
  const { changeSchemas, activeSchema } = props;
93
96
  const alignments = ['left', 'center', 'right'];
94
97
  const font = useContext(FontContext);
@@ -1,5 +1,5 @@
1
1
  import React, { useContext, useRef, useCallback } from 'react';
2
- import { schemaTypes } from '@pdfme/common';
2
+ import { schemaTypes, SchemaForUI } from '@pdfme/common';
3
3
  import { SidebarProps } from '..';
4
4
  import { I18nContext } from '../../../../contexts';
5
5
 
@@ -12,7 +12,7 @@ const ErrorLabel = ({ isError, msg }: { isError: boolean; msg: string }) => (
12
12
  );
13
13
 
14
14
  const TypeAndKeyEditor = (
15
- props: Pick<SidebarProps, 'schemas' | 'changeSchemas' | 'activeSchema'>
15
+ props: Pick<SidebarProps, 'schemas' | 'changeSchemas'> & { activeSchema: SchemaForUI }
16
16
  ) => {
17
17
  const { changeSchemas, activeSchema, schemas } = props;
18
18
  const i18n = useContext(I18nContext);
@@ -1,4 +1,5 @@
1
1
  import React, { useContext } from 'react';
2
+ import { SchemaForUI } from '@pdfme/common';
2
3
  import { I18nContext } from '../../../../contexts';
3
4
  import Divider from '../../../Divider';
4
5
  import { SidebarProps } from '..';
@@ -8,7 +9,9 @@ import PositionAndSizeEditor from './PositionAndSizeEditor';
8
9
  import TypeAndKeyEditor from './TypeAndKeyEditor';
9
10
 
10
11
  const DetailView = (
11
- props: Pick<SidebarProps, 'schemas' | 'pageSize' | 'changeSchemas' | 'activeSchema'>
12
+ props: Pick<SidebarProps, 'schemas' | 'pageSize' | 'changeSchemas' | 'activeElements'> & {
13
+ activeSchema: SchemaForUI;
14
+ }
12
15
  ) => {
13
16
  const { activeSchema } = props;
14
17
  const i18n = useContext(I18nContext);
@@ -0,0 +1,113 @@
1
+ import React, { useEffect, useContext } from 'react';
2
+ import { DraggableSyntheticListeners } from '@dnd-kit/core';
3
+ import { I18nContext } from '../../../../contexts';
4
+ import dragIcon from '../../../../assets/icons/drag.svg';
5
+ import warningIcon from '../../../../assets/icons/warning.svg';
6
+
7
+ interface Props {
8
+ value: React.ReactNode;
9
+ style?: React.CSSProperties;
10
+ status?: 'is-warning' | 'is-danger';
11
+ title?: string;
12
+ dragOverlay?: boolean;
13
+ onClick?: () => void;
14
+ onMouseEnter?: () => void;
15
+ onMouseLeave?: () => void;
16
+ dragging?: boolean;
17
+ sorting?: boolean;
18
+ transition?: string;
19
+ transform?: { x: number; y: number; scaleX: number; scaleY: number } | null;
20
+ fadeIn?: boolean;
21
+ listeners?: DraggableSyntheticListeners;
22
+ }
23
+ const Item = React.memo(
24
+ React.forwardRef<HTMLLIElement, Props>(
25
+ (
26
+ {
27
+ value,
28
+ status,
29
+ title,
30
+ style,
31
+ dragOverlay,
32
+ onClick,
33
+ onMouseEnter,
34
+ onMouseLeave,
35
+ dragging,
36
+ fadeIn,
37
+ listeners,
38
+ sorting,
39
+ transition,
40
+ transform,
41
+ ...props
42
+ },
43
+ ref
44
+ ) => {
45
+ const i18n = useContext(I18nContext);
46
+
47
+ useEffect(() => {
48
+ if (!dragOverlay) {
49
+ return;
50
+ }
51
+
52
+ document.body.style.cursor = 'grabbing';
53
+
54
+ return () => {
55
+ document.body.style.cursor = '';
56
+ };
57
+ }, [dragOverlay]);
58
+
59
+ const { x, y, scaleX, scaleY } = transform || { x: 0, y: 0, scaleX: 1, scaleY: 1 };
60
+
61
+ return (
62
+ <li
63
+ style={{
64
+ transition,
65
+ transform: `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`,
66
+ }}
67
+ onMouseEnter={onMouseEnter}
68
+ onMouseLeave={onMouseLeave}
69
+ ref={ref}
70
+ >
71
+ <div style={{ display: 'flex', alignItems: 'center', ...style }} {...props}>
72
+ <button
73
+ {...listeners}
74
+ style={{ padding: '0.5rem', background: 'none', border: 'none', display: 'flex' }}
75
+ >
76
+ <img style={{ cursor: 'grab' }} src={dragIcon} width={15} alt="Drag icon" />
77
+ </button>
78
+ <div
79
+ style={{
80
+ width: '100%',
81
+ padding: '0.5rem',
82
+ paddingLeft: 0,
83
+ cursor: 'pointer',
84
+ overflow: 'hidden',
85
+ whiteSpace: 'nowrap',
86
+ textOverflow: 'ellipsis',
87
+ }}
88
+ title={title || ''}
89
+ onClick={() => onClick && onClick()}
90
+ >
91
+ {status === undefined ? (
92
+ value
93
+ ) : (
94
+ <span style={{ display: 'flex', alignItems: 'center' }}>
95
+ <img
96
+ alt="Warning icon"
97
+ src={warningIcon}
98
+ width={15}
99
+ style={{ marginRight: '0.5rem' }}
100
+ />
101
+ {status === 'is-warning' ? i18n('noKeyName') : value}
102
+ {status === 'is-danger' ? i18n('notUniq') : ''}
103
+ </span>
104
+ )}
105
+ </div>
106
+ </div>
107
+ </li>
108
+ );
109
+ }
110
+ )
111
+ );
112
+
113
+ export default Item;