File size: 3,684 Bytes
eb846d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import React, { ReactNode } from 'react';
import { cn } from '@/utils/cn';

interface ToggleGroupItemProps {
  value: string;
  isSelected: boolean;
  onClick: () => void;
  children: ReactNode;
}

export const ToggleGroupItem: React.FC<ToggleGroupItemProps> = ({
  value,
  isSelected,
  onClick,
  children
}) => {
  return (
    <button
      type="button"
      role="checkbox"
      aria-checked={isSelected}
      className={cn(
        "flex w-full items-center justify-between p-2 rounded transition-colors cursor-pointer",
        isSelected 
          ? "bg-blue-50 text-blue-700 hover:bg-blue-100 border-l-4 border-blue-500" 
          : "hover:bg-gray-50 text-gray-700"
      )}
      onClick={onClick}
    >
      <span className="flex items-center">
        {children}
      </span>
      {isSelected && (
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5 text-blue-500">
          <path fillRule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clipRule="evenodd" />
        </svg>
      )}
    </button>
  );
};

interface ToggleGroupProps {
  label: string;
  helpText?: string;
  noOptionsText?: string;
  values: string[];
  options: { value: string; label: string }[];
  onChange: (values: string[]) => void;
  className?: string;
}

export const ToggleGroup: React.FC<ToggleGroupProps> = ({
  label,
  helpText,
  noOptionsText = "No options available",
  values,
  options,
  onChange,
  className
}) => {
  const handleToggle = (value: string) => {
    const isSelected = values.includes(value);
    if (isSelected) {
      onChange(values.filter(v => v !== value));
    } else {
      onChange([...values, value]);
    }
  };

  return (
    <div className={className}>
      <label className="block text-gray-700 text-sm font-bold mb-2">
        {label}
      </label>
      <div className="border rounded shadow max-h-60 overflow-y-auto">
        {options.length === 0 ? (
          <p className="text-gray-500 text-sm p-3">{noOptionsText}</p>
        ) : (
          <div className="space-y-1 p-1">
            {options.map(option => (
              <ToggleGroupItem
                key={option.value}
                value={option.value}
                isSelected={values.includes(option.value)}
                onClick={() => handleToggle(option.value)}
              >
                {option.label}
              </ToggleGroupItem>
            ))}
          </div>
        )}
      </div>
      {helpText && (
        <p className="text-xs text-gray-500 mt-1">
          {helpText}
        </p>
      )}
    </div>
  );
};

interface SwitchProps {
  checked: boolean;
  onCheckedChange: (checked: boolean) => void;
  disabled?: boolean;
}

export const Switch: React.FC<SwitchProps> = ({
  checked,
  onCheckedChange,
  disabled = false
}) => {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={checked}
      disabled={disabled}
      className={cn(
        "relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500",
        checked ? "bg-blue-600" : "bg-gray-200",
        disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
      )}
      onClick={() => !disabled && onCheckedChange(!checked)}
    >
      <span
        className={cn(
          "inline-block h-4 w-4 transform rounded-full bg-white transition-transform",
          checked ? "translate-x-6" : "translate-x-1"
        )}
      />
    </button>
  );
};