@sesionario/blocks 1.22.3 → 1.24.0

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import * as React7 from 'react';
3
3
  import { createContext, forwardRef, useContext, useState, useRef, useId, useEffect, useCallback, useLayoutEffect, useMemo } from 'react';
4
- import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, BadgeCheck, Settings, User, Activity, LogOut, LogIn, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, PictureInPicture2, Minimize, Maximize, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, ChevronUp, ChevronDown, XIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
4
+ import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, X, ChevronLeft, ChevronRight, Monitor, Sun, Moon, Building2, Check, Plus, Languages, BadgeCheck, Settings, User, Activity, LogOut, LogIn, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, PictureInPicture2, Minimize, Maximize, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, XIcon, ChevronUp, ChevronDown, ChevronRightIcon, CircleIcon } from 'lucide-react';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { cva } from 'class-variance-authority';
@@ -9,12 +9,12 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
9
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
10
10
  import { Slot } from '@radix-ui/react-slot';
11
11
  import { createPortal } from 'react-dom';
12
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
12
13
  import { useTheme } from 'next-themes';
13
14
  import { useReducedMotion, AnimatePresence, motion } from 'framer-motion';
14
15
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
15
16
  import * as SliderPrimitive from '@radix-ui/react-slider';
16
17
  import * as TogglePrimitive from '@radix-ui/react-toggle';
17
- import * as DialogPrimitive from '@radix-ui/react-dialog';
18
18
  import { Drawer as Drawer$1 } from 'vaul';
19
19
  import * as PopoverPrimitive from '@radix-ui/react-popover';
20
20
  import { Command as Command$1 } from 'cmdk';
@@ -829,19 +829,19 @@ function ExpandableText({
829
829
  ) : null
830
830
  ] });
831
831
  }
832
- var MIN_SCALE = 1;
833
- var MAX_SCALE = 4;
834
- var DOUBLE_TAP_MS = 300;
835
- var SWIPE_LOCK_PX = 8;
836
- var SWIPE_COMMIT_PX = 56;
837
- var SWIPE_SNAP_MS = 180;
832
+ var ZOOM_PAN_MIN_SCALE = 1;
833
+ var ZOOM_PAN_MAX_SCALE = 4;
834
+ var ZOOM_PAN_DOUBLE_TAP_MS = 300;
835
+ var ZOOM_PAN_SWIPE_LOCK_PX = 8;
836
+ var ZOOM_PAN_SWIPE_COMMIT_PX = 56;
837
+ var ZOOM_PAN_SWIPE_SNAP_MS = 180;
838
838
  var IDENTITY = { scale: 1, tx: 0, ty: 0 };
839
839
  function clamp(value, min, max) {
840
840
  return Math.min(max, Math.max(min, value));
841
841
  }
842
- function zoomToward(prev, nextScale, originX, originY) {
843
- const scale = clamp(nextScale, MIN_SCALE, MAX_SCALE);
844
- if (scale === MIN_SCALE) return IDENTITY;
842
+ function zoomToward(prev, nextScale, originX, originY, minScale = ZOOM_PAN_MIN_SCALE, maxScale = ZOOM_PAN_MAX_SCALE) {
843
+ const scale = clamp(nextScale, minScale, maxScale);
844
+ if (scale === minScale) return { ...IDENTITY, scale: minScale };
845
845
  const ratio = scale / prev.scale;
846
846
  return {
847
847
  scale,
@@ -856,19 +856,19 @@ function prefersReducedMotion() {
856
856
  if (typeof window === "undefined" || !window.matchMedia) return false;
857
857
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
858
858
  }
859
- function ImageLightbox({
860
- images,
861
- index,
862
- onIndexChange,
863
- onClose,
864
- labels
865
- }) {
866
- const ariaLabel = labels?.ariaLabel ?? "Visor de im\xE1genes";
867
- const closeLabel = labels?.close ?? "Cerrar";
868
- const previousLabel = labels?.previous ?? "Anterior";
869
- const nextLabel = labels?.next ?? "Siguiente";
870
- const hasMultiple = images.length > 1;
871
- const [transform, setTransform] = useState(IDENTITY);
859
+ function useZoomPanGestures(options = {}) {
860
+ const {
861
+ minScale = ZOOM_PAN_MIN_SCALE,
862
+ maxScale = ZOOM_PAN_MAX_SCALE,
863
+ swipeEnabled = true,
864
+ onSwipeCommit,
865
+ wheelZoom = true,
866
+ doubleTap = "reset"
867
+ } = options;
868
+ const [transform, setTransform] = useState({
869
+ ...IDENTITY,
870
+ scale: minScale
871
+ });
872
872
  const transformRef = useRef(transform);
873
873
  transformRef.current = transform;
874
874
  const [isSwipeDragging, setIsSwipeDragging] = useState(false);
@@ -881,46 +881,28 @@ function ImageLightbox({
881
881
  const lastTapRef = useRef(
882
882
  null
883
883
  );
884
- const indexRef = useRef(index);
885
- indexRef.current = index;
886
- const imagesLengthRef = useRef(images.length);
887
- imagesLengthRef.current = images.length;
888
- const resetZoom = useCallback(() => {
889
- setTransform(IDENTITY);
884
+ const onSwipeCommitRef = useRef(onSwipeCommit);
885
+ onSwipeCommitRef.current = onSwipeCommit;
886
+ const swipeEnabledRef = useRef(swipeEnabled);
887
+ swipeEnabledRef.current = swipeEnabled;
888
+ const minScaleRef = useRef(minScale);
889
+ minScaleRef.current = minScale;
890
+ const maxScaleRef = useRef(maxScale);
891
+ maxScaleRef.current = maxScale;
892
+ const doubleTapRef = useRef(doubleTap);
893
+ doubleTapRef.current = doubleTap;
894
+ const reset = useCallback(() => {
895
+ setTransform({ ...IDENTITY, scale: minScaleRef.current });
890
896
  setIsSwipeDragging(false);
891
897
  setSnapBack(false);
892
898
  }, []);
893
- useEffect(() => {
894
- resetZoom();
895
- }, [index, images[index], resetZoom]);
896
- function goPrev() {
897
- onIndexChange((index - 1 + images.length) % images.length);
898
- }
899
- function goNext() {
900
- onIndexChange((index + 1) % images.length);
901
- }
902
- useEffect(() => {
903
- function handleKeyDown(e) {
904
- if (e.key === "Escape") {
905
- onClose();
906
- return;
907
- }
908
- if (!hasMultiple) return;
909
- if (e.key === "ArrowLeft") {
910
- onIndexChange((index - 1 + images.length) % images.length);
911
- } else if (e.key === "ArrowRight") {
912
- onIndexChange((index + 1) % images.length);
913
- }
914
- }
915
- window.addEventListener("keydown", handleKeyDown);
916
- return () => window.removeEventListener("keydown", handleKeyDown);
917
- }, [index, images.length, hasMultiple, onClose, onIndexChange]);
918
- useEffect(() => {
919
- const previousOverflow = document.body.style.overflow;
920
- document.body.style.overflow = "hidden";
921
- return () => {
922
- document.body.style.overflow = previousOverflow;
923
- };
899
+ const setScale = useCallback((next) => {
900
+ setTransform(
901
+ (prev) => zoomToward(prev, next, 0, 0, minScaleRef.current, maxScaleRef.current)
902
+ );
903
+ }, []);
904
+ const clearSnapBack = useCallback(() => {
905
+ setSnapBack(false);
924
906
  }, []);
925
907
  const stageOrigin = useCallback((clientX, clientY) => {
926
908
  const stage = stageRef.current;
@@ -932,6 +914,7 @@ function ImageLightbox({
932
914
  };
933
915
  }, []);
934
916
  useEffect(() => {
917
+ if (!wheelZoom) return;
935
918
  const stage = stageRef.current;
936
919
  if (!stage) return;
937
920
  const handleWheel = (e) => {
@@ -940,11 +923,20 @@ function ImageLightbox({
940
923
  const prev = transformRef.current;
941
924
  const factor = e.deltaY > 0 ? 0.9 : 1.1;
942
925
  const { x, y } = stageOrigin(e.clientX, e.clientY);
943
- setTransform(zoomToward(prev, prev.scale * factor, x, y));
926
+ setTransform(
927
+ zoomToward(
928
+ prev,
929
+ prev.scale * factor,
930
+ x,
931
+ y,
932
+ minScaleRef.current,
933
+ maxScaleRef.current
934
+ )
935
+ );
944
936
  };
945
937
  stage.addEventListener("wheel", handleWheel, { passive: false });
946
938
  return () => stage.removeEventListener("wheel", handleWheel);
947
- }, [stageOrigin]);
939
+ }, [stageOrigin, wheelZoom]);
948
940
  function clearSwipe() {
949
941
  swipeRef.current = null;
950
942
  setIsSwipeDragging(false);
@@ -979,7 +971,7 @@ function ImageLightbox({
979
971
  beginPinch();
980
972
  return;
981
973
  }
982
- if (transformRef.current.scale > MIN_SCALE) {
974
+ if (transformRef.current.scale > minScaleRef.current) {
983
975
  panRef.current = {
984
976
  pointerId: e.pointerId,
985
977
  startX: e.clientX,
@@ -990,7 +982,7 @@ function ImageLightbox({
990
982
  };
991
983
  return;
992
984
  }
993
- if (hasMultiple) {
985
+ if (swipeEnabledRef.current) {
994
986
  swipeRef.current = {
995
987
  pointerId: e.pointerId,
996
988
  startX: e.clientX,
@@ -1016,7 +1008,9 @@ function ImageLightbox({
1016
1008
  pinchStart.transform,
1017
1009
  nextScale,
1018
1010
  pinchStart.originX,
1019
- pinchStart.originY
1011
+ pinchStart.originY,
1012
+ minScaleRef.current,
1013
+ maxScaleRef.current
1020
1014
  )
1021
1015
  );
1022
1016
  return;
@@ -1041,16 +1035,22 @@ function ImageLightbox({
1041
1035
  if (swipe.axis === null) {
1042
1036
  const absX = Math.abs(dx);
1043
1037
  const absY = Math.abs(dy);
1044
- if (absX < SWIPE_LOCK_PX && absY < SWIPE_LOCK_PX) return;
1038
+ if (absX < ZOOM_PAN_SWIPE_LOCK_PX && absY < ZOOM_PAN_SWIPE_LOCK_PX) {
1039
+ return;
1040
+ }
1045
1041
  swipe.axis = absX >= absY ? "x" : "y";
1046
1042
  if (swipe.axis === "y") {
1047
1043
  clearSwipe();
1048
- setTransform(IDENTITY);
1044
+ setTransform({ ...IDENTITY, scale: minScaleRef.current });
1049
1045
  return;
1050
1046
  }
1051
1047
  }
1052
1048
  if (swipe.axis === "x") {
1053
- setTransform({ scale: MIN_SCALE, tx: dx, ty: 0 });
1049
+ setTransform({
1050
+ scale: minScaleRef.current,
1051
+ tx: dx,
1052
+ ty: 0
1053
+ });
1054
1054
  }
1055
1055
  }
1056
1056
  function finishSwipe(e) {
@@ -1060,19 +1060,13 @@ function ImageLightbox({
1060
1060
  const wasMoved = swipe.moved;
1061
1061
  const axis = swipe.axis;
1062
1062
  clearSwipe();
1063
- if (axis === "x" && Math.abs(dx) >= SWIPE_COMMIT_PX) {
1064
- const length = imagesLengthRef.current;
1065
- const current = indexRef.current;
1066
- if (dx < 0) {
1067
- onIndexChange((current + 1) % length);
1068
- } else {
1069
- onIndexChange((current - 1 + length) % length);
1070
- }
1063
+ if (axis === "x" && Math.abs(dx) >= ZOOM_PAN_SWIPE_COMMIT_PX) {
1064
+ onSwipeCommitRef.current?.(dx < 0 ? "next" : "prev");
1071
1065
  return true;
1072
1066
  }
1073
1067
  if (wasMoved || transformRef.current.tx !== 0) {
1074
1068
  if (!prefersReducedMotion()) setSnapBack(true);
1075
- setTransform(IDENTITY);
1069
+ setTransform({ ...IDENTITY, scale: minScaleRef.current });
1076
1070
  }
1077
1071
  return wasMoved;
1078
1072
  }
@@ -1087,7 +1081,7 @@ function ImageLightbox({
1087
1081
  }
1088
1082
  if (pointersRef.current.size === 1) {
1089
1083
  const remaining = [...pointersRef.current.entries()][0];
1090
- if (remaining && transformRef.current.scale > MIN_SCALE) {
1084
+ if (remaining && transformRef.current.scale > minScaleRef.current) {
1091
1085
  const [pointerId, point] = remaining;
1092
1086
  panRef.current = {
1093
1087
  pointerId,
@@ -1110,19 +1104,121 @@ function ImageLightbox({
1110
1104
  if (pointersRef.current.size === 0 && e.button === 0) {
1111
1105
  const now = Date.now();
1112
1106
  const last = lastTapRef.current;
1113
- if (last && now - last.time < DOUBLE_TAP_MS && Math.hypot(e.clientX - last.x, e.clientY - last.y) < 24) {
1107
+ if (last && now - last.time < ZOOM_PAN_DOUBLE_TAP_MS && Math.hypot(e.clientX - last.x, e.clientY - last.y) < 24) {
1114
1108
  lastTapRef.current = null;
1115
- resetZoom();
1109
+ if (doubleTapRef.current === "toggle" && transformRef.current.scale <= minScaleRef.current) {
1110
+ const { x, y } = stageOrigin(e.clientX, e.clientY);
1111
+ setTransform(
1112
+ zoomToward(
1113
+ transformRef.current,
1114
+ Math.min(2, maxScaleRef.current),
1115
+ x,
1116
+ y,
1117
+ minScaleRef.current,
1118
+ maxScaleRef.current
1119
+ )
1120
+ );
1121
+ } else {
1122
+ reset();
1123
+ }
1116
1124
  return;
1117
1125
  }
1118
1126
  lastTapRef.current = { time: now, x: e.clientX, y: e.clientY };
1119
1127
  }
1120
1128
  }
1129
+ return {
1130
+ stageRef,
1131
+ transform,
1132
+ isDragging: isSwipeDragging,
1133
+ snapBack,
1134
+ handlers: {
1135
+ onPointerDown,
1136
+ onPointerMove,
1137
+ onPointerUp,
1138
+ onPointerCancel: onPointerUp
1139
+ },
1140
+ reset,
1141
+ setScale,
1142
+ clearSnapBack
1143
+ };
1144
+ }
1145
+ function ImageLightbox({
1146
+ images,
1147
+ index,
1148
+ onIndexChange,
1149
+ onClose,
1150
+ labels
1151
+ }) {
1152
+ const ariaLabel = labels?.ariaLabel ?? "Visor de im\xE1genes";
1153
+ const closeLabel = labels?.close ?? "Cerrar";
1154
+ const previousLabel = labels?.previous ?? "Anterior";
1155
+ const nextLabel = labels?.next ?? "Siguiente";
1156
+ const hasMultiple = images.length > 1;
1157
+ const indexRef = useRef(index);
1158
+ indexRef.current = index;
1159
+ const imagesLengthRef = useRef(images.length);
1160
+ imagesLengthRef.current = images.length;
1161
+ const onIndexChangeRef = useRef(onIndexChange);
1162
+ onIndexChangeRef.current = onIndexChange;
1163
+ const onSwipeCommit = useCallback((direction) => {
1164
+ const length = imagesLengthRef.current;
1165
+ const current = indexRef.current;
1166
+ if (direction === "next") {
1167
+ onIndexChangeRef.current((current + 1) % length);
1168
+ } else {
1169
+ onIndexChangeRef.current((current - 1 + length) % length);
1170
+ }
1171
+ }, []);
1172
+ const {
1173
+ stageRef,
1174
+ transform,
1175
+ isDragging,
1176
+ snapBack,
1177
+ handlers,
1178
+ reset,
1179
+ clearSnapBack
1180
+ } = useZoomPanGestures({
1181
+ swipeEnabled: hasMultiple,
1182
+ onSwipeCommit,
1183
+ doubleTap: "reset"
1184
+ });
1185
+ useEffect(() => {
1186
+ reset();
1187
+ }, [index, images[index], reset]);
1188
+ function goPrev() {
1189
+ onIndexChange((index - 1 + images.length) % images.length);
1190
+ }
1191
+ function goNext() {
1192
+ onIndexChange((index + 1) % images.length);
1193
+ }
1194
+ useEffect(() => {
1195
+ function handleKeyDown(e) {
1196
+ if (e.key === "Escape") {
1197
+ onClose();
1198
+ return;
1199
+ }
1200
+ if (!hasMultiple) return;
1201
+ if (e.key === "ArrowLeft") {
1202
+ onIndexChange((index - 1 + images.length) % images.length);
1203
+ } else if (e.key === "ArrowRight") {
1204
+ onIndexChange((index + 1) % images.length);
1205
+ }
1206
+ }
1207
+ window.addEventListener("keydown", handleKeyDown);
1208
+ return () => window.removeEventListener("keydown", handleKeyDown);
1209
+ }, [index, images.length, hasMultiple, onClose, onIndexChange]);
1210
+ useEffect(() => {
1211
+ const previousOverflow = document.body.style.overflow;
1212
+ document.body.style.overflow = "hidden";
1213
+ return () => {
1214
+ document.body.style.overflow = previousOverflow;
1215
+ };
1216
+ }, []);
1121
1217
  function stopDialogClose(e) {
1122
1218
  e.stopPropagation();
1123
1219
  }
1124
1220
  const { scale, tx, ty } = transform;
1125
- const transition = snapBack && !isSwipeDragging && !prefersReducedMotion() ? `transform ${SWIPE_SNAP_MS}ms ease-out` : void 0;
1221
+ const transition = snapBack && !isDragging && !prefersReducedMotion() ? `transform ${ZOOM_PAN_SWIPE_SNAP_MS}ms ease-out` : void 0;
1126
1222
  return createPortal(
1127
1223
  /* @__PURE__ */ jsxs(
1128
1224
  "div",
@@ -1162,12 +1258,12 @@ function ImageLightbox({
1162
1258
  ref: stageRef,
1163
1259
  className: "relative flex max-h-full max-w-full touch-none items-center justify-center",
1164
1260
  "data-scale": scale,
1165
- "data-swipe-dragging": isSwipeDragging ? "true" : "false",
1261
+ "data-swipe-dragging": isDragging ? "true" : "false",
1166
1262
  onClick: stopDialogClose,
1167
- onPointerDown,
1168
- onPointerMove,
1169
- onPointerUp,
1170
- onPointerCancel: onPointerUp,
1263
+ onPointerDown: handlers.onPointerDown,
1264
+ onPointerMove: handlers.onPointerMove,
1265
+ onPointerUp: handlers.onPointerUp,
1266
+ onPointerCancel: handlers.onPointerCancel,
1171
1267
  children: /* @__PURE__ */ jsx(
1172
1268
  "img",
1173
1269
  {
@@ -1178,9 +1274,9 @@ function ImageLightbox({
1178
1274
  style: {
1179
1275
  transform: `translate(${tx}px, ${ty}px) scale(${scale})`,
1180
1276
  transition,
1181
- cursor: scale > MIN_SCALE ? "grab" : "default"
1277
+ cursor: scale > ZOOM_PAN_MIN_SCALE ? "grab" : "default"
1182
1278
  },
1183
- onTransitionEnd: () => setSnapBack(false)
1279
+ onTransitionEnd: clearSnapBack
1184
1280
  }
1185
1281
  )
1186
1282
  }
@@ -1209,6 +1305,321 @@ function ImageLightbox({
1209
1305
  document.body
1210
1306
  );
1211
1307
  }
1308
+ function MediaCarousel({
1309
+ items,
1310
+ index,
1311
+ onIndexChange,
1312
+ labels,
1313
+ className,
1314
+ renderPdf
1315
+ }) {
1316
+ const previousLabel = labels?.previous ?? "Anterior";
1317
+ const nextLabel = labels?.next ?? "Siguiente";
1318
+ const safeIndex = Math.min(Math.max(index, 0), Math.max(items.length - 1, 0));
1319
+ const item = items[safeIndex];
1320
+ const hasMultiple = items.length > 1;
1321
+ const indexRef = useRef(safeIndex);
1322
+ indexRef.current = safeIndex;
1323
+ const lengthRef = useRef(items.length);
1324
+ lengthRef.current = items.length;
1325
+ const onIndexChangeRef = useRef(onIndexChange);
1326
+ onIndexChangeRef.current = onIndexChange;
1327
+ const onSwipeCommit = useCallback((direction) => {
1328
+ const length = lengthRef.current;
1329
+ if (length <= 1) return;
1330
+ const current = indexRef.current;
1331
+ if (direction === "next") {
1332
+ onIndexChangeRef.current((current + 1) % length);
1333
+ } else {
1334
+ onIndexChangeRef.current((current - 1 + length) % length);
1335
+ }
1336
+ }, []);
1337
+ const isImage = item?.kind === "image";
1338
+ const {
1339
+ stageRef,
1340
+ transform,
1341
+ isDragging,
1342
+ snapBack,
1343
+ handlers,
1344
+ reset,
1345
+ clearSnapBack
1346
+ } = useZoomPanGestures({
1347
+ swipeEnabled: hasMultiple && isImage,
1348
+ onSwipeCommit,
1349
+ doubleTap: "toggle",
1350
+ wheelZoom: isImage
1351
+ });
1352
+ useEffect(() => {
1353
+ reset();
1354
+ }, [safeIndex, item?.kind === "image" ? item.src : null, reset]);
1355
+ useEffect(() => {
1356
+ if (!hasMultiple) return;
1357
+ function handleKeyDown(e) {
1358
+ if (e.key === "ArrowLeft") {
1359
+ onIndexChangeRef.current(
1360
+ (indexRef.current - 1 + lengthRef.current) % lengthRef.current
1361
+ );
1362
+ } else if (e.key === "ArrowRight") {
1363
+ onIndexChangeRef.current((indexRef.current + 1) % lengthRef.current);
1364
+ }
1365
+ }
1366
+ window.addEventListener("keydown", handleKeyDown);
1367
+ return () => window.removeEventListener("keydown", handleKeyDown);
1368
+ }, [hasMultiple]);
1369
+ function stopBubble(e) {
1370
+ e.stopPropagation();
1371
+ }
1372
+ if (!item) {
1373
+ return null;
1374
+ }
1375
+ const { scale, tx, ty } = transform;
1376
+ const transition = snapBack && !isDragging && !prefersReducedMotion() ? `transform ${ZOOM_PAN_SWIPE_SNAP_MS}ms ease-out` : void 0;
1377
+ let body;
1378
+ switch (item.kind) {
1379
+ case "image":
1380
+ body = /* @__PURE__ */ jsx(
1381
+ "div",
1382
+ {
1383
+ ref: stageRef,
1384
+ className: "relative flex h-full w-full touch-none items-center justify-center",
1385
+ "data-scale": scale,
1386
+ "data-swipe-dragging": isDragging ? "true" : "false",
1387
+ onClick: stopBubble,
1388
+ onPointerDown: handlers.onPointerDown,
1389
+ onPointerMove: handlers.onPointerMove,
1390
+ onPointerUp: handlers.onPointerUp,
1391
+ onPointerCancel: handlers.onPointerCancel,
1392
+ children: /* @__PURE__ */ jsx(
1393
+ "img",
1394
+ {
1395
+ src: item.src,
1396
+ alt: item.alt ?? "",
1397
+ draggable: false,
1398
+ className: "max-h-full max-w-full select-none object-contain",
1399
+ style: {
1400
+ transform: `translate(${tx}px, ${ty}px) scale(${scale})`,
1401
+ transition,
1402
+ cursor: scale > ZOOM_PAN_MIN_SCALE ? "grab" : "default"
1403
+ },
1404
+ onTransitionEnd: clearSnapBack
1405
+ }
1406
+ )
1407
+ }
1408
+ );
1409
+ break;
1410
+ case "video":
1411
+ body = /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center p-2", children: item.node });
1412
+ break;
1413
+ case "pdf":
1414
+ body = renderPdf ? renderPdf({
1415
+ fileUrl: item.fileUrl,
1416
+ fileName: item.fileName,
1417
+ downloadUrl: item.downloadUrl
1418
+ }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-6 text-sm text-muted-foreground", children: item.fileName });
1419
+ break;
1420
+ default: {
1421
+ const _exhaustive = item;
1422
+ return _exhaustive;
1423
+ }
1424
+ }
1425
+ const counterText = labels?.counter ? labels.counter(safeIndex + 1, items.length) : `${safeIndex + 1} / ${items.length}`;
1426
+ return /* @__PURE__ */ jsxs(
1427
+ "div",
1428
+ {
1429
+ className: cn(
1430
+ "relative flex h-full min-h-0 w-full flex-col bg-black",
1431
+ item.kind === "pdf" && "bg-muted",
1432
+ className
1433
+ ),
1434
+ children: [
1435
+ /* @__PURE__ */ jsx("div", { className: "relative min-h-0 flex-1", children: body }),
1436
+ hasMultiple && item.kind !== "pdf" && /* @__PURE__ */ jsxs(Fragment, { children: [
1437
+ /* @__PURE__ */ jsx(
1438
+ "button",
1439
+ {
1440
+ type: "button",
1441
+ onClick: (e) => {
1442
+ e.stopPropagation();
1443
+ onIndexChange((safeIndex - 1 + items.length) % items.length);
1444
+ },
1445
+ "aria-label": previousLabel,
1446
+ className: "absolute left-3 top-1/2 z-10 hidden -translate-y-1/2 rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70 sm:inline-flex",
1447
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-6 w-6" })
1448
+ }
1449
+ ),
1450
+ /* @__PURE__ */ jsx(
1451
+ "button",
1452
+ {
1453
+ type: "button",
1454
+ onClick: (e) => {
1455
+ e.stopPropagation();
1456
+ onIndexChange((safeIndex + 1) % items.length);
1457
+ },
1458
+ "aria-label": nextLabel,
1459
+ className: "absolute right-3 top-1/2 z-10 hidden -translate-y-1/2 rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70 sm:inline-flex",
1460
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-6 w-6" })
1461
+ }
1462
+ ),
1463
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-3 left-1/2 z-10 -translate-x-1/2 rounded-full bg-black/50 px-3 py-1 text-xs text-white", children: counterText })
1464
+ ] })
1465
+ ]
1466
+ }
1467
+ );
1468
+ }
1469
+ function Dialog({
1470
+ ...props
1471
+ }) {
1472
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
1473
+ }
1474
+ function DialogPortal({
1475
+ ...props
1476
+ }) {
1477
+ return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
1478
+ }
1479
+ function DialogClose({
1480
+ ...props
1481
+ }) {
1482
+ return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
1483
+ }
1484
+ function DialogOverlay({
1485
+ className,
1486
+ ...props
1487
+ }) {
1488
+ return /* @__PURE__ */ jsx(
1489
+ DialogPrimitive.Overlay,
1490
+ {
1491
+ "data-slot": "dialog-overlay",
1492
+ className: cn(
1493
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1494
+ className
1495
+ ),
1496
+ ...props
1497
+ }
1498
+ );
1499
+ }
1500
+ function DialogContent({
1501
+ className,
1502
+ children,
1503
+ showCloseButton = true,
1504
+ ...props
1505
+ }) {
1506
+ return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
1507
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
1508
+ /* @__PURE__ */ jsxs(
1509
+ DialogPrimitive.Content,
1510
+ {
1511
+ "data-slot": "dialog-content",
1512
+ className: cn(
1513
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
1514
+ className
1515
+ ),
1516
+ ...props,
1517
+ children: [
1518
+ children,
1519
+ showCloseButton && /* @__PURE__ */ jsxs(
1520
+ DialogPrimitive.Close,
1521
+ {
1522
+ "data-slot": "dialog-close",
1523
+ className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1524
+ children: [
1525
+ /* @__PURE__ */ jsx(XIcon, {}),
1526
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1527
+ ]
1528
+ }
1529
+ )
1530
+ ]
1531
+ }
1532
+ )
1533
+ ] });
1534
+ }
1535
+ function DialogHeader({ className, ...props }) {
1536
+ return /* @__PURE__ */ jsx(
1537
+ "div",
1538
+ {
1539
+ "data-slot": "dialog-header",
1540
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1541
+ ...props
1542
+ }
1543
+ );
1544
+ }
1545
+ function DialogFooter({ className, ...props }) {
1546
+ return /* @__PURE__ */ jsx(
1547
+ "div",
1548
+ {
1549
+ "data-slot": "dialog-footer",
1550
+ className: cn(
1551
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1552
+ className
1553
+ ),
1554
+ ...props
1555
+ }
1556
+ );
1557
+ }
1558
+ function DialogTitle({
1559
+ className,
1560
+ ...props
1561
+ }) {
1562
+ return /* @__PURE__ */ jsx(
1563
+ DialogPrimitive.Title,
1564
+ {
1565
+ "data-slot": "dialog-title",
1566
+ className: cn("text-lg leading-none font-semibold", className),
1567
+ ...props
1568
+ }
1569
+ );
1570
+ }
1571
+ function DialogDescription({
1572
+ className,
1573
+ ...props
1574
+ }) {
1575
+ return /* @__PURE__ */ jsx(
1576
+ DialogPrimitive.Description,
1577
+ {
1578
+ "data-slot": "dialog-description",
1579
+ className: cn("text-muted-foreground text-sm", className),
1580
+ ...props
1581
+ }
1582
+ );
1583
+ }
1584
+ function MediaViewerDialog({
1585
+ open,
1586
+ onOpenChange,
1587
+ children,
1588
+ labels,
1589
+ className,
1590
+ contentClassName,
1591
+ showCloseButton = true
1592
+ }) {
1593
+ const closeLabel = labels?.close ?? "Cerrar";
1594
+ const title = labels?.title ?? "Visor";
1595
+ const description = labels?.description ?? title;
1596
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
1597
+ DialogContent,
1598
+ {
1599
+ showCloseButton: false,
1600
+ className: cn(
1601
+ "inset-0 flex h-dvh max-h-dvh w-screen max-w-none translate-x-0 translate-y-0 top-0 left-0 gap-0 overflow-hidden rounded-none border-0 p-0 shadow-none sm:max-w-none",
1602
+ contentClassName,
1603
+ className
1604
+ ),
1605
+ children: [
1606
+ /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: title }),
1607
+ /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: description }),
1608
+ showCloseButton && /* @__PURE__ */ jsx(
1609
+ "button",
1610
+ {
1611
+ type: "button",
1612
+ onClick: () => onOpenChange(false),
1613
+ "aria-label": closeLabel,
1614
+ className: "absolute top-3 right-3 z-20 rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70",
1615
+ children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
1616
+ }
1617
+ ),
1618
+ children
1619
+ ]
1620
+ }
1621
+ ) });
1622
+ }
1212
1623
  var miniMenuContentClassName = "max-h-none overflow-x-visible overflow-y-visible";
1213
1624
  var miniMenuContentDefaults = {
1214
1625
  sideOffset: 8,
@@ -3114,121 +3525,6 @@ function AvatarEditor({
3114
3525
  }
3115
3526
  );
3116
3527
  }
3117
- function Dialog({
3118
- ...props
3119
- }) {
3120
- return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
3121
- }
3122
- function DialogPortal({
3123
- ...props
3124
- }) {
3125
- return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
3126
- }
3127
- function DialogClose({
3128
- ...props
3129
- }) {
3130
- return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
3131
- }
3132
- function DialogOverlay({
3133
- className,
3134
- ...props
3135
- }) {
3136
- return /* @__PURE__ */ jsx(
3137
- DialogPrimitive.Overlay,
3138
- {
3139
- "data-slot": "dialog-overlay",
3140
- className: cn(
3141
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
3142
- className
3143
- ),
3144
- ...props
3145
- }
3146
- );
3147
- }
3148
- function DialogContent({
3149
- className,
3150
- children,
3151
- showCloseButton = true,
3152
- ...props
3153
- }) {
3154
- return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
3155
- /* @__PURE__ */ jsx(DialogOverlay, {}),
3156
- /* @__PURE__ */ jsxs(
3157
- DialogPrimitive.Content,
3158
- {
3159
- "data-slot": "dialog-content",
3160
- className: cn(
3161
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
3162
- className
3163
- ),
3164
- ...props,
3165
- children: [
3166
- children,
3167
- showCloseButton && /* @__PURE__ */ jsxs(
3168
- DialogPrimitive.Close,
3169
- {
3170
- "data-slot": "dialog-close",
3171
- className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
3172
- children: [
3173
- /* @__PURE__ */ jsx(XIcon, {}),
3174
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
3175
- ]
3176
- }
3177
- )
3178
- ]
3179
- }
3180
- )
3181
- ] });
3182
- }
3183
- function DialogHeader({ className, ...props }) {
3184
- return /* @__PURE__ */ jsx(
3185
- "div",
3186
- {
3187
- "data-slot": "dialog-header",
3188
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
3189
- ...props
3190
- }
3191
- );
3192
- }
3193
- function DialogFooter({ className, ...props }) {
3194
- return /* @__PURE__ */ jsx(
3195
- "div",
3196
- {
3197
- "data-slot": "dialog-footer",
3198
- className: cn(
3199
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
3200
- className
3201
- ),
3202
- ...props
3203
- }
3204
- );
3205
- }
3206
- function DialogTitle({
3207
- className,
3208
- ...props
3209
- }) {
3210
- return /* @__PURE__ */ jsx(
3211
- DialogPrimitive.Title,
3212
- {
3213
- "data-slot": "dialog-title",
3214
- className: cn("text-lg leading-none font-semibold", className),
3215
- ...props
3216
- }
3217
- );
3218
- }
3219
- function DialogDescription({
3220
- className,
3221
- ...props
3222
- }) {
3223
- return /* @__PURE__ */ jsx(
3224
- DialogPrimitive.Description,
3225
- {
3226
- "data-slot": "dialog-description",
3227
- className: cn("text-muted-foreground text-sm", className),
3228
- ...props
3229
- }
3230
- );
3231
- }
3232
3528
  function Drawer({
3233
3529
  ...props
3234
3530
  }) {
@@ -5615,6 +5911,340 @@ function useSearchHotkey(options = {}) {
5615
5911
  return { open, setOpen, toggle };
5616
5912
  }
5617
5913
 
5914
+ // src/lib/scroll-restoration/store.ts
5915
+ var MAX_MARKS = 30;
5916
+ var MAX_SNAPSHOT_ROUTES = 3;
5917
+ var PENDING_TRAVERSAL_TTL_MS = 1e3;
5918
+ var scrollMarks = /* @__PURE__ */ new Map();
5919
+ var snapshotsByRoute = /* @__PURE__ */ new Map();
5920
+ var pendingTraversalKey = null;
5921
+ var pendingTraversalTimeout = null;
5922
+ var popstateInstalled = false;
5923
+ function touchMapEntry(map, key, value, max) {
5924
+ if (map.has(key)) map.delete(key);
5925
+ map.set(key, value);
5926
+ while (map.size > max) {
5927
+ const oldest = map.keys().next().value;
5928
+ if (oldest === void 0) break;
5929
+ map.delete(oldest);
5930
+ }
5931
+ }
5932
+ function getScrollMark(routeKey) {
5933
+ const mark = scrollMarks.get(routeKey);
5934
+ if (!mark) return void 0;
5935
+ scrollMarks.delete(routeKey);
5936
+ scrollMarks.set(routeKey, mark);
5937
+ return mark;
5938
+ }
5939
+ function setScrollMark(routeKey, mark) {
5940
+ touchMapEntry(scrollMarks, routeKey, mark, MAX_MARKS);
5941
+ }
5942
+ function deleteScrollMark(routeKey) {
5943
+ scrollMarks.delete(routeKey);
5944
+ }
5945
+ function getSnapshot(routeKey, subKey) {
5946
+ const route = snapshotsByRoute.get(routeKey);
5947
+ if (!route) return void 0;
5948
+ snapshotsByRoute.delete(routeKey);
5949
+ snapshotsByRoute.set(routeKey, route);
5950
+ return route.get(subKey);
5951
+ }
5952
+ function setSnapshot(routeKey, subKey, value) {
5953
+ let route = snapshotsByRoute.get(routeKey);
5954
+ if (route) {
5955
+ snapshotsByRoute.delete(routeKey);
5956
+ } else {
5957
+ route = /* @__PURE__ */ new Map();
5958
+ }
5959
+ route.set(subKey, value);
5960
+ snapshotsByRoute.set(routeKey, route);
5961
+ while (snapshotsByRoute.size > MAX_SNAPSHOT_ROUTES) {
5962
+ const oldest = snapshotsByRoute.keys().next().value;
5963
+ if (oldest === void 0) break;
5964
+ snapshotsByRoute.delete(oldest);
5965
+ }
5966
+ }
5967
+ function deleteSnapshotsForRoute(routeKey) {
5968
+ snapshotsByRoute.delete(routeKey);
5969
+ }
5970
+ function clearRoute(routeKey) {
5971
+ deleteScrollMark(routeKey);
5972
+ deleteSnapshotsForRoute(routeKey);
5973
+ }
5974
+ function clearPendingTraversalTimer() {
5975
+ if (pendingTraversalTimeout !== null) {
5976
+ clearTimeout(pendingTraversalTimeout);
5977
+ pendingTraversalTimeout = null;
5978
+ }
5979
+ }
5980
+ function schedulePendingTraversalClear() {
5981
+ clearPendingTraversalTimer();
5982
+ pendingTraversalTimeout = setTimeout(() => {
5983
+ pendingTraversalKey = null;
5984
+ pendingTraversalTimeout = null;
5985
+ }, PENDING_TRAVERSAL_TTL_MS);
5986
+ }
5987
+ function ensurePopstateListener() {
5988
+ if (popstateInstalled || typeof window === "undefined") return;
5989
+ popstateInstalled = true;
5990
+ window.addEventListener("popstate", () => {
5991
+ pendingTraversalKey = window.location.pathname + window.location.search;
5992
+ schedulePendingTraversalClear();
5993
+ });
5994
+ }
5995
+ function isPendingTraversal(routeKey) {
5996
+ ensurePopstateListener();
5997
+ return pendingTraversalKey === routeKey;
5998
+ }
5999
+ function consumePendingTraversal(routeKey) {
6000
+ ensurePopstateListener();
6001
+ if (pendingTraversalKey !== routeKey) return false;
6002
+ pendingTraversalKey = null;
6003
+ clearPendingTraversalTimer();
6004
+ return true;
6005
+ }
6006
+ function getScrollTop(container) {
6007
+ if (!container) {
6008
+ return window.scrollY || document.documentElement.scrollTop || 0;
6009
+ }
6010
+ return container.scrollTop;
6011
+ }
6012
+ function setScrollTop(container, top) {
6013
+ if (!container) {
6014
+ window.scrollTo(0, top);
6015
+ return;
6016
+ }
6017
+ container.scrollTop = top;
6018
+ }
6019
+ function scrollportTop(container) {
6020
+ return container ? container.getBoundingClientRect().top : 0;
6021
+ }
6022
+ function scrollportBottom(container) {
6023
+ if (!container) return window.innerHeight;
6024
+ return container.getBoundingClientRect().bottom;
6025
+ }
6026
+ function escapeAttrValue(value) {
6027
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
6028
+ return CSS.escape(value);
6029
+ }
6030
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
6031
+ }
6032
+ function resolveAnchor(container, attribute) {
6033
+ const scope = container ?? document;
6034
+ const nodes = scope.querySelectorAll(`[${attribute}]`);
6035
+ if (nodes.length === 0) return null;
6036
+ const portTop = scrollportTop(container);
6037
+ const portBottom = scrollportBottom(container);
6038
+ let bestId = null;
6039
+ let bestTop = 0;
6040
+ let bestScore = Infinity;
6041
+ for (const node of nodes) {
6042
+ if (!(node instanceof HTMLElement)) continue;
6043
+ const id = node.getAttribute(attribute);
6044
+ if (!id) continue;
6045
+ const rect = node.getBoundingClientRect();
6046
+ if (rect.bottom <= portTop || rect.top >= portBottom) continue;
6047
+ const relativeTop = rect.top - portTop;
6048
+ const score = Math.abs(relativeTop);
6049
+ if (score < bestScore) {
6050
+ bestScore = score;
6051
+ bestId = id;
6052
+ bestTop = relativeTop;
6053
+ }
6054
+ }
6055
+ if (!bestId) return null;
6056
+ return { id: bestId, top: bestTop };
6057
+ }
6058
+ function captureMark(container, attribute) {
6059
+ const anchor = resolveAnchor(container, attribute);
6060
+ return {
6061
+ top: getScrollTop(container),
6062
+ anchorId: anchor?.id ?? null,
6063
+ anchorTop: anchor?.top ?? 0
6064
+ };
6065
+ }
6066
+ function applyMark(container, mark, attribute) {
6067
+ if (mark.anchorId) {
6068
+ const scope = container ?? document;
6069
+ const el = scope.querySelector(
6070
+ `[${attribute}="${escapeAttrValue(mark.anchorId)}"]`
6071
+ );
6072
+ if (el instanceof HTMLElement) {
6073
+ const portTop = scrollportTop(container);
6074
+ const currentRelativeTop = el.getBoundingClientRect().top - portTop;
6075
+ const delta = currentRelativeTop - mark.anchorTop;
6076
+ setScrollTop(container, getScrollTop(container) + delta);
6077
+ return true;
6078
+ }
6079
+ }
6080
+ setScrollTop(container, mark.top);
6081
+ return false;
6082
+ }
6083
+ function getScrollHeight(container) {
6084
+ if (!container) {
6085
+ return Math.max(
6086
+ document.documentElement.scrollHeight,
6087
+ document.body?.scrollHeight ?? 0
6088
+ );
6089
+ }
6090
+ return container.scrollHeight;
6091
+ }
6092
+ var DEFAULT_ANCHOR_ATTRIBUTE = "data-scroll-anchor";
6093
+ var STABILIZE_MS = 600;
6094
+ var ScrollRestorationContext = createContext(null);
6095
+ function useScrollRestorationContext() {
6096
+ return useContext(ScrollRestorationContext);
6097
+ }
6098
+ function readContainer(containerRef) {
6099
+ return containerRef?.current ?? null;
6100
+ }
6101
+ function ScrollRestorationProvider({
6102
+ routeKey,
6103
+ containerRef,
6104
+ anchorAttribute = DEFAULT_ANCHOR_ATTRIBUTE,
6105
+ children
6106
+ }) {
6107
+ ensurePopstateListener();
6108
+ const traversalForRouteRef = useRef({ routeKey: "", isTraversal: false });
6109
+ if (traversalForRouteRef.current.routeKey !== routeKey) {
6110
+ traversalForRouteRef.current = {
6111
+ routeKey,
6112
+ isTraversal: isPendingTraversal(routeKey)
6113
+ };
6114
+ }
6115
+ const isTraversal = traversalForRouteRef.current.isTraversal;
6116
+ const value = useMemo(
6117
+ () => ({
6118
+ routeKey,
6119
+ isTraversal,
6120
+ anchorAttribute
6121
+ }),
6122
+ [routeKey, isTraversal, anchorAttribute]
6123
+ );
6124
+ useEffect(() => {
6125
+ if (typeof window === "undefined" || !("scrollRestoration" in history)) {
6126
+ return;
6127
+ }
6128
+ const previous = history.scrollRestoration;
6129
+ history.scrollRestoration = "manual";
6130
+ return () => {
6131
+ history.scrollRestoration = previous;
6132
+ };
6133
+ }, []);
6134
+ const prevRouteKeyRef = useRef(routeKey);
6135
+ const anchorAttributeRef = useRef(anchorAttribute);
6136
+ anchorAttributeRef.current = anchorAttribute;
6137
+ useEffect(() => {
6138
+ const container = readContainer(containerRef);
6139
+ const target = container ?? window;
6140
+ let rafId = 0;
6141
+ const onScroll = () => {
6142
+ if (rafId) return;
6143
+ rafId = requestAnimationFrame(() => {
6144
+ rafId = 0;
6145
+ setScrollMark(
6146
+ routeKey,
6147
+ captureMark(readContainer(containerRef), anchorAttribute)
6148
+ );
6149
+ });
6150
+ };
6151
+ target.addEventListener("scroll", onScroll, { passive: true });
6152
+ return () => {
6153
+ target.removeEventListener("scroll", onScroll);
6154
+ if (rafId) cancelAnimationFrame(rafId);
6155
+ };
6156
+ }, [routeKey, containerRef, anchorAttribute]);
6157
+ useLayoutEffect(() => {
6158
+ const container = readContainer(containerRef);
6159
+ const prevKey = prevRouteKeyRef.current;
6160
+ if (prevKey !== routeKey) {
6161
+ setScrollMark(
6162
+ prevKey,
6163
+ captureMark(container, anchorAttributeRef.current)
6164
+ );
6165
+ prevRouteKeyRef.current = routeKey;
6166
+ }
6167
+ const hasHash = typeof window !== "undefined" && window.location.hash.length > 1;
6168
+ if (isTraversal) {
6169
+ consumePendingTraversal(routeKey);
6170
+ const mark = getScrollMark(routeKey);
6171
+ if (!mark) return void 0;
6172
+ applyMark(container, mark, anchorAttribute);
6173
+ return startStabilizationLoop(container, mark, anchorAttribute);
6174
+ }
6175
+ clearRoute(routeKey);
6176
+ if (!hasHash) {
6177
+ setScrollTop(container, 0);
6178
+ }
6179
+ return void 0;
6180
+ }, [routeKey, isTraversal, containerRef, anchorAttribute]);
6181
+ return /* @__PURE__ */ jsx(ScrollRestorationContext.Provider, { value, children });
6182
+ }
6183
+ function startStabilizationLoop(container, mark, attribute) {
6184
+ const startedAt = performance.now();
6185
+ let lastHeight = getScrollHeight(container);
6186
+ let rafId = 0;
6187
+ let aborted = false;
6188
+ const abort = () => {
6189
+ aborted = true;
6190
+ };
6191
+ const onUserIntent = () => {
6192
+ abort();
6193
+ };
6194
+ window.addEventListener("wheel", onUserIntent, {
6195
+ passive: true,
6196
+ once: true
6197
+ });
6198
+ window.addEventListener("touchstart", onUserIntent, {
6199
+ passive: true,
6200
+ once: true
6201
+ });
6202
+ window.addEventListener("keydown", onUserIntent, {
6203
+ passive: true,
6204
+ once: true
6205
+ });
6206
+ window.addEventListener("pointerdown", onUserIntent, {
6207
+ passive: true,
6208
+ once: true
6209
+ });
6210
+ const tick = () => {
6211
+ if (aborted) return;
6212
+ if (performance.now() - startedAt > STABILIZE_MS) return;
6213
+ const height = getScrollHeight(container);
6214
+ if (height !== lastHeight) {
6215
+ lastHeight = height;
6216
+ applyMark(container, mark, attribute);
6217
+ }
6218
+ rafId = requestAnimationFrame(tick);
6219
+ };
6220
+ rafId = requestAnimationFrame(tick);
6221
+ return () => {
6222
+ abort();
6223
+ if (rafId) cancelAnimationFrame(rafId);
6224
+ window.removeEventListener("wheel", onUserIntent);
6225
+ window.removeEventListener("touchstart", onUserIntent);
6226
+ window.removeEventListener("keydown", onUserIntent);
6227
+ window.removeEventListener("pointerdown", onUserIntent);
6228
+ };
6229
+ }
6230
+
6231
+ // src/hooks/useRestorableState.ts
6232
+ function useRestorableState(subKey, initializer) {
6233
+ const ctx = useScrollRestorationContext();
6234
+ const [state, setState] = useState(() => {
6235
+ if (!ctx) return initializer();
6236
+ const traversal = ctx.isTraversal || isPendingTraversal(ctx.routeKey);
6237
+ if (!traversal) return initializer();
6238
+ const snap = getSnapshot(ctx.routeKey, subKey);
6239
+ return snap !== void 0 ? snap : initializer();
6240
+ });
6241
+ useEffect(() => {
6242
+ if (!ctx) return;
6243
+ setSnapshot(ctx.routeKey, subKey, state);
6244
+ }, [ctx, subKey, state]);
6245
+ return [state, setState];
6246
+ }
6247
+
5618
6248
  // src/components/search/fuzzy.ts
5619
6249
  function normalize(value) {
5620
6250
  return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
@@ -8665,6 +9295,6 @@ function useRealtimeChannel({
8665
9295
  }, [enabled, reconnectDelayMs]);
8666
9296
  }
8667
9297
 
8668
- export { AvatarEditor, AvatarEditorDialog, BrandX, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, DEFAULT_EDITABLE_SECTION_LABELS, DataList, DataListItem, DropdownMenuItem, DropdownMenuSeparator, EditableSection, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, FeedVideoPlayer, FieldMessage, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageComposer, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PLATFORM_ACCOUNT_ORG_COLLAPSE_AT, PaymentIcon, PhoneInput, PlatformAccountMenu, PlatformNavbar, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describeAvResync, describePlaybackError, formatPaymentCardBrand, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, mergePlatformAccountOrgs, normalizeEmojiQuery, normalizePaymentCardBrand, paymentIconBrandFromStripe, paymentTypeFromStripeBrand, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useEditableSection, useFeedExclusivePlayback, useHlsPlayback, useLanguageContext, useMediaQuery2 as useMediaQuery, usePlaybackStats, useRealtimeChannel, useScrollFadeXEdges, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
9298
+ export { AvatarEditor, AvatarEditorDialog, BrandX, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, DEFAULT_EDITABLE_SECTION_LABELS, DataList, DataListItem, DropdownMenuItem, DropdownMenuSeparator, EditableSection, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, ExpandableText, FeedVideoPlayer, FieldMessage, ImageLightbox, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, MediaCarousel, MediaViewerDialog, Message, MessageAvatar, MessageComposer, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PLATFORM_ACCOUNT_ORG_COLLAPSE_AT, PaymentIcon, PhoneInput, PlatformAccountMenu, PlatformNavbar, ScrollRestorationProvider, SearchCommand, SearchablePicker, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, ZOOM_PAN_MAX_SCALE, ZOOM_PAN_MIN_SCALE, ZOOM_PAN_SWIPE_COMMIT_PX, ZOOM_PAN_SWIPE_SNAP_MS, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describeAvResync, describePlaybackError, formatPaymentCardBrand, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, mergePlatformAccountOrgs, normalizeEmojiQuery, normalizePaymentCardBrand, paymentIconBrandFromStripe, paymentTypeFromStripeBrand, prefersReducedMotion, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useEditableSection, useFeedExclusivePlayback, useHlsPlayback, useLanguageContext, useMediaQuery2 as useMediaQuery, usePlaybackStats, useRealtimeChannel, useRestorableState, useScrollFadeXEdges, useScrollRestorationContext, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional, useZoomPanGestures, zoomToward };
8669
9299
  //# sourceMappingURL=index.js.map
8670
9300
  //# sourceMappingURL=index.js.map