@pdfme/ui 5.5.10-dev.13 → 5.5.10-dev.134

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 (77) hide show
  1. package/README.md +3 -1
  2. package/__mocks__/converter.ts +3 -0
  3. package/__mocks__/form-render.js +3 -3
  4. package/__mocks__/form-render.ts +18 -0
  5. package/__mocks__/lucide-react.ts +15 -0
  6. package/package.json +39 -86
  7. package/src/Designer.tsx +1 -3
  8. package/src/Form.tsx +1 -3
  9. package/src/Viewer.tsx +1 -3
  10. package/src/class.ts +17 -6
  11. package/src/components/CtlBar.tsx +7 -2
  12. package/src/components/Designer/PluginIcon.tsx +64 -16
  13. package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +1 -2
  14. package/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.tsx +8 -7
  15. package/src/components/Designer/RightSidebar/DetailView/index.tsx +15 -17
  16. package/src/components/Designer/RightSidebar/ListView/Item.tsx +3 -5
  17. package/src/components/Designer/index.tsx +28 -17
  18. package/src/components/Preview.tsx +40 -14
  19. package/src/components/Renderer.tsx +73 -27
  20. package/src/components/Root.tsx +8 -2
  21. package/src/contexts.ts +1 -1
  22. package/src/helper.ts +1 -1
  23. package/src/hooks.ts +102 -67
  24. package/tsconfig.build.json +15 -0
  25. package/tsconfig.json +5 -40
  26. package/tsconfig.typecheck.json +19 -0
  27. package/vite.config.mts +46 -5
  28. package/vitest.setup.ts +47 -0
  29. package/dist/index.es.js +0 -159737
  30. package/dist/index.umd.js +0 -1060
  31. package/dist/types/__tests__/assets/helper.d.ts +0 -3
  32. package/dist/types/__tests__/components/Designer.test.d.ts +0 -1
  33. package/dist/types/__tests__/components/PluginIcon.test.d.ts +0 -1
  34. package/dist/types/__tests__/components/Preview.test.d.ts +0 -1
  35. package/dist/types/__tests__/helper.test.d.ts +0 -1
  36. package/dist/types/src/Designer.d.ts +0 -21
  37. package/dist/types/src/Form.d.ts +0 -24
  38. package/dist/types/src/Viewer.d.ts +0 -15
  39. package/dist/types/src/class.d.ts +0 -89
  40. package/dist/types/src/components/AppContextProvider.d.ts +0 -11
  41. package/dist/types/src/components/CtlBar.d.ts +0 -14
  42. package/dist/types/src/components/Designer/Canvas/Guides.d.ts +0 -9
  43. package/dist/types/src/components/Designer/Canvas/Mask.d.ts +0 -4
  44. package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +0 -37
  45. package/dist/types/src/components/Designer/Canvas/Padding.d.ts +0 -6
  46. package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +0 -10
  47. package/dist/types/src/components/Designer/Canvas/index.d.ts +0 -22
  48. package/dist/types/src/components/Designer/LeftSidebar.d.ts +0 -8
  49. package/dist/types/src/components/Designer/PluginIcon.d.ts +0 -10
  50. package/dist/types/src/components/Designer/RightSidebar/DetailView/AlignWidget.d.ts +0 -4
  51. package/dist/types/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.d.ts +0 -4
  52. package/dist/types/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.d.ts +0 -7
  53. package/dist/types/src/components/Designer/RightSidebar/DetailView/index.d.ts +0 -8
  54. package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +0 -45
  55. package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.d.ts +0 -4
  56. package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.d.ts +0 -14
  57. package/dist/types/src/components/Designer/RightSidebar/ListView/index.d.ts +0 -4
  58. package/dist/types/src/components/Designer/RightSidebar/index.d.ts +0 -4
  59. package/dist/types/src/components/Designer/RightSidebar/layout.d.ts +0 -15
  60. package/dist/types/src/components/Designer/index.d.ts +0 -11
  61. package/dist/types/src/components/ErrorScreen.d.ts +0 -7
  62. package/dist/types/src/components/Paper.d.ts +0 -20
  63. package/dist/types/src/components/Preview.d.ts +0 -15
  64. package/dist/types/src/components/Renderer.d.ts +0 -13
  65. package/dist/types/src/components/Root.d.ts +0 -9
  66. package/dist/types/src/components/Spinner.d.ts +0 -3
  67. package/dist/types/src/components/StaticSchema.d.ts +0 -10
  68. package/dist/types/src/components/UnitPager.d.ts +0 -10
  69. package/dist/types/src/constants.d.ts +0 -11
  70. package/dist/types/src/contexts.d.ts +0 -10
  71. package/dist/types/src/helper.d.ts +0 -73
  72. package/dist/types/src/hooks.d.ts +0 -46
  73. package/dist/types/src/i18n.d.ts +0 -3
  74. package/dist/types/src/index.d.ts +0 -4
  75. package/dist/types/src/theme.d.ts +0 -2
  76. package/dist/types/src/types.d.ts +0 -19
  77. package/eslint.config.mjs +0 -41
package/README.md CHANGED
@@ -6,4 +6,6 @@ This library provides the following classes, which can be integrated into applic
6
6
  - `Form`: A form based on templates for collecting input data.
7
7
  - `Viewer`: A viewer for displaying PDF files generated with [@pdfme/generator](https://www.npmjs.com/package/@pdfme/generator) using templates and input data.
8
8
 
9
- For the complete documentation of pdfme, please refer to [this link](https://pdfme.com/docs/getting-started).
9
+ The published `@pdfme/ui` package ships as a standalone bundle, so consumers do not need to install `react` or `react-dom` separately just to use these classes.
10
+
11
+ For the complete documentation of pdfme, please refer to [this link](https://pdfme.com/docs/getting-started).
@@ -0,0 +1,3 @@
1
+ export const pdf2size = async () => [{ width: 210, height: 297 }];
2
+
3
+ export const pdf2img = async () => [new Uint8Array([137, 80, 78, 71]).buffer];
@@ -1,7 +1,7 @@
1
- const useForm = jest.fn();
2
- const FormRender = jest.fn();
1
+ const useForm = () => ({});
2
+ const FormRender = () => null;
3
3
 
4
4
  module.exports = {
5
5
  useForm,
6
6
  default: FormRender,
7
- };
7
+ };
@@ -0,0 +1,18 @@
1
+ const formState: Record<string, unknown> = {};
2
+
3
+ export const useForm = () => ({
4
+ resetFields: () => {
5
+ Object.keys(formState).forEach((key) => {
6
+ delete formState[key];
7
+ });
8
+ },
9
+ setValues: (values: Record<string, unknown>) => {
10
+ Object.assign(formState, values);
11
+ },
12
+ getValues: () => formState,
13
+ validateFields: () => Promise.resolve(formState),
14
+ });
15
+
16
+ const FormRender = () => null;
17
+
18
+ export default FormRender;
@@ -0,0 +1,15 @@
1
+ export const Plus = () => 'Plus';
2
+ export const Minus = () => 'Minus';
3
+ export const ChevronLeft = () => 'ChevronLeft';
4
+ export const ChevronRight = () => 'ChevronRight';
5
+ export const ChevronsLeft = () => 'ChevronsLeft';
6
+ export const ChevronsRight = () => 'ChevronsRight';
7
+ export const Ellipsis = () => 'Ellipsis';
8
+ export const X = () => 'X';
9
+ export const Menu = () => 'Menu';
10
+ export const GripVertical = () => 'GripVertical';
11
+ export const CircleAlert = () => 'CircleAlert';
12
+ export const Lock = () => 'Lock';
13
+ export const ArrowLeft = () => 'ArrowLeft';
14
+ export const ArrowRight = () => 'ArrowRight';
15
+ export const LoaderCircle = () => 'LoaderCircle';
package/package.json CHANGED
@@ -1,127 +1,80 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "5.5.10-dev.13",
4
- "sideEffects": false,
5
- "author": "hand-dot",
6
- "license": "MIT",
3
+ "version": "5.5.10-dev.134",
4
+ "description": "TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!",
7
5
  "keywords": [
8
6
  "pdf",
9
- "pdf-generation",
10
7
  "pdf-designer",
8
+ "pdf-generation",
11
9
  "pdf-viewer",
12
- "typescript",
13
- "react"
10
+ "react",
11
+ "typescript"
14
12
  ],
15
- "description": "TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!",
16
13
  "homepage": "https://pdfme.com",
14
+ "bugs": {
15
+ "url": "https://github.com/pdfme/pdfme/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "hand-dot",
17
19
  "repository": {
18
20
  "type": "git",
19
21
  "url": "git@github.com:pdfme/pdfme.git"
20
22
  },
21
- "bugs": {
22
- "url": "https://github.com/pdfme/pdfme/issues"
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "main": "./dist/index.js",
26
+ "module": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js",
32
+ "default": "./dist/index.js"
33
+ }
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
23
37
  },
24
- "main": "dist/index.umd.js",
25
- "module": "dist/index.es.js",
26
- "types": "dist/types/src/index.d.ts",
27
38
  "scripts": {
28
- "dev": "run-p devBuild:watch devBuildType:watch",
29
- "devBuild:watch": "esbuild src/index.ts --bundle --outfile=dist/index.es.js --format=esm --watch",
30
- "devBuildType:watch": "tsc --emitDeclarationOnly --watch",
31
- "build": "vite build && tsc --emitDeclarationOnly",
39
+ "dev": "vite build --watch",
40
+ "build": "vite build && tsc -p tsconfig.build.json",
32
41
  "clean": "rimraf dist",
33
- "lint": "eslint --ext .ts,.tsx src --config eslint.config.mjs",
34
- "test": "jest",
35
- "prune": "ts-prune src",
36
- "prettier": "prettier --write 'src/**/*.{ts,tsx}'"
42
+ "lint": "vp lint -c ../../.oxlintrc.json src",
43
+ "fmt": "vp fmt -c ../../.oxfmtrc.json src --write",
44
+ "test": "vitest run --config ../../vitest.config.ts"
37
45
  },
38
46
  "dependencies": {
39
47
  "@dnd-kit/core": "^6.0.8",
40
48
  "@dnd-kit/sortable": "^10.0.0",
49
+ "@dnd-kit/utilities": "^3.2.2",
41
50
  "@pdfme/converter": "*",
42
51
  "@scena/react-guides": "^0.28.2",
43
- "antd": "^5.27.4",
52
+ "antd": "^6.3.5",
44
53
  "dompurify": "^3.3.3",
45
54
  "form-render": "^2.5.5",
46
- "globrex": "^0.1.2",
47
55
  "hotkeys-js": "^4.0.2",
48
- "lucide-react": "^0.577.0",
49
- "react": "^16.14.0",
50
- "react-dom": "^16.14.0",
56
+ "lucide-react": "^1.7.0",
57
+ "rc-field-form": "^2.7.1",
58
+ "react": "^18.2.0",
59
+ "react-dom": "^18.2.0",
51
60
  "react-moveable": "^0.56.0",
52
- "react-refresh": "^0.18.0",
53
61
  "react-selecto": "^1.12.0"
54
62
  },
55
63
  "devDependencies": {
56
64
  "@pdfme/common": "*",
57
65
  "@pdfme/schemas": "*",
58
66
  "@testing-library/jest-dom": "^6.8.0",
59
- "@testing-library/react": "^12.1.2",
60
- "@types/dompurify": "^3.2.0",
61
- "@types/jest": "^30.0.0",
62
- "@types/react": "^17.0.52",
63
- "@types/react-dom": "^17.0.18",
64
- "@ungap/structured-clone": "^1.3.0",
67
+ "@testing-library/react": "^16.3.0",
68
+ "@types/react": "^18.2.15",
69
+ "@types/react-dom": "^18.2.7",
65
70
  "@vitejs/plugin-react": "^6.0.1",
66
71
  "csstype": "^3.2.3",
67
- "esbuild": "^0.27.4",
68
- "eslint-plugin-react": "^7.37.5",
69
- "eslint-plugin-react-hooks": "^7.0.1",
70
- "is-path-inside": "^4.0.0",
71
- "jest-canvas-mock": "^2.3.1",
72
- "jest-environment-jsdom": "^30.3.0",
73
- "postcss": "^8.5.8",
74
- "process": "^0.11.10",
75
- "rollup": "^4.59.0",
76
- "vite": "^7.3.1",
72
+ "vite": "^8.0.3",
77
73
  "vite-plugin-css-injected-by-js": "^4.0.1",
78
74
  "vite-tsconfig-paths": "^6.1.1"
79
75
  },
80
76
  "peerDependencies": {
81
77
  "@pdfme/common": "latest",
82
78
  "@pdfme/schemas": "latest"
83
- },
84
- "jest": {
85
- "testEnvironment": "jest-environment-jsdom",
86
- "setupFiles": [
87
- "jest-canvas-mock"
88
- ],
89
- "setupFilesAfterEnv": [
90
- "./__tests__/test-helpers.js"
91
- ],
92
- "moduleNameMapper": {
93
- "\\.(png|css)$": "<rootDir>/__mocks__/assetsTransformer.js",
94
- "^@pdfme/converter$": "<rootDir>/../converter/src/index.node.ts",
95
- "^@pdfme/schemas/utils$": "<rootDir>/../schemas/src/utils.ts",
96
- "^antd/es/": "antd/lib/",
97
- "^form-render/es/": "form-render/lib/",
98
- "^rc-picker/es/": "rc-picker/lib/",
99
- "^lodash-es$": "lodash",
100
- "\\^lucide-react$": "<rootDir>/__mocks__/lucide-react.js"
101
- },
102
- "resolver": "ts-jest-resolver",
103
- "moduleFileExtensions": [
104
- "js",
105
- "ts",
106
- "tsx"
107
- ],
108
- "transform": {
109
- "^.+\\.tsx?$": [
110
- "ts-jest",
111
- {
112
- "tsconfig": "tsconfig.json"
113
- }
114
- ]
115
- },
116
- "testMatch": [
117
- "**/*.test.ts",
118
- "**/*.test.tsx"
119
- ],
120
- "transformIgnorePatterns": [
121
- "/node_modules/(?!lucide-react)"
122
- ]
123
- },
124
- "publishConfig": {
125
- "access": "public"
126
79
  }
127
80
  }
package/src/Designer.tsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import ReactDOM from 'react-dom';
3
2
  import {
4
3
  cloneDeep,
5
4
  Template,
@@ -64,7 +63,7 @@ class Designer extends BaseUIClass {
64
63
 
65
64
  protected render() {
66
65
  if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
67
- ReactDOM.render(
66
+ this.mount(
68
67
  <AppContextProvider
69
68
  lang={this.getLang()}
70
69
  font={this.getFont()}
@@ -99,7 +98,6 @@ class Designer extends BaseUIClass {
99
98
  size={this.size}
100
99
  />
101
100
  </AppContextProvider>,
102
- this.domContainer,
103
101
  );
104
102
  }
105
103
  }
package/src/Form.tsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import ReactDOM from 'react-dom';
3
2
  import { PreviewProps } from '@pdfme/common';
4
3
  import { PreviewUI } from './class.js';
5
4
  import { DESTROYED_ERR_MSG } from './constants.js';
@@ -63,7 +62,7 @@ class Form extends PreviewUI {
63
62
 
64
63
  protected render() {
65
64
  if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
66
- ReactDOM.render(
65
+ this.mount(
67
66
  <AppContextProvider
68
67
  lang={this.getLang()}
69
68
  font={this.getFont()}
@@ -94,7 +93,6 @@ class Form extends PreviewUI {
94
93
  }}
95
94
  />
96
95
  </AppContextProvider>,
97
- this.domContainer,
98
96
  );
99
97
  }
100
98
  }
package/src/Viewer.tsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import ReactDOM from 'react-dom';
3
2
  import { PreviewProps } from '@pdfme/common';
4
3
  import { PreviewUI } from './class.js';
5
4
  import { DESTROYED_ERR_MSG } from './constants.js';
@@ -32,7 +31,7 @@ class Viewer extends PreviewUI {
32
31
 
33
32
  protected render() {
34
33
  if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
35
- ReactDOM.render(
34
+ this.mount(
36
35
  <AppContextProvider
37
36
  lang={this.getLang()}
38
37
  font={this.getFont()}
@@ -51,7 +50,6 @@ class Viewer extends PreviewUI {
51
50
  }}
52
51
  />
53
52
  </AppContextProvider>,
54
- this.domContainer,
55
53
  );
56
54
  }
57
55
  }
package/src/class.ts CHANGED
@@ -1,4 +1,5 @@
1
- import ReactDOM from 'react-dom';
1
+ import { ReactNode } from 'react';
2
+ import { createRoot, Root } from 'react-dom/client';
2
3
  import { DESTROYED_ERR_MSG, DEFAULT_LANG } from './constants.js';
3
4
  import { debounce } from './helper.js';
4
5
  import {
@@ -19,7 +20,7 @@ import {
19
20
  checkPreviewProps,
20
21
  pluginRegistry,
21
22
  } from '@pdfme/common';
22
- import { builtInPlugins } from '@pdfme/schemas';
23
+ import { builtInPlugins } from '@pdfme/schemas/builtins';
23
24
 
24
25
  export abstract class BaseUIClass {
25
26
  protected domContainer!: HTMLElement | null;
@@ -28,6 +29,8 @@ export abstract class BaseUIClass {
28
29
 
29
30
  protected size!: Size;
30
31
 
32
+ private reactRoot: Root | null = null;
33
+
31
34
  private lang: Lang = DEFAULT_LANG;
32
35
 
33
36
  private font: Font = getDefaultFont();
@@ -65,11 +68,12 @@ export abstract class BaseUIClass {
65
68
  this.domContainer = domContainer;
66
69
  this.template = cloneDeep(template);
67
70
  this.options = options;
71
+ const container = this.domContainer;
68
72
  this.size = {
69
- height: this.domContainer.clientHeight || window.innerHeight,
70
- width: this.domContainer.clientWidth || window.innerWidth,
73
+ height: container.clientHeight || window.innerHeight,
74
+ width: container.clientWidth || window.innerWidth,
71
75
  };
72
- this.resizeObserver.observe(this.domContainer);
76
+ this.resizeObserver.observe(container);
73
77
 
74
78
  const { lang, font } = options;
75
79
  if (lang) {
@@ -130,12 +134,19 @@ export abstract class BaseUIClass {
130
134
 
131
135
  public destroy() {
132
136
  if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
133
- ReactDOM.unmountComponentAtNode(this.domContainer);
137
+ this.reactRoot?.unmount();
138
+ this.reactRoot = null;
134
139
 
135
140
  this.resizeObserver.unobserve(this.domContainer);
136
141
  this.domContainer = null;
137
142
  }
138
143
 
144
+ protected mount(node: ReactNode) {
145
+ if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
146
+ this.reactRoot ??= createRoot(this.domContainer);
147
+ this.reactRoot.render(node);
148
+ }
149
+
139
150
  protected abstract render(): void;
140
151
  }
141
152
  export abstract class PreviewUI extends BaseUIClass {
@@ -1,7 +1,7 @@
1
1
  import React, { useContext } from 'react';
2
2
  import { Size } from '@pdfme/common';
3
3
  // Import icons from lucide-react
4
- // Note: In tests, these will be mocked by the mock file in __mocks__/lucide-react.js
4
+ // Note: In tests, these are replaced via the Vitest lucide-react mock.
5
5
  import { Plus, Minus, ChevronLeft, ChevronRight, Ellipsis } from 'lucide-react';
6
6
 
7
7
  import type { MenuProps } from 'antd';
@@ -60,7 +60,12 @@ type PagerProps = {
60
60
  const Pager = ({ pageCursor, pageNum, setPageCursor, style }: PagerProps) => {
61
61
  return (
62
62
  <div style={{ display: 'flex', alignItems: 'center' }}>
63
- <Button className={UI_CLASSNAME + 'page-prev'} type="text" disabled={pageCursor <= 0} onClick={() => setPageCursor(pageCursor - 1)}>
63
+ <Button
64
+ className={UI_CLASSNAME + 'page-prev'}
65
+ type="text"
66
+ disabled={pageCursor <= 0}
67
+ onClick={() => setPageCursor(pageCursor - 1)}
68
+ >
64
69
  <ChevronLeft size={16} color={style.textStyle.color} />
65
70
  </Button>
66
71
  <Text strong style={style.textStyle}>
@@ -11,26 +11,80 @@ interface PluginIconProps {
11
11
  styles?: React.CSSProperties;
12
12
  }
13
13
 
14
- const SVGIcon = ({ svgString, size, styles, label }: {
15
- svgString: string;
16
- size?: number;
17
- styles?: React.CSSProperties;
14
+ const SVGIcon = ({
15
+ svgString,
16
+ size,
17
+ styles,
18
+ label,
19
+ }: {
20
+ svgString: string;
21
+ size?: number;
22
+ styles?: React.CSSProperties;
18
23
  label: string;
19
24
  }) => {
20
25
  const processedSVG = useMemo(() => {
21
26
  // First sanitize the SVG string using DOMPurify with SVG profile
22
27
  const sanitizedSVG = DOMPurify.sanitize(svgString, {
23
28
  USE_PROFILES: { svg: true, svgFilters: true },
24
- ALLOWED_TAGS: ['svg', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'ellipse', 'g', 'defs', 'title', 'desc', 'metadata'],
25
- ALLOWED_ATTR: ['class', 'id', 'fill', 'stroke', 'stroke-width', 'viewBox', 'width', 'height', 'd', 'cx', 'cy', 'r', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'points', 'rx', 'ry', 'transform'],
29
+ ALLOWED_TAGS: [
30
+ 'svg',
31
+ 'path',
32
+ 'circle',
33
+ 'rect',
34
+ 'line',
35
+ 'polygon',
36
+ 'polyline',
37
+ 'ellipse',
38
+ 'g',
39
+ 'defs',
40
+ 'title',
41
+ 'desc',
42
+ 'metadata',
43
+ ],
44
+ ALLOWED_ATTR: [
45
+ 'class',
46
+ 'id',
47
+ 'fill',
48
+ 'stroke',
49
+ 'stroke-width',
50
+ 'viewBox',
51
+ 'width',
52
+ 'height',
53
+ 'd',
54
+ 'cx',
55
+ 'cy',
56
+ 'r',
57
+ 'x',
58
+ 'y',
59
+ 'x1',
60
+ 'y1',
61
+ 'x2',
62
+ 'y2',
63
+ 'points',
64
+ 'rx',
65
+ 'ry',
66
+ 'transform',
67
+ ],
26
68
  FORBID_TAGS: ['script', 'foreignObject', 'use', 'embed', 'iframe', 'object', 'link', 'style'],
27
- FORBID_ATTR: ['onload', 'onerror', 'onclick', 'onmouseover', 'onfocus', 'onblur', 'href', 'xlink:href', 'src', 'action', 'formaction'],
28
- KEEP_CONTENT: false
69
+ FORBID_ATTR: [
70
+ 'onload',
71
+ 'onerror',
72
+ 'onclick',
73
+ 'onmouseover',
74
+ 'onfocus',
75
+ 'onblur',
76
+ 'href',
77
+ 'xlink:href',
78
+ 'src',
79
+ 'action',
80
+ 'formaction',
81
+ ],
82
+ KEEP_CONTENT: false,
29
83
  });
30
84
 
31
85
  const parser = new DOMParser();
32
86
  const doc = parser.parseFromString(sanitizedSVG, 'image/svg+xml');
33
-
87
+
34
88
  const svgElement = doc.querySelector('svg');
35
89
  if (!svgElement) {
36
90
  return null;
@@ -49,13 +103,7 @@ const SVGIcon = ({ svgString, size, styles, label }: {
49
103
  return null;
50
104
  }
51
105
 
52
- return (
53
- <div
54
- style={styles}
55
- title={label}
56
- dangerouslySetInnerHTML={{ __html: processedSVG }}
57
- />
58
- );
106
+ return <div style={styles} title={label} dangerouslySetInnerHTML={{ __html: processedSVG }} />;
59
107
  };
60
108
 
61
109
  const PluginIcon = (props: PluginIconProps) => {
@@ -66,8 +66,7 @@ const AlignWidget = (props: PropPanelWidgetProps) => {
66
66
 
67
67
  let basePos = min;
68
68
  // Define adjust function with consistent parameter usage
69
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
- let adjust = (_size: number): number => 0;
69
+ let adjust: (size: number) => number = () => 0;
71
70
 
72
71
  if (['center', 'middle'].includes(type)) {
73
72
  basePos = (min + max) / 2;
@@ -6,21 +6,22 @@ type Props = PropPanelWidgetProps & {
6
6
  };
7
7
 
8
8
  const WidgetRenderer = (props: Props) => {
9
- const { widget, ...otherProps } = props;
10
9
  const ref = useRef<HTMLDivElement>(null);
11
10
 
12
11
  useEffect(() => {
13
- if (ref.current) {
14
- ref.current.innerHTML = '';
15
- widget({ ...otherProps, rootElement: ref.current });
12
+ const element = ref.current;
13
+ if (element) {
14
+ const { widget, ...otherProps } = props;
15
+ element.innerHTML = '';
16
+ widget({ ...otherProps, rootElement: element });
16
17
  }
17
18
 
18
19
  return () => {
19
- if (ref.current) {
20
- ref.current.innerHTML = '';
20
+ if (element) {
21
+ element.innerHTML = '';
21
22
  }
22
23
  };
23
- }, [props.activeSchema]);
24
+ }, [props]);
24
25
 
25
26
  return <div ref={ref} />;
26
27
  };
@@ -1,5 +1,5 @@
1
1
  import { useForm } from 'form-render';
2
- import React, { useRef, useContext, useState, useEffect, useCallback } from 'react';
2
+ import React, { useRef, useContext, useEffect, useCallback, useMemo } from 'react';
3
3
  import type {
4
4
  Dict,
5
5
  ChangeSchemaItem,
@@ -40,6 +40,8 @@ type DetailViewProps = Pick<
40
40
  activeSchema: SchemaForUI;
41
41
  };
42
42
 
43
+ type WidgetMap = Record<string, (props: PropPanelWidgetProps) => React.JSX.Element>;
44
+
43
45
  const DetailView = (props: DetailViewProps) => {
44
46
  const { token } = theme.useToken();
45
47
 
@@ -59,12 +61,8 @@ const DetailView = (props: DetailViewProps) => {
59
61
  [i18n],
60
62
  );
61
63
 
62
- const [widgets, setWidgets] = useState<{
63
- [key: string]: (props: PropPanelWidgetProps) => React.JSX.Element;
64
- }>({});
65
-
66
- useEffect(() => {
67
- const newWidgets: typeof widgets = {
64
+ const widgets = useMemo<WidgetMap>(() => {
65
+ const newWidgets: WidgetMap = {
68
66
  AlignWidget: (p) => <AlignWidget {...p} {...props} options={options} />,
69
67
  Divider: () => (
70
68
  <Divider style={{ marginTop: token.marginXS, marginBottom: token.marginXS }} />
@@ -72,8 +70,11 @@ const DetailView = (props: DetailViewProps) => {
72
70
  ButtonGroup: (p) => <ButtonGroupWidget {...p} {...props} options={options} />,
73
71
  };
74
72
  for (const plugin of pluginsRegistry.values()) {
75
- const widgets = plugin.propPanel.widgets || {};
76
- Object.entries(widgets).forEach(([widgetKey, widgetValue]) => {
73
+ const pluginWidgets = (plugin.propPanel.widgets ?? {}) as Record<
74
+ string,
75
+ (props: PropPanelWidgetProps) => void
76
+ >;
77
+ Object.entries(pluginWidgets).forEach(([widgetKey, widgetValue]) => {
77
78
  newWidgets[widgetKey] = (p) => (
78
79
  <WidgetRenderer
79
80
  {...p}
@@ -86,10 +87,10 @@ const DetailView = (props: DetailViewProps) => {
86
87
  );
87
88
  });
88
89
  }
89
- setWidgets(newWidgets);
90
- }, [activeSchema, pluginsRegistry, JSON.stringify(options)]);
90
+ return newWidgets;
91
+ }, [options, pluginsRegistry, props, token, typedI18n]);
91
92
 
92
- useEffect(() => form.resetFields(), [activeSchema.id]);
93
+ useEffect(() => form.resetFields(), [activeSchema.id, form]);
93
94
 
94
95
  useEffect(() => {
95
96
  // Create a type-safe copy of the schema with editable property
@@ -98,7 +99,7 @@ const DetailView = (props: DetailViewProps) => {
98
99
  const readOnly = typeof values.readOnly === 'boolean' ? values.readOnly : false;
99
100
  values.editable = !readOnly;
100
101
  form.setValues(values);
101
- }, [activeSchema]);
102
+ }, [activeSchema, form]);
102
103
 
103
104
  useEffect(() => {
104
105
  uniqueSchemaName.current = (value: string): boolean => {
@@ -114,10 +115,7 @@ const DetailView = (props: DetailViewProps) => {
114
115
  }, [schemasList, activeSchema]);
115
116
 
116
117
  // Reference to a function that validates schema name uniqueness
117
- const uniqueSchemaName = useRef(
118
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
- (_unused: string): boolean => true,
120
- );
118
+ const uniqueSchemaName = useRef<(value: string) => boolean>(() => true);
121
119
 
122
120
  // Use proper type for validator function parameter
123
121
  const validateUniqueSchemaName = (_: unknown, value: string): boolean =>
@@ -46,7 +46,6 @@ interface Props {
46
46
  // Using React.memo and forwardRef for optimized rendering
47
47
  // Using TypeScript interface for prop validation instead of PropTypes
48
48
  const Item = React.memo(
49
- /* eslint-disable react/prop-types */
50
49
  React.forwardRef<HTMLLIElement, Props>(function Item(
51
50
  {
52
51
  icon,
@@ -60,12 +59,9 @@ const Item = React.memo(
60
59
  onClick,
61
60
  onMouseEnter,
62
61
  onMouseLeave,
63
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
62
  dragging,
65
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
63
  fadeIn,
67
64
  listeners,
68
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
69
65
  sorting,
70
66
  transition,
71
67
  transform,
@@ -73,8 +69,10 @@ const Item = React.memo(
73
69
  },
74
70
  ref,
75
71
  ) {
76
- /* eslint-enable react/prop-types */
77
72
  const i18n = useContext(I18nContext);
73
+ void dragging;
74
+ void fadeIn;
75
+ void sorting;
78
76
 
79
77
  useEffect(() => {
80
78
  if (!dragOverlay) {