sh-ui-cli 0.52.1 → 0.52.3

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.
Files changed (88) hide show
  1. package/data/changelog/versions.json +27 -0
  2. package/data/registry/react/components/_smoke/vanilla-extract.test.ts +33 -0
  3. package/data/registry/react/components/input/styles.css.ts +6 -6
  4. package/data/registry/react/registry.json +35 -852
  5. package/package.json +1 -1
  6. package/src/api.d.ts +3 -4
  7. package/src/constants.js +9 -5
  8. package/src/mcp.mjs +0 -1
  9. package/data/registry/react/components/accordion/index.vanilla-extract.tsx +0 -97
  10. package/data/registry/react/components/accordion/styles.css.ts +0 -131
  11. package/data/registry/react/components/avatar/index.vanilla-extract.tsx +0 -73
  12. package/data/registry/react/components/avatar/styles.css.ts +0 -68
  13. package/data/registry/react/components/badge/index.vanilla-extract.tsx +0 -40
  14. package/data/registry/react/components/badge/styles.css.ts +0 -71
  15. package/data/registry/react/components/breadcrumb/index.vanilla-extract.tsx +0 -152
  16. package/data/registry/react/components/breadcrumb/styles.css.ts +0 -95
  17. package/data/registry/react/components/calendar/index.vanilla-extract.tsx +0 -806
  18. package/data/registry/react/components/calendar/styles.css.ts +0 -250
  19. package/data/registry/react/components/carousel/index.vanilla-extract.tsx +0 -430
  20. package/data/registry/react/components/carousel/styles.css.ts +0 -169
  21. package/data/registry/react/components/checkbox/index.vanilla-extract.tsx +0 -96
  22. package/data/registry/react/components/checkbox/styles.css.ts +0 -74
  23. package/data/registry/react/components/code-editor/index.vanilla-extract.tsx +0 -230
  24. package/data/registry/react/components/code-editor/styles.css.ts +0 -97
  25. package/data/registry/react/components/code-panel/index.vanilla-extract.tsx +0 -191
  26. package/data/registry/react/components/code-panel/styles.css.ts +0 -151
  27. package/data/registry/react/components/color-picker/index.vanilla-extract.tsx +0 -467
  28. package/data/registry/react/components/color-picker/styles.css.ts +0 -169
  29. package/data/registry/react/components/combobox/index.vanilla-extract.tsx +0 -165
  30. package/data/registry/react/components/combobox/styles.css.ts +0 -174
  31. package/data/registry/react/components/context-menu/index.vanilla-extract.tsx +0 -251
  32. package/data/registry/react/components/context-menu/styles.css.ts +0 -167
  33. package/data/registry/react/components/date-picker/index.vanilla-extract.tsx +0 -520
  34. package/data/registry/react/components/date-picker/styles.css.ts +0 -111
  35. package/data/registry/react/components/dialog/index.vanilla-extract.tsx +0 -95
  36. package/data/registry/react/components/dialog/styles.css.ts +0 -140
  37. package/data/registry/react/components/dropdown-menu/index.vanilla-extract.tsx +0 -255
  38. package/data/registry/react/components/dropdown-menu/styles.css.ts +0 -175
  39. package/data/registry/react/components/file-upload/index.vanilla-extract.tsx +0 -487
  40. package/data/registry/react/components/file-upload/styles.css.ts +0 -193
  41. package/data/registry/react/components/form/index.vanilla-extract.tsx +0 -61
  42. package/data/registry/react/components/form/styles.css.ts +0 -56
  43. package/data/registry/react/components/header/index.vanilla-extract.tsx +0 -805
  44. package/data/registry/react/components/header/styles.css.ts +0 -413
  45. package/data/registry/react/components/label/index.vanilla-extract.tsx +0 -52
  46. package/data/registry/react/components/label/styles.css.ts +0 -141
  47. package/data/registry/react/components/markdown-editor/index.vanilla-extract.tsx +0 -119
  48. package/data/registry/react/components/markdown-editor/styles.css.ts +0 -231
  49. package/data/registry/react/components/menubar/index.vanilla-extract.tsx +0 -32
  50. package/data/registry/react/components/menubar/styles.css.ts +0 -53
  51. package/data/registry/react/components/numeric-input/index.vanilla-extract.tsx +0 -148
  52. package/data/registry/react/components/numeric-input/styles.css.ts +0 -65
  53. package/data/registry/react/components/page-toc/index.vanilla-extract.tsx +0 -174
  54. package/data/registry/react/components/page-toc/styles.css.ts +0 -97
  55. package/data/registry/react/components/pagination/index.vanilla-extract.tsx +0 -269
  56. package/data/registry/react/components/pagination/styles.css.ts +0 -113
  57. package/data/registry/react/components/popover/index.vanilla-extract.tsx +0 -113
  58. package/data/registry/react/components/popover/styles.css.ts +0 -78
  59. package/data/registry/react/components/progress/index.vanilla-extract.tsx +0 -54
  60. package/data/registry/react/components/progress/styles.css.ts +0 -53
  61. package/data/registry/react/components/radio/index.vanilla-extract.tsx +0 -65
  62. package/data/registry/react/components/radio/styles.css.ts +0 -79
  63. package/data/registry/react/components/rich-text-editor/index.vanilla-extract.tsx +0 -348
  64. package/data/registry/react/components/rich-text-editor/styles.css.ts +0 -243
  65. package/data/registry/react/components/select/index.vanilla-extract.tsx +0 -234
  66. package/data/registry/react/components/select/styles.css.ts +0 -225
  67. package/data/registry/react/components/separator/index.vanilla-extract.tsx +0 -46
  68. package/data/registry/react/components/separator/styles.css.ts +0 -24
  69. package/data/registry/react/components/sidebar/index.vanilla-extract.tsx +0 -1067
  70. package/data/registry/react/components/sidebar/styles.css.ts +0 -578
  71. package/data/registry/react/components/skeleton/index.vanilla-extract.tsx +0 -22
  72. package/data/registry/react/components/skeleton/styles.css.ts +0 -30
  73. package/data/registry/react/components/slider/index.vanilla-extract.tsx +0 -298
  74. package/data/registry/react/components/slider/styles.css.ts +0 -75
  75. package/data/registry/react/components/spinner/index.vanilla-extract.tsx +0 -38
  76. package/data/registry/react/components/spinner/styles.css.ts +0 -60
  77. package/data/registry/react/components/switch/index.vanilla-extract.tsx +0 -39
  78. package/data/registry/react/components/switch/styles.css.ts +0 -87
  79. package/data/registry/react/components/tabs/index.vanilla-extract.tsx +0 -91
  80. package/data/registry/react/components/tabs/styles.css.ts +0 -145
  81. package/data/registry/react/components/textarea/index.vanilla-extract.tsx +0 -23
  82. package/data/registry/react/components/textarea/styles.css.ts +0 -55
  83. package/data/registry/react/components/toast/index.vanilla-extract.tsx +0 -258
  84. package/data/registry/react/components/toast/styles.css.ts +0 -307
  85. package/data/registry/react/components/toggle/index.vanilla-extract.tsx +0 -131
  86. package/data/registry/react/components/toggle/styles.css.ts +0 -109
  87. package/data/registry/react/components/tooltip/index.vanilla-extract.tsx +0 -83
  88. package/data/registry/react/components/tooltip/styles.css.ts +0 -59
@@ -1,467 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { cn } from "@SH_UI_UTILS@";
5
- import { byKey, colorPicker, colorPickerSv, colorPickerSvSaturation, colorPickerSvValue, colorPickerSvThumb, colorPickerHue, colorPickerHueThumb, colorPickerAlpha, colorPickerAlphaTrack, colorPickerRow, colorPickerSwatch, colorPickerHex, colorPickerSwatches, colorPickerSwatchBtn } from "./styles.css";
6
-
7
- /* ───────────── types ───────────── */
8
-
9
- interface HSV {
10
- h: number; // 0~360
11
- s: number; // 0~1
12
- v: number; // 0~1
13
- }
14
-
15
- interface HSVA extends HSV {
16
- a: number; // 0~1
17
- }
18
-
19
- export interface ColorPickerProps
20
- extends Omit<
21
- React.HTMLAttributes<HTMLDivElement>,
22
- "onChange" | "defaultValue" | "children"
23
- > {
24
- /** 제어 모드 색상값 (hex, 예: `"#FF8800"`). 6자리 / 3자리 / `#` 생략 모두 허용. */
25
- value?: string;
26
- /** 색상 변경 콜백. 항상 6자리 대문자 hex(`"#RRGGBB"`)로 통일되어 전달된다. */
27
- onChange?: (hex: string) => void;
28
- /**
29
- * 비제어 모드 초기값.
30
- * @default "#000000"
31
- */
32
- defaultValue?: string;
33
- /**
34
- * compound 모드. 미지정 시 기본 레이아웃(Saturation + Hue + Hex)이 자동 렌더된다.
35
- * 직접 조립하려면 `ColorPickerSaturation`/`Hue`/`Alpha`/`Hex`/`Swatches`를 자식으로 넘긴다.
36
- */
37
- children?: React.ReactNode;
38
- }
39
-
40
- /* ───────────── color math ───────────── */
41
-
42
- function clamp(n: number, min: number, max: number) {
43
- return Math.min(max, Math.max(min, n));
44
- }
45
-
46
- function hexToRgb(hex: string): [number, number, number] {
47
- const m = hex.replace("#", "");
48
- const full = m.length === 3 ? m.split("").map((c) => c + c).join("") : m;
49
- const n = parseInt(full, 16);
50
- return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
51
- }
52
-
53
- function rgbToHex(r: number, g: number, b: number): string {
54
- const toHex = (n: number) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0");
55
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
56
- }
57
-
58
- function rgbToHsv(r: number, g: number, b: number): HSV {
59
- const rn = r / 255, gn = g / 255, bn = b / 255;
60
- const max = Math.max(rn, gn, bn);
61
- const min = Math.min(rn, gn, bn);
62
- const d = max - min;
63
- let h = 0;
64
- if (d !== 0) {
65
- if (max === rn) h = ((gn - bn) / d) % 6;
66
- else if (max === gn) h = (bn - rn) / d + 2;
67
- else h = (rn - gn) / d + 4;
68
- h *= 60;
69
- if (h < 0) h += 360;
70
- }
71
- const s = max === 0 ? 0 : d / max;
72
- const v = max;
73
- return { h, s, v };
74
- }
75
-
76
- function hsvToRgb({ h, s, v }: HSV): [number, number, number] {
77
- const c = v * s;
78
- const hh = h / 60;
79
- const x = c * (1 - Math.abs((hh % 2) - 1));
80
- let r = 0, g = 0, b = 0;
81
- if (hh >= 0 && hh < 1) [r, g, b] = [c, x, 0];
82
- else if (hh < 2) [r, g, b] = [x, c, 0];
83
- else if (hh < 3) [r, g, b] = [0, c, x];
84
- else if (hh < 4) [r, g, b] = [0, x, c];
85
- else if (hh < 5) [r, g, b] = [x, 0, c];
86
- else [r, g, b] = [c, 0, x];
87
- const m = v - c;
88
- return [(r + m) * 255, (g + m) * 255, (b + m) * 255];
89
- }
90
-
91
- function hexToHsv(hex: string): HSV {
92
- const [r, g, b] = hexToRgb(hex);
93
- return rgbToHsv(r, g, b);
94
- }
95
-
96
- function hsvToHex(hsv: HSV): string {
97
- const [r, g, b] = hsvToRgb(hsv);
98
- return rgbToHex(r, g, b);
99
- }
100
-
101
- const HEX_RE = /^#?[0-9a-f]{6}$/i;
102
-
103
- /* ───────────── drag hook ───────────── */
104
-
105
- function useDrag(onMove: (e: PointerEvent, el: HTMLElement) => void) {
106
- const ref = React.useRef<HTMLDivElement>(null);
107
-
108
- const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
109
- const el = ref.current;
110
- if (!el) return;
111
- el.setPointerCapture(e.pointerId);
112
- onMove(e.nativeEvent, el);
113
-
114
- const onPointerMove = (ev: PointerEvent) => onMove(ev, el);
115
- const onPointerUp = (ev: PointerEvent) => {
116
- el.releasePointerCapture(ev.pointerId);
117
- el.removeEventListener("pointermove", onPointerMove);
118
- el.removeEventListener("pointerup", onPointerUp);
119
- };
120
- el.addEventListener("pointermove", onPointerMove);
121
- el.addEventListener("pointerup", onPointerUp);
122
- };
123
-
124
- return { ref, onPointerDown };
125
- }
126
-
127
- /* ───────────── context ───────────── */
128
-
129
- interface ColorPickerContextValue {
130
- hsva: HSVA;
131
- hex: string;
132
- /** 현재 hue에 해당하는 순색(pure) hex. SV 배경용. */
133
- pureHueHex: string;
134
- setHsv: (next: Partial<HSV>) => void;
135
- setAlpha: (a: number) => void;
136
- commitHex: (raw: string) => boolean;
137
- }
138
-
139
- const ColorPickerContext = React.createContext<ColorPickerContextValue | null>(null);
140
-
141
- function useColorPicker() {
142
- const ctx = React.useContext(ColorPickerContext);
143
- if (!ctx) {
144
- throw new Error(
145
- "ColorPicker 하위 컴포넌트는 <ColorPicker> 내부에서만 사용할 수 있습니다.",
146
- );
147
- }
148
- return ctx;
149
- }
150
-
151
- /* ───────────── root ───────────── */
152
-
153
- /**
154
- * HSV 모델 기반 색상 선택기. children을 생략하면 기본 레이아웃(SV + Hue + Hex)이 자동 렌더되고,
155
- * 직접 조립하려면 ColorPickerSaturation/Hue/Alpha/Hex/Swatches를 자식으로 넘긴다.
156
- * 외부 노출값은 항상 6자리 대문자 hex(`#RRGGBB`).
157
- */
158
- export function ColorPicker({
159
- value: valueProp,
160
- onChange,
161
- defaultValue = "#000000",
162
- className,
163
- children,
164
- ...rest
165
- }: ColorPickerProps) {
166
- const isControlled = valueProp !== undefined;
167
- const [internal, setInternal] = React.useState(defaultValue);
168
- const value = isControlled ? valueProp! : internal;
169
-
170
- const [hsva, setHsva] = React.useState<HSVA>(() => ({ ...hexToHsv(value), a: 1 }));
171
-
172
- /* 외부 value 변경 시 hsv 동기화 (우리가 내놓은 hex는 무시 — 무한 루프 방지) */
173
- const lastEmittedRef = React.useRef(value);
174
- React.useEffect(() => {
175
- if (value === lastEmittedRef.current) return;
176
- setHsva((prev) => ({ ...hexToHsv(value), a: prev.a }));
177
- }, [value]);
178
-
179
- const emit = React.useCallback(
180
- (next: HSVA) => {
181
- const hex = hsvToHex(next);
182
- lastEmittedRef.current = hex;
183
- setHsva(next);
184
- if (!isControlled) setInternal(hex);
185
- onChange?.(hex);
186
- },
187
- [isControlled, onChange],
188
- );
189
-
190
- const setHsv = React.useCallback(
191
- (partial: Partial<HSV>) => {
192
- const next: HSVA = { ...hsva, ...partial };
193
- const hex = hsvToHex(next);
194
- lastEmittedRef.current = hex;
195
- setHsva(next);
196
- if (!isControlled) setInternal(hex);
197
- onChange?.(hex);
198
- },
199
- [hsva, isControlled, onChange],
200
- );
201
-
202
- const setAlpha = React.useCallback((a: number) => {
203
- setHsva((prev) => ({ ...prev, a: clamp(a, 0, 1) }));
204
- }, []);
205
-
206
- const commitHex = React.useCallback(
207
- (raw: string) => {
208
- const v = raw.trim();
209
- if (!HEX_RE.test(v)) return false;
210
- const normalized = (v.startsWith("#") ? v : `#${v}`).toUpperCase();
211
- const nextHsv = hexToHsv(normalized);
212
- emit({ ...nextHsv, a: hsva.a });
213
- return true;
214
- },
215
- [emit, hsva.a],
216
- );
217
-
218
- const pureHueHex = React.useMemo(
219
- () => hsvToHex({ h: hsva.h, s: 1, v: 1 }),
220
- [hsva.h],
221
- );
222
-
223
- const ctx = React.useMemo<ColorPickerContextValue>(
224
- () => ({
225
- hsva,
226
- hex: value,
227
- pureHueHex,
228
- setHsv,
229
- setAlpha,
230
- commitHex,
231
- }),
232
- [hsva, value, pureHueHex, setHsv, setAlpha, commitHex],
233
- );
234
-
235
- return (
236
- <ColorPickerContext.Provider value={ctx}>
237
- <div
238
- className={cn(colorPicker, className)}
239
- {...rest}
240
- >
241
- {children ?? (
242
- <>
243
- <ColorPickerSaturation />
244
- <ColorPickerHue />
245
- <ColorPickerHex />
246
- </>
247
- )}
248
- </div>
249
- </ColorPickerContext.Provider>
250
- );
251
- }
252
-
253
- /* ───────────── parts ───────────── */
254
-
255
- export interface ColorPickerSaturationProps
256
- extends Omit<React.HTMLAttributes<HTMLDivElement>, "onPointerDown"> {}
257
-
258
- /** 채도(S)와 명도(V)를 동시에 조절하는 2D 박스. 포인터 드래그로 조작. */
259
- export function ColorPickerSaturation({
260
- className,
261
- style,
262
- ...rest
263
- }: ColorPickerSaturationProps) {
264
- const { hsva, hex, pureHueHex, setHsv } = useColorPicker();
265
- const drag = useDrag((e, el) => {
266
- const r = el.getBoundingClientRect();
267
- const x = clamp((e.clientX - r.left) / r.width, 0, 1);
268
- const y = clamp((e.clientY - r.top) / r.height, 0, 1);
269
- setHsv({ s: x, v: 1 - y });
270
- });
271
- return (
272
- <div
273
- ref={drag.ref}
274
- onPointerDown={drag.onPointerDown}
275
- className={cn(colorPickerSv, className)}
276
- style={{ background: pureHueHex, ...style }}
277
- role="slider"
278
- aria-label="채도/명도"
279
- aria-valuemin={0}
280
- aria-valuemax={100}
281
- aria-valuenow={Math.round(hsva.s * 100)}
282
- {...rest}
283
- >
284
- <div className={colorPickerSvSaturation} />
285
- <div className={colorPickerSvValue} />
286
- <div
287
- className={colorPickerSvThumb}
288
- style={{
289
- left: `${hsva.s * 100}%`,
290
- top: `${(1 - hsva.v) * 100}%`,
291
- background: hex,
292
- }}
293
- />
294
- </div>
295
- );
296
- }
297
-
298
- export interface ColorPickerHueProps
299
- extends Omit<React.HTMLAttributes<HTMLDivElement>, "onPointerDown"> {}
300
-
301
- /** 색상(H, 0~360°) 슬라이더. 무지개 그라데이션 위에 thumb이 위치. */
302
- export function ColorPickerHue({ className, ...rest }: ColorPickerHueProps) {
303
- const { hsva, setHsv } = useColorPicker();
304
- const drag = useDrag((e, el) => {
305
- const r = el.getBoundingClientRect();
306
- const x = clamp((e.clientX - r.left) / r.width, 0, 1);
307
- setHsv({ h: x * 360 });
308
- });
309
- return (
310
- <div
311
- ref={drag.ref}
312
- onPointerDown={drag.onPointerDown}
313
- className={cn(colorPickerHue, className)}
314
- role="slider"
315
- aria-label="색조"
316
- aria-valuemin={0}
317
- aria-valuemax={360}
318
- aria-valuenow={Math.round(hsva.h)}
319
- {...rest}
320
- >
321
- <div
322
- className={colorPickerHueThumb}
323
- style={{ left: `${(hsva.h / 360) * 100}%` }}
324
- />
325
- </div>
326
- );
327
- }
328
-
329
- export interface ColorPickerAlphaProps
330
- extends Omit<React.HTMLAttributes<HTMLDivElement>, "onPointerDown"> {}
331
-
332
- /** 투명도(A, 0~100%) 슬라이더. 외부에 알파를 노출하지 않는 hex 모드와는 시각 표시용. */
333
- export function ColorPickerAlpha({ className, style, ...rest }: ColorPickerAlphaProps) {
334
- const { hsva, hex, setAlpha } = useColorPicker();
335
- const drag = useDrag((e, el) => {
336
- const r = el.getBoundingClientRect();
337
- const x = clamp((e.clientX - r.left) / r.width, 0, 1);
338
- setAlpha(x);
339
- });
340
- const gradient = `linear-gradient(to right, rgba(0,0,0,0) 0%, ${hex} 100%)`;
341
- return (
342
- <div
343
- ref={drag.ref}
344
- onPointerDown={drag.onPointerDown}
345
- className={cn(colorPickerAlpha, className)}
346
- role="slider"
347
- aria-label="투명도"
348
- aria-valuemin={0}
349
- aria-valuemax={100}
350
- aria-valuenow={Math.round(hsva.a * 100)}
351
- style={style}
352
- {...rest}
353
- >
354
- <div
355
- className={colorPickerAlphaTrack}
356
- style={{ backgroundImage: gradient }}
357
- />
358
- <div
359
- className={colorPickerHueThumb}
360
- style={{ left: `${hsva.a * 100}%` }}
361
- />
362
- </div>
363
- );
364
- }
365
-
366
- export interface ColorPickerHexProps
367
- extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
368
- /**
369
- * input 좌측에 현재 색상 미리보기 swatch 표시 여부.
370
- * @default true
371
- */
372
- showSwatch?: boolean;
373
- }
374
-
375
- /** Hex 직접 입력 + 좌측 swatch. blur·Enter 시 검증·커밋되며 잘못된 값은 이전 값으로 되돌린다. */
376
- export function ColorPickerHex({
377
- className,
378
- showSwatch = true,
379
- ...rest
380
- }: ColorPickerHexProps) {
381
- const { hex, commitHex } = useColorPicker();
382
- const [draft, setDraft] = React.useState(hex);
383
-
384
- // 외부 hex 변경 시 draft 동기화 (단, 포커스 중이 아닐 때만)
385
- const inputRef = React.useRef<HTMLInputElement>(null);
386
- React.useEffect(() => {
387
- if (document.activeElement !== inputRef.current) setDraft(hex);
388
- }, [hex]);
389
-
390
- const onCommit = () => {
391
- if (!commitHex(draft)) setDraft(hex);
392
- };
393
-
394
- return (
395
- <div
396
- className={cn(colorPickerRow, className)}
397
- {...rest}
398
- >
399
- {showSwatch && (
400
- <div
401
- className={colorPickerSwatch}
402
- style={{ background: hex }}
403
- aria-hidden
404
- />
405
- )}
406
- <input
407
- ref={inputRef}
408
- type="text"
409
- className={colorPickerHex}
410
- value={draft}
411
- onChange={(e) => setDraft(e.target.value)}
412
- onBlur={onCommit}
413
- onKeyDown={(e) => {
414
- if (e.key === "Enter") {
415
- e.preventDefault();
416
- (e.target as HTMLInputElement).blur();
417
- }
418
- }}
419
- spellCheck={false}
420
- aria-label="Hex"
421
- />
422
- </div>
423
- );
424
- }
425
-
426
- export interface ColorPickerSwatchesProps
427
- extends React.HTMLAttributes<HTMLDivElement> {
428
- /**
429
- * 표시할 hex 색상 목록. 항목 클릭 시 해당 색상으로 즉시 commit된다.
430
- * 형식은 `"#RRGGBB"` 권장 (입력 시 대소문자는 자동 정규화).
431
- */
432
- colors: string[];
433
- }
434
-
435
- /** 미리 정의된 색상 팔레트 그리드. 각 항목 클릭 시 그 색상으로 즉시 커밋한다. */
436
- export function ColorPickerSwatches({
437
- className,
438
- colors,
439
- ...rest
440
- }: ColorPickerSwatchesProps) {
441
- const { hex, commitHex } = useColorPicker();
442
- return (
443
- <div
444
- role="group"
445
- aria-label="미리 준비된 색상"
446
- className={cn(colorPickerSwatches, className)}
447
- {...rest}
448
- >
449
- {colors.map((c) => {
450
- const normalized = c.toUpperCase();
451
- const selected = normalized === hex.toUpperCase();
452
- return (
453
- <button
454
- key={c}
455
- type="button"
456
- className={colorPickerSwatchBtn}
457
- aria-label={c}
458
- aria-pressed={selected}
459
- data-selected={selected || undefined}
460
- style={{ background: c }}
461
- onClick={() => commitHex(c)}
462
- />
463
- );
464
- })}
465
- </div>
466
- );
467
- }
@@ -1,169 +0,0 @@
1
- import { style } from "@vanilla-extract/css";
2
-
3
- export const colorPicker = style({
4
- display: "flex",
5
- flexDirection: "column",
6
- gap: "0.625rem",
7
- width: "100%",
8
- userSelect: "none",
9
- WebkitUserSelect: "none",
10
- });
11
-
12
- export const colorPickerSv = style({
13
- position: "relative",
14
- width: "100%",
15
- aspectRatio: "4 / 3",
16
- borderRadius: "var(--radius)",
17
- cursor: "crosshair",
18
- overflow: "hidden",
19
- touchAction: "none",
20
- });
21
-
22
- export const colorPickerSvSaturation = style({
23
- position: "absolute",
24
- inset: 0,
25
- background: "linear-gradient(to right, #fff, transparent)",
26
- });
27
-
28
- export const colorPickerSvValue = style({
29
- position: "absolute",
30
- inset: 0,
31
- background: "linear-gradient(to top, #000, transparent)",
32
- });
33
-
34
- export const colorPickerSvThumb = style({
35
- position: "absolute",
36
- width: "0.875rem",
37
- height: "0.875rem",
38
- marginLeft: "-0.4375rem",
39
- marginTop: "-0.4375rem",
40
- border: "2px solid #fff",
41
- borderRadius: "50%",
42
- boxShadow: "0 0 0 1px rgba(0, 0, 0, 0.4)",
43
- pointerEvents: "none",
44
- });
45
-
46
- export const colorPickerHue = style({
47
- position: "relative",
48
- width: "100%",
49
- height: "0.875rem",
50
- borderRadius: "999px",
51
- cursor: "pointer",
52
- touchAction: "none",
53
- background: "linear-gradient(\n to right,\n #f00 0%,\n #ff0 16.66%,\n #0f0 33.33%,\n #0ff 50%,\n #00f 66.66%,\n #f0f 83.33%,\n #f00 100%\n )",
54
- });
55
-
56
- export const colorPickerHueThumb = style({
57
- position: "absolute",
58
- top: "50%",
59
- width: "0.875rem",
60
- height: "0.875rem",
61
- marginLeft: "-0.4375rem",
62
- transform: "translateY(-50%)",
63
- background: "#fff",
64
- borderRadius: "50%",
65
- boxShadow: "0 0 0 1px rgba(0, 0, 0, 0.4)",
66
- pointerEvents: "none",
67
- });
68
-
69
- export const colorPickerAlpha = style({
70
- position: "relative",
71
- width: "100%",
72
- height: "0.875rem",
73
- borderRadius: "999px",
74
- cursor: "pointer",
75
- touchAction: "none",
76
- backgroundColor: "#fff",
77
- backgroundImage: "linear-gradient(45deg, #ccc 25%, transparent 25%),\n linear-gradient(-45deg, #ccc 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, #ccc 75%),\n linear-gradient(-45deg, transparent 75%, #ccc 75%)",
78
- backgroundSize: "8px 8px",
79
- backgroundPosition: "0 0, 0 4px, 4px -4px, -4px 0",
80
- overflow: "hidden",
81
- });
82
-
83
- export const colorPickerAlphaTrack = style({
84
- position: "absolute",
85
- inset: 0,
86
- borderRadius: "inherit",
87
- pointerEvents: "none",
88
- });
89
-
90
- export const colorPickerRow = style({
91
- display: "flex",
92
- alignItems: "center",
93
- gap: "var(--space-2)",
94
- });
95
-
96
- export const colorPickerSwatch = style({
97
- width: "1.75rem",
98
- height: "1.75rem",
99
- borderRadius: "calc(var(--radius) - 2px)",
100
- border: "1px solid var(--border)",
101
- flexShrink: 0,
102
- });
103
-
104
- export const colorPickerHex = style({
105
- flex: 1,
106
- minWidth: 0,
107
- height: "1.75rem",
108
- padding: "0 var(--space-2)",
109
- border: "1px solid var(--border)",
110
- borderRadius: "calc(var(--radius) - 2px)",
111
- background: "var(--background)",
112
- color: "var(--foreground)",
113
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
114
- fontSize: "0.8125rem",
115
- textTransform: "uppercase",
116
- selectors: {
117
- "&:focus": {
118
- outline: "none",
119
- borderColor: "var(--foreground)",
120
- boxShadow: "0 0 0 1px var(--foreground)",
121
- },
122
- },
123
- });
124
-
125
- export const colorPickerSwatches = style({
126
- display: "flex",
127
- flexWrap: "wrap",
128
- gap: "0.375rem",
129
- });
130
-
131
- export const colorPickerSwatchBtn = style({
132
- width: "1.25rem",
133
- height: "1.25rem",
134
- padding: 0,
135
- border: "1px solid var(--border)",
136
- borderRadius: "calc(var(--radius) - 4px)",
137
- cursor: "pointer",
138
- transition: "transform var(--duration-fast), box-shadow var(--duration-fast)",
139
- selectors: {
140
- "&:hover": {
141
- transform: "scale(1.08)",
142
- },
143
- "&:focus-visible": {
144
- outline: "var(--border-width-strong) solid var(--foreground)",
145
- outlineOffset: "2px",
146
- },
147
- "&[data-selected]": {
148
- boxShadow: "0 0 0 2px var(--background), 0 0 0 3.5px var(--foreground)",
149
- },
150
- },
151
- });
152
-
153
- /** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
154
- export const byKey: Record<string, string> = {
155
- "color-picker": colorPicker,
156
- "color-picker__sv": colorPickerSv,
157
- "color-picker__sv-saturation": colorPickerSvSaturation,
158
- "color-picker__sv-value": colorPickerSvValue,
159
- "color-picker__sv-thumb": colorPickerSvThumb,
160
- "color-picker__hue": colorPickerHue,
161
- "color-picker__hue-thumb": colorPickerHueThumb,
162
- "color-picker__alpha": colorPickerAlpha,
163
- "color-picker__alpha-track": colorPickerAlphaTrack,
164
- "color-picker__row": colorPickerRow,
165
- "color-picker__swatch": colorPickerSwatch,
166
- "color-picker__hex": colorPickerHex,
167
- "color-picker__swatches": colorPickerSwatches,
168
- "color-picker__swatch-btn": colorPickerSwatchBtn,
169
- };