@ship-it-ui/ui 0.0.10 → 0.0.11

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
@@ -1218,7 +1218,8 @@ declare const Crumb: react.ForwardRefExoticComponent<CrumbProps & react.RefAttri
1218
1218
  * behavior; no library.
1219
1219
  *
1220
1220
  * Pass an array of `items` and a `renderItem` function — the carousel
1221
- * handles snapping, active-index tracking, and keyboard nav.
1221
+ * handles snapping, active-index tracking, and keyboard nav. Set
1222
+ * `loop` to make arrows / dots / native swipe wrap continuously.
1222
1223
  */
1223
1224
  interface CarouselProps<T = unknown> extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
1224
1225
  /** Slide data. */
@@ -1239,6 +1240,13 @@ interface CarouselProps<T = unknown> extends Omit<HTMLAttributes<HTMLDivElement>
1239
1240
  showDots?: boolean;
1240
1241
  /** When false, hides the prev/next arrows. Default `true`. */
1241
1242
  showArrows?: boolean;
1243
+ /**
1244
+ * Wrap arrows / dots / native swipe past the boundaries. Default `false`.
1245
+ * When `true`, hidden clones of the first / last slide bracket the real
1246
+ * items so swipe past the end jumps invisibly to the other side.
1247
+ * `onIndexChange` still only emits real indices in `0..items.length - 1`.
1248
+ */
1249
+ loop?: boolean;
1242
1250
  /** Accessible label for the carousel region. */
1243
1251
  'aria-label'?: string;
1244
1252
  }
@@ -1495,7 +1503,8 @@ declare const DateRangePicker: react.ForwardRefExoticComponent<DateRangePickerPr
1495
1503
  /**
1496
1504
  * Lightbox — fullscreen photo viewer. Built on Radix Dialog (reuses the
1497
1505
  * focus trap, portal, and escape-to-close). Adds keyboard ←/→ navigation
1498
- * between items and a counter overlay.
1506
+ * between items and a counter overlay. Set `loop` to wrap navigation
1507
+ * past the boundaries.
1499
1508
  */
1500
1509
  interface LightboxProps {
1501
1510
  open?: boolean;
@@ -1511,6 +1520,13 @@ interface LightboxProps {
1511
1520
  defaultIndex?: number;
1512
1521
  /** Fires when the index changes. */
1513
1522
  onIndexChange?: (index: number) => void;
1523
+ /**
1524
+ * Wrap prev / next (buttons and ←/→ keys) past the boundaries. Default
1525
+ * `false`. When `true`, "next" on the last item goes to the first and
1526
+ * vice versa, and the arrow buttons never disable while there's more
1527
+ * than one item.
1528
+ */
1529
+ loop?: boolean;
1514
1530
  /** Accessible title (visually hidden). */
1515
1531
  title?: ReactNode;
1516
1532
  }
@@ -1561,6 +1577,12 @@ interface ListingCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
1561
1577
  * placeholders that follow `currentColor`) or non-image slides.
1562
1578
  */
1563
1579
  renderPhoto?: (src: string, index: number) => ReactNode;
1580
+ /**
1581
+ * Wrap the photo carousel past the boundaries (next from the last
1582
+ * photo goes to the first). Default `true` — marketplace photo
1583
+ * browsing expects looping. Pass `false` to restore stop-at-end.
1584
+ */
1585
+ loop?: boolean;
1564
1586
  /** Listing title — e.g. "2023 Tesla Model 3". */
1565
1587
  title: ReactNode;
1566
1588
  /** Optional eyebrow text above the title (location, vehicle type). */
@@ -1715,6 +1737,13 @@ interface ListingDetailProps {
1715
1737
  * placeholders or non-image slides.
1716
1738
  */
1717
1739
  renderPhoto?: (src: string, index: number, mode: 'gallery' | 'lightbox') => ReactNode;
1740
+ /**
1741
+ * Wrap the gallery carousel and the fullscreen lightbox past the
1742
+ * boundaries (next from the last photo goes to the first). Default
1743
+ * `true` — marketplace photo browsing expects looping. One prop
1744
+ * drives both surfaces.
1745
+ */
1746
+ loop?: boolean;
1718
1747
  /** Listing title — e.g. "2023 Tesla Model 3". */
1719
1748
  title: ReactNode;
1720
1749
  /** Optional eyebrow above the title — vehicle type, location. */
package/dist/index.d.ts CHANGED
@@ -1218,7 +1218,8 @@ declare const Crumb: react.ForwardRefExoticComponent<CrumbProps & react.RefAttri
1218
1218
  * behavior; no library.
1219
1219
  *
1220
1220
  * Pass an array of `items` and a `renderItem` function — the carousel
1221
- * handles snapping, active-index tracking, and keyboard nav.
1221
+ * handles snapping, active-index tracking, and keyboard nav. Set
1222
+ * `loop` to make arrows / dots / native swipe wrap continuously.
1222
1223
  */
1223
1224
  interface CarouselProps<T = unknown> extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
1224
1225
  /** Slide data. */
@@ -1239,6 +1240,13 @@ interface CarouselProps<T = unknown> extends Omit<HTMLAttributes<HTMLDivElement>
1239
1240
  showDots?: boolean;
1240
1241
  /** When false, hides the prev/next arrows. Default `true`. */
1241
1242
  showArrows?: boolean;
1243
+ /**
1244
+ * Wrap arrows / dots / native swipe past the boundaries. Default `false`.
1245
+ * When `true`, hidden clones of the first / last slide bracket the real
1246
+ * items so swipe past the end jumps invisibly to the other side.
1247
+ * `onIndexChange` still only emits real indices in `0..items.length - 1`.
1248
+ */
1249
+ loop?: boolean;
1242
1250
  /** Accessible label for the carousel region. */
1243
1251
  'aria-label'?: string;
1244
1252
  }
@@ -1495,7 +1503,8 @@ declare const DateRangePicker: react.ForwardRefExoticComponent<DateRangePickerPr
1495
1503
  /**
1496
1504
  * Lightbox — fullscreen photo viewer. Built on Radix Dialog (reuses the
1497
1505
  * focus trap, portal, and escape-to-close). Adds keyboard ←/→ navigation
1498
- * between items and a counter overlay.
1506
+ * between items and a counter overlay. Set `loop` to wrap navigation
1507
+ * past the boundaries.
1499
1508
  */
1500
1509
  interface LightboxProps {
1501
1510
  open?: boolean;
@@ -1511,6 +1520,13 @@ interface LightboxProps {
1511
1520
  defaultIndex?: number;
1512
1521
  /** Fires when the index changes. */
1513
1522
  onIndexChange?: (index: number) => void;
1523
+ /**
1524
+ * Wrap prev / next (buttons and ←/→ keys) past the boundaries. Default
1525
+ * `false`. When `true`, "next" on the last item goes to the first and
1526
+ * vice versa, and the arrow buttons never disable while there's more
1527
+ * than one item.
1528
+ */
1529
+ loop?: boolean;
1514
1530
  /** Accessible title (visually hidden). */
1515
1531
  title?: ReactNode;
1516
1532
  }
@@ -1561,6 +1577,12 @@ interface ListingCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
1561
1577
  * placeholders that follow `currentColor`) or non-image slides.
1562
1578
  */
1563
1579
  renderPhoto?: (src: string, index: number) => ReactNode;
1580
+ /**
1581
+ * Wrap the photo carousel past the boundaries (next from the last
1582
+ * photo goes to the first). Default `true` — marketplace photo
1583
+ * browsing expects looping. Pass `false` to restore stop-at-end.
1584
+ */
1585
+ loop?: boolean;
1564
1586
  /** Listing title — e.g. "2023 Tesla Model 3". */
1565
1587
  title: ReactNode;
1566
1588
  /** Optional eyebrow text above the title (location, vehicle type). */
@@ -1715,6 +1737,13 @@ interface ListingDetailProps {
1715
1737
  * placeholders or non-image slides.
1716
1738
  */
1717
1739
  renderPhoto?: (src: string, index: number, mode: 'gallery' | 'lightbox') => ReactNode;
1740
+ /**
1741
+ * Wrap the gallery carousel and the fullscreen lightbox past the
1742
+ * boundaries (next from the last photo goes to the first). Default
1743
+ * `true` — marketplace photo browsing expects looping. One prop
1744
+ * drives both surfaces.
1745
+ */
1746
+ loop?: boolean;
1718
1747
  /** Listing title — e.g. "2023 Tesla Model 3". */
1719
1748
  title: ReactNode;
1720
1749
  /** Optional eyebrow above the title — vehicle type, location. */
package/dist/index.js CHANGED
@@ -3080,6 +3080,7 @@ import {
3080
3080
  forwardRef as forwardRef44,
3081
3081
  useCallback as useCallback9,
3082
3082
  useEffect as useEffect8,
3083
+ useLayoutEffect as useLayoutEffect2,
3083
3084
  useRef as useRef8
3084
3085
  } from "react";
3085
3086
  import { Fragment, jsx as jsx45, jsxs as jsxs38 } from "react/jsx-runtime";
@@ -3093,27 +3094,36 @@ var Carousel = forwardRef44(function Carousel2({
3093
3094
  aspectRatio = 16 / 10,
3094
3095
  showDots = true,
3095
3096
  showArrows = true,
3097
+ loop = false,
3096
3098
  className,
3097
3099
  "aria-label": ariaLabel = "Carousel",
3098
3100
  ...props
3099
3101
  }, ref) {
3102
+ const N = items.length;
3103
+ const isLooping = loop && N > 1;
3100
3104
  const [active, setActive] = useControllableState({
3101
3105
  value: indexProp,
3102
3106
  defaultValue: defaultIndex ?? 0,
3103
3107
  onChange: onIndexChange
3104
3108
  });
3105
3109
  const viewportRef = useRef8(null);
3110
+ const internalScrollRef = useRef8(false);
3111
+ const activeIdx = active ?? 0;
3112
+ const domIndexFor = useCallback9((real) => isLooping ? real + 1 : real, [isLooping]);
3106
3113
  const goTo = useCallback9(
3107
3114
  (i) => {
3108
- const clamped = Math.max(0, Math.min(items.length - 1, i));
3109
- setActive(clamped);
3115
+ const next = isLooping ? (i % N + N) % N : Math.max(0, Math.min(N - 1, i));
3116
+ setActive(next);
3110
3117
  const node = viewportRef.current;
3111
3118
  if (node) {
3112
- const slide = node.children[clamped];
3113
- slide?.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "start" });
3119
+ const slide = node.children[domIndexFor(next)];
3120
+ if (slide) {
3121
+ internalScrollRef.current = true;
3122
+ slide.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "start" });
3123
+ }
3114
3124
  }
3115
3125
  },
3116
- [items.length, setActive]
3126
+ [N, isLooping, domIndexFor, setActive]
3117
3127
  );
3118
3128
  useEffect8(() => {
3119
3129
  const node = viewportRef.current;
@@ -3121,13 +3131,59 @@ var Carousel = forwardRef44(function Carousel2({
3121
3131
  const onScroll = () => {
3122
3132
  const width = node.clientWidth;
3123
3133
  if (width === 0) return;
3124
- const i = Math.round(node.scrollLeft / width);
3125
- if (i !== active) setActive(i);
3134
+ const domIdx = Math.round(node.scrollLeft / width);
3135
+ if (!isLooping) {
3136
+ if (domIdx !== activeIdx) setActive(domIdx);
3137
+ return;
3138
+ }
3139
+ if (domIdx === 0) {
3140
+ const realTwin = node.children[N];
3141
+ if (realTwin) {
3142
+ internalScrollRef.current = true;
3143
+ realTwin.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
3144
+ }
3145
+ if (activeIdx !== N - 1) setActive(N - 1);
3146
+ return;
3147
+ }
3148
+ if (domIdx === N + 1) {
3149
+ const realTwin = node.children[1];
3150
+ if (realTwin) {
3151
+ internalScrollRef.current = true;
3152
+ realTwin.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
3153
+ }
3154
+ if (activeIdx !== 0) setActive(0);
3155
+ return;
3156
+ }
3157
+ const realIdx = domIdx - 1;
3158
+ if (realIdx !== activeIdx) setActive(realIdx);
3126
3159
  };
3127
3160
  node.addEventListener("scroll", onScroll, { passive: true });
3128
3161
  return () => node.removeEventListener("scroll", onScroll);
3129
- }, [active, setActive]);
3130
- const activeIdx = active ?? 0;
3162
+ }, [activeIdx, isLooping, N, setActive]);
3163
+ useEffect8(() => {
3164
+ if (internalScrollRef.current) {
3165
+ internalScrollRef.current = false;
3166
+ return;
3167
+ }
3168
+ const node = viewportRef.current;
3169
+ if (!node) return;
3170
+ const width = node.clientWidth;
3171
+ if (width === 0) return;
3172
+ const targetDom = domIndexFor(activeIdx);
3173
+ const currentDom = Math.round(node.scrollLeft / width);
3174
+ if (currentDom === targetDom) return;
3175
+ const slide = node.children[targetDom];
3176
+ slide?.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
3177
+ }, [activeIdx, domIndexFor]);
3178
+ useLayoutEffect2(() => {
3179
+ if (!isLooping) return;
3180
+ const node = viewportRef.current;
3181
+ if (!node) return;
3182
+ const slide = node.children[domIndexFor(activeIdx)];
3183
+ if (!slide) return;
3184
+ internalScrollRef.current = true;
3185
+ slide.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
3186
+ }, [isLooping]);
3131
3187
  return /* @__PURE__ */ jsxs38(
3132
3188
  "div",
3133
3189
  {
@@ -3139,34 +3195,58 @@ var Carousel = forwardRef44(function Carousel2({
3139
3195
  ...props,
3140
3196
  children: [
3141
3197
  /* @__PURE__ */ jsxs38("div", { className: "relative overflow-hidden rounded-md", children: [
3142
- /* @__PURE__ */ jsx45(
3198
+ /* @__PURE__ */ jsxs38(
3143
3199
  "div",
3144
3200
  {
3145
3201
  ref: viewportRef,
3146
3202
  className: "flex w-full snap-x snap-mandatory overflow-x-auto scroll-smooth [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
3147
3203
  "aria-live": "polite",
3148
- children: items.map((item, i) => /* @__PURE__ */ jsx45(
3149
- "div",
3150
- {
3151
- className: "w-full shrink-0 snap-start",
3152
- style: { aspectRatio: String(aspectRatio) },
3153
- role: "group",
3154
- "aria-roledescription": "slide",
3155
- "aria-label": `${i + 1} of ${items.length}`,
3156
- children: renderItem(item, i)
3157
- },
3158
- i
3159
- ))
3204
+ children: [
3205
+ isLooping && /* @__PURE__ */ jsx45(
3206
+ "div",
3207
+ {
3208
+ "aria-hidden": "true",
3209
+ tabIndex: -1,
3210
+ className: "w-full shrink-0 snap-start",
3211
+ style: { aspectRatio: String(aspectRatio) },
3212
+ children: renderItem(items[N - 1], N - 1)
3213
+ },
3214
+ "clone-start"
3215
+ ),
3216
+ items.map((item, i) => /* @__PURE__ */ jsx45(
3217
+ "div",
3218
+ {
3219
+ className: "w-full shrink-0 snap-start",
3220
+ style: { aspectRatio: String(aspectRatio) },
3221
+ role: "group",
3222
+ "aria-roledescription": "slide",
3223
+ "aria-label": `${i + 1} of ${N}`,
3224
+ children: renderItem(item, i)
3225
+ },
3226
+ i
3227
+ )),
3228
+ isLooping && /* @__PURE__ */ jsx45(
3229
+ "div",
3230
+ {
3231
+ "aria-hidden": "true",
3232
+ tabIndex: -1,
3233
+ className: "w-full shrink-0 snap-start",
3234
+ style: { aspectRatio: String(aspectRatio) },
3235
+ children: renderItem(items[0], 0)
3236
+ },
3237
+ "clone-end"
3238
+ )
3239
+ ]
3160
3240
  }
3161
3241
  ),
3162
- showArrows && items.length > 1 && /* @__PURE__ */ jsxs38(Fragment, { children: [
3242
+ showArrows && N > 1 && /* @__PURE__ */ jsxs38(Fragment, { children: [
3163
3243
  /* @__PURE__ */ jsx45(
3164
3244
  "button",
3165
3245
  {
3166
3246
  type: "button",
3167
3247
  "aria-label": "Previous slide",
3168
3248
  onClick: () => goTo(activeIdx - 1),
3169
- disabled: activeIdx === 0,
3249
+ disabled: !isLooping && activeIdx === 0,
3170
3250
  className: "bg-panel/85 border-border text-text hover:bg-panel absolute top-1/2 left-2 inline-grid h-9 w-9 -translate-y-1/2 cursor-pointer place-items-center rounded-full border shadow-md backdrop-blur disabled:cursor-not-allowed disabled:opacity-40",
3171
3251
  children: /* @__PURE__ */ jsx45(IconGlyph4, { name: "caretLeft", size: 16 })
3172
3252
  }
@@ -3177,13 +3257,13 @@ var Carousel = forwardRef44(function Carousel2({
3177
3257
  type: "button",
3178
3258
  "aria-label": "Next slide",
3179
3259
  onClick: () => goTo(activeIdx + 1),
3180
- disabled: activeIdx === items.length - 1,
3260
+ disabled: !isLooping && activeIdx === N - 1,
3181
3261
  className: "bg-panel/85 border-border text-text hover:bg-panel absolute top-1/2 right-2 inline-grid h-9 w-9 -translate-y-1/2 cursor-pointer place-items-center rounded-full border shadow-md backdrop-blur disabled:cursor-not-allowed disabled:opacity-40",
3182
3262
  children: /* @__PURE__ */ jsx45(IconGlyph4, { name: "caretRight", size: 16 })
3183
3263
  }
3184
3264
  )
3185
3265
  ] }),
3186
- showDots && items.length > 1 && /*
3266
+ showDots && N > 1 && /*
3187
3267
  * Plain `<button>` + `aria-current` rather than the tabs pattern
3188
3268
  * (`role="tablist" / "tab"`). The APG carousel pattern recommends
3189
3269
  * this lighter semantic; the viewport's `aria-live="polite"`
@@ -4256,19 +4336,28 @@ var Lightbox = forwardRef50(function Lightbox2({
4256
4336
  index,
4257
4337
  defaultIndex,
4258
4338
  onIndexChange,
4339
+ loop = false,
4259
4340
  title = "Photo viewer"
4260
4341
  }, ref) {
4342
+ const N = items.length;
4343
+ const isLooping = loop && N > 1;
4261
4344
  const [active, setActive] = useControllableState({
4262
4345
  value: index,
4263
4346
  defaultValue: defaultIndex ?? 0,
4264
4347
  onChange: onIndexChange
4265
4348
  });
4266
4349
  const goPrev = useCallback11(() => {
4267
- setActive((prev) => Math.max(0, (prev ?? 0) - 1));
4268
- }, [setActive]);
4350
+ setActive((prev) => {
4351
+ const p = prev ?? 0;
4352
+ return isLooping ? (p - 1 + N) % N : Math.max(0, p - 1);
4353
+ });
4354
+ }, [setActive, isLooping, N]);
4269
4355
  const goNext = useCallback11(() => {
4270
- setActive((prev) => Math.min(items.length - 1, (prev ?? 0) + 1));
4271
- }, [items.length, setActive]);
4356
+ setActive((prev) => {
4357
+ const p = prev ?? 0;
4358
+ return isLooping ? (p + 1) % N : Math.min(N - 1, p + 1);
4359
+ });
4360
+ }, [setActive, isLooping, N]);
4272
4361
  const onKey = useCallback11(
4273
4362
  (e) => {
4274
4363
  if (e.key === "ArrowLeft") {
@@ -4308,7 +4397,7 @@ var Lightbox = forwardRef50(function Lightbox2({
4308
4397
  type: "button",
4309
4398
  "aria-label": "Previous photo",
4310
4399
  onClick: goPrev,
4311
- disabled: activeIdx === 0,
4400
+ disabled: !isLooping && activeIdx === 0,
4312
4401
  className: "absolute top-1/2 left-4 inline-grid h-11 w-11 -translate-y-1/2 cursor-pointer place-items-center rounded-full bg-white/10 text-white hover:bg-white/20 disabled:cursor-not-allowed disabled:opacity-40",
4313
4402
  children: /* @__PURE__ */ jsx52(IconGlyph6, { name: "caretLeft", size: 20 })
4314
4403
  }
@@ -4319,7 +4408,7 @@ var Lightbox = forwardRef50(function Lightbox2({
4319
4408
  type: "button",
4320
4409
  "aria-label": "Next photo",
4321
4410
  onClick: goNext,
4322
- disabled: activeIdx === items.length - 1,
4411
+ disabled: !isLooping && activeIdx === N - 1,
4323
4412
  className: "absolute top-1/2 right-4 inline-grid h-11 w-11 -translate-y-1/2 cursor-pointer place-items-center rounded-full bg-white/10 text-white hover:bg-white/20 disabled:cursor-not-allowed disabled:opacity-40",
4324
4413
  children: /* @__PURE__ */ jsx52(IconGlyph6, { name: "caretRight", size: 20 })
4325
4414
  }
@@ -4372,6 +4461,7 @@ var ListingCard = forwardRef51(function ListingCard2({
4372
4461
  variant = "default",
4373
4462
  photos,
4374
4463
  renderPhoto,
4464
+ loop = true,
4375
4465
  onClick,
4376
4466
  hoverEffect,
4377
4467
  title,
@@ -4418,6 +4508,7 @@ var ListingCard = forwardRef51(function ListingCard2({
4418
4508
  Carousel,
4419
4509
  {
4420
4510
  items: photos,
4511
+ loop,
4421
4512
  ...isSpec ? {
4422
4513
  index: photoIndex,
4423
4514
  onIndexChange: setPhotoIndex,
@@ -4658,6 +4749,7 @@ var ListingDetail = forwardRef52(function ListingDetail2({
4658
4749
  onOpenChange,
4659
4750
  photos,
4660
4751
  renderPhoto,
4752
+ loop = true,
4661
4753
  title,
4662
4754
  eyebrow,
4663
4755
  description,
@@ -4727,6 +4819,7 @@ var ListingDetail = forwardRef52(function ListingDetail2({
4727
4819
  items: photos,
4728
4820
  index: galleryIndex,
4729
4821
  onIndexChange: setGalleryIndex,
4822
+ loop,
4730
4823
  ...isSpec ? { showDots: false } : {},
4731
4824
  "aria-label": typeof title === "string" ? `${title} photos` : "Listing photos",
4732
4825
  renderItem: (src, i) => renderPhoto ? renderPhoto(src, i, "gallery") : /* @__PURE__ */ jsx54(
@@ -4972,6 +5065,7 @@ var ListingDetail = forwardRef52(function ListingDetail2({
4972
5065
  items: photos,
4973
5066
  index: galleryIndex,
4974
5067
  onIndexChange: setGalleryIndex,
5068
+ loop,
4975
5069
  title: lightboxTitle,
4976
5070
  renderItem: (src, i) => renderPhoto ? renderPhoto(src, i, "lightbox") : /* @__PURE__ */ jsx54("img", { src, alt: "", className: "max-h-[88vh] max-w-[92vw] object-contain" })
4977
5071
  }
@@ -7241,7 +7335,6 @@ Topbar.displayName = "Topbar";
7241
7335
  import {
7242
7336
  forwardRef as forwardRef79,
7243
7337
  useCallback as useCallback16,
7244
- useEffect as useEffect16,
7245
7338
  useMemo as useMemo7,
7246
7339
  useRef as useRef14,
7247
7340
  useState as useState21
@@ -7287,11 +7380,9 @@ var Tree = forwardRef79(function Tree2({
7287
7380
  return out;
7288
7381
  }, [items, expandedSet]);
7289
7382
  const [activeId, setActiveId] = useState21(null);
7290
- useEffect16(() => {
7291
- if (activeId && !flatVisible.some((f) => f.id === activeId)) {
7292
- setActiveId(null);
7293
- }
7294
- }, [activeId, flatVisible]);
7383
+ if (activeId && !flatVisible.some((f) => f.id === activeId)) {
7384
+ setActiveId(null);
7385
+ }
7295
7386
  const tabStopId = useMemo7(() => {
7296
7387
  if (activeId && flatVisible.some((f) => f.id === activeId)) return activeId;
7297
7388
  if (value && flatVisible.some((f) => f.id === value)) return value;