@jekrch/react-viewport-lightbox 0.2.0 → 0.3.1

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.d.cts CHANGED
@@ -50,6 +50,12 @@ interface ViewerContext<TData = unknown> {
50
50
  index: number;
51
51
  item: ViewerItem<TData>;
52
52
  total: number;
53
+ /**
54
+ * True once the exit animation has started (after a close was requested,
55
+ * before `onClose` fires and the viewer unmounts). Lets overlay content fade
56
+ * out in step with the closing chrome instead of vanishing on unmount.
57
+ */
58
+ closing: boolean;
53
59
  hasPrev: boolean;
54
60
  hasNext: boolean;
55
61
  goPrev: () => void;
@@ -90,6 +96,14 @@ interface ImageViewerProps<TData = unknown> {
90
96
  onNavigate?: (direction: "prev" | "next") => void;
91
97
  /** Called AFTER the exit animation completes. */
92
98
  onClose: () => void;
99
+ /**
100
+ * Called when the user presses Escape, before the viewer closes. Return
101
+ * `true` to mark the key handled and veto the default close — e.g. to dismiss
102
+ * a consumer overlay (drawer/graph) first, closing the viewer only on a
103
+ * second press. Return `false`/`undefined` to fall through to the default
104
+ * close.
105
+ */
106
+ onEscape?: () => boolean;
93
107
  /**
94
108
  * Enables a shared-element "zoom from thumbnail" open/close transition. Given
95
109
  * the active index, return the on-screen rect of the source element (e.g. the
@@ -109,6 +123,20 @@ interface ImageViewerProps<TData = unknown> {
109
123
  zoomToCursor?: boolean;
110
124
  /** Show the `index / total` counter. Default `true`. */
111
125
  showCounter?: boolean;
126
+ /**
127
+ * Show the built-in zoom in/out/reset buttons in the top bar. Independent of
128
+ * `zoom` (which governs the gesture behavior): set this `false` to keep
129
+ * zoom/pan gestures while a consumer overlay (e.g. an open graph/drawer that
130
+ * covers the image) temporarily owns the chrome. Default `true`.
131
+ */
132
+ showZoomControls?: boolean;
133
+ /**
134
+ * Suppress built-in arrow-key navigation (and the swipe commit) without
135
+ * tearing the viewer down. Useful while an overlay that has its own
136
+ * left/right handling is open. Does not hide the on-screen nav buttons.
137
+ * Default `false`.
138
+ */
139
+ disableNavigation?: boolean;
112
140
  /** Wrap around at the ends. Default `false`. */
113
141
  loop?: boolean;
114
142
  /**
@@ -129,6 +157,35 @@ interface ImageViewerProps<TData = unknown> {
129
157
  renderNavStart?: (ctx: ViewerContext<TData>) => ReactNode;
130
158
  /** Pinned to the RIGHT edge of the nav row; mirror of `renderNavStart`. */
131
159
  renderNavEnd?: (ctx: ViewerContext<TData>) => ReactNode;
160
+ /**
161
+ * Where the `renderNavStart` / `renderNavEnd` slots sit relative to the
162
+ * prev/counter/next group:
163
+ * - `"edge"` (default): pinned to the left/right edges of the nav row (max
164
+ * 42rem), keeping the nav group optically centered regardless of slot width.
165
+ * - `"inline"`: placed directly flanking the nav group as one centered
166
+ * cluster, so a details/info toggle hugs the arrows.
167
+ */
168
+ navSlotPlacement?: "edge" | "inline";
169
+ /**
170
+ * Size of the prev/next nav arrows (the bottom nav controls). A number is
171
+ * treated as pixels; a string is used verbatim (e.g. `"1.5rem"`). Sets the
172
+ * `--rvl-nav-height` custom property, so it can equally be themed in CSS.
173
+ * Defaults to `2.375rem` (38px) to match the comic-snaps viewer.
174
+ */
175
+ navHeight?: number | string;
176
+ /**
177
+ * Gap between the bottom nav controls and the viewport's bottom edge. A number
178
+ * is treated as pixels; a string is used verbatim (e.g. `"2rem"`). Sets the
179
+ * `--rvl-nav-inset` custom property and is floored by the device safe-area
180
+ * inset. Defaults to `1.3rem`.
181
+ */
182
+ navInset?: number | string;
183
+ /**
184
+ * Counter font size. By default the counter scales with `navHeight` (≈0.29×);
185
+ * set this to override that ratio with a fixed size. A number is treated as
186
+ * pixels; a string is used verbatim. Sets `--rvl-counter-font-size`.
187
+ */
188
+ counterFontSize?: number | string;
132
189
  /** Content below the nav row. */
133
190
  renderFooter?: (ctx: ViewerContext<TData>) => ReactNode;
134
191
  /** Drawers/graphs layered over the image. */
@@ -144,7 +201,7 @@ interface ImageViewerProps<TData = unknown> {
144
201
  * `onIndexChange`; mount it when open and it runs its own enter/exit animation,
145
202
  * calling `onClose` after the exit completes.
146
203
  */
147
- declare function ImageViewer<TData = unknown>({ items, index, onIndexChange, onNavigate, onClose, getOriginRect, zoom, zoomToCursor, showCounter, loop, closeOnBackdropClick, renderHeader, renderHeaderActions, renderNavStart, renderNavEnd, renderFooter, renderOverlay, classNames, icons, ariaLabel, }: ImageViewerProps<TData>): react.JSX.Element | null;
204
+ declare function ImageViewer<TData = unknown>({ items, index, onIndexChange, onNavigate, onClose, onEscape, getOriginRect, zoom, zoomToCursor, showCounter, showZoomControls, disableNavigation, loop, closeOnBackdropClick, renderHeader, renderHeaderActions, renderNavStart, renderNavEnd, navSlotPlacement, navHeight, navInset, counterFontSize, renderFooter, renderOverlay, classNames, icons, ariaLabel, }: ImageViewerProps<TData>): react.JSX.Element | null;
148
205
 
149
206
  interface NavButtonProps {
150
207
  direction: "prev" | "next";
package/dist/index.d.ts CHANGED
@@ -50,6 +50,12 @@ interface ViewerContext<TData = unknown> {
50
50
  index: number;
51
51
  item: ViewerItem<TData>;
52
52
  total: number;
53
+ /**
54
+ * True once the exit animation has started (after a close was requested,
55
+ * before `onClose` fires and the viewer unmounts). Lets overlay content fade
56
+ * out in step with the closing chrome instead of vanishing on unmount.
57
+ */
58
+ closing: boolean;
53
59
  hasPrev: boolean;
54
60
  hasNext: boolean;
55
61
  goPrev: () => void;
@@ -90,6 +96,14 @@ interface ImageViewerProps<TData = unknown> {
90
96
  onNavigate?: (direction: "prev" | "next") => void;
91
97
  /** Called AFTER the exit animation completes. */
92
98
  onClose: () => void;
99
+ /**
100
+ * Called when the user presses Escape, before the viewer closes. Return
101
+ * `true` to mark the key handled and veto the default close — e.g. to dismiss
102
+ * a consumer overlay (drawer/graph) first, closing the viewer only on a
103
+ * second press. Return `false`/`undefined` to fall through to the default
104
+ * close.
105
+ */
106
+ onEscape?: () => boolean;
93
107
  /**
94
108
  * Enables a shared-element "zoom from thumbnail" open/close transition. Given
95
109
  * the active index, return the on-screen rect of the source element (e.g. the
@@ -109,6 +123,20 @@ interface ImageViewerProps<TData = unknown> {
109
123
  zoomToCursor?: boolean;
110
124
  /** Show the `index / total` counter. Default `true`. */
111
125
  showCounter?: boolean;
126
+ /**
127
+ * Show the built-in zoom in/out/reset buttons in the top bar. Independent of
128
+ * `zoom` (which governs the gesture behavior): set this `false` to keep
129
+ * zoom/pan gestures while a consumer overlay (e.g. an open graph/drawer that
130
+ * covers the image) temporarily owns the chrome. Default `true`.
131
+ */
132
+ showZoomControls?: boolean;
133
+ /**
134
+ * Suppress built-in arrow-key navigation (and the swipe commit) without
135
+ * tearing the viewer down. Useful while an overlay that has its own
136
+ * left/right handling is open. Does not hide the on-screen nav buttons.
137
+ * Default `false`.
138
+ */
139
+ disableNavigation?: boolean;
112
140
  /** Wrap around at the ends. Default `false`. */
113
141
  loop?: boolean;
114
142
  /**
@@ -129,6 +157,35 @@ interface ImageViewerProps<TData = unknown> {
129
157
  renderNavStart?: (ctx: ViewerContext<TData>) => ReactNode;
130
158
  /** Pinned to the RIGHT edge of the nav row; mirror of `renderNavStart`. */
131
159
  renderNavEnd?: (ctx: ViewerContext<TData>) => ReactNode;
160
+ /**
161
+ * Where the `renderNavStart` / `renderNavEnd` slots sit relative to the
162
+ * prev/counter/next group:
163
+ * - `"edge"` (default): pinned to the left/right edges of the nav row (max
164
+ * 42rem), keeping the nav group optically centered regardless of slot width.
165
+ * - `"inline"`: placed directly flanking the nav group as one centered
166
+ * cluster, so a details/info toggle hugs the arrows.
167
+ */
168
+ navSlotPlacement?: "edge" | "inline";
169
+ /**
170
+ * Size of the prev/next nav arrows (the bottom nav controls). A number is
171
+ * treated as pixels; a string is used verbatim (e.g. `"1.5rem"`). Sets the
172
+ * `--rvl-nav-height` custom property, so it can equally be themed in CSS.
173
+ * Defaults to `2.375rem` (38px) to match the comic-snaps viewer.
174
+ */
175
+ navHeight?: number | string;
176
+ /**
177
+ * Gap between the bottom nav controls and the viewport's bottom edge. A number
178
+ * is treated as pixels; a string is used verbatim (e.g. `"2rem"`). Sets the
179
+ * `--rvl-nav-inset` custom property and is floored by the device safe-area
180
+ * inset. Defaults to `1.3rem`.
181
+ */
182
+ navInset?: number | string;
183
+ /**
184
+ * Counter font size. By default the counter scales with `navHeight` (≈0.29×);
185
+ * set this to override that ratio with a fixed size. A number is treated as
186
+ * pixels; a string is used verbatim. Sets `--rvl-counter-font-size`.
187
+ */
188
+ counterFontSize?: number | string;
132
189
  /** Content below the nav row. */
133
190
  renderFooter?: (ctx: ViewerContext<TData>) => ReactNode;
134
191
  /** Drawers/graphs layered over the image. */
@@ -144,7 +201,7 @@ interface ImageViewerProps<TData = unknown> {
144
201
  * `onIndexChange`; mount it when open and it runs its own enter/exit animation,
145
202
  * calling `onClose` after the exit completes.
146
203
  */
147
- declare function ImageViewer<TData = unknown>({ items, index, onIndexChange, onNavigate, onClose, getOriginRect, zoom, zoomToCursor, showCounter, loop, closeOnBackdropClick, renderHeader, renderHeaderActions, renderNavStart, renderNavEnd, renderFooter, renderOverlay, classNames, icons, ariaLabel, }: ImageViewerProps<TData>): react.JSX.Element | null;
204
+ declare function ImageViewer<TData = unknown>({ items, index, onIndexChange, onNavigate, onClose, onEscape, getOriginRect, zoom, zoomToCursor, showCounter, showZoomControls, disableNavigation, loop, closeOnBackdropClick, renderHeader, renderHeaderActions, renderNavStart, renderNavEnd, navSlotPlacement, navHeight, navInset, counterFontSize, renderFooter, renderOverlay, classNames, icons, ariaLabel, }: ImageViewerProps<TData>): react.JSX.Element | null;
148
205
 
149
206
  interface NavButtonProps {
150
207
  direction: "prev" | "next";
package/dist/index.js CHANGED
@@ -590,16 +590,29 @@ function useBarMeasure(topBarRef, bottomBarRef, measureKey) {
590
590
  var lockCount = 0;
591
591
  var previousOverflow = "";
592
592
  var previousPaddingRight = "";
593
+ var previousPosition = "";
594
+ var previousTop = "";
595
+ var previousWidth = "";
596
+ var lockedScrollY = 0;
593
597
  function useBodyScrollLock(isLocked) {
594
598
  useEffect(() => {
595
599
  if (!isLocked) return;
596
600
  if (typeof document === "undefined") return;
597
601
  if (lockCount === 0) {
598
602
  const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
603
+ const rootGutter = window.getComputedStyle(document.documentElement).scrollbarGutter;
604
+ const reservesGutter = typeof rootGutter === "string" && rootGutter.includes("stable");
605
+ lockedScrollY = window.scrollY;
599
606
  previousOverflow = document.body.style.overflow;
600
607
  previousPaddingRight = document.body.style.paddingRight;
608
+ previousPosition = document.body.style.position;
609
+ previousTop = document.body.style.top;
610
+ previousWidth = document.body.style.width;
601
611
  document.body.style.overflow = "hidden";
602
- if (scrollbarWidth > 0) {
612
+ document.body.style.position = "fixed";
613
+ document.body.style.top = `-${lockedScrollY}px`;
614
+ document.body.style.width = "100%";
615
+ if (scrollbarWidth > 0 && !reservesGutter) {
603
616
  const currentPaddingRight = parseFloat(window.getComputedStyle(document.body).paddingRight) || 0;
604
617
  document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;
605
618
  }
@@ -610,6 +623,10 @@ function useBodyScrollLock(isLocked) {
610
623
  if (lockCount === 0) {
611
624
  document.body.style.overflow = previousOverflow;
612
625
  document.body.style.paddingRight = previousPaddingRight;
626
+ document.body.style.position = previousPosition;
627
+ document.body.style.top = previousTop;
628
+ document.body.style.width = previousWidth;
629
+ window.scrollTo(0, lockedScrollY);
613
630
  }
614
631
  };
615
632
  }, [isLocked]);
@@ -722,6 +739,7 @@ function NavButton({ direction, enabled, onClick, icon, className }) {
722
739
  }
723
740
  var ANIM_MS = 250;
724
741
  var IMG_PADDING = 44;
742
+ var GHOST_CLICK_MS = 700;
725
743
  var ZOOM_EASE = "cubic-bezier(0.22, 1, 0.36, 1)";
726
744
  function prefersReducedMotion() {
727
745
  if (typeof window === "undefined" || !window.matchMedia) return false;
@@ -747,16 +765,23 @@ function ImageViewer({
747
765
  onIndexChange,
748
766
  onNavigate,
749
767
  onClose,
768
+ onEscape,
750
769
  getOriginRect,
751
770
  zoom = true,
752
771
  zoomToCursor = true,
753
772
  showCounter = true,
773
+ showZoomControls = true,
774
+ disableNavigation = false,
754
775
  loop = false,
755
776
  closeOnBackdropClick = false,
756
777
  renderHeader,
757
778
  renderHeaderActions,
758
779
  renderNavStart,
759
780
  renderNavEnd,
781
+ navSlotPlacement = "edge",
782
+ navHeight,
783
+ navInset,
784
+ counterFontSize,
760
785
  renderFooter,
761
786
  renderOverlay,
762
787
  classNames,
@@ -768,6 +793,11 @@ function ImageViewer({
768
793
  const [collapsing, setCollapsing] = useState(false);
769
794
  const [isTouchDevice, setIsTouchDevice] = useState(false);
770
795
  const [contentShift, setContentShiftState] = useState({ transform: null, animate: true });
796
+ const openedAtRef = useRef(Date.now());
797
+ const isGhostMouseEvent = useCallback(
798
+ () => Date.now() - openedAtRef.current < GHOST_CLICK_MS,
799
+ []
800
+ );
771
801
  const containerRef = useRef(null);
772
802
  const imgWrapperRef = useRef(null);
773
803
  const topBarRef = useRef(null);
@@ -801,7 +831,15 @@ function ImageViewer({
801
831
  const { slideTrackRef, slideActive, slideAnimating, swipeOffset, commitSlide } = slide;
802
832
  const gestures = useGestureHandler(zoomPan, slide, hasPrev, hasNext, zoom, zoomToCursor);
803
833
  useEffect(() => {
804
- setIsTouchDevice("ontouchstart" in window || navigator.maxTouchPoints > 0);
834
+ if (typeof window === "undefined" || !window.matchMedia) {
835
+ setIsTouchDevice("ontouchstart" in window || navigator.maxTouchPoints > 0);
836
+ return;
837
+ }
838
+ const mq = window.matchMedia("(hover: none) and (pointer: coarse)");
839
+ const update = () => setIsTouchDevice(mq.matches);
840
+ update();
841
+ mq.addEventListener("change", update);
842
+ return () => mq.removeEventListener("change", update);
805
843
  }, []);
806
844
  useEffect(() => {
807
845
  const onResize = () => setViewportWidth(window.innerWidth);
@@ -907,6 +945,33 @@ function ImageViewer({
907
945
  }
908
946
  setTimeout(onClose, reduce ? 0 : ANIM_MS);
909
947
  }, [onClose, getOriginRect, index, isZoomed, imgRef, imgWrapperRef]);
948
+ const handleBackdropTouchEnd = useCallback(
949
+ (e) => {
950
+ if (e.target !== e.currentTarget) return;
951
+ e.preventDefault();
952
+ handleClose();
953
+ },
954
+ [handleClose]
955
+ );
956
+ const handleDoubleClickGuarded = useCallback(
957
+ (e) => {
958
+ if (isGhostMouseEvent()) return;
959
+ handleDoubleClick(e);
960
+ },
961
+ [handleDoubleClick, isGhostMouseEvent]
962
+ );
963
+ const handleBackdropClick = useCallback(() => {
964
+ if (isGhostMouseEvent()) return;
965
+ handleClose();
966
+ }, [handleClose, isGhostMouseEvent]);
967
+ const handleStageClick = useCallback(
968
+ (e) => {
969
+ if (e.target !== e.currentTarget) return;
970
+ if (isGhostMouseEvent()) return;
971
+ handleClose();
972
+ },
973
+ [handleClose, isGhostMouseEvent]
974
+ );
910
975
  const navigate = useCallback(
911
976
  (dir) => {
912
977
  if (dir === "prev") {
@@ -920,16 +985,17 @@ function ImageViewer({
920
985
  useEffect(() => {
921
986
  const handler = (e) => {
922
987
  if (e.key === "Escape") {
988
+ if (onEscape?.()) return;
923
989
  handleClose();
924
990
  return;
925
991
  }
926
- if (displayScale > 1) return;
992
+ if (disableNavigation || displayScale > 1) return;
927
993
  if (e.key === "ArrowLeft" && hasPrev) navigate("prev");
928
994
  if (e.key === "ArrowRight" && hasNext) navigate("next");
929
995
  };
930
996
  window.addEventListener("keydown", handler);
931
997
  return () => window.removeEventListener("keydown", handler);
932
- }, [handleClose, hasPrev, hasNext, displayScale, navigate]);
998
+ }, [handleClose, hasPrev, hasNext, displayScale, navigate, onEscape, disableNavigation]);
933
999
  const setContentShift = useCallback((transform, animate = true) => {
934
1000
  setContentShiftState({ transform, animate });
935
1001
  }, []);
@@ -938,6 +1004,7 @@ function ImageViewer({
938
1004
  index,
939
1005
  item,
940
1006
  total: items.length,
1007
+ closing,
941
1008
  hasPrev,
942
1009
  hasNext,
943
1010
  goPrev: () => navigate("prev"),
@@ -970,6 +1037,12 @@ function ImageViewer({
970
1037
  const reservedH = bottomBarH + IMG_PADDING * 2;
971
1038
  const imgMaxHeight = `calc(100vh - ${reservedH}px)`;
972
1039
  const imgStyle = { maxHeight: imgMaxHeight };
1040
+ const dim = (v) => typeof v === "number" ? `${v}px` : v;
1041
+ const rootStyle = {
1042
+ ...navHeight != null && { "--rvl-nav-height": dim(navHeight) },
1043
+ ...navInset != null && { "--rvl-nav-inset": dim(navInset) },
1044
+ ...counterFontSize != null && { "--rvl-counter-font-size": dim(counterFontSize) }
1045
+ };
973
1046
  if (gateEntry && !fullLoaded) imgStyle.opacity = 0;
974
1047
  const totalDigits = String(items.length).length;
975
1048
  const counterMinWidth = `${totalDigits * 2 * 0.6 + 1.5}em`;
@@ -979,7 +1052,7 @@ function ImageViewer({
979
1052
  const nextItem = nextIndex >= 0 ? items[nextIndex] : null;
980
1053
  const showAdjacent = slideActive || slideAnimating || swipeOffset !== 0;
981
1054
  const adjacentOpacity = Math.min(1, Math.abs(swipeOffset) / (viewportWidth * 0.8 || 1));
982
- const showZoomControls = zoom && !isTouchDevice;
1055
+ const showZoomCtrls = zoom && !isTouchDevice && showZoomControls && !contentShift.transform;
983
1056
  const headerActions = renderHeaderActions?.(ctx);
984
1057
  const navStart = renderNavStart?.(ctx);
985
1058
  const navEnd = renderNavEnd?.(ctx);
@@ -994,12 +1067,14 @@ function ImageViewer({
994
1067
  "aria-modal": "true",
995
1068
  "aria-label": ariaLabel ?? item.alt ?? "Image viewer",
996
1069
  tabIndex: -1,
1070
+ style: rootStyle,
997
1071
  children: [
998
1072
  /* @__PURE__ */ jsx(
999
1073
  "div",
1000
1074
  {
1001
1075
  className: cx("rvl-backdrop", cn("backdrop")),
1002
- onClick: closeOnBackdropClick ? handleClose : void 0,
1076
+ onClick: closeOnBackdropClick ? handleBackdropClick : void 0,
1077
+ onTouchEnd: closeOnBackdropClick ? handleBackdropTouchEnd : void 0,
1003
1078
  "aria-hidden": "true"
1004
1079
  }
1005
1080
  ),
@@ -1007,7 +1082,7 @@ function ImageViewer({
1007
1082
  /* @__PURE__ */ jsx("div", { className: cx("rvl-header", cn("topBar")), children: renderHeader?.(ctx) }),
1008
1083
  /* @__PURE__ */ jsxs("div", { className: "rvl-header-actions", children: [
1009
1084
  headerActions,
1010
- showZoomControls && isZoomed && /* @__PURE__ */ jsxs(
1085
+ showZoomCtrls && isZoomed && /* @__PURE__ */ jsxs(
1011
1086
  "button",
1012
1087
  {
1013
1088
  type: "button",
@@ -1024,7 +1099,7 @@ function ImageViewer({
1024
1099
  ]
1025
1100
  }
1026
1101
  ),
1027
- showZoomControls && /* @__PURE__ */ jsx(
1102
+ showZoomCtrls && /* @__PURE__ */ jsx(
1028
1103
  "button",
1029
1104
  {
1030
1105
  type: "button",
@@ -1038,7 +1113,7 @@ function ImageViewer({
1038
1113
  children: mergedIcons.zoomIn
1039
1114
  }
1040
1115
  ),
1041
- showZoomControls && /* @__PURE__ */ jsx(
1116
+ showZoomCtrls && /* @__PURE__ */ jsx(
1042
1117
  "button",
1043
1118
  {
1044
1119
  type: "button",
@@ -1072,9 +1147,8 @@ function ImageViewer({
1072
1147
  "div",
1073
1148
  {
1074
1149
  className: "rvl-stage",
1075
- onClick: closeOnBackdropClick ? (e) => {
1076
- if (e.target === e.currentTarget) handleClose();
1077
- } : void 0,
1150
+ onClick: closeOnBackdropClick ? handleStageClick : void 0,
1151
+ onTouchEnd: closeOnBackdropClick ? handleBackdropTouchEnd : void 0,
1078
1152
  style: {
1079
1153
  transform: contentShift.transform ?? "translateY(0)",
1080
1154
  // animate=false snaps with no transition (overrides the CSS transition)
@@ -1117,7 +1191,7 @@ function ImageViewer({
1117
1191
  ref: imgWrapperRef,
1118
1192
  className: "rvl-img-wrapper",
1119
1193
  onClick: (e) => e.stopPropagation(),
1120
- onDoubleClick: handleDoubleClick,
1194
+ onDoubleClick: handleDoubleClickGuarded,
1121
1195
  onPointerDown: gestures.handlePointerDown,
1122
1196
  onPointerMove: gestures.handlePointerMove,
1123
1197
  onPointerUp: gestures.handlePointerUp,
@@ -1164,7 +1238,7 @@ function ImageViewer({
1164
1238
  }
1165
1239
  ),
1166
1240
  /* @__PURE__ */ jsxs("div", { ref: bottomBarRef, className: cx("rvl-bar", "rvl-bottom-bar", cn("bottomBar")), children: [
1167
- showNavRow && /* @__PURE__ */ jsx("div", { className: "rvl-nav-row", children: /* @__PURE__ */ jsxs("div", { className: "rvl-nav-inner", children: [
1241
+ showNavRow && /* @__PURE__ */ jsx("div", { className: "rvl-nav-row", children: /* @__PURE__ */ jsxs("div", { className: cx("rvl-nav-inner", navSlotPlacement === "inline" && "rvl-nav-inline"), children: [
1168
1242
  navStart != null && /* @__PURE__ */ jsx("div", { className: cx("rvl-nav-start", cn("navStart")), children: navStart }),
1169
1243
  hasNavGroup && /* @__PURE__ */ jsxs("div", { className: "rvl-nav-group", children: [
1170
1244
  /* @__PURE__ */ jsx(