@sia.soul/sia-react-ui 0.1.39 → 0.1.40
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/dist/{chunk-GLS4WVIZ.js → chunk-6TLANQVX.js} +1 -1
- package/dist/{chunk-WR7XTQRU.js → chunk-CEZ4GW3X.js} +96 -50
- package/dist/{chunk-LRGD2QDA.js → chunk-ZTJUFVJO.js} +820 -820
- package/dist/{core-D4opRFkZ.d.cts → core-BAHoW7_e.d.cts} +2 -2
- package/dist/{core-VNY_7WRq.d.ts → core-fn8h4yRg.d.ts} +2 -2
- package/dist/core.cjs +996 -949
- package/dist/core.css +46 -18
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +2 -2
- package/dist/index.cjs +534 -487
- package/dist/index.css +46 -18
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/select-date-range.js +2 -2
- package/package.json +1 -1
|
@@ -15,532 +15,157 @@ import {
|
|
|
15
15
|
withTitleTooltip
|
|
16
16
|
} from "./chunk-H7IAG72O.js";
|
|
17
17
|
|
|
18
|
-
// src/components/
|
|
19
|
-
import { useEffect
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
// src/components/Checkbox.tsx
|
|
19
|
+
import { createContext, forwardRef, useContext, useEffect, useId, useMemo, useRef } from "react";
|
|
20
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var CheckboxGroupContext = createContext(null);
|
|
22
|
+
var CheckboxRoot = forwardRef(function Checkbox({ value, size: sizeProp, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
|
|
23
|
+
const size = useControlSize(sizeProp);
|
|
24
|
+
const group = useContext(CheckboxGroupContext);
|
|
25
|
+
const localRef = useRef(null);
|
|
26
|
+
const fallbackId = useId();
|
|
27
|
+
const grouped = group !== null && value !== void 0;
|
|
28
|
+
const currentChecked = grouped ? group.values.includes(value) : checked;
|
|
26
29
|
useEffect(() => {
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
};
|
|
53
|
-
const move = (event) => {
|
|
54
|
-
if (event.pointerType === "touch") return;
|
|
55
|
-
x = event.clientX;
|
|
56
|
-
y = event.clientY;
|
|
57
|
-
if (!frame) frame = requestAnimationFrame(update);
|
|
58
|
-
};
|
|
59
|
-
const leave = () => {
|
|
60
|
-
x = y = -Infinity;
|
|
61
|
-
clear();
|
|
62
|
-
};
|
|
63
|
-
document.addEventListener("pointermove", move, { passive: true });
|
|
64
|
-
document.documentElement.addEventListener("pointerleave", leave);
|
|
65
|
-
window.addEventListener("blur", leave);
|
|
66
|
-
cleanup = () => {
|
|
67
|
-
if (frame) cancelAnimationFrame(frame);
|
|
68
|
-
document.removeEventListener("pointermove", move);
|
|
69
|
-
document.documentElement.removeEventListener("pointerleave", leave);
|
|
70
|
-
window.removeEventListener("blur", leave);
|
|
71
|
-
clear();
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
return () => {
|
|
75
|
-
if (--users === 0) {
|
|
76
|
-
cleanup?.();
|
|
77
|
-
cleanup = void 0;
|
|
30
|
+
if (localRef.current) localRef.current.indeterminate = indeterminate;
|
|
31
|
+
}, [indeterminate]);
|
|
32
|
+
function setRef(node) {
|
|
33
|
+
localRef.current = node;
|
|
34
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
35
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
36
|
+
}
|
|
37
|
+
return /* @__PURE__ */ jsxs("label", { className: `sia-checkbox sia-checkbox--${size}${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
|
|
38
|
+
/* @__PURE__ */ jsx(
|
|
39
|
+
"input",
|
|
40
|
+
{
|
|
41
|
+
...withTitleTooltip(props),
|
|
42
|
+
ref: setRef,
|
|
43
|
+
id: props.id ?? fallbackId,
|
|
44
|
+
className: "sia-checkbox__input",
|
|
45
|
+
type: "checkbox",
|
|
46
|
+
name: group?.name ?? props.name,
|
|
47
|
+
disabled: disabled || group?.disabled,
|
|
48
|
+
checked: currentChecked,
|
|
49
|
+
defaultChecked: grouped ? void 0 : defaultChecked,
|
|
50
|
+
"aria-checked": indeterminate ? "mixed" : currentChecked,
|
|
51
|
+
onChange: (event) => {
|
|
52
|
+
if (grouped) group.toggle(value, event.currentTarget.checked);
|
|
53
|
+
onChange?.(event.currentTarget.checked, event);
|
|
54
|
+
}
|
|
78
55
|
}
|
|
79
|
-
|
|
80
|
-
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", {}) }),
|
|
58
|
+
children !== void 0 ? /* @__PURE__ */ jsx("span", { className: "sia-checkbox__label", children }) : null
|
|
59
|
+
] });
|
|
60
|
+
});
|
|
61
|
+
function CheckboxGroup({
|
|
62
|
+
size: sizeProp,
|
|
63
|
+
columns,
|
|
64
|
+
direction = "horizontal",
|
|
65
|
+
gap,
|
|
66
|
+
value,
|
|
67
|
+
defaultValue = [],
|
|
68
|
+
options,
|
|
69
|
+
disabled,
|
|
70
|
+
name,
|
|
71
|
+
className = "",
|
|
72
|
+
children,
|
|
73
|
+
onChange
|
|
74
|
+
}) {
|
|
75
|
+
const size = useControlSize(sizeProp);
|
|
76
|
+
const gridColumns = columns !== void 0 && Number.isInteger(columns) && columns > 0 ? columns : void 0;
|
|
77
|
+
const layoutStyle = {
|
|
78
|
+
gridTemplateColumns: gridColumns ? `repeat(${gridColumns}, minmax(0, 1fr))` : void 0,
|
|
79
|
+
gap: gap === void 0 ? void 0 : typeof gap === "number" ? Math.max(0, gap) : `${Math.max(0, gap[0])}px ${Math.max(0, gap[1])}px`
|
|
80
|
+
};
|
|
81
|
+
const [values, setValues] = useControllableState({ value, defaultValue, onChange });
|
|
82
|
+
const context = useMemo(() => ({
|
|
83
|
+
values,
|
|
84
|
+
disabled,
|
|
85
|
+
name,
|
|
86
|
+
toggle(optionValue, checked) {
|
|
87
|
+
const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
|
|
88
|
+
setValues(next);
|
|
89
|
+
}
|
|
90
|
+
}), [disabled, name, setValues, values]);
|
|
91
|
+
return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsx(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ jsxs("div", { className: `sia-checkbox-group${gridColumns ? " sia-checkbox-group--grid" : direction === "vertical" ? " sia-checkbox-group--vertical" : ""} ${className}`.trim(), style: layoutStyle, role: "group", children: [
|
|
92
|
+
options?.map((option) => {
|
|
93
|
+
const normalized = typeof option === "object" ? option : { label: String(option), value: option };
|
|
94
|
+
return /* @__PURE__ */ jsx(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
|
|
95
|
+
}),
|
|
96
|
+
children
|
|
97
|
+
] }) }) });
|
|
81
98
|
}
|
|
99
|
+
var Checkbox2 = Object.assign(CheckboxRoot, {
|
|
100
|
+
/** 组合多个子项并统一管理布局或选择状态。 */
|
|
101
|
+
Group: CheckboxGroup
|
|
102
|
+
});
|
|
82
103
|
|
|
83
|
-
// src/components/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return target === document.scrollingElement || target === document.documentElement;
|
|
104
|
+
// src/components/treeCheckState.ts
|
|
105
|
+
function affectedKeys(node) {
|
|
106
|
+
if (node.disabled || node.disableCheckbox) return [];
|
|
107
|
+
return [...node.checkable === false ? [] : [node.key], ...node.children?.flatMap(affectedKeys) ?? []];
|
|
88
108
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
109
|
+
function getTreeCheckState(nodes, keys, strictly = false) {
|
|
110
|
+
const checked = new Set(keys);
|
|
111
|
+
const halfChecked = /* @__PURE__ */ new Set();
|
|
112
|
+
if (strictly) return { checked, halfChecked };
|
|
113
|
+
const explicit = new Set(keys);
|
|
114
|
+
function expand(items) {
|
|
115
|
+
for (const node of items) {
|
|
116
|
+
if (explicit.has(node.key)) affectedKeys(node).forEach((key) => checked.add(key));
|
|
117
|
+
if (node.children) expand(node.children);
|
|
118
|
+
}
|
|
93
119
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
expand(nodes);
|
|
121
|
+
function fold(node) {
|
|
122
|
+
const children = node.children?.map(fold).filter((value) => value !== void 0) ?? [];
|
|
123
|
+
if (node.disabled || node.disableCheckbox) return void 0;
|
|
124
|
+
if (!children.length) return { checked: checked.has(node.key), partial: false };
|
|
125
|
+
const all = children.every((child) => child.checked);
|
|
126
|
+
const some = children.some((child) => child.checked || child.partial);
|
|
127
|
+
if (node.checkable !== false) {
|
|
128
|
+
if (all) checked.add(node.key);
|
|
129
|
+
else checked.delete(node.key);
|
|
130
|
+
if (!all && some) halfChecked.add(node.key);
|
|
131
|
+
}
|
|
132
|
+
return { checked: all, partial: !all && some };
|
|
102
133
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (clipY) {
|
|
120
|
-
top = Math.max(top, rect.top);
|
|
121
|
-
bottom = Math.min(bottom, rect.bottom);
|
|
134
|
+
nodes.forEach(fold);
|
|
135
|
+
return { checked, halfChecked };
|
|
136
|
+
}
|
|
137
|
+
function toggleTreeCheck(nodes, keys, node, strictly = false) {
|
|
138
|
+
const state = getTreeCheckState(nodes, keys, strictly);
|
|
139
|
+
const next = new Set(state.checked);
|
|
140
|
+
const checked = !next.has(node.key);
|
|
141
|
+
if (node.disabled || node.disableCheckbox || node.checkable === false) return { ...state, checkedValue: !checked };
|
|
142
|
+
(strictly ? [node.key] : affectedKeys(node)).forEach((key) => checked ? next.add(key) : next.delete(key));
|
|
143
|
+
if (!strictly && !checked) {
|
|
144
|
+
let removeParents2 = function(items) {
|
|
145
|
+
let found = false;
|
|
146
|
+
for (const item of items) {
|
|
147
|
+
const childFound = item.children ? removeParents2(item.children) : false;
|
|
148
|
+
if (childFound) next.delete(item.key);
|
|
149
|
+
if (item.key === node.key || childFound) found = true;
|
|
122
150
|
}
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
return found;
|
|
152
|
+
};
|
|
153
|
+
var removeParents = removeParents2;
|
|
154
|
+
removeParents2(nodes);
|
|
125
155
|
}
|
|
126
|
-
return {
|
|
127
|
-
top,
|
|
128
|
-
right,
|
|
129
|
-
bottom,
|
|
130
|
-
left,
|
|
131
|
-
width: Math.max(0, right - left),
|
|
132
|
-
height: Math.max(0, bottom - top)
|
|
133
|
-
};
|
|
156
|
+
return { ...getTreeCheckState(nodes, [...next], strictly), checkedValue: checked };
|
|
134
157
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
useScrollbarProximity();
|
|
147
|
-
useEffect2(() => {
|
|
148
|
-
if (disabled) return;
|
|
149
|
-
const layer = document.createElement("div");
|
|
150
|
-
layer.className = "sia-floating-scrollbar-layer";
|
|
151
|
-
layer.setAttribute("aria-hidden", "true");
|
|
152
|
-
document.body.append(layer);
|
|
153
|
-
const entries = /* @__PURE__ */ new Map();
|
|
154
|
-
let scanFrame = 0;
|
|
155
|
-
let updateFrame = 0;
|
|
156
|
-
const setEntryVisible = (entry, visible) => {
|
|
157
|
-
entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
|
|
158
|
-
entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
|
|
159
|
-
};
|
|
160
|
-
const showEntry = (entry) => {
|
|
161
|
-
if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
|
|
162
|
-
entry.hideTimer = null;
|
|
163
|
-
setEntryVisible(entry, true);
|
|
164
|
-
};
|
|
165
|
-
const scheduleHide = (entry, delay = 500) => {
|
|
166
|
-
if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
|
|
167
|
-
entry.hideTimer = window.setTimeout(() => {
|
|
168
|
-
entry.hideTimer = null;
|
|
169
|
-
if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
|
|
170
|
-
}, delay);
|
|
171
|
-
};
|
|
172
|
-
const updateEntry = (entry) => {
|
|
173
|
-
const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
|
|
174
|
-
const hasHorizontal = canScroll(target, "horizontal");
|
|
175
|
-
const hasVertical = canScroll(target, "vertical");
|
|
176
|
-
const rect = visibleRect(target);
|
|
177
|
-
const thickness = 10;
|
|
178
|
-
horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
|
|
179
|
-
verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
|
|
180
|
-
if (!horizontalTrack.hidden) {
|
|
181
|
-
const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
|
|
182
|
-
const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
|
|
183
|
-
const travel = Math.max(0, trackWidth - thumbWidth);
|
|
184
|
-
const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
|
|
185
|
-
horizontalTrack.style.left = `${rect.left}px`;
|
|
186
|
-
horizontalTrack.style.top = `${rect.bottom - thickness}px`;
|
|
187
|
-
horizontalTrack.style.width = `${trackWidth}px`;
|
|
188
|
-
horizontalTrack.style.height = `${thickness}px`;
|
|
189
|
-
horizontalThumb.style.width = `${thumbWidth}px`;
|
|
190
|
-
horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
|
|
191
|
-
}
|
|
192
|
-
if (!verticalTrack.hidden) {
|
|
193
|
-
const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
|
|
194
|
-
const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
|
|
195
|
-
const travel = Math.max(0, trackHeight - thumbHeight);
|
|
196
|
-
const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
|
|
197
|
-
verticalTrack.style.left = `${rect.right - thickness}px`;
|
|
198
|
-
verticalTrack.style.top = `${rect.top}px`;
|
|
199
|
-
verticalTrack.style.width = `${thickness}px`;
|
|
200
|
-
verticalTrack.style.height = `${trackHeight}px`;
|
|
201
|
-
verticalThumb.style.height = `${thumbHeight}px`;
|
|
202
|
-
verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
const scheduleUpdateAll = () => {
|
|
206
|
-
if (updateFrame) return;
|
|
207
|
-
updateFrame = window.requestAnimationFrame(() => {
|
|
208
|
-
updateFrame = 0;
|
|
209
|
-
entries.forEach(updateEntry);
|
|
210
|
-
});
|
|
211
|
-
};
|
|
212
|
-
const bindTrackDrag = (entry, axis, track, thumb) => {
|
|
213
|
-
const target = entry.target;
|
|
214
|
-
const onTrackPointerEnter = () => {
|
|
215
|
-
entry.hoveringTrack = true;
|
|
216
|
-
showEntry(entry);
|
|
217
|
-
};
|
|
218
|
-
const onTrackPointerLeave = () => {
|
|
219
|
-
entry.hoveringTrack = false;
|
|
220
|
-
scheduleHide(entry);
|
|
221
|
-
};
|
|
222
|
-
const onTrackPointerDown = (event) => {
|
|
223
|
-
if (event.target === thumb || event.button !== 0) return;
|
|
224
|
-
event.preventDefault();
|
|
225
|
-
event.stopPropagation();
|
|
226
|
-
const trackRect = track.getBoundingClientRect();
|
|
227
|
-
const thumbRect = thumb.getBoundingClientRect();
|
|
228
|
-
const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
|
|
229
|
-
const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
|
|
230
|
-
const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
|
|
231
|
-
const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
|
|
232
|
-
if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
|
|
233
|
-
else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
|
|
234
|
-
};
|
|
235
|
-
const onThumbPointerDown = (event) => {
|
|
236
|
-
if (event.button !== 0) return;
|
|
237
|
-
event.preventDefault();
|
|
238
|
-
event.stopPropagation();
|
|
239
|
-
entry.dragging = true;
|
|
240
|
-
showEntry(entry);
|
|
241
|
-
thumb.setPointerCapture(event.pointerId);
|
|
242
|
-
const startPointer = axis === "vertical" ? event.clientY : event.clientX;
|
|
243
|
-
const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
|
|
244
|
-
const trackRect = track.getBoundingClientRect();
|
|
245
|
-
const thumbRect = thumb.getBoundingClientRect();
|
|
246
|
-
const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
|
|
247
|
-
const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
|
|
248
|
-
const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
|
|
249
|
-
const travel = Math.max(1, trackLength - thumbLength);
|
|
250
|
-
const onPointerMove = (moveEvent) => {
|
|
251
|
-
if (moveEvent.pointerId !== event.pointerId) return;
|
|
252
|
-
const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
|
|
253
|
-
const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
|
|
254
|
-
if (axis === "vertical") target.scrollTop = nextScroll;
|
|
255
|
-
else target.scrollLeft = nextScroll;
|
|
256
|
-
};
|
|
257
|
-
const onPointerUp = (upEvent) => {
|
|
258
|
-
if (upEvent.pointerId !== event.pointerId) return;
|
|
259
|
-
entry.dragging = false;
|
|
260
|
-
thumb.releasePointerCapture(event.pointerId);
|
|
261
|
-
window.removeEventListener("pointermove", onPointerMove);
|
|
262
|
-
window.removeEventListener("pointerup", onPointerUp);
|
|
263
|
-
window.removeEventListener("pointercancel", onPointerUp);
|
|
264
|
-
scheduleHide(entry);
|
|
265
|
-
};
|
|
266
|
-
window.addEventListener("pointermove", onPointerMove);
|
|
267
|
-
window.addEventListener("pointerup", onPointerUp);
|
|
268
|
-
window.addEventListener("pointercancel", onPointerUp);
|
|
269
|
-
};
|
|
270
|
-
track.addEventListener("pointerenter", onTrackPointerEnter);
|
|
271
|
-
track.addEventListener("pointerleave", onTrackPointerLeave);
|
|
272
|
-
track.addEventListener("pointerdown", onTrackPointerDown);
|
|
273
|
-
thumb.addEventListener("pointerdown", onThumbPointerDown);
|
|
274
|
-
return () => {
|
|
275
|
-
track.removeEventListener("pointerenter", onTrackPointerEnter);
|
|
276
|
-
track.removeEventListener("pointerleave", onTrackPointerLeave);
|
|
277
|
-
track.removeEventListener("pointerdown", onTrackPointerDown);
|
|
278
|
-
thumb.removeEventListener("pointerdown", onThumbPointerDown);
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
const resizeObserver = new ResizeObserver((observedEntries) => {
|
|
282
|
-
observedEntries.forEach(({ target }) => {
|
|
283
|
-
const entry = entries.get(target);
|
|
284
|
-
if (entry) updateEntry(entry);
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
const addTarget = (target) => {
|
|
288
|
-
if (entries.has(target)) return;
|
|
289
|
-
const horizontal = createTrack("horizontal");
|
|
290
|
-
const vertical = createTrack("vertical");
|
|
291
|
-
layer.append(horizontal.track, vertical.track);
|
|
292
|
-
const onPointerEnter = () => {
|
|
293
|
-
const entry2 = entries.get(target);
|
|
294
|
-
if (!entry2) return;
|
|
295
|
-
entry2.hoveringTarget = true;
|
|
296
|
-
showEntry(entry2);
|
|
297
|
-
};
|
|
298
|
-
const onPointerLeave = () => {
|
|
299
|
-
const entry2 = entries.get(target);
|
|
300
|
-
if (!entry2) return;
|
|
301
|
-
entry2.hoveringTarget = false;
|
|
302
|
-
scheduleHide(entry2);
|
|
303
|
-
};
|
|
304
|
-
const onScroll = () => {
|
|
305
|
-
const entry2 = entries.get(target);
|
|
306
|
-
if (!entry2) return;
|
|
307
|
-
updateEntry(entry2);
|
|
308
|
-
showEntry(entry2);
|
|
309
|
-
scheduleHide(entry2, 700);
|
|
310
|
-
};
|
|
311
|
-
target.addEventListener("pointerenter", onPointerEnter);
|
|
312
|
-
target.addEventListener("pointerleave", onPointerLeave);
|
|
313
|
-
target.addEventListener("scroll", onScroll, { passive: true });
|
|
314
|
-
const entry = {
|
|
315
|
-
target,
|
|
316
|
-
horizontalTrack: horizontal.track,
|
|
317
|
-
horizontalThumb: horizontal.thumb,
|
|
318
|
-
verticalTrack: vertical.track,
|
|
319
|
-
verticalThumb: vertical.thumb,
|
|
320
|
-
hideTimer: null,
|
|
321
|
-
hoveringTarget: target.matches(":hover"),
|
|
322
|
-
hoveringTrack: false,
|
|
323
|
-
dragging: false,
|
|
324
|
-
cleanup: () => void 0
|
|
325
|
-
};
|
|
326
|
-
const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
|
|
327
|
-
const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
|
|
328
|
-
entry.cleanup = () => {
|
|
329
|
-
if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
|
|
330
|
-
cleanupHorizontal();
|
|
331
|
-
cleanupVertical();
|
|
332
|
-
target.removeEventListener("pointerenter", onPointerEnter);
|
|
333
|
-
target.removeEventListener("pointerleave", onPointerLeave);
|
|
334
|
-
target.removeEventListener("scroll", onScroll);
|
|
335
|
-
resizeObserver.unobserve(target);
|
|
336
|
-
target.removeAttribute("data-sia-floating-scrollbar");
|
|
337
|
-
horizontal.track.remove();
|
|
338
|
-
vertical.track.remove();
|
|
339
|
-
};
|
|
340
|
-
entries.set(target, entry);
|
|
341
|
-
target.setAttribute("data-sia-floating-scrollbar", "");
|
|
342
|
-
resizeObserver.observe(target);
|
|
343
|
-
updateEntry(entry);
|
|
344
|
-
if (entry.hoveringTarget) showEntry(entry);
|
|
345
|
-
};
|
|
346
|
-
const removeTarget = (target) => {
|
|
347
|
-
const entry = entries.get(target);
|
|
348
|
-
if (!entry) return;
|
|
349
|
-
entry.cleanup();
|
|
350
|
-
entries.delete(target);
|
|
351
|
-
};
|
|
352
|
-
const scanTargets = () => {
|
|
353
|
-
scanFrame = 0;
|
|
354
|
-
const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
|
|
355
|
-
const nextTargets = /* @__PURE__ */ new Set();
|
|
356
|
-
candidates.forEach((target) => {
|
|
357
|
-
if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
|
|
358
|
-
nextTargets.add(target);
|
|
359
|
-
if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
entries.forEach((_entry, target) => {
|
|
363
|
-
if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
|
|
364
|
-
else updateEntry(_entry);
|
|
365
|
-
});
|
|
366
|
-
};
|
|
367
|
-
const scheduleScan = () => {
|
|
368
|
-
if (scanFrame) return;
|
|
369
|
-
scanFrame = window.requestAnimationFrame(scanTargets);
|
|
370
|
-
};
|
|
371
|
-
const mutationObserver = new MutationObserver((mutations) => {
|
|
372
|
-
if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
|
|
373
|
-
});
|
|
374
|
-
mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
|
|
375
|
-
window.addEventListener("resize", scheduleScan);
|
|
376
|
-
window.addEventListener("scroll", scheduleUpdateAll, true);
|
|
377
|
-
scheduleScan();
|
|
378
|
-
return () => {
|
|
379
|
-
mutationObserver.disconnect();
|
|
380
|
-
resizeObserver.disconnect();
|
|
381
|
-
window.removeEventListener("resize", scheduleScan);
|
|
382
|
-
window.removeEventListener("scroll", scheduleUpdateAll, true);
|
|
383
|
-
if (scanFrame) window.cancelAnimationFrame(scanFrame);
|
|
384
|
-
if (updateFrame) window.cancelAnimationFrame(updateFrame);
|
|
385
|
-
entries.forEach((entry) => entry.cleanup());
|
|
386
|
-
entries.clear();
|
|
387
|
-
layer.remove();
|
|
388
|
-
};
|
|
389
|
-
}, [disabled, targetRef]);
|
|
390
|
-
return null;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// src/components/Checkbox.tsx
|
|
394
|
-
import { createContext, forwardRef, useContext, useEffect as useEffect3, useId, useMemo, useRef } from "react";
|
|
395
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
396
|
-
var CheckboxGroupContext = createContext(null);
|
|
397
|
-
var CheckboxRoot = forwardRef(function Checkbox({ value, size: sizeProp, indeterminate = false, children, disabled, checked, defaultChecked, className = "", onChange, ...props }, forwardedRef) {
|
|
398
|
-
const size = useControlSize(sizeProp);
|
|
399
|
-
const group = useContext(CheckboxGroupContext);
|
|
400
|
-
const localRef = useRef(null);
|
|
401
|
-
const fallbackId = useId();
|
|
402
|
-
const grouped = group !== null && value !== void 0;
|
|
403
|
-
const currentChecked = grouped ? group.values.includes(value) : checked;
|
|
404
|
-
useEffect3(() => {
|
|
405
|
-
if (localRef.current) localRef.current.indeterminate = indeterminate;
|
|
406
|
-
}, [indeterminate]);
|
|
407
|
-
function setRef(node) {
|
|
408
|
-
localRef.current = node;
|
|
409
|
-
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
410
|
-
else if (forwardedRef) forwardedRef.current = node;
|
|
411
|
-
}
|
|
412
|
-
return /* @__PURE__ */ jsxs("label", { className: `sia-checkbox sia-checkbox--${size}${disabled || group?.disabled ? " is-disabled" : ""} ${className}`.trim(), children: [
|
|
413
|
-
/* @__PURE__ */ jsx(
|
|
414
|
-
"input",
|
|
415
|
-
{
|
|
416
|
-
...withTitleTooltip(props),
|
|
417
|
-
ref: setRef,
|
|
418
|
-
id: props.id ?? fallbackId,
|
|
419
|
-
className: "sia-checkbox__input",
|
|
420
|
-
type: "checkbox",
|
|
421
|
-
name: group?.name ?? props.name,
|
|
422
|
-
disabled: disabled || group?.disabled,
|
|
423
|
-
checked: currentChecked,
|
|
424
|
-
defaultChecked: grouped ? void 0 : defaultChecked,
|
|
425
|
-
"aria-checked": indeterminate ? "mixed" : currentChecked,
|
|
426
|
-
onChange: (event) => {
|
|
427
|
-
if (grouped) group.toggle(value, event.currentTarget.checked);
|
|
428
|
-
onChange?.(event.currentTarget.checked, event);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
),
|
|
432
|
-
/* @__PURE__ */ jsx("span", { className: "sia-checkbox__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", {}) }),
|
|
433
|
-
children !== void 0 ? /* @__PURE__ */ jsx("span", { className: "sia-checkbox__label", children }) : null
|
|
434
|
-
] });
|
|
435
|
-
});
|
|
436
|
-
function CheckboxGroup({
|
|
437
|
-
size: sizeProp,
|
|
438
|
-
columns,
|
|
439
|
-
direction = "horizontal",
|
|
440
|
-
gap,
|
|
441
|
-
value,
|
|
442
|
-
defaultValue = [],
|
|
443
|
-
options,
|
|
444
|
-
disabled,
|
|
445
|
-
name,
|
|
446
|
-
className = "",
|
|
447
|
-
children,
|
|
448
|
-
onChange
|
|
449
|
-
}) {
|
|
450
|
-
const size = useControlSize(sizeProp);
|
|
451
|
-
const gridColumns = columns !== void 0 && Number.isInteger(columns) && columns > 0 ? columns : void 0;
|
|
452
|
-
const layoutStyle = {
|
|
453
|
-
gridTemplateColumns: gridColumns ? `repeat(${gridColumns}, minmax(0, 1fr))` : void 0,
|
|
454
|
-
gap: gap === void 0 ? void 0 : typeof gap === "number" ? Math.max(0, gap) : `${Math.max(0, gap[0])}px ${Math.max(0, gap[1])}px`
|
|
455
|
-
};
|
|
456
|
-
const [values, setValues] = useControllableState({ value, defaultValue, onChange });
|
|
457
|
-
const context = useMemo(() => ({
|
|
458
|
-
values,
|
|
459
|
-
disabled,
|
|
460
|
-
name,
|
|
461
|
-
toggle(optionValue, checked) {
|
|
462
|
-
const next = checked ? values.includes(optionValue) ? values : [...values, optionValue] : values.filter((item) => item !== optionValue);
|
|
463
|
-
setValues(next);
|
|
464
|
-
}
|
|
465
|
-
}), [disabled, name, setValues, values]);
|
|
466
|
-
return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsx(CheckboxGroupContext.Provider, { value: context, children: /* @__PURE__ */ jsxs("div", { className: `sia-checkbox-group${gridColumns ? " sia-checkbox-group--grid" : direction === "vertical" ? " sia-checkbox-group--vertical" : ""} ${className}`.trim(), style: layoutStyle, role: "group", children: [
|
|
467
|
-
options?.map((option) => {
|
|
468
|
-
const normalized = typeof option === "object" ? option : { label: String(option), value: option };
|
|
469
|
-
return /* @__PURE__ */ jsx(CheckboxRoot, { value: normalized.value, disabled: normalized.disabled, children: normalized.label }, normalized.value);
|
|
470
|
-
}),
|
|
471
|
-
children
|
|
472
|
-
] }) }) });
|
|
473
|
-
}
|
|
474
|
-
var Checkbox2 = Object.assign(CheckboxRoot, {
|
|
475
|
-
/** 组合多个子项并统一管理布局或选择状态。 */
|
|
476
|
-
Group: CheckboxGroup
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
// src/components/treeCheckState.ts
|
|
480
|
-
function affectedKeys(node) {
|
|
481
|
-
if (node.disabled || node.disableCheckbox) return [];
|
|
482
|
-
return [...node.checkable === false ? [] : [node.key], ...node.children?.flatMap(affectedKeys) ?? []];
|
|
483
|
-
}
|
|
484
|
-
function getTreeCheckState(nodes, keys, strictly = false) {
|
|
485
|
-
const checked = new Set(keys);
|
|
486
|
-
const halfChecked = /* @__PURE__ */ new Set();
|
|
487
|
-
if (strictly) return { checked, halfChecked };
|
|
488
|
-
const explicit = new Set(keys);
|
|
489
|
-
function expand(items) {
|
|
490
|
-
for (const node of items) {
|
|
491
|
-
if (explicit.has(node.key)) affectedKeys(node).forEach((key) => checked.add(key));
|
|
492
|
-
if (node.children) expand(node.children);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
expand(nodes);
|
|
496
|
-
function fold(node) {
|
|
497
|
-
const children = node.children?.map(fold).filter((value) => value !== void 0) ?? [];
|
|
498
|
-
if (node.disabled || node.disableCheckbox) return void 0;
|
|
499
|
-
if (!children.length) return { checked: checked.has(node.key), partial: false };
|
|
500
|
-
const all = children.every((child) => child.checked);
|
|
501
|
-
const some = children.some((child) => child.checked || child.partial);
|
|
502
|
-
if (node.checkable !== false) {
|
|
503
|
-
if (all) checked.add(node.key);
|
|
504
|
-
else checked.delete(node.key);
|
|
505
|
-
if (!all && some) halfChecked.add(node.key);
|
|
506
|
-
}
|
|
507
|
-
return { checked: all, partial: !all && some };
|
|
508
|
-
}
|
|
509
|
-
nodes.forEach(fold);
|
|
510
|
-
return { checked, halfChecked };
|
|
511
|
-
}
|
|
512
|
-
function toggleTreeCheck(nodes, keys, node, strictly = false) {
|
|
513
|
-
const state = getTreeCheckState(nodes, keys, strictly);
|
|
514
|
-
const next = new Set(state.checked);
|
|
515
|
-
const checked = !next.has(node.key);
|
|
516
|
-
if (node.disabled || node.disableCheckbox || node.checkable === false) return { ...state, checkedValue: !checked };
|
|
517
|
-
(strictly ? [node.key] : affectedKeys(node)).forEach((key) => checked ? next.add(key) : next.delete(key));
|
|
518
|
-
if (!strictly && !checked) {
|
|
519
|
-
let removeParents2 = function(items) {
|
|
520
|
-
let found = false;
|
|
521
|
-
for (const item of items) {
|
|
522
|
-
const childFound = item.children ? removeParents2(item.children) : false;
|
|
523
|
-
if (childFound) next.delete(item.key);
|
|
524
|
-
if (item.key === node.key || childFound) found = true;
|
|
525
|
-
}
|
|
526
|
-
return found;
|
|
527
|
-
};
|
|
528
|
-
var removeParents = removeParents2;
|
|
529
|
-
removeParents2(nodes);
|
|
530
|
-
}
|
|
531
|
-
return { ...getTreeCheckState(nodes, [...next], strictly), checkedValue: checked };
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// src/components/FloatingDisplay.tsx
|
|
535
|
-
import { cloneElement, useEffect as useEffect4, useId as useId2, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
|
|
536
|
-
import { createPortal } from "react-dom";
|
|
537
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
538
|
-
var FLOATING_GAP = 9;
|
|
539
|
-
var FLOATING_VIEWPORT_PADDING = 8;
|
|
540
|
-
var FLOATING_ARROW_EDGE_OFFSET = 16;
|
|
541
|
-
var activeTooltipClose;
|
|
542
|
-
function clampFloating(value, min, max) {
|
|
543
|
-
return Math.min(Math.max(value, min), Math.max(min, max));
|
|
158
|
+
|
|
159
|
+
// src/components/FloatingDisplay.tsx
|
|
160
|
+
import { cloneElement, useEffect as useEffect2, useId as useId2, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState } from "react";
|
|
161
|
+
import { createPortal } from "react-dom";
|
|
162
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
163
|
+
var FLOATING_GAP = 9;
|
|
164
|
+
var FLOATING_VIEWPORT_PADDING = 8;
|
|
165
|
+
var FLOATING_ARROW_EDGE_OFFSET = 16;
|
|
166
|
+
var activeTooltipClose;
|
|
167
|
+
function clampFloating(value, min, max) {
|
|
168
|
+
return Math.min(Math.max(value, min), Math.max(min, max));
|
|
544
169
|
}
|
|
545
170
|
function resolveFloatingPosition(trigger, overlay, placement) {
|
|
546
171
|
const viewportWidth = document.documentElement.clientWidth;
|
|
@@ -628,7 +253,7 @@ function Floating({
|
|
|
628
253
|
if (delay <= 0) closeNow();
|
|
629
254
|
else leaveTimer.current = window.setTimeout(closeNow, delay * 1e3);
|
|
630
255
|
}
|
|
631
|
-
|
|
256
|
+
useEffect2(() => {
|
|
632
257
|
if (!visible || !triggers.includes("click")) return;
|
|
633
258
|
const close = (event) => {
|
|
634
259
|
const target = event.target;
|
|
@@ -658,7 +283,7 @@ function Floating({
|
|
|
658
283
|
observer.disconnect();
|
|
659
284
|
};
|
|
660
285
|
}, [children, content, placement, visible]);
|
|
661
|
-
|
|
286
|
+
useEffect2(() => () => {
|
|
662
287
|
clearTimers();
|
|
663
288
|
if (activeTooltipClose === closeSelfRef.current) activeTooltipClose = void 0;
|
|
664
289
|
}, []);
|
|
@@ -974,318 +599,693 @@ function Tree({
|
|
|
974
599
|
] }, node.key);
|
|
975
600
|
});
|
|
976
601
|
}
|
|
977
|
-
return /* @__PURE__ */ jsx2("div", { className: `sia-tree${showLine ? " sia-tree--line" : ""}${stickyAncestors ? " sia-tree--sticky-ancestors" : ""} ${className}`.trim(), role: "tree", "aria-multiselectable": multiple || void 0, ...withTitleTooltip(props), children: render(treeData) });
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// src/components/Modal.tsx
|
|
981
|
-
import { forwardRef as forwardRef2, useEffect as
|
|
982
|
-
import { createRoot } from "react-dom/client";
|
|
983
|
-
|
|
984
|
-
// src/components/Overlay.tsx
|
|
985
|
-
import { useEffect as
|
|
986
|
-
import { createPortal as createPortal2 } from "react-dom";
|
|
987
|
-
var bodyLockCount = 0;
|
|
988
|
-
var previousBodyOverflow = "";
|
|
989
|
-
var overlayStack = [];
|
|
990
|
-
function lockBodyScroll() {
|
|
991
|
-
if (bodyLockCount === 0) {
|
|
992
|
-
previousBodyOverflow = document.body.style.overflow;
|
|
993
|
-
document.body.style.overflow = "hidden";
|
|
602
|
+
return /* @__PURE__ */ jsx2("div", { className: `sia-tree${showLine ? " sia-tree--line" : ""}${stickyAncestors ? " sia-tree--sticky-ancestors" : ""} ${className}`.trim(), role: "tree", "aria-multiselectable": multiple || void 0, ...withTitleTooltip(props), children: render(treeData) });
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/components/Modal.tsx
|
|
606
|
+
import { forwardRef as forwardRef2, useEffect as useEffect4, useId as useId3, useRef as useRef4, useState as useState2 } from "react";
|
|
607
|
+
import { createRoot } from "react-dom/client";
|
|
608
|
+
|
|
609
|
+
// src/components/Overlay.tsx
|
|
610
|
+
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
611
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
612
|
+
var bodyLockCount = 0;
|
|
613
|
+
var previousBodyOverflow = "";
|
|
614
|
+
var overlayStack = [];
|
|
615
|
+
function lockBodyScroll() {
|
|
616
|
+
if (bodyLockCount === 0) {
|
|
617
|
+
previousBodyOverflow = document.body.style.overflow;
|
|
618
|
+
document.body.style.overflow = "hidden";
|
|
619
|
+
}
|
|
620
|
+
bodyLockCount += 1;
|
|
621
|
+
return () => {
|
|
622
|
+
bodyLockCount = Math.max(0, bodyLockCount - 1);
|
|
623
|
+
if (bodyLockCount === 0) document.body.style.overflow = previousBodyOverflow;
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function focusableElements(container) {
|
|
627
|
+
return [...container.querySelectorAll(
|
|
628
|
+
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
629
|
+
)].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
|
|
630
|
+
}
|
|
631
|
+
function useOverlayLifecycle(open, panelRef, onRequestClose, keyboard = true) {
|
|
632
|
+
const overlayIdRef = useRef3(/* @__PURE__ */ Symbol("sia-overlay"));
|
|
633
|
+
const closeRef = useRef3(onRequestClose);
|
|
634
|
+
closeRef.current = onRequestClose;
|
|
635
|
+
useEffect3(() => {
|
|
636
|
+
if (!open || typeof document === "undefined") return void 0;
|
|
637
|
+
const overlayId = overlayIdRef.current;
|
|
638
|
+
const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
639
|
+
overlayStack.push(overlayId);
|
|
640
|
+
const unlockBody = lockBodyScroll();
|
|
641
|
+
const focusTimer = window.setTimeout(() => {
|
|
642
|
+
const panel = panelRef.current;
|
|
643
|
+
if (!panel) return;
|
|
644
|
+
const [firstFocusable] = focusableElements(panel);
|
|
645
|
+
(firstFocusable ?? panel).focus({ preventScroll: true });
|
|
646
|
+
}, 0);
|
|
647
|
+
function handleKeyDown(event) {
|
|
648
|
+
if (overlayStack.at(-1) !== overlayId) return;
|
|
649
|
+
if (event.key === "Escape" && keyboard) {
|
|
650
|
+
event.preventDefault();
|
|
651
|
+
closeRef.current();
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (event.key !== "Tab" || !panelRef.current) return;
|
|
655
|
+
const focusable = focusableElements(panelRef.current);
|
|
656
|
+
if (focusable.length === 0) {
|
|
657
|
+
event.preventDefault();
|
|
658
|
+
panelRef.current.focus();
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const first = focusable[0];
|
|
662
|
+
const last = focusable.at(-1);
|
|
663
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
664
|
+
event.preventDefault();
|
|
665
|
+
last.focus();
|
|
666
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
667
|
+
event.preventDefault();
|
|
668
|
+
first.focus();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
672
|
+
return () => {
|
|
673
|
+
window.clearTimeout(focusTimer);
|
|
674
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
675
|
+
const index = overlayStack.lastIndexOf(overlayId);
|
|
676
|
+
if (index >= 0) overlayStack.splice(index, 1);
|
|
677
|
+
unlockBody();
|
|
678
|
+
previouslyFocused?.focus({ preventScroll: true });
|
|
679
|
+
};
|
|
680
|
+
}, [keyboard, open, panelRef]);
|
|
681
|
+
}
|
|
682
|
+
function OverlayPortal({ children, container }) {
|
|
683
|
+
if (typeof document === "undefined") return null;
|
|
684
|
+
const target = typeof container === "function" ? container() : container ?? document.body;
|
|
685
|
+
return createPortal2(children, target);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/components/Modal.tsx
|
|
689
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
690
|
+
var modalHandles = /* @__PURE__ */ new Set();
|
|
691
|
+
var MODAL_MOTION_DURATION = 220;
|
|
692
|
+
var typeIcons = {
|
|
693
|
+
info: /* @__PURE__ */ jsx3(Icon, { name: "info", size: 23 }),
|
|
694
|
+
success: /* @__PURE__ */ jsx3(Icon, { name: "circle-check", size: 23 }),
|
|
695
|
+
warning: /* @__PURE__ */ jsx3(Icon, { name: "warning", size: 23 }),
|
|
696
|
+
error: /* @__PURE__ */ jsx3(Icon, { name: "circle-close", size: 23 }),
|
|
697
|
+
confirm: /* @__PURE__ */ jsx3(Icon, { name: "question", size: 23 })
|
|
698
|
+
};
|
|
699
|
+
var ModalRoot = forwardRef2(function Modal({
|
|
700
|
+
open,
|
|
701
|
+
title,
|
|
702
|
+
children,
|
|
703
|
+
footer,
|
|
704
|
+
footerExtra,
|
|
705
|
+
type = "default",
|
|
706
|
+
okText = "\u786E\u5B9A",
|
|
707
|
+
cancelText = "\u53D6\u6D88",
|
|
708
|
+
showCancel = type === "default" || type === "confirm",
|
|
709
|
+
confirmLoading = false,
|
|
710
|
+
okButtonProps,
|
|
711
|
+
cancelButtonProps,
|
|
712
|
+
closable = true,
|
|
713
|
+
maskClosable = true,
|
|
714
|
+
keyboard = true,
|
|
715
|
+
centered = false,
|
|
716
|
+
width = 520,
|
|
717
|
+
height,
|
|
718
|
+
zIndex = 1e3,
|
|
719
|
+
getContainer,
|
|
720
|
+
onOk,
|
|
721
|
+
onCancel,
|
|
722
|
+
onOpenChange,
|
|
723
|
+
afterOpenChange,
|
|
724
|
+
className = "",
|
|
725
|
+
style,
|
|
726
|
+
...props
|
|
727
|
+
}, forwardedRef) {
|
|
728
|
+
const localRef = useRef4(null);
|
|
729
|
+
const titleId = useId3();
|
|
730
|
+
const [submitting, setSubmitting] = useState2(false);
|
|
731
|
+
const [rendered, setRendered] = useState2(open);
|
|
732
|
+
const [closing, setClosing] = useState2(false);
|
|
733
|
+
const afterOpenChangeRef = useRef4(afterOpenChange);
|
|
734
|
+
afterOpenChangeRef.current = afterOpenChange;
|
|
735
|
+
const submitLock = useRef4(false);
|
|
736
|
+
useEffect4(() => {
|
|
737
|
+
if (!open || !rendered) return void 0;
|
|
738
|
+
const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
|
|
739
|
+
return () => window.clearTimeout(timer);
|
|
740
|
+
}, [open, rendered]);
|
|
741
|
+
useEffect4(() => {
|
|
742
|
+
if (open) {
|
|
743
|
+
setRendered(true);
|
|
744
|
+
setClosing(false);
|
|
745
|
+
return void 0;
|
|
746
|
+
}
|
|
747
|
+
if (!rendered) return void 0;
|
|
748
|
+
setClosing(true);
|
|
749
|
+
const timer = window.setTimeout(() => {
|
|
750
|
+
setRendered(false);
|
|
751
|
+
setClosing(false);
|
|
752
|
+
afterOpenChangeRef.current?.(false);
|
|
753
|
+
}, MODAL_MOTION_DURATION);
|
|
754
|
+
return () => window.clearTimeout(timer);
|
|
755
|
+
}, [open, rendered]);
|
|
756
|
+
function setRef(node) {
|
|
757
|
+
localRef.current = node;
|
|
758
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
759
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
760
|
+
}
|
|
761
|
+
function requestClose() {
|
|
762
|
+
if (submitting || confirmLoading) return;
|
|
763
|
+
onCancel?.();
|
|
764
|
+
onOpenChange?.(false);
|
|
765
|
+
}
|
|
766
|
+
async function handleOk() {
|
|
767
|
+
if (submitLock.current || confirmLoading) return;
|
|
768
|
+
submitLock.current = true;
|
|
769
|
+
if (!onOk) {
|
|
770
|
+
onOpenChange?.(false);
|
|
771
|
+
submitLock.current = false;
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
try {
|
|
775
|
+
const result = onOk();
|
|
776
|
+
if (result instanceof Promise) {
|
|
777
|
+
setSubmitting(true);
|
|
778
|
+
const resolved = await result;
|
|
779
|
+
if (resolved !== false) onOpenChange?.(false);
|
|
780
|
+
} else if (result !== false) {
|
|
781
|
+
onOpenChange?.(false);
|
|
782
|
+
}
|
|
783
|
+
} catch {
|
|
784
|
+
return;
|
|
785
|
+
} finally {
|
|
786
|
+
submitLock.current = false;
|
|
787
|
+
setSubmitting(false);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
|
|
791
|
+
if (!rendered) return null;
|
|
792
|
+
const mergedStyle = {
|
|
793
|
+
...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
|
|
794
|
+
...style,
|
|
795
|
+
"--sia-modal-width": typeof width === "number" ? `${width}px` : width
|
|
796
|
+
};
|
|
797
|
+
const loading = submitting || confirmLoading;
|
|
798
|
+
const defaultFooter = /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
799
|
+
showCancel ? /* @__PURE__ */ jsx3(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
|
|
800
|
+
/* @__PURE__ */ jsx3(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
|
|
801
|
+
] });
|
|
802
|
+
return /* @__PURE__ */ jsx3(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ jsx3(
|
|
803
|
+
"div",
|
|
804
|
+
{
|
|
805
|
+
className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
|
|
806
|
+
"data-state": closing ? "closing" : "open",
|
|
807
|
+
style: { zIndex },
|
|
808
|
+
onMouseDown: (event) => {
|
|
809
|
+
if (event.target === event.currentTarget && maskClosable) requestClose();
|
|
810
|
+
},
|
|
811
|
+
children: /* @__PURE__ */ jsxs3(
|
|
812
|
+
"div",
|
|
813
|
+
{
|
|
814
|
+
ref: setRef,
|
|
815
|
+
className: `sia-modal sia-modal--${type} ${className}`.trim(),
|
|
816
|
+
style: mergedStyle,
|
|
817
|
+
role: "dialog",
|
|
818
|
+
"aria-modal": "true",
|
|
819
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
820
|
+
tabIndex: -1,
|
|
821
|
+
...withTitleTooltip(props),
|
|
822
|
+
children: [
|
|
823
|
+
/* @__PURE__ */ jsxs3("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
|
|
824
|
+
/* @__PURE__ */ jsxs3("div", { className: "sia-modal__heading", children: [
|
|
825
|
+
typeIcons[type] ? /* @__PURE__ */ jsx3("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
|
|
826
|
+
title ? /* @__PURE__ */ jsx3("strong", { id: titleId, children: title }) : null
|
|
827
|
+
] }),
|
|
828
|
+
closable ? /* @__PURE__ */ jsx3("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ jsx3(Icon, { name: "close", size: 17 }) }) : null
|
|
829
|
+
] }),
|
|
830
|
+
children !== void 0 ? /* @__PURE__ */ jsx3("div", { className: "sia-modal__body", style: height !== void 0 ? { flex: 1, minHeight: 0, overflow: "auto" } : void 0, children }) : null,
|
|
831
|
+
footer !== null ? /* @__PURE__ */ jsxs3("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
|
|
832
|
+
footer === void 0 && footerExtra != null ? /* @__PURE__ */ jsx3("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
|
|
833
|
+
footer ?? defaultFooter
|
|
834
|
+
] }) : null
|
|
835
|
+
]
|
|
836
|
+
}
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
) });
|
|
840
|
+
});
|
|
841
|
+
function openModal(config) {
|
|
842
|
+
if (typeof document === "undefined") return { destroy() {
|
|
843
|
+
}, update() {
|
|
844
|
+
} };
|
|
845
|
+
const container = document.createElement("div");
|
|
846
|
+
container.className = "sia-modal-api-host";
|
|
847
|
+
document.body.appendChild(container);
|
|
848
|
+
const root = createRoot(container);
|
|
849
|
+
let currentConfig = config;
|
|
850
|
+
let currentOpen = true;
|
|
851
|
+
let destroyed = false;
|
|
852
|
+
let cleanupTimer;
|
|
853
|
+
function cleanup2() {
|
|
854
|
+
if (destroyed) return;
|
|
855
|
+
destroyed = true;
|
|
856
|
+
if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
|
|
857
|
+
root.unmount();
|
|
858
|
+
container.remove();
|
|
859
|
+
modalHandles.delete(handle);
|
|
860
|
+
}
|
|
861
|
+
function closeWithMotion() {
|
|
862
|
+
if (destroyed || !currentOpen) return;
|
|
863
|
+
currentOpen = false;
|
|
864
|
+
render();
|
|
865
|
+
cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
|
|
994
866
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
867
|
+
function render() {
|
|
868
|
+
root.render(/* @__PURE__ */ jsx3(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
|
|
869
|
+
currentConfig.onOpenChange?.(nextOpen);
|
|
870
|
+
if (!nextOpen) closeWithMotion();
|
|
871
|
+
} }));
|
|
872
|
+
}
|
|
873
|
+
const handle = {
|
|
874
|
+
destroy: closeWithMotion,
|
|
875
|
+
update(next) {
|
|
876
|
+
if (destroyed) return;
|
|
877
|
+
currentConfig = { ...currentConfig, ...next };
|
|
878
|
+
render();
|
|
879
|
+
}
|
|
999
880
|
};
|
|
881
|
+
modalHandles.add(handle);
|
|
882
|
+
render();
|
|
883
|
+
return handle;
|
|
1000
884
|
}
|
|
1001
|
-
function
|
|
1002
|
-
return
|
|
1003
|
-
|
|
1004
|
-
|
|
885
|
+
function preset(type, defaults = {}) {
|
|
886
|
+
return (config) => openModal({
|
|
887
|
+
okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
|
|
888
|
+
showCancel: type === "confirm",
|
|
889
|
+
closable: type !== "confirm",
|
|
890
|
+
maskClosable: type !== "confirm",
|
|
891
|
+
...defaults,
|
|
892
|
+
...config,
|
|
893
|
+
type
|
|
894
|
+
});
|
|
1005
895
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
896
|
+
var Modal2 = Object.assign(ModalRoot, {
|
|
897
|
+
/** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
|
|
898
|
+
open: openModal,
|
|
899
|
+
/** 显示普通信息提示。 */
|
|
900
|
+
info: preset("info", { title: "\u63D0\u793A" }),
|
|
901
|
+
/** 显示成功提示。 */
|
|
902
|
+
success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
|
|
903
|
+
/** 显示警告提示。 */
|
|
904
|
+
warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
|
|
905
|
+
/** 显示错误提示。 */
|
|
906
|
+
error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
|
|
907
|
+
/** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
|
|
908
|
+
confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
|
|
909
|
+
/** 关闭由此 API 打开的所有弹层。 */
|
|
910
|
+
destroyAll() {
|
|
911
|
+
[...modalHandles].forEach((handle) => handle.destroy());
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
// src/components/FloatingScrollbarProvider.tsx
|
|
916
|
+
import { useEffect as useEffect6 } from "react";
|
|
917
|
+
|
|
918
|
+
// src/components/useScrollbarProximity.ts
|
|
919
|
+
import { useEffect as useEffect5 } from "react";
|
|
920
|
+
var users = 0;
|
|
921
|
+
var cleanup;
|
|
922
|
+
function useScrollbarProximity() {
|
|
1010
923
|
useEffect5(() => {
|
|
1011
|
-
if (
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
924
|
+
if (users++ === 0) {
|
|
925
|
+
let frame = 0;
|
|
926
|
+
let x = -Infinity;
|
|
927
|
+
let y = -Infinity;
|
|
928
|
+
const nearby = /* @__PURE__ */ new Set();
|
|
929
|
+
const clear = () => {
|
|
930
|
+
nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
|
|
931
|
+
nearby.clear();
|
|
932
|
+
};
|
|
933
|
+
const update = () => {
|
|
934
|
+
frame = 0;
|
|
935
|
+
const next = /* @__PURE__ */ new Set();
|
|
936
|
+
document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
|
|
937
|
+
const rect = track.getBoundingClientRect();
|
|
938
|
+
const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
|
|
939
|
+
if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
|
|
940
|
+
next.add(track);
|
|
941
|
+
if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
nearby.forEach((track) => {
|
|
945
|
+
if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
|
|
946
|
+
});
|
|
947
|
+
nearby.clear();
|
|
948
|
+
next.forEach((track) => nearby.add(track));
|
|
949
|
+
};
|
|
950
|
+
const move = (event) => {
|
|
951
|
+
if (event.pointerType === "touch") return;
|
|
952
|
+
x = event.clientX;
|
|
953
|
+
y = event.clientY;
|
|
954
|
+
if (!frame) frame = requestAnimationFrame(update);
|
|
955
|
+
};
|
|
956
|
+
const leave = () => {
|
|
957
|
+
x = y = -Infinity;
|
|
958
|
+
clear();
|
|
959
|
+
};
|
|
960
|
+
document.addEventListener("pointermove", move, { passive: true });
|
|
961
|
+
document.documentElement.addEventListener("pointerleave", leave);
|
|
962
|
+
window.addEventListener("blur", leave);
|
|
963
|
+
cleanup = () => {
|
|
964
|
+
if (frame) cancelAnimationFrame(frame);
|
|
965
|
+
document.removeEventListener("pointermove", move);
|
|
966
|
+
document.documentElement.removeEventListener("pointerleave", leave);
|
|
967
|
+
window.removeEventListener("blur", leave);
|
|
968
|
+
clear();
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
return () => {
|
|
972
|
+
if (--users === 0) {
|
|
973
|
+
cleanup?.();
|
|
974
|
+
cleanup = void 0;
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
}, []);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// src/components/FloatingScrollbarProvider.tsx
|
|
981
|
+
var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
|
|
982
|
+
var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
|
|
983
|
+
function isRootScroller(target) {
|
|
984
|
+
return target === document.scrollingElement || target === document.documentElement;
|
|
985
|
+
}
|
|
986
|
+
function canScroll(target, axis) {
|
|
987
|
+
if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
|
|
988
|
+
if (isRootScroller(target)) {
|
|
989
|
+
return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
|
|
990
|
+
}
|
|
991
|
+
const style = getComputedStyle(target);
|
|
992
|
+
const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
|
|
993
|
+
const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
|
|
994
|
+
return hasOverflow && scrollableOverflow.has(overflow);
|
|
995
|
+
}
|
|
996
|
+
function visibleRect(target) {
|
|
997
|
+
if (isRootScroller(target)) {
|
|
998
|
+
return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
|
|
999
|
+
}
|
|
1000
|
+
const ownRect = target.getBoundingClientRect();
|
|
1001
|
+
let top = Math.max(0, ownRect.top);
|
|
1002
|
+
let right = Math.min(window.innerWidth, ownRect.right);
|
|
1003
|
+
let bottom = Math.min(window.innerHeight, ownRect.bottom);
|
|
1004
|
+
let left = Math.max(0, ownRect.left);
|
|
1005
|
+
let parent = target.parentElement;
|
|
1006
|
+
while (parent && parent !== document.body && parent !== document.documentElement) {
|
|
1007
|
+
const style = getComputedStyle(parent);
|
|
1008
|
+
const clipX = clippingOverflow.has(style.overflowX);
|
|
1009
|
+
const clipY = clippingOverflow.has(style.overflowY);
|
|
1010
|
+
if (clipX || clipY) {
|
|
1011
|
+
const rect = parent.getBoundingClientRect();
|
|
1012
|
+
if (clipX) {
|
|
1013
|
+
left = Math.max(left, rect.left);
|
|
1014
|
+
right = Math.min(right, rect.right);
|
|
1015
|
+
}
|
|
1016
|
+
if (clipY) {
|
|
1017
|
+
top = Math.max(top, rect.top);
|
|
1018
|
+
bottom = Math.min(bottom, rect.bottom);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
parent = parent.parentElement;
|
|
1022
|
+
}
|
|
1023
|
+
return {
|
|
1024
|
+
top,
|
|
1025
|
+
right,
|
|
1026
|
+
bottom,
|
|
1027
|
+
left,
|
|
1028
|
+
width: Math.max(0, right - left),
|
|
1029
|
+
height: Math.max(0, bottom - top)
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
function createTrack(axis) {
|
|
1033
|
+
const track = document.createElement("div");
|
|
1034
|
+
const thumb = document.createElement("div");
|
|
1035
|
+
track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
|
|
1036
|
+
thumb.className = "sia-floating-scrollbar__thumb";
|
|
1037
|
+
track.dataset.axis = axis;
|
|
1038
|
+
track.setAttribute("aria-hidden", "true");
|
|
1039
|
+
track.append(thumb);
|
|
1040
|
+
return { track, thumb };
|
|
1041
|
+
}
|
|
1042
|
+
function FloatingScrollbarProvider({ disabled = false, targetRef }) {
|
|
1043
|
+
useScrollbarProximity();
|
|
1044
|
+
useEffect6(() => {
|
|
1045
|
+
if (disabled) return;
|
|
1046
|
+
const layer = document.createElement("div");
|
|
1047
|
+
layer.className = "sia-floating-scrollbar-layer";
|
|
1048
|
+
layer.setAttribute("aria-hidden", "true");
|
|
1049
|
+
document.body.append(layer);
|
|
1050
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1051
|
+
let scanFrame = 0;
|
|
1052
|
+
let updateFrame = 0;
|
|
1053
|
+
const setEntryVisible = (entry, visible) => {
|
|
1054
|
+
entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
|
|
1055
|
+
entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
|
|
1056
|
+
};
|
|
1057
|
+
const showEntry = (entry) => {
|
|
1058
|
+
if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
|
|
1059
|
+
entry.hideTimer = null;
|
|
1060
|
+
setEntryVisible(entry, true);
|
|
1061
|
+
};
|
|
1062
|
+
const scheduleHide = (entry, delay = 500) => {
|
|
1063
|
+
if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
|
|
1064
|
+
entry.hideTimer = window.setTimeout(() => {
|
|
1065
|
+
entry.hideTimer = null;
|
|
1066
|
+
if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
|
|
1067
|
+
}, delay);
|
|
1068
|
+
};
|
|
1069
|
+
const updateEntry = (entry) => {
|
|
1070
|
+
const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
|
|
1071
|
+
const hasHorizontal = canScroll(target, "horizontal");
|
|
1072
|
+
const hasVertical = canScroll(target, "vertical");
|
|
1073
|
+
const rect = visibleRect(target);
|
|
1074
|
+
const thickness = 10;
|
|
1075
|
+
horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
|
|
1076
|
+
verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
|
|
1077
|
+
if (!horizontalTrack.hidden) {
|
|
1078
|
+
const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
|
|
1079
|
+
const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
|
|
1080
|
+
const travel = Math.max(0, trackWidth - thumbWidth);
|
|
1081
|
+
const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
|
|
1082
|
+
horizontalTrack.style.left = `${rect.left}px`;
|
|
1083
|
+
horizontalTrack.style.top = `${rect.bottom - thickness}px`;
|
|
1084
|
+
horizontalTrack.style.width = `${trackWidth}px`;
|
|
1085
|
+
horizontalTrack.style.height = `${thickness}px`;
|
|
1086
|
+
horizontalThumb.style.width = `${thumbWidth}px`;
|
|
1087
|
+
horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
|
|
1028
1088
|
}
|
|
1029
|
-
if (
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1089
|
+
if (!verticalTrack.hidden) {
|
|
1090
|
+
const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
|
|
1091
|
+
const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
|
|
1092
|
+
const travel = Math.max(0, trackHeight - thumbHeight);
|
|
1093
|
+
const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
|
|
1094
|
+
verticalTrack.style.left = `${rect.right - thickness}px`;
|
|
1095
|
+
verticalTrack.style.top = `${rect.top}px`;
|
|
1096
|
+
verticalTrack.style.width = `${thickness}px`;
|
|
1097
|
+
verticalTrack.style.height = `${trackHeight}px`;
|
|
1098
|
+
verticalThumb.style.height = `${thumbHeight}px`;
|
|
1099
|
+
verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
|
|
1035
1100
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
if (
|
|
1101
|
+
};
|
|
1102
|
+
const scheduleUpdateAll = () => {
|
|
1103
|
+
if (updateFrame) return;
|
|
1104
|
+
updateFrame = window.requestAnimationFrame(() => {
|
|
1105
|
+
updateFrame = 0;
|
|
1106
|
+
entries.forEach(updateEntry);
|
|
1107
|
+
});
|
|
1108
|
+
};
|
|
1109
|
+
const bindTrackDrag = (entry, axis, track, thumb) => {
|
|
1110
|
+
const target = entry.target;
|
|
1111
|
+
const onTrackPointerEnter = () => {
|
|
1112
|
+
entry.hoveringTrack = true;
|
|
1113
|
+
showEntry(entry);
|
|
1114
|
+
};
|
|
1115
|
+
const onTrackPointerLeave = () => {
|
|
1116
|
+
entry.hoveringTrack = false;
|
|
1117
|
+
scheduleHide(entry);
|
|
1118
|
+
};
|
|
1119
|
+
const onTrackPointerDown = (event) => {
|
|
1120
|
+
if (event.target === thumb || event.button !== 0) return;
|
|
1039
1121
|
event.preventDefault();
|
|
1040
|
-
|
|
1041
|
-
|
|
1122
|
+
event.stopPropagation();
|
|
1123
|
+
const trackRect = track.getBoundingClientRect();
|
|
1124
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
1125
|
+
const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
|
|
1126
|
+
const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
|
|
1127
|
+
const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
|
|
1128
|
+
const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
|
|
1129
|
+
if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
|
|
1130
|
+
else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
|
|
1131
|
+
};
|
|
1132
|
+
const onThumbPointerDown = (event) => {
|
|
1133
|
+
if (event.button !== 0) return;
|
|
1042
1134
|
event.preventDefault();
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1135
|
+
event.stopPropagation();
|
|
1136
|
+
entry.dragging = true;
|
|
1137
|
+
showEntry(entry);
|
|
1138
|
+
thumb.setPointerCapture(event.pointerId);
|
|
1139
|
+
const startPointer = axis === "vertical" ? event.clientY : event.clientX;
|
|
1140
|
+
const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
|
|
1141
|
+
const trackRect = track.getBoundingClientRect();
|
|
1142
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
1143
|
+
const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
|
|
1144
|
+
const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
|
|
1145
|
+
const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
|
|
1146
|
+
const travel = Math.max(1, trackLength - thumbLength);
|
|
1147
|
+
const onPointerMove = (moveEvent) => {
|
|
1148
|
+
if (moveEvent.pointerId !== event.pointerId) return;
|
|
1149
|
+
const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
|
|
1150
|
+
const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
|
|
1151
|
+
if (axis === "vertical") target.scrollTop = nextScroll;
|
|
1152
|
+
else target.scrollLeft = nextScroll;
|
|
1153
|
+
};
|
|
1154
|
+
const onPointerUp = (upEvent) => {
|
|
1155
|
+
if (upEvent.pointerId !== event.pointerId) return;
|
|
1156
|
+
entry.dragging = false;
|
|
1157
|
+
thumb.releasePointerCapture(event.pointerId);
|
|
1158
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
1159
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
1160
|
+
window.removeEventListener("pointercancel", onPointerUp);
|
|
1161
|
+
scheduleHide(entry);
|
|
1162
|
+
};
|
|
1163
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
1164
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
1165
|
+
window.addEventListener("pointercancel", onPointerUp);
|
|
1166
|
+
};
|
|
1167
|
+
track.addEventListener("pointerenter", onTrackPointerEnter);
|
|
1168
|
+
track.addEventListener("pointerleave", onTrackPointerLeave);
|
|
1169
|
+
track.addEventListener("pointerdown", onTrackPointerDown);
|
|
1170
|
+
thumb.addEventListener("pointerdown", onThumbPointerDown);
|
|
1171
|
+
return () => {
|
|
1172
|
+
track.removeEventListener("pointerenter", onTrackPointerEnter);
|
|
1173
|
+
track.removeEventListener("pointerleave", onTrackPointerLeave);
|
|
1174
|
+
track.removeEventListener("pointerdown", onTrackPointerDown);
|
|
1175
|
+
thumb.removeEventListener("pointerdown", onThumbPointerDown);
|
|
1176
|
+
};
|
|
1177
|
+
};
|
|
1178
|
+
const resizeObserver = new ResizeObserver((observedEntries) => {
|
|
1179
|
+
observedEntries.forEach(({ target }) => {
|
|
1180
|
+
const entry = entries.get(target);
|
|
1181
|
+
if (entry) updateEntry(entry);
|
|
1182
|
+
});
|
|
1183
|
+
});
|
|
1184
|
+
const addTarget = (target) => {
|
|
1185
|
+
if (entries.has(target)) return;
|
|
1186
|
+
const horizontal = createTrack("horizontal");
|
|
1187
|
+
const vertical = createTrack("vertical");
|
|
1188
|
+
layer.append(horizontal.track, vertical.track);
|
|
1189
|
+
const onPointerEnter = () => {
|
|
1190
|
+
const entry2 = entries.get(target);
|
|
1191
|
+
if (!entry2) return;
|
|
1192
|
+
entry2.hoveringTarget = true;
|
|
1193
|
+
showEntry(entry2);
|
|
1194
|
+
};
|
|
1195
|
+
const onPointerLeave = () => {
|
|
1196
|
+
const entry2 = entries.get(target);
|
|
1197
|
+
if (!entry2) return;
|
|
1198
|
+
entry2.hoveringTarget = false;
|
|
1199
|
+
scheduleHide(entry2);
|
|
1200
|
+
};
|
|
1201
|
+
const onScroll = () => {
|
|
1202
|
+
const entry2 = entries.get(target);
|
|
1203
|
+
if (!entry2) return;
|
|
1204
|
+
updateEntry(entry2);
|
|
1205
|
+
showEntry(entry2);
|
|
1206
|
+
scheduleHide(entry2, 700);
|
|
1207
|
+
};
|
|
1208
|
+
target.addEventListener("pointerenter", onPointerEnter);
|
|
1209
|
+
target.addEventListener("pointerleave", onPointerLeave);
|
|
1210
|
+
target.addEventListener("scroll", onScroll, { passive: true });
|
|
1211
|
+
const entry = {
|
|
1212
|
+
target,
|
|
1213
|
+
horizontalTrack: horizontal.track,
|
|
1214
|
+
horizontalThumb: horizontal.thumb,
|
|
1215
|
+
verticalTrack: vertical.track,
|
|
1216
|
+
verticalThumb: vertical.thumb,
|
|
1217
|
+
hideTimer: null,
|
|
1218
|
+
hoveringTarget: target.matches(":hover"),
|
|
1219
|
+
hoveringTrack: false,
|
|
1220
|
+
dragging: false,
|
|
1221
|
+
cleanup: () => void 0
|
|
1222
|
+
};
|
|
1223
|
+
const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
|
|
1224
|
+
const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
|
|
1225
|
+
entry.cleanup = () => {
|
|
1226
|
+
if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
|
|
1227
|
+
cleanupHorizontal();
|
|
1228
|
+
cleanupVertical();
|
|
1229
|
+
target.removeEventListener("pointerenter", onPointerEnter);
|
|
1230
|
+
target.removeEventListener("pointerleave", onPointerLeave);
|
|
1231
|
+
target.removeEventListener("scroll", onScroll);
|
|
1232
|
+
resizeObserver.unobserve(target);
|
|
1233
|
+
target.removeAttribute("data-sia-floating-scrollbar");
|
|
1234
|
+
horizontal.track.remove();
|
|
1235
|
+
vertical.track.remove();
|
|
1236
|
+
};
|
|
1237
|
+
entries.set(target, entry);
|
|
1238
|
+
target.setAttribute("data-sia-floating-scrollbar", "");
|
|
1239
|
+
resizeObserver.observe(target);
|
|
1240
|
+
updateEntry(entry);
|
|
1241
|
+
if (entry.hoveringTarget) showEntry(entry);
|
|
1242
|
+
};
|
|
1243
|
+
const removeTarget = (target) => {
|
|
1244
|
+
const entry = entries.get(target);
|
|
1245
|
+
if (!entry) return;
|
|
1246
|
+
entry.cleanup();
|
|
1247
|
+
entries.delete(target);
|
|
1248
|
+
};
|
|
1249
|
+
const scanTargets = () => {
|
|
1250
|
+
scanFrame = 0;
|
|
1251
|
+
const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
|
|
1252
|
+
const nextTargets = /* @__PURE__ */ new Set();
|
|
1253
|
+
candidates.forEach((target) => {
|
|
1254
|
+
if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
|
|
1255
|
+
nextTargets.add(target);
|
|
1256
|
+
if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
entries.forEach((_entry, target) => {
|
|
1260
|
+
if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
|
|
1261
|
+
else updateEntry(_entry);
|
|
1262
|
+
});
|
|
1263
|
+
};
|
|
1264
|
+
const scheduleScan = () => {
|
|
1265
|
+
if (scanFrame) return;
|
|
1266
|
+
scanFrame = window.requestAnimationFrame(scanTargets);
|
|
1267
|
+
};
|
|
1268
|
+
const mutationObserver = new MutationObserver((mutations) => {
|
|
1269
|
+
if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
|
|
1270
|
+
});
|
|
1271
|
+
mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
|
|
1272
|
+
window.addEventListener("resize", scheduleScan);
|
|
1273
|
+
window.addEventListener("scroll", scheduleUpdateAll, true);
|
|
1274
|
+
scheduleScan();
|
|
1047
1275
|
return () => {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1276
|
+
mutationObserver.disconnect();
|
|
1277
|
+
resizeObserver.disconnect();
|
|
1278
|
+
window.removeEventListener("resize", scheduleScan);
|
|
1279
|
+
window.removeEventListener("scroll", scheduleUpdateAll, true);
|
|
1280
|
+
if (scanFrame) window.cancelAnimationFrame(scanFrame);
|
|
1281
|
+
if (updateFrame) window.cancelAnimationFrame(updateFrame);
|
|
1282
|
+
entries.forEach((entry) => entry.cleanup());
|
|
1283
|
+
entries.clear();
|
|
1284
|
+
layer.remove();
|
|
1054
1285
|
};
|
|
1055
|
-
}, [
|
|
1056
|
-
|
|
1057
|
-
function OverlayPortal({ children, container }) {
|
|
1058
|
-
if (typeof document === "undefined") return null;
|
|
1059
|
-
const target = typeof container === "function" ? container() : container ?? document.body;
|
|
1060
|
-
return createPortal2(children, target);
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
// src/components/Modal.tsx
|
|
1064
|
-
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1065
|
-
var modalHandles = /* @__PURE__ */ new Set();
|
|
1066
|
-
var MODAL_MOTION_DURATION = 220;
|
|
1067
|
-
var typeIcons = {
|
|
1068
|
-
info: /* @__PURE__ */ jsx3(Icon, { name: "info", size: 23 }),
|
|
1069
|
-
success: /* @__PURE__ */ jsx3(Icon, { name: "circle-check", size: 23 }),
|
|
1070
|
-
warning: /* @__PURE__ */ jsx3(Icon, { name: "warning", size: 23 }),
|
|
1071
|
-
error: /* @__PURE__ */ jsx3(Icon, { name: "circle-close", size: 23 }),
|
|
1072
|
-
confirm: /* @__PURE__ */ jsx3(Icon, { name: "question", size: 23 })
|
|
1073
|
-
};
|
|
1074
|
-
var ModalRoot = forwardRef2(function Modal({
|
|
1075
|
-
open,
|
|
1076
|
-
title,
|
|
1077
|
-
children,
|
|
1078
|
-
footer,
|
|
1079
|
-
footerExtra,
|
|
1080
|
-
type = "default",
|
|
1081
|
-
okText = "\u786E\u5B9A",
|
|
1082
|
-
cancelText = "\u53D6\u6D88",
|
|
1083
|
-
showCancel = type === "default" || type === "confirm",
|
|
1084
|
-
confirmLoading = false,
|
|
1085
|
-
okButtonProps,
|
|
1086
|
-
cancelButtonProps,
|
|
1087
|
-
closable = true,
|
|
1088
|
-
maskClosable = true,
|
|
1089
|
-
keyboard = true,
|
|
1090
|
-
centered = false,
|
|
1091
|
-
width = 520,
|
|
1092
|
-
height,
|
|
1093
|
-
zIndex = 1e3,
|
|
1094
|
-
getContainer,
|
|
1095
|
-
onOk,
|
|
1096
|
-
onCancel,
|
|
1097
|
-
onOpenChange,
|
|
1098
|
-
afterOpenChange,
|
|
1099
|
-
className = "",
|
|
1100
|
-
style,
|
|
1101
|
-
...props
|
|
1102
|
-
}, forwardedRef) {
|
|
1103
|
-
const localRef = useRef4(null);
|
|
1104
|
-
const titleId = useId3();
|
|
1105
|
-
const [submitting, setSubmitting] = useState2(false);
|
|
1106
|
-
const [rendered, setRendered] = useState2(open);
|
|
1107
|
-
const [closing, setClosing] = useState2(false);
|
|
1108
|
-
const afterOpenChangeRef = useRef4(afterOpenChange);
|
|
1109
|
-
afterOpenChangeRef.current = afterOpenChange;
|
|
1110
|
-
const submitLock = useRef4(false);
|
|
1111
|
-
useEffect6(() => {
|
|
1112
|
-
if (!open || !rendered) return void 0;
|
|
1113
|
-
const timer = window.setTimeout(() => afterOpenChangeRef.current?.(true), MODAL_MOTION_DURATION);
|
|
1114
|
-
return () => window.clearTimeout(timer);
|
|
1115
|
-
}, [open, rendered]);
|
|
1116
|
-
useEffect6(() => {
|
|
1117
|
-
if (open) {
|
|
1118
|
-
setRendered(true);
|
|
1119
|
-
setClosing(false);
|
|
1120
|
-
return void 0;
|
|
1121
|
-
}
|
|
1122
|
-
if (!rendered) return void 0;
|
|
1123
|
-
setClosing(true);
|
|
1124
|
-
const timer = window.setTimeout(() => {
|
|
1125
|
-
setRendered(false);
|
|
1126
|
-
setClosing(false);
|
|
1127
|
-
afterOpenChangeRef.current?.(false);
|
|
1128
|
-
}, MODAL_MOTION_DURATION);
|
|
1129
|
-
return () => window.clearTimeout(timer);
|
|
1130
|
-
}, [open, rendered]);
|
|
1131
|
-
function setRef(node) {
|
|
1132
|
-
localRef.current = node;
|
|
1133
|
-
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
1134
|
-
else if (forwardedRef) forwardedRef.current = node;
|
|
1135
|
-
}
|
|
1136
|
-
function requestClose() {
|
|
1137
|
-
if (submitting || confirmLoading) return;
|
|
1138
|
-
onCancel?.();
|
|
1139
|
-
onOpenChange?.(false);
|
|
1140
|
-
}
|
|
1141
|
-
async function handleOk() {
|
|
1142
|
-
if (submitLock.current || confirmLoading) return;
|
|
1143
|
-
submitLock.current = true;
|
|
1144
|
-
if (!onOk) {
|
|
1145
|
-
onOpenChange?.(false);
|
|
1146
|
-
submitLock.current = false;
|
|
1147
|
-
return;
|
|
1148
|
-
}
|
|
1149
|
-
try {
|
|
1150
|
-
const result = onOk();
|
|
1151
|
-
if (result instanceof Promise) {
|
|
1152
|
-
setSubmitting(true);
|
|
1153
|
-
const resolved = await result;
|
|
1154
|
-
if (resolved !== false) onOpenChange?.(false);
|
|
1155
|
-
} else if (result !== false) {
|
|
1156
|
-
onOpenChange?.(false);
|
|
1157
|
-
}
|
|
1158
|
-
} catch {
|
|
1159
|
-
return;
|
|
1160
|
-
} finally {
|
|
1161
|
-
submitLock.current = false;
|
|
1162
|
-
setSubmitting(false);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
useOverlayLifecycle(rendered, localRef, requestClose, keyboard);
|
|
1166
|
-
if (!rendered) return null;
|
|
1167
|
-
const mergedStyle = {
|
|
1168
|
-
...height !== void 0 ? { display: "flex", flexDirection: "column", height, maxHeight: "calc(100dvh - 48px)" } : {},
|
|
1169
|
-
...style,
|
|
1170
|
-
"--sia-modal-width": typeof width === "number" ? `${width}px` : width
|
|
1171
|
-
};
|
|
1172
|
-
const loading = submitting || confirmLoading;
|
|
1173
|
-
const defaultFooter = /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1174
|
-
showCancel ? /* @__PURE__ */ jsx3(Button, { ...cancelButtonProps, onClick: requestClose, disabled: loading || cancelButtonProps?.disabled, children: cancelText }) : null,
|
|
1175
|
-
/* @__PURE__ */ jsx3(Button, { variant: "primary", ...okButtonProps, onClick: handleOk, loading, children: okText })
|
|
1176
|
-
] });
|
|
1177
|
-
return /* @__PURE__ */ jsx3(OverlayPortal, { container: getContainer, children: /* @__PURE__ */ jsx3(
|
|
1178
|
-
"div",
|
|
1179
|
-
{
|
|
1180
|
-
className: `sia-modal-root${centered ? " sia-modal-root--centered" : ""}`,
|
|
1181
|
-
"data-state": closing ? "closing" : "open",
|
|
1182
|
-
style: { zIndex },
|
|
1183
|
-
onMouseDown: (event) => {
|
|
1184
|
-
if (event.target === event.currentTarget && maskClosable) requestClose();
|
|
1185
|
-
},
|
|
1186
|
-
children: /* @__PURE__ */ jsxs3(
|
|
1187
|
-
"div",
|
|
1188
|
-
{
|
|
1189
|
-
ref: setRef,
|
|
1190
|
-
className: `sia-modal sia-modal--${type} ${className}`.trim(),
|
|
1191
|
-
style: mergedStyle,
|
|
1192
|
-
role: "dialog",
|
|
1193
|
-
"aria-modal": "true",
|
|
1194
|
-
"aria-labelledby": title ? titleId : void 0,
|
|
1195
|
-
tabIndex: -1,
|
|
1196
|
-
...withTitleTooltip(props),
|
|
1197
|
-
children: [
|
|
1198
|
-
/* @__PURE__ */ jsxs3("div", { className: "sia-modal__header", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
|
|
1199
|
-
/* @__PURE__ */ jsxs3("div", { className: "sia-modal__heading", children: [
|
|
1200
|
-
typeIcons[type] ? /* @__PURE__ */ jsx3("span", { className: "sia-modal__type-icon", "aria-hidden": "true", children: typeIcons[type] }) : null,
|
|
1201
|
-
title ? /* @__PURE__ */ jsx3("strong", { id: titleId, children: title }) : null
|
|
1202
|
-
] }),
|
|
1203
|
-
closable ? /* @__PURE__ */ jsx3("button", { type: "button", className: "sia-modal__close", "aria-label": "\u5173\u95ED\u5BF9\u8BDD\u6846", onClick: requestClose, children: /* @__PURE__ */ jsx3(Icon, { name: "close", size: 17 }) }) : null
|
|
1204
|
-
] }),
|
|
1205
|
-
children !== void 0 ? /* @__PURE__ */ jsx3("div", { className: "sia-modal__body", style: height !== void 0 ? { flex: 1, minHeight: 0, overflow: "auto" } : void 0, children }) : null,
|
|
1206
|
-
footer !== null ? /* @__PURE__ */ jsxs3("div", { className: "sia-modal__footer", style: height !== void 0 ? { flexShrink: 0 } : void 0, children: [
|
|
1207
|
-
footer === void 0 && footerExtra != null ? /* @__PURE__ */ jsx3("div", { style: { marginRight: "auto" }, children: footerExtra }) : null,
|
|
1208
|
-
footer ?? defaultFooter
|
|
1209
|
-
] }) : null
|
|
1210
|
-
]
|
|
1211
|
-
}
|
|
1212
|
-
)
|
|
1213
|
-
}
|
|
1214
|
-
) });
|
|
1215
|
-
});
|
|
1216
|
-
function openModal(config) {
|
|
1217
|
-
if (typeof document === "undefined") return { destroy() {
|
|
1218
|
-
}, update() {
|
|
1219
|
-
} };
|
|
1220
|
-
const container = document.createElement("div");
|
|
1221
|
-
container.className = "sia-modal-api-host";
|
|
1222
|
-
document.body.appendChild(container);
|
|
1223
|
-
const root = createRoot(container);
|
|
1224
|
-
let currentConfig = config;
|
|
1225
|
-
let currentOpen = true;
|
|
1226
|
-
let destroyed = false;
|
|
1227
|
-
let cleanupTimer;
|
|
1228
|
-
function cleanup2() {
|
|
1229
|
-
if (destroyed) return;
|
|
1230
|
-
destroyed = true;
|
|
1231
|
-
if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
|
|
1232
|
-
root.unmount();
|
|
1233
|
-
container.remove();
|
|
1234
|
-
modalHandles.delete(handle);
|
|
1235
|
-
}
|
|
1236
|
-
function closeWithMotion() {
|
|
1237
|
-
if (destroyed || !currentOpen) return;
|
|
1238
|
-
currentOpen = false;
|
|
1239
|
-
render();
|
|
1240
|
-
cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
|
|
1241
|
-
}
|
|
1242
|
-
function render() {
|
|
1243
|
-
root.render(/* @__PURE__ */ jsx3(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
|
|
1244
|
-
currentConfig.onOpenChange?.(nextOpen);
|
|
1245
|
-
if (!nextOpen) closeWithMotion();
|
|
1246
|
-
} }));
|
|
1247
|
-
}
|
|
1248
|
-
const handle = {
|
|
1249
|
-
destroy: closeWithMotion,
|
|
1250
|
-
update(next) {
|
|
1251
|
-
if (destroyed) return;
|
|
1252
|
-
currentConfig = { ...currentConfig, ...next };
|
|
1253
|
-
render();
|
|
1254
|
-
}
|
|
1255
|
-
};
|
|
1256
|
-
modalHandles.add(handle);
|
|
1257
|
-
render();
|
|
1258
|
-
return handle;
|
|
1259
|
-
}
|
|
1260
|
-
function preset(type, defaults = {}) {
|
|
1261
|
-
return (config) => openModal({
|
|
1262
|
-
okText: type === "confirm" ? "\u786E\u5B9A" : "\u77E5\u9053\u4E86",
|
|
1263
|
-
showCancel: type === "confirm",
|
|
1264
|
-
closable: type !== "confirm",
|
|
1265
|
-
maskClosable: type !== "confirm",
|
|
1266
|
-
...defaults,
|
|
1267
|
-
...config,
|
|
1268
|
-
type
|
|
1269
|
-
});
|
|
1286
|
+
}, [disabled, targetRef]);
|
|
1287
|
+
return null;
|
|
1270
1288
|
}
|
|
1271
|
-
var Modal2 = Object.assign(ModalRoot, {
|
|
1272
|
-
/** 直接打开实例,返回可用于更新或关闭的操作句柄。 */
|
|
1273
|
-
open: openModal,
|
|
1274
|
-
/** 显示普通信息提示。 */
|
|
1275
|
-
info: preset("info", { title: "\u63D0\u793A" }),
|
|
1276
|
-
/** 显示成功提示。 */
|
|
1277
|
-
success: preset("success", { title: "\u64CD\u4F5C\u6210\u529F" }),
|
|
1278
|
-
/** 显示警告提示。 */
|
|
1279
|
-
warning: preset("warning", { title: "\u8BF7\u6CE8\u610F" }),
|
|
1280
|
-
/** 显示错误提示。 */
|
|
1281
|
-
error: preset("error", { title: "\u64CD\u4F5C\u5931\u8D25" }),
|
|
1282
|
-
/** 打开确认弹框,支持异步确认;返回 false 可阻止关闭。 */
|
|
1283
|
-
confirm: preset("confirm", { title: "\u786E\u8BA4\u64CD\u4F5C" }),
|
|
1284
|
-
/** 关闭由此 API 打开的所有弹层。 */
|
|
1285
|
-
destroyAll() {
|
|
1286
|
-
[...modalHandles].forEach((handle) => handle.destroy());
|
|
1287
|
-
}
|
|
1288
|
-
});
|
|
1289
1289
|
|
|
1290
1290
|
// src/components/SelectionPanel.tsx
|
|
1291
1291
|
import { useEffect as useEffect7, useId as useId4, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
|
|
@@ -2006,8 +2006,6 @@ var Select = forwardRef3(function Select2({
|
|
|
2006
2006
|
});
|
|
2007
2007
|
|
|
2008
2008
|
export {
|
|
2009
|
-
useScrollbarProximity,
|
|
2010
|
-
FloatingScrollbarProvider,
|
|
2011
2009
|
Checkbox2 as Checkbox,
|
|
2012
2010
|
Tooltip,
|
|
2013
2011
|
Popover,
|
|
@@ -2016,6 +2014,8 @@ export {
|
|
|
2016
2014
|
useOverlayLifecycle,
|
|
2017
2015
|
OverlayPortal,
|
|
2018
2016
|
Modal2 as Modal,
|
|
2017
|
+
useScrollbarProximity,
|
|
2018
|
+
FloatingScrollbarProvider,
|
|
2019
2019
|
SelectionPanel,
|
|
2020
2020
|
OverflowTags,
|
|
2021
2021
|
Select
|