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/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, cascadeSelection) {
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, cascadeSelection)
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 = cascadeSelection ? collectSelectableIds(node) : [node.id];
7943
- const everySelected = getSelectionState(node, selectedSet, cascadeSelection).selected;
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, cascadeSelection);
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;