@pdfme/ui 0.0.0
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.
- package/README.md +9 -0
- package/__mocks__/assetsTransformer.js +7 -0
- package/__mocks__/form-render.js +7 -0
- package/__mocks__/lucide-react.js +19 -0
- package/dist/index.es.js +159393 -0
- package/dist/index.umd.js +1041 -0
- package/dist/types/__tests__/assets/helper.d.ts +3 -0
- package/dist/types/__tests__/components/Designer.test.d.ts +1 -0
- package/dist/types/__tests__/components/PluginIcon.test.d.ts +1 -0
- package/dist/types/__tests__/components/Preview.test.d.ts +1 -0
- package/dist/types/__tests__/helper.test.d.ts +1 -0
- package/dist/types/src/Designer.d.ts +21 -0
- package/dist/types/src/Form.d.ts +24 -0
- package/dist/types/src/Viewer.d.ts +15 -0
- package/dist/types/src/class.d.ts +89 -0
- package/dist/types/src/components/AppContextProvider.d.ts +11 -0
- package/dist/types/src/components/CtlBar.d.ts +14 -0
- package/dist/types/src/components/Designer/Canvas/Guides.d.ts +9 -0
- package/dist/types/src/components/Designer/Canvas/Mask.d.ts +4 -0
- package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +37 -0
- package/dist/types/src/components/Designer/Canvas/Padding.d.ts +6 -0
- package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +10 -0
- package/dist/types/src/components/Designer/Canvas/index.d.ts +22 -0
- package/dist/types/src/components/Designer/LeftSidebar.d.ts +8 -0
- package/dist/types/src/components/Designer/PluginIcon.d.ts +10 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/AlignWidget.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.d.ts +7 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/index.d.ts +8 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +45 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.d.ts +14 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/index.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/index.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/layout.d.ts +15 -0
- package/dist/types/src/components/Designer/index.d.ts +11 -0
- package/dist/types/src/components/ErrorScreen.d.ts +7 -0
- package/dist/types/src/components/Paper.d.ts +20 -0
- package/dist/types/src/components/Preview.d.ts +15 -0
- package/dist/types/src/components/Renderer.d.ts +13 -0
- package/dist/types/src/components/Root.d.ts +9 -0
- package/dist/types/src/components/Spinner.d.ts +3 -0
- package/dist/types/src/components/StaticSchema.d.ts +10 -0
- package/dist/types/src/components/UnitPager.d.ts +10 -0
- package/dist/types/src/constants.d.ts +11 -0
- package/dist/types/src/contexts.d.ts +10 -0
- package/dist/types/src/helper.d.ts +73 -0
- package/dist/types/src/hooks.d.ts +46 -0
- package/dist/types/src/i18n.d.ts +3 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/theme.d.ts +2 -0
- package/dist/types/src/types.d.ts +19 -0
- package/eslint.config.mjs +41 -0
- package/package.json +127 -0
- package/src/Designer.tsx +107 -0
- package/src/Form.tsx +102 -0
- package/src/Viewer.tsx +59 -0
- package/src/class.ts +188 -0
- package/src/components/AppContextProvider.tsx +78 -0
- package/src/components/CtlBar.tsx +183 -0
- package/src/components/Designer/Canvas/Guides.tsx +49 -0
- package/src/components/Designer/Canvas/Mask.tsx +20 -0
- package/src/components/Designer/Canvas/Moveable.tsx +91 -0
- package/src/components/Designer/Canvas/Padding.tsx +56 -0
- package/src/components/Designer/Canvas/Selecto.tsx +45 -0
- package/src/components/Designer/Canvas/index.tsx +536 -0
- package/src/components/Designer/LeftSidebar.tsx +120 -0
- package/src/components/Designer/PluginIcon.tsx +87 -0
- package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +229 -0
- package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +78 -0
- package/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.tsx +28 -0
- package/src/components/Designer/RightSidebar/DetailView/index.tsx +469 -0
- package/src/components/Designer/RightSidebar/ListView/Item.tsx +158 -0
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +204 -0
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +88 -0
- package/src/components/Designer/RightSidebar/ListView/index.tsx +116 -0
- package/src/components/Designer/RightSidebar/index.tsx +72 -0
- package/src/components/Designer/RightSidebar/layout.tsx +75 -0
- package/src/components/Designer/index.tsx +389 -0
- package/src/components/ErrorScreen.tsx +33 -0
- package/src/components/Paper.tsx +117 -0
- package/src/components/Preview.tsx +220 -0
- package/src/components/Renderer.tsx +165 -0
- package/src/components/Root.tsx +38 -0
- package/src/components/Spinner.tsx +45 -0
- package/src/components/StaticSchema.tsx +50 -0
- package/src/components/UnitPager.tsx +119 -0
- package/src/constants.ts +21 -0
- package/src/contexts.ts +14 -0
- package/src/helper.ts +534 -0
- package/src/hooks.ts +308 -0
- package/src/i18n.ts +903 -0
- package/src/index.ts +5 -0
- package/src/theme.ts +20 -0
- package/src/types.ts +20 -0
- package/tsconfig.json +48 -0
- package/vite.config.mts +22 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { Plugin, Schema } from '@pdfme/common';
|
|
3
|
+
import { OptionsContext } from '../../contexts.js';
|
|
4
|
+
import { theme } from 'antd';
|
|
5
|
+
import DOMPurify from 'dompurify';
|
|
6
|
+
|
|
7
|
+
interface PluginIconProps {
|
|
8
|
+
plugin: Plugin<Schema>;
|
|
9
|
+
label: string;
|
|
10
|
+
size?: number;
|
|
11
|
+
styles?: React.CSSProperties;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SVGIcon = ({ svgString, size, styles, label }: {
|
|
15
|
+
svgString: string;
|
|
16
|
+
size?: number;
|
|
17
|
+
styles?: React.CSSProperties;
|
|
18
|
+
label: string;
|
|
19
|
+
}) => {
|
|
20
|
+
const processedSVG = useMemo(() => {
|
|
21
|
+
// First sanitize the SVG string using DOMPurify with SVG profile
|
|
22
|
+
const sanitizedSVG = DOMPurify.sanitize(svgString, {
|
|
23
|
+
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'],
|
|
26
|
+
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
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const parser = new DOMParser();
|
|
32
|
+
const doc = parser.parseFromString(sanitizedSVG, 'image/svg+xml');
|
|
33
|
+
|
|
34
|
+
const svgElement = doc.querySelector('svg');
|
|
35
|
+
if (!svgElement) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Apply size attributes if specified
|
|
40
|
+
if (size) {
|
|
41
|
+
svgElement.setAttribute('width', size.toString());
|
|
42
|
+
svgElement.setAttribute('height', size.toString());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return svgElement.outerHTML;
|
|
46
|
+
}, [svgString, size]);
|
|
47
|
+
|
|
48
|
+
if (!processedSVG) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
style={styles}
|
|
55
|
+
title={label}
|
|
56
|
+
dangerouslySetInnerHTML={{ __html: processedSVG }}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const PluginIcon = (props: PluginIconProps) => {
|
|
62
|
+
const { plugin, label, size, styles } = props;
|
|
63
|
+
const { token } = theme.useToken();
|
|
64
|
+
const options = useContext(OptionsContext);
|
|
65
|
+
|
|
66
|
+
const schemaType = plugin.propPanel.defaultSchema?.type ?? '';
|
|
67
|
+
|
|
68
|
+
const icon = options.icons?.[schemaType] ?? plugin.icon;
|
|
69
|
+
const iconStyles = {
|
|
70
|
+
...styles,
|
|
71
|
+
color: token.colorText,
|
|
72
|
+
display: 'flex',
|
|
73
|
+
justifyContent: 'center',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (icon) {
|
|
77
|
+
return <SVGIcon svgString={icon} size={size} styles={iconStyles} label={label} />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div style={{ ...styles, overflow: 'hidden', fontSize: 10 }} title={label}>
|
|
82
|
+
{label}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default PluginIcon;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { Space, Button, Form } from 'antd';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { PropPanelWidgetProps } from '@pdfme/common';
|
|
4
|
+
import { DESIGNER_CLASSNAME } from '../../../../constants.js';
|
|
5
|
+
import {
|
|
6
|
+
AlignStartVertical,
|
|
7
|
+
AlignStartHorizontal,
|
|
8
|
+
AlignCenterVertical,
|
|
9
|
+
AlignCenterHorizontal,
|
|
10
|
+
AlignEndVertical,
|
|
11
|
+
AlignEndHorizontal,
|
|
12
|
+
AlignVerticalSpaceAround,
|
|
13
|
+
AlignHorizontalSpaceAround,
|
|
14
|
+
} from 'lucide-react';
|
|
15
|
+
import { round } from '../../../../helper.js';
|
|
16
|
+
|
|
17
|
+
const AlignWidget = (props: PropPanelWidgetProps) => {
|
|
18
|
+
const { activeElements, changeSchemas, schemas, pageSize, schema } = props;
|
|
19
|
+
const align = (type: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom') => {
|
|
20
|
+
const ids = activeElements.map((ae) => ae.id);
|
|
21
|
+
const ass = schemas.filter((s) => ids.includes(s.id));
|
|
22
|
+
|
|
23
|
+
const isVertical = ['left', 'center', 'right'].includes(type);
|
|
24
|
+
const tgtPos = isVertical ? 'x' : 'y';
|
|
25
|
+
const tgtSize = isVertical ? 'width' : 'height';
|
|
26
|
+
const isSingle = ass.length === 1;
|
|
27
|
+
// Access pageSize property safely with proper type assertion
|
|
28
|
+
const root =
|
|
29
|
+
pageSize && typeof pageSize === 'object'
|
|
30
|
+
? tgtSize === 'width'
|
|
31
|
+
? (pageSize as unknown as { width: number }).width
|
|
32
|
+
: (pageSize as unknown as { height: number }).height
|
|
33
|
+
: 0;
|
|
34
|
+
|
|
35
|
+
// Access position properties safely with proper type assertion
|
|
36
|
+
const min = isSingle
|
|
37
|
+
? 0
|
|
38
|
+
: Math.min(
|
|
39
|
+
...ass.map((as) => {
|
|
40
|
+
// Safely access position property with proper type assertion
|
|
41
|
+
const position =
|
|
42
|
+
as.position && typeof as.position === 'object'
|
|
43
|
+
? (as.position as unknown as { x: number; y: number })
|
|
44
|
+
: { x: 0, y: 0 };
|
|
45
|
+
return tgtPos === 'x' ? position.x : position.y;
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
const max = isSingle
|
|
49
|
+
? root
|
|
50
|
+
: Math.max(
|
|
51
|
+
...ass.map((as) => {
|
|
52
|
+
// Safely access position and size properties with proper type assertion
|
|
53
|
+
const position =
|
|
54
|
+
as.position && typeof as.position === 'object'
|
|
55
|
+
? (as.position as unknown as { x: number; y: number })
|
|
56
|
+
: { x: 0, y: 0 };
|
|
57
|
+
const posValue = tgtPos === 'x' ? position.x : position.y;
|
|
58
|
+
|
|
59
|
+
// Safely access width/height with proper type assertion
|
|
60
|
+
const asWithSize = as as unknown as { width?: number; height?: number };
|
|
61
|
+
const sizeValue = tgtSize === 'width' ? asWithSize.width || 0 : asWithSize.height || 0;
|
|
62
|
+
|
|
63
|
+
return posValue + sizeValue;
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
let basePos = min;
|
|
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;
|
|
71
|
+
|
|
72
|
+
if (['center', 'middle'].includes(type)) {
|
|
73
|
+
basePos = (min + max) / 2;
|
|
74
|
+
adjust = (size: number): number => size / 2;
|
|
75
|
+
} else if (['right', 'bottom'].includes(type)) {
|
|
76
|
+
basePos = max;
|
|
77
|
+
adjust = (size: number): number => size;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
changeSchemas(
|
|
81
|
+
ass.map((as) => {
|
|
82
|
+
// Safely access size property with proper type assertion
|
|
83
|
+
const asWithSize = as as unknown as { width?: number; height?: number; id: string };
|
|
84
|
+
const sizeValue = tgtSize === 'width' ? asWithSize.width || 0 : asWithSize.height || 0;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
key: `position.${tgtPos}`,
|
|
88
|
+
value: round(basePos - adjust(sizeValue), 2),
|
|
89
|
+
schemaId: asWithSize.id,
|
|
90
|
+
};
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const distribute = (type: 'vertical' | 'horizontal') => {
|
|
96
|
+
const ids = activeElements.map((ae) => ae.id);
|
|
97
|
+
const ass = schemas.filter((s) => ids.includes(s.id));
|
|
98
|
+
|
|
99
|
+
const isVertical = type === 'vertical';
|
|
100
|
+
const tgtPos = isVertical ? 'y' : 'x';
|
|
101
|
+
const tgtSize = isVertical ? 'height' : 'width';
|
|
102
|
+
|
|
103
|
+
// Safely access position property with proper type assertion
|
|
104
|
+
const min = Math.min(
|
|
105
|
+
...ass.map((as) => {
|
|
106
|
+
const position =
|
|
107
|
+
as.position && typeof as.position === 'object'
|
|
108
|
+
? (as.position as unknown as { x: number; y: number })
|
|
109
|
+
: { x: 0, y: 0 };
|
|
110
|
+
return tgtPos === 'x' ? position.x : position.y;
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Safely access position and size properties with proper type assertion
|
|
115
|
+
const max = Math.max(
|
|
116
|
+
...ass.map((as) => {
|
|
117
|
+
const position =
|
|
118
|
+
as.position && typeof as.position === 'object'
|
|
119
|
+
? (as.position as unknown as { x: number; y: number })
|
|
120
|
+
: { x: 0, y: 0 };
|
|
121
|
+
const posValue = tgtPos === 'x' ? position.x : position.y;
|
|
122
|
+
|
|
123
|
+
// Safely access width/height with proper type assertion
|
|
124
|
+
const asWithSize = as as unknown as { width?: number; height?: number };
|
|
125
|
+
const sizeValue = tgtSize === 'width' ? asWithSize.width || 0 : asWithSize.height || 0;
|
|
126
|
+
|
|
127
|
+
return posValue + sizeValue;
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
if (ass.length < 3) return;
|
|
132
|
+
|
|
133
|
+
const boxPos = min;
|
|
134
|
+
const boxSize = max - min;
|
|
135
|
+
// Safely access size property with proper type assertion
|
|
136
|
+
const sum = ass.reduce((acc, cur) => {
|
|
137
|
+
const curWithSize = cur as unknown as { width?: number; height?: number };
|
|
138
|
+
const sizeValue = tgtSize === 'width' ? curWithSize.width || 0 : curWithSize.height || 0;
|
|
139
|
+
return acc + sizeValue;
|
|
140
|
+
}, 0);
|
|
141
|
+
const remain = boxSize - sum;
|
|
142
|
+
const unit = remain / (ass.length - 1);
|
|
143
|
+
|
|
144
|
+
let prev = 0;
|
|
145
|
+
changeSchemas(
|
|
146
|
+
ass.map((as, index) => {
|
|
147
|
+
// Safely access size property of previous element with proper type assertion
|
|
148
|
+
const prevSize =
|
|
149
|
+
index === 0
|
|
150
|
+
? 0
|
|
151
|
+
: (() => {
|
|
152
|
+
const prevAs = ass[index - 1] as unknown as { width?: number; height?: number };
|
|
153
|
+
return tgtSize === 'width' ? prevAs.width || 0 : prevAs.height || 0;
|
|
154
|
+
})();
|
|
155
|
+
|
|
156
|
+
prev += index === 0 ? 0 : prevSize + unit;
|
|
157
|
+
const value = round(boxPos + prev, 2);
|
|
158
|
+
|
|
159
|
+
// Safely access id with proper type assertion
|
|
160
|
+
const asWithId = as as unknown as { id: string };
|
|
161
|
+
return { key: `position.${tgtPos}`, value, schemaId: asWithId.id };
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
const layoutBtns: {
|
|
166
|
+
id: string;
|
|
167
|
+
icon: React.JSX.Element;
|
|
168
|
+
onClick: () => void;
|
|
169
|
+
}[] = [
|
|
170
|
+
{
|
|
171
|
+
id: 'left',
|
|
172
|
+
icon: <AlignStartVertical size={15} />,
|
|
173
|
+
onClick: () => align('left'),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'center',
|
|
177
|
+
icon: <AlignCenterVertical size={15} />,
|
|
178
|
+
onClick: () => align('center'),
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: 'right',
|
|
182
|
+
icon: <AlignEndVertical size={15} />,
|
|
183
|
+
onClick: () => align('right'),
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: 'top',
|
|
187
|
+
icon: <AlignStartHorizontal size={15} />,
|
|
188
|
+
onClick: () => align('top'),
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'middle',
|
|
192
|
+
icon: <AlignCenterHorizontal size={15} />,
|
|
193
|
+
onClick: () => align('middle'),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'bottom',
|
|
197
|
+
icon: <AlignEndHorizontal size={15} />,
|
|
198
|
+
onClick: () => align('bottom'),
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'vertical',
|
|
202
|
+
icon: <AlignVerticalSpaceAround size={15} />,
|
|
203
|
+
onClick: () => distribute('vertical'),
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'horizontal',
|
|
207
|
+
icon: <AlignHorizontalSpaceAround size={15} />,
|
|
208
|
+
onClick: () => distribute('horizontal'),
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<Form.Item label={schema.title}>
|
|
214
|
+
<Space.Compact>
|
|
215
|
+
{layoutBtns.map((btn) => (
|
|
216
|
+
<Button
|
|
217
|
+
className={DESIGNER_CLASSNAME + 'align-' + btn.id}
|
|
218
|
+
key={btn.id}
|
|
219
|
+
style={{ padding: 7 }}
|
|
220
|
+
disabled={activeElements.length <= 2 && ['vertical', 'horizontal'].includes(btn.id)}
|
|
221
|
+
{...btn}
|
|
222
|
+
/>
|
|
223
|
+
))}
|
|
224
|
+
</Space.Compact>
|
|
225
|
+
</Form.Item>
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export default AlignWidget;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Space, Button, Form, theme } from 'antd';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { PropPanelWidgetProps, SchemaForUI } from '@pdfme/common';
|
|
4
|
+
interface ButtonConfig {
|
|
5
|
+
key: string;
|
|
6
|
+
icon: string;
|
|
7
|
+
type: 'boolean' | 'select';
|
|
8
|
+
value?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ButtonGroupWidget = (props: PropPanelWidgetProps) => {
|
|
12
|
+
const { activeElements, changeSchemas, schemas, schema } = props;
|
|
13
|
+
const { token } = theme.useToken();
|
|
14
|
+
|
|
15
|
+
const apply = (btn: ButtonConfig) => {
|
|
16
|
+
const key = btn.key;
|
|
17
|
+
const type = btn.type;
|
|
18
|
+
const ids = activeElements.map((ae) => ae.id);
|
|
19
|
+
const ass = schemas.filter((s) => ids.includes(s.id));
|
|
20
|
+
changeSchemas(
|
|
21
|
+
ass.map((s: SchemaForUI) => {
|
|
22
|
+
const oldValue = Boolean((s as Record<string, unknown>)[key] ?? false);
|
|
23
|
+
const newValue = type === 'boolean' ? !oldValue : btn.value;
|
|
24
|
+
return { key, value: newValue, schemaId: s.id };
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const isActive = (btn: ButtonConfig) => {
|
|
30
|
+
const key = btn.key;
|
|
31
|
+
const type = btn.type;
|
|
32
|
+
let active = false;
|
|
33
|
+
const ids = activeElements.map((ae) => ae.id);
|
|
34
|
+
const ass = schemas.filter((s) => ids.includes(s.id));
|
|
35
|
+
ass.forEach((s: SchemaForUI) => {
|
|
36
|
+
// Cast schema to Record to safely access dynamic properties
|
|
37
|
+
const schemaRecord = s as Record<string, unknown>;
|
|
38
|
+
active =
|
|
39
|
+
type === 'boolean' ? Boolean(schemaRecord[key] ?? false) : schemaRecord[key] === btn.value;
|
|
40
|
+
});
|
|
41
|
+
return active;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const replaceCurrentColor = (svgString: string, color?: string) =>
|
|
45
|
+
color ? svgString.replace(/="currentColor"/g, `="${color}"`) : svgString;
|
|
46
|
+
|
|
47
|
+
const svgIcon = (svgString: string) => {
|
|
48
|
+
const svgDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(
|
|
49
|
+
replaceCurrentColor(svgString, token.colorText),
|
|
50
|
+
)}`;
|
|
51
|
+
return <img width={17} height={17} src={svgDataUrl} alt="" />;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Form.Item>
|
|
56
|
+
<Space.Compact>
|
|
57
|
+
{(schema.buttons as ButtonConfig[]).map((btn: ButtonConfig, index: number) => {
|
|
58
|
+
const active = isActive(btn);
|
|
59
|
+
return (
|
|
60
|
+
<Button
|
|
61
|
+
type={active ? 'primary' : undefined}
|
|
62
|
+
ghost={active}
|
|
63
|
+
onClick={() => apply(btn)}
|
|
64
|
+
style={{
|
|
65
|
+
padding: 7,
|
|
66
|
+
zIndex: active ? 2 : 0,
|
|
67
|
+
}}
|
|
68
|
+
key={index}
|
|
69
|
+
icon={svgIcon(btn.icon)}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
})}
|
|
73
|
+
</Space.Compact>
|
|
74
|
+
</Form.Item>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default ButtonGroupWidget;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import type { PropPanelWidgetProps } from '@pdfme/common';
|
|
3
|
+
|
|
4
|
+
type Props = PropPanelWidgetProps & {
|
|
5
|
+
widget: (props: PropPanelWidgetProps) => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const WidgetRenderer = (props: Props) => {
|
|
9
|
+
const { widget, ...otherProps } = props;
|
|
10
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (ref.current) {
|
|
14
|
+
ref.current.innerHTML = '';
|
|
15
|
+
widget({ ...otherProps, rootElement: ref.current });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return () => {
|
|
19
|
+
if (ref.current) {
|
|
20
|
+
ref.current.innerHTML = '';
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}, [props.activeSchema]);
|
|
24
|
+
|
|
25
|
+
return <div ref={ref} />;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default WidgetRenderer;
|