zuljaman-banner-components 1.0.23 → 1.0.24

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 (117) hide show
  1. package/README.md +228 -131
  2. package/dist/components/BannerRenderer/BannerRenderer.d.ts +8 -0
  3. package/dist/components/BannerRenderer/BannerRenderer.d.ts.map +1 -0
  4. package/dist/components/BannerRenderer/BannerRenderer.js +325 -0
  5. package/dist/components/BannerRenderer/components/CopyElement.d.ts +62 -0
  6. package/dist/components/BannerRenderer/components/CopyElement.d.ts.map +1 -0
  7. package/dist/components/BannerRenderer/components/CopyElement.js +220 -0
  8. package/dist/components/BannerRenderer/components/InteractiveLines.d.ts +26 -0
  9. package/dist/components/BannerRenderer/components/InteractiveLines.d.ts.map +1 -0
  10. package/dist/components/BannerRenderer/components/InteractiveLines.js +172 -0
  11. package/dist/components/BannerRenderer/components/LogoElement.d.ts +55 -0
  12. package/dist/components/BannerRenderer/components/LogoElement.d.ts.map +1 -0
  13. package/dist/components/BannerRenderer/components/LogoElement.js +53 -0
  14. package/dist/components/BannerRenderer/components/VisualGuides.d.ts +43 -0
  15. package/dist/components/BannerRenderer/components/VisualGuides.d.ts.map +1 -0
  16. package/dist/components/BannerRenderer/components/VisualGuides.js +110 -0
  17. package/dist/components/BannerRenderer/components/index.d.ts +12 -0
  18. package/dist/components/BannerRenderer/components/index.d.ts.map +1 -0
  19. package/dist/components/BannerRenderer/components/index.js +14 -0
  20. package/dist/components/BannerRenderer/constants.d.ts +15 -0
  21. package/dist/components/BannerRenderer/constants.d.ts.map +1 -0
  22. package/dist/components/BannerRenderer/constants.js +36 -0
  23. package/dist/components/BannerRenderer/hooks/index.d.ts +14 -0
  24. package/dist/components/BannerRenderer/hooks/index.d.ts.map +1 -0
  25. package/dist/components/BannerRenderer/hooks/index.js +16 -0
  26. package/dist/components/BannerRenderer/hooks/useAutoPositioning/debugPositions.d.ts +30 -0
  27. package/dist/components/BannerRenderer/hooks/useAutoPositioning/debugPositions.d.ts.map +1 -0
  28. package/dist/components/BannerRenderer/hooks/useAutoPositioning/debugPositions.js +87 -0
  29. package/dist/components/BannerRenderer/hooks/useAutoPositioning/index.d.ts +13 -0
  30. package/dist/components/BannerRenderer/hooks/useAutoPositioning/index.d.ts.map +1 -0
  31. package/dist/components/BannerRenderer/hooks/useAutoPositioning/index.js +21 -0
  32. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAnchorEdgeLocking.d.ts +71 -0
  33. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAnchorEdgeLocking.d.ts.map +1 -0
  34. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAnchorEdgeLocking.js +151 -0
  35. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAutoPositioningMain.d.ts +66 -0
  36. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAutoPositioningMain.d.ts.map +1 -0
  37. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useAutoPositioningMain.js +332 -0
  38. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.d.ts +61 -0
  39. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.d.ts.map +1 -0
  40. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useChainPositioning.js +180 -0
  41. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.d.ts +60 -0
  42. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.d.ts.map +1 -0
  43. package/dist/components/BannerRenderer/hooks/useAutoPositioning/useHeightCompensation.js +178 -0
  44. package/dist/components/BannerRenderer/hooks/useAutoPositioning.d.ts +8 -0
  45. package/dist/components/BannerRenderer/hooks/useAutoPositioning.d.ts.map +1 -0
  46. package/dist/components/BannerRenderer/hooks/useAutoPositioning.js +12 -0
  47. package/dist/components/BannerRenderer/hooks/useDragSnap.d.ts +30 -0
  48. package/dist/components/BannerRenderer/hooks/useDragSnap.d.ts.map +1 -0
  49. package/dist/components/BannerRenderer/hooks/useDragSnap.js +90 -0
  50. package/dist/components/BannerRenderer/hooks/useElementRefs.d.ts +22 -0
  51. package/dist/components/BannerRenderer/hooks/useElementRefs.d.ts.map +1 -0
  52. package/dist/components/BannerRenderer/hooks/useElementRefs.js +70 -0
  53. package/dist/components/BannerRenderer/hooks/useLineDrawing.d.ts +40 -0
  54. package/dist/components/BannerRenderer/hooks/useLineDrawing.d.ts.map +1 -0
  55. package/dist/components/BannerRenderer/hooks/useLineDrawing.js +198 -0
  56. package/dist/components/BannerRenderer/hooks/useProximityDetection.d.ts +43 -0
  57. package/dist/components/BannerRenderer/hooks/useProximityDetection.d.ts.map +1 -0
  58. package/dist/components/BannerRenderer/hooks/useProximityDetection.js +491 -0
  59. package/dist/components/BannerRenderer/index.d.ts +6 -0
  60. package/dist/components/BannerRenderer/index.d.ts.map +1 -0
  61. package/dist/components/BannerRenderer/index.js +8 -0
  62. package/dist/components/{BannerRenderer.d.ts → BannerRenderer/types.d.ts} +29 -8
  63. package/dist/components/BannerRenderer/types.d.ts.map +1 -0
  64. package/dist/components/BannerRenderer/types.js +5 -0
  65. package/dist/components/BannerRenderer/utils/alignmentUtils.d.ts +38 -0
  66. package/dist/components/BannerRenderer/utils/alignmentUtils.d.ts.map +1 -0
  67. package/dist/components/BannerRenderer/utils/alignmentUtils.js +138 -0
  68. package/dist/components/BannerRenderer/utils/elementCheckUtils.d.ts +22 -0
  69. package/dist/components/BannerRenderer/utils/elementCheckUtils.d.ts.map +1 -0
  70. package/dist/components/BannerRenderer/utils/elementCheckUtils.js +37 -0
  71. package/dist/components/BannerRenderer/utils/elementIdUtils.d.ts +25 -0
  72. package/dist/components/BannerRenderer/utils/elementIdUtils.d.ts.map +1 -0
  73. package/dist/components/BannerRenderer/utils/elementIdUtils.js +56 -0
  74. package/dist/components/BannerRenderer/utils/fontUtils.d.ts +12 -0
  75. package/dist/components/BannerRenderer/utils/fontUtils.d.ts.map +1 -0
  76. package/dist/components/BannerRenderer/utils/fontUtils.js +26 -0
  77. package/dist/components/BannerRenderer/utils/graphUtils.d.ts +54 -0
  78. package/dist/components/BannerRenderer/utils/graphUtils.d.ts.map +1 -0
  79. package/dist/components/BannerRenderer/utils/graphUtils.js +138 -0
  80. package/dist/components/BannerRenderer/utils/index.d.ts +12 -0
  81. package/dist/components/BannerRenderer/utils/index.d.ts.map +1 -0
  82. package/dist/components/BannerRenderer/utils/index.js +27 -0
  83. package/dist/components/BannerRenderer/utils/snapUtils.d.ts +38 -0
  84. package/dist/components/BannerRenderer/utils/snapUtils.d.ts.map +1 -0
  85. package/dist/components/BannerRenderer/utils/snapUtils.js +109 -0
  86. package/dist/components/BannerVisor.d.ts.map +1 -1
  87. package/dist/components/BannerVisor.js +8 -1
  88. package/dist/components/index.js +1 -1
  89. package/dist/components/shared/DraggableElement.d.ts +11 -0
  90. package/dist/components/shared/DraggableElement.d.ts.map +1 -1
  91. package/dist/components/shared/DraggableElement.js +47 -51
  92. package/dist/components/styles/Style1/substyleConfig.d.ts.map +1 -1
  93. package/dist/components/styles/Style1/substyleConfig.js +53 -54
  94. package/dist/components/styles/Style2/substyleConfig.js +78 -78
  95. package/dist/components/styles/Style3/substyleConfig.d.ts.map +1 -1
  96. package/dist/components/styles/Style3/substyleConfig.js +40 -37
  97. package/dist/components/styles/Style4/substyleConfig.js +57 -57
  98. package/dist/components/styles/types/substyleTypes.d.ts +32 -15
  99. package/dist/components/styles/types/substyleTypes.d.ts.map +1 -1
  100. package/dist/components/styles/utils/chainValidation.d.ts +41 -0
  101. package/dist/components/styles/utils/chainValidation.d.ts.map +1 -0
  102. package/dist/components/styles/utils/chainValidation.js +148 -0
  103. package/dist/components/styles/utils/positioningUtils.d.ts +207 -11
  104. package/dist/components/styles/utils/positioningUtils.d.ts.map +1 -1
  105. package/dist/components/styles/utils/positioningUtils.js +520 -19
  106. package/dist/constants/characterLimits.d.ts +4 -16
  107. package/dist/constants/characterLimits.d.ts.map +1 -1
  108. package/dist/constants/characterLimits.js +28 -26
  109. package/dist/constants/styleConfigs.js +6 -6
  110. package/dist/styleConfig.d.ts +4 -4
  111. package/dist/styleConfig.d.ts.map +1 -1
  112. package/dist/styleConfig.js +8 -16
  113. package/dist/types.d.ts +42 -0
  114. package/dist/types.d.ts.map +1 -1
  115. package/package.json +1 -1
  116. package/dist/components/BannerRenderer.d.ts.map +0 -1
  117. package/dist/components/BannerRenderer.js +0 -559
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ /**
3
+ * BannerRenderer component - Generic platform-agnostic banner renderer
4
+ * Unified component that handles all banner styles based on configuration
5
+ */
6
+ "use client";
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.BannerRenderer = void 0;
45
+ const jsx_runtime_1 = require("react/jsx-runtime");
46
+ const react_1 = __importStar(require("react"));
47
+ const clsx_1 = __importDefault(require("clsx"));
48
+ const shadowUtils_1 = require("../../styles/shadowUtils");
49
+ const styleConfig_1 = require("../../styleConfig");
50
+ const substyleConfig_1 = require("../styles/Style1/substyleConfig");
51
+ const substyleConfig_2 = require("../styles/Style2/substyleConfig");
52
+ const substyleConfig_3 = require("../styles/Style3/substyleConfig");
53
+ const substyleConfig_4 = require("../styles/Style4/substyleConfig");
54
+ const hooks_1 = require("./hooks");
55
+ const components_1 = require("./components");
56
+ // Note: marching ants animation is auto-injected by constants.ts import
57
+ const BannerRenderer = ({ bannerStyle, copies = [], bannerSubstyle = 1, logoUrl, sizeMultiplier = 1.0, logoSizeMultiplier = 1.0, overlayIntensityMultiplier: _overlayIntensityMultiplier = 1.0, shadowIntensityMultiplier = 1.0, backgroundImageUrl, postType, ImageComponent, logoTranslateX = 0, logoTranslateY = 0, draggableMode = false, creationMode = false, linkingModeActive = false, onDebugPositions, onApplyAutoPositioning, onConnectionsDeleted, onLineGapsChange, onLogoPositionChange, logoRotation = 0, onLogoRotationChange, logoWidth, onLogoWidthChange, onCopyPositionChange, onCopyRotationChange, onCopyWidthChange, textShadowEnabled, textShadowSize, textShadowIntensity, logoShadowEnabled, logoShadowSize, logoShadowIntensity, noiseEnabled, noiseIntensity = 1.0, fontFamily, autoPositioningOverride, }) => {
58
+ const [selectedElement, setSelectedElement] = (0, react_1.useState)(null);
59
+ // Element refs
60
+ const logoRef = (0, react_1.useRef)(null);
61
+ const copyRefs = (0, react_1.useRef)([]);
62
+ const bannerContainerRef = (0, react_1.useRef)(null);
63
+ // Element reference helpers from custom hook
64
+ const { getElementRef, areElementsOverlapping, areVerticallyAligned } = (0, hooks_1.useElementRefs)({
65
+ logoRef,
66
+ copyRefs,
67
+ copies,
68
+ });
69
+ // Drag snap and alignment helpers from custom hook
70
+ const { handleRotationSnap, handlePositionSnap, calculateAlignments } = (0, hooks_1.useDragSnap)({
71
+ logoRef,
72
+ copyRefs,
73
+ bannerContainerRef,
74
+ copies,
75
+ sizeMultiplier,
76
+ });
77
+ // Config key for auto-resetting hooks when style/substyle changes
78
+ // This allows hooks to self-reset without requiring consumer to use key prop
79
+ const configKey = `${bannerStyle}-${bannerSubstyle}`;
80
+ // Get autoPositioningConfig early (needed for useLineDrawing to initialize completedLines)
81
+ const autoPositioningConfigForLines = (0, react_1.useMemo)(() => {
82
+ let baseConfig;
83
+ switch (bannerStyle) {
84
+ case 2:
85
+ baseConfig = (0, substyleConfig_2.getSubstyleConfig)(bannerSubstyle);
86
+ break;
87
+ case 3:
88
+ baseConfig = (0, substyleConfig_3.getSubstyleConfig)(bannerSubstyle);
89
+ break;
90
+ case 4:
91
+ baseConfig = (0, substyleConfig_4.getSubstyleConfig)(bannerSubstyle);
92
+ break;
93
+ case 1:
94
+ default:
95
+ baseConfig = (0, substyleConfig_1.getSubstyleConfig)(bannerSubstyle);
96
+ }
97
+ // Apply override if provided
98
+ if (autoPositioningOverride === null || autoPositioningOverride === void 0 ? void 0 : autoPositioningOverride.config) {
99
+ return autoPositioningOverride.config;
100
+ }
101
+ return baseConfig.autoPositioningConfig;
102
+ }, [bannerStyle, bannerSubstyle, autoPositioningOverride]);
103
+ // Line drawing state and handlers from custom hook
104
+ const { drawingFrom, drawingMousePos, completedLines, manualLines, setCompletedLines, setManualLines, handleToggleAutoPositioning, handleMouseMove, handleContainerClick, handleCompleteLineTo, convertToStyleSlotId, } = (0, hooks_1.useLineDrawing)({
105
+ bannerContainerRef,
106
+ copies,
107
+ sizeMultiplier,
108
+ creationMode,
109
+ areElementsOverlapping,
110
+ areVerticallyAligned,
111
+ autoPositioningConfig: autoPositioningConfigForLines,
112
+ configKey,
113
+ });
114
+ // Live drag position for guide lines
115
+ const [liveDragPosition, setLiveDragPosition] = (0, react_1.useState)(null);
116
+ // Track if currently dragging
117
+ const [isDragging, setIsDragging] = (0, react_1.useState)(false);
118
+ // Track which line is being hovered
119
+ const [hoveredLine, setHoveredLine] = (0, react_1.useState)(null);
120
+ // Store gap values for each line (preserved when link is created)
121
+ const [lineGaps, setLineGaps] = (0, react_1.useState)(new Map());
122
+ // Expose lineGaps to parent via callback (using ref to avoid infinite loops)
123
+ const prevLineGapsRef = (0, react_1.useRef)('');
124
+ (0, react_1.useEffect)(() => {
125
+ if (onLineGapsChange && lineGaps.size > 0) {
126
+ const gapsObject = {};
127
+ lineGaps.forEach((value, key) => {
128
+ gapsObject[key] = value;
129
+ });
130
+ // Only call callback if gaps actually changed
131
+ const gapsJson = JSON.stringify(gapsObject);
132
+ if (gapsJson !== prevLineGapsRef.current) {
133
+ prevLineGapsRef.current = gapsJson;
134
+ onLineGapsChange(gapsObject);
135
+ }
136
+ }
137
+ }, [lineGaps, onLineGapsChange]);
138
+ // Proximity detection from custom hook
139
+ const { proximityLines, setProximityLines, detectProximityLines, calculateProximityLines, } = (0, hooks_1.useProximityDetection)({
140
+ logoRef,
141
+ copyRefs,
142
+ bannerContainerRef,
143
+ copies,
144
+ sizeMultiplier,
145
+ creationMode,
146
+ manualLines,
147
+ completedLines,
148
+ setCompletedLines,
149
+ setLineGaps,
150
+ getElementRef,
151
+ });
152
+ // Alignment lines when dragging
153
+ const [alignmentLines, setAlignmentLines] = (0, react_1.useState)([]);
154
+ // Get substyle configuration based on bannerStyle (needed before useAutoPositioning)
155
+ const config = (0, react_1.useMemo)(() => {
156
+ var _a, _b;
157
+ let baseConfig;
158
+ switch (bannerStyle) {
159
+ case 2:
160
+ baseConfig = (0, substyleConfig_2.getSubstyleConfig)(bannerSubstyle);
161
+ break;
162
+ case 3:
163
+ baseConfig = (0, substyleConfig_3.getSubstyleConfig)(bannerSubstyle);
164
+ break;
165
+ case 4:
166
+ baseConfig = (0, substyleConfig_4.getSubstyleConfig)(bannerSubstyle);
167
+ break;
168
+ case 1:
169
+ default:
170
+ baseConfig = (0, substyleConfig_1.getSubstyleConfig)(bannerSubstyle);
171
+ }
172
+ // Apply auto-positioning override if provided
173
+ if (autoPositioningOverride) {
174
+ const result = {
175
+ ...baseConfig,
176
+ autoPositioning: autoPositioningOverride.enabled,
177
+ autoPositioningConfig: autoPositioningOverride.config,
178
+ gapRem: (_a = autoPositioningOverride.gapRem) !== null && _a !== void 0 ? _a : baseConfig.gapRem,
179
+ copies: (_b = autoPositioningOverride.copies) !== null && _b !== void 0 ? _b : baseConfig.copies,
180
+ };
181
+ return result;
182
+ }
183
+ return baseConfig;
184
+ }, [bannerStyle, bannerSubstyle, autoPositioningOverride]);
185
+ // Get style configuration
186
+ const styleConfig = (0, styleConfig_1.getStyleConfig)(bannerStyle);
187
+ // Y-axis multiplier for STORY format (to spread elements vertically)
188
+ const yMultiplier = postType === "STORY" ? 1.42 : 1.0; // 1920/1350 ≈ 1.42
189
+ // Auto-positioning state and calculations from custom hook
190
+ const { autoOffsets, anchorCompensation, lineRecalcTrigger, derivedAutoConfig, debugPositions, applyAutoPositioningConfig, } = (0, hooks_1.useAutoPositioning)({
191
+ logoRef,
192
+ copyRefs,
193
+ bannerContainerRef,
194
+ copies,
195
+ sizeMultiplier,
196
+ creationMode,
197
+ completedLines,
198
+ manualLines,
199
+ lineGaps,
200
+ setCompletedLines,
201
+ setManualLines,
202
+ setLineGaps,
203
+ config,
204
+ autoPositioningOverride,
205
+ logoTranslateX,
206
+ logoTranslateY,
207
+ logoRotation,
208
+ logoWidth,
209
+ bannerStyle,
210
+ onApplyAutoPositioning,
211
+ onCopyPositionChange,
212
+ onConnectionsDeleted,
213
+ exposeDebugFunctions: !!onDebugPositions,
214
+ areElementsOverlapping,
215
+ areVerticallyAligned,
216
+ configKey,
217
+ });
218
+ // Shadow calculations using style configuration
219
+ const { shadowStyle, logoShadowStyle } = (0, react_1.useMemo)(() => {
220
+ var _a, _b;
221
+ // Get shadow defaults from style config (or use fallback values)
222
+ const textShadowConfig = ((_a = styleConfig.shadows) === null || _a === void 0 ? void 0 : _a.textShadow) || {
223
+ enabled: true,
224
+ size: 70,
225
+ intensity: 0.85,
226
+ direction: 'bottom',
227
+ };
228
+ const logoShadowConfig = ((_b = styleConfig.shadows) === null || _b === void 0 ? void 0 : _b.logoShadow) || {
229
+ enabled: true,
230
+ size: 150,
231
+ intensity: 0.375,
232
+ direction: 'top-left',
233
+ };
234
+ // Use prop overrides if provided, otherwise use config values
235
+ const effectiveTextShadowEnabled = textShadowEnabled !== null && textShadowEnabled !== void 0 ? textShadowEnabled : textShadowConfig.enabled;
236
+ const effectiveTextShadowSize = textShadowSize !== null && textShadowSize !== void 0 ? textShadowSize : textShadowConfig.size;
237
+ const effectiveTextShadowIntensity = textShadowIntensity !== null && textShadowIntensity !== void 0 ? textShadowIntensity : textShadowConfig.intensity;
238
+ const effectiveLogoShadowEnabled = logoShadowEnabled !== null && logoShadowEnabled !== void 0 ? logoShadowEnabled : logoShadowConfig.enabled;
239
+ const effectiveLogoShadowSize = logoShadowSize !== null && logoShadowSize !== void 0 ? logoShadowSize : logoShadowConfig.size;
240
+ const effectiveLogoShadowIntensity = logoShadowIntensity !== null && logoShadowIntensity !== void 0 ? logoShadowIntensity : logoShadowConfig.intensity;
241
+ // Generate shadow styles only if enabled
242
+ const shadowStyle = effectiveTextShadowEnabled
243
+ ? (0, shadowUtils_1.createAbstractShadowStyle)({
244
+ direction: textShadowConfig.direction,
245
+ size: `${effectiveTextShadowSize}%`,
246
+ intensity: effectiveTextShadowIntensity * shadowIntensityMultiplier,
247
+ })
248
+ : {};
249
+ const logoShadowStyle = effectiveLogoShadowEnabled
250
+ ? (0, shadowUtils_1.createAbstractShadowStyle)({
251
+ direction: logoShadowConfig.direction,
252
+ size: `${effectiveLogoShadowSize}%`,
253
+ intensity: effectiveLogoShadowIntensity * shadowIntensityMultiplier,
254
+ })
255
+ : {};
256
+ return { shadowStyle, logoShadowStyle };
257
+ }, [
258
+ styleConfig.shadows,
259
+ textShadowEnabled,
260
+ textShadowSize,
261
+ textShadowIntensity,
262
+ logoShadowEnabled,
263
+ logoShadowSize,
264
+ logoShadowIntensity,
265
+ shadowIntensityMultiplier,
266
+ ]);
267
+ // ===== DETAILED POSITIONING DEBUG LOGGING =====
268
+ react_1.default.useEffect(() => {
269
+ // Log detailed positioning information for all copies
270
+ const positioningDetails = copies.map((copy, idx) => {
271
+ var _a, _b, _c;
272
+ const styleSlot = copy.styleSlot !== undefined ? copy.styleSlot : idx;
273
+ const copyConfig = ((_a = config.copies) === null || _a === void 0 ? void 0 : _a[styleSlot]) || ((_b = config.copies) === null || _b === void 0 ? void 0 : _b[0]);
274
+ const configY = (_c = copyConfig === null || copyConfig === void 0 ? void 0 : copyConfig.y) !== null && _c !== void 0 ? _c : 0;
275
+ const autoOffset = autoOffsets[styleSlot] || 0;
276
+ const baseY = configY * yMultiplier + autoOffset;
277
+ const effectiveTranslateY = copy.translateY || 0;
278
+ const finalY = baseY + effectiveTranslateY;
279
+ return {
280
+ index: idx,
281
+ styleSlot,
282
+ text: copy.text.substring(0, 30) + '...',
283
+ configY,
284
+ yMultiplier,
285
+ autoOffset,
286
+ baseY,
287
+ translateY: copy.translateY,
288
+ effectiveTranslateY,
289
+ finalY: Math.round(finalY),
290
+ translateX: copy.translateX,
291
+ };
292
+ });
293
+ console.log('📐 POSITIONING DETAILS:', Date.now(), JSON.stringify(positioningDetails, null, 2));
294
+ }, [copies, autoOffsets, config, yMultiplier]);
295
+ // Shared logo component (extracted to LogoElement)
296
+ const logoComponent = ((0, jsx_runtime_1.jsx)(components_1.LogoElement, { logoTranslateX: logoTranslateX, logoTranslateY: logoTranslateY, logoRotation: logoRotation, logoWidth: logoWidth, logoUrl: logoUrl, logoSizeMultiplier: logoSizeMultiplier, sizeMultiplier: sizeMultiplier, draggableMode: draggableMode, creationMode: creationMode, linkingModeActive: linkingModeActive, selectedElement: selectedElement, completedLines: completedLines, drawingFrom: drawingFrom, logoRef: logoRef, bannerContainerRef: bannerContainerRef, ImageComponent: ImageComponent, onLogoPositionChange: onLogoPositionChange, onLogoRotationChange: onLogoRotationChange, onLogoWidthChange: onLogoWidthChange, setSelectedElement: setSelectedElement, setIsDragging: setIsDragging, setLiveDragPosition: setLiveDragPosition, setAlignmentLines: setAlignmentLines, setProximityLines: setProximityLines, handleToggleAutoPositioning: handleToggleAutoPositioning, handleCompleteLineTo: handleCompleteLineTo, handleRotationSnap: handleRotationSnap, handlePositionSnap: handlePositionSnap, calculateAlignments: calculateAlignments, calculateProximityLines: calculateProximityLines, detectProximityLines: detectProximityLines }));
297
+ return ((0, jsx_runtime_1.jsx)("div", { className: "w-full h-full text-white leading-[1.05]", children: (0, jsx_runtime_1.jsxs)("div", { ref: bannerContainerRef, className: (0, clsx_1.default)("inset-0 flex flex-col items-start h-full relative isolate overflow-hidden gap-4 px-20 banner-container-bg", postType === "STORY" ? "pb-32 pt-44" : "py-20"), onMouseMove: handleMouseMove, onClick: handleContainerClick, children: [backgroundImageUrl && ((0, jsx_runtime_1.jsx)(ImageComponent, { src: backgroundImageUrl, alt: "Background", width: 1080, height: postType === "STORY" ? 1920 : 1350, className: "object-cover absolute inset-0 w-full h-full -z-10", style: { objectFit: "cover", pointerEvents: "none" } })), noiseEnabled && ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 pointer-events-none", style: {
298
+ opacity: Math.min(noiseIntensity / 2, 1), // Normalize 0-2 range to 0-1
299
+ mixBlendMode: 'overlay',
300
+ backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
301
+ zIndex: 5,
302
+ } })), (0, jsx_runtime_1.jsx)("div", { style: shadowStyle, className: "relative z-10", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("div", { style: logoShadowStyle, className: "relative z-10", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute z-20", style: {
303
+ top: '50%',
304
+ left: '50%',
305
+ transform: `translate(${config.logo.x}px, ${config.logo.y * yMultiplier}px)`,
306
+ }, children: logoComponent }), creationMode && (completedLines.size > 0 || drawingFrom) && ((0, jsx_runtime_1.jsx)(components_1.InteractiveLines, { copies: copies, completedLines: completedLines, drawingFrom: drawingFrom, drawingMousePos: drawingMousePos, hoveredLine: hoveredLine, lineRecalcTrigger: lineRecalcTrigger, sizeMultiplier: sizeMultiplier, logoRef: logoRef, copyRefs: copyRefs, bannerContainerRef: bannerContainerRef, setCompletedLines: setCompletedLines, setManualLines: setManualLines, setHoveredLine: setHoveredLine })), (0, jsx_runtime_1.jsx)(components_1.VisualGuides, { draggableMode: draggableMode, isDragging: isDragging, selectedElement: selectedElement, alignmentLines: alignmentLines, proximityLines: proximityLines, liveDragPosition: liveDragPosition, logoTranslateX: logoTranslateX, logoTranslateY: logoTranslateY, copies: copies, config: config, autoOffsets: autoOffsets, yMultiplier: yMultiplier }), (0, jsx_runtime_1.jsx)("div", { className: "absolute pointer-events-none", style: {
307
+ top: '50%',
308
+ left: '50%',
309
+ width: 0,
310
+ height: 0,
311
+ zIndex: 10,
312
+ }, children: copies.map((copy, copyIndex) => {
313
+ var _a, _b;
314
+ // Get width from config for the wrapper
315
+ const styleSlot = copy.styleSlot !== undefined ? copy.styleSlot : copyIndex;
316
+ const copyConfig = ((_a = config.copies) === null || _a === void 0 ? void 0 : _a[styleSlot]) || ((_b = config.copies) === null || _b === void 0 ? void 0 : _b[0]);
317
+ const configWidth = copy.width || (copyConfig === null || copyConfig === void 0 ? void 0 : copyConfig.width);
318
+ return ((0, jsx_runtime_1.jsx)("div", { className: "pointer-events-auto absolute", style: {
319
+ top: '50%',
320
+ left: '50%',
321
+ width: configWidth,
322
+ }, children: (0, jsx_runtime_1.jsx)(components_1.CopyElement, { copy: copy, copyIndex: copyIndex, copies: copies, config: config, styleConfig: styleConfig, yMultiplier: yMultiplier, autoOffsets: autoOffsets, sizeMultiplier: sizeMultiplier, fontFamily: fontFamily, draggableMode: draggableMode, creationMode: creationMode, linkingModeActive: linkingModeActive, selectedElement: selectedElement, completedLines: completedLines, drawingFrom: drawingFrom, copyRefs: copyRefs, bannerContainerRef: bannerContainerRef, onCopyPositionChange: onCopyPositionChange, onCopyRotationChange: onCopyRotationChange, onCopyWidthChange: onCopyWidthChange, setSelectedElement: setSelectedElement, setIsDragging: setIsDragging, setLiveDragPosition: setLiveDragPosition, setAlignmentLines: setAlignmentLines, setProximityLines: setProximityLines, handleToggleAutoPositioning: handleToggleAutoPositioning, handleCompleteLineTo: handleCompleteLineTo, handleRotationSnap: handleRotationSnap, handlePositionSnap: handlePositionSnap, calculateAlignments: calculateAlignments, calculateProximityLines: calculateProximityLines, detectProximityLines: detectProximityLines }) }, copy.id));
323
+ }) })] }) }));
324
+ };
325
+ exports.BannerRenderer = BannerRenderer;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * CopyElement component
3
+ * Renders a single copy/text element with drag, rotation, and resize capabilities
4
+ */
5
+ import React from 'react';
6
+ import type { CopyConfig } from '../../../types';
7
+ import type { CopyElementConfig } from '../../styles/types/substyleTypes';
8
+ import type { AlignmentLine } from '../utils';
9
+ export interface CopyElementProps {
10
+ copy: CopyConfig;
11
+ copyIndex: number;
12
+ copies: CopyConfig[];
13
+ config: {
14
+ copies?: CopyElementConfig[];
15
+ };
16
+ styleConfig: {
17
+ copyFontWeights?: Record<number, string>;
18
+ fontFamily?: string;
19
+ };
20
+ yMultiplier: number;
21
+ autoOffsets: Record<number, number>;
22
+ sizeMultiplier: number;
23
+ fontFamily?: string;
24
+ draggableMode: boolean;
25
+ creationMode: boolean;
26
+ linkingModeActive: boolean;
27
+ selectedElement: string | null;
28
+ completedLines: Map<string, string>;
29
+ drawingFrom: string | null;
30
+ copyRefs: React.MutableRefObject<(HTMLDivElement | null)[]>;
31
+ bannerContainerRef: React.RefObject<HTMLDivElement | null>;
32
+ onCopyPositionChange?: (copyIndex: number, pos: {
33
+ x: number;
34
+ y: number;
35
+ }) => void;
36
+ onCopyRotationChange?: (copyIndex: number, rotation: number) => void;
37
+ onCopyWidthChange?: (copyIndex: number, width: number) => void;
38
+ setSelectedElement: (id: string | null) => void;
39
+ setIsDragging: (dragging: boolean) => void;
40
+ setLiveDragPosition: (pos: {
41
+ elementId: string;
42
+ x: number;
43
+ y: number;
44
+ } | null) => void;
45
+ setAlignmentLines: (lines: AlignmentLine[]) => void;
46
+ setProximityLines: (lines: any[]) => void;
47
+ handleToggleAutoPositioning: (elementId: string) => void;
48
+ handleCompleteLineTo: (targetId: string) => void;
49
+ handleRotationSnap: (angle: number) => number;
50
+ handlePositionSnap: (bounds: DOMRect, position: {
51
+ x: number;
52
+ y: number;
53
+ }) => {
54
+ x: number;
55
+ y: number;
56
+ };
57
+ calculateAlignments: (elementId: string) => AlignmentLine[];
58
+ calculateProximityLines: (elementId: string) => void;
59
+ detectProximityLines: (elementId: string) => void;
60
+ }
61
+ export declare const CopyElement: React.NamedExoticComponent<CopyElementProps>;
62
+ //# sourceMappingURL=CopyElement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyElement.d.ts","sourceRoot":"","sources":["../../../../src/components/BannerRenderer/components/CopyElement.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,220 @@
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.CopyElement = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ /**
9
+ * CopyElement component
10
+ * Renders a single copy/text element with drag, rotation, and resize capabilities
11
+ */
12
+ const react_1 = __importDefault(require("react"));
13
+ const clsx_1 = __importDefault(require("clsx"));
14
+ const shared_1 = require("../../shared");
15
+ const defaults_1 = require("../../../constants/defaults");
16
+ const utils_1 = require("../utils");
17
+ const CopyElementComponent = ({ copy, copyIndex, copies, config, styleConfig, yMultiplier, autoOffsets, sizeMultiplier, fontFamily, draggableMode, creationMode, linkingModeActive, selectedElement, completedLines, drawingFrom, copyRefs, bannerContainerRef, onCopyPositionChange, onCopyRotationChange, onCopyWidthChange, setSelectedElement, setIsDragging, setLiveDragPosition, setAlignmentLines, setProximityLines, handleToggleAutoPositioning, handleCompleteLineTo, handleRotationSnap, handlePositionSnap, calculateAlignments, calculateProximityLines, detectProximityLines, }) => {
18
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19
+ // Use styleSlot to determine config (0 = first copy, 1 = second copy, etc.)
20
+ // Fallback to copyIndex for creation mode
21
+ const styleSlot = copy.styleSlot !== undefined ? copy.styleSlot : copyIndex;
22
+ // Wrapper to convert UUID-based targetId to styleSlot format before calling handleCompleteLineTo
23
+ // This is needed because DraggableElement passes elementId (UUID format) but completedLines uses styleSlot format
24
+ const handleCompleteLineToWithConversion = react_1.default.useCallback((targetId) => {
25
+ var _a;
26
+ // targetId comes as 'copy-{uuid}' from DraggableElement
27
+ // We need to find the copy with that UUID and get its styleSlot
28
+ if (targetId === 'logo') {
29
+ handleCompleteLineTo('logo');
30
+ return;
31
+ }
32
+ if (targetId.startsWith('copy-')) {
33
+ const suffix = targetId.substring(5); // Remove 'copy-' prefix
34
+ const targetCopy = copies.find(c => c.id === suffix);
35
+ const targetStyleSlot = (_a = targetCopy === null || targetCopy === void 0 ? void 0 : targetCopy.styleSlot) !== null && _a !== void 0 ? _a : copies.findIndex(c => c.id === suffix);
36
+ if (targetStyleSlot !== -1) {
37
+ handleCompleteLineTo(`copy-${targetStyleSlot}`);
38
+ }
39
+ else {
40
+ // Fallback: pass as-is (handleCompleteLineTo will try to convert)
41
+ handleCompleteLineTo(targetId);
42
+ }
43
+ }
44
+ else {
45
+ handleCompleteLineTo(targetId);
46
+ }
47
+ }, [copies, handleCompleteLineTo]);
48
+ // DYNAMIC: Check if this styleSlot has config (not limited to 0 and 1)
49
+ const isStyled = ((_a = config.copies) === null || _a === void 0 ? void 0 : _a[styleSlot]) !== undefined;
50
+ // Get copy config from copies array
51
+ const copyConfig = ((_b = config.copies) === null || _b === void 0 ? void 0 : _b[styleSlot]) || ((_c = config.copies) === null || _c === void 0 ? void 0 : _c[0]);
52
+ // Get config position and width
53
+ const configX = (_d = copyConfig === null || copyConfig === void 0 ? void 0 : copyConfig.x) !== null && _d !== void 0 ? _d : 0;
54
+ const configY = (_e = copyConfig === null || copyConfig === void 0 ? void 0 : copyConfig.y) !== null && _e !== void 0 ? _e : 0;
55
+ const configWidth = copyConfig === null || copyConfig === void 0 ? void 0 : copyConfig.width;
56
+ const autoOffset = autoOffsets[styleSlot] || 0;
57
+ // Get the font size multiplier from copy config (used for both font size and padding scaling)
58
+ const copyFontSizeMultiplier = copy.fontSize || 1.0;
59
+ // Calculate font size
60
+ // copy.fontSize is now an absolute multiplier, not relative to config
61
+ // Config fontSize values only set the initial copy.fontSize value
62
+ const effectiveFontSize = `${defaults_1.DEFAULT_BASE_FONT_SIZE_REM * copyFontSizeMultiplier}rem`;
63
+ // Get alignment - always prioritize copy's own align property
64
+ const effectiveAlign = copy.align || (isStyled && copyConfig ? copyConfig.alignment : 'center');
65
+ // Get font weight - use copy's fontWeight, then config default, then fallback
66
+ const effectiveFontWeight = copy.fontWeight || (isStyled ? (((_f = styleConfig.copyFontWeights) === null || _f === void 0 ? void 0 : _f[styleSlot]) || 'normal') : 'normal');
67
+ // Get font family - prioritize copy's own fontFamily, then global override, then style config
68
+ const effectiveCopyFontFamily = copy.fontFamily || fontFamily || styleConfig.fontFamily;
69
+ // Get color properties - prioritize copy's own properties, then substyle config, then defaults
70
+ const effectiveColor = copy.color || (isStyled && copyConfig ? copyConfig.color : undefined) || 'white';
71
+ const effectiveBgColor = copy.bgColor || (isStyled && copyConfig ? copyConfig.bgColor : undefined);
72
+ const effectiveHasBg = copy.hasBg !== undefined ? copy.hasBg : (isStyled && copyConfig ? copyConfig.hasBg : false);
73
+ // Get padding and scale it proportionally with font size to maintain visual consistency
74
+ const basePaddingX = copy.paddingX || (isStyled && copyConfig ? copyConfig.paddingX : undefined);
75
+ const basePaddingY = copy.paddingY || (isStyled && copyConfig ? copyConfig.paddingY : undefined);
76
+ const effectivePaddingX = basePaddingX
77
+ ? `${parseFloat(basePaddingX.replace('rem', '')) * copyFontSizeMultiplier}rem`
78
+ : undefined;
79
+ const effectivePaddingY = basePaddingY
80
+ ? `${parseFloat(basePaddingY.replace('rem', '')) * copyFontSizeMultiplier}rem`
81
+ : undefined;
82
+ const effectiveBorderRadius = copy.borderRadius || (isStyled && copyConfig ? copyConfig.borderRadius : undefined);
83
+ // Apply text decoration styles
84
+ const textDecorationStyles = [];
85
+ if (copy.underline)
86
+ textDecorationStyles.push('underline');
87
+ const textDecoration = textDecorationStyles.length > 0 ? textDecorationStyles.join(' ') : 'none';
88
+ // Apply font style
89
+ const fontStyle = copy.italic ? 'italic' : 'normal';
90
+ // Apply additional bold if textBold is true (combines with fontWeight)
91
+ const finalFontWeight = copy.textBold
92
+ ? 'bold'
93
+ : (0, utils_1.getFontWeight)(effectiveFontWeight);
94
+ // Text element
95
+ const textElement = ((0, jsx_runtime_1.jsx)("p", { style: {
96
+ fontSize: effectiveFontSize,
97
+ fontWeight: finalFontWeight,
98
+ fontFamily: effectiveCopyFontFamily,
99
+ color: effectiveColor,
100
+ textDecoration,
101
+ fontStyle,
102
+ }, className: (0, clsx_1.default)("whitespace-pre-wrap", `text-${effectiveAlign}`), children: copy.text }));
103
+ // Wrap with background if needed
104
+ const contentElement = effectiveHasBg ? ((0, jsx_runtime_1.jsx)("div", { style: {
105
+ backgroundColor: effectiveBgColor,
106
+ paddingLeft: effectivePaddingX,
107
+ paddingRight: effectivePaddingX,
108
+ paddingTop: effectivePaddingY,
109
+ paddingBottom: effectivePaddingY,
110
+ borderRadius: effectiveBorderRadius,
111
+ display: 'inline-block',
112
+ }, children: textElement })) : textElement;
113
+ // Calculate final position: config position + auto-offset + user adjustment
114
+ const baseX = isStyled ? configX : 0;
115
+ const baseY = isStyled ? (configY * yMultiplier + autoOffset) : 0;
116
+ // DYNAMIC: Consistent 'copy-N' format for all styleSlots
117
+ const elementId = `copy-${styleSlot}`;
118
+ // Plan v30: Check if this element is a TARGET in the chain
119
+ // TARGETs appear as values in completedLines (they are the "to" in "from -> to")
120
+ // When an element is a TARGET with autoOffset, ignore translateY because position is calculated by auto-positioning
121
+ const isTargetInChain = react_1.default.useMemo(() => {
122
+ // Check if this element appears as a target (value) in completedLines
123
+ for (const [, toId] of completedLines.entries()) {
124
+ if (toId === `copy-${styleSlot}`) {
125
+ return true;
126
+ }
127
+ }
128
+ return false;
129
+ }, [completedLines, styleSlot]);
130
+ // If element is TARGET in chain and has autoOffset, ignore translateY
131
+ // This ensures auto-positioning controls the Y position completely
132
+ const effectiveTranslateY = (isTargetInChain && autoOffset !== 0) ? 0 : (copy.translateY || 0);
133
+ const finalX = baseX + (copy.translateX || 0);
134
+ const finalY = baseY + effectiveTranslateY;
135
+ const finalWidth = copy.width || configWidth;
136
+ const finalRotation = (_g = copy.rotation) !== null && _g !== void 0 ? _g : (isStyled ? ((_h = copyConfig === null || copyConfig === void 0 ? void 0 : copyConfig.rotation) !== null && _h !== void 0 ? _h : 0) : 0);
137
+ // DEBUG: Log positioning only when final position changes (not on every render)
138
+ react_1.default.useEffect(() => {
139
+ console.log(`📍 Copy[${styleSlot}] position changed:`, {
140
+ finalX: Math.round(finalX),
141
+ finalY: Math.round(finalY),
142
+ baseY: Math.round(baseY),
143
+ autoOffset: Math.round(autoOffset),
144
+ translateY: Math.round(effectiveTranslateY),
145
+ });
146
+ }, [finalX, finalY, styleSlot, baseY, autoOffset, effectiveTranslateY]);
147
+ return ((0, jsx_runtime_1.jsx)(shared_1.DraggableElement, { enabled: draggableMode, position: { x: finalX, y: finalY }, rotation: finalRotation, width: finalWidth, scale: sizeMultiplier, elementId: `copy-${copy.id}`, isSelected: selectedElement === `copy-${copy.id}`, centerOrigin: true, creationMode: creationMode, linkingModeActive: linkingModeActive, onStartLinking: creationMode && linkingModeActive ? () => handleToggleAutoPositioning(`copy-${styleSlot}`) : undefined, onElementClickWhileDrawing: creationMode && drawingFrom ? handleCompleteLineToWithConversion : undefined, onPositionChange: (pos) => {
148
+ // Clear live drag position and dragging state when drag ends
149
+ setLiveDragPosition(null);
150
+ setIsDragging(false);
151
+ setAlignmentLines([]); // Clear alignment lines
152
+ setProximityLines([]); // Clear proximity lines
153
+ // Subtract base position to get only user adjustment
154
+ const adjustedPos = {
155
+ x: pos.x - baseX,
156
+ y: pos.y - baseY,
157
+ };
158
+ // Find index of this copy in the copies array to call the callback
159
+ const foundIndex = copies.findIndex(c => c.id === copy.id);
160
+ if (foundIndex !== -1) {
161
+ console.log('🖱️ COPY DRAG - copyIndex:', foundIndex, 'adjustedPos:', adjustedPos);
162
+ onCopyPositionChange === null || onCopyPositionChange === void 0 ? void 0 : onCopyPositionChange(foundIndex, adjustedPos);
163
+ }
164
+ }, onRotationChange: (rot) => {
165
+ const foundIndex = copies.findIndex(c => c.id === copy.id);
166
+ if (foundIndex !== -1) {
167
+ onCopyRotationChange === null || onCopyRotationChange === void 0 ? void 0 : onCopyRotationChange(foundIndex, rot);
168
+ }
169
+ }, onWidthChange: (w) => {
170
+ const foundIndex = copies.findIndex(c => c.id === copy.id);
171
+ if (foundIndex !== -1) {
172
+ onCopyWidthChange === null || onCopyWidthChange === void 0 ? void 0 : onCopyWidthChange(foundIndex, w);
173
+ }
174
+ }, onSelect: (id) => setSelectedElement(id), onDeselect: () => {
175
+ setSelectedElement(null);
176
+ setLiveDragPosition(null);
177
+ setProximityLines([]); // Clear proximity lines
178
+ }, onRotationSnap: handleRotationSnap, onPositionSnap: handlePositionSnap, onDragMove: (dragElementId, bounds) => {
179
+ // Set dragging state to true
180
+ setIsDragging(true);
181
+ // Calculate live position from element bounds
182
+ if (!bannerContainerRef.current)
183
+ return;
184
+ const bannerBounds = bannerContainerRef.current.getBoundingClientRect();
185
+ const elementCenterX = bounds.left + bounds.width / 2;
186
+ const elementCenterY = bounds.top + bounds.height / 2;
187
+ // Banner center in viewport coordinates
188
+ const bannerCenterX = bannerBounds.left + bannerBounds.width / 2;
189
+ const bannerCenterY = bannerBounds.top + bannerBounds.height / 2;
190
+ // Calculate offset from banner center (in scaled pixels)
191
+ const offsetX = (elementCenterX - bannerCenterX) / sizeMultiplier;
192
+ const offsetY = (elementCenterY - bannerCenterY) / sizeMultiplier;
193
+ // Store the live position
194
+ setLiveDragPosition({
195
+ elementId: dragElementId,
196
+ x: offsetX,
197
+ y: offsetY,
198
+ });
199
+ // Calculate alignment lines
200
+ const alignments = calculateAlignments(dragElementId);
201
+ setAlignmentLines(alignments);
202
+ // Calculate proximity lines (visual distance indicators)
203
+ calculateProximityLines(dragElementId);
204
+ // Detect proximity and auto-create lines (creation mode only)
205
+ detectProximityLines(dragElementId);
206
+ }, children: (0, jsx_runtime_1.jsx)("div", { ref: (el) => {
207
+ // Store ref by copy index in the copies array
208
+ const foundIndex = copies.findIndex(c => c.id === copy.id);
209
+ if (foundIndex !== -1) {
210
+ copyRefs.current[foundIndex] = el;
211
+ }
212
+ }, style: {
213
+ // Remove transform - DraggableElement already handles positioning and rotation
214
+ width: '100%',
215
+ // Hide copy if hidden property is true
216
+ display: copy.hidden ? 'none' : undefined,
217
+ }, children: contentElement }) }, copy.id));
218
+ };
219
+ // Memoize CopyElement to prevent unnecessary re-renders
220
+ exports.CopyElement = react_1.default.memo(CopyElementComponent);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * InteractiveLines component
3
+ * Renders the green dotted lines for auto-positioning connections between elements
4
+ */
5
+ import React from 'react';
6
+ import type { CopyConfig } from '../../../types';
7
+ export interface InteractiveLinesProps {
8
+ copies: CopyConfig[];
9
+ completedLines: Map<string, string>;
10
+ drawingFrom: string | null;
11
+ drawingMousePos: {
12
+ x: number;
13
+ y: number;
14
+ } | null;
15
+ hoveredLine: string | null;
16
+ lineRecalcTrigger: number;
17
+ sizeMultiplier: number;
18
+ logoRef: React.RefObject<HTMLDivElement | null>;
19
+ copyRefs: React.MutableRefObject<(HTMLDivElement | null)[]>;
20
+ bannerContainerRef: React.RefObject<HTMLDivElement | null>;
21
+ setCompletedLines: React.Dispatch<React.SetStateAction<Map<string, string>>>;
22
+ setManualLines: React.Dispatch<React.SetStateAction<Set<string>>>;
23
+ setHoveredLine: (lineId: string | null) => void;
24
+ }
25
+ export declare const InteractiveLines: React.FC<InteractiveLinesProps>;
26
+ //# sourceMappingURL=InteractiveLines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InteractiveLines.d.ts","sourceRoot":"","sources":["../../../../src/components/BannerRenderer/components/InteractiveLines.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IAEpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IAGvB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAG3D,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7E,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClE,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA4Q5D,CAAC"}