Files
netbird/client/ui/frontend/src/components/dialog/ConfirmModal.tsx
T
Eduard GertandEdward f5707c3485 [client] Add RTL layout support to the desktop UI (#8076)
* [client] Add RTL layout support to the desktop UI

<html dir> follows the active language, Radix primitives get the
matching dir, physical spacing/positioning uses logical utilities, and
directional icons, animations, arrow-key navigation and tooltip sides
flip in RTL.

* [client] Address RTL review feedback

Force LTR with isolation for monospace values, let truncated names
take their direction from their content, and keep the profile name
field in the UI direction.

* [client] Keep translated monospace labels in their own direction

The forced-LTR rule for monospace values now skips elements with an
explicit dir, translated development labels use dir=auto, and
monospace values rendered through TruncatedText opt into LTR.

---------

Co-authored-by: Edward <43848523+thomashacker@users.noreply.github.com>
2026-10-06 12:08:11 +02:00

88 lines
2.9 KiB
TypeScript

import { type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import * as Dialog from "@/components/dialog/Dialog";
import { Button } from "@/components/buttons/Button";
import { DialogHeading } from "@/components/dialog/DialogHeading";
import { DialogDescription } from "@/components/dialog/DialogDescription";
import { DialogActions } from "@/components/dialog/DialogActions";
type ConfirmModalProps = {
open: boolean;
title: ReactNode;
description: ReactNode;
confirmLabel: string;
cancelLabel?: string;
danger?: boolean;
busy?: boolean;
cancellable?: boolean;
onConfirm: () => void;
onCancel: () => void;
};
export const ConfirmModal = ({
open,
title,
description,
confirmLabel,
cancelLabel,
danger = false,
busy = false,
cancellable,
onConfirm,
onCancel,
}: ConfirmModalProps) => {
const { t } = useTranslation();
const resolvedCancel = cancelLabel ?? t("common.cancel");
const canCancel = cancellable ?? !busy;
const srTitle = typeof title === "string" ? title : undefined;
const srDescription = typeof description === "string" ? description : undefined;
return (
<Dialog.Root
open={open}
onOpenChange={(next) => {
if (!next && canCancel) onCancel();
}}
>
<Dialog.Content
maxWidthClass={"max-w-sm"}
showClose={false}
className={"py-5"}
srTitle={srTitle}
srDescription={srDescription}
onOpenAutoFocus={(e) => e.preventDefault()}
>
<div className={"flex flex-col gap-5 px-5"}>
<div className={"flex flex-col gap-1 ps-1"}>
<DialogHeading align={"start"}>{title}</DialogHeading>
<DialogDescription align={"start"} className={"whitespace-pre-line"}>
{description}
</DialogDescription>
</div>
<DialogActions className={"flex-row justify-end gap-2.5"}>
<Button
variant={"secondary"}
size={"sm"}
disabled={!canCancel}
onClick={onCancel}
>
{resolvedCancel}
</Button>
<Button
autoFocus
variant={danger ? "danger" : "primary"}
size={"sm"}
loading={busy}
onClick={onConfirm}
>
{confirmLabel}
</Button>
</DialogActions>
</div>
</Dialog.Content>
</Dialog.Root>
);
};