sellmate-design-system-react 9.0.0-beta.4 → 9.0.0-beta.6

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.
@@ -40,6 +40,7 @@
40
40
  | `expandedIds?` | `string[]` | — | 펼쳐진 노드 ID 목록 |
41
41
  | `defaultExpandedIds?` | `string[]` | — | 비제어 펼침 초기값 |
42
42
  | `selectable?` | `boolean` | `true` | 체크박스 표시 여부 |
43
+ | `useAll?` | `boolean` | `false` | 체크박스 사용 시 최상단 전체 선택 아이템 표시 여부 |
43
44
  | `guideline?` | `boolean` | `true` | depth 연결선 표시 여부 |
44
45
  | `border?` | `boolean` | `true` | 아이템 하단 border 표시 여부 |
45
46
  | `cascadeSelection?` | `boolean` | `true` | 부모 선택 시 하위 노드까지 함께 토글 |
@@ -61,6 +61,8 @@ export interface STreeProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childr
61
61
  defaultExpandedIds?: string[];
62
62
  /** 체크박스 표시 여부 */
63
63
  selectable?: boolean;
64
+ /** 체크박스 사용 시 최상단 전체 선택 아이템 표시 여부 */
65
+ useAll?: boolean;
64
66
  /** depth 연결선 표시 여부 */
65
67
  guideline?: boolean;
66
68
  /** 아이템 하단 border 표시 여부 */
package/dist/index.cjs CHANGED
@@ -4623,18 +4623,19 @@ function collectSelectableIds(node) {
4623
4623
  function toSelectedSet(value) {
4624
4624
  return new Set(value ?? []);
4625
4625
  }
4626
- function getSelectionState(node, selectedSet) {
4627
- if (selectedSet.has(node.id)) {
4628
- return { selected: true, indeterminate: false };
4626
+ var TREE_ALL_NODE = { id: "__all__", label: "\uC804\uCCB4" };
4627
+ function getSelectionState(node, selectedSet, cascadeSelection) {
4628
+ if (!cascadeSelection) {
4629
+ return { selected: selectedSet.has(node.id), indeterminate: false };
4629
4630
  }
4630
4631
  const selectableChildren = (node.children ?? []).filter(
4631
4632
  (child) => collectSelectableIds(child).length > 0
4632
4633
  );
4633
4634
  if (selectableChildren.length === 0) {
4634
- return { selected: false, indeterminate: false };
4635
+ return { selected: selectedSet.has(node.id), indeterminate: false };
4635
4636
  }
4636
4637
  const childStates = selectableChildren.map(
4637
- (child) => getSelectionState(child, selectedSet)
4638
+ (child) => getSelectionState(child, selectedSet, cascadeSelection)
4638
4639
  );
4639
4640
  const selectedCount = childStates.filter((state2) => state2.selected).length;
4640
4641
  return {
@@ -4679,6 +4680,40 @@ function TreeConnector({
4679
4680
  ] })
4680
4681
  ] });
4681
4682
  }
4683
+ function STreeAllItem({
4684
+ selected,
4685
+ indeterminate,
4686
+ border,
4687
+ onSelect
4688
+ }) {
4689
+ const checkboxValue = indeterminate ? null : selected;
4690
+ return /* @__PURE__ */ jsxRuntime.jsx(
4691
+ "div",
4692
+ {
4693
+ role: "treeitem",
4694
+ "aria-level": 1,
4695
+ "aria-selected": selected,
4696
+ onClick: onSelect,
4697
+ className: cn(
4698
+ "group/tree-item box-border flex h-[32px] w-full cursor-pointer items-center overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left hover:bg-(--cmp-tree-item-bg-hover)",
4699
+ border && "border-b border-(--cmp-tree-item-border-default)"
4700
+ ),
4701
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "box-border flex h-full min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4702
+ /* @__PURE__ */ jsxRuntime.jsx("span", { onClick: (event) => event.stopPropagation(), className: "flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(SCheckbox, { value: checkboxValue, onValueChange: () => onSelect() }) }),
4703
+ /* @__PURE__ */ jsxRuntime.jsx(
4704
+ "span",
4705
+ {
4706
+ className: cn(
4707
+ "block min-w-0 flex-1 truncate text-[14px] leading-[24px]",
4708
+ selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)"
4709
+ ),
4710
+ children: "\uC804\uCCB4"
4711
+ }
4712
+ )
4713
+ ] })
4714
+ }
4715
+ );
4716
+ }
4682
4717
  var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4683
4718
  node,
4684
4719
  depth = 1,
@@ -4729,7 +4764,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4729
4764
  "aria-disabled": node.disabled || void 0,
4730
4765
  onClick: handleRowClick,
4731
4766
  className: cn(
4732
- "group/tree-item flex min-h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4767
+ "group/tree-item box-border flex h-[32px] w-full items-center gap-(--cmp-tree-item-rowGap) overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left",
4733
4768
  border && "border-b border-(--cmp-tree-item-border-default)",
4734
4769
  !node.disabled && "hover:bg-(--cmp-tree-item-bg-hover)",
4735
4770
  node.disabled && "cursor-not-allowed opacity-60",
@@ -4748,7 +4783,7 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4748
4783
  ancestorLast
4749
4784
  }
4750
4785
  ),
4751
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "box-border flex h-full min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap) py-(--cmp-tree-item-paddingY)", children: [
4752
4787
  hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
4753
4788
  SGhostButton,
4754
4789
  {
@@ -4784,25 +4819,17 @@ var STreeItemImpl = /* @__PURE__ */ react.forwardRef(function STreeItem({
4784
4819
  /* @__PURE__ */ jsxRuntime.jsx(
4785
4820
  "span",
4786
4821
  {
4822
+ onClick: (event) => {
4823
+ if (!selectable) return;
4824
+ event.stopPropagation();
4825
+ if (!node.disabled && selectable) onTreeSelect?.(node, event);
4826
+ },
4787
4827
  className: cn(
4788
- "min-w-0 flex-1 text-[14px] leading-[24px]",
4789
- selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)"
4828
+ "block min-w-0 flex-1 truncate text-[14px] leading-[24px]",
4829
+ selected || indeterminate ? "font-bold text-(--cmp-tree-item-text-selected)" : "font-medium text-(--cmp-tree-item-text-default)",
4830
+ node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4790
4831
  ),
4791
- children: /* @__PURE__ */ jsxRuntime.jsx(
4792
- "span",
4793
- {
4794
- onClick: (event) => {
4795
- if (!selectable) return;
4796
- event.stopPropagation();
4797
- if (!node.disabled && selectable) onTreeSelect?.(node, event);
4798
- },
4799
- className: cn(
4800
- "inline-block max-w-full truncate",
4801
- node.disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
4802
- ),
4803
- children: node.label
4804
- }
4805
- )
4832
+ children: node.label
4806
4833
  }
4807
4834
  ),
4808
4835
  trailingNode
@@ -4819,6 +4846,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4819
4846
  expandedIds,
4820
4847
  defaultExpandedIds,
4821
4848
  selectable = true,
4849
+ useAll = false,
4822
4850
  cascadeSelection = true,
4823
4851
  guideline = true,
4824
4852
  border = true,
@@ -4837,6 +4865,11 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4837
4865
  const currentExpandedIds = expandedIds ?? internalExpandedIds;
4838
4866
  const selectedSet = react.useMemo(() => toSelectedSet(currentValue), [currentValue]);
4839
4867
  const expandedSet = react.useMemo(() => new Set(currentExpandedIds), [currentExpandedIds]);
4868
+ const selectableIds = react.useMemo(() => items.flatMap(collectSelectableIds), [items]);
4869
+ const allSelected = selectableIds.length > 0 && selectableIds.every((id) => selectedSet.has(id));
4870
+ const someSelected = selectableIds.some((id) => selectedSet.has(id));
4871
+ const allIndeterminate = someSelected && !allSelected;
4872
+ const showAllItem = selectable && useAll;
4840
4873
  const updateExpanded = (node) => {
4841
4874
  const next = new Set(currentExpandedIds);
4842
4875
  if (next.has(node.id)) next.delete(node.id);
@@ -4848,7 +4881,7 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4848
4881
  const updateSelected = (node) => {
4849
4882
  if (node.disabled) return;
4850
4883
  const targetIds = cascadeSelection ? collectSelectableIds(node) : [node.id];
4851
- const everySelected = getSelectionState(node, selectedSet).selected;
4884
+ const everySelected = getSelectionState(node, selectedSet, cascadeSelection).selected;
4852
4885
  const next = new Set(selectedSet);
4853
4886
  targetIds.forEach((id) => {
4854
4887
  if (everySelected) next.delete(id);
@@ -4858,10 +4891,22 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4858
4891
  if (value === void 0) setInternalValue(orderedIds);
4859
4892
  onValueChange?.(orderedIds, node);
4860
4893
  };
4894
+ const updateAllSelected = (event) => {
4895
+ event?.stopPropagation();
4896
+ if (selectableIds.length === 0) return;
4897
+ const next = new Set(selectedSet);
4898
+ selectableIds.forEach((id) => {
4899
+ if (allSelected) next.delete(id);
4900
+ else next.add(id);
4901
+ });
4902
+ const orderedIds = collectIds(items).filter((id) => next.has(id));
4903
+ if (value === void 0) setInternalValue(orderedIds);
4904
+ onValueChange?.(orderedIds, TREE_ALL_NODE);
4905
+ };
4861
4906
  const renderNodes = (nodes, depth = 1, ancestorLast = []) => nodes.map((node, index) => {
4862
4907
  const isLast = index === nodes.length - 1;
4863
4908
  const isExpanded = expandedSet.has(node.id);
4864
- const { selected, indeterminate } = getSelectionState(node, selectedSet);
4909
+ const { selected, indeterminate } = getSelectionState(node, selectedSet, cascadeSelection);
4865
4910
  return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
4866
4911
  /* @__PURE__ */ jsxRuntime.jsx(
4867
4912
  STreeItem2,
@@ -4895,14 +4940,25 @@ var STreeImpl = /* @__PURE__ */ react.forwardRef(function STree({
4895
4940
  ) : null
4896
4941
  ] }, node.id);
4897
4942
  });
4898
- return /* @__PURE__ */ jsxRuntime.jsx(
4943
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4899
4944
  "div",
4900
4945
  {
4901
4946
  ref,
4902
4947
  role: "tree",
4903
4948
  className: cn("flex w-full flex-col items-start bg-(--cmp-tree-item-bg-default)", className),
4904
4949
  ...rest,
4905
- children: renderNodes(items)
4950
+ children: [
4951
+ showAllItem && /* @__PURE__ */ jsxRuntime.jsx(
4952
+ STreeAllItem,
4953
+ {
4954
+ selected: allSelected,
4955
+ indeterminate: allIndeterminate,
4956
+ border,
4957
+ onSelect: updateAllSelected
4958
+ }
4959
+ ),
4960
+ renderNodes(items)
4961
+ ]
4906
4962
  }
4907
4963
  );
4908
4964
  });