labellife-design-tool 2.1.0 → 2.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # labellife-design-tool
2
2
 
3
- A fully featured, modular React design editor library for building label, print, and graphic design applications. Built on top of Konva.js for high-performance canvas rendering and MobX-State-Tree for reactive state management.
3
+ A fully featured, modular React design editor library for building label, print, and graphic design applications. Built on top of **Konva.js** for high-performance canvas rendering and **MobX-State-Tree** for reactive state management.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/labellife-design-tool.svg)](https://www.npmjs.com/package/labellife-design-tool)
6
6
  [![license](https://img.shields.io/npm/l/labellife-design-tool.svg)](LICENSE)
@@ -9,33 +9,39 @@ A fully featured, modular React design editor library for building label, print,
9
9
 
10
10
  ## Table of Contents
11
11
 
12
- - [Features](#features)
13
- - [Installation](#installation)
14
- - [Quick Start](#quick-start)
15
- - [Architecture Overview](#architecture-overview)
16
- - [Store API](#store-api)
17
- - [Element Types](#element-types)
18
- - [UI Components](#ui-components)
19
- - [Side Panels](#side-panels)
20
- - [Toolbar](#toolbar)
21
- - [Image Effects & Filters](#image-effects--filters)
22
- - [Image Masking](#image-masking)
23
- - [Export & Download](#export--download)
24
- - [Internationalization (i18n)](#internationalization-i18n)
25
- - [Theming](#theming)
26
- - [Utility Functions](#utility-functions)
27
- - [Keyboard Shortcuts](#keyboard-shortcuts)
28
- - [JSON Schema](#json-schema)
29
- - [Peer Dependencies](#peer-dependencies)
30
- - [License](#license)
12
+ 1. [Features](#features)
13
+ 2. [Installation](#installation)
14
+ 3. [Quick Start](#quick-start)
15
+ 4. [Architecture Overview](#architecture-overview)
16
+ 5. [Store API](#store-api)
17
+ 6. [Element Types](#element-types)
18
+ 7. [UI Components](#ui-components)
19
+ 8. [Side Panels](#side-panels)
20
+ 9. [Toolbar](#toolbar)
21
+ 10. [Input Fields](#input-fields)
22
+ 11. [Image Effects & Filters](#image-effects--filters)
23
+ 12. [Image Masking](#image-masking)
24
+ 13. [Export & Download](#export--download)
25
+ 14. [Configuration](#configuration)
26
+ 15. [Keyboard Shortcuts](#keyboard-shortcuts)
27
+ 16. [JSON Schema](#json-schema)
28
+ 17. [Dependencies](#dependencies)
29
+ 18. [Roadmap](#roadmap)
30
+ 19. [License](#license)
31
31
 
32
32
  ---
33
33
 
34
34
  ## Features
35
35
 
36
+ ### Core Editor
36
37
  - **Canvas Editor** — Zoom, pan, drag-and-drop, multi-select, and keyboard shortcuts
37
38
  - **Rich Element Types** — Images, text, shapes (60+ presets), lines, SVGs, and groups
38
39
  - **Context-Sensitive Toolbar** — Adapts controls to the selected element type
40
+ - **Multi-Page Support** — Add, remove, reorder, and navigate between pages
41
+ - **Undo/Redo** — Full history stack with snapshot-based state management
42
+ - **Responsive Layout** — Flexible container system that adapts to any viewport
43
+
44
+ ### Design Capabilities
39
45
  - **Image Editing** — Crop, flip, border, corner radius, masks (circle, triangle, star, diamond, hexagon, pentagon, heart, cloud, cross), and visual effects (blur, brightness, grayscale, sepia, warm, cold)
40
46
  - **Text Editing** — Inline editing, 200+ Google Fonts with auto-loading, font weight/style, text decoration, alignment, letter spacing, line height, stroke, background highlight, and curved text
41
47
  - **Shapes Library** — 60+ vector shapes including basic geometry, arrows, decorative shapes, organic blobs, and custom SVG paths
@@ -43,13 +49,21 @@ A fully featured, modular React design editor library for building label, print,
43
49
  - **Shadow System** — Configurable blur, offset, color, and opacity on any element
44
50
  - **Layer Management** — Reorder, lock/unlock, show/hide, delete per element
45
51
  - **Grouping** — Group and ungroup elements with preserved relative positioning
46
- - **Multi-Page Support** — Add, remove, reorder, and navigate between pages
47
- - **Undo/Redo** Full history stack with snapshot-based state management
52
+
53
+ ### Template & Input System
54
+ - **Input Fields** — Define placeholder elements (Text, Date, Integer, Image) that prompt end-users for values when a template is loaded
55
+ - **Input Field Popup** — Built-in sequential dialog that collects user input for each field, with full style customization
56
+ - **Required Fields** — Mark input fields as required to prevent skipping
57
+ - **Custom Dialog Support** — Replace the default popup with your own component
58
+
59
+ ### Import / Export
48
60
  - **Export** — PNG, JPEG, PDF (multi-page), and JSON serialization
49
61
  - **Import** — Load designs from JSON, import images via drag-and-drop or file picker
50
- - **Internationalization** — Deep-merge translation system with dot-notation key resolution
62
+
63
+ ### Extensibility
51
64
  - **Pluggable Panels** — Extend the side panel with custom sections
52
- - **Responsive Layout** — Flexible container system that adapts to any viewport
65
+ - **Internationalization** — Deep-merge translation system with dot-notation key resolution
66
+ - **Theming** — Light/dark theme support
53
67
  - **Unit Conversion** — px, mm, cm, in, pt, pc with configurable DPI
54
68
 
55
69
  ---
@@ -73,6 +87,7 @@ npm install react react-dom @mui/material @mui/icons-material @emotion/react @em
73
87
  ## Quick Start
74
88
 
75
89
  ```jsx
90
+ import React, { useMemo } from 'react';
76
91
  import { createStore } from 'labellife-design-tool/model/store';
77
92
  import { DesignContainer, SidePanelWrap, WorkspaceWrap } from 'labellife-design-tool';
78
93
  import { SidePanel } from 'labellife-design-tool/side-panel';
@@ -80,20 +95,24 @@ import { Toolbar } from 'labellife-design-tool/toolbar/toolbar';
80
95
  import { ZoomButtons } from 'labellife-design-tool/toolbar/zoom-buttons';
81
96
  import { Workspace } from 'labellife-design-tool/canvas/workspace';
82
97
 
83
- const store = createStore();
84
- store.addPage();
85
-
86
98
  function App() {
99
+ const store = useMemo(() => {
100
+ const s = createStore();
101
+ s.setSize(800, 600);
102
+ s.addPage();
103
+ return s;
104
+ }, []);
105
+
87
106
  return (
88
107
  <DesignContainer style={{ width: '100vw', height: '100vh' }}>
89
- <Toolbar store={store} />
90
108
  <SidePanelWrap>
91
109
  <SidePanel store={store} />
92
- <WorkspaceWrap>
93
- <Workspace store={store} />
94
- <ZoomButtons store={store} />
95
- </WorkspaceWrap>
96
110
  </SidePanelWrap>
111
+ <WorkspaceWrap>
112
+ <Toolbar store={store} />
113
+ <Workspace store={store} />
114
+ <ZoomButtons store={store} />
115
+ </WorkspaceWrap>
97
116
  </DesignContainer>
98
117
  );
99
118
  }
@@ -104,22 +123,23 @@ function App() {
104
123
  ## Architecture Overview
105
124
 
106
125
  ```
107
- ┌─────────────────────────────────────────────────┐
108
- │ DesignContainer
109
- ┌─────────────────────────────────────────────┐│
110
- │ │ Toolbar (context-sensitive) ││
111
- ├──────────┬──────────────────────────────────┤│
112
- │ │ SidePanelWorkspace (Konva Stage) ││
113
- │ │ ││
114
- │ │ Text ┌────────────────────────────┐ ││
115
- │ │ Shapes │ │ Canvas (zoom, pan, select) │ ││
116
- │ │ Upload │ │ ││
117
- │ │ Bg │ │ Elements rendered here ││
118
- │ │ Layers ││
119
- │ │ └────────────────────────────┘ ││
120
- │ │ ZoomButtons ││
121
- └──────────┴──────────────────────────────────┘│
122
- └─────────────────────────────────────────────────┘
126
+ ┌──────────────────────────────────────────────────────┐
127
+ │ DesignContainer
128
+ ┌────────────┬─────────────────────────────────────┐│
129
+ │ │ SidePanel │ WorkspaceWrap ││
130
+ │ │ ┌────────────────────────────────┐ ││
131
+ │ │ Text │ Toolbar (context-sensitive) │ ││
132
+ │ │ • Shapes ├────────────────────────────────┤ ││
133
+ │ │ Upload Workspace (Konva Stage) │ ││
134
+ │ │ Input │ │ ││
135
+ │ │ Bg │ │ Canvas: zoom, pan, select ││
136
+ │ │ Layers │ │ Elements rendered here ││
137
+ │ │ │ │ Input Fields Popup (optional) ││
138
+ │ │ │ │ ││
139
+ │ │ ├────────────────────────────────┤ ││
140
+ │ │ │ ZoomButtons │ ││
141
+ │ └────────────┴──┴────────────────────────────────┘ ││
142
+ └──────────────────────────────────────────────────────┘
123
143
  ```
124
144
 
125
145
  **State management** uses MobX-State-Tree. The `store` is the single source of truth — all UI components observe it reactively.
@@ -139,7 +159,7 @@ const store = createStore();
139
159
  ### Canvas Size
140
160
 
141
161
  ```js
142
- store.setSize(800, 600); // Set canvas dimensions in pixels
162
+ store.setSize(800, 600); // Set canvas dimensions in pixels
143
163
  store.setUnit({ unit: 'mm', dpi: 300 }); // Set working unit and DPI
144
164
  ```
145
165
 
@@ -188,7 +208,7 @@ store.ungroupElements([groupId]); // Ungroup a group
188
208
 
189
209
  // Find
190
210
  store.getElementById(id); // Find element by ID across all pages
191
- store.find(callback); // Find first element matching callback
211
+ store.find(callback); // Find first element matching callback
192
212
  ```
193
213
 
194
214
  ### Element Actions
@@ -228,6 +248,8 @@ store.clear(); // Reset store to empty state
228
248
  store.validate(json); // Returns array of validation errors
229
249
  ```
230
250
 
251
+ > **Note:** `loadJSON` automatically loads referenced Google Fonts and triggers the [Input Fields popup](#input-fields) if the template contains input field elements.
252
+
231
253
  ### Events
232
254
 
233
255
  ```js
@@ -245,8 +267,6 @@ store.addFont({ fontFamily: 'Custom', url: '...' });
245
267
  store.removeFont('Custom');
246
268
  ```
247
269
 
248
- > When loading a design via `loadJSON`, all fonts referenced in text elements are **automatically loaded** from Google Fonts.
249
-
250
270
  ---
251
271
 
252
272
  ## Element Types
@@ -330,7 +350,7 @@ store.removeFont('Custom');
330
350
  | `selectable` | boolean | `true` | Can be selected |
331
351
  | `removable` | boolean | `true` | Can be deleted |
332
352
  | `name` | string | `''` | Display name (shown in layers) |
333
- | `custom` | object | `{}` | Arbitrary custom data |
353
+ | `custom` | object | `{}` | Arbitrary custom data (preserved through serialization) |
334
354
 
335
355
  ### Shadow (All Elements)
336
356
 
@@ -356,8 +376,8 @@ import { DesignContainer, SidePanelWrap, WorkspaceWrap } from 'labellife-design-
356
376
  | Component | Description |
357
377
  |---|---|
358
378
  | `DesignContainer` | Root layout container. Accepts `style` prop for dimensions. |
359
- | `SidePanelWrap` | Horizontal wrapper for side panel + workspace. |
360
- | `WorkspaceWrap` | Flex container for the canvas area + zoom buttons. |
379
+ | `SidePanelWrap` | Wrapper for the side panel area. |
380
+ | `WorkspaceWrap` | Flex container for toolbar, canvas, and zoom controls. |
361
381
 
362
382
  ### Workspace
363
383
 
@@ -367,12 +387,19 @@ import { Workspace } from 'labellife-design-tool/canvas/workspace';
367
387
  <Workspace store={store} />
368
388
  ```
369
389
 
390
+ | Prop | Type | Default | Description |
391
+ |---|---|---|---|
392
+ | `store` | Store | *required* | The MobX-State-Tree store instance |
393
+ | `showInputFieldsPopup` | boolean | `true` | Show the input fields popup when a template with input fields is loaded. Set to `false` to disable. |
394
+ | `components` | object | `{}` | Override slots (e.g. `{ PageControls: MyComponent }`) |
395
+
370
396
  The workspace provides:
371
397
  - **Infinite canvas** with zoom and pan (mouse wheel + drag)
372
398
  - **Click selection** and **multi-select** (Shift+click)
373
399
  - **Transform handles** for resize and rotation
374
400
  - **Auto-fit** to container on mount
375
401
  - **Background rendering** per page
402
+ - **Input fields popup** (automatically shown when a loaded template contains input fields)
376
403
 
377
404
  ### Zoom Buttons
378
405
 
@@ -406,13 +433,21 @@ import {
406
433
  TextSection,
407
434
  ElementsSection,
408
435
  UploadSection,
436
+ InputFieldsSection,
409
437
  BackgroundSection,
410
438
  LayersSection,
411
439
  } from 'labellife-design-tool/side-panel';
412
440
 
413
441
  <SidePanel
414
442
  store={store}
415
- sections={[TextSection, ElementsSection, UploadSection, BackgroundSection, LayersSection]}
443
+ sections={[
444
+ TextSection,
445
+ ElementsSection,
446
+ UploadSection,
447
+ InputFieldsSection,
448
+ BackgroundSection,
449
+ LayersSection,
450
+ ]}
416
451
  defaultSection="text"
417
452
  />
418
453
  ```
@@ -424,6 +459,7 @@ import {
424
459
  | `TextSection` | Text | Add heading, subheading, and body text presets |
425
460
  | `ElementsSection` | Elements | 60+ shapes and 6 line styles |
426
461
  | `UploadSection` | Upload | Drag-and-drop or file picker for images |
462
+ | `InputFieldsSection` | Input Fields | Add placeholder input elements (Text, Date, Integer, Image) for template-based workflows |
427
463
  | `BackgroundSection` | Background | Color picker with 22 preset colors |
428
464
  | `LayersSection` | Layers | Layer list with visibility, lock, reorder, and delete |
429
465
 
@@ -447,7 +483,6 @@ const MyCustomSection = {
447
483
  ),
448
484
  };
449
485
 
450
- // Use it
451
486
  <SidePanel store={store} sections={[MyCustomSection, TextSection, LayersSection]} />
452
487
  ```
453
488
 
@@ -480,23 +515,165 @@ The toolbar is **context-sensitive** — it automatically shows relevant control
480
515
  | **Line** | Line color |
481
516
  | **All** | Opacity, Move up/down, Lock/Unlock, Duplicate, Delete |
482
517
 
483
- ### Custom Action Controls
518
+ The toolbar includes built-in **Import** and **Download** buttons by default.
519
+
520
+ ### Customizing Toolbar Buttons
484
521
 
485
522
  ```jsx
486
523
  <Toolbar
487
524
  store={store}
488
525
  components={{
526
+ // Add custom action buttons
489
527
  ActionControls: ({ store }) => (
490
- <>
491
- {/* Custom Download button */}
492
- <button onClick={() => store.saveAsImage()}>Export</button>
493
- </>
528
+ <button onClick={() => store.saveAsImage()}>Export</button>
494
529
  ),
530
+ // Replace or hide the Import button
531
+ ImportButton: null, // Set to null to hide
532
+ // Replace or hide the Download button
533
+ DownloadButton: MyDownload, // Provide a custom component
495
534
  }}
496
535
  />
497
536
  ```
498
537
 
499
- The toolbar includes built-in **Download** and **Import** buttons by default.
538
+ ---
539
+
540
+ ## Input Fields
541
+
542
+ Input fields allow template designers (admins) to define placeholder elements that prompt end-users for values when a template is loaded. This is useful for creating reusable templates where certain fields — such as a name, date, or quantity — need to be filled in by the user.
543
+
544
+ ### Overview
545
+
546
+ The input fields system consists of three parts:
547
+
548
+ 1. **Admin Panel** (`InputFieldsSection`) — A side panel where the admin adds input field elements to the template and configures their prompt text and whether they are required.
549
+ 2. **Metadata** — Each input field element stores its configuration in the `custom` property, which is preserved through JSON serialization.
550
+ 3. **User Popup** — When a template containing input fields is loaded via `store.loadJSON()`, a sequential dialog automatically prompts the user to fill in each field.
551
+
552
+ ### Supported Input Types
553
+
554
+ | Type | Description | Input Control |
555
+ |---|---|---|
556
+ | `text` | Free-form text entry | Text input |
557
+ | `date` | Date value with configurable format | Date picker |
558
+ | `integer` | Whole number value | Number input (integers only) |
559
+ | `image` | Image upload placeholder | **Not yet implemented** — see [Roadmap](#roadmap) |
560
+
561
+ ### Adding Input Fields (Admin Side)
562
+
563
+ Include `InputFieldsSection` in your side panel sections:
564
+
565
+ ```jsx
566
+ import { InputFieldsSection } from 'labellife-design-tool/side-panel';
567
+
568
+ <SidePanel
569
+ store={store}
570
+ sections={[TextSection, ElementsSection, InputFieldsSection, LayersSection]}
571
+ />
572
+ ```
573
+
574
+ For each input type, the admin can configure:
575
+
576
+ - **Prompt Text** — The message displayed to the end-user (e.g. "Enter your name", "Select the event date").
577
+ - **Required** — Whether the field must be filled in. If required, the Skip button is hidden and the Confirm button is disabled until a value is entered.
578
+ - **Date Format** *(date type only)* — Choose from `MM-DD-YYYY`, `Month Name-DD-YYYY`, `Month Name-DD`, `YYYY`, or `MM-DD`.
579
+
580
+ ### Input Field Metadata
581
+
582
+ Each input field element stores the following in its `custom` property:
583
+
584
+ ```json
585
+ {
586
+ "inputField": true,
587
+ "inputType": "text",
588
+ "placeholder": "Enter text",
589
+ "promptText": "Please enter your name",
590
+ "required": false
591
+ }
592
+ ```
593
+
594
+ For date fields, an additional `dateFormat` property is included:
595
+
596
+ ```json
597
+ {
598
+ "inputField": true,
599
+ "inputType": "date",
600
+ "placeholder": "Select date",
601
+ "promptText": "When is the event?",
602
+ "required": true,
603
+ "dateFormat": "MM-DD-YYYY"
604
+ }
605
+ ```
606
+
607
+ ### Controlling the Popup
608
+
609
+ The input fields popup is rendered inside the `<Workspace>` component and is **enabled by default**. When `store.loadJSON()` is called with a template that contains input field elements, the popup automatically appears.
610
+
611
+ ```jsx
612
+ // Popup enabled (default) — dialog appears after loadJSON if input fields exist
613
+ <Workspace store={store} />
614
+
615
+ // Popup disabled — input fields retain their template placeholder values
616
+ <Workspace store={store} showInputFieldsPopup={false} />
617
+ ```
618
+
619
+ When `showInputFieldsPopup` is set to `false`, all input fields are treated as if they were skipped — their original template values are preserved regardless of whether they are marked as required.
620
+
621
+ ### Customizing the Popup Appearance
622
+
623
+ Use `setInputFieldsConfig` to override the default dialog styles:
624
+
625
+ ```js
626
+ import { setInputFieldsConfig } from 'labellife-design-tool';
627
+
628
+ setInputFieldsConfig({
629
+ // Style overrides (applied via MUI sx prop)
630
+ dialogStyle: { borderRadius: 16 },
631
+ titleStyle: { color: '#1a1a1a', fontFamily: 'Georgia' },
632
+ promptStyle: { fontSize: 15 },
633
+ inputStyle: { borderRadius: 8 },
634
+ submitButtonStyle: { backgroundColor: '#4caf50', '&:hover': { backgroundColor: '#388e3c' } },
635
+ skipButtonStyle: { color: '#757575' },
636
+
637
+ // Button text overrides
638
+ submitButtonText: 'Apply',
639
+ skipButtonText: 'Leave as is',
640
+ });
641
+ ```
642
+
643
+ ### Providing a Custom Dialog Component
644
+
645
+ For complete control over the popup UI, provide a `CustomDialog` component:
646
+
647
+ ```js
648
+ import { setInputFieldsConfig } from 'labellife-design-tool';
649
+
650
+ setInputFieldsConfig({
651
+ CustomDialog: ({ fields, currentIndex, onSubmit, onSkip, onComplete }) => {
652
+ // fields — array of input field elements
653
+ // currentIndex — index of the current field being displayed
654
+ // onSubmit(elementId, value) — call to set the value and advance
655
+ // onSkip(elementId) — call to skip a field and advance
656
+ // onComplete() — call when all fields are processed
657
+ return <MyCustomDialogUI />;
658
+ },
659
+ });
660
+ ```
661
+
662
+ ### Accessing Input Fields Programmatically
663
+
664
+ ```js
665
+ // Get all input field elements across all pages
666
+ const fields = store.inputFields;
667
+
668
+ // Get pending fields awaiting user input
669
+ const pending = store._pendingInputFields;
670
+
671
+ // Manually trigger the input fields flow (called automatically by loadJSON)
672
+ store.requestInputFields();
673
+
674
+ // Clear all pending fields
675
+ store.clearPendingInputFields();
676
+ ```
500
677
 
501
678
  ---
502
679
 
@@ -546,11 +723,8 @@ To remove a mask, click **Remove Mask** in the mask panel.
546
723
  Programmatically:
547
724
 
548
725
  ```js
549
- // Apply mask
550
- element.set({ clipSrc: 'circle' });
551
-
552
- // Remove mask
553
- element.set({ clipSrc: '' });
726
+ element.set({ clipSrc: 'circle' }); // Apply mask
727
+ element.set({ clipSrc: '' }); // Remove mask
554
728
  ```
555
729
 
556
730
  ---
@@ -560,172 +734,138 @@ element.set({ clipSrc: '' });
560
734
  ### Image Export
561
735
 
562
736
  ```js
563
- // Export as data URL
564
737
  const dataURL = await store.toDataURL({
565
738
  mimeType: 'image/png', // 'image/png' or 'image/jpeg'
566
739
  quality: 1, // 0–1 (JPEG quality)
567
740
  pixelRatio: 2, // Resolution multiplier
568
741
  });
569
742
 
570
- // Export as Blob
571
743
  const blob = await store.toBlob({ mimeType: 'image/png' });
572
744
 
573
- // Direct download
574
745
  await store.saveAsImage({ fileName: 'my-design.png' });
575
746
  ```
576
747
 
577
748
  ### PDF Export
578
749
 
579
750
  ```js
580
- // Multi-page PDF as data URL
581
- const pdfDataURL = await store.toPDFDataURL({
582
- pixelRatio: 2,
583
- dpi: 300,
584
- });
751
+ const pdfDataURL = await store.toPDFDataURL({ pixelRatio: 2, dpi: 300 });
585
752
 
586
- // Direct download
587
753
  await store.saveAsPDF({ fileName: 'my-design.pdf' });
588
754
  ```
589
755
 
590
756
  ### JSON Export
591
757
 
592
758
  ```js
593
- // Serialize
594
759
  const json = store.toJSON();
595
-
596
- // Download as file
597
760
  store.saveAsJSON({ fileName: 'my-design.json' });
598
-
599
- // Load back
600
761
  store.loadJSON(json);
601
762
  ```
602
763
 
603
764
  ---
604
765
 
605
- ## Internationalization (i18n)
766
+ ## Configuration
606
767
 
607
- The library supports full internationalization via a nested translation system with dot-notation key resolution.
768
+ ### Internationalization (i18n)
608
769
 
609
- ### Setting Translations
770
+ The library supports full internationalization via a nested translation system with dot-notation key resolution.
610
771
 
611
772
  ```js
612
- import { setTranslations } from 'labellife-design-tool/config';
773
+ import { setTranslations, t } from 'labellife-design-tool/config';
613
774
 
614
775
  setTranslations({
615
776
  toolbar: {
616
777
  flip: 'Spiegelen',
617
- flipHorizontally: 'Horizontaal spiegelen',
618
- flipVertically: 'Verticaal spiegelen',
619
778
  effects: 'Effecten',
620
- fitToBackground: 'Passend maken',
621
- clip: 'Masker toepassen',
622
- removeMask: 'Masker verwijderen',
623
- crop: 'Bijsnijden',
624
- fill: 'Vulkleur',
625
- textStroke: 'Lijnkleur',
626
- border: 'Rand',
627
- cornerRadius: 'Hoekstraal',
628
- color: 'Kleur',
629
- opacity: 'Dekking',
630
- up: 'Omhoog',
631
- down: 'Omlaag',
632
- lockedDescription: 'Vergrendelen',
633
- unlockedDescription: 'Ontgrendelen',
634
- duplicateElements: 'Dupliceren',
635
- removeElements: 'Verwijderen',
636
779
  download: 'Downloaden',
637
780
  import: 'Importeren',
638
- fileType: 'Bestandstype',
639
- quality: 'Kwaliteit',
640
781
  undo: 'Ongedaan maken',
641
782
  redo: 'Opnieuw',
642
- blur: 'Vervagen',
643
- brightness: 'Helderheid',
644
- shadow: 'Schaduw',
645
- offsetX: 'Verschuiving X',
646
- offsetY: 'Verschuiving Y',
783
+ // ... see full key reference below
647
784
  },
648
785
  sidePanel: {
649
786
  text: 'Tekst',
650
787
  elements: 'Elementen',
651
- shapes: 'Vormen',
652
- lines: 'Lijnen',
653
788
  upload: 'Uploaden',
654
- uploadTip: 'Sleep afbeeldingen hierheen of klik om te uploaden',
655
789
  background: 'Achtergrond',
656
790
  layers: 'Lagen',
657
- noLayers: 'Geen lagen op deze pagina',
658
- headerText: 'Kop toevoegen',
659
- subHeaderText: 'Subkop toevoegen',
660
- bodyText: 'Broodtekst toevoegen',
791
+ inputFields: {
792
+ tab: 'Invoervelden',
793
+ title: 'Invoervelden',
794
+ required: 'Verplicht',
795
+ // ...
796
+ },
797
+ },
798
+ inputFieldsDialog: {
799
+ title: 'Invoer vereist',
800
+ submit: 'Bevestigen',
801
+ skip: 'Overslaan',
802
+ stepIndicator: 'Veld {current} van {total}',
661
803
  },
662
804
  });
663
- ```
664
805
 
665
- Translations are **deep-merged**, so you can set them incrementally:
666
-
667
- ```js
668
- // First call
806
+ // Translations are deep-merged safe to call multiple times
669
807
  setTranslations({ toolbar: { flip: 'Flip' } });
808
+ setTranslations({ toolbar: { effects: 'Effects' } }); // Does not overwrite toolbar.flip
670
809
 
671
- // Second call merges, does not overwrite toolbar.flip
672
- setTranslations({ toolbar: { effects: 'Effects' } });
810
+ // Use the translation function anywhere
811
+ t('toolbar.flip'); // Returns translated value
812
+ t('toolbar.flip', 'Flip'); // Returns default if key not found
673
813
  ```
674
814
 
675
- ### Using the Translation Function
815
+ <details>
816
+ <summary><strong>Full translation key reference</strong></summary>
676
817
 
677
- ```js
678
- import { t } from 'labellife-design-tool/config';
679
-
680
- t('toolbar.flip'); // Returns translated value
681
- t('toolbar.flip', 'Flip'); // Returns default if key not found
682
818
  ```
819
+ toolbar.flip, toolbar.flipHorizontally, toolbar.flipVertically, toolbar.effects,
820
+ toolbar.fitToBackground, toolbar.clip, toolbar.removeMask, toolbar.crop, toolbar.fill,
821
+ toolbar.textStroke, toolbar.border, toolbar.cornerRadius, toolbar.color, toolbar.opacity,
822
+ toolbar.up, toolbar.down, toolbar.lockedDescription, toolbar.unlockedDescription,
823
+ toolbar.duplicateElements, toolbar.removeElements, toolbar.download, toolbar.import,
824
+ toolbar.fileType, toolbar.quality, toolbar.undo, toolbar.redo, toolbar.blur,
825
+ toolbar.brightness, toolbar.shadow, toolbar.offsetX, toolbar.offsetY,
826
+ toolbar.group, toolbar.ungroup
683
827
 
684
- ---
685
-
686
- ## Theming
687
-
688
- ```js
689
- import { setTheme, getTheme } from 'labellife-design-tool/config';
828
+ sidePanel.text, sidePanel.elements, sidePanel.shapes, sidePanel.lines,
829
+ sidePanel.upload, sidePanel.uploadTip, sidePanel.background, sidePanel.layers,
830
+ sidePanel.noLayers, sidePanel.headerText, sidePanel.subHeaderText, sidePanel.bodyText,
831
+ sidePanel.inputFields.tab, sidePanel.inputFields.title, sidePanel.inputFields.description,
832
+ sidePanel.inputFields.textInput, sidePanel.inputFields.dateInput,
833
+ sidePanel.inputFields.integerInput, sidePanel.inputFields.imageInput,
834
+ sidePanel.inputFields.required, sidePanel.inputFields.promptTextHint,
835
+ sidePanel.inputFields.dateFormat
690
836
 
691
- setTheme('light'); // or 'dark'
837
+ inputFieldsDialog.title, inputFieldsDialog.submit, inputFieldsDialog.skip,
838
+ inputFieldsDialog.stepIndicator
692
839
  ```
693
840
 
694
- ---
841
+ </details>
695
842
 
696
- ## Utility Functions
697
-
698
- ### Unit Conversion
843
+ ### Theming
699
844
 
700
845
  ```js
701
- import { unitToPx, pxToUnit } from 'labellife-design-tool/utils/unit';
846
+ import { setTheme, getTheme } from 'labellife-design-tool/config';
702
847
 
703
- unitToPx({ unit: 'mm', dpi: 300, unitVal: 50 }); // pixels
704
- pxToUnit({ unit: 'mm', dpi: 300, pxVal: 590.55 }); // → millimeters
848
+ setTheme('light'); // or 'dark'
705
849
  ```
706
850
 
707
- **Supported units:** `px`, `mm`, `cm`, `in`, `pt`, `pc`
708
-
709
- ### Image Utilities
710
-
711
- ```js
712
- import { getImageSize } from 'labellife-design-tool/utils/image';
713
-
714
- const { width, height } = await getImageSize('https://example.com/photo.jpg');
715
- ```
851
+ ### Input Fields Dialog Configuration
716
852
 
717
- ### Font Utilities
853
+ See [Input Fields — Customizing the Popup Appearance](#customizing-the-popup-appearance) for full details.
718
854
 
719
855
  ```js
720
- import { fetchGoogleFonts, loadGoogleFont, DEFAULT_FONTS } from 'labellife-design-tool';
721
-
722
- // Fetch full Google Fonts catalog
723
- const fonts = await fetchGoogleFonts(apiKey);
724
-
725
- // Load a specific font
726
- await loadGoogleFont('Open Sans');
727
-
728
- // DEFAULT_FONTS array of 200+ popular font family names
856
+ import { setInputFieldsConfig, getInputFieldsConfig } from 'labellife-design-tool';
857
+
858
+ setInputFieldsConfig({
859
+ dialogStyle: {}, // MUI sx overrides for the Dialog paper
860
+ titleStyle: {}, // MUI sx overrides for the Dialog title
861
+ promptStyle: {}, // MUI sx overrides for the prompt text
862
+ inputStyle: {}, // MUI sx overrides for the input field
863
+ submitButtonStyle: {}, // MUI sx overrides for the Confirm button
864
+ skipButtonStyle: {}, // MUI sx overrides for the Skip button
865
+ submitButtonText: '', // Override Confirm button label
866
+ skipButtonText: '', // Override Skip button label
867
+ CustomDialog: null, // Provide a fully custom dialog component
868
+ });
729
869
  ```
730
870
 
731
871
  ---
@@ -762,7 +902,6 @@ The design JSON follows this structure:
762
902
  {
763
903
  "id": "page-1",
764
904
  "background": "#ffffff",
765
- "duration": 5000,
766
905
  "children": [
767
906
  {
768
907
  "id": "el-1",
@@ -774,16 +913,25 @@ The design JSON follows this structure:
774
913
  "text": "Hello World",
775
914
  "fontSize": 32,
776
915
  "fontFamily": "Roboto",
777
- "fill": "#000000"
916
+ "fill": "#000000",
917
+ "custom": {}
778
918
  },
779
919
  {
780
920
  "id": "el-2",
781
- "type": "image",
782
- "x": 50,
921
+ "type": "text",
922
+ "x": 200,
783
923
  "y": 200,
784
- "width": 400,
785
- "height": 300,
786
- "src": "https://example.com/photo.jpg"
924
+ "width": 250,
925
+ "height": 40,
926
+ "text": "Sample Text",
927
+ "fontSize": 20,
928
+ "custom": {
929
+ "inputField": true,
930
+ "inputType": "text",
931
+ "placeholder": "Enter text",
932
+ "promptText": "What is your name?",
933
+ "required": true
934
+ }
787
935
  }
788
936
  ]
789
937
  }
@@ -793,7 +941,11 @@ The design JSON follows this structure:
793
941
 
794
942
  ---
795
943
 
796
- ## Peer Dependencies
944
+ ## Dependencies
945
+
946
+ ### Peer Dependencies
947
+
948
+ These must be installed in your host application:
797
949
 
798
950
  | Package | Version |
799
951
  |---|---|
@@ -808,10 +960,20 @@ The design JSON follows this structure:
808
960
 
809
961
  These are included in the package — no need to install separately:
810
962
 
811
- - `konva` + `react-konva` — Canvas rendering
812
- - `mobx` + `mobx-state-tree` + `mobx-react-lite` — State management
813
- - `jspdf` PDF generation
814
- - `uuid` Unique ID generation
963
+ | Package | Purpose |
964
+ |---|---|
965
+ | `konva` + `react-konva` | Canvas rendering |
966
+ | `mobx` + `mobx-state-tree` + `mobx-react-lite` | State management |
967
+ | `jspdf` | PDF generation |
968
+ | `uuid` | Unique ID generation |
969
+
970
+ ---
971
+
972
+ ## Roadmap
973
+
974
+ The following features are planned but not yet implemented:
975
+
976
+ - **Image Input Field Popup** — The `image` input type can be added to templates and its metadata is preserved in JSON, but the popup dialog for uploading images at load time is not yet available. Image fields are skipped during the input collection flow.
815
977
 
816
978
  ---
817
979