Bin
2025-12-17 1d710f844b65d9bfdf986a71a3b924cd70598a41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
 * StateChip - Base interactive state display component
 *
 * A reusable chip component that displays a state with optional popover interaction.
 * This base component provides the visual display and interactive behavior,
 * while entity-specific implementations handle state logic and history.
 */
 
import { useState, type ReactNode } from "react";
import { Badge, Tooltip, Popover } from "@humansignal/ui";
 
export interface StateChipProps {
  /**
   * Visual label to display in the chip
   */
  label: string;
 
  /**
   * Description for the tooltip when not interactive
   */
  description?: string;
 
  /**
   * Tailwind CSS classes for styling the chip
   */
  className: string;
 
  /**
   * Whether the chip should be interactive (clickable with popover)
   */
  interactive?: boolean;
 
  /**
   * Content to display in the popover when interactive
   */
  popoverContent?: ReactNode;
 
  /**
   * Controlled open state for the popover
   */
  open?: boolean;
 
  /**
   * Callback when popover open state changes
   */
  onOpenChange?: (open: boolean) => void;
}
 
export function StateChip({
  label,
  description,
  className,
  interactive = false,
  popoverContent,
  open: controlledOpen,
  onOpenChange,
}: StateChipProps) {
  const [internalOpen, setInternalOpen] = useState(false);
 
  // Use controlled state if provided, otherwise use internal state
  const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
  const setOpen = onOpenChange || setInternalOpen;
 
  // Non-interactive chip - just show the badge with tooltip
  if (!interactive || !popoverContent) {
    return (
      <Tooltip title={description || label}>
        <span>
          <Badge className={className}>{label}</Badge>
        </span>
      </Tooltip>
    );
  }
 
  // Interactive chip with popover
  const trigger = (
    <button
      className="cursor-pointer outline-none hover:opacity-80 transition-opacity"
      onClick={(e) => {
        e.stopPropagation();
        setOpen(!isOpen);
      }}
      type="button"
      title="Click to view history"
    >
      <Badge className={className}>{label}</Badge>
    </button>
  );
 
  return (
    <Popover trigger={trigger} open={isOpen} onOpenChange={setOpen} align="start" sideOffset={8}>
      {popoverContent}
    </Popover>
  );
}