@pdfme/ui 2.1.0 → 2.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.
@@ -25,6 +25,7 @@ export declare abstract class BaseUIClass {
25
25
  };
26
26
  rotate?: number | undefined;
27
27
  alignment?: "center" | "left" | "right" | undefined;
28
+ verticalAlignment?: "top" | "bottom" | "middle" | undefined;
28
29
  fontSize?: number | undefined;
29
30
  fontName?: string | undefined;
30
31
  fontColor?: string | undefined;
@@ -11,6 +11,7 @@ declare const TemplateEditor: ({ template, size, onSaveTemplate, onChangeTemplat
11
11
  };
12
12
  rotate?: number | undefined;
13
13
  alignment?: "center" | "left" | "right" | undefined;
14
+ verticalAlignment?: "top" | "bottom" | "middle" | undefined;
14
15
  fontSize?: number | undefined;
15
16
  fontName?: string | undefined;
16
17
  fontColor?: string | undefined;
@@ -60,6 +61,7 @@ declare const TemplateEditor: ({ template, size, onSaveTemplate, onChangeTemplat
60
61
  };
61
62
  rotate?: number | undefined;
62
63
  alignment?: "center" | "left" | "right" | undefined;
64
+ verticalAlignment?: "top" | "bottom" | "middle" | undefined;
63
65
  fontSize?: number | undefined;
64
66
  fontName?: string | undefined;
65
67
  fontColor?: string | undefined;
@@ -11,6 +11,7 @@ declare const _default: React.ForwardRefExoticComponent<SchemaUIProps & {
11
11
  };
12
12
  rotate?: number | undefined;
13
13
  alignment?: "center" | "left" | "right" | undefined;
14
+ verticalAlignment?: "top" | "bottom" | "middle" | undefined;
14
15
  fontSize?: number | undefined;
15
16
  fontName?: string | undefined;
16
17
  fontColor?: string | undefined;
@@ -42,6 +42,7 @@ export declare const templateSchemas2SchemasList: (_template: Template) => Promi
42
42
  lineHeight?: number | undefined;
43
43
  rotate?: number | undefined;
44
44
  alignment?: "center" | "left" | "right" | undefined;
45
+ verticalAlignment?: "top" | "bottom" | "middle" | undefined;
45
46
  fontName?: string | undefined;
46
47
  fontColor?: string | undefined;
47
48
  characterSpacing?: number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "sideEffects": false,
5
5
  "author": "hand-dot",
6
6
  "license": "MIT",
@@ -6,6 +6,10 @@ import {
6
6
  DEFAULT_LINE_HEIGHT,
7
7
  DEFAULT_CHARACTER_SPACING,
8
8
  DEFAULT_FONT_COLOR,
9
+ VERTICAL_ALIGN_TOP,
10
+ VERTICAL_ALIGN_MIDDLE,
11
+ VERTICAL_ALIGN_BOTTOM,
12
+ DEFAULT_VERTICAL_ALIGNMENT,
9
13
  DYNAMIC_FIT_VERTICAL,
10
14
  DYNAMIC_FIT_HORIZONTAL,
11
15
  DEFAULT_DYNAMIC_FIT,
@@ -154,6 +158,7 @@ const TextPropEditor = (
154
158
  ) => {
155
159
  const { changeSchemas, activeSchema } = props;
156
160
  const alignments = ['left', 'center', 'right'];
161
+ const verticalAlignments = [VERTICAL_ALIGN_TOP, VERTICAL_ALIGN_MIDDLE, VERTICAL_ALIGN_BOTTOM];
157
162
  const dynamicFits = [DYNAMIC_FIT_HORIZONTAL, DYNAMIC_FIT_VERTICAL];
158
163
  const font = useContext(FontContext);
159
164
  const fallbackFontName = getFallbackFontName(font);
@@ -180,13 +185,22 @@ const TextPropEditor = (
180
185
  />
181
186
 
182
187
  <SelectSet
183
- label={'Alignment'}
188
+ label={'Horizontal Align'}
184
189
  value={activeSchema.alignment ?? 'left'}
185
190
  options={alignments}
186
191
  onChange={(e) =>
187
192
  changeSchemas([{ key: 'alignment', value: e.target.value, schemaId: activeSchema.id }])
188
193
  }
189
194
  />
195
+
196
+ <SelectSet
197
+ label={'Vertical Align'}
198
+ value={activeSchema.verticalAlignment ?? DEFAULT_VERTICAL_ALIGNMENT}
199
+ options={verticalAlignments}
200
+ onChange={(e) => {
201
+ changeSchemas([{ key: 'verticalAlignment', value: e.target.value, schemaId: activeSchema.id }]);
202
+ }}
203
+ />
190
204
  </div>
191
205
  <div
192
206
  style={{
@@ -2,18 +2,34 @@ import React, { useContext, forwardRef, Ref, useState, useEffect } from 'react';
2
2
  import {
3
3
  DEFAULT_FONT_SIZE,
4
4
  DEFAULT_ALIGNMENT,
5
+ VERTICAL_ALIGN_TOP,
6
+ VERTICAL_ALIGN_MIDDLE,
7
+ VERTICAL_ALIGN_BOTTOM,
8
+ DEFAULT_VERTICAL_ALIGNMENT,
5
9
  DEFAULT_LINE_HEIGHT,
6
10
  DEFAULT_CHARACTER_SPACING,
7
11
  DEFAULT_FONT_COLOR,
8
12
  TextSchema,
9
13
  calculateDynamicFontSize,
10
14
  getFontKitFont,
11
- getFontAlignmentValue,
15
+ getBrowserVerticalFontAdjustments,
12
16
  } from '@pdfme/common';
13
17
  import { SchemaUIProps } from './SchemaUI';
14
18
  import { ZOOM } from '../../constants';
15
19
  import { FontContext } from '../../contexts';
16
20
 
21
+ const mapVerticalAlignToFlex = (verticalAlignmentValue: string | undefined) => {
22
+ switch (verticalAlignmentValue) {
23
+ case VERTICAL_ALIGN_TOP:
24
+ return 'flex-start';
25
+ case VERTICAL_ALIGN_MIDDLE:
26
+ return 'center';
27
+ case VERTICAL_ALIGN_BOTTOM:
28
+ return 'flex-end';
29
+ }
30
+ return 'flex-start';
31
+ };
32
+
17
33
  type Props = SchemaUIProps & { schema: TextSchema };
18
34
 
19
35
  const TextSchemaUI = (
@@ -22,8 +38,8 @@ const TextSchemaUI = (
22
38
  ) => {
23
39
  const font = useContext(FontContext);
24
40
  const [dynamicFontSize, setDynamicFontSize] = useState<number | undefined>(undefined);
25
- const [fontAlignmentValue, setFontAlignmentValue] = useState<number>(0);
26
-
41
+ const [topAdjustment, setTopAdjustment] = useState<number>(0);
42
+ const [bottomAdjustment, setBottomAdjustment] = useState<number>(0);
27
43
 
28
44
  useEffect(() => {
29
45
  if (schema.dynamicFontSize && schema.data) {
@@ -50,10 +66,31 @@ const TextSchemaUI = (
50
66
 
51
67
  useEffect(() => {
52
68
  getFontKitFont(schema, font).then(fontKitFont => {
53
- const fav = getFontAlignmentValue(fontKitFont, dynamicFontSize ?? schema.fontSize ?? DEFAULT_FONT_SIZE);
54
- setFontAlignmentValue(fav);
69
+ // Depending on vertical alignment, we need to move the top or bottom of the font to keep
70
+ // it within it's defined box and align it with the generated pdf.
71
+ const { topAdj, bottomAdj } = getBrowserVerticalFontAdjustments(
72
+ fontKitFont,
73
+ dynamicFontSize ?? schema.fontSize ?? DEFAULT_FONT_SIZE,
74
+ schema.lineHeight ?? DEFAULT_LINE_HEIGHT,
75
+ schema.verticalAlignment ?? DEFAULT_VERTICAL_ALIGNMENT
76
+ );
77
+ setTopAdjustment(topAdj);
78
+ setBottomAdjustment(bottomAdj);
55
79
  });
80
+
81
+ if (ref && 'current' in ref) {
82
+ const textarea = ref.current;
83
+
84
+ if (textarea) {
85
+ // Textareas cannot be vertically aligned, so we need to adjust the height of the textarea
86
+ // to exactly fit the height of it's content, whilst aligned within it's container.
87
+ // This gives the appearance of being vertically aligned.
88
+ textarea.style.height = 'auto'; // Reset the height to auto to ensure we get the correct height.
89
+ textarea.style.height = `${textarea.scrollHeight}px`;
90
+ }
91
+ }
56
92
  }, [
93
+ schema.data,
57
94
  schema.width,
58
95
  schema.height,
59
96
  schema.fontName,
@@ -63,20 +100,36 @@ const TextSchemaUI = (
63
100
  schema.dynamicFontSize?.fit,
64
101
  schema.characterSpacing,
65
102
  schema.lineHeight,
103
+ schema.verticalAlignment,
66
104
  font,
67
- dynamicFontSize
105
+ dynamicFontSize,
106
+ editable,
68
107
  ]);
69
108
 
70
-
71
- const style: React.CSSProperties = {
109
+ const containerStyle: React.CSSProperties = {
72
110
  position: 'absolute',
73
111
  top: 0,
74
112
  padding: 0,
75
- height: fontAlignmentValue < 0 ? schema.height * ZOOM + Math.abs(fontAlignmentValue) : schema.height * ZOOM,
113
+ height: schema.height * ZOOM,
76
114
  width: schema.width * ZOOM,
77
115
  resize: 'none',
78
- marginTop: fontAlignmentValue < 0 ? fontAlignmentValue : 0,
79
- paddingTop: fontAlignmentValue >= 0 ? fontAlignmentValue : 0,
116
+ backgroundColor: schema.data && schema.backgroundColor ? schema.backgroundColor : 'rgb(242 244 255 / 75%)',
117
+ border: 'none',
118
+ display: 'flex',
119
+ flexDirection: 'column',
120
+ justifyContent: mapVerticalAlignToFlex(schema.verticalAlignment),
121
+ };
122
+
123
+ const textareaStyle: React.CSSProperties = {
124
+ padding: 0,
125
+ resize: 'none',
126
+ border: 'none',
127
+ outline: 'none',
128
+ paddingTop: topAdjustment,
129
+ background: 'none',
130
+ };
131
+
132
+ const fontStyles: React.CSSProperties = {
80
133
  fontFamily: schema.fontName ? `'${schema.fontName}'` : 'inherit',
81
134
  color: schema.fontColor ? schema.fontColor : DEFAULT_FONT_COLOR,
82
135
  fontSize: `${dynamicFontSize ?? schema.fontSize ?? DEFAULT_FONT_SIZE}pt`,
@@ -85,27 +138,32 @@ const TextSchemaUI = (
85
138
  textAlign: schema.alignment ?? DEFAULT_ALIGNMENT,
86
139
  whiteSpace: 'pre-wrap',
87
140
  wordBreak: 'break-word',
88
- backgroundColor:
89
- schema.data && schema.backgroundColor ? schema.backgroundColor : 'rgb(242 244 255 / 75%)',
90
- border: 'none',
91
141
  };
92
142
 
93
143
  return editable ? (
94
- <textarea
95
- ref={ref}
96
- placeholder={placeholder}
97
- tabIndex={tabIndex}
98
- style={style}
99
- onChange={(e) => onChange(e.target.value)}
100
- onBlur={onStopEditing}
101
- value={schema.data}
102
- ></textarea>
144
+ <div style={containerStyle}>
145
+ <textarea
146
+ ref={ref}
147
+ placeholder={placeholder}
148
+ tabIndex={tabIndex}
149
+ style={{ ...textareaStyle, ...fontStyles }}
150
+ onChange={(e) => onChange(e.target.value)}
151
+ onBlur={onStopEditing}
152
+ value={schema.data}
153
+ ></textarea>
154
+ </div>
103
155
  ) : (
104
- <div style={{ ...style, height: schema.height * ZOOM, marginTop: 0, paddingTop: 0 }}>
105
- <div style={{ marginTop: style.marginTop, paddingTop: style.paddingTop }}>
156
+ <div style={containerStyle}>
157
+ <div
158
+ style={{
159
+ ...fontStyles,
160
+ marginBottom: bottomAdjustment,
161
+ paddingTop: topAdjustment,
162
+ }}
163
+ >
106
164
  {/* Set the letterSpacing of the last character to 0. */}
107
165
  {schema.data.split('').map((l, i) => (
108
- <span key={i} style={{ letterSpacing: String(schema.data).length === i + 1 ? 0 : 'inherit', }} >
166
+ <span key={i} style={{ letterSpacing: String(schema.data).length === i + 1 ? 0 : 'inherit' }}>
109
167
  {l}
110
168
  </span>
111
169
  ))}