labellife-design-tool 2.1.1 → 2.1.3

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,20 @@ 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
+ | `onInputFieldsComplete` | function | `undefined` | Callback fired after the user has completed (submitted or skipped) all input fields. Only called when the popup is enabled. |
395
+ | `components` | object | `{}` | Override slots (e.g. `{ PageControls: MyComponent }`) |
396
+
370
397
  The workspace provides:
371
398
  - **Infinite canvas** with zoom and pan (mouse wheel + drag)
372
399
  - **Click selection** and **multi-select** (Shift+click)
373
400
  - **Transform handles** for resize and rotation
374
401
  - **Auto-fit** to container on mount
375
402
  - **Background rendering** per page
403
+ - **Input fields popup** (automatically shown when a loaded template contains input fields)
376
404
 
377
405
  ### Zoom Buttons
378
406
 
@@ -406,13 +434,21 @@ import {
406
434
  TextSection,
407
435
  ElementsSection,
408
436
  UploadSection,
437
+ InputFieldsSection,
409
438
  BackgroundSection,
410
439
  LayersSection,
411
440
  } from 'labellife-design-tool/side-panel';
412
441
 
413
442
  <SidePanel
414
443
  store={store}
415
- sections={[TextSection, ElementsSection, UploadSection, BackgroundSection, LayersSection]}
444
+ sections={[
445
+ TextSection,
446
+ ElementsSection,
447
+ UploadSection,
448
+ InputFieldsSection,
449
+ BackgroundSection,
450
+ LayersSection,
451
+ ]}
416
452
  defaultSection="text"
417
453
  />
418
454
  ```
@@ -424,6 +460,7 @@ import {
424
460
  | `TextSection` | Text | Add heading, subheading, and body text presets |
425
461
  | `ElementsSection` | Elements | 60+ shapes and 6 line styles |
426
462
  | `UploadSection` | Upload | Drag-and-drop or file picker for images |
463
+ | `InputFieldsSection` | Input Fields | Add placeholder input elements (Text, Date, Integer, Image) for template-based workflows |
427
464
  | `BackgroundSection` | Background | Color picker with 22 preset colors |
428
465
  | `LayersSection` | Layers | Layer list with visibility, lock, reorder, and delete |
429
466
 
@@ -447,7 +484,6 @@ const MyCustomSection = {
447
484
  ),
448
485
  };
449
486
 
450
- // Use it
451
487
  <SidePanel store={store} sections={[MyCustomSection, TextSection, LayersSection]} />
452
488
  ```
453
489
 
@@ -480,23 +516,232 @@ The toolbar is **context-sensitive** — it automatically shows relevant control
480
516
  | **Line** | Line color |
481
517
  | **All** | Opacity, Move up/down, Lock/Unlock, Duplicate, Delete |
482
518
 
483
- ### Custom Action Controls
519
+ The toolbar includes built-in **Import** and **Download** buttons by default.
520
+
521
+ ### Customizing Toolbar Buttons
484
522
 
485
523
  ```jsx
486
524
  <Toolbar
487
525
  store={store}
488
526
  components={{
527
+ // Add custom action buttons
489
528
  ActionControls: ({ store }) => (
490
- <>
491
- {/* Custom Download button */}
492
- <button onClick={() => store.saveAsImage()}>Export</button>
493
- </>
529
+ <button onClick={() => store.saveAsImage()}>Export</button>
494
530
  ),
531
+ // Replace or hide the Import button
532
+ ImportButton: null, // Set to null to hide
533
+ // Replace or hide the Download button
534
+ DownloadButton: MyDownload, // Provide a custom component
535
+ }}
536
+ />
537
+ ```
538
+
539
+ ---
540
+
541
+ ## Input Fields
542
+
543
+ 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.
544
+
545
+ ### Overview
546
+
547
+ The input fields system consists of three parts:
548
+
549
+ 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.
550
+ 2. **Metadata** — Each input field element stores its configuration in the `custom` property, which is preserved through JSON serialization.
551
+ 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.
552
+
553
+ ### Supported Input Types
554
+
555
+ | Type | Description | Input Control |
556
+ |---|---|---|
557
+ | `text` | Free-form text entry | Text input |
558
+ | `date` | Date value with configurable format | Date picker |
559
+ | `integer` | Whole number value | Number input (integers only) |
560
+ | `image` | Image upload placeholder | File picker with preview |
561
+
562
+ ### Adding Input Fields (Admin Side)
563
+
564
+ Include `InputFieldsSection` in your side panel sections:
565
+
566
+ ```jsx
567
+ import { InputFieldsSection } from 'labellife-design-tool/side-panel';
568
+
569
+ <SidePanel
570
+ store={store}
571
+ sections={[TextSection, ElementsSection, InputFieldsSection, LayersSection]}
572
+ />
573
+ ```
574
+
575
+ For each input type, the admin can configure:
576
+
577
+ - **Prompt Text** — The message displayed to the end-user (e.g. "Enter your name", "Select the event date").
578
+ - **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.
579
+ - **Date Format** *(date type only)* — Choose from `MM-DD-YYYY`, `Month Name-DD-YYYY`, `Month Name-DD`, `YYYY`, or `MM-DD`.
580
+
581
+ ### Input Field Metadata
582
+
583
+ Each input field element stores the following in its `custom` property:
584
+
585
+ ```json
586
+ {
587
+ "inputField": true,
588
+ "inputType": "text",
589
+ "placeholder": "Enter text",
590
+ "promptText": "Please enter your name",
591
+ "required": false
592
+ }
593
+ ```
594
+
595
+ For date fields, an additional `dateFormat` property is included:
596
+
597
+ ```json
598
+ {
599
+ "inputField": true,
600
+ "inputType": "date",
601
+ "placeholder": "Select date",
602
+ "promptText": "When is the event?",
603
+ "required": true,
604
+ "dateFormat": "MM-DD-YYYY"
605
+ }
606
+ ```
607
+
608
+ ### Controlling the Popup
609
+
610
+ 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.
611
+
612
+ ```jsx
613
+ // Popup enabled (default) — dialog appears after loadJSON if input fields exist
614
+ <Workspace store={store} />
615
+
616
+ // Popup disabled — input fields retain their template placeholder values
617
+ <Workspace store={store} showInputFieldsPopup={false} />
618
+
619
+ // With completion callback
620
+ <Workspace
621
+ store={store}
622
+ onInputFieldsComplete={() => {
623
+ console.log('All input fields have been filled!');
624
+ // e.g. navigate, save, show a toast, etc.
495
625
  }}
496
626
  />
497
627
  ```
498
628
 
499
- The toolbar includes built-in **Download** and **Import** buttons by default.
629
+ 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.
630
+
631
+ ### Customizing the Popup Appearance
632
+
633
+ Use `setInputFieldsConfig` to override the default dialog styles:
634
+
635
+ ```js
636
+ import { setInputFieldsConfig } from 'labellife-design-tool';
637
+
638
+ setInputFieldsConfig({
639
+ // Style overrides (applied via MUI sx prop)
640
+ dialogStyle: { borderRadius: 16 },
641
+ titleStyle: { color: '#1a1a1a', fontFamily: 'Georgia' },
642
+ promptStyle: { fontSize: 15 },
643
+ inputStyle: { borderRadius: 8 },
644
+ submitButtonStyle: { backgroundColor: '#4caf50', '&:hover': { backgroundColor: '#388e3c' } },
645
+ skipButtonStyle: { color: '#757575' },
646
+
647
+ // Button text overrides
648
+ submitButtonText: 'Apply',
649
+ skipButtonText: 'Leave as is',
650
+ });
651
+ ```
652
+
653
+ ### Providing Custom Dialog Components
654
+
655
+ You can replace the built-in popup with your own component — either globally for all types, or per input type.
656
+
657
+ **Per-type custom dialogs** (override specific types only):
658
+
659
+ ```js
660
+ import { setInputFieldsConfig } from 'labellife-design-tool';
661
+
662
+ setInputFieldsConfig({
663
+ CustomTextDialog: MyTextPopup, // Used for text fields
664
+ CustomDateDialog: MyDatePopup, // Used for date fields
665
+ CustomIntegerDialog: MyNumberPopup, // Used for integer fields
666
+ CustomImageDialog: MyImagePopup, // Used for image fields
667
+ });
668
+ ```
669
+
670
+ **Global custom dialog** (catches any type without a per-type override):
671
+
672
+ ```js
673
+ setInputFieldsConfig({
674
+ CustomDialog: MyGenericPopup, // Fallback for all types
675
+ });
676
+ ```
677
+
678
+ **Mix and match** — per-type takes priority over global:
679
+
680
+ ```js
681
+ setInputFieldsConfig({
682
+ CustomImageDialog: MyImagePopup, // Only image fields use this
683
+ CustomDialog: MyGenericPopup, // Text, date, and integer use this
684
+ });
685
+ ```
686
+
687
+ **Resolution order:** `CustomTextDialog` / `CustomDateDialog` / `CustomIntegerDialog` / `CustomImageDialog` > `CustomDialog` > built-in dialog.
688
+
689
+ > **Note:** When `showInputFieldsPopup={false}` is set on `<Workspace>`, no popup is shown regardless of any custom dialog configuration.
690
+
691
+ **Props passed to every custom dialog component:**
692
+
693
+ | Prop | Type | Description |
694
+ |---|---|---|
695
+ | `field` | object | The current input field element (MST node with `id`, `custom`, etc.) |
696
+ | `fields` | array | All pending input field elements |
697
+ | `currentIndex` | number | Zero-based index of the current field |
698
+ | `totalCount` | number | Total number of fields to process |
699
+ | `onSubmit(elementId, value)` | function | Call to set the value and advance to the next field |
700
+ | `onSkip(elementId)` | function | Call to skip the field and advance |
701
+ | `onComplete()` | function | Call when all fields are processed |
702
+
703
+ **Example custom dialog:**
704
+
705
+ ```jsx
706
+ const MyImagePopup = ({ field, currentIndex, totalCount, onSubmit, onSkip }) => {
707
+ const promptText = field.custom?.promptText || 'Upload an image';
708
+
709
+ const handleFile = (e) => {
710
+ const file = e.target.files?.[0];
711
+ if (!file) return;
712
+ const reader = new FileReader();
713
+ reader.onload = (ev) => onSubmit(field.id, ev.target.result);
714
+ reader.readAsDataURL(file);
715
+ };
716
+
717
+ return (
718
+ <div className="my-dialog">
719
+ <h3>{promptText}</h3>
720
+ <p>Field {currentIndex + 1} of {totalCount}</p>
721
+ <input type="file" accept="image/*" onChange={handleFile} />
722
+ {!field.custom?.required && (
723
+ <button onClick={() => onSkip(field.id)}>Skip</button>
724
+ )}
725
+ </div>
726
+ );
727
+ };
728
+ ```
729
+
730
+ ### Accessing Input Fields Programmatically
731
+
732
+ ```js
733
+ // Get all input field elements across all pages
734
+ const fields = store.inputFields;
735
+
736
+ // Get pending fields awaiting user input
737
+ const pending = store._pendingInputFields;
738
+
739
+ // Manually trigger the input fields flow (called automatically by loadJSON)
740
+ store.requestInputFields();
741
+
742
+ // Clear all pending fields
743
+ store.clearPendingInputFields();
744
+ ```
500
745
 
501
746
  ---
502
747
 
@@ -546,11 +791,8 @@ To remove a mask, click **Remove Mask** in the mask panel.
546
791
  Programmatically:
547
792
 
548
793
  ```js
549
- // Apply mask
550
- element.set({ clipSrc: 'circle' });
551
-
552
- // Remove mask
553
- element.set({ clipSrc: '' });
794
+ element.set({ clipSrc: 'circle' }); // Apply mask
795
+ element.set({ clipSrc: '' }); // Remove mask
554
796
  ```
555
797
 
556
798
  ---
@@ -560,130 +802,113 @@ element.set({ clipSrc: '' });
560
802
  ### Image Export
561
803
 
562
804
  ```js
563
- // Export as data URL
564
805
  const dataURL = await store.toDataURL({
565
806
  mimeType: 'image/png', // 'image/png' or 'image/jpeg'
566
807
  quality: 1, // 0–1 (JPEG quality)
567
808
  pixelRatio: 2, // Resolution multiplier
568
809
  });
569
810
 
570
- // Export as Blob
571
811
  const blob = await store.toBlob({ mimeType: 'image/png' });
572
812
 
573
- // Direct download
574
813
  await store.saveAsImage({ fileName: 'my-design.png' });
575
814
  ```
576
815
 
577
816
  ### PDF Export
578
817
 
579
818
  ```js
580
- // Multi-page PDF as data URL
581
- const pdfDataURL = await store.toPDFDataURL({
582
- pixelRatio: 2,
583
- dpi: 300,
584
- });
819
+ const pdfDataURL = await store.toPDFDataURL({ pixelRatio: 2, dpi: 300 });
585
820
 
586
- // Direct download
587
821
  await store.saveAsPDF({ fileName: 'my-design.pdf' });
588
822
  ```
589
823
 
590
824
  ### JSON Export
591
825
 
592
826
  ```js
593
- // Serialize
594
827
  const json = store.toJSON();
595
-
596
- // Download as file
597
828
  store.saveAsJSON({ fileName: 'my-design.json' });
598
-
599
- // Load back
600
829
  store.loadJSON(json);
601
830
  ```
602
831
 
603
832
  ---
604
833
 
605
- ## Internationalization (i18n)
834
+ ## Configuration
606
835
 
607
- The library supports full internationalization via a nested translation system with dot-notation key resolution.
836
+ ### Internationalization (i18n)
608
837
 
609
- ### Setting Translations
838
+ The library supports full internationalization via a nested translation system with dot-notation key resolution.
610
839
 
611
840
  ```js
612
- import { setTranslations } from 'labellife-design-tool/config';
841
+ import { setTranslations, t } from 'labellife-design-tool/config';
613
842
 
614
843
  setTranslations({
615
844
  toolbar: {
616
845
  flip: 'Spiegelen',
617
- flipHorizontally: 'Horizontaal spiegelen',
618
- flipVertically: 'Verticaal spiegelen',
619
846
  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
847
  download: 'Downloaden',
637
848
  import: 'Importeren',
638
- fileType: 'Bestandstype',
639
- quality: 'Kwaliteit',
640
849
  undo: 'Ongedaan maken',
641
850
  redo: 'Opnieuw',
642
- blur: 'Vervagen',
643
- brightness: 'Helderheid',
644
- shadow: 'Schaduw',
645
- offsetX: 'Verschuiving X',
646
- offsetY: 'Verschuiving Y',
851
+ // ... see full key reference below
647
852
  },
648
853
  sidePanel: {
649
854
  text: 'Tekst',
650
855
  elements: 'Elementen',
651
- shapes: 'Vormen',
652
- lines: 'Lijnen',
653
856
  upload: 'Uploaden',
654
- uploadTip: 'Sleep afbeeldingen hierheen of klik om te uploaden',
655
857
  background: 'Achtergrond',
656
858
  layers: 'Lagen',
657
- noLayers: 'Geen lagen op deze pagina',
658
- headerText: 'Kop toevoegen',
659
- subHeaderText: 'Subkop toevoegen',
660
- bodyText: 'Broodtekst toevoegen',
859
+ inputFields: {
860
+ tab: 'Invoervelden',
861
+ title: 'Invoervelden',
862
+ required: 'Verplicht',
863
+ // ...
864
+ },
865
+ },
866
+ inputFieldsDialog: {
867
+ title: 'Invoer vereist',
868
+ submit: 'Bevestigen',
869
+ skip: 'Overslaan',
870
+ stepIndicator: 'Veld {current} van {total}',
661
871
  },
662
872
  });
663
- ```
664
873
 
665
- Translations are **deep-merged**, so you can set them incrementally:
666
-
667
- ```js
668
- // First call
874
+ // Translations are deep-merged safe to call multiple times
669
875
  setTranslations({ toolbar: { flip: 'Flip' } });
876
+ setTranslations({ toolbar: { effects: 'Effects' } }); // Does not overwrite toolbar.flip
670
877
 
671
- // Second call merges, does not overwrite toolbar.flip
672
- setTranslations({ toolbar: { effects: 'Effects' } });
878
+ // Use the translation function anywhere
879
+ t('toolbar.flip'); // Returns translated value
880
+ t('toolbar.flip', 'Flip'); // Returns default if key not found
673
881
  ```
674
882
 
675
- ### Using the Translation Function
883
+ <details>
884
+ <summary><strong>Full translation key reference</strong></summary>
676
885
 
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
886
+ ```
887
+ toolbar.flip, toolbar.flipHorizontally, toolbar.flipVertically, toolbar.effects,
888
+ toolbar.fitToBackground, toolbar.clip, toolbar.removeMask, toolbar.crop, toolbar.fill,
889
+ toolbar.textStroke, toolbar.border, toolbar.cornerRadius, toolbar.color, toolbar.opacity,
890
+ toolbar.up, toolbar.down, toolbar.lockedDescription, toolbar.unlockedDescription,
891
+ toolbar.duplicateElements, toolbar.removeElements, toolbar.download, toolbar.import,
892
+ toolbar.fileType, toolbar.quality, toolbar.undo, toolbar.redo, toolbar.blur,
893
+ toolbar.brightness, toolbar.shadow, toolbar.offsetX, toolbar.offsetY,
894
+ toolbar.group, toolbar.ungroup
895
+
896
+ sidePanel.text, sidePanel.elements, sidePanel.shapes, sidePanel.lines,
897
+ sidePanel.upload, sidePanel.uploadTip, sidePanel.background, sidePanel.layers,
898
+ sidePanel.noLayers, sidePanel.headerText, sidePanel.subHeaderText, sidePanel.bodyText,
899
+ sidePanel.inputFields.tab, sidePanel.inputFields.title, sidePanel.inputFields.description,
900
+ sidePanel.inputFields.textInput, sidePanel.inputFields.dateInput,
901
+ sidePanel.inputFields.integerInput, sidePanel.inputFields.imageInput,
902
+ sidePanel.inputFields.required, sidePanel.inputFields.promptTextHint,
903
+ sidePanel.inputFields.dateFormat
904
+
905
+ inputFieldsDialog.title, inputFieldsDialog.submit, inputFieldsDialog.skip,
906
+ inputFieldsDialog.stepIndicator
682
907
  ```
683
908
 
684
- ---
909
+ </details>
685
910
 
686
- ## Theming
911
+ ### Theming
687
912
 
688
913
  ```js
689
914
  import { setTheme, getTheme } from 'labellife-design-tool/config';
@@ -691,41 +916,28 @@ import { setTheme, getTheme } from 'labellife-design-tool/config';
691
916
  setTheme('light'); // or 'dark'
692
917
  ```
693
918
 
694
- ---
695
-
696
- ## Utility Functions
697
-
698
- ### Unit Conversion
699
-
700
- ```js
701
- import { unitToPx, pxToUnit } from 'labellife-design-tool/utils/unit';
702
-
703
- unitToPx({ unit: 'mm', dpi: 300, unitVal: 50 }); // → pixels
704
- pxToUnit({ unit: 'mm', dpi: 300, pxVal: 590.55 }); // → millimeters
705
- ```
706
-
707
- **Supported units:** `px`, `mm`, `cm`, `in`, `pt`, `pc`
919
+ ### Input Fields Dialog Configuration
708
920
 
709
- ### Image Utilities
921
+ See [Input Fields — Customizing the Popup Appearance](#customizing-the-popup-appearance) for full details.
710
922
 
711
923
  ```js
712
- import { getImageSize } from 'labellife-design-tool/utils/image';
713
-
714
- const { width, height } = await getImageSize('https://example.com/photo.jpg');
715
- ```
716
-
717
- ### Font Utilities
718
-
719
- ```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
924
+ import { setInputFieldsConfig, getInputFieldsConfig } from 'labellife-design-tool';
925
+
926
+ setInputFieldsConfig({
927
+ dialogStyle: {}, // MUI sx overrides for the Dialog paper
928
+ titleStyle: {}, // MUI sx overrides for the Dialog title
929
+ promptStyle: {}, // MUI sx overrides for the prompt text
930
+ inputStyle: {}, // MUI sx overrides for the input field
931
+ submitButtonStyle: {}, // MUI sx overrides for the Confirm button
932
+ skipButtonStyle: {}, // MUI sx overrides for the Skip button
933
+ submitButtonText: '', // Override Confirm button label
934
+ skipButtonText: '', // Override Skip button label
935
+ CustomDialog: null, // Custom dialog for all types (global fallback)
936
+ CustomTextDialog: null, // Custom dialog for text fields only
937
+ CustomDateDialog: null, // Custom dialog for date fields only
938
+ CustomIntegerDialog: null,// Custom dialog for integer fields only
939
+ CustomImageDialog: null, // Custom dialog for image fields only
940
+ });
729
941
  ```
730
942
 
731
943
  ---
@@ -762,7 +974,6 @@ The design JSON follows this structure:
762
974
  {
763
975
  "id": "page-1",
764
976
  "background": "#ffffff",
765
- "duration": 5000,
766
977
  "children": [
767
978
  {
768
979
  "id": "el-1",
@@ -774,16 +985,25 @@ The design JSON follows this structure:
774
985
  "text": "Hello World",
775
986
  "fontSize": 32,
776
987
  "fontFamily": "Roboto",
777
- "fill": "#000000"
988
+ "fill": "#000000",
989
+ "custom": {}
778
990
  },
779
991
  {
780
992
  "id": "el-2",
781
- "type": "image",
782
- "x": 50,
993
+ "type": "text",
994
+ "x": 200,
783
995
  "y": 200,
784
- "width": 400,
785
- "height": 300,
786
- "src": "https://example.com/photo.jpg"
996
+ "width": 250,
997
+ "height": 40,
998
+ "text": "Sample Text",
999
+ "fontSize": 20,
1000
+ "custom": {
1001
+ "inputField": true,
1002
+ "inputType": "text",
1003
+ "placeholder": "Enter text",
1004
+ "promptText": "What is your name?",
1005
+ "required": true
1006
+ }
787
1007
  }
788
1008
  ]
789
1009
  }
@@ -793,7 +1013,11 @@ The design JSON follows this structure:
793
1013
 
794
1014
  ---
795
1015
 
796
- ## Peer Dependencies
1016
+ ## Dependencies
1017
+
1018
+ ### Peer Dependencies
1019
+
1020
+ These must be installed in your host application:
797
1021
 
798
1022
  | Package | Version |
799
1023
  |---|---|
@@ -808,10 +1032,21 @@ The design JSON follows this structure:
808
1032
 
809
1033
  These are included in the package — no need to install separately:
810
1034
 
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
1035
+ | Package | Purpose |
1036
+ |---|---|
1037
+ | `konva` + `react-konva` | Canvas rendering |
1038
+ | `mobx` + `mobx-state-tree` + `mobx-react-lite` | State management |
1039
+ | `jspdf` | PDF generation |
1040
+ | `uuid` | Unique ID generation |
1041
+
1042
+ ---
1043
+
1044
+ ## Roadmap
1045
+
1046
+ All four input field types (Text, Date, Integer, Image) are fully implemented. Future enhancements under consideration:
1047
+
1048
+ - **Drag-and-drop image upload** in the input fields popup dialog
1049
+ - **URL-based image input** as an alternative to file upload
815
1050
 
816
1051
  ---
817
1052