fix(dns-controls): improve responsive styles (#2534)

* fix(dns-controls): improve responsive styles

* fix: further dns-control improvements
This commit is contained in:
Meier Lukas
2025-03-08 20:54:35 +01:00
committed by GitHub
parent 7ec6fbc376
commit c0e1355d7f
2 changed files with 183 additions and 111 deletions

View File

@@ -3,7 +3,20 @@
import "../../widgets-common.css"; import "../../widgets-common.css";
import { useState } from "react"; import { useState } from "react";
import { ActionIcon, Badge, Button, Card, Flex, ScrollArea, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core"; import {
ActionIcon,
Badge,
Button,
Card,
Flex,
Group,
Indicator,
ScrollArea,
Stack,
Text,
Tooltip,
UnstyledButton,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks"; import { useDisclosure } from "@mantine/hooks";
import { IconCircleFilled, IconClockPause, IconPlayerPlay, IconPlayerStop } from "@tabler/icons-react"; import { IconCircleFilled, IconClockPause, IconPlayerPlay, IconPlayerStop } from "@tabler/icons-react";
import combineClasses from "clsx"; import combineClasses from "clsx";
@@ -30,6 +43,7 @@ export default function DnsHoleControlsWidget({
options, options,
integrationIds, integrationIds,
isEditMode, isEditMode,
width,
}: WidgetComponentProps<typeof widgetKind>) { }: WidgetComponentProps<typeof widgetKind>) {
const board = useRequiredBoard(); const board = useRequiredBoard();
// DnsHole integrations with interaction permissions // DnsHole integrations with interaction permissions
@@ -177,10 +191,10 @@ export default function DnsHoleControlsWidget({
const controlAllButtonsVisible = options.showToggleAllButtons && integrationsWithInteractions.length > 0; const controlAllButtonsVisible = options.showToggleAllButtons && integrationsWithInteractions.length > 0;
return ( return (
<Flex <Stack
className="dns-hole-controls-stack" className="dns-hole-controls-stack"
justify="space-between"
h="100%" h="100%"
direction="column"
p="sm" p="sm"
gap="sm" gap="sm"
style={{ pointerEvents: isEditMode ? "none" : undefined }} style={{ pointerEvents: isEditMode ? "none" : undefined }}
@@ -189,23 +203,25 @@ export default function DnsHoleControlsWidget({
<Flex className="dns-hole-controls-buttons" gap="sm"> <Flex className="dns-hole-controls-buttons" gap="sm">
<Tooltip label={t("widget.dnsHoleControls.controls.enableAll")}> <Tooltip label={t("widget.dnsHoleControls.controls.enableAll")}>
<Button <Button
size="xs"
p={0}
className="dns-hole-controls-enable-all-button" className="dns-hole-controls-enable-all-button"
onClick={() => integrationsSummaries.disabled.forEach((integrationId) => toggleDns(integrationId))} onClick={() => integrationsSummaries.disabled.forEach((integrationId) => toggleDns(integrationId))}
disabled={integrationsSummaries.disabled.length === 0} disabled={integrationsSummaries.disabled.length === 0}
variant="light" variant="light"
color="green" color="green"
h="fit-content"
p="xs"
bd={0} bd={0}
radius={board.itemRadius} radius={board.itemRadius}
flex={1} flex={1}
> >
<IconPlayerPlay className="dns-hole-controls-enable-all-icon" size={24} /> <IconPlayerPlay className="dns-hole-controls-enable-all-icon" size={16} />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip label={t("widget.dnsHoleControls.controls.setTimer")}> <Tooltip label={t("widget.dnsHoleControls.controls.setTimer")}>
<Button <Button
size="xs"
p={0}
className="dns-hole-controls-timer-all-button" className="dns-hole-controls-timer-all-button"
onClick={() => { onClick={() => {
setSelectedIntegrationIds(integrationsSummaries.enabled); setSelectedIntegrationIds(integrationsSummaries.enabled);
@@ -214,36 +230,34 @@ export default function DnsHoleControlsWidget({
disabled={integrationsSummaries.enabled.length === 0} disabled={integrationsSummaries.enabled.length === 0}
variant="light" variant="light"
color="yellow" color="yellow"
h="fit-content"
p="xs"
bd={0} bd={0}
radius={board.itemRadius} radius={board.itemRadius}
flex={1} flex={1}
> >
<IconClockPause className="dns-hole-controls-timer-all-icon" size={24} /> <IconClockPause className="dns-hole-controls-timer-all-icon" size={16} />
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip label={t("widget.dnsHoleControls.controls.disableAll")}> <Tooltip label={t("widget.dnsHoleControls.controls.disableAll")}>
<Button <Button
size="xs"
p={0}
className="dns-hole-controls-disable-all-button" className="dns-hole-controls-disable-all-button"
onClick={() => integrationsSummaries.enabled.forEach((integrationId) => toggleDns(integrationId))} onClick={() => integrationsSummaries.enabled.forEach((integrationId) => toggleDns(integrationId))}
disabled={integrationsSummaries.enabled.length === 0} disabled={integrationsSummaries.enabled.length === 0}
variant="light" variant="light"
color="red" color="red"
h="fit-content"
p="xs"
bd={0} bd={0}
radius={board.itemRadius} radius={board.itemRadius}
flex={1} flex={1}
> >
<IconPlayerStop className="dns-hole-controls-disable-all-icon" size={24} /> <IconPlayerStop className="dns-hole-controls-disable-all-icon" size={16} />
</Button> </Button>
</Tooltip> </Tooltip>
</Flex> </Flex>
)} )}
<ScrollArea className="dns-hole-controls-integration-list-scroll-area flexed-scroll-area"> <ScrollArea.Autosize className="dns-hole-controls-integration-list-scroll-area flexed-scroll-area">
<Stack <Stack
className="dns-hole-controls-integration-list" className="dns-hole-controls-integration-list"
gap="sm" gap="sm"
@@ -260,10 +274,11 @@ export default function DnsHoleControlsWidget({
open={open} open={open}
t={t} t={t}
hasIconColor={board.iconColor !== null} hasIconColor={board.iconColor !== null}
rootWidth={width}
/> />
))} ))}
</Stack> </Stack>
</ScrollArea> </ScrollArea.Autosize>
<TimerModal <TimerModal
opened={opened} opened={opened}
@@ -271,7 +286,7 @@ export default function DnsHoleControlsWidget({
selectedIntegrationIds={selectedIntegrationIds} selectedIntegrationIds={selectedIntegrationIds}
disableDns={disableDns} disableDns={disableDns}
/> />
</Flex> </Stack>
); );
} }
@@ -283,6 +298,7 @@ interface ControlsCardProps {
open: () => void; open: () => void;
t: TranslationFunction; t: TranslationFunction;
hasIconColor: boolean; hasIconColor: boolean;
rootWidth: number;
} }
const ControlsCard: React.FC<ControlsCardProps> = ({ const ControlsCard: React.FC<ControlsCardProps> = ({
@@ -293,6 +309,7 @@ const ControlsCard: React.FC<ControlsCardProps> = ({
open, open,
t, t,
hasIconColor, hasIconColor,
rootWidth,
}) => { }) => {
const isConnected = useIntegrationConnected(data.integration.updatedAt, { timeout: 30000 }); const isConnected = useIntegrationConnected(data.integration.updatedAt, { timeout: 30000 });
const isEnabled = data.summary.status ? data.summary.status === "enabled" : undefined; const isEnabled = data.summary.status ? data.summary.status === "enabled" : undefined;
@@ -302,95 +319,161 @@ const ControlsCard: React.FC<ControlsCardProps> = ({
const board = useRequiredBoard(); const board = useRequiredBoard();
const iconUrl = integrationDefs[data.integration.kind].iconUrl; const iconUrl = integrationDefs[data.integration.kind].iconUrl;
const layout = rootWidth < 256 ? "sm" : "md";
return ( return (
<Card <Indicator
className={combineClasses( disabled={!isConnected || layout !== "sm"}
"dns-hole-controls-integration-item-outer-shell", color={dnsLightStatus(isEnabled)}
`dns-hole-controls-integration-item-${data.integration.id}`, position="top-end"
`dns-hole-controls-integration-item-${data.integration.name}`, offset={14}
classes.card,
)}
key={data.integration.id}
p="sm"
py={8}
radius={board.itemRadius}
> >
<Flex className="dns-hole-controls-item-container" gap="md" align="center" direction="row"> <Card
<MaskedOrNormalImage className={combineClasses(
imageUrl={iconUrl} "dns-hole-controls-integration-item-outer-shell",
hasColor={hasIconColor} `dns-hole-controls-integration-item-${data.integration.id}`,
alt={data.integration.name} `dns-hole-controls-integration-item-${data.integration.name}`,
className="dns-hole-controls-item-icon" classes.card,
style={{ )}
height: 30, key={data.integration.id}
width: 30, p="sm"
filter: !isConnected ? "grayscale(100%)" : undefined, py={8}
}} radius={board.itemRadius}
/> >
<Flex className="dns-hole-controls-item-data-stack" direction="column" gap={5}> <Flex className="dns-hole-controls-item-container" gap="md" align="center" direction="row" w="100%">
<Text className="dns-hole-controls-item-integration-name" fz="md" fw={"bold"}> {layout === "md" && (
{data.integration.name} <MaskedOrNormalImage
</Text> imageUrl={iconUrl}
<Flex className="dns-hole-controls-item-controls" direction="row" gap="lg"> hasColor={hasIconColor}
<UnstyledButton alt={data.integration.name}
className="dns-hole-controls-item-toggle-button" className="dns-hole-controls-item-icon"
disabled={!controlEnabled} style={{
display="contents" height: 30,
style={{ cursor: controlEnabled ? "pointer" : "default" }} width: 30,
onClick={() => toggleDns(data.integration.id)} filter: !isConnected ? "grayscale(100%)" : undefined,
> }}
<Badge />
className={`dns-hole-controls-item-toggle-button-styling${controlEnabled ? " hoverable-component clickable-component" : ""}`} )}
bd="1px solid var(--border-color)"
px="sm" <Flex className="dns-hole-controls-item-data-stack" direction="column" w="100%" gap={5}>
h="lg" <Group gap="xs" align="center" wrap="nowrap">
color="var(--background-color)" {layout === "sm" && (
c="var(--mantine-color-text)" <MaskedOrNormalImage
styles={{ section: { marginInlineEnd: "sm" }, root: { cursor: "inherit" } }} imageUrl={iconUrl}
leftSection={ hasColor={hasIconColor}
isConnected && ( alt={data.integration.name}
<IconCircleFilled className="dns-hole-controls-item-icon"
className="dns-hole-controls-item-status-icon" style={{
color={dnsLightStatus(isEnabled)} height: 16,
size={16} width: 16,
/> filter: !isConnected ? "grayscale(100%)" : undefined,
) }}
} />
> )}
{t( <Text className="dns-hole-controls-item-integration-name" fz="sm">
`widget.dnsHoleControls.controls.${ {data.integration.name}
!isConnected </Text>
? "disconnected" </Group>
: typeof isEnabled === "undefined" <Flex className="dns-hole-controls-item-controls" direction="row" gap="sm" w="100%">
? "processing" {layout === "sm" && (
: isEnabled <Group gap="xs" grow wrap="nowrap" w="100%">
? "enabled" {!isEnabled ? (
: "disabled" <ActionIcon
}`, onClick={() => toggleDns(data.integration.id)}
)} disabled={!controlEnabled}
</Badge> size="sm"
</UnstyledButton> color="green"
variant="light"
>
<IconPlayerPlay size={12} />
</ActionIcon>
) : (
<ActionIcon
onClick={() => toggleDns(data.integration.id)}
disabled={!controlEnabled}
size="sm"
color="red"
variant="light"
>
<IconPlayerStop size={12} />
</ActionIcon>
)}
<ActionIcon
onClick={() => {
setSelectedIntegrationIds([data.integration.id]);
open();
}}
size="sm"
color="yellow"
variant="light"
>
<IconClockPause size={12} />
</ActionIcon>
</Group>
)}
{layout === "md" && (
<UnstyledButton
className="dns-hole-controls-item-toggle-button"
disabled={!controlEnabled}
display="contents"
style={{ cursor: controlEnabled ? "pointer" : "default" }}
onClick={() => toggleDns(data.integration.id)}
>
<Badge
className={`dns-hole-controls-item-toggle-button-styling${controlEnabled ? " hoverable-component clickable-component" : ""}`}
bd="1px solid var(--border-color)"
px="sm"
h="lg"
color="var(--background-color)"
c="var(--mantine-color-text)"
styles={{ section: { marginInlineEnd: "sm" }, root: { cursor: "inherit" } }}
leftSection={
isConnected && (
<IconCircleFilled
className="dns-hole-controls-item-status-icon"
color={dnsLightStatus(isEnabled)}
size={16}
/>
)
}
>
{t(
`widget.dnsHoleControls.controls.${
!isConnected
? "disconnected"
: typeof isEnabled === "undefined"
? "processing"
: isEnabled
? "enabled"
: "disabled"
}`,
)}
</Badge>
</UnstyledButton>
)}
</Flex>
</Flex> </Flex>
{layout === "md" && (
<ActionIcon
className="dns-hole-controls-item-timer-button"
display={isInteractPermitted ? undefined : "none"}
disabled={!controlEnabled || !isEnabled}
color="yellow"
size={30}
radius={board.itemRadius}
bd={0}
ms={"auto"}
variant="subtle"
onClick={() => {
setSelectedIntegrationIds([data.integration.id]);
open();
}}
>
<IconClockPause className="dns-hole-controls-item-timer-icon" size={20} />
</ActionIcon>
)}
</Flex> </Flex>
<ActionIcon </Card>
className="dns-hole-controls-item-timer-button" </Indicator>
display={isInteractPermitted ? undefined : "none"}
disabled={!controlEnabled || !isEnabled}
color="yellow"
size={30}
radius={board.itemRadius}
bd={0}
ms={"auto"}
variant="subtle"
onClick={() => {
setSelectedIntegrationIds([data.integration.id]);
open();
}}
>
<IconClockPause className="dns-hole-controls-item-timer-icon" size={20} />
</ActionIcon>
</Flex>
</Card>
); );
}; };

View File

@@ -62,14 +62,3 @@
.text-flash { .text-flash {
animation: glow 1s ease-in-out; animation: glow 1s ease-in-out;
} }
/*To apply to any ScrollArea that we want to flex. Same weird workaround as before*/
.flexed-scroll-area {
height: 100%;
.mantine-ScrollArea-viewport {
& div[style="min-width: 100%; display: table;"] {
display: flex !important;
height: 100%;
}
}
}