@sixthshift/design-system 0.6.0 → 0.8.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/components/Badge/Badge.d.ts +10 -2
- package/dist/components/Badge/Badge.d.ts.map +1 -1
- package/dist/components/Badge/Badge.js +6 -4
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Button/Button.d.ts +25 -2
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/Button.js +13 -4
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/DataTable/DataTable.d.ts +89 -0
- package/dist/components/DataTable/DataTable.d.ts.map +1 -0
- package/dist/components/DataTable/DataTable.js +107 -0
- package/dist/components/DataTable/DataTable.js.map +1 -0
- package/dist/components/DataTable/index.d.ts +2 -0
- package/dist/components/DataTable/index.d.ts.map +1 -0
- package/dist/components/DataTable/index.js +2 -0
- package/dist/components/DataTable/index.js.map +1 -0
- package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/DatePicker/DatePicker.js +2 -1
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.js +2 -1
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +2 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
- package/dist/components/FileDropzone/FileDropzone.d.ts +87 -0
- package/dist/components/FileDropzone/FileDropzone.d.ts.map +1 -0
- package/dist/components/FileDropzone/FileDropzone.js +146 -0
- package/dist/components/FileDropzone/FileDropzone.js.map +1 -0
- package/dist/components/FileDropzone/index.d.ts +2 -0
- package/dist/components/FileDropzone/index.d.ts.map +1 -0
- package/dist/components/FileDropzone/index.js +2 -0
- package/dist/components/FileDropzone/index.js.map +1 -0
- package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/components/HoverCard/HoverCard.js +3 -0
- package/dist/components/HoverCard/HoverCard.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContent.d.ts +16 -2
- package/dist/components/HoverCard/components/HoverCardContent.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContent.js +3 -3
- package/dist/components/HoverCard/components/HoverCardContent.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContext.d.ts +2 -0
- package/dist/components/HoverCard/components/HoverCardContext.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContext.js.map +1 -1
- package/dist/components/ImageGallery/ImageGallery.d.ts +93 -0
- package/dist/components/ImageGallery/ImageGallery.d.ts.map +1 -0
- package/dist/components/ImageGallery/ImageGallery.js +121 -0
- package/dist/components/ImageGallery/ImageGallery.js.map +1 -0
- package/dist/components/ImageGallery/index.d.ts +2 -0
- package/dist/components/ImageGallery/index.d.ts.map +1 -0
- package/dist/components/ImageGallery/index.js +2 -0
- package/dist/components/ImageGallery/index.js.map +1 -0
- package/dist/components/Message/Message.d.ts +4 -0
- package/dist/components/Message/Message.d.ts.map +1 -1
- package/dist/components/Message/Message.js +2 -2
- package/dist/components/Message/Message.js.map +1 -1
- package/dist/components/Modal/Modal.d.ts +12 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -1
- package/dist/components/Modal/Modal.js +5 -2
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/NumberStepper/NumberStepper.d.ts +78 -0
- package/dist/components/NumberStepper/NumberStepper.d.ts.map +1 -0
- package/dist/components/NumberStepper/NumberStepper.js +129 -0
- package/dist/components/NumberStepper/NumberStepper.js.map +1 -0
- package/dist/components/NumberStepper/index.d.ts +2 -0
- package/dist/components/NumberStepper/index.d.ts.map +1 -0
- package/dist/components/NumberStepper/index.js +2 -0
- package/dist/components/NumberStepper/index.js.map +1 -0
- package/dist/components/Popover/Popover.d.ts +3 -1
- package/dist/components/Popover/Popover.d.ts.map +1 -1
- package/dist/components/Popover/Popover.js +3 -0
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/components/PopoverBody.d.ts +16 -2
- package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverBody.js +3 -3
- package/dist/components/Popover/components/PopoverBody.js.map +1 -1
- package/dist/components/Popover/components/PopoverContext.d.ts +2 -0
- package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverContext.js.map +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +3 -2
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectDropdown.d.ts +3 -1
- package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
- package/dist/components/Select/SelectDropdown.js +2 -2
- package/dist/components/Select/SelectDropdown.js.map +1 -1
- package/dist/components/Sheet/Sheet.d.ts +12 -0
- package/dist/components/Sheet/Sheet.d.ts.map +1 -1
- package/dist/components/Sheet/Sheet.js +4 -2
- package/dist/components/Sheet/Sheet.js.map +1 -1
- package/dist/components/Steps/Steps.d.ts +65 -0
- package/dist/components/Steps/Steps.d.ts.map +1 -0
- package/dist/components/Steps/Steps.js +62 -0
- package/dist/components/Steps/Steps.js.map +1 -0
- package/dist/components/Steps/index.d.ts +2 -0
- package/dist/components/Steps/index.d.ts.map +1 -0
- package/dist/components/Steps/index.js +2 -0
- package/dist/components/Steps/index.js.map +1 -0
- package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/components/TimePicker/TimePicker.js +2 -1
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/Toast/Toast.d.ts.map +1 -1
- package/dist/components/Toast/Toast.js +2 -2
- package/dist/components/Toast/Toast.js.map +1 -1
- package/dist/components/Toggle/Toggle.d.ts +4 -0
- package/dist/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle/Toggle.js +2 -2
- package/dist/components/Toggle/Toggle.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +1 -0
- package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupMultiple.js +2 -2
- package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +1 -0
- package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupSingle.js +2 -2
- package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
- package/dist/components/ToggleGroup/toggleGroup.types.d.ts +6 -0
- package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +3 -1
- package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +3 -0
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipBody.d.ts +16 -2
- package/dist/components/Tooltip/components/TooltipBody.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipBody.js +3 -3
- package/dist/components/Tooltip/components/TooltipBody.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipContext.d.ts +2 -0
- package/dist/components/Tooltip/components/TooltipContext.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipContext.js.map +1 -1
- package/dist/internal/brandScope.d.ts +25 -0
- package/dist/internal/brandScope.d.ts.map +1 -0
- package/dist/internal/brandScope.js +37 -0
- package/dist/internal/brandScope.js.map +1 -0
- package/package.json +21 -1
- package/src/components/Badge/Badge.tsx +10 -4
- package/src/components/Button/Button.tsx +24 -4
- package/src/components/DataTable/DataTable.tsx +261 -0
- package/src/components/DataTable/data-table.recipe.css +28 -0
- package/src/components/DataTable/index.ts +10 -0
- package/src/components/DatePicker/DatePicker.tsx +3 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +3 -0
- package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +3 -0
- package/src/components/FileDropzone/FileDropzone.tsx +239 -0
- package/src/components/FileDropzone/file-dropzone.recipe.css +38 -0
- package/src/components/FileDropzone/index.ts +1 -0
- package/src/components/HoverCard/HoverCard.tsx +3 -0
- package/src/components/HoverCard/components/HoverCardContent.tsx +11 -3
- package/src/components/HoverCard/components/HoverCardContext.tsx +2 -0
- package/src/components/ImageGallery/ImageGallery.tsx +285 -0
- package/src/components/ImageGallery/image-gallery.recipe.css +50 -0
- package/src/components/ImageGallery/index.ts +7 -0
- package/src/components/Message/Message.tsx +26 -22
- package/src/components/Modal/Modal.tsx +11 -0
- package/src/components/NumberStepper/NumberStepper.tsx +227 -0
- package/src/components/NumberStepper/index.ts +1 -0
- package/src/components/NumberStepper/number-stepper.recipe.css +31 -0
- package/src/components/Popover/Popover.tsx +3 -0
- package/src/components/Popover/components/PopoverBody.tsx +11 -3
- package/src/components/Popover/components/PopoverContext.tsx +2 -0
- package/src/components/Select/Select.tsx +3 -1
- package/src/components/Select/SelectDropdown.tsx +4 -0
- package/src/components/Sheet/Sheet.tsx +10 -0
- package/src/components/Steps/Steps.tsx +149 -0
- package/src/components/Steps/index.ts +1 -0
- package/src/components/Steps/steps.recipe.css +51 -0
- package/src/components/TimePicker/TimePicker.tsx +3 -0
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Toggle/Toggle.tsx +17 -2
- package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +2 -1
- package/src/components/ToggleGroup/ToggleGroupSingle.tsx +2 -0
- package/src/components/ToggleGroup/toggleGroup.types.ts +6 -0
- package/src/components/Tooltip/Tooltip.tsx +3 -0
- package/src/components/Tooltip/components/TooltipBody.tsx +11 -3
- package/src/components/Tooltip/components/TooltipContext.tsx +2 -0
- package/src/internal/brandScope.ts +39 -0
- package/src/theming/tailwind.css +5 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Geometry only. Every colour reads an `--image-gallery-*` component token
|
|
5
|
+
* whose value is decided by src/components/ImageGallery/image-gallery.recipe.css.
|
|
6
|
+
*
|
|
7
|
+
* `ratio` is the frame's shape, not the images': every slide is cropped to it
|
|
8
|
+
* with `object-cover`, so a mixed set of uploads still produces a steady
|
|
9
|
+
* frame that does not jump as you swipe. `portrait` is 4:5, the tallest crop
|
|
10
|
+
* Instagram allows in a feed — photography shot for the feed lands on it
|
|
11
|
+
* uncropped.
|
|
12
|
+
*/
|
|
13
|
+
declare const frameVariants: (props?: ({
|
|
14
|
+
ratio?: "square" | "portrait" | "landscape" | "wide" | null | undefined;
|
|
15
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
16
|
+
export type ImageGalleryRatio = NonNullable<VariantProps<typeof frameVariants>["ratio"]>;
|
|
17
|
+
export type ImageGalleryImage = {
|
|
18
|
+
src: string;
|
|
19
|
+
/** Required: describe the photo. The gallery adds position ("2 of 5") on its own. */
|
|
20
|
+
alt: string;
|
|
21
|
+
srcSet?: string;
|
|
22
|
+
sizes?: string;
|
|
23
|
+
width?: number;
|
|
24
|
+
height?: number;
|
|
25
|
+
};
|
|
26
|
+
export type ImageGalleryProps = Omit<React.HTMLAttributes<HTMLElement>, "onChange"> & VariantProps<typeof frameVariants> & {
|
|
27
|
+
images: readonly ImageGalleryImage[];
|
|
28
|
+
/** Controlled index of the image shown. */
|
|
29
|
+
index?: number;
|
|
30
|
+
/** Initial index in uncontrolled mode. */
|
|
31
|
+
defaultIndex?: number;
|
|
32
|
+
/** Called when the shown image changes — by swipe, arrow, key or thumbnail. */
|
|
33
|
+
onIndexChange?: (index: number) => void;
|
|
34
|
+
/**
|
|
35
|
+
* What sits under the frame: a strip of `thumbnails`, a row of `dots`, or
|
|
36
|
+
* `none`. Only rendered when there is more than one image.
|
|
37
|
+
*/
|
|
38
|
+
indicator?: "thumbnails" | "dots" | "none";
|
|
39
|
+
/** Accessible name for the carousel, e.g. the product's name. */
|
|
40
|
+
label?: string;
|
|
41
|
+
/** Accessible name for the previous-image button. */
|
|
42
|
+
previousLabel?: string;
|
|
43
|
+
/** Accessible name for the next-image button. */
|
|
44
|
+
nextLabel?: string;
|
|
45
|
+
/** Shown in the frame when `images` is empty. */
|
|
46
|
+
placeholder?: React.ReactNode;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A product photo carousel: one frame, swipeable on touch, with previous/next
|
|
50
|
+
* buttons, arrow-key navigation and an optional thumbnail strip or dots.
|
|
51
|
+
*
|
|
52
|
+
* Swiping is native scrolling — a horizontal `scroll-snap` track, not a
|
|
53
|
+
* pointer-event reimplementation — so it has the platform's own momentum and
|
|
54
|
+
* edge behaviour on phones. The shown index follows the scroll position, and
|
|
55
|
+
* setting the index (by button, key, thumbnail or a controlled `index`)
|
|
56
|
+
* scrolls the track. Both directions meet at one rule: the effect only scrolls
|
|
57
|
+
* when the track is not already showing the index, so a swipe that updates
|
|
58
|
+
* the index never fights itself.
|
|
59
|
+
*
|
|
60
|
+
* Controlled or uncontrolled through `index`/`defaultIndex`/`onIndexChange`.
|
|
61
|
+
*
|
|
62
|
+
* Follows the WAI-ARIA carousel pattern: the root is a `<section>` with
|
|
63
|
+
* `aria-roledescription="carousel"` (a landmark region once `label` names
|
|
64
|
+
* it), each slide a group named "n of total", and slides out of view are
|
|
65
|
+
* `aria-hidden`. The first image loads eagerly and
|
|
66
|
+
* the rest lazily, so a gallery of ten costs one download up front.
|
|
67
|
+
*/
|
|
68
|
+
export declare const ImageGallery: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLElement>, "onChange"> & VariantProps<(props?: ({
|
|
69
|
+
ratio?: "square" | "portrait" | "landscape" | "wide" | null | undefined;
|
|
70
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
71
|
+
images: readonly ImageGalleryImage[];
|
|
72
|
+
/** Controlled index of the image shown. */
|
|
73
|
+
index?: number;
|
|
74
|
+
/** Initial index in uncontrolled mode. */
|
|
75
|
+
defaultIndex?: number;
|
|
76
|
+
/** Called when the shown image changes — by swipe, arrow, key or thumbnail. */
|
|
77
|
+
onIndexChange?: (index: number) => void;
|
|
78
|
+
/**
|
|
79
|
+
* What sits under the frame: a strip of `thumbnails`, a row of `dots`, or
|
|
80
|
+
* `none`. Only rendered when there is more than one image.
|
|
81
|
+
*/
|
|
82
|
+
indicator?: "thumbnails" | "dots" | "none";
|
|
83
|
+
/** Accessible name for the carousel, e.g. the product's name. */
|
|
84
|
+
label?: string;
|
|
85
|
+
/** Accessible name for the previous-image button. */
|
|
86
|
+
previousLabel?: string;
|
|
87
|
+
/** Accessible name for the next-image button. */
|
|
88
|
+
nextLabel?: string;
|
|
89
|
+
/** Shown in the frame when `images` is empty. */
|
|
90
|
+
placeholder?: React.ReactNode;
|
|
91
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
92
|
+
export { frameVariants as imageGalleryFrameVariants };
|
|
93
|
+
//# sourceMappingURL=ImageGallery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageGallery.d.ts","sourceRoot":"","sources":["../../../src/components/ImageGallery/ImageGallery.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;;;;;;;;GASG;AACH,QAAA,MAAM,aAAa;;8EAYjB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAEzF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,GACjF,YAAY,CAAC,OAAO,aAAa,CAAC,GAAG;IACnC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;IAC3C,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC/B,CAAC;AAMJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY;;;YA9Cb,SAAS,iBAAiB,EAAE;IACpC,2CAA2C;YACnC,MAAM;IACd,0CAA0C;mBAC3B,MAAM;IACrB,+EAA+E;oBAC/D,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IACvC;;;OAGG;gBACS,YAAY,GAAG,MAAM,GAAG,MAAM;IAC1C,iEAAiE;YACzD,MAAM;IACd,qDAAqD;oBACrC,MAAM;IACtB,iDAAiD;gBACrC,MAAM;IAClB,iDAAiD;kBACnC,KAAK,CAAC,SAAS;qCAwNhC,CAAC;AAGF,OAAO,EAAE,aAAa,IAAI,yBAAyB,EAAE,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
4
|
+
import { cn } from "@sixthshift/design-system/utils";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
/**
|
|
9
|
+
* Geometry only. Every colour reads an `--image-gallery-*` component token
|
|
10
|
+
* whose value is decided by src/components/ImageGallery/image-gallery.recipe.css.
|
|
11
|
+
*
|
|
12
|
+
* `ratio` is the frame's shape, not the images': every slide is cropped to it
|
|
13
|
+
* with `object-cover`, so a mixed set of uploads still produces a steady
|
|
14
|
+
* frame that does not jump as you swipe. `portrait` is 4:5, the tallest crop
|
|
15
|
+
* Instagram allows in a feed — photography shot for the feed lands on it
|
|
16
|
+
* uncropped.
|
|
17
|
+
*/
|
|
18
|
+
const frameVariants = cva("relative overflow-hidden rounded-lg bg-(--image-gallery-bg)", {
|
|
19
|
+
variants: {
|
|
20
|
+
ratio: {
|
|
21
|
+
square: "aspect-square",
|
|
22
|
+
portrait: "aspect-[4/5]",
|
|
23
|
+
landscape: "aspect-[4/3]",
|
|
24
|
+
wide: "aspect-video",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
ratio: "square",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
function prefersReducedMotion() {
|
|
32
|
+
return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A product photo carousel: one frame, swipeable on touch, with previous/next
|
|
36
|
+
* buttons, arrow-key navigation and an optional thumbnail strip or dots.
|
|
37
|
+
*
|
|
38
|
+
* Swiping is native scrolling — a horizontal `scroll-snap` track, not a
|
|
39
|
+
* pointer-event reimplementation — so it has the platform's own momentum and
|
|
40
|
+
* edge behaviour on phones. The shown index follows the scroll position, and
|
|
41
|
+
* setting the index (by button, key, thumbnail or a controlled `index`)
|
|
42
|
+
* scrolls the track. Both directions meet at one rule: the effect only scrolls
|
|
43
|
+
* when the track is not already showing the index, so a swipe that updates
|
|
44
|
+
* the index never fights itself.
|
|
45
|
+
*
|
|
46
|
+
* Controlled or uncontrolled through `index`/`defaultIndex`/`onIndexChange`.
|
|
47
|
+
*
|
|
48
|
+
* Follows the WAI-ARIA carousel pattern: the root is a `<section>` with
|
|
49
|
+
* `aria-roledescription="carousel"` (a landmark region once `label` names
|
|
50
|
+
* it), each slide a group named "n of total", and slides out of view are
|
|
51
|
+
* `aria-hidden`. The first image loads eagerly and
|
|
52
|
+
* the rest lazily, so a gallery of ten costs one download up front.
|
|
53
|
+
*/
|
|
54
|
+
export const ImageGallery = React.forwardRef(({ className, images, index: controlledIndex, defaultIndex = 0, onIndexChange, ratio = "square", indicator = "thumbnails", label, previousLabel = "Previous image", nextLabel = "Next image", placeholder, onKeyDown, ...props }, ref) => {
|
|
55
|
+
const count = images.length;
|
|
56
|
+
const [rawIndex, setIndex] = useControllableState({
|
|
57
|
+
value: controlledIndex,
|
|
58
|
+
defaultValue: defaultIndex,
|
|
59
|
+
onChange: onIndexChange,
|
|
60
|
+
});
|
|
61
|
+
const index = count === 0 ? 0 : Math.min(Math.max(rawIndex, 0), count - 1);
|
|
62
|
+
const trackRef = React.useRef(null);
|
|
63
|
+
// The slide a programmatic scroll is heading for. While set, the slides it
|
|
64
|
+
// passes on the way are not reported — jumping from 1 to 4 reports 4, not
|
|
65
|
+
// 2, 3 and 4. Any touch, wheel or pointer on the track hands control back.
|
|
66
|
+
const scrollTargetRef = React.useRef(null);
|
|
67
|
+
// Index → scroll. Skipped when the track already shows `index`, which is
|
|
68
|
+
// always the case right after a swipe reported it.
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
const track = trackRef.current;
|
|
71
|
+
if (!track || track.clientWidth === 0)
|
|
72
|
+
return;
|
|
73
|
+
if (Math.round(track.scrollLeft / track.clientWidth) === index)
|
|
74
|
+
return;
|
|
75
|
+
scrollTargetRef.current = index;
|
|
76
|
+
track.scrollTo({ left: index * track.clientWidth, behavior: prefersReducedMotion() ? "auto" : "smooth" });
|
|
77
|
+
}, [index]);
|
|
78
|
+
// Scroll → index. Rounds, so the index flips at the halfway point of a swipe.
|
|
79
|
+
const handleScroll = () => {
|
|
80
|
+
const track = trackRef.current;
|
|
81
|
+
if (!track || track.clientWidth === 0)
|
|
82
|
+
return;
|
|
83
|
+
const shown = Math.round(track.scrollLeft / track.clientWidth);
|
|
84
|
+
if (scrollTargetRef.current !== null) {
|
|
85
|
+
if (shown !== scrollTargetRef.current)
|
|
86
|
+
return;
|
|
87
|
+
scrollTargetRef.current = null;
|
|
88
|
+
}
|
|
89
|
+
if (shown !== index && shown >= 0 && shown < count)
|
|
90
|
+
setIndex(shown);
|
|
91
|
+
};
|
|
92
|
+
const releaseScroll = () => {
|
|
93
|
+
scrollTargetRef.current = null;
|
|
94
|
+
};
|
|
95
|
+
const go = (next) => {
|
|
96
|
+
if (next >= 0 && next < count && next !== index)
|
|
97
|
+
setIndex(next);
|
|
98
|
+
};
|
|
99
|
+
const handleKeyDown = (event) => {
|
|
100
|
+
onKeyDown?.(event);
|
|
101
|
+
if (event.defaultPrevented)
|
|
102
|
+
return;
|
|
103
|
+
const targets = { ArrowLeft: index - 1, ArrowRight: index + 1, Home: 0, End: count - 1 };
|
|
104
|
+
const target = targets[event.key];
|
|
105
|
+
if (target === undefined)
|
|
106
|
+
return;
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
go(target);
|
|
109
|
+
};
|
|
110
|
+
const controlClass = "image-gallery-control focus-visible:ring-(color:--image-gallery-control-ring) absolute top-1/2 flex size-9 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-(--image-gallery-control-bg) text-(--image-gallery-control-fg) shadow transition-opacity hover:bg-(--image-gallery-control-bg-hovered) focus-visible:outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-0 max-sm:hidden [&_svg]:size-5";
|
|
111
|
+
return (_jsxs("section", { ref: ref, "aria-roledescription": "carousel", "aria-label": label, className: cn("image-gallery flex flex-col gap-3", className), onKeyDown: handleKeyDown, ...props, children: [_jsxs("div", { className: frameVariants({ ratio }), children: [count === 0 ? (_jsx("div", { className: "flex h-full w-full items-center justify-center text-(--image-gallery-placeholder-fg) text-sm", children: placeholder })) : (_jsx("div", { ref: trackRef, onScroll: handleScroll, onPointerDown: releaseScroll, onTouchStart: releaseScroll, onWheel: releaseScroll,
|
|
112
|
+
// Focusable so the arrow keys work without first reaching a button,
|
|
113
|
+
// and because a scrollable region must be keyboard-reachable.
|
|
114
|
+
// biome-ignore lint/a11y/noNoninteractiveTabindex: scrollable regions must be focusable (axe `scrollable-region-focusable`)
|
|
115
|
+
tabIndex: 0, className: "focus-visible:ring-(color:--image-gallery-control-ring) flex h-full w-full snap-x snap-mandatory overflow-x-auto overscroll-x-contain [scrollbar-width:none] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset [&::-webkit-scrollbar]:hidden", children: images.map((image, i) => (
|
|
116
|
+
// biome-ignore lint/a11y/useSemanticElements: the WAI-ARIA carousel pattern names each slide a `group`; a <fieldset> is for form controls
|
|
117
|
+
_jsx("div", { role: "group", "aria-roledescription": "slide", "aria-label": `${i + 1} of ${count}`, "aria-hidden": i !== index, className: "h-full w-full shrink-0 snap-center snap-always", children: _jsx("img", { src: image.src, srcSet: image.srcSet, sizes: image.sizes, width: image.width, height: image.height, alt: image.alt, loading: i === 0 ? "eager" : "lazy", decoding: "async", draggable: false, className: "h-full w-full select-none object-cover" }) }, i))) })), count > 1 && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": previousLabel, disabled: index === 0, onClick: () => go(index - 1), className: cn(controlClass, "left-2"), children: _jsx(ChevronLeft, { "aria-hidden": "true" }) }), _jsx("button", { type: "button", "aria-label": nextLabel, disabled: index === count - 1, onClick: () => go(index + 1), className: cn(controlClass, "right-2"), children: _jsx(ChevronRight, { "aria-hidden": "true" }) })] }))] }), count > 1 && indicator === "thumbnails" && (_jsx("div", { className: "flex gap-2 overflow-x-auto p-0.5", children: images.map((image, i) => (_jsx("button", { type: "button", "aria-label": `Show image ${i + 1} of ${count}`, "aria-current": i === index ? "true" : undefined, "data-selected": i === index, onClick: () => go(i), className: "image-gallery-thumb border-(color:--image-gallery-thumb-border) focus-visible:ring-(color:--image-gallery-control-ring) hover:border-(color:--image-gallery-thumb-border-hovered) size-16 shrink-0 cursor-pointer overflow-hidden rounded-md border-2 transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2", children: _jsx("img", { src: image.src, srcSet: image.srcSet, sizes: "64px", alt: "", loading: "lazy", decoding: "async", draggable: false, className: "h-full w-full object-cover" }) }, i))) })), count > 1 && indicator === "dots" && (_jsx("div", { className: "flex justify-center gap-1.5", children: images.map((_, i) => (_jsx("button", { type: "button", "aria-label": `Show image ${i + 1} of ${count}`, "aria-current": i === index ? "true" : undefined, "data-selected": i === index, onClick: () => go(i), className: "image-gallery-dot focus-visible:ring-(color:--image-gallery-control-ring) flex size-6 cursor-pointer items-center justify-center rounded-full focus-visible:outline-hidden focus-visible:ring-2", children: _jsx("span", { "aria-hidden": "true", className: "block size-2 rounded-full bg-(--image-gallery-dot-bg) transition-colors" }) }, i))) }))] }));
|
|
118
|
+
});
|
|
119
|
+
ImageGallery.displayName = "ImageGallery";
|
|
120
|
+
export { frameVariants as imageGalleryFrameVariants };
|
|
121
|
+
//# sourceMappingURL=ImageGallery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageGallery.js","sourceRoot":"","sources":["../../../src/components/ImageGallery/ImageGallery.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ImageGallery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/ImageGallery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIZ,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC"}
|
|
@@ -24,6 +24,8 @@ export type MessageIntentName = "neutral" | "success" | "warning" | "danger";
|
|
|
24
24
|
export type MessageIntent = Loose<MessageIntentName>;
|
|
25
25
|
export type MessageProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof messageVariants> & {
|
|
26
26
|
intent?: MessageIntent | undefined;
|
|
27
|
+
/** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the message follows the nearest `data-brand` ancestor. */
|
|
28
|
+
brand?: string | undefined;
|
|
27
29
|
/** Simple API: Optional title displayed above children */
|
|
28
30
|
title?: React.ReactNode;
|
|
29
31
|
/** Simple API: Optional icon displayed on the left */
|
|
@@ -58,6 +60,8 @@ export declare const Message: React.ForwardRefExoticComponent<React.HTMLAttribut
|
|
|
58
60
|
size?: "sm" | "md" | null | undefined;
|
|
59
61
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
60
62
|
intent?: MessageIntent | undefined;
|
|
63
|
+
/** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the message follows the nearest `data-brand` ancestor. */
|
|
64
|
+
brand?: string | undefined;
|
|
61
65
|
/** Simple API: Optional title displayed above children */
|
|
62
66
|
title?: React.ReactNode;
|
|
63
67
|
/** Simple API: Optional icon displayed on the left */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/components/Message/Message.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;8EAgB3B,CAAC;AAEF;;;;;GAKG;AACH,KAAK,KAAK,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC7E,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC;AAErD,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,GAC7D,YAAY,CAAC,OAAO,eAAe,CAAC,GAAG;IACrC,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,0DAA0D;IAC1D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,sDAAsD;IACtD,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAsBJ;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,OAAO;;;
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/components/Message/Message.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;8EAgB3B,CAAC;AAEF;;;;;GAKG;AACH,KAAK,KAAK,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC7E,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC;AAErD,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,GAC7D,YAAY,CAAC,OAAO,eAAe,CAAC,GAAG;IACrC,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,mIAAmI;IACnI,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,0DAA0D;IAC1D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,sDAAsD;IACtD,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAsBJ;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,OAAO;;;aAtDP,aAAa,GAAG,SAAS;IAClC,mIAAmI;YAC3H,MAAM,GAAG,SAAS;IAC1B,0DAA0D;YAClD,KAAK,CAAC,SAAS;IACvB,sDAAsD;WAC/C,KAAK,CAAC,SAAS;wCAwEzB,CAAC"}
|
|
@@ -75,10 +75,10 @@ function intentRole(intent) {
|
|
|
75
75
|
* simple API can't express. Message detects which one is in use by
|
|
76
76
|
* inspecting its children, so the two aren't meant to be mixed.
|
|
77
77
|
*/
|
|
78
|
-
export const Message = React.forwardRef(({ className, intent = "neutral", size, title, icon, children, ...props }, ref) => {
|
|
78
|
+
export const Message = React.forwardRef(({ className, intent = "neutral", brand, size, title, icon, children, ...props }, ref) => {
|
|
79
79
|
const hasCompoundChildren = React.Children.toArray(children).some((child) => React.isValidElement(child) &&
|
|
80
80
|
(child.type === MessageIcon || child.type === MessageBody || child.type === MessageTitle || child.type === MessageDescription));
|
|
81
|
-
return (_jsx("div", { ref: ref, role: intentRole(intent), "data-intent": intent, className: cn(messageVariants({ size, className })), ...props, children: hasCompoundChildren ? (children) : (_jsxs(_Fragment, { children: [icon && _jsx(MessageIcon, { children: icon }), _jsxs(MessageBody, { children: [title && _jsx(MessageTitle, { children: title }), _jsx(MessageDescription, { children: children })] })] })) }));
|
|
81
|
+
return (_jsx("div", { ref: ref, role: intentRole(intent), "data-intent": intent, "data-brand": brand, className: cn(messageVariants({ size, className })), ...props, children: hasCompoundChildren ? (children) : (_jsxs(_Fragment, { children: [icon && _jsx(MessageIcon, { children: icon }), _jsxs(MessageBody, { children: [title && _jsx(MessageTitle, { children: title }), _jsx(MessageDescription, { children: children })] })] })) }));
|
|
82
82
|
});
|
|
83
83
|
Message.displayName = "Message";
|
|
84
84
|
//# sourceMappingURL=Message.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.js","sourceRoot":"","sources":["../../../src/components/Message/Message.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AACrD,OAAO,EAAE,GAAG,EAAqB,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG;AAChC,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,yIAAyI,EACzI;IACE,QAAQ,EAAE;QACR,IAAI,EAAE;YACJ,EAAE,EAAE,KAAK;YACT,EAAE,EAAE,mBAAmB;SACxB;KACF;IACD,eAAe,EAAE;QACf,IAAI,EAAE,IAAI;KACX;CACF,CACF,CAAC;
|
|
1
|
+
{"version":3,"file":"Message.js","sourceRoot":"","sources":["../../../src/components/Message/Message.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AACrD,OAAO,EAAE,GAAG,EAAqB,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG;AAChC,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,yIAAyI,EACzI;IACE,QAAQ,EAAE;QACR,IAAI,EAAE;YACJ,EAAE,EAAE,KAAK;YACT,EAAE,EAAE,mBAAmB;SACxB;KACF;IACD,eAAe,EAAE;QACf,IAAI,EAAE,IAAI;KACX;CACF,CACF,CAAC;AAwBF;;;;;;;;;;;;;;GAcG;AACH,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClF,SAAS,UAAU,CAAC,MAAqB;IACvC,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CACrC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE;IACvF,MAAM,mBAAmB,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC/D,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;QAC3B,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,CAAC,CACjI,CAAC;IAEF,OAAO,CACL,cAAK,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,iBAAe,MAAM,gBAAc,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,KAAM,KAAK,YAC5I,mBAAmB,CAAC,CAAC,CAAC,CACrB,QAAQ,CACT,CAAC,CAAC,CAAC,CACF,8BACG,IAAI,IAAI,KAAC,WAAW,cAAE,IAAI,GAAe,EAC1C,MAAC,WAAW,eACT,KAAK,IAAI,KAAC,YAAY,cAAE,KAAK,GAAgB,EAC9C,KAAC,kBAAkB,cAAE,QAAQ,GAAsB,IACvC,IACb,CACJ,GACG,CACP,CAAC;AACJ,CAAC,CACF,CAAC;AACF,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC"}
|
|
@@ -23,6 +23,12 @@ export type ModalProps = Pick<HTMLAttributes<HTMLDivElement>, "className" | "sty
|
|
|
23
23
|
closable?: boolean;
|
|
24
24
|
/** Vertical alignment on desktop. "center" (default) or "top" (anchored near top, content grows down) */
|
|
25
25
|
align?: "center" | "top";
|
|
26
|
+
/**
|
|
27
|
+
* Name of a brand the theme defines, rendered as `data-brand` on the dialog.
|
|
28
|
+
* Omitted, the modal takes the brand scope of the control that opened it
|
|
29
|
+
* (whatever held focus), since the portal leaves the trigger's scope behind.
|
|
30
|
+
*/
|
|
31
|
+
brand?: string | undefined;
|
|
26
32
|
};
|
|
27
33
|
/**
|
|
28
34
|
* A centered, backdrop-dimmed dialog that fully interrupts the page — the
|
|
@@ -85,6 +91,12 @@ export declare const Modal: import("react").ForwardRefExoticComponent<Pick<HTMLA
|
|
|
85
91
|
closable?: boolean;
|
|
86
92
|
/** Vertical alignment on desktop. "center" (default) or "top" (anchored near top, content grows down) */
|
|
87
93
|
align?: "center" | "top";
|
|
94
|
+
/**
|
|
95
|
+
* Name of a brand the theme defines, rendered as `data-brand` on the dialog.
|
|
96
|
+
* Omitted, the modal takes the brand scope of the control that opened it
|
|
97
|
+
* (whatever held focus), since the portal leaves the trigger's scope behind.
|
|
98
|
+
*/
|
|
99
|
+
brand?: string | undefined;
|
|
88
100
|
} & import("react").RefAttributes<HTMLDivElement>> & {
|
|
89
101
|
Header: import("react").ForwardRefExoticComponent<import("./components/index.js").ModalHeaderProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
90
102
|
Body: import("react").ForwardRefExoticComponent<import("./components/index.js").ModalBodyProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/Modal/Modal.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,SAAS,EAAgE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/Modal/Modal.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,SAAS,EAAgE,MAAM,OAAO,CAAC;AAStI,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,YAAY,GAAG,iBAAiB,GAAG,kBAAkB,CAAC,GAAG;IAC7I;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,iBAAiB;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IACnC,4DAA4D;IAC5D,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yGAAyG;IACzG,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IACzB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,CAAC;AAwKF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,KAAK;IA1OhB;;;;;OAKG;mBACY,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI;IACtC,iBAAiB;WACV,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM;IAClC,4DAA4D;cAClD,SAAS;IACnB;;;;;;OAMG;kBACW,OAAO;IACrB,8DAA8D;eACnD,OAAO;IAClB,yGAAyG;YACjG,QAAQ,GAAG,KAAK;IACxB;;;;OAIG;YACK,MAAM,GAAG,SAAS;;;;;CAkN1B,CAAC"}
|
|
@@ -4,6 +4,7 @@ import { FloatingFocusManager, FloatingOverlay, FloatingPortal, useDismiss, useF
|
|
|
4
4
|
import { useMergedFloatingRef, usePresence } from "@sixthshift/design-system/hooks";
|
|
5
5
|
import { cn } from "@sixthshift/design-system/utils";
|
|
6
6
|
import { forwardRef, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
7
|
+
import { useOpenerBrand } from "../../internal/brandScope.js";
|
|
7
8
|
import { hasOpenEscapeLayer, useEscapeDialog } from "../../internal/escapeLayers.js";
|
|
8
9
|
import { ModalBody, ModalContext, ModalFooter, ModalHeader } from "./components/index.js";
|
|
9
10
|
// =============================================================================
|
|
@@ -18,7 +19,7 @@ const sizeClasses = {
|
|
|
18
19
|
// =============================================================================
|
|
19
20
|
// Modal
|
|
20
21
|
// =============================================================================
|
|
21
|
-
const ModalRoot = forwardRef(({ onOpenChange, size = "md", className, style, children, dismissable = true, closable, align = "center", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }, ref) => {
|
|
22
|
+
const ModalRoot = forwardRef(({ onOpenChange, size = "md", className, style, children, dismissable = true, closable, align = "center", brand, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }, ref) => {
|
|
22
23
|
// Prefer explicit prop over context (context is set by useModal's overlay system)
|
|
23
24
|
const contextValue = useContext(ModalContext);
|
|
24
25
|
// A context means useModal's stack owns this modal — and with it, Escape
|
|
@@ -32,6 +33,8 @@ const ModalRoot = forwardRef(({ onOpenChange, size = "md", className, style, chi
|
|
|
32
33
|
contextValue?.onClose();
|
|
33
34
|
}, [onOpenChange, contextValue]);
|
|
34
35
|
const { ref: presenceRef, state, isMounted, show, hide } = usePresence();
|
|
36
|
+
// Mount-driven, so the modal is open for as long as it exists.
|
|
37
|
+
const openerBrand = useOpenerBrand(true);
|
|
35
38
|
useEffect(() => {
|
|
36
39
|
show();
|
|
37
40
|
}, [show]);
|
|
@@ -88,7 +91,7 @@ const ModalRoot = forwardRef(({ onOpenChange, size = "md", className, style, chi
|
|
|
88
91
|
// `transform` container would be positioned (and clipped) by it. Nested
|
|
89
92
|
// FloatingPortals attach to the parent portal's node, so a stack-managed
|
|
90
93
|
// modal stays inside the `OverlayProvider`'s custom `modal` root.
|
|
91
|
-
_jsx(FloatingPortal, { children: _jsx(FloatingFocusManager, { context: context, modal: true, children: _jsx(FloatingOverlay, { lockScroll: true, className: cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out"), children: _jsx("div", { ref: mergedRef, role: "dialog", "aria-modal": "true", "aria-label": ariaLabel, "aria-labelledby": labelledBy, "aria-describedby": ariaDescribedBy, tabIndex: -1, "data-state": state, className: cn("modal fixed flex flex-col overflow-hidden outline-hidden", "border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
|
|
94
|
+
_jsx(FloatingPortal, { children: _jsx(FloatingFocusManager, { context: context, modal: true, children: _jsx(FloatingOverlay, { lockScroll: true, className: cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out"), children: _jsx("div", { ref: mergedRef, role: "dialog", "aria-modal": "true", "aria-label": ariaLabel, "aria-labelledby": labelledBy, "aria-describedby": ariaDescribedBy, tabIndex: -1, "data-state": state, "data-brand": brand ?? openerBrand, className: cn("modal fixed flex flex-col overflow-hidden outline-hidden", "border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
|
|
92
95
|
// Size
|
|
93
96
|
sizeClasses[size],
|
|
94
97
|
// Cap height so ModalBody scrolls instead of overflowing the viewport
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../../src/components/Modal/Modal.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC9I,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AACrD,OAAO,EAAE,UAAU,EAAuC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtI,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../../src/components/Modal/Modal.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC9I,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AACrD,OAAO,EAAE,UAAU,EAAuC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtI,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAsCjF,gFAAgF;AAChF,eAAe;AACf,gFAAgF;AAEhF,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,SAAS;IACb,EAAE,EAAE,cAAc;IAClB,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,uBAAuB;CAC9B,CAAC;AAEF,gFAAgF;AAChF,QAAQ;AACR,gFAAgF;AAEhF,MAAM,SAAS,GAAG,UAAU,CAC1B,CACE,EACE,YAAY,EACZ,IAAI,GAAG,IAAI,EACX,SAAS,EACT,KAAK,EACL,QAAQ,EACR,WAAW,GAAG,IAAI,EAClB,QAAQ,EACR,KAAK,GAAG,QAAQ,EAChB,KAAK,EACL,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,kBAAkB,EAAE,eAAe,GACpC,EACD,GAAG,EACH,EAAE;IACF,kFAAkF;IAClF,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAC9C,yEAAyE;IACzE,2EAA2E;IAC3E,MAAM,cAAc,GAAG,YAAY,IAAI,IAAI,CAAC;IAC5C,gGAAgG;IAChG,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,IAAI,YAAY;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;;YACjC,YAAY,EAAE,OAAO,EAAE,CAAC;IAC/B,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;IAEjC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,CAAC;IACzE,+DAA+D;IAC/D,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAEzC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,EAAE,CAAC;IACT,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,IAAI,CAAC,aAAa,CAAC,CAAC;IACtB,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC;IAE1B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC;QACpC,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACzC,IAAI,QAAQ;gBAAE,OAAO;YACrB,oEAAoE;YACpE,sEAAsE;YACtE,IAAI,MAAM,KAAK,YAAY,IAAI,kBAAkB,EAAE;gBAAE,OAAO;YAC5D,WAAW,EAAE,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,oBAAoB,CAAiB,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IAE/E,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE;QAClC,OAAO,EAAE,WAAW;QACpB,oEAAoE;QACpE,oEAAoE;QACpE,+BAA+B;QAC/B,SAAS,EAAE,CAAC,cAAc;QAC1B,YAAY,EAAE,IAAI;QAClB,iBAAiB,EAAE,WAAW;KAC/B,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE;QAC5B,IAAI,EAAE,QAAQ;KACf,CAAC,CAAC;IAEH,MAAM,EAAE,gBAAgB,EAAE,GAAG,eAAe,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAE9D,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAElF,4EAA4E;IAC5E,yEAAyE;IACzE,kCAAkC;IAClC,MAAM,UAAU,GAAG,cAAc,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAEpF,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IAEvJ,wEAAwE;IACxE,oCAAoC;IACpC,eAAe,CAAC,SAAS,CAAC,CAAC;IAE3B,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,UAAU,GAAG,KAAK,KAAK,UAAU,CAAC;IACxC,MAAM,SAAS,GAAG,KAAK,KAAK,SAAS,CAAC;IAEtC,OAAO;IACL,iEAAiE;IACjE,uEAAuE;IACvE,wEAAwE;IACxE,yEAAyE;IACzE,kEAAkE;IAClE,KAAC,cAAc,cACb,KAAC,oBAAoB,IAAC,OAAO,EAAE,OAAO,EAAE,KAAK,kBAC3C,KAAC,eAAe,IACd,UAAU,QACV,SAAS,EAAE,EAAE,CAAC,6DAA6D,EAAE,UAAU,IAAI,iBAAiB,EAAE,SAAS,IAAI,kBAAkB,CAAC,YAE9I,cACE,GAAG,EAAE,SAAS,EACd,IAAI,EAAC,QAAQ,gBACF,MAAM,gBACL,SAAS,qBACJ,UAAU,sBACT,eAAe,EACjC,QAAQ,EAAE,CAAC,CAAC,gBACA,KAAK,gBACL,KAAK,IAAI,WAAW,EAChC,SAAS,EAAE,EAAE,CACX,0DAA0D,EAC1D,iEAAiE;oBACjE,OAAO;oBACP,WAAW,CAAC,IAAI,CAAC;oBACjB,sEAAsE;oBACtE,aAAa;oBACb,2CAA2C;oBAC3C,wDAAwD,EACxD,UAAU,IAAI,4BAA4B,EAC1C,SAAS,IAAI,6BAA6B;oBAC1C,kDAAkD;oBAClD,iCAAiC,EACjC,KAAK,KAAK,QAAQ,IAAI,gCAAgC,EACtD,KAAK,KAAK,KAAK,IAAI,eAAe,EAClC,UAAU,IAAI,oBAAoB,EAClC,SAAS,IAAI,qBAAqB,EAClC,SAAS,CACV,EACD,KAAK,EAAE,KAAK,KACR,gBAAgB,CAAC,EAAE,CAAC,YAExB,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,iBAAiB,YAAG,QAAQ,GAAyB,GAC/E,GACU,GACG,GACR,CAClB,CAAC;AACJ,CAAC,CACF,CAAC;AACF,SAAS,CAAC,WAAW,GAAG,OAAO,CAAC;AAEhC,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE;IAC5C,MAAM,EAAE,WAAW;IACnB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,WAAW;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Geometry only. Every colour reads a `--number-stepper-*` component token
|
|
5
|
+
* whose value is decided by src/components/NumberStepper/number-stepper.recipe.css.
|
|
6
|
+
*
|
|
7
|
+
* `size` tracks Input's and Button's heights (`sm` 32px, `md` 36px, `lg`
|
|
8
|
+
* 40px) so a stepper sits on the same baseline as the fields beside it.
|
|
9
|
+
*/
|
|
10
|
+
declare const numberStepperVariants: (props?: ({
|
|
11
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
12
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
13
|
+
export type NumberStepperSize = NonNullable<VariantProps<typeof numberStepperVariants>["size"]>;
|
|
14
|
+
export type NumberStepperProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "size" | "min" | "max" | "step" | "type"> & VariantProps<typeof numberStepperVariants> & {
|
|
15
|
+
/** Controlled value. */
|
|
16
|
+
value?: number;
|
|
17
|
+
/** Initial value in uncontrolled mode. Defaults to `min`, or `0` without one. */
|
|
18
|
+
defaultValue?: number;
|
|
19
|
+
/** Called with the new, already-clamped value. */
|
|
20
|
+
onValueChange?: (value: number) => void;
|
|
21
|
+
/** Lowest reachable value. Defaults to `0` — quantities are the common case. */
|
|
22
|
+
min?: number;
|
|
23
|
+
/** Highest reachable value. Unbounded when omitted. */
|
|
24
|
+
max?: number;
|
|
25
|
+
/** Increment for the buttons and arrow keys. `PageUp`/`PageDown` move ten of them. */
|
|
26
|
+
step?: number;
|
|
27
|
+
/** Accessible name for the decrement button. */
|
|
28
|
+
decrementLabel?: string;
|
|
29
|
+
/** Accessible name for the increment button. */
|
|
30
|
+
incrementLabel?: string;
|
|
31
|
+
/** Extra classes for the inner `<input>`; `className` styles the surrounding group. */
|
|
32
|
+
inputClassName?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A whole-number field with decrement and increment buttons — a cart
|
|
36
|
+
* quantity, a guest count, a per-day capacity.
|
|
37
|
+
*
|
|
38
|
+
* Implements the WAI-ARIA spinbutton pattern on the `<input>` itself:
|
|
39
|
+
* `role="spinbutton"` with `aria-valuenow`/`min`/`max`, `ArrowUp`/`ArrowDown`
|
|
40
|
+
* step, `PageUp`/`PageDown` step by ten, and `Home`/`End` jump to the bounds
|
|
41
|
+
* that exist. The two buttons are pointer affordances and stay out of the tab
|
|
42
|
+
* order, so the field is one tab stop — the same reason RadioButtonGroup is.
|
|
43
|
+
*
|
|
44
|
+
* Typing is free-form while focused and committed on blur or `Enter`: the
|
|
45
|
+
* draft is parsed, rounded to the nearest `step` from `min`, and clamped.
|
|
46
|
+
* Anything unparseable reverts to the last committed value rather than
|
|
47
|
+
* reporting `NaN`. Every value `onValueChange` receives is in range.
|
|
48
|
+
*
|
|
49
|
+
* Controlled or uncontrolled through the `value`/`defaultValue`/
|
|
50
|
+
* `onValueChange` triad. `name` submits the committed value with a form, since
|
|
51
|
+
* the input is a real `<input>`. The ref and every unlisted prop land on that
|
|
52
|
+
* input — so `FormField`'s `id`/`aria-describedby` wiring reaches the control
|
|
53
|
+
* — while `className` styles the surrounding group.
|
|
54
|
+
*/
|
|
55
|
+
export declare const NumberStepper: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "defaultValue" | "onChange" | "step" | "size" | "type" | "value" | "max" | "min"> & VariantProps<(props?: ({
|
|
56
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
57
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
58
|
+
/** Controlled value. */
|
|
59
|
+
value?: number;
|
|
60
|
+
/** Initial value in uncontrolled mode. Defaults to `min`, or `0` without one. */
|
|
61
|
+
defaultValue?: number;
|
|
62
|
+
/** Called with the new, already-clamped value. */
|
|
63
|
+
onValueChange?: (value: number) => void;
|
|
64
|
+
/** Lowest reachable value. Defaults to `0` — quantities are the common case. */
|
|
65
|
+
min?: number;
|
|
66
|
+
/** Highest reachable value. Unbounded when omitted. */
|
|
67
|
+
max?: number;
|
|
68
|
+
/** Increment for the buttons and arrow keys. `PageUp`/`PageDown` move ten of them. */
|
|
69
|
+
step?: number;
|
|
70
|
+
/** Accessible name for the decrement button. */
|
|
71
|
+
decrementLabel?: string;
|
|
72
|
+
/** Accessible name for the increment button. */
|
|
73
|
+
incrementLabel?: string;
|
|
74
|
+
/** Extra classes for the inner `<input>`; `className` styles the surrounding group. */
|
|
75
|
+
inputClassName?: string;
|
|
76
|
+
} & React.RefAttributes<HTMLInputElement>>;
|
|
77
|
+
export { numberStepperVariants };
|
|
78
|
+
//# sourceMappingURL=NumberStepper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NumberStepper.d.ts","sourceRoot":"","sources":["../../../src/components/NumberStepper/NumberStepper.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,QAAA,MAAM,qBAAqB;;8EAe1B,CAAC;AAgBF,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEhG,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,OAAO,GAAG,cAAc,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CACjF,GACC,YAAY,CAAC,OAAO,qBAAqB,CAAC,GAAG;IAC3C,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAOJ;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,aAAa;;;IA9CtB,wBAAwB;YAChB,MAAM;IACd,iFAAiF;mBAClE,MAAM;IACrB,kDAAkD;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;IACvC,gFAAgF;UAC1E,MAAM;IACZ,uDAAuD;UACjD,MAAM;IACZ,sFAAsF;WAC/E,MAAM;IACb,gDAAgD;qBAC/B,MAAM;IACvB,gDAAgD;qBAC/B,MAAM;IACvB,uFAAuF;qBACtE,MAAM;0CAyJ1B,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
4
|
+
import { cn } from "@sixthshift/design-system/utils";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { Minus, Plus } from "lucide-react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
/**
|
|
9
|
+
* Geometry only. Every colour reads a `--number-stepper-*` component token
|
|
10
|
+
* whose value is decided by src/components/NumberStepper/number-stepper.recipe.css.
|
|
11
|
+
*
|
|
12
|
+
* `size` tracks Input's and Button's heights (`sm` 32px, `md` 36px, `lg`
|
|
13
|
+
* 40px) so a stepper sits on the same baseline as the fields beside it.
|
|
14
|
+
*/
|
|
15
|
+
const numberStepperVariants = cva(
|
|
16
|
+
// One literal, deliberately — see Button.tsx for why a `+` concatenation is a trap.
|
|
17
|
+
"number-stepper border-(color:--number-stepper-border) focus-within:ring-(color:--number-stepper-ring) inline-flex w-fit items-stretch overflow-hidden rounded-md border bg-(--number-stepper-bg) text-(--number-stepper-fg) shadow-xs transition-colors focus-within:ring-2 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50", {
|
|
18
|
+
variants: {
|
|
19
|
+
size: {
|
|
20
|
+
sm: "h-8 text-xs [&_svg]:size-3.5",
|
|
21
|
+
md: "h-9 text-sm [&_svg]:size-4",
|
|
22
|
+
lg: "h-10 text-sm [&_svg]:size-4",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
size: "md",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
/** Button width per size — square against the field's height. */
|
|
30
|
+
const buttonGeometry = {
|
|
31
|
+
sm: "w-8",
|
|
32
|
+
md: "w-9",
|
|
33
|
+
lg: "w-10",
|
|
34
|
+
};
|
|
35
|
+
/** Input width per size — room for three digits before it grows. */
|
|
36
|
+
const inputGeometry = {
|
|
37
|
+
sm: "w-10",
|
|
38
|
+
md: "w-12",
|
|
39
|
+
lg: "w-14",
|
|
40
|
+
};
|
|
41
|
+
function clamp(value, min, max) {
|
|
42
|
+
const floored = Math.max(min, value);
|
|
43
|
+
return max === undefined ? floored : Math.min(max, floored);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A whole-number field with decrement and increment buttons — a cart
|
|
47
|
+
* quantity, a guest count, a per-day capacity.
|
|
48
|
+
*
|
|
49
|
+
* Implements the WAI-ARIA spinbutton pattern on the `<input>` itself:
|
|
50
|
+
* `role="spinbutton"` with `aria-valuenow`/`min`/`max`, `ArrowUp`/`ArrowDown`
|
|
51
|
+
* step, `PageUp`/`PageDown` step by ten, and `Home`/`End` jump to the bounds
|
|
52
|
+
* that exist. The two buttons are pointer affordances and stay out of the tab
|
|
53
|
+
* order, so the field is one tab stop — the same reason RadioButtonGroup is.
|
|
54
|
+
*
|
|
55
|
+
* Typing is free-form while focused and committed on blur or `Enter`: the
|
|
56
|
+
* draft is parsed, rounded to the nearest `step` from `min`, and clamped.
|
|
57
|
+
* Anything unparseable reverts to the last committed value rather than
|
|
58
|
+
* reporting `NaN`. Every value `onValueChange` receives is in range.
|
|
59
|
+
*
|
|
60
|
+
* Controlled or uncontrolled through the `value`/`defaultValue`/
|
|
61
|
+
* `onValueChange` triad. `name` submits the committed value with a form, since
|
|
62
|
+
* the input is a real `<input>`. The ref and every unlisted prop land on that
|
|
63
|
+
* input — so `FormField`'s `id`/`aria-describedby` wiring reaches the control
|
|
64
|
+
* — while `className` styles the surrounding group.
|
|
65
|
+
*/
|
|
66
|
+
export const NumberStepper = React.forwardRef(({ className, inputClassName, size = "md", value: controlledValue, defaultValue, onValueChange, min = 0, max, step = 1, disabled = false, readOnly = false, decrementLabel = "Decrease", incrementLabel = "Increase", onKeyDown, onBlur, onFocus, ...props }, ref) => {
|
|
67
|
+
const [value, setValue] = useControllableState({
|
|
68
|
+
value: controlledValue,
|
|
69
|
+
defaultValue: clamp(defaultValue ?? min, min, max),
|
|
70
|
+
onChange: onValueChange,
|
|
71
|
+
});
|
|
72
|
+
// The text being typed, or `null` when the field shows the committed value.
|
|
73
|
+
const [draft, setDraft] = React.useState(null);
|
|
74
|
+
const commit = (next) => {
|
|
75
|
+
const snapped = min + Math.round((next - min) / step) * step;
|
|
76
|
+
const clamped = clamp(snapped, min, max);
|
|
77
|
+
if (clamped !== value)
|
|
78
|
+
setValue(clamped);
|
|
79
|
+
};
|
|
80
|
+
const commitDraft = () => {
|
|
81
|
+
if (draft === null)
|
|
82
|
+
return;
|
|
83
|
+
const parsed = Number.parseFloat(draft);
|
|
84
|
+
setDraft(null);
|
|
85
|
+
if (Number.isFinite(parsed))
|
|
86
|
+
commit(parsed);
|
|
87
|
+
};
|
|
88
|
+
const interactive = !disabled && !readOnly;
|
|
89
|
+
const atMin = value <= min;
|
|
90
|
+
const atMax = max !== undefined && value >= max;
|
|
91
|
+
const handleKeyDown = (event) => {
|
|
92
|
+
onKeyDown?.(event);
|
|
93
|
+
if (event.defaultPrevented || !interactive)
|
|
94
|
+
return;
|
|
95
|
+
const moves = {
|
|
96
|
+
ArrowUp: () => value + step,
|
|
97
|
+
ArrowDown: () => value - step,
|
|
98
|
+
PageUp: () => value + step * 10,
|
|
99
|
+
PageDown: () => value - step * 10,
|
|
100
|
+
Home: () => min,
|
|
101
|
+
End: max === undefined ? undefined : () => max,
|
|
102
|
+
};
|
|
103
|
+
const move = moves[event.key];
|
|
104
|
+
if (event.key === "Enter") {
|
|
105
|
+
commitDraft();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!move)
|
|
109
|
+
return;
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
setDraft(null);
|
|
112
|
+
commit(move());
|
|
113
|
+
};
|
|
114
|
+
const handleBlur = (event) => {
|
|
115
|
+
commitDraft();
|
|
116
|
+
onBlur?.(event);
|
|
117
|
+
};
|
|
118
|
+
const stepBy = (direction) => {
|
|
119
|
+
setDraft(null);
|
|
120
|
+
commit(value + step * direction);
|
|
121
|
+
};
|
|
122
|
+
const buttonClass = cn("number-stepper-button flex shrink-0 cursor-pointer items-center justify-center text-(--number-stepper-button-fg) transition-colors enabled:active:bg-(--number-stepper-button-bg-pressed) enabled:hover:bg-(--number-stepper-button-bg-hovered) disabled:cursor-not-allowed disabled:text-(--number-stepper-button-fg-disabled)", buttonGeometry[size ?? "md"]);
|
|
123
|
+
return (
|
|
124
|
+
// biome-ignore lint/a11y/useSemanticElements: a <fieldset> brings a border, min-width and legend semantics this inline control does not want; role="group" only ties the buttons to the field
|
|
125
|
+
_jsxs("div", { role: "group", "data-disabled": disabled, className: numberStepperVariants({ size, className }), children: [_jsx("button", { type: "button", tabIndex: -1, "aria-label": decrementLabel, disabled: !interactive || atMin, onClick: () => stepBy(-1), className: buttonClass, children: _jsx(Minus, { "aria-hidden": "true" }) }), _jsx("input", { ref: ref, type: "text", inputMode: "numeric", role: "spinbutton", autoComplete: "off", "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, disabled: disabled, readOnly: readOnly, value: draft ?? String(value), onChange: (event) => setDraft(event.target.value), onKeyDown: handleKeyDown, onBlur: handleBlur, onFocus: onFocus, className: cn("border-(color:--number-stepper-divider-border) min-w-0 border-x bg-transparent text-center text-[length:inherit] tabular-nums outline-hidden disabled:cursor-not-allowed", inputGeometry[size ?? "md"], inputClassName), ...props }), _jsx("button", { type: "button", tabIndex: -1, "aria-label": incrementLabel, disabled: !interactive || atMax, onClick: () => stepBy(1), className: buttonClass, children: _jsx(Plus, { "aria-hidden": "true" }) })] }));
|
|
126
|
+
});
|
|
127
|
+
NumberStepper.displayName = "NumberStepper";
|
|
128
|
+
export { numberStepperVariants };
|
|
129
|
+
//# sourceMappingURL=NumberStepper.js.map
|