@akinon/pz-theme 2.0.117-rc.0 → 2.0.118-beta.0
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 +15 -35
- package/package.json +4 -4
- package/src/blocks/__tests__/locale-links.test.tsx +429 -0
- package/src/blocks/account-button-block.tsx +1 -1
- package/src/blocks/account-menu-block.tsx +9 -3
- package/src/blocks/applied-filters-block.tsx +7 -1
- package/src/blocks/auth-form-block.tsx +7 -4
- package/src/blocks/basket-items-block.tsx +1 -1
- package/src/blocks/basket-summary-block.tsx +1 -1
- package/src/blocks/button-block.tsx +5 -1
- package/src/blocks/carousel-item-block.tsx +1 -1
- package/src/blocks/coupon-wallet-block.tsx +3 -1
- package/src/blocks/facet-filters-block.tsx +14 -10
- package/src/blocks/favorite-button-block.tsx +1 -1
- package/src/blocks/favourites-grid-block.tsx +1 -1
- package/src/blocks/group-block.tsx +19 -6
- package/src/blocks/hamburger-menu-block.tsx +1 -1
- package/src/blocks/hotspot-block.tsx +3 -1
- package/src/blocks/hover-showcase-item-block.tsx +3 -1
- package/src/blocks/link-block.tsx +5 -22
- package/src/blocks/listing-breadcrumb-block.tsx +1 -1
- package/src/blocks/listing-pagination-block.tsx +11 -3
- package/src/blocks/mega-menu-block.tsx +9 -3
- package/src/blocks/mini-basket-block.tsx +1 -1
- package/src/blocks/nav-menu-block.tsx +9 -3
- package/src/blocks/oauth-buttons-block.tsx +4 -0
- package/src/blocks/order-detail-block.tsx +1 -1
- package/src/blocks/order-list-block.tsx +1 -1
- package/src/blocks/order-success-block.tsx +1 -1
- package/src/blocks/otp-form-block.tsx +4 -1
- package/src/blocks/payment-badges-block.tsx +1 -1
- package/src/blocks/product-breadcrumb-block.tsx +1 -1
- package/src/blocks/product-grid-block.tsx +1 -1
- package/src/blocks/product-recommendations-block.tsx +1 -1
- package/src/blocks/register-form-block.tsx +6 -3
- package/src/blocks/search-box-block.tsx +7 -1
- package/src/blocks/social-icons-list-block.tsx +1 -1
- package/src/blocks/social-links-block.tsx +1 -1
- package/src/blocks/sort-select-block.tsx +8 -2
- package/src/blocks/store-locator-block.tsx +3 -1
- package/src/blocks/text-block.tsx +8 -1
- package/src/blocks/variant-selector-block.tsx +6 -2
- package/src/blocks/wishlist-button-block.tsx +1 -1
- package/src/components/__tests__/theme-link.test.tsx +169 -0
- package/src/components/theme-link.tsx +36 -0
- package/src/custom/README-sections.md +6 -0
- package/src/custom/README.md +71 -0
- package/src/custom/__tests__/custom-entry-exports.test.tsx +13 -0
- package/src/custom/__tests__/theme-editable.test.tsx +179 -0
- package/src/custom/__tests__/use-theme-setting.test.tsx +101 -0
- package/src/custom/index.ts +16 -0
- package/src/custom/theme-editable.tsx +57 -4
- package/src/custom/use-theme-setting.ts +77 -10
- package/src/hooks/use-localize-href.ts +34 -0
- package/src/index.ts +12 -0
- package/src/sections/find-in-store-section.tsx +3 -1
- package/src/sections/hover-showcase-section.tsx +6 -2
- package/src/sections/installment-options-section.tsx +3 -1
- package/src/sections/slider-quick-view.tsx +1 -1
- package/src/utils/__tests__/localize-href.test.ts +328 -0
- package/src/utils/localize-href.ts +192 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { getResponsiveValue, resolveOption } from '../utils';
|
|
6
6
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
7
7
|
import { DesignerPlaceholder } from './product-block-utils';
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@akinon/next/data/client/account';
|
|
9
9
|
import { useLocalization } from '@akinon/next/hooks';
|
|
10
10
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
11
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
11
12
|
import { formatDate, formatMoney } from './commerce-format';
|
|
12
13
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
13
14
|
import { DesignerPlaceholder, readToggle } from './product-block-utils';
|
|
@@ -114,6 +115,7 @@ const CouponWalletBlock = ({
|
|
|
114
115
|
|
|
115
116
|
const isMobile = currentBreakpoint === 'mobile';
|
|
116
117
|
const { locale, currency } = useLocalization();
|
|
118
|
+
const localize = useLocalizeHref();
|
|
117
119
|
const { status } = useSession();
|
|
118
120
|
const isAuthenticated = status === 'authenticated';
|
|
119
121
|
|
|
@@ -159,7 +161,7 @@ const CouponWalletBlock = ({
|
|
|
159
161
|
const renderTermsLink = () =>
|
|
160
162
|
termsLinkLabel ? (
|
|
161
163
|
<a
|
|
162
|
-
href={isDesigner ? '#' : termsHref || '#'}
|
|
164
|
+
href={isDesigner ? '#' : localize(termsHref) || '#'}
|
|
163
165
|
target={isDesigner ? undefined : termsLinkTarget}
|
|
164
166
|
rel={termsLinkTarget === '_blank' ? 'noreferrer' : undefined}
|
|
165
167
|
onClick={guard}
|
|
@@ -4,6 +4,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
4
4
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
6
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
7
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
7
8
|
import { useOverlay } from '../hooks/use-overlay';
|
|
8
9
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
9
10
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -309,6 +310,7 @@ const FacetFiltersBlock = ({
|
|
|
309
310
|
const pathname = usePathname();
|
|
310
311
|
const searchParams = useSearchParams();
|
|
311
312
|
const { locale } = useLocalization();
|
|
313
|
+
const localize = useLocalizeHref();
|
|
312
314
|
const [collapsedKeys, setCollapsedKeys] = useState<Record<string, boolean>>(
|
|
313
315
|
{}
|
|
314
316
|
);
|
|
@@ -786,6 +788,14 @@ const FacetFiltersBlock = ({
|
|
|
786
788
|
return params;
|
|
787
789
|
};
|
|
788
790
|
|
|
791
|
+
/**
|
|
792
|
+
* Every URL this block navigates to or renders. Commerce's `listing.path`
|
|
793
|
+
* has no locale; `localize` adds the page's (and leaves the one a
|
|
794
|
+
* `pathname` already carries).
|
|
795
|
+
*/
|
|
796
|
+
const listingHref = (params: URLSearchParams): string =>
|
|
797
|
+
localize(buildListingHref(pathname || listing?.path || '', params));
|
|
798
|
+
|
|
789
799
|
/**
|
|
790
800
|
* The URL that toggles one choice. Split out from the click handler so a
|
|
791
801
|
* designer-authored option row can bind a link to `item.url` and still
|
|
@@ -795,8 +805,7 @@ const FacetFiltersBlock = ({
|
|
|
795
805
|
facet: ListingFacet,
|
|
796
806
|
choice: ListingFacetChoice
|
|
797
807
|
): string =>
|
|
798
|
-
|
|
799
|
-
pathname || listing?.path || '',
|
|
808
|
+
listingHref(
|
|
800
809
|
toggleChoiceParams(
|
|
801
810
|
new URLSearchParams(appliedQuery),
|
|
802
811
|
facet,
|
|
@@ -810,7 +819,7 @@ const FacetFiltersBlock = ({
|
|
|
810
819
|
const params = deletePageCursor(new URLSearchParams(appliedQuery));
|
|
811
820
|
params.delete(facet.searchKey);
|
|
812
821
|
|
|
813
|
-
return
|
|
822
|
+
return listingHref(params);
|
|
814
823
|
};
|
|
815
824
|
|
|
816
825
|
const handleSelect = (facet: ListingFacet, choice: ListingFacetChoice) => {
|
|
@@ -1407,7 +1416,7 @@ const FacetFiltersBlock = ({
|
|
|
1407
1416
|
const params = deletePageCursor(new URLSearchParams(appliedQuery));
|
|
1408
1417
|
facets.forEach((facet) => params.delete(facet.searchKey));
|
|
1409
1418
|
|
|
1410
|
-
router.push(
|
|
1419
|
+
router.push(listingHref(params));
|
|
1411
1420
|
};
|
|
1412
1421
|
|
|
1413
1422
|
const hasStagedChanges =
|
|
@@ -1427,12 +1436,7 @@ const FacetFiltersBlock = ({
|
|
|
1427
1436
|
*/
|
|
1428
1437
|
const handleApply = () => {
|
|
1429
1438
|
if (!isDesigner && hasStagedChanges && stagedQuery !== null) {
|
|
1430
|
-
router.push(
|
|
1431
|
-
buildListingHref(
|
|
1432
|
-
pathname || listing?.path || '',
|
|
1433
|
-
new URLSearchParams(stagedQuery)
|
|
1434
|
-
)
|
|
1435
|
-
);
|
|
1439
|
+
router.push(listingHref(new URLSearchParams(stagedQuery)));
|
|
1436
1440
|
}
|
|
1437
1441
|
|
|
1438
1442
|
setPanelOpen(false);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import NextLink from '
|
|
2
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
3
3
|
import { getResponsiveValue } from '../utils';
|
|
4
|
+
import { asSitePath, isRootRelativeHref } from '../utils/localize-href';
|
|
4
5
|
import {
|
|
5
6
|
buildCommerceItem,
|
|
6
7
|
emitCommerceEvent,
|
|
@@ -17,9 +18,12 @@ import { renderComposedChildren } from './render-composed-children';
|
|
|
17
18
|
* composition data (wishlist context, carousel items) does not reliably
|
|
18
19
|
* survive. Router navigation keeps the app state alive in both directions.
|
|
19
20
|
* External hrefs keep the plain anchor — the router cannot take those anyway.
|
|
21
|
+
*
|
|
22
|
+
* The package's own test (`isRootRelativeHref`) rather than a local one: it
|
|
23
|
+
* trims, and a pasted ' /yatak-odasi/' used to miss this branch and go out as
|
|
24
|
+
* a plain anchor — which also meant it never reached `ThemeLink` and lost the
|
|
25
|
+
* page's locale with it.
|
|
20
26
|
*/
|
|
21
|
-
const isInternalHref = (href: unknown): href is string =>
|
|
22
|
-
typeof href === 'string' && href.startsWith('/') && !href.startsWith('//');
|
|
23
27
|
const hasOwnLength = (value: unknown, breakpoint: string): boolean => {
|
|
24
28
|
const raw = String(getResponsiveValue(value, breakpoint, '')).trim();
|
|
25
29
|
return raw !== '' && raw !== 'auto' && raw !== '0' && raw !== '0px';
|
|
@@ -65,7 +69,16 @@ const GroupBlock = ({
|
|
|
65
69
|
const tagProps: Record<string, unknown> = {
|
|
66
70
|
className: 'contents'
|
|
67
71
|
};
|
|
68
|
-
|
|
72
|
+
// Trimmed once here, because it is what the router link is given. A full URL
|
|
73
|
+
// of the storefront's own counts as one of its routes as well: a merchant
|
|
74
|
+
// pastes the address bar into a group as readily as into a link, and
|
|
75
|
+
// `link-block` already routes that form through `ThemeLink`.
|
|
76
|
+
const routerHref = isRootRelativeHref(href)
|
|
77
|
+
? href.trim()
|
|
78
|
+
: typeof href === 'string'
|
|
79
|
+
? asSitePath(href.trim(), process.env.NEXT_PUBLIC_URL)
|
|
80
|
+
: undefined;
|
|
81
|
+
const useRouterLink = tag === 'a' && !isDesigner && routerHref !== undefined;
|
|
69
82
|
if (tag === 'a' && href && !useRouterLink) {
|
|
70
83
|
tagProps.href = href;
|
|
71
84
|
}
|
|
@@ -105,7 +118,7 @@ const GroupBlock = ({
|
|
|
105
118
|
if (useRouterLink) {
|
|
106
119
|
return (
|
|
107
120
|
<NextLink
|
|
108
|
-
href={
|
|
121
|
+
href={routerHref as string}
|
|
109
122
|
prefetch={false}
|
|
110
123
|
className="contents"
|
|
111
124
|
onClick={handleAnchorAnalytics}
|
|
@@ -154,7 +167,7 @@ const GroupBlock = ({
|
|
|
154
167
|
if (useRouterLink) {
|
|
155
168
|
return (
|
|
156
169
|
<NextLink
|
|
157
|
-
href={
|
|
170
|
+
href={routerHref as string}
|
|
158
171
|
prefetch={false}
|
|
159
172
|
className="contents"
|
|
160
173
|
onClick={handleAnchorAnalytics}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { useGetMenuQuery } from '@akinon/next/data/client/misc';
|
|
6
6
|
import { getResponsiveValue, partAttrs, resolveForceOpen } from '../utils';
|
|
7
7
|
import { useOverlay } from '../hooks/use-overlay';
|
|
@@ -5,6 +5,7 @@ import { BlockRendererProps } from './block-renderer-registry';
|
|
|
5
5
|
import { getResponsiveValue, getCSSStyles, resolveForceOpen } from '../utils';
|
|
6
6
|
import { useThemeSettingsContext } from '../theme-settings-context';
|
|
7
7
|
import { useLocalization } from '@akinon/next/hooks';
|
|
8
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
8
9
|
import ThemeBlock from '../theme-block';
|
|
9
10
|
|
|
10
11
|
import { postToEditor } from '../editor-bridge';
|
|
@@ -19,6 +20,7 @@ const HotspotBlock = ({
|
|
|
19
20
|
const [isOpen, setIsOpen] = useState(false);
|
|
20
21
|
const themeSettings = useThemeSettingsContext();
|
|
21
22
|
const { locale } = useLocalization();
|
|
23
|
+
const localize = useLocalizeHref();
|
|
22
24
|
const defaultLocale = process.env.NEXT_PUBLIC_DEFAULT_LOCALE || 'en';
|
|
23
25
|
|
|
24
26
|
const styles = block.styles || {};
|
|
@@ -224,7 +226,7 @@ const HotspotBlock = ({
|
|
|
224
226
|
|
|
225
227
|
{link && (
|
|
226
228
|
<a
|
|
227
|
-
href={link}
|
|
229
|
+
href={localize(link)}
|
|
228
230
|
className="text-blue-600 hover:underline font-medium block mt-2"
|
|
229
231
|
onClick={(e) => e.stopPropagation()}
|
|
230
232
|
>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useLocalization } from '@akinon/next/hooks';
|
|
5
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
5
6
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
6
7
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
7
8
|
import { DesignerPlaceholder } from './product-block-utils';
|
|
@@ -73,6 +74,7 @@ const HoverShowcaseItemBlock = ({
|
|
|
73
74
|
isDesigner = false
|
|
74
75
|
}: BlockRendererProps) => {
|
|
75
76
|
const { locale } = useLocalization();
|
|
77
|
+
const localize = useLocalizeHref();
|
|
76
78
|
const defaultLocale = process.env.NEXT_PUBLIC_DEFAULT_LOCALE || 'en';
|
|
77
79
|
|
|
78
80
|
/**
|
|
@@ -200,7 +202,7 @@ const HoverShowcaseItemBlock = ({
|
|
|
200
202
|
// is nothing to hang the anchor on, so an authored link is simply not
|
|
201
203
|
// reachable — the same as in the section today.
|
|
202
204
|
<a
|
|
203
|
-
href={link}
|
|
205
|
+
href={localize(link)}
|
|
204
206
|
data-hover-showcase-link=""
|
|
205
207
|
{...partAttrs('link')}
|
|
206
208
|
onClick={handleLinkClick}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useMemo } from 'react';
|
|
4
|
-
import NextLink from '
|
|
5
|
-
import {
|
|
6
|
-
import { LocaleUrlStrategy } from '@akinon/next/localization';
|
|
7
|
-
import { urlLocaleMatcherRegex, urlSchemes } from '@akinon/next/utils';
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
|
+
import { urlSchemes } from '@akinon/next/utils';
|
|
8
6
|
import {
|
|
9
7
|
getInnerRootInlineStyles,
|
|
10
8
|
getResponsiveValue,
|
|
@@ -25,7 +23,6 @@ const LinkBlock = ({
|
|
|
25
23
|
currentBreakpoint = 'desktop',
|
|
26
24
|
isDesigner = false
|
|
27
25
|
}: BlockRendererProps) => {
|
|
28
|
-
const { locale, defaultLocaleValue, localeUrlStrategy } = useLocalization();
|
|
29
26
|
const themeSettings = useThemeSettingsContext();
|
|
30
27
|
const action = useBlockAction(block, { currentBreakpoint, isDesigner });
|
|
31
28
|
|
|
@@ -93,23 +90,9 @@ const LinkBlock = ({
|
|
|
93
90
|
return '#';
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
100
|
-
const hrefWithLocale = `/${locale}${pathnameWithoutLocale}`;
|
|
101
|
-
|
|
102
|
-
if (localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales) {
|
|
103
|
-
return hrefWithLocale;
|
|
104
|
-
} else if (
|
|
105
|
-
localeUrlStrategy === LocaleUrlStrategy.HideDefaultLocale &&
|
|
106
|
-
locale !== defaultLocaleValue
|
|
107
|
-
) {
|
|
108
|
-
return hrefWithLocale;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return trimmedHref || '#';
|
|
112
|
-
}, [href, defaultLocaleValue, locale, localeUrlStrategy]);
|
|
93
|
+
// The locale is ThemeLink's job, the same as for every other block link.
|
|
94
|
+
return trimmedHref;
|
|
95
|
+
}, [href]);
|
|
113
96
|
|
|
114
97
|
/**
|
|
115
98
|
* Only the values the generated stylesheet cannot produce; the rest now
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
6
6
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
7
7
|
import {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { usePathname, useSearchParams } from 'next/navigation';
|
|
6
6
|
import { useLocalization } from '@akinon/next/hooks';
|
|
7
7
|
import { getResponsiveValue, partAttrs, resolveOption } from '../utils';
|
|
8
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
8
9
|
import { hasItemTemplate, ItemTemplateList } from './item-template';
|
|
9
10
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
10
11
|
import {
|
|
@@ -301,6 +302,7 @@ const ListingPaginationBlock = ({
|
|
|
301
302
|
const pathname = usePathname();
|
|
302
303
|
const searchParams = useSearchParams();
|
|
303
304
|
const { locale } = useLocalization();
|
|
305
|
+
const localize = useLocalizeHref();
|
|
304
306
|
|
|
305
307
|
const mode = String(
|
|
306
308
|
getResponsiveValue(block.properties?.mode, currentBreakpoint, 'pages')
|
|
@@ -455,9 +457,15 @@ const ListingPaginationBlock = ({
|
|
|
455
457
|
params.set(queryParam, String(page));
|
|
456
458
|
}
|
|
457
459
|
|
|
458
|
-
|
|
460
|
+
// Linked, fetched and written to history alike, so it carries the page's
|
|
461
|
+
// locale: an unprefixed fetch would come back as the default-locale page
|
|
462
|
+
// (and switch the locale cookie with it). Commerce's `listing.path` has
|
|
463
|
+
// none; one a `pathname` already carries is kept.
|
|
464
|
+
return localize(
|
|
465
|
+
buildListingHref(pathname || listing?.path || '', params)
|
|
466
|
+
);
|
|
459
467
|
},
|
|
460
|
-
[searchParams, queryParam, pathname, listing?.path]
|
|
468
|
+
[searchParams, queryParam, pathname, listing?.path, localize]
|
|
461
469
|
);
|
|
462
470
|
|
|
463
471
|
const loadNextPage = React.useCallback(async () => {
|
|
@@ -7,8 +7,9 @@ import React, {
|
|
|
7
7
|
useRef,
|
|
8
8
|
useState
|
|
9
9
|
} from 'react';
|
|
10
|
-
import NextLink from '
|
|
10
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
11
11
|
import { usePathname } from 'next/navigation';
|
|
12
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
12
13
|
import { useGetMenuQuery } from '@akinon/next/data/client/misc';
|
|
13
14
|
import { getResponsiveValue, partAttrs, resolveForceOpen } from '../utils';
|
|
14
15
|
import { useOverlay } from '../hooks/use-overlay';
|
|
@@ -206,6 +207,7 @@ const MegaMenuBlock = ({
|
|
|
206
207
|
isDesigner = false
|
|
207
208
|
}: BlockRendererProps) => {
|
|
208
209
|
const pathname = usePathname();
|
|
210
|
+
const localize = useLocalizeHref();
|
|
209
211
|
const [openKey, setOpenKey] = useState<string | null>(null);
|
|
210
212
|
const openTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
211
213
|
const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -402,12 +404,16 @@ const MegaMenuBlock = ({
|
|
|
402
404
|
|
|
403
405
|
// The entry for the page the shopper is on carries `selected` so a part
|
|
404
406
|
// style can address it without the brand hand-listing every label.
|
|
407
|
+
//
|
|
408
|
+
// Menu URLs are stored without a locale while the pathname carries it
|
|
409
|
+
// (`/en/yatak-odasi`), so the entry is matched in the localized form its link
|
|
410
|
+
// renders — and so is the home entry the section match must never widen to.
|
|
405
411
|
const isCurrent = (url: string) => {
|
|
406
|
-
const target = normalizePath(url);
|
|
412
|
+
const target = normalizePath(localize(url));
|
|
407
413
|
const current = normalizePath(pathname);
|
|
408
414
|
|
|
409
415
|
if (!target || target === '#' || !current) return false;
|
|
410
|
-
if (activeMatch === 'section' && target !== '/') {
|
|
416
|
+
if (activeMatch === 'section' && target !== normalizePath(localize('/'))) {
|
|
411
417
|
return current === target || current.startsWith(`${target}/`);
|
|
412
418
|
}
|
|
413
419
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { useGetBasketQuery } from '@akinon/next/data/client/basket';
|
|
6
6
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
7
7
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { usePathname } from 'next/navigation';
|
|
6
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
6
7
|
import { useGetMenuQuery } from '@akinon/next/data/client/misc';
|
|
7
8
|
import { getResponsiveValue, partAttrs, resolveForceOpen } from '../utils';
|
|
8
9
|
import { useMenuSeed } from '../menu-seed-context';
|
|
@@ -53,6 +54,7 @@ const NavMenuBlock = ({
|
|
|
53
54
|
isDesigner = false
|
|
54
55
|
}: BlockRendererProps) => {
|
|
55
56
|
const pathname = usePathname();
|
|
57
|
+
const localize = useLocalizeHref();
|
|
56
58
|
const [openKey, setOpenKey] = useState<string | null>(null);
|
|
57
59
|
|
|
58
60
|
// Shared with the server's menu collector, so the key it seeded under is the
|
|
@@ -130,12 +132,16 @@ const NavMenuBlock = ({
|
|
|
130
132
|
|
|
131
133
|
// The entry for the page the shopper is on is what a brand reaches for first
|
|
132
134
|
// after colour; it carries `selected` so a part style can address it.
|
|
135
|
+
//
|
|
136
|
+
// Menu URLs are stored without a locale while the pathname carries it
|
|
137
|
+
// (`/en/yatak-odasi`), so the entry is matched in the localized form its link
|
|
138
|
+
// renders — and so is the home entry the section match must never widen to.
|
|
133
139
|
const isCurrent = (url?: string) => {
|
|
134
|
-
const target = normalizePath(url);
|
|
140
|
+
const target = normalizePath(localize(url ?? ''));
|
|
135
141
|
const current = normalizePath(pathname);
|
|
136
142
|
|
|
137
143
|
if (!target || target === '#' || !current) return false;
|
|
138
|
-
if (activeMatch === 'section' && target !== '/') {
|
|
144
|
+
if (activeMatch === 'section' && target !== normalizePath(localize('/'))) {
|
|
139
145
|
return current === target || current.startsWith(`${target}/`);
|
|
140
146
|
}
|
|
141
147
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
+
// Not ThemeLink: `withOauthLogin` only matches an unprefixed
|
|
5
|
+
// `/{provider}/login/`, so there is no locale to add. `localizeHref` leaves that
|
|
6
|
+
// route alone too (`OAUTH_ROUTE`), which keeps a row template that renders
|
|
7
|
+
// `item.url` through a button or group working on an English page.
|
|
4
8
|
import NextLink from 'next/link';
|
|
5
9
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
6
10
|
import { DesignerPlaceholder, readToggle } from './product-block-utils';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { useParams, useSearchParams } from 'next/navigation';
|
|
6
6
|
import { useGetOrderQuery } from '@akinon/next/data/client/account';
|
|
7
7
|
import { useLocalization } from '@akinon/next/hooks';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { useSession } from 'next-auth/react';
|
|
6
6
|
import { useGetOrdersQuery } from '@akinon/next/data/client/account';
|
|
7
7
|
import { useLocalization } from '@akinon/next/hooks';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { usePathname, useSearchParams } from 'next/navigation';
|
|
6
6
|
import { useFetchCheckoutResultQuery } from '@akinon/next/data/client/checkout';
|
|
7
7
|
import { useLocalization } from '@akinon/next/hooks';
|
|
@@ -6,6 +6,7 @@ import { signIn } from 'next-auth/react';
|
|
|
6
6
|
import { useLocalization } from '@akinon/next/hooks';
|
|
7
7
|
import { useOtpLoginMutation } from '@akinon/next/data/client/user';
|
|
8
8
|
import { getResponsiveValue, partAttrs, resolveOption } from '../utils';
|
|
9
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
9
10
|
import { readToggle } from './product-block-utils';
|
|
10
11
|
import { closeAllModals } from '../overlays/overlay-bus';
|
|
11
12
|
import {
|
|
@@ -273,6 +274,7 @@ const OtpFormBlock = ({
|
|
|
273
274
|
);
|
|
274
275
|
|
|
275
276
|
const router = useRouter();
|
|
277
|
+
const localize = useLocalizeHref();
|
|
276
278
|
const search = useSearchParams();
|
|
277
279
|
const pathname = usePathname();
|
|
278
280
|
const destination = resolveAuthDestination({
|
|
@@ -565,7 +567,8 @@ const OtpFormBlock = ({
|
|
|
565
567
|
router.refresh();
|
|
566
568
|
// Our own resolved path rather than next-auth's echo of it; `null` is
|
|
567
569
|
// `stay`, where the refresh above is the whole navigation.
|
|
568
|
-
|
|
570
|
+
// An authored destination has no locale; a returned one keeps its own.
|
|
571
|
+
if (destination) router.push(localize(destination));
|
|
569
572
|
} catch {
|
|
570
573
|
setError(errorLabel);
|
|
571
574
|
} finally {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useMemo } from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import ThemeBlock, { Block } from '../theme-block';
|
|
6
6
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
7
7
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
6
6
|
import { useThemePageContext } from '../theme-page-context';
|
|
7
7
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useRef } from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { useSession } from 'next-auth/react';
|
|
6
6
|
import { useGetWidgetQuery } from '@akinon/next/data/client/misc';
|
|
7
7
|
import { useGetFavoritesQuery } from '@akinon/next/data/client/wishlist';
|
|
@@ -5,6 +5,7 @@ import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
|
5
5
|
import { signIn, useSession } from 'next-auth/react';
|
|
6
6
|
import { useLocalization } from '@akinon/next/hooks';
|
|
7
7
|
import { getResponsiveValue, partAttrs, resolveOption } from '../utils';
|
|
8
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
8
9
|
import { readToggle } from './product-block-utils';
|
|
9
10
|
import { closeAllModals } from '../overlays/overlay-bus';
|
|
10
11
|
import {
|
|
@@ -293,6 +294,7 @@ const RegisterFormBlock = ({
|
|
|
293
294
|
);
|
|
294
295
|
|
|
295
296
|
const router = useRouter();
|
|
297
|
+
const localize = useLocalizeHref();
|
|
296
298
|
const search = useSearchParams();
|
|
297
299
|
const pathname = usePathname();
|
|
298
300
|
const { status } = useSession();
|
|
@@ -351,8 +353,8 @@ const RegisterFormBlock = ({
|
|
|
351
353
|
React.useEffect(() => {
|
|
352
354
|
if (!isSignedIn || whenSignedIn !== 'redirect') return;
|
|
353
355
|
|
|
354
|
-
router.replace(resolveSignedInDestination(signedInHref, search));
|
|
355
|
-
}, [isSignedIn, whenSignedIn, signedInHref, router, search]);
|
|
356
|
+
router.replace(localize(resolveSignedInDestination(signedInHref, search)));
|
|
357
|
+
}, [isSignedIn, whenSignedIn, signedInHref, router, search, localize]);
|
|
356
358
|
|
|
357
359
|
/**
|
|
358
360
|
* The form's contents, wrapped so a Security Check block (or the form's own
|
|
@@ -718,7 +720,8 @@ const RegisterFormBlock = ({
|
|
|
718
720
|
|
|
719
721
|
// Our own resolved path rather than next-auth's echo of it: this one has
|
|
720
722
|
// been through the same-origin check. `null` is `stay`.
|
|
721
|
-
|
|
723
|
+
// An authored destination has no locale; a returned one keeps its own.
|
|
724
|
+
if (destination) router.push(localize(destination));
|
|
722
725
|
} catch {
|
|
723
726
|
setError(errorLabel);
|
|
724
727
|
} finally {
|
|
@@ -4,6 +4,7 @@ import React, { useRef, useState } from 'react';
|
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
5
|
import { getResponsiveValue, partAttrs, resolveForceOpen } from '../utils';
|
|
6
6
|
import { emitCommerceEvent } from '../utils/analytics';
|
|
7
|
+
import { useLocalizeHref } from '../hooks/use-localize-href';
|
|
7
8
|
import { applySearchTermTransform } from '../utils/search-transform';
|
|
8
9
|
import { useOverlay } from '../hooks/use-overlay';
|
|
9
10
|
import ThemeBlock from '../theme-block';
|
|
@@ -49,6 +50,7 @@ const SearchBoxBlock = ({
|
|
|
49
50
|
isDesigner = false
|
|
50
51
|
}: BlockRendererProps) => {
|
|
51
52
|
const router = useRouter();
|
|
53
|
+
const localize = useLocalizeHref();
|
|
52
54
|
const [open, setOpen] = useState(false);
|
|
53
55
|
const [term, setTerm] = useState('');
|
|
54
56
|
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
@@ -190,8 +192,12 @@ const SearchBoxBlock = ({
|
|
|
190
192
|
if (!value || isDesigner) return;
|
|
191
193
|
emitCommerceEvent('search', { searchTerm: value });
|
|
192
194
|
const separator = searchUrl.includes('?') ? '&' : '?';
|
|
195
|
+
// The authored `/list` has no locale; a search from an English page must
|
|
196
|
+
// land on the English results.
|
|
193
197
|
router.push(
|
|
194
|
-
|
|
198
|
+
localize(
|
|
199
|
+
`${searchUrl}${separator}${queryParam}=${encodeURIComponent(value)}`
|
|
200
|
+
)
|
|
195
201
|
);
|
|
196
202
|
if (clearOnSubmit) setTerm('');
|
|
197
203
|
setOpen(false);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { partAttrs } from '../utils';
|
|
6
6
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
7
7
|
import { DesignerPlaceholder } from './product-block-utils';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import NextLink from '
|
|
4
|
+
import { ThemeLink as NextLink } from '../components/theme-link';
|
|
5
5
|
import { getResponsiveValue, partAttrs } from '../utils';
|
|
6
6
|
import { BlockRendererProps } from './block-renderer-registry';
|
|
7
7
|
import { DesignerPlaceholder, readToggle } from './product-block-utils';
|