sellmate-design-system-react 9.0.0-beta.43 → 9.0.0-beta.45
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 +100 -1
- package/dist/components/SDraggableList/README.md +3 -3
- package/dist/components/SDraggableList/SDraggableList.d.ts +7 -3
- package/dist/components/SFooter/README.md +13 -8
- package/dist/components/SFooter/SFooter.d.ts +14 -9
- package/dist/components/SNumberInput/README.md +3 -2
- package/dist/components/SNumberInput/SNumberInput.d.ts +22 -2
- package/dist/components/SPopup/README.md +5 -6
- package/dist/components/SPopup/SPopup.d.ts +6 -7
- package/dist/components/SSelect/README.md +8 -0
- package/dist/components/SSelect/SSelect.d.ts +44 -0
- package/dist/components/SSelect/row-metrics.d.ts +60 -0
- package/dist/components/SSelect/select.config.d.ts +15 -0
- package/dist/components/STable/README.md +1 -1
- package/dist/components/STable/STable.d.ts +1 -1
- package/dist/components/STextarea/README.md +4 -1
- package/dist/components/STextarea/STextarea.d.ts +25 -2
- package/dist/index.cjs +455 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +455 -59
- package/dist/index.js.map +1 -1
- package/dist/llms-full.txt +137 -22
- package/dist/llms.txt +100 -1
- package/dist/styles.css +3 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3717,14 +3717,14 @@ function SFooter({
|
|
|
3717
3717
|
]
|
|
3718
3718
|
}
|
|
3719
3719
|
),
|
|
3720
|
-
hasButton &&
|
|
3720
|
+
hasButton && // 스프레드를 먼저 두고 기본값을 뒤에 덮는다 — 순서를 뒤집으면 `{ color: undefined }`
|
|
3721
|
+
// 처럼 값 없이 넘어온 키가 footer 기본값을 지우고 SButton 기본값으로 떨어진다.
|
|
3722
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3721
3723
|
SButton,
|
|
3722
3724
|
{
|
|
3725
|
+
...button,
|
|
3726
|
+
size: "md",
|
|
3723
3727
|
color: button.color ?? "primary",
|
|
3724
|
-
outline: button.outline,
|
|
3725
|
-
size: button.size ?? "md",
|
|
3726
|
-
disabled: button.disabled,
|
|
3727
|
-
onClick: () => button.onClick?.(),
|
|
3728
3728
|
label: button.label ?? "\uD655\uC778"
|
|
3729
3729
|
}
|
|
3730
3730
|
)
|
|
@@ -4293,6 +4293,13 @@ function SDraggableProvider({ children }) {
|
|
|
4293
4293
|
function SDraggableGroup({ children, onMove, ...rest }) {
|
|
4294
4294
|
return /* @__PURE__ */ jsxRuntime.jsx(SDraggableProviderInternal, { defaultGroup: "__legacy", legacyOnMove: onMove, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ...rest, children }) });
|
|
4295
4295
|
}
|
|
4296
|
+
var INTERACTIVE_DESCENDANT_SELECTOR = 'input,textarea,select,button,a[href],label,[contenteditable="true"],[role="button"],[role="checkbox"],[role="radio"],[role="switch"],[role="tab"],[role="menuitem"]';
|
|
4297
|
+
function isFromInteractiveDescendant(event) {
|
|
4298
|
+
const target = event.target;
|
|
4299
|
+
if (target == null || target === event.currentTarget) return false;
|
|
4300
|
+
const interactive = target.closest?.(INTERACTIVE_DESCENDANT_SELECTOR);
|
|
4301
|
+
return interactive != null && interactive !== event.currentTarget;
|
|
4302
|
+
}
|
|
4296
4303
|
function reorder(items, fromIndex, toIndex) {
|
|
4297
4304
|
if (fromIndex === toIndex) return items;
|
|
4298
4305
|
const nextItems = [...items];
|
|
@@ -4376,6 +4383,7 @@ function SDraggableListInner({
|
|
|
4376
4383
|
const listRef = react.useRef(null);
|
|
4377
4384
|
const fromIndexRef = react.useRef(null);
|
|
4378
4385
|
const currentSelectedKey = selectedKey ?? internalSelectedKey;
|
|
4386
|
+
const selectable = selectedKey !== void 0 || defaultSelectedKey !== void 0 || onSelectedKeyChange != null;
|
|
4379
4387
|
const usesDepth = getDepth != null;
|
|
4380
4388
|
const depthEnabled = usesDepth && !gap;
|
|
4381
4389
|
const groupDragging = dragContext?.dragging;
|
|
@@ -4535,20 +4543,28 @@ function SDraggableListInner({
|
|
|
4535
4543
|
setInternalSelectedKey(itemKey);
|
|
4536
4544
|
onSelectedKeyChange?.(itemKey);
|
|
4537
4545
|
};
|
|
4546
|
+
const handleItemClick = (event) => {
|
|
4547
|
+
if (isFromInteractiveDescendant(event)) return;
|
|
4548
|
+
selectItem();
|
|
4549
|
+
};
|
|
4538
4550
|
const handleItemKeyDown = (event) => {
|
|
4551
|
+
if (event.target !== event.currentTarget) return;
|
|
4539
4552
|
if (event.key !== "Enter" && event.key !== " ") return;
|
|
4540
4553
|
event.preventDefault();
|
|
4541
4554
|
selectItem();
|
|
4542
4555
|
};
|
|
4556
|
+
const selectionProps = selectable ? {
|
|
4557
|
+
role: "button",
|
|
4558
|
+
tabIndex: disabled ? -1 : 0,
|
|
4559
|
+
onClick: handleItemClick,
|
|
4560
|
+
onKeyDown: handleItemKeyDown
|
|
4561
|
+
} : {};
|
|
4543
4562
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4544
4563
|
"div",
|
|
4545
4564
|
{
|
|
4546
4565
|
"data-draggable-list-item": true,
|
|
4547
|
-
role: "button",
|
|
4548
|
-
tabIndex: disabled ? -1 : 0,
|
|
4549
4566
|
"aria-disabled": disabled || void 0,
|
|
4550
|
-
|
|
4551
|
-
onKeyDown: handleItemKeyDown,
|
|
4567
|
+
...selectionProps,
|
|
4552
4568
|
className: "relative",
|
|
4553
4569
|
children: [
|
|
4554
4570
|
showBefore && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -12736,6 +12752,130 @@ function collectGroupLeafValues(flat, groupIndex) {
|
|
|
12736
12752
|
}
|
|
12737
12753
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
12738
12754
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
12755
|
+
var OPTION_ROW_HEIGHT = 28;
|
|
12756
|
+
var DEFAULT_VIRTUAL_BUFFER = 10;
|
|
12757
|
+
var DEFAULT_REACH_END_THRESHOLD = 10;
|
|
12758
|
+
var DEFAULT_SEARCH_DEBOUNCE = 300;
|
|
12759
|
+
|
|
12760
|
+
// src/components/SSelect/row-metrics.ts
|
|
12761
|
+
function createRowMetrics(count, estimate) {
|
|
12762
|
+
const safeCount = Math.max(0, Math.floor(count));
|
|
12763
|
+
const safeEstimate = Math.max(1, estimate);
|
|
12764
|
+
const metrics = {
|
|
12765
|
+
sizes: new Array(safeCount).fill(safeEstimate),
|
|
12766
|
+
measured: new Array(safeCount).fill(false),
|
|
12767
|
+
offsets: [],
|
|
12768
|
+
estimate: safeEstimate,
|
|
12769
|
+
measuredCount: 0,
|
|
12770
|
+
measuredTotal: 0,
|
|
12771
|
+
dirtyFrom: 0
|
|
12772
|
+
};
|
|
12773
|
+
rebuildOffsets(metrics);
|
|
12774
|
+
return metrics;
|
|
12775
|
+
}
|
|
12776
|
+
function resizeRowMetrics(metrics, count) {
|
|
12777
|
+
const next = Math.max(0, Math.floor(count));
|
|
12778
|
+
const prev = metrics.sizes.length;
|
|
12779
|
+
if (next === prev) return;
|
|
12780
|
+
if (next < prev) {
|
|
12781
|
+
for (let i = next; i < prev; i++) {
|
|
12782
|
+
if (metrics.measured[i]) {
|
|
12783
|
+
metrics.measuredCount--;
|
|
12784
|
+
metrics.measuredTotal -= metrics.sizes[i];
|
|
12785
|
+
}
|
|
12786
|
+
}
|
|
12787
|
+
metrics.sizes.length = next;
|
|
12788
|
+
metrics.measured.length = next;
|
|
12789
|
+
} else {
|
|
12790
|
+
for (let i = prev; i < next; i++) {
|
|
12791
|
+
metrics.sizes.push(metrics.estimate);
|
|
12792
|
+
metrics.measured.push(false);
|
|
12793
|
+
}
|
|
12794
|
+
}
|
|
12795
|
+
markDirty(metrics, Math.min(prev, next));
|
|
12796
|
+
}
|
|
12797
|
+
function resetRowMetrics(metrics, count) {
|
|
12798
|
+
const fresh = createRowMetrics(count, metrics.estimate);
|
|
12799
|
+
metrics.sizes = fresh.sizes;
|
|
12800
|
+
metrics.measured = fresh.measured;
|
|
12801
|
+
metrics.offsets = fresh.offsets;
|
|
12802
|
+
metrics.measuredCount = 0;
|
|
12803
|
+
metrics.measuredTotal = 0;
|
|
12804
|
+
metrics.dirtyFrom = Infinity;
|
|
12805
|
+
}
|
|
12806
|
+
function setRowSize(metrics, index, size) {
|
|
12807
|
+
if (index < 0 || index >= metrics.sizes.length) return false;
|
|
12808
|
+
const next = Math.max(1, Math.round(size));
|
|
12809
|
+
const wasMeasured = metrics.measured[index];
|
|
12810
|
+
const prev = metrics.sizes[index];
|
|
12811
|
+
if (wasMeasured && prev === next) return false;
|
|
12812
|
+
if (wasMeasured) {
|
|
12813
|
+
metrics.measuredTotal += next - prev;
|
|
12814
|
+
} else {
|
|
12815
|
+
metrics.measured[index] = true;
|
|
12816
|
+
metrics.measuredCount++;
|
|
12817
|
+
metrics.measuredTotal += next;
|
|
12818
|
+
}
|
|
12819
|
+
metrics.sizes[index] = next;
|
|
12820
|
+
let changedFrom = prev === next ? Infinity : index;
|
|
12821
|
+
const average = Math.max(1, Math.round(metrics.measuredTotal / metrics.measuredCount));
|
|
12822
|
+
if (average !== metrics.estimate) {
|
|
12823
|
+
metrics.estimate = average;
|
|
12824
|
+
for (let i = 0; i < metrics.sizes.length; i++) {
|
|
12825
|
+
if (metrics.measured[i] || metrics.sizes[i] === average) continue;
|
|
12826
|
+
metrics.sizes[i] = average;
|
|
12827
|
+
if (i < changedFrom) changedFrom = i;
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
if (changedFrom === Infinity) return false;
|
|
12831
|
+
markDirty(metrics, changedFrom);
|
|
12832
|
+
return true;
|
|
12833
|
+
}
|
|
12834
|
+
function markDirty(metrics, from) {
|
|
12835
|
+
metrics.dirtyFrom = Math.min(metrics.dirtyFrom, Math.max(0, from));
|
|
12836
|
+
}
|
|
12837
|
+
function rebuildOffsets(metrics) {
|
|
12838
|
+
const count = metrics.sizes.length;
|
|
12839
|
+
const stale = metrics.offsets.length !== count + 1;
|
|
12840
|
+
if (!stale && metrics.dirtyFrom === Infinity) return;
|
|
12841
|
+
const start = stale ? 0 : Math.min(metrics.dirtyFrom, count);
|
|
12842
|
+
const acc0 = start === 0 ? 0 : metrics.offsets[start] ?? 0;
|
|
12843
|
+
metrics.offsets.length = count + 1;
|
|
12844
|
+
let acc = acc0;
|
|
12845
|
+
for (let i = start; i < count; i++) {
|
|
12846
|
+
metrics.offsets[i] = acc;
|
|
12847
|
+
acc += metrics.sizes[i];
|
|
12848
|
+
}
|
|
12849
|
+
metrics.offsets[count] = acc;
|
|
12850
|
+
metrics.dirtyFrom = Infinity;
|
|
12851
|
+
}
|
|
12852
|
+
function totalSize(metrics) {
|
|
12853
|
+
rebuildOffsets(metrics);
|
|
12854
|
+
return metrics.offsets[metrics.sizes.length] ?? 0;
|
|
12855
|
+
}
|
|
12856
|
+
function offsetOf(metrics, index) {
|
|
12857
|
+
rebuildOffsets(metrics);
|
|
12858
|
+
const clamped = Math.max(0, Math.min(Math.floor(index), metrics.sizes.length));
|
|
12859
|
+
return metrics.offsets[clamped] ?? 0;
|
|
12860
|
+
}
|
|
12861
|
+
function sizeOf(metrics, index) {
|
|
12862
|
+
if (index < 0 || index >= metrics.sizes.length) return metrics.estimate;
|
|
12863
|
+
return metrics.sizes[index];
|
|
12864
|
+
}
|
|
12865
|
+
function indexAtOffset(metrics, y) {
|
|
12866
|
+
rebuildOffsets(metrics);
|
|
12867
|
+
const count = metrics.sizes.length;
|
|
12868
|
+
if (count === 0) return 0;
|
|
12869
|
+
const target = Math.max(0, y);
|
|
12870
|
+
let lo = 0;
|
|
12871
|
+
let hi = count - 1;
|
|
12872
|
+
while (lo < hi) {
|
|
12873
|
+
const mid = lo + hi + 1 >> 1;
|
|
12874
|
+
if (metrics.offsets[mid] <= target) lo = mid;
|
|
12875
|
+
else hi = mid - 1;
|
|
12876
|
+
}
|
|
12877
|
+
return lo;
|
|
12878
|
+
}
|
|
12739
12879
|
var SEARCH_THRESHOLD = 11;
|
|
12740
12880
|
var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
12741
12881
|
value,
|
|
@@ -12757,6 +12897,14 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12757
12897
|
dropdownHeight = "640px",
|
|
12758
12898
|
dropdownWidth,
|
|
12759
12899
|
maxDropdownWidth = "640px",
|
|
12900
|
+
virtualBuffer = DEFAULT_VIRTUAL_BUFFER,
|
|
12901
|
+
onReachEnd,
|
|
12902
|
+
reachEndThreshold = DEFAULT_REACH_END_THRESHOLD,
|
|
12903
|
+
hasMore = false,
|
|
12904
|
+
loading: loading2 = false,
|
|
12905
|
+
serverSearch = false,
|
|
12906
|
+
onSearchChange,
|
|
12907
|
+
searchDebounce = DEFAULT_SEARCH_DEBOUNCE,
|
|
12760
12908
|
label,
|
|
12761
12909
|
labelWidth,
|
|
12762
12910
|
icon,
|
|
@@ -12771,6 +12919,9 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12771
12919
|
style
|
|
12772
12920
|
}, ref) {
|
|
12773
12921
|
const multi = type === "multi" || type === "multi_depth";
|
|
12922
|
+
const isDepth = type === "default_depth" || type === "multi_depth";
|
|
12923
|
+
const pagingEnabled = onReachEnd != null;
|
|
12924
|
+
const partialOptions = pagingEnabled || serverSearch;
|
|
12774
12925
|
const portalContainer = usePortalContainer();
|
|
12775
12926
|
const insideModal = useInsideModal();
|
|
12776
12927
|
const [search, setSearch] = react.useState("");
|
|
@@ -12778,6 +12929,15 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12778
12929
|
const [open, setOpen] = react.useState(false);
|
|
12779
12930
|
const [pendingOpen, setPendingOpen] = react.useState(false);
|
|
12780
12931
|
const [focusedIndex, setFocusedIndex] = react.useState(-1);
|
|
12932
|
+
const [win, setWin] = react.useState({ start: 0, end: 0, top: 0, bottom: 0 });
|
|
12933
|
+
const winRef = react.useRef(win);
|
|
12934
|
+
winRef.current = win;
|
|
12935
|
+
const metricsRef = react.useRef(null);
|
|
12936
|
+
metricsRef.current ?? (metricsRef.current = createRowMetrics(0, OPTION_ROW_HEIGHT));
|
|
12937
|
+
const [searchBarHeight, setSearchBarHeight] = react.useState(0);
|
|
12938
|
+
const [selectAllHeight, setSelectAllHeight] = react.useState(0);
|
|
12939
|
+
const measuredKeyRef = react.useRef("");
|
|
12940
|
+
const lastReachEndCountRef = react.useRef(-1);
|
|
12781
12941
|
const triggerRef = react.useRef(null);
|
|
12782
12942
|
const searchInputRef = react.useRef(null);
|
|
12783
12943
|
const searchBarRef = react.useRef(null);
|
|
@@ -12792,7 +12952,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12792
12952
|
const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
12793
12953
|
const allOptionsDisabled = hasLeaf && selectable.length === 0;
|
|
12794
12954
|
const effectiveDisabled = disabled || allOptionsDisabled;
|
|
12795
|
-
const searchVisible = showSearch && selectable.length >= SEARCH_THRESHOLD;
|
|
12955
|
+
const searchVisible = showSearch && (partialOptions || selectable.length >= SEARCH_THRESHOLD);
|
|
12796
12956
|
const labelByValue = react.useMemo(() => new Map(flat.map((o) => [o.value, o.label])), [flat]);
|
|
12797
12957
|
const rawByValue = react.useMemo(() => new Map(flat.map((o) => [o.value, o.raw])), [flat]);
|
|
12798
12958
|
const optionByValue = react.useMemo(
|
|
@@ -12804,12 +12964,26 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12804
12964
|
const arr = Array.isArray(value) ? value : [value];
|
|
12805
12965
|
return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
|
|
12806
12966
|
}, [value]);
|
|
12967
|
+
const valueOptions = react.useMemo(() => {
|
|
12968
|
+
const map = /* @__PURE__ */ new Map();
|
|
12969
|
+
if (value == null) return map;
|
|
12970
|
+
for (const v of Array.isArray(value) ? value : [value]) {
|
|
12971
|
+
if (v != null && typeof v === "object") {
|
|
12972
|
+
map.set(String(v.value), { value: v.value, label: v.label });
|
|
12973
|
+
}
|
|
12974
|
+
}
|
|
12975
|
+
return map;
|
|
12976
|
+
}, [value]);
|
|
12807
12977
|
const selectedRef = react.useRef(selected);
|
|
12808
12978
|
selectedRef.current = selected;
|
|
12979
|
+
const selectedSet = react.useMemo(() => new Set(selected), [selected]);
|
|
12980
|
+
const cbRef = react.useRef({ onReachEnd, onSearchChange });
|
|
12981
|
+
cbRef.current = { onReachEnd, onSearchChange };
|
|
12982
|
+
const emittedQueryRef = react.useRef("");
|
|
12809
12983
|
const emit3 = (next) => {
|
|
12810
12984
|
if (!onValueChange) return;
|
|
12811
12985
|
const mapped = next.map(
|
|
12812
|
-
(v) => valueAsPrimitive ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
12986
|
+
(v) => valueAsPrimitive ? rawByValue.get(v) ?? valueOptions.get(v)?.value ?? v : optionByValue.get(v) ?? valueOptions.get(v) ?? { value: v, label: v }
|
|
12813
12987
|
);
|
|
12814
12988
|
onValueChange(multi ? mapped : mapped[0] ?? (valueAsPrimitive ? "" : null));
|
|
12815
12989
|
};
|
|
@@ -12858,14 +13032,15 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12858
13032
|
resetValidation: () => setRuleError("")
|
|
12859
13033
|
});
|
|
12860
13034
|
const filtered = react.useMemo(() => {
|
|
12861
|
-
if (!searchVisible || search.trim() === "") return flat;
|
|
13035
|
+
if (serverSearch || !searchVisible || search.trim() === "") return flat;
|
|
12862
13036
|
const q = search.trim().toLowerCase();
|
|
12863
13037
|
return flat.filter(
|
|
12864
13038
|
(o) => o.isGroup || (isHtmlLabel(o.label) ? extractText(o.label) : o.label).toLowerCase().includes(q)
|
|
12865
13039
|
);
|
|
12866
|
-
}, [flat, search, searchVisible]);
|
|
13040
|
+
}, [flat, search, searchVisible, serverSearch]);
|
|
12867
13041
|
const allValues = selectable.map((o) => o.value);
|
|
12868
|
-
const allSelected = allValues.length > 0 && allValues.every((v) =>
|
|
13042
|
+
const allSelected = !partialOptions && allValues.length > 0 && allValues.every((v) => selectedSet.has(v));
|
|
13043
|
+
const selectAllVisible = multi && showSelectAll && !partialOptions && allValues.length > 0;
|
|
12869
13044
|
const hasItems = filtered.some((o) => !o.isGroup);
|
|
12870
13045
|
const groupLeafValues = react.useMemo(() => {
|
|
12871
13046
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -12876,7 +13051,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12876
13051
|
}, [filtered]);
|
|
12877
13052
|
const navigableItems = react.useMemo(() => {
|
|
12878
13053
|
const items = [];
|
|
12879
|
-
if (
|
|
13054
|
+
if (selectAllVisible) {
|
|
12880
13055
|
items.push({ key: "__select_all__", isSelectAll: true });
|
|
12881
13056
|
}
|
|
12882
13057
|
filtered.forEach((o, i) => {
|
|
@@ -12891,20 +13066,129 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12891
13066
|
}
|
|
12892
13067
|
});
|
|
12893
13068
|
return items;
|
|
12894
|
-
}, [
|
|
13069
|
+
}, [filtered, groupLeafValues, multi, selectAllVisible]);
|
|
12895
13070
|
const selectedAnchorIndex = react.useMemo(() => {
|
|
12896
|
-
if (
|
|
12897
|
-
const selectedSet = new Set(selected);
|
|
13071
|
+
if (selectedSet.size === 0) return -1;
|
|
12898
13072
|
return navigableItems.findIndex((item4) => item4.value != null && selectedSet.has(item4.value));
|
|
12899
|
-
}, [navigableItems,
|
|
13073
|
+
}, [navigableItems, selectedSet]);
|
|
12900
13074
|
selectedAnchorRef.current = selectedAnchorIndex;
|
|
13075
|
+
const navigableItemsRef = react.useRef(navigableItems);
|
|
13076
|
+
navigableItemsRef.current = navigableItems;
|
|
13077
|
+
const rowCount = filtered.length;
|
|
13078
|
+
const metrics = metricsRef.current;
|
|
13079
|
+
const lastSearchRef = react.useRef(search);
|
|
13080
|
+
if (lastSearchRef.current !== search) {
|
|
13081
|
+
lastSearchRef.current = search;
|
|
13082
|
+
resetRowMetrics(metrics, rowCount);
|
|
13083
|
+
measuredKeyRef.current = "";
|
|
13084
|
+
} else {
|
|
13085
|
+
resizeRowMetrics(metrics, rowCount);
|
|
13086
|
+
}
|
|
13087
|
+
const rowsOffset = searchBarHeight + selectAllHeight;
|
|
13088
|
+
const syncScrollState = react.useCallback(() => {
|
|
13089
|
+
if (!listEl) return;
|
|
13090
|
+
const m = metricsRef.current;
|
|
13091
|
+
if (m == null) return;
|
|
13092
|
+
const buffer = Math.max(0, Math.floor(virtualBuffer));
|
|
13093
|
+
const viewTop = Math.max(0, listEl.scrollTop - rowsOffset);
|
|
13094
|
+
const viewBottom = viewTop + Math.max(0, listEl.clientHeight);
|
|
13095
|
+
const start = Math.max(0, indexAtOffset(m, viewTop) - buffer);
|
|
13096
|
+
const end = Math.min(rowCount, indexAtOffset(m, viewBottom) + 1 + buffer);
|
|
13097
|
+
const top = offsetOf(m, start);
|
|
13098
|
+
const bottom = Math.max(0, totalSize(m) - offsetOf(m, end));
|
|
13099
|
+
setWin(
|
|
13100
|
+
(prev) => prev.start === start && prev.end === end && prev.top === top && prev.bottom === bottom ? prev : { start, end, top, bottom }
|
|
13101
|
+
);
|
|
13102
|
+
if (!pagingEnabled || !hasMore || loading2) return;
|
|
13103
|
+
const contentHeight = rowsOffset + totalSize(m);
|
|
13104
|
+
const remaining = contentHeight - listEl.scrollTop - listEl.clientHeight;
|
|
13105
|
+
if (remaining > Math.max(1, reachEndThreshold) * m.estimate) return;
|
|
13106
|
+
if (lastReachEndCountRef.current === rowCount) return;
|
|
13107
|
+
lastReachEndCountRef.current = rowCount;
|
|
13108
|
+
cbRef.current.onReachEnd?.();
|
|
13109
|
+
}, [
|
|
13110
|
+
hasMore,
|
|
13111
|
+
listEl,
|
|
13112
|
+
loading2,
|
|
13113
|
+
pagingEnabled,
|
|
13114
|
+
reachEndThreshold,
|
|
13115
|
+
rowCount,
|
|
13116
|
+
rowsOffset,
|
|
13117
|
+
virtualBuffer
|
|
13118
|
+
]);
|
|
13119
|
+
const syncScrollStateRef = react.useRef(syncScrollState);
|
|
13120
|
+
syncScrollStateRef.current = syncScrollState;
|
|
13121
|
+
react.useLayoutEffect(() => {
|
|
13122
|
+
if (!open) return;
|
|
13123
|
+
syncScrollState();
|
|
13124
|
+
}, [open, syncScrollState]);
|
|
13125
|
+
react.useLayoutEffect(() => {
|
|
13126
|
+
if (!open || !listEl) {
|
|
13127
|
+
setSearchBarHeight(0);
|
|
13128
|
+
setSelectAllHeight(0);
|
|
13129
|
+
return;
|
|
13130
|
+
}
|
|
13131
|
+
const nextBar = searchVisible ? searchBarRef.current?.offsetHeight ?? 0 : 0;
|
|
13132
|
+
setSearchBarHeight((prev) => prev === nextBar ? prev : nextBar);
|
|
13133
|
+
const nextSelectAll = selectAllVisible ? listEl.querySelector("[data-select-all-row]")?.offsetHeight ?? 0 : 0;
|
|
13134
|
+
setSelectAllHeight((prev) => prev === nextSelectAll ? prev : nextSelectAll);
|
|
13135
|
+
}, [listEl, open, searchVisible, selectAllVisible]);
|
|
13136
|
+
react.useLayoutEffect(() => {
|
|
13137
|
+
if (!open || !listEl) return;
|
|
13138
|
+
const m = metricsRef.current;
|
|
13139
|
+
if (m == null) return;
|
|
13140
|
+
const key = `${winRef.current.start}:${winRef.current.end}:${rowCount}`;
|
|
13141
|
+
if (measuredKeyRef.current === key) return;
|
|
13142
|
+
measuredKeyRef.current = key;
|
|
13143
|
+
const anchor = winRef.current.start;
|
|
13144
|
+
const anchorBefore = offsetOf(m, anchor);
|
|
13145
|
+
let changed = false;
|
|
13146
|
+
for (const row of listEl.querySelectorAll("[data-select-row]")) {
|
|
13147
|
+
const index = Number(row.dataset.index);
|
|
13148
|
+
if (!Number.isInteger(index)) continue;
|
|
13149
|
+
if (setRowSize(m, index, row.offsetHeight)) changed = true;
|
|
13150
|
+
}
|
|
13151
|
+
if (!changed) return;
|
|
13152
|
+
const delta = offsetOf(m, anchor) - anchorBefore;
|
|
13153
|
+
if (delta !== 0 && listEl.scrollTop > 0) listEl.scrollTop += delta;
|
|
13154
|
+
syncScrollStateRef.current();
|
|
13155
|
+
});
|
|
13156
|
+
react.useEffect(() => {
|
|
13157
|
+
if (cbRef.current.onSearchChange == null) return;
|
|
13158
|
+
if (search === emittedQueryRef.current) return;
|
|
13159
|
+
if (!serverSearch) {
|
|
13160
|
+
emittedQueryRef.current = search;
|
|
13161
|
+
cbRef.current.onSearchChange(search);
|
|
13162
|
+
return;
|
|
13163
|
+
}
|
|
13164
|
+
const timer = setTimeout(
|
|
13165
|
+
() => {
|
|
13166
|
+
emittedQueryRef.current = search;
|
|
13167
|
+
cbRef.current.onSearchChange?.(search);
|
|
13168
|
+
},
|
|
13169
|
+
Math.max(0, searchDebounce)
|
|
13170
|
+
);
|
|
13171
|
+
return () => clearTimeout(timer);
|
|
13172
|
+
}, [search, searchDebounce, serverSearch]);
|
|
13173
|
+
const selectAllMisuse = multi && showSelectAll && partialOptions;
|
|
13174
|
+
react.useEffect(() => {
|
|
13175
|
+
if (!isDev()) return;
|
|
13176
|
+
if (selectAllMisuse) {
|
|
13177
|
+
console.warn(
|
|
13178
|
+
'[SSelect] onReachEnd\xB7serverSearch \uC640 \uD568\uAED8 \uC900 showSelectAll \uC744 \uBB34\uC2DC\uD569\uB2C8\uB2E4. options \uAC00 \uBAA9\uB85D\uC758 \uC870\uAC01\uC77C \uBFD0\uC774\uB77C \uC544\uC9C1 \uBC1B\uC9C0 \uC54A\uC740 \uC635\uC158\uC740 \uACE0\uB97C \uC218 \uC5C6\uACE0, \uBC1B\uC544 \uC628 \uB9CC\uD07C\uB9CC \uACE0\uB978 \uC0C1\uD0DC\uB97C "\uC804\uCCB4"\uB77C\uACE0 \uD45C\uC2DC\uD558\uBA74 \uAC70\uC9D3\uC774 \uB429\uB2C8\uB2E4.'
|
|
13179
|
+
);
|
|
13180
|
+
}
|
|
13181
|
+
}, [selectAllMisuse]);
|
|
13182
|
+
const windowStart = Math.min(win.start, rowCount);
|
|
13183
|
+
const windowEnd = Math.max(windowStart, Math.min(win.end, rowCount));
|
|
13184
|
+
const visibleItems = filtered.slice(windowStart, windowEnd);
|
|
12901
13185
|
const triggerText = (() => {
|
|
12902
13186
|
if (selected.length === 0) return placeholder;
|
|
12903
13187
|
if (multi) {
|
|
12904
13188
|
if (allSelected) return allSelectedLabel;
|
|
12905
|
-
return selected.map((v) => labelByValue.get(v) ?? v).filter(Boolean).join(", ");
|
|
13189
|
+
return selected.map((v) => labelByValue.get(v) ?? valueOptions.get(v)?.label ?? v).filter(Boolean).join(", ");
|
|
12906
13190
|
}
|
|
12907
|
-
return labelByValue.get(selected[0]) ?? selected[0];
|
|
13191
|
+
return labelByValue.get(selected[0]) ?? valueOptions.get(selected[0])?.label ?? selected[0];
|
|
12908
13192
|
})();
|
|
12909
13193
|
const isPlaceholder = selected.length === 0;
|
|
12910
13194
|
const showClear = clearable && !effectiveDisabled && !isPlaceholder;
|
|
@@ -12917,7 +13201,6 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12917
13201
|
const anchor = rangeAnchorRef.current;
|
|
12918
13202
|
if (extend && anchor != null && anchor !== index && filtered[anchor]?.isGroup === false) {
|
|
12919
13203
|
const [from, to] = anchor < index ? [anchor, index] : [index, anchor];
|
|
12920
|
-
const selectedSet = new Set(selected);
|
|
12921
13204
|
const shouldSelect = selectedSet.has(filtered[anchor].value);
|
|
12922
13205
|
const rangeValues = filtered.slice(from, to + 1).filter((o) => !o.isGroup && !o.disabled).map((o) => o.value);
|
|
12923
13206
|
const inRange = new Set(rangeValues);
|
|
@@ -12942,7 +13225,6 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12942
13225
|
const handleToggleGroup = (leafValues) => {
|
|
12943
13226
|
if (leafValues.length === 0) return;
|
|
12944
13227
|
rangeAnchorRef.current = null;
|
|
12945
|
-
const selectedSet = new Set(selected);
|
|
12946
13228
|
const allSelectedInGroup = leafValues.every((v) => selectedSet.has(v));
|
|
12947
13229
|
if (allSelectedInGroup) {
|
|
12948
13230
|
const remove = new Set(leafValues);
|
|
@@ -12951,35 +13233,51 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
12951
13233
|
emit3([...selected, ...leafValues.filter((v) => !selectedSet.has(v))]);
|
|
12952
13234
|
}
|
|
12953
13235
|
};
|
|
13236
|
+
const hasNavigableItems = navigableItems.length > 0;
|
|
12954
13237
|
react.useEffect(() => {
|
|
12955
|
-
if (!open ||
|
|
13238
|
+
if (!open || !hasNavigableItems) {
|
|
12956
13239
|
setFocusedIndex(-1);
|
|
12957
13240
|
return;
|
|
12958
13241
|
}
|
|
12959
13242
|
const anchor = alignSelectedTopRef.current ? selectedAnchorRef.current : -1;
|
|
12960
13243
|
setFocusedIndex(anchor >= 0 ? anchor : 0);
|
|
12961
|
-
}, [open, search,
|
|
13244
|
+
}, [open, search, hasNavigableItems]);
|
|
13245
|
+
const lastResetSearchRef = react.useRef(search);
|
|
13246
|
+
react.useLayoutEffect(() => {
|
|
13247
|
+
if (lastResetSearchRef.current === search) return;
|
|
13248
|
+
lastResetSearchRef.current = search;
|
|
13249
|
+
if (!open || !listEl) return;
|
|
13250
|
+
listEl.scrollTop = 0;
|
|
13251
|
+
syncScrollStateRef.current();
|
|
13252
|
+
}, [listEl, open, search]);
|
|
12962
13253
|
react.useEffect(() => {
|
|
12963
13254
|
rangeAnchorRef.current = null;
|
|
13255
|
+
lastReachEndCountRef.current = -1;
|
|
12964
13256
|
}, [open, search]);
|
|
12965
13257
|
react.useLayoutEffect(() => {
|
|
12966
13258
|
if (!open || focusedIndex < 0 || !listEl) return;
|
|
12967
|
-
const
|
|
12968
|
-
if (
|
|
12969
|
-
const
|
|
12970
|
-
const
|
|
12971
|
-
if (
|
|
13259
|
+
const m = metricsRef.current;
|
|
13260
|
+
if (m == null) return;
|
|
13261
|
+
const stickyOffset = searchBarRef.current?.offsetHeight ?? 0;
|
|
13262
|
+
const rowIndex = navigableItemsRef.current[focusedIndex]?.index;
|
|
13263
|
+
if (rowIndex == null) {
|
|
12972
13264
|
alignSelectedTopRef.current = false;
|
|
12973
|
-
|
|
12974
|
-
|
|
13265
|
+
listEl.scrollTop = 0;
|
|
13266
|
+
syncScrollStateRef.current();
|
|
12975
13267
|
return;
|
|
12976
13268
|
}
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
13269
|
+
const top = rowsOffset + offsetOf(m, rowIndex);
|
|
13270
|
+
const height = sizeOf(m, rowIndex);
|
|
13271
|
+
if (alignSelectedTopRef.current) {
|
|
13272
|
+
alignSelectedTopRef.current = false;
|
|
13273
|
+
listEl.scrollTop = top - stickyOffset;
|
|
13274
|
+
} else if (top < listEl.scrollTop + stickyOffset) {
|
|
13275
|
+
listEl.scrollTop = top - stickyOffset;
|
|
13276
|
+
} else if (top + height > listEl.scrollTop + listEl.clientHeight) {
|
|
13277
|
+
listEl.scrollTop = top + height - listEl.clientHeight;
|
|
13278
|
+
}
|
|
13279
|
+
syncScrollStateRef.current();
|
|
13280
|
+
}, [focusedIndex, listEl, open, rowsOffset]);
|
|
12983
13281
|
const moveFocusedIndex = (direction) => {
|
|
12984
13282
|
if (!open || navigableItems.length === 0) return;
|
|
12985
13283
|
setFocusedIndex((current) => {
|
|
@@ -13169,6 +13467,7 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
13169
13467
|
SCROLLBAR_TRACK_BORDER_CLASS
|
|
13170
13468
|
),
|
|
13171
13469
|
style: { maxHeight: clampToAvailableHeight(dropdownHeight, POPOVER_AVAILABLE_HEIGHT) },
|
|
13470
|
+
onScroll: syncScrollState,
|
|
13172
13471
|
onMouseLeave: () => setFocusedIndex(-1),
|
|
13173
13472
|
children: [
|
|
13174
13473
|
searchVisible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -13192,12 +13491,16 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
13192
13491
|
hasItems ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
13193
13492
|
ItemList,
|
|
13194
13493
|
{
|
|
13195
|
-
items:
|
|
13494
|
+
items: visibleItems,
|
|
13495
|
+
indexOffset: windowStart,
|
|
13496
|
+
topSpacer: win.top,
|
|
13497
|
+
bottomSpacer: win.bottom,
|
|
13498
|
+
setSize: isDepth ? void 0 : rowCount,
|
|
13196
13499
|
multi,
|
|
13197
13500
|
selected,
|
|
13198
13501
|
groupLeafValues,
|
|
13199
13502
|
focusedValue: navigableItems[focusedIndex]?.key,
|
|
13200
|
-
showSelectAll:
|
|
13503
|
+
showSelectAll: selectAllVisible,
|
|
13201
13504
|
selectAllLabel: allSelectedLabel,
|
|
13202
13505
|
selectAllSelected: allSelected,
|
|
13203
13506
|
onSelectAll: handleSelectAll,
|
|
@@ -13212,7 +13515,18 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
13212
13515
|
if (index >= 0) setFocusedIndex(index);
|
|
13213
13516
|
}
|
|
13214
13517
|
}
|
|
13215
|
-
) :
|
|
13518
|
+
) : loading2 ? null : (
|
|
13519
|
+
// 첫 페이지를 받는 중에는 "없다"고 단정하지 않는다 — 아래 로딩 표시가 대신 나온다.
|
|
13520
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listBoxItem-content-disabled)]", children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })
|
|
13521
|
+
),
|
|
13522
|
+
loading2 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13523
|
+
"div",
|
|
13524
|
+
{
|
|
13525
|
+
role: "status",
|
|
13526
|
+
className: "px-[12px] py-[12px] text-center text-[12px] leading-[20px] text-[color:var(--cmp-listBoxItem-content-disabled)]",
|
|
13527
|
+
children: "\uBD88\uB7EC\uC624\uB294 \uC911..."
|
|
13528
|
+
}
|
|
13529
|
+
)
|
|
13216
13530
|
]
|
|
13217
13531
|
}
|
|
13218
13532
|
)
|
|
@@ -13225,6 +13539,10 @@ var SSelectImpl = /* @__PURE__ */ react.forwardRef(function SSelect({
|
|
|
13225
13539
|
var SSelect2 = /* @__PURE__ */ withDisplayName(SSelectImpl, "SSelect");
|
|
13226
13540
|
function ItemList({
|
|
13227
13541
|
items,
|
|
13542
|
+
indexOffset,
|
|
13543
|
+
topSpacer,
|
|
13544
|
+
bottomSpacer,
|
|
13545
|
+
setSize,
|
|
13228
13546
|
multi,
|
|
13229
13547
|
selected,
|
|
13230
13548
|
groupLeafValues,
|
|
@@ -13249,6 +13567,7 @@ function ItemList({
|
|
|
13249
13567
|
"button",
|
|
13250
13568
|
{
|
|
13251
13569
|
type: "button",
|
|
13570
|
+
"data-select-all-row": true,
|
|
13252
13571
|
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
13253
13572
|
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
13254
13573
|
className: cn(
|
|
@@ -13265,7 +13584,9 @@ function ItemList({
|
|
|
13265
13584
|
]
|
|
13266
13585
|
}
|
|
13267
13586
|
),
|
|
13268
|
-
|
|
13587
|
+
topSpacer > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, style: { height: topSpacer } }),
|
|
13588
|
+
items.map((o, windowIndex) => {
|
|
13589
|
+
const idx = windowIndex + indexOffset;
|
|
13269
13590
|
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
13270
13591
|
const rowKey = `${o.value}-${idx}`;
|
|
13271
13592
|
if (o.isGroup) {
|
|
@@ -13282,6 +13603,8 @@ function ItemList({
|
|
|
13282
13603
|
"button",
|
|
13283
13604
|
{
|
|
13284
13605
|
type: "button",
|
|
13606
|
+
"data-select-row": true,
|
|
13607
|
+
"data-index": idx,
|
|
13285
13608
|
role: "option",
|
|
13286
13609
|
"aria-selected": groupState === true,
|
|
13287
13610
|
disabled: groupDisabled,
|
|
@@ -13332,6 +13655,8 @@ function ItemList({
|
|
|
13332
13655
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13333
13656
|
"div",
|
|
13334
13657
|
{
|
|
13658
|
+
"data-select-row": true,
|
|
13659
|
+
"data-index": idx,
|
|
13335
13660
|
className: cn(
|
|
13336
13661
|
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listBoxItem-content-default)]",
|
|
13337
13662
|
groupTone,
|
|
@@ -13351,9 +13676,13 @@ function ItemList({
|
|
|
13351
13676
|
"button",
|
|
13352
13677
|
{
|
|
13353
13678
|
type: "button",
|
|
13679
|
+
"data-select-row": true,
|
|
13680
|
+
"data-index": idx,
|
|
13354
13681
|
disabled: o.disabled,
|
|
13355
13682
|
role: "option",
|
|
13356
13683
|
"aria-selected": isSelected,
|
|
13684
|
+
"aria-setsize": setSize,
|
|
13685
|
+
"aria-posinset": setSize != null ? idx + 1 : void 0,
|
|
13357
13686
|
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
13358
13687
|
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
13359
13688
|
className: cn(
|
|
@@ -13388,7 +13717,8 @@ function ItemList({
|
|
|
13388
13717
|
},
|
|
13389
13718
|
rowKey
|
|
13390
13719
|
);
|
|
13391
|
-
})
|
|
13720
|
+
}),
|
|
13721
|
+
bottomSpacer > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, style: { height: bottomSpacer } })
|
|
13392
13722
|
] });
|
|
13393
13723
|
}
|
|
13394
13724
|
function CheckboxBox({
|
|
@@ -14549,12 +14879,29 @@ var SInput = /* @__PURE__ */ react.forwardRef(function SInput2({
|
|
|
14549
14879
|
}
|
|
14550
14880
|
);
|
|
14551
14881
|
});
|
|
14552
|
-
var
|
|
14882
|
+
var TEXTAREA_SIZE = {
|
|
14883
|
+
sm: {
|
|
14884
|
+
fontSize: 12,
|
|
14885
|
+
lineHeight: 20,
|
|
14886
|
+
paddingX: "var(--cmp-textinput-sm-paddingX)",
|
|
14887
|
+
paddingY: "var(--cmp-textinput-sm-paddingY)"
|
|
14888
|
+
},
|
|
14889
|
+
md: {
|
|
14890
|
+
fontSize: 14,
|
|
14891
|
+
lineHeight: 24,
|
|
14892
|
+
paddingX: "var(--cmp-textinput-md-paddingX)",
|
|
14893
|
+
paddingY: "var(--cmp-textinput-md-paddingY)"
|
|
14894
|
+
}
|
|
14895
|
+
};
|
|
14896
|
+
var MIN_ROWS = 2;
|
|
14553
14897
|
var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
|
|
14554
14898
|
value,
|
|
14555
14899
|
onValueChange,
|
|
14556
14900
|
onChange,
|
|
14557
|
-
|
|
14901
|
+
size = "sm",
|
|
14902
|
+
rows = MIN_ROWS,
|
|
14903
|
+
autogrow = false,
|
|
14904
|
+
maxRows,
|
|
14558
14905
|
rules,
|
|
14559
14906
|
status,
|
|
14560
14907
|
focused,
|
|
@@ -14581,8 +14928,30 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
|
|
|
14581
14928
|
style,
|
|
14582
14929
|
...rest
|
|
14583
14930
|
}, ref) {
|
|
14584
|
-
const p =
|
|
14931
|
+
const p = TEXTAREA_SIZE[size];
|
|
14932
|
+
const heightForRows = (n) => `calc(${p.lineHeight * n}px + ${p.paddingY} * 2)`;
|
|
14933
|
+
const minHeight = heightForRows(MIN_ROWS);
|
|
14934
|
+
const maxHeight = autogrow && maxRows != null ? heightForRows(maxRows) : void 0;
|
|
14585
14935
|
const [ruleError, setRuleError] = react.useState("");
|
|
14936
|
+
const innerRef = react.useRef(null);
|
|
14937
|
+
const setRefs2 = react.useCallback(
|
|
14938
|
+
(node) => {
|
|
14939
|
+
innerRef.current = node;
|
|
14940
|
+
if (typeof ref === "function") ref(node);
|
|
14941
|
+
else if (ref) ref.current = node;
|
|
14942
|
+
},
|
|
14943
|
+
[ref]
|
|
14944
|
+
);
|
|
14945
|
+
react.useLayoutEffect(() => {
|
|
14946
|
+
const el = innerRef.current;
|
|
14947
|
+
if (el == null) return;
|
|
14948
|
+
if (!autogrow) {
|
|
14949
|
+
el.style.height = "";
|
|
14950
|
+
return;
|
|
14951
|
+
}
|
|
14952
|
+
el.style.height = "auto";
|
|
14953
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
14954
|
+
}, [autogrow, maxHeight, minHeight, rows, value]);
|
|
14586
14955
|
useFormField({
|
|
14587
14956
|
name: rest.name,
|
|
14588
14957
|
validate: () => {
|
|
@@ -14607,6 +14976,7 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
|
|
|
14607
14976
|
addonLabel,
|
|
14608
14977
|
addonAlign,
|
|
14609
14978
|
hint,
|
|
14979
|
+
size,
|
|
14610
14980
|
error: resolvedError,
|
|
14611
14981
|
status: resolvedStatus,
|
|
14612
14982
|
focused,
|
|
@@ -14621,7 +14991,7 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
|
|
|
14621
14991
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14622
14992
|
"textarea",
|
|
14623
14993
|
{
|
|
14624
|
-
ref,
|
|
14994
|
+
ref: setRefs2,
|
|
14625
14995
|
value: value ?? "",
|
|
14626
14996
|
rows,
|
|
14627
14997
|
disabled,
|
|
@@ -14637,15 +15007,21 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
|
|
|
14637
15007
|
if (rules && rules.length > 0) setRuleError(runRules(e.target.value, rules));
|
|
14638
15008
|
},
|
|
14639
15009
|
className: cn(
|
|
14640
|
-
"w-full
|
|
15010
|
+
"w-full border-0 bg-transparent outline-none placeholder:text-[color:var(--sys-color-field-text-placeholder)] disabled:cursor-not-allowed",
|
|
15011
|
+
// autogrow 와 끌어서 늘리기는 같은 높이를 두고 다투므로 함께 두지 않는다
|
|
15012
|
+
autogrow ? "resize-none" : "resize-y",
|
|
14641
15013
|
AUTOFILL_RESET_CLASS,
|
|
14642
15014
|
textareaClassName
|
|
14643
15015
|
),
|
|
14644
15016
|
style: {
|
|
14645
|
-
minHeight
|
|
14646
|
-
|
|
15017
|
+
minHeight,
|
|
15018
|
+
maxHeight,
|
|
15019
|
+
// 상한이 있으면 거기 닿았을 때 안에서 스크롤한다. 상한이 없으면 높이가 늘 내용과
|
|
15020
|
+
// 같으므로 스크롤바가 나올 일이 없고, 반올림으로 1px 이 넘쳐 깜빡이는 것만 막는다.
|
|
15021
|
+
overflowY: autogrow ? maxRows != null ? "auto" : "hidden" : void 0,
|
|
15022
|
+
padding: `${p.paddingY} ${p.paddingX}`,
|
|
14647
15023
|
fontSize: p.fontSize,
|
|
14648
|
-
lineHeight:
|
|
15024
|
+
lineHeight: `${p.lineHeight}px`,
|
|
14649
15025
|
color: disabled ? "var(--cmp-textinput-text-disabled)" : "var(--sys-color-field-text-default)",
|
|
14650
15026
|
...textareaStyle
|
|
14651
15027
|
},
|
|
@@ -14655,7 +15031,7 @@ var STextarea = /* @__PURE__ */ react.forwardRef(function STextarea2({
|
|
|
14655
15031
|
}
|
|
14656
15032
|
);
|
|
14657
15033
|
});
|
|
14658
|
-
var
|
|
15034
|
+
var PAD2 = {
|
|
14659
15035
|
sm: { fontSize: 12, lineHeight: 20, stepper: 20, icon: 12 },
|
|
14660
15036
|
md: { fontSize: 14, lineHeight: 24, stepper: 24, icon: 14 }
|
|
14661
15037
|
};
|
|
@@ -14684,6 +15060,16 @@ var formatInputWithCommas = (value) => {
|
|
|
14684
15060
|
const formattedInt = addCommas(intPart === "" ? "0" : intPart);
|
|
14685
15061
|
return `${isNeg ? "-" : ""}${formattedInt}${hasDecimal ? `.${decPart}` : ""}`;
|
|
14686
15062
|
};
|
|
15063
|
+
var getRangeError = (value, min, max) => {
|
|
15064
|
+
if (value === null) return "";
|
|
15065
|
+
const belowMin = value < min;
|
|
15066
|
+
const aboveMax = value > max;
|
|
15067
|
+
if (!belowMin && !aboveMax) return "";
|
|
15068
|
+
if (Number.isFinite(min) && Number.isFinite(max)) {
|
|
15069
|
+
return `${formatWithCommas(min)}~${formatWithCommas(max)} \uC0AC\uC774\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.`;
|
|
15070
|
+
}
|
|
15071
|
+
return belowMin ? `${formatWithCommas(min)} \uC774\uC0C1\uC73C\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.` : `${formatWithCommas(max)} \uC774\uD558\uB85C \uC785\uB825\uD574 \uC8FC\uC138\uC694.`;
|
|
15072
|
+
};
|
|
14687
15073
|
var getDecimalLimit = (allowDecimal, maxDecimalLength) => {
|
|
14688
15074
|
if (!allowDecimal || maxDecimalLength === void 0 || !Number.isFinite(maxDecimalLength)) {
|
|
14689
15075
|
return void 0;
|
|
@@ -14714,6 +15100,7 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
|
|
|
14714
15100
|
min = Number.NEGATIVE_INFINITY,
|
|
14715
15101
|
max = Number.POSITIVE_INFINITY,
|
|
14716
15102
|
step = 1,
|
|
15103
|
+
clampOnBlur = false,
|
|
14717
15104
|
showButton = false,
|
|
14718
15105
|
allowDecimal = false,
|
|
14719
15106
|
maxDecimalLength,
|
|
@@ -14747,12 +15134,13 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
|
|
|
14747
15134
|
className,
|
|
14748
15135
|
style
|
|
14749
15136
|
}, ref) {
|
|
14750
|
-
const p =
|
|
15137
|
+
const p = PAD2[size];
|
|
14751
15138
|
const padX = `var(--cmp-numberInput-${size}-paddingX)`;
|
|
14752
15139
|
const decimalLimit = getDecimalLimit(allowDecimal, maxDecimalLength);
|
|
14753
15140
|
const numValue = applyDecimalLimit(toNumber(value), decimalLimit);
|
|
14754
15141
|
const [display, setDisplay] = react.useState(() => formatWithCommas(numValue));
|
|
14755
15142
|
const [ruleError, setRuleError] = react.useState("");
|
|
15143
|
+
const [rangeError, setRangeError] = react.useState("");
|
|
14756
15144
|
const innerRef = react.useRef(null);
|
|
14757
15145
|
const valueTooltip = useTruncatedValueTooltip({ enabled: readOnly && !disabled, text: display });
|
|
14758
15146
|
const setRefs2 = (el) => {
|
|
@@ -14770,12 +15158,19 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
|
|
|
14770
15158
|
validate: () => {
|
|
14771
15159
|
const msg = runRules(numValue, rules);
|
|
14772
15160
|
setRuleError(msg);
|
|
14773
|
-
|
|
15161
|
+
const range = clampOnBlur ? "" : getRangeError(numValue, min, max);
|
|
15162
|
+
setRangeError(range);
|
|
15163
|
+
return msg === "" && range === "";
|
|
14774
15164
|
},
|
|
14775
|
-
resetValidation: () =>
|
|
15165
|
+
resetValidation: () => {
|
|
15166
|
+
setRuleError("");
|
|
15167
|
+
setRangeError("");
|
|
15168
|
+
}
|
|
14776
15169
|
});
|
|
14777
|
-
const
|
|
14778
|
-
const
|
|
15170
|
+
const autoError = ruleError !== "" ? ruleError : rangeError;
|
|
15171
|
+
const resolvedError = error || autoError !== "";
|
|
15172
|
+
const resolvedStatus = status ?? (autoError !== "" ? "error" : void 0);
|
|
15173
|
+
const resolvedErrorMessage = ruleError !== "" ? ruleError : (errorMessage ?? "") !== "" ? errorMessage : rangeError;
|
|
14779
15174
|
const restoreCaret = (nextDisplay, logicalCaret) => {
|
|
14780
15175
|
const input = innerRef.current;
|
|
14781
15176
|
if (logicalCaret === void 0 || !input || document.activeElement !== input) return;
|
|
@@ -14907,7 +15302,7 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
|
|
|
14907
15302
|
status: resolvedStatus,
|
|
14908
15303
|
focused,
|
|
14909
15304
|
hovered,
|
|
14910
|
-
errorMessage:
|
|
15305
|
+
errorMessage: resolvedErrorMessage,
|
|
14911
15306
|
width,
|
|
14912
15307
|
disabled,
|
|
14913
15308
|
readOnly,
|
|
@@ -14953,10 +15348,11 @@ var SNumberInput = /* @__PURE__ */ react.forwardRef(function SNumberInput2({
|
|
|
14953
15348
|
onBlur: (e) => {
|
|
14954
15349
|
const cleaned = display.replace(/,/g, "");
|
|
14955
15350
|
const n = toNumber(cleaned);
|
|
14956
|
-
const clamped = n === null ?
|
|
15351
|
+
const clamped = !clampOnBlur || n === null ? n : Math.min(Math.max(n, min), max);
|
|
14957
15352
|
if (clamped !== n) emit3(clamped);
|
|
14958
15353
|
else setDisplay(n === null ? "" : formatInputWithCommas(cleaned));
|
|
14959
15354
|
if (rules && rules.length > 0) setRuleError(runRules(clamped, rules));
|
|
15355
|
+
setRangeError(clampOnBlur ? "" : getRangeError(clamped, min, max));
|
|
14960
15356
|
onBlur?.(e);
|
|
14961
15357
|
},
|
|
14962
15358
|
className: cn(
|