@uniai-fe/uds-primitives 0.2.8 → 0.2.10

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 (72) hide show
  1. package/dist/styles.css +419 -0
  2. package/package.json +2 -1
  3. package/src/components/calendar/types/calendar.ts +5 -0
  4. package/src/components/dropdown/markup/Template.tsx +41 -17
  5. package/src/components/dropdown/markup/foundation/Container.tsx +14 -2
  6. package/src/components/dropdown/markup/foundation/MenuItem.tsx +20 -6
  7. package/src/components/dropdown/markup/foundation/Root.tsx +8 -1
  8. package/src/components/dropdown/markup/foundation/Trigger.tsx +7 -1
  9. package/src/components/dropdown/styles/dropdown.scss +4 -0
  10. package/src/components/dropdown/types/props.ts +5 -0
  11. package/src/components/input/markup/date/Template.tsx +36 -5
  12. package/src/components/input/markup/date/Trigger.tsx +22 -4
  13. package/src/components/input/markup/foundation/Input.tsx +19 -11
  14. package/src/components/input/markup/foundation/Utility.tsx +11 -7
  15. package/src/components/input/styles/date.scss +21 -0
  16. package/src/components/input/styles/foundation.scss +30 -0
  17. package/src/components/input/styles/variables.scss +11 -0
  18. package/src/components/input/types/date.ts +15 -0
  19. package/src/components/input/types/foundation.ts +18 -11
  20. package/src/components/input/utils/date.ts +15 -1
  21. package/src/components/select/hooks/index.ts +1 -45
  22. package/src/components/select/hooks/interaction.ts +62 -0
  23. package/src/components/select/markup/Default.tsx +59 -35
  24. package/src/components/select/markup/foundation/Base.tsx +12 -4
  25. package/src/components/select/markup/foundation/Container.tsx +37 -34
  26. package/src/components/select/markup/foundation/Icon.tsx +6 -1
  27. package/src/components/select/markup/multiple/Multiple.tsx +62 -35
  28. package/src/components/select/markup/multiple/SelectedChip.tsx +5 -2
  29. package/src/components/select/styles/select.scss +50 -0
  30. package/src/components/select/styles/variables.scss +26 -0
  31. package/src/components/select/types/base.ts +3 -2
  32. package/src/components/select/types/icon.ts +7 -6
  33. package/src/components/select/types/index.ts +1 -0
  34. package/src/components/select/types/interaction.ts +30 -0
  35. package/src/components/select/types/props.ts +8 -0
  36. package/src/components/select/types/trigger.ts +4 -0
  37. package/src/components/table/hooks/index.ts +0 -3
  38. package/src/components/table/index.tsx +5 -3
  39. package/src/components/table/markup/Container.tsx +126 -0
  40. package/src/components/table/markup/foundation/Body.tsx +24 -0
  41. package/src/components/table/markup/foundation/Cell.tsx +72 -0
  42. package/src/components/table/markup/foundation/Col.tsx +22 -0
  43. package/src/components/table/markup/foundation/Colgroup.tsx +29 -0
  44. package/src/components/table/markup/foundation/Foot.tsx +24 -0
  45. package/src/components/table/markup/foundation/Head.tsx +24 -0
  46. package/src/components/table/markup/foundation/Root.tsx +32 -0
  47. package/src/components/table/markup/foundation/Row.tsx +32 -0
  48. package/src/components/table/markup/foundation/Td.tsx +37 -0
  49. package/src/components/table/markup/foundation/Th.tsx +39 -0
  50. package/src/components/table/markup/foundation/index.tsx +30 -0
  51. package/src/components/table/markup/index.tsx +8 -2
  52. package/src/components/table/styles/foundation.scss +247 -0
  53. package/src/components/table/styles/index.scss +2 -0
  54. package/src/components/table/styles/variables.scss +29 -0
  55. package/src/components/table/types/foundation.ts +250 -0
  56. package/src/components/table/types/index.ts +1 -4
  57. package/src/components/tooltip/img/info.svg +5 -0
  58. package/src/components/tooltip/img/information.svg +9 -0
  59. package/src/components/tooltip/index.scss +1 -0
  60. package/src/components/tooltip/index.tsx +4 -0
  61. package/src/components/tooltip/markup/Message.tsx +70 -0
  62. package/src/components/tooltip/markup/Root.tsx +32 -0
  63. package/src/components/tooltip/markup/Template.tsx +46 -0
  64. package/src/components/tooltip/markup/Trigger.tsx +32 -0
  65. package/src/components/tooltip/markup/index.tsx +18 -0
  66. package/src/components/tooltip/styles/index.scss +2 -0
  67. package/src/components/tooltip/styles/tooltip.scss +47 -0
  68. package/src/components/tooltip/styles/variables.scss +14 -0
  69. package/src/components/tooltip/types/index.ts +1 -0
  70. package/src/components/tooltip/types/props.ts +118 -0
  71. package/src/index.scss +1 -0
  72. package/src/index.tsx +1 -0
@@ -6,13 +6,16 @@ import RemoveIcon from "../../img/remove.svg";
6
6
  import type { SelectMultipleChipProps } from "../../types/multiple";
7
7
 
8
8
  /**
9
- * Select multi chip; 선택된 값을 chip 형태로 표시하고 필요 시 제거 버튼을 노출한다.
9
+ * Select Markup; Multiple 선택값 Chip 렌더링 컴포넌트
10
10
  * @component
11
11
  * @param {SelectMultipleChipProps} props chip props
12
12
  * @param {React.ReactNode} props.label chip 라벨
13
13
  * @param {React.ReactNode} [props.suffix] 라벨 뒤에 붙는 서브 라벨
14
- * @param {boolean} [props.removable] remove 버튼 노출 여부
14
+ * @param {boolean} [props.removable=true] remove 버튼 노출 여부
15
15
  * @param {() => void} [props.onRemove] remove 클릭 핸들러
16
+ * @param {"value" | "summary"} [props.kind="value"] chip 용도 구분
17
+ * @example
18
+ * <SelectMultipleSelectedChip label="Apple" removable onRemove={() => {}} />
16
19
  */
17
20
  export function SelectMultipleSelectedChip({
18
21
  label,
@@ -109,6 +109,14 @@
109
109
  }
110
110
  }
111
111
 
112
+ &[data-priority="table"] {
113
+ // 변경: table priority는 input table과 동일하게 기본 border/background를 최소화한다.
114
+ border-radius: var(--select-table-radius);
115
+ border-color: var(--select-table-border-default-color);
116
+ background-color: var(--select-table-surface-color);
117
+ --select-icon-fill: var(--select-table-icon-color-default);
118
+ }
119
+
112
120
  &:not([data-priority="secondary"])[data-state="focused"],
113
121
  &:not([data-priority="secondary"])[data-open="true"],
114
122
  &:not([data-priority="secondary"]):focus-visible {
@@ -117,6 +125,14 @@
117
125
  --select-icon-fill: var(--select-icon-color-focused);
118
126
  }
119
127
 
128
+ &[data-priority="table"][data-state="focused"],
129
+ &[data-priority="table"][data-open="true"],
130
+ &[data-priority="table"]:focus-visible {
131
+ border-color: var(--select-table-border-focus-color);
132
+ border-width: var(--select-primary-border-width-focus);
133
+ --select-icon-fill: var(--select-table-icon-color-focused);
134
+ }
135
+
120
136
  // &:not([data-priority="secondary"]):hover:not(:disabled) {
121
137
  // }
122
138
 
@@ -138,6 +154,14 @@
138
154
  height: var(--select-secondary-underline-width-default);
139
155
  }
140
156
  }
157
+
158
+ &[data-priority="table"][data-state="disabled"],
159
+ &[data-priority="table"]:disabled {
160
+ border-color: var(--select-table-border-disabled-color);
161
+ background-color: var(--select-table-surface-disabled-color);
162
+ cursor: not-allowed;
163
+ --select-icon-fill: var(--select-table-icon-color-disabled);
164
+ }
141
165
  }
142
166
 
143
167
  .select-value {
@@ -165,6 +189,10 @@
165
189
  color: var(--select-secondary-color-text);
166
190
  }
167
191
 
192
+ .select-button[data-priority="table"] & {
193
+ color: var(--select-table-color-text);
194
+ }
195
+
168
196
  .select-button[data-state="focused"] &,
169
197
  .select-button[data-open="true"] & {
170
198
  color: var(--select-primary-color-text-focused);
@@ -185,6 +213,10 @@
185
213
  .select-button[data-priority="secondary"] & {
186
214
  color: var(--select-secondary-color-placeholder);
187
215
  }
216
+
217
+ .select-button[data-priority="table"] & {
218
+ color: var(--select-table-color-placeholder);
219
+ }
188
220
  }
189
221
 
190
222
  .select-button[data-size="small"] .select-label {
@@ -194,6 +226,12 @@
194
226
  font-weight: var(--select-text-small-weight);
195
227
  }
196
228
 
229
+ .select-button[data-priority="table"][data-size="small"] .select-label {
230
+ font-size: var(--select-table-text-small-size);
231
+ line-height: var(--select-table-text-small-line-height);
232
+ font-weight: var(--select-table-text-small-weight);
233
+ }
234
+
197
235
  .select-button[data-size="large"] .select-label {
198
236
  font-size: var(--select-text-large-size);
199
237
  line-height: var(--select-text-large-line-height);
@@ -201,6 +239,18 @@
201
239
  font-weight: var(--select-text-large-weight);
202
240
  }
203
241
 
242
+ .select-button[data-priority="table"][data-size="medium"] .select-label {
243
+ font-size: var(--select-table-text-medium-size);
244
+ line-height: var(--select-table-text-medium-line-height);
245
+ font-weight: var(--select-table-text-medium-weight);
246
+ }
247
+
248
+ .select-button[data-priority="table"][data-size="large"] .select-label {
249
+ font-size: var(--select-table-text-large-size);
250
+ line-height: var(--select-table-text-large-line-height);
251
+ font-weight: var(--select-table-text-large-weight);
252
+ }
253
+
204
254
  .select-button[data-priority="secondary"][data-size="large"] .select-label {
205
255
  font-weight: 600;
206
256
  }
@@ -24,6 +24,32 @@
24
24
  --select-secondary-gap: var(--spacing-gap-2);
25
25
  --select-secondary-underline-width-default: 1px;
26
26
  --select-secondary-underline-width-focus: 1.4px;
27
+ --select-table-radius: var(--input-table-radius-base);
28
+ --select-table-border-default-color: var(--input-border-table-default-color);
29
+ --select-table-border-focus-color: var(--input-border-active-color);
30
+ --select-table-border-disabled-color: var(--input-border-disabled-color);
31
+ --select-table-surface-color: transparent;
32
+ --select-table-surface-disabled-color: var(--input-surface-disabled-color);
33
+ --select-table-color-text: var(--input-text-color);
34
+ --select-table-color-placeholder: var(--input-placeholder-color);
35
+ --select-table-icon-color-default: var(--select-icon-color-default);
36
+ --select-table-icon-color-focused: var(--select-icon-color-focused);
37
+ --select-table-icon-color-disabled: var(--select-icon-color-disabled);
38
+ --select-table-text-small-size: var(--input-table-text-small-size);
39
+ --select-table-text-small-line-height: var(
40
+ --input-table-text-small-line-height
41
+ );
42
+ --select-table-text-small-weight: var(--input-table-text-small-weight);
43
+ --select-table-text-medium-size: var(--input-table-text-medium-size);
44
+ --select-table-text-medium-line-height: var(
45
+ --input-table-text-medium-line-height
46
+ );
47
+ --select-table-text-medium-weight: var(--input-table-text-medium-weight);
48
+ --select-table-text-large-size: var(--input-table-text-large-size);
49
+ --select-table-text-large-line-height: var(
50
+ --input-table-text-large-line-height
51
+ );
52
+ --select-table-text-large-weight: var(--input-table-text-large-weight);
27
53
  --select-text-small-size: 15px;
28
54
  --select-text-small-line-height: 1.4;
29
55
  --select-text-small-letter-spacing: 0.2px;
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * Select priority scale
3
- * @typedef {"primary" | "secondary"} SelectPriority
3
+ * @typedef {"primary" | "secondary" | "table"} SelectPriority
4
4
  * - primary: input primary 스타일과 동일한 기본 형태
5
5
  * - secondary: borderless + underline 스타일
6
+ * - table: input table과 동일한 셀 임베드 스타일
6
7
  */
7
- export type SelectPriority = "primary" | "secondary";
8
+ export type SelectPriority = "primary" | "secondary" | "table";
8
9
 
9
10
  /**
10
11
  * Select size scale
@@ -14,19 +14,20 @@ export type SelectIconSizeMap = Record<SelectSize, ElementType>;
14
14
  * Select 아이콘 priority 맵
15
15
  * @property {SelectIconSizeMap} primary primary priority용 아이콘 컬렉션
16
16
  * @property {SelectIconSizeMap} secondary secondary priority용 아이콘 컬렉션
17
+ * @property {SelectIconSizeMap} table table priority용 아이콘 컬렉션
17
18
  */
18
19
  export type SelectIconPriorityMap = Record<SelectPriority, SelectIconSizeMap>;
19
20
 
20
21
  /**
21
22
  * Select multi remove priority 맵
22
23
  * @property {SelectIconSizeMap} primary primary priority remove 아이콘
24
+ * @property {SelectIconSizeMap} secondary secondary priority remove 아이콘
25
+ * @property {SelectIconSizeMap} table table priority remove 아이콘
23
26
  */
24
- export interface SelectIconRemovePriorityMap {
25
- /**
26
- * primary priority remove 아이콘
27
- */
28
- primary: SelectIconSizeMap;
29
- }
27
+ export type SelectIconRemovePriorityMap = Record<
28
+ SelectPriority,
29
+ SelectIconSizeMap
30
+ >;
30
31
 
31
32
  /**
32
33
  * Select 아이콘 전체 컬렉션
@@ -1,5 +1,6 @@
1
1
  export type * from "./base";
2
2
  export type * from "./icon";
3
+ export type * from "./interaction";
3
4
  export type * from "./props";
4
5
  export type * from "./trigger";
5
6
  export type * from "./multiple";
@@ -0,0 +1,30 @@
1
+ import type { SelectDropdownBehaviorProps } from "./props";
2
+
3
+ /**
4
+ * Select Hook Types; Dropdown open state hook 입력 파라미터
5
+ * @property {boolean} [open] 외부 제어형 open 상태
6
+ * @property {boolean} [defaultOpen] 비제어형 초기 open 상태
7
+ * @property {(open: boolean) => void} [onOpenChange] open 상태 변경 콜백
8
+ */
9
+ export interface UseSelectDropdownOpenStateParams extends SelectDropdownBehaviorProps {}
10
+
11
+ /**
12
+ * Select Hook Types; Dropdown open state hook 반환값
13
+ * @property {boolean} open 최종 open 상태
14
+ * @property {(nextOpen: boolean) => void} setOpen open 상태 업데이트 함수
15
+ * @property {boolean} isControlled open prop 기반 제어형 여부
16
+ */
17
+ export interface UseSelectDropdownOpenStateReturn {
18
+ /**
19
+ * 최종 open 상태
20
+ */
21
+ open: boolean;
22
+ /**
23
+ * open 상태 업데이트 함수
24
+ */
25
+ setOpen: (nextOpen: boolean) => void;
26
+ /**
27
+ * open prop 기반 제어형 여부
28
+ */
29
+ isControlled: boolean;
30
+ }
@@ -24,6 +24,7 @@ import type {
24
24
  export interface SelectStyleOptions {
25
25
  /**
26
26
  * priority scale
27
+ * - table 우선순위는 width 미지정 시 기본 block(full)로 동작한다.
27
28
  */
28
29
  priority?: SelectPriority;
29
30
  /**
@@ -142,6 +143,7 @@ export type SelectProps = SelectStyleOptions &
142
143
  * @property {DropdownMenuProps} [dropdownRootProps] Dropdown.Root 전달 props(제어 props 제외)
143
144
  * @property {DropdownContainerProps} [dropdownContainerProps] Dropdown.Container 전달 props(children/size 제외)
144
145
  * @property {DropdownMenuListProps} [dropdownMenuListProps] Dropdown.Menu.List 전달 props
146
+ * @property {ReactNode} [alt] option이 비어 있을 때 렌더링할 alternate 콘텐츠
145
147
  */
146
148
  export interface SelectDropdownConfigProps {
147
149
  /**
@@ -183,6 +185,10 @@ export interface SelectDropdownConfigProps {
183
185
  * Dropdown.Menu.List 전달 props
184
186
  */
185
187
  dropdownMenuListProps?: DropdownMenuListProps;
188
+ /**
189
+ * option이 비어 있을 때 렌더링할 alternate 콘텐츠
190
+ */
191
+ alt?: ReactNode;
186
192
  }
187
193
 
188
194
  /**
@@ -227,6 +233,7 @@ export interface SelectDropdownBehaviorProps {
227
233
  * @property {Omit<DropdownMenuProps, "open" | "defaultOpen" | "onOpenChange">} [dropdownRootProps] Dropdown.Root 전달 props
228
234
  * @property {Omit<DropdownContainerProps, "children" | "size" | "width">} [dropdownContainerProps] Dropdown.Container 전달 props
229
235
  * @property {DropdownMenuListProps} [dropdownMenuListProps] Dropdown.Menu.List 전달 props
236
+ * @property {ReactNode} [alt] option이 비어 있을 때 렌더링할 alternate 콘텐츠
230
237
  * @property {boolean} [open] dropdown open 상태
231
238
  * @property {boolean} [defaultOpen] uncontrolled 초기 open 상태
232
239
  * @property {(open: boolean) => void} [onOpenChange] open state change 콜백
@@ -257,6 +264,7 @@ export type SelectDefaultComponentProps = SelectTriggerDefaultProps &
257
264
  * @property {Omit<DropdownMenuProps, "open" | "defaultOpen" | "onOpenChange">} [dropdownRootProps] Dropdown.Root 전달 props
258
265
  * @property {Omit<DropdownContainerProps, "children" | "size" | "width">} [dropdownContainerProps] Dropdown.Container 전달 props
259
266
  * @property {DropdownMenuListProps} [dropdownMenuListProps] Dropdown.Menu.List 전달 props
267
+ * @property {ReactNode} [alt] option이 비어 있을 때 렌더링할 alternate 콘텐츠
260
268
  * @property {boolean} [open] dropdown open 상태
261
269
  * @property {boolean} [defaultOpen] uncontrolled 초기 open 상태
262
270
  * @property {(open: boolean) => void} [onOpenChange] open state change 콜백
@@ -26,6 +26,7 @@ export interface SelectTriggerBaseProps extends HTMLAttributes<HTMLElement> {
26
26
  * priority scale
27
27
  * - primary
28
28
  * - secondary
29
+ * - table
29
30
  */
30
31
  priority?: SelectPriority;
31
32
  /**
@@ -90,6 +91,7 @@ export interface SelectTriggerDefaultProps extends HTMLAttributes<HTMLElement> {
90
91
  * priority scale
91
92
  * - primary
92
93
  * - secondary
94
+ * - table
93
95
  */
94
96
  priority?: SelectPriority;
95
97
  /**
@@ -144,6 +146,8 @@ export interface SelectTriggerMultipleProps extends HTMLAttributes<HTMLElement>
144
146
  /**
145
147
  * priority scale
146
148
  * - primary
149
+ * - secondary
150
+ * - table
147
151
  */
148
152
  priority?: SelectPriority;
149
153
  /**
@@ -1,4 +1 @@
1
- /**
2
- * TODO(table): 접근성/상태 계산 hook을 정의한다.
3
- */
4
1
  export {};
@@ -1,4 +1,6 @@
1
- /**
2
- * table 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
3
- */
1
+ import "./index.scss";
2
+
4
3
  export * from "./markup";
4
+ export * from "./hooks";
5
+ export type * from "./types";
6
+ export * from "./utils";
@@ -0,0 +1,126 @@
1
+ import { forwardRef } from "react";
2
+ import type { TableTemplateColumn, TableTemplateProps } from "../types";
3
+ import TableBody from "./foundation/Body";
4
+ import TableCell from "./foundation/Cell";
5
+ import TableHead from "./foundation/Head";
6
+ import TableTh from "./foundation/Th";
7
+ import TableRoot from "./foundation/Root";
8
+ import TableRow from "./foundation/Row";
9
+
10
+ const resolveHeadContent = (column: TableTemplateColumn): React.ReactNode => {
11
+ if (typeof column.cellContents !== "undefined") {
12
+ return column.cellContents;
13
+ }
14
+
15
+ if (typeof column.cellChildren !== "undefined") {
16
+ return column.cellChildren;
17
+ }
18
+
19
+ if (typeof column.headContent !== "undefined") {
20
+ return column.headContent;
21
+ }
22
+
23
+ if (typeof column.dataName !== "undefined") {
24
+ return column.dataName;
25
+ }
26
+
27
+ return column.dataKey;
28
+ };
29
+
30
+ /**
31
+ * Table Preset; 기본 Container 조합 컴포넌트
32
+ * @component
33
+ * @param {TableTemplateProps} props
34
+ * @param {TableTemplateColumn[]} [props.columns] colgroup/head 자동 렌더링 column 목록
35
+ * @param {boolean} [props.isCustomBody=false] true면 body wrapper 없이 children 직접 렌더링
36
+ * @param {"legacy-rem" | "raw"} [props.widthMode="legacy-rem"] number width 해석 방식
37
+ * @param {React.ReactNode} [props.footer] footer 노드. `<Table.Foot />`를 직접 전달하는 방식을 권장
38
+ * @param {React.ReactNode} [props.children] table body 콘텐츠
39
+ * @example
40
+ * <Table.Container
41
+ * columns={columns}
42
+ * footer={
43
+ * <Table.Foot>
44
+ * <Table.Row>
45
+ * <Table.Td colSpan={4}>
46
+ * <Table.Cell>합계</Table.Cell>
47
+ * </Table.Td>
48
+ * </Table.Row>
49
+ * </Table.Foot>
50
+ * }
51
+ * >
52
+ * {rows}
53
+ * </Table.Container>
54
+ */
55
+ const TableContainer = forwardRef<HTMLTableElement, TableTemplateProps>(
56
+ (
57
+ {
58
+ columns,
59
+ isCustomBody = false,
60
+ widthMode = "legacy-rem",
61
+ footer,
62
+ children,
63
+ ...tableProps
64
+ },
65
+ ref,
66
+ ) => {
67
+ const hasColumns = Array.isArray(columns) && columns.length > 0;
68
+
69
+ return (
70
+ <TableRoot {...tableProps} ref={ref}>
71
+ {hasColumns && (
72
+ <colgroup>
73
+ {columns.map(({ key, width, dataKey, className, span }) => {
74
+ const normalizedWidth =
75
+ typeof width === "number"
76
+ ? widthMode === "legacy-rem"
77
+ ? `${width / 10}rem`
78
+ : width
79
+ : width;
80
+
81
+ return (
82
+ <col
83
+ key={`${key}/colgroup`}
84
+ className={className}
85
+ data-key={dataKey}
86
+ span={span}
87
+ style={
88
+ normalizedWidth
89
+ ? {
90
+ width: normalizedWidth,
91
+ }
92
+ : undefined
93
+ }
94
+ width={typeof width === "number" ? width : undefined}
95
+ />
96
+ );
97
+ })}
98
+ </colgroup>
99
+ )}
100
+
101
+ {hasColumns && (
102
+ <TableHead>
103
+ <TableRow>
104
+ {columns.map(column => (
105
+ <TableTh key={`${column.key}/head`} data-key={column.dataKey}>
106
+ {/* 변경: 헤더 셀 여백/정렬도 Cell 레이어에서 일관 제어한다. */}
107
+ <TableCell section="head" alignX={column.align}>
108
+ {resolveHeadContent(column)}
109
+ </TableCell>
110
+ </TableTh>
111
+ ))}
112
+ </TableRow>
113
+ </TableHead>
114
+ )}
115
+
116
+ {isCustomBody ? children : <TableBody>{children}</TableBody>}
117
+
118
+ {footer}
119
+ </TableRoot>
120
+ );
121
+ },
122
+ );
123
+
124
+ TableContainer.displayName = "Table.Container";
125
+
126
+ export default TableContainer;
@@ -0,0 +1,24 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableBodyProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Tbody 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableBodyProps} props
9
+ * @param {string} [props.className] tbody className
10
+ * @param {React.ReactNode} [props.children] tbody 내부 콘텐츠
11
+ */
12
+ const TableBody = forwardRef<HTMLTableSectionElement, TableBodyProps>(
13
+ ({ className, children, ...bodyProps }, ref) => {
14
+ return (
15
+ <tbody {...bodyProps} ref={ref} className={clsx("table-body", className)}>
16
+ {children}
17
+ </tbody>
18
+ );
19
+ },
20
+ );
21
+
22
+ TableBody.displayName = "TableFoundation.Body";
23
+
24
+ export default TableBody;
@@ -0,0 +1,72 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableCellContentProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Cell 콘텐츠 래퍼 컴포넌트
7
+ * @component
8
+ * @param {TableCellContentProps} props
9
+ * @param {"left" | "center" | "right"} [props.alignX] cell 콘텐츠 가로 정렬
10
+ * @param {"top" | "center" | "bottom"} [props.alignY] cell 콘텐츠 세로 정렬
11
+ * @param {boolean} [props.noPadding=false] true면 cell 내부 padding 제거
12
+ * @param {"default" | "none"} [props.padding="default"] cell 내부 padding 제어
13
+ * @param {"left" | "center" | "right"} [props.align] cell 콘텐츠 가로 정렬(호환 alias)
14
+ * @param {string} [props.className] cell content className
15
+ * @param {React.ReactNode} [props.children] 셀 내부 콘텐츠
16
+ */
17
+ const TableCell = forwardRef<HTMLDivElement, TableCellContentProps>(
18
+ (
19
+ {
20
+ className,
21
+ children,
22
+ section,
23
+ alignX,
24
+ alignY,
25
+ noPadding = false,
26
+ padding = "default",
27
+ align,
28
+ ...cellProps
29
+ },
30
+ ref,
31
+ ) => {
32
+ const resolvedAlignX = alignX ?? align ?? "left";
33
+ const resolvedAlignY = alignY ?? "center";
34
+ // 변경: noPadding boolean이 주어지면 padding 옵션보다 우선해 none으로 고정한다.
35
+ const resolvedPadding = noPadding ? "none" : padding;
36
+
37
+ return (
38
+ <div
39
+ {...cellProps}
40
+ ref={ref}
41
+ data-align-x={resolvedAlignX}
42
+ data-align-y={resolvedAlignY}
43
+ data-padding={resolvedPadding}
44
+ data-no-padding={noPadding ? "true" : undefined}
45
+ className={clsx(
46
+ "table-cell",
47
+ section && `table-${section}-cell`,
48
+ "table-cell-content",
49
+ className,
50
+ )}
51
+ >
52
+ {/* 변경: 텍스트 콘텐츠는 slot 전용 className으로 감싸 스타일 적용 범위를 고정한다. */}
53
+ {typeof children === "string" || typeof children === "number" ? (
54
+ <span
55
+ className={clsx(
56
+ "table-cell-text",
57
+ section && `table-${section}-cell-text`,
58
+ )}
59
+ >
60
+ {children}
61
+ </span>
62
+ ) : (
63
+ children
64
+ )}
65
+ </div>
66
+ );
67
+ },
68
+ );
69
+
70
+ TableCell.displayName = "TableFoundation.Cell";
71
+
72
+ export default TableCell;
@@ -0,0 +1,22 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableColProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Col 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableColProps} props
9
+ * @param {string} [props.className] col className
10
+ * @deprecated `Table.Template` 또는 native `<col />` 사용을 권장한다.
11
+ */
12
+ const TableCol = forwardRef<HTMLTableColElement, TableColProps>(
13
+ ({ className, ...colProps }, ref) => {
14
+ return (
15
+ <col {...colProps} ref={ref} className={clsx("table-col", className)} />
16
+ );
17
+ },
18
+ );
19
+
20
+ TableCol.displayName = "TableFoundation.Col";
21
+
22
+ export default TableCol;
@@ -0,0 +1,29 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableColgroupProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Colgroup 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableColgroupProps} props
9
+ * @param {string} [props.className] colgroup className
10
+ * @param {React.ReactNode} [props.children] col 목록
11
+ * @deprecated `Table.Container` 또는 native `<colgroup />` 사용을 권장한다.
12
+ */
13
+ const TableColgroup = forwardRef<HTMLTableColElement, TableColgroupProps>(
14
+ ({ className, children, ...colgroupProps }, ref) => {
15
+ return (
16
+ <colgroup
17
+ {...colgroupProps}
18
+ ref={ref}
19
+ className={clsx("table-colgroup", className)}
20
+ >
21
+ {children}
22
+ </colgroup>
23
+ );
24
+ },
25
+ );
26
+
27
+ TableColgroup.displayName = "TableFoundation.Colgroup";
28
+
29
+ export default TableColgroup;
@@ -0,0 +1,24 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableFootProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Tfoot 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableFootProps} props
9
+ * @param {string} [props.className] tfoot className
10
+ * @param {React.ReactNode} [props.children] tfoot 내부 콘텐츠
11
+ */
12
+ const TableFoot = forwardRef<HTMLTableSectionElement, TableFootProps>(
13
+ ({ className, children, ...footProps }, ref) => {
14
+ return (
15
+ <tfoot {...footProps} ref={ref} className={clsx("table-foot", className)}>
16
+ {children}
17
+ </tfoot>
18
+ );
19
+ },
20
+ );
21
+
22
+ TableFoot.displayName = "TableFoundation.Foot";
23
+
24
+ export default TableFoot;
@@ -0,0 +1,24 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableHeadProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Thead 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableHeadProps} props
9
+ * @param {string} [props.className] thead className
10
+ * @param {React.ReactNode} [props.children] thead 내부 콘텐츠
11
+ */
12
+ const TableHead = forwardRef<HTMLTableSectionElement, TableHeadProps>(
13
+ ({ className, children, ...headProps }, ref) => {
14
+ return (
15
+ <thead {...headProps} ref={ref} className={clsx("table-head", className)}>
16
+ {children}
17
+ </thead>
18
+ );
19
+ },
20
+ );
21
+
22
+ TableHead.displayName = "TableFoundation.Head";
23
+
24
+ export default TableHead;
@@ -0,0 +1,32 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableRootProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Table Root 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableRootProps} props
9
+ * @param {string} [props.className] table 루트 className
10
+ * @param {React.ReactNode} [props.children] table 내부 콘텐츠
11
+ */
12
+ const TableRoot = forwardRef<HTMLTableElement, TableRootProps>(
13
+ ({ className, children, role, layout = "line", ...tableProps }, ref) => {
14
+ return (
15
+ <table
16
+ {...tableProps}
17
+ ref={ref}
18
+ className={clsx("table", "table-container", className)}
19
+ // 변경: line/grid 스타일 축을 data attribute로 명시해 foundation.scss에서 분기한다.
20
+ data-layout={layout}
21
+ // 기본 접근성 role은 table로 고정하고, 외부에서 전달한 role이 있으면 우선한다.
22
+ role={role ?? "table"}
23
+ >
24
+ {children}
25
+ </table>
26
+ );
27
+ },
28
+ );
29
+
30
+ TableRoot.displayName = "TableFoundation.Root";
31
+
32
+ export default TableRoot;