sellmate-design-system-react 9.0.0-beta.87 → 9.0.0-beta.89
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/AGENTS.md +26 -3
- package/dist/components/SGhostButton/README.md +2 -0
- package/dist/components/SRadioButton/README.md +1 -1
- package/dist/components/SRadioButton/SRadioButton.d.ts +1 -1
- package/dist/components/STable/README.md +7 -0
- package/dist/components/STable/STable.d.ts +20 -0
- package/dist/components/STree/README.md +0 -1
- package/dist/components/STree/STree.d.ts +0 -2
- package/dist/components/STreeTable/README.md +49 -0
- package/dist/components/STreeTable/STreeTable.d.ts +70 -0
- package/dist/components/STreeTable/STreeTable.examples.d.ts +32 -0
- package/dist/components/STreeTable/index.d.ts +1 -0
- package/dist/index.cjs +240 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +240 -13
- package/dist/index.js.map +1 -1
- package/dist/llms-full.txt +79 -5
- package/dist/llms.txt +28 -5
- package/dist/styles.css +9 -0
- package/dist/theme.css +6 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7596,10 +7596,7 @@ function toSelectedSet(value) {
|
|
|
7596
7596
|
return new Set(value ?? []);
|
|
7597
7597
|
}
|
|
7598
7598
|
var TREE_ALL_NODE = { id: "__all__", label: "\uC804\uCCB4" };
|
|
7599
|
-
function getSelectionState(node, selectedSet
|
|
7600
|
-
if (!cascadeSelection) {
|
|
7601
|
-
return { selected: selectedSet.has(node.id), indeterminate: false };
|
|
7602
|
-
}
|
|
7599
|
+
function getSelectionState(node, selectedSet) {
|
|
7603
7600
|
const selectableChildren = (node.children ?? []).filter(
|
|
7604
7601
|
(child) => collectSelectableIds(child).length > 0
|
|
7605
7602
|
);
|
|
@@ -7607,7 +7604,7 @@ function getSelectionState(node, selectedSet, cascadeSelection) {
|
|
|
7607
7604
|
return { selected: selectedSet.has(node.id), indeterminate: false };
|
|
7608
7605
|
}
|
|
7609
7606
|
const childStates = selectableChildren.map(
|
|
7610
|
-
(child) => getSelectionState(child, selectedSet
|
|
7607
|
+
(child) => getSelectionState(child, selectedSet)
|
|
7611
7608
|
);
|
|
7612
7609
|
const selectedCount = childStates.filter((state2) => state2.selected).length;
|
|
7613
7610
|
return {
|
|
@@ -7878,7 +7875,6 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
|
|
|
7878
7875
|
selectable = true,
|
|
7879
7876
|
size = "sm",
|
|
7880
7877
|
showSelectAll = false,
|
|
7881
|
-
cascadeSelection = true,
|
|
7882
7878
|
guideline = true,
|
|
7883
7879
|
border = true,
|
|
7884
7880
|
leading,
|
|
@@ -7939,8 +7935,8 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
|
|
|
7939
7935
|
};
|
|
7940
7936
|
const updateSelected = (node) => {
|
|
7941
7937
|
if (node.disabled) return;
|
|
7942
|
-
const targetIds =
|
|
7943
|
-
const everySelected = getSelectionState(node, selectedSet
|
|
7938
|
+
const targetIds = collectSelectableIds(node);
|
|
7939
|
+
const everySelected = getSelectionState(node, selectedSet).selected;
|
|
7944
7940
|
const next = new Set(selectedSet);
|
|
7945
7941
|
targetIds.forEach((id) => {
|
|
7946
7942
|
if (everySelected) next.delete(id);
|
|
@@ -8018,7 +8014,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
|
|
|
8018
8014
|
const renderNodes = (nodes, depth = 1, ancestorLast = []) => nodes.map((node, index) => {
|
|
8019
8015
|
const isLast = index === nodes.length - 1;
|
|
8020
8016
|
const isExpanded = expandedSet.has(node.id);
|
|
8021
|
-
const { selected, indeterminate } = getSelectionState(node, selectedSet
|
|
8017
|
+
const { selected, indeterminate } = getSelectionState(node, selectedSet);
|
|
8022
8018
|
return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
|
|
8023
8019
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8024
8020
|
STreeItem2,
|
|
@@ -16759,6 +16755,8 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
16759
16755
|
contextMenuItems,
|
|
16760
16756
|
onContextMenuItemClick,
|
|
16761
16757
|
isRowSelectable,
|
|
16758
|
+
isRowIndeterminate,
|
|
16759
|
+
rowClassName,
|
|
16762
16760
|
selected = [],
|
|
16763
16761
|
onSelectedChange,
|
|
16764
16762
|
sort = null,
|
|
@@ -17655,7 +17653,8 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17655
17653
|
"group/table-row",
|
|
17656
17654
|
rowHover && "hover:bg-[var(--color-grey-05)]",
|
|
17657
17655
|
(onRowClick || onRowDoubleClick) && "cursor-pointer",
|
|
17658
|
-
dragSelectOn && "select-none"
|
|
17656
|
+
dragSelectOn && "select-none",
|
|
17657
|
+
rowClassName?.(row)
|
|
17659
17658
|
),
|
|
17660
17659
|
style: { height: virtualScroll ? effectiveRowHeight : rowH },
|
|
17661
17660
|
children: [
|
|
@@ -17685,7 +17684,7 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17685
17684
|
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex select-none items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17686
17685
|
SCheckbox,
|
|
17687
17686
|
{
|
|
17688
|
-
value: isSel,
|
|
17687
|
+
value: isSel || !isRowIndeterminate?.(row) ? isSel : null,
|
|
17689
17688
|
disabled: !canSelectRow(row),
|
|
17690
17689
|
onValueChange: () => toggleRow(row, rowOffset + ri)
|
|
17691
17690
|
}
|
|
@@ -17935,6 +17934,233 @@ var STableBar = /* @__PURE__ */ react.forwardRef(function STableBar2({ title, to
|
|
|
17935
17934
|
}
|
|
17936
17935
|
);
|
|
17937
17936
|
});
|
|
17937
|
+
var NO_KEYS = /* @__PURE__ */ new Set();
|
|
17938
|
+
var ROW_ENTER_MS = 200;
|
|
17939
|
+
function STreeTable({
|
|
17940
|
+
rows = [],
|
|
17941
|
+
columns = [],
|
|
17942
|
+
rowKey = "id",
|
|
17943
|
+
childrenKey = "children",
|
|
17944
|
+
resizable = true,
|
|
17945
|
+
treeColumn,
|
|
17946
|
+
expandedKeys,
|
|
17947
|
+
defaultExpandedKeys,
|
|
17948
|
+
onExpandedChange,
|
|
17949
|
+
selectable = false,
|
|
17950
|
+
isRowSelectable,
|
|
17951
|
+
rowClassName,
|
|
17952
|
+
selected = [],
|
|
17953
|
+
onSelectedChange,
|
|
17954
|
+
...rest
|
|
17955
|
+
}) {
|
|
17956
|
+
const keyOf = (row) => row[rowKey];
|
|
17957
|
+
const childrenOf = (row) => {
|
|
17958
|
+
const children = row[childrenKey];
|
|
17959
|
+
return Array.isArray(children) ? children : [];
|
|
17960
|
+
};
|
|
17961
|
+
const canSelectRow = (row) => isRowSelectable ? isRowSelectable(row) : true;
|
|
17962
|
+
const entries4 = react.useMemo(() => {
|
|
17963
|
+
const map = /* @__PURE__ */ new Map();
|
|
17964
|
+
const walk = (nodes, depth, parentKey) => {
|
|
17965
|
+
nodes.forEach((row) => {
|
|
17966
|
+
const children = childrenOf(row);
|
|
17967
|
+
map.set(keyOf(row), { row, depth, children, parentKey });
|
|
17968
|
+
walk(children, depth + 1, keyOf(row));
|
|
17969
|
+
});
|
|
17970
|
+
};
|
|
17971
|
+
walk(rows, 0, null);
|
|
17972
|
+
return map;
|
|
17973
|
+
}, [rows, rowKey, childrenKey]);
|
|
17974
|
+
const [innerExpandedKeys, setInnerExpandedKeys] = react.useState(
|
|
17975
|
+
() => defaultExpandedKeys ?? rows.filter((row) => childrenOf(row).length > 0).map(keyOf)
|
|
17976
|
+
);
|
|
17977
|
+
const currentExpandedKeys = expandedKeys ?? innerExpandedKeys;
|
|
17978
|
+
const expandedSet = react.useMemo(() => new Set(currentExpandedKeys), [currentExpandedKeys]);
|
|
17979
|
+
const toggleExpanded = (row) => {
|
|
17980
|
+
const key = keyOf(row);
|
|
17981
|
+
const next = expandedSet.has(key) ? currentExpandedKeys.filter((k) => k !== key) : [...currentExpandedKeys, key];
|
|
17982
|
+
if (expandedKeys === void 0) setInnerExpandedKeys(next);
|
|
17983
|
+
onExpandedChange?.(next, row);
|
|
17984
|
+
};
|
|
17985
|
+
const visibleRows = react.useMemo(() => {
|
|
17986
|
+
const out = [];
|
|
17987
|
+
const walk = (nodes) => {
|
|
17988
|
+
nodes.forEach((row) => {
|
|
17989
|
+
out.push(row);
|
|
17990
|
+
if (expandedSet.has(keyOf(row))) walk(childrenOf(row));
|
|
17991
|
+
});
|
|
17992
|
+
};
|
|
17993
|
+
walk(rows);
|
|
17994
|
+
return out;
|
|
17995
|
+
}, [rows, expandedSet, rowKey, childrenKey]);
|
|
17996
|
+
const [enteringKeys, setEnteringKeys] = react.useState(NO_KEYS);
|
|
17997
|
+
const prevVisibleRef = react.useRef(null);
|
|
17998
|
+
useIsomorphicLayoutEffect(() => {
|
|
17999
|
+
const keys = new Set(visibleRows.map(keyOf));
|
|
18000
|
+
const prev = prevVisibleRef.current;
|
|
18001
|
+
prevVisibleRef.current = { rows, keys };
|
|
18002
|
+
if (!prev || prev.rows !== rows) return;
|
|
18003
|
+
const added = [...keys].filter((key) => !prev.keys.has(key));
|
|
18004
|
+
if (added.length > 0) setEnteringKeys(new Set(added));
|
|
18005
|
+
}, [visibleRows]);
|
|
18006
|
+
react.useEffect(() => {
|
|
18007
|
+
if (enteringKeys.size === 0) return;
|
|
18008
|
+
const timer = setTimeout(() => setEnteringKeys(NO_KEYS), ROW_ENTER_MS);
|
|
18009
|
+
return () => clearTimeout(timer);
|
|
18010
|
+
}, [enteringKeys]);
|
|
18011
|
+
const collectSelectable = (row) => canSelectRow(row) ? [row, ...childrenOf(row).flatMap(collectSelectable)] : [];
|
|
18012
|
+
const selectionOf = (keys) => {
|
|
18013
|
+
const state2 = /* @__PURE__ */ new Map();
|
|
18014
|
+
const visit = (row) => {
|
|
18015
|
+
const children = childrenOf(row);
|
|
18016
|
+
const childStates = children.map(visit);
|
|
18017
|
+
const counted = childStates.filter((_, i) => canSelectRow(children[i]));
|
|
18018
|
+
const result = counted.length === 0 ? { selected: keys.has(keyOf(row)), indeterminate: false } : {
|
|
18019
|
+
selected: counted.every((s) => s.selected),
|
|
18020
|
+
indeterminate: !counted.every((s) => s.selected) && counted.some((s) => s.selected || s.indeterminate)
|
|
18021
|
+
};
|
|
18022
|
+
state2.set(keyOf(row), result);
|
|
18023
|
+
return result;
|
|
18024
|
+
};
|
|
18025
|
+
rows.forEach(visit);
|
|
18026
|
+
return state2;
|
|
18027
|
+
};
|
|
18028
|
+
const selectionState = react.useMemo(
|
|
18029
|
+
() => selectable ? selectionOf(new Set(selected.map(keyOf))) : null,
|
|
18030
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18031
|
+
[selectable, selected, entries4, isRowSelectable]
|
|
18032
|
+
);
|
|
18033
|
+
const tableSelected = react.useMemo(() => {
|
|
18034
|
+
if (!selectionState) return selected;
|
|
18035
|
+
return [...entries4.values()].filter((entry) => selectionState.get(keyOf(entry.row))?.selected).map((entry) => entry.row);
|
|
18036
|
+
}, [selectionState, selected, entries4]);
|
|
18037
|
+
const handleSelectedChange = (next) => {
|
|
18038
|
+
const prevKeys = new Set(tableSelected.map(keyOf));
|
|
18039
|
+
const nextKeys = new Set(next.map(keyOf));
|
|
18040
|
+
const keys = new Set(prevKeys);
|
|
18041
|
+
prevKeys.forEach((key) => {
|
|
18042
|
+
if (nextKeys.has(key)) return;
|
|
18043
|
+
const entry = entries4.get(key);
|
|
18044
|
+
keys.delete(key);
|
|
18045
|
+
if (entry) collectSelectable(entry.row).forEach((row) => keys.delete(keyOf(row)));
|
|
18046
|
+
});
|
|
18047
|
+
nextKeys.forEach((key) => {
|
|
18048
|
+
if (prevKeys.has(key)) return;
|
|
18049
|
+
const entry = entries4.get(key);
|
|
18050
|
+
keys.add(key);
|
|
18051
|
+
if (entry) collectSelectable(entry.row).forEach((row) => keys.add(keyOf(row)));
|
|
18052
|
+
});
|
|
18053
|
+
const state2 = selectionOf(keys);
|
|
18054
|
+
onSelectedChange?.(
|
|
18055
|
+
[...entries4.values()].filter((entry) => state2.get(keyOf(entry.row))?.selected).map((entry) => entry.row)
|
|
18056
|
+
);
|
|
18057
|
+
};
|
|
18058
|
+
const treeColumnName = treeColumn ?? columns.find((col) => col.visible !== false)?.name;
|
|
18059
|
+
const tableColumns = react.useMemo(
|
|
18060
|
+
() => columns.map((col) => {
|
|
18061
|
+
if (col.name !== treeColumnName) return col;
|
|
18062
|
+
if (col.renderCell) {
|
|
18063
|
+
throw new Error(
|
|
18064
|
+
`[STreeTable] \uD2B8\uB9AC \uCEEC\uB7FC(${col.name})\uC5D0\uB294 renderCell \uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. render \uB97C \uC4F0\uC138\uC694.`
|
|
18065
|
+
);
|
|
18066
|
+
}
|
|
18067
|
+
return {
|
|
18068
|
+
...col,
|
|
18069
|
+
// 셀 전체를 펼침 영역으로 쓰려면 <td> 에 손을 대야 해서 render 가 아니라 renderCell 로 그린다.
|
|
18070
|
+
// 소비 앱의 render 는 그 안쪽 내용 자리에 그대로 들어간다.
|
|
18071
|
+
renderCell: ({ row, value, className, style }) => {
|
|
18072
|
+
const entry = entries4.get(keyOf(row));
|
|
18073
|
+
const hasChildren = (entry?.children.length ?? 0) > 0;
|
|
18074
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
18075
|
+
"td",
|
|
18076
|
+
{
|
|
18077
|
+
className: cn(className, hasChildren && "cursor-pointer"),
|
|
18078
|
+
style,
|
|
18079
|
+
onClick: hasChildren ? (event) => {
|
|
18080
|
+
if (isOwnControl(event.target, event.currentTarget)) return;
|
|
18081
|
+
event.stopPropagation();
|
|
18082
|
+
toggleExpanded(row);
|
|
18083
|
+
} : void 0,
|
|
18084
|
+
onDoubleClick: hasChildren ? (event) => event.stopPropagation() : void 0,
|
|
18085
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18086
|
+
TreeCell,
|
|
18087
|
+
{
|
|
18088
|
+
depth: entry?.depth ?? 0,
|
|
18089
|
+
hasChildren,
|
|
18090
|
+
expanded: expandedSet.has(keyOf(row)),
|
|
18091
|
+
children: col.render ? col.render(row, value) : value
|
|
18092
|
+
}
|
|
18093
|
+
)
|
|
18094
|
+
}
|
|
18095
|
+
);
|
|
18096
|
+
}
|
|
18097
|
+
};
|
|
18098
|
+
}),
|
|
18099
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18100
|
+
[columns, treeColumnName, entries4, expandedSet, currentExpandedKeys]
|
|
18101
|
+
);
|
|
18102
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
18103
|
+
STable2,
|
|
18104
|
+
{
|
|
18105
|
+
...rest,
|
|
18106
|
+
columns: tableColumns,
|
|
18107
|
+
rows: visibleRows,
|
|
18108
|
+
rowKey,
|
|
18109
|
+
resizable,
|
|
18110
|
+
selectable,
|
|
18111
|
+
isRowSelectable,
|
|
18112
|
+
isRowIndeterminate: selectionState ? (row) => !!selectionState.get(keyOf(row))?.indeterminate : void 0,
|
|
18113
|
+
rowClassName: (row) => cn(
|
|
18114
|
+
rowClassName?.(row),
|
|
18115
|
+
// 위에서 4px 내려오며 나타난다(theme.css --animate-table-row-in). 모션을 줄인 환경에서는 끈다
|
|
18116
|
+
enteringKeys.has(keyOf(row)) && "motion-safe:animate-table-row-in"
|
|
18117
|
+
) || void 0,
|
|
18118
|
+
selected: tableSelected,
|
|
18119
|
+
onSelectedChange: handleSelectedChange
|
|
18120
|
+
}
|
|
18121
|
+
);
|
|
18122
|
+
}
|
|
18123
|
+
var OWN_CONTROL_SELECTOR = 'a, button, input, select, textarea, label, [role="button"], [role="checkbox"], [contenteditable="true"]';
|
|
18124
|
+
function isOwnControl(target, cell) {
|
|
18125
|
+
if (!(target instanceof Element)) return false;
|
|
18126
|
+
if (target.closest("[data-tree-toggle]")) return false;
|
|
18127
|
+
const control = target.closest(OWN_CONTROL_SELECTOR);
|
|
18128
|
+
return control != null && cell.contains(control);
|
|
18129
|
+
}
|
|
18130
|
+
function TreeCell({
|
|
18131
|
+
depth,
|
|
18132
|
+
hasChildren,
|
|
18133
|
+
expanded,
|
|
18134
|
+
children
|
|
18135
|
+
}) {
|
|
18136
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18137
|
+
"div",
|
|
18138
|
+
{
|
|
18139
|
+
className: "flex min-w-0 items-center gap-(--cmp-tree-item-contentGap)",
|
|
18140
|
+
style: {
|
|
18141
|
+
paddingLeft: `calc(${depth} * (var(--cmp-ghostButton-xxs-size) + var(--cmp-tree-item-contentGap)))`
|
|
18142
|
+
},
|
|
18143
|
+
children: [
|
|
18144
|
+
hasChildren ? (
|
|
18145
|
+
// 누르면(키보드 Enter·Space 포함) 클릭이 셀로 올라가 셀이 접고 편다 — 버튼은 따로 처리하지 않는다
|
|
18146
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-tree-toggle": true, className: "flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18147
|
+
SGhostButton,
|
|
18148
|
+
{
|
|
18149
|
+
icon: expanded ? "caretDown" : "caretRight",
|
|
18150
|
+
size: "xxs",
|
|
18151
|
+
ariaLabel: expanded ? "collapse row" : "expand row",
|
|
18152
|
+
ariaExpanded: expanded
|
|
18153
|
+
}
|
|
18154
|
+
) })
|
|
18155
|
+
) : (
|
|
18156
|
+
// 하위 행이 없는 행도 버튼 자리를 비워 형제끼리 내용의 시작점을 맞춘다
|
|
18157
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-(--cmp-ghostButton-xxs-size) shrink-0", "aria-hidden": "true" })
|
|
18158
|
+
),
|
|
18159
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children })
|
|
18160
|
+
]
|
|
18161
|
+
}
|
|
18162
|
+
);
|
|
18163
|
+
}
|
|
17938
18164
|
var HIDE_DELAY_MS2 = 100;
|
|
17939
18165
|
function mergeRefs(...refs) {
|
|
17940
18166
|
return (node) => {
|
|
@@ -23012,7 +23238,8 @@ function SRadioButton({
|
|
|
23012
23238
|
].join(" "),
|
|
23013
23239
|
background: od ? "var(--cmp-radioButton-bg-disabled)" : ro ? "var(--cmp-radioButton-bg-readOnly)" : "var(--cmp-radioButton-bg-default)",
|
|
23014
23240
|
borderColor: od ? "var(--cmp-radioButton-border-disabled)" : selected ? ro ? "var(--cmp-radioButton-border-readOnly)" : "var(--cmp-radioButton-border-select)" : "var(--cmp-radioButton-border-default)",
|
|
23015
|
-
color: od || ro && !selected ? "var(--cmp-radioButton-content-disabled)" : selected && !ro ? "var(--cmp-radioButton-content-select)" : "var(--cmp-radioButton-content-default)"
|
|
23241
|
+
color: od || ro && !selected ? "var(--cmp-radioButton-content-disabled)" : selected && !ro ? "var(--cmp-radioButton-content-select)" : "var(--cmp-radioButton-content-default)",
|
|
23242
|
+
opacity: ro && !selected ? "var(--opacity-060)" : void 0
|
|
23016
23243
|
},
|
|
23017
23244
|
children: o.label
|
|
23018
23245
|
},
|
|
@@ -28783,6 +29010,7 @@ exports.SToggle = SToggle;
|
|
|
28783
29010
|
exports.STooltip = STooltip2;
|
|
28784
29011
|
exports.STree = STree2;
|
|
28785
29012
|
exports.STreeItem = STreeItem2;
|
|
29013
|
+
exports.STreeTable = STreeTable;
|
|
28786
29014
|
exports.SYSTEM_ACTION_BUTTON_COLORS = SYSTEM_ACTION_BUTTON_COLORS;
|
|
28787
29015
|
exports.SYSTEM_ACTION_BUTTON_COLOR_CONFIG = SYSTEM_ACTION_BUTTON_COLOR_CONFIG;
|
|
28788
29016
|
exports.SYSTEM_ACTION_BUTTON_LAYOUT = SYSTEM_ACTION_BUTTON_LAYOUT;
|