bo-ui-kit 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 eromnet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # bo-ui-kit
2
+
3
+ Figma **"BO UI Kit"** 디자인 시스템을 React 컴포넌트로 만들고, **디자인 변경을 자동으로 코드에 동기화**하는 프로젝트.
4
+
5
+ `React 18 · TypeScript · Tailwind v3 · cva` · 컴포넌트 10종 · npm 라이브러리(ESM+CJS) · 5종 CI 게이트
6
+
7
+ ---
8
+
9
+ ## 이 저장소가 하는 일 (3가지)
10
+
11
+ | | 무엇 | 핵심 |
12
+ |---|---|---|
13
+ | 1. **컴포넌트 라이브러리** | `bo-ui-kit` — 설치해서 쓰는 React UI | ESM+CJS, Tailwind 없이도 동작(프리빌드 CSS), 타입 포함 |
14
+ | 2. **자동 동기화 파이프라인** | Figma 변경 → 감지 → 추출 → PR | 헤드리스 Claude가 변경분만 추출, 사람은 머지만 |
15
+ | 3. **품질 게이트(CI)** | 시각 회귀 + 소비자 스모크 | 컴포넌트/배포물이 깨지면 PR에서 빨강 |
16
+
17
+ > 한 줄 요약: **디자이너가 Figma를 바꾸면 → 코드 PR이 알아서 생기고(증거 스크린샷 첨부) → 라이브러리로 배포 가능**.
18
+
19
+ ---
20
+
21
+ ## 빠른 시작 — 라이브러리로 쓰기
22
+
23
+ ```bash
24
+ npm i bo-ui-kit # peer: react, react-dom >=18
25
+ ```
26
+
27
+ ```tsx
28
+ import { Button, Meter, Field, Input } from "bo-ui-kit";
29
+ import "bo-ui-kit/styles.css"; // 토큰+유틸 한 줄 (앱 진입점에서 1회)
30
+
31
+ export default function App() {
32
+ return (
33
+ <Field label="이메일" required>
34
+ <Input placeholder="you@example.com" />
35
+ </Field>
36
+ );
37
+ }
38
+ ```
39
+
40
+ - **ESM·CommonJS 모두 지원** (`import` / `require` 둘 다)
41
+ - **Tailwind 불필요** — `styles.css` 한 줄이면 토큰·유틸 전부 포함
42
+ - **다크 모드** — 상위 요소에 `class="dark"`
43
+ - **테마 변경** — `styles.css`의 CSS 변수(`--primary` 등)를 덮어쓰기
44
+ - **폰트** — Pretendard 우선, 없으면 시스템 산세리프 폴백
45
+
46
+ ---
47
+
48
+ ## 컴포넌트 (10종)
49
+
50
+ | 컴포넌트 | export | Figma node |
51
+ |---|---|---|
52
+ | Button | `Button` | 1692:74 |
53
+ | Checkbox | `Checkbox` | 5667:129 |
54
+ | Input | `Input` | 7745:699 |
55
+ | Label | `Label` | 7658:2157 |
56
+ | Field | `Field` | 7745:713 |
57
+ | Input OTP | `InputOTP` `InputOTPSlot` `InputOTPSeparator` | 8060:1580 |
58
+ | Meter | `Meter` | 7664:31 |
59
+ | Toggle | `Toggle` | 5685:204 |
60
+ | Toggle Group | `ToggleGroup` `ToggleGroupItem` | 5686:270 |
61
+ | Select | `Select` | 7751:1561 |
62
+
63
+ 미리보기(자체 플레이그라운드): `npm run dev` → 좌측 컴포넌트 목록.
64
+ 추출 대상 단일 소스: `figma.manifest.json`.
65
+
66
+ ---
67
+
68
+ ## 자동 동기화 파이프라인
69
+
70
+ ```
71
+ Figma (BO UI Kit)
72
+ │ 디자인 변경
73
+
74
+ ① 감지 figma-poll.yml (cron) — 파일 버전 + 추적 노드 지문 비교
75
+ │ 실제 컴포넌트가 바뀐 경우에만
76
+
77
+ ② 추출 figma-sync.yml — 헤드리스 Claude가 변경분만 추출 → 검증(tsc+build) → PR
78
+ │ (PR에 "Closes #이슈" 연결)
79
+
80
+ ③ 검증 visual.yml — 시각 회귀 + Figma/Before/After/Diff 증거 코멘트
81
+ │ smoke.yml — 소비자 설치·렌더 + tarball 검증
82
+
83
+ ④ 사람 PR 증거 보고 머지 → 이슈 자동 종료 → baseline 자동 갱신
84
+ ```
85
+
86
+ - **autosave는 무시**: 버전만 올라가고 추적 노드(컴포넌트)가 안 바뀌면 헤드리스를 돌리지 않음(`figma.fingerprints.json` 지문 비교).
87
+ - **PR 시각 증거**: 변경된 컴포넌트의 Figma 원본 + 렌더 Before/After/Diff를 PR 코멘트로 자동 첨부.
88
+ - **토큰 점검**: `token-health.yml`이 `FIGMA_TOKEN` 만료를 매일 점검(만료 시 Issue).
89
+
90
+ > 파이프라인 구성·운영·시크릿·함정 상세는 **[docs/CONFLUENCE.md](docs/CONFLUENCE.md)**.
91
+
92
+ ---
93
+
94
+ ## 개발
95
+
96
+ | 명령 | 설명 |
97
+ |---|---|
98
+ | `npm run dev` | 플레이그라운드(컴포넌트 카탈로그) |
99
+ | `npm run verify` | 타입체크 + 빌드 |
100
+ | `npm run build:lib` | 라이브러리 빌드 → `dist/`(ESM+CJS+타입+styles.css) |
101
+ | `npm run test:visual` | 시각 회귀(로컬은 비교 생략, CI=Linux 기준) |
102
+ | `npm run check:figma` | Figma 변경 감지(exit 10=변경) |
103
+ | `npm run pack:test` | tarball 만들어 `examples/pack-consumer`에 설치(배포물 검증) |
104
+
105
+ 소비자 검증 예시: `examples/consumer`(워크스페이스 링크) · `examples/pack-consumer`(tarball 전용).
106
+
107
+ ---
108
+
109
+ ## 프로젝트 구조
110
+
111
+ ```
112
+ src/
113
+ components/ui/ 컴포넌트 (배럴: index.ts)
114
+ stories/ 플레이그라운드 스토리
115
+ tokens.css 디자인 토큰(단일 소스 — 앱·라이브러리 공유)
116
+ index.css 앱 진입 CSS (tokens 임포트)
117
+ lib/styles.css 라이브러리 CSS 입력(프리빌드용)
118
+ scripts/ 감지/빌드/검증 스크립트
119
+ .github/workflows/ figma-poll · figma-sync · visual · smoke · token-health
120
+ examples/ consumer(링크) · pack-consumer(tarball)
121
+ figma.manifest.json / figma.fingerprints.json 추출 대상 + 노드 지문
122
+ CLAUDE.md 컴포넌트 작성 규칙(★ Variant 매핑)
123
+ docs/CONFLUENCE.md 파이프라인/운영 레퍼런스
124
+ ```
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ /**
4
+ * Button — Figma "BO UI Kit" Button 컴포넌트(node 1692:74)를 옮긴 React 컴포넌트.
5
+ *
6
+ * 7가지 타입 × 5가지 사이즈 매트릭스를 그대로 재현하며, 모든 색상/간격/타이포는
7
+ * Tailwind config(= Figma 디자인 토큰)에 매핑돼 하드코딩을 최소화했다.
8
+ * 좌/우 아이콘 슬롯(icon, rightIcon)을 지원한다.
9
+ *
10
+ * @see https://coss.com/ui/docs/components/button
11
+ */
12
+ declare const buttonVariants: (props?: ({
13
+ variant?: "default" | "outline" | "secondary" | "destructive" | "destructive-outline" | "ghost" | "link" | null | undefined;
14
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
15
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
16
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
17
+ /** 좌측 아이콘 (예: <PlusIcon />) */
18
+ icon?: React.ReactNode;
19
+ /** 우측 아이콘 */
20
+ rightIcon?: React.ReactNode;
21
+ }
22
+ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
23
+ export { Button, buttonVariants };
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Checkbox — Figma "BO UI Kit" `.Selector` 컴포넌트(node 5667:129)를 옮긴 React 컴포넌트.
4
+ *
5
+ * 라벨/설명 없는 **체크박스 박스 자체**만 담는다(Title·Description·CardStyle 은
6
+ * Figma 의 사용 예시였던 `CheckBox` 컴포넌트 쪽 구성이라 제외).
7
+ *
8
+ * 상태 (Figma `.Selector` 진리표 — `checked`(IsSelected)가 마스터 게이트):
9
+ * | checked | indeterminate | 렌더 |
10
+ * | true | false | bg primary + 흰 체크 |
11
+ * | true | true | 흰 배경 + 테두리 + primary minus 바 |
12
+ * | false | (무관) | 흰 배경 + 테두리 (빈 박스) |
13
+ * → indeterminate 는 `checked`가 true 일 때만 마크를 minus 로 바꾼다(독립 X).
14
+ * - disabled : opacity 64% / mobile : 16px → 18px
15
+ *
16
+ * 네이티브 <input type="checkbox">를 시각적으로 숨겨 키보드/포커스 접근성을 확보하며,
17
+ * 제어(controlled, `checked`)·비제어(uncontrolled, `defaultChecked`) 모두 지원한다.
18
+ *
19
+ * @see https://coss.com/ui/docs/components/checkbox
20
+ */
21
+ export interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "checked" | "defaultChecked" | "onChange" | "size"> {
22
+ /** 제어 모드 체크 상태 */
23
+ checked?: boolean;
24
+ /** 비제어 모드 초기 체크 상태 */
25
+ defaultChecked?: boolean;
26
+ /** 부모(부분 선택) 상태 — minus 바 표시 */
27
+ indeterminate?: boolean;
28
+ /** 모바일 사이즈(18px). 기본은 데스크톱 16px */
29
+ mobile?: boolean;
30
+ onCheckedChange?: (checked: boolean) => void;
31
+ }
32
+ declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>;
33
+ export { Checkbox };
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Field — Figma "BO UI Kit" Field 컴포넌트(node 7745:713)를 옮긴 폼 필드 래퍼.
4
+ *
5
+ * 구조: [Label(+필수 *)] + [컨트롤(children)] + [보조 영역].
6
+ * Figma 의 `Type` 변형(상호 배타) → 보조 영역의 종류로 매핑:
7
+ * | Type | 보조 영역 렌더 |
8
+ * | Default | 없음 |
9
+ * | Error | <p> 에러 텍스트 (destructive-foreground 빨강, text-xs) |
10
+ * | Description | <p> 설명 텍스트 (muted-foreground, text-xs) |
11
+ * | Validity | muted 배경 박스(radius-lg) + 텍스트 (foreground, text-xs) |
12
+ *
13
+ * 보조 텍스트는 error 가 우선(error 가 있으면 description 은 숨김). validity 박스는 독립.
14
+ * label/control gap = 6px(spacing/1-5), 보조 영역과의 gap = 8px(spacing/2).
15
+ *
16
+ * 참고(의도된 디자인 변환): Figma label 토큰은 Inter Medium 이지만, 이 프로젝트는
17
+ * sans 를 Pretendard 로 표준화하므로 `font-medium`(Pretendard Medium)으로 옮긴다.
18
+ *
19
+ * @see https://coss.com/ui/docs/components/field
20
+ */
21
+ export interface FieldProps extends React.HTMLAttributes<HTMLDivElement> {
22
+ label?: React.ReactNode;
23
+ /** 필수 표시(*) */
24
+ required?: boolean;
25
+ /** 컨트롤과 label 을 연결할 id (label htmlFor + 권장: 컨트롤 id 동일) */
26
+ htmlFor?: string;
27
+ /** 회색 설명 텍스트 */
28
+ description?: React.ReactNode;
29
+ /** 빨간 에러 텍스트 (있으면 description 대신 표시) */
30
+ error?: React.ReactNode;
31
+ /** muted 배경 박스 안내 */
32
+ validity?: React.ReactNode;
33
+ /** 폼 컨트롤 (Input, Select 등) */
34
+ children?: React.ReactNode;
35
+ }
36
+ declare const Field: React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>>;
37
+ export { Field };
@@ -0,0 +1,20 @@
1
+ export { Button, buttonVariants } from "./button";
2
+ export type { ButtonProps } from "./button";
3
+ export { Checkbox } from "./checkbox";
4
+ export type { CheckboxProps } from "./checkbox";
5
+ export { Input, inputVariants } from "./input";
6
+ export type { InputProps } from "./input";
7
+ export { Label } from "./label";
8
+ export type { LabelProps } from "./label";
9
+ export { Field } from "./field";
10
+ export type { FieldProps } from "./field";
11
+ export { InputOTP, InputOTPSlot, InputOTPSeparator, slotVariants } from "./input-otp";
12
+ export type { InputOTPProps, InputOTPSlotProps } from "./input-otp";
13
+ export { Meter } from "./meter";
14
+ export type { MeterProps } from "./meter";
15
+ export { Toggle, toggleVariants } from "./toggle";
16
+ export type { ToggleProps } from "./toggle";
17
+ export { ToggleGroup, ToggleGroupItem } from "./toggle-group";
18
+ export type { ToggleGroupProps, ToggleGroupItemProps } from "./toggle-group";
19
+ export { Select, selectTriggerVariants } from "./select";
20
+ export type { SelectProps, SelectOption } from "./select";
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ export interface InputOTPProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ /** 큰 사이즈(슬롯 36px). 기본 false(32px). Figma IsLarge 변형 */
5
+ isLarge?: boolean;
6
+ /** 상단 제목 텍스트 */
7
+ title?: string;
8
+ /** 제목 표시 여부 */
9
+ showTitle?: boolean;
10
+ /** 하단 안내 텍스트 */
11
+ belowText?: string;
12
+ /** 하단 안내 표시 여부 */
13
+ showBelowText?: boolean;
14
+ }
15
+ declare const InputOTP: React.ForwardRefExoticComponent<InputOTPProps & React.RefAttributes<HTMLDivElement>>;
16
+ declare const slotVariants: (props?: ({
17
+ isLarge?: boolean | null | undefined;
18
+ active?: boolean | null | undefined;
19
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
20
+ export interface InputOTPSlotProps extends React.HTMLAttributes<HTMLDivElement>, Omit<VariantProps<typeof slotVariants>, "isLarge"> {
21
+ /** 표시할 글자(채워진 숫자). 없으면 빈 슬롯 */
22
+ char?: React.ReactNode;
23
+ /** 사이즈 오버라이드(미지정 시 InputOTP 의 isLarge context 사용) */
24
+ isLarge?: boolean;
25
+ }
26
+ /**
27
+ * InputOTPSlot — 한 자리 입력 칸.
28
+ * 상태 진리표: 기본=빈칸(border-input) · char 있음=글자 표시(foreground) · active=링(다음 입력 위치).
29
+ */
30
+ declare const InputOTPSlot: React.ForwardRefExoticComponent<InputOTPSlotProps & React.RefAttributes<HTMLDivElement>>;
31
+ /** InputOTPSeparator — 슬롯 그룹 사이의 짧은 구분선(Figma 12px 점선 대시). */
32
+ declare const InputOTPSeparator: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
33
+ export { InputOTP, InputOTPSlot, InputOTPSeparator, slotVariants };
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ /**
4
+ * Input — Figma "BO UI Kit" Input 컴포넌트(node 7745:699)를 옮긴 슬롯형 입력.
5
+ *
6
+ * 구조: 테두리 컨테이너 + [leftIcon][prefix][native input][suffix][rightIcon].
7
+ * 상태(Figma Type 변형):
8
+ * - Default : border input-border
9
+ * - Error : border destructive(#ef4444 빨강) → `invalid` prop
10
+ * - Disabled: 컨테이너 opacity 64% → input `disabled`
11
+ * - Focus : 테두리가 disabled(#45556c@30%)로 진해지고 3px 회색 글로우 링
12
+ * (Figma Focus 노드 7745:700 Stroke: 3px disabled@30% × opacity23% ≈ @7%, offset 없음).
13
+ * ErrorFocused(8060:3643)는 테두리 destructive + 3px destructive@15% 링.
14
+ * ※ Figma 의 회색 링이며 shadcn 기본 파란 ring 이 아님.
15
+ * 사이즈(Figma Size): Small h28 / Default h32 / Large h36.
16
+ *
17
+ * ※ 미구현(특수 변형 — 필요 시 추가): 인라인 Button, Badge, Kbd 단축키, MultiSelect,
18
+ * File, InnerLabel(플로팅 라벨), Fill=True(채움 배경), darkBackground.
19
+ *
20
+ * @see https://coss.com/ui/docs/components/input
21
+ */
22
+ declare const inputVariants: (props?: ({
23
+ inputSize?: "sm" | "md" | "lg" | null | undefined;
24
+ invalid?: boolean | null | undefined;
25
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
26
+ type SlotProps = {
27
+ /** 좌측 아이콘 */
28
+ leftIcon?: React.ReactNode;
29
+ /** 우측 아이콘 */
30
+ rightIcon?: React.ReactNode;
31
+ /** 앞 텍스트 (예: https://) */
32
+ prefix?: React.ReactNode;
33
+ /** 뒤 텍스트 (예: .com) */
34
+ suffix?: React.ReactNode;
35
+ /** 에러 상태 (빨간 테두리) */
36
+ invalid?: boolean;
37
+ };
38
+ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size" | "prefix">, Pick<VariantProps<typeof inputVariants>, "inputSize">, SlotProps {
39
+ /** 컨테이너 클래스(테두리/배경 등) */
40
+ containerClassName?: string;
41
+ }
42
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
43
+ export { Input, inputVariants };
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Label — Figma "BO UI Kit" Label 컴포넌트(node 7658:2157).
4
+ *
5
+ * 폼 컨트롤에 연결되는 접근성 라벨. text-xs(12px, Pretendard Medium, tracking 0.12) + foreground.
6
+ * `htmlFor` 로 컨트롤과 연결한다.
7
+ *
8
+ * @see https://coss.com/ui/docs/components/label
9
+ */
10
+ export type LabelProps = React.LabelHTMLAttributes<HTMLLabelElement>;
11
+ declare const Label: React.ForwardRefExoticComponent<LabelProps & React.RefAttributes<HTMLLabelElement>>;
12
+ export { Label };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Meter — Figma "BO UI Kit" Meter 컴포넌트(node 7664:31)를 옮긴 수치 표시 막대.
4
+ *
5
+ * 범위(min~max) 안에서 현재 값을 막대로 보여준다(디스크 사용량·점수 등). 진행 상태가
6
+ * 아니라 "정적 측정값"이므로 role="meter" + aria-value* 로 접근성을 갖춘다.
7
+ *
8
+ * 구조: [라벨행: label · valueLabel] + [트랙(input) + 인디케이터(primary)].
9
+ * 변형(Figma): showLabels / showSecondaryLabel(우측 X% 라벨). 채움 비율 = (value-min)/(max-min).
10
+ *
11
+ * @see https://coss.com/ui/docs/components/meter
12
+ */
13
+ export interface MeterProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ /** 현재 값 (min~max 범위 안). 기본 0~100 */
15
+ value?: number;
16
+ /** 범위 최소 (기본 0) */
17
+ min?: number;
18
+ /** 범위 최대 (기본 100) */
19
+ max?: number;
20
+ /** 좌측 라벨 텍스트 */
21
+ label?: string;
22
+ /** 우측 보조 라벨(예: "50%"). 미지정 시 채움 비율을 % 로 자동 표기 */
23
+ valueLabel?: string;
24
+ /** 라벨 행 표시 여부 */
25
+ showLabels?: boolean;
26
+ /** 우측 보조 라벨 표시 여부 */
27
+ showSecondaryLabel?: boolean;
28
+ }
29
+ declare const Meter: React.ForwardRefExoticComponent<MeterProps & React.RefAttributes<HTMLDivElement>>;
30
+ export { Meter };
@@ -0,0 +1,47 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ /**
4
+ * Select — Figma "BO UI Kit" Select(node 7751:561 패널) + .SelectItem(7751:1704) 를 옮긴
5
+ * 드롭다운 선택 컴포넌트. 미리 정의된 값 중 하나를 고르는 폼 컨트롤.
6
+ *
7
+ * 구성:
8
+ * - 트리거: Input 과 동일한 토큰/포커스(회색 3px 링)로 디자인. (Figma Select 노드는 열린 패널만
9
+ * 포함하므로 트리거는 디자인 시스템 Input 관례를 따른다 — 문서화된 결정.)
10
+ * - 패널(SelectContent): popover 배경, border, rounded-radius-xl, shadow-popover, p-1, gap-px.
11
+ * - 항목(SelectItem): h-28, rounded-radius-lg, text-xs medium, accent-foreground 텍스트.
12
+ * · 선택됨 → 좌측 체크 표시 + 배경 primary/4%
13
+ * · hover → 배경 accent(#f8fafc)
14
+ *
15
+ * 동작: 클릭 토글, 바깥 클릭/Escape 로 닫힘, ↑/↓ 로 항목 이동(roving focus). 제어·비제어 지원.
16
+ *
17
+ * @see https://coss.com/ui/docs/components/select
18
+ */
19
+ export interface SelectOption {
20
+ value: string;
21
+ label: React.ReactNode;
22
+ /** 라벨 앞 아이콘(선택) */
23
+ icon?: React.ReactNode;
24
+ disabled?: boolean;
25
+ }
26
+ declare const selectTriggerVariants: (props?: ({
27
+ inputSize?: "sm" | "md" | "lg" | null | undefined;
28
+ invalid?: boolean | null | undefined;
29
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
30
+ export interface SelectProps extends VariantProps<typeof selectTriggerVariants> {
31
+ options: SelectOption[];
32
+ /** 제어 값 */
33
+ value?: string;
34
+ /** 비제어 초기 값 */
35
+ defaultValue?: string;
36
+ onValueChange?: (value: string) => void;
37
+ placeholder?: React.ReactNode;
38
+ disabled?: boolean;
39
+ /** 트리거 클래스 */
40
+ className?: string;
41
+ /** 패널 클래스 */
42
+ contentClassName?: string;
43
+ id?: string;
44
+ name?: string;
45
+ }
46
+ declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLButtonElement>>;
47
+ export { Select, selectTriggerVariants };
@@ -0,0 +1,42 @@
1
+ import * as React from "react";
2
+ import { type ToggleProps } from "./toggle";
3
+ /**
4
+ * ToggleGroup / ToggleGroupItem — Figma "BO UI Kit" Toggle Group(node 5686:270).
5
+ *
6
+ * 여러 Toggle 에 공유 상태를 제공하는 컨테이너(세그먼티드 컨트롤 형태).
7
+ * Figma 변형: `Outline`(T/F) → `variant`, `Orientation`(Horizontal/Vertical) → `orientation`,
8
+ * `Size`(Small/Default/Large) → `size`. 아이템들은 테두리/모서리를 공유하도록 이어 붙인다
9
+ * (그룹이 rounded-radius-lg + overflow-hidden, 인접 테두리는 음수 마진으로 겹침 제거).
10
+ *
11
+ * 선택 동작: `type="single"`(string, 라디오식 — 같은 값 재클릭 해제) 또는
12
+ * `type="multiple"`(string[]). 활성 아이템은 Toggle 의 on 상태(muted/64)로 표시.
13
+ *
14
+ * @see https://coss.com/ui/docs/components/toggle-group
15
+ */
16
+ type Orientation = "horizontal" | "vertical";
17
+ type ToggleGroupBaseProps = {
18
+ variant?: ToggleProps["variant"];
19
+ size?: ToggleProps["size"];
20
+ orientation?: Orientation;
21
+ className?: string;
22
+ children?: React.ReactNode;
23
+ };
24
+ export type ToggleGroupProps = ToggleGroupBaseProps & ({
25
+ type: "single";
26
+ value?: string;
27
+ defaultValue?: string;
28
+ onValueChange?: (value: string) => void;
29
+ } | {
30
+ type: "multiple";
31
+ value?: string[];
32
+ defaultValue?: string[];
33
+ onValueChange?: (value: string[]) => void;
34
+ });
35
+ declare const ToggleGroup: React.ForwardRefExoticComponent<ToggleGroupProps & React.RefAttributes<HTMLDivElement>>;
36
+ export interface ToggleGroupItemProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type" | "value"> {
37
+ /** 이 아이템의 값 — 그룹 선택 상태와 비교된다. */
38
+ value: string;
39
+ iconOnly?: ToggleProps["iconOnly"];
40
+ }
41
+ declare const ToggleGroupItem: React.ForwardRefExoticComponent<ToggleGroupItemProps & React.RefAttributes<HTMLButtonElement>>;
42
+ export { ToggleGroup, ToggleGroupItem };
@@ -0,0 +1,41 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ /**
4
+ * Toggle — Figma "BO UI Kit" Toggle 컴포넌트(node 5685:204)를 옮긴 2-state 버튼.
5
+ *
6
+ * Figma 변형(2026-06-16 갱신): hover 가 예전엔 `State` 한 축에 묶여 있었으나 디자이너가
7
+ * **별도 축 `IsHovered`(True/False)** 로 분리 → `State`(Default/Active/Disabled) × `IsHovered`.
8
+ * 이는 우리가 처음부터 hover 를 직교 CSS `:hover` 로 다룬 매핑과 1:1 로 맞는다(코드 동작 불변).
9
+ * 상호작용/의미를 평탄화하지 않고 매핑:
10
+ * - `pressed`(boolean) → `State=Active`(눌림/켜짐). aria-pressed + data-state=on.
11
+ * - `:hover` → `IsHovered=True` (CSS — off 일 때만 배경 변화)
12
+ * - `disabled` → `State=Disabled`(CSS)
13
+ * - 기본 → `State=Default`
14
+ *
15
+ * 상태 진리표(배경):
16
+ * - off : 투명 (outline 변형은 card+border)
17
+ * - off + hover : card/50 (아주 옅은 오버레이)
18
+ * - on (pressed) : secondary/64 회색 채움 (Figma `muted`#e2e8f0 = 프로젝트 secondary 토큰)
19
+ * - on + hover : secondary/64 그대로 (Figma Active+IsHovered=True 도 동일 — 호버로 안 바뀜)
20
+ * - disabled (+hover) : opacity 50% (disabled 는 pointer-events-none 이라 hover 미발동)
21
+ *
22
+ * 변형: `variant` Default(테두리 없음) / Outline(border+card+shadow), `size` sm/md/lg(28/32/36),
23
+ * `iconOnly`(정사각 패딩). 텍스트/아이콘 색은 foreground(#1d293d).
24
+ *
25
+ * @see https://coss.com/ui/docs/components/toggle
26
+ */
27
+ declare const toggleVariants: (props?: ({
28
+ variant?: "default" | "outline" | null | undefined;
29
+ size?: "sm" | "md" | "lg" | null | undefined;
30
+ iconOnly?: boolean | null | undefined;
31
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
32
+ export interface ToggleProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type">, VariantProps<typeof toggleVariants> {
33
+ /** 눌림(켜짐) 상태 — Figma State=Active. 제어 컴포넌트로 쓸 때 사용. */
34
+ pressed?: boolean;
35
+ /** 비제어 초기 눌림 상태 */
36
+ defaultPressed?: boolean;
37
+ /** 눌림 상태가 바뀔 때 호출 */
38
+ onPressedChange?: (pressed: boolean) => void;
39
+ }
40
+ declare const Toggle: React.ForwardRefExoticComponent<ToggleProps & React.RefAttributes<HTMLButtonElement>>;
41
+ export { Toggle, toggleVariants };
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),ee=require("react"),S=require("class-variance-authority"),te=require("clsx"),re=require("tailwind-merge");function se(t){const s=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const n in t)if(n!=="default"){const a=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(s,n,a.get?a:{enumerable:!0,get:()=>t[n]})}}return s.default=t,Object.freeze(s)}const o=se(ee);function f(...t){return re.twMerge(te.clsx(t))}const _=S.cva("inline-flex shrink-0 items-center justify-center gap-0 whitespace-nowrap border font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"border-primary bg-primary text-primary-foreground shadow-btn hover:bg-primary/90",outline:"border-border bg-card text-foreground shadow-xs hover:bg-secondary/50",secondary:"border-transparent bg-secondary text-foreground hover:bg-secondary/80",destructive:"border-border bg-destructive text-primary-foreground shadow-btn hover:bg-destructive/90","destructive-outline":"border-border bg-background text-destructive-foreground shadow-xs hover:bg-destructive/10",ghost:"border-transparent bg-transparent text-foreground hover:bg-secondary",link:"border-transparent bg-transparent text-foreground underline-offset-4 hover:underline"},size:{xs:"h-6 min-w-6 rounded-radius-lg px-1 text-xs [&_svg]:size-4",sm:"h-7 min-w-7 rounded-radius px-1.5 text-sm [&_svg]:size-4",md:"h-8 min-w-8 rounded-radius px-1.5 text-sm [&_svg]:size-[18px]",lg:"h-9 min-w-9 rounded-radius px-2 text-sm [&_svg]:size-5 [&_[data-label]]:px-1.5",xl:"h-10 min-w-10 rounded-radius px-2.5 text-base [&_svg]:size-5 [&_[data-label]]:px-1.5"}},defaultVariants:{variant:"default",size:"md"}}),M=o.forwardRef(({className:t,variant:s,size:n,type:a="button",icon:l,rightIcon:d,children:i,...u},c)=>e.jsxs("button",{ref:c,type:a,className:f(_({variant:s,size:n}),t),"data-node-id":"1692:74",...u,children:[l?e.jsx("span",{className:"inline-flex shrink-0 opacity-80",children:l}):null,i!=null?e.jsx("span",{"data-label":!0,className:"px-1",children:i}):null,d?e.jsx("span",{className:"inline-flex shrink-0 opacity-80",children:d}):null]}));M.displayName="Button";const A=o.forwardRef(({checked:t,defaultChecked:s,indeterminate:n=!1,disabled:a=!1,mobile:l=!1,onCheckedChange:d,className:i,...u},c)=>{const g=t!==void 0,[b,m]=o.useState(s??!1),j=g?t:b,x=o.useRef(null);o.useImperativeHandle(c,()=>x.current);const v=j&&!n,w=j&&n;o.useEffect(()=>{x.current&&(x.current.indeterminate=w)},[w]);const y=h=>{const N=h.target.checked;g||m(N),d==null||d(N)};return e.jsxs("label",{className:f("inline-flex shrink-0 items-center justify-center",a?"cursor-not-allowed opacity-[0.64]":"cursor-pointer",i),children:[e.jsx("input",{ref:x,type:"checkbox",className:"peer sr-only",checked:g?t:void 0,defaultChecked:g?void 0:s,disabled:a,onChange:y,...u}),e.jsxs("span",{"aria-hidden":!0,className:f("relative flex items-center justify-center rounded-radius-sm border transition-colors","peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-background",l?"size-[18px]":"size-4",v?"border-transparent bg-primary text-primary-foreground":"border-input bg-card shadow-xs"),children:[v&&e.jsx("svg",{viewBox:"0 0 8 8",fill:"none",stroke:"currentColor",strokeWidth:"1.5",className:l?"size-2.5":"size-2","aria-hidden":!0,children:e.jsx("path",{d:"M1 4.2 3 6l4-4.5",strokeLinecap:"round",strokeLinejoin:"round"})}),w&&e.jsx("span",{className:f("h-0.5 rounded-full bg-primary",l?"w-2":"w-1.5")})]})]})});A.displayName="Checkbox";const E=S.cva("flex w-full items-center gap-1 rounded-radius border bg-card text-sm text-foreground shadow-xs transition-colors focus-within:ring-[3px] focus-within:ring-offset-0 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-[0.64]",{variants:{inputSize:{sm:"h-7 px-1.5",md:"h-8 px-2",lg:"h-9 px-2"},invalid:{true:"border-destructive focus-within:ring-destructive/[0.15]",false:"border-input focus-within:border-disabled/30 focus-within:ring-disabled/[0.07]"}},defaultVariants:{inputSize:"md",invalid:!1}}),D=o.forwardRef(({className:t,containerClassName:s,inputSize:n,invalid:a=!1,leftIcon:l,rightIcon:d,prefix:i,suffix:u,type:c="text",...g},b)=>e.jsxs("div",{className:f(E({inputSize:n,invalid:a}),s),"data-node-id":"7745:699",children:[l?e.jsx("span",{className:"inline-flex shrink-0 opacity-80 [&_svg]:size-4",children:l}):null,i!=null?e.jsx("span",{className:"shrink-0 text-muted-foreground",children:i}):null,e.jsx("input",{ref:b,type:c,className:f("min-w-0 flex-1 bg-transparent px-1 text-foreground outline-none placeholder:text-muted-foreground",t),...g}),u!=null?e.jsx("span",{className:"shrink-0 text-muted-foreground",children:u}):null,d?e.jsx("span",{className:"inline-flex shrink-0 opacity-80 [&_svg]:size-4",children:d}):null]}));D.displayName="Input";const P=o.forwardRef(({className:t,...s},n)=>e.jsx("label",{ref:n,className:f("inline-flex items-center text-xs font-medium text-foreground","peer-disabled:cursor-not-allowed peer-disabled:opacity-70",t),"data-node-id":"7658:2157",...s}));P.displayName="Label";const G=o.forwardRef(({className:t,label:s,required:n=!1,htmlFor:a,description:l,error:d,validity:i,children:u,...c},g)=>e.jsxs("div",{ref:g,className:f("flex w-full flex-col gap-2",t),"data-node-id":"7745:713",...c,children:[e.jsxs("div",{className:"flex flex-col gap-1.5",children:[s!=null&&e.jsxs("label",{htmlFor:a,className:"flex items-center gap-1.5 text-sm font-medium leading-5 text-foreground",children:[s,n&&e.jsx("span",{className:"text-destructive-foreground","aria-hidden":!0,children:"*"})]}),u]}),d!=null?e.jsx("p",{className:"text-xs leading-[14px] tracking-[0.12px] text-destructive-foreground",children:d}):l!=null?e.jsx("p",{className:"text-xs leading-[14px] tracking-[0.12px] text-muted-foreground",children:l}):null,i!=null&&e.jsx("div",{className:"rounded-radius-lg bg-secondary p-2",children:e.jsx("p",{className:"text-xs leading-[14px] tracking-[0.12px] text-foreground",children:i})})]}));G.displayName="Field";const q=o.createContext(!1),B=o.forwardRef(({className:t,isLarge:s=!1,title:n="Verification code",showTitle:a=!0,belowText:l="Enter the 6-digit code sent to your email.",showBelowText:d=!0,children:i,...u},c)=>e.jsx(q.Provider,{value:s,children:e.jsxs("div",{ref:c,className:f("flex flex-col items-center gap-2",t),"data-node-id":"8060:1580",...u,children:[a?e.jsx("p",{className:"w-full text-center text-sm font-medium text-foreground",children:n}):null,e.jsx("div",{className:"flex items-center justify-center gap-2",children:i}),d?e.jsx("p",{className:"w-full text-center text-xs text-muted-foreground",children:l}):null]})}));B.displayName="InputOTP";const C=S.cva("relative flex shrink-0 items-center justify-center rounded-radius border bg-card text-sm text-foreground shadow-xs transition-[color,box-shadow]",{variants:{isLarge:{true:"size-9",false:"size-8"},active:{true:"z-10 border-disabled/30 ring-[3px] ring-disabled/20",false:"border-input"}},defaultVariants:{isLarge:!1,active:!1}}),F=o.forwardRef(({className:t,char:s,active:n=!1,isLarge:a,...l},d)=>{const i=o.useContext(q),u=a??i;return e.jsx("div",{ref:d,className:f(C({isLarge:u,active:n}),t),...l,children:s!=null?e.jsx("span",{className:"text-center",children:s}):null})});F.displayName="InputOTPSlot";const W=o.forwardRef(({className:t,...s},n)=>e.jsx("div",{ref:n,role:"separator",className:f("flex w-3 shrink-0 items-center justify-center",t),...s,children:e.jsx("div",{className:"h-0.5 w-full rounded-full bg-border"})}));W.displayName="InputOTPSeparator";const K=o.forwardRef(({className:t,value:s=50,min:n=0,max:a=100,label:l="Label",valueLabel:d,showLabels:i=!0,showSecondaryLabel:u=!0,...c},g)=>{const b=a-n||1,m=Math.min(100,Math.max(0,(s-n)/b*100));return e.jsxs("div",{ref:g,role:"meter","aria-valuenow":s,"aria-valuemin":n,"aria-valuemax":a,"aria-label":l,className:f("flex w-full flex-col gap-2",t),"data-node-id":"7664:31",...c,children:[i?e.jsxs("div",{className:"flex items-center justify-between",children:[l!=null?e.jsx("span",{className:"text-xs font-medium text-foreground",children:l}):null,u?e.jsx("span",{className:"text-xs font-medium text-foreground",children:d??`${Math.round(m)}%`}):null]}):null,e.jsx("div",{className:"relative h-2 w-full overflow-hidden bg-input","data-node-id":"7664:42",children:e.jsx("div",{className:"absolute inset-y-0 left-0 bg-primary transition-[width]",style:{width:`${m}%`},"data-node-id":"7664:44"})})]})});K.displayName="Meter";const I=S.cva("inline-flex shrink-0 items-center justify-center gap-1 rounded-radius-lg text-sm text-foreground transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 focus-visible:ring-disabled/[0.07] disabled:pointer-events-none disabled:opacity-50 data-[state=off]:hover:bg-card/50 data-[state=on]:bg-secondary/[0.64] [&_svg]:size-4 [&_svg]:shrink-0",{variants:{variant:{default:"border border-transparent",outline:"border border-border bg-card shadow-xs"},size:{sm:"h-7 px-1.5",md:"h-8 px-2",lg:"h-9 px-2.5"},iconOnly:{true:"px-0",false:""}},compoundVariants:[{iconOnly:!0,size:"sm",class:"w-7"},{iconOnly:!0,size:"md",class:"w-8"},{iconOnly:!0,size:"lg",class:"w-9"}],defaultVariants:{variant:"default",size:"md",iconOnly:!1}}),$=o.forwardRef(({className:t,variant:s,size:n,iconOnly:a,pressed:l,defaultPressed:d=!1,onPressedChange:i,onClick:u,...c},g)=>{const b=l!==void 0,[m,j]=o.useState(d),x=b?l:m;return e.jsx("button",{ref:g,type:"button","aria-pressed":x,"data-state":x?"on":"off",className:f(I({variant:s,size:n,iconOnly:a}),t),onClick:v=>{b||j(w=>!w),i==null||i(!x),u==null||u(v)},"data-node-id":"5685:204",...c})});$.displayName="Toggle";const H=o.createContext(null),U=o.forwardRef((t,s)=>{const{type:n,variant:a="outline",size:l="md",orientation:d="horizontal",className:i,children:u}=t,c=t.value!==void 0,[g,b]=o.useState(()=>t.defaultValue==null?[]:Array.isArray(t.defaultValue)?t.defaultValue:[t.defaultValue]),m=c?Array.isArray(t.value)?t.value:t.value?[t.value]:[]:g,j=x=>{var w,y;let v;n==="single"?(v=m[0]===x?[]:[x],c||b(v),(w=t.onValueChange)==null||w.call(t,v[0]??"")):(v=m.includes(x)?m.filter(h=>h!==x):[...m,x],c||b(v),(y=t.onValueChange)==null||y.call(t,v))};return e.jsx(H.Provider,{value:{value:m,toggle:j,variant:a,size:l,orientation:d},children:e.jsx("div",{ref:s,role:"group","data-node-id":"5686:270",className:f("inline-flex w-fit overflow-hidden rounded-radius-lg",d==="vertical"?"flex-col [&>*:not(:first-child)]:-mt-px":"flex-row [&>*:not(:first-child)]:-ml-px",i),children:u})})});U.displayName="ToggleGroup";const J=o.forwardRef(({value:t,iconOnly:s,className:n,onClick:a,...l},d)=>{const i=o.useContext(H);if(!i)throw new Error("ToggleGroupItem must be used within ToggleGroup");const u=i.value.includes(t);return e.jsx("button",{ref:d,type:"button","aria-pressed":u,"data-state":u?"on":"off",className:f(I({variant:i.variant,size:i.size,iconOnly:s}),"!rounded-none focus-visible:z-10",n),onClick:c=>{i.toggle(t),a==null||a(c)},...l})});J.displayName="ToggleGroupItem";const Q=S.cva("flex w-full select-none items-center justify-between gap-1 rounded-radius border bg-card text-sm text-foreground shadow-xs transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-[0.64]",{variants:{inputSize:{sm:"h-7 px-1.5",md:"h-8 px-2",lg:"h-9 px-2"},invalid:{true:"border-destructive focus-visible:ring-destructive/[0.15]",false:"border-input focus-visible:border-disabled/30 focus-visible:ring-disabled/[0.07]"}},defaultVariants:{inputSize:"md",invalid:!1}});function ne({className:t}){return e.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:t,"aria-hidden":!0,children:e.jsx("path",{d:"m6 9 6 6 6-6",strokeLinecap:"round",strokeLinejoin:"round"})})}function ae(){return e.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",className:"size-4","aria-hidden":!0,children:e.jsx("path",{d:"M20 6 9 17l-5-5",strokeLinecap:"round",strokeLinejoin:"round"})})}const X=o.forwardRef(({options:t,value:s,defaultValue:n,onValueChange:a,placeholder:l="Select…",disabled:d=!1,inputSize:i,invalid:u,className:c,contentClassName:g,id:b,name:m},j)=>{const x=s!==void 0,[v,w]=o.useState(n),y=x?s:v,[h,N]=o.useState(!1),T=o.useRef(null),O=o.useRef(null),R=t.find(r=>r.value===y);o.useEffect(()=>{if(!h)return;const r=p=>{T.current&&!T.current.contains(p.target)&&N(!1)};return document.addEventListener("mousedown",r),()=>document.removeEventListener("mousedown",r)},[h]),o.useEffect(()=>{var k;if(!h)return;const r=(k=O.current)==null?void 0:k.querySelectorAll("[data-select-item]:not([disabled])");if(!(r!=null&&r.length))return;(Array.from(r).find(z=>z.dataset.value===y)??r[0]).focus()},[h,y]);const Y=r=>{x||w(r),a==null||a(r),N(!1)},L=r=>{var V;const p=Array.from(((V=O.current)==null?void 0:V.querySelectorAll("[data-select-item]:not([disabled])"))??[]),k=p.findIndex(Z=>Z===document.activeElement),z=p[(k+r+p.length)%p.length];z==null||z.focus()};return e.jsxs("div",{ref:T,className:"relative",children:[e.jsxs("button",{ref:j,type:"button",id:b,name:m,disabled:d,"aria-haspopup":"listbox","aria-expanded":h,"data-node-id":"7751:1561",className:f(Q({inputSize:i,invalid:u}),c),onClick:()=>N(r=>!r),onKeyDown:r=>{(r.key==="ArrowDown"||r.key==="Enter"||r.key===" ")&&(r.preventDefault(),N(!0))},children:[e.jsx("span",{className:f("truncate",!R&&"text-muted-foreground"),children:R?R.label:l}),e.jsx(ne,{className:f("size-4 shrink-0 opacity-50 transition-transform",h&&"rotate-180")})]}),h&&e.jsx("div",{ref:O,role:"listbox",tabIndex:-1,className:f("absolute left-0 top-full z-50 mt-1 flex max-h-64 w-full flex-col gap-px overflow-auto rounded-radius-xl border border-border bg-popover p-1 shadow-popover",g),onKeyDown:r=>{var p,k;r.key==="Escape"?(N(!1),(k=(p=T.current)==null?void 0:p.querySelector("button"))==null||k.focus()):r.key==="ArrowDown"?(r.preventDefault(),L(1)):r.key==="ArrowUp"&&(r.preventDefault(),L(-1))},children:t.map(r=>{const p=r.value===y;return e.jsxs("button",{type:"button",role:"option","aria-selected":p,disabled:r.disabled,"data-select-item":"","data-value":r.value,className:f("flex h-7 w-full items-center gap-0.5 rounded-radius-lg px-0.5 text-left text-xs font-medium text-accent-foreground outline-none transition-colors","hover:bg-accent focus-visible:bg-accent","disabled:pointer-events-none disabled:opacity-[0.64]",p&&"bg-primary/[0.04]"),onClick:()=>Y(r.value),children:[e.jsx("span",{className:"flex size-7 shrink-0 items-center justify-center",children:p?e.jsx(ae,{}):null}),r.icon?e.jsx("span",{className:"inline-flex size-4 shrink-0 items-center justify-center [&_svg]:size-4",children:r.icon}):null,e.jsx("span",{className:"min-w-0 flex-1 truncate",children:r.label})]},r.value)})})]})});X.displayName="Select";exports.Button=M;exports.Checkbox=A;exports.Field=G;exports.Input=D;exports.InputOTP=B;exports.InputOTPSeparator=W;exports.InputOTPSlot=F;exports.Label=P;exports.Meter=K;exports.Select=X;exports.Toggle=$;exports.ToggleGroup=U;exports.ToggleGroupItem=J;exports.buttonVariants=_;exports.inputVariants=E;exports.selectTriggerVariants=Q;exports.slotVariants=C;exports.toggleVariants=I;