@akinon/pz-theme 2.0.38-rc.0 → 2.0.38
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 +7 -31
- package/package.json +4 -4
- package/src/__tests__/chrome-takeover-gate.test.tsx +252 -0
- package/src/__tests__/registry-parity.test.ts +14 -1
- package/src/__tests__/route-slug.test.ts +116 -0
- package/src/blocks/__tests__/overlay-sweep.test.tsx +592 -0
- package/src/blocks/account-button-block.tsx +133 -0
- package/src/blocks/announcement-bar-block.tsx +407 -0
- package/src/blocks/block-renderer-registry.tsx +26 -0
- package/src/blocks/currency-selector-block.tsx +177 -0
- package/src/blocks/hamburger-menu-block.tsx +461 -0
- package/src/blocks/language-selector-block.tsx +172 -0
- package/src/blocks/mega-menu-block.tsx +712 -0
- package/src/blocks/mini-basket-block.tsx +173 -0
- package/src/blocks/nav-menu-block.tsx +309 -0
- package/src/blocks/newsletter-form-block.tsx +184 -0
- package/src/blocks/payment-badges-block.tsx +393 -0
- package/src/blocks/search-box-block.tsx +277 -0
- package/src/blocks/social-links-block.tsx +391 -0
- package/src/blocks/wishlist-button-block.tsx +115 -0
- package/src/chrome/chrome-fallback.tsx +147 -0
- package/src/chrome/chrome-slot.tsx +68 -0
- package/src/chrome/composition-state.ts +92 -0
- package/src/chrome/page-context.ts +387 -0
- package/src/chrome/placeholder-slugs.ts +86 -0
- package/src/chrome/route-slug.ts +159 -0
- package/src/index.ts +20 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
5
|
+
import { getResponsiveValue, partAttrs } from '../utils';
|
|
6
|
+
import { BlockRendererProps } from './block-renderer-registry';
|
|
7
|
+
import { readToggle } from './product-block-utils';
|
|
8
|
+
|
|
9
|
+
const clampNumber = (
|
|
10
|
+
value: unknown,
|
|
11
|
+
fallback: number,
|
|
12
|
+
min: number,
|
|
13
|
+
max: number
|
|
14
|
+
) => {
|
|
15
|
+
const numeric = Math.round(Number(value));
|
|
16
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
17
|
+
return Math.min(max, Math.max(min, numeric));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Functional chrome block: locale switcher rendered as inline codes
|
|
22
|
+
* (TR | EN) or a select. Locales come from the storefront settings; the
|
|
23
|
+
* editor owns placement, wording, separator and styling.
|
|
24
|
+
* Properties: mode, separator, showSeparator, uppercase, labelSource, gap,
|
|
25
|
+
* ariaLabel, emptyText.
|
|
26
|
+
* Parts: option-list, option (selected/hover), separator, dropdown.
|
|
27
|
+
*/
|
|
28
|
+
const LanguageSelectorBlock = ({
|
|
29
|
+
block,
|
|
30
|
+
currentBreakpoint = 'desktop',
|
|
31
|
+
isDesigner = false
|
|
32
|
+
}: BlockRendererProps) => {
|
|
33
|
+
const { locale, locales, setLocale } = useLocalization();
|
|
34
|
+
|
|
35
|
+
const mode = String(
|
|
36
|
+
getResponsiveValue(block.properties?.mode, currentBreakpoint, 'inline')
|
|
37
|
+
);
|
|
38
|
+
const separator = String(
|
|
39
|
+
getResponsiveValue(block.properties?.separator, currentBreakpoint, '|')
|
|
40
|
+
);
|
|
41
|
+
const uppercase =
|
|
42
|
+
String(
|
|
43
|
+
getResponsiveValue(block.properties?.uppercase, currentBreakpoint, 'true')
|
|
44
|
+
) !== 'false';
|
|
45
|
+
const labelSource = String(
|
|
46
|
+
getResponsiveValue(
|
|
47
|
+
block.properties?.labelSource,
|
|
48
|
+
currentBreakpoint,
|
|
49
|
+
'label'
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
const ariaLabel = String(
|
|
53
|
+
getResponsiveValue(
|
|
54
|
+
block.properties?.ariaLabel,
|
|
55
|
+
currentBreakpoint,
|
|
56
|
+
'Language'
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
// Empty on purpose: the editor placeholder has always been built from the
|
|
60
|
+
// separator, so an unset override must keep doing exactly that.
|
|
61
|
+
const emptyText = String(
|
|
62
|
+
getResponsiveValue(block.properties?.emptyText, currentBreakpoint, '')
|
|
63
|
+
);
|
|
64
|
+
const gap = clampNumber(
|
|
65
|
+
getResponsiveValue(block.properties?.gap, currentBreakpoint, 6),
|
|
66
|
+
6,
|
|
67
|
+
0,
|
|
68
|
+
32
|
|
69
|
+
);
|
|
70
|
+
const showSeparator = readToggle(
|
|
71
|
+
block.properties?.showSeparator,
|
|
72
|
+
currentBreakpoint,
|
|
73
|
+
true
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const items = Array.isArray(locales) ? locales : [];
|
|
77
|
+
|
|
78
|
+
const format = (value: string) =>
|
|
79
|
+
uppercase ? String(value).toUpperCase() : String(value);
|
|
80
|
+
|
|
81
|
+
const readLabel = (item: { label?: string; value?: string }): string =>
|
|
82
|
+
labelSource === 'code'
|
|
83
|
+
? String(item.value ?? '')
|
|
84
|
+
: String(item.label || item.value || '');
|
|
85
|
+
|
|
86
|
+
const change = (value: string) => {
|
|
87
|
+
if (isDesigner || value === locale) return;
|
|
88
|
+
setLocale?.(value);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (items.length === 0) {
|
|
92
|
+
return isDesigner ? (
|
|
93
|
+
<span
|
|
94
|
+
data-language-selector=""
|
|
95
|
+
{...partAttrs('option-list', { empty: true })}
|
|
96
|
+
>
|
|
97
|
+
{emptyText || `TR ${separator} EN`}
|
|
98
|
+
</span>
|
|
99
|
+
) : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (mode === 'select') {
|
|
103
|
+
return (
|
|
104
|
+
<select
|
|
105
|
+
data-language-selector=""
|
|
106
|
+
{...partAttrs('dropdown')}
|
|
107
|
+
value={locale}
|
|
108
|
+
onChange={(event) => change(event.target.value)}
|
|
109
|
+
aria-label={ariaLabel}
|
|
110
|
+
style={{
|
|
111
|
+
background: 'transparent',
|
|
112
|
+
border: 'none',
|
|
113
|
+
color: 'inherit',
|
|
114
|
+
cursor: 'pointer',
|
|
115
|
+
outline: 'none'
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
{items.map((item) => (
|
|
119
|
+
<option key={item.value} value={item.value}>
|
|
120
|
+
{format(readLabel(item))}
|
|
121
|
+
</option>
|
|
122
|
+
))}
|
|
123
|
+
</select>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<span
|
|
129
|
+
data-language-selector=""
|
|
130
|
+
{...partAttrs('option-list')}
|
|
131
|
+
style={{
|
|
132
|
+
display: 'inline-flex',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
gap: `${gap}px`
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
{items.map((item, index) => {
|
|
138
|
+
const active = item.value === locale;
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<React.Fragment key={item.value}>
|
|
142
|
+
{index > 0 && showSeparator && (
|
|
143
|
+
<span aria-hidden="true" {...partAttrs('separator')}>
|
|
144
|
+
{separator}
|
|
145
|
+
</span>
|
|
146
|
+
)}
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
onClick={() => change(item.value)}
|
|
150
|
+
aria-current={active}
|
|
151
|
+
data-language-option={active ? 'selected' : 'default'}
|
|
152
|
+
{...partAttrs('option', { selected: active })}
|
|
153
|
+
style={{
|
|
154
|
+
background: 'none',
|
|
155
|
+
border: 'none',
|
|
156
|
+
padding: 0,
|
|
157
|
+
cursor: 'pointer',
|
|
158
|
+
color: 'inherit',
|
|
159
|
+
opacity: active ? 1 : 0.6,
|
|
160
|
+
fontWeight: active ? 600 : 400
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
{format(readLabel(item))}
|
|
164
|
+
</button>
|
|
165
|
+
</React.Fragment>
|
|
166
|
+
);
|
|
167
|
+
})}
|
|
168
|
+
</span>
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export default LanguageSelectorBlock;
|