@ship-it-ui/ui 0.0.12 → 0.0.14

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
@@ -5,7 +5,6 @@ import { useEffect, KeyboardEvent, RefObject, ButtonHTMLAttributes, ReactNode, H
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
6
6
  import { VariantProps } from 'class-variance-authority';
7
7
  import * as RadixCheckbox from '@radix-ui/react-checkbox';
8
- import * as react_jsx_runtime from 'react/jsx-runtime';
9
8
  import * as RadixRadio from '@radix-ui/react-radio-group';
10
9
  import * as RadixSelect from '@radix-ui/react-select';
11
10
  import * as RadixSlider from '@radix-ui/react-slider';
@@ -291,7 +290,7 @@ interface FieldProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
291
290
  * {(p) => <Input type="email" placeholder="me@org.com" {...p} />}
292
291
  * </Field>
293
292
  */
294
- declare function Field({ label, hint, error, required, className, children, ...props }: FieldProps): react_jsx_runtime.JSX.Element;
293
+ declare function Field({ label, hint, error, required, className, children, ...props }: FieldProps): react.JSX.Element;
295
294
 
296
295
  /**
297
296
  * Display-to-input rename primitive. Renders `value` as a static element until
@@ -504,7 +503,7 @@ interface SelectProps extends Omit<RadixSelect.SelectProps, 'children'> {
504
503
  * One-line Select. For composition (groups, separators), use the lower-level
505
504
  * `SelectRoot/Trigger/Content/Item` exports directly.
506
505
  */
507
- declare function Select({ options, placeholder, size, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...rootProps }: SelectProps): react_jsx_runtime.JSX.Element;
506
+ declare function Select({ options, placeholder, size, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...rootProps }: SelectProps): react.JSX.Element;
508
507
 
509
508
  interface SliderProps extends Omit<RadixSlider.SliderProps, 'asChild' | 'value' | 'defaultValue' | 'onValueChange'> {
510
509
  /** Show the numeric value to the right of the track. */
@@ -944,7 +943,7 @@ interface DialogProps extends RadixDialog.DialogProps {
944
943
  /** When set, content is wrapped in a content frame; omit for full custom layout. */
945
944
  children?: ReactNode;
946
945
  }
947
- declare function Dialog({ title, description, footer, width, children, ...rootProps }: DialogProps): react_jsx_runtime.JSX.Element;
946
+ declare function Dialog({ title, description, footer, width, children, ...rootProps }: DialogProps): react.JSX.Element;
948
947
 
949
948
  type DrawerSide = 'left' | 'right' | 'bottom';
950
949
  interface DrawerProps extends RadixDialog.DialogProps {
@@ -1042,7 +1041,7 @@ interface HoverCardProps extends RadixHoverCard.HoverCardProps {
1042
1041
  content: ReactNode;
1043
1042
  }
1044
1043
  /** Convenience wrapper — pass `trigger` and `content` as props. */
1045
- declare function HoverCard({ trigger, content, ...rootProps }: HoverCardProps): react_jsx_runtime.JSX.Element;
1044
+ declare function HoverCard({ trigger, content, ...rootProps }: HoverCardProps): react.JSX.Element;
1046
1045
 
1047
1046
  declare const PopoverRoot: react.FC<RadixPopover.PopoverProps>;
1048
1047
  declare const PopoverTrigger: react.ForwardRefExoticComponent<RadixPopover.PopoverTriggerProps & react.RefAttributes<HTMLButtonElement>>;
@@ -1086,7 +1085,7 @@ interface ToastContextValue {
1086
1085
  */
1087
1086
  declare function ToastProvider({ children }: {
1088
1087
  children: ReactNode;
1089
- }): react_jsx_runtime.JSX.Element;
1088
+ }): react.JSX.Element;
1090
1089
  declare function useToast(): ToastContextValue;
1091
1090
  interface ToastCardProps {
1092
1091
  toast: ToastEntry;
@@ -1132,7 +1131,7 @@ interface SimpleTooltipProps {
1132
1131
  * (multiple triggers in a list, shared delay config), use the lower-level
1133
1132
  * `Tooltip` + `TooltipTrigger` + `TooltipContent` primitives.
1134
1133
  */
1135
- declare function SimpleTooltip({ content, children, side, delayDuration, }: SimpleTooltipProps): react_jsx_runtime.JSX.Element;
1134
+ declare function SimpleTooltip({ content, children, side, delayDuration, }: SimpleTooltipProps): react.JSX.Element;
1136
1135
 
1137
1136
  /**
1138
1137
  * Alert — inline messaging block. Four tones (accent / ok / warn / err) with a
@@ -1423,7 +1422,7 @@ interface DataTableProps<T> {
1423
1422
  }
1424
1423
  declare function DataTable<T>(props: DataTableProps<T> & {
1425
1424
  ref?: Ref<HTMLTableElement>;
1426
- }): react_jsx_runtime.JSX.Element;
1425
+ }): react.JSX.Element;
1427
1426
 
1428
1427
  /** A `{from, to}` date range used by `Calendar` and `DateRangePicker`. */
1429
1428
  interface DateRange {
@@ -1590,9 +1589,10 @@ interface ListingCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
1590
1589
  /**
1591
1590
  * Wrap the photo carousel past the boundaries (next from the last
1592
1591
  * photo goes to the first). Default `true` — marketplace photo
1593
- * browsing expects looping. Pass `false` to restore stop-at-end.
1592
+ * browsing expects looping. Pass `false` to restore stop-at-end, or
1593
+ * `'circular'` / `'sweep'` to pick the loop variant explicitly.
1594
1594
  */
1595
- loop?: boolean;
1595
+ loop?: boolean | 'circular' | 'sweep';
1596
1596
  /** Listing title — e.g. "Sun-soaked cabin in Marin". */
1597
1597
  title: ReactNode;
1598
1598
  /** Optional eyebrow text above the title (location, listing type). */
@@ -1751,9 +1751,11 @@ interface ListingDetailProps {
1751
1751
  * Wrap the gallery carousel and the fullscreen lightbox past the
1752
1752
  * boundaries (next from the last photo goes to the first). Default
1753
1753
  * `true` — marketplace photo browsing expects looping. One prop
1754
- * drives both surfaces.
1754
+ * drives both surfaces. Pass `'circular'` or `'sweep'` to pick the
1755
+ * gallery's loop variant explicitly; both forward as truthy to the
1756
+ * lightbox.
1755
1757
  */
1756
- loop?: boolean;
1758
+ loop?: boolean | 'circular' | 'sweep';
1757
1759
  /** Listing title — e.g. "Sun-soaked cabin in Marin". */
1758
1760
  title: ReactNode;
1759
1761
  /** Optional eyebrow above the title — listing type, location. */
package/dist/index.d.ts CHANGED
@@ -5,7 +5,6 @@ import { useEffect, KeyboardEvent, RefObject, ButtonHTMLAttributes, ReactNode, H
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
6
6
  import { VariantProps } from 'class-variance-authority';
7
7
  import * as RadixCheckbox from '@radix-ui/react-checkbox';
8
- import * as react_jsx_runtime from 'react/jsx-runtime';
9
8
  import * as RadixRadio from '@radix-ui/react-radio-group';
10
9
  import * as RadixSelect from '@radix-ui/react-select';
11
10
  import * as RadixSlider from '@radix-ui/react-slider';
@@ -291,7 +290,7 @@ interface FieldProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
291
290
  * {(p) => <Input type="email" placeholder="me@org.com" {...p} />}
292
291
  * </Field>
293
292
  */
294
- declare function Field({ label, hint, error, required, className, children, ...props }: FieldProps): react_jsx_runtime.JSX.Element;
293
+ declare function Field({ label, hint, error, required, className, children, ...props }: FieldProps): react.JSX.Element;
295
294
 
296
295
  /**
297
296
  * Display-to-input rename primitive. Renders `value` as a static element until
@@ -504,7 +503,7 @@ interface SelectProps extends Omit<RadixSelect.SelectProps, 'children'> {
504
503
  * One-line Select. For composition (groups, separators), use the lower-level
505
504
  * `SelectRoot/Trigger/Content/Item` exports directly.
506
505
  */
507
- declare function Select({ options, placeholder, size, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...rootProps }: SelectProps): react_jsx_runtime.JSX.Element;
506
+ declare function Select({ options, placeholder, size, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...rootProps }: SelectProps): react.JSX.Element;
508
507
 
509
508
  interface SliderProps extends Omit<RadixSlider.SliderProps, 'asChild' | 'value' | 'defaultValue' | 'onValueChange'> {
510
509
  /** Show the numeric value to the right of the track. */
@@ -944,7 +943,7 @@ interface DialogProps extends RadixDialog.DialogProps {
944
943
  /** When set, content is wrapped in a content frame; omit for full custom layout. */
945
944
  children?: ReactNode;
946
945
  }
947
- declare function Dialog({ title, description, footer, width, children, ...rootProps }: DialogProps): react_jsx_runtime.JSX.Element;
946
+ declare function Dialog({ title, description, footer, width, children, ...rootProps }: DialogProps): react.JSX.Element;
948
947
 
949
948
  type DrawerSide = 'left' | 'right' | 'bottom';
950
949
  interface DrawerProps extends RadixDialog.DialogProps {
@@ -1042,7 +1041,7 @@ interface HoverCardProps extends RadixHoverCard.HoverCardProps {
1042
1041
  content: ReactNode;
1043
1042
  }
1044
1043
  /** Convenience wrapper — pass `trigger` and `content` as props. */
1045
- declare function HoverCard({ trigger, content, ...rootProps }: HoverCardProps): react_jsx_runtime.JSX.Element;
1044
+ declare function HoverCard({ trigger, content, ...rootProps }: HoverCardProps): react.JSX.Element;
1046
1045
 
1047
1046
  declare const PopoverRoot: react.FC<RadixPopover.PopoverProps>;
1048
1047
  declare const PopoverTrigger: react.ForwardRefExoticComponent<RadixPopover.PopoverTriggerProps & react.RefAttributes<HTMLButtonElement>>;
@@ -1086,7 +1085,7 @@ interface ToastContextValue {
1086
1085
  */
1087
1086
  declare function ToastProvider({ children }: {
1088
1087
  children: ReactNode;
1089
- }): react_jsx_runtime.JSX.Element;
1088
+ }): react.JSX.Element;
1090
1089
  declare function useToast(): ToastContextValue;
1091
1090
  interface ToastCardProps {
1092
1091
  toast: ToastEntry;
@@ -1132,7 +1131,7 @@ interface SimpleTooltipProps {
1132
1131
  * (multiple triggers in a list, shared delay config), use the lower-level
1133
1132
  * `Tooltip` + `TooltipTrigger` + `TooltipContent` primitives.
1134
1133
  */
1135
- declare function SimpleTooltip({ content, children, side, delayDuration, }: SimpleTooltipProps): react_jsx_runtime.JSX.Element;
1134
+ declare function SimpleTooltip({ content, children, side, delayDuration, }: SimpleTooltipProps): react.JSX.Element;
1136
1135
 
1137
1136
  /**
1138
1137
  * Alert — inline messaging block. Four tones (accent / ok / warn / err) with a
@@ -1423,7 +1422,7 @@ interface DataTableProps<T> {
1423
1422
  }
1424
1423
  declare function DataTable<T>(props: DataTableProps<T> & {
1425
1424
  ref?: Ref<HTMLTableElement>;
1426
- }): react_jsx_runtime.JSX.Element;
1425
+ }): react.JSX.Element;
1427
1426
 
1428
1427
  /** A `{from, to}` date range used by `Calendar` and `DateRangePicker`. */
1429
1428
  interface DateRange {
@@ -1590,9 +1589,10 @@ interface ListingCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
1590
1589
  /**
1591
1590
  * Wrap the photo carousel past the boundaries (next from the last
1592
1591
  * photo goes to the first). Default `true` — marketplace photo
1593
- * browsing expects looping. Pass `false` to restore stop-at-end.
1592
+ * browsing expects looping. Pass `false` to restore stop-at-end, or
1593
+ * `'circular'` / `'sweep'` to pick the loop variant explicitly.
1594
1594
  */
1595
- loop?: boolean;
1595
+ loop?: boolean | 'circular' | 'sweep';
1596
1596
  /** Listing title — e.g. "Sun-soaked cabin in Marin". */
1597
1597
  title: ReactNode;
1598
1598
  /** Optional eyebrow text above the title (location, listing type). */
@@ -1751,9 +1751,11 @@ interface ListingDetailProps {
1751
1751
  * Wrap the gallery carousel and the fullscreen lightbox past the
1752
1752
  * boundaries (next from the last photo goes to the first). Default
1753
1753
  * `true` — marketplace photo browsing expects looping. One prop
1754
- * drives both surfaces.
1754
+ * drives both surfaces. Pass `'circular'` or `'sweep'` to pick the
1755
+ * gallery's loop variant explicitly; both forward as truthy to the
1756
+ * lightbox.
1755
1757
  */
1756
- loop?: boolean;
1758
+ loop?: boolean | 'circular' | 'sweep';
1757
1759
  /** Listing title — e.g. "Sun-soaked cabin in Marin". */
1758
1760
  title: ReactNode;
1759
1761
  /** Optional eyebrow above the title — listing type, location. */
package/dist/index.js CHANGED
@@ -3109,7 +3109,9 @@ var Carousel = forwardRef44(function Carousel2({
3109
3109
  });
3110
3110
  const viewportRef = useRef8(null);
3111
3111
  const internalScrollRef = useRef8(false);
3112
- const wrapInProgressRef = useRef8(false);
3112
+ const goToInProgressRef = useRef8(false);
3113
+ const wrapInFlightRef = useRef8(null);
3114
+ const rebaseConsumeRef = useRef8(null);
3113
3115
  const activeIdx = active ?? 0;
3114
3116
  const domIndexFor = useCallback9((real) => isLooping ? real + 1 : real, [isLooping]);
3115
3117
  const goTo = useCallback9(
@@ -3118,13 +3120,32 @@ var Carousel = forwardRef44(function Carousel2({
3118
3120
  setActive(next);
3119
3121
  const node = viewportRef.current;
3120
3122
  if (node) {
3123
+ const width = node.clientWidth;
3124
+ if (isLooping && wrapInFlightRef.current !== null && width > 0) {
3125
+ const rebaseTarget = wrapInFlightRef.current === N + 1 ? 0 : wrapInFlightRef.current === 0 ? N + 1 : null;
3126
+ if (rebaseTarget !== null) {
3127
+ const rebaseSlide = node.children[rebaseTarget];
3128
+ if (rebaseSlide) {
3129
+ internalScrollRef.current = true;
3130
+ rebaseConsumeRef.current = rebaseTarget;
3131
+ rebaseSlide.scrollIntoView({
3132
+ behavior: "instant",
3133
+ block: "nearest",
3134
+ inline: "start"
3135
+ });
3136
+ }
3137
+ }
3138
+ wrapInFlightRef.current = null;
3139
+ }
3121
3140
  const isNextWrap = loopMode === "circular" && activeIdx === N - 1 && i === activeIdx + 1;
3122
3141
  const isPrevWrap = loopMode === "circular" && activeIdx === 0 && i === activeIdx - 1;
3123
3142
  const targetDom = isNextWrap ? N + 1 : isPrevWrap ? 0 : domIndexFor(next);
3124
3143
  const slide = node.children[targetDom];
3125
3144
  if (slide) {
3126
3145
  internalScrollRef.current = true;
3127
- if (isNextWrap || isPrevWrap) wrapInProgressRef.current = true;
3146
+ goToInProgressRef.current = true;
3147
+ if (isNextWrap) wrapInFlightRef.current = N + 1;
3148
+ else if (isPrevWrap) wrapInFlightRef.current = 0;
3128
3149
  slide.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "start" });
3129
3150
  }
3130
3151
  }
@@ -3139,37 +3160,59 @@ var Carousel = forwardRef44(function Carousel2({
3139
3160
  if (width === 0) return;
3140
3161
  const domIdx = Math.round(node.scrollLeft / width);
3141
3162
  if (!isLooping) {
3163
+ if (goToInProgressRef.current) {
3164
+ if (domIdx === activeIdx) goToInProgressRef.current = false;
3165
+ return;
3166
+ }
3142
3167
  if (domIdx !== activeIdx) setActive(domIdx);
3143
3168
  return;
3144
3169
  }
3170
+ if (rebaseConsumeRef.current !== null) {
3171
+ if (domIdx === rebaseConsumeRef.current) return;
3172
+ rebaseConsumeRef.current = null;
3173
+ }
3145
3174
  if (domIdx === 0) {
3146
- if (wrapInProgressRef.current && node.scrollLeft > 1) return;
3175
+ if (goToInProgressRef.current && node.scrollLeft > 1) return;
3147
3176
  const realTwin = node.children[N];
3148
3177
  if (realTwin) {
3149
3178
  internalScrollRef.current = true;
3150
3179
  realTwin.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
3151
3180
  }
3152
3181
  if (activeIdx !== N - 1) setActive(N - 1);
3153
- wrapInProgressRef.current = false;
3182
+ goToInProgressRef.current = false;
3183
+ wrapInFlightRef.current = null;
3154
3184
  return;
3155
3185
  }
3156
3186
  if (domIdx === N + 1) {
3157
- if (wrapInProgressRef.current && node.scrollLeft < (N + 1) * width - 1) return;
3187
+ if (goToInProgressRef.current && node.scrollLeft < (N + 1) * width - 1) return;
3158
3188
  const realTwin = node.children[1];
3159
3189
  if (realTwin) {
3160
3190
  internalScrollRef.current = true;
3161
3191
  realTwin.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
3162
3192
  }
3163
3193
  if (activeIdx !== 0) setActive(0);
3164
- wrapInProgressRef.current = false;
3194
+ goToInProgressRef.current = false;
3195
+ wrapInFlightRef.current = null;
3165
3196
  return;
3166
3197
  }
3167
- if (wrapInProgressRef.current) return;
3168
3198
  const realIdx = domIdx - 1;
3199
+ if (goToInProgressRef.current) {
3200
+ if (realIdx === activeIdx) goToInProgressRef.current = false;
3201
+ return;
3202
+ }
3169
3203
  if (realIdx !== activeIdx) setActive(realIdx);
3170
3204
  };
3205
+ const onPointerDown = () => {
3206
+ goToInProgressRef.current = false;
3207
+ wrapInFlightRef.current = null;
3208
+ rebaseConsumeRef.current = null;
3209
+ };
3171
3210
  node.addEventListener("scroll", onScroll, { passive: true });
3172
- return () => node.removeEventListener("scroll", onScroll);
3211
+ node.addEventListener("pointerdown", onPointerDown, { passive: true });
3212
+ return () => {
3213
+ node.removeEventListener("scroll", onScroll);
3214
+ node.removeEventListener("pointerdown", onPointerDown);
3215
+ };
3173
3216
  }, [activeIdx, isLooping, N, setActive]);
3174
3217
  useEffect8(() => {
3175
3218
  if (internalScrollRef.current) {
@@ -3305,19 +3348,32 @@ var Carousel = forwardRef44(function Carousel2({
3305
3348
  }
3306
3349
  )
3307
3350
  ] }),
3308
- renderThumbnail && /* @__PURE__ */ jsx45("div", { className: "mt-2 flex gap-2 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item, i) => /* @__PURE__ */ jsx45(
3309
- "button",
3310
- {
3311
- type: "button",
3312
- "aria-label": `Show slide ${i + 1}`,
3313
- onClick: () => goTo(i),
3314
- className: cn(
3315
- "shrink-0 cursor-pointer overflow-hidden rounded transition-opacity",
3316
- i === activeIdx ? "ring-accent opacity-100 ring-2" : "opacity-60 hover:opacity-100"
3317
- ),
3318
- children: renderThumbnail(item, i)
3319
- },
3320
- i
3351
+ renderThumbnail && /* @__PURE__ */ jsx45("div", { className: "-mx-0.5 mt-1.5 flex gap-2 overflow-x-auto p-0.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: items.map((item, i) => (
3352
+ // The active ring is applied to the rendered thumbnail (the
3353
+ // button's first child) rather than the button itself, so it
3354
+ // traces whatever border-radius the consumer's thumbnail
3355
+ // already has. Picking a fixed radius here would always be
3356
+ // wrong for one consumer or another — `rounded-lg` thumbs got
3357
+ // a 4px-radius ring; future thumbs could be circular or
3358
+ // square. `box-shadow` (what `ring-2` compiles to) follows
3359
+ // the child's `border-radius` automatically, so this is
3360
+ // self-adjusting.
3361
+ /* @__PURE__ */ jsx45(
3362
+ "button",
3363
+ {
3364
+ type: "button",
3365
+ "aria-label": `Show slide ${i + 1}`,
3366
+ onClick: () => goTo(i),
3367
+ "data-active": i === activeIdx ? "true" : void 0,
3368
+ className: cn(
3369
+ "shrink-0 cursor-pointer transition-opacity",
3370
+ "[&[data-active]>*]:ring-accent [&[data-active]>*]:ring-2",
3371
+ i === activeIdx ? "opacity-100" : "opacity-60 hover:opacity-100"
3372
+ ),
3373
+ children: renderThumbnail(item, i)
3374
+ },
3375
+ i
3376
+ )
3321
3377
  )) })
3322
3378
  ]
3323
3379
  }
@@ -5076,7 +5132,7 @@ var ListingDetail = forwardRef52(function ListingDetail2({
5076
5132
  items: photos,
5077
5133
  index: galleryIndex,
5078
5134
  onIndexChange: setGalleryIndex,
5079
- loop,
5135
+ loop: Boolean(loop),
5080
5136
  title: lightboxTitle,
5081
5137
  renderItem: (src, i) => renderPhoto ? renderPhoto(src, i, "lightbox") : /* @__PURE__ */ jsx54("img", { src, alt: "", className: "max-h-[88vh] max-w-[92vw] object-contain" })
5082
5138
  }