💄 Flex layout and text fitting

This commit is contained in:
Tagaishi
2023-08-10 23:49:55 +02:00
parent d5f74eb4bf
commit a14a9d4601
2 changed files with 107 additions and 124 deletions

View File

@@ -6,6 +6,14 @@
"title": "Settings for DNS Hole summary", "title": "Settings for DNS Hole summary",
"usePiHoleColors": { "usePiHoleColors": {
"label": "Use colors from PiHole" "label": "Use colors from PiHole"
},
"layout": {
"label": "Layout",
"data": {
"grid": "2 by 2",
"row": "Horizontal",
"column": "Vertical"
}
} }
} }
}, },

View File

@@ -1,4 +1,5 @@
import { Card, Center, Container, Stack, Text } from '@mantine/core'; import { Card, Center, Container, Flex, Text } from '@mantine/core';
import { useElementSize } from '@mantine/hooks';
import { import {
IconAd, IconAd,
IconBarrierBlock, IconBarrierBlock,
@@ -7,6 +8,7 @@ import {
IconWorldWww, IconWorldWww,
} from '@tabler/icons-react'; } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import React from 'react';
import { useConfigContext } from '~/config/provider'; import { useConfigContext } from '~/config/provider';
import { api } from '~/utils/api'; import { api } from '~/utils/api';
@@ -23,10 +25,15 @@ const definition = defineWidget({
type: 'switch', type: 'switch',
defaultValue: true, defaultValue: true,
}, },
layout: {
type: 'select',
defaultValue: 'grid',
data: [{ value: 'grid' }, { value: 'row' }, { value: 'column' }],
},
}, },
gridstack: { gridstack: {
minWidth: 2, minWidth: 2,
minHeight: 2, minHeight: 1,
maxWidth: 12, maxWidth: 12,
maxHeight: 12, maxHeight: 12,
}, },
@@ -42,6 +49,7 @@ interface DnsHoleSummaryWidgetProps {
function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) { function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) {
const { t } = useTranslation('modules/dns-hole-summary'); const { t } = useTranslation('modules/dns-hole-summary');
const { isInitialLoading, data } = useDnsHoleSummeryQuery(); const { isInitialLoading, data } = useDnsHoleSummeryQuery();
const flexLayout = widget.properties.layout as 'row' | 'column';
if (isInitialLoading || !data) { if (isInitialLoading || !data) {
return <WidgetLoading />; return <WidgetLoading />;
@@ -49,134 +57,46 @@ function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) {
return ( return (
<Container <Container
display="grid"
h="100%" h="100%"
p={0}
style={{ style={{
gridTemplateColumns: '1fr 1fr', gridTemplateColumns: '1fr 1fr',
gridTemplateRows: '1fr 1fr', gridTemplateRows: '1fr 1fr',
marginLeft: -20, display: flexLayout?.includes('grid') ? 'grid' : 'flex',
marginRight: -20, flexDirection: flexLayout,
}} }}
> >
<Card <StatCard
m="xs" icon={<IconBarrierBlock />}
sx={(theme) => { number={formatNumber(data.adsBlockedToday, 2)}
if (!widget.properties.usePiHoleColors) { label={t('card.metrics.queriesBlockedToday') as string}
return {}; color={
} widget.properties.usePiHoleColors ? 'rgba(240, 82, 60, 0.4)' : 'rgba(96, 96, 96, 0.1)'
}
if (theme.colorScheme === 'dark') { />
return { <StatCard
backgroundColor: 'rgba(240, 82, 60, 0.4)', icon={<IconPercentage />}
}; number={(data.adsBlockedTodayPercentage * 100).toFixed(2) + '%'}
} color={
widget.properties.usePiHoleColors ? 'rgba(255, 165, 20, 0.4)' : 'rgba(96, 96, 96, 0.1)'
return { }
backgroundColor: 'rgba(240, 82, 60, 0.2)', />
}; <StatCard
}} icon={<IconSearch />}
withBorder number={formatNumber(data.dnsQueriesToday, 2)}
> label={t('card.metrics.queriesToday') as string}
<Center h="100%"> color={
<Stack align="center" spacing="xs"> widget.properties.usePiHoleColors ? 'rgba(0, 175, 218, 0.4)' : 'rgba(96, 96, 96, 0.1)'
<IconBarrierBlock size={30} /> }
<div> />
<Text align="center">{formatNumber(data.adsBlockedToday, 0)}</Text> <StatCard
<Text align="center" lh={1.2} size="sm"> icon={<IconWorldWww />}
{t('card.metrics.queriesBlockedToday')} number={formatNumber(data.domainsBeingBlocked, 2)}
</Text> label={t('card.metrics.domainsOnAdlist') as string}
</div> color={
</Stack> widget.properties.usePiHoleColors ? 'rgba(0, 176, 96, 0.4)' : 'rgba(96, 96, 96, 0.1)'
</Center> }
</Card> />
<Card
m="xs"
sx={(theme) => {
if (!widget.properties.usePiHoleColors) {
return {};
}
if (theme.colorScheme === 'dark') {
return {
backgroundColor: 'rgba(255, 165, 20, 0.4)',
};
}
return {
backgroundColor: 'rgba(255, 165, 20, 0.4)',
};
}}
withBorder
>
<Center h="100%">
<Stack align="center" spacing="xs">
<IconPercentage size={30} />
<Text align="center">{(data.adsBlockedTodayPercentage * 100).toFixed(2)}%</Text>
</Stack>
</Center>
</Card>
<Card
m="xs"
sx={(theme) => {
if (!widget.properties.usePiHoleColors) {
return {};
}
if (theme.colorScheme === 'dark') {
return {
backgroundColor: 'rgba(0, 175, 218, 0.4)',
};
}
return {
backgroundColor: 'rgba(0, 175, 218, 0.4)',
};
}}
withBorder
>
<Center h="100%">
<Stack align="center" spacing="xs">
<IconSearch size={30} />
<div>
<Text align="center">{formatNumber(data.dnsQueriesToday, 3)}</Text>
<Text align="center" lh={1.2} size="sm">
{t('card.metrics.queriesToday')}
</Text>
</div>
</Stack>
</Center>
</Card>
<Card
m="xs"
sx={(theme) => {
if (!widget.properties.usePiHoleColors) {
return {};
}
if (theme.colorScheme === 'dark') {
return {
backgroundColor: 'rgba(0, 176, 96, 0.4)',
};
}
return {
backgroundColor: 'rgba(0, 176, 96, 0.4)',
};
}}
withBorder
>
<Center h="100%">
<Stack align="center" spacing="xs">
<IconWorldWww size={30} />
<div>
<Text align="center">{formatNumber(data.domainsBeingBlocked, 0)}</Text>
<Text align="center" lh={1.2} size="sm">
{t('card.metrics.domainsOnAdlist')}
</Text>
</div>
</Stack>
</Center>
</Card>
</Container> </Container>
); );
} }
@@ -194,4 +114,59 @@ export const useDnsHoleSummeryQuery = () => {
); );
}; };
interface StatCardProps {
icon: JSX.Element;
number: string;
label?: string;
color?: string;
}
const StatCard = ({ icon, number, label, color }: StatCardProps) => {
const { ref, height, width } = useElementSize();
return (
<Card
ref={ref}
m="0.4rem"
p="0.2rem"
sx={{
backgroundColor: color,
flex: '1',
}}
withBorder
>
<Center h="100%" w="100%">
<Flex
h="100%"
w="100%"
align="center"
justify="space-evenly"
direction={width > height + 20 ? 'row' : 'column'}
>
{React.cloneElement(icon, {
size: 30,
style: { margin: '0 10' }
})}
<div
style={{
flex: '1',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
}}
>
<Text align="center" lh={1.2} size="md" weight="bold">
{number}
</Text>
{label && (
<Text align="center" lh={1.2} size="0.75rem">
{label}
</Text>
)}
</div>
</Flex>
</Center>
</Card>
);
};
export default definition; export default definition;