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