@akinon/next 2.0.0-beta.20 → 2.0.0-beta.22
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/CHANGELOG.md +25 -0
- package/api/auth.ts +292 -60
- package/bin/pz-install-plugins.js +1 -1
- package/package.json +3 -3
- package/types/index.ts +19 -6
- package/types/next-auth.d.ts +1 -1
- package/with-pz-config.js +8 -1
- package/components/theme-editor/blocks/accordion-block.tsx +0 -136
- package/components/theme-editor/blocks/block-renderer-registry.tsx +0 -77
- package/components/theme-editor/blocks/button-block.tsx +0 -593
- package/components/theme-editor/blocks/counter-block.tsx +0 -348
- package/components/theme-editor/blocks/divider-block.tsx +0 -20
- package/components/theme-editor/blocks/embed-block.tsx +0 -208
- package/components/theme-editor/blocks/group-block.tsx +0 -116
- package/components/theme-editor/blocks/hotspot-block.tsx +0 -147
- package/components/theme-editor/blocks/icon-block.tsx +0 -230
- package/components/theme-editor/blocks/image-block.tsx +0 -137
- package/components/theme-editor/blocks/image-gallery-block.tsx +0 -269
- package/components/theme-editor/blocks/input-block.tsx +0 -123
- package/components/theme-editor/blocks/link-block.tsx +0 -216
- package/components/theme-editor/blocks/lottie-block.tsx +0 -325
- package/components/theme-editor/blocks/map-block.tsx +0 -89
- package/components/theme-editor/blocks/slider-block.tsx +0 -595
- package/components/theme-editor/blocks/tab-block.tsx +0 -10
- package/components/theme-editor/blocks/text-block.tsx +0 -52
- package/components/theme-editor/blocks/video-block.tsx +0 -122
- package/components/theme-editor/components/action-toolbar.tsx +0 -305
- package/components/theme-editor/components/designer-overlay.tsx +0 -74
- package/components/theme-editor/components/with-designer-features.tsx +0 -142
- package/components/theme-editor/dynamic-font-loader.tsx +0 -79
- package/components/theme-editor/hooks/use-designer-features.tsx +0 -100
- package/components/theme-editor/hooks/use-external-designer.tsx +0 -95
- package/components/theme-editor/hooks/use-native-widget-data.ts +0 -188
- package/components/theme-editor/hooks/use-visibility-context.ts +0 -27
- package/components/theme-editor/placeholder-registry.ts +0 -31
- package/components/theme-editor/sections/before-after-section.tsx +0 -245
- package/components/theme-editor/sections/contact-form-section.tsx +0 -563
- package/components/theme-editor/sections/countdown-campaign-banner-section.tsx +0 -433
- package/components/theme-editor/sections/coupon-banner-section.tsx +0 -710
- package/components/theme-editor/sections/divider-section.tsx +0 -62
- package/components/theme-editor/sections/featured-product-spotlight-section.tsx +0 -507
- package/components/theme-editor/sections/find-in-store-section.tsx +0 -1995
- package/components/theme-editor/sections/hover-showcase-section.tsx +0 -326
- package/components/theme-editor/sections/image-hotspot-section.tsx +0 -142
- package/components/theme-editor/sections/installment-options-section.tsx +0 -1065
- package/components/theme-editor/sections/notification-banner-section.tsx +0 -173
- package/components/theme-editor/sections/order-tracking-lookup-section.tsx +0 -1379
- package/components/theme-editor/sections/posts-slider-section.tsx +0 -472
- package/components/theme-editor/sections/pre-order-launch-banner-section.tsx +0 -663
- package/components/theme-editor/sections/section-renderer-registry.tsx +0 -89
- package/components/theme-editor/sections/section-wrapper.tsx +0 -135
- package/components/theme-editor/sections/shipping-threshold-progress-section.tsx +0 -586
- package/components/theme-editor/sections/stats-counter-section.tsx +0 -486
- package/components/theme-editor/sections/tabs-section.tsx +0 -578
- package/components/theme-editor/theme-block.tsx +0 -102
- package/components/theme-editor/theme-placeholder-client.tsx +0 -218
- package/components/theme-editor/theme-placeholder-wrapper.tsx +0 -732
- package/components/theme-editor/theme-placeholder.tsx +0 -288
- package/components/theme-editor/theme-section.tsx +0 -1224
- package/components/theme-editor/theme-settings-context.tsx +0 -13
- package/components/theme-editor/utils/index.ts +0 -792
- package/components/theme-editor/utils/iterator-utils.ts +0 -234
- package/components/theme-editor/utils/publish-window.ts +0 -86
- package/components/theme-editor/utils/visibility-rules.ts +0 -188
|
@@ -1,586 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useMemo } from 'react';
|
|
4
|
-
import clsx from 'clsx';
|
|
5
|
-
import { twMerge } from 'tailwind-merge';
|
|
6
|
-
import { useGetBasketQuery } from '@akinon/next/data/client/basket';
|
|
7
|
-
|
|
8
|
-
import ThemeBlock, { Block } from '../theme-block';
|
|
9
|
-
import { Section } from '../theme-section';
|
|
10
|
-
import { useThemeSettingsContext } from '../theme-settings-context';
|
|
11
|
-
import {
|
|
12
|
-
getCSSStyles,
|
|
13
|
-
getResponsiveValue,
|
|
14
|
-
resolveThemeCssVariables
|
|
15
|
-
} from '../utils';
|
|
16
|
-
|
|
17
|
-
interface ShippingThresholdProgressSectionProps {
|
|
18
|
-
section: Section;
|
|
19
|
-
currentBreakpoint?: string;
|
|
20
|
-
placeholderId?: string;
|
|
21
|
-
isDesigner?: boolean;
|
|
22
|
-
selectedBlockId?: string | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const parseAmount = (value: unknown, fallback: number): number => {
|
|
26
|
-
if (value === undefined || value === null || value === '') return fallback;
|
|
27
|
-
|
|
28
|
-
if (typeof value === 'number') {
|
|
29
|
-
return Number.isFinite(value) ? value : fallback;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (typeof value !== 'string') return fallback;
|
|
33
|
-
|
|
34
|
-
const trimmed = value.trim();
|
|
35
|
-
if (!trimmed) return fallback;
|
|
36
|
-
|
|
37
|
-
const directParsed = Number(trimmed);
|
|
38
|
-
if (Number.isFinite(directParsed)) return directParsed;
|
|
39
|
-
|
|
40
|
-
let normalized = trimmed.replace(/[^\d.,-]/g, '');
|
|
41
|
-
if (!normalized) return fallback;
|
|
42
|
-
|
|
43
|
-
const hasComma = normalized.includes(',');
|
|
44
|
-
const hasDot = normalized.includes('.');
|
|
45
|
-
|
|
46
|
-
if (hasComma && hasDot) {
|
|
47
|
-
const lastComma = normalized.lastIndexOf(',');
|
|
48
|
-
const lastDot = normalized.lastIndexOf('.');
|
|
49
|
-
|
|
50
|
-
if (lastComma > lastDot) {
|
|
51
|
-
// Example: 1.234,56 -> 1234.56
|
|
52
|
-
normalized = normalized.replace(/\./g, '').replace(',', '.');
|
|
53
|
-
} else {
|
|
54
|
-
// Example: 1,234.56 -> 1234.56
|
|
55
|
-
normalized = normalized.replace(/,/g, '');
|
|
56
|
-
}
|
|
57
|
-
} else if (hasComma && !hasDot) {
|
|
58
|
-
const parts = normalized.split(',');
|
|
59
|
-
if (parts.length === 2 && parts[1].length <= 2) {
|
|
60
|
-
normalized = `${parts[0]}.${parts[1]}`;
|
|
61
|
-
} else {
|
|
62
|
-
normalized = normalized.replace(/,/g, '');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const parsed = Number(normalized);
|
|
67
|
-
return Number.isFinite(parsed) ? parsed : fallback;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const parseBoolean = (value: unknown, fallback: boolean): boolean => {
|
|
71
|
-
if (typeof value === 'boolean') return value;
|
|
72
|
-
if (typeof value === 'string') {
|
|
73
|
-
const normalized = value.trim().toLowerCase();
|
|
74
|
-
if (normalized === 'true') return true;
|
|
75
|
-
if (normalized === 'false') return false;
|
|
76
|
-
}
|
|
77
|
-
if (typeof value === 'number') return value !== 0;
|
|
78
|
-
return fallback;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const formatAmount = (value: number): string => {
|
|
82
|
-
const hasDecimal = Math.abs(value - Math.round(value)) >= 0.001;
|
|
83
|
-
|
|
84
|
-
return value.toLocaleString(undefined, {
|
|
85
|
-
minimumFractionDigits: hasDecimal ? 2 : 0,
|
|
86
|
-
maximumFractionDigits: hasDecimal ? 2 : 0
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const replaceTokens = (
|
|
91
|
-
input: string,
|
|
92
|
-
tokens: Record<string, string>
|
|
93
|
-
): string => {
|
|
94
|
-
let output = input;
|
|
95
|
-
|
|
96
|
-
Object.entries(tokens).forEach(([key, value]) => {
|
|
97
|
-
output = output.split(`{{${key}}}`).join(value);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
return output;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const replaceTokensInUnknown = (
|
|
104
|
-
value: unknown,
|
|
105
|
-
tokens: Record<string, string>
|
|
106
|
-
): unknown => {
|
|
107
|
-
if (typeof value === 'string') {
|
|
108
|
-
return replaceTokens(value, tokens);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (Array.isArray(value)) {
|
|
112
|
-
return value.map((item) => replaceTokensInUnknown(item, tokens));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (typeof value === 'object' && value !== null) {
|
|
116
|
-
return Object.fromEntries(
|
|
117
|
-
Object.entries(value).map(([key, nestedValue]) => [
|
|
118
|
-
key,
|
|
119
|
-
replaceTokensInUnknown(nestedValue, tokens)
|
|
120
|
-
])
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return value;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const toResponsiveStyle = (
|
|
128
|
-
existing: unknown,
|
|
129
|
-
nextValue: string
|
|
130
|
-
): Record<string, string> => {
|
|
131
|
-
if (typeof existing === 'object' && existing !== null && !Array.isArray(existing)) {
|
|
132
|
-
const existingObject = existing as Record<string, unknown>;
|
|
133
|
-
const responsiveKeys = Object.keys(existingObject).filter((key) =>
|
|
134
|
-
['desktop', 'tablet', 'mobile'].includes(key)
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
if (responsiveKeys.length > 0) {
|
|
138
|
-
return Object.fromEntries(responsiveKeys.map((key) => [key, nextValue]));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
desktop: nextValue,
|
|
144
|
-
tablet: nextValue,
|
|
145
|
-
mobile: nextValue
|
|
146
|
-
};
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const resolveResponsiveString = (
|
|
150
|
-
value: unknown,
|
|
151
|
-
breakpoint: string
|
|
152
|
-
): string => {
|
|
153
|
-
if (typeof value === 'string') return value;
|
|
154
|
-
|
|
155
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
156
|
-
const responsiveValue = getResponsiveValue(value, breakpoint, '');
|
|
157
|
-
if (typeof responsiveValue === 'string') return responsiveValue;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return '';
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const getShippingRole = (
|
|
164
|
-
block: Pick<Block, 'label' | 'properties'>,
|
|
165
|
-
breakpoint: string
|
|
166
|
-
): string => {
|
|
167
|
-
const roleFromProperties = resolveResponsiveString(
|
|
168
|
-
block.properties?.shippingRole,
|
|
169
|
-
breakpoint
|
|
170
|
-
);
|
|
171
|
-
const normalizedLabel = (block.label || '').toLowerCase();
|
|
172
|
-
|
|
173
|
-
return (
|
|
174
|
-
roleFromProperties ||
|
|
175
|
-
(normalizedLabel.includes('progress fill')
|
|
176
|
-
? 'progress-fill'
|
|
177
|
-
: normalizedLabel.includes('progress marker')
|
|
178
|
-
? 'progress-marker'
|
|
179
|
-
: normalizedLabel.includes('progress wrapper')
|
|
180
|
-
? 'progress-wrapper'
|
|
181
|
-
: normalizedLabel.includes('start label')
|
|
182
|
-
? 'start-label'
|
|
183
|
-
: '')
|
|
184
|
-
);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const findProgressBlockIds = (
|
|
188
|
-
blocks: Block[],
|
|
189
|
-
breakpoint: string
|
|
190
|
-
): {
|
|
191
|
-
fillBlockId: string | null;
|
|
192
|
-
markerBlockId: string | null;
|
|
193
|
-
wrapperBlockId: string | null;
|
|
194
|
-
startLabelBlockId: string | null;
|
|
195
|
-
} => {
|
|
196
|
-
let fillBlockId: string | null = null;
|
|
197
|
-
let markerBlockId: string | null = null;
|
|
198
|
-
let wrapperBlockId: string | null = null;
|
|
199
|
-
let startLabelBlockId: string | null = null;
|
|
200
|
-
|
|
201
|
-
const walk = (block: Block) => {
|
|
202
|
-
const role = getShippingRole(block, breakpoint);
|
|
203
|
-
|
|
204
|
-
if (role === 'progress-wrapper') {
|
|
205
|
-
wrapperBlockId = block.id;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (role === 'progress-fill') {
|
|
209
|
-
fillBlockId = block.id;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (role === 'progress-marker') {
|
|
213
|
-
markerBlockId = block.id;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (role === 'start-label') {
|
|
217
|
-
startLabelBlockId = block.id;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (block.blocks && block.blocks.length > 0) {
|
|
221
|
-
block.blocks.forEach(walk);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
blocks.forEach(walk);
|
|
226
|
-
|
|
227
|
-
return { fillBlockId, markerBlockId, wrapperBlockId, startLabelBlockId };
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const escapeCssAttributeValue = (value: string): string => {
|
|
231
|
-
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const injectDynamicState = (
|
|
235
|
-
blocks: Block[],
|
|
236
|
-
tokens: Record<string, string>,
|
|
237
|
-
currentBreakpoint: string,
|
|
238
|
-
progressPercent: number
|
|
239
|
-
): Block[] => {
|
|
240
|
-
const walk = (block: Block): Block => {
|
|
241
|
-
const cloned: Block = {
|
|
242
|
-
...block,
|
|
243
|
-
properties: block.properties ? { ...block.properties } : block.properties,
|
|
244
|
-
styles: block.styles ? { ...block.styles } : block.styles,
|
|
245
|
-
value: replaceTokensInUnknown(block.value, tokens) as Block['value']
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const role = getShippingRole(cloned, currentBreakpoint);
|
|
249
|
-
|
|
250
|
-
if (role === 'progress-fill') {
|
|
251
|
-
cloned.styles = {
|
|
252
|
-
...cloned.styles,
|
|
253
|
-
width: toResponsiveStyle(
|
|
254
|
-
cloned.styles?.width,
|
|
255
|
-
`${Math.max(0, Math.min(progressPercent, 100)).toFixed(2)}%`
|
|
256
|
-
)
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (cloned.blocks && cloned.blocks.length > 0) {
|
|
261
|
-
cloned.blocks = cloned.blocks.map(walk);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return cloned;
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
return blocks.map(walk);
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const ShippingThresholdProgressSection: React.FC<
|
|
271
|
-
ShippingThresholdProgressSectionProps
|
|
272
|
-
> = ({
|
|
273
|
-
section,
|
|
274
|
-
currentBreakpoint = 'desktop',
|
|
275
|
-
placeholderId = '',
|
|
276
|
-
isDesigner = false,
|
|
277
|
-
selectedBlockId = null
|
|
278
|
-
}) => {
|
|
279
|
-
const themeSettings = useThemeSettingsContext();
|
|
280
|
-
|
|
281
|
-
const sortedBlocks = useMemo(
|
|
282
|
-
() =>
|
|
283
|
-
[...(section.blocks || [])]
|
|
284
|
-
.sort((a, b) => (a.order || 0) - (b.order || 0))
|
|
285
|
-
.filter((block) => (isDesigner ? true : !block.hidden)),
|
|
286
|
-
[section.blocks, isDesigner]
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
const useLiveBasket = parseBoolean(
|
|
290
|
-
getResponsiveValue(section.properties?.['use-live-basket'], currentBreakpoint, true),
|
|
291
|
-
true
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
const { data: basketData } = useGetBasketQuery(undefined, {
|
|
295
|
-
skip: !useLiveBasket
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
const targetAmount = Math.max(
|
|
299
|
-
parseAmount(
|
|
300
|
-
getResponsiveValue(section.properties?.['target-amount'], currentBreakpoint, 1000),
|
|
301
|
-
1000
|
|
302
|
-
),
|
|
303
|
-
1
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
const fallbackCurrentAmount = Math.max(
|
|
307
|
-
parseAmount(
|
|
308
|
-
getResponsiveValue(
|
|
309
|
-
section.properties?.['fallback-current-amount'],
|
|
310
|
-
currentBreakpoint,
|
|
311
|
-
760
|
|
312
|
-
),
|
|
313
|
-
760
|
|
314
|
-
),
|
|
315
|
-
0
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
const liveBasketAmount = parseAmount(basketData?.total_amount, Number.NaN);
|
|
319
|
-
const currentAmount =
|
|
320
|
-
useLiveBasket && Number.isFinite(liveBasketAmount)
|
|
321
|
-
? Math.max(0, liveBasketAmount)
|
|
322
|
-
: fallbackCurrentAmount;
|
|
323
|
-
|
|
324
|
-
const remainingAmount = Math.max(targetAmount - currentAmount, 0);
|
|
325
|
-
const progressPercent = Math.min((currentAmount / targetAmount) * 100, 100);
|
|
326
|
-
|
|
327
|
-
const currencySymbol =
|
|
328
|
-
String(
|
|
329
|
-
getResponsiveValue(section.properties?.['currency-symbol'], currentBreakpoint, 'TL') ||
|
|
330
|
-
'TL'
|
|
331
|
-
).trim() || 'TL';
|
|
332
|
-
|
|
333
|
-
const tokenContext = {
|
|
334
|
-
current: formatAmount(currentAmount),
|
|
335
|
-
remaining: formatAmount(remainingAmount),
|
|
336
|
-
target: formatAmount(targetAmount),
|
|
337
|
-
currency: currencySymbol,
|
|
338
|
-
progress_percent: progressPercent.toFixed(2),
|
|
339
|
-
marker_right_percent: (100 - progressPercent).toFixed(2)
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
const remainingMessageTemplate = String(
|
|
343
|
-
getResponsiveValue(
|
|
344
|
-
section.properties?.['remaining-message-template'],
|
|
345
|
-
currentBreakpoint,
|
|
346
|
-
'Add {{remaining}} {{currency}} more for free shipping'
|
|
347
|
-
) || 'Add {{remaining}} {{currency}} more for free shipping'
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
const unlockedMessageTemplate = String(
|
|
351
|
-
getResponsiveValue(
|
|
352
|
-
section.properties?.['unlocked-message-template'],
|
|
353
|
-
currentBreakpoint,
|
|
354
|
-
'Free shipping unlocked'
|
|
355
|
-
) || 'Free shipping unlocked'
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
const summaryTemplate = String(
|
|
359
|
-
getResponsiveValue(
|
|
360
|
-
section.properties?.['summary-template'],
|
|
361
|
-
currentBreakpoint,
|
|
362
|
-
'{{current}} {{currency}} / {{target}} {{currency}}'
|
|
363
|
-
) || '{{current}} {{currency}} / {{target}} {{currency}}'
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
const ctaIncompleteText = String(
|
|
367
|
-
getResponsiveValue(
|
|
368
|
-
section.properties?.['cta-incomplete-text'],
|
|
369
|
-
currentBreakpoint,
|
|
370
|
-
'Add products to unlock'
|
|
371
|
-
) || 'Add products to unlock'
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
const ctaCompleteText = String(
|
|
375
|
-
getResponsiveValue(
|
|
376
|
-
section.properties?.['cta-complete-text'],
|
|
377
|
-
currentBreakpoint,
|
|
378
|
-
'Continue to checkout'
|
|
379
|
-
) || 'Continue to checkout'
|
|
380
|
-
);
|
|
381
|
-
const showStartLabel = parseBoolean(
|
|
382
|
-
getResponsiveValue(section.properties?.['show-start-label'], currentBreakpoint, false),
|
|
383
|
-
false
|
|
384
|
-
);
|
|
385
|
-
|
|
386
|
-
const progressTrackColor = resolveThemeCssVariables(
|
|
387
|
-
String(
|
|
388
|
-
getResponsiveValue(section.styles?.['progress-track-color'], currentBreakpoint, '#e2e8f0')
|
|
389
|
-
),
|
|
390
|
-
themeSettings
|
|
391
|
-
);
|
|
392
|
-
const progressFillStartColor = resolveThemeCssVariables(
|
|
393
|
-
String(
|
|
394
|
-
getResponsiveValue(
|
|
395
|
-
section.styles?.['progress-fill-start-color'],
|
|
396
|
-
currentBreakpoint,
|
|
397
|
-
'#22c55e'
|
|
398
|
-
)
|
|
399
|
-
),
|
|
400
|
-
themeSettings
|
|
401
|
-
);
|
|
402
|
-
const progressFillEndColor = resolveThemeCssVariables(
|
|
403
|
-
String(
|
|
404
|
-
getResponsiveValue(
|
|
405
|
-
section.styles?.['progress-fill-end-color'],
|
|
406
|
-
currentBreakpoint,
|
|
407
|
-
'#84cc16'
|
|
408
|
-
)
|
|
409
|
-
),
|
|
410
|
-
themeSettings
|
|
411
|
-
);
|
|
412
|
-
const progressBarHeight = Math.min(
|
|
413
|
-
Math.max(
|
|
414
|
-
parseAmount(
|
|
415
|
-
getResponsiveValue(section.styles?.['progress-bar-height'], currentBreakpoint, 14),
|
|
416
|
-
14
|
|
417
|
-
),
|
|
418
|
-
8
|
|
419
|
-
),
|
|
420
|
-
28
|
|
421
|
-
);
|
|
422
|
-
const progressBarRadius = String(
|
|
423
|
-
getResponsiveValue(
|
|
424
|
-
section.styles?.['progress-bar-radius'],
|
|
425
|
-
currentBreakpoint,
|
|
426
|
-
'999px'
|
|
427
|
-
) || '999px'
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
const remainingMessage = replaceTokens(
|
|
431
|
-
remainingAmount > 0 ? remainingMessageTemplate : unlockedMessageTemplate,
|
|
432
|
-
tokenContext
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
const summaryMessage = replaceTokens(summaryTemplate, tokenContext);
|
|
436
|
-
|
|
437
|
-
const effectiveTokens: Record<string, string> = {
|
|
438
|
-
...tokenContext,
|
|
439
|
-
remaining_message: remainingMessage,
|
|
440
|
-
summary_message: summaryMessage,
|
|
441
|
-
cta_text: remainingAmount > 0 ? ctaIncompleteText : ctaCompleteText
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
const renderedBlocks = injectDynamicState(
|
|
445
|
-
sortedBlocks,
|
|
446
|
-
effectiveTokens,
|
|
447
|
-
currentBreakpoint,
|
|
448
|
-
progressPercent
|
|
449
|
-
);
|
|
450
|
-
|
|
451
|
-
const { fillBlockId, markerBlockId, wrapperBlockId, startLabelBlockId } = useMemo(
|
|
452
|
-
() => findProgressBlockIds(sortedBlocks, currentBreakpoint),
|
|
453
|
-
[sortedBlocks, currentBreakpoint]
|
|
454
|
-
);
|
|
455
|
-
|
|
456
|
-
const dynamicProgressCss = useMemo(() => {
|
|
457
|
-
const rules: string[] = [];
|
|
458
|
-
|
|
459
|
-
if (wrapperBlockId) {
|
|
460
|
-
const escapedWrapperId = escapeCssAttributeValue(wrapperBlockId);
|
|
461
|
-
rules.push(
|
|
462
|
-
`[data-block-id="${escapedWrapperId}"]{position:relative !important;height:${progressBarHeight}px !important;background-color:${progressTrackColor} !important;border-radius:${progressBarRadius} !important;overflow:visible !important;}`
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (fillBlockId) {
|
|
467
|
-
const escapedFillId = escapeCssAttributeValue(fillBlockId);
|
|
468
|
-
rules.push(
|
|
469
|
-
`[data-block-id="${escapedFillId}"]{width:${Math.max(
|
|
470
|
-
0,
|
|
471
|
-
Math.min(progressPercent, 100)
|
|
472
|
-
).toFixed(
|
|
473
|
-
2
|
|
474
|
-
)}% !important;position:absolute !important;top:0 !important;left:0 !important;height:100% !important;background:linear-gradient(90deg, ${progressFillStartColor} 0%, ${progressFillEndColor} 100%) !important;border-radius:${progressBarRadius} !important;padding:0 !important;min-height:0 !important;font-size:0 !important;line-height:0 !important;color:transparent !important;box-sizing:border-box !important;overflow:hidden !important;}`
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (markerBlockId) {
|
|
479
|
-
const escapedMarkerId = escapeCssAttributeValue(markerBlockId);
|
|
480
|
-
rules.push(`[data-block-id="${escapedMarkerId}"]{display:none !important;}`);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (startLabelBlockId && !showStartLabel) {
|
|
484
|
-
const escapedStartLabelId = escapeCssAttributeValue(startLabelBlockId);
|
|
485
|
-
rules.push(`[data-block-id="${escapedStartLabelId}"]{display:none !important;}`);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return rules.join('');
|
|
489
|
-
}, [
|
|
490
|
-
fillBlockId,
|
|
491
|
-
markerBlockId,
|
|
492
|
-
wrapperBlockId,
|
|
493
|
-
startLabelBlockId,
|
|
494
|
-
progressPercent,
|
|
495
|
-
progressTrackColor,
|
|
496
|
-
progressFillStartColor,
|
|
497
|
-
progressFillEndColor,
|
|
498
|
-
progressBarHeight,
|
|
499
|
-
progressBarRadius,
|
|
500
|
-
showStartLabel
|
|
501
|
-
]);
|
|
502
|
-
|
|
503
|
-
const maxWidth = getResponsiveValue(
|
|
504
|
-
section.styles?.['max-width'],
|
|
505
|
-
currentBreakpoint,
|
|
506
|
-
'normal'
|
|
507
|
-
);
|
|
508
|
-
const maxWidthClass =
|
|
509
|
-
maxWidth === 'narrow'
|
|
510
|
-
? 'max-w-4xl'
|
|
511
|
-
: maxWidth === 'normal'
|
|
512
|
-
? 'max-w-7xl'
|
|
513
|
-
: '';
|
|
514
|
-
const hasMaxWidth = maxWidth !== 'none' && maxWidth !== 'full';
|
|
515
|
-
|
|
516
|
-
const filteredStyles = Object.fromEntries(
|
|
517
|
-
Object.entries(section.styles || {}).filter(
|
|
518
|
-
([key]) =>
|
|
519
|
-
![
|
|
520
|
-
'max-width',
|
|
521
|
-
'progress-track-color',
|
|
522
|
-
'progress-fill-start-color',
|
|
523
|
-
'progress-fill-end-color',
|
|
524
|
-
'progress-bar-height',
|
|
525
|
-
'progress-bar-radius'
|
|
526
|
-
].includes(key)
|
|
527
|
-
)
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
const sectionStyles = getCSSStyles(filteredStyles, themeSettings, currentBreakpoint);
|
|
531
|
-
|
|
532
|
-
const postBlockAction = (type: string, blockId: string, label?: string) => {
|
|
533
|
-
if (!window.parent) return;
|
|
534
|
-
window.parent.postMessage(
|
|
535
|
-
{
|
|
536
|
-
type,
|
|
537
|
-
data: {
|
|
538
|
-
placeholderId,
|
|
539
|
-
sectionId: section.id,
|
|
540
|
-
blockId,
|
|
541
|
-
...(label ? { label } : {})
|
|
542
|
-
}
|
|
543
|
-
},
|
|
544
|
-
'*'
|
|
545
|
-
);
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
const renderBlock = (block: Block) => (
|
|
549
|
-
<ThemeBlock
|
|
550
|
-
key={block.id}
|
|
551
|
-
block={block}
|
|
552
|
-
placeholderId={placeholderId}
|
|
553
|
-
sectionId={section.id}
|
|
554
|
-
isDesigner={isDesigner}
|
|
555
|
-
isSelected={selectedBlockId === block.id}
|
|
556
|
-
selectedBlockId={selectedBlockId}
|
|
557
|
-
currentBreakpoint={currentBreakpoint}
|
|
558
|
-
onMoveUp={() => postBlockAction('MOVE_BLOCK_UP', block.id)}
|
|
559
|
-
onMoveDown={() => postBlockAction('MOVE_BLOCK_DOWN', block.id)}
|
|
560
|
-
onDuplicate={() => postBlockAction('DUPLICATE_BLOCK', block.id)}
|
|
561
|
-
onToggleVisibility={() => postBlockAction('TOGGLE_BLOCK_VISIBILITY', block.id)}
|
|
562
|
-
onDelete={() => postBlockAction('DELETE_BLOCK', block.id)}
|
|
563
|
-
onRename={(newLabel) => postBlockAction('RENAME_BLOCK', block.id, newLabel)}
|
|
564
|
-
/>
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
return (
|
|
568
|
-
<div
|
|
569
|
-
className={twMerge(
|
|
570
|
-
clsx(
|
|
571
|
-
'shipping-threshold-progress-section relative z-10 w-full',
|
|
572
|
-
hasMaxWidth && 'mx-auto',
|
|
573
|
-
maxWidthClass
|
|
574
|
-
)
|
|
575
|
-
)}
|
|
576
|
-
style={sectionStyles}
|
|
577
|
-
>
|
|
578
|
-
{dynamicProgressCss ? (
|
|
579
|
-
<style dangerouslySetInnerHTML={{ __html: dynamicProgressCss }} />
|
|
580
|
-
) : null}
|
|
581
|
-
{renderedBlocks.map(renderBlock)}
|
|
582
|
-
</div>
|
|
583
|
-
);
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
export default ShippingThresholdProgressSection;
|