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

View File

@@ -62,14 +62,3 @@
.text-flash {
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%;
}
}
}