@instruments/materia-ui 0.1.0 → 0.3.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/agent-docs.md +4 -3
- package/dist/components/animated-number/number-digit.d.ts +5 -3
- package/dist/components/animated-number/number-digit.js +39 -16
- package/dist/components/animated-number/number-section.d.ts +7 -5
- package/dist/components/animated-number/number-section.js +81 -65
- package/dist/components/animated-number/number-symbol.js +2 -0
- package/dist/components/animated-number/target-widths.d.ts +7 -0
- package/dist/components/animated-number/target-widths.js +13 -1
- package/dist/components/button/button-variants.js +53 -8
- package/dist/components/button-group/button-group-root.js +8 -6
- package/dist/components/calendar/calendar-root.js +39 -13
- package/dist/components/chart/chart-tooltip.js +7 -5
- package/dist/components/empty/empty-media.js +5 -5
- package/dist/components/input-group/input-group-textarea.d.ts +3 -2
- package/dist/components/input-group/input-group-textarea.js +8 -4
- package/dist/components/pill/pill-variants.js +21 -10
- package/dist/components/progress/progress-root.d.ts +7 -5
- package/dist/components/progress/progress-root.js +26 -5
- package/dist/components/radio-group/radio-group-root.d.ts +7 -3
- package/dist/components/radio-group/radio-group-root.js +14 -4
- package/dist/components/segmented-control/segmented-control-indicator.js +1 -1
- package/dist/components/segmented-control/segmented-control-item.js +1 -1
- package/dist/components/segmented-control/segmented-control-root.js +1 -1
- package/dist/components/segmented-control/segmented-control-variants.js +1 -1
- package/dist/components/stepper/stepper-item.js +6 -2
- package/dist/components/stepper/stepper-variants.d.ts +1 -0
- package/dist/components/stepper/stepper-variants.js +11 -1
- package/dist/components/tab-navigation/tab-navigation-link.js +1 -1
- package/dist/components/tabs/tabs-indicator.js +1 -1
- package/dist/components/tabs/tabs-trigger.js +1 -1
- package/dist/components/tabs/tabs-variants.js +1 -1
- package/dist/components/textarea/textarea-root.d.ts +1 -1
- package/dist/components/textarea/textarea-root.js +6 -9
- package/dist/components/textarea/use-textarea-rows.d.ts +22 -0
- package/dist/components/textarea/use-textarea-rows.js +43 -0
- package/dist/compositions/data-grid-pagination/data-grid-pagination-root.d.ts +8 -6
- package/dist/compositions/data-grid-pagination/data-grid-pagination-root.js +11 -20
- package/dist/compositions/product-card/product-card-color-count.js +26 -26
- package/dist/compositions/product-card/product-card-compact-arm.js +2 -2
- package/dist/compositions/product-card/product-card-content-types.d.ts +5 -0
- package/dist/compositions/product-card/product-card-full-arm.js +2 -2
- package/dist/compositions/product-card/product-card-provider-types.d.ts +10 -0
- package/dist/compositions/product-card/product-card-provider.js +1 -1
- package/dist/compositions/product-card/product-card-swatch-selector.js +4 -3
- package/dist/data/docs-manifest.d.ts +1 -1
- package/dist/data/docs-manifest.js +29 -74
- package/dist/styles/index.css +17 -6
- package/dist/{components/segmented/segmented-variants.d.ts → tokens/segmented.d.ts} +12 -1
- package/dist/{components/segmented/segmented-variants.js → tokens/segmented.js} +11 -0
- package/lint/tokens.mjs +1 -0
- package/package.json +5 -5
- package/dist/components/segmented/segmented-root.d.ts +0 -12
- package/dist/components/segmented/segmented-root.js +0 -12
- package/dist/components/segmented.d.ts +0 -1
- package/dist/components/segmented.js +0 -1
|
@@ -13,9 +13,16 @@ interface DataGridPaginationProps {
|
|
|
13
13
|
info?: string;
|
|
14
14
|
/** Placeholder rendered in place of the range summary while loading. */
|
|
15
15
|
infoSkeleton?: ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Collapse the page buttons into groups of `moreLimit`, with ellipsis
|
|
18
|
+
* buttons that jump to the next or previous group. Off shows a button for
|
|
19
|
+
* every page.
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
16
22
|
more?: boolean;
|
|
17
23
|
/**
|
|
18
|
-
* Number of page buttons shown before collapsing to an ellipsis.
|
|
24
|
+
* Number of page buttons shown before collapsing to an ellipsis. Needs
|
|
25
|
+
* `more`.
|
|
19
26
|
* @default 5
|
|
20
27
|
*/
|
|
21
28
|
moreLimit?: number;
|
|
@@ -39,11 +46,6 @@ interface DataGridPaginationProps {
|
|
|
39
46
|
* @default [5, 10, 25, 50, 100]
|
|
40
47
|
*/
|
|
41
48
|
sizes?: number[];
|
|
42
|
-
/** @default "per page" */
|
|
43
|
-
sizesDescription?: string;
|
|
44
|
-
sizesInfo?: string;
|
|
45
|
-
/** @default "Show" */
|
|
46
|
-
sizesLabel?: string;
|
|
47
49
|
/** Placeholder rendered in place of the rows-per-page select while loading. */
|
|
48
50
|
sizesSkeleton?: ReactNode;
|
|
49
51
|
}
|
|
@@ -6,28 +6,17 @@ import { Skeleton } from "@instruments/materia-ui/components/skeleton";
|
|
|
6
6
|
import { useDataGrid } from "@instruments/materia-ui/compositions/data-grid";
|
|
7
7
|
import { cn } from "@instruments/materia-ui/utils/cn";
|
|
8
8
|
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
9
|
+
import { useId } from "react";
|
|
9
10
|
const defaultProps = {
|
|
10
|
-
/** Ellipsis text */
|
|
11
11
|
ellipsisText: "...",
|
|
12
12
|
info: "{from} - {to} of {count}",
|
|
13
|
-
/** Info skeleton */
|
|
14
13
|
infoSkeleton: _jsx(Skeleton, { className: "h-8 w-60" }),
|
|
15
|
-
more:
|
|
16
|
-
/** More limit */
|
|
14
|
+
more: true,
|
|
17
15
|
moreLimit: 5,
|
|
18
|
-
/** Next page label */
|
|
19
16
|
nextPageLabel: "Go to next page",
|
|
20
|
-
/** Previous page label */
|
|
21
17
|
previousPageLabel: "Go to previous page",
|
|
22
|
-
/** Rows per page label */
|
|
23
18
|
rowsPerPageLabel: "Rows per page",
|
|
24
|
-
/** Component size */
|
|
25
19
|
sizes: [5, 10, 25, 50, 100],
|
|
26
|
-
/** Component size */
|
|
27
|
-
sizesDescription: "per page",
|
|
28
|
-
/** Component size */
|
|
29
|
-
sizesLabel: "Show",
|
|
30
|
-
/** Component size */
|
|
31
20
|
sizesSkeleton: _jsx(Skeleton, { className: "h-8 w-44" }),
|
|
32
21
|
};
|
|
33
22
|
const BTN_BASE_CLASSES = "size-7 p-0 text-paragraph-sm";
|
|
@@ -41,20 +30,22 @@ const formatPaginationInfo = (template, from, to, count) => template !== undefin
|
|
|
41
30
|
: `${from} - ${to} of ${count}`;
|
|
42
31
|
const RowsPerPageSelect = ({ rowsPerPageLabel, sizes, table, }) => {
|
|
43
32
|
const { pageSize } = table.state.pagination;
|
|
44
|
-
|
|
33
|
+
const labelId = useId();
|
|
34
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-paragraph-sm", id: labelId, children: rowsPerPageLabel }), _jsxs(Select, { onValueChange: (value) => {
|
|
45
35
|
const newPageSize = Number(value);
|
|
46
36
|
table.setPageSize(newPageSize);
|
|
47
|
-
}, value: `${pageSize}`, children: [_jsx(SelectTrigger, { className: "w-fit", size: "sm", children: _jsx(SelectValue, { placeholder: `${pageSize}` }) }), _jsx(SelectContent, { className: "min-w-12.5", side: "top", children: sizes?.map((size) => (_jsx(SelectItem, { value: `${size}`, children: size }, size))) })] })] }));
|
|
37
|
+
}, value: `${pageSize}`, children: [_jsx(SelectTrigger, { "aria-labelledby": labelId, className: "w-fit", size: "sm", children: _jsx(SelectValue, { placeholder: `${pageSize}` }) }), _jsx(SelectContent, { className: "min-w-12.5", side: "top", children: sizes?.map((size) => (_jsx(SelectItem, { value: `${size}`, children: size }, size))) })] })] }));
|
|
48
38
|
};
|
|
49
|
-
const PaginationNav = ({ ellipsisText, moreLimit, nextPageLabel, previousPageLabel, table, }) => {
|
|
39
|
+
const PaginationNav = ({ ellipsisText, more, moreLimit, nextPageLabel, previousPageLabel, table, }) => {
|
|
50
40
|
const { pageIndex } = table.state.pagination;
|
|
51
41
|
const pageCount = table.getPageCount();
|
|
52
42
|
if (pageCount <= 1) {
|
|
53
43
|
return null;
|
|
54
44
|
}
|
|
55
|
-
//
|
|
56
|
-
const
|
|
57
|
-
const
|
|
45
|
+
// Without `more`, one group holds every page and no ellipsis renders.
|
|
46
|
+
const groupSize = more ? moreLimit : pageCount;
|
|
47
|
+
const currentGroupStart = Math.floor(pageIndex / groupSize) * groupSize;
|
|
48
|
+
const currentGroupEnd = Math.min(currentGroupStart + groupSize, pageCount);
|
|
58
49
|
// Page indices for the current group
|
|
59
50
|
const pageIndices = Array.from({ length: Math.max(0, currentGroupEnd - currentGroupStart) }, (_, idx) => currentGroupStart + idx);
|
|
60
51
|
return (_jsxs(Flex, { align: "center", className: "order-1 space-x-1 sm:order-2", children: [_jsx(Button, { "aria-label": previousPageLabel, emphasis: "ghost", className: BTN_ARROW_CLASSES, disabled: !table.getCanPreviousPage(), icon: ChevronLeftIcon, onClick: () => {
|
|
@@ -91,6 +82,6 @@ const DataGridPagination = (props) => {
|
|
|
91
82
|
const from = pageIndex * pageSize + 1;
|
|
92
83
|
const to = Math.min((pageIndex + 1) * pageSize, recordCount);
|
|
93
84
|
const paginationInfo = formatPaginationInfo(mergedProps.info, from, to, recordCount);
|
|
94
|
-
return (_jsxs(Flex, { align: "center", className: cn("grow gap-2.5 py-2.5 sm:flex-row sm:py-0", mergedProps?.className), "data-component": "data-grid-pagination", "data-slot": "data-grid-pagination", direction: "column", justify: "space-between", wrap: "wrap", children: [_jsx(Flex, { align: "center", className: "order-2 space-x-2.5 pb-2.5 sm:order-1 sm:pb-0", wrap: "wrap", children: loading === true ? (mergedProps?.sizesSkeleton) : (_jsx(RowsPerPageSelect, { rowsPerPageLabel: mergedProps.rowsPerPageLabel, sizes: mergedProps.sizes, table: table })) }), _jsx(Flex, { align: "center", className: "order-1 gap-2.5 pt-2.5 sm:order-2 sm:flex-row sm:justify-end sm:pt-0", direction: "column", justify: "center", children: loading === true ? (mergedProps?.infoSkeleton) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-paragraph-sm order-2 text-nowrap sm:order-1", children: paginationInfo }), _jsx(PaginationNav, { ellipsisText: mergedProps.ellipsisText, moreLimit: mergedProps?.moreLimit ?? 5, nextPageLabel: mergedProps.nextPageLabel, previousPageLabel: mergedProps.previousPageLabel, table: table })] })) })] }));
|
|
85
|
+
return (_jsxs(Flex, { align: "center", className: cn("grow gap-2.5 py-2.5 sm:flex-row sm:py-0", mergedProps?.className), "data-component": "data-grid-pagination", "data-slot": "data-grid-pagination", direction: "column", justify: "space-between", wrap: "wrap", children: [_jsx(Flex, { align: "center", className: "order-2 space-x-2.5 pb-2.5 sm:order-1 sm:pb-0", wrap: "wrap", children: loading === true ? (mergedProps?.sizesSkeleton) : (_jsx(RowsPerPageSelect, { rowsPerPageLabel: mergedProps.rowsPerPageLabel, sizes: mergedProps.sizes, table: table })) }), _jsx(Flex, { align: "center", className: "order-1 gap-2.5 pt-2.5 sm:order-2 sm:flex-row sm:justify-end sm:pt-0", direction: "column", justify: "center", children: loading === true ? (mergedProps?.infoSkeleton) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-paragraph-sm order-2 text-nowrap sm:order-1", children: paginationInfo }), _jsx(PaginationNav, { ellipsisText: mergedProps.ellipsisText, more: mergedProps.more ?? true, moreLimit: mergedProps?.moreLimit ?? 5, nextPageLabel: mergedProps.nextPageLabel, previousPageLabel: mergedProps.previousPageLabel, table: table })] })) })] }));
|
|
95
86
|
};
|
|
96
87
|
export { DataGridPagination };
|
|
@@ -15,25 +15,24 @@ import { useProductCard } from "./product-card-context.js";
|
|
|
15
15
|
* instantly where a count cannot.
|
|
16
16
|
*
|
|
17
17
|
* The design draws 16px dots on a 26px pitch, the selected one wearing a ring,
|
|
18
|
-
* then `+N` for the remainder; what renders is the nearest token step
|
|
19
|
-
*
|
|
18
|
+
* then `+N` for the remainder; what renders is the nearest token step. The
|
|
19
|
+
* card's `maxSwatches` caps the row (five by default) and clicking `+N` opens
|
|
20
|
+
* the full set.
|
|
20
21
|
*/
|
|
21
22
|
/**
|
|
22
|
-
* Circles
|
|
23
|
+
* Circles a card under 240px wide can show beside the overflow label.
|
|
23
24
|
*
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* Below 240px one dot is dropped and counted into `+N` instead: a 180px card
|
|
30
|
-
* offers 144px and the label would clip off the right edge.
|
|
25
|
+
* The kit's smallest chip step is `xs` (20px), which on the row's 10px gap
|
|
26
|
+
* gives a 30px pitch. Five dots plus the label come to ~170px, which fits the
|
|
27
|
+
* full card's 204px content column, but a 180px card offers 144px and the label
|
|
28
|
+
* would clip off the right edge. So below 240px the row stops at four and
|
|
29
|
+
* counts the rest into `+N`.
|
|
31
30
|
*/
|
|
32
|
-
const
|
|
31
|
+
const NARROW_SWATCHES = 4;
|
|
33
32
|
/** The popover still caps its grid, so a 200-colourway family stays a panel. */
|
|
34
33
|
const MAX_POPOVER_SWATCHES = 25;
|
|
35
34
|
export const ProductCardColorCount = () => {
|
|
36
|
-
const {
|
|
35
|
+
const { maxSwatches, onVariantChange, product, renderImage, selectedVariantId, } = useProductCard();
|
|
37
36
|
const variantCount = product.variantCount ?? 1;
|
|
38
37
|
const variants = product.variants ?? [product];
|
|
39
38
|
// One colourway is not a choice, so it is not a control. Same rule the primary
|
|
@@ -41,22 +40,23 @@ export const ProductCardColorCount = () => {
|
|
|
41
40
|
if (variantCount <= 1) {
|
|
42
41
|
return null;
|
|
43
42
|
}
|
|
44
|
-
const inlineVariants = variants.slice(0,
|
|
43
|
+
const inlineVariants = variants.slice(0, Math.max(0, maxSwatches));
|
|
45
44
|
// The overflow counts against the GRAPH's total, not the array we happen to
|
|
46
45
|
// hold - a page of variants is not the family.
|
|
47
46
|
const inlineOverflow = variantCount - inlineVariants.length;
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
const
|
|
51
|
-
const mediumOverflow = inlineOverflow + (
|
|
52
|
-
// WHICH
|
|
53
|
-
// SELECTED colourway
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
// Dots past NARROW_SWATCHES are hidden below 240px, so the label there must
|
|
48
|
+
// count them too.
|
|
49
|
+
const narrowCount = Math.min(inlineVariants.length, NARROW_SWATCHES);
|
|
50
|
+
const mediumOverflow = inlineOverflow + (inlineVariants.length - narrowCount);
|
|
51
|
+
// WHICH dots drop below 240px: the ones past the cut, unless one of them is
|
|
52
|
+
// the SELECTED colourway. Hiding that leaves a narrow card with no ring on
|
|
53
|
+
// any dot, so the card reads as if nothing is chosen while its photograph
|
|
54
|
+
// shows a colourway the row does not contain. The selected dot keeps its
|
|
55
|
+
// place and the last dot before the cut drops instead.
|
|
56
|
+
const selectedIndex = inlineVariants.findIndex((variant) => variant.id === selectedVariantId);
|
|
57
|
+
const isNarrowHidden = (index) => index < narrowCount
|
|
58
|
+
? selectedIndex >= narrowCount && index === narrowCount - 1
|
|
59
|
+
: index !== selectedIndex;
|
|
60
60
|
const hasPopoverOverflow = variantCount > MAX_POPOVER_SWATCHES;
|
|
61
61
|
const popoverVisibleCount = hasPopoverOverflow
|
|
62
62
|
? MAX_POPOVER_SWATCHES - 1
|
|
@@ -71,7 +71,7 @@ export const ProductCardColorCount = () => {
|
|
|
71
71
|
sizes: size,
|
|
72
72
|
src: variant.images[0] ?? "",
|
|
73
73
|
})] }));
|
|
74
|
-
return (_jsxs(Box, { className: "flex items-center gap-2.5", children: [_jsx(SwatchSelector, { className: "flex items-center gap-2.5", onValueChange: onVariantChange, size: "xs", value: selectedVariantId, children: inlineVariants.map((variant, index) => (_jsx(Swatch, { className:
|
|
74
|
+
return (_jsxs(Box, { className: "flex items-center gap-2.5", children: [_jsx(SwatchSelector, { className: "flex items-center gap-2.5", onValueChange: onVariantChange, size: "xs", value: selectedVariantId, children: inlineVariants.map((variant, index) => (_jsx(Swatch, { className: isNarrowHidden(index)
|
|
75
75
|
? "hidden @[240px]/product-card:inline-flex"
|
|
76
76
|
: undefined, unavailable: variant.available === false, value: variant.id, children: swatchImage(variant, "20px") }, variant.id))) }), mediumOverflow > 0 ? (_jsxs(Popover, { children: [_jsxs(PopoverTrigger, { render: _jsx("button", { "aria-label": `Show all ${variantCount} ${product.variantLabel ?? "colours"}`, className: cn("text-muted-foreground hover:text-foreground text-caption cursor-pointer tabular-nums",
|
|
77
77
|
// Exactly five colourways leaves nothing behind once the fifth
|
|
@@ -14,8 +14,8 @@ import { ProductCardStatusBadge } from "./product-card-status-badge.js";
|
|
|
14
14
|
import { ProductCardTools } from "./product-card-tools.js";
|
|
15
15
|
/** Compact variant: below 180px, or always in compact mode. */
|
|
16
16
|
export const ProductCardCompactArm = ({ compactFooterControl, overrideImageUrl, priority, similarityScore, toolsActive, }) => {
|
|
17
|
-
const { action, mode, onQuickView, statusBadge } = useProductCard();
|
|
17
|
+
const { action, mode, onQuickView, showSwatches, statusBadge } = useProductCard();
|
|
18
18
|
const isCompactMode = mode === "compact";
|
|
19
19
|
const hasCompactFooterControl = compactFooterControl !== null && compactFooterControl !== undefined;
|
|
20
|
-
return (_jsxs(Box, { className: cn("flex-col", isCompactMode ? "flex" : "flex h-full @[180px]/product-card:hidden"), children: [_jsxs(Box, { className: "relative w-full overflow-hidden rounded-3xl shadow-sm", children: [_jsx(ProductCardImageLink, { imageClassName: isCompactMode ? "!p-0" : undefined, overrideImageUrl: overrideImageUrl, priority: priority, similarityScore: similarityScore }), _jsx(ProductCardTools, { active: toolsActive }), _jsx(ProductCardQuickView, { forceVisible: true, onQuickView: onQuickView })] }), _jsxs(VStack, { className: "min-h-0 flex-1 gap-2.5 px-1.5 py-2", children: [_jsxs(VStack, { className: "gap-1 px-0.5", children: [_jsxs(Box, { className: "flex items-start justify-between gap-2", children: [_jsx(VStack, { className: "min-w-0 flex-1 gap-0.5", children: _jsx(ProductCardCaption, { scale: "compact" }) }), statusBadge?.icon ? (_jsx(ProductCardStatusBadge, { ...statusBadge })) : null] }), _jsx(ProductCardMeta, {}), action ? _jsx(ProductCardAction, { ...action }) : null, mode !== "single" && (_jsx(Box, { className: "min-h-5 pt-2", children: _jsx(ProductCardColorCount, {}) }))] }), hasCompactFooterControl ? (_jsx(Box, { className: "mt-auto flex justify-end", children: compactFooterControl })) : null] })] }));
|
|
20
|
+
return (_jsxs(Box, { className: cn("flex-col", isCompactMode ? "flex" : "flex h-full @[180px]/product-card:hidden"), children: [_jsxs(Box, { className: "relative w-full overflow-hidden rounded-3xl shadow-sm", children: [_jsx(ProductCardImageLink, { imageClassName: isCompactMode ? "!p-0" : undefined, overrideImageUrl: overrideImageUrl, priority: priority, similarityScore: similarityScore }), _jsx(ProductCardTools, { active: toolsActive }), _jsx(ProductCardQuickView, { forceVisible: true, onQuickView: onQuickView })] }), _jsxs(VStack, { className: "min-h-0 flex-1 gap-2.5 px-1.5 py-2", children: [_jsxs(VStack, { className: "gap-1 px-0.5", children: [_jsxs(Box, { className: "flex items-start justify-between gap-2", children: [_jsx(VStack, { className: "min-w-0 flex-1 gap-0.5", children: _jsx(ProductCardCaption, { scale: "compact" }) }), statusBadge?.icon ? (_jsx(ProductCardStatusBadge, { ...statusBadge })) : null] }), _jsx(ProductCardMeta, {}), action ? _jsx(ProductCardAction, { ...action }) : null, showSwatches && mode !== "single" && (_jsx(Box, { className: "min-h-5 pt-2", children: _jsx(ProductCardColorCount, {}) }))] }), hasCompactFooterControl ? (_jsx(Box, { className: "mt-auto flex justify-end", children: compactFooterControl })) : null] })] }));
|
|
21
21
|
};
|
|
@@ -28,6 +28,11 @@ interface ProductCardDisplayOptions {
|
|
|
28
28
|
* reserved height.
|
|
29
29
|
*/
|
|
30
30
|
nameLines?: 1 | 2 | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Show the row of colourway swatches. Single-colour products and `single`
|
|
33
|
+
* mode never show it.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
31
36
|
swatches?: boolean | undefined;
|
|
32
37
|
}
|
|
33
38
|
/**
|
|
@@ -31,6 +31,6 @@ import { ProductCardTools } from "./product-card-tools.js";
|
|
|
31
31
|
* fetched - and the popover mounts nothing until it opens.
|
|
32
32
|
*/
|
|
33
33
|
export const ProductCardFullArm = ({ footerControl, overrideImageUrl, priority, similarityScore, toolsActive, }) => {
|
|
34
|
-
const { action, active, mode, onQuickView, statusBadge } = useProductCard();
|
|
35
|
-
return (_jsxs(VStack, { className: cn("bg-card duration-normal h-full overflow-hidden rounded-3xl transition-shadow ease-in-out @[240px]/product-card:rounded-4xl", mode === "compact" ? "hidden" : "hidden @[180px]/product-card:flex", active ? "shadow-lg" : "shadow-sm hover:shadow-md"), gap: "none", children: [_jsxs(Box, { className: "relative w-full", children: [_jsx(ProductCardImageLink, { overrideImageUrl: overrideImageUrl, priority: priority, similarityScore: similarityScore }), statusBadge && !statusBadge.icon ? (_jsx(ProductCardStatusBadge, { ...statusBadge })) : null, _jsx(ProductCardTools, { active: toolsActive }), _jsx(ProductCardQuickView, { onQuickView: onQuickView })] }), _jsx(VStack, { className: "flex-1 pt-2 pb-4", gap: "none", children: _jsxs(VStack, { className: "min-h-0 flex-1 gap-2.5 px-4 @[240px]/product-card:gap-4", gap: "none", children: [_jsxs(VStack, { className: "gap-1 px-0.5", gap: "none", children: [_jsxs(Box, { className: "flex items-start justify-between gap-2", children: [_jsx(VStack, { className: "min-w-0 flex-1", gap: "none", children: _jsx(ProductCardCaption, {}) }), statusBadge?.icon ? (_jsx(ProductCardStatusBadge, { ...statusBadge })) : null] }), _jsx(ProductCardMeta, {}), action ? _jsx(ProductCardAction, { ...action }) : null, mode !== "single" && (_jsx(Box, { className: "pt-2", children: _jsx(Box, { className: "min-h-5", children: _jsx(ProductCardColorCount, {}) }) }))] }), footerControl !== null && footerControl !== undefined ? (_jsx(HStack, { className: "mt-auto", children: footerControl })) : null] }) })] }));
|
|
34
|
+
const { action, active, mode, onQuickView, showSwatches, statusBadge } = useProductCard();
|
|
35
|
+
return (_jsxs(VStack, { className: cn("bg-card duration-normal h-full overflow-hidden rounded-3xl transition-shadow ease-in-out @[240px]/product-card:rounded-4xl", mode === "compact" ? "hidden" : "hidden @[180px]/product-card:flex", active ? "shadow-lg" : "shadow-sm hover:shadow-md"), gap: "none", children: [_jsxs(Box, { className: "relative w-full", children: [_jsx(ProductCardImageLink, { overrideImageUrl: overrideImageUrl, priority: priority, similarityScore: similarityScore }), statusBadge && !statusBadge.icon ? (_jsx(ProductCardStatusBadge, { ...statusBadge })) : null, _jsx(ProductCardTools, { active: toolsActive }), _jsx(ProductCardQuickView, { onQuickView: onQuickView })] }), _jsx(VStack, { className: "flex-1 pt-2 pb-4", gap: "none", children: _jsxs(VStack, { className: "min-h-0 flex-1 gap-2.5 px-4 @[240px]/product-card:gap-4", gap: "none", children: [_jsxs(VStack, { className: "gap-1 px-0.5", gap: "none", children: [_jsxs(Box, { className: "flex items-start justify-between gap-2", children: [_jsx(VStack, { className: "min-w-0 flex-1", gap: "none", children: _jsx(ProductCardCaption, {}) }), statusBadge?.icon ? (_jsx(ProductCardStatusBadge, { ...statusBadge })) : null] }), _jsx(ProductCardMeta, {}), action ? _jsx(ProductCardAction, { ...action }) : null, showSwatches && mode !== "single" && (_jsx(Box, { className: "pt-2", children: _jsx(Box, { className: "min-h-5", children: _jsx(ProductCardColorCount, {}) }) }))] }), footerControl !== null && footerControl !== undefined ? (_jsx(HStack, { className: "mt-auto", children: footerControl })) : null] }) })] }));
|
|
36
36
|
};
|
|
@@ -134,6 +134,11 @@ interface ProductCardProviderProps {
|
|
|
134
134
|
* dynamic routes).
|
|
135
135
|
*/
|
|
136
136
|
linkPrefetch?: boolean | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* How many colourway swatches the row shows before the rest go behind `+N`.
|
|
139
|
+
* Cards under 240px wide show at most four.
|
|
140
|
+
* @default 5
|
|
141
|
+
*/
|
|
137
142
|
maxSwatches?: number | undefined;
|
|
138
143
|
/**
|
|
139
144
|
* Mode determines the card behavior:
|
|
@@ -253,6 +258,11 @@ type ProductCardProps = ComponentProps<"div"> & Pick<ProductCardProviderProps, "
|
|
|
253
258
|
showAddToCart?: boolean | undefined;
|
|
254
259
|
showProductBrand?: boolean | undefined;
|
|
255
260
|
showProductName?: boolean | undefined;
|
|
261
|
+
/**
|
|
262
|
+
* Show the row of colourway swatches. Single-colour products and `single`
|
|
263
|
+
* mode never show it.
|
|
264
|
+
* @default true
|
|
265
|
+
*/
|
|
256
266
|
showSwatches?: boolean | undefined;
|
|
257
267
|
similarityScore?: number | undefined;
|
|
258
268
|
};
|
|
@@ -66,7 +66,7 @@ const resolveDisplay = (display, hasRenderAddToCart) => ({
|
|
|
66
66
|
showAddToCart: (display?.addToCart ?? true) && hasRenderAddToCart,
|
|
67
67
|
showProductBrand: display?.brand ?? true,
|
|
68
68
|
showProductName: display?.name ?? true,
|
|
69
|
-
showSwatches: display?.swatches ??
|
|
69
|
+
showSwatches: display?.swatches ?? true,
|
|
70
70
|
});
|
|
71
71
|
/**
|
|
72
72
|
* Which image the card is showing. Card-local because it is a way of LOOKING
|
|
@@ -9,9 +9,10 @@ export const ProductCardSwatchSelector = ({ className, ...props }) => {
|
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
11
11
|
const variants = product.variants ?? [product];
|
|
12
|
-
const visibleVariants = variants.slice(0, maxSwatches);
|
|
13
|
-
//
|
|
14
|
-
|
|
12
|
+
const visibleVariants = variants.slice(0, Math.max(0, maxSwatches));
|
|
13
|
+
// Count the overflow against the family's total (variantCount), not the
|
|
14
|
+
// loaded variants, and against the dots actually shown, not the cap.
|
|
15
|
+
const overflowCount = (product.variantCount ?? variants.length) - visibleVariants.length;
|
|
15
16
|
return (_jsxs(SwatchSelector, { className: cn("px-6", className), onValueChange: onVariantChange, value: selectedVariantId, ...props, children: [visibleVariants.map((variant) => (_jsx(Swatch, { unavailable: variant.available === false, value: variant.id, children: renderImage({
|
|
16
17
|
alt: `${variant.name} variant`,
|
|
17
18
|
className: "object-cover",
|
|
@@ -72,5 +72,5 @@ export interface DocsManifest {
|
|
|
72
72
|
storybook: DocsStorybookInventory;
|
|
73
73
|
}
|
|
74
74
|
/** The slug of every documented component and composition. */
|
|
75
|
-
export type DocsUnitSlug = "accordion" | "alert" | "alert-dialog" | "anchor" | "animated-number" | "aspect-ratio" | "attribute-card" | "avatar" | "badge" | "border-beam" | "box" | "breadcrumb" | "button" | "button-group" | "calendar" | "card" | "card-grid" | "carousel" | "center" | "chart" | "checkbox" | "checkbox-card" | "checkbox-field" | "code" | "collapsible" | "color-palette" | "color-wheel" | "combobox" | "command" | "container" | "context-menu" | "cookie-banner" | "country-combobox" | "country-select" | "data-grid" | "data-grid-column-filter" | "data-grid-column-header" | "data-grid-column-visibility" | "data-grid-pagination" | "data-grid-table" | "delivery-banner" | "description-list" | "dialog" | "document-list" | "dot" | "dot-matrix-loader" | "drawer" | "dropdown-menu" | "easing-curve" | "editorial-card" | "empty" | "empty-state" | "error-boundary" | "error-state" | "expand-button" | "expandable-text" | "fade-overlay" | "field" | "file-upload" | "flex" | "folder-tile" | "gallery" | "grid" | "group" | "heading" | "hotspot-indicator" | "hover-card" | "icon" | "icon-box" | "icon-card" | "icon-morph" | "identified-surface" | "input" | "input-card" | "input-group" | "input-otp" | "item" | "kbd" | "label" | "loading-state" | "location-input" | "logo" | "marquee-text" | "media-frame" | "mega-menu" | "menu-item" | "menubar" | "mobile-navigation" | "month-year-picker" | "morph-dialog" | "motion-config" | "native-select" | "navigation-menu" | "page-header" | "page-toolbar" | "pagination" | "phone-input" | "pill" | "pixel-loader" | "popover" | "product-card" | "product-specifications-selector" | "progress" | "progress-dots" | "prompt-panel" | "quantity-control" | "quick-view" | "radio" | "radio-card" | "radio-group" | "requirement-item" | "resizable" | "scrim" | "scroll-area" | "scroll-fade" | "scroll-frame" | "search-input" | "section" | "section-header" | "segmented
|
|
75
|
+
export type DocsUnitSlug = "accordion" | "alert" | "alert-dialog" | "anchor" | "animated-number" | "aspect-ratio" | "attribute-card" | "avatar" | "badge" | "border-beam" | "box" | "breadcrumb" | "button" | "button-group" | "calendar" | "card" | "card-grid" | "carousel" | "center" | "chart" | "checkbox" | "checkbox-card" | "checkbox-field" | "code" | "collapsible" | "color-palette" | "color-wheel" | "combobox" | "command" | "container" | "context-menu" | "cookie-banner" | "country-combobox" | "country-select" | "data-grid" | "data-grid-column-filter" | "data-grid-column-header" | "data-grid-column-visibility" | "data-grid-pagination" | "data-grid-table" | "delivery-banner" | "description-list" | "dialog" | "document-list" | "dot" | "dot-matrix-loader" | "drawer" | "dropdown-menu" | "easing-curve" | "editorial-card" | "empty" | "empty-state" | "error-boundary" | "error-state" | "expand-button" | "expandable-text" | "fade-overlay" | "field" | "file-upload" | "flex" | "folder-tile" | "gallery" | "grid" | "group" | "heading" | "hotspot-indicator" | "hover-card" | "icon" | "icon-box" | "icon-card" | "icon-morph" | "identified-surface" | "input" | "input-card" | "input-group" | "input-otp" | "item" | "kbd" | "label" | "loading-state" | "location-input" | "logo" | "marquee-text" | "media-frame" | "mega-menu" | "menu-item" | "menubar" | "mobile-navigation" | "month-year-picker" | "morph-dialog" | "motion-config" | "native-select" | "navigation-menu" | "page-header" | "page-toolbar" | "pagination" | "phone-input" | "pill" | "pixel-loader" | "popover" | "product-card" | "product-specifications-selector" | "progress" | "progress-dots" | "prompt-panel" | "quantity-control" | "quick-view" | "radio" | "radio-card" | "radio-group" | "requirement-item" | "resizable" | "scrim" | "scroll-area" | "scroll-fade" | "scroll-frame" | "search-input" | "section" | "section-header" | "segmented-control" | "select" | "separator" | "settings-card" | "sheet" | "shimmer" | "shine" | "sidebar" | "sidepanel" | "simple-grid" | "site-footer" | "skeleton" | "slider" | "social-icon" | "space" | "spinner" | "stack" | "stagger" | "stepper" | "streaming-text" | "sub-navigation" | "success-tick-ripple" | "swatch" | "swatch-fan" | "swatch-group" | "swatch-selector" | "switch" | "tab-navigation" | "table" | "tabs" | "tag" | "tag-selector" | "text" | "textarea" | "thinking-loader" | "thinking-orb" | "thinking-trace" | "time-slot-picker" | "toast" | "toggle" | "toggle-group" | "tooltip" | "variant-selector" | "verge" | "visually-hidden" | "voice";
|
|
76
76
|
export declare const docsManifest: DocsManifest;
|
|
@@ -5118,6 +5118,7 @@ export const docsManifest = {
|
|
|
5118
5118
|
"pixel-loader",
|
|
5119
5119
|
"prompt-panel",
|
|
5120
5120
|
"quick-view",
|
|
5121
|
+
"radio-group",
|
|
5121
5122
|
"search-input",
|
|
5122
5123
|
"site-footer",
|
|
5123
5124
|
"streaming-text",
|
|
@@ -6197,7 +6198,7 @@ export const docsManifest = {
|
|
|
6197
6198
|
"Set `size`/`radius`/`disabled` once on `InputGroup`; children inherit them via context, so don't re-set them per child.",
|
|
6198
6199
|
"Inside a `Field` the group inherits invalid state and its `InputGroupInput`/`InputGroupTextarea` becomes the field's control (labelled, described, `aria-invalid`) - pass `hasError` only to force error styling and `aria-invalid` independently.",
|
|
6199
6200
|
],
|
|
6200
|
-
uses: ["button", "field", "flex", "icon"],
|
|
6201
|
+
uses: ["button", "field", "flex", "icon", "textarea"],
|
|
6201
6202
|
usedBy: ["country-select", "search-input"],
|
|
6202
6203
|
anatomy: {
|
|
6203
6204
|
children: [
|
|
@@ -8361,7 +8362,7 @@ export const docsManifest = {
|
|
|
8361
8362
|
module: "components/progress",
|
|
8362
8363
|
title: "Progress",
|
|
8363
8364
|
category: "feedback",
|
|
8364
|
-
description: "Linear progress bar showing completion from 0 to 100
|
|
8365
|
+
description: "Linear progress bar showing completion from 0 to 100. Pass `value={null}`\nwhen the amount of work is unknown: a short bar slides across the track on a\nloop, and under reduced motion it holds still in the middle. Built on Base\nUI Progress for accessible semantics.",
|
|
8365
8366
|
exports: [
|
|
8366
8367
|
{
|
|
8367
8368
|
name: "Progress",
|
|
@@ -8437,8 +8438,9 @@ export const docsManifest = {
|
|
|
8437
8438
|
},
|
|
8438
8439
|
],
|
|
8439
8440
|
bestPractices: [
|
|
8440
|
-
"Use `Progress` for determinate 0–100 completion
|
|
8441
|
+
"Use `Progress` for determinate 0–100 completion and `value={null}` when the total is unknown; position in a stepped flow is `ProgressDots`.",
|
|
8441
8442
|
"For a ring instead of a bar use `ProgressCircle` - it needs an explicit `label` for its native `<progress>` accessible name.",
|
|
8443
|
+
"Mount `ReducedMotionProvider` at the app root - the indeterminate bar slides via `m.*` (LazyMotion).",
|
|
8442
8444
|
],
|
|
8443
8445
|
uses: ["center"],
|
|
8444
8446
|
usedBy: ["file-upload"],
|
|
@@ -8787,6 +8789,12 @@ export const docsManifest = {
|
|
|
8787
8789
|
defaultValue: '"sm"',
|
|
8788
8790
|
description: 'Radio size - a single token or a per-breakpoint object like\n`{ base: "sm", md: "lg" }`.',
|
|
8789
8791
|
},
|
|
8792
|
+
{
|
|
8793
|
+
name: "description",
|
|
8794
|
+
type: "React.ReactNode",
|
|
8795
|
+
required: false,
|
|
8796
|
+
description: "Optional helper text below the label; wired to the radio via `aria-describedby`.",
|
|
8797
|
+
},
|
|
8790
8798
|
],
|
|
8791
8799
|
},
|
|
8792
8800
|
{
|
|
@@ -8800,7 +8808,7 @@ export const docsManifest = {
|
|
|
8800
8808
|
"Drive selection with `value` + `onValueChange` (controlled) or `defaultValue` (uncontrolled) - don't set both on the same group.",
|
|
8801
8809
|
"Prefer `RadioGroupItem` (radio + wired `Label`) over hand-pairing `Radio` and `Label`; it auto-generates the `id`/`htmlFor` link.",
|
|
8802
8810
|
],
|
|
8803
|
-
uses: ["flex", "label", "radio"],
|
|
8811
|
+
uses: ["flex", "grid", "label", "radio", "text"],
|
|
8804
8812
|
usedBy: [],
|
|
8805
8813
|
},
|
|
8806
8814
|
{
|
|
@@ -9385,51 +9393,6 @@ export const docsManifest = {
|
|
|
9385
9393
|
uses: [],
|
|
9386
9394
|
usedBy: [],
|
|
9387
9395
|
},
|
|
9388
|
-
{
|
|
9389
|
-
slug: "segmented",
|
|
9390
|
-
module: "components/segmented",
|
|
9391
|
-
title: "Segmented",
|
|
9392
|
-
category: "forms",
|
|
9393
|
-
description: "Shared class-name token primitives for the segmented-control visual language.\n\nConsumed by SegmentedControl, Tabs, and TabNavigation so they render an\nidentical shell, padding, gap, and active indicator at any size.\nNot components - these are Tailwind class strings and size→class maps.",
|
|
9394
|
-
exports: [
|
|
9395
|
-
{
|
|
9396
|
-
name: "SEGMENTED_GAP",
|
|
9397
|
-
kind: "const",
|
|
9398
|
-
props: [],
|
|
9399
|
-
},
|
|
9400
|
-
{
|
|
9401
|
-
name: "SEGMENTED_INDICATOR_ACTIVE_CLASS",
|
|
9402
|
-
kind: "const",
|
|
9403
|
-
props: [],
|
|
9404
|
-
},
|
|
9405
|
-
{
|
|
9406
|
-
name: "SEGMENTED_INDICATOR_CLASS",
|
|
9407
|
-
kind: "const",
|
|
9408
|
-
props: [],
|
|
9409
|
-
},
|
|
9410
|
-
{
|
|
9411
|
-
name: "SEGMENTED_SHELL_BASE",
|
|
9412
|
-
kind: "const",
|
|
9413
|
-
props: [],
|
|
9414
|
-
},
|
|
9415
|
-
{
|
|
9416
|
-
name: "SEGMENTED_SHELL_PADDING",
|
|
9417
|
-
kind: "const",
|
|
9418
|
-
props: [],
|
|
9419
|
-
},
|
|
9420
|
-
{
|
|
9421
|
-
name: "SEGMENTED_TRIGGER_PADDING",
|
|
9422
|
-
kind: "const",
|
|
9423
|
-
props: [],
|
|
9424
|
-
},
|
|
9425
|
-
],
|
|
9426
|
-
bestPractices: [
|
|
9427
|
-
"These are class-name strings and size→class maps, not components - apply them via `className`, never render them as JSX.",
|
|
9428
|
-
'Reuse these tokens to give a custom control the segmented "pill" shell; for actual segmented UIs prefer `SegmentedControl`; `Tabs` and `TabNavigation` consume the same tokens for panel and navigation switching.',
|
|
9429
|
-
],
|
|
9430
|
-
uses: [],
|
|
9431
|
-
usedBy: ["segmented-control", "tab-navigation", "tabs"],
|
|
9432
|
-
},
|
|
9433
9396
|
{
|
|
9434
9397
|
slug: "segmented-control",
|
|
9435
9398
|
module: "components/segmented-control",
|
|
@@ -9534,7 +9497,7 @@ export const docsManifest = {
|
|
|
9534
9497
|
"Switching visible content panels is Tabs; navigating URLs is TabNavigation - SegmentedControl is an input that selects a value or mode.",
|
|
9535
9498
|
"Icon-only items must carry an `aria-label`.",
|
|
9536
9499
|
],
|
|
9537
|
-
uses: ["dot", "flex", "icon", "scroll-area", "scroll-fade"
|
|
9500
|
+
uses: ["dot", "flex", "icon", "scroll-area", "scroll-fade"],
|
|
9538
9501
|
usedBy: ["color-palette"],
|
|
9539
9502
|
anatomy: {
|
|
9540
9503
|
children: [
|
|
@@ -11933,7 +11896,7 @@ export const docsManifest = {
|
|
|
11933
11896
|
"Use `TabNavigation` when each tab is a route/URL (it renders a `<nav>` of links); for in-page panels that swap without navigating, use `Tabs`.",
|
|
11934
11897
|
'It shares the Tabs look via context but defaults to `variant="line"`; mark the active link yourself (e.g. router `aria-current`) - there\'s no Base UI value tracking here.',
|
|
11935
11898
|
],
|
|
11936
|
-
uses: ["flex", "icon", "
|
|
11899
|
+
uses: ["flex", "icon", "tabs"],
|
|
11937
11900
|
usedBy: [],
|
|
11938
11901
|
anatomy: {
|
|
11939
11902
|
children: [
|
|
@@ -12175,7 +12138,7 @@ export const docsManifest = {
|
|
|
12175
12138
|
"Reach for `Tabs` to swap in-page panels via `TabsContent`; when each tab is a route, use `TabNavigation` instead (renders links, no panels).",
|
|
12176
12139
|
"Compose `TabsList`/`TabsTrigger`/`TabsContent` - shared `variant`/`size`/`fullWidth` flow through context, so set them on `Tabs`, not each trigger.",
|
|
12177
12140
|
],
|
|
12178
|
-
uses: ["dot", "flex", "icon", "scroll-area"
|
|
12141
|
+
uses: ["dot", "flex", "icon", "scroll-area"],
|
|
12179
12142
|
usedBy: ["tab-navigation"],
|
|
12180
12143
|
anatomy: {
|
|
12181
12144
|
children: [
|
|
@@ -12413,6 +12376,7 @@ export const docsManifest = {
|
|
|
12413
12376
|
"page-header",
|
|
12414
12377
|
"pixel-loader",
|
|
12415
12378
|
"product-card",
|
|
12379
|
+
"radio-group",
|
|
12416
12380
|
"section-header",
|
|
12417
12381
|
"site-footer",
|
|
12418
12382
|
"slider",
|
|
@@ -12447,11 +12411,11 @@ export const docsManifest = {
|
|
|
12447
12411
|
],
|
|
12448
12412
|
bestPractices: [
|
|
12449
12413
|
"Inside a `Field`, let invalid state cascade like `Input` - omit `hasError`; pass it only to force error styling on a standalone `Textarea`.",
|
|
12450
|
-
"It auto-grows with content (`field-sizing-content`); set `rows` for
|
|
12414
|
+
"It auto-grows with content (`field-sizing-content`); set `rows` for a minimum height in lines rather than pinning height via CSS.",
|
|
12451
12415
|
"Give it a label - inside a `Field` a `FieldLabel` targets it with no ids to wire; standalone, point a `Label`'s `htmlFor` at its `id`. A placeholder is not an accessible name.",
|
|
12452
12416
|
],
|
|
12453
12417
|
uses: ["field"],
|
|
12454
|
-
usedBy: [],
|
|
12418
|
+
usedBy: ["input-group"],
|
|
12455
12419
|
},
|
|
12456
12420
|
{
|
|
12457
12421
|
slug: "thinking-loader",
|
|
@@ -14506,13 +14470,15 @@ export const docsManifest = {
|
|
|
14506
14470
|
name: "more",
|
|
14507
14471
|
type: "boolean",
|
|
14508
14472
|
required: false,
|
|
14473
|
+
defaultValue: "true",
|
|
14474
|
+
description: "Collapse the page buttons into groups of `moreLimit`, with ellipsis\nbuttons that jump to the next or previous group. Off shows a button for\nevery page.",
|
|
14509
14475
|
},
|
|
14510
14476
|
{
|
|
14511
14477
|
name: "moreLimit",
|
|
14512
14478
|
type: "number",
|
|
14513
14479
|
required: false,
|
|
14514
14480
|
defaultValue: "5",
|
|
14515
|
-
description: "Number of page buttons shown before collapsing to an ellipsis.",
|
|
14481
|
+
description: "Number of page buttons shown before collapsing to an ellipsis. Needs\n`more`.",
|
|
14516
14482
|
},
|
|
14517
14483
|
{
|
|
14518
14484
|
name: "nextPageLabel",
|
|
@@ -14542,23 +14508,6 @@ export const docsManifest = {
|
|
|
14542
14508
|
defaultValue: "[5, 10, 25, 50, 100]",
|
|
14543
14509
|
description: "Page-size options offered by the rows-per-page select.",
|
|
14544
14510
|
},
|
|
14545
|
-
{
|
|
14546
|
-
name: "sizesDescription",
|
|
14547
|
-
type: "string",
|
|
14548
|
-
required: false,
|
|
14549
|
-
defaultValue: '"per page"',
|
|
14550
|
-
},
|
|
14551
|
-
{
|
|
14552
|
-
name: "sizesInfo",
|
|
14553
|
-
type: "string",
|
|
14554
|
-
required: false,
|
|
14555
|
-
},
|
|
14556
|
-
{
|
|
14557
|
-
name: "sizesLabel",
|
|
14558
|
-
type: "string",
|
|
14559
|
-
required: false,
|
|
14560
|
-
defaultValue: '"Show"',
|
|
14561
|
-
},
|
|
14562
14511
|
{
|
|
14563
14512
|
name: "sizesSkeleton",
|
|
14564
14513
|
type: "ReactNode",
|
|
@@ -18517,6 +18466,8 @@ export const docsManifest = {
|
|
|
18517
18466
|
name: "maxSwatches",
|
|
18518
18467
|
type: "number | undefined",
|
|
18519
18468
|
required: false,
|
|
18469
|
+
defaultValue: "5",
|
|
18470
|
+
description: "How many colourway swatches the row shows before the rest go behind `+N`.\nCards under 240px wide show at most four.",
|
|
18520
18471
|
},
|
|
18521
18472
|
{
|
|
18522
18473
|
name: "menuOpen",
|
|
@@ -18643,6 +18594,8 @@ export const docsManifest = {
|
|
|
18643
18594
|
name: "showSwatches",
|
|
18644
18595
|
type: "boolean | undefined",
|
|
18645
18596
|
required: false,
|
|
18597
|
+
defaultValue: "true",
|
|
18598
|
+
description: "Show the row of colourway swatches. Single-colour products and `single`\nmode never show it.",
|
|
18646
18599
|
},
|
|
18647
18600
|
{
|
|
18648
18601
|
name: "similarityScore",
|
|
@@ -19119,6 +19072,8 @@ export const docsManifest = {
|
|
|
19119
19072
|
name: "maxSwatches",
|
|
19120
19073
|
type: "number | undefined",
|
|
19121
19074
|
required: false,
|
|
19075
|
+
defaultValue: "5",
|
|
19076
|
+
description: "How many colourway swatches the row shows before the rest go behind `+N`.\nCards under 240px wide show at most four.",
|
|
19122
19077
|
},
|
|
19123
19078
|
{
|
|
19124
19079
|
name: "mode",
|
|
@@ -23055,7 +23010,7 @@ export const docsManifest = {
|
|
|
23055
23010
|
storybook: {
|
|
23056
23011
|
addons: ["@storybook/addon-docs", "@storybook/react-vite"],
|
|
23057
23012
|
anatomyFiles: 31,
|
|
23058
|
-
storyFiles:
|
|
23059
|
-
storiesWithPlay:
|
|
23013
|
+
storyFiles: 171,
|
|
23014
|
+
storiesWithPlay: 63,
|
|
23060
23015
|
},
|
|
23061
23016
|
};
|
package/dist/styles/index.css
CHANGED
|
@@ -164,7 +164,9 @@
|
|
|
164
164
|
--color-muted-foreground: var(--color-gray-600);
|
|
165
165
|
--color-accent: var(--color-gray-200);
|
|
166
166
|
--color-accent-foreground: var(--color-gray-900);
|
|
167
|
-
|
|
167
|
+
/* Red-700, not 600: red-50 on red-600 is 4.4:1 and drops under the button
|
|
168
|
+
sheen and hover; red-700 holds 4.5:1 as a fill and as text on the page. */
|
|
169
|
+
--color-destructive: var(--color-red-700);
|
|
168
170
|
--color-destructive-foreground: var(--color-red-50);
|
|
169
171
|
--color-favorite: var(--color-red-500);
|
|
170
172
|
--color-border: var(--color-gray-200);
|
|
@@ -213,8 +215,9 @@
|
|
|
213
215
|
--color-text-muted: var(--color-gray-600);
|
|
214
216
|
|
|
215
217
|
/* Calm semantic colours - Next.js inspired */
|
|
218
|
+
/* Accents double as text on the page, so they hold 4.5:1 on white. */
|
|
216
219
|
--color-affirmative-soft: #f0fdf4; /* green-50 */
|
|
217
|
-
--color-affirmative-accent: #
|
|
220
|
+
--color-affirmative-accent: #15803d; /* green-700 */
|
|
218
221
|
--color-affirmative-foreground: #14532d; /* green-900 */
|
|
219
222
|
|
|
220
223
|
--color-warning-soft: #fffbeb; /* very soft amber */
|
|
@@ -226,13 +229,17 @@
|
|
|
226
229
|
--color-error-foreground: #991b1b; /* red-800 */
|
|
227
230
|
|
|
228
231
|
--color-info-soft: #eff6ff; /* blue-50 */
|
|
229
|
-
--color-info-accent: #
|
|
232
|
+
--color-info-accent: #2563eb; /* blue-600 */
|
|
230
233
|
--color-info-foreground: #1e3a5f; /* blue-900 */
|
|
231
234
|
|
|
232
235
|
--color-brand: #ffe600; /* Brand yellow, per the Figma base/brand token */
|
|
233
236
|
--color-brand-soft: #fffef5; /* very soft brand yellow (code-only) */
|
|
234
237
|
--color-brand-accent: #dda600; /* per Figma base/brand accent - consumed at /40 for borders/accents */
|
|
235
238
|
--color-brand-foreground: var(--color-gray-800); /* #1F2937 - per Figma */
|
|
239
|
+
/* Brand-hued text on the page background, for brand buttons without a fill.
|
|
240
|
+
6.1:1 on white, so it still passes under a faded hover; override it
|
|
241
|
+
alongside --color-brand. */
|
|
242
|
+
--color-brand-strong: #776000;
|
|
236
243
|
|
|
237
244
|
/* Status border tokens (light mode base values - desaturated blends) */
|
|
238
245
|
--color-affirmative-border: #dcfce7; /* green-100 - muted */
|
|
@@ -547,7 +554,7 @@
|
|
|
547
554
|
--color-muted-foreground: var(--color-gray-600);
|
|
548
555
|
--color-accent: var(--color-gray-200);
|
|
549
556
|
--color-accent-foreground: var(--color-gray-900);
|
|
550
|
-
--color-destructive: var(--color-red-
|
|
557
|
+
--color-destructive: var(--color-red-700);
|
|
551
558
|
--color-destructive-foreground: var(--color-red-50);
|
|
552
559
|
--color-favorite: var(--color-red-500);
|
|
553
560
|
--color-border: var(--color-gray-200);
|
|
@@ -602,8 +609,10 @@
|
|
|
602
609
|
--color-muted-foreground: var(--color-gray-400);
|
|
603
610
|
--color-accent: var(--color-surface-2);
|
|
604
611
|
--color-accent-foreground: var(--color-foreground);
|
|
605
|
-
|
|
606
|
-
|
|
612
|
+
/* Like the other dark solids, danger is a light fill with dark text: no
|
|
613
|
+
red reads both as text on graphite and under light text. */
|
|
614
|
+
--color-destructive: var(--color-red-400);
|
|
615
|
+
--color-destructive-foreground: var(--color-surface-sunk);
|
|
607
616
|
--color-favorite: var(--color-red-400);
|
|
608
617
|
--color-border: oklch(0.82 0.007 250 / 0.1);
|
|
609
618
|
--color-input: var(--color-surface-1);
|
|
@@ -629,6 +638,8 @@
|
|
|
629
638
|
--color-info-accent: oklch(0.72 0.1 245);
|
|
630
639
|
--color-info-foreground: oklch(0.74 0.08 245);
|
|
631
640
|
--color-brand-soft: var(--color-surface-1);
|
|
641
|
+
/* The brand itself reads on graphite (14.6:1 on the background). */
|
|
642
|
+
--color-brand-strong: var(--color-brand);
|
|
632
643
|
|
|
633
644
|
/* Dark surfaces are separated by the role ladder and hairline, never
|
|
634
645
|
elevation. This also neutralises custom shadow utilities package-wide. */
|