sellmate-design-system-react 9.0.0-beta.94 → 9.0.0-beta.95
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 +347 -257
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +347 -257
- package/dist/index.js.map +1 -1
- package/dist/lib/tree-row.d.ts +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7702,6 +7702,13 @@ var TREE_LABEL_TYPOGRAPHY = {
|
|
|
7702
7702
|
rowHeight: "h-[32px]"
|
|
7703
7703
|
}
|
|
7704
7704
|
};
|
|
7705
|
+
var TREE_BRANCH_MOTION = { duration: 200, easing: "cubic-bezier(0, 0, 0.2, 1)" };
|
|
7706
|
+
function canAnimateTreeBranch() {
|
|
7707
|
+
if (typeof window === "undefined" || typeof HTMLElement.prototype.animate !== "function") {
|
|
7708
|
+
return false;
|
|
7709
|
+
}
|
|
7710
|
+
return !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
7711
|
+
}
|
|
7705
7712
|
var TREE_ROW_CLASS = "group/tree-item box-border flex w-full items-center gap-(--cmp-tree-item-rowGap) overflow-hidden bg-(--cmp-tree-item-bg-default) px-(--cmp-tree-item-paddingX) text-left hover:bg-(--cmp-tree-item-bg-hover)";
|
|
7706
7713
|
var TREE_ROW_BORDER_CLASS = "border-b-(length:--cmp-tree-item-border-width-default) border-(--cmp-tree-item-border-default)";
|
|
7707
7714
|
var TREE_ROW_ACTIVE_CLASS = "bg-(--cmp-tree-item-bg-hover)";
|
|
@@ -16098,6 +16105,21 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16098
16105
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
16099
16106
|
const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
|
|
16100
16107
|
const [searchCollapsed, setSearchCollapsed] = useState(NO_SEARCH_COLLAPSED);
|
|
16108
|
+
const [treeMotion, setTreeMotion] = useState(null);
|
|
16109
|
+
const treeMotionTimerRef = useRef(null);
|
|
16110
|
+
const pendingCollapseRef = useRef(null);
|
|
16111
|
+
const cancelTreeMotion = () => {
|
|
16112
|
+
if (treeMotionTimerRef.current != null) clearTimeout(treeMotionTimerRef.current);
|
|
16113
|
+
treeMotionTimerRef.current = null;
|
|
16114
|
+
pendingCollapseRef.current = null;
|
|
16115
|
+
setTreeMotion(null);
|
|
16116
|
+
};
|
|
16117
|
+
useEffect(
|
|
16118
|
+
() => () => {
|
|
16119
|
+
if (treeMotionTimerRef.current != null) clearTimeout(treeMotionTimerRef.current);
|
|
16120
|
+
},
|
|
16121
|
+
[]
|
|
16122
|
+
);
|
|
16101
16123
|
const baseId = useId();
|
|
16102
16124
|
const listboxId = `${baseId}-listbox`;
|
|
16103
16125
|
const activeOptionId = `${baseId}-active-option`;
|
|
@@ -16165,6 +16187,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16165
16187
|
const commitOpen = (next, valueForRules) => {
|
|
16166
16188
|
setOpen(next);
|
|
16167
16189
|
onOpenChange?.(next);
|
|
16190
|
+
cancelTreeMotion();
|
|
16168
16191
|
if (next) {
|
|
16169
16192
|
dismissedByPointerRef.current = false;
|
|
16170
16193
|
alignSelectedTopRef.current = true;
|
|
@@ -16235,6 +16258,10 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16235
16258
|
(groupValue) => treeSearching ? !searchCollapsed.has(groupValue) : expanded.has(groupValue),
|
|
16236
16259
|
[expanded, searchCollapsed, treeSearching]
|
|
16237
16260
|
);
|
|
16261
|
+
const isGroupShownExpanded = useCallback(
|
|
16262
|
+
(groupValue) => isGroupExpanded(groupValue) && !(treeMotion != null && !treeMotion.opening && treeMotion.group === groupValue),
|
|
16263
|
+
[isGroupExpanded, treeMotion]
|
|
16264
|
+
);
|
|
16238
16265
|
const treeRows = useMemo(
|
|
16239
16266
|
() => isTree ? visibleTreeRows(searched, (row) => isGroupExpanded(row.value)) : null,
|
|
16240
16267
|
[isGroupExpanded, isTree, searched]
|
|
@@ -16524,6 +16551,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16524
16551
|
const handleSearchChange = (next) => {
|
|
16525
16552
|
setSearch(next);
|
|
16526
16553
|
setSearchCollapsed(NO_SEARCH_COLLAPSED);
|
|
16554
|
+
cancelTreeMotion();
|
|
16527
16555
|
};
|
|
16528
16556
|
const handleToggleExpand = (groupValue) => {
|
|
16529
16557
|
rangeAnchorRef.current = null;
|
|
@@ -16533,8 +16561,41 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16533
16561
|
else next.add(groupValue);
|
|
16534
16562
|
return next;
|
|
16535
16563
|
};
|
|
16536
|
-
|
|
16537
|
-
|
|
16564
|
+
const applyToggle = treeSearching ? () => setSearchCollapsed(toggle) : () => setExpanded(toggle);
|
|
16565
|
+
if (treeMotionTimerRef.current != null) clearTimeout(treeMotionTimerRef.current);
|
|
16566
|
+
treeMotionTimerRef.current = null;
|
|
16567
|
+
const pendingCollapse = pendingCollapseRef.current;
|
|
16568
|
+
pendingCollapseRef.current = null;
|
|
16569
|
+
if (treeMotion?.group === groupValue && !treeMotion.opening) {
|
|
16570
|
+
setTreeMotion({ group: groupValue, opening: true });
|
|
16571
|
+
treeMotionTimerRef.current = setTimeout(
|
|
16572
|
+
() => setTreeMotion(null),
|
|
16573
|
+
TREE_BRANCH_MOTION.duration
|
|
16574
|
+
);
|
|
16575
|
+
return;
|
|
16576
|
+
}
|
|
16577
|
+
pendingCollapse?.();
|
|
16578
|
+
if (!canAnimateTreeBranch()) {
|
|
16579
|
+
setTreeMotion(null);
|
|
16580
|
+
applyToggle();
|
|
16581
|
+
return;
|
|
16582
|
+
}
|
|
16583
|
+
if (isGroupExpanded(groupValue)) {
|
|
16584
|
+
setTreeMotion({ group: groupValue, opening: false });
|
|
16585
|
+
pendingCollapseRef.current = applyToggle;
|
|
16586
|
+
treeMotionTimerRef.current = setTimeout(() => {
|
|
16587
|
+
pendingCollapseRef.current = null;
|
|
16588
|
+
setTreeMotion(null);
|
|
16589
|
+
applyToggle();
|
|
16590
|
+
}, TREE_BRANCH_MOTION.duration);
|
|
16591
|
+
} else {
|
|
16592
|
+
applyToggle();
|
|
16593
|
+
setTreeMotion({ group: groupValue, opening: true });
|
|
16594
|
+
treeMotionTimerRef.current = setTimeout(
|
|
16595
|
+
() => setTreeMotion(null),
|
|
16596
|
+
TREE_BRANCH_MOTION.duration
|
|
16597
|
+
);
|
|
16598
|
+
}
|
|
16538
16599
|
};
|
|
16539
16600
|
const handleToggleGroup = (leafValues) => {
|
|
16540
16601
|
if (leafValues.length === 0) return;
|
|
@@ -16644,7 +16705,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16644
16705
|
const row = rowIndex != null ? filtered[rowIndex] : void 0;
|
|
16645
16706
|
if (rowIndex == null || row == null) return;
|
|
16646
16707
|
event.preventDefault();
|
|
16647
|
-
const rowExpanded = row.isGroup &&
|
|
16708
|
+
const rowExpanded = row.isGroup && isGroupShownExpanded(row.value);
|
|
16648
16709
|
if (event.key === "ArrowRight") {
|
|
16649
16710
|
if (!row.isGroup) return;
|
|
16650
16711
|
if (!rowExpanded) {
|
|
@@ -16889,8 +16950,9 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16889
16950
|
setSize: isDepth || isTree ? void 0 : rowCount,
|
|
16890
16951
|
multi,
|
|
16891
16952
|
tree: isTree,
|
|
16892
|
-
isExpanded:
|
|
16953
|
+
isExpanded: isGroupShownExpanded,
|
|
16893
16954
|
onToggleExpand: handleToggleExpand,
|
|
16955
|
+
treeMotion,
|
|
16894
16956
|
treeLines,
|
|
16895
16957
|
selected,
|
|
16896
16958
|
groupLeafValues,
|
|
@@ -16947,6 +17009,7 @@ function ItemList({
|
|
|
16947
17009
|
tree,
|
|
16948
17010
|
isExpanded,
|
|
16949
17011
|
onToggleExpand,
|
|
17012
|
+
treeMotion,
|
|
16950
17013
|
treeLines,
|
|
16951
17014
|
selected,
|
|
16952
17015
|
groupLeafValues,
|
|
@@ -16967,295 +17030,322 @@ function ItemList({
|
|
|
16967
17030
|
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listBoxItem-content-disabled)]"
|
|
16968
17031
|
);
|
|
16969
17032
|
const activeClass = "hover:bg-[var(--cmp-listBoxItem-bg-hover)] hover:text-[color:var(--cmp-listBoxItem-content-hover)] focus-visible:bg-[var(--cmp-listBoxItem-bg-hover)] focus-visible:text-[color:var(--cmp-listBoxItem-content-hover)]";
|
|
16970
|
-
|
|
16971
|
-
|
|
16972
|
-
|
|
16973
|
-
|
|
16974
|
-
|
|
16975
|
-
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
onToggle: onSelectAll
|
|
16988
|
-
}
|
|
16989
|
-
)
|
|
16990
|
-
) : /* @__PURE__ */ jsxs(
|
|
16991
|
-
"button",
|
|
17033
|
+
const wrapTreeMotion = (nodes) => {
|
|
17034
|
+
if (!tree || treeMotion == null) return nodes;
|
|
17035
|
+
const start = items.findIndex((o) => o.isGroup && o.value === treeMotion.group);
|
|
17036
|
+
if (start < 0) return nodes;
|
|
17037
|
+
let end = start + 1;
|
|
17038
|
+
while (end < items.length && items[end].depth > items[start].depth) end++;
|
|
17039
|
+
if (end === start + 1) return nodes;
|
|
17040
|
+
return [
|
|
17041
|
+
...nodes.slice(0, start + 1),
|
|
17042
|
+
/* @__PURE__ */ jsx(TreeRowsMotion, { opening: treeMotion.opening, children: nodes.slice(start + 1, end) }, `__motion_${treeMotion.group}__`),
|
|
17043
|
+
...nodes.slice(end)
|
|
17044
|
+
];
|
|
17045
|
+
};
|
|
17046
|
+
const selectAllRow = showSelectAll && (tree ? (
|
|
17047
|
+
// 트리의 전체선택 줄은 STree 의 "전체" 줄 그대로다 (lib/tree-row).
|
|
17048
|
+
/* @__PURE__ */ jsx(
|
|
17049
|
+
TreeAllRow,
|
|
16992
17050
|
{
|
|
16993
|
-
type: "button",
|
|
16994
17051
|
"data-select-all-row": true,
|
|
16995
17052
|
id: focusedValue === "__select_all__" ? activeOptionId : void 0,
|
|
16996
17053
|
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
16997
17054
|
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17055
|
+
tabIndex: -1,
|
|
17056
|
+
label: selectAllLabel,
|
|
17057
|
+
selected: selectAllSelected,
|
|
17058
|
+
active: focusedValue === "__select_all__",
|
|
17059
|
+
className: "select-none",
|
|
17003
17060
|
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
17004
17061
|
onMouseLeave: () => onHoverValue(null),
|
|
17005
|
-
|
|
17006
|
-
children: [
|
|
17007
|
-
/* @__PURE__ */ jsx(CheckboxBox, { state: selectAllSelected, focused: focusedValue === "__select_all__" }),
|
|
17008
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
17009
|
-
]
|
|
17062
|
+
onToggle: onSelectAll
|
|
17010
17063
|
}
|
|
17011
|
-
)
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
|
|
17079
|
-
|
|
17080
|
-
|
|
17081
|
-
|
|
17082
|
-
|
|
17083
|
-
|
|
17084
|
-
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
|
|
17088
|
-
|
|
17089
|
-
|
|
17090
|
-
|
|
17091
|
-
|
|
17092
|
-
selectable: true,
|
|
17093
|
-
disabled: rowDisabled,
|
|
17094
|
-
control: multi && /* @__PURE__ */ jsx(
|
|
17095
|
-
"span",
|
|
17096
|
-
{
|
|
17097
|
-
"data-tree-control": true,
|
|
17098
|
-
className: "flex shrink-0",
|
|
17099
|
-
onPointerDownCapture: (event) => {
|
|
17100
|
-
checkboxShiftRef.current = event.shiftKey;
|
|
17101
|
-
},
|
|
17102
|
-
children: /* @__PURE__ */ jsx(
|
|
17103
|
-
SCheckbox,
|
|
17104
|
-
{
|
|
17105
|
-
value: checkState,
|
|
17106
|
-
disabled: rowDisabled,
|
|
17107
|
-
onValueChange: toggle
|
|
17108
|
-
}
|
|
17109
|
-
)
|
|
17110
|
-
}
|
|
17111
|
-
),
|
|
17112
|
-
children: /* @__PURE__ */ jsx(
|
|
17113
|
-
OptionLabel,
|
|
17114
|
-
{
|
|
17115
|
-
label: o.label,
|
|
17116
|
-
className: treeLabelClass("sm", {
|
|
17117
|
-
// 고른 줄은 굵게 — multi_tree 는 일부만 고른 그룹도 굵다 (STree 와 같다)
|
|
17118
|
-
emphasized: checkState !== false,
|
|
17119
|
-
disabled: rowDisabled,
|
|
17120
|
-
selectable: true
|
|
17121
|
-
})
|
|
17122
|
-
}
|
|
17123
|
-
)
|
|
17124
|
-
}
|
|
17125
|
-
)
|
|
17126
|
-
] })
|
|
17127
|
-
]
|
|
17064
|
+
)
|
|
17065
|
+
) : /* @__PURE__ */ jsxs(
|
|
17066
|
+
"button",
|
|
17067
|
+
{
|
|
17068
|
+
type: "button",
|
|
17069
|
+
"data-select-all-row": true,
|
|
17070
|
+
id: focusedValue === "__select_all__" ? activeOptionId : void 0,
|
|
17071
|
+
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
17072
|
+
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
17073
|
+
className: cn(
|
|
17074
|
+
itemBase,
|
|
17075
|
+
focusedValue === "__select_all__" && "bg-[var(--cmp-listBoxItem-bg-hover)] text-[color:var(--cmp-listBoxItem-content-hover)]"
|
|
17076
|
+
),
|
|
17077
|
+
style: { ...LIST_BOX_ITEM_PADDING_Y_RIGHT, paddingLeft: LIST_BOX_ITEM_PADDING_LEFT[1] },
|
|
17078
|
+
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
17079
|
+
onMouseLeave: () => onHoverValue(null),
|
|
17080
|
+
onClick: onSelectAll,
|
|
17081
|
+
children: [
|
|
17082
|
+
/* @__PURE__ */ jsx(CheckboxBox, { state: selectAllSelected, focused: focusedValue === "__select_all__" }),
|
|
17083
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
17084
|
+
]
|
|
17085
|
+
}
|
|
17086
|
+
));
|
|
17087
|
+
const rowNodes = items.map((o, windowIndex) => {
|
|
17088
|
+
const idx = windowIndex + indexOffset;
|
|
17089
|
+
const padLeft = LIST_BOX_ITEM_PADDING_LEFT[o.depth === 1 ? 1 : o.depth === 2 ? 2 : 3];
|
|
17090
|
+
const rowKey = `${o.value}-${idx}`;
|
|
17091
|
+
if (tree) {
|
|
17092
|
+
const togglesLeaves = multi && o.isGroup;
|
|
17093
|
+
const leafValues = togglesLeaves ? groupLeafValues.get(idx) ?? [] : [];
|
|
17094
|
+
const total = leafValues.length;
|
|
17095
|
+
const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
|
|
17096
|
+
const isSelected2 = selectedSet.has(o.value);
|
|
17097
|
+
const checkState = togglesLeaves ? total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null : isSelected2;
|
|
17098
|
+
const rowDisabled = o.disabled || togglesLeaves && total === 0;
|
|
17099
|
+
const focusKey = togglesLeaves ? `__group_${idx}__` : o.value;
|
|
17100
|
+
const focused = focusedValue === focusKey;
|
|
17101
|
+
const expanded = o.isGroup && isExpanded(o.value);
|
|
17102
|
+
const line = treeLines[idx];
|
|
17103
|
+
const toggle = () => {
|
|
17104
|
+
if (rowDisabled) return;
|
|
17105
|
+
if (togglesLeaves) onToggleGroup(leafValues);
|
|
17106
|
+
else onSelect(o.value, idx, checkboxShiftRef.current);
|
|
17107
|
+
};
|
|
17108
|
+
return (
|
|
17109
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events -- 키보드는 트리거(또는 검색 입력)가 aria-activedescendant 로 맡는다. 줄로 포커스를 옮기지 않는다
|
|
17110
|
+
/* @__PURE__ */ jsxs(
|
|
17111
|
+
"div",
|
|
17112
|
+
{
|
|
17113
|
+
tabIndex: -1,
|
|
17114
|
+
"data-select-row": true,
|
|
17115
|
+
"data-index": idx,
|
|
17116
|
+
id: focused ? activeOptionId : void 0,
|
|
17117
|
+
role: "treeitem",
|
|
17118
|
+
"aria-level": o.depth,
|
|
17119
|
+
"aria-posinset": line?.posInSet,
|
|
17120
|
+
"aria-setsize": line?.setSize,
|
|
17121
|
+
"aria-expanded": o.isGroup ? expanded : void 0,
|
|
17122
|
+
"aria-selected": checkState === true,
|
|
17123
|
+
"aria-disabled": rowDisabled || void 0,
|
|
17124
|
+
"data-select-focused": focused ? "true" : void 0,
|
|
17125
|
+
"data-select-active": focused ? "true" : void 0,
|
|
17126
|
+
className: cn(
|
|
17127
|
+
TREE_ROW_CLASS,
|
|
17128
|
+
TREE_LABEL_TYPOGRAPHY.sm.rowHeight,
|
|
17129
|
+
TREE_ROW_BORDER_CLASS,
|
|
17130
|
+
"select-none",
|
|
17131
|
+
o.isGroup && "cursor-pointer",
|
|
17132
|
+
focused && TREE_ROW_ACTIVE_CLASS
|
|
17133
|
+
),
|
|
17134
|
+
onMouseEnter: () => onHoverValue(focusKey),
|
|
17135
|
+
onMouseLeave: () => onHoverValue(null),
|
|
17136
|
+
onClick: (event) => {
|
|
17137
|
+
if (isRowControlClick(event)) return;
|
|
17138
|
+
if (isRowSelectClick(event)) {
|
|
17139
|
+
if (rowDisabled) return;
|
|
17140
|
+
if (togglesLeaves) onToggleGroup(leafValues);
|
|
17141
|
+
else onSelect(o.value, idx, event.shiftKey);
|
|
17142
|
+
return;
|
|
17143
|
+
}
|
|
17144
|
+
if (o.isGroup) onToggleExpand(o.value);
|
|
17128
17145
|
},
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
if (multi) {
|
|
17141
|
-
const leafValues = groupLeafValues.get(idx) ?? [];
|
|
17142
|
-
const total = leafValues.length;
|
|
17143
|
-
const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
|
|
17144
|
-
const groupState = total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null;
|
|
17145
|
-
const groupDisabled = o.disabled || total === 0;
|
|
17146
|
-
const groupFocused = focusedValue === `__group_${idx}__`;
|
|
17147
|
-
return /* @__PURE__ */ jsxs(
|
|
17148
|
-
"button",
|
|
17149
|
-
{
|
|
17150
|
-
type: "button",
|
|
17151
|
-
"data-select-row": true,
|
|
17152
|
-
"data-index": idx,
|
|
17153
|
-
id: groupFocused ? activeOptionId : void 0,
|
|
17154
|
-
role: "option",
|
|
17155
|
-
"aria-selected": groupState === true,
|
|
17156
|
-
disabled: groupDisabled,
|
|
17157
|
-
"data-select-focused": groupFocused ? "true" : void 0,
|
|
17158
|
-
"data-select-active": groupFocused ? "true" : void 0,
|
|
17159
|
-
className: cn(
|
|
17160
|
-
"flex w-full select-none items-center gap-(--cmp-listBoxItem-gap) border-none py-(--cmp-listBoxItem-paddingY) pr-(--cmp-listBoxItem-paddingRight) text-left typo-field-sm-default text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
17161
|
-
groupTone,
|
|
17162
|
-
groupDisabled ? "cursor-not-allowed text-[color:var(--cmp-listBoxItem-content-disabled)]" : "cursor-pointer",
|
|
17163
|
-
groupDisabled && isDepth1 && "bg-[var(--cmp-listBoxItem-depth1-bg-disabled)]",
|
|
17164
|
-
// 포커스 하이라이트는 depth 배경보다 뒤에 둬 tailwind-merge 로 이기게 한다.
|
|
17165
|
-
groupFocused && !groupDisabled && "bg-[var(--cmp-listBoxItem-bg-hover)] text-[color:var(--cmp-listBoxItem-content-hover)]"
|
|
17146
|
+
children: [
|
|
17147
|
+
/* @__PURE__ */ jsx(
|
|
17148
|
+
TreeConnector,
|
|
17149
|
+
{
|
|
17150
|
+
guideline: true,
|
|
17151
|
+
size: "sm",
|
|
17152
|
+
depth: o.depth,
|
|
17153
|
+
hasChildren: o.isGroup,
|
|
17154
|
+
isLast: line?.isLast ?? true,
|
|
17155
|
+
ancestorLast: line?.ancestorLast ?? []
|
|
17156
|
+
}
|
|
17166
17157
|
),
|
|
17167
|
-
|
|
17168
|
-
|
|
17169
|
-
|
|
17170
|
-
|
|
17171
|
-
|
|
17172
|
-
/* @__PURE__ */ jsx(
|
|
17173
|
-
|
|
17174
|
-
total > 0 && /* @__PURE__ */ jsxs(
|
|
17175
|
-
"span",
|
|
17158
|
+
/* @__PURE__ */ jsxs("div", { className: TREE_CONTENT_CLASS, children: [
|
|
17159
|
+
o.isGroup ? (
|
|
17160
|
+
// 비활성 그룹도 접고 편다 — 그룹이 비활성이어도 아래에 고를 수 있는 자식이 있을 수 있다.
|
|
17161
|
+
/* @__PURE__ */ jsx(TreeCaret, { expanded, onToggle: () => onToggleExpand(o.value) })
|
|
17162
|
+
) : /* @__PURE__ */ jsx(TreeCaretSpacer, {}),
|
|
17163
|
+
/* @__PURE__ */ jsx(
|
|
17164
|
+
TreeSelectZone,
|
|
17176
17165
|
{
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17166
|
+
selectable: true,
|
|
17167
|
+
disabled: rowDisabled,
|
|
17168
|
+
control: multi && /* @__PURE__ */ jsx(
|
|
17169
|
+
"span",
|
|
17170
|
+
{
|
|
17171
|
+
"data-tree-control": true,
|
|
17172
|
+
className: "flex shrink-0",
|
|
17173
|
+
onPointerDownCapture: (event) => {
|
|
17174
|
+
checkboxShiftRef.current = event.shiftKey;
|
|
17175
|
+
},
|
|
17176
|
+
children: /* @__PURE__ */ jsx(SCheckbox, { value: checkState, disabled: rowDisabled, onValueChange: toggle })
|
|
17177
|
+
}
|
|
17180
17178
|
),
|
|
17181
|
-
children:
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17179
|
+
children: /* @__PURE__ */ jsx(
|
|
17180
|
+
OptionLabel,
|
|
17181
|
+
{
|
|
17182
|
+
label: o.label,
|
|
17183
|
+
className: treeLabelClass("sm", {
|
|
17184
|
+
// 고른 줄은 굵게 — multi_tree 는 일부만 고른 그룹도 굵다 (STree 와 같다)
|
|
17185
|
+
emphasized: checkState !== false,
|
|
17186
|
+
disabled: rowDisabled,
|
|
17187
|
+
selectable: true
|
|
17188
|
+
})
|
|
17189
|
+
}
|
|
17190
|
+
)
|
|
17188
17191
|
}
|
|
17189
17192
|
)
|
|
17190
|
-
]
|
|
17191
|
-
|
|
17192
|
-
|
|
17193
|
-
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17193
|
+
] })
|
|
17194
|
+
]
|
|
17195
|
+
},
|
|
17196
|
+
rowKey
|
|
17197
|
+
)
|
|
17198
|
+
);
|
|
17199
|
+
}
|
|
17200
|
+
if (o.isGroup) {
|
|
17201
|
+
const isDepth1 = o.depth === 1;
|
|
17202
|
+
const isFirstRow = idx === 0 && !showSelectAll;
|
|
17203
|
+
const groupTone = cn(
|
|
17204
|
+
isDepth1 ? "bg-[var(--cmp-listBoxItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listBoxItem-depth2-middle-bg)] font-medium",
|
|
17205
|
+
isDepth1 && !isFirstRow && "border-t border-solid border-[color:var(--cmp-listBoxItem-depth1-border-color)]"
|
|
17206
|
+
);
|
|
17207
|
+
if (multi) {
|
|
17208
|
+
const leafValues = groupLeafValues.get(idx) ?? [];
|
|
17209
|
+
const total = leafValues.length;
|
|
17210
|
+
const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
|
|
17211
|
+
const groupState = total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null;
|
|
17212
|
+
const groupDisabled = o.disabled || total === 0;
|
|
17213
|
+
const groupFocused = focusedValue === `__group_${idx}__`;
|
|
17214
|
+
return /* @__PURE__ */ jsxs(
|
|
17215
|
+
"button",
|
|
17197
17216
|
{
|
|
17217
|
+
type: "button",
|
|
17198
17218
|
"data-select-row": true,
|
|
17199
17219
|
"data-index": idx,
|
|
17220
|
+
id: groupFocused ? activeOptionId : void 0,
|
|
17221
|
+
role: "option",
|
|
17222
|
+
"aria-selected": groupState === true,
|
|
17223
|
+
disabled: groupDisabled,
|
|
17224
|
+
"data-select-focused": groupFocused ? "true" : void 0,
|
|
17225
|
+
"data-select-active": groupFocused ? "true" : void 0,
|
|
17200
17226
|
className: cn(
|
|
17201
|
-
"select-none py-(--cmp-listBoxItem-paddingY) pr-(--cmp-listBoxItem-paddingRight) typo-field-sm-default text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
17227
|
+
"flex w-full select-none items-center gap-(--cmp-listBoxItem-gap) border-none py-(--cmp-listBoxItem-paddingY) pr-(--cmp-listBoxItem-paddingRight) text-left typo-field-sm-default text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
17202
17228
|
groupTone,
|
|
17203
|
-
|
|
17204
|
-
|
|
17205
|
-
|
|
17206
|
-
|
|
17229
|
+
groupDisabled ? "cursor-not-allowed text-[color:var(--cmp-listBoxItem-content-disabled)]" : "cursor-pointer",
|
|
17230
|
+
groupDisabled && isDepth1 && "bg-[var(--cmp-listBoxItem-depth1-bg-disabled)]",
|
|
17231
|
+
// 포커스 하이라이트는 depth 배경보다 뒤에 둬 tailwind-merge 로 이기게 한다.
|
|
17232
|
+
groupFocused && !groupDisabled && "bg-[var(--cmp-listBoxItem-bg-hover)] text-[color:var(--cmp-listBoxItem-content-hover)]"
|
|
17207
17233
|
),
|
|
17208
17234
|
style: { paddingLeft: padLeft },
|
|
17209
|
-
|
|
17235
|
+
onMouseEnter: () => onHoverValue(`__group_${idx}__`),
|
|
17236
|
+
onMouseLeave: () => onHoverValue(null),
|
|
17237
|
+
onClick: () => onToggleGroup(leafValues),
|
|
17238
|
+
children: [
|
|
17239
|
+
/* @__PURE__ */ jsx(CheckboxBox, { state: groupState, disabled: groupDisabled, focused: groupFocused }),
|
|
17240
|
+
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "min-w-0 truncate" }),
|
|
17241
|
+
total > 0 && /* @__PURE__ */ jsxs(
|
|
17242
|
+
"span",
|
|
17243
|
+
{
|
|
17244
|
+
className: cn(
|
|
17245
|
+
"shrink-0 font-medium",
|
|
17246
|
+
groupFocused ? "text-white" : "text-[color:var(--cmp-listBoxItem-content-disabled)]"
|
|
17247
|
+
),
|
|
17248
|
+
children: [
|
|
17249
|
+
"(",
|
|
17250
|
+
selectedCount,
|
|
17251
|
+
"/",
|
|
17252
|
+
total,
|
|
17253
|
+
")"
|
|
17254
|
+
]
|
|
17255
|
+
}
|
|
17256
|
+
)
|
|
17257
|
+
]
|
|
17210
17258
|
},
|
|
17211
17259
|
rowKey
|
|
17212
17260
|
);
|
|
17213
17261
|
}
|
|
17214
|
-
|
|
17215
|
-
|
|
17216
|
-
"button",
|
|
17262
|
+
return /* @__PURE__ */ jsx(
|
|
17263
|
+
"div",
|
|
17217
17264
|
{
|
|
17218
|
-
type: "button",
|
|
17219
17265
|
"data-select-row": true,
|
|
17220
17266
|
"data-index": idx,
|
|
17221
|
-
id: focusedValue === o.value ? activeOptionId : void 0,
|
|
17222
|
-
disabled: o.disabled,
|
|
17223
|
-
role: "option",
|
|
17224
|
-
"aria-selected": isSelected,
|
|
17225
|
-
"aria-setsize": setSize,
|
|
17226
|
-
"aria-posinset": setSize != null ? idx + 1 : void 0,
|
|
17227
|
-
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
17228
|
-
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
17229
17267
|
className: cn(
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
//
|
|
17234
|
-
|
|
17235
|
-
|
|
17268
|
+
"select-none py-(--cmp-listBoxItem-paddingY) pr-(--cmp-listBoxItem-paddingRight) typo-field-sm-default text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
17269
|
+
groupTone,
|
|
17270
|
+
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
17271
|
+
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
17272
|
+
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listBoxItem-content-disabled)]",
|
|
17273
|
+
o.disabled && isDepth1 && "bg-[var(--cmp-listBoxItem-depth1-bg-disabled)]"
|
|
17236
17274
|
),
|
|
17237
|
-
style: {
|
|
17238
|
-
|
|
17239
|
-
onMouseLeave: () => onHoverValue(null),
|
|
17240
|
-
onClick: (event) => onSelect(o.value, idx, event.shiftKey),
|
|
17241
|
-
children: [
|
|
17242
|
-
multi && /* @__PURE__ */ jsx(
|
|
17243
|
-
CheckboxBox,
|
|
17244
|
-
{
|
|
17245
|
-
state: isSelected,
|
|
17246
|
-
disabled: o.disabled,
|
|
17247
|
-
focused: focusedValue === o.value
|
|
17248
|
-
}
|
|
17249
|
-
),
|
|
17250
|
-
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "flex-1 truncate" })
|
|
17251
|
-
]
|
|
17275
|
+
style: { paddingLeft: padLeft },
|
|
17276
|
+
children: o.label
|
|
17252
17277
|
},
|
|
17253
17278
|
rowKey
|
|
17254
17279
|
);
|
|
17255
|
-
}
|
|
17280
|
+
}
|
|
17281
|
+
const isSelected = selected.includes(o.value);
|
|
17282
|
+
return /* @__PURE__ */ jsxs(
|
|
17283
|
+
"button",
|
|
17284
|
+
{
|
|
17285
|
+
type: "button",
|
|
17286
|
+
"data-select-row": true,
|
|
17287
|
+
"data-index": idx,
|
|
17288
|
+
id: focusedValue === o.value ? activeOptionId : void 0,
|
|
17289
|
+
disabled: o.disabled,
|
|
17290
|
+
role: "option",
|
|
17291
|
+
"aria-selected": isSelected,
|
|
17292
|
+
"aria-setsize": setSize,
|
|
17293
|
+
"aria-posinset": setSize != null ? idx + 1 : void 0,
|
|
17294
|
+
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
17295
|
+
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
17296
|
+
className: cn(
|
|
17297
|
+
itemBase,
|
|
17298
|
+
isSelected && !multi && "font-bold text-[color:var(--cmp-listBoxItem-content-selected)]",
|
|
17299
|
+
focusedValue === o.value && "bg-[var(--cmp-listBoxItem-bg-hover)] text-[color:var(--cmp-listBoxItem-content-hover)]",
|
|
17300
|
+
// 비활성 항목은 hover/focus 스타일 대상에서 제외한다.
|
|
17301
|
+
// (:hover 는 disabled 버튼에도 매칭되므로 클래스 자체를 붙이지 않아야 한다)
|
|
17302
|
+
!o.disabled && activeClass
|
|
17303
|
+
),
|
|
17304
|
+
style: { ...LIST_BOX_ITEM_PADDING_Y_RIGHT, paddingLeft: padLeft },
|
|
17305
|
+
onMouseEnter: () => onHoverValue(o.value),
|
|
17306
|
+
onMouseLeave: () => onHoverValue(null),
|
|
17307
|
+
onClick: (event) => onSelect(o.value, idx, event.shiftKey),
|
|
17308
|
+
children: [
|
|
17309
|
+
multi && /* @__PURE__ */ jsx(
|
|
17310
|
+
CheckboxBox,
|
|
17311
|
+
{
|
|
17312
|
+
state: isSelected,
|
|
17313
|
+
disabled: o.disabled,
|
|
17314
|
+
focused: focusedValue === o.value
|
|
17315
|
+
}
|
|
17316
|
+
),
|
|
17317
|
+
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "flex-1 truncate" })
|
|
17318
|
+
]
|
|
17319
|
+
},
|
|
17320
|
+
rowKey
|
|
17321
|
+
);
|
|
17322
|
+
});
|
|
17323
|
+
return /* @__PURE__ */ jsxs("div", { id: listboxId, role: tree ? "tree" : "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
17324
|
+
selectAllRow,
|
|
17325
|
+
topSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: topSpacer } }),
|
|
17326
|
+
wrapTreeMotion(rowNodes),
|
|
17256
17327
|
bottomSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: bottomSpacer } })
|
|
17257
17328
|
] });
|
|
17258
17329
|
}
|
|
17330
|
+
function TreeRowsMotion({ opening, children }) {
|
|
17331
|
+
const ref = useRef(null);
|
|
17332
|
+
const animationRef = useRef(null);
|
|
17333
|
+
const mountedRef = useRef(false);
|
|
17334
|
+
useLayoutEffect(() => {
|
|
17335
|
+
const el = ref.current;
|
|
17336
|
+
if (el == null || typeof el.animate !== "function") return;
|
|
17337
|
+
const from = !mountedRef.current && opening ? 0 : el.getBoundingClientRect().height;
|
|
17338
|
+
mountedRef.current = true;
|
|
17339
|
+
animationRef.current?.cancel();
|
|
17340
|
+
animationRef.current = el.animate(
|
|
17341
|
+
[{ height: `${from}px` }, { height: opening ? `${el.scrollHeight}px` : "0px" }],
|
|
17342
|
+
// 접힌 뒤 줄이 목록에서 빠질 때까지 0 으로 붙잡아 둔다. 편 뒤에는 제 높이(auto)로 돌아간다
|
|
17343
|
+
{ ...TREE_BRANCH_MOTION, fill: opening ? "none" : "forwards" }
|
|
17344
|
+
);
|
|
17345
|
+
}, [opening]);
|
|
17346
|
+
useEffect(() => () => animationRef.current?.cancel(), []);
|
|
17347
|
+
return /* @__PURE__ */ jsx("div", { ref, className: "overflow-hidden", "aria-hidden": opening ? void 0 : true, children });
|
|
17348
|
+
}
|
|
17259
17349
|
function OptionLabel({ label, className }) {
|
|
17260
17350
|
return isHtmlLabel(label) ? /* @__PURE__ */ jsx("span", { className, dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(label) } }) : /* @__PURE__ */ jsx("span", { className, children: label });
|
|
17261
17351
|
}
|