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.
- package/data/changelog/versions.json +26 -0
- package/data/registry/react/components/accordion/index.tailwind.tsx +5 -7
- package/data/registry/react/components/accordion/index.tsx +5 -7
- package/data/registry/react/components/avatar/index.tailwind.tsx +4 -6
- package/data/registry/react/components/avatar/index.tsx +4 -6
- package/data/registry/react/components/badge/index.tailwind.tsx +2 -4
- package/data/registry/react/components/badge/index.tsx +2 -4
- package/data/registry/react/components/breadcrumb/index.tailwind.tsx +8 -10
- package/data/registry/react/components/breadcrumb/index.tsx +8 -10
- package/data/registry/react/components/button/index.module.tsx +45 -0
- package/data/registry/react/components/button/index.tailwind.tsx +2 -1
- package/data/registry/react/components/button/index.tsx +3 -4
- package/data/registry/react/components/button/styles.module.css +92 -0
- package/data/registry/react/components/calendar/index.tailwind.tsx +10 -12
- package/data/registry/react/components/calendar/index.tsx +9 -11
- package/data/registry/react/components/card/index.module.tsx +63 -0
- package/data/registry/react/components/card/index.tailwind.tsx +8 -10
- package/data/registry/react/components/card/index.tsx +8 -10
- package/data/registry/react/components/card/styles.module.css +73 -0
- package/data/registry/react/components/carousel/index.tailwind.tsx +7 -9
- package/data/registry/react/components/carousel/index.tsx +7 -9
- package/data/registry/react/components/checkbox/index.tailwind.tsx +3 -5
- package/data/registry/react/components/checkbox/index.tsx +3 -5
- package/data/registry/react/components/code-editor/index.tailwind.tsx +2 -4
- package/data/registry/react/components/code-editor/index.tsx +2 -4
- package/data/registry/react/components/code-panel/index.tailwind.tsx +5 -7
- package/data/registry/react/components/code-panel/index.tsx +5 -7
- package/data/registry/react/components/color-picker/index.tailwind.tsx +7 -6
- package/data/registry/react/components/color-picker/index.tsx +7 -6
- package/data/registry/react/components/combobox/index.tailwind.tsx +8 -10
- package/data/registry/react/components/combobox/index.tsx +8 -10
- package/data/registry/react/components/context-menu/index.tailwind.tsx +10 -12
- package/data/registry/react/components/context-menu/index.tsx +10 -12
- package/data/registry/react/components/date-picker/index.tailwind.tsx +7 -9
- package/data/registry/react/components/date-picker/index.tsx +7 -9
- package/data/registry/react/components/dialog/index.tailwind.tsx +6 -8
- package/data/registry/react/components/dialog/index.tsx +6 -8
- package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +10 -12
- package/data/registry/react/components/dropdown-menu/index.tsx +10 -12
- package/data/registry/react/components/file-upload/index.tailwind.tsx +6 -8
- package/data/registry/react/components/file-upload/index.tsx +6 -8
- package/data/registry/react/components/form/field.tailwind.tsx +2 -1
- package/data/registry/react/components/form/field.tsx +2 -3
- package/data/registry/react/components/header/index.tailwind.tsx +17 -19
- package/data/registry/react/components/header/index.tsx +17 -19
- package/data/registry/react/components/input/index.module.tsx +486 -0
- package/data/registry/react/components/input/index.tailwind.tsx +4 -6
- package/data/registry/react/components/input/index.tsx +4 -6
- package/data/registry/react/components/input/styles.module.css +200 -0
- package/data/registry/react/components/label/index.tailwind.tsx +6 -8
- package/data/registry/react/components/label/index.tsx +6 -8
- package/data/registry/react/components/markdown-editor/index.tailwind.tsx +2 -4
- package/data/registry/react/components/markdown-editor/index.tsx +2 -4
- package/data/registry/react/components/menubar/index.tailwind.tsx +2 -4
- package/data/registry/react/components/menubar/index.tsx +2 -4
- package/data/registry/react/components/numeric-input/index.tailwind.tsx +2 -4
- package/data/registry/react/components/numeric-input/index.tsx +2 -4
- package/data/registry/react/components/page-toc/index.tailwind.tsx +3 -2
- package/data/registry/react/components/page-toc/index.tsx +2 -3
- package/data/registry/react/components/pagination/index.tailwind.tsx +8 -10
- package/data/registry/react/components/pagination/index.tsx +8 -10
- package/data/registry/react/components/popover/index.tailwind.tsx +4 -6
- package/data/registry/react/components/popover/index.tsx +4 -6
- package/data/registry/react/components/progress/index.tailwind.tsx +3 -5
- package/data/registry/react/components/progress/index.tsx +2 -4
- package/data/registry/react/components/radio/index.tailwind.tsx +3 -5
- package/data/registry/react/components/radio/index.tsx +3 -5
- package/data/registry/react/components/rich-text-editor/index.tailwind.tsx +3 -5
- package/data/registry/react/components/rich-text-editor/index.tsx +3 -5
- package/data/registry/react/components/select/index.tailwind.tsx +8 -10
- package/data/registry/react/components/select/index.tsx +8 -10
- package/data/registry/react/components/separator/index.tailwind.tsx +2 -4
- package/data/registry/react/components/separator/index.tsx +2 -4
- package/data/registry/react/components/sidebar/index.tailwind.tsx +32 -43
- package/data/registry/react/components/sidebar/index.tsx +29 -46
- package/data/registry/react/components/skeleton/index.tailwind.tsx +2 -4
- package/data/registry/react/components/skeleton/index.tsx +2 -4
- package/data/registry/react/components/slider/index.tailwind.tsx +5 -7
- package/data/registry/react/components/slider/index.tsx +5 -7
- package/data/registry/react/components/spinner/index.tailwind.tsx +3 -5
- package/data/registry/react/components/spinner/index.tsx +2 -4
- package/data/registry/react/components/switch/index.tailwind.tsx +3 -5
- package/data/registry/react/components/switch/index.tsx +2 -4
- package/data/registry/react/components/tabs/index.tailwind.tsx +6 -8
- package/data/registry/react/components/tabs/index.tsx +6 -8
- package/data/registry/react/components/textarea/index.tailwind.tsx +2 -4
- package/data/registry/react/components/textarea/index.tsx +2 -4
- package/data/registry/react/components/toggle/index.tailwind.tsx +4 -6
- package/data/registry/react/components/toggle/index.tsx +4 -6
- package/data/registry/react/components/tooltip/index.tailwind.tsx +2 -4
- package/data/registry/react/components/tooltip/index.tsx +2 -4
- package/data/registry/react/lib/cn.tailwind.ts +17 -0
- package/data/registry/react/peer-versions.json +3 -1
- package/data/registry/react/registry.json +202 -43
- package/data/tokens/build.mjs +4 -0
- package/package.json +1 -1
- package/src/add.mjs +37 -13
- package/templates/ui-app-template/sh-ui.config.json +5 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
4
5
|
import {
|
|
5
6
|
Select,
|
|
6
7
|
SelectContent,
|
|
@@ -11,9 +12,6 @@ import "./styles.css";
|
|
|
11
12
|
|
|
12
13
|
/* ───────── Helpers ───────── */
|
|
13
14
|
|
|
14
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
15
|
-
return args.filter(Boolean).join(" ");
|
|
16
|
-
}
|
|
17
15
|
|
|
18
16
|
const DEFAULT_WEEKDAYS_KO = ["일", "월", "화", "수", "목", "금", "토"] as const;
|
|
19
17
|
|
|
@@ -505,7 +503,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
505
503
|
|
|
506
504
|
return (
|
|
507
505
|
<div
|
|
508
|
-
className={
|
|
506
|
+
className={cn("sh-ui-calendar", numberOfMonths > 1 && "sh-ui-calendar--multi", className)}
|
|
509
507
|
aria-label={ariaLabel}
|
|
510
508
|
>
|
|
511
509
|
{children
|
|
@@ -545,7 +543,7 @@ export interface CalendarHeaderProps extends React.HTMLAttributes<HTMLDivElement
|
|
|
545
543
|
/** 헤더 컨테이너. 화살표/dropdown 등을 children 으로 자유롭게 배치. */
|
|
546
544
|
export const CalendarHeader = React.forwardRef<HTMLDivElement, CalendarHeaderProps>(
|
|
547
545
|
function CalendarHeader({ className, ...props }, ref) {
|
|
548
|
-
return <div ref={ref} className={
|
|
546
|
+
return <div ref={ref} className={cn("sh-ui-calendar__header", className)} {...props} />;
|
|
549
547
|
},
|
|
550
548
|
);
|
|
551
549
|
|
|
@@ -574,7 +572,7 @@ function makeNavButton(
|
|
|
574
572
|
<button
|
|
575
573
|
ref={ref}
|
|
576
574
|
type="button"
|
|
577
|
-
className={
|
|
575
|
+
className={cn("sh-ui-calendar__nav", className)}
|
|
578
576
|
aria-label={ariaLabel ?? defaultLabel}
|
|
579
577
|
onClick={(e) => {
|
|
580
578
|
resolveHandler(ctx)();
|
|
@@ -649,7 +647,7 @@ export function CalendarYearSelect({
|
|
|
649
647
|
onValueChange={(v) => ctx.setYearForVisible(Number(v))}
|
|
650
648
|
>
|
|
651
649
|
<SelectTrigger
|
|
652
|
-
className={
|
|
650
|
+
className={cn("sh-ui-calendar__select-trigger", className)}
|
|
653
651
|
aria-label="연도"
|
|
654
652
|
>
|
|
655
653
|
<span className="sh-ui-calendar__select-value">{formatYear(year)}</span>
|
|
@@ -684,7 +682,7 @@ export function CalendarMonthSelect({
|
|
|
684
682
|
onValueChange={(v) => ctx.setMonthForVisible(Number(v))}
|
|
685
683
|
>
|
|
686
684
|
<SelectTrigger
|
|
687
|
-
className={
|
|
685
|
+
className={cn("sh-ui-calendar__select-trigger", className)}
|
|
688
686
|
aria-label="월"
|
|
689
687
|
>
|
|
690
688
|
<span className="sh-ui-calendar__select-value">{formatMonth(month)}</span>
|
|
@@ -716,7 +714,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
716
714
|
const ariaLabel = ctx.ariaLabel ?? monthLabel;
|
|
717
715
|
|
|
718
716
|
return (
|
|
719
|
-
<div ref={ref} className={
|
|
717
|
+
<div ref={ref} className={cn("sh-ui-calendar__grid-wrap", className)} {...rest}>
|
|
720
718
|
<div className="sh-ui-calendar__weekdays" role="row">
|
|
721
719
|
{ctx.weekdayLabels.map((label) => (
|
|
722
720
|
<span
|
|
@@ -751,7 +749,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
751
749
|
return (
|
|
752
750
|
<div
|
|
753
751
|
key={i}
|
|
754
|
-
className={
|
|
752
|
+
className={cn(
|
|
755
753
|
"sh-ui-calendar__cell",
|
|
756
754
|
inRange && "sh-ui-calendar__cell--in-range",
|
|
757
755
|
isStart && "sh-ui-calendar__cell--range-start",
|
|
@@ -760,7 +758,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
760
758
|
>
|
|
761
759
|
<button
|
|
762
760
|
type="button"
|
|
763
|
-
className={
|
|
761
|
+
className={cn(
|
|
764
762
|
"sh-ui-calendar__day",
|
|
765
763
|
!current && "sh-ui-calendar__day--outside",
|
|
766
764
|
selected && "sh-ui-calendar__day--selected",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
3
|
+
import styles from "./styles.module.css";
|
|
4
|
+
|
|
5
|
+
type DivProps = React.HTMLAttributes<HTMLDivElement>;
|
|
6
|
+
|
|
7
|
+
export const Card = React.forwardRef<HTMLDivElement, DivProps>(
|
|
8
|
+
({ className, ...props }, ref) => (
|
|
9
|
+
<div ref={ref} className={cn(styles.card, className)} {...props} />
|
|
10
|
+
),
|
|
11
|
+
);
|
|
12
|
+
Card.displayName = "Card";
|
|
13
|
+
|
|
14
|
+
export const CardHeader = React.forwardRef<HTMLDivElement, DivProps>(
|
|
15
|
+
({ className, ...props }, ref) => (
|
|
16
|
+
<div
|
|
17
|
+
ref={ref}
|
|
18
|
+
data-slot="card-header"
|
|
19
|
+
className={cn(styles.header, className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
CardHeader.displayName = "CardHeader";
|
|
25
|
+
|
|
26
|
+
export const CardTitle = React.forwardRef<HTMLDivElement, DivProps>(
|
|
27
|
+
({ className, ...props }, ref) => (
|
|
28
|
+
<div ref={ref} className={cn(styles.title, className)} {...props} />
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
CardTitle.displayName = "CardTitle";
|
|
32
|
+
|
|
33
|
+
export const CardDescription = React.forwardRef<HTMLDivElement, DivProps>(
|
|
34
|
+
({ className, ...props }, ref) => (
|
|
35
|
+
<div ref={ref} className={cn(styles.description, className)} {...props} />
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
CardDescription.displayName = "CardDescription";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 헤더 우측에 배치되는 슬롯. CardHeader 내부에서 grid 2번째 컬럼을 차지.
|
|
42
|
+
* CardHeader가 `:has(.action)`으로 감지해 레이아웃을 전환한다.
|
|
43
|
+
*/
|
|
44
|
+
export const CardAction = React.forwardRef<HTMLDivElement, DivProps>(
|
|
45
|
+
({ className, ...props }, ref) => (
|
|
46
|
+
<div ref={ref} className={cn(styles.action, className)} {...props} />
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
CardAction.displayName = "CardAction";
|
|
50
|
+
|
|
51
|
+
export const CardContent = React.forwardRef<HTMLDivElement, DivProps>(
|
|
52
|
+
({ className, ...props }, ref) => (
|
|
53
|
+
<div ref={ref} className={cn(styles.content, className)} {...props} />
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
CardContent.displayName = "CardContent";
|
|
57
|
+
|
|
58
|
+
export const CardFooter = React.forwardRef<HTMLDivElement, DivProps>(
|
|
59
|
+
({ className, ...props }, ref) => (
|
|
60
|
+
<div ref={ref} className={cn(styles.footer, className)} {...props} />
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
CardFooter.displayName = "CardFooter";
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
3
4
|
type DivProps = React.HTMLAttributes<HTMLDivElement>;
|
|
4
5
|
|
|
5
|
-
function mergeClass(base: string, extra?: string) {
|
|
6
|
-
return extra ? `${base} ${extra}` : base;
|
|
7
|
-
}
|
|
8
6
|
|
|
9
7
|
export const Card = React.forwardRef<HTMLDivElement, DivProps>(
|
|
10
8
|
({ className, ...props }, ref) => (
|
|
11
9
|
<div
|
|
12
10
|
ref={ref}
|
|
13
|
-
className={
|
|
11
|
+
className={cn(
|
|
14
12
|
"flex flex-col gap-[var(--space-6)] py-[var(--space-6)] bg-background text-foreground border border-border rounded-[var(--radius)] max-sm:gap-[var(--space-4)] max-sm:py-[var(--space-4)]",
|
|
15
13
|
className,
|
|
16
14
|
)}
|
|
@@ -25,7 +23,7 @@ export const CardHeader = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
25
23
|
<div
|
|
26
24
|
ref={ref}
|
|
27
25
|
data-slot="card-header"
|
|
28
|
-
className={
|
|
26
|
+
className={cn(
|
|
29
27
|
"grid grid-cols-1 auto-rows-auto gap-y-1.5 px-[var(--space-6)] has-[[data-slot=card-action]]:grid-cols-[1fr_auto] max-sm:px-[var(--space-4)]",
|
|
30
28
|
className,
|
|
31
29
|
)}
|
|
@@ -39,7 +37,7 @@ export const CardTitle = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
39
37
|
({ className, ...props }, ref) => (
|
|
40
38
|
<div
|
|
41
39
|
ref={ref}
|
|
42
|
-
className={
|
|
40
|
+
className={cn(
|
|
43
41
|
"text-[length:var(--text-base)] font-semibold leading-tight tracking-tight",
|
|
44
42
|
className,
|
|
45
43
|
)}
|
|
@@ -53,7 +51,7 @@ export const CardDescription = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
53
51
|
({ className, ...props }, ref) => (
|
|
54
52
|
<div
|
|
55
53
|
ref={ref}
|
|
56
|
-
className={
|
|
54
|
+
className={cn(
|
|
57
55
|
"text-[length:var(--text-sm)] leading-normal text-foreground-muted",
|
|
58
56
|
className,
|
|
59
57
|
)}
|
|
@@ -72,7 +70,7 @@ export const CardAction = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
72
70
|
<div
|
|
73
71
|
ref={ref}
|
|
74
72
|
data-slot="card-action"
|
|
75
|
-
className={
|
|
73
|
+
className={cn(
|
|
76
74
|
"col-start-2 row-span-2 self-start justify-self-end",
|
|
77
75
|
className,
|
|
78
76
|
)}
|
|
@@ -86,7 +84,7 @@ export const CardContent = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
86
84
|
({ className, ...props }, ref) => (
|
|
87
85
|
<div
|
|
88
86
|
ref={ref}
|
|
89
|
-
className={
|
|
87
|
+
className={cn(
|
|
90
88
|
"px-[var(--space-6)] text-[length:var(--text-sm)] leading-relaxed max-sm:px-[var(--space-4)]",
|
|
91
89
|
className,
|
|
92
90
|
)}
|
|
@@ -100,7 +98,7 @@ export const CardFooter = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
100
98
|
({ className, ...props }, ref) => (
|
|
101
99
|
<div
|
|
102
100
|
ref={ref}
|
|
103
|
-
className={
|
|
101
|
+
className={cn(
|
|
104
102
|
"px-[var(--space-6)] flex items-center gap-[var(--space-2)] max-sm:px-[var(--space-4)] max-sm:flex-wrap",
|
|
105
103
|
className,
|
|
106
104
|
)}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
2
3
|
import "./styles.css";
|
|
3
4
|
|
|
4
5
|
type DivProps = React.HTMLAttributes<HTMLDivElement>;
|
|
5
6
|
|
|
6
|
-
function mergeClass(base: string, extra?: string) {
|
|
7
|
-
return extra ? `${base} ${extra}` : base;
|
|
8
|
-
}
|
|
9
7
|
|
|
10
8
|
export const Card = React.forwardRef<HTMLDivElement, DivProps>(
|
|
11
9
|
({ className, ...props }, ref) => (
|
|
12
|
-
<div ref={ref} className={
|
|
10
|
+
<div ref={ref} className={cn("sh-ui-card", className)} {...props} />
|
|
13
11
|
),
|
|
14
12
|
);
|
|
15
13
|
Card.displayName = "Card";
|
|
@@ -19,7 +17,7 @@ export const CardHeader = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
19
17
|
<div
|
|
20
18
|
ref={ref}
|
|
21
19
|
data-slot="card-header"
|
|
22
|
-
className={
|
|
20
|
+
className={cn("sh-ui-card__header", className)}
|
|
23
21
|
{...props}
|
|
24
22
|
/>
|
|
25
23
|
),
|
|
@@ -30,7 +28,7 @@ export const CardTitle = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
30
28
|
({ className, ...props }, ref) => (
|
|
31
29
|
<div
|
|
32
30
|
ref={ref}
|
|
33
|
-
className={
|
|
31
|
+
className={cn("sh-ui-card__title", className)}
|
|
34
32
|
{...props}
|
|
35
33
|
/>
|
|
36
34
|
),
|
|
@@ -41,7 +39,7 @@ export const CardDescription = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
41
39
|
({ className, ...props }, ref) => (
|
|
42
40
|
<div
|
|
43
41
|
ref={ref}
|
|
44
|
-
className={
|
|
42
|
+
className={cn("sh-ui-card__description", className)}
|
|
45
43
|
{...props}
|
|
46
44
|
/>
|
|
47
45
|
),
|
|
@@ -56,7 +54,7 @@ export const CardAction = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
56
54
|
({ className, ...props }, ref) => (
|
|
57
55
|
<div
|
|
58
56
|
ref={ref}
|
|
59
|
-
className={
|
|
57
|
+
className={cn("sh-ui-card__action", className)}
|
|
60
58
|
{...props}
|
|
61
59
|
/>
|
|
62
60
|
),
|
|
@@ -67,7 +65,7 @@ export const CardContent = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
67
65
|
({ className, ...props }, ref) => (
|
|
68
66
|
<div
|
|
69
67
|
ref={ref}
|
|
70
|
-
className={
|
|
68
|
+
className={cn("sh-ui-card__content", className)}
|
|
71
69
|
{...props}
|
|
72
70
|
/>
|
|
73
71
|
),
|
|
@@ -78,7 +76,7 @@ export const CardFooter = React.forwardRef<HTMLDivElement, DivProps>(
|
|
|
78
76
|
({ className, ...props }, ref) => (
|
|
79
77
|
<div
|
|
80
78
|
ref={ref}
|
|
81
|
-
className={
|
|
79
|
+
className={cn("sh-ui-card__footer", className)}
|
|
82
80
|
{...props}
|
|
83
81
|
/>
|
|
84
82
|
),
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
.card {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--space-6);
|
|
5
|
+
padding: var(--space-6) 0;
|
|
6
|
+
background: var(--background);
|
|
7
|
+
color: var(--foreground);
|
|
8
|
+
border: 1px solid var(--border);
|
|
9
|
+
border-radius: var(--radius);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* 헤더: 기본은 타이틀/설명 세로 스택. action 있으면 2열 그리드로 전환. */
|
|
13
|
+
.header {
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template-columns: 1fr;
|
|
16
|
+
grid-auto-rows: auto;
|
|
17
|
+
row-gap: 0.375rem;
|
|
18
|
+
padding: 0 var(--space-6);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.header:has(.action) {
|
|
22
|
+
grid-template-columns: 1fr auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.title {
|
|
26
|
+
font-size: var(--text-base);
|
|
27
|
+
font-weight: var(--weight-semibold);
|
|
28
|
+
line-height: 1.25;
|
|
29
|
+
letter-spacing: -0.01em;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.description {
|
|
33
|
+
font-size: var(--text-sm);
|
|
34
|
+
line-height: 1.5;
|
|
35
|
+
color: var(--foreground-muted);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.action {
|
|
39
|
+
grid-column: 2;
|
|
40
|
+
grid-row: 1 / span 2;
|
|
41
|
+
align-self: start;
|
|
42
|
+
justify-self: end;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.content {
|
|
46
|
+
padding: 0 var(--space-6);
|
|
47
|
+
font-size: var(--text-sm);
|
|
48
|
+
line-height: 1.6;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.footer {
|
|
52
|
+
padding: 0 var(--space-6);
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: var(--space-2);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* 모바일: 패딩 축소 */
|
|
59
|
+
@media (max-width: 640px) {
|
|
60
|
+
.card {
|
|
61
|
+
gap: var(--space-4);
|
|
62
|
+
padding: var(--space-4) 0;
|
|
63
|
+
}
|
|
64
|
+
.header,
|
|
65
|
+
.content,
|
|
66
|
+
.footer {
|
|
67
|
+
padding-left: var(--space-4);
|
|
68
|
+
padding-right: var(--space-4);
|
|
69
|
+
}
|
|
70
|
+
.footer {
|
|
71
|
+
flex-wrap: wrap;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
6
|
-
return args.filter(Boolean).join(" ");
|
|
7
|
-
}
|
|
8
5
|
|
|
6
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
9
7
|
type Orientation = "horizontal" | "vertical";
|
|
10
8
|
|
|
11
9
|
interface CarouselContextValue {
|
|
@@ -144,7 +142,7 @@ export const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
|
|
|
144
142
|
<CarouselContext.Provider value={value}>
|
|
145
143
|
<div
|
|
146
144
|
ref={ref}
|
|
147
|
-
className={
|
|
145
|
+
className={cn("relative w-full", className)}
|
|
148
146
|
data-orientation={orientation}
|
|
149
147
|
role="region"
|
|
150
148
|
aria-roledescription="carousel"
|
|
@@ -171,7 +169,7 @@ export const CarouselContent = React.forwardRef<HTMLDivElement, React.HTMLAttrib
|
|
|
171
169
|
return (
|
|
172
170
|
<div
|
|
173
171
|
ref={mergedRef}
|
|
174
|
-
className={
|
|
172
|
+
className={cn(
|
|
175
173
|
"flex gap-[var(--space-4)] overflow-x-auto overflow-y-hidden snap-x snap-mandatory scroll-smooth [scrollbar-width:none] [-ms-overflow-style:none] [-webkit-overflow-scrolling:touch] overscroll-x-contain [&::-webkit-scrollbar]:hidden motion-reduce:scroll-auto",
|
|
176
174
|
orientation === "vertical" && "flex-col overflow-x-hidden overflow-y-auto snap-y snap-mandatory h-80",
|
|
177
175
|
className,
|
|
@@ -193,7 +191,7 @@ export const CarouselItem = React.forwardRef<HTMLDivElement, React.HTMLAttribute
|
|
|
193
191
|
ref={ref}
|
|
194
192
|
role="group"
|
|
195
193
|
aria-roledescription="slide"
|
|
196
|
-
className={
|
|
194
|
+
className={cn(
|
|
197
195
|
"flex-[0_0_100%] min-w-0 snap-start snap-always",
|
|
198
196
|
orientation === "vertical" && "basis-auto",
|
|
199
197
|
className,
|
|
@@ -218,7 +216,7 @@ export const CarouselPrevious = React.forwardRef<HTMLButtonElement, React.Button
|
|
|
218
216
|
ref={ref}
|
|
219
217
|
type="button"
|
|
220
218
|
aria-label="이전"
|
|
221
|
-
className={
|
|
219
|
+
className={cn(
|
|
222
220
|
navClasses,
|
|
223
221
|
orientation === "horizontal" ? "-left-4" : "-top-4 left-1/2",
|
|
224
222
|
className,
|
|
@@ -248,7 +246,7 @@ export const CarouselNext = React.forwardRef<HTMLButtonElement, React.ButtonHTML
|
|
|
248
246
|
ref={ref}
|
|
249
247
|
type="button"
|
|
250
248
|
aria-label="다음"
|
|
251
|
-
className={
|
|
249
|
+
className={cn(
|
|
252
250
|
navClasses,
|
|
253
251
|
orientation === "horizontal" ? "-right-4" : "-bottom-4 left-1/2 [top:auto]",
|
|
254
252
|
className,
|
|
@@ -282,7 +280,7 @@ export const CarouselIndicators = React.forwardRef<HTMLDivElement, CarouselIndic
|
|
|
282
280
|
ref={ref}
|
|
283
281
|
role="tablist"
|
|
284
282
|
aria-label="슬라이드 선택"
|
|
285
|
-
className={
|
|
283
|
+
className={cn(
|
|
286
284
|
"flex justify-center items-center gap-[var(--space-2)] mt-[var(--space-3)]",
|
|
287
285
|
orientation === "vertical" && "absolute top-1/2 right-2 mt-0 flex-col -translate-y-1/2",
|
|
288
286
|
className,
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import "./styles.css";
|
|
5
5
|
|
|
6
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
7
|
-
return args.filter(Boolean).join(" ");
|
|
8
|
-
}
|
|
9
6
|
|
|
7
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
10
8
|
type Orientation = "horizontal" | "vertical";
|
|
11
9
|
|
|
12
10
|
interface CarouselContextValue {
|
|
@@ -259,7 +257,7 @@ export const Carousel = React.forwardRef<HTMLDivElement, CarouselProps>(
|
|
|
259
257
|
<CarouselContext.Provider value={value}>
|
|
260
258
|
<div
|
|
261
259
|
ref={ref}
|
|
262
|
-
className={
|
|
260
|
+
className={cn("sh-ui-carousel", className)}
|
|
263
261
|
data-orientation={orientation}
|
|
264
262
|
role="region"
|
|
265
263
|
aria-roledescription="carousel"
|
|
@@ -292,7 +290,7 @@ export const CarouselContent = React.forwardRef<
|
|
|
292
290
|
return (
|
|
293
291
|
<div
|
|
294
292
|
ref={mergedRef}
|
|
295
|
-
className={
|
|
293
|
+
className={cn("sh-ui-carousel__content", className)}
|
|
296
294
|
data-orientation={orientation}
|
|
297
295
|
{...props}
|
|
298
296
|
/>
|
|
@@ -313,7 +311,7 @@ export const CarouselItem = React.forwardRef<
|
|
|
313
311
|
ref={ref}
|
|
314
312
|
role="group"
|
|
315
313
|
aria-roledescription="slide"
|
|
316
|
-
className={
|
|
314
|
+
className={cn("sh-ui-carousel__item", className)}
|
|
317
315
|
data-orientation={orientation}
|
|
318
316
|
{...props}
|
|
319
317
|
/>
|
|
@@ -332,7 +330,7 @@ export const CarouselPrevious = React.forwardRef<
|
|
|
332
330
|
ref={ref}
|
|
333
331
|
type="button"
|
|
334
332
|
aria-label="이전"
|
|
335
|
-
className={
|
|
333
|
+
className={cn("sh-ui-carousel__nav", "sh-ui-carousel__nav--prev", className)}
|
|
336
334
|
data-orientation={orientation}
|
|
337
335
|
disabled={disabled || atStart || count === 0}
|
|
338
336
|
onClick={(e) => {
|
|
@@ -368,7 +366,7 @@ export const CarouselNext = React.forwardRef<
|
|
|
368
366
|
ref={ref}
|
|
369
367
|
type="button"
|
|
370
368
|
aria-label="다음"
|
|
371
|
-
className={
|
|
369
|
+
className={cn("sh-ui-carousel__nav", "sh-ui-carousel__nav--next", className)}
|
|
372
370
|
data-orientation={orientation}
|
|
373
371
|
disabled={disabled || atEnd || count === 0}
|
|
374
372
|
onClick={(e) => {
|
|
@@ -410,7 +408,7 @@ export const CarouselIndicators = React.forwardRef<
|
|
|
410
408
|
ref={ref}
|
|
411
409
|
role="tablist"
|
|
412
410
|
aria-label="슬라이드 선택"
|
|
413
|
-
className={
|
|
411
|
+
className={cn("sh-ui-carousel__indicators", className)}
|
|
414
412
|
data-orientation={orientation}
|
|
415
413
|
{...props}
|
|
416
414
|
>
|
|
@@ -2,10 +2,8 @@ import * as React from "react";
|
|
|
2
2
|
import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
|
3
3
|
import { CheckboxGroup as BaseCheckboxGroup } from "@base-ui/react/checkbox-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 CheckboxProps = Omit<
|
|
10
8
|
React.ComponentPropsWithoutRef<typeof BaseCheckbox.Root>,
|
|
11
9
|
"className"
|
|
@@ -17,7 +15,7 @@ export const Checkbox = React.forwardRef<HTMLElement, CheckboxProps>(
|
|
|
17
15
|
({ className, ...props }, ref) => (
|
|
18
16
|
<BaseCheckbox.Root
|
|
19
17
|
ref={ref}
|
|
20
|
-
className={
|
|
18
|
+
className={cn(
|
|
21
19
|
"inline-flex items-center justify-center w-[1.125rem] h-[1.125rem] border border-border-strong rounded-[calc(var(--radius)-2px)] bg-background text-primary-foreground cursor-pointer shrink-0 transition-[background-color,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]:bg-primary data-[checked]:border-primary data-[indeterminate]:bg-primary data-[indeterminate]: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
|
)}
|
|
@@ -43,7 +41,7 @@ export const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps
|
|
|
43
41
|
({ className, orientation = "vertical", ...props }, ref) => (
|
|
44
42
|
<BaseCheckboxGroup
|
|
45
43
|
ref={ref}
|
|
46
|
-
className={
|
|
44
|
+
className={cn(
|
|
47
45
|
"flex gap-2.5",
|
|
48
46
|
orientation === "vertical" ? "flex-col" : "flex-row flex-wrap",
|
|
49
47
|
className,
|
|
@@ -3,10 +3,8 @@ import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
|
|
3
3
|
import { CheckboxGroup as BaseCheckboxGroup } from "@base-ui/react/checkbox-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
|
/* ───────────── Checkbox ───────────── */
|
|
11
9
|
|
|
12
10
|
export type CheckboxProps = Omit<
|
|
@@ -24,7 +22,7 @@ export const Checkbox = React.forwardRef<HTMLElement, CheckboxProps>(
|
|
|
24
22
|
({ className, ...props }, ref) => (
|
|
25
23
|
<BaseCheckbox.Root
|
|
26
24
|
ref={ref}
|
|
27
|
-
className={
|
|
25
|
+
className={cn("sh-ui-checkbox", className)}
|
|
28
26
|
{...props}
|
|
29
27
|
>
|
|
30
28
|
<BaseCheckbox.Indicator className="sh-ui-checkbox__indicator">
|
|
@@ -60,7 +58,7 @@ export const CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps
|
|
|
60
58
|
({ className, orientation = "vertical", ...props }, ref) => (
|
|
61
59
|
<BaseCheckboxGroup
|
|
62
60
|
ref={ref}
|
|
63
|
-
className={
|
|
61
|
+
className={cn("sh-ui-checkbox-group", className)}
|
|
64
62
|
data-orientation={orientation}
|
|
65
63
|
{...props}
|
|
66
64
|
/>
|
|
@@ -10,6 +10,7 @@ import { css as cssLang } from "@codemirror/lang-css";
|
|
|
10
10
|
import { html } from "@codemirror/lang-html";
|
|
11
11
|
import { markdown } from "@codemirror/lang-markdown";
|
|
12
12
|
|
|
13
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
13
14
|
export type CodeEditorLanguage =
|
|
14
15
|
| "text" | "javascript" | "typescript" | "jsx" | "tsx"
|
|
15
16
|
| "json" | "css" | "html" | "markdown";
|
|
@@ -30,9 +31,6 @@ export interface CodeEditorProps {
|
|
|
30
31
|
"aria-labelledby"?: string;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
34
|
-
return args.filter(Boolean).join(" ");
|
|
35
|
-
}
|
|
36
34
|
|
|
37
35
|
function languageExtension(language: CodeEditorLanguage): Extension {
|
|
38
36
|
switch (language) {
|
|
@@ -136,7 +134,7 @@ export function CodeEditor({
|
|
|
136
134
|
return (
|
|
137
135
|
<div
|
|
138
136
|
ref={hostRef}
|
|
139
|
-
className={
|
|
137
|
+
className={cn(
|
|
140
138
|
"sh-ui-code-editor relative border border-border rounded-[var(--radius)] bg-background text-[0.8125rem] leading-relaxed 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",
|
|
141
139
|
className,
|
|
142
140
|
)}
|
|
@@ -11,6 +11,7 @@ import { html } from "@codemirror/lang-html";
|
|
|
11
11
|
import { markdown } from "@codemirror/lang-markdown";
|
|
12
12
|
import "./styles.css";
|
|
13
13
|
|
|
14
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
14
15
|
export type CodeEditorLanguage =
|
|
15
16
|
| "text"
|
|
16
17
|
| "javascript"
|
|
@@ -59,9 +60,6 @@ export interface CodeEditorProps {
|
|
|
59
60
|
"aria-labelledby"?: string;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
63
|
-
return args.filter(Boolean).join(" ");
|
|
64
|
-
}
|
|
65
63
|
|
|
66
64
|
function languageExtension(language: CodeEditorLanguage): Extension {
|
|
67
65
|
switch (language) {
|
|
@@ -219,7 +217,7 @@ export function CodeEditor({
|
|
|
219
217
|
return (
|
|
220
218
|
<div
|
|
221
219
|
ref={hostRef}
|
|
222
|
-
className={
|
|
220
|
+
className={cn("sh-ui-code-editor", className)}
|
|
223
221
|
data-readonly={readOnly || undefined}
|
|
224
222
|
style={
|
|
225
223
|
{
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { codeToHtml } from "shiki";
|
|
2
2
|
import { CodePanelCopyButton } from "./copy";
|
|
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 interface CodePanelProps
|
|
9
7
|
extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
10
8
|
code?: string;
|
|
@@ -21,7 +19,7 @@ const rootClasses =
|
|
|
21
19
|
export async function CodePanel({
|
|
22
20
|
code, language = "text", filename, showLineNumbers = true, hideCopy, className, children, ...rest
|
|
23
21
|
}: CodePanelProps) {
|
|
24
|
-
const classes =
|
|
22
|
+
const classes = cn(rootClasses, className);
|
|
25
23
|
|
|
26
24
|
if (children !== undefined) {
|
|
27
25
|
return <div className={classes} {...rest}>{children}</div>;
|
|
@@ -52,7 +50,7 @@ export async function CodePanel({
|
|
|
52
50
|
export function CodePanelHeader({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
53
51
|
return (
|
|
54
52
|
<div
|
|
55
|
-
className={
|
|
53
|
+
className={cn(
|
|
56
54
|
"flex items-center justify-between gap-[var(--space-2)] py-[var(--space-2)] pl-[var(--space-4)] pr-[var(--space-3)] border-b border-border bg-background-muted text-[length:var(--text-xs)] text-foreground-muted",
|
|
57
55
|
className,
|
|
58
56
|
)}
|
|
@@ -65,7 +63,7 @@ export function CodePanelHeader({ className, children, ...props }: React.HTMLAtt
|
|
|
65
63
|
|
|
66
64
|
export function CodePanelFilename({ className, children, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
|
|
67
65
|
return (
|
|
68
|
-
<span className={
|
|
66
|
+
<span className={cn("font-mono text-foreground", className)} {...props}>{children}</span>
|
|
69
67
|
);
|
|
70
68
|
}
|
|
71
69
|
|
|
@@ -93,7 +91,7 @@ export async function CodePanelBody({
|
|
|
93
91
|
|
|
94
92
|
return (
|
|
95
93
|
<div
|
|
96
|
-
className={
|
|
94
|
+
className={cn(
|
|
97
95
|
"overflow-x-auto [&_pre]:m-0 [&_pre]:py-[var(--space-3)] [&_pre]:px-[var(--space-4)] [&_pre]:!bg-transparent [&_pre]:font-mono [&_pre]:text-[length:inherit] [&_pre]:leading-[inherit] [&_pre]:border-none [&_pre]:rounded-none [&_code]:bg-transparent [&_code]:p-0 [&_code]:text-[length:inherit] [&_code]:block [&_.shiki]:!text-[var(--shiki-light)] [&_.shiki_span]:!text-[var(--shiki-light)] [&_.shiki]:!bg-transparent [&_.shiki_span]:!bg-transparent [.dark_&_.shiki]:!text-[var(--shiki-dark)] [.dark_&_.shiki_span]:!text-[var(--shiki-dark)] data-[line-numbers]:[&_pre_code]:[counter-reset:step] data-[line-numbers]:[&_pre_code_.line]:before:[content:counter(step)] data-[line-numbers]:[&_pre_code_.line]:before:[counter-increment:step] data-[line-numbers]:[&_pre_code_.line]:before:inline-block data-[line-numbers]:[&_pre_code_.line]:before:w-7 data-[line-numbers]:[&_pre_code_.line]:before:mr-[var(--space-4)] data-[line-numbers]:[&_pre_code_.line]:before:text-right data-[line-numbers]:[&_pre_code_.line]:before:text-foreground-muted data-[line-numbers]:[&_pre_code_.line]:before:opacity-70 data-[line-numbers]:[&_pre_code_.line]:before:select-none",
|
|
98
96
|
className,
|
|
99
97
|
)}
|