@hilum/ui 3.3.4 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/{chunk-MAHPAP65.js → chunk-RI6QJR2L.js} +6 -3
- package/dist/chunk-RI6QJR2L.js.map +1 -0
- package/dist/index.d.ts +800 -122
- package/dist/index.js +10458 -1119
- package/dist/index.js.map +1 -1
- package/dist/tokens.css +17 -0
- package/dist/tokens.d.ts +3 -0
- package/dist/tokens.js +1 -1
- package/llms.txt +18 -4
- package/package.json +19 -1
- package/src/components/accordion.tsx +746 -58
- package/src/components/alert-dialog.tsx +11 -5
- package/src/components/ask-user-questions.tsx +1366 -0
- package/src/components/badge.tsx +105 -24
- package/src/components/button.tsx +207 -38
- package/src/components/card.tsx +3 -2
- package/src/components/chat-message.tsx +116 -0
- package/src/components/checkbox-group.tsx +403 -0
- package/src/components/checkbox.tsx +6 -3
- package/src/components/color-input.tsx +3 -1
- package/src/components/color-picker.tsx +1992 -80
- package/src/components/dialog.tsx +141 -81
- package/src/components/dropdown-menu.tsx +21 -13
- package/src/components/dropdown.tsx +245 -0
- package/src/components/file-thumbnail.tsx +156 -0
- package/src/components/hover-card.tsx +2 -0
- package/src/components/input-copy.tsx +307 -0
- package/src/components/input-group.tsx +303 -108
- package/src/components/input-message.tsx +883 -0
- package/src/components/input-number.tsx +62 -21
- package/src/components/input.tsx +6 -3
- package/src/components/menu-item.tsx +156 -0
- package/src/components/mobile-drawer.tsx +50 -0
- package/src/components/nav-item.tsx +43 -0
- package/src/components/nav-menu.tsx +47 -0
- package/src/components/popover.tsx +3 -1
- package/src/components/radio-group.tsx +381 -35
- package/src/components/scroll-area.tsx +9 -2
- package/src/components/select.tsx +708 -121
- package/src/components/sheet.tsx +15 -6
- package/src/components/slider.tsx +1583 -20
- package/src/components/status-badge.tsx +2 -0
- package/src/components/switch.tsx +245 -28
- package/src/components/table.tsx +191 -77
- package/src/components/tabs-subtle.tsx +411 -0
- package/src/components/tabs.tsx +459 -49
- package/src/components/textarea.tsx +7 -3
- package/src/components/thinking-indicator.tsx +106 -0
- package/src/components/thinking-steps.tsx +382 -0
- package/src/components/tooltip.tsx +158 -33
- package/src/hooks/use-merge-split.tsx +359 -0
- package/src/hooks/use-proximity-hover.ts +208 -0
- package/src/index.ts +14 -0
- package/src/lib/elevated.tsx +52 -0
- package/src/lib/font-weight.ts +18 -0
- package/src/lib/icon-context.tsx +98 -0
- package/src/lib/icon-map.tsx +669 -0
- package/src/lib/interaction.ts +102 -0
- package/src/lib/scroll-fade.tsx +241 -0
- package/src/lib/shape-context.tsx +121 -0
- package/src/lib/springs.ts +30 -0
- package/src/lib/surface-classes.ts +43 -0
- package/src/lib/surface-context.tsx +17 -0
- package/src/tokens/tokens.ts +3 -0
- package/dist/chunk-MAHPAP65.js.map +0 -1
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Children,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
useEffect,
|
|
8
|
+
createContext,
|
|
9
|
+
useContext,
|
|
10
|
+
forwardRef,
|
|
11
|
+
isValidElement,
|
|
12
|
+
cloneElement,
|
|
13
|
+
type ReactNode,
|
|
14
|
+
type HTMLAttributes,
|
|
15
|
+
} from "react";
|
|
16
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
17
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
18
|
+
import { cn } from "../lib/utils";
|
|
19
|
+
import { spring } from "../lib/springs";
|
|
20
|
+
import { fontWeights } from "../lib/font-weight";
|
|
21
|
+
import { useProximityHover } from "../hooks/use-proximity-hover";
|
|
22
|
+
import { useMergeSplitBlocks, SelectionBackgrounds } from "../hooks/use-merge-split";
|
|
23
|
+
import { useShape } from "../lib/shape-context";
|
|
24
|
+
|
|
25
|
+
interface CheckboxGroupContextValue {
|
|
26
|
+
registerItem: (index: number, element: HTMLElement | null) => void;
|
|
27
|
+
activeIndex: number | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const CheckboxGroupContext = createContext<CheckboxGroupContextValue | null>(null);
|
|
31
|
+
|
|
32
|
+
function useCheckboxGroup() {
|
|
33
|
+
const ctx = useContext(CheckboxGroupContext);
|
|
34
|
+
if (!ctx) throw new Error("useCheckboxGroup must be used within a CheckboxGroup");
|
|
35
|
+
return ctx;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CheckboxGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
checkedIndices?: Set<number>;
|
|
41
|
+
options?: Array<{ value: string; label: string; disabled?: boolean }>;
|
|
42
|
+
value?: string[];
|
|
43
|
+
onValueChange?: (value: string[]) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
|
|
47
|
+
({ children, checkedIndices, options, value = [], onValueChange, className, ...props }, ref) => {
|
|
48
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
49
|
+
const groupIdCounter = useRef(0);
|
|
50
|
+
const prevGroupMap = useRef(new Map<number, number>());
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
activeIndex,
|
|
54
|
+
setActiveIndex,
|
|
55
|
+
itemRects,
|
|
56
|
+
sessionRef,
|
|
57
|
+
handlers,
|
|
58
|
+
registerItem,
|
|
59
|
+
measureItems,
|
|
60
|
+
} = useProximityHover(containerRef);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
measureItems();
|
|
64
|
+
}, [measureItems, children]);
|
|
65
|
+
|
|
66
|
+
// Group contiguous checked indices into runs with stable IDs
|
|
67
|
+
const runs: { start: number; end: number }[] = [];
|
|
68
|
+
const optionCheckedIndices = new Set(
|
|
69
|
+
options
|
|
70
|
+
?.map((option, index) => (value.includes(option.value) ? index : -1))
|
|
71
|
+
.filter((index) => index >= 0) ?? [],
|
|
72
|
+
);
|
|
73
|
+
const resolvedCheckedIndices = checkedIndices ?? optionCheckedIndices;
|
|
74
|
+
const sortedChecked = [...resolvedCheckedIndices].sort((a, b) => a - b);
|
|
75
|
+
for (const idx of sortedChecked) {
|
|
76
|
+
const last = runs[runs.length - 1];
|
|
77
|
+
if (last && idx === last.end + 1) {
|
|
78
|
+
last.end = idx;
|
|
79
|
+
} else {
|
|
80
|
+
runs.push({ start: idx, end: idx });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Assign stable IDs: reuse previous ID if any member overlaps
|
|
85
|
+
const usedIds = new Set<number>();
|
|
86
|
+
const newGroupMap = new Map<number, number>();
|
|
87
|
+
const checkedGroups = runs.map((run) => {
|
|
88
|
+
let stableId: number | null = null;
|
|
89
|
+
for (let i = run.start; i <= run.end; i++) {
|
|
90
|
+
const prevId = prevGroupMap.current.get(i);
|
|
91
|
+
if (prevId !== undefined && !usedIds.has(prevId)) {
|
|
92
|
+
stableId = prevId;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const id = stableId ?? ++groupIdCounter.current;
|
|
97
|
+
usedIds.add(id);
|
|
98
|
+
for (let i = run.start; i <= run.end; i++) {
|
|
99
|
+
newGroupMap.set(i, id);
|
|
100
|
+
}
|
|
101
|
+
return { ...run, id };
|
|
102
|
+
});
|
|
103
|
+
prevGroupMap.current = newGroupMap;
|
|
104
|
+
|
|
105
|
+
const [focusedIndex, setFocusedIndex] = useState<number | null>(null);
|
|
106
|
+
|
|
107
|
+
const activeRect = activeIndex !== null ? itemRects[activeIndex] : null;
|
|
108
|
+
const focusRect = focusedIndex !== null ? itemRects[focusedIndex] : null;
|
|
109
|
+
const isHoveringOther = activeIndex !== null && !resolvedCheckedIndices.has(activeIndex);
|
|
110
|
+
const shape = useShape();
|
|
111
|
+
|
|
112
|
+
// Selected backgrounds, with the merge/split boundary animation when one
|
|
113
|
+
// unchecked row bridges or splits two checked runs.
|
|
114
|
+
const blocks = useMergeSplitBlocks(checkedGroups, itemRects, shape.mergedRadius);
|
|
115
|
+
const renderedChildren =
|
|
116
|
+
options?.map((option, index) => (
|
|
117
|
+
<CheckboxItem
|
|
118
|
+
key={option.value}
|
|
119
|
+
index={index}
|
|
120
|
+
label={option.label}
|
|
121
|
+
checked={value.includes(option.value)}
|
|
122
|
+
onToggle={() => {
|
|
123
|
+
if (option.disabled) return;
|
|
124
|
+
const next = value.includes(option.value)
|
|
125
|
+
? value.filter((item) => item !== option.value)
|
|
126
|
+
: [...value, option.value];
|
|
127
|
+
onValueChange?.(next);
|
|
128
|
+
}}
|
|
129
|
+
aria-disabled={option.disabled || undefined}
|
|
130
|
+
/>
|
|
131
|
+
)) ??
|
|
132
|
+
Children.map(children, (child, index) => {
|
|
133
|
+
if (!isValidElement(child)) return child;
|
|
134
|
+
const childProps = child.props as { index?: number };
|
|
135
|
+
if (childProps.index !== undefined) return child;
|
|
136
|
+
return cloneElement(child, { index } as Record<string, unknown>);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<CheckboxGroupContext.Provider value={{ registerItem, activeIndex }}>
|
|
141
|
+
<div
|
|
142
|
+
ref={(node) => {
|
|
143
|
+
(containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
144
|
+
if (typeof ref === "function") ref(node);
|
|
145
|
+
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
146
|
+
}}
|
|
147
|
+
onMouseEnter={handlers.onMouseEnter}
|
|
148
|
+
onMouseMove={handlers.onMouseMove}
|
|
149
|
+
onMouseLeave={handlers.onMouseLeave}
|
|
150
|
+
onFocus={(e) => {
|
|
151
|
+
const indexAttr = (e.target as HTMLElement)
|
|
152
|
+
.closest("[data-proximity-index]")
|
|
153
|
+
?.getAttribute("data-proximity-index");
|
|
154
|
+
if (indexAttr != null) {
|
|
155
|
+
const idx = Number(indexAttr);
|
|
156
|
+
setActiveIndex(idx);
|
|
157
|
+
setFocusedIndex((e.target as HTMLElement).matches(":focus-visible") ? idx : null);
|
|
158
|
+
}
|
|
159
|
+
}}
|
|
160
|
+
onBlur={(e) => {
|
|
161
|
+
// Don't clear hover when focus moves to another item within the group
|
|
162
|
+
if (containerRef.current?.contains(e.relatedTarget as Node)) return;
|
|
163
|
+
setFocusedIndex(null);
|
|
164
|
+
setActiveIndex(null);
|
|
165
|
+
}}
|
|
166
|
+
onKeyDown={(e) => {
|
|
167
|
+
// Scope to row wrappers only. The inner checkbox primitive also
|
|
168
|
+
// carries role="checkbox", so a bare [role="checkbox"] selector
|
|
169
|
+
// matches twice per row and arrows skip onto the hidden control.
|
|
170
|
+
const items = Array.from(
|
|
171
|
+
containerRef.current?.querySelectorAll("[data-proximity-index]") ?? [],
|
|
172
|
+
) as HTMLElement[];
|
|
173
|
+
const currentIdx = items.indexOf(e.target as HTMLElement);
|
|
174
|
+
if (currentIdx === -1) return;
|
|
175
|
+
|
|
176
|
+
if (["ArrowDown", "ArrowUp"].includes(e.key)) {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
const next =
|
|
179
|
+
e.key === "ArrowDown"
|
|
180
|
+
? (currentIdx + 1) % items.length
|
|
181
|
+
: (currentIdx - 1 + items.length) % items.length;
|
|
182
|
+
items[next].focus();
|
|
183
|
+
} else if (e.key === "Home") {
|
|
184
|
+
e.preventDefault();
|
|
185
|
+
items[0]?.focus();
|
|
186
|
+
} else if (e.key === "End") {
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
items[items.length - 1]?.focus();
|
|
189
|
+
}
|
|
190
|
+
}}
|
|
191
|
+
role="group"
|
|
192
|
+
className={cn("relative flex flex-col w-72 max-w-full select-none", className)}
|
|
193
|
+
{...props}
|
|
194
|
+
>
|
|
195
|
+
{/* Selected backgrounds (merged for contiguous checked items).
|
|
196
|
+
A run is normally one block; mid merge/split it is drawn as two
|
|
197
|
+
abutting halves — see useMergeSplitBlocks. */}
|
|
198
|
+
<SelectionBackgrounds blocks={blocks} dimmed={isHoveringOther} />
|
|
199
|
+
|
|
200
|
+
{/* Hover background */}
|
|
201
|
+
<AnimatePresence>
|
|
202
|
+
{activeRect && (
|
|
203
|
+
<motion.div
|
|
204
|
+
key={sessionRef.current}
|
|
205
|
+
className={`absolute ${shape.bg} bg-hover pointer-events-none`}
|
|
206
|
+
initial={{
|
|
207
|
+
opacity: 0,
|
|
208
|
+
top: activeRect.top,
|
|
209
|
+
left: activeRect.left,
|
|
210
|
+
width: activeRect.width,
|
|
211
|
+
height: activeRect.height,
|
|
212
|
+
}}
|
|
213
|
+
animate={{
|
|
214
|
+
opacity: 1,
|
|
215
|
+
top: activeRect.top,
|
|
216
|
+
left: activeRect.left,
|
|
217
|
+
width: activeRect.width,
|
|
218
|
+
height: activeRect.height,
|
|
219
|
+
}}
|
|
220
|
+
exit={{ opacity: 0, transition: spring.fast.exit }}
|
|
221
|
+
transition={{
|
|
222
|
+
...spring.fast,
|
|
223
|
+
opacity: { duration: 0.08 },
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
)}
|
|
227
|
+
</AnimatePresence>
|
|
228
|
+
|
|
229
|
+
{/* Focus ring */}
|
|
230
|
+
<AnimatePresence>
|
|
231
|
+
{focusRect && (
|
|
232
|
+
<motion.div
|
|
233
|
+
className={`absolute ${shape.focusRing} pointer-events-none z-20 border border-[#6B97FF]`}
|
|
234
|
+
initial={false}
|
|
235
|
+
animate={{
|
|
236
|
+
left: focusRect.left - 2,
|
|
237
|
+
top: focusRect.top - 2,
|
|
238
|
+
width: focusRect.width + 4,
|
|
239
|
+
height: focusRect.height + 4,
|
|
240
|
+
}}
|
|
241
|
+
exit={{ opacity: 0, transition: spring.fast.exit }}
|
|
242
|
+
transition={{
|
|
243
|
+
...spring.fast,
|
|
244
|
+
opacity: { duration: 0.08 },
|
|
245
|
+
}}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
</AnimatePresence>
|
|
249
|
+
|
|
250
|
+
{renderedChildren}
|
|
251
|
+
</div>
|
|
252
|
+
</CheckboxGroupContext.Provider>
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
CheckboxGroup.displayName = "CheckboxGroup";
|
|
258
|
+
|
|
259
|
+
interface CheckboxItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
260
|
+
label: string;
|
|
261
|
+
index: number;
|
|
262
|
+
checked: boolean;
|
|
263
|
+
onToggle: () => void;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const CheckboxItem = forwardRef<HTMLDivElement, CheckboxItemProps>(
|
|
267
|
+
({ label, index, checked, onToggle, className, ...props }, ref) => {
|
|
268
|
+
const internalRef = useRef<HTMLDivElement>(null);
|
|
269
|
+
const hasMounted = useRef(false);
|
|
270
|
+
const { registerItem, activeIndex } = useCheckboxGroup();
|
|
271
|
+
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
registerItem(index, internalRef.current);
|
|
274
|
+
return () => registerItem(index, null);
|
|
275
|
+
}, [index, registerItem]);
|
|
276
|
+
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
hasMounted.current = true;
|
|
279
|
+
}, []);
|
|
280
|
+
|
|
281
|
+
const isActive = activeIndex === index;
|
|
282
|
+
const skipAnimation = !hasMounted.current;
|
|
283
|
+
const shape = useShape();
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<div
|
|
287
|
+
ref={(node) => {
|
|
288
|
+
(internalRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
289
|
+
if (typeof ref === "function") ref(node);
|
|
290
|
+
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
291
|
+
}}
|
|
292
|
+
data-proximity-index={index}
|
|
293
|
+
tabIndex={0}
|
|
294
|
+
role="checkbox"
|
|
295
|
+
aria-checked={checked}
|
|
296
|
+
aria-label={label}
|
|
297
|
+
onClick={onToggle}
|
|
298
|
+
onKeyDown={(e) => {
|
|
299
|
+
if (e.key === " " || e.key === "Enter") {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
onToggle();
|
|
302
|
+
}
|
|
303
|
+
}}
|
|
304
|
+
className={cn(
|
|
305
|
+
`relative z-10 flex items-center gap-2.5 ${shape.item} px-3 py-1.5 cursor-pointer outline-none`,
|
|
306
|
+
className,
|
|
307
|
+
)}
|
|
308
|
+
{...props}
|
|
309
|
+
>
|
|
310
|
+
{/* Checkbox — Radix primitive for accessibility */}
|
|
311
|
+
<CheckboxPrimitive.Root
|
|
312
|
+
checked={checked}
|
|
313
|
+
onCheckedChange={() => onToggle()}
|
|
314
|
+
tabIndex={-1}
|
|
315
|
+
aria-hidden
|
|
316
|
+
className="relative w-[15px] h-[15px] shrink-0 appearance-none bg-transparent p-0 border-0 outline-none cursor-pointer"
|
|
317
|
+
onClick={(e) => e.stopPropagation()}
|
|
318
|
+
>
|
|
319
|
+
{/* Border */}
|
|
320
|
+
<div
|
|
321
|
+
className={cn(
|
|
322
|
+
"absolute inset-0 rounded-[5px] border-solid transition-all duration-80",
|
|
323
|
+
checked
|
|
324
|
+
? "border-[1.5px] border-transparent"
|
|
325
|
+
: isActive
|
|
326
|
+
? "border-[1.5px] border-neutral-400 dark:border-neutral-500"
|
|
327
|
+
: "border-[1.5px] border-border",
|
|
328
|
+
)}
|
|
329
|
+
/>
|
|
330
|
+
{/* Check mark */}
|
|
331
|
+
<AnimatePresence>
|
|
332
|
+
{checked && (
|
|
333
|
+
<CheckboxPrimitive.Indicator forceMount asChild>
|
|
334
|
+
<motion.svg
|
|
335
|
+
width={18}
|
|
336
|
+
height={18}
|
|
337
|
+
viewBox="0 0 24 24"
|
|
338
|
+
fill="none"
|
|
339
|
+
stroke="currentColor"
|
|
340
|
+
strokeWidth={2}
|
|
341
|
+
strokeLinecap="round"
|
|
342
|
+
strokeLinejoin="round"
|
|
343
|
+
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-foreground"
|
|
344
|
+
initial={{ opacity: 1 }}
|
|
345
|
+
animate={{ opacity: 1 }}
|
|
346
|
+
exit={{ opacity: 1 }}
|
|
347
|
+
>
|
|
348
|
+
<motion.path
|
|
349
|
+
d="M6 12L10 16L18 8"
|
|
350
|
+
initial={{
|
|
351
|
+
pathLength: skipAnimation ? 1 : 0,
|
|
352
|
+
}}
|
|
353
|
+
animate={{
|
|
354
|
+
pathLength: 1,
|
|
355
|
+
transition: {
|
|
356
|
+
duration: 0.08,
|
|
357
|
+
ease: "easeOut",
|
|
358
|
+
},
|
|
359
|
+
}}
|
|
360
|
+
exit={{
|
|
361
|
+
pathLength: 0,
|
|
362
|
+
transition: {
|
|
363
|
+
duration: 0.04,
|
|
364
|
+
ease: "easeIn",
|
|
365
|
+
},
|
|
366
|
+
}}
|
|
367
|
+
/>
|
|
368
|
+
</motion.svg>
|
|
369
|
+
</CheckboxPrimitive.Indicator>
|
|
370
|
+
)}
|
|
371
|
+
</AnimatePresence>
|
|
372
|
+
</CheckboxPrimitive.Root>
|
|
373
|
+
|
|
374
|
+
{/* Label */}
|
|
375
|
+
<span className="inline-grid text-[13px]">
|
|
376
|
+
<span
|
|
377
|
+
className="col-start-1 row-start-1 invisible"
|
|
378
|
+
style={{ fontVariationSettings: fontWeights.semibold }}
|
|
379
|
+
aria-hidden="true"
|
|
380
|
+
>
|
|
381
|
+
{label}
|
|
382
|
+
</span>
|
|
383
|
+
<span
|
|
384
|
+
className={cn(
|
|
385
|
+
"col-start-1 row-start-1 transition-[color,font-variation-settings] duration-80",
|
|
386
|
+
checked || isActive ? "text-foreground" : "text-muted-foreground",
|
|
387
|
+
)}
|
|
388
|
+
style={{
|
|
389
|
+
fontVariationSettings: checked ? fontWeights.semibold : fontWeights.normal,
|
|
390
|
+
}}
|
|
391
|
+
>
|
|
392
|
+
{label}
|
|
393
|
+
</span>
|
|
394
|
+
</span>
|
|
395
|
+
</div>
|
|
396
|
+
);
|
|
397
|
+
},
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
CheckboxItem.displayName = "CheckboxItem";
|
|
401
|
+
|
|
402
|
+
export { CheckboxGroup, CheckboxItem };
|
|
403
|
+
export default CheckboxGroup;
|
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { Checkbox } from "radix-ui";
|
|
5
5
|
import { Check, Minus } from "lucide-react";
|
|
6
6
|
import { cn } from "../lib/utils";
|
|
7
|
+
import { focusRingClasses, motionClasses, pressClasses } from "../lib/interaction";
|
|
7
8
|
|
|
8
9
|
const CheckboxRoot = React.forwardRef<
|
|
9
10
|
React.ComponentRef<typeof Checkbox.Root>,
|
|
@@ -12,9 +13,11 @@ const CheckboxRoot = React.forwardRef<
|
|
|
12
13
|
<Checkbox.Root
|
|
13
14
|
ref={ref}
|
|
14
15
|
className={cn(
|
|
15
|
-
"peer relative size-4 shrink-0 rounded border border-border bg-card
|
|
16
|
+
"peer relative size-4 shrink-0 rounded border border-border bg-card",
|
|
17
|
+
motionClasses,
|
|
18
|
+
pressClasses,
|
|
16
19
|
"after:absolute after:left-1/2 after:top-1/2 after:size-10 after:-translate-x-1/2 after:-translate-y-1/2",
|
|
17
|
-
|
|
20
|
+
focusRingClasses,
|
|
18
21
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
19
22
|
"data-[state=checked]:bg-brand-primary data-[state=checked]:border-brand-primary",
|
|
20
23
|
"data-[state=indeterminate]:bg-brand-primary data-[state=indeterminate]:border-brand-primary",
|
|
@@ -22,7 +25,7 @@ const CheckboxRoot = React.forwardRef<
|
|
|
22
25
|
)}
|
|
23
26
|
{...props}
|
|
24
27
|
>
|
|
25
|
-
<Checkbox.Indicator className="flex items-center justify-center text-background">
|
|
28
|
+
<Checkbox.Indicator className="flex items-center justify-center text-background data-[state=checked]:animate-in data-[state=checked]:zoom-in-75">
|
|
26
29
|
{props.checked === "indeterminate" ? (
|
|
27
30
|
<Minus size={11} strokeWidth={3} />
|
|
28
31
|
) : (
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/utils";
|
|
5
|
+
import { motionClasses } from "../lib/interaction";
|
|
5
6
|
import { ColorPicker } from "./color-picker";
|
|
6
7
|
|
|
7
8
|
interface ColorInputProps {
|
|
@@ -43,7 +44,8 @@ function ColorInput({
|
|
|
43
44
|
<div
|
|
44
45
|
className={cn(
|
|
45
46
|
"inline-flex h-8 items-stretch gap-0 rounded-md border border-border bg-card overflow-hidden",
|
|
46
|
-
"focus-within:ring-2 focus-within:ring-brand-primary/20 focus-within:border-brand-primary",
|
|
47
|
+
"shadow-natural focus-within:ring-2 focus-within:ring-brand-primary/20 focus-within:border-brand-primary",
|
|
48
|
+
motionClasses,
|
|
47
49
|
disabled && "opacity-50 pointer-events-none",
|
|
48
50
|
className,
|
|
49
51
|
)}
|