refactor(ui): adopt Mantine Marquee and drop mount guards

Rebuild the integration marquee on Mantine's Marquee, SimpleGrid and
ThemeIcon instead of the hand-rolled grid, track and keyframes, and
reduce the stylesheet to the mask and reduced-motion rule.

Drop the useMounted guards from the login form and let the buttons rely
on the pending state alone, and use Group for the restore flow's file
summary.
This commit is contained in:
Thomas Camlong committed 2026-08-17 01:15:45 +02:00
1 parent 206905ea2d
commit c53852fdb6
5 files changed
+81 -95

No files matched your search

@@ -4,7 +4,7 @@ import type { PropsWithChildren } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { Anchor, Button, Card, Code, Collapse, Divider, PasswordInput, Stack, Text, TextInput } from "@mantine/core";
import { useDisclosure, useMounted } from "@mantine/hooks";
import { useDisclosure } from "@mantine/hooks";
import { z } from "zod/v4";
import { signIn } from "@homarr/auth/client";
@@ -32,7 +32,6 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
const searchParams = useSearchParams();
const isError = searchParams.has("error");
const router = useRouter();
const isMounted = useMounted();
const [isPending, setIsPending] = useState(false);
const form = useZodForm(extendedValidation, {
initialValues: {
@@ -140,7 +139,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
{providers.includes("credentials") && (
<Stack gap="sm">
<SubmitButton isMounted={isMounted} isPending={isPending} form={form} provider="credentials">
<SubmitButton isPending={isPending} form={form} provider="credentials">
{t("action.login.label")}
</SubmitButton>
<PasswordForgottenCollapse username={form.values.name} />
@@ -148,7 +147,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
)}
{providers.includes("ldap") && (
<SubmitButton isMounted={isMounted} isPending={isPending} form={form} provider="ldap">
<SubmitButton isPending={isPending} form={form} provider="ldap">
{t("action.login.labelWith", { provider: "LDAP" })}
</SubmitButton>
)}
@@ -159,13 +158,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
)}
{providers.includes("oidc") && (
<Button
type="button"
fullWidth
variant="light"
disabled={!isMounted || isPending}
onClick={() => void signInAsync("oidc")}
>
<Button type="button" fullWidth variant="light" disabled={isPending} onClick={() => void signInAsync("oidc")}>
{t("action.login.labelWith", { provider: oidcClientName })}
</Button>
)}
@@ -175,13 +168,12 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
};
interface SubmitButtonProps {
isMounted: boolean;
isPending: boolean;
form: UseFormReturnType<FormType>;
provider: "credentials" | "ldap";
}
const SubmitButton = ({ isMounted, isPending, form, provider, children }: PropsWithChildren<SubmitButtonProps>) => {
const SubmitButton = ({ isPending, form, provider, children }: PropsWithChildren<SubmitButtonProps>) => {
const isCurrentProviderActive = form.getValues().provider === provider;
return (
@@ -192,7 +184,7 @@ const SubmitButton = ({ isMounted, isPending, form, provider, children }: PropsW
fullWidth
onClick={() => form.setFieldValue("provider", provider)}
loading={isPending && isCurrentProviderActive}
disabled={!isMounted || (isPending && !isCurrentProviderActive)}
disabled={isPending && !isCurrentProviderActive}
>
{children}
</Button>
@@ -254,14 +254,14 @@ export const DatabaseRestoreFlow = ({ variant = "card", onRestoreComplete }: Dat
<Group justify="space-between" wrap="nowrap">
<Group gap="sm" wrap="nowrap" miw={0}>
<IconFileZip size={20} />
<div style={{ minWidth: 0 }}>
<Group>
<Text size="sm" fw={500} truncate>
{file?.name}
</Text>
<Text size="xs" c="dimmed" style={{ whiteSpace: "nowrap" }}>
{file ? `${(file.size / 1024 / 1024).toFixed(2)} MB` : ""}
</Text>
</div>
</Group>
</Group>
<Button variant="subtle" size="xs" onClick={handleClear} style={{ flexShrink: 0 }}>
{t("changeFile")}