@pdfme/ui 5.4.6-dev.31 → 5.4.6-dev.33

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.
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { SchemaForUI } from '@pdfme/common';
3
3
  import type { SidebarProps } from '../../../../types.js';
4
- type DetailViewProps = Pick<SidebarProps, 'size' | 'schemas' | 'schemasList' | 'pageSize' | 'changeSchemas' | 'activeElements' | 'deselectSchema'> & {
4
+ type DetailViewProps = Pick<SidebarProps, 'size' | 'schemas' | 'schemasList' | 'pageSize' | 'basePdf' | 'changeSchemas' | 'activeElements' | 'deselectSchema'> & {
5
5
  activeSchema: SchemaForUI;
6
6
  };
7
7
  declare const _default: React.MemoExoticComponent<(props: DetailViewProps) => React.JSX.Element>;
@@ -1,10 +1,11 @@
1
- import type { SchemaForUI, Size, ChangeSchemas } from '@pdfme/common';
1
+ import type { SchemaForUI, Size, ChangeSchemas, BasePdf } from '@pdfme/common';
2
2
  export type SidebarProps = {
3
3
  height: number;
4
4
  hoveringSchemaId: string | null;
5
5
  onChangeHoveringSchemaId: (id: string | null) => void;
6
6
  size: Size;
7
7
  pageSize: Size;
8
+ basePdf: BasePdf;
8
9
  activeElements: HTMLElement[];
9
10
  schemas: SchemaForUI[];
10
11
  schemasList: SchemaForUI[][];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "5.4.6-dev.31",
3
+ "version": "5.4.6-dev.33",
4
4
  "sideEffects": false,
5
5
  "author": "hand-dot",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@ import type {
8
8
  PropPanelSchema,
9
9
  Schema,
10
10
  } from '@pdfme/common';
11
+ import { isBlankPdf } from '@pdfme/common';
11
12
  import type { SidebarProps } from '../../../../types.js';
12
13
  import { Menu } from 'lucide-react';
13
14
  import { I18nContext, PluginsRegistry, OptionsContext } from '../../../../contexts.js';
@@ -29,6 +30,7 @@ type DetailViewProps = Pick<
29
30
  | 'schemas'
30
31
  | 'schemasList'
31
32
  | 'pageSize'
33
+ | 'basePdf'
32
34
  | 'changeSchemas'
33
35
  | 'activeElements'
34
36
  | 'deselectSchema'
@@ -39,7 +41,8 @@ type DetailViewProps = Pick<
39
41
  const DetailView = (props: DetailViewProps) => {
40
42
  const { token } = theme.useToken();
41
43
 
42
- const { size, schemasList, changeSchemas, deselectSchema, activeSchema } = props;
44
+ const { size, schemasList, changeSchemas, deselectSchema, activeSchema, pageSize, basePdf } =
45
+ props;
43
46
  const form = useForm();
44
47
 
45
48
  const i18n = useContext(I18nContext);
@@ -116,6 +119,37 @@ const DetailView = (props: DetailViewProps) => {
116
119
  const validateUniqueSchemaName = (_: unknown, value: string): boolean =>
117
120
  uniqueSchemaName.current(value);
118
121
 
122
+ // Calculate padding values once
123
+ const [paddingTop, paddingRight, paddingBottom, paddingLeft] = isBlankPdf(basePdf)
124
+ ? basePdf.padding
125
+ : [0, 0, 0, 0];
126
+
127
+ // Cross-field validation: only checks when both fields are individually valid
128
+ const validatePosition = (_: unknown, value: number, fieldName: string): boolean => {
129
+ const formValues = form.getValues() as Record<string, unknown>;
130
+ const position = formValues.position as { x: number; y: number } | undefined;
131
+ const width = formValues.width as number | undefined;
132
+ const height = formValues.height as number | undefined;
133
+
134
+ if (!position || width === undefined || height === undefined) return true;
135
+
136
+ if (fieldName === 'x') {
137
+ if (value < paddingLeft || value > pageSize.width - paddingRight) return true;
138
+ if (width > 0 && value + width > pageSize.width - paddingRight) return false;
139
+ } else if (fieldName === 'y') {
140
+ if (value < paddingTop || value > pageSize.height - paddingBottom) return true;
141
+ if (height > 0 && value + height > pageSize.height - paddingBottom) return false;
142
+ } else if (fieldName === 'width') {
143
+ if (position.x < paddingLeft || position.x > pageSize.width - paddingRight) return true;
144
+ if (value > 0 && position.x + value > pageSize.width - paddingRight) return false;
145
+ } else if (fieldName === 'height') {
146
+ if (position.y < paddingTop || position.y > pageSize.height - paddingBottom) return true;
147
+ if (value > 0 && position.y + value > pageSize.height - paddingBottom) return false;
148
+ }
149
+
150
+ return true;
151
+ };
152
+
119
153
  // Use explicit type for debounce function that matches the expected signature
120
154
  const handleWatch = debounce(function (...args: unknown[]) {
121
155
  const formSchema = args[0] as Record<string, unknown>;
@@ -203,6 +237,10 @@ const DetailView = (props: DetailViewProps) => {
203
237
  })()
204
238
  : emptySchema;
205
239
 
240
+ // Calculate max values considering padding
241
+ const maxWidth = pageSize.width - paddingLeft - paddingRight;
242
+ const maxHeight = pageSize.height - paddingTop - paddingBottom;
243
+
206
244
  // Create a type-safe schema object
207
245
  const propPanelSchema: PropPanelSchema = {
208
246
  type: 'object',
@@ -247,8 +285,36 @@ const DetailView = (props: DetailViewProps) => {
247
285
  type: 'object',
248
286
  widget: 'card',
249
287
  properties: {
250
- x: { title: 'X', type: 'number', widget: 'inputNumber', required: true, span: 8, min: 0 },
251
- y: { title: 'Y', type: 'number', widget: 'inputNumber', required: true, span: 8, min: 0 },
288
+ x: {
289
+ title: 'X',
290
+ type: 'number',
291
+ widget: 'inputNumber',
292
+ required: true,
293
+ span: 8,
294
+ min: paddingLeft,
295
+ max: pageSize.width - paddingRight,
296
+ rules: [
297
+ {
298
+ validator: (_: unknown, value: number) => validatePosition(_, value, 'x'),
299
+ message: typedI18n('validation.outOfBounds'),
300
+ },
301
+ ],
302
+ },
303
+ y: {
304
+ title: 'Y',
305
+ type: 'number',
306
+ widget: 'inputNumber',
307
+ required: true,
308
+ span: 8,
309
+ min: paddingTop,
310
+ max: pageSize.height - paddingBottom,
311
+ rules: [
312
+ {
313
+ validator: (_: unknown, value: number) => validatePosition(_, value, 'y'),
314
+ message: typedI18n('validation.outOfBounds'),
315
+ },
316
+ ],
317
+ },
252
318
  },
253
319
  },
254
320
  width: {
@@ -257,7 +323,13 @@ const DetailView = (props: DetailViewProps) => {
257
323
  widget: 'inputNumber',
258
324
  required: true,
259
325
  span: 6,
260
- props: { min: 0 },
326
+ props: { min: 0, max: maxWidth },
327
+ rules: [
328
+ {
329
+ validator: (_: unknown, value: number) => validatePosition(_, value, 'width'),
330
+ message: typedI18n('validation.outOfBounds'),
331
+ },
332
+ ],
261
333
  },
262
334
  height: {
263
335
  title: typedI18n('height'),
@@ -265,7 +337,13 @@ const DetailView = (props: DetailViewProps) => {
265
337
  widget: 'inputNumber',
266
338
  required: true,
267
339
  span: 6,
268
- props: { min: 0 },
340
+ props: { min: 0, max: maxHeight },
341
+ rules: [
342
+ {
343
+ validator: (_: unknown, value: number) => validatePosition(_, value, 'height'),
344
+ message: typedI18n('validation.outOfBounds'),
345
+ },
346
+ ],
269
347
  },
270
348
  rotate: {
271
349
  title: typedI18n('rotate'),
@@ -339,6 +339,7 @@ const TemplateEditor = ({
339
339
  height={canvasRef.current ? canvasRef.current.clientHeight : 0}
340
340
  size={size}
341
341
  pageSize={pageSizes[pageCursor] ?? []}
342
+ basePdf={template.basePdf}
342
343
  activeElements={activeElements}
343
344
  schemasList={schemasList}
344
345
  schemas={schemasList[pageCursor] ?? []}
package/src/i18n.ts CHANGED
@@ -34,6 +34,7 @@ const dictEn: { [key in keyof Dict]: string } = {
34
34
  'validation.hexColor': 'Please enter a valid hex color code.',
35
35
  'validation.uniqueName': 'Please enter a unique name.',
36
36
  'validation.dateTimeFormat': 'Invalid date time format.',
37
+ 'validation.outOfBounds': 'Exceeds page boundaries.',
37
38
  'schemas.color': 'Color',
38
39
  'schemas.borderWidth': 'Border Width',
39
40
  'schemas.borderColor': 'Border Color',
@@ -112,6 +113,7 @@ const dictZh: { [key in keyof Dict]: string } = {
112
113
  'validation.hexColor': '请输入有效的十六进制颜色代码。',
113
114
  'validation.uniqueName': '请输入一个唯一的名称。',
114
115
  'validation.dateTimeFormat': '日期时间格式无效。',
116
+ 'validation.outOfBounds': '超出页面边界。',
115
117
  'schemas.color': '颜色',
116
118
  'schemas.borderWidth': '边框宽度',
117
119
  'schemas.borderColor': '边框颜色',
@@ -189,6 +191,7 @@ const dictJa: { [key in keyof Dict]: string } = {
189
191
  'validation.hexColor': '有効な16進数のカラーコードを入力してください。',
190
192
  'validation.uniqueName': '一意の名前を入力してください。',
191
193
  'validation.dateTimeFormat': '日付と時刻のフォーマットが無効です。',
194
+ 'validation.outOfBounds': 'ページ境界を超えています。',
192
195
  'schemas.color': '色',
193
196
  'schemas.borderWidth': '枠線の太さ',
194
197
  'schemas.borderColor': '枠線の色',
@@ -266,6 +269,7 @@ const dictKo: { [key in keyof Dict]: string } = {
266
269
  'validation.hexColor': '유효한 16진수 색상 코드를 입력하세요.',
267
270
  'validation.uniqueName': '고유한 이름을 입력하세요.',
268
271
  'validation.dateTimeFormat': '날짜/시간 형식이 잘못되었습니다.',
272
+ 'validation.outOfBounds': '페이지 경계를 초과합니다.',
269
273
  'schemas.color': '색상',
270
274
  'schemas.borderWidth': '테두리 너비',
271
275
  'schemas.borderColor': '테두리 색상',
@@ -343,6 +347,7 @@ const dictAr: { [key in keyof Dict]: string } = {
343
347
  'validation.hexColor': 'الرجاء إدخال رمز لون سداسي عشري صالح.',
344
348
  'validation.uniqueName': 'الرجاء إدخال اسم فريد.',
345
349
  'validation.dateTimeFormat': 'تنسيق التاريخ والوقت غير صالح.',
350
+ 'validation.outOfBounds': 'يتجاوز حدود الصفحة.',
346
351
  'schemas.color': 'اللون',
347
352
  'schemas.borderWidth': 'عرض الحدود',
348
353
  'schemas.borderColor': 'لون الحدود',
@@ -421,6 +426,7 @@ const dictTh: { [key in keyof Dict]: string } = {
421
426
  'validation.hexColor': 'กรุณาใส่รหัสสีแบบฐานสิบหกที่ถูกต้อง',
422
427
  'validation.uniqueName': 'กรุณาระบุชื่อที่ไม่ซ้ำ',
423
428
  'validation.dateTimeFormat': 'รูปแบบวันที่และเวลาไม่ถูกต้อง',
429
+ 'validation.outOfBounds': 'เกินขอบเขตหน้า',
424
430
  'schemas.color': 'สี',
425
431
  'schemas.borderWidth': 'ความกว้างของเส้นขอบ',
426
432
  'schemas.borderColor': 'สีขอบ',
@@ -500,6 +506,7 @@ const dictIt: { [key in keyof Dict]: string } = {
500
506
  'validation.hexColor': 'Inserisci un codice colore esadecimale valido.',
501
507
  'validation.uniqueName': 'Inserisci un nome univoco.',
502
508
  'validation.dateTimeFormat': 'Formato data-ora non valido.',
509
+ 'validation.outOfBounds': 'Supera i limiti della pagina.',
503
510
  'schemas.color': 'Colore',
504
511
  'schemas.borderWidth': 'Spessore bordo',
505
512
  'schemas.borderColor': 'Colore bordo',
@@ -578,6 +585,7 @@ const dictPl: { [key in keyof Dict]: string } = {
578
585
  'validation.hexColor': 'Wprowadź poprawny kod koloru szesnastkowego.',
579
586
  'validation.uniqueName': 'Proszę wpisać unikalną nazwę.',
580
587
  'validation.dateTimeFormat': 'Nieprawidłowy format daty i godziny.',
588
+ 'validation.outOfBounds': 'Przekracza granice strony.',
581
589
  'schemas.color': 'Kolor',
582
590
  'schemas.borderWidth': 'Szerokość obramowania',
583
591
  'schemas.borderColor': 'Kolor obramowania',
@@ -657,6 +665,7 @@ const dictDe: { [key in keyof Dict]: string } = {
657
665
  'validation.hexColor': 'Bitte geben Sie einen gültigen Hex-Farbcode ein.',
658
666
  'validation.uniqueName': 'Bitte geben Sie einen eindeutigen Namen ein.',
659
667
  'validation.dateTimeFormat': 'Ungültiges Datums- und Zeitformat.',
668
+ 'validation.outOfBounds': 'Überschreitet die Seitengrenzen.',
660
669
  'schemas.color': 'Farbe',
661
670
  'schemas.borderWidth': 'Rahmenbreite',
662
671
  'schemas.borderColor': 'Rahmenfarbe',
@@ -737,6 +746,7 @@ const dictEs: { [key in keyof Dict]: string } = {
737
746
  'validation.hexColor': 'Introduce un código de color hexadecimal válido.',
738
747
  'validation.uniqueName': 'Por favor, introduzca un nombre único.',
739
748
  'validation.dateTimeFormat': 'Formato de fecha y hora no válido.',
749
+ 'validation.outOfBounds': 'Excede los límites de la página.',
740
750
  'schemas.color': 'Color',
741
751
  'schemas.borderWidth': 'Ancho del borde',
742
752
  'schemas.borderColor': 'Color del borde',
@@ -816,6 +826,7 @@ const dictFr: { [key in keyof Dict]: string } = {
816
826
  'validation.hexColor': 'Veuillez entrer un code couleur hexadécimal valide.',
817
827
  'validation.uniqueName': 'Veuillez saisir un nom unique.',
818
828
  'validation.dateTimeFormat': "Format de date et d'heure non valide.",
829
+ 'validation.outOfBounds': 'Dépasse les limites de la page.',
819
830
  'schemas.color': 'Couleur',
820
831
  'schemas.borderWidth': 'Largeur de la bordure',
821
832
  'schemas.borderColor': 'Couleur de la bordure',
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SchemaForUI, Size, ChangeSchemas } from '@pdfme/common';
1
+ import type { SchemaForUI, Size, ChangeSchemas, BasePdf } from '@pdfme/common';
2
2
 
3
3
  export type SidebarProps = {
4
4
  height: number;
@@ -6,6 +6,7 @@ export type SidebarProps = {
6
6
  onChangeHoveringSchemaId: (id: string | null) => void;
7
7
  size: Size;
8
8
  pageSize: Size;
9
+ basePdf: BasePdf;
9
10
  activeElements: HTMLElement[];
10
11
  schemas: SchemaForUI[];
11
12
  schemasList: SchemaForUI[][];