2b7c087ac8
* feat: limit badges to 2 with hover tooltip in UserListTable Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: reuse LimitedBadges component with clickable popover for mobile Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: move LimitedBadges to components/ui with hover+click support Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: fix Biome lint issues in LimitedBadges and UserListTable - Refactor LimitedBadges to concrete component with BadgeItem type - Move exports to end of files to comply with useExportsLast rule - Add explicit types to callback parameters for useExplicitType rule - Convert nested ternary to if-else for filterType calculation - Remove unused imports (Row, Table types) - Update ResponseValueCell and UserListTable to use new LimitedBadges API Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * fix: add proper types for filterType and getFacetedUniqueValues - Add FilterType import from @calcom/types/data-table - Add FacetedValue import from @calcom/features/data-table - Type filterType as FilterType to allow reassignment to different ColumnFilterType values - Type getFacetedUniqueValues return as Map<FacetedValue, number> Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * add vertical gap * fix: address code review feedback for LimitedBadges - Add index to id for unique keys in ResponseValueCell.tsx - Restore orange variant for group options in UserListTable.tsx - Fix useMemo dependency array (add t, remove dispatch) - Fix import ordering with biome - Convert ternary operators to if-else statements for biome compliance Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: remove id from BadgeItem type, use label as key - Remove id field from BadgeItem type in LimitedBadges - Use label as React key instead of id - Remove unused rowId parameter from ResponseValueCell - Update all consumers to not pass id field Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * refactor: use index for key instead of label in LimitedBadges Co-Authored-By: eunjae@cal.com <hey@eunjae.dev> * Revert "refactor: use index for key instead of label in LimitedBadges" This reverts commit 1daaac47e596fd6b5f5583847faa10b131783349. --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
110 lines
2.9 KiB
TypeScript
110 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
|
|
import { Badge } from "@calcom/ui/components/badge";
|
|
import { Button } from "@calcom/ui/components/button";
|
|
import { Popover, PopoverContent, PopoverTrigger } from "@calcom/ui/components/popover";
|
|
import { useCallback, useMemo, useState } from "react";
|
|
|
|
const MAX_VISIBLE_BADGES = 2;
|
|
|
|
type BadgeItem = {
|
|
label: string;
|
|
variant?:
|
|
| "default"
|
|
| "warning"
|
|
| "orange"
|
|
| "success"
|
|
| "green"
|
|
| "gray"
|
|
| "blue"
|
|
| "red"
|
|
| "error"
|
|
| "grayWithoutHover"
|
|
| "purple";
|
|
onClick?: () => void;
|
|
};
|
|
|
|
type LimitedBadgesProps = {
|
|
items: BadgeItem[];
|
|
maxVisible?: number;
|
|
className?: string;
|
|
};
|
|
|
|
function LimitedBadges({
|
|
items,
|
|
maxVisible = MAX_VISIBLE_BADGES,
|
|
className,
|
|
}: LimitedBadgesProps): JSX.Element | null {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
|
|
const { visibleItems, hiddenItems } = useMemo(
|
|
() => ({
|
|
visibleItems: items.slice(0, maxVisible),
|
|
hiddenItems: items.slice(maxVisible),
|
|
}),
|
|
[items, maxVisible]
|
|
);
|
|
|
|
const handleMouseEnter = useCallback(() => {
|
|
if (!isMobile) {
|
|
setIsOpen(true);
|
|
}
|
|
}, [isMobile]);
|
|
|
|
const handleMouseLeave = useCallback(() => {
|
|
if (!isMobile) {
|
|
setIsOpen(false);
|
|
}
|
|
}, [isMobile]);
|
|
|
|
if (items.length === 0) return null;
|
|
|
|
const hasHiddenItems = hiddenItems.length > 0;
|
|
|
|
return (
|
|
<div className={`flex flex-wrap items-center gap-x-1 gap-y-1 ${className || ""}`}>
|
|
{visibleItems.map((item) => (
|
|
<Badge key={item.label} variant={item.variant || "gray"} onClick={item.onClick}>
|
|
{item.label}
|
|
</Badge>
|
|
))}
|
|
{hasHiddenItems && (
|
|
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
color="minimal"
|
|
className="h-auto p-0 border-0 hover:border-0"
|
|
aria-label={`Show ${hiddenItems.length} more items`}
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={handleMouseLeave}>
|
|
<Badge variant="gray">+{hiddenItems.length}</Badge>
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent
|
|
side="bottom"
|
|
align="start"
|
|
className="w-fit p-2"
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={handleMouseLeave}>
|
|
<div className="flex flex-col gap-1">
|
|
{hiddenItems.map((item) => (
|
|
<span
|
|
key={item.label}
|
|
className="text-default cursor-pointer text-sm hover:text-emphasis"
|
|
onClick={item.onClick}>
|
|
{item.label}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export { LimitedBadges };
|
|
export type { BadgeItem };
|