Extract locale mismatch callout variants
Match the unsigned-NDA banner layout: keep layout classes in tv slots so the callout shell stays free of raw utilities. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -37,6 +37,7 @@ import { useLocale } from "#/lib/i18n/useLocale";
|
|||||||
import { useUpdateLocale } from "#/lib/i18n/useUpdateLocale";
|
import { useUpdateLocale } from "#/lib/i18n/useUpdateLocale";
|
||||||
|
|
||||||
import type { LocaleMismatchCallout_identity$key } from "./__generated__/LocaleMismatchCallout_identity.graphql";
|
import type { LocaleMismatchCallout_identity$key } from "./__generated__/LocaleMismatchCallout_identity.graphql";
|
||||||
|
import { localeMismatchCallout } from "./variants";
|
||||||
|
|
||||||
const localeMismatchCalloutFragment = graphql`
|
const localeMismatchCalloutFragment = graphql`
|
||||||
fragment LocaleMismatchCallout_identity on Identity {
|
fragment LocaleMismatchCallout_identity on Identity {
|
||||||
@@ -94,6 +95,7 @@ export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProp
|
|||||||
const urlLabel = URL_LOCALE_LABELS[urlLocale];
|
const urlLabel = URL_LOCALE_LABELS[urlLocale];
|
||||||
const savedLabel = URL_LOCALE_LABELS[savedLocale];
|
const savedLabel = URL_LOCALE_LABELS[savedLocale];
|
||||||
const busy = isChanging || isUpdating;
|
const busy = isChanging || isUpdating;
|
||||||
|
const slots = localeMismatchCallout();
|
||||||
|
|
||||||
const switchToSaved = () => {
|
const switchToSaved = () => {
|
||||||
void changeLocale(savedLocale, { persist: false });
|
void changeLocale(savedLocale, { persist: false });
|
||||||
@@ -106,13 +108,10 @@ export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProp
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside className={slots.root()} role="status">
|
||||||
className="flex w-full items-center gap-3 bg-gold-3 px-8 py-2.5 max-md:flex-col max-md:items-stretch max-md:gap-3 max-md:px-4 max-md:py-3"
|
<div className={slots.content()}>
|
||||||
role="status"
|
<GlobeIcon weight="fill" className={slots.icon()} aria-hidden />
|
||||||
>
|
<Text size={2} color="neutral" highContrast className={slots.message()}>
|
||||||
<div className="flex min-w-0 flex-1 items-start gap-2">
|
|
||||||
<GlobeIcon weight="fill" className="mt-0.5 size-4 shrink-0 text-gold-11" aria-hidden />
|
|
||||||
<Text size={2} color="neutral" highContrast className="min-w-0 flex-1">
|
|
||||||
{t("locale.mismatch.message", { language: urlLabel })}
|
{t("locale.mismatch.message", { language: urlLabel })}
|
||||||
</Text>
|
</Text>
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -121,19 +120,19 @@ export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProp
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
aria-label={t("locale.mismatch.dismiss")}
|
aria-label={t("locale.mismatch.dismiss")}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="md:hidden"
|
className={slots.dismissMobile()}
|
||||||
onClick={() => setDismissed(true)}
|
onClick={() => setDismissed(true)}
|
||||||
>
|
>
|
||||||
<XIcon />
|
<XIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex shrink-0 items-center gap-2 max-md:flex-col max-md:items-stretch">
|
<div className={slots.actions()}>
|
||||||
<Button
|
<Button
|
||||||
size={1}
|
size={1}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="gold"
|
color="gold"
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="max-md:w-full"
|
className={slots.action()}
|
||||||
onClick={switchToSaved}
|
onClick={switchToSaved}
|
||||||
>
|
>
|
||||||
{t("locale.mismatch.switchToMine", {
|
{t("locale.mismatch.switchToMine", {
|
||||||
@@ -149,7 +148,7 @@ export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProp
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
highContrast
|
highContrast
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="max-md:w-full"
|
className={slots.action()}
|
||||||
onClick={adoptUrlLocale}
|
onClick={adoptUrlLocale}
|
||||||
>
|
>
|
||||||
{t("locale.mismatch.useThis", { language: urlLabel })}
|
{t("locale.mismatch.useThis", { language: urlLabel })}
|
||||||
@@ -160,7 +159,7 @@ export function LocaleMismatchCallout({ identityKey }: LocaleMismatchCalloutProp
|
|||||||
color="neutral"
|
color="neutral"
|
||||||
aria-label={t("locale.mismatch.dismiss")}
|
aria-label={t("locale.mismatch.dismiss")}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="max-md:hidden"
|
className={slots.dismissDesktop()}
|
||||||
onClick={() => setDismissed(true)}
|
onClick={() => setDismissed(true)}
|
||||||
>
|
>
|
||||||
<XIcon />
|
<XIcon />
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
import { tv } from "tailwind-variants/lite";
|
||||||
|
|
||||||
|
// Full-bleed locale-mismatch banner under the TopBar: message row + actions,
|
||||||
|
// stacking on small screens.
|
||||||
|
export const localeMismatchCallout = tv({
|
||||||
|
slots: {
|
||||||
|
root: "flex w-full items-center gap-3 bg-gold-3 px-8 py-2.5 max-md:flex-col max-md:items-stretch max-md:gap-3 max-md:px-4 max-md:py-3",
|
||||||
|
content: "flex min-w-0 flex-1 items-start gap-2",
|
||||||
|
icon: "mt-0.5 size-4 shrink-0 text-gold-11",
|
||||||
|
message: "min-w-0 flex-1",
|
||||||
|
dismissMobile: "md:hidden",
|
||||||
|
actions: "flex shrink-0 items-center gap-2 max-md:flex-col max-md:items-stretch",
|
||||||
|
action: "max-md:w-full",
|
||||||
|
dismissDesktop: "max-md:hidden",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
import { tv } from "tailwind-variants/lite";
|
import { tv } from "tailwind-variants/lite";
|
||||||
|
|
||||||
// Full-bleed unsigned-NDA banner under the TopBar: message row + CTA/dismiss,
|
// Full-bleed unsigned-NDA banner under the TopBar: message row + CTA/dismiss,
|
||||||
// stacking on small screens like LocaleMismatchCallout.
|
// stacking on small screens like the locale-mismatch callout.
|
||||||
export const unsignedNDACallout = tv({
|
export const unsignedNDACallout = tv({
|
||||||
slots: {
|
slots: {
|
||||||
root: "flex w-full items-center gap-3 bg-gold-3 px-8 py-2.5 max-md:flex-col max-md:items-stretch max-md:gap-3 max-md:px-4 max-md:py-3",
|
root: "flex w-full items-center gap-3 bg-gold-3 px-8 py-2.5 max-md:flex-col max-md:items-stretch max-md:gap-3 max-md:px-4 max-md:py-3",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import type { PreloadedQuery } from "react-relay";
|
|||||||
import { graphql, usePreloadedQuery } from "react-relay";
|
import { graphql, usePreloadedQuery } from "react-relay";
|
||||||
import { Outlet, useMatch } from "react-router";
|
import { Outlet, useMatch } from "react-router";
|
||||||
|
|
||||||
import { LocaleMismatchCallout } from "#/components/LocaleMismatchCallout";
|
import { LocaleMismatchCallout } from "#/components/LocaleMismatchCallout/LocaleMismatchCallout";
|
||||||
import { PoweredBy } from "#/components/PoweredBy/PoweredBy";
|
import { PoweredBy } from "#/components/PoweredBy/PoweredBy";
|
||||||
import { TopBar } from "#/components/TopBar/TopBar";
|
import { TopBar } from "#/components/TopBar/TopBar";
|
||||||
import { UnsignedNDACallout } from "#/components/UnsignedNDACallout/UnsignedNDACallout";
|
import { UnsignedNDACallout } from "#/components/UnsignedNDACallout/UnsignedNDACallout";
|
||||||
|
|||||||
Reference in New Issue
Block a user