@sia.soul/sia-react-ui 0.1.15 → 0.1.18

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/core.cjs CHANGED
@@ -1188,7 +1188,8 @@ function MenuEntry({
1188
1188
  item,
1189
1189
  level,
1190
1190
  parents,
1191
- props
1191
+ props,
1192
+ reserveIcon
1192
1193
  }) {
1193
1194
  const anchorRef = (0, import_react6.useRef)(null);
1194
1195
  const closeTimerRef = (0, import_react6.useRef)(void 0);
@@ -1237,7 +1238,7 @@ function MenuEntry({
1237
1238
  "aria-expanded": hasChildren ? isOpen : void 0,
1238
1239
  onClick: (event) => hasChildren ? props.toggle(item.key) : props.select(item, path, event),
1239
1240
  children: [
1240
- item.icon ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "sia-menu__icon", children: item.icon }) : null,
1241
+ reserveIcon ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "sia-menu__icon", "aria-hidden": !item.icon || void 0, children: item.icon }) : null,
1241
1242
  !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "sia-menu__label", children: item.label }) : null,
1242
1243
  !collapsed && item.extra ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "sia-menu__extra", children: item.extra }) : null,
1243
1244
  !collapsed && hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { className: "sia-menu__expand-icon", name: "chevron-down", size: 13 }) : null
@@ -1261,19 +1262,23 @@ function MenuEntry({
1261
1262
  }
1262
1263
  );
1263
1264
  }
1265
+ function hasMenuIcon(items) {
1266
+ return items.some((item) => item.type === "group" ? hasMenuIcon(item.children ?? []) : item.type !== "divider" && Boolean(item.icon));
1267
+ }
1264
1268
  function MenuLevel({
1265
1269
  items,
1266
1270
  level,
1267
1271
  parents,
1268
- props
1272
+ props,
1273
+ reserveIcon = hasMenuIcon(items)
1269
1274
  }) {
1270
1275
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: items.map((item, index) => {
1271
1276
  if (item.type === "divider") return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { className: "sia-menu__divider", role: "separator" }, item.key || `divider-${index}`);
1272
1277
  if (item.type === "group") return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: "sia-menu__group", children: [
1273
1278
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "sia-menu__group-title", children: item.label }),
1274
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { role: "group", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuLevel, { items: item.children ?? [], level: level + 1, parents: [...parents, item.key], props }) })
1279
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { role: "group", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuLevel, { items: item.children ?? [], level: level + 1, parents: [...parents, item.key], props, reserveIcon }) })
1275
1280
  ] }, item.key);
1276
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuEntry, { item, level, parents, props }, item.key);
1281
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuEntry, { item, level, parents, props, reserveIcon }, item.key);
1277
1282
  }) });
1278
1283
  }
1279
1284
  function Menu({
@@ -2295,7 +2300,7 @@ function useFloatingPlaceholder({ mode, placeholder, filled, active = false }) {
2295
2300
  }
2296
2301
 
2297
2302
  // src/components/Select.tsx
2298
- var import_react18 = require("react");
2303
+ var import_react20 = require("react");
2299
2304
 
2300
2305
  // src/components/Modal.tsx
2301
2306
  var import_react14 = require("react");
@@ -2458,7 +2463,7 @@ function openModal(config) {
2458
2463
  let currentOpen = true;
2459
2464
  let destroyed = false;
2460
2465
  let cleanupTimer;
2461
- function cleanup() {
2466
+ function cleanup2() {
2462
2467
  if (destroyed) return;
2463
2468
  destroyed = true;
2464
2469
  if (cleanupTimer !== void 0) window.clearTimeout(cleanupTimer);
@@ -2470,7 +2475,7 @@ function openModal(config) {
2470
2475
  if (destroyed || !currentOpen) return;
2471
2476
  currentOpen = false;
2472
2477
  render();
2473
- cleanupTimer = window.setTimeout(cleanup, MODAL_MOTION_DURATION);
2478
+ cleanupTimer = window.setTimeout(cleanup2, MODAL_MOTION_DURATION);
2474
2479
  }
2475
2480
  function render() {
2476
2481
  root.render(/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ModalRoot, { ...currentConfig, open: currentOpen, onOpenChange: (nextOpen) => {
@@ -2840,13 +2845,388 @@ function OverflowTags({ items, maxCount: maxCount2, className = "", tagClassName
2840
2845
  ] });
2841
2846
  }
2842
2847
 
2848
+ // src/components/FloatingScrollbarProvider.tsx
2849
+ var import_react19 = require("react");
2850
+
2851
+ // src/components/useScrollbarProximity.ts
2852
+ var import_react18 = require("react");
2853
+ var users = 0;
2854
+ var cleanup;
2855
+ function useScrollbarProximity() {
2856
+ (0, import_react18.useEffect)(() => {
2857
+ if (users++ === 0) {
2858
+ let frame = 0;
2859
+ let x = -Infinity;
2860
+ let y = -Infinity;
2861
+ const nearby = /* @__PURE__ */ new Set();
2862
+ const clear = () => {
2863
+ nearby.forEach((track) => track.removeAttribute("data-scrollbar-near"));
2864
+ nearby.clear();
2865
+ };
2866
+ const update = () => {
2867
+ frame = 0;
2868
+ const next = /* @__PURE__ */ new Set();
2869
+ document.querySelectorAll(".sia-scrollbar-track").forEach((track) => {
2870
+ const rect = track.getBoundingClientRect();
2871
+ const proximity = Number.parseFloat(getComputedStyle(track).getPropertyValue("--sia-scrollbar-proximity")) || 16;
2872
+ if (rect.width && rect.height && x >= rect.left - proximity && x <= rect.right + proximity && y >= rect.top - proximity && y <= rect.bottom + proximity) {
2873
+ next.add(track);
2874
+ if (!nearby.has(track)) track.setAttribute("data-scrollbar-near", "true");
2875
+ }
2876
+ });
2877
+ nearby.forEach((track) => {
2878
+ if (!next.has(track)) track.removeAttribute("data-scrollbar-near");
2879
+ });
2880
+ nearby.clear();
2881
+ next.forEach((track) => nearby.add(track));
2882
+ };
2883
+ const move = (event) => {
2884
+ if (event.pointerType === "touch") return;
2885
+ x = event.clientX;
2886
+ y = event.clientY;
2887
+ if (!frame) frame = requestAnimationFrame(update);
2888
+ };
2889
+ const leave = () => {
2890
+ x = y = -Infinity;
2891
+ clear();
2892
+ };
2893
+ document.addEventListener("pointermove", move, { passive: true });
2894
+ document.documentElement.addEventListener("pointerleave", leave);
2895
+ window.addEventListener("blur", leave);
2896
+ cleanup = () => {
2897
+ if (frame) cancelAnimationFrame(frame);
2898
+ document.removeEventListener("pointermove", move);
2899
+ document.documentElement.removeEventListener("pointerleave", leave);
2900
+ window.removeEventListener("blur", leave);
2901
+ clear();
2902
+ };
2903
+ }
2904
+ return () => {
2905
+ if (--users === 0) {
2906
+ cleanup?.();
2907
+ cleanup = void 0;
2908
+ }
2909
+ };
2910
+ }, []);
2911
+ }
2912
+
2913
+ // src/components/FloatingScrollbarProvider.tsx
2914
+ var scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
2915
+ var clippingOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
2916
+ function isRootScroller(target) {
2917
+ return target === document.scrollingElement || target === document.documentElement;
2918
+ }
2919
+ function canScroll(target, axis) {
2920
+ if (target.matches(".sia-table__viewport, [data-sia-native-scrollbar]")) return false;
2921
+ if (isRootScroller(target)) {
2922
+ return axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2923
+ }
2924
+ const style = getComputedStyle(target);
2925
+ const overflow = axis === "horizontal" ? style.overflowX : style.overflowY;
2926
+ const hasOverflow = axis === "horizontal" ? target.scrollWidth > target.clientWidth + 1 : target.scrollHeight > target.clientHeight + 1;
2927
+ return hasOverflow && scrollableOverflow.has(overflow);
2928
+ }
2929
+ function visibleRect(target) {
2930
+ if (isRootScroller(target)) {
2931
+ return { top: 0, right: window.innerWidth, bottom: window.innerHeight, left: 0, width: window.innerWidth, height: window.innerHeight };
2932
+ }
2933
+ const ownRect = target.getBoundingClientRect();
2934
+ let top = Math.max(0, ownRect.top);
2935
+ let right = Math.min(window.innerWidth, ownRect.right);
2936
+ let bottom = Math.min(window.innerHeight, ownRect.bottom);
2937
+ let left = Math.max(0, ownRect.left);
2938
+ let parent = target.parentElement;
2939
+ while (parent && parent !== document.body && parent !== document.documentElement) {
2940
+ const style = getComputedStyle(parent);
2941
+ const clipX = clippingOverflow.has(style.overflowX);
2942
+ const clipY = clippingOverflow.has(style.overflowY);
2943
+ if (clipX || clipY) {
2944
+ const rect = parent.getBoundingClientRect();
2945
+ if (clipX) {
2946
+ left = Math.max(left, rect.left);
2947
+ right = Math.min(right, rect.right);
2948
+ }
2949
+ if (clipY) {
2950
+ top = Math.max(top, rect.top);
2951
+ bottom = Math.min(bottom, rect.bottom);
2952
+ }
2953
+ }
2954
+ parent = parent.parentElement;
2955
+ }
2956
+ return {
2957
+ top,
2958
+ right,
2959
+ bottom,
2960
+ left,
2961
+ width: Math.max(0, right - left),
2962
+ height: Math.max(0, bottom - top)
2963
+ };
2964
+ }
2965
+ function createTrack(axis) {
2966
+ const track = document.createElement("div");
2967
+ const thumb = document.createElement("div");
2968
+ track.className = `sia-scrollbar-track sia-floating-scrollbar sia-floating-scrollbar--${axis}`;
2969
+ thumb.className = "sia-floating-scrollbar__thumb";
2970
+ track.dataset.axis = axis;
2971
+ track.setAttribute("aria-hidden", "true");
2972
+ track.append(thumb);
2973
+ return { track, thumb };
2974
+ }
2975
+ function FloatingScrollbarProvider({ disabled = false, targetRef }) {
2976
+ useScrollbarProximity();
2977
+ (0, import_react19.useEffect)(() => {
2978
+ if (disabled) return;
2979
+ const layer = document.createElement("div");
2980
+ layer.className = "sia-floating-scrollbar-layer";
2981
+ layer.setAttribute("aria-hidden", "true");
2982
+ document.body.append(layer);
2983
+ const entries = /* @__PURE__ */ new Map();
2984
+ let scanFrame = 0;
2985
+ let updateFrame = 0;
2986
+ const setEntryVisible = (entry, visible) => {
2987
+ entry.horizontalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2988
+ entry.verticalTrack.classList.toggle("sia-floating-scrollbar--visible", visible);
2989
+ };
2990
+ const showEntry = (entry) => {
2991
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2992
+ entry.hideTimer = null;
2993
+ setEntryVisible(entry, true);
2994
+ };
2995
+ const scheduleHide = (entry, delay = 500) => {
2996
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
2997
+ entry.hideTimer = window.setTimeout(() => {
2998
+ entry.hideTimer = null;
2999
+ if (!entry.hoveringTarget && !entry.hoveringTrack && !entry.dragging) setEntryVisible(entry, false);
3000
+ }, delay);
3001
+ };
3002
+ const updateEntry = (entry) => {
3003
+ const { target, horizontalTrack, horizontalThumb, verticalTrack, verticalThumb } = entry;
3004
+ const hasHorizontal = canScroll(target, "horizontal");
3005
+ const hasVertical = canScroll(target, "vertical");
3006
+ const rect = visibleRect(target);
3007
+ const thickness = 10;
3008
+ horizontalTrack.hidden = !hasHorizontal || rect.width <= thickness || rect.height <= 0;
3009
+ verticalTrack.hidden = !hasVertical || rect.height <= thickness || rect.width <= 0;
3010
+ if (!horizontalTrack.hidden) {
3011
+ const trackWidth = Math.max(0, rect.width - (hasVertical ? thickness : 0));
3012
+ const thumbWidth = Math.max(24, Math.min(trackWidth, trackWidth * target.clientWidth / target.scrollWidth));
3013
+ const travel = Math.max(0, trackWidth - thumbWidth);
3014
+ const maxScroll = Math.max(1, target.scrollWidth - target.clientWidth);
3015
+ horizontalTrack.style.left = `${rect.left}px`;
3016
+ horizontalTrack.style.top = `${rect.bottom - thickness}px`;
3017
+ horizontalTrack.style.width = `${trackWidth}px`;
3018
+ horizontalTrack.style.height = `${thickness}px`;
3019
+ horizontalThumb.style.width = `${thumbWidth}px`;
3020
+ horizontalThumb.style.transform = `translate3d(${travel * target.scrollLeft / maxScroll}px, 0, 0)`;
3021
+ }
3022
+ if (!verticalTrack.hidden) {
3023
+ const trackHeight = Math.max(0, rect.height - (hasHorizontal ? thickness : 0));
3024
+ const thumbHeight = Math.max(24, Math.min(trackHeight, trackHeight * target.clientHeight / target.scrollHeight));
3025
+ const travel = Math.max(0, trackHeight - thumbHeight);
3026
+ const maxScroll = Math.max(1, target.scrollHeight - target.clientHeight);
3027
+ verticalTrack.style.left = `${rect.right - thickness}px`;
3028
+ verticalTrack.style.top = `${rect.top}px`;
3029
+ verticalTrack.style.width = `${thickness}px`;
3030
+ verticalTrack.style.height = `${trackHeight}px`;
3031
+ verticalThumb.style.height = `${thumbHeight}px`;
3032
+ verticalThumb.style.transform = `translate3d(0, ${travel * target.scrollTop / maxScroll}px, 0)`;
3033
+ }
3034
+ };
3035
+ const scheduleUpdateAll = () => {
3036
+ if (updateFrame) return;
3037
+ updateFrame = window.requestAnimationFrame(() => {
3038
+ updateFrame = 0;
3039
+ entries.forEach(updateEntry);
3040
+ });
3041
+ };
3042
+ const bindTrackDrag = (entry, axis, track, thumb) => {
3043
+ const target = entry.target;
3044
+ const onTrackPointerEnter = () => {
3045
+ entry.hoveringTrack = true;
3046
+ showEntry(entry);
3047
+ };
3048
+ const onTrackPointerLeave = () => {
3049
+ entry.hoveringTrack = false;
3050
+ scheduleHide(entry);
3051
+ };
3052
+ const onTrackPointerDown = (event) => {
3053
+ if (event.target === thumb || event.button !== 0) return;
3054
+ event.preventDefault();
3055
+ event.stopPropagation();
3056
+ const trackRect = track.getBoundingClientRect();
3057
+ const thumbRect = thumb.getBoundingClientRect();
3058
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3059
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3060
+ const pointer = axis === "vertical" ? event.clientY - trackRect.top : event.clientX - trackRect.left;
3061
+ const ratio = Math.max(0, Math.min(1, (pointer - thumbLength / 2) / Math.max(1, trackLength - thumbLength)));
3062
+ if (axis === "vertical") target.scrollTop = ratio * (target.scrollHeight - target.clientHeight);
3063
+ else target.scrollLeft = ratio * (target.scrollWidth - target.clientWidth);
3064
+ };
3065
+ const onThumbPointerDown = (event) => {
3066
+ if (event.button !== 0) return;
3067
+ event.preventDefault();
3068
+ event.stopPropagation();
3069
+ entry.dragging = true;
3070
+ showEntry(entry);
3071
+ thumb.setPointerCapture(event.pointerId);
3072
+ const startPointer = axis === "vertical" ? event.clientY : event.clientX;
3073
+ const startScroll = axis === "vertical" ? target.scrollTop : target.scrollLeft;
3074
+ const trackRect = track.getBoundingClientRect();
3075
+ const thumbRect = thumb.getBoundingClientRect();
3076
+ const trackLength = axis === "vertical" ? trackRect.height : trackRect.width;
3077
+ const thumbLength = axis === "vertical" ? thumbRect.height : thumbRect.width;
3078
+ const maxScroll = axis === "vertical" ? target.scrollHeight - target.clientHeight : target.scrollWidth - target.clientWidth;
3079
+ const travel = Math.max(1, trackLength - thumbLength);
3080
+ const onPointerMove = (moveEvent) => {
3081
+ if (moveEvent.pointerId !== event.pointerId) return;
3082
+ const pointer = axis === "vertical" ? moveEvent.clientY : moveEvent.clientX;
3083
+ const nextScroll = startScroll + (pointer - startPointer) * maxScroll / travel;
3084
+ if (axis === "vertical") target.scrollTop = nextScroll;
3085
+ else target.scrollLeft = nextScroll;
3086
+ };
3087
+ const onPointerUp = (upEvent) => {
3088
+ if (upEvent.pointerId !== event.pointerId) return;
3089
+ entry.dragging = false;
3090
+ thumb.releasePointerCapture(event.pointerId);
3091
+ window.removeEventListener("pointermove", onPointerMove);
3092
+ window.removeEventListener("pointerup", onPointerUp);
3093
+ window.removeEventListener("pointercancel", onPointerUp);
3094
+ scheduleHide(entry);
3095
+ };
3096
+ window.addEventListener("pointermove", onPointerMove);
3097
+ window.addEventListener("pointerup", onPointerUp);
3098
+ window.addEventListener("pointercancel", onPointerUp);
3099
+ };
3100
+ track.addEventListener("pointerenter", onTrackPointerEnter);
3101
+ track.addEventListener("pointerleave", onTrackPointerLeave);
3102
+ track.addEventListener("pointerdown", onTrackPointerDown);
3103
+ thumb.addEventListener("pointerdown", onThumbPointerDown);
3104
+ return () => {
3105
+ track.removeEventListener("pointerenter", onTrackPointerEnter);
3106
+ track.removeEventListener("pointerleave", onTrackPointerLeave);
3107
+ track.removeEventListener("pointerdown", onTrackPointerDown);
3108
+ thumb.removeEventListener("pointerdown", onThumbPointerDown);
3109
+ };
3110
+ };
3111
+ const resizeObserver = new ResizeObserver((observedEntries) => {
3112
+ observedEntries.forEach(({ target }) => {
3113
+ const entry = entries.get(target);
3114
+ if (entry) updateEntry(entry);
3115
+ });
3116
+ });
3117
+ const addTarget = (target) => {
3118
+ if (entries.has(target)) return;
3119
+ const horizontal = createTrack("horizontal");
3120
+ const vertical = createTrack("vertical");
3121
+ layer.append(horizontal.track, vertical.track);
3122
+ const onPointerEnter = () => {
3123
+ const entry2 = entries.get(target);
3124
+ if (!entry2) return;
3125
+ entry2.hoveringTarget = true;
3126
+ showEntry(entry2);
3127
+ };
3128
+ const onPointerLeave = () => {
3129
+ const entry2 = entries.get(target);
3130
+ if (!entry2) return;
3131
+ entry2.hoveringTarget = false;
3132
+ scheduleHide(entry2);
3133
+ };
3134
+ const onScroll = () => {
3135
+ const entry2 = entries.get(target);
3136
+ if (!entry2) return;
3137
+ updateEntry(entry2);
3138
+ showEntry(entry2);
3139
+ scheduleHide(entry2, 700);
3140
+ };
3141
+ target.addEventListener("pointerenter", onPointerEnter);
3142
+ target.addEventListener("pointerleave", onPointerLeave);
3143
+ target.addEventListener("scroll", onScroll, { passive: true });
3144
+ const entry = {
3145
+ target,
3146
+ horizontalTrack: horizontal.track,
3147
+ horizontalThumb: horizontal.thumb,
3148
+ verticalTrack: vertical.track,
3149
+ verticalThumb: vertical.thumb,
3150
+ hideTimer: null,
3151
+ hoveringTarget: target.matches(":hover"),
3152
+ hoveringTrack: false,
3153
+ dragging: false,
3154
+ cleanup: () => void 0
3155
+ };
3156
+ const cleanupHorizontal = bindTrackDrag(entry, "horizontal", horizontal.track, horizontal.thumb);
3157
+ const cleanupVertical = bindTrackDrag(entry, "vertical", vertical.track, vertical.thumb);
3158
+ entry.cleanup = () => {
3159
+ if (entry.hideTimer !== null) window.clearTimeout(entry.hideTimer);
3160
+ cleanupHorizontal();
3161
+ cleanupVertical();
3162
+ target.removeEventListener("pointerenter", onPointerEnter);
3163
+ target.removeEventListener("pointerleave", onPointerLeave);
3164
+ target.removeEventListener("scroll", onScroll);
3165
+ resizeObserver.unobserve(target);
3166
+ target.removeAttribute("data-sia-floating-scrollbar");
3167
+ horizontal.track.remove();
3168
+ vertical.track.remove();
3169
+ };
3170
+ entries.set(target, entry);
3171
+ target.setAttribute("data-sia-floating-scrollbar", "");
3172
+ resizeObserver.observe(target);
3173
+ updateEntry(entry);
3174
+ if (entry.hoveringTarget) showEntry(entry);
3175
+ };
3176
+ const removeTarget = (target) => {
3177
+ const entry = entries.get(target);
3178
+ if (!entry) return;
3179
+ entry.cleanup();
3180
+ entries.delete(target);
3181
+ };
3182
+ const scanTargets = () => {
3183
+ scanFrame = 0;
3184
+ const candidates = (targetRef ? [targetRef.current] : [document.scrollingElement, ...document.querySelectorAll("*")]).filter((target) => target instanceof HTMLElement);
3185
+ const nextTargets = /* @__PURE__ */ new Set();
3186
+ candidates.forEach((target) => {
3187
+ if (canScroll(target, "horizontal") || canScroll(target, "vertical")) {
3188
+ nextTargets.add(target);
3189
+ if (entries.has(target) || !target.hasAttribute("data-sia-floating-scrollbar")) addTarget(target);
3190
+ }
3191
+ });
3192
+ entries.forEach((_entry, target) => {
3193
+ if (!target.isConnected || !nextTargets.has(target)) removeTarget(target);
3194
+ else updateEntry(_entry);
3195
+ });
3196
+ };
3197
+ const scheduleScan = () => {
3198
+ if (scanFrame) return;
3199
+ scanFrame = window.requestAnimationFrame(scanTargets);
3200
+ };
3201
+ const mutationObserver = new MutationObserver((mutations) => {
3202
+ if (mutations.some((mutation) => !layer.contains(mutation.target))) scheduleScan();
3203
+ });
3204
+ mutationObserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true });
3205
+ window.addEventListener("resize", scheduleScan);
3206
+ window.addEventListener("scroll", scheduleUpdateAll, true);
3207
+ scheduleScan();
3208
+ return () => {
3209
+ mutationObserver.disconnect();
3210
+ resizeObserver.disconnect();
3211
+ window.removeEventListener("resize", scheduleScan);
3212
+ window.removeEventListener("scroll", scheduleUpdateAll, true);
3213
+ if (scanFrame) window.cancelAnimationFrame(scanFrame);
3214
+ if (updateFrame) window.cancelAnimationFrame(updateFrame);
3215
+ entries.forEach((entry) => entry.cleanup());
3216
+ entries.clear();
3217
+ layer.remove();
3218
+ };
3219
+ }, [disabled, targetRef]);
3220
+ return null;
3221
+ }
3222
+
2843
3223
  // src/components/Select.tsx
2844
3224
  var import_jsx_runtime19 = require("react/jsx-runtime");
2845
3225
  function defaultFilterOption(query, option) {
2846
3226
  const keyword = query.toLowerCase();
2847
3227
  return [option.label, option.value].some((value) => String(value).toLowerCase().includes(keyword));
2848
3228
  }
2849
- var Select = (0, import_react18.forwardRef)(function Select2({
3229
+ var Select = (0, import_react20.forwardRef)(function Select2({
2850
3230
  options,
2851
3231
  mode = "single",
2852
3232
  popupMode = "dropdown",
@@ -2883,20 +3263,20 @@ var Select = (0, import_react18.forwardRef)(function Select2({
2883
3263
  ...props
2884
3264
  }, forwardedRef) {
2885
3265
  const size = useControlSize(sizeProp);
2886
- const fallbackId = (0, import_react18.useId)();
3266
+ const fallbackId = (0, import_react20.useId)();
2887
3267
  const selectId = id ?? fallbackId;
2888
3268
  const listboxId = `${selectId}-listbox`;
2889
- const rootRef = (0, import_react18.useRef)(null);
2890
- const popupRef = (0, import_react18.useRef)(null);
2891
- const searchRef = (0, import_react18.useRef)(null);
2892
- const listRef = (0, import_react18.useRef)(null);
2893
- const [open, setOpen] = (0, import_react18.useState)(false);
2894
- const [modalValues, setModalValues] = (0, import_react18.useState)([]);
2895
- const [selectedOnly, setSelectedOnly] = (0, import_react18.useState)(false);
2896
- const [query, setQuery] = (0, import_react18.useState)("");
2897
- const [activeIndex, setActiveIndex] = (0, import_react18.useState)(-1);
2898
- const [scrollTop, setScrollTop] = (0, import_react18.useState)(0);
2899
- const deferredQuery = (0, import_react18.useDeferredValue)(query.trim());
3269
+ const rootRef = (0, import_react20.useRef)(null);
3270
+ const popupRef = (0, import_react20.useRef)(null);
3271
+ const searchRef = (0, import_react20.useRef)(null);
3272
+ const listRef = (0, import_react20.useRef)(null);
3273
+ const [open, setOpen] = (0, import_react20.useState)(false);
3274
+ const [modalValues, setModalValues] = (0, import_react20.useState)([]);
3275
+ const [selectedOnly, setSelectedOnly] = (0, import_react20.useState)(false);
3276
+ const [query, setQuery] = (0, import_react20.useState)("");
3277
+ const [activeIndex, setActiveIndex] = (0, import_react20.useState)(-1);
3278
+ const [scrollTop, setScrollTop] = (0, import_react20.useState)(0);
3279
+ const deferredQuery = (0, import_react20.useDeferredValue)(query.trim());
2900
3280
  const initialValue = defaultValue === void 0 ? mode === "multiple" ? [] : null : defaultValue;
2901
3281
  const [currentValue, setCurrentValue] = useControllableState({
2902
3282
  value,
@@ -2911,19 +3291,19 @@ var Select = (0, import_react18.forwardRef)(function Select2({
2911
3291
  }
2912
3292
  }
2913
3293
  });
2914
- const selectedValues = (0, import_react18.useMemo)(() => {
3294
+ const selectedValues = (0, import_react20.useMemo)(() => {
2915
3295
  if (mode === "multiple") return Array.isArray(currentValue) ? currentValue : currentValue === null ? [] : [currentValue];
2916
3296
  return Array.isArray(currentValue) ? currentValue.slice(0, 1) : currentValue === null ? [] : [currentValue];
2917
3297
  }, [currentValue, mode]);
2918
- const selectedValueSet = (0, import_react18.useMemo)(() => new Set(selectedValues), [selectedValues]);
2919
- const availableOptions = (0, import_react18.useMemo)(() => {
3298
+ const selectedValueSet = (0, import_react20.useMemo)(() => new Set(selectedValues), [selectedValues]);
3299
+ const availableOptions = (0, import_react20.useMemo)(() => {
2920
3300
  if (!allowInput) return options;
2921
3301
  const optionValueSet = new Set(options.map((option) => option.value));
2922
3302
  const selectedCustomOptions = selectedValues.filter((selectedValue) => !optionValueSet.has(selectedValue)).map((selectedValue) => ({ label: String(selectedValue), value: selectedValue }));
2923
3303
  return selectedCustomOptions.length ? [...selectedCustomOptions, ...options] : options;
2924
3304
  }, [allowInput, options, selectedValues]);
2925
- const selectedOptions = (0, import_react18.useMemo)(() => selectedValues.map((selectedValue) => availableOptions.find((option) => option.value === selectedValue)).filter((option) => option !== void 0), [availableOptions, selectedValues]);
2926
- const filteredOptions = (0, import_react18.useMemo)(() => deferredQuery ? availableOptions.filter((option) => filterOption(deferredQuery, option)) : availableOptions, [availableOptions, deferredQuery, filterOption]);
3305
+ const selectedOptions = (0, import_react20.useMemo)(() => selectedValues.map((selectedValue) => availableOptions.find((option) => option.value === selectedValue)).filter((option) => option !== void 0), [availableOptions, selectedValues]);
3306
+ const filteredOptions = (0, import_react20.useMemo)(() => deferredQuery ? availableOptions.filter((option) => filterOption(deferredQuery, option)) : availableOptions, [availableOptions, deferredQuery, filterOption]);
2927
3307
  const inputValue = query.trim();
2928
3308
  const exactInputOption = inputValue ? availableOptions.find((option) => String(option.value).toLowerCase() === inputValue.toLowerCase() || typeof option.label === "string" && option.label.toLowerCase() === inputValue.toLowerCase()) : void 0;
2929
3309
  const canSubmitInput = allowInput && Boolean(inputValue) && !exactInputOption;
@@ -2937,7 +3317,7 @@ var Select = (0, import_react18.forwardRef)(function Select2({
2937
3317
  const renderedOptions = virtualEnabled ? filteredOptions.slice(virtualStart, virtualEnd) : filteredOptions;
2938
3318
  const enabledFilteredOptions = filteredOptions.filter((option) => !option.disabled);
2939
3319
  const allFilteredSelected = enabledFilteredOptions.length > 0 && enabledFilteredOptions.every((option) => selectedValueSet.has(option.value));
2940
- (0, import_react18.useEffect)(() => {
3320
+ (0, import_react20.useEffect)(() => {
2941
3321
  if (!open || popupMode === "modal") return void 0;
2942
3322
  function closeFromOutside(event) {
2943
3323
  const target = event.target;
@@ -2946,10 +3326,10 @@ var Select = (0, import_react18.forwardRef)(function Select2({
2946
3326
  document.addEventListener("mousedown", closeFromOutside);
2947
3327
  return () => document.removeEventListener("mousedown", closeFromOutside);
2948
3328
  }, [open, popupMode]);
2949
- (0, import_react18.useEffect)(() => {
3329
+ (0, import_react20.useEffect)(() => {
2950
3330
  if (open && (showSearch || allowInput)) searchRef.current?.focus();
2951
3331
  }, [allowInput, open, showSearch]);
2952
- (0, import_react18.useEffect)(() => {
3332
+ (0, import_react20.useEffect)(() => {
2953
3333
  if (disabled || loading) closeDropdown();
2954
3334
  }, [disabled, loading]);
2955
3335
  function firstEnabledIndex() {
@@ -3136,6 +3516,7 @@ var Select = (0, import_react18.forwardRef)(function Select2({
3136
3516
  ),
3137
3517
  allowClear && hasValue && !disabled && !loading ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { className: "sia-select__clear", type: "button", "aria-label": "\u6E05\u7A7A\u9009\u62E9", onClick: clearValue, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Icon, { name: "circle-close", variant: "filled", size: 15 }) }) : null,
3138
3518
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(PopupPortal, { ref: popupRef, anchorRef: rootRef, open: open && popupMode === "dropdown", className: `sia-select__dropdown${filteredOptions.length === 0 && !canSubmitInput ? " sia-select__dropdown--empty" : ""}`, children: [
3519
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FloatingScrollbarProvider, { targetRef: listRef }),
3139
3520
  showSearch || allowInput ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { className: "sia-select__search", children: [
3140
3521
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Icon, { name: "search", size: 15 }),
3141
3522
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { ref: searchRef, value: query, maxLength: inputMaxLength, onChange: handleSearchChange, onKeyDown: (event) => handleInteractionKeyDown(event, true), placeholder: searchPlaceholder, "aria-label": searchPlaceholder })
@@ -3266,7 +3647,7 @@ var Select = (0, import_react18.forwardRef)(function Select2({
3266
3647
  });
3267
3648
 
3268
3649
  // src/components/Upload.tsx
3269
- var import_react19 = require("react");
3650
+ var import_react21 = require("react");
3270
3651
  var import_jsx_runtime20 = require("react/jsx-runtime");
3271
3652
  var LIST_IGNORE = /* @__PURE__ */ Symbol("SIA_UPLOAD_LIST_IGNORE");
3272
3653
  function UploadRoot({
@@ -3289,9 +3670,9 @@ function UploadRoot({
3289
3670
  onDrop,
3290
3671
  ...props
3291
3672
  }) {
3292
- const inputRef = (0, import_react19.useRef)(null);
3293
- const filesRef = (0, import_react19.useRef)(fileList ?? defaultFileList);
3294
- const id = (0, import_react19.useId)();
3673
+ const inputRef = (0, import_react21.useRef)(null);
3674
+ const filesRef = (0, import_react21.useRef)(fileList ?? defaultFileList);
3675
+ const id = (0, import_react21.useId)();
3295
3676
  const [files, setFiles] = useControllableState({ value: fileList, defaultValue: defaultFileList });
3296
3677
  filesRef.current = files;
3297
3678
  function emit(file, nextList) {
@@ -3514,12 +3895,12 @@ function Breadcrumb({ items, separator = "/", itemRender, className = "", ...pro
3514
3895
  }
3515
3896
 
3516
3897
  // src/components/Typography.tsx
3517
- var import_react21 = require("react");
3898
+ var import_react23 = require("react");
3518
3899
 
3519
3900
  // src/components/TextArea.tsx
3520
- var import_react20 = require("react");
3901
+ var import_react22 = require("react");
3521
3902
  var import_jsx_runtime23 = require("react/jsx-runtime");
3522
- var TextArea = (0, import_react20.forwardRef)(function TextArea2({
3903
+ var TextArea = (0, import_react22.forwardRef)(function TextArea2({
3523
3904
  label,
3524
3905
  hint,
3525
3906
  status = "default",
@@ -3537,9 +3918,9 @@ var TextArea = (0, import_react20.forwardRef)(function TextArea2({
3537
3918
  ...props
3538
3919
  }, ref) {
3539
3920
  const size = useControlSize(sizeProp);
3540
- const fallbackId = (0, import_react20.useId)();
3921
+ const fallbackId = (0, import_react22.useId)();
3541
3922
  const textAreaId = id ?? fallbackId;
3542
- const [uncontrolledValue, setUncontrolledValue] = (0, import_react20.useState)(() => defaultValue === void 0 ? "" : String(defaultValue));
3923
+ const [uncontrolledValue, setUncontrolledValue] = (0, import_react22.useState)(() => defaultValue === void 0 ? "" : String(defaultValue));
3543
3924
  const currentText = value !== void 0 ? String(value) : uncontrolledValue;
3544
3925
  const floating = useFloatingPlaceholder({ mode: placeholderMode, placeholder, filled: currentText.length > 0 });
3545
3926
  const hintId = `${textAreaId}-hint`;
@@ -3600,26 +3981,26 @@ function TypographyContent({
3600
3981
  className = "",
3601
3982
  ...props
3602
3983
  }) {
3603
- const contentRef = (0, import_react21.useRef)(null);
3604
- const editRef = (0, import_react21.useRef)(null);
3605
- const timer = (0, import_react21.useRef)();
3606
- const [localText, setLocalText] = (0, import_react21.useState)();
3607
- const [draft, setDraft] = (0, import_react21.useState)("");
3608
- const [editing, setEditing] = (0, import_react21.useState)(false);
3609
- const [copied, setCopied] = (0, import_react21.useState)(false);
3610
- const [copyError, setCopyError] = (0, import_react21.useState)(false);
3611
- const [expanded, setExpanded] = (0, import_react21.useState)(false);
3612
- const [overflow, setOverflow] = (0, import_react21.useState)(false);
3984
+ const contentRef = (0, import_react23.useRef)(null);
3985
+ const editRef = (0, import_react23.useRef)(null);
3986
+ const timer = (0, import_react23.useRef)();
3987
+ const [localText, setLocalText] = (0, import_react23.useState)();
3988
+ const [draft, setDraft] = (0, import_react23.useState)("");
3989
+ const [editing, setEditing] = (0, import_react23.useState)(false);
3990
+ const [copied, setCopied] = (0, import_react23.useState)(false);
3991
+ const [copyError, setCopyError] = (0, import_react23.useState)(false);
3992
+ const [expanded, setExpanded] = (0, import_react23.useState)(false);
3993
+ const [overflow, setOverflow] = (0, import_react23.useState)(false);
3613
3994
  const editConfig = typeof editable === "object" ? editable : void 0;
3614
3995
  const content = editConfig?.text ?? localText ?? children;
3615
3996
  const requestedRows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
3616
3997
  const rows = Number.isFinite(requestedRows) ? Math.max(1, Math.floor(requestedRows)) : 1;
3617
3998
  const expandable = typeof ellipsis === "object" && ellipsis.expandable;
3618
- (0, import_react21.useEffect)(() => {
3999
+ (0, import_react23.useEffect)(() => {
3619
4000
  setLocalText(void 0);
3620
4001
  }, [children]);
3621
- (0, import_react21.useEffect)(() => () => clearTimeout(timer.current), []);
3622
- (0, import_react21.useLayoutEffect)(() => {
4002
+ (0, import_react23.useEffect)(() => () => clearTimeout(timer.current), []);
4003
+ (0, import_react23.useLayoutEffect)(() => {
3623
4004
  const element = contentRef.current;
3624
4005
  if (!element || !ellipsis || editing || expanded) return;
3625
4006
  const measure = () => setOverflow(element.scrollHeight > element.clientHeight + 1 || element.scrollWidth > element.clientWidth + 1);
@@ -3728,10 +4109,10 @@ function TypographyRoot({ className = "", ...props }) {
3728
4109
  var Typography = Object.assign(TypographyRoot, { Title, Text, Paragraph, Link });
3729
4110
 
3730
4111
  // src/components/FloatButton.tsx
3731
- var import_react22 = require("react");
4112
+ var import_react24 = require("react");
3732
4113
  var import_jsx_runtime25 = require("react/jsx-runtime");
3733
- var GroupShape = (0, import_react22.createContext)(void 0);
3734
- var FloatButtonRoot = (0, import_react22.forwardRef)(function FloatButton({
4114
+ var GroupShape = (0, import_react24.createContext)(void 0);
4115
+ var FloatButtonRoot = (0, import_react24.forwardRef)(function FloatButton({
3735
4116
  icon,
3736
4117
  description,
3737
4118
  tooltip,
@@ -3744,7 +4125,7 @@ var FloatButtonRoot = (0, import_react22.forwardRef)(function FloatButton({
3744
4125
  children,
3745
4126
  ...props
3746
4127
  }, ref) {
3747
- const groupShape = (0, import_react22.useContext)(GroupShape);
4128
+ const groupShape = (0, import_react24.useContext)(GroupShape);
3748
4129
  const label = props["aria-label"] ?? (typeof tooltip === "string" ? tooltip : typeof description === "string" ? description : "\u60AC\u6D6E\u64CD\u4F5C");
3749
4130
  const button = /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3750
4131
  "button",
@@ -3782,10 +4163,10 @@ function FloatButtonGroup({
3782
4163
  ...props
3783
4164
  }) {
3784
4165
  const [visible, setVisible] = useControllableState({ value: open, defaultValue: defaultOpen, onChange: onOpenChange });
3785
- const rootRef = (0, import_react22.useRef)(null);
3786
- const triggerRef = (0, import_react22.useRef)(null);
3787
- const id = (0, import_react22.useId)();
3788
- (0, import_react22.useEffect)(() => {
4166
+ const rootRef = (0, import_react24.useRef)(null);
4167
+ const triggerRef = (0, import_react24.useRef)(null);
4168
+ const id = (0, import_react24.useId)();
4169
+ (0, import_react24.useEffect)(() => {
3789
4170
  if (!trigger || !visible) return;
3790
4171
  const close = (event) => {
3791
4172
  if (!rootRef.current?.contains(event.target)) setVisible(false);
@@ -3839,8 +4220,8 @@ function FloatButtonGroup({
3839
4220
  ) });
3840
4221
  }
3841
4222
  function FloatButtonBackTop({ target, visibilityHeight = 400, behavior = "smooth", onClick, icon, tooltip = "\u8FD4\u56DE\u9876\u90E8", ...props }) {
3842
- const [visible, setVisible] = (0, import_react22.useState)(false);
3843
- (0, import_react22.useEffect)(() => {
4223
+ const [visible, setVisible] = (0, import_react24.useState)(false);
4224
+ (0, import_react24.useEffect)(() => {
3844
4225
  const element = target ? target() : window;
3845
4226
  if (!element) return;
3846
4227
  const update = () => setVisible((element === window ? window.scrollY : element.scrollTop) >= visibilityHeight);