sellmate-design-system-react 9.0.0-beta.95 → 9.0.0-beta.98
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 +23 -2
- package/dist/components/SButton/README.md +1 -0
- package/dist/components/SButton/SButton.d.ts +9 -1
- package/dist/components/SSelect/README.md +1 -0
- package/dist/components/SSelect/SSelect.d.ts +8 -0
- package/dist/components/STable/README.md +15 -1
- package/dist/components/STable/STable.d.ts +45 -1
- package/dist/components/STable/index.d.ts +1 -1
- package/dist/components/STreeTable/STreeTable.d.ts +1 -1
- package/dist/index.cjs +399 -206
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +399 -206
- package/dist/index.js.map +1 -1
- package/dist/lib/row-motion.d.ts +56 -0
- package/dist/llms-full.txt +40 -3
- package/dist/llms.txt +23 -2
- package/dist/styles.css +9 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4261,6 +4261,7 @@ var SButton = /* @__PURE__ */ react.forwardRef(function SButton2({
|
|
|
4261
4261
|
icon,
|
|
4262
4262
|
rightIcon,
|
|
4263
4263
|
label,
|
|
4264
|
+
pill = false,
|
|
4264
4265
|
className,
|
|
4265
4266
|
style,
|
|
4266
4267
|
type = "button",
|
|
@@ -4291,7 +4292,8 @@ var SButton = /* @__PURE__ */ react.forwardRef(function SButton2({
|
|
|
4291
4292
|
"minHeight": s.height,
|
|
4292
4293
|
"minWidth": iconOnly ? s.iconOnlyWidth : hasLabel ? s.minWidth : void 0,
|
|
4293
4294
|
"padding": iconOnly ? 0 : `0 ${s.paddingX}`,
|
|
4294
|
-
|
|
4295
|
+
// 버튼 전용 pill 반경 토큰이 아직 없어 공용 반경을 쓴다
|
|
4296
|
+
"borderRadius": pill ? "var(--radius-full)" : s.radius,
|
|
4295
4297
|
"gap": s.gap,
|
|
4296
4298
|
"fontSize": s.fontSize,
|
|
4297
4299
|
"fontWeight": s.fontWeight,
|
|
@@ -15686,6 +15688,93 @@ function SCalendarBoard({
|
|
|
15686
15688
|
);
|
|
15687
15689
|
}
|
|
15688
15690
|
|
|
15691
|
+
// src/lib/row-motion.ts
|
|
15692
|
+
var EXPAND_MOTION = { duration: 220, easing: "ease-out" };
|
|
15693
|
+
var COLLAPSE_MOTION = { duration: 140, easing: "ease-in" };
|
|
15694
|
+
function sameKeys(a, b) {
|
|
15695
|
+
return a.size === b.size && [...a].every((key) => b.has(key));
|
|
15696
|
+
}
|
|
15697
|
+
function motionAllowed() {
|
|
15698
|
+
if (typeof window === "undefined" || typeof Element.prototype.animate !== "function")
|
|
15699
|
+
return false;
|
|
15700
|
+
return !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
15701
|
+
}
|
|
15702
|
+
function startRowMotion({
|
|
15703
|
+
scrollEl,
|
|
15704
|
+
isInBlock,
|
|
15705
|
+
blockKeys,
|
|
15706
|
+
from,
|
|
15707
|
+
to
|
|
15708
|
+
}) {
|
|
15709
|
+
const spec = to === 1 ? EXPAND_MOTION : COLLAPSE_MOTION;
|
|
15710
|
+
const duration = spec.duration * Math.abs(to - from);
|
|
15711
|
+
if (duration < 1) return null;
|
|
15712
|
+
const trs = [
|
|
15713
|
+
...scrollEl.querySelectorAll("tr[data-row-index], tr[data-expansion-row]")
|
|
15714
|
+
].map((tr) => ({ tr, inBlock: isInBlock(tr), height: tr.getBoundingClientRect().height }));
|
|
15715
|
+
const viewBottom = scrollEl.scrollTop + scrollEl.clientHeight;
|
|
15716
|
+
const openAt = (offset) => from + (to - from) * offset;
|
|
15717
|
+
const translate = (shift2, s) => `translateY(${-shift2 * (1 - s)}px)`;
|
|
15718
|
+
const timing = {
|
|
15719
|
+
duration,
|
|
15720
|
+
easing: spec.easing,
|
|
15721
|
+
// 접힘은 남겨 둔 행이 빠질 때까지 끝 위치를 붙잡는다 — 놓으면 그 사이 한 프레임 제자리로 튄다
|
|
15722
|
+
fill: to === 0 ? "forwards" : "none"
|
|
15723
|
+
};
|
|
15724
|
+
const animations = [];
|
|
15725
|
+
let shift = 0;
|
|
15726
|
+
let blockHeight = 0;
|
|
15727
|
+
let y = 0;
|
|
15728
|
+
trs.forEach(({ tr, inBlock, height }, i) => {
|
|
15729
|
+
if (inBlock && !trs[i - 1]?.inBlock) {
|
|
15730
|
+
blockHeight = 0;
|
|
15731
|
+
for (let j = i; j < trs.length && trs[j].inBlock; j++) blockHeight += trs[j].height;
|
|
15732
|
+
shift += blockHeight;
|
|
15733
|
+
y = 0;
|
|
15734
|
+
}
|
|
15735
|
+
if (shift === 0 || tr.offsetTop - shift > viewBottom) return;
|
|
15736
|
+
if (!inBlock) {
|
|
15737
|
+
animations.push(
|
|
15738
|
+
tr.animate(
|
|
15739
|
+
[{ transform: translate(shift, from) }, { transform: translate(shift, to) }],
|
|
15740
|
+
timing
|
|
15741
|
+
)
|
|
15742
|
+
);
|
|
15743
|
+
return;
|
|
15744
|
+
}
|
|
15745
|
+
const rowY = y;
|
|
15746
|
+
const clipAt = (s) => Math.min(height, Math.max(0, blockHeight * (1 - s) - rowY));
|
|
15747
|
+
const bends = [1 - rowY / blockHeight, 1 - (rowY + height) / blockHeight].map((s) => (s - from) / (to - from)).filter((offset) => offset > 0 && offset < 1);
|
|
15748
|
+
const offsets = [0, ...bends, 1].sort((a, b) => a - b);
|
|
15749
|
+
animations.push(
|
|
15750
|
+
tr.animate(
|
|
15751
|
+
offsets.map((offset) => ({
|
|
15752
|
+
offset,
|
|
15753
|
+
transform: translate(shift, openAt(offset)),
|
|
15754
|
+
clipPath: `inset(${clipAt(openAt(offset))}px 0 0 0)`
|
|
15755
|
+
})),
|
|
15756
|
+
timing
|
|
15757
|
+
)
|
|
15758
|
+
);
|
|
15759
|
+
y += height;
|
|
15760
|
+
});
|
|
15761
|
+
if (animations.length === 0) return null;
|
|
15762
|
+
return {
|
|
15763
|
+
keys: blockKeys,
|
|
15764
|
+
to,
|
|
15765
|
+
done: false,
|
|
15766
|
+
finished: Promise.all(animations.map((animation) => animation.finished)).then(
|
|
15767
|
+
() => true,
|
|
15768
|
+
() => false
|
|
15769
|
+
),
|
|
15770
|
+
openness: () => {
|
|
15771
|
+
const progress = animations[0].effect?.getComputedTiming().progress;
|
|
15772
|
+
return progress == null ? to : openAt(progress);
|
|
15773
|
+
},
|
|
15774
|
+
cancel: () => animations.forEach((animation) => animation.cancel())
|
|
15775
|
+
};
|
|
15776
|
+
}
|
|
15777
|
+
|
|
15689
15778
|
// src/lib/row-metrics.ts
|
|
15690
15779
|
function createRowMetrics(count, estimate) {
|
|
15691
15780
|
const safeCount = Math.max(0, Math.floor(count));
|
|
@@ -16105,6 +16194,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
16105
16194
|
onSearchChange,
|
|
16106
16195
|
searchDebounce = DEFAULT_SEARCH_DEBOUNCE,
|
|
16107
16196
|
label,
|
|
16197
|
+
ariaLabel,
|
|
16108
16198
|
labelPosition,
|
|
16109
16199
|
subLabel,
|
|
16110
16200
|
labelWidth,
|
|
@@ -16807,6 +16897,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
16807
16897
|
style,
|
|
16808
16898
|
children: [
|
|
16809
16899
|
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
16900
|
+
!label && ariaLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { id: `${triggerId}-aria-label`, hidden: true, children: ariaLabel }),
|
|
16810
16901
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16811
16902
|
"div",
|
|
16812
16903
|
{
|
|
@@ -16815,7 +16906,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
16815
16906
|
role: "combobox",
|
|
16816
16907
|
tabIndex: effectiveDisabled ? -1 : 0,
|
|
16817
16908
|
"aria-disabled": effectiveDisabled || void 0,
|
|
16818
|
-
"aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerId}` : triggerId,
|
|
16909
|
+
"aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerId}` : ariaLabel ? `${triggerId}-aria-label ${triggerId}` : triggerId,
|
|
16819
16910
|
"aria-haspopup": isTree ? "tree" : "listbox",
|
|
16820
16911
|
"aria-expanded": open,
|
|
16821
16912
|
"aria-controls": open ? listboxId : void 0,
|
|
@@ -17423,6 +17514,7 @@ function CheckboxBox({
|
|
|
17423
17514
|
}
|
|
17424
17515
|
);
|
|
17425
17516
|
}
|
|
17517
|
+
var NO_KEYS = /* @__PURE__ */ new Set();
|
|
17426
17518
|
var DEFAULT_ROW_HEIGHT = 44;
|
|
17427
17519
|
var DENSE_ROW_HEIGHT = 32;
|
|
17428
17520
|
var SELECTABLE_COLUMN_WIDTH = 48;
|
|
@@ -17453,6 +17545,11 @@ var CONTEXT_MENU_ITEM_CLASS = cn(
|
|
|
17453
17545
|
"data-[highlighted]:bg-[var(--sys-color-bg-accent)] data-[highlighted]:text-[color:var(--sys-color-fg-inverse)]"
|
|
17454
17546
|
);
|
|
17455
17547
|
var ROW_INTERACTIVE_SELECTOR = 'button, a, input, select, textarea, label, [role="button"], [role="checkbox"], [contenteditable="true"]';
|
|
17548
|
+
function rowBlockHeight(tr) {
|
|
17549
|
+
const next = tr.nextElementSibling;
|
|
17550
|
+
const expansion = next instanceof HTMLElement && next.dataset.expansionRow != null ? next.offsetHeight : 0;
|
|
17551
|
+
return tr.offsetHeight + expansion;
|
|
17552
|
+
}
|
|
17456
17553
|
var colWidthFromDef = (c) => {
|
|
17457
17554
|
if (c.autoWidth) return 0;
|
|
17458
17555
|
if (c.width == null) return DEFAULT_COLUMN_WIDTH;
|
|
@@ -17465,6 +17562,8 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17465
17562
|
rows = [],
|
|
17466
17563
|
rowKey = "id",
|
|
17467
17564
|
selectable = false,
|
|
17565
|
+
rowSelectLabel,
|
|
17566
|
+
selectAllLabel = "\uC804\uCCB4 \uC120\uD0DD",
|
|
17468
17567
|
dragSelectable = false,
|
|
17469
17568
|
contextMenuItems,
|
|
17470
17569
|
onContextMenuItemClick,
|
|
@@ -17503,6 +17602,11 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17503
17602
|
tableId,
|
|
17504
17603
|
onRowClick,
|
|
17505
17604
|
onRowDoubleClick,
|
|
17605
|
+
renderExpansion,
|
|
17606
|
+
expansionColumn,
|
|
17607
|
+
expandedKeys,
|
|
17608
|
+
defaultExpandedKeys,
|
|
17609
|
+
onExpandedChange,
|
|
17506
17610
|
className,
|
|
17507
17611
|
style
|
|
17508
17612
|
}, ref) {
|
|
@@ -17517,6 +17621,44 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17517
17621
|
onDenseChange?.(next);
|
|
17518
17622
|
};
|
|
17519
17623
|
const visibleCols = react.useMemo(() => columns.filter((c) => c.visible !== false), [columns]);
|
|
17624
|
+
const expansionOn = renderExpansion != null;
|
|
17625
|
+
const [innerExpandedKeys, setInnerExpandedKeys] = react.useState(
|
|
17626
|
+
() => defaultExpandedKeys ?? []
|
|
17627
|
+
);
|
|
17628
|
+
const currentExpandedKeys = expandedKeys ?? innerExpandedKeys;
|
|
17629
|
+
const expandedSet = react.useMemo(() => new Set(currentExpandedKeys), [currentExpandedKeys]);
|
|
17630
|
+
const isRowExpanded = (row) => expansionOn && expandedSet.has(row[rowKey]);
|
|
17631
|
+
const [closingKeys, setClosingKeys] = react.useState(NO_KEYS);
|
|
17632
|
+
const [prevExpandedSet, setPrevExpandedSet] = react.useState(expandedSet);
|
|
17633
|
+
if (prevExpandedSet !== expandedSet) {
|
|
17634
|
+
setPrevExpandedSet(expandedSet);
|
|
17635
|
+
let nextClosing = NO_KEYS;
|
|
17636
|
+
if (expansionOn && motionAllowed()) {
|
|
17637
|
+
const collapsed = [...prevExpandedSet].filter((key) => !expandedSet.has(key));
|
|
17638
|
+
const opened = [...expandedSet].filter((key) => !prevExpandedSet.has(key));
|
|
17639
|
+
if (collapsed.length > 0 && opened.length === 0) nextClosing = new Set(collapsed);
|
|
17640
|
+
}
|
|
17641
|
+
if (nextClosing !== closingKeys) setClosingKeys(nextClosing);
|
|
17642
|
+
}
|
|
17643
|
+
const shownExpansionSet = react.useMemo(
|
|
17644
|
+
() => closingKeys.size === 0 ? expandedSet : /* @__PURE__ */ new Set([...expandedSet, ...closingKeys]),
|
|
17645
|
+
[expandedSet, closingKeys]
|
|
17646
|
+
);
|
|
17647
|
+
const toggleExpanded = (row) => {
|
|
17648
|
+
const key = row[rowKey];
|
|
17649
|
+
const next = expandedSet.has(key) ? currentExpandedKeys.filter((k) => k !== key) : [...currentExpandedKeys, key];
|
|
17650
|
+
if (expandedKeys === void 0) setInnerExpandedKeys(next);
|
|
17651
|
+
onExpandedChange?.(next, row);
|
|
17652
|
+
};
|
|
17653
|
+
const expansionColIndex = !expansionOn ? -1 : expansionColumn == null ? 0 : visibleCols.findIndex((c) => c.name === expansionColumn);
|
|
17654
|
+
if (expansionColIndex >= 0) {
|
|
17655
|
+
const col = visibleCols[expansionColIndex];
|
|
17656
|
+
if (col?.renderCell) {
|
|
17657
|
+
throw new Error(
|
|
17658
|
+
`[STable] \uD3BC\uCE68 \uCEEC\uB7FC(${col.name})\uC5D0\uB294 renderCell \uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. render \uB97C \uC4F0\uC138\uC694.`
|
|
17659
|
+
);
|
|
17660
|
+
}
|
|
17661
|
+
}
|
|
17520
17662
|
const headerGroupInfo = react.useMemo(() => {
|
|
17521
17663
|
if (!headerGroups || headerGroups.length === 0) return null;
|
|
17522
17664
|
const indexByName = new Map(visibleCols.map((col, index) => [col.name, index]));
|
|
@@ -17621,6 +17763,7 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17621
17763
|
metricsRef.current ?? (metricsRef.current = createRowMetrics(0, effectiveRowHeight));
|
|
17622
17764
|
const metrics = metricsRef.current;
|
|
17623
17765
|
const measuredKeyRef = react.useRef("");
|
|
17766
|
+
const measuredExpansionRef = react.useRef(shownExpansionSet);
|
|
17624
17767
|
const pendingRowRef = react.useRef(null);
|
|
17625
17768
|
const pinnedTopRef = react.useRef(-1);
|
|
17626
17769
|
const lastEstimateRef = react.useRef(effectiveRowHeight);
|
|
@@ -17634,6 +17777,7 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17634
17777
|
}
|
|
17635
17778
|
const [scrolledLeft, setScrolledLeft] = react.useState(false);
|
|
17636
17779
|
const [scrolledRight, setScrolledRight] = react.useState(false);
|
|
17780
|
+
const [viewportWidth, setViewportWidth] = react.useState(0);
|
|
17637
17781
|
const setRowCount = react.useCallback((count) => {
|
|
17638
17782
|
const safe = Math.max(0, Math.floor(Number.isFinite(count) ? count : 0));
|
|
17639
17783
|
lastReachEndRef.current = -1;
|
|
@@ -17659,6 +17803,7 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17659
17803
|
setHeaderHeight(headerH);
|
|
17660
17804
|
setScrolledLeft(el.scrollLeft > 0);
|
|
17661
17805
|
setScrolledRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
17806
|
+
setViewportWidth(el.clientWidth);
|
|
17662
17807
|
if (virtualScroll) {
|
|
17663
17808
|
const { start, end } = calcRange(el.scrollTop, el.clientHeight - headerH);
|
|
17664
17809
|
setVsRange((prev) => prev.start === start && prev.end === end ? prev : { start, end });
|
|
@@ -17676,6 +17821,13 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17676
17821
|
react.useEffect(() => {
|
|
17677
17822
|
handleScroll();
|
|
17678
17823
|
}, [handleScroll, rowCount, columnWidths, loading2, height]);
|
|
17824
|
+
react.useEffect(() => {
|
|
17825
|
+
const el = scrollRef.current;
|
|
17826
|
+
if (!expansionOn || !el || typeof ResizeObserver === "undefined") return;
|
|
17827
|
+
const observer = new ResizeObserver(() => handleScroll());
|
|
17828
|
+
observer.observe(el);
|
|
17829
|
+
return () => observer.disconnect();
|
|
17830
|
+
}, [expansionOn, handleScroll]);
|
|
17679
17831
|
const handleScrollRef = react.useRef(handleScroll);
|
|
17680
17832
|
handleScrollRef.current = handleScroll;
|
|
17681
17833
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -17685,9 +17837,10 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17685
17837
|
if (!el || m == null) return;
|
|
17686
17838
|
const pending2 = pendingRowRef.current;
|
|
17687
17839
|
const key = `${vsRange.start}:${vsRange.end}:${rowCount}`;
|
|
17688
|
-
const alreadyMeasured = measuredKeyRef.current === key;
|
|
17840
|
+
const alreadyMeasured = measuredKeyRef.current === key && measuredExpansionRef.current === shownExpansionSet;
|
|
17689
17841
|
if (alreadyMeasured && pending2 == null) return;
|
|
17690
17842
|
measuredKeyRef.current = key;
|
|
17843
|
+
measuredExpansionRef.current = shownExpansionSet;
|
|
17691
17844
|
const anchor = vsRange.start;
|
|
17692
17845
|
const anchorBefore = offsetOf(m, anchor);
|
|
17693
17846
|
let changed = false;
|
|
@@ -17695,7 +17848,7 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17695
17848
|
for (const tr of el.querySelectorAll("tr[data-row-index]")) {
|
|
17696
17849
|
const index = Number(tr.dataset.rowIndex);
|
|
17697
17850
|
if (!Number.isInteger(index)) continue;
|
|
17698
|
-
const h = tr
|
|
17851
|
+
const h = rowBlockHeight(tr);
|
|
17699
17852
|
if (h <= 0) continue;
|
|
17700
17853
|
if (setRowSize(m, index, h)) changed = true;
|
|
17701
17854
|
}
|
|
@@ -17717,6 +17870,54 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17717
17870
|
if (delta !== 0 && el.scrollTop > 0) el.scrollTop += delta;
|
|
17718
17871
|
handleScrollRef.current();
|
|
17719
17872
|
});
|
|
17873
|
+
const expansionMotionRef = react.useRef(null);
|
|
17874
|
+
const prevExpansionRef = react.useRef(null);
|
|
17875
|
+
useIsomorphicLayoutEffect(() => {
|
|
17876
|
+
if (!expansionOn) return;
|
|
17877
|
+
const prev = prevExpansionRef.current;
|
|
17878
|
+
prevExpansionRef.current = { rows, keys: expandedSet };
|
|
17879
|
+
const running = expansionMotionRef.current;
|
|
17880
|
+
if (running?.done) {
|
|
17881
|
+
running.cancel();
|
|
17882
|
+
expansionMotionRef.current = null;
|
|
17883
|
+
}
|
|
17884
|
+
if (!prev || prev.keys === expandedSet) return;
|
|
17885
|
+
const added = new Set([...expandedSet].filter((key) => !prev.keys.has(key)));
|
|
17886
|
+
const removed = new Set([...prev.keys].filter((key) => !expandedSet.has(key)));
|
|
17887
|
+
if (added.size === 0 && removed.size === 0) return;
|
|
17888
|
+
let from = added.size > 0 ? 0 : 1;
|
|
17889
|
+
if (expansionMotionRef.current) {
|
|
17890
|
+
const current = expansionMotionRef.current;
|
|
17891
|
+
const reversing = current.to === 0 && sameKeys(added, current.keys) || current.to === 1 && sameKeys(removed, current.keys);
|
|
17892
|
+
if (reversing) from = current.openness();
|
|
17893
|
+
current.cancel();
|
|
17894
|
+
expansionMotionRef.current = null;
|
|
17895
|
+
}
|
|
17896
|
+
const scrollEl = scrollRef.current;
|
|
17897
|
+
const keys = added.size > 0 ? added : removed;
|
|
17898
|
+
const keyNames = new Set([...keys].map(String));
|
|
17899
|
+
const canAnimate = scrollEl != null && prev.rows === rows && motionAllowed() && (added.size === 0 || removed.size === 0);
|
|
17900
|
+
const motion = canAnimate ? startRowMotion({
|
|
17901
|
+
scrollEl,
|
|
17902
|
+
isInBlock: (tr) => tr.dataset.expansionRow != null && keyNames.has(tr.dataset.expansionKey ?? ""),
|
|
17903
|
+
blockKeys: keys,
|
|
17904
|
+
from,
|
|
17905
|
+
to: added.size > 0 ? 1 : 0
|
|
17906
|
+
}) : null;
|
|
17907
|
+
if (motion) {
|
|
17908
|
+
expansionMotionRef.current = motion;
|
|
17909
|
+
if (motion.to === 0) {
|
|
17910
|
+
void motion.finished.then((completed) => {
|
|
17911
|
+
if (!completed || expansionMotionRef.current !== motion) return;
|
|
17912
|
+
motion.done = true;
|
|
17913
|
+
setClosingKeys(NO_KEYS);
|
|
17914
|
+
});
|
|
17915
|
+
}
|
|
17916
|
+
} else if (closingKeys.size > 0) {
|
|
17917
|
+
setClosingKeys(NO_KEYS);
|
|
17918
|
+
}
|
|
17919
|
+
}, [expandedSet, closingKeys]);
|
|
17920
|
+
react.useEffect(() => () => expansionMotionRef.current?.cancel(), []);
|
|
17720
17921
|
react.useEffect(() => {
|
|
17721
17922
|
if (!virtualScroll) return;
|
|
17722
17923
|
cbRef.current.onVirtualUpdate?.({ from: vsRange.start, to: vsRange.end });
|
|
@@ -17773,7 +17974,7 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
17773
17974
|
if (virtualScroll && m != null) return indexAtOffset(m, el.scrollTop);
|
|
17774
17975
|
const top = el.scrollTop;
|
|
17775
17976
|
for (const tr of el.querySelectorAll("tr[data-row-index]")) {
|
|
17776
|
-
if (tr.offsetTop + tr
|
|
17977
|
+
if (tr.offsetTop + rowBlockHeight(tr) > top) return Number(tr.dataset.rowIndex) || 0;
|
|
17777
17978
|
}
|
|
17778
17979
|
return 0;
|
|
17779
17980
|
},
|
|
@@ -18101,7 +18302,8 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
18101
18302
|
{
|
|
18102
18303
|
value: headerCheck,
|
|
18103
18304
|
disabled: !hasSelectableRow,
|
|
18104
|
-
onValueChange: toggleAll
|
|
18305
|
+
onValueChange: toggleAll,
|
|
18306
|
+
ariaLabel: selectAllLabel
|
|
18105
18307
|
}
|
|
18106
18308
|
) })
|
|
18107
18309
|
}
|
|
@@ -18147,7 +18349,8 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
18147
18349
|
{
|
|
18148
18350
|
value: headerCheck,
|
|
18149
18351
|
disabled: !hasSelectableRow,
|
|
18150
|
-
onValueChange: toggleAll
|
|
18352
|
+
onValueChange: toggleAll,
|
|
18353
|
+
ariaLabel: selectAllLabel
|
|
18151
18354
|
}
|
|
18152
18355
|
) }),
|
|
18153
18356
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -18347,112 +18550,168 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
18347
18550
|
const rowLocked = (selectable || dragSelectable) && !canSelectRow(row);
|
|
18348
18551
|
const rowBg = "bg-white";
|
|
18349
18552
|
const rowHover = hoverable && !rowLocked;
|
|
18350
|
-
|
|
18351
|
-
|
|
18352
|
-
|
|
18353
|
-
"
|
|
18354
|
-
|
|
18355
|
-
|
|
18356
|
-
|
|
18357
|
-
|
|
18358
|
-
|
|
18359
|
-
|
|
18360
|
-
|
|
18361
|
-
|
|
18362
|
-
|
|
18363
|
-
|
|
18364
|
-
|
|
18365
|
-
|
|
18366
|
-
(
|
|
18367
|
-
dragSelectOn
|
|
18368
|
-
|
|
18369
|
-
|
|
18370
|
-
|
|
18371
|
-
|
|
18372
|
-
|
|
18373
|
-
"
|
|
18374
|
-
|
|
18375
|
-
|
|
18376
|
-
|
|
18377
|
-
|
|
18553
|
+
const expansionShown = expansionOn && shownExpansionSet.has(row[rowKey]);
|
|
18554
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
|
|
18555
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
18556
|
+
"tr",
|
|
18557
|
+
{
|
|
18558
|
+
"data-row-index": rowIndex,
|
|
18559
|
+
onClick: onRowClick ? (event) => {
|
|
18560
|
+
const target = event.target;
|
|
18561
|
+
if (target.closest(ROW_INTERACTIVE_SELECTOR)) return;
|
|
18562
|
+
onRowClick(row);
|
|
18563
|
+
} : void 0,
|
|
18564
|
+
onDoubleClick: onRowDoubleClick ? (event) => {
|
|
18565
|
+
const target = event.target;
|
|
18566
|
+
if (target.closest(ROW_INTERACTIVE_SELECTOR)) return;
|
|
18567
|
+
onRowDoubleClick(row);
|
|
18568
|
+
} : void 0,
|
|
18569
|
+
onMouseDown: dragSelectOn ? (event) => startDragSelect(rowIndex, event) : void 0,
|
|
18570
|
+
onMouseEnter: dragSelectOn ? () => extendDragSelect(rowIndex) : void 0,
|
|
18571
|
+
onContextMenu: contextMenuOn ? (event) => openContextMenu(row, event) : void 0,
|
|
18572
|
+
className: cn(
|
|
18573
|
+
"group/table-row",
|
|
18574
|
+
rowHover && "hover:bg-[var(--color-grey-05)]",
|
|
18575
|
+
(onRowClick || onRowDoubleClick) && "cursor-pointer",
|
|
18576
|
+
dragSelectOn && "select-none",
|
|
18577
|
+
rowClassName?.(row)
|
|
18578
|
+
),
|
|
18579
|
+
style: { height: virtualScroll ? effectiveRowHeight : rowH },
|
|
18580
|
+
children: [
|
|
18581
|
+
selectable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18582
|
+
"td",
|
|
18583
|
+
{
|
|
18584
|
+
className: cn(
|
|
18585
|
+
// 원본 .td--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
18586
|
+
"sticky left-0 z-[2] border-b border-solid border-[color:var(--cmp-table-border-color)] align-middle",
|
|
18587
|
+
rowBg,
|
|
18588
|
+
rowHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
18589
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
18590
|
+
),
|
|
18591
|
+
style: {
|
|
18592
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
18593
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
18594
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH,
|
|
18595
|
+
// 선택 열도 행의 일부다 — 여기만 희지 않으면 회색 줄이 끊긴다
|
|
18596
|
+
background: rowLocked ? LOCKED_BG : void 0
|
|
18597
|
+
},
|
|
18598
|
+
onClick: (e) => e.stopPropagation(),
|
|
18599
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
18600
|
+
onClickCapture: (e) => {
|
|
18601
|
+
shiftPressedRef.current = e.shiftKey;
|
|
18602
|
+
if (e.shiftKey) window.getSelection()?.removeAllRanges();
|
|
18603
|
+
},
|
|
18604
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex select-none items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18605
|
+
SCheckbox,
|
|
18606
|
+
{
|
|
18607
|
+
value: isSel || !isRowIndeterminate?.(row) ? isSel : null,
|
|
18608
|
+
disabled: !canSelectRow(row),
|
|
18609
|
+
onValueChange: () => toggleRow(row, rowOffset + ri),
|
|
18610
|
+
ariaLabel: rowSelectLabel?.(row) ?? "\uD589 \uC120\uD0DD"
|
|
18611
|
+
}
|
|
18612
|
+
) })
|
|
18613
|
+
}
|
|
18614
|
+
),
|
|
18615
|
+
visibleCols.map((col, i) => {
|
|
18616
|
+
const tdClassName = cn(
|
|
18617
|
+
// 원본 .td: 컬럼 폭에 맞춰 내용 말줄임(overflow hidden + ellipsis) + 행 구분선(border-b)
|
|
18618
|
+
// 셀 배경은 흰색으로 채워 뒤 배경이 비치지 않게 하고, hover 시 행 배경(grey_05)을 따른다.
|
|
18619
|
+
"overflow-hidden text-ellipsis whitespace-nowrap border-b border-solid border-[color:var(--cmp-table-border-color)] text-[12px] leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
18378
18620
|
rowBg,
|
|
18379
18621
|
rowHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
18380
|
-
|
|
18381
|
-
)
|
|
18382
|
-
|
|
18383
|
-
|
|
18384
|
-
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18622
|
+
typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
|
|
18623
|
+
);
|
|
18624
|
+
const tdPadX = cellPadXOf(col);
|
|
18625
|
+
const dragEdges = dragSel ? [
|
|
18626
|
+
dragTop && `inset 0 ${DRAG_EDGE_W} 0 0 ${DRAG_EDGE_C}`,
|
|
18627
|
+
dragBottom && `inset 0 ${DRAG_EDGE_NEG_W} 0 0 ${DRAG_EDGE_C}`,
|
|
18628
|
+
i === 0 && `inset ${DRAG_EDGE_W} 0 0 0 ${DRAG_EDGE_C}`,
|
|
18629
|
+
i === visibleCols.length - 1 && `inset ${DRAG_EDGE_NEG_W} 0 0 0 ${DRAG_EDGE_C}`
|
|
18630
|
+
].filter(Boolean) : [];
|
|
18631
|
+
const shadows = [...dragEdges, stickyShadowValue(i)].filter(Boolean);
|
|
18632
|
+
const tdStyle = {
|
|
18633
|
+
paddingLeft: tdPadX,
|
|
18634
|
+
paddingRight: tdPadX,
|
|
18635
|
+
paddingTop: cellPadY,
|
|
18636
|
+
paddingBottom: cellPadY,
|
|
18637
|
+
textAlign: col.align ?? "left",
|
|
18638
|
+
// 인라인이라 hover 배경보다 세다 — 고른 행은 hover 로 흐려지지 않는다.
|
|
18639
|
+
background: dragSel ? DRAG_BG : rowLocked ? LOCKED_BG : void 0,
|
|
18640
|
+
// 클래스의 기본 글자색을 덮는다. 셀이 자기 색을 정한 내용(SIcon 의
|
|
18641
|
+
// color, STag 등)까지는 닿지 않는다 — 그건 소비 앱 몫이다.
|
|
18642
|
+
color: rowLocked ? LOCKED_CONTENT : void 0,
|
|
18643
|
+
boxShadow: shadows.length > 0 ? shadows.join(", ") : void 0,
|
|
18644
|
+
...cellStyle(i)
|
|
18645
|
+
};
|
|
18646
|
+
if (col.renderCell) {
|
|
18647
|
+
const cell = col.renderCell({
|
|
18648
|
+
row,
|
|
18649
|
+
value: cellValue(col, row),
|
|
18650
|
+
column: col,
|
|
18651
|
+
colIndex: i,
|
|
18652
|
+
rowIndex: ri,
|
|
18653
|
+
isSelected: isSel,
|
|
18654
|
+
className: tdClassName,
|
|
18655
|
+
style: tdStyle
|
|
18656
|
+
});
|
|
18657
|
+
if (cell == null || cell === false) return null;
|
|
18658
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: cell }, col.name);
|
|
18659
|
+
}
|
|
18660
|
+
const content = col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row);
|
|
18661
|
+
if (i === expansionColIndex) {
|
|
18662
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
18663
|
+
"td",
|
|
18664
|
+
{
|
|
18665
|
+
className: cn(tdClassName, "cursor-pointer"),
|
|
18666
|
+
style: tdStyle,
|
|
18667
|
+
onClick: (event) => {
|
|
18668
|
+
if (isOwnControl(event.target, event.currentTarget)) return;
|
|
18669
|
+
event.stopPropagation();
|
|
18670
|
+
toggleExpanded(row);
|
|
18671
|
+
},
|
|
18672
|
+
onDoubleClick: (event) => event.stopPropagation(),
|
|
18673
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ExpansionToggleCell, { expanded: isRowExpanded(row), children: content })
|
|
18674
|
+
},
|
|
18675
|
+
col.name
|
|
18676
|
+
);
|
|
18677
|
+
}
|
|
18678
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { className: tdClassName, style: tdStyle, children: content }, col.name);
|
|
18679
|
+
})
|
|
18680
|
+
]
|
|
18681
|
+
}
|
|
18682
|
+
),
|
|
18683
|
+
expansionShown && // 상세 영역 — 행이 아니므로 data-row-index 를 달지 않는다 (행 수·선택·실측 대상 밖)
|
|
18684
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18685
|
+
"tr",
|
|
18686
|
+
{
|
|
18687
|
+
"data-expansion-row": "",
|
|
18688
|
+
"data-expansion-key": String(row[rowKey]),
|
|
18689
|
+
className: expandedSet.has(row[rowKey]) ? void 0 : "pointer-events-none",
|
|
18690
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18691
|
+
"td",
|
|
18692
|
+
{
|
|
18693
|
+
colSpan: totalColSpan,
|
|
18694
|
+
className: "whitespace-normal border-b border-solid border-[color:var(--cmp-table-border-color)] bg-white p-0 text-[12px] leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
18695
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18696
|
+
"div",
|
|
18397
18697
|
{
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
|
|
18698
|
+
className: "sticky left-0 box-border min-w-0",
|
|
18699
|
+
style: {
|
|
18700
|
+
// 가로 스크롤이 있어도 보이는 폭에 머문다. 재기 전(0)에는 표 폭을 따른다
|
|
18701
|
+
width: viewportWidth > 0 ? viewportWidth : void 0,
|
|
18702
|
+
paddingLeft: "var(--cmp-table-body-paddingX-wide)",
|
|
18703
|
+
paddingRight: "var(--cmp-table-body-paddingX-wide)",
|
|
18704
|
+
paddingTop: cellPadY,
|
|
18705
|
+
paddingBottom: cellPadY
|
|
18706
|
+
},
|
|
18707
|
+
children: renderExpansion?.(row)
|
|
18401
18708
|
}
|
|
18402
|
-
)
|
|
18403
|
-
}
|
|
18404
|
-
),
|
|
18405
|
-
visibleCols.map((col, i) => {
|
|
18406
|
-
const tdClassName = cn(
|
|
18407
|
-
// 원본 .td: 컬럼 폭에 맞춰 내용 말줄임(overflow hidden + ellipsis) + 행 구분선(border-b)
|
|
18408
|
-
// 셀 배경은 흰색으로 채워 뒤 배경이 비치지 않게 하고, hover 시 행 배경(grey_05)을 따른다.
|
|
18409
|
-
"overflow-hidden text-ellipsis whitespace-nowrap border-b border-solid border-[color:var(--cmp-table-border-color)] text-[12px] leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
18410
|
-
rowBg,
|
|
18411
|
-
rowHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
18412
|
-
typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
|
|
18413
|
-
);
|
|
18414
|
-
const tdPadX = cellPadXOf(col);
|
|
18415
|
-
const dragEdges = dragSel ? [
|
|
18416
|
-
dragTop && `inset 0 ${DRAG_EDGE_W} 0 0 ${DRAG_EDGE_C}`,
|
|
18417
|
-
dragBottom && `inset 0 ${DRAG_EDGE_NEG_W} 0 0 ${DRAG_EDGE_C}`,
|
|
18418
|
-
i === 0 && `inset ${DRAG_EDGE_W} 0 0 0 ${DRAG_EDGE_C}`,
|
|
18419
|
-
i === visibleCols.length - 1 && `inset ${DRAG_EDGE_NEG_W} 0 0 0 ${DRAG_EDGE_C}`
|
|
18420
|
-
].filter(Boolean) : [];
|
|
18421
|
-
const shadows = [...dragEdges, stickyShadowValue(i)].filter(Boolean);
|
|
18422
|
-
const tdStyle = {
|
|
18423
|
-
paddingLeft: tdPadX,
|
|
18424
|
-
paddingRight: tdPadX,
|
|
18425
|
-
paddingTop: cellPadY,
|
|
18426
|
-
paddingBottom: cellPadY,
|
|
18427
|
-
textAlign: col.align ?? "left",
|
|
18428
|
-
// 인라인이라 hover 배경보다 세다 — 고른 행은 hover 로 흐려지지 않는다.
|
|
18429
|
-
background: dragSel ? DRAG_BG : rowLocked ? LOCKED_BG : void 0,
|
|
18430
|
-
// 클래스의 기본 글자색을 덮는다. 셀이 자기 색을 정한 내용(SIcon 의
|
|
18431
|
-
// color, STag 등)까지는 닿지 않는다 — 그건 소비 앱 몫이다.
|
|
18432
|
-
color: rowLocked ? LOCKED_CONTENT : void 0,
|
|
18433
|
-
boxShadow: shadows.length > 0 ? shadows.join(", ") : void 0,
|
|
18434
|
-
...cellStyle(i)
|
|
18435
|
-
};
|
|
18436
|
-
if (col.renderCell) {
|
|
18437
|
-
const cell = col.renderCell({
|
|
18438
|
-
row,
|
|
18439
|
-
value: cellValue(col, row),
|
|
18440
|
-
column: col,
|
|
18441
|
-
colIndex: i,
|
|
18442
|
-
rowIndex: ri,
|
|
18443
|
-
isSelected: isSel,
|
|
18444
|
-
className: tdClassName,
|
|
18445
|
-
style: tdStyle
|
|
18446
|
-
});
|
|
18447
|
-
if (cell == null || cell === false) return null;
|
|
18448
|
-
return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: cell }, col.name);
|
|
18709
|
+
)
|
|
18449
18710
|
}
|
|
18450
|
-
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
row[rowKey] ?? ri
|
|
18455
|
-
);
|
|
18711
|
+
)
|
|
18712
|
+
}
|
|
18713
|
+
)
|
|
18714
|
+
] }, row[rowKey] ?? ri);
|
|
18456
18715
|
}),
|
|
18457
18716
|
virtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
|
|
18458
18717
|
] })
|
|
@@ -18578,6 +18837,26 @@ var STableImpl = /* @__PURE__ */ react.forwardRef(function STable({
|
|
|
18578
18837
|
);
|
|
18579
18838
|
});
|
|
18580
18839
|
var STable2 = STableImpl;
|
|
18840
|
+
function isOwnControl(target, cell) {
|
|
18841
|
+
if (!(target instanceof Element)) return false;
|
|
18842
|
+
if (target.closest("[data-expansion-toggle]")) return false;
|
|
18843
|
+
const control = target.closest(ROW_INTERACTIVE_SELECTOR);
|
|
18844
|
+
return control != null && cell.contains(control);
|
|
18845
|
+
}
|
|
18846
|
+
function ExpansionToggleCell({ expanded, children }) {
|
|
18847
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-(--cmp-tree-item-contentGap)", children: [
|
|
18848
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-expansion-toggle": true, className: "flex shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18849
|
+
SGhostButton,
|
|
18850
|
+
{
|
|
18851
|
+
icon: expanded ? "caretDown" : "caretRight",
|
|
18852
|
+
size: "xxs",
|
|
18853
|
+
ariaLabel: expanded ? "collapse row" : "expand row",
|
|
18854
|
+
ariaExpanded: expanded
|
|
18855
|
+
}
|
|
18856
|
+
) }),
|
|
18857
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children })
|
|
18858
|
+
] });
|
|
18859
|
+
}
|
|
18581
18860
|
var COUNT_NULL = "var(--cmp-table-bar-count-null)";
|
|
18582
18861
|
var totalColor = (n) => n > 0 ? "var(--cmp-table-bar-count-total)" : COUNT_NULL;
|
|
18583
18862
|
var selectedColor = (n) => n > 0 ? "var(--color-brilliantblue-75)" : COUNT_NULL;
|
|
@@ -18646,9 +18925,7 @@ var STableBar = /* @__PURE__ */ react.forwardRef(function STableBar2({ title, to
|
|
|
18646
18925
|
}
|
|
18647
18926
|
);
|
|
18648
18927
|
});
|
|
18649
|
-
var
|
|
18650
|
-
var EXPAND_MOTION = { duration: 220, easing: "ease-out" };
|
|
18651
|
-
var COLLAPSE_MOTION = { duration: 140, easing: "ease-in" };
|
|
18928
|
+
var NO_KEYS2 = /* @__PURE__ */ new Set();
|
|
18652
18929
|
function STreeTable({
|
|
18653
18930
|
rows = [],
|
|
18654
18931
|
columns = [],
|
|
@@ -18701,11 +18978,11 @@ function STreeTable({
|
|
|
18701
18978
|
[rows, expandedSet, rowKey, childrenKey]
|
|
18702
18979
|
);
|
|
18703
18980
|
const tableRef = react.useRef(null);
|
|
18704
|
-
const [closingKeys, setClosingKeys] = react.useState(
|
|
18981
|
+
const [closingKeys, setClosingKeys] = react.useState(NO_KEYS2);
|
|
18705
18982
|
const [prevExpand, setPrevExpand] = react.useState({ rows, expandedSet });
|
|
18706
18983
|
if (prevExpand.rows !== rows || prevExpand.expandedSet !== expandedSet) {
|
|
18707
18984
|
setPrevExpand({ rows, expandedSet });
|
|
18708
|
-
let nextClosing =
|
|
18985
|
+
let nextClosing = NO_KEYS2;
|
|
18709
18986
|
if (prevExpand.rows === rows && motionAllowed()) {
|
|
18710
18987
|
const collapsed = [...prevExpand.expandedSet].filter((key) => !expandedSet.has(key));
|
|
18711
18988
|
const expanded = [...expandedSet].filter((key) => !prevExpand.expandedSet.has(key));
|
|
@@ -18754,11 +19031,15 @@ function STreeTable({
|
|
|
18754
19031
|
motionRef.current = null;
|
|
18755
19032
|
}
|
|
18756
19033
|
const scrollEl = tableRef.current?.getScrollElement();
|
|
19034
|
+
const rowKeys = renderedRows.map(keyOf);
|
|
18757
19035
|
const keys = added.size > 0 ? added : removed;
|
|
18758
19036
|
const canAnimate = scrollEl != null && motionAllowed() && (added.size === 0 || removed.size === 0);
|
|
18759
19037
|
const motion = canAnimate ? startRowMotion({
|
|
18760
19038
|
scrollEl,
|
|
18761
|
-
|
|
19039
|
+
isInBlock: (tr) => {
|
|
19040
|
+
const key = rowKeys[Number(tr.dataset.rowIndex)];
|
|
19041
|
+
return key !== void 0 && keys.has(key);
|
|
19042
|
+
},
|
|
18762
19043
|
blockKeys: keys,
|
|
18763
19044
|
from,
|
|
18764
19045
|
to: added.size > 0 ? 1 : 0
|
|
@@ -18769,11 +19050,11 @@ function STreeTable({
|
|
|
18769
19050
|
void motion.finished.then((completed) => {
|
|
18770
19051
|
if (!completed || motionRef.current !== motion) return;
|
|
18771
19052
|
motion.done = true;
|
|
18772
|
-
setClosingKeys(
|
|
19053
|
+
setClosingKeys(NO_KEYS2);
|
|
18773
19054
|
});
|
|
18774
19055
|
}
|
|
18775
19056
|
} else if (closingKeys.size > 0) {
|
|
18776
|
-
setClosingKeys(
|
|
19057
|
+
setClosingKeys(NO_KEYS2);
|
|
18777
19058
|
}
|
|
18778
19059
|
}, [renderedRows]);
|
|
18779
19060
|
react.useEffect(() => () => motionRef.current?.cancel(), []);
|
|
@@ -18846,7 +19127,7 @@ function STreeTable({
|
|
|
18846
19127
|
className: cn(className, hasChildren && "cursor-pointer"),
|
|
18847
19128
|
style,
|
|
18848
19129
|
onClick: hasChildren ? (event) => {
|
|
18849
|
-
if (
|
|
19130
|
+
if (isOwnControl2(event.target, event.currentTarget)) return;
|
|
18850
19131
|
event.stopPropagation();
|
|
18851
19132
|
toggleExpanded(row);
|
|
18852
19133
|
} : void 0,
|
|
@@ -18901,96 +19182,8 @@ function flattenRows(nodes, isOpen, childrenOf) {
|
|
|
18901
19182
|
walk(nodes);
|
|
18902
19183
|
return out;
|
|
18903
19184
|
}
|
|
18904
|
-
function sameKeys(a, b) {
|
|
18905
|
-
return a.size === b.size && [...a].every((key) => b.has(key));
|
|
18906
|
-
}
|
|
18907
|
-
function motionAllowed() {
|
|
18908
|
-
if (typeof window === "undefined" || typeof Element.prototype.animate !== "function")
|
|
18909
|
-
return false;
|
|
18910
|
-
return !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
18911
|
-
}
|
|
18912
|
-
function startRowMotion({
|
|
18913
|
-
scrollEl,
|
|
18914
|
-
rowKeys,
|
|
18915
|
-
blockKeys,
|
|
18916
|
-
from,
|
|
18917
|
-
to
|
|
18918
|
-
}) {
|
|
18919
|
-
const spec = to === 1 ? EXPAND_MOTION : COLLAPSE_MOTION;
|
|
18920
|
-
const duration = spec.duration * Math.abs(to - from);
|
|
18921
|
-
if (duration < 1) return null;
|
|
18922
|
-
const trs = [...scrollEl.querySelectorAll("tr[data-row-index]")].map((tr) => {
|
|
18923
|
-
const key = rowKeys[Number(tr.dataset.rowIndex)];
|
|
18924
|
-
return {
|
|
18925
|
-
tr,
|
|
18926
|
-
inBlock: key !== void 0 && blockKeys.has(key),
|
|
18927
|
-
height: tr.getBoundingClientRect().height
|
|
18928
|
-
};
|
|
18929
|
-
});
|
|
18930
|
-
const viewBottom = scrollEl.scrollTop + scrollEl.clientHeight;
|
|
18931
|
-
const openAt = (offset) => from + (to - from) * offset;
|
|
18932
|
-
const translate = (shift2, s) => `translateY(${-shift2 * (1 - s)}px)`;
|
|
18933
|
-
const timing = {
|
|
18934
|
-
duration,
|
|
18935
|
-
easing: spec.easing,
|
|
18936
|
-
// 접힘은 남겨 둔 행이 빠질 때까지 끝 위치를 붙잡는다 — 놓으면 그 사이 한 프레임 제자리로 튄다
|
|
18937
|
-
fill: to === 0 ? "forwards" : "none"
|
|
18938
|
-
};
|
|
18939
|
-
const animations = [];
|
|
18940
|
-
let shift = 0;
|
|
18941
|
-
let blockHeight = 0;
|
|
18942
|
-
let y = 0;
|
|
18943
|
-
trs.forEach(({ tr, inBlock, height }, i) => {
|
|
18944
|
-
if (inBlock && !trs[i - 1]?.inBlock) {
|
|
18945
|
-
blockHeight = 0;
|
|
18946
|
-
for (let j = i; j < trs.length && trs[j].inBlock; j++) blockHeight += trs[j].height;
|
|
18947
|
-
shift += blockHeight;
|
|
18948
|
-
y = 0;
|
|
18949
|
-
}
|
|
18950
|
-
if (shift === 0 || tr.offsetTop - shift > viewBottom) return;
|
|
18951
|
-
if (!inBlock) {
|
|
18952
|
-
animations.push(
|
|
18953
|
-
tr.animate(
|
|
18954
|
-
[{ transform: translate(shift, from) }, { transform: translate(shift, to) }],
|
|
18955
|
-
timing
|
|
18956
|
-
)
|
|
18957
|
-
);
|
|
18958
|
-
return;
|
|
18959
|
-
}
|
|
18960
|
-
const rowY = y;
|
|
18961
|
-
const clipAt = (s) => Math.min(height, Math.max(0, blockHeight * (1 - s) - rowY));
|
|
18962
|
-
const bends = [1 - rowY / blockHeight, 1 - (rowY + height) / blockHeight].map((s) => (s - from) / (to - from)).filter((offset) => offset > 0 && offset < 1);
|
|
18963
|
-
const offsets = [0, ...bends, 1].sort((a, b) => a - b);
|
|
18964
|
-
animations.push(
|
|
18965
|
-
tr.animate(
|
|
18966
|
-
offsets.map((offset) => ({
|
|
18967
|
-
offset,
|
|
18968
|
-
transform: translate(shift, openAt(offset)),
|
|
18969
|
-
clipPath: `inset(${clipAt(openAt(offset))}px 0 0 0)`
|
|
18970
|
-
})),
|
|
18971
|
-
timing
|
|
18972
|
-
)
|
|
18973
|
-
);
|
|
18974
|
-
y += height;
|
|
18975
|
-
});
|
|
18976
|
-
if (animations.length === 0) return null;
|
|
18977
|
-
return {
|
|
18978
|
-
keys: blockKeys,
|
|
18979
|
-
to,
|
|
18980
|
-
done: false,
|
|
18981
|
-
finished: Promise.all(animations.map((animation) => animation.finished)).then(
|
|
18982
|
-
() => true,
|
|
18983
|
-
() => false
|
|
18984
|
-
),
|
|
18985
|
-
openness: () => {
|
|
18986
|
-
const progress = animations[0].effect?.getComputedTiming().progress;
|
|
18987
|
-
return progress == null ? to : openAt(progress);
|
|
18988
|
-
},
|
|
18989
|
-
cancel: () => animations.forEach((animation) => animation.cancel())
|
|
18990
|
-
};
|
|
18991
|
-
}
|
|
18992
19185
|
var OWN_CONTROL_SELECTOR = 'a, button, input, select, textarea, label, [role="button"], [role="checkbox"], [contenteditable="true"]';
|
|
18993
|
-
function
|
|
19186
|
+
function isOwnControl2(target, cell) {
|
|
18994
19187
|
if (!(target instanceof Element)) return false;
|
|
18995
19188
|
if (target.closest("[data-tree-toggle]")) return false;
|
|
18996
19189
|
const control = target.closest(OWN_CONTROL_SELECTOR);
|