@bytebrand/fe-ui-core-autobahn 1.0.158 → 1.0.160
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/cards/CarCard.tsx +9 -39
- package/cards/cardParts.tsx +1 -6
- package/constants/app.ts +1 -11
- package/constants/core.ts +1 -12
- package/locales/legacy/data.ts +1 -2
- package/package.json +30 -30
- package/ui/Modal.tsx +10 -2
- package/vehiclesProps/decoratedLightProps.tsx +6 -16
- package/vehiclesProps/decoratedProps.tsx +4 -8
package/cards/CarCard.tsx
CHANGED
|
@@ -157,8 +157,6 @@ const CarCard: React.FunctionComponent<CarCardProps> = ({
|
|
|
157
157
|
// active that's already 3, so NEU is dropped rather than causing a 4th to overflow.
|
|
158
158
|
const activeDealChips = [showDrop || showSave, showPct, !!car.zeroDown].filter(Boolean).length;
|
|
159
159
|
const showNeu = asGoodAsNew && car.isNew && activeDealChips < 3;
|
|
160
|
-
// tabs the price block can switch to (PriceTabs shows Leasing only when the car has a lease rate)
|
|
161
|
-
const paymentModes: PaymentMode[] = car.lease != null ? ['financing', 'leasing', 'buy'] : ['financing', 'buy'];
|
|
162
160
|
return (
|
|
163
161
|
<article
|
|
164
162
|
className="mcard mcard--interactive cc-card"
|
|
@@ -279,46 +277,18 @@ const CarCard: React.FunctionComponent<CarCardProps> = ({
|
|
|
279
277
|
<div className="cc-price" style={{ marginTop: 0, borderTop: '1px solid var(--md-outline-variant)' }}>
|
|
280
278
|
<PriceTabs car={car} mode={mode} onModeChange={setMode} t={t} />
|
|
281
279
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, marginTop: 10 }}>
|
|
282
|
-
{
|
|
283
|
-
visible, bottom-aligned — Kaufen's struck list price sits on its own line above
|
|
284
|
-
the price, so without this the row (and the card) grew/shrank by that line on
|
|
285
|
-
every tab switch. Same trick as .cc-terms-stack below. */}
|
|
286
|
-
<div className="cc-hero-stack">
|
|
287
|
-
{paymentModes.map((m) => (
|
|
288
|
-
<div
|
|
289
|
-
key={m}
|
|
290
|
-
className={'cc-hero-slot' + (m === mode ? '' : ' cc-hero-slot--hidden')}
|
|
291
|
-
aria-hidden={m === mode ? undefined : true}
|
|
292
|
-
>
|
|
293
|
-
<PriceHero car={car} mode={m} t={t} alwaysShowUnit />
|
|
294
|
-
</div>
|
|
295
|
-
))}
|
|
296
|
-
</div>
|
|
280
|
+
<PriceHero car={car} mode={mode} t={t} alwaysShowUnit />
|
|
297
281
|
<PriceEvalCol car={car} t={t} />
|
|
298
282
|
</div>
|
|
299
283
|
{/* same fallback CardCtaRow already uses below: pages that haven't wired a real
|
|
300
|
-
"Rate anpassen" sheet yet still get a working "Details" link (opens the car).
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
{
|
|
307
|
-
|
|
308
|
-
key={m}
|
|
309
|
-
className={'cc-terms-slot' + (m === mode ? '' : ' cc-terms-slot--hidden')}
|
|
310
|
-
aria-hidden={m === mode ? undefined : true}
|
|
311
|
-
>
|
|
312
|
-
<PriceTerms
|
|
313
|
-
car={car}
|
|
314
|
-
mode={m}
|
|
315
|
-
onOpenRateAdjust={onOpenRateAdjust || (() => onOpen(car))}
|
|
316
|
-
onShowDetails={onShowFinancingDetails ? () => onShowFinancingDetails(car, m) : undefined}
|
|
317
|
-
t={t}
|
|
318
|
-
/>
|
|
319
|
-
</div>
|
|
320
|
-
))}
|
|
321
|
-
</div>
|
|
284
|
+
"Rate anpassen" sheet yet still get a working "Details" link (opens the car). */}
|
|
285
|
+
<PriceTerms
|
|
286
|
+
car={car}
|
|
287
|
+
mode={mode}
|
|
288
|
+
onOpenRateAdjust={onOpenRateAdjust || (() => onOpen(car))}
|
|
289
|
+
onShowDetails={onShowFinancingDetails ? () => onShowFinancingDetails(car, mode) : undefined}
|
|
290
|
+
t={t}
|
|
291
|
+
/>
|
|
322
292
|
</div>
|
|
323
293
|
{/* Mobile-only row (CardCtaRow itself carries the show-mobile class) — always
|
|
324
294
|
rendered, with real fallbacks the page can override once it wires its own
|
package/cards/cardParts.tsx
CHANGED
|
@@ -710,12 +710,7 @@ export const PriceTerms: React.FunctionComponent<{
|
|
|
710
710
|
onShowDetails?: () => void;
|
|
711
711
|
t: TranslationFunction;
|
|
712
712
|
}> = ({ car, mode, onOpenRateAdjust, onShowDetails, t }) => {
|
|
713
|
-
|
|
714
|
-
// here, which collapsed .cc-terms to 0 height on that tab while Financing/Leasing rendered a
|
|
715
|
-
// real line, jumping the price row (and everything below it: rating pill, CTA row) up/down on
|
|
716
|
-
// every tab switch. Render an empty, still-present .cc-terms div instead — same fix as the VDP
|
|
717
|
-
// FinanceCalculator's .fin-cond (md3.css), which reserves its line's height the same way.
|
|
718
|
-
if (mode === 'buy') return <div className="cc-terms" />;
|
|
713
|
+
if (mode === 'buy') return null;
|
|
719
714
|
const finFirstInstallment =
|
|
720
715
|
car.finFirstInstallment != null ? car.finFirstInstallment.toLocaleString('de-DE') : _finFirstInstallment;
|
|
721
716
|
const finMonths = car.finMonths ?? _finMonths;
|
package/constants/app.ts
CHANGED
|
@@ -40,17 +40,7 @@ export const LOCAL_STORAGE_KEY = {
|
|
|
40
40
|
i18n_KEY: 'i18nLanguage'
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
// SecurityError that took down the whole bundle. Blocked storage behaves like "nothing saved".
|
|
45
|
-
const readStoredLang = (): string | null => {
|
|
46
|
-
try {
|
|
47
|
-
return localStorage.getItem(LOCAL_STORAGE_KEY.i18n_KEY);
|
|
48
|
-
} catch (e) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const DEFAULT_LANG: string = typeof window !== 'undefined' ? readStoredLang() : 'de';
|
|
43
|
+
export const DEFAULT_LANG: string = typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_KEY.i18n_KEY) : 'de';
|
|
54
44
|
|
|
55
45
|
// Default support contact phone
|
|
56
46
|
export const CONTACT_PHONE: string = '+498004030182';
|
package/constants/core.ts
CHANGED
|
@@ -35,18 +35,7 @@ export const LOCAL_STORAGE_KEY = {
|
|
|
35
35
|
i18n_KEY: 'i18nLanguage'
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
// window.localStorage throws a SecurityError, which took down the whole bundle (landing
|
|
40
|
-
// page never rendered). Treat blocked storage like "no language saved".
|
|
41
|
-
const readStoredLang = (): string | null => {
|
|
42
|
-
try {
|
|
43
|
-
return typeof window !== 'undefined' ? window.localStorage.getItem(LOCAL_STORAGE_KEY.i18n_KEY) : null;
|
|
44
|
-
} catch (e) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export const DEFAULT_LANG: string = readStoredLang() ?? 'de';
|
|
38
|
+
export const DEFAULT_LANG: string = (typeof window !== 'undefined' ? window.localStorage.getItem(LOCAL_STORAGE_KEY.i18n_KEY) : null) ?? 'de';
|
|
50
39
|
|
|
51
40
|
export const ROW_STYLES = {
|
|
52
41
|
marginBottom: 10
|
package/locales/legacy/data.ts
CHANGED
|
@@ -103,8 +103,7 @@ export const vehicleProps = (car?: any, isOfferAvailable: boolean = true) => {
|
|
|
103
103
|
wltpDischargedCombinedLabel: `${getFormattedNumber(vehicleOption.wltpCombined != null ? vehicleOption.wltpCombined : 19.9)}l/100km (entladen, komb)`,
|
|
104
104
|
wltpCo2WtdCombinedShort: `${getFormattedNumber(vehicleOption.wltpCo2)}g CO2/km (gew., komb)*`,
|
|
105
105
|
co2ClassWeighted: `CO2-Klasse ${(cbd.cbd as any)[vehicleOption.wltpEnergyEfficiencyClass] || vehicleOption.wltpEnergyEfficiencyClass || ''} (gew., komb)`,
|
|
106
|
-
|
|
107
|
-
co2ClassDischarged: `CO2-Klasse ${(cbd.cbd as any)[vehicleOption.wltpDischargedEnergyEfficiencyClass] || vehicleOption.wltpDischargedEnergyEfficiencyClass || ''} (entladen, komb)`
|
|
106
|
+
co2ClassDischarged: `CO2-Klasse ${(cbd.cbd as any)[vehicleOption.wltpDischargedEnergyEfficiencyClass || 'selector_energyEfficiencyClass_g'] || ''} (entladen, komb)`
|
|
108
107
|
},
|
|
109
108
|
title: {
|
|
110
109
|
'addOfferToMainSlide': 'Zum Schieberegler hinzufügen',
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bytebrand/fe-ui-core-autobahn",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "auto.de md3 (Autobahn) design system — UI primitives, cards, chrome, VDP modules, theme CSS and locales. Ships raw TypeScript source (like fe-ui-core before it); each consumer's bundler compiles it.",
|
|
5
|
-
"main": "index.ts",
|
|
6
|
-
"license": "UNLICENSED",
|
|
7
|
-
"peerDependencies": {
|
|
8
|
-
"react": "^18.0.0",
|
|
9
|
-
"react-dom": "^18.0.0"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"@bytebrand/car-schema-selectors": "^2.0.7",
|
|
13
|
-
"@bytebrand/fe-financing": "^5.14.20",
|
|
14
|
-
"classnames": "2.2.6",
|
|
15
|
-
"clsx": "^2.1.1",
|
|
16
|
-
"d3-format": "1.4.5",
|
|
17
|
-
"js-cookie": "^2.2.0",
|
|
18
|
-
"lodash": "^4.17.21",
|
|
19
|
-
"lucide-react": "^0.288.0",
|
|
20
|
-
"mobx-react": "^7.6.0",
|
|
21
|
-
"moment": "^2.29.1",
|
|
22
|
-
"qs": "^6.6.0",
|
|
23
|
-
"react-device-detect": "^2.1.2",
|
|
24
|
-
"react-grid-system": "^5.0.2",
|
|
25
|
-
"react-lazyload": "^3.2.0",
|
|
26
|
-
"react-scroll": "^1.9.3",
|
|
27
|
-
"react-slick": "^0.29.0",
|
|
28
|
-
"slick-carousel": "1.8.1"
|
|
29
|
-
}
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@bytebrand/fe-ui-core-autobahn",
|
|
3
|
+
"version": "1.0.160",
|
|
4
|
+
"description": "auto.de md3 (Autobahn) design system — UI primitives, cards, chrome, VDP modules, theme CSS and locales. Ships raw TypeScript source (like fe-ui-core before it); each consumer's bundler compiles it.",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"peerDependencies": {
|
|
8
|
+
"react": "^18.0.0",
|
|
9
|
+
"react-dom": "^18.0.0"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@bytebrand/car-schema-selectors": "^2.0.7",
|
|
13
|
+
"@bytebrand/fe-financing": "^5.14.20",
|
|
14
|
+
"classnames": "2.2.6",
|
|
15
|
+
"clsx": "^2.1.1",
|
|
16
|
+
"d3-format": "1.4.5",
|
|
17
|
+
"js-cookie": "^2.2.0",
|
|
18
|
+
"lodash": "^4.17.21",
|
|
19
|
+
"lucide-react": "^0.288.0",
|
|
20
|
+
"mobx-react": "^7.6.0",
|
|
21
|
+
"moment": "^2.29.1",
|
|
22
|
+
"qs": "^6.6.0",
|
|
23
|
+
"react-device-detect": "^2.1.2",
|
|
24
|
+
"react-grid-system": "^5.0.2",
|
|
25
|
+
"react-lazyload": "^3.2.0",
|
|
26
|
+
"react-scroll": "^1.9.3",
|
|
27
|
+
"react-slick": "^0.29.0",
|
|
28
|
+
"slick-carousel": "1.8.1"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/ui/Modal.tsx
CHANGED
|
@@ -136,7 +136,10 @@ const Modal: React.FunctionComponent<ModalProps> = ({
|
|
|
136
136
|
const prevOverflow = document.body.style.overflow;
|
|
137
137
|
document.body.style.overflow = 'hidden';
|
|
138
138
|
|
|
139
|
-
// focus the
|
|
139
|
+
// move focus to the first control in the body/footer (an input, a link, a button) so forms are
|
|
140
|
+
// ready to type into and browser autofill can offer itself; the head row (close + headerAction
|
|
141
|
+
// icons) is skipped so a header icon never takes the :focus-visible ring on open. Falls back to
|
|
142
|
+
// the surface itself when the body has nothing focusable.
|
|
140
143
|
// { preventScroll: true }: this runs at the very first animation frame of the mobile
|
|
141
144
|
// sheet's own mmodal-sheet-up entrance (md3.css) — at that instant the surface's actual
|
|
142
145
|
// computed transform is still the keyframe's `from` state (translateY(100%), pushed
|
|
@@ -148,7 +151,12 @@ const Modal: React.FunctionComponent<ModalProps> = ({
|
|
|
148
151
|
// skips that native auto-scroll entirely, so the CSS animation alone moves the sheet
|
|
149
152
|
// (same fix as fe-main's BaseModal.tsx, which hit this same glitch first).
|
|
150
153
|
const surface = surfaceRef.current;
|
|
151
|
-
if (surface)
|
|
154
|
+
if (surface) {
|
|
155
|
+
const first = Array.prototype.slice
|
|
156
|
+
.call(surface.querySelectorAll<HTMLElement>(FOCUSABLE))
|
|
157
|
+
.find((el: HTMLElement) => !el.closest('.mmodal-head'));
|
|
158
|
+
(first || surface).focus({ preventScroll: true });
|
|
159
|
+
}
|
|
152
160
|
|
|
153
161
|
return () => {
|
|
154
162
|
document.body.style.overflow = prevOverflow;
|
|
@@ -165,10 +165,7 @@ const getDecoratedLightProps = (
|
|
|
165
165
|
if (hybridPlugin) {
|
|
166
166
|
if (wltpWeightedPowerCombinedContent && wltpWeightedCombinedContent) {
|
|
167
167
|
const dischargedCombined = Number.isFinite(wltpConsumptionCombined) ? wltpConsumptionCombined : 19.9;
|
|
168
|
-
|
|
169
|
-
// backend could not derive it the field is selector_unknown — hide the segment, never
|
|
170
|
-
// fall back to a hard-coded class here.
|
|
171
|
-
const dischargedClass = car.environmentEmissions.wltpDischargedEnergyEfficiencyClass;
|
|
168
|
+
const dischargedClass = car.environmentEmissions.wltpDischargedEnergyEfficiencyClass || 'selector_energyEfficiencyClass_g';
|
|
172
169
|
const weightedCombinedLabeled = t('vehicleProps:value.wltpWeightedCombinedLabel', { consumption: wltpWeightedCombined.toLocaleString(language), unit: translatedUnit });
|
|
173
170
|
const dischargedCombinedLabeled = t('vehicleProps:value.wltpDischargedCombinedLabel', { consumption: dischargedCombined.toLocaleString(language), unit: translatedUnit });
|
|
174
171
|
const co2WeightedContent = Number.isFinite(wltpCo2)
|
|
@@ -177,16 +174,14 @@ const getDecoratedLightProps = (
|
|
|
177
174
|
const co2ClassWeighted = isPropDefined(wltpEnergyEfficiencyClass)
|
|
178
175
|
? t('vehicleProps:value.co2ClassWeighted', { class: t(`cbd:${wltpEnergyEfficiencyClass}`) })
|
|
179
176
|
: null;
|
|
180
|
-
const co2ClassDischarged =
|
|
181
|
-
? t('vehicleProps:value.co2ClassDischarged', { class: t(`cbd:${dischargedClass}`) })
|
|
182
|
-
: null;
|
|
177
|
+
const co2ClassDischarged = t('vehicleProps:value.co2ClassDischarged', { class: t(`cbd:${dischargedClass}`) });
|
|
183
178
|
return <React.Fragment>
|
|
184
179
|
{`${wltpWeightedPowerCombinedContent} + `}
|
|
185
180
|
{`${weightedCombinedLabeled}, `}
|
|
186
181
|
{`${dischargedCombinedLabeled}`}
|
|
187
182
|
{co2WeightedContent && <>, {co2WeightedContent}</>}
|
|
188
183
|
{co2ClassWeighted && <>, {co2ClassWeighted}</>}
|
|
189
|
-
{
|
|
184
|
+
{<>, {co2ClassDischarged}</>}
|
|
190
185
|
</React.Fragment>
|
|
191
186
|
}
|
|
192
187
|
|
|
@@ -321,10 +316,7 @@ const getDecoratedLightProps = (
|
|
|
321
316
|
if (hybridPlugin) {
|
|
322
317
|
if (wltpWeightedPowerCombinedContent && wltpWeightedCombinedContent) {
|
|
323
318
|
const dischargedCombined = Number.isFinite(wltpConsumptionCombined) ? wltpConsumptionCombined : 19.9;
|
|
324
|
-
|
|
325
|
-
// backend could not derive it the field is selector_unknown — hide the segment, never
|
|
326
|
-
// fall back to a hard-coded class here.
|
|
327
|
-
const dischargedClass = car.environmentEmissions.wltpDischargedEnergyEfficiencyClass;
|
|
319
|
+
const dischargedClass = car.environmentEmissions.wltpDischargedEnergyEfficiencyClass || 'selector_energyEfficiencyClass_g';
|
|
328
320
|
const weightedCombinedLabeled = t('vehicleProps:value.wltpWeightedCombinedLabel', { consumption: wltpWeightedCombined.toLocaleString(language), unit: translatedUnit });
|
|
329
321
|
const dischargedCombinedLabeled = t('vehicleProps:value.wltpDischargedCombinedLabel', { consumption: dischargedCombined.toLocaleString(language), unit: translatedUnit });
|
|
330
322
|
const co2WeightedContent = Number.isFinite(wltpCo2)
|
|
@@ -333,16 +325,14 @@ const getDecoratedLightProps = (
|
|
|
333
325
|
const co2ClassWeighted = isPropDefined(wltpEnergyEfficiencyClass)
|
|
334
326
|
? t('vehicleProps:value.co2ClassWeighted', { class: t(`cbd:${wltpEnergyEfficiencyClass}`) })
|
|
335
327
|
: null;
|
|
336
|
-
const co2ClassDischarged =
|
|
337
|
-
? t('vehicleProps:value.co2ClassDischarged', { class: t(`cbd:${dischargedClass}`) })
|
|
338
|
-
: null;
|
|
328
|
+
const co2ClassDischarged = t('vehicleProps:value.co2ClassDischarged', { class: t(`cbd:${dischargedClass}`) });
|
|
339
329
|
return <React.Fragment>
|
|
340
330
|
{`${wltpWeightedPowerCombinedContent} + `}
|
|
341
331
|
{`${weightedCombinedLabeled}, `}
|
|
342
332
|
{`${dischargedCombinedLabeled}`}
|
|
343
333
|
{co2WeightedContent && <>, {co2WeightedContent}</>}
|
|
344
334
|
{co2ClassWeighted && <>, {co2ClassWeighted}</>}
|
|
345
|
-
{
|
|
335
|
+
{<>, {co2ClassDischarged}</>}
|
|
346
336
|
</React.Fragment>
|
|
347
337
|
}
|
|
348
338
|
|
|
@@ -880,12 +880,10 @@ const getDecoratedProps = (
|
|
|
880
880
|
// (entladen, komb) fuel consumption + a discharged CO2 class.
|
|
881
881
|
if (hybridPlugin && wltpConsumptionWeightedPowerCombinedContent && wltpConsumptionWeightedCombinedContent) {
|
|
882
882
|
const ADV88_DEFAULT_DISCHARGED_COMBINED = 19.9;
|
|
883
|
+
const ADV88_DEFAULT_DISCHARGED_CO2_CLASS = 'selector_energyEfficiencyClass_g';
|
|
883
884
|
|
|
884
885
|
const dischargedCombined = Number.isFinite(wltpCombined) ? wltpCombined : ADV88_DEFAULT_DISCHARGED_COMBINED;
|
|
885
|
-
|
|
886
|
-
// backend could not derive it the field is selector_unknown — hide the segment, never
|
|
887
|
-
// fall back to a hard-coded class here.
|
|
888
|
-
const dischargedClass = car.environmentEmissions.wltpDischargedEnergyEfficiencyClass;
|
|
886
|
+
const dischargedClass = car.environmentEmissions.wltpDischargedEnergyEfficiencyClass || ADV88_DEFAULT_DISCHARGED_CO2_CLASS;
|
|
889
887
|
|
|
890
888
|
const weightedCombinedLabeled = t('vehicleProps:value.wltpWeightedCombinedLabel', { consumption: wltpWeightedCombined.toLocaleString(language), unit: translatedUnit });
|
|
891
889
|
const dischargedCombinedLabeled = t('vehicleProps:value.wltpDischargedCombinedLabel', { consumption: dischargedCombined.toLocaleString(language), unit: translatedUnit });
|
|
@@ -895,9 +893,7 @@ const getDecoratedProps = (
|
|
|
895
893
|
const co2ClassWeighted = isPropDefined(wltpEnergyEfficiencyClass)
|
|
896
894
|
? t('vehicleProps:value.co2ClassWeighted', { class: t(`cbd:${wltpEnergyEfficiencyClass}`) })
|
|
897
895
|
: null;
|
|
898
|
-
const co2ClassDischarged =
|
|
899
|
-
? t('vehicleProps:value.co2ClassDischarged', { class: t(`cbd:${dischargedClass}`) })
|
|
900
|
-
: null;
|
|
896
|
+
const co2ClassDischarged = t('vehicleProps:value.co2ClassDischarged', { class: t(`cbd:${dischargedClass}`) });
|
|
901
897
|
|
|
902
898
|
return <React.Fragment>
|
|
903
899
|
{wrapValue(wltpConsumptionWeightedPowerCombinedContent)}{' + '}
|
|
@@ -905,7 +901,7 @@ const getDecoratedProps = (
|
|
|
905
901
|
{wrapValue(dischargedCombinedLabeled)}
|
|
906
902
|
{co2WeightedContent && <>{', '}{wrapValue(co2WeightedContent)}</>}
|
|
907
903
|
{co2ClassWeighted && <>{', '}{co2ClassWeighted}</>}
|
|
908
|
-
{
|
|
904
|
+
{', '}{co2ClassDischarged}
|
|
909
905
|
</React.Fragment>
|
|
910
906
|
}
|
|
911
907
|
|