sh-ui-cli 0.45.2 → 0.46.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.
Files changed (98) hide show
  1. package/data/changelog/versions.json +26 -0
  2. package/data/registry/react/components/accordion/index.tailwind.tsx +5 -7
  3. package/data/registry/react/components/accordion/index.tsx +5 -7
  4. package/data/registry/react/components/avatar/index.tailwind.tsx +4 -6
  5. package/data/registry/react/components/avatar/index.tsx +4 -6
  6. package/data/registry/react/components/badge/index.tailwind.tsx +2 -4
  7. package/data/registry/react/components/badge/index.tsx +2 -4
  8. package/data/registry/react/components/breadcrumb/index.tailwind.tsx +8 -10
  9. package/data/registry/react/components/breadcrumb/index.tsx +8 -10
  10. package/data/registry/react/components/button/index.module.tsx +45 -0
  11. package/data/registry/react/components/button/index.tailwind.tsx +2 -1
  12. package/data/registry/react/components/button/index.tsx +3 -4
  13. package/data/registry/react/components/button/styles.module.css +92 -0
  14. package/data/registry/react/components/calendar/index.tailwind.tsx +10 -12
  15. package/data/registry/react/components/calendar/index.tsx +9 -11
  16. package/data/registry/react/components/card/index.module.tsx +63 -0
  17. package/data/registry/react/components/card/index.tailwind.tsx +8 -10
  18. package/data/registry/react/components/card/index.tsx +8 -10
  19. package/data/registry/react/components/card/styles.module.css +73 -0
  20. package/data/registry/react/components/carousel/index.tailwind.tsx +7 -9
  21. package/data/registry/react/components/carousel/index.tsx +7 -9
  22. package/data/registry/react/components/checkbox/index.tailwind.tsx +3 -5
  23. package/data/registry/react/components/checkbox/index.tsx +3 -5
  24. package/data/registry/react/components/code-editor/index.tailwind.tsx +2 -4
  25. package/data/registry/react/components/code-editor/index.tsx +2 -4
  26. package/data/registry/react/components/code-panel/index.tailwind.tsx +5 -7
  27. package/data/registry/react/components/code-panel/index.tsx +5 -7
  28. package/data/registry/react/components/color-picker/index.tailwind.tsx +7 -6
  29. package/data/registry/react/components/color-picker/index.tsx +7 -6
  30. package/data/registry/react/components/combobox/index.tailwind.tsx +8 -10
  31. package/data/registry/react/components/combobox/index.tsx +8 -10
  32. package/data/registry/react/components/context-menu/index.tailwind.tsx +10 -12
  33. package/data/registry/react/components/context-menu/index.tsx +10 -12
  34. package/data/registry/react/components/date-picker/index.tailwind.tsx +7 -9
  35. package/data/registry/react/components/date-picker/index.tsx +7 -9
  36. package/data/registry/react/components/dialog/index.tailwind.tsx +6 -8
  37. package/data/registry/react/components/dialog/index.tsx +6 -8
  38. package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +10 -12
  39. package/data/registry/react/components/dropdown-menu/index.tsx +10 -12
  40. package/data/registry/react/components/file-upload/index.tailwind.tsx +6 -8
  41. package/data/registry/react/components/file-upload/index.tsx +6 -8
  42. package/data/registry/react/components/form/field.tailwind.tsx +2 -1
  43. package/data/registry/react/components/form/field.tsx +2 -3
  44. package/data/registry/react/components/header/index.tailwind.tsx +17 -19
  45. package/data/registry/react/components/header/index.tsx +17 -19
  46. package/data/registry/react/components/input/index.module.tsx +486 -0
  47. package/data/registry/react/components/input/index.tailwind.tsx +4 -6
  48. package/data/registry/react/components/input/index.tsx +4 -6
  49. package/data/registry/react/components/input/styles.module.css +200 -0
  50. package/data/registry/react/components/label/index.tailwind.tsx +6 -8
  51. package/data/registry/react/components/label/index.tsx +6 -8
  52. package/data/registry/react/components/markdown-editor/index.tailwind.tsx +2 -4
  53. package/data/registry/react/components/markdown-editor/index.tsx +2 -4
  54. package/data/registry/react/components/menubar/index.tailwind.tsx +2 -4
  55. package/data/registry/react/components/menubar/index.tsx +2 -4
  56. package/data/registry/react/components/numeric-input/index.tailwind.tsx +2 -4
  57. package/data/registry/react/components/numeric-input/index.tsx +2 -4
  58. package/data/registry/react/components/page-toc/index.tailwind.tsx +3 -2
  59. package/data/registry/react/components/page-toc/index.tsx +2 -3
  60. package/data/registry/react/components/pagination/index.tailwind.tsx +8 -10
  61. package/data/registry/react/components/pagination/index.tsx +8 -10
  62. package/data/registry/react/components/popover/index.tailwind.tsx +4 -6
  63. package/data/registry/react/components/popover/index.tsx +4 -6
  64. package/data/registry/react/components/progress/index.tailwind.tsx +3 -5
  65. package/data/registry/react/components/progress/index.tsx +2 -4
  66. package/data/registry/react/components/radio/index.tailwind.tsx +3 -5
  67. package/data/registry/react/components/radio/index.tsx +3 -5
  68. package/data/registry/react/components/rich-text-editor/index.tailwind.tsx +3 -5
  69. package/data/registry/react/components/rich-text-editor/index.tsx +3 -5
  70. package/data/registry/react/components/select/index.tailwind.tsx +8 -10
  71. package/data/registry/react/components/select/index.tsx +8 -10
  72. package/data/registry/react/components/separator/index.tailwind.tsx +2 -4
  73. package/data/registry/react/components/separator/index.tsx +2 -4
  74. package/data/registry/react/components/sidebar/index.tailwind.tsx +32 -43
  75. package/data/registry/react/components/sidebar/index.tsx +29 -46
  76. package/data/registry/react/components/skeleton/index.tailwind.tsx +2 -4
  77. package/data/registry/react/components/skeleton/index.tsx +2 -4
  78. package/data/registry/react/components/slider/index.tailwind.tsx +5 -7
  79. package/data/registry/react/components/slider/index.tsx +5 -7
  80. package/data/registry/react/components/spinner/index.tailwind.tsx +3 -5
  81. package/data/registry/react/components/spinner/index.tsx +2 -4
  82. package/data/registry/react/components/switch/index.tailwind.tsx +3 -5
  83. package/data/registry/react/components/switch/index.tsx +2 -4
  84. package/data/registry/react/components/tabs/index.tailwind.tsx +6 -8
  85. package/data/registry/react/components/tabs/index.tsx +6 -8
  86. package/data/registry/react/components/textarea/index.tailwind.tsx +2 -4
  87. package/data/registry/react/components/textarea/index.tsx +2 -4
  88. package/data/registry/react/components/toggle/index.tailwind.tsx +4 -6
  89. package/data/registry/react/components/toggle/index.tsx +4 -6
  90. package/data/registry/react/components/tooltip/index.tailwind.tsx +2 -4
  91. package/data/registry/react/components/tooltip/index.tsx +2 -4
  92. package/data/registry/react/lib/cn.tailwind.ts +17 -0
  93. package/data/registry/react/peer-versions.json +3 -1
  94. package/data/registry/react/registry.json +202 -43
  95. package/data/tokens/build.mjs +4 -0
  96. package/package.json +1 -1
  97. package/src/add.mjs +37 -13
  98. package/templates/ui-app-template/sh-ui.config.json +5 -0
@@ -1,10 +1,8 @@
1
1
  import * as React from "react";
2
2
  import "./styles.css";
3
3
 
4
- function cx(...args: (string | undefined | false | null)[]) {
5
- return args.filter(Boolean).join(" ");
6
- }
7
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
8
6
  /* ───────── Pagination (nav) ─────────
9
7
  * 시맨틱: <nav aria-label="Pagination"><ul>...</ul></nav>.
10
8
  * 현재 페이지 링크에 aria-current="page"를 부여해 스크린리더가 위치를 읽게 한다.
@@ -22,7 +20,7 @@ export const Pagination = React.forwardRef<
22
20
  <nav
23
21
  ref={ref}
24
22
  aria-label="Pagination"
25
- className={cx("sh-ui-pagination", className)}
23
+ className={cn("sh-ui-pagination", className)}
26
24
  {...props}
27
25
  />
28
26
  );
@@ -38,7 +36,7 @@ export const PaginationContent = React.forwardRef<
38
36
  return (
39
37
  <ul
40
38
  ref={ref}
41
- className={cx("sh-ui-pagination__content", className)}
39
+ className={cn("sh-ui-pagination__content", className)}
42
40
  {...props}
43
41
  />
44
42
  );
@@ -54,7 +52,7 @@ export const PaginationItem = React.forwardRef<
54
52
  return (
55
53
  <li
56
54
  ref={ref}
57
- className={cx("sh-ui-pagination__item", className)}
55
+ className={cn("sh-ui-pagination__item", className)}
58
56
  {...props}
59
57
  />
60
58
  );
@@ -98,7 +96,7 @@ export const PaginationLink = React.forwardRef<
98
96
  aria-current={isActive ? "page" : undefined}
99
97
  data-active={isActive ? "" : undefined}
100
98
  data-size={size}
101
- className={cx("sh-ui-pagination__link", className)}
99
+ className={cn("sh-ui-pagination__link", className)}
102
100
  {...props}
103
101
  />
104
102
  );
@@ -117,7 +115,7 @@ export const PaginationPrevious = React.forwardRef<
117
115
  <PaginationLink
118
116
  ref={ref}
119
117
  aria-label="이전 페이지"
120
- className={cx("sh-ui-pagination__nav", className)}
118
+ className={cn("sh-ui-pagination__nav", className)}
121
119
  {...props}
122
120
  >
123
121
  <ChevronLeftIcon />
@@ -135,7 +133,7 @@ export const PaginationNext = React.forwardRef<
135
133
  <PaginationLink
136
134
  ref={ref}
137
135
  aria-label="다음 페이지"
138
- className={cx("sh-ui-pagination__nav", className)}
136
+ className={cn("sh-ui-pagination__nav", className)}
139
137
  {...props}
140
138
  >
141
139
  {children ?? <span>다음</span>}
@@ -156,7 +154,7 @@ export const PaginationEllipsis = React.forwardRef<
156
154
  ref={ref}
157
155
  role="presentation"
158
156
  aria-hidden="true"
159
- className={cx("sh-ui-pagination__ellipsis", className)}
157
+ className={cn("sh-ui-pagination__ellipsis", className)}
160
158
  {...props}
161
159
  >
162
160
  <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden>
@@ -1,11 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { Popover as BasePopover } from "@base-ui/react/popover";
3
3
 
4
+ import { cn } from "@SH_UI_UTILS@";
4
5
  type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
5
6
 
6
- function cx(...args: (string | undefined | false)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
7
 
10
8
  export const Popover = BasePopover.Root;
11
9
  export const PopoverTrigger = BasePopover.Trigger;
@@ -35,7 +33,7 @@ export const PopoverContent = React.forwardRef<HTMLDivElement, PopoverContentPro
35
33
  >
36
34
  <BasePopover.Popup
37
35
  ref={ref}
38
- className={cx(
36
+ className={cn(
39
37
  "min-w-48 p-[var(--space-2)] bg-background text-foreground border border-border rounded-[var(--radius)] shadow-[0_8px_24px_rgba(0,0,0,0.12)] outline-none text-[length:var(--text-sm)] leading-snug origin-[var(--transform-origin)] transition-[opacity,transform] duration-[140ms] ease-out motion-reduce:transition-none data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 motion-reduce:data-[starting-style]:scale-100 motion-reduce:data-[ending-style]:scale-100 focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2",
40
38
  className,
41
39
  )}
@@ -57,7 +55,7 @@ export const PopoverTitle = React.forwardRef<
57
55
  return (
58
56
  <BasePopover.Title
59
57
  ref={ref}
60
- className={cx("m-0 mb-[var(--space-1)] font-semibold text-[0.9375rem]", className)}
58
+ className={cn("m-0 mb-[var(--space-1)] font-semibold text-[0.9375rem]", className)}
61
59
  {...props}
62
60
  />
63
61
  );
@@ -70,7 +68,7 @@ export const PopoverDescription = React.forwardRef<
70
68
  return (
71
69
  <BasePopover.Description
72
70
  ref={ref}
73
- className={cx("m-0 text-foreground-muted text-[0.8125rem]", className)}
71
+ className={cn("m-0 text-foreground-muted text-[0.8125rem]", className)}
74
72
  {...props}
75
73
  />
76
74
  );
@@ -2,11 +2,9 @@ import * as React from "react";
2
2
  import { Popover as BasePopover } from "@base-ui/react/popover";
3
3
  import "./styles.css";
4
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
5
6
  type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
6
7
 
7
- function cx(...args: (string | undefined | false)[]) {
8
- return args.filter(Boolean).join(" ");
9
- }
10
8
 
11
9
  /**
12
10
  * 트리거 요소에 떠다니는 가벼운 패널을 띄우는 비모달 컨테이너. 포커스를 가두지 않으므로
@@ -72,7 +70,7 @@ export const PopoverContent = React.forwardRef<HTMLDivElement, PopoverContentPro
72
70
  >
73
71
  <BasePopover.Popup
74
72
  ref={ref}
75
- className={cx("sh-ui-popover__content", className)}
73
+ className={cn("sh-ui-popover__content", className)}
76
74
  {...props}
77
75
  >
78
76
  {showArrow && (
@@ -94,7 +92,7 @@ export const PopoverTitle = React.forwardRef<
94
92
  return (
95
93
  <BasePopover.Title
96
94
  ref={ref}
97
- className={cx("sh-ui-popover__title", className)}
95
+ className={cn("sh-ui-popover__title", className)}
98
96
  {...props}
99
97
  />
100
98
  );
@@ -108,7 +106,7 @@ export const PopoverDescription = React.forwardRef<
108
106
  return (
109
107
  <BasePopover.Description
110
108
  ref={ref}
111
- className={cx("sh-ui-popover__description", className)}
109
+ className={cn("sh-ui-popover__description", className)}
112
110
  {...props}
113
111
  />
114
112
  );
@@ -1,9 +1,7 @@
1
1
  import * as React from "react";
2
2
 
3
- function cx(...args: (string | undefined | false | null)[]) {
4
- return args.filter(Boolean).join(" ");
5
- }
6
3
 
4
+ import { cn } from "@SH_UI_UTILS@";
7
5
  function clamp(n: number, min: number, max: number) {
8
6
  return Math.min(max, Math.max(min, n));
9
7
  }
@@ -32,14 +30,14 @@ export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
32
30
  aria-valuemax={isDeterminate ? max : undefined}
33
31
  aria-valuenow={isDeterminate ? value : undefined}
34
32
  data-state={isDeterminate ? "determinate" : "indeterminate"}
35
- className={cx(
33
+ className={cn(
36
34
  "relative w-full h-2 overflow-hidden bg-background-muted rounded-full",
37
35
  className,
38
36
  )}
39
37
  {...props}
40
38
  >
41
39
  <div
42
- className={cx(
40
+ className={cn(
43
41
  "h-full bg-primary rounded-full transition-[width] duration-[var(--duration-base)] ease-out motion-reduce:transition-none",
44
42
  !isDeterminate &&
45
43
  "w-2/5 animate-[sh-ui-progress-slide_1.2s_ease-in-out_infinite] motion-reduce:animate-none motion-reduce:translate-x-3/4",
@@ -1,10 +1,8 @@
1
1
  import * as React from "react";
2
2
  import "./styles.css";
3
3
 
4
- function cx(...args: (string | undefined | false | null)[]) {
5
- return args.filter(Boolean).join(" ");
6
- }
7
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
8
6
  function clamp(n: number, min: number, max: number) {
9
7
  return Math.min(max, Math.max(min, n));
10
8
  }
@@ -42,7 +40,7 @@ export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
42
40
  aria-valuemax={isDeterminate ? max : undefined}
43
41
  aria-valuenow={isDeterminate ? value : undefined}
44
42
  data-state={isDeterminate ? "determinate" : "indeterminate"}
45
- className={cx("sh-ui-progress", className)}
43
+ className={cn("sh-ui-progress", className)}
46
44
  {...props}
47
45
  >
48
46
  <div
@@ -2,10 +2,8 @@ import * as React from "react";
2
2
  import { Radio as BaseRadio } from "@base-ui/react/radio";
3
3
  import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
4
4
 
5
- function cx(...args: (string | undefined | false)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
5
 
6
+ import { cn } from "@SH_UI_UTILS@";
9
7
  export type RadioProps = Omit<
10
8
  React.ComponentPropsWithoutRef<typeof BaseRadio.Root>,
11
9
  "className"
@@ -17,7 +15,7 @@ export const Radio = React.forwardRef<HTMLElement, RadioProps>(
17
15
  ({ className, ...props }, ref) => (
18
16
  <BaseRadio.Root
19
17
  ref={ref}
20
- className={cx(
18
+ className={cn(
21
19
  "inline-flex items-center justify-center w-[1.125rem] h-[1.125rem] border border-border-strong rounded-full bg-background cursor-pointer shrink-0 transition-[border-color] duration-[var(--duration-fast)] hover:not-data-[disabled]:border-foreground focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 data-[checked]:border-primary data-[disabled]:opacity-[var(--opacity-disabled)] data-[disabled]:cursor-not-allowed motion-reduce:transition-none [@media(hover:none)_and_(pointer:coarse)]:w-5 [@media(hover:none)_and_(pointer:coarse)]:h-5",
22
20
  className,
23
21
  )}
@@ -41,7 +39,7 @@ export const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
41
39
  ({ className, orientation = "vertical", ...props }, ref) => (
42
40
  <BaseRadioGroup
43
41
  ref={ref}
44
- className={cx(
42
+ className={cn(
45
43
  "flex gap-2.5",
46
44
  orientation === "vertical" ? "flex-col" : "flex-row flex-wrap",
47
45
  className,
@@ -3,10 +3,8 @@ import { Radio as BaseRadio } from "@base-ui/react/radio";
3
3
  import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
4
4
  import "./styles.css";
5
5
 
6
- function cx(...args: (string | undefined | false)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
6
 
7
+ import { cn } from "@SH_UI_UTILS@";
10
8
  /* ───────────── Radio ───────────── */
11
9
 
12
10
  export type RadioProps = Omit<
@@ -24,7 +22,7 @@ export const Radio = React.forwardRef<HTMLElement, RadioProps>(
24
22
  ({ className, ...props }, ref) => (
25
23
  <BaseRadio.Root
26
24
  ref={ref}
27
- className={cx("sh-ui-radio", className)}
25
+ className={cn("sh-ui-radio", className)}
28
26
  {...props}
29
27
  >
30
28
  <BaseRadio.Indicator className="sh-ui-radio__indicator" />
@@ -58,7 +56,7 @@ export const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
58
56
  ({ className, orientation = "vertical", ...props }, ref) => (
59
57
  <BaseRadioGroup
60
58
  ref={ref}
61
- className={cx("sh-ui-radio-group", className)}
59
+ className={cn("sh-ui-radio-group", className)}
62
60
  data-orientation={orientation}
63
61
  {...props}
64
62
  />
@@ -5,6 +5,7 @@ import { useEditor, EditorContent, type Editor } from "@tiptap/react";
5
5
  import StarterKit from "@tiptap/starter-kit";
6
6
  import Placeholder from "@tiptap/extension-placeholder";
7
7
  import Link from "@tiptap/extension-link";
8
+ import { cn } from "@SH_UI_UTILS@";
8
9
  import {
9
10
  BoldIcon, ItalicIcon, StrikethroughIcon,
10
11
  Heading1Icon, Heading2Icon, Heading3Icon,
@@ -26,9 +27,6 @@ export interface RichTextEditorProps {
26
27
  "aria-label"?: string;
27
28
  }
28
29
 
29
- function cx(...args: (string | undefined | false | null)[]) {
30
- return args.filter(Boolean).join(" ");
31
- }
32
30
 
33
31
  export function RichTextEditor({
34
32
  value: valueProp, defaultValue, onChange, placeholder, readOnly = false, hideToolbar = false,
@@ -68,7 +66,7 @@ export function RichTextEditor({
68
66
 
69
67
  return (
70
68
  <div
71
- className={cx(
69
+ className={cn(
72
70
  "sh-ui-rte flex flex-col border border-border rounded-[var(--radius)] bg-background overflow-hidden transition-[border-color] duration-[var(--duration-fast)] focus-within:border-foreground focus-within:outline-[length:var(--border-width-strong)] focus-within:outline-foreground focus-within:outline-offset-2 data-[readonly]:bg-background-subtle motion-reduce:transition-none",
73
71
  className,
74
72
  )}
@@ -158,7 +156,7 @@ function ToolbarButton({ editor, label, icon, isActive, canRun, run, disabled }:
158
156
  return (
159
157
  <button
160
158
  type="button"
161
- className={cx(
159
+ className={cn(
162
160
  "inline-flex items-center justify-center w-7 h-7 p-0 bg-transparent text-foreground-muted border border-transparent rounded-[calc(var(--radius)-2px)] cursor-pointer transition-[color,background-color,border-color] duration-[var(--duration-fast)] hover:not-disabled:text-foreground hover:not-disabled:bg-background hover:not-disabled:border-border focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-1 disabled:opacity-50 disabled:cursor-not-allowed motion-reduce:transition-none",
163
161
  isActive && "text-foreground bg-background border-border-strong",
164
162
  )}
@@ -5,6 +5,7 @@ import { useEditor, EditorContent, type Editor } from "@tiptap/react";
5
5
  import StarterKit from "@tiptap/starter-kit";
6
6
  import Placeholder from "@tiptap/extension-placeholder";
7
7
  import Link from "@tiptap/extension-link";
8
+ import { cn } from "@SH_UI_UTILS@";
8
9
  import {
9
10
  BoldIcon,
10
11
  ItalicIcon,
@@ -51,9 +52,6 @@ export interface RichTextEditorProps {
51
52
  "aria-label"?: string;
52
53
  }
53
54
 
54
- function cx(...args: (string | undefined | false | null)[]) {
55
- return args.filter(Boolean).join(" ");
56
- }
57
55
 
58
56
  /**
59
57
  * Tiptap 기반 리치 텍스트 에디터.
@@ -117,7 +115,7 @@ export function RichTextEditor({
117
115
 
118
116
  return (
119
117
  <div
120
- className={cx("sh-ui-rte", className)}
118
+ className={cn("sh-ui-rte", className)}
121
119
  data-readonly={readOnly || undefined}
122
120
  style={
123
121
  {
@@ -330,7 +328,7 @@ function ToolbarButton({
330
328
  return (
331
329
  <button
332
330
  type="button"
333
- className={cx("sh-ui-rte__btn", isActive && "is-active")}
331
+ className={cn("sh-ui-rte__btn", isActive && "is-active")}
334
332
  aria-label={label}
335
333
  aria-pressed={isActive || undefined}
336
334
  title={label}
@@ -3,17 +3,15 @@
3
3
  import * as React from "react";
4
4
  import { Select as BaseSelect } from "@base-ui/react/select";
5
5
 
6
- function cx(...args: (string | undefined | false)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
6
 
7
+ import { cn } from "@SH_UI_UTILS@";
10
8
  export const Select = BaseSelect.Root;
11
9
 
12
10
  export function SelectValue({ placeholder, className, ...props }: { placeholder?: string; className?: string } & Omit<
13
11
  React.ComponentPropsWithoutRef<typeof BaseSelect.Value>, "children"
14
12
  >) {
15
13
  return (
16
- <BaseSelect.Value className={cx("flex-1 text-left overflow-hidden text-ellipsis whitespace-nowrap", className)} {...props}>
14
+ <BaseSelect.Value className={cn("flex-1 text-left overflow-hidden text-ellipsis whitespace-nowrap", className)} {...props}>
17
15
  {(value) =>
18
16
  value !== null && value !== undefined && value !== "" ? (
19
17
  (value as React.ReactNode)
@@ -31,7 +29,7 @@ export const SelectTrigger = React.forwardRef<
31
29
  >(({ className, children, ...props }, ref) => (
32
30
  <BaseSelect.Trigger
33
31
  ref={ref}
34
- className={cx(
32
+ className={cn(
35
33
  "inline-flex items-center justify-between gap-[var(--space-2)] min-w-40 h-[var(--control-md)] px-[var(--space-3)] bg-background text-foreground border border-border rounded-[var(--radius)] text-[length:var(--text-sm)] leading-none cursor-pointer transition-[border-color,background-color] duration-[var(--duration-fast)] select-none hover:not-disabled:border-border-strong focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 data-[popup-open]:border-border-strong disabled:opacity-[var(--opacity-disabled)] disabled:pointer-events-none",
36
34
  className,
37
35
  )}
@@ -61,7 +59,7 @@ export const SelectContent = React.forwardRef<
61
59
  <BaseSelect.Positioner className="outline-none z-[var(--z-dropdown)]" sideOffset={4} align="start">
62
60
  <BaseSelect.Popup
63
61
  ref={ref}
64
- className={cx(
62
+ className={cn(
65
63
  "min-w-[var(--anchor-width,10rem)] max-h-[min(24rem,var(--available-height,24rem))] overflow-y-auto p-[var(--space-1)] bg-background text-foreground border border-border rounded-[var(--radius)] shadow-[0_4px_6px_-1px_rgba(0,0,0,0.08),0_2px_4px_-2px_rgba(0,0,0,0.05)] text-[length:var(--text-sm)] origin-[var(--transform-origin)] animate-[sh-ui-select-in_140ms_ease-out] data-[ending-style]:animate-[sh-ui-select-out_100ms_ease-in_forwards] motion-reduce:animate-none motion-reduce:data-[ending-style]:animate-none",
66
64
  className,
67
65
  )}
@@ -82,7 +80,7 @@ export const SelectLabel = React.forwardRef<
82
80
  >(({ className, ...props }, ref) => (
83
81
  <BaseSelect.GroupLabel
84
82
  ref={ref}
85
- className={cx(
83
+ className={cn(
86
84
  "py-[var(--space-2)] px-[var(--space-2)] pb-[var(--space-1)] text-[length:var(--text-xs)] font-semibold text-foreground-muted uppercase tracking-[0.04em]",
87
85
  className,
88
86
  )}
@@ -97,7 +95,7 @@ export const SelectItem = React.forwardRef<
97
95
  >(({ className, children, ...props }, ref) => (
98
96
  <BaseSelect.Item
99
97
  ref={ref}
100
- className={cx(
98
+ className={cn(
101
99
  "flex items-center gap-[var(--space-2)] py-2 px-3 rounded-[calc(var(--radius)-2px)] cursor-pointer outline-none select-none transition-colors duration-[80ms] data-[highlighted]:bg-background-muted hover:bg-background-muted data-[disabled]:opacity-[var(--opacity-disabled)] data-[disabled]:pointer-events-none",
102
100
  className,
103
101
  )}
@@ -122,7 +120,7 @@ export const SelectSeparator = React.forwardRef<
122
120
  >(({ className, ...props }, ref) => (
123
121
  <BaseSelect.Separator
124
122
  ref={ref}
125
- className={cx("h-px bg-border my-[var(--space-1)]", className)}
123
+ className={cn("h-px bg-border my-[var(--space-1)]", className)}
126
124
  {...props}
127
125
  />
128
126
  ));
@@ -178,7 +176,7 @@ export function MultiSelectValue({
178
176
  } & Omit<React.ComponentPropsWithoutRef<typeof BaseSelect.Value>, "children" | "render">) {
179
177
  const { remove, clear } = useMultiSelect();
180
178
  return (
181
- <BaseSelect.Value className={cx("flex-1 text-left overflow-hidden text-ellipsis whitespace-nowrap", className)} {...props}>
179
+ <BaseSelect.Value className={cn("flex-1 text-left overflow-hidden text-ellipsis whitespace-nowrap", className)} {...props}>
182
180
  {(value) => {
183
181
  const arr = Array.isArray(value) ? (value as string[]) : [];
184
182
  if (arr.length === 0) return <span className="text-foreground-subtle">{placeholder}</span>;
@@ -4,10 +4,8 @@ import * as React from "react";
4
4
  import { Select as BaseSelect } from "@base-ui/react/select";
5
5
  import "./styles.css";
6
6
 
7
- function cx(...args: (string | undefined | false)[]) {
8
- return args.filter(Boolean).join(" ");
9
- }
10
7
 
8
+ import { cn } from "@SH_UI_UTILS@";
11
9
  export const Select = BaseSelect.Root;
12
10
 
13
11
  /** shadcn 호환: <SelectValue placeholder="..." /> */
@@ -20,7 +18,7 @@ export function SelectValue({
20
18
  "children"
21
19
  >) {
22
20
  return (
23
- <BaseSelect.Value className={cx("sh-ui-select__value", className)} {...props}>
21
+ <BaseSelect.Value className={cn("sh-ui-select__value", className)} {...props}>
24
22
  {(value) =>
25
23
  value !== null && value !== undefined && value !== "" ? (
26
24
  (value as React.ReactNode)
@@ -38,7 +36,7 @@ export const SelectTrigger = React.forwardRef<
38
36
  >(({ className, children, ...props }, ref) => (
39
37
  <BaseSelect.Trigger
40
38
  ref={ref}
41
- className={cx("sh-ui-select__trigger", className)}
39
+ className={cn("sh-ui-select__trigger", className)}
42
40
  {...props}
43
41
  >
44
42
  {children}
@@ -74,7 +72,7 @@ export const SelectContent = React.forwardRef<
74
72
  >
75
73
  <BaseSelect.Popup
76
74
  ref={ref}
77
- className={cx("sh-ui-select__content", className)}
75
+ className={cn("sh-ui-select__content", className)}
78
76
  {...props}
79
77
  >
80
78
  {children}
@@ -92,7 +90,7 @@ export const SelectLabel = React.forwardRef<
92
90
  >(({ className, ...props }, ref) => (
93
91
  <BaseSelect.GroupLabel
94
92
  ref={ref}
95
- className={cx("sh-ui-select__label", className)}
93
+ className={cn("sh-ui-select__label", className)}
96
94
  {...props}
97
95
  />
98
96
  ));
@@ -104,7 +102,7 @@ export const SelectItem = React.forwardRef<
104
102
  >(({ className, children, ...props }, ref) => (
105
103
  <BaseSelect.Item
106
104
  ref={ref}
107
- className={cx("sh-ui-select__item", className)}
105
+ className={cn("sh-ui-select__item", className)}
108
106
  {...props}
109
107
  >
110
108
  <BaseSelect.ItemIndicator className="sh-ui-select__indicator" aria-hidden>
@@ -131,7 +129,7 @@ export const SelectSeparator = React.forwardRef<
131
129
  >(({ className, ...props }, ref) => (
132
130
  <BaseSelect.Separator
133
131
  ref={ref}
134
- className={cx("sh-ui-select__separator", className)}
132
+ className={cn("sh-ui-select__separator", className)}
135
133
  {...props}
136
134
  />
137
135
  ));
@@ -223,7 +221,7 @@ export function MultiSelectValue({
223
221
  >) {
224
222
  const { remove, clear } = useMultiSelect();
225
223
  return (
226
- <BaseSelect.Value className={cx("sh-ui-select__value", className)} {...props}>
224
+ <BaseSelect.Value className={cn("sh-ui-select__value", className)} {...props}>
227
225
  {(value) => {
228
226
  const arr = Array.isArray(value) ? (value as string[]) : [];
229
227
  if (arr.length === 0) {
@@ -1,9 +1,7 @@
1
1
  import * as React from "react";
2
2
 
3
- function cx(...args: (string | undefined | false | null)[]) {
4
- return args.filter(Boolean).join(" ");
5
- }
6
3
 
4
+ import { cn } from "@SH_UI_UTILS@";
7
5
  export type SeparatorOrientation = "horizontal" | "vertical";
8
6
 
9
7
  export interface SeparatorProps
@@ -33,7 +31,7 @@ export const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
33
31
  aria-orientation={decorative ? undefined : orientation}
34
32
  aria-hidden={decorative || undefined}
35
33
  data-orientation={orientation}
36
- className={cx("bg-border shrink-0", sizing, className)}
34
+ className={cn("bg-border shrink-0", sizing, className)}
37
35
  {...props}
38
36
  />
39
37
  );
@@ -1,10 +1,8 @@
1
1
  import * as React from "react";
2
2
  import "./styles.css";
3
3
 
4
- function cx(...args: (string | undefined | false | null)[]) {
5
- return args.filter(Boolean).join(" ");
6
- }
7
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
8
6
  export type SeparatorOrientation = "horizontal" | "vertical";
9
7
 
10
8
  export interface SeparatorProps
@@ -35,7 +33,7 @@ export const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
35
33
  aria-orientation={decorative ? undefined : orientation}
36
34
  aria-hidden={decorative || undefined}
37
35
  data-orientation={orientation}
38
- className={cx(
36
+ className={cn(
39
37
  "sh-ui-separator",
40
38
  `sh-ui-separator--${orientation}`,
41
39
  className,