sellmate-design-system-react 9.0.0-beta.25 → 9.0.0-beta.26
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/SChipFilter/README.md +5 -5
- package/dist/components/SChipFilter/SChipFilter.d.ts +10 -6
- package/dist/components/STable/README.md +16 -0
- package/dist/components/STable/STable.d.ts +12 -0
- package/dist/components/STable/index.d.ts +1 -1
- package/dist/index.cjs +272 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +272 -142
- package/dist/index.js.map +1 -1
- package/dist/llms-full.txt +21 -5
- package/dist/styles.css +4 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11506,6 +11506,7 @@ var colWidthFromDef = (c) => {
|
|
|
11506
11506
|
};
|
|
11507
11507
|
var STable = /* @__PURE__ */ react.forwardRef(function STable2({
|
|
11508
11508
|
columns = [],
|
|
11509
|
+
headerGroups,
|
|
11509
11510
|
rows = [],
|
|
11510
11511
|
rowKey = "id",
|
|
11511
11512
|
selectable = false,
|
|
@@ -11553,6 +11554,49 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
|
|
|
11553
11554
|
onDenseChange?.(next);
|
|
11554
11555
|
};
|
|
11555
11556
|
const visibleCols = react.useMemo(() => columns.filter((c) => c.visible !== false), [columns]);
|
|
11557
|
+
const headerGroupInfo = react.useMemo(() => {
|
|
11558
|
+
if (!headerGroups || headerGroups.length === 0) return null;
|
|
11559
|
+
const indexByName = new Map(visibleCols.map((col, index) => [col.name, index]));
|
|
11560
|
+
const covered = /* @__PURE__ */ new Set();
|
|
11561
|
+
let previousLast = -1;
|
|
11562
|
+
const cells = headerGroups.map((group) => {
|
|
11563
|
+
if (group.columnNames.length === 0) {
|
|
11564
|
+
throw new Error("[STable] headerGroups\uC758 columnNames\uB294 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
11565
|
+
}
|
|
11566
|
+
const indexes = group.columnNames.map((name) => {
|
|
11567
|
+
const index = indexByName.get(name);
|
|
11568
|
+
if (index == null) {
|
|
11569
|
+
throw new Error(`[STable] headerGroups\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uCEEC\uB7FC\uC774 \uC9C0\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${name}`);
|
|
11570
|
+
}
|
|
11571
|
+
if (covered.has(name)) {
|
|
11572
|
+
throw new Error(`[STable] headerGroups\uC5D0\uC11C \uCEEC\uB7FC\uC774 \uC911\uBCF5 \uC9C0\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${name}`);
|
|
11573
|
+
}
|
|
11574
|
+
covered.add(name);
|
|
11575
|
+
return index;
|
|
11576
|
+
});
|
|
11577
|
+
const first = indexes[0];
|
|
11578
|
+
const last = indexes[indexes.length - 1];
|
|
11579
|
+
if (last - first + 1 !== indexes.length || indexes.some((index, i) => index !== first + i)) {
|
|
11580
|
+
throw new Error(
|
|
11581
|
+
"[STable] headerGroups\uC758 columnNames\uB294 \uC778\uC811\uD55C \uCEEC\uB7FC \uC21C\uC11C\uB85C \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4."
|
|
11582
|
+
);
|
|
11583
|
+
}
|
|
11584
|
+
if (first <= previousLast) {
|
|
11585
|
+
throw new Error("[STable] headerGroups\uB294 \uCEEC\uB7FC \uC21C\uC11C\uB300\uB85C \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4.");
|
|
11586
|
+
}
|
|
11587
|
+
previousLast = last;
|
|
11588
|
+
return { ...group, firstIndex: first, colSpan: indexes.length };
|
|
11589
|
+
});
|
|
11590
|
+
if (covered.size !== visibleCols.length) {
|
|
11591
|
+
throw new Error("[STable] headerGroups\uC758 columnNames\uAC00 \uBAA8\uB4E0 visible \uCEEC\uB7FC\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4.");
|
|
11592
|
+
}
|
|
11593
|
+
return {
|
|
11594
|
+
cells,
|
|
11595
|
+
rowSpanNames: new Set(
|
|
11596
|
+
cells.filter((cell) => cell.rowSpan === 2).flatMap((cell) => cell.columnNames)
|
|
11597
|
+
)
|
|
11598
|
+
};
|
|
11599
|
+
}, [headerGroups, visibleCols]);
|
|
11556
11600
|
const stickyLeft = stickyColumn?.left ?? 0;
|
|
11557
11601
|
const stickyRight = stickyColumn?.right ?? 0;
|
|
11558
11602
|
const effectiveRowHeight = rowHeight ?? (innerDense ? DENSE_ROW_HEIGHT : DEFAULT_ROW_HEIGHT);
|
|
@@ -11815,6 +11859,35 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
|
|
|
11815
11859
|
isRightEdge && scrolledRight && "shadow-[-5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
11816
11860
|
);
|
|
11817
11861
|
};
|
|
11862
|
+
const headerResizeHitArea = (i, key) => {
|
|
11863
|
+
const col = visibleCols[i];
|
|
11864
|
+
if (!headerGroupInfo || !resizable || !col || col.autoWidth || i >= visibleCols.length - 1) {
|
|
11865
|
+
return null;
|
|
11866
|
+
}
|
|
11867
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11868
|
+
"span",
|
|
11869
|
+
{
|
|
11870
|
+
onMouseDown: (e) => startResize(i, e),
|
|
11871
|
+
className: "absolute inset-y-0 right-0 z-[5] w-[9px] cursor-col-resize",
|
|
11872
|
+
"aria-hidden": true
|
|
11873
|
+
},
|
|
11874
|
+
key
|
|
11875
|
+
);
|
|
11876
|
+
};
|
|
11877
|
+
const headerResizer = (i, key) => {
|
|
11878
|
+
const col = visibleCols[i];
|
|
11879
|
+
if (!resizable || !col || col.autoWidth || i >= visibleCols.length - 1) return null;
|
|
11880
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11881
|
+
"span",
|
|
11882
|
+
{
|
|
11883
|
+
onMouseDown: (e) => startResize(i, e),
|
|
11884
|
+
className: "absolute right-0 top-1/2 z-[5] flex h-[16px] w-[9px] -translate-y-1/2 cursor-col-resize items-center justify-end",
|
|
11885
|
+
"aria-hidden": true,
|
|
11886
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-full w-[4px] border-x border-solid border-[color:var(--cmp-table-header-resizingBar-color)]" })
|
|
11887
|
+
},
|
|
11888
|
+
key
|
|
11889
|
+
);
|
|
11890
|
+
};
|
|
11818
11891
|
const bodyScrollClass = cn(
|
|
11819
11892
|
"min-h-0 flex-1 overflow-auto border-t border-solid border-[color:var(--cmp-table-border-color)]",
|
|
11820
11893
|
SCROLLBAR_CLASS,
|
|
@@ -11879,145 +11952,179 @@ var STable = /* @__PURE__ */ react.forwardRef(function STable2({
|
|
|
11879
11952
|
style: { fontSize: 12 },
|
|
11880
11953
|
children: [
|
|
11881
11954
|
colgroup,
|
|
11882
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11955
|
+
/* @__PURE__ */ jsxRuntime.jsxs("thead", { className: cn(isNoData && "opacity-40"), children: [
|
|
11956
|
+
headerGroupInfo && /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
11957
|
+
selectable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11958
|
+
"th",
|
|
11959
|
+
{
|
|
11960
|
+
rowSpan: 2,
|
|
11961
|
+
className: cn(
|
|
11962
|
+
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
11963
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
11964
|
+
),
|
|
11965
|
+
style: {
|
|
11966
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
11967
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
11968
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
11969
|
+
},
|
|
11970
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SCheckbox, { value: headerCheck, onValueChange: toggleAll }) })
|
|
11971
|
+
}
|
|
11972
|
+
),
|
|
11973
|
+
headerGroupInfo.cells.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11974
|
+
"th",
|
|
11975
|
+
{
|
|
11976
|
+
colSpan: group.colSpan,
|
|
11977
|
+
rowSpan: group.rowSpan,
|
|
11978
|
+
className: cn(
|
|
11979
|
+
"relative border-b border-r border-solid border-[color:var(--cmp-table-border-color)] bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
11980
|
+
group.firstIndex + group.colSpan === visibleCols.length && "border-r-0",
|
|
11981
|
+
group.rowSpan === 2 && "border-b-0",
|
|
11982
|
+
group.className
|
|
11983
|
+
),
|
|
11984
|
+
style: { textAlign: group.align ?? "left", ...group.style },
|
|
11985
|
+
children: [
|
|
11986
|
+
group.render ? group.render() : group.label,
|
|
11987
|
+
group.rowSpan === 2 && group.colSpan === 1 ? headerResizeHitArea(
|
|
11988
|
+
group.firstIndex,
|
|
11989
|
+
`group-resizer-${group.columnNames[0]}`
|
|
11990
|
+
) : null
|
|
11991
|
+
]
|
|
11896
11992
|
},
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
"aria-hidden": true,
|
|
11903
|
-
className: "pointer-events-none absolute right-0 top-1/2 h-[16px] w-px -translate-y-1/2 bg-[var(--cmp-table-header-resizingBar-color)]"
|
|
11904
|
-
}
|
|
11905
|
-
)
|
|
11906
|
-
]
|
|
11907
|
-
}
|
|
11908
|
-
),
|
|
11909
|
-
visibleCols.map((col, i) => {
|
|
11910
|
-
const isSortable = !!col.sortable && !col.renderHeader;
|
|
11911
|
-
const sortDir = isSortable && sort?.name === col.name ? sort.dir : void 0;
|
|
11912
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11993
|
+
group.columnNames.join("|")
|
|
11994
|
+
))
|
|
11995
|
+
] }),
|
|
11996
|
+
/* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
11997
|
+
selectable && !headerGroupInfo && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11913
11998
|
"th",
|
|
11914
11999
|
{
|
|
11915
12000
|
className: cn(
|
|
11916
|
-
//
|
|
11917
|
-
//
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
// 세로 가운데도 어긋난다. 글자 줄높이는 아래 th__content 가 다시 준다.
|
|
11921
|
-
"relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[0] text-[color:var(--sys-color-fg-primary)]",
|
|
11922
|
-
stickyInfo(i).isSticky && "z-[6]",
|
|
11923
|
-
stickyShadowClass(i),
|
|
11924
|
-
col.thClass
|
|
12001
|
+
// 원본 .th--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
12002
|
+
// 하단 border는 헤더 래퍼가 연속으로 그리므로 셀에는 두지 않는다.
|
|
12003
|
+
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
12004
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
11925
12005
|
),
|
|
11926
|
-
style: {
|
|
11927
|
-
|
|
12006
|
+
style: {
|
|
12007
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
12008
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
12009
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
12010
|
+
},
|
|
11928
12011
|
children: [
|
|
12012
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SCheckbox, { value: headerCheck, onValueChange: toggleAll }) }),
|
|
11929
12013
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11930
12014
|
"span",
|
|
11931
12015
|
{
|
|
11932
|
-
className: cn(
|
|
11933
|
-
// 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
|
|
11934
|
-
// th 가 leading-[0] 이므로 글자 줄높이는 여기서 준다 (truncate 가
|
|
11935
|
-
// overflow-hidden 이라 줄높이가 0이면 라벨이 잘려 사라진다).
|
|
11936
|
-
"inline-flex w-full min-w-0 items-center gap-[4px] leading-[20px]",
|
|
11937
|
-
col.align === "center" && "justify-center",
|
|
11938
|
-
col.align === "right" && "justify-end"
|
|
11939
|
-
),
|
|
11940
|
-
children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11941
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: col.label }),
|
|
11942
|
-
col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
|
|
11943
|
-
// shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
|
|
11944
|
-
// 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
|
|
11945
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11946
|
-
STooltip2,
|
|
11947
|
-
{
|
|
11948
|
-
trigger: "hover",
|
|
11949
|
-
icon: "helpOutline",
|
|
11950
|
-
iconSize: 12,
|
|
11951
|
-
color: "var(--sys-color-fg-success)",
|
|
11952
|
-
placement: "top",
|
|
11953
|
-
message: col.helpText
|
|
11954
|
-
}
|
|
11955
|
-
) }),
|
|
11956
|
-
col.editable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11957
|
-
SIcon,
|
|
11958
|
-
{
|
|
11959
|
-
name: "pageEdit",
|
|
11960
|
-
size: 12,
|
|
11961
|
-
color: "var(--sys-color-link-accent)",
|
|
11962
|
-
label: "\uC218\uC815 \uAC00\uB2A5",
|
|
11963
|
-
className: "shrink-0"
|
|
11964
|
-
}
|
|
11965
|
-
),
|
|
11966
|
-
col.navigable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11967
|
-
SIcon,
|
|
11968
|
-
{
|
|
11969
|
-
name: "pageMove",
|
|
11970
|
-
size: 12,
|
|
11971
|
-
color: "var(--sys-color-link-accent)",
|
|
11972
|
-
label: "\uB2E4\uB978 \uD654\uBA74\uC73C\uB85C \uC774\uB3D9",
|
|
11973
|
-
className: "shrink-0"
|
|
11974
|
-
}
|
|
11975
|
-
),
|
|
11976
|
-
col.required && // 라벨이 truncate 돼도 필수 표시는 남아야 하므로 shrink-0.
|
|
11977
|
-
// 아이콘·크기·색은 SKeyValueTable 헤더 필수 표시와 동일하게 맞춘다.
|
|
11978
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11979
|
-
SIcon,
|
|
11980
|
-
{
|
|
11981
|
-
name: "asterisk",
|
|
11982
|
-
size: 12,
|
|
11983
|
-
color: "var(--sys-color-fg-accent)",
|
|
11984
|
-
className: "shrink-0"
|
|
11985
|
-
}
|
|
11986
|
-
),
|
|
11987
|
-
isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
|
|
11988
|
-
// 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
|
|
11989
|
-
// 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
|
|
11990
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11991
|
-
SGhostButton,
|
|
11992
|
-
{
|
|
11993
|
-
size: "xxs",
|
|
11994
|
-
intent: sortDir ? "action" : "default",
|
|
11995
|
-
icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
|
|
11996
|
-
ariaLabel: `${col.label} \uC815\uB82C`,
|
|
11997
|
-
onClick: () => toggleSort(col)
|
|
11998
|
-
}
|
|
11999
|
-
) })
|
|
12000
|
-
] })
|
|
12001
|
-
}
|
|
12002
|
-
),
|
|
12003
|
-
resizable && !col.autoWidth && i < visibleCols.length - 1 && // 원본 th__resizer: 셀 우측 경계 안쪽 4px 그립(좌우 1px #CCCCCC 라인, 세로 16px).
|
|
12004
|
-
// 셀 밖으로 넘치면 다음 셀에 가려져 클릭이 안 먹으므로 translate 없이 셀 내부에 배치.
|
|
12005
|
-
// 겉보기 그립은 4px(원본과 동일), 클릭 영역은 9px로 넓혀 사용성 확보.
|
|
12006
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12007
|
-
"span",
|
|
12008
|
-
{
|
|
12009
|
-
onMouseDown: (e) => startResize(i, e),
|
|
12010
|
-
className: "absolute right-0 top-1/2 z-[5] flex h-[16px] w-[9px] -translate-y-1/2 cursor-col-resize items-center justify-end",
|
|
12011
12016
|
"aria-hidden": true,
|
|
12012
|
-
|
|
12017
|
+
className: "pointer-events-none absolute right-0 top-1/2 h-[16px] w-px -translate-y-1/2 bg-[var(--cmp-table-header-resizingBar-color)]"
|
|
12013
12018
|
}
|
|
12014
12019
|
)
|
|
12015
12020
|
]
|
|
12016
|
-
}
|
|
12017
|
-
|
|
12018
|
-
)
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
+
}
|
|
12022
|
+
),
|
|
12023
|
+
visibleCols.map((col, i) => {
|
|
12024
|
+
if (headerGroupInfo?.rowSpanNames.has(col.name)) return null;
|
|
12025
|
+
const isSortable = !!col.sortable && !col.renderHeader;
|
|
12026
|
+
const sortDir = isSortable && sort?.name === col.name ? sort.dir : void 0;
|
|
12027
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12028
|
+
"th",
|
|
12029
|
+
{
|
|
12030
|
+
className: cn(
|
|
12031
|
+
// 하단 border는 헤더 래퍼가 연속으로 그린다 (거터 영역까지 이어짐).
|
|
12032
|
+
// leading-[0]: th 의 스트럿(line-height 만큼의 빈 인라인 박스)을 없앤다.
|
|
12033
|
+
// 내용이 inline-flex 라 스트럿이 남아 있으면 라벨보다 큰 컨트롤(정렬 버튼 등)이
|
|
12034
|
+
// 들어올 때 줄 상자가 베이스라인 기준으로 늘어나 헤더 높이를 넘기고
|
|
12035
|
+
// 세로 가운데도 어긋난다. 글자 줄높이는 아래 th__content 가 다시 준다.
|
|
12036
|
+
headerGroupInfo && "border-r border-solid border-[color:var(--cmp-table-border-color)]",
|
|
12037
|
+
headerGroupInfo && i === visibleCols.length - 1 && "border-r-0",
|
|
12038
|
+
"relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[0] text-[color:var(--sys-color-fg-primary)]",
|
|
12039
|
+
stickyInfo(i).isSticky && "z-[6]",
|
|
12040
|
+
stickyShadowClass(i),
|
|
12041
|
+
col.thClass
|
|
12042
|
+
),
|
|
12043
|
+
style: { textAlign: col.align ?? "left", ...cellStyle(i), ...col.thStyle },
|
|
12044
|
+
"aria-sort": isSortable ? sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : "none" : void 0,
|
|
12045
|
+
children: [
|
|
12046
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12047
|
+
"span",
|
|
12048
|
+
{
|
|
12049
|
+
className: cn(
|
|
12050
|
+
// 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
|
|
12051
|
+
// th 가 leading-[0] 이므로 글자 줄높이는 여기서 준다 (truncate 가
|
|
12052
|
+
// overflow-hidden 이라 줄높이가 0이면 라벨이 잘려 사라진다).
|
|
12053
|
+
"inline-flex w-full min-w-0 items-center gap-[4px] leading-[20px]",
|
|
12054
|
+
col.align === "center" && "justify-center",
|
|
12055
|
+
col.align === "right" && "justify-end"
|
|
12056
|
+
),
|
|
12057
|
+
children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
12058
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: col.label }),
|
|
12059
|
+
col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
|
|
12060
|
+
// shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
|
|
12061
|
+
// 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
|
|
12062
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12063
|
+
STooltip2,
|
|
12064
|
+
{
|
|
12065
|
+
trigger: "hover",
|
|
12066
|
+
icon: "helpOutline",
|
|
12067
|
+
iconSize: 12,
|
|
12068
|
+
color: "var(--sys-color-fg-success)",
|
|
12069
|
+
placement: "top",
|
|
12070
|
+
message: col.helpText
|
|
12071
|
+
}
|
|
12072
|
+
) }),
|
|
12073
|
+
col.editable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
12074
|
+
SIcon,
|
|
12075
|
+
{
|
|
12076
|
+
name: "pageEdit",
|
|
12077
|
+
size: 12,
|
|
12078
|
+
color: "var(--sys-color-link-accent)",
|
|
12079
|
+
label: "\uC218\uC815 \uAC00\uB2A5",
|
|
12080
|
+
className: "shrink-0"
|
|
12081
|
+
}
|
|
12082
|
+
),
|
|
12083
|
+
col.navigable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
12084
|
+
SIcon,
|
|
12085
|
+
{
|
|
12086
|
+
name: "pageMove",
|
|
12087
|
+
size: 12,
|
|
12088
|
+
color: "var(--sys-color-link-accent)",
|
|
12089
|
+
label: "\uB2E4\uB978 \uD654\uBA74\uC73C\uB85C \uC774\uB3D9",
|
|
12090
|
+
className: "shrink-0"
|
|
12091
|
+
}
|
|
12092
|
+
),
|
|
12093
|
+
col.required && // 라벨이 truncate 돼도 필수 표시는 남아야 하므로 shrink-0.
|
|
12094
|
+
// 아이콘·크기·색은 SKeyValueTable 헤더 필수 표시와 동일하게 맞춘다.
|
|
12095
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12096
|
+
SIcon,
|
|
12097
|
+
{
|
|
12098
|
+
name: "asterisk",
|
|
12099
|
+
size: 12,
|
|
12100
|
+
color: "var(--sys-color-fg-accent)",
|
|
12101
|
+
className: "shrink-0"
|
|
12102
|
+
}
|
|
12103
|
+
),
|
|
12104
|
+
isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
|
|
12105
|
+
// 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
|
|
12106
|
+
// 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
|
|
12107
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12108
|
+
SGhostButton,
|
|
12109
|
+
{
|
|
12110
|
+
size: "xxs",
|
|
12111
|
+
intent: sortDir ? "action" : "default",
|
|
12112
|
+
icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
|
|
12113
|
+
ariaLabel: `${col.label} \uC815\uB82C`,
|
|
12114
|
+
onClick: () => toggleSort(col)
|
|
12115
|
+
}
|
|
12116
|
+
) })
|
|
12117
|
+
] })
|
|
12118
|
+
}
|
|
12119
|
+
),
|
|
12120
|
+
headerGroupInfo ? headerResizeHitArea(i, `resizer-${col.name}`) : headerResizer(i, `resizer-${col.name}`)
|
|
12121
|
+
]
|
|
12122
|
+
},
|
|
12123
|
+
col.name
|
|
12124
|
+
);
|
|
12125
|
+
})
|
|
12126
|
+
] })
|
|
12127
|
+
] })
|
|
12021
12128
|
]
|
|
12022
12129
|
}
|
|
12023
12130
|
)
|
|
@@ -17380,7 +17487,8 @@ var resolveFieldDefault = (field) => {
|
|
|
17380
17487
|
if (field.type === "datepicker-day") return todayIso();
|
|
17381
17488
|
if (field.type === "select") return field.options?.[0]?.value;
|
|
17382
17489
|
if (field.type === "datepicker-statistics") {
|
|
17383
|
-
const
|
|
17490
|
+
const presets = getFieldPresets(field).filter((preset) => !preset.custom);
|
|
17491
|
+
const unit = presets.some((preset) => preset.value === "day") ? "day" : presets[0]?.value;
|
|
17384
17492
|
return unit ? createPeriodValue(unit) : void 0;
|
|
17385
17493
|
}
|
|
17386
17494
|
return void 0;
|
|
@@ -17510,8 +17618,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
|
|
|
17510
17618
|
onActiveKeysChange,
|
|
17511
17619
|
onReset,
|
|
17512
17620
|
onAddFilter,
|
|
17513
|
-
label
|
|
17514
|
-
showLabel = false,
|
|
17621
|
+
label,
|
|
17515
17622
|
showReset = true,
|
|
17516
17623
|
disabled = false,
|
|
17517
17624
|
className,
|
|
@@ -17528,6 +17635,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
|
|
|
17528
17635
|
const pendingOpenKeyRef = react.useRef(null);
|
|
17529
17636
|
const triggerRefs = react.useRef(/* @__PURE__ */ new Map());
|
|
17530
17637
|
const initializedDefaultsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
17638
|
+
const misuseWarningSignatureRef = react.useRef(null);
|
|
17531
17639
|
const prevOpenKeyRef = react.useRef(null);
|
|
17532
17640
|
const valuesRef = react.useRef(values);
|
|
17533
17641
|
valuesRef.current = values;
|
|
@@ -17543,14 +17651,19 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
|
|
|
17543
17651
|
(current) => groups.every((group) => isGroupSatisfied(group, current)),
|
|
17544
17652
|
[groups]
|
|
17545
17653
|
);
|
|
17654
|
+
const isRequiredSatisfied = react.useCallback(
|
|
17655
|
+
(current) => fields.every((field) => !field.required || !isEmptyValue(current[field.key])),
|
|
17656
|
+
[fields]
|
|
17657
|
+
);
|
|
17546
17658
|
const runSearch = react.useCallback(
|
|
17547
17659
|
(next) => {
|
|
17548
17660
|
if (valuesMapEqual(lastSearchedValuesRef.current, next)) return;
|
|
17549
17661
|
if (!isGroupsValid(next)) return;
|
|
17662
|
+
if (!isRequiredSatisfied(next)) return;
|
|
17550
17663
|
lastSearchedValuesRef.current = next;
|
|
17551
17664
|
onSearch?.(next);
|
|
17552
17665
|
},
|
|
17553
|
-
[isGroupsValid, onSearch]
|
|
17666
|
+
[isGroupsValid, isRequiredSatisfied, onSearch]
|
|
17554
17667
|
);
|
|
17555
17668
|
react.useEffect(() => {
|
|
17556
17669
|
const prevKey = prevOpenKeyRef.current;
|
|
@@ -17585,14 +17698,24 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
|
|
|
17585
17698
|
});
|
|
17586
17699
|
if (changed) onValueChange?.({ ...values, ...patch });
|
|
17587
17700
|
}, [fields, values, onValueChange]);
|
|
17588
|
-
|
|
17589
|
-
|
|
17590
|
-
|
|
17701
|
+
react.useEffect(() => {
|
|
17702
|
+
if (typeof console === "undefined") return;
|
|
17703
|
+
const misusedKeys = fields.filter((f) => f.defaultValue !== void 0 && !isFixedField(f)).map((f) => f.key);
|
|
17704
|
+
const requiredWithoutDefaultKeys = fields.filter((f) => f.required === true && resolveFieldDefault(f) === void 0).map((f) => f.key);
|
|
17705
|
+
const signature = JSON.stringify([misusedKeys, requiredWithoutDefaultKeys]);
|
|
17706
|
+
if (signature === misuseWarningSignatureRef.current) return;
|
|
17707
|
+
misuseWarningSignatureRef.current = signature;
|
|
17708
|
+
if (misusedKeys.length > 0) {
|
|
17591
17709
|
console.warn(
|
|
17592
|
-
`[SChipFilter] defaultValue \uB294 fixed\xB7required \uD544\uD130\uC5D0\uC11C\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uBB34\uC2DC\uD569\uB2C8\uB2E4. (${
|
|
17710
|
+
`[SChipFilter] defaultValue \uB294 fixed\xB7required \uD544\uD130\uC5D0\uC11C\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uBB34\uC2DC\uD569\uB2C8\uB2E4. (${misusedKeys.join(", ")})`
|
|
17593
17711
|
);
|
|
17594
17712
|
}
|
|
17595
|
-
|
|
17713
|
+
if (requiredWithoutDefaultKeys.length > 0) {
|
|
17714
|
+
console.warn(
|
|
17715
|
+
`[SChipFilter] required \uD544\uD130\uC5D0 defaultValue \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC774 \uD0C0\uC785\uC740 \uB0B4\uC7A5 \uAE30\uBCF8\uAC12\uC774 \uC5C6\uC5B4 "\uAC80\uC0C9 \uCD08\uAE30\uD654" \uC2DC \uAC12\uC774 \uBE44\uC6CC\uC9C0\uACE0 \uAC80\uC0C9\uB3C4 \uB9C9\uD799\uB2C8\uB2E4. defaultValue \uB97C \uC9C0\uC815\uD558\uC138\uC694. (${requiredWithoutDefaultKeys.join(", ")})`
|
|
17716
|
+
);
|
|
17717
|
+
}
|
|
17718
|
+
}, [fields]);
|
|
17596
17719
|
const fixedKeys = fields.filter(isFixedField).map((field) => field.key);
|
|
17597
17720
|
const resolvedActiveKeys = Array.from(
|
|
17598
17721
|
/* @__PURE__ */ new Set([...activeKeys ?? internalActiveKeys, ...fixedKeys])
|
|
@@ -17660,7 +17783,10 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
|
|
|
17660
17783
|
},
|
|
17661
17784
|
[fields]
|
|
17662
17785
|
);
|
|
17663
|
-
const validate = react.useCallback(
|
|
17786
|
+
const validate = react.useCallback(
|
|
17787
|
+
() => invalidGroups.size === 0 && isRequiredSatisfied(values),
|
|
17788
|
+
[invalidGroups, isRequiredSatisfied, values]
|
|
17789
|
+
);
|
|
17664
17790
|
react.useImperativeHandle(ref, () => ({ open, reset, validate }), [open, reset, validate]);
|
|
17665
17791
|
const addField = (key) => {
|
|
17666
17792
|
const next = [...resolvedActiveKeys, key];
|
|
@@ -17697,7 +17823,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
|
|
|
17697
17823
|
),
|
|
17698
17824
|
style: { ...rootStyle, borderColor: "var(--chipfilter-bar-border)" },
|
|
17699
17825
|
children: [
|
|
17700
|
-
|
|
17826
|
+
label && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17701
17827
|
/* @__PURE__ */ jsxRuntime.jsx(STag, { className: "shrink-0", shape: "square", size: "sm", color: "darkblue", label }),
|
|
17702
17828
|
/* @__PURE__ */ jsxRuntime.jsx(Divider, { top: true })
|
|
17703
17829
|
] }),
|
|
@@ -18407,6 +18533,10 @@ function InlineDatePresetFilter({
|
|
|
18407
18533
|
setSelectedId(preset.value);
|
|
18408
18534
|
onValueChange(preset.resolve ? preset.resolve() : null);
|
|
18409
18535
|
};
|
|
18536
|
+
const badgeBorder = disabled ? CHIPFILTER.chipDisabledBorder : warning ? CHIPFILTER.chipWarningBorder : CHIPFILTER.chipAppliedBorder;
|
|
18537
|
+
const badgeBackground = disabled ? CHIPFILTER.chipDisabledBg : CHIPFILTER.chipAppliedBg;
|
|
18538
|
+
const badgeLabelColor = disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipAppliedLabel;
|
|
18539
|
+
const badgeValueColor = disabled ? CHIPFILTER.chipDisabledText : CHIPFILTER.chipAppliedValue;
|
|
18410
18540
|
const badge = applied && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18411
18541
|
"span",
|
|
18412
18542
|
{
|
|
@@ -18414,17 +18544,17 @@ function InlineDatePresetFilter({
|
|
|
18414
18544
|
style: {
|
|
18415
18545
|
minHeight: CHIPFILTER.chipHeight,
|
|
18416
18546
|
borderRadius: CHIPFILTER.chipRadius,
|
|
18417
|
-
borderColor:
|
|
18418
|
-
background:
|
|
18419
|
-
color:
|
|
18420
|
-
cursor: rangeValue ? "pointer" : void 0
|
|
18547
|
+
borderColor: badgeBorder,
|
|
18548
|
+
background: badgeBackground,
|
|
18549
|
+
color: badgeLabelColor,
|
|
18550
|
+
cursor: rangeValue && !disabled ? "pointer" : void 0
|
|
18421
18551
|
},
|
|
18422
18552
|
children: [
|
|
18423
18553
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold", children: [
|
|
18424
18554
|
field.label,
|
|
18425
18555
|
":"
|
|
18426
18556
|
] }),
|
|
18427
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", style: { color:
|
|
18557
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", style: { color: badgeValueColor }, children: displayValue(field, value) }),
|
|
18428
18558
|
!disabled && showClear && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18429
18559
|
"span",
|
|
18430
18560
|
{
|