sellmate-design-system-react 9.0.0-beta.91 → 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 +27 -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/SGnbSystem/README.md +6 -2
- package/dist/components/SGnbSystem/SGnbSystem.d.ts +3 -3
- package/dist/components/SGnbSystem/gnbSystem.config.d.ts +5 -3
- 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/STimePicker/README.md +1 -1
- package/dist/components/STimePicker/STimePicker.d.ts +3 -7
- package/dist/components/STimePicker/TimeListbox.d.ts +21 -0
- package/dist/components/STimeRangePicker/README.md +2 -2
- package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +5 -1
- package/dist/components/STreeTable/STreeTable.examples.d.ts +7 -0
- package/dist/index.cjs +1339 -1128
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1339 -1128
- 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/mode-listbox.d.ts +10 -0
- package/dist/lib/tree-row.d.ts +74 -0
- package/dist/llms-full.txt +55 -11
- package/dist/llms.txt +27 -2
- package/dist/styles.css +12 -37
- 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
|
),
|
|
@@ -13394,9 +13439,20 @@ var SystemButton = /* @__PURE__ */ forwardRef(function SystemButton2({
|
|
|
13394
13439
|
}
|
|
13395
13440
|
),
|
|
13396
13441
|
label && /* @__PURE__ */ jsx("span", { className: "typo-body-sm-semibold min-w-0 truncate", children: label }),
|
|
13397
|
-
count != null && count > 0 && //
|
|
13442
|
+
count != null && count > 0 && // 개수는 채운 배지로 단다 — 배경·글자색·치수는 xs deep 태그 토큰이 쥔다(시안).
|
|
13443
|
+
// 배지 색은 고정이다: 흰 면은 파랑, 어두운 면(dark·darker)은 노랑 바탕에 검은 글자.
|
|
13444
|
+
// 배지 오른쪽은 버튼 여백(alertButton.paddingX)만으로는 좁다 — 시안이 4px 을 더 준다.
|
|
13398
13445
|
// 대응 토큰이 없어 디자인 값을 그대로 쓴다.
|
|
13399
|
-
/* @__PURE__ */ jsx(
|
|
13446
|
+
/* @__PURE__ */ jsx(
|
|
13447
|
+
STag,
|
|
13448
|
+
{
|
|
13449
|
+
size: "xs",
|
|
13450
|
+
deep: true,
|
|
13451
|
+
color: color === "light" ? "blue" : "yellow",
|
|
13452
|
+
label: count.toLocaleString(),
|
|
13453
|
+
className: "me-[4px] shrink-0"
|
|
13454
|
+
}
|
|
13455
|
+
)
|
|
13400
13456
|
]
|
|
13401
13457
|
}
|
|
13402
13458
|
);
|
|
@@ -14275,7 +14331,7 @@ var SStepper = /* @__PURE__ */ forwardRef(function SStepper2({
|
|
|
14275
14331
|
items.findIndex((item4, index) => getItemValue(item4, index) === value)
|
|
14276
14332
|
);
|
|
14277
14333
|
const lastCompletedIndex = items.reduce(
|
|
14278
|
-
(
|
|
14334
|
+
(lastIndex2, item4, index) => item4.completed === true ? index : lastIndex2,
|
|
14279
14335
|
-1
|
|
14280
14336
|
);
|
|
14281
14337
|
const resolvedClickableIndex = lastCompletedIndex + 1;
|
|
@@ -15733,13 +15789,13 @@ var SSearchInput = /* @__PURE__ */ forwardRef(function SSearchInput2({
|
|
|
15733
15789
|
});
|
|
15734
15790
|
|
|
15735
15791
|
// src/components/SSelect/select.config.ts
|
|
15736
|
-
function flattenOptions(options, depth = 1) {
|
|
15792
|
+
function flattenOptions(options, depth = 1, inheritDisabled = true) {
|
|
15737
15793
|
const out = [];
|
|
15738
15794
|
for (const o of options) {
|
|
15739
15795
|
const hasChildren = Array.isArray(o.children) && o.children.length > 0;
|
|
15740
15796
|
if (hasChildren) {
|
|
15741
|
-
const childFlat = flattenOptions(o.children, depth + 1);
|
|
15742
|
-
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);
|
|
15743
15799
|
out.push({
|
|
15744
15800
|
value: String(o.value),
|
|
15745
15801
|
label: o.label,
|
|
@@ -15773,12 +15829,71 @@ function collectGroupLeafValues(flat, groupIndex) {
|
|
|
15773
15829
|
}
|
|
15774
15830
|
return values;
|
|
15775
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
|
+
}
|
|
15776
15890
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
15777
15891
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
15778
15892
|
var OPTION_ROW_HEIGHT = 28;
|
|
15779
15893
|
var DEFAULT_VIRTUAL_BUFFER = 10;
|
|
15780
15894
|
var DEFAULT_REACH_END_THRESHOLD = 10;
|
|
15781
15895
|
var DEFAULT_SEARCH_DEBOUNCE = 300;
|
|
15896
|
+
var NO_SEARCH_COLLAPSED = /* @__PURE__ */ new Set();
|
|
15782
15897
|
var SEARCH_THRESHOLD = 11;
|
|
15783
15898
|
function isListSized(el) {
|
|
15784
15899
|
return el.clientHeight > 0 && getComputedStyle(el).maxHeight !== "none";
|
|
@@ -15833,8 +15948,9 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15833
15948
|
className,
|
|
15834
15949
|
style
|
|
15835
15950
|
}, ref) {
|
|
15836
|
-
const multi = type === "multi" || type === "multi_depth";
|
|
15951
|
+
const multi = type === "multi" || type === "multi_depth" || type === "multi_tree";
|
|
15837
15952
|
const isDepth = type === "default_depth" || type === "multi_depth";
|
|
15953
|
+
const isTree = type === "tree" || type === "multi_tree";
|
|
15838
15954
|
const pagingEnabled = onReachEnd != null;
|
|
15839
15955
|
const partialOptions = pagingEnabled || serverSearch;
|
|
15840
15956
|
const portalContainer = usePortalContainer();
|
|
@@ -15844,6 +15960,8 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15844
15960
|
const [open, setOpen] = useState(false);
|
|
15845
15961
|
const [pendingOpen, setPendingOpen] = useState(false);
|
|
15846
15962
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
15963
|
+
const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
|
|
15964
|
+
const [searchCollapsed, setSearchCollapsed] = useState(NO_SEARCH_COLLAPSED);
|
|
15847
15965
|
const baseId = useId();
|
|
15848
15966
|
const listboxId = `${baseId}-listbox`;
|
|
15849
15967
|
const activeOptionId = `${baseId}-active-option`;
|
|
@@ -15867,10 +15985,11 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15867
15985
|
const alignSelectedTopRef = useRef(false);
|
|
15868
15986
|
const selectedAnchorRef = useRef(-1);
|
|
15869
15987
|
const rangeAnchorRef = useRef(null);
|
|
15870
|
-
const
|
|
15988
|
+
const groupIsValue = type === "tree";
|
|
15989
|
+
const flat = useMemo(() => flattenOptions(options, 1, !groupIsValue), [groupIsValue, options]);
|
|
15871
15990
|
const selectable = useMemo(() => selectableOptions(flat), [flat]);
|
|
15872
15991
|
const hasLeaf = useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
15873
|
-
const allOptionsDisabled = hasLeaf && selectable.length === 0;
|
|
15992
|
+
const allOptionsDisabled = groupIsValue ? flat.length > 0 && flat.every((o) => o.disabled) : hasLeaf && selectable.length === 0;
|
|
15874
15993
|
const effectiveDisabled = disabled || allOptionsDisabled;
|
|
15875
15994
|
const searchVisible = showSearch && (partialOptions || selectable.length >= SEARCH_THRESHOLD);
|
|
15876
15995
|
const labelByValue = useMemo(() => new Map(flat.map((o) => [o.value, o.label])), [flat]);
|
|
@@ -15913,6 +16032,10 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15913
16032
|
if (next) {
|
|
15914
16033
|
dismissedByPointerRef.current = false;
|
|
15915
16034
|
alignSelectedTopRef.current = true;
|
|
16035
|
+
if (isTree) {
|
|
16036
|
+
setExpanded(ancestorGroupValues(flat, new Set(selectedRef.current)));
|
|
16037
|
+
setSearchCollapsed(NO_SEARCH_COLLAPSED);
|
|
16038
|
+
}
|
|
15916
16039
|
} else {
|
|
15917
16040
|
setPendingOpen(false);
|
|
15918
16041
|
setSearch("");
|
|
@@ -15955,7 +16078,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15955
16078
|
},
|
|
15956
16079
|
resetValidation: () => setRuleError("")
|
|
15957
16080
|
});
|
|
15958
|
-
const
|
|
16081
|
+
const searched = useMemo(() => {
|
|
15959
16082
|
if (serverSearch || !searchVisible || search.trim() === "") return flat;
|
|
15960
16083
|
const q = search.trim().toLowerCase();
|
|
15961
16084
|
return filterDepthRows(
|
|
@@ -15963,23 +16086,53 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15963
16086
|
(o) => (isHtmlLabel(o.label) ? extractText(o.label) : o.label).toLowerCase().includes(q)
|
|
15964
16087
|
);
|
|
15965
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]);
|
|
15966
16100
|
const allValues = selectable.map((o) => o.value);
|
|
15967
16101
|
const allSelected = !partialOptions && allValues.length > 0 && allValues.every((v) => selectedSet.has(v));
|
|
15968
16102
|
const selectAllVisible = multi && showSelectAll && !partialOptions && allValues.length > 0;
|
|
15969
|
-
const hasItems = filtered.some((o) => !o.isGroup);
|
|
16103
|
+
const hasItems = isTree ? filtered.length > 0 : filtered.some((o) => !o.isGroup);
|
|
15970
16104
|
const groupLeafValues = useMemo(() => {
|
|
15971
16105
|
const map = /* @__PURE__ */ new Map();
|
|
15972
16106
|
filtered.forEach((o, i) => {
|
|
15973
|
-
if (o.isGroup)
|
|
16107
|
+
if (!o.isGroup) return;
|
|
16108
|
+
map.set(
|
|
16109
|
+
i,
|
|
16110
|
+
treeRows ? collectGroupLeafValues(searched, treeRows.sourceIndex[i]) : collectGroupLeafValues(filtered, i)
|
|
16111
|
+
);
|
|
15974
16112
|
});
|
|
15975
16113
|
return map;
|
|
15976
|
-
}, [filtered]);
|
|
16114
|
+
}, [filtered, searched, treeRows]);
|
|
15977
16115
|
const navigableItems = useMemo(() => {
|
|
15978
16116
|
const items = [];
|
|
15979
16117
|
if (selectAllVisible) {
|
|
15980
16118
|
items.push({ key: "__select_all__", isSelectAll: true });
|
|
15981
16119
|
}
|
|
15982
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
|
+
}
|
|
15983
16136
|
if (o.disabled) return;
|
|
15984
16137
|
if (o.isGroup) {
|
|
15985
16138
|
if (!multi) return;
|
|
@@ -15991,7 +16144,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
15991
16144
|
}
|
|
15992
16145
|
});
|
|
15993
16146
|
return items;
|
|
15994
|
-
}, [filtered, groupLeafValues, multi, selectAllVisible]);
|
|
16147
|
+
}, [filtered, groupLeafValues, isTree, multi, selectAllVisible]);
|
|
15995
16148
|
const selectedAnchorIndex = useMemo(() => {
|
|
15996
16149
|
if (selectedSet.size === 0) return -1;
|
|
15997
16150
|
return navigableItems.findIndex((item4) => item4.value != null && selectedSet.has(item4.value));
|
|
@@ -16002,7 +16155,10 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16002
16155
|
const rowCount = filtered.length;
|
|
16003
16156
|
const metrics = metricsRef.current;
|
|
16004
16157
|
const lastSearchRef = useRef(search);
|
|
16005
|
-
|
|
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) {
|
|
16006
16162
|
lastSearchRef.current = search;
|
|
16007
16163
|
resetRowMetrics(metrics, rowCount);
|
|
16008
16164
|
measuredKeyRef.current = "";
|
|
@@ -16142,7 +16298,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16142
16298
|
);
|
|
16143
16299
|
const fullGroupByLeaf = useMemo(() => {
|
|
16144
16300
|
const map = /* @__PURE__ */ new Map();
|
|
16145
|
-
if (!showChips || type !== "multi_depth") return map;
|
|
16301
|
+
if (!showChips || type !== "multi_depth" && type !== "multi_tree") return map;
|
|
16146
16302
|
flat.forEach((o, i) => {
|
|
16147
16303
|
if (!o.isGroup) return;
|
|
16148
16304
|
const leafValues = collectGroupLeafValues(flat, i);
|
|
@@ -16220,6 +16376,21 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16220
16376
|
rangeAnchorRef.current = null;
|
|
16221
16377
|
emit3(allSelected ? [] : allValues);
|
|
16222
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
|
+
};
|
|
16223
16394
|
const handleToggleGroup = (leafValues) => {
|
|
16224
16395
|
if (leafValues.length === 0) return;
|
|
16225
16396
|
rangeAnchorRef.current = null;
|
|
@@ -16292,6 +16463,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16292
16463
|
const selectFocusedItem = (extend = false) => {
|
|
16293
16464
|
if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
|
|
16294
16465
|
const focused = navigableItems[focusedIndex];
|
|
16466
|
+
if (focused.disabled) return;
|
|
16295
16467
|
if (focused.isSelectAll) {
|
|
16296
16468
|
handleSelectAll();
|
|
16297
16469
|
return;
|
|
@@ -16320,6 +16492,41 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16320
16492
|
moveFocusedIndex("prev");
|
|
16321
16493
|
return;
|
|
16322
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
|
+
}
|
|
16323
16530
|
if (event.key === "Enter") {
|
|
16324
16531
|
event.preventDefault();
|
|
16325
16532
|
selectFocusedItem(event.shiftKey);
|
|
@@ -16364,7 +16571,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16364
16571
|
type: "button",
|
|
16365
16572
|
disabled: effectiveDisabled,
|
|
16366
16573
|
"aria-labelledby": label ? `${fieldLabelId(triggerId)} ${triggerId}` : void 0,
|
|
16367
|
-
"aria-haspopup": "listbox",
|
|
16574
|
+
"aria-haspopup": isTree ? "tree" : "listbox",
|
|
16368
16575
|
"aria-expanded": open,
|
|
16369
16576
|
"aria-controls": open ? listboxId : void 0,
|
|
16370
16577
|
"aria-activedescendant": open && focusedIndex >= 0 ? activeOptionId : void 0,
|
|
@@ -16501,7 +16708,7 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16501
16708
|
{
|
|
16502
16709
|
ref: searchInputRef,
|
|
16503
16710
|
value: search,
|
|
16504
|
-
onValueChange:
|
|
16711
|
+
onValueChange: handleSearchChange,
|
|
16505
16712
|
onKeyDown: handleKeyDown,
|
|
16506
16713
|
placeholder: "\uAC80\uC0C9",
|
|
16507
16714
|
clearable: true,
|
|
@@ -16520,8 +16727,12 @@ var SSelectImpl = /* @__PURE__ */ forwardRef(function SSelect({
|
|
|
16520
16727
|
indexOffset: windowStart,
|
|
16521
16728
|
topSpacer: win.top,
|
|
16522
16729
|
bottomSpacer: win.bottom,
|
|
16523
|
-
setSize: isDepth ? void 0 : rowCount,
|
|
16730
|
+
setSize: isDepth || isTree ? void 0 : rowCount,
|
|
16524
16731
|
multi,
|
|
16732
|
+
tree: isTree,
|
|
16733
|
+
isExpanded: isGroupExpanded,
|
|
16734
|
+
onToggleExpand: handleToggleExpand,
|
|
16735
|
+
treeLines,
|
|
16525
16736
|
selected,
|
|
16526
16737
|
groupLeafValues,
|
|
16527
16738
|
focusedValue: navigableItems[focusedIndex]?.key,
|
|
@@ -16575,6 +16786,10 @@ function ItemList({
|
|
|
16575
16786
|
bottomSpacer,
|
|
16576
16787
|
setSize,
|
|
16577
16788
|
multi,
|
|
16789
|
+
tree,
|
|
16790
|
+
isExpanded,
|
|
16791
|
+
onToggleExpand,
|
|
16792
|
+
treeLines,
|
|
16578
16793
|
selected,
|
|
16579
16794
|
groupLeafValues,
|
|
16580
16795
|
focusedValue,
|
|
@@ -16588,14 +16803,34 @@ function ItemList({
|
|
|
16588
16803
|
onHoverValue
|
|
16589
16804
|
}) {
|
|
16590
16805
|
const selectedSet = useMemo(() => new Set(selected), [selected]);
|
|
16806
|
+
const checkboxShiftRef = useRef(false);
|
|
16591
16807
|
const itemBase = cn(
|
|
16592
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",
|
|
16593
16809
|
"text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
16594
16810
|
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listBoxItem-content-disabled)]"
|
|
16595
16811
|
);
|
|
16596
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)]";
|
|
16597
|
-
return /* @__PURE__ */ jsxs("div", { id: listboxId, role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
16598
|
-
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(
|
|
16599
16834
|
"button",
|
|
16600
16835
|
{
|
|
16601
16836
|
type: "button",
|
|
@@ -16616,12 +16851,109 @@ function ItemList({
|
|
|
16616
16851
|
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
16617
16852
|
]
|
|
16618
16853
|
}
|
|
16619
|
-
),
|
|
16854
|
+
)),
|
|
16620
16855
|
topSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: topSpacer } }),
|
|
16621
16856
|
items.map((o, windowIndex) => {
|
|
16622
16857
|
const idx = windowIndex + indexOffset;
|
|
16623
16858
|
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
16624
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
|
+
}
|
|
16625
16957
|
if (o.isGroup) {
|
|
16626
16958
|
const isDepth1 = o.depth === 1;
|
|
16627
16959
|
const isFirstRow = idx === 0 && !showSelectAll && !searchVisible;
|
|
@@ -16662,13 +16994,7 @@ function ItemList({
|
|
|
16662
16994
|
onClick: () => onToggleGroup(leafValues),
|
|
16663
16995
|
children: [
|
|
16664
16996
|
/* @__PURE__ */ jsx(CheckboxBox, { state: groupState, disabled: groupDisabled, focused: groupFocused }),
|
|
16665
|
-
|
|
16666
|
-
"span",
|
|
16667
|
-
{
|
|
16668
|
-
className: "min-w-0 truncate",
|
|
16669
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
16670
|
-
}
|
|
16671
|
-
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: o.label }),
|
|
16997
|
+
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "min-w-0 truncate" }),
|
|
16672
16998
|
total > 0 && /* @__PURE__ */ jsxs(
|
|
16673
16999
|
"span",
|
|
16674
17000
|
{
|
|
@@ -16745,13 +17071,7 @@ function ItemList({
|
|
|
16745
17071
|
focused: focusedValue === o.value
|
|
16746
17072
|
}
|
|
16747
17073
|
),
|
|
16748
|
-
|
|
16749
|
-
"span",
|
|
16750
|
-
{
|
|
16751
|
-
className: "flex-1 truncate",
|
|
16752
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
16753
|
-
}
|
|
16754
|
-
) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
17074
|
+
/* @__PURE__ */ jsx(OptionLabel, { label: o.label, className: "flex-1 truncate" })
|
|
16755
17075
|
]
|
|
16756
17076
|
},
|
|
16757
17077
|
rowKey
|
|
@@ -16760,8 +17080,11 @@ function ItemList({
|
|
|
16760
17080
|
bottomSpacer > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, style: { height: bottomSpacer } })
|
|
16761
17081
|
] });
|
|
16762
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
|
+
}
|
|
16763
17086
|
function TriggerLabel({ label }) {
|
|
16764
|
-
return
|
|
17087
|
+
return /* @__PURE__ */ jsx(OptionLabel, { label, className: "truncate" });
|
|
16765
17088
|
}
|
|
16766
17089
|
function TriggerRemoveButton({ ariaLabel, onRemove }) {
|
|
16767
17090
|
return /* @__PURE__ */ jsxs(
|
|
@@ -17016,8 +17339,8 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17016
17339
|
const [rowCountOverride, setRowCountOverride] = useState(null);
|
|
17017
17340
|
const rowCount = rowCountOverride ?? rows.length;
|
|
17018
17341
|
const scrollRef = useRef(null);
|
|
17019
|
-
const
|
|
17020
|
-
const [
|
|
17342
|
+
const headerRef = useRef(null);
|
|
17343
|
+
const [headerHeight, setHeaderHeight] = useState(0);
|
|
17021
17344
|
const [vsRange, setVsRange] = useState({ start: 0, end: 0 });
|
|
17022
17345
|
const lastReachEndRef = useRef(-1);
|
|
17023
17346
|
const cbRef = useRef({ onVirtualUpdate, onVirtualReachEnd, onScroll });
|
|
@@ -17059,17 +17382,13 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17059
17382
|
);
|
|
17060
17383
|
const handleScroll = useCallback(() => {
|
|
17061
17384
|
const el = scrollRef.current;
|
|
17062
|
-
if (!el)
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
}
|
|
17066
|
-
const header = headerScrollRef.current;
|
|
17067
|
-
if (header) header.scrollLeft = el.scrollLeft;
|
|
17068
|
-
setHeaderGutter(el.offsetWidth - el.clientWidth);
|
|
17385
|
+
if (!el) return;
|
|
17386
|
+
const headerH = headerRef.current?.offsetHeight ?? 0;
|
|
17387
|
+
setHeaderHeight(headerH);
|
|
17069
17388
|
setScrolledLeft(el.scrollLeft > 0);
|
|
17070
17389
|
setScrolledRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
17071
17390
|
if (virtualScroll) {
|
|
17072
|
-
const { start, end } = calcRange(el.scrollTop, el.clientHeight);
|
|
17391
|
+
const { start, end } = calcRange(el.scrollTop, el.clientHeight - headerH);
|
|
17073
17392
|
setVsRange((prev) => prev.start === start && prev.end === end ? prev : { start, end });
|
|
17074
17393
|
}
|
|
17075
17394
|
}, [virtualScroll, calcRange]);
|
|
@@ -17466,7 +17785,7 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17466
17785
|
);
|
|
17467
17786
|
};
|
|
17468
17787
|
const bodyScrollClass = cn(
|
|
17469
|
-
"min-h-0 flex-1 overflow-auto
|
|
17788
|
+
"min-h-0 flex-1 overflow-auto",
|
|
17470
17789
|
SCROLLBAR_CLASS,
|
|
17471
17790
|
SCROLLBAR_TRACK_BG_CLASS,
|
|
17472
17791
|
SCROLLBAR_TRACK_BORDER_CLASS
|
|
@@ -17483,23 +17802,221 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17483
17802
|
selectable && /* @__PURE__ */ jsx("col", { style: { width: SELECTABLE_COLUMN_WIDTH } }),
|
|
17484
17803
|
visibleCols.map((col, i) => /* @__PURE__ */ jsx("col", { style: col.autoWidth ? void 0 : { width: columnWidths[i] } }, col.name))
|
|
17485
17804
|
] });
|
|
17486
|
-
|
|
17487
|
-
"
|
|
17805
|
+
const headerTable = /* @__PURE__ */ jsxs(
|
|
17806
|
+
"table",
|
|
17488
17807
|
{
|
|
17489
|
-
className:
|
|
17490
|
-
style: {
|
|
17491
|
-
"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 },
|
|
17492
17810
|
children: [
|
|
17493
|
-
|
|
17494
|
-
|
|
17495
|
-
{
|
|
17496
|
-
|
|
17497
|
-
|
|
17498
|
-
|
|
17499
|
-
|
|
17500
|
-
|
|
17501
|
-
|
|
17502
|
-
|
|
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)]",
|
|
17503
18020
|
// height가 없으면 루트가 받은 높이를 끝까지 채운다. 호스트가 className으로
|
|
17504
18021
|
// `flex-1 min-h-0` 등을 줘 루트 높이가 확정된 경우, 행이 적어도 테두리 박스가
|
|
17505
18022
|
// 남은 영역을 그대로 차지한다. basis는 auto로 둬 높이가 확정되지 않은
|
|
@@ -17514,220 +18031,20 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17514
18031
|
minHeight: isNoData ? "calc(var(--cmp-table-header-height) + 60px)" : void 0
|
|
17515
18032
|
},
|
|
17516
18033
|
children: [
|
|
17517
|
-
/* @__PURE__ */
|
|
17518
|
-
"div",
|
|
17519
|
-
{
|
|
17520
|
-
|
|
17521
|
-
|
|
17522
|
-
|
|
17523
|
-
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
|
|
17529
|
-
|
|
17530
|
-
|
|
17531
|
-
colgroup,
|
|
17532
|
-
/* @__PURE__ */ jsxs("thead", { className: cn(isNoData && "opacity-40"), children: [
|
|
17533
|
-
headerGroupInfo && /* @__PURE__ */ jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
17534
|
-
selectable && /* @__PURE__ */ jsx(
|
|
17535
|
-
"th",
|
|
17536
|
-
{
|
|
17537
|
-
rowSpan: 2,
|
|
17538
|
-
className: cn(
|
|
17539
|
-
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
17540
|
-
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
17541
|
-
),
|
|
17542
|
-
style: {
|
|
17543
|
-
width: SELECTABLE_COLUMN_WIDTH,
|
|
17544
|
-
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
17545
|
-
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
17546
|
-
},
|
|
17547
|
-
children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
17548
|
-
SCheckbox,
|
|
17549
|
-
{
|
|
17550
|
-
value: headerCheck,
|
|
17551
|
-
disabled: !hasSelectableRow,
|
|
17552
|
-
onValueChange: toggleAll
|
|
17553
|
-
}
|
|
17554
|
-
) })
|
|
17555
|
-
}
|
|
17556
|
-
),
|
|
17557
|
-
headerGroupInfo.cells.map((group) => /* @__PURE__ */ jsxs(
|
|
17558
|
-
"th",
|
|
17559
|
-
{
|
|
17560
|
-
colSpan: group.colSpan,
|
|
17561
|
-
rowSpan: group.rowSpan,
|
|
17562
|
-
className: cn(
|
|
17563
|
-
"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)]",
|
|
17564
|
-
group.firstIndex + group.colSpan === visibleCols.length && "border-r-0",
|
|
17565
|
-
group.rowSpan === 2 && "border-b-0",
|
|
17566
|
-
group.className
|
|
17567
|
-
),
|
|
17568
|
-
style: { textAlign: group.align ?? "left", ...group.style },
|
|
17569
|
-
children: [
|
|
17570
|
-
group.render ? group.render() : group.label,
|
|
17571
|
-
group.rowSpan === 2 && group.colSpan === 1 ? headerResizeHitArea(
|
|
17572
|
-
group.firstIndex,
|
|
17573
|
-
`group-resizer-${group.columnNames[0]}`
|
|
17574
|
-
) : null
|
|
17575
|
-
]
|
|
17576
|
-
},
|
|
17577
|
-
group.columnNames.join("|")
|
|
17578
|
-
))
|
|
17579
|
-
] }),
|
|
17580
|
-
/* @__PURE__ */ jsxs("tr", { className: "h-[var(--cmp-table-header-height)] bg-[var(--cmp-table-header-bg)]", children: [
|
|
17581
|
-
selectable && !headerGroupInfo && /* @__PURE__ */ jsxs(
|
|
17582
|
-
"th",
|
|
17583
|
-
{
|
|
17584
|
-
className: cn(
|
|
17585
|
-
// 원본 .th--selected: 48px 고정폭, 항상 left sticky, 체크박스 중앙정렬
|
|
17586
|
-
// 하단 border는 헤더 래퍼가 연속으로 그리므로 셀에는 두지 않는다.
|
|
17587
|
-
"sticky left-0 z-[7] bg-[var(--cmp-table-header-bg)] text-center align-middle",
|
|
17588
|
-
stickyLeft === 0 && scrolledLeft && "shadow-[5px_0_8px_0_rgba(34,34,34,0.1)]"
|
|
17589
|
-
),
|
|
17590
|
-
style: {
|
|
17591
|
-
width: SELECTABLE_COLUMN_WIDTH,
|
|
17592
|
-
minWidth: SELECTABLE_COLUMN_WIDTH,
|
|
17593
|
-
maxWidth: SELECTABLE_COLUMN_WIDTH
|
|
17594
|
-
},
|
|
17595
|
-
children: [
|
|
17596
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
17597
|
-
SCheckbox,
|
|
17598
|
-
{
|
|
17599
|
-
value: headerCheck,
|
|
17600
|
-
disabled: !hasSelectableRow,
|
|
17601
|
-
onValueChange: toggleAll
|
|
17602
|
-
}
|
|
17603
|
-
) }),
|
|
17604
|
-
/* @__PURE__ */ jsx(
|
|
17605
|
-
"span",
|
|
17606
|
-
{
|
|
17607
|
-
"aria-hidden": true,
|
|
17608
|
-
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)]"
|
|
17609
|
-
}
|
|
17610
|
-
)
|
|
17611
|
-
]
|
|
17612
|
-
}
|
|
17613
|
-
),
|
|
17614
|
-
visibleCols.map((col, i) => {
|
|
17615
|
-
if (headerGroupInfo?.rowSpanNames.has(col.name)) return null;
|
|
17616
|
-
const isSortable = !!col.sortable && !col.renderHeader;
|
|
17617
|
-
const sortDir = isSortable && sort?.name === col.name ? sort.dir : void 0;
|
|
17618
|
-
return /* @__PURE__ */ jsxs(
|
|
17619
|
-
"th",
|
|
17620
|
-
{
|
|
17621
|
-
className: cn(
|
|
17622
|
-
// 하단 border는 헤더 래퍼가 연속으로 그린다 (거터 영역까지 이어짐).
|
|
17623
|
-
// leading-[0]: th 의 스트럿(line-height 만큼의 빈 인라인 박스)을 없앤다.
|
|
17624
|
-
// 내용이 inline-flex 라 스트럿이 남아 있으면 라벨보다 큰 컨트롤(정렬 버튼 등)이
|
|
17625
|
-
// 들어올 때 줄 상자가 베이스라인 기준으로 늘어나 헤더 높이를 넘기고
|
|
17626
|
-
// 세로 가운데도 어긋난다. 글자 줄높이는 아래 th__content 가 다시 준다.
|
|
17627
|
-
headerGroupInfo && "border-r border-solid border-[color:var(--cmp-table-border-color)]",
|
|
17628
|
-
headerGroupInfo && i === visibleCols.length - 1 && "border-r-0",
|
|
17629
|
-
"relative bg-[var(--cmp-table-header-bg)] px-[16px] text-[12px] font-medium leading-[0] text-[color:var(--sys-color-fg-primary)]",
|
|
17630
|
-
stickyInfo(i).isSticky && "z-[6]",
|
|
17631
|
-
col.thClass
|
|
17632
|
-
),
|
|
17633
|
-
style: {
|
|
17634
|
-
textAlign: col.align ?? "left",
|
|
17635
|
-
boxShadow: stickyShadowValue(i) ?? void 0,
|
|
17636
|
-
...cellStyle(i),
|
|
17637
|
-
...col.thStyle
|
|
17638
|
-
},
|
|
17639
|
-
"aria-sort": isSortable ? sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : "none" : void 0,
|
|
17640
|
-
children: [
|
|
17641
|
-
/* @__PURE__ */ jsx(
|
|
17642
|
-
"span",
|
|
17643
|
-
{
|
|
17644
|
-
className: cn(
|
|
17645
|
-
// 원본 th__content: align에 따라 justify-content 적용 (th도 정렬 반영)
|
|
17646
|
-
// th 가 leading-[0] 이므로 글자 줄높이는 여기서 준다 (truncate 가
|
|
17647
|
-
// overflow-hidden 이라 줄높이가 0이면 라벨이 잘려 사라진다).
|
|
17648
|
-
"inline-flex w-full min-w-0 items-center gap-[4px] leading-[20px]",
|
|
17649
|
-
col.align === "center" && "justify-center",
|
|
17650
|
-
col.align === "right" && "justify-end"
|
|
17651
|
-
),
|
|
17652
|
-
children: col.renderHeader ? col.renderHeader(col, i) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
17653
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: col.label }),
|
|
17654
|
-
col.helpText && col.helpText.length > 0 && // 라벨이 좁아져 truncate 되더라도 아이콘까지 함께 줄어들면 안 되므로
|
|
17655
|
-
// shrink-0 래퍼로 감싼다 (STooltip 의 className 은 본문 쪽으로 간다).
|
|
17656
|
-
// 아이콘·크기·색은 SKeyValueTable 헤더 도움말과 동일하게 맞춘다.
|
|
17657
|
-
/* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(
|
|
17658
|
-
STooltip2,
|
|
17659
|
-
{
|
|
17660
|
-
trigger: "hover",
|
|
17661
|
-
icon: "helpOutline",
|
|
17662
|
-
iconSize: 12,
|
|
17663
|
-
color: "var(--sys-color-fg-success)",
|
|
17664
|
-
placement: "top",
|
|
17665
|
-
message: col.helpText
|
|
17666
|
-
}
|
|
17667
|
-
) }),
|
|
17668
|
-
col.editable && /* @__PURE__ */ jsx(
|
|
17669
|
-
SIcon,
|
|
17670
|
-
{
|
|
17671
|
-
name: "pageEdit",
|
|
17672
|
-
size: 12,
|
|
17673
|
-
color: "var(--sys-color-link-accent)",
|
|
17674
|
-
label: "\uC218\uC815 \uAC00\uB2A5",
|
|
17675
|
-
className: "shrink-0"
|
|
17676
|
-
}
|
|
17677
|
-
),
|
|
17678
|
-
col.navigable && /* @__PURE__ */ jsx(
|
|
17679
|
-
SIcon,
|
|
17680
|
-
{
|
|
17681
|
-
name: "pageMove",
|
|
17682
|
-
size: 12,
|
|
17683
|
-
color: "var(--sys-color-link-accent)",
|
|
17684
|
-
label: "\uC0C8 \uD654\uBA74\uC5D0\uC11C \uC5F4\uAE30",
|
|
17685
|
-
className: "shrink-0"
|
|
17686
|
-
}
|
|
17687
|
-
),
|
|
17688
|
-
col.required && // 라벨이 truncate 돼도 필수 표시는 남아야 하므로 shrink-0.
|
|
17689
|
-
// 아이콘·크기·색은 SKeyValueTable 헤더 필수 표시와 동일하게 맞춘다.
|
|
17690
|
-
/* @__PURE__ */ jsx(
|
|
17691
|
-
SIcon,
|
|
17692
|
-
{
|
|
17693
|
-
name: "asterisk",
|
|
17694
|
-
size: 12,
|
|
17695
|
-
color: "var(--sys-color-fg-accent)",
|
|
17696
|
-
className: "shrink-0"
|
|
17697
|
-
}
|
|
17698
|
-
),
|
|
17699
|
-
isSortable && // 미정렬은 updown 을 default(회색)로 — 정렬할 수 있다는 것 자체가
|
|
17700
|
-
// 헤더에 보여야 한다. 정렬 중이면 action 색으로 올려 활성임을 알린다.
|
|
17701
|
-
// 라벨이 truncate 돼도 버튼은 남도록 shrink-0.
|
|
17702
|
-
/* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(
|
|
17703
|
-
SGhostButton,
|
|
17704
|
-
{
|
|
17705
|
-
size: "xxs",
|
|
17706
|
-
intent: sortDir ? "action" : "default",
|
|
17707
|
-
icon: sortDir === "asc" ? "arrowUp" : sortDir === "desc" ? "arrowDown" : "updown",
|
|
17708
|
-
ariaLabel: `${col.label} \uC815\uB82C`,
|
|
17709
|
-
onClick: () => toggleSort(col)
|
|
17710
|
-
}
|
|
17711
|
-
) })
|
|
17712
|
-
] })
|
|
17713
|
-
}
|
|
17714
|
-
),
|
|
17715
|
-
headerGroupInfo ? headerResizeHitArea(i, `resizer-${col.name}`) : headerResizer(i, `resizer-${col.name}`)
|
|
17716
|
-
]
|
|
17717
|
-
},
|
|
17718
|
-
col.name
|
|
17719
|
-
);
|
|
17720
|
-
})
|
|
17721
|
-
] })
|
|
17722
|
-
] })
|
|
17723
|
-
]
|
|
17724
|
-
}
|
|
17725
|
-
)
|
|
17726
|
-
}
|
|
17727
|
-
),
|
|
17728
|
-
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 }) }) : (
|
|
17729
|
-
/* 바디: 세로 스크롤은 이 컨테이너에만 생긴다 (헤더 제외) */
|
|
17730
|
-
/* @__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(
|
|
17731
18048
|
"div",
|
|
17732
18049
|
{
|
|
17733
18050
|
ref: scrollRef,
|
|
@@ -17737,136 +18054,140 @@ var STableImpl = /* @__PURE__ */ forwardRef(function STable({
|
|
|
17737
18054
|
// 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 스크롤 차단
|
|
17738
18055
|
(loading2 || rowCount === 0) && "overflow-hidden"
|
|
17739
18056
|
),
|
|
17740
|
-
|
|
17741
|
-
|
|
17742
|
-
{
|
|
17743
|
-
|
|
17744
|
-
|
|
17745
|
-
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
"
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
onRowDoubleClick(
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
(
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
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)]",
|
|
17786
18138
|
rowBg,
|
|
17787
18139
|
rowHover && "group-hover/table-row:bg-[var(--color-grey-05)]",
|
|
17788
|
-
|
|
17789
|
-
)
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
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
|
-
// color, STag 등)까지는 닿지 않는다 — 그건 소비 앱 몫이다.
|
|
17840
|
-
color: rowLocked ? LOCKED_CONTENT : void 0,
|
|
17841
|
-
boxShadow: shadows.length > 0 ? shadows.join(", ") : void 0,
|
|
17842
|
-
...cellStyle(i)
|
|
17843
|
-
};
|
|
17844
|
-
if (col.renderCell) {
|
|
17845
|
-
const cell = col.renderCell({
|
|
17846
|
-
row,
|
|
17847
|
-
value: cellValue(col, row),
|
|
17848
|
-
column: col,
|
|
17849
|
-
colIndex: i,
|
|
17850
|
-
rowIndex: ri,
|
|
17851
|
-
isSelected: isSel,
|
|
17852
|
-
className: tdClassName,
|
|
17853
|
-
style: tdStyle
|
|
17854
|
-
});
|
|
17855
|
-
if (cell == null || cell === false) return null;
|
|
17856
|
-
return /* @__PURE__ */ jsx(Fragment$1, { children: cell }, col.name);
|
|
17857
|
-
}
|
|
17858
|
-
return /* @__PURE__ */ jsx("td", { className: tdClassName, style: tdStyle, children: col.render ? col.render(row, cellValue(col, row)) : cellValue(col, row) }, col.name);
|
|
17859
|
-
})
|
|
17860
|
-
]
|
|
17861
|
-
},
|
|
17862
|
-
row[rowKey] ?? ri
|
|
17863
|
-
);
|
|
17864
|
-
}),
|
|
17865
|
-
virtualScroll && bottomSpacer > 0 && /* @__PURE__ */ jsx("tr", { "aria-hidden": true, style: { height: bottomSpacer }, children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0" }) })
|
|
17866
|
-
] })
|
|
17867
|
-
]
|
|
17868
|
-
}
|
|
17869
|
-
)
|
|
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
|
+
]
|
|
17870
18191
|
}
|
|
17871
18192
|
)
|
|
17872
18193
|
),
|
|
@@ -19374,13 +19695,21 @@ function SCalendar({
|
|
|
19374
19695
|
}
|
|
19375
19696
|
);
|
|
19376
19697
|
}
|
|
19698
|
+
|
|
19699
|
+
// src/lib/mode-listbox.ts
|
|
19700
|
+
var MODE_LIST_HEIGHT = 240;
|
|
19701
|
+
var MODE_ITEM_HEIGHT = 28;
|
|
19702
|
+
var modeListItemClass = (selected, disabled) => cn(
|
|
19703
|
+
"flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
|
|
19704
|
+
disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
|
|
19705
|
+
!disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
|
|
19706
|
+
!disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
|
|
19707
|
+
);
|
|
19377
19708
|
var MODE_NOUN = {
|
|
19378
19709
|
date: "\uB0A0\uC9DC",
|
|
19379
19710
|
month: "\uC6D4",
|
|
19380
19711
|
year: "\uC5F0\uB3C4"
|
|
19381
19712
|
};
|
|
19382
|
-
var DATEPICKER_MODE_LIST_HEIGHT = 240;
|
|
19383
|
-
var DATEPICKER_MODE_ITEM_HEIGHT = 28;
|
|
19384
19713
|
var YEAR_LIST_EDGE_BUFFER = 24;
|
|
19385
19714
|
var YEAR_LIST_EXPAND_SIZE = 80;
|
|
19386
19715
|
var MONTHS = Array.from({ length: 12 }, (_, i) => i + 1);
|
|
@@ -19431,7 +19760,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19431
19760
|
if (!list) return;
|
|
19432
19761
|
const index = anchorYearRef.current - rangeStartRef.current;
|
|
19433
19762
|
syncingScrollRef.current = true;
|
|
19434
|
-
list.scrollTop = index *
|
|
19763
|
+
list.scrollTop = index * MODE_ITEM_HEIGHT;
|
|
19435
19764
|
requestAnimationFrame(() => {
|
|
19436
19765
|
syncingScrollRef.current = false;
|
|
19437
19766
|
});
|
|
@@ -19440,7 +19769,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19440
19769
|
const list = listRef.current;
|
|
19441
19770
|
if (!list) return;
|
|
19442
19771
|
if (syncingScrollRef.current) return;
|
|
19443
|
-
if (list.scrollTop <
|
|
19772
|
+
if (list.scrollTop < MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
|
|
19444
19773
|
const previousHeight = list.scrollHeight;
|
|
19445
19774
|
setRange((current) => ({
|
|
19446
19775
|
start: current.start - YEAR_LIST_EXPAND_SIZE,
|
|
@@ -19456,7 +19785,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19456
19785
|
});
|
|
19457
19786
|
return;
|
|
19458
19787
|
}
|
|
19459
|
-
if (list.scrollHeight - list.scrollTop - list.clientHeight <
|
|
19788
|
+
if (list.scrollHeight - list.scrollTop - list.clientHeight < MODE_ITEM_HEIGHT * YEAR_LIST_EDGE_BUFFER) {
|
|
19460
19789
|
setRange((current) => ({
|
|
19461
19790
|
start: current.start,
|
|
19462
19791
|
end: current.end + YEAR_LIST_EXPAND_SIZE
|
|
@@ -19471,8 +19800,8 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19471
19800
|
"aria-label": "year",
|
|
19472
19801
|
className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
19473
19802
|
style: {
|
|
19474
|
-
paddingTop: `calc((${
|
|
19475
|
-
paddingBottom: `calc((${
|
|
19803
|
+
paddingTop: `calc((${MODE_LIST_HEIGHT}px - ${MODE_ITEM_HEIGHT}px) / 2)`,
|
|
19804
|
+
paddingBottom: `calc((${MODE_LIST_HEIGHT}px - ${MODE_ITEM_HEIGHT}px) / 2)`
|
|
19476
19805
|
},
|
|
19477
19806
|
onScroll: handleScroll,
|
|
19478
19807
|
children: years.map((year) => {
|
|
@@ -19485,12 +19814,7 @@ function YearWheel({ value, anchorYear, selectable, onCommit }) {
|
|
|
19485
19814
|
role: "option",
|
|
19486
19815
|
"aria-selected": selected,
|
|
19487
19816
|
disabled,
|
|
19488
|
-
className:
|
|
19489
|
-
"flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
|
|
19490
|
-
disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
|
|
19491
|
-
!disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
|
|
19492
|
-
!disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
|
|
19493
|
-
),
|
|
19817
|
+
className: modeListItemClass(selected, disabled),
|
|
19494
19818
|
onClick: () => !disabled && onCommit(year),
|
|
19495
19819
|
children: year
|
|
19496
19820
|
},
|
|
@@ -19546,7 +19870,7 @@ function MonthWheel({ year, value, selectable, onCommit }) {
|
|
|
19546
19870
|
if (!list) return;
|
|
19547
19871
|
list.scrollTop = Math.max(
|
|
19548
19872
|
0,
|
|
19549
|
-
(initialValueRef.current - 1) *
|
|
19873
|
+
(initialValueRef.current - 1) * MODE_ITEM_HEIGHT - (list.clientHeight - MODE_ITEM_HEIGHT) / 2
|
|
19550
19874
|
);
|
|
19551
19875
|
}, []);
|
|
19552
19876
|
return /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsx(
|
|
@@ -19566,12 +19890,7 @@ function MonthWheel({ year, value, selectable, onCommit }) {
|
|
|
19566
19890
|
role: "option",
|
|
19567
19891
|
"aria-selected": selected,
|
|
19568
19892
|
disabled,
|
|
19569
|
-
className:
|
|
19570
|
-
"flex w-full items-center justify-center gap-[var(--cmp-datepicker-mode-item-gap)] border-0 px-[var(--cmp-datepicker-mode-item-paddingX)] py-[var(--cmp-datepicker-mode-item-paddingY)] text-center font-base text-12 leading-[20px]",
|
|
19571
|
-
disabled && "cursor-not-allowed bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-disabled)]",
|
|
19572
|
-
!disabled && !selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-medium text-[color:var(--cmp-datepicker-mode-item-content-default)] hover:bg-[var(--cmp-datepicker-mode-item-bg-hover)] hover:text-[color:var(--cmp-datepicker-mode-item-content-hover)]",
|
|
19573
|
-
!disabled && selected && "cursor-pointer bg-[var(--cmp-datepicker-mode-item-bg-default)] font-bold text-[color:var(--cmp-datepicker-mode-item-content-selected)]"
|
|
19574
|
-
),
|
|
19893
|
+
className: modeListItemClass(selected, disabled),
|
|
19575
19894
|
onClick: () => !disabled && onCommit(month),
|
|
19576
19895
|
children: [
|
|
19577
19896
|
month,
|
|
@@ -21145,6 +21464,169 @@ function ChartAxisLabels({
|
|
|
21145
21464
|
}
|
|
21146
21465
|
);
|
|
21147
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
|
+
}
|
|
21148
21630
|
var ChartFrame = /* @__PURE__ */ forwardRef(function ChartFrame2({ title, unit, legend, legendPosition = "top", children, className, style, ...props }, ref) {
|
|
21149
21631
|
return /* @__PURE__ */ jsxs(
|
|
21150
21632
|
"div",
|
|
@@ -21336,44 +21818,6 @@ function linePointDimmedColor(palette, index, colors) {
|
|
|
21336
21818
|
function dataValueColor(palette, index, colors) {
|
|
21337
21819
|
return pick(DATA_VALUE_COLORS, DATA_VALUE_PALETTES, palette, index, colors);
|
|
21338
21820
|
}
|
|
21339
|
-
function useElementSize() {
|
|
21340
|
-
const ref = useRef(null);
|
|
21341
|
-
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
21342
|
-
useEffect(() => {
|
|
21343
|
-
const element = ref.current;
|
|
21344
|
-
if (!element) return;
|
|
21345
|
-
const update = () => {
|
|
21346
|
-
const { width, height } = element.getBoundingClientRect();
|
|
21347
|
-
setSize(
|
|
21348
|
-
(prev) => Math.abs(prev.width - width) < 0.5 && Math.abs(prev.height - height) < 0.5 ? prev : { width, height }
|
|
21349
|
-
);
|
|
21350
|
-
};
|
|
21351
|
-
update();
|
|
21352
|
-
if (typeof ResizeObserver === "undefined") return;
|
|
21353
|
-
const observer = new ResizeObserver(update);
|
|
21354
|
-
observer.observe(element);
|
|
21355
|
-
return () => observer.disconnect();
|
|
21356
|
-
}, []);
|
|
21357
|
-
return { ref, size };
|
|
21358
|
-
}
|
|
21359
|
-
function useSvgTextMetrics() {
|
|
21360
|
-
const ref = useRef(null);
|
|
21361
|
-
const [metrics, setMetrics] = useState({ top: 0, width: 0 });
|
|
21362
|
-
useEffect(() => {
|
|
21363
|
-
const element = ref.current;
|
|
21364
|
-
if (!element) return;
|
|
21365
|
-
let box3;
|
|
21366
|
-
try {
|
|
21367
|
-
box3 = element.getBBox();
|
|
21368
|
-
} catch {
|
|
21369
|
-
return;
|
|
21370
|
-
}
|
|
21371
|
-
setMetrics(
|
|
21372
|
-
(prev) => Math.abs(prev.top - box3.y) < 0.5 && Math.abs(prev.width - box3.width) < 0.5 ? prev : { top: box3.y, width: box3.width }
|
|
21373
|
-
);
|
|
21374
|
-
});
|
|
21375
|
-
return { ref, metrics };
|
|
21376
|
-
}
|
|
21377
21821
|
var DOT_SIZE2 = 6;
|
|
21378
21822
|
var DEFAULT_GAP_X = { after: 4, before: 4 };
|
|
21379
21823
|
var ANCHOR_GAP_Y = 0;
|
|
@@ -21535,6 +21979,7 @@ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
|
|
|
21535
21979
|
tickCount = 8,
|
|
21536
21980
|
barSize = BAR_SIZE_MAX,
|
|
21537
21981
|
showValueLabel = true,
|
|
21982
|
+
rotateCategoryLabels = false,
|
|
21538
21983
|
showLegend = true,
|
|
21539
21984
|
legendPosition = "top",
|
|
21540
21985
|
showTooltip = true,
|
|
@@ -21546,9 +21991,10 @@ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
|
|
|
21546
21991
|
style
|
|
21547
21992
|
}, ref) {
|
|
21548
21993
|
const vertical = direction === "vertical";
|
|
21994
|
+
const rotateLabels = rotateCategoryLabels && vertical;
|
|
21549
21995
|
const { ref: plotRef, size } = useElementSize();
|
|
21550
|
-
const { ref: axisTextRef, size: axisText } = useElementSize();
|
|
21551
|
-
const { ref: axisSampleRef, size: axisSample } = useElementSize();
|
|
21996
|
+
const { ref: axisTextRef, size: axisText } = useElementSize(rotateLabels);
|
|
21997
|
+
const { ref: axisSampleRef, size: axisSample } = useElementSize(rotateLabels);
|
|
21552
21998
|
const clipId = useId();
|
|
21553
21999
|
const [hoveredCategory, setHoveredCategory] = useState(null);
|
|
21554
22000
|
const perCategory = series.length === 1;
|
|
@@ -22007,7 +22453,29 @@ var SBarChart = /* @__PURE__ */ forwardRef(function SBarChart2({
|
|
|
22007
22453
|
// 눈금은 왼쪽 한 줄뿐이다 — 오른쪽에도 적으면 서로 다른 단위처럼 읽힌다.
|
|
22008
22454
|
gridTemplateColumns: "max-content 1fr"
|
|
22009
22455
|
},
|
|
22010
|
-
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: [
|
|
22011
22479
|
/* @__PURE__ */ jsx("div", { style: { height: plotHeight, ...inset }, children: /* @__PURE__ */ jsx(
|
|
22012
22480
|
ChartAxisLabels,
|
|
22013
22481
|
{
|
|
@@ -22138,6 +22606,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22138
22606
|
tickCount = 8,
|
|
22139
22607
|
showPoint = true,
|
|
22140
22608
|
showValueLabel = false,
|
|
22609
|
+
rotateCategoryLabels = false,
|
|
22141
22610
|
showLegend = true,
|
|
22142
22611
|
legendPosition = "top",
|
|
22143
22612
|
showTooltip = true,
|
|
@@ -22150,8 +22619,8 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22150
22619
|
}, ref) {
|
|
22151
22620
|
const smooth = curve === "smooth";
|
|
22152
22621
|
const { ref: plotRef, size } = useElementSize();
|
|
22153
|
-
const { ref: axisTextRef, size: axisText } = useElementSize();
|
|
22154
|
-
const { ref: axisSampleRef, size: axisSample } = useElementSize();
|
|
22622
|
+
const { ref: axisTextRef, size: axisText } = useElementSize(rotateCategoryLabels);
|
|
22623
|
+
const { ref: axisSampleRef, size: axisSample } = useElementSize(rotateCategoryLabels);
|
|
22155
22624
|
const gradientId = useId();
|
|
22156
22625
|
const [hoveredCategory, setHoveredCategory] = useState(null);
|
|
22157
22626
|
const colorAt = useCallback(
|
|
@@ -22185,23 +22654,16 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22185
22654
|
[rows, tickCount, max]
|
|
22186
22655
|
);
|
|
22187
22656
|
const count = categories.length;
|
|
22188
|
-
const
|
|
22189
|
-
const xAt = useCallback(
|
|
22190
|
-
(index) => count > 1 ? index * step : size.width / 2,
|
|
22191
|
-
[count, step, size.width]
|
|
22192
|
-
);
|
|
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]);
|
|
22193
22659
|
const cellOf = useCallback(
|
|
22194
|
-
(index) =>
|
|
22195
|
-
left:
|
|
22196
|
-
right:
|
|
22197
|
-
}
|
|
22198
|
-
[
|
|
22199
|
-
);
|
|
22200
|
-
const edgeOf = useCallback(
|
|
22201
|
-
(index) => count > 1 && index === 0 ? "start" : count > 1 && index === count - 1 ? "end" : null,
|
|
22202
|
-
[count]
|
|
22660
|
+
(index) => ({
|
|
22661
|
+
left: AXIS_EDGE_PADDING + index * band,
|
|
22662
|
+
right: AXIS_EDGE_PADDING + (index + 1) * band
|
|
22663
|
+
}),
|
|
22664
|
+
[band]
|
|
22203
22665
|
);
|
|
22204
|
-
const minPlotWidth =
|
|
22666
|
+
const minPlotWidth = count * BAND_MIN + AXIS_EDGE_PADDING * 2;
|
|
22205
22667
|
const totalHeight = height ?? 320;
|
|
22206
22668
|
const plotHeight = typeof totalHeight === "number" ? Math.max(0, totalHeight - axisText.height) : `calc(${totalHeight} - ${axisText.height}px)`;
|
|
22207
22669
|
const plotted = useMemo(() => {
|
|
@@ -22215,10 +22677,10 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22215
22677
|
}, [rows, size.width, size.height, xAt, scale]);
|
|
22216
22678
|
const legend = showLegend ? /* @__PURE__ */ jsx(ChartLegend, { items: series.map((s, i) => ({ name: s.name, color: colorAt(i) })) }) : null;
|
|
22217
22679
|
const handleMove = (event) => {
|
|
22218
|
-
if (!showTooltip || count === 0 ||
|
|
22680
|
+
if (!showTooltip || count === 0 || band <= 0 || series.length === 0) return;
|
|
22219
22681
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
22220
|
-
const along = event.clientX - rect.left;
|
|
22221
|
-
const category =
|
|
22682
|
+
const along = event.clientX - rect.left - AXIS_EDGE_PADDING;
|
|
22683
|
+
const category = Math.floor(along / band);
|
|
22222
22684
|
setHoveredCategory(category >= 0 && category < categories.length ? category : null);
|
|
22223
22685
|
};
|
|
22224
22686
|
const tooltipItems = hoveredCategory == null ? [] : rows.flatMap((row, seriesIndex) => {
|
|
@@ -22257,10 +22719,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22257
22719
|
categories.map((label, index) => /* @__PURE__ */ jsx(
|
|
22258
22720
|
"span",
|
|
22259
22721
|
{
|
|
22260
|
-
className:
|
|
22261
|
-
"absolute top-0 whitespace-nowrap",
|
|
22262
|
-
edgeOf(index) === "start" ? "translate-x-0" : edgeOf(index) === "end" ? "-translate-x-full" : "-translate-x-1/2"
|
|
22263
|
-
),
|
|
22722
|
+
className: "absolute top-0 -translate-x-1/2 whitespace-nowrap",
|
|
22264
22723
|
style: { left: xAt(index) },
|
|
22265
22724
|
children: label
|
|
22266
22725
|
},
|
|
@@ -22441,17 +22900,114 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22441
22900
|
y: point.y - POINT_RADIUS - VALUE_LABEL_GAP2,
|
|
22442
22901
|
className: hoveredCategory === categoryIndex ? LINE_DATA_LABEL_TYPO_HOVER : LINE_DATA_LABEL_TYPO,
|
|
22443
22902
|
style: { fill: LINE_DATA_LABEL_COLOR },
|
|
22444
|
-
textAnchor:
|
|
22903
|
+
textAnchor: "middle",
|
|
22445
22904
|
dominantBaseline: "auto",
|
|
22446
22905
|
children: formatValue(point.own)
|
|
22447
22906
|
},
|
|
22448
22907
|
`label-${seriesIndex}-${categoryIndex}`
|
|
22449
22908
|
)
|
|
22450
|
-
)
|
|
22451
|
-
) : [],
|
|
22452
|
-
[showValueLabel, plotted, hoveredCategory, formatValue
|
|
22909
|
+
)
|
|
22910
|
+
) : [],
|
|
22911
|
+
[showValueLabel, plotted, hoveredCategory, formatValue]
|
|
22912
|
+
);
|
|
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
|
+
)
|
|
22453
23010
|
);
|
|
22454
|
-
const inset = { paddingTop: axisSample.height / 2, paddingBottom: axisSample.height / 2 };
|
|
22455
23011
|
return /* @__PURE__ */ jsx(
|
|
22456
23012
|
ChartFrame,
|
|
22457
23013
|
{
|
|
@@ -22462,7 +23018,7 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22462
23018
|
legendPosition,
|
|
22463
23019
|
className,
|
|
22464
23020
|
style,
|
|
22465
|
-
children: /* @__PURE__ */
|
|
23021
|
+
children: /* @__PURE__ */ jsx(
|
|
22466
23022
|
"div",
|
|
22467
23023
|
{
|
|
22468
23024
|
className: "grid",
|
|
@@ -22471,119 +23027,27 @@ var SLineChart = /* @__PURE__ */ forwardRef(function SLineChart2({
|
|
|
22471
23027
|
// 눈금은 왼쪽 한 줄뿐이다 — 오른쪽에도 적으면 서로 다른 단위처럼 읽힌다.
|
|
22472
23028
|
gridTemplateColumns: "max-content 1fr"
|
|
22473
23029
|
},
|
|
22474
|
-
children:
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
scale,
|
|
22479
|
-
direction: "vertical",
|
|
22480
|
-
format: formatValue,
|
|
22481
|
-
sampleRef: axisSampleRef
|
|
22482
|
-
}
|
|
22483
|
-
) }),
|
|
22484
|
-
/* @__PURE__ */ jsxs(
|
|
22485
|
-
"div",
|
|
23030
|
+
children: rotateCategoryLabels ? (
|
|
23031
|
+
// 기울인 이름은 눈금 열 아래까지 뻗으므로 스크롤 상자가 눈금 열까지 감싼다.
|
|
23032
|
+
/* @__PURE__ */ jsx(
|
|
23033
|
+
ChartRotatedAxisLayout,
|
|
22486
23034
|
{
|
|
22487
|
-
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
|
|
22492
|
-
|
|
22493
|
-
|
|
22494
|
-
style: { height: plotHeight, minWidth: minPlotWidth || void 0, ...inset },
|
|
22495
|
-
children: /* @__PURE__ */ jsxs(
|
|
22496
|
-
"div",
|
|
22497
|
-
{
|
|
22498
|
-
ref: plotRef,
|
|
22499
|
-
className: "relative h-full w-full",
|
|
22500
|
-
onMouseMove: handleMove,
|
|
22501
|
-
onMouseLeave: () => setHoveredCategory(null),
|
|
22502
|
-
children: [
|
|
22503
|
-
size.width > 0 && size.height > 0 && /* @__PURE__ */ jsxs(
|
|
22504
|
-
"svg",
|
|
22505
|
-
{
|
|
22506
|
-
width: size.width,
|
|
22507
|
-
height: size.height,
|
|
22508
|
-
overflow: "visible",
|
|
22509
|
-
role: "img",
|
|
22510
|
-
"aria-label": `${categories.length}\uAC1C \uD56D\uBAA9, ${series.length}\uAC1C \uACC4\uC5F4 ${stacked ? "\uB204\uC801 " : ""}\uAEBE\uC740\uC120 \uADF8\uB798\uD504`,
|
|
22511
|
-
children: [
|
|
22512
|
-
/* @__PURE__ */ jsx(
|
|
22513
|
-
"text",
|
|
22514
|
-
{
|
|
22515
|
-
ref: valueLabelRef,
|
|
22516
|
-
"aria-hidden": true,
|
|
22517
|
-
x: 0,
|
|
22518
|
-
y: 0,
|
|
22519
|
-
className: LINE_DATA_LABEL_TYPO_HOVER,
|
|
22520
|
-
dominantBaseline: "auto",
|
|
22521
|
-
style: { visibility: "hidden" },
|
|
22522
|
-
children: "0"
|
|
22523
|
-
}
|
|
22524
|
-
),
|
|
22525
|
-
/* @__PURE__ */ jsx(
|
|
22526
|
-
ChartGrid,
|
|
22527
|
-
{
|
|
22528
|
-
scale,
|
|
22529
|
-
direction: "vertical",
|
|
22530
|
-
width: size.width,
|
|
22531
|
-
height: size.height
|
|
22532
|
-
}
|
|
22533
|
-
),
|
|
22534
|
-
hoveredCategory != null && /* @__PURE__ */ jsx(
|
|
22535
|
-
"rect",
|
|
22536
|
-
{
|
|
22537
|
-
"aria-hidden": true,
|
|
22538
|
-
x: cellOf(hoveredCategory).left,
|
|
22539
|
-
y: 0,
|
|
22540
|
-
width: cellOf(hoveredCategory).right - cellOf(hoveredCategory).left,
|
|
22541
|
-
height: size.height,
|
|
22542
|
-
style: {
|
|
22543
|
-
fill: "var(--cmp-chart-barContainer-hover-bg)",
|
|
22544
|
-
fillOpacity: "var(--cmp-chart-barContainer-hover-opacity)"
|
|
22545
|
-
}
|
|
22546
|
-
}
|
|
22547
|
-
),
|
|
22548
|
-
areaGradients,
|
|
22549
|
-
areaMask,
|
|
22550
|
-
area && /* @__PURE__ */ jsx("g", { mask: `url(#${areaMaskId})`, children: areas }),
|
|
22551
|
-
lines,
|
|
22552
|
-
pointHalos,
|
|
22553
|
-
pointDots,
|
|
22554
|
-
valueLabels
|
|
22555
|
-
]
|
|
22556
|
-
}
|
|
22557
|
-
),
|
|
22558
|
-
showTooltip && hoveredCategory != null && tooltipAnchor != null && tooltipItems.length > 0 && /* @__PURE__ */ jsx(
|
|
22559
|
-
ChartTooltip,
|
|
22560
|
-
{
|
|
22561
|
-
title: categories[hoveredCategory] ?? "",
|
|
22562
|
-
items: tooltipItems,
|
|
22563
|
-
anchor: tooltipAnchor,
|
|
22564
|
-
bounds: size,
|
|
22565
|
-
gapX: { after: -4, before: -4 },
|
|
22566
|
-
footer: tooltipFooter?.({
|
|
22567
|
-
categoryIndex: hoveredCategory,
|
|
22568
|
-
name: categories[hoveredCategory] ?? "",
|
|
22569
|
-
points: rows.flatMap((row, seriesIndex) => {
|
|
22570
|
-
const point = row[hoveredCategory];
|
|
22571
|
-
if (point == null) return [];
|
|
22572
|
-
return [{ seriesIndex, categoryIndex: hoveredCategory, value: point.own }];
|
|
22573
|
-
})
|
|
22574
|
-
})
|
|
22575
|
-
}
|
|
22576
|
-
)
|
|
22577
|
-
]
|
|
22578
|
-
}
|
|
22579
|
-
)
|
|
22580
|
-
}
|
|
22581
|
-
),
|
|
22582
|
-
/* @__PURE__ */ jsx("div", { ref: axisTextRef, children: categoryLabels })
|
|
22583
|
-
]
|
|
23035
|
+
axis: axisLabels,
|
|
23036
|
+
axisStyle: { height: plotHeight, ...inset },
|
|
23037
|
+
plot,
|
|
23038
|
+
minPlotWidth,
|
|
23039
|
+
labels: categories,
|
|
23040
|
+
xAt,
|
|
23041
|
+
labelsRef: axisTextRef
|
|
22584
23042
|
}
|
|
22585
23043
|
)
|
|
22586
|
-
|
|
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
|
+
] })
|
|
22587
23051
|
}
|
|
22588
23052
|
)
|
|
22589
23053
|
}
|
|
@@ -28658,212 +29122,171 @@ function formatTimeDisplay(value, type) {
|
|
|
28658
29122
|
const displayHour = hour % 12 === 0 ? 12 : hour % 12;
|
|
28659
29123
|
return `${meridiem} ${padTimePart(displayHour)}:${padTimePart(minute)}`;
|
|
28660
29124
|
}
|
|
28661
|
-
|
|
28662
|
-
|
|
28663
|
-
|
|
29125
|
+
function scrollSelectedIntoView(list, index, center) {
|
|
29126
|
+
const top = index * MODE_ITEM_HEIGHT;
|
|
29127
|
+
if (center) {
|
|
29128
|
+
list.scrollTop = Math.max(0, top - (list.clientHeight - MODE_ITEM_HEIGHT) / 2);
|
|
29129
|
+
return;
|
|
29130
|
+
}
|
|
29131
|
+
if (top < list.scrollTop) list.scrollTop = top;
|
|
29132
|
+
else if (top + MODE_ITEM_HEIGHT > list.scrollTop + list.clientHeight)
|
|
29133
|
+
list.scrollTop = top + MODE_ITEM_HEIGHT - list.clientHeight;
|
|
29134
|
+
}
|
|
29135
|
+
function lastIndex(arr, pred) {
|
|
29136
|
+
for (let i = arr.length - 1; i >= 0; i--) if (pred(arr[i], i)) return i;
|
|
29137
|
+
return -1;
|
|
29138
|
+
}
|
|
29139
|
+
function ListColumn({ ariaLabel, items }) {
|
|
29140
|
+
const listRef = useRef(null);
|
|
29141
|
+
const mountedRef = useRef(false);
|
|
29142
|
+
const selectedIndex = items.findIndex((item4) => item4.selected);
|
|
29143
|
+
const tabIndexTarget = selectedIndex >= 0 ? selectedIndex : items.findIndex((item4) => !item4.disabled);
|
|
29144
|
+
useLayoutEffect(() => {
|
|
29145
|
+
const list = listRef.current;
|
|
29146
|
+
if (!list || selectedIndex < 0) return;
|
|
29147
|
+
scrollSelectedIntoView(list, selectedIndex, !mountedRef.current);
|
|
29148
|
+
mountedRef.current = true;
|
|
29149
|
+
}, [selectedIndex]);
|
|
29150
|
+
const onKeyDown = (event) => {
|
|
29151
|
+
const buttons = [...listRef.current?.querySelectorAll("button") ?? []];
|
|
29152
|
+
const current = buttons.indexOf(document.activeElement);
|
|
29153
|
+
if (current < 0) return;
|
|
29154
|
+
const enabled = (item4, i) => !item4.disabled && i !== current;
|
|
29155
|
+
let next;
|
|
29156
|
+
if (event.key === "ArrowDown") next = items.findIndex((it, i) => i > current && enabled(it, i));
|
|
29157
|
+
else if (event.key === "ArrowUp")
|
|
29158
|
+
next = lastIndex(items, (it, i) => i < current && enabled(it, i));
|
|
29159
|
+
else if (event.key === "Home") next = items.findIndex((it) => !it.disabled);
|
|
29160
|
+
else if (event.key === "End") next = lastIndex(items, (it) => !it.disabled);
|
|
29161
|
+
else return;
|
|
29162
|
+
event.preventDefault();
|
|
29163
|
+
if (next < 0) return;
|
|
29164
|
+
buttons[next]?.focus({ preventScroll: true });
|
|
29165
|
+
if (listRef.current) scrollSelectedIntoView(listRef.current, next, false);
|
|
29166
|
+
};
|
|
29167
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full w-[var(--cmp-datepicker-mode-yearMonth-column-width)] min-w-0 overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]", children: /* @__PURE__ */ jsx(
|
|
29168
|
+
"div",
|
|
29169
|
+
{
|
|
29170
|
+
ref: listRef,
|
|
29171
|
+
role: "listbox",
|
|
29172
|
+
"aria-label": ariaLabel,
|
|
29173
|
+
tabIndex: -1,
|
|
29174
|
+
className: "h-full overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
29175
|
+
onKeyDown,
|
|
29176
|
+
children: items.map((item4, index) => /* @__PURE__ */ jsx(
|
|
29177
|
+
"button",
|
|
29178
|
+
{
|
|
29179
|
+
type: "button",
|
|
29180
|
+
role: "option",
|
|
29181
|
+
"aria-selected": item4.selected,
|
|
29182
|
+
disabled: item4.disabled,
|
|
29183
|
+
tabIndex: index === tabIndexTarget ? 0 : -1,
|
|
29184
|
+
className: cn(modeListItemClass(item4.selected, item4.disabled), "outline-offset-[-2px]"),
|
|
29185
|
+
onClick: () => !item4.disabled && item4.onSelect(),
|
|
29186
|
+
children: item4.label
|
|
29187
|
+
},
|
|
29188
|
+
item4.key
|
|
29189
|
+
))
|
|
29190
|
+
}
|
|
29191
|
+
) });
|
|
29192
|
+
}
|
|
29193
|
+
var HOURS_24 = Array.from({ length: 24 }, (_, i) => i);
|
|
29194
|
+
var HOURS_12 = [12, ...Array.from({ length: 11 }, (_, i) => i + 1)];
|
|
29195
|
+
function TimeListbox({
|
|
28664
29196
|
value,
|
|
28665
29197
|
showMeridiem,
|
|
28666
29198
|
minuteStep,
|
|
28667
|
-
|
|
29199
|
+
isDisabled,
|
|
29200
|
+
onChange,
|
|
29201
|
+
labelPrefix,
|
|
29202
|
+
elevated = true
|
|
28668
29203
|
}) {
|
|
28669
|
-
const
|
|
28670
|
-
const [
|
|
28671
|
-
const [minute, setMinute] = useState(parsed.minute);
|
|
28672
|
-
const [editingPart, setEditingPart] = useState(null);
|
|
28673
|
-
const [inputDraft, setInputDraft] = useState({});
|
|
29204
|
+
const toState = (v) => v ? parseTimeValue(v) : null;
|
|
29205
|
+
const [time, setTime] = useState(() => toState(value));
|
|
28674
29206
|
useEffect(() => {
|
|
28675
|
-
|
|
28676
|
-
|
|
28677
|
-
|
|
28678
|
-
const
|
|
28679
|
-
const
|
|
28680
|
-
const
|
|
28681
|
-
|
|
28682
|
-
|
|
28683
|
-
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
28687
|
-
|
|
28688
|
-
if (next
|
|
28689
|
-
|
|
28690
|
-
|
|
28691
|
-
const changeMinute = (delta) => {
|
|
28692
|
-
const next = minute + delta;
|
|
28693
|
-
if (next > 59) {
|
|
28694
|
-
commit((hour + 1) % 24, next - 60);
|
|
28695
|
-
return;
|
|
28696
|
-
}
|
|
28697
|
-
if (next < 0) {
|
|
28698
|
-
commit((hour + 23) % 24, next + 60);
|
|
28699
|
-
return;
|
|
28700
|
-
}
|
|
28701
|
-
commit(hour, next);
|
|
28702
|
-
};
|
|
28703
|
-
const changeHour = (delta) => commit((hour + delta + 24) % 24, minute);
|
|
28704
|
-
const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
|
|
28705
|
-
const beginEdit = (part, currentValue) => {
|
|
28706
|
-
setEditingPart(part);
|
|
28707
|
-
setInputDraft((prev) => ({ ...prev, [part]: String(currentValue).padStart(2, "0") }));
|
|
28708
|
-
};
|
|
28709
|
-
const commitHourDraft = (draft) => {
|
|
28710
|
-
const clamped = clampTimePart(Number(draft), showMeridiem ? 1 : 0, showMeridiem ? 12 : 23);
|
|
28711
|
-
const nextHour = showMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
|
|
28712
|
-
commit(nextHour, minute);
|
|
28713
|
-
return clamped;
|
|
28714
|
-
};
|
|
28715
|
-
const commitMinuteDraft = (draft) => {
|
|
28716
|
-
const clamped = clampTimePart(Number(draft), 0, 59);
|
|
28717
|
-
commit(hour, clamped);
|
|
28718
|
-
return clamped;
|
|
28719
|
-
};
|
|
28720
|
-
const endEdit = (part) => {
|
|
28721
|
-
const draft = inputDraft[part];
|
|
28722
|
-
if (part === "hour" && draft != null && draft !== "") {
|
|
28723
|
-
commitHourDraft(draft);
|
|
28724
|
-
}
|
|
28725
|
-
if (part === "minute" && draft != null && draft !== "") {
|
|
28726
|
-
commitMinuteDraft(draft);
|
|
28727
|
-
}
|
|
28728
|
-
setEditingPart(null);
|
|
28729
|
-
setInputDraft({});
|
|
28730
|
-
};
|
|
28731
|
-
const inputStyle = {
|
|
28732
|
-
width: "var(--cmp-timepicker-selector-time-input-width)",
|
|
28733
|
-
height: "var(--cmp-textinput-sm-height)",
|
|
28734
|
-
borderRadius: "var(--cmp-textinput-sm-radius)",
|
|
28735
|
-
border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
|
|
28736
|
-
color: "var(--cmp-textinput-text-default)",
|
|
28737
|
-
fontSize: 12,
|
|
28738
|
-
lineHeight: "20px"
|
|
29207
|
+
setTime(toState(value));
|
|
29208
|
+
}, [value]);
|
|
29209
|
+
const step = Math.max(1, Math.floor(minuteStep));
|
|
29210
|
+
const minutes = Array.from({ length: Math.ceil(60 / step) }, (_, i) => i * step);
|
|
29211
|
+
const blocked = (h, m) => isDisabled?.(h * 60 + m) ?? false;
|
|
29212
|
+
const hourBlocked = (h) => minutes.every((m) => blocked(h, m));
|
|
29213
|
+
const commit = (hours, preferred, part) => {
|
|
29214
|
+
let next = blocked(preferred.hour, preferred.minute) ? null : preferred;
|
|
29215
|
+
for (const h of hours) {
|
|
29216
|
+
if (next) break;
|
|
29217
|
+
const m = minutes.find((mm) => !blocked(h, mm));
|
|
29218
|
+
if (m !== void 0) next = { hour: h, minute: m };
|
|
29219
|
+
}
|
|
29220
|
+
if (!next) return;
|
|
29221
|
+
setTime(next);
|
|
29222
|
+
onChange(formatTimeValue(next.hour, next.minute), part);
|
|
28739
29223
|
};
|
|
28740
|
-
const
|
|
28741
|
-
|
|
28742
|
-
|
|
28743
|
-
|
|
28744
|
-
|
|
28745
|
-
|
|
28746
|
-
|
|
28747
|
-
|
|
28748
|
-
|
|
28749
|
-
|
|
28750
|
-
|
|
28751
|
-
|
|
28752
|
-
|
|
28753
|
-
|
|
28754
|
-
|
|
28755
|
-
|
|
28756
|
-
onClick: () => isHour ? changeHour(1) : changeMinute(minuteStep)
|
|
28757
|
-
}
|
|
28758
|
-
),
|
|
28759
|
-
/* @__PURE__ */ jsx(
|
|
28760
|
-
"input",
|
|
28761
|
-
{
|
|
28762
|
-
"aria-label": isHour ? "\uC2DC" : "\uBD84",
|
|
28763
|
-
inputMode: "numeric",
|
|
28764
|
-
value: displayValue2,
|
|
28765
|
-
placeholder: "-",
|
|
28766
|
-
onFocus: () => beginEdit(type, val),
|
|
28767
|
-
onBlur: () => endEdit(type),
|
|
28768
|
-
onChange: (event) => {
|
|
28769
|
-
const draft = sanitizeInput(event.target.value);
|
|
28770
|
-
setInputDraft((prev) => ({ ...prev, [type]: draft }));
|
|
28771
|
-
if (draft === "") return;
|
|
28772
|
-
const raw = Number(draft);
|
|
28773
|
-
if (isHour) {
|
|
28774
|
-
if (draft.length < 2) return;
|
|
28775
|
-
const clamped2 = commitHourDraft(draft);
|
|
28776
|
-
const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
|
|
28777
|
-
setInputDraft((prev) => ({ ...prev, [type]: displayDraft2 }));
|
|
28778
|
-
return;
|
|
28779
|
-
}
|
|
28780
|
-
if (draft.length < 2) return;
|
|
28781
|
-
const clamped = commitMinuteDraft(draft);
|
|
28782
|
-
const displayDraft = raw === clamped ? draft : String(clamped);
|
|
28783
|
-
setInputDraft((prev) => ({ ...prev, [type]: displayDraft }));
|
|
28784
|
-
},
|
|
28785
|
-
className: "box-border bg-white text-center outline-none",
|
|
28786
|
-
style: inputStyle
|
|
28787
|
-
}
|
|
28788
|
-
),
|
|
28789
|
-
/* @__PURE__ */ jsx(
|
|
28790
|
-
SGhostButton,
|
|
28791
|
-
{
|
|
28792
|
-
icon: "chevronDown",
|
|
28793
|
-
size: "xs",
|
|
28794
|
-
ariaLabel: `${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
|
|
28795
|
-
onClick: () => isHour ? changeHour(-1) : changeMinute(-minuteStep)
|
|
28796
|
-
}
|
|
28797
|
-
)
|
|
28798
|
-
]
|
|
29224
|
+
const hour = time?.hour;
|
|
29225
|
+
const minute = time?.minute ?? 0;
|
|
29226
|
+
const meridiem = hour === void 0 ? void 0 : hour >= 12 ? "PM" : "AM";
|
|
29227
|
+
const prefix = labelPrefix ? `${labelPrefix} ` : "";
|
|
29228
|
+
const meridiemItems = ["AM", "PM"].map((item4) => {
|
|
29229
|
+
const base = item4 === "AM" ? 0 : 12;
|
|
29230
|
+
const range = HOURS_24.slice(base, base + 12);
|
|
29231
|
+
return {
|
|
29232
|
+
key: item4,
|
|
29233
|
+
label: item4 === "AM" ? "\uC624\uC804" : "\uC624\uD6C4",
|
|
29234
|
+
selected: meridiem === item4,
|
|
29235
|
+
disabled: range.every(hourBlocked),
|
|
29236
|
+
onSelect: () => {
|
|
29237
|
+
if (meridiem === item4) return;
|
|
29238
|
+
const h = hour === void 0 ? base : hour % 12 + base;
|
|
29239
|
+
commit(range, { hour: h, minute }, "meridiem");
|
|
28799
29240
|
}
|
|
28800
|
-
|
|
28801
|
-
};
|
|
29241
|
+
};
|
|
29242
|
+
});
|
|
29243
|
+
const hourItems = showMeridiem ? HOURS_12.map((display) => {
|
|
29244
|
+
const h = display % 12 + (meridiem === "PM" ? 12 : 0);
|
|
29245
|
+
return {
|
|
29246
|
+
key: String(display),
|
|
29247
|
+
label: `${padTimePart(display)}\uC2DC`,
|
|
29248
|
+
selected: hour === h,
|
|
29249
|
+
disabled: hourBlocked(h),
|
|
29250
|
+
onSelect: () => commit([h], { hour: h, minute }, "hour")
|
|
29251
|
+
};
|
|
29252
|
+
}) : HOURS_24.map((h) => ({
|
|
29253
|
+
key: String(h),
|
|
29254
|
+
label: `${padTimePart(h)}\uC2DC`,
|
|
29255
|
+
selected: hour === h,
|
|
29256
|
+
disabled: hourBlocked(h),
|
|
29257
|
+
onSelect: () => commit([h], { hour: h, minute }, "hour")
|
|
29258
|
+
}));
|
|
29259
|
+
const minuteHour = hour ?? (meridiem === "PM" ? 12 : 0);
|
|
29260
|
+
const minuteItems = minutes.map((m) => ({
|
|
29261
|
+
key: String(m),
|
|
29262
|
+
label: `${padTimePart(m)}\uBD84`,
|
|
29263
|
+
selected: time != null && minute === m,
|
|
29264
|
+
disabled: blocked(minuteHour, m),
|
|
29265
|
+
onSelect: () => commit([minuteHour], { hour: minuteHour, minute: m }, "minute")
|
|
29266
|
+
}));
|
|
29267
|
+
const divider = /* @__PURE__ */ jsx(
|
|
29268
|
+
"div",
|
|
29269
|
+
{
|
|
29270
|
+
"aria-hidden": true,
|
|
29271
|
+
className: "w-[var(--border-width-1)] self-stretch bg-[var(--sys-color-divider-default)]"
|
|
29272
|
+
}
|
|
29273
|
+
);
|
|
28802
29274
|
return /* @__PURE__ */ jsxs(
|
|
28803
29275
|
"div",
|
|
28804
29276
|
{
|
|
28805
|
-
className:
|
|
28806
|
-
|
|
28807
|
-
|
|
28808
|
-
|
|
28809
|
-
|
|
28810
|
-
},
|
|
29277
|
+
className: cn(
|
|
29278
|
+
"inline-flex gap-[var(--cmp-datepicker-mode-yearMonth-gap)] overflow-hidden bg-[var(--cmp-datepicker-mode-bg)]",
|
|
29279
|
+
elevated && "rounded-[var(--cmp-datepicker-mode-radius)] shadow-normal-sm"
|
|
29280
|
+
),
|
|
29281
|
+
style: { height: MODE_LIST_HEIGHT },
|
|
28811
29282
|
children: [
|
|
28812
|
-
showMeridiem && /* @__PURE__ */
|
|
28813
|
-
|
|
28814
|
-
|
|
28815
|
-
|
|
28816
|
-
|
|
28817
|
-
|
|
28818
|
-
|
|
28819
|
-
children: /* @__PURE__ */ jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item4, index) => {
|
|
28820
|
-
const selected = item4 === meridiem;
|
|
28821
|
-
return /* @__PURE__ */ jsx(
|
|
28822
|
-
"button",
|
|
28823
|
-
{
|
|
28824
|
-
type: "button",
|
|
28825
|
-
onClick: () => setMeridiem(item4),
|
|
28826
|
-
className: cn(
|
|
28827
|
-
"box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
|
|
28828
|
-
index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)]" : "rounded-r-[var(--cmp-select-sm-radius)]",
|
|
28829
|
-
index === 0 ? "-mr-px" : "",
|
|
28830
|
-
selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
|
|
28831
|
-
),
|
|
28832
|
-
children: item4 === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
|
|
28833
|
-
},
|
|
28834
|
-
item4
|
|
28835
|
-
);
|
|
28836
|
-
}) })
|
|
28837
|
-
}
|
|
28838
|
-
),
|
|
28839
|
-
showMeridiem && /* @__PURE__ */ jsx("div", { className: "h-px w-full bg-[var(--sys-color-divider-default)]" }),
|
|
28840
|
-
/* @__PURE__ */ jsxs(
|
|
28841
|
-
"div",
|
|
28842
|
-
{
|
|
28843
|
-
className: "flex items-center justify-center",
|
|
28844
|
-
style: {
|
|
28845
|
-
gap: "var(--cmp-timepicker-selector-time-gap)",
|
|
28846
|
-
padding: "var(--cmp-timepicker-selector-section-paddingY) var(--cmp-timepicker-selector-section-paddingX)"
|
|
28847
|
-
},
|
|
28848
|
-
children: [
|
|
28849
|
-
renderColumn("hour"),
|
|
28850
|
-
/* @__PURE__ */ jsx(
|
|
28851
|
-
"span",
|
|
28852
|
-
{
|
|
28853
|
-
className: "text-center",
|
|
28854
|
-
style: {
|
|
28855
|
-
width: "var(--cmp-timepicker-selector-colon-size)",
|
|
28856
|
-
color: "var(--cmp-field-hint-color)",
|
|
28857
|
-
fontSize: "var(--cmp-timepicker-selector-colon-size)",
|
|
28858
|
-
lineHeight: "var(--cmp-timepicker-selector-colon-size)"
|
|
28859
|
-
},
|
|
28860
|
-
children: ":"
|
|
28861
|
-
}
|
|
28862
|
-
),
|
|
28863
|
-
renderColumn("minute")
|
|
28864
|
-
]
|
|
28865
|
-
}
|
|
28866
|
-
)
|
|
29283
|
+
showMeridiem && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29284
|
+
/* @__PURE__ */ jsx(ListColumn, { ariaLabel: `${prefix}\uC624\uC804/\uC624\uD6C4`, items: meridiemItems }),
|
|
29285
|
+
divider
|
|
29286
|
+
] }),
|
|
29287
|
+
/* @__PURE__ */ jsx(ListColumn, { ariaLabel: `${prefix}\uC2DC`, items: hourItems }),
|
|
29288
|
+
divider,
|
|
29289
|
+
/* @__PURE__ */ jsx(ListColumn, { ariaLabel: `${prefix}\uBD84`, items: minuteItems })
|
|
28867
29290
|
]
|
|
28868
29291
|
}
|
|
28869
29292
|
);
|
|
@@ -28901,11 +29324,9 @@ function STimePicker({
|
|
|
28901
29324
|
style
|
|
28902
29325
|
}) {
|
|
28903
29326
|
const portalContainer = usePortalContainer();
|
|
28904
|
-
const triggerRef = useRef(null);
|
|
28905
29327
|
const [open, setOpen] = useState(false);
|
|
28906
29328
|
const triggerId = useFieldControlId();
|
|
28907
29329
|
const triggerTextId = `${triggerId}-text`;
|
|
28908
|
-
const [popoverAlignOffset, setPopoverAlignOffset] = useState(0);
|
|
28909
29330
|
const [focused, setFocused] = useState(false);
|
|
28910
29331
|
const [ruleError, setRuleError] = useState("");
|
|
28911
29332
|
const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
|
|
@@ -28926,11 +29347,6 @@ function STimePicker({
|
|
|
28926
29347
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
28927
29348
|
const commitOpen = (next) => {
|
|
28928
29349
|
if (disabled) return;
|
|
28929
|
-
if (next) {
|
|
28930
|
-
const triggerWidth = triggerRef.current?.getBoundingClientRect().width ?? 0;
|
|
28931
|
-
const selectorWidth = effectiveShowMeridiem ? MERIDIEM_TIME_SELECTOR_WIDTH : TIME_SELECTOR_WIDTH;
|
|
28932
|
-
setPopoverAlignOffset(Math.max(0, (triggerWidth - selectorWidth) / 2));
|
|
28933
|
-
}
|
|
28934
29350
|
setOpen(next);
|
|
28935
29351
|
onOpenChange?.(next);
|
|
28936
29352
|
if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
|
|
@@ -28969,7 +29385,6 @@ function STimePicker({
|
|
|
28969
29385
|
/* @__PURE__ */ jsx(RPopover13.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
28970
29386
|
"div",
|
|
28971
29387
|
{
|
|
28972
|
-
ref: triggerRef,
|
|
28973
29388
|
id: triggerId,
|
|
28974
29389
|
role: "button",
|
|
28975
29390
|
"aria-disabled": disabled,
|
|
@@ -29033,20 +29448,22 @@ function STimePicker({
|
|
|
29033
29448
|
RPopover13.Content,
|
|
29034
29449
|
{
|
|
29035
29450
|
side: "bottom",
|
|
29036
|
-
align: "
|
|
29451
|
+
align: "center",
|
|
29037
29452
|
sideOffset: 4,
|
|
29038
|
-
alignOffset: popoverAlignOffset,
|
|
29039
29453
|
className: cn(
|
|
29040
29454
|
"z-50 max-w-[var(--radix-popover-content-available-width)]",
|
|
29041
29455
|
FLOATING_SLIDE_ANIM
|
|
29042
29456
|
),
|
|
29043
29457
|
children: /* @__PURE__ */ jsx(
|
|
29044
|
-
|
|
29458
|
+
TimeListbox,
|
|
29045
29459
|
{
|
|
29046
29460
|
value: normalized,
|
|
29047
29461
|
showMeridiem: effectiveShowMeridiem,
|
|
29048
29462
|
minuteStep,
|
|
29049
|
-
onChange:
|
|
29463
|
+
onChange: (time, part) => {
|
|
29464
|
+
commitValue(time);
|
|
29465
|
+
if (part === "minute") commitOpen(false);
|
|
29466
|
+
}
|
|
29050
29467
|
}
|
|
29051
29468
|
)
|
|
29052
29469
|
}
|
|
@@ -29076,7 +29493,6 @@ function formatTimeRangeDisplay(value, type) {
|
|
|
29076
29493
|
if (value == null || value[0] === "" || value[1] === "") return "";
|
|
29077
29494
|
return `${formatTimeDisplay(value[0], type)} ~ ${formatTimeDisplay(value[1], type)}`;
|
|
29078
29495
|
}
|
|
29079
|
-
var TIME_RANGE_SELECTOR_WIDTH = 345;
|
|
29080
29496
|
var MIDDAY_FIELD_MIN_WIDTH = {
|
|
29081
29497
|
sm: 178,
|
|
29082
29498
|
md: 216
|
|
@@ -29085,6 +29501,10 @@ var MIDDAY_FIELD_MAX_WIDTH = {
|
|
|
29085
29501
|
sm: "lg",
|
|
29086
29502
|
md: "lg"
|
|
29087
29503
|
};
|
|
29504
|
+
var toMinutes = (time) => {
|
|
29505
|
+
const { hour, minute } = parseTimeValue(time);
|
|
29506
|
+
return hour * 60 + minute;
|
|
29507
|
+
};
|
|
29088
29508
|
function RangeTimeSelector({
|
|
29089
29509
|
value,
|
|
29090
29510
|
showMeridiem,
|
|
@@ -29092,233 +29512,32 @@ function RangeTimeSelector({
|
|
|
29092
29512
|
rangeOrder,
|
|
29093
29513
|
onChange
|
|
29094
29514
|
}) {
|
|
29095
|
-
const range =
|
|
29096
|
-
const
|
|
29097
|
-
const
|
|
29098
|
-
|
|
29099
|
-
|
|
29100
|
-
const getMeridiem = (side) => getParts(side).hour >= 12 ? "PM" : "AM";
|
|
29101
|
-
const getDisplayHour = (side) => {
|
|
29102
|
-
const { hour } = getParts(side);
|
|
29103
|
-
if (!showMeridiem) return hour;
|
|
29104
|
-
const displayHour = hour % 12;
|
|
29105
|
-
return displayHour === 0 ? 12 : displayHour;
|
|
29106
|
-
};
|
|
29107
|
-
const commit = (side, hour, minute, direction = "unknown") => {
|
|
29108
|
-
const next = formatTimeValue(hour, minute);
|
|
29109
|
-
onChange(
|
|
29110
|
-
normalizeNextRange(side === "start" ? [next, range[1]] : [range[0], next], side, direction)
|
|
29111
|
-
);
|
|
29112
|
-
};
|
|
29113
|
-
const getTimeMinutes2 = (time) => {
|
|
29114
|
-
const { hour, minute } = parseTimeValue(time);
|
|
29115
|
-
return hour * 60 + minute;
|
|
29116
|
-
};
|
|
29117
|
-
const normalizeNextRange = (nextRange, changedSide, direction) => {
|
|
29118
|
-
if (rangeOrder === "allow-cross-day") return nextRange;
|
|
29119
|
-
const [start, end] = nextRange;
|
|
29120
|
-
const [currentStart, currentEnd] = range;
|
|
29121
|
-
if (getTimeMinutes2(start) <= getTimeMinutes2(end)) {
|
|
29122
|
-
const shouldKeepSynced = autoAdjustedRef.current?.side === changedSide && currentStart === currentEnd && start !== end && (direction === "unknown" || autoAdjustedRef.current.direction === direction);
|
|
29123
|
-
if (shouldKeepSynced) {
|
|
29124
|
-
return changedSide === "start" ? [start, start] : [end, end];
|
|
29125
|
-
}
|
|
29126
|
-
autoAdjustedRef.current = null;
|
|
29127
|
-
return nextRange;
|
|
29128
|
-
}
|
|
29129
|
-
autoAdjustedRef.current = {
|
|
29130
|
-
side: changedSide,
|
|
29131
|
-
direction: changedSide === "start" ? "increase" : "decrease"
|
|
29132
|
-
};
|
|
29133
|
-
return changedSide === "start" ? [start, start] : [end, end];
|
|
29134
|
-
};
|
|
29135
|
-
const changeHour = (side, delta) => {
|
|
29136
|
-
const { hour, minute } = getParts(side);
|
|
29137
|
-
commit(side, (hour + delta + 24) % 24, minute, delta > 0 ? "increase" : "decrease");
|
|
29138
|
-
};
|
|
29139
|
-
const changeMinute = (side, delta) => {
|
|
29140
|
-
const { hour, minute } = getParts(side);
|
|
29141
|
-
const next = minute + delta;
|
|
29142
|
-
if (next > 59) {
|
|
29143
|
-
commit(side, (hour + 1) % 24, next - 60, delta > 0 ? "increase" : "decrease");
|
|
29144
|
-
return;
|
|
29145
|
-
}
|
|
29146
|
-
if (next < 0) {
|
|
29147
|
-
commit(side, (hour + 23) % 24, next + 60, delta > 0 ? "increase" : "decrease");
|
|
29515
|
+
const range = value ?? ["00:00", "23:59"];
|
|
29516
|
+
const strict = rangeOrder !== "allow-cross-day";
|
|
29517
|
+
const change = (side, time) => {
|
|
29518
|
+
if (side === "end") {
|
|
29519
|
+
onChange([range[0], time]);
|
|
29148
29520
|
return;
|
|
29149
29521
|
}
|
|
29150
|
-
|
|
29151
|
-
};
|
|
29152
|
-
const setMeridiem = (side, next) => {
|
|
29153
|
-
const current = getMeridiem(side);
|
|
29154
|
-
if (current === next) return;
|
|
29155
|
-
const { hour, minute } = getParts(side);
|
|
29156
|
-
commit(side, next === "AM" ? hour - 12 : hour + 12, minute);
|
|
29157
|
-
};
|
|
29158
|
-
const getEditingKey = (side, part) => `${side}-${part}`;
|
|
29159
|
-
const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
|
|
29160
|
-
const beginEdit = (side, part, currentValue) => {
|
|
29161
|
-
const key = getEditingKey(side, part);
|
|
29162
|
-
setEditingKey(key);
|
|
29163
|
-
setInputDraft((prev) => ({ ...prev, [key]: String(currentValue).padStart(2, "0") }));
|
|
29164
|
-
};
|
|
29165
|
-
const commitHourDraft = (side, draft) => {
|
|
29166
|
-
const clamped = clampTimePart(Number(draft), showMeridiem ? 1 : 0, showMeridiem ? 12 : 23);
|
|
29167
|
-
const parts = getParts(side);
|
|
29168
|
-
const meridiem = getMeridiem(side);
|
|
29169
|
-
const nextHour = showMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
|
|
29170
|
-
commit(side, nextHour, parts.minute);
|
|
29171
|
-
return clamped;
|
|
29172
|
-
};
|
|
29173
|
-
const commitMinuteDraft = (side, draft) => {
|
|
29174
|
-
const { hour } = getParts(side);
|
|
29175
|
-
const clamped = clampTimePart(Number(draft), 0, 59);
|
|
29176
|
-
commit(side, hour, clamped);
|
|
29177
|
-
return clamped;
|
|
29178
|
-
};
|
|
29179
|
-
const endEdit = (side, part) => {
|
|
29180
|
-
const key = getEditingKey(side, part);
|
|
29181
|
-
const draft = inputDraft[key];
|
|
29182
|
-
if (part === "hour" && draft != null && draft !== "") {
|
|
29183
|
-
commitHourDraft(side, draft);
|
|
29184
|
-
}
|
|
29185
|
-
if (part === "minute" && draft != null && draft !== "") {
|
|
29186
|
-
commitMinuteDraft(side, draft);
|
|
29187
|
-
}
|
|
29188
|
-
setEditingKey(null);
|
|
29189
|
-
setInputDraft({});
|
|
29190
|
-
};
|
|
29191
|
-
const inputStyle = {
|
|
29192
|
-
width: "var(--cmp-timepicker-selector-time-input-width)",
|
|
29193
|
-
height: "var(--cmp-textinput-sm-height)",
|
|
29194
|
-
borderRadius: "var(--cmp-textinput-sm-radius)",
|
|
29195
|
-
border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
|
|
29196
|
-
color: "var(--cmp-textinput-text-default)",
|
|
29197
|
-
fontSize: 12,
|
|
29198
|
-
lineHeight: "20px"
|
|
29199
|
-
};
|
|
29200
|
-
const renderColumn = (side, kind) => {
|
|
29201
|
-
const isHour = kind === "hour";
|
|
29202
|
-
const { minute } = getParts(side);
|
|
29203
|
-
const val = isHour ? getDisplayHour(side) : minute;
|
|
29204
|
-
const currentEditingKey = getEditingKey(side, kind);
|
|
29205
|
-
const displayValue2 = editingKey === currentEditingKey ? inputDraft[currentEditingKey] ?? "" : String(val).padStart(2, "0");
|
|
29206
|
-
return /* @__PURE__ */ jsxs(
|
|
29207
|
-
"div",
|
|
29208
|
-
{
|
|
29209
|
-
className: "flex flex-col items-center justify-center",
|
|
29210
|
-
style: { gap: "var(--cmp-timepicker-selector-time-column-gap)" },
|
|
29211
|
-
children: [
|
|
29212
|
-
/* @__PURE__ */ jsx(
|
|
29213
|
-
SGhostButton,
|
|
29214
|
-
{
|
|
29215
|
-
icon: "chevronUp",
|
|
29216
|
-
size: "xs",
|
|
29217
|
-
ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uC99D\uAC00`,
|
|
29218
|
-
onClick: () => isHour ? changeHour(side, 1) : changeMinute(side, minuteStep)
|
|
29219
|
-
}
|
|
29220
|
-
),
|
|
29221
|
-
/* @__PURE__ */ jsx(
|
|
29222
|
-
"input",
|
|
29223
|
-
{
|
|
29224
|
-
"aria-label": `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"}`,
|
|
29225
|
-
inputMode: "numeric",
|
|
29226
|
-
value: displayValue2,
|
|
29227
|
-
placeholder: "-",
|
|
29228
|
-
onFocus: () => beginEdit(side, kind, val),
|
|
29229
|
-
onBlur: () => endEdit(side, kind),
|
|
29230
|
-
onChange: (event) => {
|
|
29231
|
-
const draft = sanitizeInput(event.target.value);
|
|
29232
|
-
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: draft }));
|
|
29233
|
-
if (draft === "") return;
|
|
29234
|
-
const raw = Number(draft);
|
|
29235
|
-
if (isHour) {
|
|
29236
|
-
if (draft.length < 2) return;
|
|
29237
|
-
const clamped2 = commitHourDraft(side, draft);
|
|
29238
|
-
const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
|
|
29239
|
-
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft2 }));
|
|
29240
|
-
return;
|
|
29241
|
-
}
|
|
29242
|
-
if (draft.length < 2) return;
|
|
29243
|
-
const clamped = commitMinuteDraft(side, draft);
|
|
29244
|
-
const displayDraft = raw === clamped ? draft : String(clamped);
|
|
29245
|
-
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft }));
|
|
29246
|
-
},
|
|
29247
|
-
className: "box-border bg-white text-center outline-none",
|
|
29248
|
-
style: inputStyle
|
|
29249
|
-
}
|
|
29250
|
-
),
|
|
29251
|
-
/* @__PURE__ */ jsx(
|
|
29252
|
-
SGhostButton,
|
|
29253
|
-
{
|
|
29254
|
-
icon: "chevronDown",
|
|
29255
|
-
size: "xs",
|
|
29256
|
-
ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
|
|
29257
|
-
onClick: () => isHour ? changeHour(side, -1) : changeMinute(side, -minuteStep)
|
|
29258
|
-
}
|
|
29259
|
-
)
|
|
29260
|
-
]
|
|
29261
|
-
}
|
|
29262
|
-
);
|
|
29522
|
+
onChange([time, strict && toMinutes(time) > toMinutes(range[1]) ? time : range[1]]);
|
|
29263
29523
|
};
|
|
29264
29524
|
const renderPanel = (side, title) => /* @__PURE__ */ jsxs(
|
|
29265
29525
|
"div",
|
|
29266
29526
|
{
|
|
29267
|
-
className: "flex flex-col items-center
|
|
29268
|
-
style: { gap: "var(--cmp-timepicker-selector-range-
|
|
29527
|
+
className: "flex flex-col items-center",
|
|
29528
|
+
style: { gap: "var(--cmp-timepicker-selector-range-heading-gap)" },
|
|
29269
29529
|
children: [
|
|
29270
|
-
/* @__PURE__ */
|
|
29271
|
-
|
|
29272
|
-
|
|
29273
|
-
className: "flex flex-col items-center",
|
|
29274
|
-
style: { gap: "var(--cmp-timepicker-selector-range-heading-gap)" },
|
|
29275
|
-
children: [
|
|
29276
|
-
/* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-center text-[12px] font-bold leading-[20px] text-[var(--cmp-timepicker-selector-range-heading-color)]", children: title }),
|
|
29277
|
-
showMeridiem && /* @__PURE__ */ jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item4, index) => {
|
|
29278
|
-
const selected = item4 === getMeridiem(side);
|
|
29279
|
-
return /* @__PURE__ */ jsx(
|
|
29280
|
-
"button",
|
|
29281
|
-
{
|
|
29282
|
-
type: "button",
|
|
29283
|
-
onClick: () => setMeridiem(side, item4),
|
|
29284
|
-
className: cn(
|
|
29285
|
-
"box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
|
|
29286
|
-
index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)] -mr-px" : "rounded-r-[var(--cmp-select-sm-radius)]",
|
|
29287
|
-
selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
|
|
29288
|
-
),
|
|
29289
|
-
children: item4 === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
|
|
29290
|
-
},
|
|
29291
|
-
item4
|
|
29292
|
-
);
|
|
29293
|
-
}) })
|
|
29294
|
-
]
|
|
29295
|
-
}
|
|
29296
|
-
),
|
|
29297
|
-
/* @__PURE__ */ jsxs(
|
|
29298
|
-
"div",
|
|
29530
|
+
/* @__PURE__ */ jsx("div", { className: "whitespace-nowrap text-center text-[12px] font-bold leading-[20px] text-[var(--cmp-timepicker-selector-range-heading-color)]", children: title }),
|
|
29531
|
+
/* @__PURE__ */ jsx(
|
|
29532
|
+
TimeListbox,
|
|
29299
29533
|
{
|
|
29300
|
-
|
|
29301
|
-
|
|
29302
|
-
|
|
29303
|
-
|
|
29304
|
-
|
|
29305
|
-
|
|
29306
|
-
|
|
29307
|
-
/* @__PURE__ */ jsx(
|
|
29308
|
-
"span",
|
|
29309
|
-
{
|
|
29310
|
-
className: "text-center",
|
|
29311
|
-
style: {
|
|
29312
|
-
width: "var(--cmp-timepicker-selector-colon-size)",
|
|
29313
|
-
color: "var(--cmp-field-hint-color)",
|
|
29314
|
-
fontSize: "var(--cmp-timepicker-selector-colon-size)",
|
|
29315
|
-
lineHeight: "var(--cmp-timepicker-selector-colon-size)"
|
|
29316
|
-
},
|
|
29317
|
-
children: ":"
|
|
29318
|
-
}
|
|
29319
|
-
),
|
|
29320
|
-
renderColumn(side, "minute")
|
|
29321
|
-
]
|
|
29534
|
+
value: side === "start" ? range[0] : range[1],
|
|
29535
|
+
showMeridiem,
|
|
29536
|
+
minuteStep,
|
|
29537
|
+
labelPrefix: side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC",
|
|
29538
|
+
elevated: false,
|
|
29539
|
+
isDisabled: strict && side === "end" ? (minutes) => minutes < toMinutes(range[0]) : void 0,
|
|
29540
|
+
onChange: (time) => change(side, time)
|
|
29322
29541
|
}
|
|
29323
29542
|
)
|
|
29324
29543
|
]
|
|
@@ -29377,11 +29596,9 @@ function STimeRangePicker({
|
|
|
29377
29596
|
style
|
|
29378
29597
|
}) {
|
|
29379
29598
|
const portalContainer = usePortalContainer();
|
|
29380
|
-
const triggerRef = useRef(null);
|
|
29381
29599
|
const [open, setOpen] = useState(false);
|
|
29382
29600
|
const triggerId = useFieldControlId();
|
|
29383
29601
|
const triggerTextId = `${triggerId}-text`;
|
|
29384
|
-
const [popoverAlignOffset, setPopoverAlignOffset] = useState(0);
|
|
29385
29602
|
const [focused, setFocused] = useState(false);
|
|
29386
29603
|
const [ruleError, setRuleError] = useState("");
|
|
29387
29604
|
const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
|
|
@@ -29405,10 +29622,6 @@ function STimeRangePicker({
|
|
|
29405
29622
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
29406
29623
|
const commitOpen = (next) => {
|
|
29407
29624
|
if (disabled) return;
|
|
29408
|
-
if (next) {
|
|
29409
|
-
const triggerWidth = triggerRef.current?.getBoundingClientRect().width ?? 0;
|
|
29410
|
-
setPopoverAlignOffset(Math.max(0, (triggerWidth - TIME_RANGE_SELECTOR_WIDTH) / 2));
|
|
29411
|
-
}
|
|
29412
29625
|
setOpen(next);
|
|
29413
29626
|
onOpenChange?.(next);
|
|
29414
29627
|
if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
|
|
@@ -29447,7 +29660,6 @@ function STimeRangePicker({
|
|
|
29447
29660
|
/* @__PURE__ */ jsx(RPopover13.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
29448
29661
|
"div",
|
|
29449
29662
|
{
|
|
29450
|
-
ref: triggerRef,
|
|
29451
29663
|
id: triggerId,
|
|
29452
29664
|
role: "button",
|
|
29453
29665
|
"aria-disabled": disabled,
|
|
@@ -29511,9 +29723,8 @@ function STimeRangePicker({
|
|
|
29511
29723
|
RPopover13.Content,
|
|
29512
29724
|
{
|
|
29513
29725
|
side: "bottom",
|
|
29514
|
-
align: "
|
|
29726
|
+
align: "center",
|
|
29515
29727
|
sideOffset: 4,
|
|
29516
|
-
alignOffset: popoverAlignOffset,
|
|
29517
29728
|
className: cn(
|
|
29518
29729
|
"z-50 max-w-[var(--radix-popover-content-available-width)]",
|
|
29519
29730
|
FLOATING_SLIDE_ANIM
|