/**
* Size presets for arrow polygon clip-paths and spacing.
* sm: compact tags for list items
* lg: larger tags for article detail page headers
*/
import type { FunctionComponent, ComponentChildren } from 'preact';
const SIZE_STYLES = {
sm: {
borderClip: '[clip-path:polygon(0_0,calc(100%-6px)_0,100%_50%,calc(100%-6px)_100%,0_100%)]',
contentClip:
'[clip-path:polygon(1px_1px,calc(100%-7px)_1px,calc(100%-1px)_50%,calc(100%-7px)_calc(100%-1px),1px_calc(100%-1px))]',
contentSize: 'text-[0.65rem] lg:text-xs pl-[6px] pr-[12px] pt-[.2em] pb-[.2em]',
},
lg: {
borderClip: '[clip-path:polygon(0_0,calc(100%-16px)_0,100%_50%,calc(100%-16px)_100%,0_100%)]',
contentClip:
'[clip-path:polygon(1px_1px,calc(100%-17px)_1px,calc(100%-1px)_50%,calc(100%-17px)_calc(100%-1px),1px_calc(100%-1px))]',
contentSize: 'text-sm lg:text-base pl-[8px] pr-[22px] pt-[.25em] pb-[.25em]',
},
};
/**
* Renders label text with parenthesized portions in smaller font.
* Matches both full-width (...) and half-width (...) parentheses.
*/
function FormattedLabel({ label }: { label: string }) {
const parts = label.split(/(([^)]*)|\([^)]*\))/);
if (parts.length === 1) return <>{label}</>;
return (
<>
{parts.map((part, i) =>
/^[((]/.test(part) ? (
<span key={i} className="text-[.7em]">
{part}
</span>
) : (
<span key={i}>{part}</span>
),
)}
</>
);
}
interface ArrowTagProps {
label: string;
size?: 'sm' | 'lg';
suffix?: ComponentChildren;
active?: boolean;
}
/**
* Arrow polygon tag shape with bordered clip-path effect.
* Renders the visual tag element — callers handle wrapper (li, a, etc.)
*
* Hover/active inversion is self-contained via group-hover/group-active.
* Parent element must have `group` class for hover to work.
*/
export const ArrowTag: FunctionComponent<ArrowTagProps> = ({
label,
size = 'sm',
suffix,
active,
}) => {
const styles = SIZE_STYLES[size];
return (
<span className="relative inline-flex">
{/* Border layer */}
<span
className={`absolute inset-0 zd-tag-border ${active ? 'bg-zd-black' : 'bg-zd-white'} group-hover:bg-zd-black group-active:!bg-zd-active ${styles.borderClip}`}
/>
{/* Content layer with inset clip for border effect */}
<span
className={`relative inline-flex items-center font-thin no-underline text-shadow-none zd-tag-content ${active ? 'text-zd-black bg-zd-white' : 'text-zd-white bg-zd-black'} group-hover:text-zd-black group-hover:bg-zd-white group-active:!bg-zd-active ${styles.contentSize} ${styles.contentClip}`}
>
<span className="zd-hash">#</span>
<span>
<FormattedLabel label={label} />
{suffix}
</span>
</span>
</span>
);
};