aboutsummaryrefslogtreecommitdiff
path: root/src/components/common/FilterLink.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/common/FilterLink.tsx')
-rw-r--r--src/components/common/FilterLink.tsx49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/components/common/FilterLink.tsx b/src/components/common/FilterLink.tsx
new file mode 100644
index 0000000..d719a37
--- /dev/null
+++ b/src/components/common/FilterLink.tsx
@@ -0,0 +1,49 @@
+import { Icon, Row, Text } from '@umami/react-zen';
+import Link from 'next/link';
+import { type HTMLAttributes, type ReactNode, useState } from 'react';
+import { useMessages, useNavigation } from '@/components/hooks';
+import { ExternalLink } from '@/components/icons';
+
+export interface FilterLinkProps extends HTMLAttributes<HTMLDivElement> {
+ type: string;
+ value: string;
+ label?: string;
+ icon?: ReactNode;
+ externalUrl?: string;
+}
+
+export function FilterLink({ type, value, label, externalUrl, icon }: FilterLinkProps) {
+ const [showLink, setShowLink] = useState(false);
+ const { formatMessage, labels } = useMessages();
+ const { updateParams, query } = useNavigation();
+ const active = query[type] !== undefined;
+ const selected = query[type] === value;
+
+ return (
+ <Row
+ alignItems="center"
+ gap
+ fontWeight={active && selected ? 'bold' : undefined}
+ color={active && !selected ? 'muted' : undefined}
+ onMouseOver={() => setShowLink(true)}
+ onMouseOut={() => setShowLink(false)}
+ >
+ {icon}
+ {!value && `(${label || formatMessage(labels.unknown)})`}
+ {value && (
+ <Text title={label || value} truncate>
+ <Link href={updateParams({ [type]: `eq.${value}` })} replace>
+ {label || value}
+ </Link>
+ </Text>
+ )}
+ {externalUrl && showLink && (
+ <a href={externalUrl} target="_blank" rel="noreferrer noopener">
+ <Icon color="muted">
+ <ExternalLink />
+ </Icon>
+ </a>
+ )}
+ </Row>
+ );
+}