sellmate-design-system-react 9.0.0-beta.92 → 9.0.0-beta.93
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 +20 -2
- package/dist/components/SBarChart/README.md +1 -0
- package/dist/components/SBarChart/SBarChart.d.ts +7 -0
- package/dist/components/SBarcodeInput/README.md +2 -0
- package/dist/components/SCheckbox/README.md +2 -0
- package/dist/components/SGhostButton/README.md +2 -0
- package/dist/components/SIcon/README.md +2 -0
- package/dist/components/SInput/README.md +2 -0
- package/dist/components/SLineChart/README.md +1 -0
- package/dist/components/SLineChart/SLineChart.d.ts +5 -0
- package/dist/components/SLineChart/lineChart.config.d.ts +1 -2
- package/dist/components/SMeatballButton/README.md +2 -0
- package/dist/components/SNumberInput/README.md +2 -0
- package/dist/components/SSelect/README.md +8 -3
- package/dist/components/SSelect/SSelect.d.ts +17 -3
- package/dist/components/SSelect/select.config.d.ts +34 -4
- package/dist/components/STable/README.md +1 -1
- package/dist/components/STable/STable.d.ts +3 -0
- package/dist/components/STag/README.md +8 -0
- package/dist/components/STreeTable/STreeTable.examples.d.ts +7 -0
- package/dist/index.cjs +1126 -671
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1126 -671
- package/dist/index.js.map +1 -1
- package/dist/lib/chart/cartesian.d.ts +1 -3
- package/dist/lib/chart/category-labels.d.ts +37 -0
- package/dist/lib/chart/use-element-size.d.ts +4 -1
- package/dist/lib/tree-row.d.ts +74 -0
- package/dist/llms-full.txt +40 -6
- package/dist/llms.txt +20 -2
- package/dist/styles.css +9 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7682,32 +7682,47 @@ var TREE_LABEL_TYPOGRAPHY = {
|
|
|
7682
7682
|
rowHeight: "h-[32px]"
|
|
7683
7683
|
}
|
|
7684
7684
|
};
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7685
|
+
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)";
|
|
7686
|
+
var TREE_ROW_BORDER_CLASS = "border-b border-(--cmp-tree-item-border-default)";
|
|
7687
|
+
var TREE_ROW_ACTIVE_CLASS = "bg-(--cmp-tree-item-bg-hover)";
|
|
7688
|
+
var TREE_CONTENT_CLASS = "box-border flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap)";
|
|
7689
|
+
function treeLabelClass(size, {
|
|
7690
|
+
emphasized,
|
|
7691
|
+
disabled,
|
|
7692
|
+
selectable
|
|
7693
|
+
}) {
|
|
7694
|
+
const typography = TREE_LABEL_TYPOGRAPHY[size];
|
|
7695
|
+
return cn(
|
|
7696
|
+
"block min-w-0 flex-1 truncate py-(--cmp-tree-item-paddingY) text-(--cmp-tree-item-text-default)",
|
|
7697
|
+
emphasized ? typography.selected : typography.default,
|
|
7698
|
+
disabled && "opacity-60",
|
|
7699
|
+
disabled ? "cursor-not-allowed" : selectable ? "cursor-pointer" : void 0
|
|
7700
|
+
);
|
|
7691
7701
|
}
|
|
7692
|
-
function
|
|
7693
|
-
|
|
7702
|
+
function isRowControlClick(event) {
|
|
7703
|
+
const control = event.target.closest("[data-tree-control]");
|
|
7704
|
+
return control != null && event.currentTarget.contains(control);
|
|
7694
7705
|
}
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7706
|
+
function TreeCaret({
|
|
7707
|
+
expanded,
|
|
7708
|
+
onToggle
|
|
7709
|
+
}) {
|
|
7710
|
+
return /* @__PURE__ */ jsx(
|
|
7711
|
+
SGhostButton,
|
|
7712
|
+
{
|
|
7713
|
+
icon: expanded ? "caretDown" : "caretRight",
|
|
7714
|
+
size: "xxs",
|
|
7715
|
+
ariaLabel: expanded ? "collapse tree item" : "expand tree item",
|
|
7716
|
+
ariaExpanded: expanded,
|
|
7717
|
+
onClick: (event) => {
|
|
7718
|
+
event.stopPropagation();
|
|
7719
|
+
onToggle(event);
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
7705
7722
|
);
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
indeterminate: childStates.some((state2) => state2.indeterminate) || selectedCount > 0 && selectedCount < childStates.length
|
|
7710
|
-
};
|
|
7723
|
+
}
|
|
7724
|
+
function TreeCaretSpacer() {
|
|
7725
|
+
return /* @__PURE__ */ jsx("span", { className: "size-(--cmp-ghostButton-xxs-size) shrink-0", "aria-hidden": "true" });
|
|
7711
7726
|
}
|
|
7712
7727
|
function TreeConnector({
|
|
7713
7728
|
guideline,
|
|
@@ -7758,6 +7773,83 @@ function TreeConnector({
|
|
|
7758
7773
|
] })
|
|
7759
7774
|
] });
|
|
7760
7775
|
}
|
|
7776
|
+
function TreeAllRow({
|
|
7777
|
+
label = "\uC804\uCCB4",
|
|
7778
|
+
selected,
|
|
7779
|
+
indeterminate = false,
|
|
7780
|
+
size = "sm",
|
|
7781
|
+
border = true,
|
|
7782
|
+
active = false,
|
|
7783
|
+
tabIndex,
|
|
7784
|
+
onKeyDown,
|
|
7785
|
+
onToggle,
|
|
7786
|
+
className,
|
|
7787
|
+
...rest
|
|
7788
|
+
}) {
|
|
7789
|
+
return /* @__PURE__ */ jsx(
|
|
7790
|
+
"div",
|
|
7791
|
+
{
|
|
7792
|
+
role: "treeitem",
|
|
7793
|
+
"aria-level": 1,
|
|
7794
|
+
"aria-selected": selected,
|
|
7795
|
+
tabIndex,
|
|
7796
|
+
onKeyDown,
|
|
7797
|
+
onClick: (event) => {
|
|
7798
|
+
if (!isRowControlClick(event)) onToggle(event);
|
|
7799
|
+
},
|
|
7800
|
+
className: cn(
|
|
7801
|
+
TREE_ROW_CLASS,
|
|
7802
|
+
"cursor-pointer",
|
|
7803
|
+
TREE_LABEL_TYPOGRAPHY[size].rowHeight,
|
|
7804
|
+
border && TREE_ROW_BORDER_CLASS,
|
|
7805
|
+
active && TREE_ROW_ACTIVE_CLASS,
|
|
7806
|
+
className
|
|
7807
|
+
),
|
|
7808
|
+
...rest,
|
|
7809
|
+
children: /* @__PURE__ */ jsxs("div", { className: TREE_CONTENT_CLASS, children: [
|
|
7810
|
+
/* @__PURE__ */ jsx("span", { "data-tree-control": true, className: "flex shrink-0", children: /* @__PURE__ */ jsx(SCheckbox, { value: indeterminate ? null : selected, onValueChange: () => onToggle() }) }),
|
|
7811
|
+
/* @__PURE__ */ jsx(
|
|
7812
|
+
"span",
|
|
7813
|
+
{
|
|
7814
|
+
className: treeLabelClass(size, {
|
|
7815
|
+
emphasized: selected || indeterminate,
|
|
7816
|
+
disabled: false,
|
|
7817
|
+
selectable: false
|
|
7818
|
+
}),
|
|
7819
|
+
children: label
|
|
7820
|
+
}
|
|
7821
|
+
)
|
|
7822
|
+
] })
|
|
7823
|
+
}
|
|
7824
|
+
);
|
|
7825
|
+
}
|
|
7826
|
+
function collectIds(nodes) {
|
|
7827
|
+
return nodes.flatMap((node) => [node.id, ...collectIds(node.children ?? [])]);
|
|
7828
|
+
}
|
|
7829
|
+
function collectSelectableIds(node) {
|
|
7830
|
+
if (node.disabled) return [];
|
|
7831
|
+
return [node.id, ...(node.children ?? []).flatMap(collectSelectableIds)];
|
|
7832
|
+
}
|
|
7833
|
+
function toSelectedSet(value) {
|
|
7834
|
+
return new Set(value ?? []);
|
|
7835
|
+
}
|
|
7836
|
+
var TREE_ALL_NODE = { id: "__all__", label: "\uC804\uCCB4" };
|
|
7837
|
+
function getSelectionState(node, selectedSet) {
|
|
7838
|
+
const selectableChildren = (node.children ?? []).filter(
|
|
7839
|
+
(child) => collectSelectableIds(child).length > 0
|
|
7840
|
+
);
|
|
7841
|
+
if (selectableChildren.length === 0) {
|
|
7842
|
+
return { selected: selectedSet.has(node.id), indeterminate: false };
|
|
7843
|
+
}
|
|
7844
|
+
const childStates = selectableChildren.map(
|
|
7845
|
+
(child) => getSelectionState(child, selectedSet)
|
|
7846
|
+
);
|
|
7847
|
+
const selectedCount = childStates.filter((state2) => state2.selected).length;
|
|
7848
|
+
return {
|
|
7849
|
+
selected: selectedCount === childStates.length,
|
|
7850
|
+
indeterminate: childStates.some((state2) => state2.indeterminate) || selectedCount > 0 && selectedCount < childStates.length
|
|
7851
|
+
};
|
|
7852
|
+
}
|
|
7761
7853
|
function TreeBranch({ expanded, children }) {
|
|
7762
7854
|
const branchRef = useInertRef(!expanded);
|
|
7763
7855
|
return /* @__PURE__ */ jsx(
|
|
@@ -7773,10 +7865,6 @@ function TreeBranch({ expanded, children }) {
|
|
|
7773
7865
|
}
|
|
7774
7866
|
);
|
|
7775
7867
|
}
|
|
7776
|
-
function isRowControlClick(event) {
|
|
7777
|
-
const control = event.target.closest("[data-tree-control]");
|
|
7778
|
-
return control != null && event.currentTarget.contains(control);
|
|
7779
|
-
}
|
|
7780
7868
|
function STreeAllItem({
|
|
7781
7869
|
selected,
|
|
7782
7870
|
indeterminate,
|
|
@@ -7787,39 +7875,17 @@ function STreeAllItem({
|
|
|
7787
7875
|
onKeyDown,
|
|
7788
7876
|
onSelect
|
|
7789
7877
|
}) {
|
|
7790
|
-
const checkboxValue = indeterminate ? null : selected;
|
|
7791
|
-
const typography = TREE_LABEL_TYPOGRAPHY[size];
|
|
7792
|
-
const labelTypo = selected || indeterminate ? typography.selected : typography.default;
|
|
7793
7878
|
return /* @__PURE__ */ jsx(
|
|
7794
|
-
|
|
7879
|
+
TreeAllRow,
|
|
7795
7880
|
{
|
|
7796
|
-
role: "treeitem",
|
|
7797
|
-
"aria-level": 1,
|
|
7798
|
-
"aria-selected": selected,
|
|
7799
7881
|
"data-tree-row": rowId,
|
|
7800
7882
|
tabIndex,
|
|
7801
7883
|
onKeyDown,
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
typography.rowHeight,
|
|
7808
|
-
border && "border-b border-(--cmp-tree-item-border-default)"
|
|
7809
|
-
),
|
|
7810
|
-
children: /* @__PURE__ */ jsxs("div", { className: "box-border flex min-w-0 flex-1 items-center gap-(--cmp-tree-item-contentGap)", children: [
|
|
7811
|
-
/* @__PURE__ */ jsx("span", { "data-tree-control": true, className: "flex shrink-0", children: /* @__PURE__ */ jsx(SCheckbox, { value: checkboxValue, onValueChange: () => onSelect() }) }),
|
|
7812
|
-
/* @__PURE__ */ jsx(
|
|
7813
|
-
"span",
|
|
7814
|
-
{
|
|
7815
|
-
className: cn(
|
|
7816
|
-
"block min-w-0 flex-1 truncate py-(--cmp-tree-item-paddingY) text-(--cmp-tree-item-text-default)",
|
|
7817
|
-
labelTypo
|
|
7818
|
-
),
|
|
7819
|
-
children: "\uC804\uCCB4"
|
|
7820
|
-
}
|
|
7821
|
-
)
|
|
7822
|
-
] })
|
|
7884
|
+
selected,
|
|
7885
|
+
indeterminate,
|
|
7886
|
+
border,
|
|
7887
|
+
size,
|
|
7888
|
+
onToggle: onSelect
|
|
7823
7889
|
}
|
|
7824
7890
|
);
|
|
7825
7891
|
}
|
|
@@ -7860,8 +7926,6 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
|
|
|
7860
7926
|
const leadingNode = typeof leading === "function" ? leading(renderState) : leading;
|
|
7861
7927
|
const trailingNode = typeof trailing === "function" ? trailing(renderState) : trailing;
|
|
7862
7928
|
const checkboxValue = indeterminate ? null : selected;
|
|
7863
|
-
const typography = TREE_LABEL_TYPOGRAPHY[size];
|
|
7864
|
-
const labelTypo = selected || indeterminate ? typography.selected : typography.default;
|
|
7865
7929
|
const handleSelect = (event) => {
|
|
7866
7930
|
if (node.disabled) return;
|
|
7867
7931
|
onTreeSelect?.(node, event);
|
|
@@ -7887,10 +7951,9 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
|
|
|
7887
7951
|
onKeyDown,
|
|
7888
7952
|
onClick: handleRowClick,
|
|
7889
7953
|
className: cn(
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
border &&
|
|
7893
|
-
"hover:bg-(--cmp-tree-item-bg-hover)",
|
|
7954
|
+
TREE_ROW_CLASS,
|
|
7955
|
+
TREE_LABEL_TYPOGRAPHY[size].rowHeight,
|
|
7956
|
+
border && TREE_ROW_BORDER_CLASS,
|
|
7894
7957
|
hasChildren && "cursor-pointer",
|
|
7895
7958
|
className
|
|
7896
7959
|
),
|
|
@@ -7907,25 +7970,8 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
|
|
|
7907
7970
|
ancestorLast
|
|
7908
7971
|
}
|
|
7909
7972
|
),
|
|
7910
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
7911
|
-
hasChildren ? /* @__PURE__ */ jsx(
|
|
7912
|
-
SGhostButton,
|
|
7913
|
-
{
|
|
7914
|
-
icon: expanded ? "caretDown" : "caretRight",
|
|
7915
|
-
size: "xxs",
|
|
7916
|
-
ariaLabel: expanded ? "collapse tree item" : "expand tree item",
|
|
7917
|
-
ariaExpanded: expanded,
|
|
7918
|
-
onClick: (event) => {
|
|
7919
|
-
event.stopPropagation();
|
|
7920
|
-
onToggle?.(node, event);
|
|
7921
|
-
}
|
|
7922
|
-
}
|
|
7923
|
-
) : (
|
|
7924
|
-
// 자식이 없는 줄은 펼침 버튼 자리를 빈 칸으로 남겨 라벨의 시작점을 맞춘다.
|
|
7925
|
-
// 크기는 자리를 대신하는 그 버튼(`SGhostButton size="xxs"`)의 토큰을 그대로 쓴다 —
|
|
7926
|
-
// 버튼 크기가 바뀌면 빈 칸도 같이 따라가야 줄이 어긋나지 않는다.
|
|
7927
|
-
/* @__PURE__ */ jsx("span", { className: "size-(--cmp-ghostButton-xxs-size) shrink-0", "aria-hidden": "true" })
|
|
7928
|
-
),
|
|
7973
|
+
/* @__PURE__ */ jsxs("div", { className: TREE_CONTENT_CLASS, children: [
|
|
7974
|
+
hasChildren ? /* @__PURE__ */ jsx(TreeCaret, { expanded, onToggle: (event) => onToggle?.(node, event) }) : /* @__PURE__ */ jsx(TreeCaretSpacer, {}),
|
|
7929
7975
|
leadingNode,
|
|
7930
7976
|
selectable && /* @__PURE__ */ jsx(
|
|
7931
7977
|
"span",
|
|
@@ -7947,12 +7993,11 @@ var STreeItemImpl = /* @__PURE__ */ forwardRef(function STreeItem({
|
|
|
7947
7993
|
"span",
|
|
7948
7994
|
{
|
|
7949
7995
|
"data-tree-label": true,
|
|
7950
|
-
className:
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
),
|
|
7996
|
+
className: treeLabelClass(size, {
|
|
7997
|
+
emphasized: selected || indeterminate,
|
|
7998
|
+
disabled: Boolean(node.disabled),
|
|
7999
|
+
selectable
|
|
8000
|
+
}),
|
|
7956
8001
|
children: node.label
|
|
7957
8002
|
}
|
|
7958
8003
|
),
|
|
@@ -15744,13 +15789,13 @@ var SSearchInput = /* @__PURE__ */ forwardRef(function SSearchInput2({
|
|
|
15744
15789
|
});
|
|
15745
15790
|
|
|
15746
15791
|
// src/components/SSelect/select.config.ts
|
|
15747
|
-
function flattenOptions(options, depth = 1) {
|
|
15792
|
+
function flattenOptions(options, depth = 1, inheritDisabled = true) {
|
|
15748
15793
|
const out = [];
|
|
15749
15794
|
for (const o of options) {
|
|
15750
15795
|
const hasChildren = Array.isArray(o.children) && o.children.length > 0;
|
|
15751
15796
|
if (hasChildren) {
|
|
15752
|
-
const childFlat = flattenOptions(o.children, depth + 1);
|
|
15753
|
-
const allChildrenDisabled = childFlat.every((c) => c.disabled);
|
|
15797
|
+
const childFlat = flattenOptions(o.children, depth + 1, inheritDisabled);
|
|
15798
|
+
const allChildrenDisabled = inheritDisabled && childFlat.every((c) => c.disabled);
|
|
15754
15799
|
out.push({
|
|
15755
15800
|
value: String(o.value),
|
|
15756
15801
|
label: o.label,
|
|
@@ -15784,12 +15829,71 @@ function collectGroupLeafValues(flat, groupIndex) {
|
|
|
15784
15829
|
}
|
|
15785
15830
|
return values;
|
|
15786
15831
|
}
|
|
15832
|
+
function visibleTreeRows(rows, isExpanded) {
|
|
15833
|
+
const out = [];
|
|
15834
|
+
const sourceIndex = [];
|
|
15835
|
+
let hiddenBelow = null;
|
|
15836
|
+
rows.forEach((row, i) => {
|
|
15837
|
+
if (hiddenBelow != null && row.depth > hiddenBelow) return;
|
|
15838
|
+
hiddenBelow = null;
|
|
15839
|
+
out.push(row);
|
|
15840
|
+
sourceIndex.push(i);
|
|
15841
|
+
if (row.isGroup && !isExpanded(row)) hiddenBelow = row.depth;
|
|
15842
|
+
});
|
|
15843
|
+
return { rows: out, sourceIndex };
|
|
15844
|
+
}
|
|
15845
|
+
function treeLineInfo(rows) {
|
|
15846
|
+
const isLast = new Array(rows.length);
|
|
15847
|
+
const seen = [];
|
|
15848
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
15849
|
+
const depth = rows[i].depth;
|
|
15850
|
+
isLast[i] = !seen[depth];
|
|
15851
|
+
seen[depth] = true;
|
|
15852
|
+
seen.length = depth + 1;
|
|
15853
|
+
}
|
|
15854
|
+
const ancestors = [];
|
|
15855
|
+
const parents = [];
|
|
15856
|
+
const parentOf = new Array(rows.length);
|
|
15857
|
+
const posInSet = new Array(rows.length);
|
|
15858
|
+
const siblingCount = /* @__PURE__ */ new Map();
|
|
15859
|
+
rows.forEach((row, i) => {
|
|
15860
|
+
const parent = row.depth > 1 ? parents[row.depth - 2] ?? -1 : -1;
|
|
15861
|
+
parentOf[i] = parent;
|
|
15862
|
+
const count = (siblingCount.get(parent) ?? 0) + 1;
|
|
15863
|
+
siblingCount.set(parent, count);
|
|
15864
|
+
posInSet[i] = count;
|
|
15865
|
+
parents.length = row.depth - 1;
|
|
15866
|
+
parents[row.depth - 1] = i;
|
|
15867
|
+
});
|
|
15868
|
+
return rows.map((row, i) => {
|
|
15869
|
+
ancestors.length = row.depth - 1;
|
|
15870
|
+
const ancestorLast = ancestors.slice();
|
|
15871
|
+
ancestors[row.depth - 1] = isLast[i];
|
|
15872
|
+
return {
|
|
15873
|
+
isLast: isLast[i],
|
|
15874
|
+
ancestorLast,
|
|
15875
|
+
posInSet: posInSet[i],
|
|
15876
|
+
setSize: siblingCount.get(parentOf[i]) ?? 1
|
|
15877
|
+
};
|
|
15878
|
+
});
|
|
15879
|
+
}
|
|
15880
|
+
function ancestorGroupValues(flat, selected) {
|
|
15881
|
+
const out = /* @__PURE__ */ new Set();
|
|
15882
|
+
const stack = [];
|
|
15883
|
+
for (const row of flat) {
|
|
15884
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= row.depth) stack.pop();
|
|
15885
|
+
if (selected.has(row.value)) for (const group of stack) out.add(group.value);
|
|
15886
|
+
if (row.isGroup) stack.push(row);
|
|
15887
|
+
}
|
|
15888
|
+
return out;
|
|
15889
|
+
}
|
|
15787
15890
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
15788
15891
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
15789
15892
|
var OPTION_ROW_HEIGHT = 28;
|
|
15790
15893
|
var DEFAULT_VIRTUAL_BUFFER = 10;
|
|
15791
15894
|
var DEFAULT_REACH_END_THRESHOLD = 10;
|
|
15792
15895
|
var DEFAULT_SEARCH_DEBOUNCE = 300;
|
|
15896
|
+
var NO_SEARCH_COLLAPSED = /* @__PURE__ */ new Set();
|
|
15793
15897
|
var SEARCH_THRESHOLD = 11;
|
|
15794
15898
|
function isListSized(el) {
|
|
15795
15899
|
return el.clientHeight > 0 && getComputedStyle(el).maxHeight !== "none";
|
|
@@ -15844,8 +15948,9 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15844
15948
|
className,
|
|
15845
15949
|
style
|
|
15846
15950
|
}, ref) {
|
|
15847
|
-
const multi = type === "multi" || type === "multi_depth";
|
|
15951
|
+
const multi = type === "multi" || type === "multi_depth" || type === "multi_tree";
|
|
15848
15952
|
const isDepth = type === "default_depth" || type === "multi_depth";
|
|
15953
|
+
const isTree = type === "tree" || type === "multi_tree";
|
|
15849
15954
|
const pagingEnabled = onReachEnd != null;
|
|
15850
15955
|
const partialOptions = pagingEnabled || serverSearch;
|
|
15851
15956
|
const portalContainer = usePortalContainer();
|
|
@@ -15855,6 +15960,8 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15855
15960
|
const [open, setOpen] = useState(false);
|
|
15856
15961
|
const [pendingOpen, setPendingOpen] = useState(false);
|
|
15857
15962
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
15963
|
+
const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
|
|
15964
|
+
const [searchCollapsed, setSearchCollapsed] = useState(NO_SEARCH_COLLAPSED);
|
|
15858
15965
|
const baseId = useId();
|
|
15859
15966
|
const listboxId = `${baseId}-listbox`;
|
|
15860
15967
|
const activeOptionId = `${baseId}-active-option`;
|
|
@@ -15878,10 +15985,11 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15878
15985
|
const alignSelectedTopRef = useRef(false);
|
|
15879
15986
|
const selectedAnchorRef = useRef(-1);
|
|
15880
15987
|
const rangeAnchorRef = useRef(null);
|
|
15881
|
-
const
|
|
15988
|
+
const groupIsValue = type === "tree";
|
|
15989
|
+
const flat = useMemo(() => flattenOptions(options, 1, !groupIsValue), [groupIsValue, options]);
|
|
15882
15990
|
const selectable = useMemo(() => selectableOptions(flat), [flat]);
|
|
15883
15991
|
const hasLeaf = useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
15884
|
-
const allOptionsDisabled = hasLeaf && selectable.length === 0;
|
|
15992
|
+
const allOptionsDisabled = groupIsValue ? flat.length > 0 && flat.every((o) => o.disabled) : hasLeaf && selectable.length === 0;
|
|
15885
15993
|
const effectiveDisabled = disabled || allOptionsDisabled;
|
|
15886
15994
|
const searchVisible = showSearch && (partialOptions || selectable.length >= SEARCH_THRESHOLD);
|
|
15887
15995
|
const labelByValue = useMemo(() => new Map(flat.map((o) => [o.value, o.label])), [flat]);
|
|
@@ -15924,6 +16032,10 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15924
16032
|
if (next) {
|
|
15925
16033
|
dismissedByPointerRef.current = false;
|
|
15926
16034
|
alignSelectedTopRef.current = true;
|
|
16035
|
+
if (isTree) {
|
|
16036
|
+
setExpanded(ancestorGroupValues(flat, new Set(selectedRef.current)));
|
|
16037
|
+
setSearchCollapsed(NO_SEARCH_COLLAPSED);
|
|
16038
|
+
}
|
|
15927
16039
|
} else {
|
|
15928
16040
|
setPendingOpen(false);
|
|
15929
16041
|
setSearch("");
|
|
@@ -15966,7 +16078,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15966
16078
|
},
|
|
15967
16079
|
resetValidation: () => setRuleError("")
|
|
15968
16080
|
});
|
|
15969
|
-
const
|
|
16081
|
+
const searched = useMemo(() => {
|
|
15970
16082
|
if (serverSearch || !searchVisible || search.trim() === "") return flat;
|
|
15971
16083
|
const q = search.trim().toLowerCase();
|
|
15972
16084
|
return filterDepthRows(
|
|
@@ -15974,23 +16086,53 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15974
16086
|
(o) => (isHtmlLabel(o.label) ? extractText(o.label) : o.label).toLowerCase().includes(q)
|
|
15975
16087
|
);
|
|
15976
16088
|
}, [flat, search, searchVisible, serverSearch]);
|
|
16089
|
+
const treeSearching = isTree && search.trim() !== "";
|
|
16090
|
+
const isGroupExpanded = useCallback(
|
|
16091
|
+
(groupValue) => treeSearching ? !searchCollapsed.has(groupValue) : expanded.has(groupValue),
|
|
16092
|
+
[expanded, searchCollapsed, treeSearching]
|
|
16093
|
+
);
|
|
16094
|
+
const treeRows = useMemo(
|
|
16095
|
+
() => isTree ? visibleTreeRows(searched, (row) => isGroupExpanded(row.value)) : null,
|
|
16096
|
+
[isGroupExpanded, isTree, searched]
|
|
16097
|
+
);
|
|
16098
|
+
const filtered = treeRows?.rows ?? searched;
|
|
16099
|
+
const treeLines = useMemo(() => treeRows ? treeLineInfo(treeRows.rows) : [], [treeRows]);
|
|
15977
16100
|
const allValues = selectable.map((o) => o.value);
|
|
15978
16101
|
const allSelected = !partialOptions && allValues.length > 0 && allValues.every((v) => selectedSet.has(v));
|
|
15979
16102
|
const selectAllVisible = multi && showSelectAll && !partialOptions && allValues.length > 0;
|
|
15980
|
-
const hasItems = filtered.some((o) => !o.isGroup);
|
|
16103
|
+
const hasItems = isTree ? filtered.length > 0 : filtered.some((o) => !o.isGroup);
|
|
15981
16104
|
const groupLeafValues = useMemo(() => {
|
|
15982
16105
|
const map = /* @__PURE__ */ new Map();
|
|
15983
16106
|
filtered.forEach((o, i) => {
|
|
15984
|
-
if (o.isGroup)
|
|
16107
|
+
if (!o.isGroup) return;
|
|
16108
|
+
map.set(
|
|
16109
|
+
i,
|
|
16110
|
+
treeRows ? collectGroupLeafValues(searched, treeRows.sourceIndex[i]) : collectGroupLeafValues(filtered, i)
|
|
16111
|
+
);
|
|
15985
16112
|
});
|
|
15986
16113
|
return map;
|
|
15987
|
-
}, [filtered]);
|
|
16114
|
+
}, [filtered, searched, treeRows]);
|
|
15988
16115
|
const navigableItems = useMemo(() => {
|
|
15989
16116
|
const items = [];
|
|
15990
16117
|
if (selectAllVisible) {
|
|
15991
16118
|
items.push({ key: "__select_all__", isSelectAll: true });
|
|
15992
16119
|
}
|
|
15993
16120
|
filtered.forEach((o, i) => {
|
|
16121
|
+
if (o.isGroup && isTree) {
|
|
16122
|
+
if (multi) {
|
|
16123
|
+
const leafValues = groupLeafValues.get(i) ?? [];
|
|
16124
|
+
items.push({
|
|
16125
|
+
key: `__group_${i}__`,
|
|
16126
|
+
isGroup: true,
|
|
16127
|
+
index: i,
|
|
16128
|
+
leafValues,
|
|
16129
|
+
disabled: o.disabled || leafValues.length === 0
|
|
16130
|
+
});
|
|
16131
|
+
} else {
|
|
16132
|
+
items.push({ key: o.value, value: o.value, index: i, disabled: o.disabled });
|
|
16133
|
+
}
|
|
16134
|
+
return;
|
|
16135
|
+
}
|
|
15994
16136
|
if (o.disabled) return;
|
|
15995
16137
|
if (o.isGroup) {
|
|
15996
16138
|
if (!multi) return;
|
|
@@ -16002,7 +16144,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16002
16144
|
}
|
|
16003
16145
|
});
|
|
16004
16146
|
return items;
|
|
16005
|
-
}, [filtered, groupLeafValues, multi, selectAllVisible]);
|
|
16147
|
+
}, [filtered, groupLeafValues, isTree, multi, selectAllVisible]);
|
|
16006
16148
|
const selectedAnchorIndex = useMemo(() => {
|
|
16007
16149
|
if (selectedSet.size === 0) return -1;
|
|
16008
16150
|
return navigableItems.findIndex((item4) => item4.value != null && selectedSet.has(item4.value));
|
|
@@ -16013,7 +16155,10 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16013
16155
|
const rowCount = filtered.length;
|
|
16014
16156
|
const metrics = metricsRef.current;
|
|
16015
16157
|
const lastSearchRef = useRef(search);
|
|
16016
|
-
|
|
16158
|
+
const lastExpansionRef = useRef({ expanded, searchCollapsed });
|
|
16159
|
+
const expansionChanged = lastExpansionRef.current.expanded !== expanded || lastExpansionRef.current.searchCollapsed !== searchCollapsed;
|
|
16160
|
+
lastExpansionRef.current = { expanded, searchCollapsed };
|
|
16161
|
+
if (lastSearchRef.current !== search || isTree && expansionChanged) {
|
|
16017
16162
|
lastSearchRef.current = search;
|
|
16018
16163
|
resetRowMetrics(metrics, rowCount);
|
|
16019
16164
|
measuredKeyRef.current = "";
|
|
@@ -16153,7 +16298,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16153
16298
|
);
|
|
16154
16299
|
const fullGroupByLeaf = useMemo(() => {
|
|
16155
16300
|
const map = /* @__PURE__ */ new Map();
|
|
16156
|
-
if (!showChips || type !== "multi_depth") return map;
|
|
16301
|
+
if (!showChips || type !== "multi_depth" && type !== "multi_tree") return map;
|
|
16157
16302
|
flat.forEach((o, i) => {
|
|
16158
16303
|
if (!o.isGroup) return;
|
|
16159
16304
|
const leafValues = collectGroupLeafValues(flat, i);
|
|
@@ -16231,6 +16376,21 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16231
16376
|
rangeAnchorRef.current = null;
|
|
16232
16377
|
emit3(allSelected ? [] : allValues);
|
|
16233
16378
|
};
|
|
16379
|
+
const handleSearchChange = (next) => {
|
|
16380
|
+
setSearch(next);
|
|
16381
|
+
setSearchCollapsed(NO_SEARCH_COLLAPSED);
|
|
16382
|
+
};
|
|
16383
|
+
const handleToggleExpand = (groupValue) => {
|
|
16384
|
+
rangeAnchorRef.current = null;
|
|
16385
|
+
const toggle = (prev) => {
|
|
16386
|
+
const next = new Set(prev);
|
|
16387
|
+
if (next.has(groupValue)) next.delete(groupValue);
|
|
16388
|
+
else next.add(groupValue);
|
|
16389
|
+
return next;
|
|
16390
|
+
};
|
|
16391
|
+
if (treeSearching) setSearchCollapsed(toggle);
|
|
16392
|
+
else setExpanded(toggle);
|
|
16393
|
+
};
|
|
16234
16394
|
const handleToggleGroup = (leafValues) => {
|
|
16235
16395
|
if (leafValues.length === 0) return;
|
|
16236
16396
|
rangeAnchorRef.current = null;
|
|
@@ -16303,6 +16463,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16303
16463
|
const selectFocusedItem = (extend = false) => {
|
|
16304
16464
|
if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
|
|
16305
16465
|
const focused = navigableItems[focusedIndex];
|
|
16466
|
+
if (focused.disabled) return;
|
|
16306
16467
|
if (focused.isSelectAll) {
|
|
16307
16468
|
handleSelectAll();
|
|
16308
16469
|
return;
|
|
@@ -16331,6 +16492,41 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16331
16492
|
moveFocusedIndex("prev");
|
|
16332
16493
|
return;
|
|
16333
16494
|
}
|
|
16495
|
+
const input = event.target instanceof HTMLInputElement ? event.target : null;
|
|
16496
|
+
const caretAtEdge = input == null || input.selectionStart === input.selectionEnd && input.selectionStart === (event.key === "ArrowRight" ? input.value.length : 0);
|
|
16497
|
+
if (isTree && (event.key === "ArrowRight" || event.key === "ArrowLeft") && caretAtEdge) {
|
|
16498
|
+
const rowIndex = navigableItems[focusedIndex]?.index;
|
|
16499
|
+
const row = rowIndex != null ? filtered[rowIndex] : void 0;
|
|
16500
|
+
if (rowIndex == null || row == null) return;
|
|
16501
|
+
event.preventDefault();
|
|
16502
|
+
const rowExpanded = row.isGroup && isGroupExpanded(row.value);
|
|
16503
|
+
if (event.key === "ArrowRight") {
|
|
16504
|
+
if (!row.isGroup) return;
|
|
16505
|
+
if (!rowExpanded) {
|
|
16506
|
+
handleToggleExpand(row.value);
|
|
16507
|
+
return;
|
|
16508
|
+
}
|
|
16509
|
+
for (let i = rowIndex + 1; i < filtered.length && filtered[i].depth > row.depth; i++) {
|
|
16510
|
+
const child = navigableItems.findIndex((item4) => item4.index === i);
|
|
16511
|
+
if (child >= 0) {
|
|
16512
|
+
setFocusedIndex(child);
|
|
16513
|
+
break;
|
|
16514
|
+
}
|
|
16515
|
+
}
|
|
16516
|
+
return;
|
|
16517
|
+
}
|
|
16518
|
+
if (rowExpanded) {
|
|
16519
|
+
handleToggleExpand(row.value);
|
|
16520
|
+
return;
|
|
16521
|
+
}
|
|
16522
|
+
for (let i = rowIndex - 1; i >= 0; i--) {
|
|
16523
|
+
if (filtered[i].depth >= row.depth) continue;
|
|
16524
|
+
const parent = navigableItems.findIndex((item4) => item4.index === i);
|
|
16525
|
+
if (parent >= 0) setFocusedIndex(parent);
|
|
16526
|
+
return;
|
|
16527
|
+
}
|
|
16528
|
+
return;
|
|
16529
|
+
}
|
|
16334
16530
|
if (event.key === "Enter") {
|
|
16335
16531
|
event.preventDefault();
|
|
16336
16532
|
selectFocusedItem(event.shiftKey);
|
|
@@ -16375,7 +16571,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16375
16571
|
type: "button",
|
|
16376
16572
|
disabled: effectiveDisabled,
|
|
16377
16573
|
"aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerId}` : void 0,
|
|
16378
|
-
"aria-haspopup": "listbox",
|
|
16574
|
+
"aria-haspopup": isTree ? "tree" : "listbox",
|
|
16379
16575
|
"aria-expanded": open,
|
|
16380
16576
|
"aria-controls": open ? listboxId : void 0,
|
|
16381
16577
|
"aria-activedescendant": open && focusedIndex >= 0 ? activeOptionId : void 0,
|
|
@@ -16512,7 +16708,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16512
16708
|
{
|
|
16513
16709
|
ref: searchInputRef,
|
|
16514
16710
|
value: search,
|
|
16515
|
-
onValueChange:
|
|
16711
|
+
onValueChange: handleSearchChange,
|
|
16516
16712
|
onKeyDown: handleKeyDown,
|
|
16517
16713
|
placeholder: "\uAC80\uC0C9",
|
|
16518
16714
|
clearable: true,
|
|
@@ -16531,8 +16727,12 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16531
16727
|
indexOffset: windowStart,
|
|
16532
16728
|
topSpacer: win.top,
|
|
16533
16729
|
bottomSpacer: win.bottom,
|
|
16534
|
-
setSize: isDepth ? void 0 : rowCount,
|
|
16730
|
+
setSize: isDepth || isTree ? void 0 : rowCount,
|
|
16535
16731
|
multi,
|
|
16732
|
+
tree: isTree,
|
|
16733
|
+
isExpanded: isGroupExpanded,
|
|
16734
|
+
onToggleExpand: handleToggleExpand,
|
|
16735
|
+
treeLines,
|
|
16536
16736
|
selected,
|
|
16537
16737
|
groupLeafValues,
|
|
16538
16738
|
focusedValue: navigableItems[focusedIndex]?.key,
|
|
@@ -16586,6 +16786,10 @@ function ItemList({
|
|
|
16586
16786
|
bottomSpacer,
|
|
16587
16787
|
setSize,
|
|
16588
16788
|
multi,
|
|
16789
|
+
tree,
|
|
16790
|
+
isExpanded,
|
|
16791
|
+
onToggleExpand,
|
|
16792
|
+
treeLines,
|
|
16589
16793
|
selected,
|
|
16590
16794
|
groupLeafValues,
|
|
16591
16795
|
focusedValue,
|
|
@@ -16599,14 +16803,34 @@ function ItemList({
|
|
|
16599
16803
|
onHoverValue
|
|
16600
16804
|
}) {
|
|
16601
16805
|
const selectedSet = useMemo(() => new Set(selected), [selected]);
|
|
16806
|
+
const checkboxShiftRef = useRef(false);
|
|
16602
16807
|
const itemBase = cn(
|
|
16603
16808
|
"group/select-item flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
|
|
16604
16809
|
"text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
16605
16810
|
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listBoxItem-content-disabled)]"
|
|
16606
16811
|
);
|
|
16607
16812
|
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)]";
|
|
16608
|
-
return /* @__PURE__ */ jsxs("div", { id: listboxId, role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
16609
|
-
showSelectAll &&
|
|
16813
|
+
return /* @__PURE__ */ jsxs("div", { id: listboxId, role: tree ? "tree" : "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
16814
|
+
showSelectAll && (tree ? (
|
|
16815
|
+
// 트리의 전체선택 줄은 STree 의 "전체" 줄 그대로다 (lib/tree-row).
|
|
16816
|
+
/* @__PURE__ */ jsx(
|
|
16817
|
+
TreeAllRow,
|
|
16818
|
+
{
|
|
16819
|
+
"data-select-all-row": true,
|
|
16820
|
+
id: focusedValue === "__select_all__" ? activeOptionId : void 0,
|
|
16821
|
+
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
16822
|
+
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
16823
|
+
tabIndex: -1,
|
|
16824
|
+
label: selectAllLabel,
|
|
16825
|
+
selected: selectAllSelected,
|
|
16826
|
+
active: focusedValue === "__select_all__",
|
|
16827
|
+
className: "select-none",
|
|
16828
|
+
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
16829
|
+
onMouseLeave: () => onHoverValue(null),
|
|
16830
|
+
onToggle: onSelectAll
|
|
16831
|
+
}
|
|
16832
|
+
)
|
|
16833
|
+
) : /* @__PURE__ */ jsxs(
|
|
16610
16834
|
"button",
|
|
16611
16835
|
{
|
|
16612
16836
|
type: "button",
|
|
@@ -16627,12 +16851,109 @@ function ItemList({
|
|
|
16627
16851
|
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
16628
16852
|
]
|
|
16629
16853
|
}
|
|
16630
|
-
),
|
|
16854
|
+
)),
|
|
16631
16855
|
topSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: topSpacer } }),
|
|
16632
16856
|
items.map((o, windowIndex) => {
|
|
16633
16857
|
const idx = windowIndex + indexOffset;
|
|
16634
16858
|
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
16635
16859
|
const rowKey = `${o.value}-${idx}`;
|
|
16860
|
+
if (tree) {
|
|
16861
|
+
const togglesLeaves = multi && o.isGroup;
|
|
16862
|
+
const leafValues = togglesLeaves ? groupLeafValues.get(idx) ?? [] : [];
|
|
16863
|
+
const total = leafValues.length;
|
|
16864
|
+
const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
|
|
16865
|
+
const isSelected2 = selectedSet.has(o.value);
|
|
16866
|
+
const checkState = togglesLeaves ? total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null : isSelected2;
|
|
16867
|
+
const rowDisabled = o.disabled || togglesLeaves && total === 0;
|
|
16868
|
+
const focusKey = togglesLeaves ? `__group_${idx}__` : o.value;
|
|
16869
|
+
const focused = focusedValue === focusKey;
|
|
16870
|
+
const expanded = o.isGroup && isExpanded(o.value);
|
|
16871
|
+
const line = treeLines[idx];
|
|
16872
|
+
const toggle = () => {
|
|
16873
|
+
if (rowDisabled) return;
|
|
16874
|
+
if (togglesLeaves) onToggleGroup(leafValues);
|
|
16875
|
+
else onSelect(o.value, idx, checkboxShiftRef.current);
|
|
16876
|
+
};
|
|
16877
|
+
return (
|
|
16878
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events -- 키보드는 트리거(또는 검색 입력)가 aria-activedescendant 로 맡는다. 줄로 포커스를 옮기지 않는다
|
|
16879
|
+
/* @__PURE__ */ jsxs(
|
|
16880
|
+
"div",
|
|
16881
|
+
{
|
|
16882
|
+
tabIndex: -1,
|
|
16883
|
+
"data-select-row": true,
|
|
16884
|
+
"data-index": idx,
|
|
16885
|
+
id: focused ? activeOptionId : void 0,
|
|
16886
|
+
role: "treeitem",
|
|
16887
|
+
"aria-level": o.depth,
|
|
16888
|
+
"aria-posinset": line?.posInSet,
|
|
16889
|
+
"aria-setsize": line?.setSize,
|
|
16890
|
+
"aria-expanded": o.isGroup ? expanded : void 0,
|
|
16891
|
+
"aria-selected": checkState === true,
|
|
16892
|
+
"aria-disabled": rowDisabled || void 0,
|
|
16893
|
+
"data-select-focused": focused ? "true" : void 0,
|
|
16894
|
+
"data-select-active": focused ? "true" : void 0,
|
|
16895
|
+
className: cn(
|
|
16896
|
+
TREE_ROW_CLASS,
|
|
16897
|
+
TREE_LABEL_TYPOGRAPHY.sm.rowHeight,
|
|
16898
|
+
TREE_ROW_BORDER_CLASS,
|
|
16899
|
+
"select-none",
|
|
16900
|
+
rowDisabled ? "cursor-not-allowed" : "cursor-pointer",
|
|
16901
|
+
focused && TREE_ROW_ACTIVE_CLASS
|
|
16902
|
+
),
|
|
16903
|
+
onMouseEnter: () => onHoverValue(focusKey),
|
|
16904
|
+
onMouseLeave: () => onHoverValue(null),
|
|
16905
|
+
onClick: (event) => {
|
|
16906
|
+
if (isRowControlClick(event) || rowDisabled) return;
|
|
16907
|
+
if (togglesLeaves) onToggleGroup(leafValues);
|
|
16908
|
+
else onSelect(o.value, idx, event.shiftKey);
|
|
16909
|
+
},
|
|
16910
|
+
children: [
|
|
16911
|
+
/* @__PURE__ */ jsx(
|
|
16912
|
+
TreeConnector,
|
|
16913
|
+
{
|
|
16914
|
+
guideline: true,
|
|
16915
|
+
size: "sm",
|
|
16916
|
+
depth: o.depth,
|
|
16917
|
+
hasChildren: o.isGroup,
|
|
16918
|
+
isLast: line?.isLast ?? true,
|
|
16919
|
+
ancestorLast: line?.ancestorLast ?? []
|
|
16920
|
+
}
|
|
16921
|
+
),
|
|
16922
|
+
/* @__PURE__ */ jsxs("div", { className: TREE_CONTENT_CLASS, children: [
|
|
16923
|
+
o.isGroup ? (
|
|
16924
|
+
// 비활성 그룹도 접고 편다 — 그룹이 비활성이어도 아래에 고를 수 있는 자식이 있을 수 있다.
|
|
16925
|
+
/* @__PURE__ */ jsx(TreeCaret, { expanded, onToggle: () => onToggleExpand(o.value) })
|
|
16926
|
+
) : /* @__PURE__ */ jsx(TreeCaretSpacer, {}),
|
|
16927
|
+
multi && /* @__PURE__ */ jsx(
|
|
16928
|
+
"span",
|
|
16929
|
+
{
|
|
16930
|
+
"data-tree-control": true,
|
|
16931
|
+
className: "flex shrink-0",
|
|
16932
|
+
onPointerDownCapture: (event) => {
|
|
16933
|
+
checkboxShiftRef.current = event.shiftKey;
|
|
16934
|
+
},
|
|
16935
|
+
children: /* @__PURE__ */ jsx(SCheckbox, { value: checkState, disabled: rowDisabled, onValueChange: toggle })
|
|
16936
|
+
}
|
|
16937
|
+
),
|
|
16938
|
+
/* @__PURE__ */ jsx(
|
|
16939
|
+
OptionLabel,
|
|
16940
|
+
{
|
|
16941
|
+
label: o.label,
|
|
16942
|
+
className: treeLabelClass("sm", {
|
|
16943
|
+
// 고른 줄은 굵게 — multi_tree 는 일부만 고른 그룹도 굵다 (STree 와 같다)
|
|
16944
|
+
emphasized: checkState !== false,
|
|
16945
|
+
disabled: rowDisabled,
|
|
16946
|
+
selectable: true
|
|
16947
|
+
})
|
|
16948
|
+
}
|
|
16949
|
+
)
|
|
16950
|
+
] })
|
|
16951
|
+
]
|
|
16952
|
+
},
|
|
16953
|
+
rowKey
|
|
16954
|
+
)
|
|
16955
|
+
);
|
|
16956
|
+
}
|
|
16636
16957
|
if (o.isGroup) {
|
|
16637
16958
|
const isDepth1 = o.depth === 1;
|
|
16638
16959
|
const isFirstRow = idx === 0 && !showSelectAll && !searchVisible;
|
|
@@ -16673,13 +16994,7 @@ function ItemList({
|
|
|
16673
16994
|
onClick: () => onToggleGroup(leafValues),
|
|
16674
16995
|
children: [
|
|
16675
16996
|
/* @__PURE__ */ jsx(CheckboxBox, { state: groupState, disabled: groupDisabled, focused: groupFocused }),
|
|
16676
|
-
|
|
16677
|
-
"span",
|
|
16678
|
-
{
|
|
16679
|
-
className: "min-w-0 truncate",
|
|
16680
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
16681
|
-
}
|
|
16682
|
-
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: o.label }),
|
|
16997
|
+
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "min-w-0 truncate" }),
|
|
16683
16998
|
total > 0 && /* @__PURE__ */ jsxs(
|
|
16684
16999
|
"span",
|
|
16685
17000
|
{
|
|
@@ -16756,13 +17071,7 @@ function ItemList({
|
|
|
16756
17071
|
focused: focusedValue === o.value
|
|
16757
17072
|
}
|
|
16758
17073
|
),
|
|
16759
|
-
|
|
16760
|
-
"span",
|
|
16761
|
-
{
|
|
16762
|
-
className: "flex-1 truncate",
|
|
16763
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
16764
|
-
}
|
|
16765
|
-
) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
17074
|
+
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "flex-1 truncate" })
|
|
16766
17075
|
]
|
|
16767
17076
|
},
|
|
16768
17077
|
rowKey
|
|
@@ -16771,8 +17080,11 @@ function ItemList({
|
|
|
16771
17080
|
bottomSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: bottomSpacer } })
|
|
16772
17081
|
] });
|
|
16773
17082
|
}
|
|
17083
|
+
function OptionLabel({ label, className }) {
|
|
17084
|
+
return isHtmlLabel(label) ? /* @__PURE__ */ jsx("span", { className, dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(label) } }) : /* @__PURE__ */ jsx("span", { className, children: label });
|
|
17085
|
+
}
|
|
16774
17086
|
function TriggerLabel({ label }) {
|
|
16775
|
-
return
|
|
17087
|
+
return /* @__PURE__ */ jsx(OptionLabel, { label, className: "truncate" });
|
|
16776
17088
|
}
|
|
16777
17089
|
function TriggerRemoveButton({ ariaLabel, onRemove }) {
|
|
16778
17090
|
return /* @__PURE__ */ jsxs(
|
|
@@ -17027,8 +17339,8 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17027
17339
|
const [rowCountOverride, setRowCountOverride] = useState(null);
|
|
17028
17340
|
const rowCount = rowCountOverride ?? rows.length;
|
|
17029
17341
|
const scrollRef = useRef(null);
|
|
17030
|
-
const
|
|
17031
|
-
const [
|
|
17342
|
+
const headerRef = useRef(null);
|
|
17343
|
+
const [headerHeight, setHeaderHeight] = useState(0);
|
|
17032
17344
|
const [vsRange, setVsRange] = useState({ start: 0, end: 0 });
|
|
17033
17345
|
const lastReachEndRef = useRef(-1);
|
|
17034
17346
|
const cbRef = useRef({ onVirtualUpdate, onVirtualReachEnd, onScroll });
|
|
@@ -17070,17 +17382,13 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17070
17382
|
);
|
|
17071
17383
|
const handleScroll = useCallback(() => {
|
|
17072
17384
|
const el = scrollRef.current;
|
|
17073
|
-
if (!el)
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
}
|
|
17077
|
-
const header = headerScrollRef.current;
|
|
17078
|
-
if (header) header.scrollLeft = el.scrollLeft;
|
|
17079
|
-
setHeaderGutter(el.offsetWidth - el.clientWidth);
|
|
17385
|
+
if (!el) return;
|
|
17386
|
+
const headerH = headerRef.current?.offsetHeight ?? 0;
|
|
17387
|
+
setHeaderHeight(headerH);
|
|
17080
17388
|
setScrolledLeft(el.scrollLeft > 0);
|
|
17081
17389
|
setScrolledRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
17082
17390
|
if (virtualScroll) {
|
|
17083
|
-
const { start, end } = calcRange(el.scrollTop, el.clientHeight);
|
|
17391
|
+
const { start, end } = calcRange(el.scrollTop, el.clientHeight - headerH);
|
|
17084
17392
|
setVsRange((prev) => prev.start === start && prev.end === end ? prev : { start, end });
|
|
17085
17393
|
}
|
|
17086
17394
|
}, [virtualScroll, calcRange]);
|
|
@@ -17477,7 +17785,7 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17477
17785
|
);
|
|
17478
17786
|
};
|
|
17479
17787
|
const bodyScrollClass = cn(
|
|
17480
|
-
"min-h-0 flex-1 overflow-auto
|
|
17788
|
+
"min-h-0 flex-1 overflow-auto",
|
|
17481
17789
|
SCROLLBAR_CLASS,
|
|
17482
17790
|
SCROLLBAR_TRACK_BG_CLASS,
|
|
17483
17791
|
SCROLLBAR_TRACK_BORDER_CLASS
|
|
@@ -17494,28 +17802,226 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17494
17802
|
selectable && /* @__PURE__ */ jsx("col", { style: { width: SELECTABLE_COLUMN_WIDTH } }),
|
|
17495
17803
|
visibleCols.map((col, i) => /* @__PURE__ */ jsx("col", { style: col.autoWidth ? void 0 : { width: columnWidths[i] } }, col.name))
|
|
17496
17804
|
] });
|
|
17497
|
-
|
|
17498
|
-
"
|
|
17805
|
+
const headerTable = /* @__PURE__ */ jsxs(
|
|
17806
|
+
"table",
|
|
17499
17807
|
{
|
|
17500
|
-
className:
|
|
17501
|
-
style: {
|
|
17502
|
-
"data-table-id": resolvedTableId,
|
|
17808
|
+
className: "w-full table-fixed border-separate border-spacing-0 border-b border-solid border-[color:var(--cmp-table-border-color)] text-left",
|
|
17809
|
+
style: { fontSize: 12 },
|
|
17503
17810
|
children: [
|
|
17504
|
-
|
|
17505
|
-
|
|
17506
|
-
{
|
|
17507
|
-
|
|
17508
|
-
|
|
17509
|
-
|
|
17510
|
-
|
|
17511
|
-
|
|
17512
|
-
|
|
17513
|
-
|
|
17514
|
-
|
|
17515
|
-
|
|
17516
|
-
|
|
17517
|
-
|
|
17518
|
-
|
|
17811
|
+
colgroup,
|
|
17812
|
+
/* @__PURE__ */ jsxs("thead", { className: cn(isNoData && "opacity-40"), children: [
|
|
17813
|
+
headerGroupInfo && /* @__PURE__ */ jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
17814
|
+
selectable && /* @__PURE__ */ jsx(
|
|
17815
|
+
"th",
|
|
17816
|
+
{
|
|
17817
|
+
rowSpan: 2,
|
|
17818
|
+
className: cn(
|
|
17819
|
+
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
17820
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
17821
|
+
),
|
|
17822
|
+
style: {
|
|
17823
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
17824
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
17825
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
17826
|
+
},
|
|
17827
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
17828
|
+
SCheckbox,
|
|
17829
|
+
{
|
|
17830
|
+
value: headerCheck,
|
|
17831
|
+
disabled: !hasSelectableRow,
|
|
17832
|
+
onValueChange: toggleAll
|
|
17833
|
+
}
|
|
17834
|
+
) })
|
|
17835
|
+
}
|
|
17836
|
+
),
|
|
17837
|
+
headerGroupInfo.cells.map((group) => /* @__PURE__ */ jsxs(
|
|
17838
|
+
"th",
|
|
17839
|
+
{
|
|
17840
|
+
colSpan: group.colSpan,
|
|
17841
|
+
rowSpan: group.rowSpan,
|
|
17842
|
+
className: cn(
|
|
17843
|
+
"relative border-b border-r border-solid border-[color:var(--cmp-table-border-color)] bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
17844
|
+
group.firstIndex + group.colSpan === visibleCols.length && "border-r-0",
|
|
17845
|
+
group.rowSpan === 2 && "border-b-0",
|
|
17846
|
+
group.className
|
|
17847
|
+
),
|
|
17848
|
+
style: { textAlign: group.align ?? "left", ...group.style },
|
|
17849
|
+
children: [
|
|
17850
|
+
group.render ? group.render() : group.label,
|
|
17851
|
+
group.rowSpan === 2 && group.colSpan === 1 ? headerResizeHitArea(group.firstIndex, `group-resizer-${group.columnNames[0]}`) : null
|
|
17852
|
+
]
|
|
17853
|
+
},
|
|
17854
|
+
group.columnNames.join("|")
|
|
17855
|
+
))
|
|
17856
|
+
] }),
|
|
17857
|
+
/* @__PURE__ */ jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
17858
|
+
selectable && !headerGroupInfo && /* @__PURE__ */ jsxs(
|
|
17859
|
+
"th",
|
|
17860
|
+
{
|
|
17861
|
+
className: cn(
|
|
17862
|
+
// 원본 .th--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
17863
|
+
// 하단 border는 헤더 표가 연속으로 그리므로 셀에는 두지 않는다.
|
|
17864
|
+
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
17865
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
17866
|
+
),
|
|
17867
|
+
style: {
|
|
17868
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
17869
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
17870
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
17871
|
+
},
|
|
17872
|
+
children: [
|
|
17873
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
17874
|
+
SCheckbox,
|
|
17875
|
+
{
|
|
17876
|
+
value: headerCheck,
|
|
17877
|
+
disabled: !hasSelectableRow,
|
|
17878
|
+
onValueChange: toggleAll
|
|
17879
|
+
}
|
|
17880
|
+
) }),
|
|
17881
|
+
/* @__PURE__ */ jsx(
|
|
17882
|
+
"span",
|
|
17883
|
+
{
|
|
17884
|
+
"aria-hidden": true,
|
|
17885
|
+
className: "pointer-events-none absolute right-0 top-1/2 h-[16px] w-px -translate-y-1/2 bg-[var(--cmp-table-header-resizingBar-color)]"
|
|
17886
|
+
}
|
|
17887
|
+
)
|
|
17888
|
+
]
|
|
17889
|
+
}
|
|
17890
|
+
),
|
|
17891
|
+
visibleCols.map((col, i) => {
|
|
17892
|
+
if (headerGroupInfo?.rowSpanNames.has(col.name)) return null;
|
|
17893
|
+
const isSortable = !!col.sortable && !col.renderHeader;
|
|
17894
|
+
const sortDir = isSortable && sort?.name === col.name ? sort.dir : void 0;
|
|
17895
|
+
return /* @__PURE__ */ jsxs(
|
|
17896
|
+
"th",
|
|
17897
|
+
{
|
|
17898
|
+
className: cn(
|
|
17899
|
+
// 하단 border는 헤더 표가 연속으로 그린다.
|
|
17900
|
+
// leading-[0]: th 의 스트럿(line-height 만큼의 빈 인라인 박스)을 없앤다.
|
|
17901
|
+
// 내용이 inline-flex 라 스트럿이 남아 있으면 라벨보다 큰 컨트롤(정렬 버튼 등)이
|
|
17902
|
+
// 들어올 때 줄 상자가 베이스라인 기준으로 늘어나 헤더 높이를 넘기고
|
|
17903
|
+
// 세로 가운데도 어긋난다. 글자 줄높이는 아래 th__content 가 다시 준다.
|
|
17904
|
+
headerGroupInfo && "border-r border-solid border-[color:var(--cmp-table-border-color)]",
|
|
17905
|
+
headerGroupInfo && i === visibleCols.length - 1 && "border-r-0",
|
|
17906
|
+
"relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[0] text-[color:var(--sys-color-fg-primary)]",
|
|
17907
|
+
stickyInfo(i).isSticky && "z-[6]",
|
|
17908
|
+
col.thClass
|
|
17909
|
+
),
|
|
17910
|
+
style: {
|
|
17911
|
+
textAlign: col.align ?? "left",
|
|
17912
|
+
boxShadow: stickyShadowValue(i) ?? void 0,
|
|
17913
|
+
...cellStyle(i),
|
|
17914
|
+
...col.thStyle
|
|
17915
|
+
},
|
|
17916
|
+
"aria-sort": isSortable ? sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : "none" : void 0,
|
|
17917
|
+
children: [
|
|
17918
|
+
/* @__PURE__ */ jsx(
|
|
17919
|
+
"span",
|
|
17920
|
+
{
|
|
17921
|
+
className: cn(
|
|
17922
|
+
// 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
|
|
17923
|
+
// th 가 leading-[0] 이므로 글자 줄높이는 여기서 준다 (truncate 가
|
|
17924
|
+
// overflow-hidden 이라 줄높이가 0이면 라벨이 잘려 사라진다).
|
|
17925
|
+
"inline-flex w-full min-w-0 items-center gap-[4px] leading-[20px]",
|
|
17926
|
+
col.align === "center" && "justify-center",
|
|
17927
|
+
col.align === "right" && "justify-end"
|
|
17928
|
+
),
|
|
17929
|
+
children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17930
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: col.label }),
|
|
17931
|
+
col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
|
|
17932
|
+
// shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
|
|
17933
|
+
// 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
|
|
17934
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(
|
|
17935
|
+
STooltip2,
|
|
17936
|
+
{
|
|
17937
|
+
trigger: "hover",
|
|
17938
|
+
icon: "helpOutline",
|
|
17939
|
+
iconSize: 12,
|
|
17940
|
+
color: "var(--sys-color-fg-success)",
|
|
17941
|
+
placement: "top",
|
|
17942
|
+
message: col.helpText
|
|
17943
|
+
}
|
|
17944
|
+
) }),
|
|
17945
|
+
col.editable && /* @__PURE__ */ jsx(
|
|
17946
|
+
SIcon,
|
|
17947
|
+
{
|
|
17948
|
+
name: "pageEdit",
|
|
17949
|
+
size: 12,
|
|
17950
|
+
color: "var(--sys-color-link-accent)",
|
|
17951
|
+
label: "\uC218\uC815 \uAC00\uB2A5",
|
|
17952
|
+
className: "shrink-0"
|
|
17953
|
+
}
|
|
17954
|
+
),
|
|
17955
|
+
col.navigable && /* @__PURE__ */ jsx(
|
|
17956
|
+
SIcon,
|
|
17957
|
+
{
|
|
17958
|
+
name: "pageMove",
|
|
17959
|
+
size: 12,
|
|
17960
|
+
color: "var(--sys-color-link-accent)",
|
|
17961
|
+
label: "\uC0C8 \uD654\uBA74\uC5D0\uC11C \uC5F4\uAE30",
|
|
17962
|
+
className: "shrink-0"
|
|
17963
|
+
}
|
|
17964
|
+
),
|
|
17965
|
+
col.required && // 라벨이 truncate 돼도 필수 표시는 남아야 하므로 shrink-0.
|
|
17966
|
+
// 아이콘·크기·색은 SKeyValueTable 헤더 필수 표시와 동일하게 맞춘다.
|
|
17967
|
+
/* @__PURE__ */ jsx(
|
|
17968
|
+
SIcon,
|
|
17969
|
+
{
|
|
17970
|
+
name: "asterisk",
|
|
17971
|
+
size: 12,
|
|
17972
|
+
color: "var(--sys-color-fg-accent)",
|
|
17973
|
+
className: "shrink-0"
|
|
17974
|
+
}
|
|
17975
|
+
),
|
|
17976
|
+
isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
|
|
17977
|
+
// 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
|
|
17978
|
+
// 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
|
|
17979
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(
|
|
17980
|
+
SGhostButton,
|
|
17981
|
+
{
|
|
17982
|
+
size: "xxs",
|
|
17983
|
+
intent: sortDir ? "action" : "default",
|
|
17984
|
+
icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
|
|
17985
|
+
ariaLabel: `${col.label} \uC815\uB82C`,
|
|
17986
|
+
onClick: () => toggleSort(col)
|
|
17987
|
+
}
|
|
17988
|
+
) })
|
|
17989
|
+
] })
|
|
17990
|
+
}
|
|
17991
|
+
),
|
|
17992
|
+
headerGroupInfo ? headerResizeHitArea(i, `resizer-${col.name}`) : headerResizer(i, `resizer-${col.name}`)
|
|
17993
|
+
]
|
|
17994
|
+
},
|
|
17995
|
+
col.name
|
|
17996
|
+
);
|
|
17997
|
+
})
|
|
17998
|
+
] })
|
|
17999
|
+
] })
|
|
18000
|
+
]
|
|
18001
|
+
}
|
|
18002
|
+
);
|
|
18003
|
+
return /* @__PURE__ */ jsxs(
|
|
18004
|
+
"div",
|
|
18005
|
+
{
|
|
18006
|
+
className: cn("flex flex-col", className),
|
|
18007
|
+
style: { width, ...style },
|
|
18008
|
+
"data-table-id": resolvedTableId,
|
|
18009
|
+
children: [
|
|
18010
|
+
/* @__PURE__ */ jsxs(
|
|
18011
|
+
"div",
|
|
18012
|
+
{
|
|
18013
|
+
className: cn(
|
|
18014
|
+
// isolate: no-data/loading 오버레이(z-30·31)를 테이블 안에서 끝낸다. relative + z-auto
|
|
18015
|
+
// 만으로는 스태킹 컨텍스트가 생기지 않아, 모달 안에 들어가면 30·31이 모달 Content
|
|
18016
|
+
// 레벨까지 올라가 같은 컨텍스트의 floating portal 밴드(z-2)를 덮는다.
|
|
18017
|
+
// 자손은 이미 overflow-hidden으로 클리핑되므로 테이블 내부 순서는 달라지지 않는다.
|
|
18018
|
+
"bg-white relative isolate flex flex-col overflow-hidden",
|
|
18019
|
+
bordered && "border border-solid border-[color:var(--cmp-table-border-color)]",
|
|
18020
|
+
// height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
|
|
18021
|
+
// `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
|
|
18022
|
+
// 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
|
|
18023
|
+
// 기본 사용(내용만큼 커지는 케이스)에서는 동작이 바뀌지 않는다.
|
|
18024
|
+
!height && "min-h-0 grow",
|
|
17519
18025
|
radiusClass,
|
|
17520
18026
|
// 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 인터랙션 차단
|
|
17521
18027
|
(loading2 || rowCount === 0) && "pointer-events-none"
|
|
@@ -17525,220 +18031,20 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17525
18031
|
minHeight: isNoData ? "calc(var(--cmp-table-header-height) + 60px)" : void 0
|
|
17526
18032
|
},
|
|
17527
18033
|
children: [
|
|
17528
|
-
/* @__PURE__ */
|
|
17529
|
-
"div",
|
|
17530
|
-
{
|
|
17531
|
-
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
|
|
17536
|
-
|
|
17537
|
-
|
|
17538
|
-
|
|
17539
|
-
|
|
17540
|
-
|
|
17541
|
-
|
|
17542
|
-
colgroup,
|
|
17543
|
-
/* @__PURE__ */ jsxs("thead", { className: cn(isNoData && "opacity-40"), children: [
|
|
17544
|
-
headerGroupInfo && /* @__PURE__ */ jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
17545
|
-
selectable && /* @__PURE__ */ jsx(
|
|
17546
|
-
"th",
|
|
17547
|
-
{
|
|
17548
|
-
rowSpan: 2,
|
|
17549
|
-
className: cn(
|
|
17550
|
-
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
17551
|
-
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
17552
|
-
),
|
|
17553
|
-
style: {
|
|
17554
|
-
width: SELECTABLE_COLUMN_WIDTH,
|
|
17555
|
-
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
17556
|
-
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
17557
|
-
},
|
|
17558
|
-
children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
17559
|
-
SCheckbox,
|
|
17560
|
-
{
|
|
17561
|
-
value: headerCheck,
|
|
17562
|
-
disabled: !hasSelectableRow,
|
|
17563
|
-
onValueChange: toggleAll
|
|
17564
|
-
}
|
|
17565
|
-
) })
|
|
17566
|
-
}
|
|
17567
|
-
),
|
|
17568
|
-
headerGroupInfo.cells.map((group) => /* @__PURE__ */ jsxs(
|
|
17569
|
-
"th",
|
|
17570
|
-
{
|
|
17571
|
-
colSpan: group.colSpan,
|
|
17572
|
-
rowSpan: group.rowSpan,
|
|
17573
|
-
className: cn(
|
|
17574
|
-
"relative border-b border-r border-solid border-[color:var(--cmp-table-border-color)] bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[20px] text-[color:var(--sys-color-fg-primary)]",
|
|
17575
|
-
group.firstIndex + group.colSpan === visibleCols.length && "border-r-0",
|
|
17576
|
-
group.rowSpan === 2 && "border-b-0",
|
|
17577
|
-
group.className
|
|
17578
|
-
),
|
|
17579
|
-
style: { textAlign: group.align ?? "left", ...group.style },
|
|
17580
|
-
children: [
|
|
17581
|
-
group.render ? group.render() : group.label,
|
|
17582
|
-
group.rowSpan === 2 && group.colSpan === 1 ? headerResizeHitArea(
|
|
17583
|
-
group.firstIndex,
|
|
17584
|
-
`group-resizer-${group.columnNames[0]}`
|
|
17585
|
-
) : null
|
|
17586
|
-
]
|
|
17587
|
-
},
|
|
17588
|
-
group.columnNames.join("|")
|
|
17589
|
-
))
|
|
17590
|
-
] }),
|
|
17591
|
-
/* @__PURE__ */ jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
17592
|
-
selectable && !headerGroupInfo && /* @__PURE__ */ jsxs(
|
|
17593
|
-
"th",
|
|
17594
|
-
{
|
|
17595
|
-
className: cn(
|
|
17596
|
-
// 원본 .th--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
17597
|
-
// 하단 border는 헤더 래퍼가 연속으로 그리므로 셀에는 두지 않는다.
|
|
17598
|
-
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
17599
|
-
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
17600
|
-
),
|
|
17601
|
-
style: {
|
|
17602
|
-
width: SELECTABLE_COLUMN_WIDTH,
|
|
17603
|
-
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
17604
|
-
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
17605
|
-
},
|
|
17606
|
-
children: [
|
|
17607
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
17608
|
-
SCheckbox,
|
|
17609
|
-
{
|
|
17610
|
-
value: headerCheck,
|
|
17611
|
-
disabled: !hasSelectableRow,
|
|
17612
|
-
onValueChange: toggleAll
|
|
17613
|
-
}
|
|
17614
|
-
) }),
|
|
17615
|
-
/* @__PURE__ */ jsx(
|
|
17616
|
-
"span",
|
|
17617
|
-
{
|
|
17618
|
-
"aria-hidden": true,
|
|
17619
|
-
className: "pointer-events-none absolute right-0 top-1/2 h-[16px] w-px -translate-y-1/2 bg-[var(--cmp-table-header-resizingBar-color)]"
|
|
17620
|
-
}
|
|
17621
|
-
)
|
|
17622
|
-
]
|
|
17623
|
-
}
|
|
17624
|
-
),
|
|
17625
|
-
visibleCols.map((col, i) => {
|
|
17626
|
-
if (headerGroupInfo?.rowSpanNames.has(col.name)) return null;
|
|
17627
|
-
const isSortable = !!col.sortable && !col.renderHeader;
|
|
17628
|
-
const sortDir = isSortable && sort?.name === col.name ? sort.dir : void 0;
|
|
17629
|
-
return /* @__PURE__ */ jsxs(
|
|
17630
|
-
"th",
|
|
17631
|
-
{
|
|
17632
|
-
className: cn(
|
|
17633
|
-
// 하단 border는 헤더 래퍼가 연속으로 그린다 (거터 영역까지 이어짐).
|
|
17634
|
-
// leading-[0]: th 의 스트럿(line-height 만큼의 빈 인라인 박스)을 없앤다.
|
|
17635
|
-
// 내용이 inline-flex 라 스트럿이 남아 있으면 라벨보다 큰 컨트롤(정렬 버튼 등)이
|
|
17636
|
-
// 들어올 때 줄 상자가 베이스라인 기준으로 늘어나 헤더 높이를 넘기고
|
|
17637
|
-
// 세로 가운데도 어긋난다. 글자 줄높이는 아래 th__content 가 다시 준다.
|
|
17638
|
-
headerGroupInfo && "border-r border-solid border-[color:var(--cmp-table-border-color)]",
|
|
17639
|
-
headerGroupInfo && i === visibleCols.length - 1 && "border-r-0",
|
|
17640
|
-
"relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[0] text-[color:var(--sys-color-fg-primary)]",
|
|
17641
|
-
stickyInfo(i).isSticky && "z-[6]",
|
|
17642
|
-
col.thClass
|
|
17643
|
-
),
|
|
17644
|
-
style: {
|
|
17645
|
-
textAlign: col.align ?? "left",
|
|
17646
|
-
boxShadow: stickyShadowValue(i) ?? void 0,
|
|
17647
|
-
...cellStyle(i),
|
|
17648
|
-
...col.thStyle
|
|
17649
|
-
},
|
|
17650
|
-
"aria-sort": isSortable ? sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : "none" : void 0,
|
|
17651
|
-
children: [
|
|
17652
|
-
/* @__PURE__ */ jsx(
|
|
17653
|
-
"span",
|
|
17654
|
-
{
|
|
17655
|
-
className: cn(
|
|
17656
|
-
// 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
|
|
17657
|
-
// th 가 leading-[0] 이므로 글자 줄높이는 여기서 준다 (truncate 가
|
|
17658
|
-
// overflow-hidden 이라 줄높이가 0이면 라벨이 잘려 사라진다).
|
|
17659
|
-
"inline-flex w-full min-w-0 items-center gap-[4px] leading-[20px]",
|
|
17660
|
-
col.align === "center" && "justify-center",
|
|
17661
|
-
col.align === "right" && "justify-end"
|
|
17662
|
-
),
|
|
17663
|
-
children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17664
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: col.label }),
|
|
17665
|
-
col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
|
|
17666
|
-
// shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
|
|
17667
|
-
// 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
|
|
17668
|
-
/* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(
|
|
17669
|
-
STooltip2,
|
|
17670
|
-
{
|
|
17671
|
-
trigger: "hover",
|
|
17672
|
-
icon: "helpOutline",
|
|
17673
|
-
iconSize: 12,
|
|
17674
|
-
color: "var(--sys-color-fg-success)",
|
|
17675
|
-
placement: "top",
|
|
17676
|
-
message: col.helpText
|
|
17677
|
-
}
|
|
17678
|
-
) }),
|
|
17679
|
-
col.editable && /* @__PURE__ */ jsx(
|
|
17680
|
-
SIcon,
|
|
17681
|
-
{
|
|
17682
|
-
name: "pageEdit",
|
|
17683
|
-
size: 12,
|
|
17684
|
-
color: "var(--sys-color-link-accent)",
|
|
17685
|
-
label: "\uC218\uC815 \uAC00\uB2A5",
|
|
17686
|
-
className: "shrink-0"
|
|
17687
|
-
}
|
|
17688
|
-
),
|
|
17689
|
-
col.navigable && /* @__PURE__ */ jsx(
|
|
17690
|
-
SIcon,
|
|
17691
|
-
{
|
|
17692
|
-
name: "pageMove",
|
|
17693
|
-
size: 12,
|
|
17694
|
-
color: "var(--sys-color-link-accent)",
|
|
17695
|
-
label: "\uC0C8 \uD654\uBA74\uC5D0\uC11C \uC5F4\uAE30",
|
|
17696
|
-
className: "shrink-0"
|
|
17697
|
-
}
|
|
17698
|
-
),
|
|
17699
|
-
col.required && // 라벨이 truncate 돼도 필수 표시는 남아야 하므로 shrink-0.
|
|
17700
|
-
// 아이콘·크기·색은 SKeyValueTable 헤더 필수 표시와 동일하게 맞춘다.
|
|
17701
|
-
/* @__PURE__ */ jsx(
|
|
17702
|
-
SIcon,
|
|
17703
|
-
{
|
|
17704
|
-
name: "asterisk",
|
|
17705
|
-
size: 12,
|
|
17706
|
-
color: "var(--sys-color-fg-accent)",
|
|
17707
|
-
className: "shrink-0"
|
|
17708
|
-
}
|
|
17709
|
-
),
|
|
17710
|
-
isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
|
|
17711
|
-
// 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
|
|
17712
|
-
// 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
|
|
17713
|
-
/* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(
|
|
17714
|
-
SGhostButton,
|
|
17715
|
-
{
|
|
17716
|
-
size: "xxs",
|
|
17717
|
-
intent: sortDir ? "action" : "default",
|
|
17718
|
-
icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
|
|
17719
|
-
ariaLabel: `${col.label} \uC815\uB82C`,
|
|
17720
|
-
onClick: () => toggleSort(col)
|
|
17721
|
-
}
|
|
17722
|
-
) })
|
|
17723
|
-
] })
|
|
17724
|
-
}
|
|
17725
|
-
),
|
|
17726
|
-
headerGroupInfo ? headerResizeHitArea(i, `resizer-${col.name}`) : headerResizer(i, `resizer-${col.name}`)
|
|
17727
|
-
]
|
|
17728
|
-
},
|
|
17729
|
-
col.name
|
|
17730
|
-
);
|
|
17731
|
-
})
|
|
17732
|
-
] })
|
|
17733
|
-
] })
|
|
17734
|
-
]
|
|
17735
|
-
}
|
|
17736
|
-
)
|
|
17737
|
-
}
|
|
17738
|
-
),
|
|
17739
|
-
showNoDataSlot ? /* @__PURE__ */ jsx("div", { className: cn(bodyScrollClass, "pointer-events-auto bg-white"), children: /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-[60px] w-full items-center justify-center", children: emptySlot }) }) : (
|
|
17740
|
-
/* 바디: 세로 스크롤은 이 컨테이너에만 생긴다 (헤더 제외) */
|
|
17741
|
-
/* @__PURE__ */ jsx(
|
|
18034
|
+
showNoDataSlot ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
18035
|
+
/* @__PURE__ */ jsx("div", { className: "flex-none overflow-hidden", children: headerTable }),
|
|
18036
|
+
/* @__PURE__ */ jsx("div", { className: cn(bodyScrollClass, "pointer-events-auto bg-white"), children: /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-[60px] w-full items-center justify-center", children: emptySlot }) })
|
|
18037
|
+
] }) : (
|
|
18038
|
+
/**
|
|
18039
|
+
* 헤더와 body 가 한 스크롤 컨테이너에 있다 — 헤더는 위에 붙어(sticky) 세로로는 서 있고
|
|
18040
|
+
* 가로로는 body 와 같은 스크롤로 움직인다. 헤더를 따로 두고 scroll 이벤트에서
|
|
18041
|
+
* scrollLeft 를 맞추면, 브라우저가 body 스크롤을 먼저 그리고 이벤트를 나중에 돌려
|
|
18042
|
+
* 헤더가 한 프레임 이상 늦게 따라온다.
|
|
18043
|
+
*
|
|
18044
|
+
* 행 좌표는 body 표 기준 그대로다 — `scrollTop` 이 y 이면 y 에 있는 행이 헤더 바로
|
|
18045
|
+
* 아래에 선다. 보이는 높이만 헤더 높이만큼 줄어든다(`headerHeight`).
|
|
18046
|
+
*/
|
|
18047
|
+
/* @__PURE__ */ jsxs(
|
|
17742
18048
|
"div",
|
|
17743
18049
|
{
|
|
17744
18050
|
ref: scrollRef,
|
|
@@ -17748,136 +18054,140 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17748
18054
|
// 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 스크롤 차단
|
|
17749
18055
|
(loading2 || rowCount === 0) && "overflow-hidden"
|
|
17750
18056
|
),
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
{
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
"
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
onRowDoubleClick(
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
(
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
18057
|
+
style: { scrollPaddingTop: headerHeight },
|
|
18058
|
+
children: [
|
|
18059
|
+
/* @__PURE__ */ jsx("div", { ref: headerRef, className: "sticky top-0 z-[8]", children: headerTable }),
|
|
18060
|
+
/* @__PURE__ */ jsxs(
|
|
18061
|
+
"table",
|
|
18062
|
+
{
|
|
18063
|
+
className: "w-full table-fixed border-separate border-spacing-0 text-left",
|
|
18064
|
+
style: { fontSize: 12 },
|
|
18065
|
+
children: [
|
|
18066
|
+
colgroup,
|
|
18067
|
+
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
18068
|
+
virtualScroll && topSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: topSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) }),
|
|
18069
|
+
rowCount === 0 ? null : bodyRows.map((row, ri) => {
|
|
18070
|
+
const rowIndex = rowOffset + ri;
|
|
18071
|
+
const isSel = isViewSelected(row);
|
|
18072
|
+
const dragSel = dragSelectOn && isSel;
|
|
18073
|
+
const dragTop = dragSel && !isViewSelected(rows[rowIndex - 1]);
|
|
18074
|
+
const dragBottom = dragSel && !isViewSelected(rows[rowIndex + 1]);
|
|
18075
|
+
const rowLocked = (selectable || dragSelectable) && !canSelectRow(row);
|
|
18076
|
+
const rowBg = "bg-white";
|
|
18077
|
+
const rowHover = hoverable && !rowLocked;
|
|
18078
|
+
return /* @__PURE__ */ jsxs(
|
|
18079
|
+
"tr",
|
|
18080
|
+
{
|
|
18081
|
+
"data-row-index": rowIndex,
|
|
18082
|
+
onClick: () => onRowClick?.(row),
|
|
18083
|
+
onDoubleClick: onRowDoubleClick ? (event) => {
|
|
18084
|
+
const target = event.target;
|
|
18085
|
+
if (target.closest(ROW_INTERACTIVE_SELECTOR)) return;
|
|
18086
|
+
onRowDoubleClick(row);
|
|
18087
|
+
} : void 0,
|
|
18088
|
+
onMouseDown: dragSelectOn ? (event) => startDragSelect(rowIndex, event) : void 0,
|
|
18089
|
+
onMouseEnter: dragSelectOn ? () => extendDragSelect(rowIndex) : void 0,
|
|
18090
|
+
onContextMenu: contextMenuOn ? (event) => openContextMenu(row, event) : void 0,
|
|
18091
|
+
className: cn(
|
|
18092
|
+
"group/table-row",
|
|
18093
|
+
rowHover && "hover:bg-[var(--color-grey-05)]",
|
|
18094
|
+
(onRowClick || onRowDoubleClick) && "cursor-pointer",
|
|
18095
|
+
dragSelectOn && "select-none",
|
|
18096
|
+
rowClassName?.(row)
|
|
18097
|
+
),
|
|
18098
|
+
style: { height: virtualScroll ? effectiveRowHeight : rowH },
|
|
18099
|
+
children: [
|
|
18100
|
+
selectable && /* @__PURE__ */ jsx(
|
|
18101
|
+
"td",
|
|
18102
|
+
{
|
|
18103
|
+
className: cn(
|
|
18104
|
+
// 원본 .td--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
18105
|
+
"sticky left-0 z-[2] border-b border-solid border-[color:var(--cmp-table-border-color)] align-middle",
|
|
18106
|
+
rowBg,
|
|
18107
|
+
rowHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
18108
|
+
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
18109
|
+
),
|
|
18110
|
+
style: {
|
|
18111
|
+
width: SELECTABLE_COLUMN_WIDTH,
|
|
18112
|
+
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
18113
|
+
maxWidth: SELECTABLE_COLUMN_WIDTH,
|
|
18114
|
+
// 선택 열도 행의 일부다 — 여기만 희지 않으면 회색 줄이 끊긴다
|
|
18115
|
+
background: rowLocked ? LOCKED_BG : void 0
|
|
18116
|
+
},
|
|
18117
|
+
onClick: (e) => e.stopPropagation(),
|
|
18118
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
18119
|
+
onClickCapture: (e) => {
|
|
18120
|
+
shiftPressedRef.current = e.shiftKey;
|
|
18121
|
+
if (e.shiftKey) window.getSelection()?.removeAllRanges();
|
|
18122
|
+
},
|
|
18123
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex select-none items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
18124
|
+
SCheckbox,
|
|
18125
|
+
{
|
|
18126
|
+
value: isSel || !isRowIndeterminate?.(row) ? isSel : null,
|
|
18127
|
+
disabled: !canSelectRow(row),
|
|
18128
|
+
onValueChange: () => toggleRow(row, rowOffset + ri)
|
|
18129
|
+
}
|
|
18130
|
+
) })
|
|
18131
|
+
}
|
|
18132
|
+
),
|
|
18133
|
+
visibleCols.map((col, i) => {
|
|
18134
|
+
const tdClassName = cn(
|
|
18135
|
+
// 원본 .td: 컬럼 폭에 맞춰 내용 말줄임(overflow hidden + ellipsis) + 행 구분선(border-b)
|
|
18136
|
+
// 셀 배경은 흰색으로 채워 뒤 배경이 비치지 않게 하고, hover 시 행 배경(grey_05)을 따른다.
|
|
18137
|
+
"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)]",
|
|
17797
18138
|
rowBg,
|
|
17798
18139
|
rowHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
17799
|
-
|
|
17800
|
-
)
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
17825
|
-
|
|
17826
|
-
|
|
17827
|
-
|
|
17828
|
-
|
|
17829
|
-
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
|
|
17833
|
-
|
|
17834
|
-
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
|
|
17841
|
-
|
|
17842
|
-
|
|
17843
|
-
|
|
17844
|
-
|
|
17845
|
-
|
|
17846
|
-
|
|
17847
|
-
|
|
17848
|
-
|
|
17849
|
-
|
|
17850
|
-
// color, STag 등)까지는 닿지 않는다 — 그건 소비 앱 몫이다.
|
|
17851
|
-
color: rowLocked ? LOCKED_CONTENT : void 0,
|
|
17852
|
-
boxShadow: shadows.length > 0 ? shadows.join(", ") : void 0,
|
|
17853
|
-
...cellStyle(i)
|
|
17854
|
-
};
|
|
17855
|
-
if (col.renderCell) {
|
|
17856
|
-
const cell = col.renderCell({
|
|
17857
|
-
row,
|
|
17858
|
-
value: cellValue(col, row),
|
|
17859
|
-
column: col,
|
|
17860
|
-
colIndex: i,
|
|
17861
|
-
rowIndex: ri,
|
|
17862
|
-
isSelected: isSel,
|
|
17863
|
-
className: tdClassName,
|
|
17864
|
-
style: tdStyle
|
|
17865
|
-
});
|
|
17866
|
-
if (cell == null || cell === false) return null;
|
|
17867
|
-
return /* @__PURE__ */ jsx(Fragment$1, { children: cell }, col.name);
|
|
17868
|
-
}
|
|
17869
|
-
return /* @__PURE__ */ jsx("td", { className: tdClassName, style: tdStyle, children: col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row) }, col.name);
|
|
17870
|
-
})
|
|
17871
|
-
]
|
|
17872
|
-
},
|
|
17873
|
-
row[rowKey] ?? ri
|
|
17874
|
-
);
|
|
17875
|
-
}),
|
|
17876
|
-
virtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
|
|
17877
|
-
] })
|
|
17878
|
-
]
|
|
17879
|
-
}
|
|
17880
|
-
)
|
|
18140
|
+
typeof col.tdClass === "function" ? col.tdClass(row) : col.tdClass
|
|
18141
|
+
);
|
|
18142
|
+
const tdPadX = cellPadXOf(col);
|
|
18143
|
+
const dragEdges = dragSel ? [
|
|
18144
|
+
dragTop && `inset 0 ${DRAG_EDGE_W} 0 0 ${DRAG_EDGE_C}`,
|
|
18145
|
+
dragBottom && `inset 0 ${DRAG_EDGE_NEG_W} 0 0 ${DRAG_EDGE_C}`,
|
|
18146
|
+
i === 0 && `inset ${DRAG_EDGE_W} 0 0 0 ${DRAG_EDGE_C}`,
|
|
18147
|
+
i === visibleCols.length - 1 && `inset ${DRAG_EDGE_NEG_W} 0 0 0 ${DRAG_EDGE_C}`
|
|
18148
|
+
].filter(Boolean) : [];
|
|
18149
|
+
const shadows = [...dragEdges, stickyShadowValue(i)].filter(Boolean);
|
|
18150
|
+
const tdStyle = {
|
|
18151
|
+
paddingLeft: tdPadX,
|
|
18152
|
+
paddingRight: tdPadX,
|
|
18153
|
+
paddingTop: cellPadY,
|
|
18154
|
+
paddingBottom: cellPadY,
|
|
18155
|
+
textAlign: col.align ?? "left",
|
|
18156
|
+
// 인라인이라 hover 배경보다 세다 — 고른 행은 hover 로 흐려지지 않는다.
|
|
18157
|
+
background: dragSel ? DRAG_BG : rowLocked ? LOCKED_BG : void 0,
|
|
18158
|
+
// 클래스의 기본 글자색을 덮는다. 셀이 자기 색을 정한 내용(SIcon 의
|
|
18159
|
+
// color, STag 등)까지는 닿지 않는다 — 그건 소비 앱 몫이다.
|
|
18160
|
+
color: rowLocked ? LOCKED_CONTENT : void 0,
|
|
18161
|
+
boxShadow: shadows.length > 0 ? shadows.join(", ") : void 0,
|
|
18162
|
+
...cellStyle(i)
|
|
18163
|
+
};
|
|
18164
|
+
if (col.renderCell) {
|
|
18165
|
+
const cell = col.renderCell({
|
|
18166
|
+
row,
|
|
18167
|
+
value: cellValue(col, row),
|
|
18168
|
+
column: col,
|
|
18169
|
+
colIndex: i,
|
|
18170
|
+
rowIndex: ri,
|
|
18171
|
+
isSelected: isSel,
|
|
18172
|
+
className: tdClassName,
|
|
18173
|
+
style: tdStyle
|
|
18174
|
+
});
|
|
18175
|
+
if (cell == null || cell === false) return null;
|
|
18176
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: cell }, col.name);
|
|
18177
|
+
}
|
|
18178
|
+
return /* @__PURE__ */ jsx("td", { className: tdClassName, style: tdStyle, children: col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row) }, col.name);
|
|
18179
|
+
})
|
|
18180
|
+
]
|
|
18181
|
+
},
|
|
18182
|
+
row[rowKey] ?? ri
|
|
18183
|
+
);
|
|
18184
|
+
}),
|
|
18185
|
+
virtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
|
|
18186
|
+
] })
|
|
18187
|
+
]
|
|
18188
|
+
}
|
|
18189
|
+
)
|
|
18190
|
+
]
|
|
17881
18191
|
}
|
|
17882
18192
|
)
|
|
17883
18193
|
),
|
|
@@ -21154,6 +21464,169 @@ function ChartAxisLabels({
|
|
|
21154
21464
|
}
|
|
21155
21465
|
);
|
|
21156
21466
|
}
|
|
21467
|
+
function useElementSize(target) {
|
|
21468
|
+
const ref = useRef(null);
|
|
21469
|
+
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
21470
|
+
useEffect(() => {
|
|
21471
|
+
const element = ref.current;
|
|
21472
|
+
if (!element) return;
|
|
21473
|
+
const update = () => {
|
|
21474
|
+
const { width, height } = element.getBoundingClientRect();
|
|
21475
|
+
setSize(
|
|
21476
|
+
(prev) => Math.abs(prev.width - width) < 0.5 && Math.abs(prev.height - height) < 0.5 ? prev : { width, height }
|
|
21477
|
+
);
|
|
21478
|
+
};
|
|
21479
|
+
update();
|
|
21480
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
21481
|
+
const observer = new ResizeObserver(update);
|
|
21482
|
+
observer.observe(element);
|
|
21483
|
+
return () => observer.disconnect();
|
|
21484
|
+
}, [target]);
|
|
21485
|
+
return { ref, size };
|
|
21486
|
+
}
|
|
21487
|
+
function useSvgTextMetrics() {
|
|
21488
|
+
const ref = useRef(null);
|
|
21489
|
+
const [metrics, setMetrics] = useState({ top: 0, width: 0 });
|
|
21490
|
+
useEffect(() => {
|
|
21491
|
+
const element = ref.current;
|
|
21492
|
+
if (!element) return;
|
|
21493
|
+
let box3;
|
|
21494
|
+
try {
|
|
21495
|
+
box3 = element.getBBox();
|
|
21496
|
+
} catch {
|
|
21497
|
+
return;
|
|
21498
|
+
}
|
|
21499
|
+
setMetrics(
|
|
21500
|
+
(prev) => Math.abs(prev.top - box3.y) < 0.5 && Math.abs(prev.width - box3.width) < 0.5 ? prev : { top: box3.y, width: box3.width }
|
|
21501
|
+
);
|
|
21502
|
+
});
|
|
21503
|
+
return { ref, metrics };
|
|
21504
|
+
}
|
|
21505
|
+
var CATEGORY_LABEL_ROTATED_MAX = 120;
|
|
21506
|
+
var DIAGONAL = Math.SQRT1_2;
|
|
21507
|
+
function ChartRotatedAxisLayout({
|
|
21508
|
+
axis,
|
|
21509
|
+
axisStyle,
|
|
21510
|
+
plot,
|
|
21511
|
+
minPlotWidth,
|
|
21512
|
+
labels,
|
|
21513
|
+
xAt,
|
|
21514
|
+
labelsRef
|
|
21515
|
+
}) {
|
|
21516
|
+
const scrollRef = useRef(null);
|
|
21517
|
+
const { ref: axisRef, size: axisBox } = useElementSize();
|
|
21518
|
+
return /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "min-w-0 overflow-x-auto", style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsxs("div", { style: { width: `max(100%, ${axisBox.width + minPlotWidth}px)` }, children: [
|
|
21519
|
+
/* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
21520
|
+
/* @__PURE__ */ jsx(
|
|
21521
|
+
"div",
|
|
21522
|
+
{
|
|
21523
|
+
ref: axisRef,
|
|
21524
|
+
className: "sticky left-0 z-1 shrink-0 bg-(--cmp-chart-bg)",
|
|
21525
|
+
style: { ...axisStyle, paddingRight: AXIS_LABEL_GAP },
|
|
21526
|
+
children: axis
|
|
21527
|
+
}
|
|
21528
|
+
),
|
|
21529
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: plot })
|
|
21530
|
+
] }),
|
|
21531
|
+
/* @__PURE__ */ jsx("div", { ref: labelsRef, children: /* @__PURE__ */ jsx(
|
|
21532
|
+
ChartRotatedCategoryLabels,
|
|
21533
|
+
{
|
|
21534
|
+
labels,
|
|
21535
|
+
xAt,
|
|
21536
|
+
gutter: axisBox.width,
|
|
21537
|
+
scrollRef
|
|
21538
|
+
}
|
|
21539
|
+
) })
|
|
21540
|
+
] }) });
|
|
21541
|
+
}
|
|
21542
|
+
function ChartRotatedCategoryLabels({
|
|
21543
|
+
labels,
|
|
21544
|
+
xAt,
|
|
21545
|
+
gutter,
|
|
21546
|
+
scrollRef
|
|
21547
|
+
}) {
|
|
21548
|
+
const sizerRef = useRef(null);
|
|
21549
|
+
const maskRef = useRef(null);
|
|
21550
|
+
const [box3, setBox] = useState({ line: 0, widest: 0 });
|
|
21551
|
+
useEffect(() => {
|
|
21552
|
+
const sizer = sizerRef.current;
|
|
21553
|
+
if (!sizer) return;
|
|
21554
|
+
const measure = () => {
|
|
21555
|
+
const { width, height: height2 } = sizer.getBoundingClientRect();
|
|
21556
|
+
const line = labels.length > 0 ? height2 / labels.length : 0;
|
|
21557
|
+
setBox(
|
|
21558
|
+
(prev) => Math.abs(prev.line - line) < 0.5 && Math.abs(prev.widest - width) < 0.5 ? prev : { line, widest: width }
|
|
21559
|
+
);
|
|
21560
|
+
};
|
|
21561
|
+
measure();
|
|
21562
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
21563
|
+
const observer = new ResizeObserver(measure);
|
|
21564
|
+
observer.observe(sizer);
|
|
21565
|
+
return () => observer.disconnect();
|
|
21566
|
+
});
|
|
21567
|
+
useEffect(() => {
|
|
21568
|
+
const scroller = scrollRef.current;
|
|
21569
|
+
const mask = maskRef.current;
|
|
21570
|
+
if (!scroller || !mask) return;
|
|
21571
|
+
const sync2 = () => {
|
|
21572
|
+
mask.style.visibility = scroller.scrollLeft > 0 ? "visible" : "hidden";
|
|
21573
|
+
};
|
|
21574
|
+
sync2();
|
|
21575
|
+
scroller.addEventListener("scroll", sync2, { passive: true });
|
|
21576
|
+
return () => scroller.removeEventListener("scroll", sync2);
|
|
21577
|
+
}, [scrollRef]);
|
|
21578
|
+
const maxWidthAt = (index) => Math.max(
|
|
21579
|
+
0,
|
|
21580
|
+
Math.min(CATEGORY_LABEL_ROTATED_MAX, (xAt(index) + gutter) / DIAGONAL - box3.line / 2)
|
|
21581
|
+
);
|
|
21582
|
+
const height = Math.ceil(box3.line / 2 + (box3.widest + box3.line / 2) * DIAGONAL);
|
|
21583
|
+
return (
|
|
21584
|
+
// overflow 는 clip 이어야 한다 — hidden 이면 이 행이 스크롤 상자로 잡혀 가림막의
|
|
21585
|
+
// sticky 가 스크롤을 따라가지 않는다.
|
|
21586
|
+
/* @__PURE__ */ jsxs(
|
|
21587
|
+
"div",
|
|
21588
|
+
{
|
|
21589
|
+
"aria-hidden": true,
|
|
21590
|
+
className: "relative overflow-clip typo-body-sm-medium text-(--cmp-chart-axis-color)",
|
|
21591
|
+
style: { height },
|
|
21592
|
+
children: [
|
|
21593
|
+
/* @__PURE__ */ jsx(
|
|
21594
|
+
"div",
|
|
21595
|
+
{
|
|
21596
|
+
ref: maskRef,
|
|
21597
|
+
className: "invisible sticky left-0 z-1 h-full bg-(--cmp-chart-bg)",
|
|
21598
|
+
style: { width: gutter }
|
|
21599
|
+
}
|
|
21600
|
+
),
|
|
21601
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0", style: { left: gutter }, children: labels.map((label, index) => /* @__PURE__ */ jsx(
|
|
21602
|
+
"span",
|
|
21603
|
+
{
|
|
21604
|
+
className: "absolute top-0 truncate",
|
|
21605
|
+
style: {
|
|
21606
|
+
left: xAt(index),
|
|
21607
|
+
maxWidth: maxWidthAt(index),
|
|
21608
|
+
// 글자 끝(오른쪽 가운데)을 눈금에 맞춘 뒤 그 점을 축으로 기울인다.
|
|
21609
|
+
transform: "translateX(-100%) rotate(-45deg)",
|
|
21610
|
+
transformOrigin: "right center"
|
|
21611
|
+
},
|
|
21612
|
+
children: label
|
|
21613
|
+
},
|
|
21614
|
+
`${label}-${index}`
|
|
21615
|
+
)) }),
|
|
21616
|
+
/* @__PURE__ */ jsx("div", { ref: sizerRef, className: "invisible absolute top-0 left-0 flex flex-col items-start", children: labels.map((label, index) => /* @__PURE__ */ jsx(
|
|
21617
|
+
"span",
|
|
21618
|
+
{
|
|
21619
|
+
className: "block truncate",
|
|
21620
|
+
style: { maxWidth: maxWidthAt(index) },
|
|
21621
|
+
children: label
|
|
21622
|
+
},
|
|
21623
|
+
`${label}-${index}`
|
|
21624
|
+
)) })
|
|
21625
|
+
]
|
|
21626
|
+
}
|
|
21627
|
+
)
|
|
21628
|
+
);
|
|
21629
|
+
}
|
|
21157
21630
|
var ChartFrame = /* @__PURE__ */ forwardRef(function ChartFrame2({ title, unit, legend, legendPosition = "top", children, className, style, ...props }, ref) {
|
|
21158
21631
|
return /* @__PURE__ */ jsxs(
|
|
21159
21632
|
"div",
|
|
@@ -21345,44 +21818,6 @@ function linePointDimmedColor(palette, index, colors) {
|
|
|
21345
21818
|
function dataValueColor(palette, index, colors) {
|
|
21346
21819
|
return pick(DATA_VALUE_COLORS, DATA_VALUE_PALETTES, palette, index, colors);
|
|
21347
21820
|
}
|
|
21348
|
-
function useElementSize() {
|
|
21349
|
-
const ref = useRef(null);
|
|
21350
|
-
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
21351
|
-
useEffect(() => {
|
|
21352
|
-
const element = ref.current;
|
|
21353
|
-
if (!element) return;
|
|
21354
|
-
const update = () => {
|
|
21355
|
-
const { width, height } = element.getBoundingClientRect();
|
|
21356
|
-
setSize(
|
|
21357
|
-
(prev) => Math.abs(prev.width - width) < 0.5 && Math.abs(prev.height - height) < 0.5 ? prev : { width, height }
|
|
21358
|
-
);
|
|
21359
|
-
};
|
|
21360
|
-
update();
|
|
21361
|
-
if (typeof ResizeObserver === "undefined") return;
|
|
21362
|
-
const observer = new ResizeObserver(update);
|
|
21363
|
-
observer.observe(element);
|
|
21364
|
-
return () => observer.disconnect();
|
|
21365
|
-
}, []);
|
|
21366
|
-
return { ref, size };
|
|
21367
|
-
}
|
|
21368
|
-
function useSvgTextMetrics() {
|
|
21369
|
-
const ref = useRef(null);
|
|
21370
|
-
const [metrics, setMetrics] = useState({ top: 0, width: 0 });
|
|
21371
|
-
useEffect(() => {
|
|
21372
|
-
const element = ref.current;
|
|
21373
|
-
if (!element) return;
|
|
21374
|
-
let box3;
|
|
21375
|
-
try {
|
|
21376
|
-
box3 = element.getBBox();
|
|
21377
|
-
} catch {
|
|
21378
|
-
return;
|
|
21379
|
-
}
|
|
21380
|
-
setMetrics(
|
|
21381
|
-
(prev) => Math.abs(prev.top - box3.y) < 0.5 && Math.abs(prev.width - box3.width) < 0.5 ? prev : { top: box3.y, width: box3.width }
|
|
21382
|
-
);
|
|
21383
|
-
});
|
|
21384
|
-
return { ref, metrics };
|
|
21385
|
-
}
|
|
21386
21821
|
var DOT_SIZE2 = 6;
|
|
21387
21822
|
var DEFAULT_GAP_X = { after: 4, before: 4 };
|
|
21388
21823
|
var ANCHOR_GAP_Y = 0;
|
|
@@ -21544,6 +21979,7 @@ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
|
|
|
21544
21979
|
tickCount = 8,
|
|
21545
21980
|
barSize = BAR_SIZE_MAX,
|
|
21546
21981
|
showValueLabel = true,
|
|
21982
|
+
rotateCategoryLabels = false,
|
|
21547
21983
|
showLegend = true,
|
|
21548
21984
|
legendPosition = "top",
|
|
21549
21985
|
showTooltip = true,
|
|
@@ -21555,9 +21991,10 @@ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
|
|
|
21555
21991
|
style
|
|
21556
21992
|
}, ref) {
|
|
21557
21993
|
const vertical = direction === "vertical";
|
|
21994
|
+
const rotateLabels = rotateCategoryLabels && vertical;
|
|
21558
21995
|
const { ref: plotRef, size } = useElementSize();
|
|
21559
|
-
const { ref: axisTextRef, size: axisText } = useElementSize();
|
|
21560
|
-
const { ref: axisSampleRef, size: axisSample } = useElementSize();
|
|
21996
|
+
const { ref: axisTextRef, size: axisText } = useElementSize(rotateLabels);
|
|
21997
|
+
const { ref: axisSampleRef, size: axisSample } = useElementSize(rotateLabels);
|
|
21561
21998
|
const clipId = useId();
|
|
21562
21999
|
const [hoveredCategory, setHoveredCategory] = useState(null);
|
|
21563
22000
|
const perCategory = series.length === 1;
|
|
@@ -22016,7 +22453,29 @@ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
|
|
|
22016
22453
|
// 눈금은 왼쪽 한 줄뿐이다 — 오른쪽에도 적으면 서로 다른 단위처럼 읽힌다.
|
|
22017
22454
|
gridTemplateColumns: "max-content 1fr"
|
|
22018
22455
|
},
|
|
22019
|
-
children:
|
|
22456
|
+
children: rotateLabels ? (
|
|
22457
|
+
// 기울인 이름은 눈금 열 아래까지 뻗으므로 스크롤 상자가 눈금 열까지 감싼다.
|
|
22458
|
+
/* @__PURE__ */ jsx(
|
|
22459
|
+
ChartRotatedAxisLayout,
|
|
22460
|
+
{
|
|
22461
|
+
axis: /* @__PURE__ */ jsx(
|
|
22462
|
+
ChartAxisLabels,
|
|
22463
|
+
{
|
|
22464
|
+
scale,
|
|
22465
|
+
direction,
|
|
22466
|
+
format: formatValue,
|
|
22467
|
+
sampleRef: axisSampleRef
|
|
22468
|
+
}
|
|
22469
|
+
),
|
|
22470
|
+
axisStyle: { height: plotHeight, ...inset },
|
|
22471
|
+
plot,
|
|
22472
|
+
minPlotWidth,
|
|
22473
|
+
labels: categories,
|
|
22474
|
+
xAt: (index) => layout.offset + (index + 0.5) * layout.band,
|
|
22475
|
+
labelsRef: axisTextRef
|
|
22476
|
+
}
|
|
22477
|
+
)
|
|
22478
|
+
) : vertical ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22020
22479
|
/* @__PURE__ */ jsx("div", { style: { height: plotHeight, ...inset }, children: /* @__PURE__ */ jsx(
|
|
22021
22480
|
ChartAxisLabels,
|
|
22022
22481
|
{
|
|
@@ -22147,6 +22606,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22147
22606
|
tickCount = 8,
|
|
22148
22607
|
showPoint = true,
|
|
22149
22608
|
showValueLabel = false,
|
|
22609
|
+
rotateCategoryLabels = false,
|
|
22150
22610
|
showLegend = true,
|
|
22151
22611
|
legendPosition = "top",
|
|
22152
22612
|
showTooltip = true,
|
|
@@ -22159,8 +22619,8 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22159
22619
|
}, ref) {
|
|
22160
22620
|
const smooth = curve === "smooth";
|
|
22161
22621
|
const { ref: plotRef, size } = useElementSize();
|
|
22162
|
-
const { ref: axisTextRef, size: axisText } = useElementSize();
|
|
22163
|
-
const { ref: axisSampleRef, size: axisSample } = useElementSize();
|
|
22622
|
+
const { ref: axisTextRef, size: axisText } = useElementSize(rotateCategoryLabels);
|
|
22623
|
+
const { ref: axisSampleRef, size: axisSample } = useElementSize(rotateCategoryLabels);
|
|
22164
22624
|
const gradientId = useId();
|
|
22165
22625
|
const [hoveredCategory, setHoveredCategory] = useState(null);
|
|
22166
22626
|
const colorAt = useCallback(
|
|
@@ -22194,23 +22654,16 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22194
22654
|
[rows, tickCount, max]
|
|
22195
22655
|
);
|
|
22196
22656
|
const count = categories.length;
|
|
22197
|
-
const
|
|
22198
|
-
const xAt = useCallback(
|
|
22199
|
-
(index) => count > 1 ? index * step : size.width / 2,
|
|
22200
|
-
[count, step, size.width]
|
|
22201
|
-
);
|
|
22657
|
+
const band = Math.max(0, size.width - AXIS_EDGE_PADDING * 2) / Math.max(1, count);
|
|
22658
|
+
const xAt = useCallback((index) => AXIS_EDGE_PADDING + (index + 0.5) * band, [band]);
|
|
22202
22659
|
const cellOf = useCallback(
|
|
22203
|
-
(index) =>
|
|
22204
|
-
left:
|
|
22205
|
-
right:
|
|
22206
|
-
}
|
|
22207
|
-
[
|
|
22208
|
-
);
|
|
22209
|
-
const edgeOf = useCallback(
|
|
22210
|
-
(index) => count > 1 && index === 0 ? "start" : count > 1 && index === count - 1 ? "end" : null,
|
|
22211
|
-
[count]
|
|
22660
|
+
(index) => ({
|
|
22661
|
+
left: AXIS_EDGE_PADDING + index * band,
|
|
22662
|
+
right: AXIS_EDGE_PADDING + (index + 1) * band
|
|
22663
|
+
}),
|
|
22664
|
+
[band]
|
|
22212
22665
|
);
|
|
22213
|
-
const minPlotWidth =
|
|
22666
|
+
const minPlotWidth = count * BAND_MIN + AXIS_EDGE_PADDING * 2;
|
|
22214
22667
|
const totalHeight = height ?? 320;
|
|
22215
22668
|
const plotHeight = typeof totalHeight === "number" ? Math.max(0, totalHeight - axisText.height) : `calc(${totalHeight} - ${axisText.height}px)`;
|
|
22216
22669
|
const plotted = useMemo(() => {
|
|
@@ -22224,10 +22677,10 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22224
22677
|
}, [rows, size.width, size.height, xAt, scale]);
|
|
22225
22678
|
const legend = showLegend ? /* @__PURE__ */ jsx(ChartLegend, { items: series.map((s, i) => ({ name: s.name, color: colorAt(i) })) }) : null;
|
|
22226
22679
|
const handleMove = (event) => {
|
|
22227
|
-
if (!showTooltip || count === 0 ||
|
|
22680
|
+
if (!showTooltip || count === 0 || band <= 0 || series.length === 0) return;
|
|
22228
22681
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
22229
|
-
const along = event.clientX - rect.left;
|
|
22230
|
-
const category =
|
|
22682
|
+
const along = event.clientX - rect.left - AXIS_EDGE_PADDING;
|
|
22683
|
+
const category = Math.floor(along / band);
|
|
22231
22684
|
setHoveredCategory(category >= 0 && category < categories.length ? category : null);
|
|
22232
22685
|
};
|
|
22233
22686
|
const tooltipItems = hoveredCategory == null ? [] : rows.flatMap((row, seriesIndex) => {
|
|
@@ -22266,10 +22719,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22266
22719
|
categories.map((label, index) => /* @__PURE__ */ jsx(
|
|
22267
22720
|
"span",
|
|
22268
22721
|
{
|
|
22269
|
-
className:
|
|
22270
|
-
"absolute top-0 whitespace-nowrap",
|
|
22271
|
-
edgeOf(index) === "start" ? "translate-x-0" : edgeOf(index) === "end" ? "-translate-x-full" : "-translate-x-1/2"
|
|
22272
|
-
),
|
|
22722
|
+
className: "absolute top-0 -translate-x-1/2 whitespace-nowrap",
|
|
22273
22723
|
style: { left: xAt(index) },
|
|
22274
22724
|
children: label
|
|
22275
22725
|
},
|
|
@@ -22450,7 +22900,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22450
22900
|
y: point.y - POINT_RADIUS - VALUE_LABEL_GAP2,
|
|
22451
22901
|
className: hoveredCategory === categoryIndex ? LINE_DATA_LABEL_TYPO_HOVER : LINE_DATA_LABEL_TYPO,
|
|
22452
22902
|
style: { fill: LINE_DATA_LABEL_COLOR },
|
|
22453
|
-
textAnchor:
|
|
22903
|
+
textAnchor: "middle",
|
|
22454
22904
|
dominantBaseline: "auto",
|
|
22455
22905
|
children: formatValue(point.own)
|
|
22456
22906
|
},
|
|
@@ -22458,9 +22908,106 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22458
22908
|
)
|
|
22459
22909
|
)
|
|
22460
22910
|
) : [],
|
|
22461
|
-
[showValueLabel, plotted, hoveredCategory, formatValue
|
|
22911
|
+
[showValueLabel, plotted, hoveredCategory, formatValue]
|
|
22462
22912
|
);
|
|
22463
22913
|
const inset = { paddingTop: axisSample.height / 2, paddingBottom: axisSample.height / 2 };
|
|
22914
|
+
const axisLabels = /* @__PURE__ */ jsx(
|
|
22915
|
+
ChartAxisLabels,
|
|
22916
|
+
{
|
|
22917
|
+
scale,
|
|
22918
|
+
direction: "vertical",
|
|
22919
|
+
format: formatValue,
|
|
22920
|
+
sampleRef: axisSampleRef
|
|
22921
|
+
}
|
|
22922
|
+
);
|
|
22923
|
+
const plot = (
|
|
22924
|
+
// 바깥은 자리(높이·물러남)를, 안쪽은 그림을 맡는다. minWidth 는 바깥에 둬야
|
|
22925
|
+
// 스크롤 컨테이너가 넘치는 폭을 알아본다.
|
|
22926
|
+
/* @__PURE__ */ jsx(
|
|
22927
|
+
"div",
|
|
22928
|
+
{
|
|
22929
|
+
className: "relative min-w-0",
|
|
22930
|
+
style: { height: plotHeight, minWidth: minPlotWidth || void 0, ...inset },
|
|
22931
|
+
children: /* @__PURE__ */ jsxs(
|
|
22932
|
+
"div",
|
|
22933
|
+
{
|
|
22934
|
+
ref: plotRef,
|
|
22935
|
+
className: "relative h-full w-full",
|
|
22936
|
+
onMouseMove: handleMove,
|
|
22937
|
+
onMouseLeave: () => setHoveredCategory(null),
|
|
22938
|
+
children: [
|
|
22939
|
+
size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxs(
|
|
22940
|
+
"svg",
|
|
22941
|
+
{
|
|
22942
|
+
width: size.width,
|
|
22943
|
+
height: size.height,
|
|
22944
|
+
overflow: "visible",
|
|
22945
|
+
role: "img",
|
|
22946
|
+
"aria-label": `${categories.length}\uAC1C \uD56D\uBAA9, ${series.length}\uAC1C \uACC4\uC5F4 ${stacked ? "\uB204\uC801 " : ""}\uAEBE\uC740\uC120 \uADF8\uB798\uD504`,
|
|
22947
|
+
children: [
|
|
22948
|
+
/* @__PURE__ */ jsx(
|
|
22949
|
+
"text",
|
|
22950
|
+
{
|
|
22951
|
+
ref: valueLabelRef,
|
|
22952
|
+
"aria-hidden": true,
|
|
22953
|
+
x: 0,
|
|
22954
|
+
y: 0,
|
|
22955
|
+
className: LINE_DATA_LABEL_TYPO_HOVER,
|
|
22956
|
+
dominantBaseline: "auto",
|
|
22957
|
+
style: { visibility: "hidden" },
|
|
22958
|
+
children: "0"
|
|
22959
|
+
}
|
|
22960
|
+
),
|
|
22961
|
+
/* @__PURE__ */ jsx(ChartGrid, { scale, direction: "vertical", width: size.width, height: size.height }),
|
|
22962
|
+
hoveredCategory != null && /* @__PURE__ */ jsx(
|
|
22963
|
+
"rect",
|
|
22964
|
+
{
|
|
22965
|
+
"aria-hidden": true,
|
|
22966
|
+
x: cellOf(hoveredCategory).left,
|
|
22967
|
+
y: 0,
|
|
22968
|
+
width: cellOf(hoveredCategory).right - cellOf(hoveredCategory).left,
|
|
22969
|
+
height: size.height,
|
|
22970
|
+
style: {
|
|
22971
|
+
fill: "var(--cmp-chart-barContainer-hover-bg)",
|
|
22972
|
+
fillOpacity: "var(--cmp-chart-barContainer-hover-opacity)"
|
|
22973
|
+
}
|
|
22974
|
+
}
|
|
22975
|
+
),
|
|
22976
|
+
areaGradients,
|
|
22977
|
+
areaMask,
|
|
22978
|
+
area && /* @__PURE__ */ jsx("g", { mask: `url(#${areaMaskId})`, children: areas }),
|
|
22979
|
+
lines,
|
|
22980
|
+
pointHalos,
|
|
22981
|
+
pointDots,
|
|
22982
|
+
valueLabels
|
|
22983
|
+
]
|
|
22984
|
+
}
|
|
22985
|
+
),
|
|
22986
|
+
showTooltip && hoveredCategory != null && tooltipAnchor != null && tooltipItems.length > 0 && /* @__PURE__ */ jsx(
|
|
22987
|
+
ChartTooltip,
|
|
22988
|
+
{
|
|
22989
|
+
title: categories[hoveredCategory] ?? "",
|
|
22990
|
+
items: tooltipItems,
|
|
22991
|
+
anchor: tooltipAnchor,
|
|
22992
|
+
bounds: size,
|
|
22993
|
+
gapX: { after: -4, before: -4 },
|
|
22994
|
+
footer: tooltipFooter?.({
|
|
22995
|
+
categoryIndex: hoveredCategory,
|
|
22996
|
+
name: categories[hoveredCategory] ?? "",
|
|
22997
|
+
points: rows.flatMap((row, seriesIndex) => {
|
|
22998
|
+
const point = row[hoveredCategory];
|
|
22999
|
+
if (point == null) return [];
|
|
23000
|
+
return [{ seriesIndex, categoryIndex: hoveredCategory, value: point.own }];
|
|
23001
|
+
})
|
|
23002
|
+
})
|
|
23003
|
+
}
|
|
23004
|
+
)
|
|
23005
|
+
]
|
|
23006
|
+
}
|
|
23007
|
+
)
|
|
23008
|
+
}
|
|
23009
|
+
)
|
|
23010
|
+
);
|
|
22464
23011
|
return /* @__PURE__ */ jsx(
|
|
22465
23012
|
ChartFrame,
|
|
22466
23013
|
{
|
|
@@ -22471,7 +23018,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22471
23018
|
legendPosition,
|
|
22472
23019
|
className,
|
|
22473
23020
|
style,
|
|
22474
|
-
children: /* @__PURE__ */
|
|
23021
|
+
children: /* @__PURE__ */ jsx(
|
|
22475
23022
|
"div",
|
|
22476
23023
|
{
|
|
22477
23024
|
className: "grid",
|
|
@@ -22480,119 +23027,27 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22480
23027
|
// 눈금은 왼쪽 한 줄뿐이다 — 오른쪽에도 적으면 서로 다른 단위처럼 읽힌다.
|
|
22481
23028
|
gridTemplateColumns: "max-content 1fr"
|
|
22482
23029
|
},
|
|
22483
|
-
children:
|
|
22484
|
-
|
|
22485
|
-
|
|
22486
|
-
|
|
22487
|
-
scale,
|
|
22488
|
-
direction: "vertical",
|
|
22489
|
-
format: formatValue,
|
|
22490
|
-
sampleRef: axisSampleRef
|
|
22491
|
-
}
|
|
22492
|
-
) }),
|
|
22493
|
-
/* @__PURE__ */ jsxs(
|
|
22494
|
-
"div",
|
|
23030
|
+
children: rotateCategoryLabels ? (
|
|
23031
|
+
// 기울인 이름은 눈금 열 아래까지 뻗으므로 스크롤 상자가 눈금 열까지 감싼다.
|
|
23032
|
+
/* @__PURE__ */ jsx(
|
|
23033
|
+
ChartRotatedAxisLayout,
|
|
22495
23034
|
{
|
|
22496
|
-
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
style: { height: plotHeight, minWidth: minPlotWidth || void 0, ...inset },
|
|
22504
|
-
children: /* @__PURE__ */ jsxs(
|
|
22505
|
-
"div",
|
|
22506
|
-
{
|
|
22507
|
-
ref: plotRef,
|
|
22508
|
-
className: "relative h-full w-full",
|
|
22509
|
-
onMouseMove: handleMove,
|
|
22510
|
-
onMouseLeave: () => setHoveredCategory(null),
|
|
22511
|
-
children: [
|
|
22512
|
-
size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxs(
|
|
22513
|
-
"svg",
|
|
22514
|
-
{
|
|
22515
|
-
width: size.width,
|
|
22516
|
-
height: size.height,
|
|
22517
|
-
overflow: "visible",
|
|
22518
|
-
role: "img",
|
|
22519
|
-
"aria-label": `${categories.length}\uAC1C \uD56D\uBAA9, ${series.length}\uAC1C \uACC4\uC5F4 ${stacked ? "\uB204\uC801 " : ""}\uAEBE\uC740\uC120 \uADF8\uB798\uD504`,
|
|
22520
|
-
children: [
|
|
22521
|
-
/* @__PURE__ */ jsx(
|
|
22522
|
-
"text",
|
|
22523
|
-
{
|
|
22524
|
-
ref: valueLabelRef,
|
|
22525
|
-
"aria-hidden": true,
|
|
22526
|
-
x: 0,
|
|
22527
|
-
y: 0,
|
|
22528
|
-
className: LINE_DATA_LABEL_TYPO_HOVER,
|
|
22529
|
-
dominantBaseline: "auto",
|
|
22530
|
-
style: { visibility: "hidden" },
|
|
22531
|
-
children: "0"
|
|
22532
|
-
}
|
|
22533
|
-
),
|
|
22534
|
-
/* @__PURE__ */ jsx(
|
|
22535
|
-
ChartGrid,
|
|
22536
|
-
{
|
|
22537
|
-
scale,
|
|
22538
|
-
direction: "vertical",
|
|
22539
|
-
width: size.width,
|
|
22540
|
-
height: size.height
|
|
22541
|
-
}
|
|
22542
|
-
),
|
|
22543
|
-
hoveredCategory != null && /* @__PURE__ */ jsx(
|
|
22544
|
-
"rect",
|
|
22545
|
-
{
|
|
22546
|
-
"aria-hidden": true,
|
|
22547
|
-
x: cellOf(hoveredCategory).left,
|
|
22548
|
-
y: 0,
|
|
22549
|
-
width: cellOf(hoveredCategory).right - cellOf(hoveredCategory).left,
|
|
22550
|
-
height: size.height,
|
|
22551
|
-
style: {
|
|
22552
|
-
fill: "var(--cmp-chart-barContainer-hover-bg)",
|
|
22553
|
-
fillOpacity: "var(--cmp-chart-barContainer-hover-opacity)"
|
|
22554
|
-
}
|
|
22555
|
-
}
|
|
22556
|
-
),
|
|
22557
|
-
areaGradients,
|
|
22558
|
-
areaMask,
|
|
22559
|
-
area && /* @__PURE__ */ jsx("g", { mask: `url(#${areaMaskId})`, children: areas }),
|
|
22560
|
-
lines,
|
|
22561
|
-
pointHalos,
|
|
22562
|
-
pointDots,
|
|
22563
|
-
valueLabels
|
|
22564
|
-
]
|
|
22565
|
-
}
|
|
22566
|
-
),
|
|
22567
|
-
showTooltip && hoveredCategory != null && tooltipAnchor != null && tooltipItems.length > 0 && /* @__PURE__ */ jsx(
|
|
22568
|
-
ChartTooltip,
|
|
22569
|
-
{
|
|
22570
|
-
title: categories[hoveredCategory] ?? "",
|
|
22571
|
-
items: tooltipItems,
|
|
22572
|
-
anchor: tooltipAnchor,
|
|
22573
|
-
bounds: size,
|
|
22574
|
-
gapX: { after: -4, before: -4 },
|
|
22575
|
-
footer: tooltipFooter?.({
|
|
22576
|
-
categoryIndex: hoveredCategory,
|
|
22577
|
-
name: categories[hoveredCategory] ?? "",
|
|
22578
|
-
points: rows.flatMap((row, seriesIndex) => {
|
|
22579
|
-
const point = row[hoveredCategory];
|
|
22580
|
-
if (point == null) return [];
|
|
22581
|
-
return [{ seriesIndex, categoryIndex: hoveredCategory, value: point.own }];
|
|
22582
|
-
})
|
|
22583
|
-
})
|
|
22584
|
-
}
|
|
22585
|
-
)
|
|
22586
|
-
]
|
|
22587
|
-
}
|
|
22588
|
-
)
|
|
22589
|
-
}
|
|
22590
|
-
),
|
|
22591
|
-
/* @__PURE__ */ jsx("div", { ref: axisTextRef, children: categoryLabels })
|
|
22592
|
-
]
|
|
23035
|
+
axis: axisLabels,
|
|
23036
|
+
axisStyle: { height: plotHeight, ...inset },
|
|
23037
|
+
plot,
|
|
23038
|
+
minPlotWidth,
|
|
23039
|
+
labels: categories,
|
|
23040
|
+
xAt,
|
|
23041
|
+
labelsRef: axisTextRef
|
|
22593
23042
|
}
|
|
22594
23043
|
)
|
|
22595
|
-
|
|
23044
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23045
|
+
/* @__PURE__ */ jsx("div", { style: { height: plotHeight, ...inset }, children: axisLabels }),
|
|
23046
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 overflow-x-auto", children: [
|
|
23047
|
+
plot,
|
|
23048
|
+
/* @__PURE__ */ jsx("div", { ref: axisTextRef, children: categoryLabels })
|
|
23049
|
+
] })
|
|
23050
|
+
] })
|
|
22596
23051
|
}
|
|
22597
23052
|
)
|
|
22598
23053
|
}
|