sellmate-design-system-react 2.1.2 → 3.0.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/dist/components/SCallout/SCallout.cjs +76 -1
- package/dist/components/SCallout/SCallout.cjs.map +1 -1
- package/dist/components/SCallout/SCallout.d.ts +6 -2
- package/dist/components/SCallout/SCallout.js +76 -1
- package/dist/components/SCallout/SCallout.js.map +1 -1
- package/dist/components/SDraggableItem/SDraggableItem.cjs +3079 -0
- package/dist/components/SDraggableItem/SDraggableItem.cjs.map +1 -0
- package/dist/components/SDraggableItem/SDraggableItem.d.ts +33 -0
- package/dist/components/SDraggableItem/SDraggableItem.js +3077 -0
- package/dist/components/SDraggableItem/SDraggableItem.js.map +1 -0
- package/dist/components/SDraggableItem/index.d.ts +1 -0
- package/dist/components/SExpansionItem/SExpansionItem.cjs +6 -15
- package/dist/components/SExpansionItem/SExpansionItem.cjs.map +1 -1
- package/dist/components/SExpansionItem/SExpansionItem.js +6 -15
- package/dist/components/SExpansionItem/SExpansionItem.js.map +1 -1
- package/dist/components/SList/SList.cjs +38 -0
- package/dist/components/SList/SList.cjs.map +1 -0
- package/dist/components/SList/SList.d.ts +10 -0
- package/dist/components/SList/SList.js +36 -0
- package/dist/components/SList/SList.js.map +1 -0
- package/dist/components/SList/index.d.ts +1 -0
- package/dist/components/SListItem/SListItem.cjs +83 -0
- package/dist/components/SListItem/SListItem.cjs.map +1 -0
- package/dist/components/SListItem/SListItem.d.ts +26 -0
- package/dist/components/SListItem/SListItem.js +81 -0
- package/dist/components/SListItem/SListItem.js.map +1 -0
- package/dist/components/SListItem/index.d.ts +1 -0
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.cjs +3414 -0
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.cjs.map +1 -0
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +32 -0
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.js +3392 -0
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.js.map +1 -0
- package/dist/components/SSectionHeaderCard/index.d.ts +1 -0
- package/dist/components/STable/STable.cjs +101 -80
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.d.ts +36 -1
- package/dist/components/STable/STable.js +102 -81
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STable/index.d.ts +1 -1
- package/dist/index.cjs +1040 -623
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1040 -627
- package/dist/index.js.map +1 -1
- package/dist/styles.css +339 -19
- package/dist/theme.css +14 -0
- package/package.json +1 -1
|
@@ -3,6 +3,23 @@ import { type SColor } from '../../lib/color';
|
|
|
3
3
|
import { type SIconName } from '../SIcon';
|
|
4
4
|
import { type SSelectOption } from '../SSelect';
|
|
5
5
|
export type SRow = Record<string, any>;
|
|
6
|
+
/** `STableColumn.renderCell`에 전달되는 컨텍스트 */
|
|
7
|
+
export interface STableCellContext {
|
|
8
|
+
row: SRow;
|
|
9
|
+
/** `field` + `format`이 적용된 표시 값 */
|
|
10
|
+
value: any;
|
|
11
|
+
column: STableColumn;
|
|
12
|
+
/** 보이는 컬럼 기준 인덱스 */
|
|
13
|
+
colIndex: number;
|
|
14
|
+
/** 현재 렌더 중인 행 목록 기준 인덱스 (페이지/가상 스크롤 윈도우 기준) */
|
|
15
|
+
rowIndex: number;
|
|
16
|
+
/** 행 선택 여부 */
|
|
17
|
+
isSelected: boolean;
|
|
18
|
+
/** STable이 이 셀에 적용하려던 클래스 (구분선·말줄임·sticky 그림자 포함) */
|
|
19
|
+
className: string;
|
|
20
|
+
/** STable이 이 셀에 적용하려던 스타일 (패딩·정렬·폭·sticky offset 포함) */
|
|
21
|
+
style: CSSProperties;
|
|
22
|
+
}
|
|
6
23
|
export interface STableColumn {
|
|
7
24
|
name: string;
|
|
8
25
|
label: string;
|
|
@@ -21,8 +38,19 @@ export interface STableColumn {
|
|
|
21
38
|
icon?: SIconName;
|
|
22
39
|
/** 헤더 아이콘 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 */
|
|
23
40
|
iconColor?: SColor;
|
|
24
|
-
/** 셀 커스텀 렌더 (React 확장) */
|
|
41
|
+
/** 셀 커스텀 렌더 (React 확장) — `<td>` 안쪽 내용만 교체한다 */
|
|
25
42
|
render?: (row: SRow, value: any) => ReactNode;
|
|
43
|
+
/**
|
|
44
|
+
* 셀(`<td>`) 자체를 커스텀 렌더 (React 확장). 반환한 엘리먼트가 그대로 `<td>`가 되므로
|
|
45
|
+
* `colSpan` / `rowSpan`을 직접 지정할 수 있다. `render`보다 우선한다.
|
|
46
|
+
*
|
|
47
|
+
* - `ctx.className` / `ctx.style`을 그대로 펼쳐야 구분선·정렬·sticky 등 기본 스타일이 유지된다.
|
|
48
|
+
* - `null`(또는 `false`)을 반환하면 그 자리에는 `<td>`를 만들지 않는다.
|
|
49
|
+
* 다른 셀의 `colSpan` / `rowSpan`에 덮이는 자리를 이렇게 비운다.
|
|
50
|
+
* - 병합은 sticky 컬럼(인덱스 기준 offset 계산)과 함께 쓰지 않는 것을 전제로 한다.
|
|
51
|
+
* `rowSpan`은 행을 잘라 쓰는 가상 스크롤 / 내부 페이지네이션과 경계에서 어긋날 수 있다.
|
|
52
|
+
*/
|
|
53
|
+
renderCell?: (ctx: STableCellContext) => ReactNode;
|
|
26
54
|
/** 헤더 셀 커스텀 렌더 (React 확장) */
|
|
27
55
|
renderHeader?: (column: STableColumn, index: number) => ReactNode;
|
|
28
56
|
/** 본문 셀 커스텀 클래스. 함수를 주면 행마다 다른 클래스를 반환할 수 있다 */
|
|
@@ -82,8 +110,15 @@ export interface STableProps {
|
|
|
82
110
|
/** border-radius 제어 */
|
|
83
111
|
radius?: 'default' | 'useTop' | 'full';
|
|
84
112
|
noDataLabel?: string;
|
|
113
|
+
/**
|
|
114
|
+
* 데이터가 없을 때 body 영역 전체를 대체하는 슬롯.
|
|
115
|
+
* 지정하면 `noDataLabel` 대신 이 콘텐츠가 헤더 아래 영역을 채우며, 버튼 등 인터랙션도 동작한다.
|
|
116
|
+
*/
|
|
117
|
+
noDataSlot?: ReactNode;
|
|
85
118
|
isLoading?: boolean;
|
|
86
119
|
dense?: boolean;
|
|
120
|
+
/** true면 행에 마우스를 올려도 hover 배경(grey_05)을 표시하지 않는다 */
|
|
121
|
+
noHover?: boolean;
|
|
87
122
|
/** 페이지네이션 (있으면 하단 표시) */
|
|
88
123
|
pagination?: STablePagination;
|
|
89
124
|
onPageChange?: (page: number) => void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { forwardRef, createContext, useState, useRef, useImperativeHandle, useMemo, useEffect, useId, useCallback, useContext } from 'react';
|
|
2
|
+
import { forwardRef, createContext, useState, useRef, useImperativeHandle, useMemo, useEffect, useId, useCallback, Fragment as Fragment$1, useContext } from 'react';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -4509,8 +4509,10 @@ var STable = forwardRef(function STable2({
|
|
|
4509
4509
|
stickyColumn,
|
|
4510
4510
|
radius = "default",
|
|
4511
4511
|
noDataLabel = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
4512
|
+
noDataSlot,
|
|
4512
4513
|
isLoading = false,
|
|
4513
4514
|
dense = false,
|
|
4515
|
+
noHover = false,
|
|
4514
4516
|
pagination,
|
|
4515
4517
|
onPageChange,
|
|
4516
4518
|
useInternalPagination = false,
|
|
@@ -4596,7 +4598,10 @@ var STable = forwardRef(function STable2({
|
|
|
4596
4598
|
);
|
|
4597
4599
|
const handleScroll = useCallback(() => {
|
|
4598
4600
|
const el = scrollRef.current;
|
|
4599
|
-
if (!el)
|
|
4601
|
+
if (!el) {
|
|
4602
|
+
setHeaderGutter(0);
|
|
4603
|
+
return;
|
|
4604
|
+
}
|
|
4600
4605
|
const header = headerScrollRef.current;
|
|
4601
4606
|
if (header) header.scrollLeft = el.scrollLeft;
|
|
4602
4607
|
setHeaderGutter(el.offsetWidth - el.clientWidth);
|
|
@@ -4728,6 +4733,7 @@ var STable = forwardRef(function STable2({
|
|
|
4728
4733
|
bodyRows = rows.slice(pageInfo.startIndex, pageInfo.endIndex);
|
|
4729
4734
|
}
|
|
4730
4735
|
const isNoData = rowCount === 0 && !isLoading;
|
|
4736
|
+
const showNoDataSlot = isNoData && noDataSlot != null;
|
|
4731
4737
|
const showPagination = !useVirtualScroll && (pagination != null || useInternalPagination);
|
|
4732
4738
|
const lastPage = useInternalPagination ? Math.max(1, Math.ceil(rowCount / (innerRowsPerPage || 1))) : pagination?.lastPage ?? Math.max(1, Math.ceil(rowCount / (pagination?.rowsPerPage ?? 10)));
|
|
4733
4739
|
const displayPage = useInternalPagination ? currentPage : pagination?.page ?? 1;
|
|
@@ -4789,6 +4795,12 @@ var STable = forwardRef(function STable2({
|
|
|
4789
4795
|
isRightEdge && scrolledRight && "shadow-[-5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
4790
4796
|
);
|
|
4791
4797
|
};
|
|
4798
|
+
const bodyScrollClass = cn(
|
|
4799
|
+
"min-h-0 flex-1 overflow-auto border-t border-solid border-[color:var(--cmp-table-border-color)]",
|
|
4800
|
+
SCROLLBAR_CLASS,
|
|
4801
|
+
SCROLLBAR_TRACK_BG_CLASS,
|
|
4802
|
+
SCROLLBAR_TRACK_BORDER_CLASS
|
|
4803
|
+
);
|
|
4792
4804
|
const colgroup = /* @__PURE__ */ jsxs("colgroup", { children: [
|
|
4793
4805
|
selectable && /* @__PURE__ */ jsx("col", { style: { width: SELECTABLE_COLUMN_WIDTH } }),
|
|
4794
4806
|
visibleCols.map((col, i) => /* @__PURE__ */ jsx("col", { style: col.autoWidth ? void 0 : { width: columnWidths[i] } }, col.name))
|
|
@@ -4910,97 +4922,106 @@ var STable = forwardRef(function STable2({
|
|
|
4910
4922
|
)
|
|
4911
4923
|
}
|
|
4912
4924
|
),
|
|
4913
|
-
/* @__PURE__ */ jsx(
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
className: cn(
|
|
4946
|
-
"group/table-row hover:bg-[var(--color-grey-05)]",
|
|
4947
|
-
onRowClick && "cursor-pointer"
|
|
4948
|
-
),
|
|
4949
|
-
style: { height: useVirtualScroll ? effectiveRowHeight : rowH },
|
|
4950
|
-
children: [
|
|
4951
|
-
selectable && /* @__PURE__ */ jsx(
|
|
4952
|
-
"td",
|
|
4953
|
-
{
|
|
4954
|
-
className: cn(
|
|
4955
|
-
// 원본 .td--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
4956
|
-
"sticky left-0 z-[2] border-b border-solid border-[color:var(--cmp-table-border-color)] align-middle group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
4957
|
-
rowBg,
|
|
4958
|
-
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
4959
|
-
),
|
|
4960
|
-
style: {
|
|
4961
|
-
width: SELECTABLE_COLUMN_WIDTH,
|
|
4962
|
-
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
4963
|
-
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
4964
|
-
},
|
|
4965
|
-
onClick: (e) => e.stopPropagation(),
|
|
4966
|
-
children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(SCheckbox, { value: isSel, onValueChange: () => toggleRow(row) }) })
|
|
4967
|
-
}
|
|
4925
|
+
showNoDataSlot ? /* @__PURE__ */ jsx("div", { className: cn(bodyScrollClass, "pointer-events-auto bg-white"), children: /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-[60px] w-full items-center justify-center", children: noDataSlot }) }) : (
|
|
4926
|
+
/* 바디: 세로 스크롤은 이 컨테이너에만 생긴다 (헤더 제외) */
|
|
4927
|
+
/* @__PURE__ */ jsx(
|
|
4928
|
+
"div",
|
|
4929
|
+
{
|
|
4930
|
+
ref: scrollRef,
|
|
4931
|
+
onScroll: handleScroll,
|
|
4932
|
+
className: cn(
|
|
4933
|
+
bodyScrollClass,
|
|
4934
|
+
// 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 스크롤 차단
|
|
4935
|
+
(isLoading || rowCount === 0) && "overflow-hidden"
|
|
4936
|
+
),
|
|
4937
|
+
children: /* @__PURE__ */ jsxs(
|
|
4938
|
+
"table",
|
|
4939
|
+
{
|
|
4940
|
+
className: "w-full table-fixed border-separate border-spacing-0 text-left",
|
|
4941
|
+
style: { fontSize: 12 },
|
|
4942
|
+
children: [
|
|
4943
|
+
colgroup,
|
|
4944
|
+
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
4945
|
+
useVirtualScroll && topSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: topSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) }),
|
|
4946
|
+
rowCount === 0 ? null : bodyRows.map((row, ri) => {
|
|
4947
|
+
const isSel = selectedKeys.has(row[rowKey]);
|
|
4948
|
+
const rowBg = "bg-white";
|
|
4949
|
+
return /* @__PURE__ */ jsxs(
|
|
4950
|
+
"tr",
|
|
4951
|
+
{
|
|
4952
|
+
onClick: () => onRowClick?.(row),
|
|
4953
|
+
className: cn(
|
|
4954
|
+
"group/table-row",
|
|
4955
|
+
!noHover && "hover:bg-[var(--color-grey-05)]",
|
|
4956
|
+
onRowClick && "cursor-pointer"
|
|
4968
4957
|
),
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4958
|
+
style: { height: useVirtualScroll ? effectiveRowHeight : rowH },
|
|
4959
|
+
children: [
|
|
4960
|
+
selectable && /* @__PURE__ */ jsx(
|
|
4961
|
+
"td",
|
|
4962
|
+
{
|
|
4963
|
+
className: cn(
|
|
4964
|
+
// 원본 .td--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
4965
|
+
"sticky left-0 z-[2] border-b border-solid border-[color:var(--cmp-table-border-color)] align-middle",
|
|
4966
|
+
rowBg,
|
|
4967
|
+
!noHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
4968
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
4969
|
+
),
|
|
4970
|
+
style: {
|
|
4971
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
4972
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
4973
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
4974
|
+
},
|
|
4975
|
+
onClick: (e) => e.stopPropagation(),
|
|
4976
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(SCheckbox, { value: isSel, onValueChange: () => toggleRow(row) }) })
|
|
4977
|
+
}
|
|
4978
|
+
),
|
|
4979
|
+
visibleCols.map((col, i) => {
|
|
4980
|
+
const tdClassName = cn(
|
|
4973
4981
|
// 원본 .td: 컬럼 폭에 맞춰 내용 말줄임(overflow hidden + ellipsis) + 행 구분선(border-b)
|
|
4974
4982
|
// 셀 배경은 흰색으로 채워 뒤 배경이 비치지 않게 하고, hover 시 행 배경(grey_05)을 따른다.
|
|
4975
4983
|
"overflow-hidden text-ellipsis whitespace-nowrap border-b border-solid border-[color:var(--cmp-table-border-color)] text-[12px] leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
4976
4984
|
rowBg,
|
|
4977
|
-
"group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
4985
|
+
!noHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
4978
4986
|
stickyShadowClass(i),
|
|
4979
4987
|
typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
|
|
4980
|
-
)
|
|
4981
|
-
|
|
4988
|
+
);
|
|
4989
|
+
const tdStyle = {
|
|
4982
4990
|
paddingLeft: cellPadX,
|
|
4983
4991
|
paddingRight: cellPadX,
|
|
4984
4992
|
paddingTop: cellPadY,
|
|
4985
4993
|
paddingBottom: cellPadY,
|
|
4986
4994
|
textAlign: col.align ?? "left",
|
|
4987
4995
|
...cellStyle(i)
|
|
4988
|
-
}
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
4996
|
+
};
|
|
4997
|
+
if (col.renderCell) {
|
|
4998
|
+
const cell = col.renderCell({
|
|
4999
|
+
row,
|
|
5000
|
+
value: cellValue(col, row),
|
|
5001
|
+
column: col,
|
|
5002
|
+
colIndex: i,
|
|
5003
|
+
rowIndex: ri,
|
|
5004
|
+
isSelected: isSel,
|
|
5005
|
+
className: tdClassName,
|
|
5006
|
+
style: tdStyle
|
|
5007
|
+
});
|
|
5008
|
+
if (cell == null || cell === false) return null;
|
|
5009
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: cell }, col.name);
|
|
5010
|
+
}
|
|
5011
|
+
return /* @__PURE__ */ jsx("td", { className: tdClassName, style: tdStyle, children: col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row) }, col.name);
|
|
5012
|
+
})
|
|
5013
|
+
]
|
|
5014
|
+
},
|
|
5015
|
+
row[rowKey] ?? ri
|
|
5016
|
+
);
|
|
5017
|
+
}),
|
|
5018
|
+
useVirtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
|
|
5019
|
+
] })
|
|
5020
|
+
]
|
|
5021
|
+
}
|
|
5022
|
+
)
|
|
5023
|
+
}
|
|
5024
|
+
)
|
|
5004
5025
|
),
|
|
5005
5026
|
isNoData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5006
5027
|
/* @__PURE__ */ jsx(
|
|
@@ -5010,7 +5031,7 @@ var STable = forwardRef(function STable2({
|
|
|
5010
5031
|
className: "pointer-events-none absolute left-0 right-0 top-0 z-[31] h-[var(--cmp-table-header-height)] bg-white/60"
|
|
5011
5032
|
}
|
|
5012
5033
|
),
|
|
5013
|
-
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 right-0 top-[var(--cmp-table-header-height)] z-[30] flex items-center justify-center bg-white/60 text-[12px] text-[color:var(--color-grey-65)]", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-[60px] w-full items-center justify-center", children: noDataLabel }) })
|
|
5034
|
+
!showNoDataSlot && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 right-0 top-[var(--cmp-table-header-height)] z-[30] flex items-center justify-center bg-white/60 text-[12px] text-[color:var(--color-grey-65)]", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-[60px] w-full items-center justify-center", children: noDataLabel }) })
|
|
5014
5035
|
] }),
|
|
5015
5036
|
isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-white/60", children: /* @__PURE__ */ jsx(SCircleProgress, { indeterminate: true }) })
|
|
5016
5037
|
]
|