@merchi/product-editor 0.1.9

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.
Files changed (123) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/README.md +174 -0
  3. package/dist/components/AlignMenu.d.ts +3 -0
  4. package/dist/components/AlignMenu.js +61 -0
  5. package/dist/components/FloatingToolbar.d.ts +4 -0
  6. package/dist/components/FloatingToolbar.js +97 -0
  7. package/dist/components/FormatButtons.d.ts +2 -0
  8. package/dist/components/FormatButtons.js +22 -0
  9. package/dist/components/ImageNavButton.d.ts +7 -0
  10. package/dist/components/ImageNavButton.js +14 -0
  11. package/dist/components/ImageZoomModal.d.ts +12 -0
  12. package/dist/components/ImageZoomModal.js +129 -0
  13. package/dist/components/LayerPanel.d.ts +4 -0
  14. package/dist/components/LayerPanel.js +223 -0
  15. package/dist/components/LoadingOverlay.d.ts +9 -0
  16. package/dist/components/LoadingOverlay.js +17 -0
  17. package/dist/components/ProductEditor.d.ts +4 -0
  18. package/dist/components/ProductEditor.js +67 -0
  19. package/dist/components/ProductImageGallery.d.ts +6 -0
  20. package/dist/components/ProductImageGallery.js +148 -0
  21. package/dist/components/ProductPreviews.d.ts +7 -0
  22. package/dist/components/ProductPreviews.js +281 -0
  23. package/dist/components/TextToolbar.d.ts +4 -0
  24. package/dist/components/TextToolbar.js +117 -0
  25. package/dist/components/Toolbar.d.ts +3 -0
  26. package/dist/components/Toolbar.js +47 -0
  27. package/dist/config/colorConfig.d.ts +1 -0
  28. package/dist/config/colorConfig.js +10 -0
  29. package/dist/config/fontConfig.d.ts +5 -0
  30. package/dist/config/fontConfig.js +21 -0
  31. package/dist/context/ProductEditorContext.d.ts +60 -0
  32. package/dist/context/ProductEditorContext.js +743 -0
  33. package/dist/hooks/useSwipeNavigate.d.ts +11 -0
  34. package/dist/hooks/useSwipeNavigate.js +35 -0
  35. package/dist/index.d.ts +6 -0
  36. package/dist/index.js +30 -0
  37. package/dist/styles/BottomPreviewDisplay.css +65 -0
  38. package/dist/styles/FloatingToolbar.css +41 -0
  39. package/dist/styles/ImageGallery.css +147 -0
  40. package/dist/styles/ImageNavButton.css +35 -0
  41. package/dist/styles/ImageZoomModal.css +101 -0
  42. package/dist/styles/LayerPanel.css +179 -0
  43. package/dist/styles/LoadingOverlay.css +48 -0
  44. package/dist/styles/ProductEditor.css +192 -0
  45. package/dist/styles/TextToolbar.css +327 -0
  46. package/dist/styles/Toolbar.css +125 -0
  47. package/dist/styles/index.css +5 -0
  48. package/dist/styles/textToolbarSelectStyles.d.ts +3 -0
  49. package/dist/styles/textToolbarSelectStyles.js +30 -0
  50. package/dist/types.d.ts +228 -0
  51. package/dist/types.js +17 -0
  52. package/dist/utils/canvasAddText.d.ts +11 -0
  53. package/dist/utils/canvasAddText.js +66 -0
  54. package/dist/utils/canvasUtils.d.ts +37 -0
  55. package/dist/utils/canvasUtils.js +342 -0
  56. package/dist/utils/deviceUtils.d.ts +5 -0
  57. package/dist/utils/deviceUtils.js +33 -0
  58. package/dist/utils/draftTemplateUtils.d.ts +23 -0
  59. package/dist/utils/draftTemplateUtils.js +66 -0
  60. package/dist/utils/grid.d.ts +32 -0
  61. package/dist/utils/grid.js +151 -0
  62. package/dist/utils/imageUtils.d.ts +12 -0
  63. package/dist/utils/imageUtils.js +178 -0
  64. package/dist/utils/job.d.ts +40 -0
  65. package/dist/utils/job.js +115 -0
  66. package/dist/utils/keyboard.d.ts +1 -0
  67. package/dist/utils/keyboard.js +26 -0
  68. package/dist/utils/previewUtils.d.ts +9 -0
  69. package/dist/utils/previewUtils.js +39 -0
  70. package/dist/utils/psdConverter.d.ts +40 -0
  71. package/dist/utils/psdConverter.js +516 -0
  72. package/dist/utils/psdRenderUtils.d.ts +12 -0
  73. package/dist/utils/psdRenderUtils.js +465 -0
  74. package/dist/utils/renderUtils.d.ts +2 -0
  75. package/dist/utils/renderUtils.js +85 -0
  76. package/package.json +66 -0
  77. package/src/components/AlignMenu.tsx +49 -0
  78. package/src/components/FloatingToolbar.tsx +83 -0
  79. package/src/components/FormatButtons.tsx +31 -0
  80. package/src/components/ImageNavButton.tsx +23 -0
  81. package/src/components/ImageZoomModal.tsx +163 -0
  82. package/src/components/LayerPanel.tsx +256 -0
  83. package/src/components/LoadingOverlay.tsx +27 -0
  84. package/src/components/ProductEditor.tsx +104 -0
  85. package/src/components/ProductImageGallery.tsx +181 -0
  86. package/src/components/ProductPreviews.tsx +243 -0
  87. package/src/components/TextToolbar.tsx +164 -0
  88. package/src/components/Toolbar.tsx +86 -0
  89. package/src/config/colorConfig.ts +7 -0
  90. package/src/config/fontConfig.ts +23 -0
  91. package/src/context/ProductEditorContext.tsx +829 -0
  92. package/src/hooks/useSwipeNavigate.ts +40 -0
  93. package/src/index.ts +6 -0
  94. package/src/styles/BottomPreviewDisplay.css +65 -0
  95. package/src/styles/FloatingToolbar.css +41 -0
  96. package/src/styles/ImageGallery.css +147 -0
  97. package/src/styles/ImageNavButton.css +35 -0
  98. package/src/styles/ImageZoomModal.css +101 -0
  99. package/src/styles/LayerPanel.css +179 -0
  100. package/src/styles/LoadingOverlay.css +48 -0
  101. package/src/styles/ProductEditor.css +192 -0
  102. package/src/styles/TextToolbar.css +327 -0
  103. package/src/styles/Toolbar.css +125 -0
  104. package/src/styles/index.css +5 -0
  105. package/src/styles/textToolbarSelectStyles.ts +60 -0
  106. package/src/types/ag-psd/index.d.ts +1 -0
  107. package/src/types/fabric.d.ts +14 -0
  108. package/src/types/lodash/index.d.ts +1 -0
  109. package/src/types/psd.d.ts +53 -0
  110. package/src/types.ts +247 -0
  111. package/src/utils/canvasAddText.ts +72 -0
  112. package/src/utils/canvasUtils.ts +406 -0
  113. package/src/utils/deviceUtils.ts +31 -0
  114. package/src/utils/draftTemplateUtils.ts +70 -0
  115. package/src/utils/grid.ts +154 -0
  116. package/src/utils/imageUtils.ts +218 -0
  117. package/src/utils/job.ts +128 -0
  118. package/src/utils/keyboard.ts +28 -0
  119. package/src/utils/previewUtils.ts +43 -0
  120. package/src/utils/psdConverter.ts +595 -0
  121. package/src/utils/psdRenderUtils.ts +453 -0
  122. package/src/utils/renderUtils.ts +44 -0
  123. package/tsconfig.json +17 -0
@@ -0,0 +1 @@
1
+ $ tsc && copyfiles -u 2 src/styles/*.css dist/styles/
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # Merchi Product Editor
2
+
3
+ A React-based product editor component that allows users to edit product templates using Fabric.js. This library enables customization of product designs through variations, template switching, and canvas manipulation.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Template Support**: Select and switch between different draft templates
8
+ - **Variation Management**: Update canvas objects based on product variations
9
+ - **Responsive Design**: Works on both desktop and mobile devices
10
+ - **Canvas Customization**: Grid toggle, image upload
11
+ - **Context-based Architecture**: Uses React Context API for state management
12
+ - **Type-safe**: Written in TypeScript with proper type definitions
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @merchi/product-editor
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Basic Example
23
+
24
+ ```jsx
25
+ import { ProductEditor } from '@merchi/product-editor';
26
+
27
+ function App() {
28
+ const product = {
29
+ id: 1,
30
+ name: "Sample Product",
31
+ draftTemplates: [
32
+ {
33
+ id: 1,
34
+ name: "Template 1",
35
+ file: {
36
+ viewUrl: "https://example.com/template1.jpg"
37
+ },
38
+ width: 800,
39
+ height: 600
40
+ }
41
+ ]
42
+ };
43
+
44
+ const variations = [
45
+ {
46
+ variationField: {
47
+ id: 1,
48
+ fieldType: "TEXT_INPUT"
49
+ },
50
+ value: "Sample Text"
51
+ }
52
+ ];
53
+
54
+ const handleSave = () => {
55
+ // Handle save action
56
+ console.log('Saved product design');
57
+ };
58
+
59
+ const handleCancel = () => {
60
+ // Handle cancel action
61
+ console.log('Editing cancelled');
62
+ };
63
+
64
+ return (
65
+ <ProductEditor
66
+ product={product}
67
+ job={{id: 1}}
68
+ width={800}
69
+ height={600}
70
+ onSave={handleSave}
71
+ onCancel={handleCancel}
72
+ variations={variations}
73
+ groupVariations={[]}
74
+ />
75
+ );
76
+ }
77
+ ```
78
+
79
+ ### Using the Context API
80
+
81
+ You can access the editor's context in child components:
82
+
83
+ ```jsx
84
+ import { useProductEditor } from '@merchi/product-editor';
85
+
86
+ function CustomButton() {
87
+ const { handleSave, updateCanvasFromVariations } = useProductEditor();
88
+
89
+ const handleUpdate = () => {
90
+ const newVariations = [/* updated variations */];
91
+ updateCanvasFromVariations(newVariations);
92
+ };
93
+
94
+ return (
95
+ <button onClick={handleUpdate}>Update Design</button>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ## Props
101
+
102
+ | Prop | Type | Required | Default | Description |
103
+ |------|------|----------|---------|-------------|
104
+ | product | Product | Yes | - | Merchi Product entity containing draft templates |
105
+ | job | Job | Yes | - | Job information |
106
+ | width | number | No | 800 | Width of the editor canvas |
107
+ | height | number | No | 600 | Height of the editor canvas |
108
+ | onSave | () => void | Yes | - | Callback function when saving the edited image |
109
+ | onCancel | () => void | Yes | - | Callback function when canceling the edit |
110
+ | variations | Variation[] | Yes | [] | Array of variations to apply to the templates |
111
+ | groupVariations | Variation[] | Yes | [] | Array of group variations to apply |
112
+ | fontOptions | FontOption[] | No | Default list | Custom fonts for text toolbar |
113
+ | colorPalette | (string \| null)[][] | No | Default palette | Custom color palette for text toolbar |
114
+
115
+ ## Context API
116
+
117
+ The `useProductEditor` hook provides access to the following properties and methods:
118
+
119
+ | Property/Method | Type | Description |
120
+ |----------------|------|-------------|
121
+ | canvas | fabric.Canvas | The Fabric.js canvas instance |
122
+ | draftTemplates | Array | Available draft templates with associated variation objects |
123
+ | selectedTemplate | number | ID of the currently selected template |
124
+ | handleTemplateChange | Function | Method to switch between templates |
125
+ | showGrid | boolean | Whether the grid is currently displayed |
126
+ | setShowGrid | Function | Toggle grid visibility |
127
+ | showPreview | boolean | Whether the preview area is displayed |
128
+ | togglePreview | Function | Toggle preview area visibility |
129
+ | canvasObjects | Map | Map of canvas objects indexed by variation field ID |
130
+ | updateCanvasFromVariations | Function | Update canvas objects based on changed variations |
131
+
132
+ ## ImageGallery Component
133
+
134
+ The `ImageGallery` component provides a simple way to display images in your product editor when other images fail to load.
135
+
136
+ ```jsx
137
+ import { ImageGallery } from '@merchi/product-editor';
138
+
139
+ function Example() {
140
+ return (
141
+ <ImageGallery
142
+ fallbackImageUrl="https://example.com/default-image.jpg"
143
+ />
144
+ );
145
+ }
146
+ ```
147
+
148
+ ### Props for ImageGallery
149
+
150
+ | Prop | Type | Required | Default | Description |
151
+ |------|------|----------|---------|-------------|
152
+ | fallbackImageUrl | string | No | null | URL to an image that will be displayed when the primary image fails to load |
153
+
154
+ The ImageGallery component is designed to be simple and focused on providing fallback functionality for image loading errors.
155
+
156
+ ## Development
157
+
158
+ ```bash
159
+ # Install dependencies
160
+ npm install
161
+
162
+ # Start development server
163
+ npm start
164
+
165
+ # Build the library
166
+ npm run build
167
+
168
+ # Run tests
169
+ npm test
170
+ ```
171
+
172
+ ## License
173
+
174
+ MIT
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import '../styles/TextToolbar.css';
3
+ export declare const AlignMenu: React.FC;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.AlignMenu = void 0;
27
+ var react_1 = __importStar(require("react"));
28
+ var grommet_icons_1 = require("grommet-icons");
29
+ var ProductEditorContext_1 = require("../context/ProductEditorContext");
30
+ require("../styles/TextToolbar.css");
31
+ var AlignMenu = function () {
32
+ var _a = (0, ProductEditorContext_1.useProductEditor)(), selectedTextObject = _a.selectedTextObject, updateSelectedText = _a.updateSelectedText;
33
+ var _b = (0, react_1.useState)(false), showMenu = _b[0], setShowMenu = _b[1];
34
+ var ref = (0, react_1.useRef)(null);
35
+ (0, react_1.useEffect)(function () {
36
+ var handleClickOutside = function (e) {
37
+ if (ref.current && !ref.current.contains(e.target)) {
38
+ setShowMenu(false);
39
+ }
40
+ };
41
+ document.addEventListener('mousedown', handleClickOutside);
42
+ return function () { return document.removeEventListener('mousedown', handleClickOutside); };
43
+ }, []);
44
+ if (!selectedTextObject || !updateSelectedText)
45
+ return null;
46
+ var handleAlign = function (align) {
47
+ updateSelectedText({ textAlign: align });
48
+ setShowMenu(false);
49
+ };
50
+ return (react_1.default.createElement("div", { className: "align-group", ref: ref },
51
+ react_1.default.createElement("button", { type: "button", className: "toolbar-button align-trigger", onClick: function () { return setShowMenu(function (prev) { return !prev; }); }, title: "Text Align" }, selectedTextObject.textAlign === 'center' ? react_1.default.createElement(grommet_icons_1.TextAlignCenter, null) :
52
+ selectedTextObject.textAlign === 'right' ? react_1.default.createElement(grommet_icons_1.TextAlignRight, null) : react_1.default.createElement(grommet_icons_1.TextAlignLeft, null)),
53
+ showMenu && (react_1.default.createElement("div", { className: "align-popover" },
54
+ react_1.default.createElement("button", { type: "button", className: "toolbar-button ".concat(selectedTextObject.textAlign === 'left' ? 'active' : ''), onClick: function () { return handleAlign('left'); } },
55
+ react_1.default.createElement(grommet_icons_1.TextAlignLeft, null)),
56
+ react_1.default.createElement("button", { type: "button", className: "toolbar-button ".concat(selectedTextObject.textAlign === 'center' ? 'active' : ''), onClick: function () { return handleAlign('center'); } },
57
+ react_1.default.createElement(grommet_icons_1.TextAlignCenter, null)),
58
+ react_1.default.createElement("button", { type: "button", className: "toolbar-button ".concat(selectedTextObject.textAlign === 'right' ? 'active' : ''), onClick: function () { return handleAlign('right'); } },
59
+ react_1.default.createElement(grommet_icons_1.TextAlignRight, null))))));
60
+ };
61
+ exports.AlignMenu = AlignMenu;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import '../styles/FloatingToolbar.css';
3
+ declare const FloatingToolbar: React.FC;
4
+ export default FloatingToolbar;
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ var react_1 = __importStar(require("react"));
30
+ var Toolbar_1 = __importDefault(require("./Toolbar"));
31
+ require("../styles/FloatingToolbar.css");
32
+ var FloatingToolbar = function () {
33
+ var _a = (0, react_1.useState)({ x: 15, y: 15 }), position = _a[0], setPosition = _a[1];
34
+ var _b = (0, react_1.useState)(false), isDragging = _b[0], setIsDragging = _b[1];
35
+ var toolbarRef = (0, react_1.useRef)(null);
36
+ var offset = (0, react_1.useRef)({ x: 0, y: 0 });
37
+ var handleMouseDown = function (e) {
38
+ if (!toolbarRef.current)
39
+ return;
40
+ // Prevent dragging if clicking on buttons inside the toolbar
41
+ if (e.target.closest('.toolbar-button')) {
42
+ return;
43
+ }
44
+ setIsDragging(true);
45
+ var rect = toolbarRef.current.getBoundingClientRect();
46
+ offset.current = {
47
+ x: e.clientX - rect.left,
48
+ y: e.clientY - rect.top,
49
+ };
50
+ if (toolbarRef.current) {
51
+ toolbarRef.current.style.cursor = 'grabbing';
52
+ }
53
+ e.preventDefault();
54
+ };
55
+ var handleMouseMove = function (e) {
56
+ if (!isDragging || !toolbarRef.current)
57
+ return;
58
+ var newX = e.clientX - offset.current.x;
59
+ var newY = e.clientY - offset.current.y;
60
+ var parent = toolbarRef.current.parentElement;
61
+ if (parent) {
62
+ var parentRect = parent.getBoundingClientRect();
63
+ var toolbarRect = toolbarRef.current.getBoundingClientRect();
64
+ newX = Math.max(parentRect.left, Math.min(newX, parentRect.right - toolbarRect.width));
65
+ newY = Math.max(parentRect.top, Math.min(newY, parentRect.bottom - toolbarRect.height));
66
+ newX -= parentRect.left;
67
+ newY -= parentRect.top;
68
+ }
69
+ setPosition({ x: newX, y: newY });
70
+ };
71
+ var handleMouseUp = function () {
72
+ setIsDragging(false);
73
+ if (toolbarRef.current) {
74
+ toolbarRef.current.style.cursor = 'grab';
75
+ }
76
+ };
77
+ (0, react_1.useEffect)(function () {
78
+ if (isDragging) {
79
+ window.addEventListener('mousemove', handleMouseMove);
80
+ window.addEventListener('mouseup', handleMouseUp);
81
+ }
82
+ else {
83
+ window.removeEventListener('mousemove', handleMouseMove);
84
+ window.removeEventListener('mouseup', handleMouseUp);
85
+ }
86
+ return function () {
87
+ window.removeEventListener('mousemove', handleMouseMove);
88
+ window.removeEventListener('mouseup', handleMouseUp);
89
+ };
90
+ }, [isDragging]);
91
+ return (react_1.default.createElement("div", { ref: toolbarRef, className: "floating-toolbar", style: {
92
+ left: "".concat(position.x, "px"),
93
+ top: "".concat(position.y, "px"),
94
+ }, onMouseDown: handleMouseDown },
95
+ react_1.default.createElement(Toolbar_1.default, null)));
96
+ };
97
+ exports.default = FloatingToolbar;
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const FormatButtons: React.FC;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FormatButtons = void 0;
7
+ var react_1 = __importDefault(require("react"));
8
+ var ProductEditorContext_1 = require("../context/ProductEditorContext");
9
+ var FormatButtons = function () {
10
+ var _a = (0, ProductEditorContext_1.useProductEditor)(), selectedTextObject = _a.selectedTextObject, updateSelectedText = _a.updateSelectedText;
11
+ if (!selectedTextObject || !updateSelectedText)
12
+ return null;
13
+ var isBold = selectedTextObject.fontWeight === 'bold';
14
+ var isUnderlined = selectedTextObject.underline === true;
15
+ return (react_1.default.createElement(react_1.default.Fragment, null,
16
+ react_1.default.createElement("button", { type: "button", className: "toolbar-button bold-button ".concat(isBold ? 'active' : ''), onClick: function () { return updateSelectedText({ fontWeight: isBold ? 'normal' : 'bold' }); }, title: "Bold" },
17
+ react_1.default.createElement("strong", null, "B")),
18
+ react_1.default.createElement("button", { type: "button", className: "toolbar-button underline-button ".concat(isUnderlined ? 'active' : ''), onClick: function () { return updateSelectedText({ underline: !isUnderlined }); }, title: "Underline" },
19
+ react_1.default.createElement("strong", null,
20
+ react_1.default.createElement("u", null, "U")))));
21
+ };
22
+ exports.FormatButtons = FormatButtons;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface ImageNavButtonProps {
3
+ direction: 'left' | 'right';
4
+ onClick: () => void;
5
+ }
6
+ declare const ImageNavButton: ({ direction, onClick }: ImageNavButtonProps) => React.JSX.Element;
7
+ export default ImageNavButton;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ var react_1 = __importDefault(require("react"));
7
+ var grommet_icons_1 = require("grommet-icons");
8
+ var ImageNavButton = function (_a) {
9
+ var direction = _a.direction, onClick = _a.onClick;
10
+ return (react_1.default.createElement("button", { onClick: onClick, className: "nav-button ".concat(direction), "aria-label": direction === 'left' ? 'Previous image' : 'Next image', type: "button" }, direction === 'left'
11
+ ? react_1.default.createElement(grommet_icons_1.FormPrevious, { className: "nav-button-icon", color: "#6b7280" })
12
+ : react_1.default.createElement(grommet_icons_1.FormNext, { className: "nav-button-icon", color: "#6b7280" })));
13
+ };
14
+ exports.default = ImageNavButton;
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ interface ImageZoomModalProps {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ imageUrl: string;
6
+ productName: string;
7
+ totalImages: number;
8
+ currentIndex: number;
9
+ allImages: any[];
10
+ }
11
+ declare const ImageZoomModal: ({ isOpen, onClose, imageUrl, productName, totalImages, currentIndex, allImages }: ImageZoomModalProps) => React.ReactPortal | null;
12
+ export default ImageZoomModal;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ var react_1 = __importStar(require("react"));
38
+ var react_dom_1 = require("react-dom");
39
+ var grommet_icons_1 = require("grommet-icons");
40
+ var framer_motion_1 = require("framer-motion");
41
+ var useSwipeNavigate_1 = require("../hooks/useSwipeNavigate");
42
+ var MODAL_BODY_CLASS = 'has-preview-modal-open';
43
+ var ImageZoomModal = function (_a) {
44
+ var isOpen = _a.isOpen, onClose = _a.onClose, imageUrl = _a.imageUrl, productName = _a.productName, totalImages = _a.totalImages, currentIndex = _a.currentIndex, allImages = _a.allImages;
45
+ var _b = (0, react_1.useState)(currentIndex), modalImageIndex = _b[0], setModalImageIndex = _b[1];
46
+ var _c = (0, react_1.useState)(imageUrl), currentImageUrl = _c[0], setCurrentImageUrl = _c[1];
47
+ var _d = (0, react_1.useState)('right'), slideDirection = _d[0], setSlideDirection = _d[1];
48
+ (0, react_1.useEffect)(function () {
49
+ if (isOpen) {
50
+ document.body.style.overflow = 'hidden';
51
+ document.body.classList.add(MODAL_BODY_CLASS);
52
+ document.documentElement.classList.add(MODAL_BODY_CLASS);
53
+ }
54
+ else {
55
+ document.body.style.overflow = '';
56
+ document.body.classList.remove(MODAL_BODY_CLASS);
57
+ document.documentElement.classList.remove(MODAL_BODY_CLASS);
58
+ }
59
+ return function () {
60
+ document.body.style.overflow = '';
61
+ document.body.classList.remove(MODAL_BODY_CLASS);
62
+ document.documentElement.classList.remove(MODAL_BODY_CLASS);
63
+ };
64
+ }, [isOpen]);
65
+ (0, react_1.useEffect)(function () {
66
+ setModalImageIndex(currentIndex);
67
+ setCurrentImageUrl(imageUrl);
68
+ setSlideDirection('right');
69
+ }, [currentIndex, imageUrl, isOpen]);
70
+ var handlePrevious = function () {
71
+ setSlideDirection('left');
72
+ setModalImageIndex(function (prev) {
73
+ var newIndex = (prev - 1 + totalImages) % totalImages;
74
+ setCurrentImageUrl(allImages[newIndex].viewUrl);
75
+ return newIndex;
76
+ });
77
+ };
78
+ var handleNext = function () {
79
+ setSlideDirection('right');
80
+ setModalImageIndex(function (prev) {
81
+ var newIndex = (prev + 1) % totalImages;
82
+ setCurrentImageUrl(allImages[newIndex].viewUrl);
83
+ return newIndex;
84
+ });
85
+ };
86
+ var swipeHandlers = (0, useSwipeNavigate_1.useSwipeNavigate)({
87
+ onSwipeLeft: handleNext,
88
+ onSwipeRight: handlePrevious,
89
+ });
90
+ var slideVariants = {
91
+ enter: function (direction) { return ({
92
+ x: direction === 'right' ? 1000 : -1000,
93
+ opacity: 0
94
+ }); },
95
+ center: {
96
+ zIndex: 1,
97
+ x: 0,
98
+ opacity: 1,
99
+ transition: {
100
+ x: { type: "spring", stiffness: 300, damping: 30 },
101
+ opacity: { duration: 0.2 }
102
+ }
103
+ },
104
+ exit: function (direction) { return ({
105
+ zIndex: 0,
106
+ x: direction === 'left' ? 1000 : -1000,
107
+ opacity: 0,
108
+ transition: {
109
+ x: { type: "spring", stiffness: 300, damping: 30 },
110
+ opacity: { duration: 0.05 }
111
+ }
112
+ }); }
113
+ };
114
+ if (!isOpen)
115
+ return null;
116
+ // Render the modal directly at the document body level
117
+ return (0, react_dom_1.createPortal)(react_1.default.createElement("div", { className: "zoom-modal" },
118
+ react_1.default.createElement("div", { className: "zoom-modal-container" },
119
+ react_1.default.createElement("button", { onClick: onClose, className: "close-button", type: "button" },
120
+ react_1.default.createElement(grommet_icons_1.Close, { width: 36, height: 36, color: "white" })),
121
+ totalImages > 1 && (react_1.default.createElement("button", { onClick: handlePrevious, className: "nav-button-zoom left", type: "button" },
122
+ react_1.default.createElement(grommet_icons_1.FormPrevious, { width: 42, height: 42, color: "white" }))),
123
+ react_1.default.createElement("div", __assign({ className: "zoom-image-container" }, swipeHandlers),
124
+ react_1.default.createElement(framer_motion_1.AnimatePresence, { initial: false, custom: slideDirection },
125
+ react_1.default.createElement(framer_motion_1.motion.img, { key: modalImageIndex, src: currentImageUrl, alt: productName, className: "zoom-image", loading: "lazy", custom: slideDirection, variants: slideVariants, initial: "enter", animate: "center", exit: "exit" }))),
126
+ totalImages > 1 && (react_1.default.createElement("button", { onClick: handleNext, className: "nav-button-zoom right", type: "button" },
127
+ react_1.default.createElement(grommet_icons_1.FormNext, { width: 42, height: 42, color: "white" }))))), document.body);
128
+ };
129
+ exports.default = ImageZoomModal;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import '../styles/LayerPanel.css';
3
+ declare const LayerPanel: React.FC;
4
+ export default LayerPanel;