@uniai-fe/uds-primitives 0.2.9 → 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 (59) hide show
  1. package/dist/styles.css +415 -0
  2. package/package.json +2 -1
  3. package/src/components/calendar/types/calendar.ts +5 -0
  4. package/src/components/input/markup/date/Template.tsx +36 -5
  5. package/src/components/input/markup/date/Trigger.tsx +22 -4
  6. package/src/components/input/markup/foundation/Input.tsx +19 -11
  7. package/src/components/input/markup/foundation/Utility.tsx +11 -7
  8. package/src/components/input/styles/date.scss +21 -0
  9. package/src/components/input/styles/foundation.scss +30 -0
  10. package/src/components/input/styles/variables.scss +11 -0
  11. package/src/components/input/types/date.ts +15 -0
  12. package/src/components/input/types/foundation.ts +18 -11
  13. package/src/components/input/utils/date.ts +15 -1
  14. package/src/components/select/markup/Default.tsx +6 -3
  15. package/src/components/select/markup/foundation/Base.tsx +1 -1
  16. package/src/components/select/markup/foundation/Icon.tsx +6 -1
  17. package/src/components/select/markup/multiple/Multiple.tsx +6 -3
  18. package/src/components/select/styles/select.scss +50 -0
  19. package/src/components/select/styles/variables.scss +26 -0
  20. package/src/components/select/types/base.ts +3 -2
  21. package/src/components/select/types/icon.ts +7 -6
  22. package/src/components/select/types/props.ts +1 -0
  23. package/src/components/select/types/trigger.ts +4 -0
  24. package/src/components/table/hooks/index.ts +0 -3
  25. package/src/components/table/index.tsx +5 -3
  26. package/src/components/table/markup/Container.tsx +126 -0
  27. package/src/components/table/markup/foundation/Body.tsx +24 -0
  28. package/src/components/table/markup/foundation/Cell.tsx +72 -0
  29. package/src/components/table/markup/foundation/Col.tsx +22 -0
  30. package/src/components/table/markup/foundation/Colgroup.tsx +29 -0
  31. package/src/components/table/markup/foundation/Foot.tsx +24 -0
  32. package/src/components/table/markup/foundation/Head.tsx +24 -0
  33. package/src/components/table/markup/foundation/Root.tsx +32 -0
  34. package/src/components/table/markup/foundation/Row.tsx +32 -0
  35. package/src/components/table/markup/foundation/Td.tsx +37 -0
  36. package/src/components/table/markup/foundation/Th.tsx +39 -0
  37. package/src/components/table/markup/foundation/index.tsx +30 -0
  38. package/src/components/table/markup/index.tsx +8 -2
  39. package/src/components/table/styles/foundation.scss +247 -0
  40. package/src/components/table/styles/index.scss +2 -0
  41. package/src/components/table/styles/variables.scss +29 -0
  42. package/src/components/table/types/foundation.ts +250 -0
  43. package/src/components/table/types/index.ts +1 -4
  44. package/src/components/tooltip/img/info.svg +5 -0
  45. package/src/components/tooltip/img/information.svg +9 -0
  46. package/src/components/tooltip/index.scss +1 -0
  47. package/src/components/tooltip/index.tsx +4 -0
  48. package/src/components/tooltip/markup/Message.tsx +70 -0
  49. package/src/components/tooltip/markup/Root.tsx +32 -0
  50. package/src/components/tooltip/markup/Template.tsx +46 -0
  51. package/src/components/tooltip/markup/Trigger.tsx +32 -0
  52. package/src/components/tooltip/markup/index.tsx +18 -0
  53. package/src/components/tooltip/styles/index.scss +2 -0
  54. package/src/components/tooltip/styles/tooltip.scss +47 -0
  55. package/src/components/tooltip/styles/variables.scss +14 -0
  56. package/src/components/tooltip/types/index.ts +1 -0
  57. package/src/components/tooltip/types/props.ts +118 -0
  58. package/src/index.scss +1 -0
  59. package/src/index.tsx +1 -0
@@ -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 아이콘 전체 컬렉션
@@ -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
  /**
@@ -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;
@@ -0,0 +1,32 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableRowProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Tr 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableRowProps} props
9
+ * @param {string} [props.className] tr className
10
+ * @param {React.ReactNode} [props.children] row 내부 콘텐츠
11
+ */
12
+ const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
13
+ ({ className, children, section, ...rowProps }, ref) => {
14
+ return (
15
+ <tr
16
+ {...rowProps}
17
+ ref={ref}
18
+ className={clsx(
19
+ "table-row",
20
+ section && `table-${section}-row`,
21
+ className,
22
+ )}
23
+ >
24
+ {children}
25
+ </tr>
26
+ );
27
+ },
28
+ );
29
+
30
+ TableRow.displayName = "TableFoundation.Row";
31
+
32
+ export default TableRow;
@@ -0,0 +1,37 @@
1
+ import clsx from "clsx";
2
+ import { forwardRef } from "react";
3
+ import type { TableTdProps } from "../../types";
4
+
5
+ /**
6
+ * Table Foundation; Td 마크업 컴포넌트
7
+ * @component
8
+ * @param {TableTdProps} props
9
+ * @param {string} [props.className] td className
10
+ * @param {React.ReactNode} [props.children] 셀 콘텐츠
11
+ */
12
+ const TableTd = forwardRef<HTMLTableCellElement, TableTdProps>(
13
+ ({ className, children, style, ...cellProps }, ref) => {
14
+ return (
15
+ <td
16
+ {...cellProps}
17
+ ref={ref}
18
+ className={clsx("table-native-cell", "table-td", className)}
19
+ // 변경: th/td 레벨 text-align은 기본 left 고정으로 유지한다.
20
+ style={{ ...style, textAlign: "left" }}
21
+ >
22
+ {/* 변경: 태그 셀렉터 의존을 피하기 위해 className 기반 텍스트 노드를 사용한다. */}
23
+ {typeof children === "string" || typeof children === "number" ? (
24
+ <span className={clsx("table-native-cell-text", "table-td-text")}>
25
+ {children}
26
+ </span>
27
+ ) : (
28
+ children
29
+ )}
30
+ </td>
31
+ );
32
+ },
33
+ );
34
+
35
+ TableTd.displayName = "TableFoundation.Td";
36
+
37
+ export default TableTd;