feat: adopt Mantine 9.6 components (#6758)
This commit is contained in:
1 parent
b3aa6155cc
commit
35155b18b9
28 files changed
+1192
-567
No files matched your search
@@ -1,21 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
ActionIcon,
|
||||
ActionBar,
|
||||
Avatar,
|
||||
Button,
|
||||
Divider,
|
||||
Group,
|
||||
HoverCard,
|
||||
Menu,
|
||||
Paper,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Text,
|
||||
Tooltip,
|
||||
UnstyledButton,
|
||||
} from "@mantine/core";
|
||||
import { IconCheck, IconClipboard, IconCopy, IconFolderShare, IconTrash, IconX } from "@tabler/icons-react";
|
||||
import { IconCheck, IconClipboard, IconCopy, IconFolderShare, IconTrash } from "@tabler/icons-react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
|
||||
@@ -129,24 +127,29 @@ export const BoardSelectionToolbar = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<Paper
|
||||
<ActionBar
|
||||
opened
|
||||
onClose={clearSelection}
|
||||
closeOnEscape
|
||||
withBorder
|
||||
shadow="xl"
|
||||
radius="xl"
|
||||
p="xs"
|
||||
position={{ bottom: 28, left: 0, right: 0 }}
|
||||
zIndex={1000}
|
||||
aria-label={tSelection("summary", { count: sortedSelectedItems.length })}
|
||||
bg="light-dark(var(--mantine-color-body), var(--mantine-color-dark-7))"
|
||||
style={{
|
||||
position: "fixed",
|
||||
bottom: 28,
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
zIndex: 1000,
|
||||
width: "max-content",
|
||||
maxWidth: "calc(100vw - 1rem)",
|
||||
boxShadow: "0 12px 40px rgba(0, 0, 0, 0.28)",
|
||||
backdropFilter: "blur(14px)",
|
||||
border: "1px solid var(--mantine-color-default-border)",
|
||||
animation: "slideUp 200ms cubic-bezier(0.4, 0, 0.2, 1) forwards",
|
||||
}}
|
||||
transitionProps={{
|
||||
transition: "slide-up",
|
||||
duration: 200,
|
||||
timingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
}}
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
@@ -220,7 +223,7 @@ export const BoardSelectionToolbar = () => {
|
||||
</HoverCard.Dropdown>
|
||||
</HoverCard>
|
||||
|
||||
<Divider orientation="vertical" />
|
||||
<ActionBar.Divider />
|
||||
|
||||
{/* Actions */}
|
||||
<Group gap="xs" wrap="nowrap" style={{ flexShrink: 0 }}>
|
||||
@@ -279,18 +282,10 @@ export const BoardSelectionToolbar = () => {
|
||||
</Button>
|
||||
|
||||
<Tooltip label={tSelection("clearShortcut")} withArrow>
|
||||
<ActionIcon
|
||||
size="sm"
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
onClick={clearSelection}
|
||||
aria-label={tSelection("clear")}
|
||||
>
|
||||
<IconX size={14} />
|
||||
</ActionIcon>
|
||||
<ActionBar.CloseButton size="sm" variant="subtle" aria-label={tSelection("clear")} />
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</Group>
|
||||
</Paper>
|
||||
</ActionBar>
|
||||
);
|
||||
};
|
||||
@@ -94,6 +94,11 @@ describe("Workshop widget install flow", () => {
|
||||
|
||||
expect(screenshots).toContain("1200x800");
|
||||
expect(screenshots).toContain("zoomScreenshot");
|
||||
expect(screenshots).toContain('import { Lightbox } from "@mantine/lightbox"');
|
||||
expect(screenshots).toContain("withThumbnails");
|
||||
expect(screenshots).toContain("withZoom");
|
||||
expect(screenshots).toContain("withKeyboardEvents");
|
||||
expect(screenshots).not.toMatch(/<Modal\b/u);
|
||||
expect(detail).toContain("WorkshopScreenshots");
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { ActionIcon, Box, Group, Image, Modal, Paper, Tooltip, UnstyledButton } from "@mantine/core";
|
||||
import { ActionIcon, Box, Group, Image, Paper, Tooltip, UnstyledButton } from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { Lightbox } from "@mantine/lightbox";
|
||||
import { IconChevronLeft, IconChevronRight, IconZoomIn } from "@tabler/icons-react";
|
||||
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
@@ -26,11 +27,18 @@ export function WorkshopScreenshots({ client, submissionId, title, screenshots }
|
||||
|
||||
if (screenshots.length === 0) return null;
|
||||
|
||||
const active = screenshots[Math.min(index, screenshots.length - 1)];
|
||||
const currentIndex = Math.min(index, screenshots.length - 1);
|
||||
const active = screenshots[currentIndex];
|
||||
if (!active) return null;
|
||||
|
||||
const alt = (position: number) => t("screenshotAlt", { title, count: position + 1 });
|
||||
const step = (delta: number) => setIndex((current) => (current + delta + screenshots.length) % screenshots.length);
|
||||
const slides = screenshots.map((file, position) => ({
|
||||
src: client.fileUrl(submissionId, file),
|
||||
thumbSrc: client.fileUrl(submissionId, file, "192x128"),
|
||||
alt: alt(position),
|
||||
caption: alt(position),
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -38,7 +46,7 @@ export function WorkshopScreenshots({ client, submissionId, title, screenshots }
|
||||
<UnstyledButton onClick={zoomControls.open} w="100%" aria-label={t("zoomScreenshot")}>
|
||||
<Image
|
||||
src={client.fileUrl(submissionId, active, "1200x800")}
|
||||
alt={alt(index)}
|
||||
alt={alt(currentIndex)}
|
||||
fit="contain"
|
||||
h={{ base: 220, sm: 380 }}
|
||||
bg="var(--mantine-color-default-hover)"
|
||||
@@ -97,7 +105,7 @@ export function WorkshopScreenshots({ client, submissionId, title, screenshots }
|
||||
key={file}
|
||||
onClick={() => setIndex(position)}
|
||||
aria-label={alt(position)}
|
||||
aria-pressed={position === index}
|
||||
aria-pressed={position === currentIndex}
|
||||
>
|
||||
<Box
|
||||
w={96}
|
||||
@@ -106,10 +114,10 @@ export function WorkshopScreenshots({ client, submissionId, title, screenshots }
|
||||
overflow: "hidden",
|
||||
borderRadius: "var(--mantine-radius-sm)",
|
||||
outline:
|
||||
position === index
|
||||
position === currentIndex
|
||||
? "2px solid var(--mantine-primary-color-filled)"
|
||||
: "1px solid var(--mantine-color-default-border)",
|
||||
opacity: position === index ? 1 : 0.65,
|
||||
opacity: position === currentIndex ? 1 : 0.65,
|
||||
}}
|
||||
>
|
||||
<Image src={client.fileUrl(submissionId, file, "192x128")} alt="" h={64} w={96} fit="cover" />
|
||||
@@ -119,23 +127,30 @@ export function WorkshopScreenshots({ client, submissionId, title, screenshots }
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<Modal
|
||||
<Lightbox
|
||||
opened={zoomOpened}
|
||||
onClose={zoomControls.close}
|
||||
size="auto"
|
||||
centered
|
||||
padding="xs"
|
||||
title={alt(index)}
|
||||
styles={{ body: { display: "flex", justifyContent: "center" } }}
|
||||
>
|
||||
<Image
|
||||
src={client.fileUrl(submissionId, active)}
|
||||
alt={alt(index)}
|
||||
fit="contain"
|
||||
mah="80dvh"
|
||||
style={{ maxWidth: "min(90vw, 1400px)" }}
|
||||
/>
|
||||
</Modal>
|
||||
slides={slides}
|
||||
currentIndex={currentIndex}
|
||||
onIndexChange={setIndex}
|
||||
withNavigation={screenshots.length > 1}
|
||||
withThumbnails={screenshots.length > 1}
|
||||
withZoom
|
||||
withKeyboardEvents
|
||||
returnFocus
|
||||
loop={screenshots.length > 1}
|
||||
labels={{
|
||||
lightboxLabel: t("screenshotViewer", { title }),
|
||||
slideLabel: (position) => alt(position - 1),
|
||||
slidesLabel: t("screenshotsLabel", { title }),
|
||||
thumbnailLabel: (position) => alt(position - 1),
|
||||
previousSlideLabel: t("previousScreenshot"),
|
||||
nextSlideLabel: t("nextScreenshot"),
|
||||
showThumbnailsLabel: t("showScreenshotThumbnails"),
|
||||
hideThumbnailsLabel: t("hideScreenshotThumbnails"),
|
||||
closeLabel: t("closeScreenshotViewer"),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user