💄 Flex layout and text fitting
This commit is contained in:
@@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user