@pdfme/ui 1.1.10 → 1.2.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.
@@ -26,6 +26,10 @@ export declare abstract class BaseUIClass {
26
26
  fontName?: string | undefined;
27
27
  fontColor?: string | undefined;
28
28
  characterSpacing?: number | undefined;
29
+ dynamicFontSize?: {
30
+ max: number;
31
+ min: number;
32
+ } | undefined;
29
33
  type: "text";
30
34
  height: number;
31
35
  width: number;
@@ -12,7 +12,10 @@ export declare type SidebarProps = {
12
12
  onEditEnd: () => void;
13
13
  changeSchemas: (objs: {
14
14
  key: string;
15
- value: string | number;
15
+ value: undefined | string | number | {
16
+ min: number;
17
+ max: number;
18
+ };
16
19
  schemaId: string;
17
20
  }[]) => void;
18
21
  addSchema: () => void;
@@ -6,7 +6,7 @@ declare const TemplateEditor: ({ template, size, onSaveTemplate, onChangeTemplat
6
6
  subset?: boolean | undefined;
7
7
  data: string | ArrayBuffer | Uint8Array;
8
8
  }> | undefined;
9
- lang?: "en" | "ja" | undefined;
9
+ lang?: "en" | "ja" | "ar" | undefined;
10
10
  } | undefined;
11
11
  template: {
12
12
  columns?: string[] | undefined;
@@ -20,6 +20,10 @@ declare const TemplateEditor: ({ template, size, onSaveTemplate, onChangeTemplat
20
20
  fontName?: string | undefined;
21
21
  fontColor?: string | undefined;
22
22
  characterSpacing?: number | undefined;
23
+ dynamicFontSize?: {
24
+ max: number;
25
+ min: number;
26
+ } | undefined;
23
27
  type: "text";
24
28
  height: number;
25
29
  width: number;
@@ -64,6 +68,10 @@ declare const TemplateEditor: ({ template, size, onSaveTemplate, onChangeTemplat
64
68
  fontName?: string | undefined;
65
69
  fontColor?: string | undefined;
66
70
  characterSpacing?: number | undefined;
71
+ dynamicFontSize?: {
72
+ max: number;
73
+ min: number;
74
+ } | undefined;
67
75
  type: "text";
68
76
  height: number;
69
77
  width: number;
@@ -8,7 +8,7 @@ export interface SchemaUIProps {
8
8
  placeholder?: string;
9
9
  }
10
10
  declare const _default: React.ForwardRefExoticComponent<SchemaUIProps & {
11
- border: string;
11
+ outline: string;
12
12
  onChangeHoveringSchemaId?: ((id: string | null) => void) | undefined;
13
13
  } & React.RefAttributes<HTMLTextAreaElement | HTMLInputElement>>;
14
14
  export default _default;
@@ -10,6 +10,10 @@ declare const _default: React.ForwardRefExoticComponent<SchemaUIProps & {
10
10
  fontName?: string | undefined;
11
11
  fontColor?: string | undefined;
12
12
  characterSpacing?: number | undefined;
13
+ dynamicFontSize?: {
14
+ max: number;
15
+ min: number;
16
+ } | undefined;
13
17
  type: "text";
14
18
  height: number;
15
19
  width: number;
@@ -35,6 +35,10 @@ export declare const templateSchemas2SchemasList: (_template: Template) => Promi
35
35
  fontName?: string | undefined;
36
36
  fontColor?: string | undefined;
37
37
  characterSpacing?: number | undefined;
38
+ dynamicFontSize?: {
39
+ max: number;
40
+ min: number;
41
+ } | undefined;
38
42
  type: "text";
39
43
  data: string;
40
44
  key: string;
@@ -1,7 +1,7 @@
1
1
  import Designer from './Designer';
2
2
  import Form from './Form';
3
3
  import Viewer from './Viewer';
4
- import { BLANK_PDF, HELVETICA, isTextSchema, isImageSchema, isBarcodeSchema, checkTemplate, checkUIProps, checkPreviewProps, checkDesignerProps, checkGenerateProps, validateBarcodeInput } from '@pdfme/common';
4
+ import { BLANK_PDF, DEFAULT_FONT_VALUE, isTextSchema, isImageSchema, isBarcodeSchema, checkTemplate, checkUIProps, checkPreviewProps, checkDesignerProps, checkGenerateProps, validateBarcodeInput } from '@pdfme/common';
5
5
  import type { Lang, Size, Alignment, SchemaType, BarCodeType, TextSchema, ImageSchema, BarcodeSchema, Schema, SchemaForUI, Font, BasePdf, Template, CommonProps, GeneratorOptions, GenerateProps, UIOptions, UIProps, PreviewProps, DesignerProps } from '@pdfme/common';
6
- export { Designer, Viewer, Form, BLANK_PDF, HELVETICA, isTextSchema, isImageSchema, isBarcodeSchema, checkTemplate, checkUIProps, checkPreviewProps, checkDesignerProps, checkGenerateProps, validateBarcodeInput, };
6
+ export { Designer, Viewer, Form, BLANK_PDF, DEFAULT_FONT_VALUE, isTextSchema, isImageSchema, isBarcodeSchema, checkTemplate, checkUIProps, checkPreviewProps, checkDesignerProps, checkGenerateProps, validateBarcodeInput, };
7
7
  export type { Lang, Size, Alignment, SchemaType, BarCodeType, TextSchema, ImageSchema, BarcodeSchema, Schema, SchemaForUI, Font, BasePdf, Template, CommonProps, GeneratorOptions, GenerateProps, UIOptions, UIProps, PreviewProps, DesignerProps, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "1.1.10",
3
+ "version": "1.2.0",
4
4
  "sideEffects": false,
5
5
  "author": "hand-dot",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "@dnd-kit/core": "^5.0.1",
42
42
  "@dnd-kit/sortable": "^6.0.0",
43
43
  "@heroicons/react": "^2.0.13",
44
- "@pdfme/common": "^1.0.0",
44
+ "@pdfme/common": "file:../common",
45
45
  "@scena/react-guides": "^0.16.0",
46
46
  "hotkeys-js": "^3.8.7",
47
47
  "pdfjs-dist": "^2.12.313",
@@ -68,6 +68,9 @@
68
68
  "setupFiles": [
69
69
  "jest-canvas-mock"
70
70
  ],
71
+ "setupFilesAfterEnv": [
72
+ "./__tests__/test-helpers.js"
73
+ ],
71
74
  "moduleNameMapper": {
72
75
  "\\.(png)$": "<rootDir>/../../assetsTransformer.js"
73
76
  },
@@ -14,6 +14,7 @@ const _Guides = ({
14
14
  }) => (
15
15
  <>
16
16
  <div
17
+ className="ruler-container"
17
18
  style={{
18
19
  width: RULER_HEIGHT,
19
20
  height: RULER_HEIGHT,
@@ -6,6 +6,7 @@ import React, {
6
6
  useEffect,
7
7
  forwardRef,
8
8
  useCallback,
9
+ useContext,
9
10
  } from 'react';
10
11
  import { OnDrag, OnResize, OnClick } from 'react-moveable';
11
12
  import { SchemaForUI, Size } from '@pdfme/common';
@@ -19,6 +20,7 @@ import Selecto from './Selecto';
19
20
  import Moveable from './Moveable';
20
21
  import Guides from './Guides';
21
22
  import Mask from './Mask';
23
+ import { FontContext } from '../../../contexts';
22
24
 
23
25
  const DELETE_BTN_ID = uuid();
24
26
  const fmt4Num = (prop: string) => Number(prop.replace('px', ''));
@@ -97,6 +99,7 @@ const Main = (props: Props, ref: Ref<HTMLDivElement>) => {
97
99
  const verticalGuides = useRef<GuidesInterface[]>([]);
98
100
  const horizontalGuides = useRef<GuidesInterface[]>([]);
99
101
  const moveable = useRef<any>(null);
102
+ const font = useContext(FontContext);
100
103
 
101
104
  const [isPressShiftKey, setIsPressShiftKey] = useState(false);
102
105
  const [editing, setEditing] = useState(false);
@@ -162,7 +165,7 @@ const Main = (props: Props, ref: Ref<HTMLDivElement>) => {
162
165
  changeSchemas(flatten(arg));
163
166
  };
164
167
 
165
- const onResizeEnd = ({ target }: { target: HTMLElement | SVGElement }) => {
168
+ const onResizeEnd = async ({ target }: { target: HTMLElement | SVGElement }) => {
166
169
  const { id, style } = target;
167
170
  const { width, height, top, left } = style;
168
171
  changeSchemas([
@@ -171,6 +174,15 @@ const Main = (props: Props, ref: Ref<HTMLDivElement>) => {
171
174
  { key: 'position.y', value: fmt(top), schemaId: id },
172
175
  { key: 'position.x', value: fmt(left), schemaId: id },
173
176
  ]);
177
+
178
+ const targetSchema = schemasList[pageCursor].find((schema) => schema.id === id);
179
+
180
+ if (!targetSchema) return;
181
+
182
+ targetSchema.width = fmt(width);
183
+ targetSchema.height = fmt(height);
184
+ targetSchema.position.y = fmt(top);
185
+ targetSchema.position.x = fmt(left);
174
186
  };
175
187
 
176
188
  const onResizeEnds = ({ targets }: { targets: (HTMLElement | SVGElement)[] }) => {
@@ -312,8 +324,10 @@ const Main = (props: Props, ref: Ref<HTMLDivElement>) => {
312
324
  schema={schema}
313
325
  onChangeHoveringSchemaId={onChangeHoveringSchemaId}
314
326
  editable={editing && activeElements.map((ae) => ae.id).includes(schema.id)}
315
- onChange={(value) => changeSchemas([{ key: 'data', value, schemaId: schema.id }])}
316
- border={hoveringSchemaId === schema.id ? '1px solid #18a0fb' : '1px dashed #4af'}
327
+ onChange={async (value) => {
328
+ changeSchemas([{ key: 'data', value, schemaId: schema.id }]);
329
+ }}
330
+ outline={hoveringSchemaId === schema.id ? '1px solid #18a0fb' : '1px dashed #4af'}
317
331
  ref={inputRef}
318
332
  />
319
333
  )}
@@ -1,7 +1,7 @@
1
1
  import React, { useContext } from 'react';
2
2
  import { SchemaForUI } from '@pdfme/common';
3
3
  import { readFiles } from '../../../../helper';
4
- import { I18nContext } from '../../../../contexts';
4
+ import { FontContext, I18nContext } from '../../../../contexts';
5
5
  import { SidebarProps } from '..';
6
6
  import { XMarkIcon } from '@heroicons/react/24/outline';
7
7
 
@@ -10,6 +10,7 @@ const ExampleInputEditor = (
10
10
  ) => {
11
11
  const { changeSchemas, activeSchema } = props;
12
12
  const i18n = useContext(I18nContext);
13
+ const fontData = useContext(FontContext);
13
14
 
14
15
  return (
15
16
  <div>
@@ -64,9 +65,9 @@ const ExampleInputEditor = (
64
65
  ) : (
65
66
  <textarea
66
67
  rows={6}
67
- onChange={(e) =>
68
- changeSchemas([{ key: 'data', value: e.target.value, schemaId: activeSchema.id }])
69
- }
68
+ onChange={async (e) => {
69
+ changeSchemas([{ key: 'data', value: e.target.value, schemaId: activeSchema.id }]);
70
+ }}
70
71
  style={{
71
72
  width: '100%',
72
73
  border: '1px solid #767676',
@@ -1,7 +1,8 @@
1
- import React, { CSSProperties } from 'react';
1
+ import React, { CSSProperties, useContext } from 'react';
2
2
  import { SchemaForUI } from '@pdfme/common';
3
3
  import { round } from '../../../../helper';
4
4
  import { SidebarProps } from '../index';
5
+ import { FontContext } from '../../../../contexts';
5
6
 
6
7
  const inputSetStyle: CSSProperties = { marginRight: '1rem', display: 'flex', alignItems: 'center' };
7
8
 
@@ -37,6 +38,7 @@ const PositionAndSizeEditor = (
37
38
  activeSchema: SchemaForUI;
38
39
  }
39
40
  ) => {
41
+ const font = useContext(FontContext);
40
42
  const { changeSchemas, schemas, activeSchema, activeElements, pageSize } = props;
41
43
 
42
44
  const align = (type: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom') => {
@@ -184,7 +186,7 @@ const PositionAndSizeEditor = (
184
186
  ];
185
187
 
186
188
  return (
187
- <div>
189
+ <section>
188
190
  <div style={{ display: 'flex', alignItems: 'center', marginBottom: '0.5rem' }}>
189
191
  {layoutBtns.map((b) => (
190
192
  <button key={b.id} title={b.id} onClick={b.action} style={buttonStyle}>
@@ -228,8 +230,12 @@ const PositionAndSizeEditor = (
228
230
  </div>
229
231
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
230
232
  <div style={inputSetStyle}>
231
- <label style={{ width: 17 }}>W</label>
233
+ <label htmlFor="input-width" style={{ width: 17 }}>
234
+ W
235
+ </label>
232
236
  <input
237
+ id="input-width"
238
+ name="input-width"
233
239
  style={inputStyle}
234
240
  type="number"
235
241
  onChange={(e) => {
@@ -243,8 +249,12 @@ const PositionAndSizeEditor = (
243
249
  <span style={{ fontSize: '0.6rem' }}>mm</span>
244
250
  </div>
245
251
  <div style={inputSetStyle}>
246
- <label style={{ width: 17 }}>H</label>
252
+ <label htmlFor="input-height" style={{ width: 17 }}>
253
+ H
254
+ </label>
247
255
  <input
256
+ id="input-height"
257
+ name="input-height"
248
258
  style={inputStyle}
249
259
  type="number"
250
260
  onChange={(e) => {
@@ -258,7 +268,7 @@ const PositionAndSizeEditor = (
258
268
  <span style={{ fontSize: '0.6rem' }}>mm</span>
259
269
  </div>
260
270
  </div>
261
- </div>
271
+ </section>
262
272
  );
263
273
  };
264
274
 
@@ -24,14 +24,26 @@ const NumberInputSet = (props: {
24
24
  width: string;
25
25
  label: string;
26
26
  value: number;
27
+ minNumber?: number;
28
+ maxNumber?: number;
27
29
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
28
30
  }) => {
29
- const { label, value, width, onChange } = props;
31
+ const { label, value, width, minNumber, maxNumber, onChange } = props;
32
+ const formattedLabel = label.replace(/\s/g, '');
30
33
 
31
34
  return (
32
35
  <div style={{ width }}>
33
- <label>{label}</label>
34
- <input style={inputStyle} onChange={onChange} value={value} type="number" />
36
+ <label htmlFor={`input-${formattedLabel}`}>{label}</label>
37
+ <input
38
+ id={`input-${formattedLabel}`}
39
+ name={`input-${formattedLabel}`}
40
+ style={inputStyle}
41
+ onChange={onChange}
42
+ value={value}
43
+ type="number"
44
+ {...(minNumber && { min: minNumber })}
45
+ {...(maxNumber && { max: maxNumber })}
46
+ />
35
47
  </div>
36
48
  );
37
49
  };
@@ -43,12 +55,20 @@ const ColorInputSet = (props: {
43
55
  onClear: () => void;
44
56
  }) => {
45
57
  const { label, value, onChange, onClear } = props;
58
+ const formattedLabel = label.replace(/\s/g, '');
46
59
 
47
60
  return (
48
61
  <div style={{ width: '45%' }}>
49
- <label>{label}</label>
62
+ <label htmlFor={`input-${formattedLabel}`}>{label}</label>
50
63
  <div style={{ display: 'flex' }}>
51
- <input onChange={onChange} value={value || '#ffffff'} type="color" style={inputStyle} />
64
+ <input
65
+ id={`input-${formattedLabel}`}
66
+ name={`input-${formattedLabel}`}
67
+ onChange={onChange}
68
+ value={value || '#ffffff'}
69
+ type="color"
70
+ style={inputStyle}
71
+ />
52
72
  <button
53
73
  onClick={onClear}
54
74
  style={{
@@ -75,11 +95,18 @@ const SelectSet = (props: {
75
95
  onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
76
96
  }) => {
77
97
  const { label, value, options, onChange } = props;
98
+ const formattedLabel = label.replace(/\s/g, '');
78
99
 
79
100
  return (
80
101
  <div style={{ width: '45%' }}>
81
- <label>{label}:</label>
82
- <select style={selectStyle} onChange={onChange} value={value}>
102
+ <label htmlFor={`select-${formattedLabel}`}>{label}:</label>
103
+ <select
104
+ id={`select-${formattedLabel}`}
105
+ name={`select-${formattedLabel}`}
106
+ style={selectStyle}
107
+ onChange={onChange}
108
+ value={value}
109
+ >
83
110
  {options.map((o) => (
84
111
  <option key={o} value={o}>
85
112
  {o}
@@ -90,6 +117,29 @@ const SelectSet = (props: {
90
117
  );
91
118
  };
92
119
 
120
+ const CheckboxSet = (props: {
121
+ width: string;
122
+ label: string;
123
+ onChange: React.ChangeEventHandler<HTMLInputElement>;
124
+ checked: boolean | undefined;
125
+ }) => {
126
+ const { width, label, onChange, checked } = props;
127
+
128
+ return (
129
+ <label
130
+ style={{
131
+ display: 'flex',
132
+ alignItems: 'center',
133
+ gap: '5px',
134
+ width: `${width}`,
135
+ }}
136
+ >
137
+ <input type="checkbox" checked={checked} onChange={onChange} />
138
+ {label}
139
+ </label>
140
+ );
141
+ };
142
+
93
143
  const TextPropEditor = (
94
144
  props: Pick<SidebarProps, 'changeSchemas'> & { activeSchema: SchemaForUI }
95
145
  ) => {
@@ -101,7 +151,7 @@ const TextPropEditor = (
101
151
  if (activeSchema.type !== 'text') return <></>;
102
152
 
103
153
  return (
104
- <div style={{ fontSize: '0.7rem' }}>
154
+ <section style={{ fontSize: '0.7rem' }}>
105
155
  <div
106
156
  style={{
107
157
  display: 'flex',
@@ -115,7 +165,9 @@ const TextPropEditor = (
115
165
  value={activeSchema.fontName ?? fallbackFontName}
116
166
  options={Object.keys(font)}
117
167
  onChange={(e) => {
118
- changeSchemas([{ key: 'fontName', value: e.target.value, schemaId: activeSchema.id }]);
168
+ changeSchemas([
169
+ { key: 'fontName', value: e.target.value, schemaId: activeSchema.id, },
170
+ ]);
119
171
  }}
120
172
  />
121
173
 
@@ -140,11 +192,12 @@ const TextPropEditor = (
140
192
  width="30%"
141
193
  label={'FontSize(pt)'}
142
194
  value={activeSchema.fontSize ?? DEFAULT_FONT_SIZE}
143
- onChange={(e) =>
195
+ onChange={(e) => {
196
+ const currentFontSize = Number(e.target.value);
144
197
  changeSchemas([
145
- { key: 'fontSize', value: Number(e.target.value), schemaId: activeSchema.id },
146
- ])
147
- }
198
+ { key: 'fontSize', value: currentFontSize, schemaId: activeSchema.id, },
199
+ ]);
200
+ }}
148
201
  />
149
202
  <NumberInputSet
150
203
  width="30%"
@@ -161,12 +214,64 @@ const TextPropEditor = (
161
214
  width="40%"
162
215
  label={'CharacterSpacing(pt)'}
163
216
  value={activeSchema.characterSpacing ?? DEFAULT_CHARACTER_SPACING}
164
- onChange={(e) =>
217
+ onChange={async (e) => {
218
+ const currentCharacterSpacing = Number(e.target.value);
165
219
  changeSchemas([
166
- { key: 'characterSpacing', value: Number(e.target.value), schemaId: activeSchema.id },
167
- ])
168
- }
220
+ { key: 'characterSpacing', value: currentCharacterSpacing, schemaId: activeSchema.id, },
221
+ ]);
222
+ }}
223
+ />
224
+ </div>
225
+ <div
226
+ style={{
227
+ display: 'flex',
228
+ alignItems: 'center',
229
+ justifyContent: 'space-between',
230
+ flexWrap: 'wrap',
231
+ marginBottom: '0.25rem',
232
+ }}
233
+ >
234
+ <CheckboxSet
235
+ width="100%"
236
+ label="Use dynamic font size"
237
+ checked={Boolean(activeSchema.dynamicFontSize)}
238
+ onChange={(e) => {
239
+ changeSchemas([
240
+ {
241
+ key: 'dynamicFontSize', value: e.target.checked ? {
242
+ min: activeSchema.fontSize || DEFAULT_FONT_SIZE,
243
+ max: activeSchema.fontSize || DEFAULT_FONT_SIZE,
244
+ } : undefined, schemaId: activeSchema.id,
245
+ },
246
+ ]);
247
+ }}
169
248
  />
249
+
250
+ {activeSchema.dynamicFontSize && (
251
+ <>
252
+ <NumberInputSet
253
+ width="45%"
254
+ label={'FontSize Min(pt)'}
255
+ value={activeSchema.dynamicFontSize.min ?? Number(activeSchema.fontSize)}
256
+ minNumber={0}
257
+ maxNumber={activeSchema.fontSize}
258
+ onChange={(e) => {
259
+ changeSchemas([{ key: 'dynamicFontSize.min', value: Number(e.target.value), schemaId: activeSchema.id }])
260
+
261
+ }}
262
+ />
263
+
264
+ <NumberInputSet
265
+ width="45%"
266
+ label={'FontSize Max(pt)'}
267
+ value={activeSchema.dynamicFontSize.max ?? Number(activeSchema.fontSize)}
268
+ minNumber={activeSchema.fontSize}
269
+ onChange={(e) => {
270
+ changeSchemas([{ key: 'dynamicFontSize.max', value: Number(e.target.value), schemaId: activeSchema.id }])
271
+ }}
272
+ />
273
+ </>
274
+ )}
170
275
  </div>
171
276
  <div
172
277
  style={{
@@ -202,7 +307,7 @@ const TextPropEditor = (
202
307
  }
203
308
  />
204
309
  </div>
205
- </div>
310
+ </section>
206
311
  );
207
312
  };
208
313
 
@@ -38,6 +38,7 @@ const ListView = (
38
38
  </span>
39
39
  </div>
40
40
  <Divider />
41
+
41
42
  {isBulkUpdateFieldNamesMode ? (
42
43
  <div>
43
44
  <textarea
@@ -1,10 +1,11 @@
1
1
  import React, { useState, useContext } from 'react';
2
2
  import { SchemaForUI, Size } from '@pdfme/common';
3
3
  import { RULER_HEIGHT, SIDEBAR_WIDTH } from '../../../constants';
4
- import { I18nContext } from '../../../contexts';
4
+ import { I18nContext, FontContext } from '../../../contexts';
5
5
  import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
6
6
  import ListView from './ListView/index';
7
7
  import DetailView from './DetailView/index';
8
+ import { getFallbackFontName } from '@pdfme/common';
8
9
 
9
10
  export type SidebarProps = {
10
11
  height: number;
@@ -17,7 +18,14 @@ export type SidebarProps = {
17
18
  onSortEnd: (sortedSchemas: SchemaForUI[]) => void;
18
19
  onEdit: (id: string) => void;
19
20
  onEditEnd: () => void;
20
- changeSchemas: (objs: { key: string; value: string | number; schemaId: string }[]) => void;
21
+ changeSchemas: (
22
+ objs: {
23
+ key: string; value: undefined | string | number | {
24
+ min: number;
25
+ max: number;
26
+ }; schemaId: string
27
+ }[]
28
+ ) => void;
21
29
  addSchema: () => void;
22
30
  };
23
31
 
@@ -25,10 +33,20 @@ const Sidebar = (props: SidebarProps) => {
25
33
  const { height, size, activeElements, schemas, addSchema } = props;
26
34
 
27
35
  const i18n = useContext(I18nContext);
36
+ const fonts = useContext(FontContext);
37
+ const fallbackFont = getFallbackFontName(fonts);
28
38
  const [open, setOpen] = useState(true);
29
39
 
30
40
  const getActiveSchemas = () => {
31
41
  const ids = activeElements.map((ae) => ae.id);
42
+ const activeSchema = schemas.find((s) => ids.includes(s.id));
43
+
44
+ if (activeSchema?.type === 'text') {
45
+ if (!activeSchema.fontName) {
46
+ activeSchema.fontName = fallbackFont;
47
+ }
48
+ }
49
+
32
50
  return schemas.filter((s) => ids.includes(s.id));
33
51
  };
34
52
 
@@ -90,7 +90,7 @@ const TemplateEditor = ({
90
90
  );
91
91
 
92
92
  const changeSchemas = useCallback(
93
- (objs: { key: string; value: string | number; schemaId: string }[]) => {
93
+ (objs: { key: string; value: undefined | string | number | { min: number, max: number }; schemaId: string }[]) => {
94
94
  const newSchemas = objs.reduce((acc, { key, value, schemaId }) => {
95
95
  const tgt = acc.find((s) => s.id === schemaId)!;
96
96
  // Assign to reference
@@ -103,7 +103,7 @@ const Preview = ({ template, inputs, size, onChangeInput }: PreviewReactProps) =
103
103
  placeholder={template.sampledata ? template.sampledata[0][key] : ''}
104
104
  tabIndex={index + 100}
105
105
  onChange={(value) => handleChangeInput({ key, value })}
106
- border={editable ? '1px dashed #4af' : 'transparent'}
106
+ outline={editable ? '1px dashed #4af' : 'transparent'}
107
107
  />
108
108
  );
109
109
  }}
@@ -14,13 +14,13 @@ export interface SchemaUIProps {
14
14
  }
15
15
 
16
16
  type Props = SchemaUIProps & {
17
- border: string;
17
+ outline: string;
18
18
  onChangeHoveringSchemaId?: (id: string | null) => void;
19
19
  };
20
20
 
21
21
  const Wrapper = ({
22
22
  children,
23
- border,
23
+ outline,
24
24
  onChangeHoveringSchemaId,
25
25
  schema,
26
26
  }: Props & { children: ReactNode }) => (
@@ -37,7 +37,7 @@ const Wrapper = ({
37
37
  width: schema.width * ZOOM,
38
38
  top: schema.position.y * ZOOM,
39
39
  left: schema.position.x * ZOOM,
40
- border,
40
+ outline,
41
41
  }}
42
42
  >
43
43
  {children}
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, Ref } from 'react';
1
+ import React, { useContext, forwardRef, Ref, useState, useEffect } from 'react';
2
2
  import {
3
3
  DEFAULT_FONT_SIZE,
4
4
  DEFAULT_ALIGNMENT,
@@ -6,9 +6,12 @@ import {
6
6
  DEFAULT_CHARACTER_SPACING,
7
7
  DEFAULT_FONT_COLOR,
8
8
  TextSchema,
9
+ calculateDynamicFontSize,
9
10
  } from '@pdfme/common';
10
11
  import { SchemaUIProps } from './SchemaUI';
11
12
  import { ZOOM } from '../../constants';
13
+ import { FontContext } from '../../contexts';
14
+
12
15
 
13
16
  type Props = SchemaUIProps & { schema: TextSchema };
14
17
 
@@ -16,20 +19,30 @@ const TextSchemaUI = (
16
19
  { schema, editable, placeholder, tabIndex, onChange }: Props,
17
20
  ref: Ref<HTMLTextAreaElement>
18
21
  ) => {
22
+ const font = useContext(FontContext);
23
+
24
+
25
+ const [dynamicFontSize, setDynamicFontSize] = useState<number | undefined>(undefined);
26
+
27
+ useEffect(() => {
28
+ if (schema.dynamicFontSize && schema.data) {
29
+ calculateDynamicFontSize({ textSchema: schema, font, input: schema.data }).then(setDynamicFontSize)
30
+ }
31
+ }, [schema.data, schema.width, schema.fontName, schema.dynamicFontSize, schema.dynamicFontSize?.max, schema.dynamicFontSize?.min, schema.characterSpacing, font]);
32
+
19
33
  const style: React.CSSProperties = {
20
34
  padding: 0,
21
35
  resize: 'none',
22
36
  position: 'absolute',
23
37
  fontFamily: schema.fontName ? `'${schema.fontName}'` : 'inherit',
24
38
  height: schema.height * ZOOM,
25
- // Increase the width by 1 point. (0.75 pixels)
26
- width: (schema.width + (schema.characterSpacing ?? DEFAULT_CHARACTER_SPACING) * 0.75) * ZOOM,
39
+ width: schema.width * ZOOM,
27
40
  textAlign: schema.alignment ?? DEFAULT_ALIGNMENT,
28
- fontSize: `${schema.fontSize ?? DEFAULT_FONT_SIZE}pt`,
41
+ fontSize: `${dynamicFontSize ?? schema.fontSize ?? DEFAULT_FONT_SIZE}pt`,
29
42
  letterSpacing: `${schema.characterSpacing ?? DEFAULT_CHARACTER_SPACING}pt`,
30
43
  lineHeight: `${schema.lineHeight ?? DEFAULT_LINE_HEIGHT}em`,
31
44
  whiteSpace: 'pre-line',
32
- wordBreak: 'break-all',
45
+ wordBreak: 'break-word',
33
46
  border: 'none',
34
47
  color: schema.fontColor ? schema.fontColor : DEFAULT_FONT_COLOR,
35
48
  backgroundColor: