@timmbr/ui 1.8.0 → 1.10.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/Breadcrumb/Breadcrumb.d.ts +9 -0
- package/dist/components/Breadcrumb/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.js +32 -0
- package/dist/components/Breadcrumb/Breadcrumb.styles.d.ts +8 -0
- package/dist/components/Breadcrumb/Breadcrumb.styles.d.ts.map +1 -0
- package/dist/components/Breadcrumb/Breadcrumb.styles.js +20 -0
- package/dist/components/Breadcrumb/Breadcrumb.types.d.ts +27 -0
- package/dist/components/Breadcrumb/Breadcrumb.types.d.ts.map +1 -0
- package/dist/components/Breadcrumb/index.d.ts +4 -0
- package/dist/components/Breadcrumb/index.d.ts.map +1 -0
- package/dist/components/Chip/Chip.d.ts.map +1 -1
- package/dist/components/Chip/Chip.js +13 -3
- package/dist/components/Chip/Chip.types.d.ts +5 -0
- package/dist/components/Chip/Chip.types.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.js +4 -4
- package/dist/components/Drawer/Drawer.styles.d.ts +1 -0
- package/dist/components/Drawer/Drawer.styles.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.styles.js +7 -1
- package/dist/components/Drawer/Drawer.types.d.ts +2 -0
- package/dist/components/Drawer/Drawer.types.d.ts.map +1 -1
- package/dist/components/FilterChips/FilterChips.d.ts +8 -0
- package/dist/components/FilterChips/FilterChips.d.ts.map +1 -0
- package/dist/components/FilterChips/FilterChips.js +39 -0
- package/dist/components/FilterChips/FilterChips.types.d.ts +17 -0
- package/dist/components/FilterChips/FilterChips.types.d.ts.map +1 -0
- package/dist/components/FilterChips/index.d.ts +3 -0
- package/dist/components/FilterChips/index.d.ts.map +1 -0
- package/dist/components/FilterDrawer/FilterDrawer.d.ts +12 -0
- package/dist/components/FilterDrawer/FilterDrawer.d.ts.map +1 -0
- package/dist/components/FilterDrawer/FilterDrawer.js +157 -0
- package/dist/components/FilterDrawer/FilterDrawer.styles.d.ts +12 -0
- package/dist/components/FilterDrawer/FilterDrawer.styles.d.ts.map +1 -0
- package/dist/components/FilterDrawer/FilterDrawer.styles.js +30 -0
- package/dist/components/FilterDrawer/FilterDrawer.types.d.ts +28 -0
- package/dist/components/FilterDrawer/FilterDrawer.types.d.ts.map +1 -0
- package/dist/components/FilterDrawer/index.d.ts +4 -0
- package/dist/components/FilterDrawer/index.d.ts.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.d.ts +33 -0
- package/dist/components/FilterPanel/FilterPanel.d.ts.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.js +239 -0
- package/dist/components/FilterPanel/FilterPanel.styles.d.ts +16 -0
- package/dist/components/FilterPanel/FilterPanel.styles.d.ts.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.styles.js +31 -0
- package/dist/components/FilterPanel/FilterPanel.types.d.ts +80 -0
- package/dist/components/FilterPanel/FilterPanel.types.d.ts.map +1 -0
- package/dist/components/FilterPanel/FilterPanel.types.js +3 -0
- package/dist/components/FilterPanel/index.d.ts +4 -0
- package/dist/components/FilterPanel/index.d.ts.map +1 -0
- package/dist/components/ImageUpload/ImageUpload.styles.d.ts +3 -3
- package/dist/components/MediaCard/MediaCard.d.ts.map +1 -1
- package/dist/components/MediaCard/MediaCard.js +3 -1
- package/dist/components/NavHeader/NavHeader.styles.d.ts +7 -0
- package/dist/components/NavHeader/NavHeader.styles.d.ts.map +1 -1
- package/dist/components/NavHeader/NavHeader.styles.js +20 -2
- package/dist/components/NavHeader/NavHeader.types.d.ts +49 -1
- package/dist/components/NavHeader/NavHeader.types.d.ts.map +1 -1
- package/dist/components/NavHeader/index.d.ts +3 -2
- package/dist/components/NavHeader/index.d.ts.map +1 -1
- package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts.map +1 -1
- package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.js +15 -33
- package/dist/components/NavHeader/subcomponents/NavSearch.d.ts.map +1 -1
- package/dist/components/NavHeader/subcomponents/NavSearch.js +37 -83
- package/dist/components/NavHeader/subcomponents/NavSearchPanel.d.ts +63 -0
- package/dist/components/NavHeader/subcomponents/NavSearchPanel.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavSearchPanel.js +289 -0
- package/dist/components/NavMenuColumns/NavMenuColumns.d.ts +8 -1
- package/dist/components/NavMenuColumns/NavMenuColumns.d.ts.map +1 -1
- package/dist/components/NavMenuColumns/NavMenuColumns.js +27 -1
- package/dist/components/NavMenuColumns/NavMenuColumns.styles.js +1 -1
- package/dist/components/NavMenuColumns/NavMenuColumns.types.d.ts +18 -0
- package/dist/components/NavMenuColumns/NavMenuColumns.types.d.ts.map +1 -1
- package/dist/components/OptionPicker/OptionPicker.d.ts +10 -0
- package/dist/components/OptionPicker/OptionPicker.d.ts.map +1 -0
- package/dist/components/OptionPicker/OptionPicker.js +116 -0
- package/dist/components/OptionPicker/OptionPicker.styles.d.ts +20 -0
- package/dist/components/OptionPicker/OptionPicker.styles.d.ts.map +1 -0
- package/dist/components/OptionPicker/OptionPicker.styles.js +60 -0
- package/dist/components/OptionPicker/OptionPicker.types.d.ts +39 -0
- package/dist/components/OptionPicker/OptionPicker.types.d.ts.map +1 -0
- package/dist/components/OptionPicker/index.d.ts +4 -0
- package/dist/components/OptionPicker/index.d.ts.map +1 -0
- package/dist/components/Price/Price.styles.d.ts.map +1 -1
- package/dist/components/Price/Price.styles.js +3 -2
- package/dist/components/ProductGallery/ProductGallery.d.ts +10 -0
- package/dist/components/ProductGallery/ProductGallery.d.ts.map +1 -0
- package/dist/components/ProductGallery/ProductGallery.js +306 -0
- package/dist/components/ProductGallery/ProductGallery.styles.d.ts +32 -0
- package/dist/components/ProductGallery/ProductGallery.styles.d.ts.map +1 -0
- package/dist/components/ProductGallery/ProductGallery.styles.js +86 -0
- package/dist/components/ProductGallery/ProductGallery.types.d.ts +55 -0
- package/dist/components/ProductGallery/ProductGallery.types.d.ts.map +1 -0
- package/dist/components/ProductGallery/ProductGalleryLightbox.d.ts +20 -0
- package/dist/components/ProductGallery/ProductGalleryLightbox.d.ts.map +1 -0
- package/dist/components/ProductGallery/ProductGalleryLightbox.js +210 -0
- package/dist/components/ProductGallery/index.d.ts +4 -0
- package/dist/components/ProductGallery/index.d.ts.map +1 -0
- package/dist/components/QuantityStepper/QuantityStepper.d.ts +8 -0
- package/dist/components/QuantityStepper/QuantityStepper.d.ts.map +1 -0
- package/dist/components/QuantityStepper/QuantityStepper.js +65 -0
- package/dist/components/QuantityStepper/QuantityStepper.styles.d.ts +8 -0
- package/dist/components/QuantityStepper/QuantityStepper.styles.d.ts.map +1 -0
- package/dist/components/QuantityStepper/QuantityStepper.styles.js +24 -0
- package/dist/components/QuantityStepper/QuantityStepper.types.d.ts +20 -0
- package/dist/components/QuantityStepper/QuantityStepper.types.d.ts.map +1 -0
- package/dist/components/QuantityStepper/index.d.ts +4 -0
- package/dist/components/QuantityStepper/index.d.ts.map +1 -0
- package/dist/components/RangeSlider/RangeSlider.d.ts.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.js +5 -3
- package/dist/components/RangeSlider/RangeSlider.types.d.ts +7 -0
- package/dist/components/RangeSlider/RangeSlider.types.d.ts.map +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +14 -1
- package/dist/components/Select/Select.types.d.ts +4 -0
- package/dist/components/Select/Select.types.d.ts.map +1 -1
- package/dist/components/Snackbar/Snackbar.d.ts +9 -0
- package/dist/components/Snackbar/Snackbar.d.ts.map +1 -0
- package/dist/components/Snackbar/Snackbar.js +108 -0
- package/dist/components/Snackbar/Snackbar.styles.d.ts +17 -0
- package/dist/components/Snackbar/Snackbar.styles.d.ts.map +1 -0
- package/dist/components/Snackbar/Snackbar.styles.js +37 -0
- package/dist/components/Snackbar/Snackbar.types.d.ts +36 -0
- package/dist/components/Snackbar/Snackbar.types.d.ts.map +1 -0
- package/dist/components/Snackbar/SnackbarHost.d.ts +4 -0
- package/dist/components/Snackbar/SnackbarHost.d.ts.map +1 -0
- package/dist/components/Snackbar/SnackbarHost.js +24 -0
- package/dist/components/Snackbar/index.d.ts +6 -0
- package/dist/components/Snackbar/index.d.ts.map +1 -0
- package/dist/components/Snackbar/snackbarStore.d.ts +42 -0
- package/dist/components/Snackbar/snackbarStore.d.ts.map +1 -0
- package/dist/components/Snackbar/snackbarStore.js +87 -0
- package/dist/components/StockMeter/StockMeter.d.ts +8 -0
- package/dist/components/StockMeter/StockMeter.d.ts.map +1 -0
- package/dist/components/StockMeter/StockMeter.js +19 -0
- package/dist/components/StockMeter/StockMeter.styles.d.ts +4 -0
- package/dist/components/StockMeter/StockMeter.styles.d.ts.map +1 -0
- package/dist/components/StockMeter/StockMeter.styles.js +7 -0
- package/dist/components/StockMeter/StockMeter.types.d.ts +10 -0
- package/dist/components/StockMeter/StockMeter.types.d.ts.map +1 -0
- package/dist/components/StockMeter/index.d.ts +4 -0
- package/dist/components/StockMeter/index.d.ts.map +1 -0
- package/dist/components/Swatch/Swatch.d.ts +11 -0
- package/dist/components/Swatch/Swatch.d.ts.map +1 -0
- package/dist/components/Swatch/Swatch.js +40 -0
- package/dist/components/Swatch/Swatch.styles.d.ts +8 -0
- package/dist/components/Swatch/Swatch.styles.d.ts.map +1 -0
- package/dist/components/Swatch/Swatch.styles.js +26 -0
- package/dist/components/Swatch/Swatch.types.d.ts +11 -0
- package/dist/components/Swatch/Swatch.types.d.ts.map +1 -0
- package/dist/components/Swatch/index.d.ts +4 -0
- package/dist/components/Swatch/index.d.ts.map +1 -0
- package/dist/components/Toast/Toast.d.ts.map +1 -1
- package/dist/components/Toast/Toast.js +49 -42
- package/dist/components/Toast/Toast.types.d.ts +7 -0
- package/dist/components/Toast/Toast.types.d.ts.map +1 -1
- package/dist/components/Toast/ToastContainer.d.ts.map +1 -1
- package/dist/components/Toast/ToastContainer.js +5 -0
- package/dist/components/Toast/toastStore.d.ts +11 -0
- package/dist/components/Toast/toastStore.d.ts.map +1 -1
- package/dist/components/Toast/toastStore.js +70 -30
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -2
- package/dist/providers/TimmbrConfigProvider.d.ts.map +1 -1
- package/dist/providers/TimmbrConfigProvider.js +5 -9
- package/package.json +6 -6
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
5
|
+
import { ZoomOut, ZoomIn, X, ChevronLeft, ChevronRight } from '@timmbr/icons';
|
|
6
|
+
import { productGalleryLightboxButtonVariants, productGalleryLightboxVariants } from './ProductGallery.styles.js';
|
|
7
|
+
|
|
8
|
+
const MAX_SCALE = 3;
|
|
9
|
+
const ZOOM_STEP = 2;
|
|
10
|
+
const SWIPE_PX = 50;
|
|
11
|
+
const TAP_SLOP_PX = 10;
|
|
12
|
+
const FIT = { scale: 1, x: 0, y: 0 };
|
|
13
|
+
function ProductGalleryLightbox({
|
|
14
|
+
open,
|
|
15
|
+
onOpenChange,
|
|
16
|
+
images,
|
|
17
|
+
index,
|
|
18
|
+
onIndexChange,
|
|
19
|
+
renderImage,
|
|
20
|
+
labels,
|
|
21
|
+
onCloseAutoFocus
|
|
22
|
+
}) {
|
|
23
|
+
const [view, setView] = React.useState(FIT);
|
|
24
|
+
const pointers = React.useRef(/* @__PURE__ */ new Map());
|
|
25
|
+
const pinch = React.useRef(null);
|
|
26
|
+
const lastTap = React.useRef(0);
|
|
27
|
+
const lastPointerType = React.useRef("mouse");
|
|
28
|
+
const swipe = React.useRef(null);
|
|
29
|
+
const [dragX, setDragX] = React.useState(0);
|
|
30
|
+
const total = images.length;
|
|
31
|
+
const image = images[index];
|
|
32
|
+
const [shown, setShown] = React.useState({ index, open });
|
|
33
|
+
if (shown.index !== index || shown.open !== open) {
|
|
34
|
+
setShown({ index, open });
|
|
35
|
+
setView(FIT);
|
|
36
|
+
}
|
|
37
|
+
const handleOpenChange = (next) => {
|
|
38
|
+
if (!next) {
|
|
39
|
+
pointers.current.clear();
|
|
40
|
+
pinch.current = null;
|
|
41
|
+
swipe.current = null;
|
|
42
|
+
lastTap.current = 0;
|
|
43
|
+
setDragX(0);
|
|
44
|
+
}
|
|
45
|
+
onOpenChange(next);
|
|
46
|
+
};
|
|
47
|
+
const go = (delta) => {
|
|
48
|
+
const next = index + delta;
|
|
49
|
+
if (next >= 0 && next < total) onIndexChange(next);
|
|
50
|
+
};
|
|
51
|
+
const zoomTo = (scale) => setView((current) => {
|
|
52
|
+
const clamped = Math.min(MAX_SCALE, Math.max(1, scale));
|
|
53
|
+
return clamped === 1 ? FIT : { ...current, scale: clamped };
|
|
54
|
+
});
|
|
55
|
+
const toggleZoom = () => zoomTo(view.scale > 1 ? 1 : ZOOM_STEP);
|
|
56
|
+
const onKeyDown = (event) => {
|
|
57
|
+
if (event.key === "ArrowRight") go(1);
|
|
58
|
+
else if (event.key === "ArrowLeft") go(-1);
|
|
59
|
+
else if (event.key === "+" || event.key === "=") zoomTo(view.scale + 0.5);
|
|
60
|
+
else if (event.key === "-") zoomTo(view.scale - 0.5);
|
|
61
|
+
};
|
|
62
|
+
const distance = () => {
|
|
63
|
+
const [a, b] = [...pointers.current.values()];
|
|
64
|
+
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
65
|
+
};
|
|
66
|
+
const onPointerDown = (event) => {
|
|
67
|
+
lastPointerType.current = event.pointerType;
|
|
68
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
69
|
+
pointers.current.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
70
|
+
if (pointers.current.size === 1) {
|
|
71
|
+
swipe.current = { x: event.clientX, y: event.clientY, multi: false };
|
|
72
|
+
} else {
|
|
73
|
+
if (swipe.current) swipe.current.multi = true;
|
|
74
|
+
setDragX(0);
|
|
75
|
+
}
|
|
76
|
+
if (pointers.current.size === 2) pinch.current = { distance: distance(), scale: view.scale };
|
|
77
|
+
};
|
|
78
|
+
const onPointerMove = (event) => {
|
|
79
|
+
const previous = pointers.current.get(event.pointerId);
|
|
80
|
+
if (!previous) return;
|
|
81
|
+
pointers.current.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
82
|
+
if (pinch.current && pointers.current.size === 2) {
|
|
83
|
+
zoomTo(pinch.current.scale * distance() / pinch.current.distance);
|
|
84
|
+
} else if (view.scale > 1) {
|
|
85
|
+
const dx = event.clientX - previous.x;
|
|
86
|
+
const dy = event.clientY - previous.y;
|
|
87
|
+
setView((current) => ({ ...current, x: current.x + dx, y: current.y + dy }));
|
|
88
|
+
} else if (swipe.current && !swipe.current.multi && event.pointerType !== "mouse") {
|
|
89
|
+
setDragX(event.clientX - swipe.current.x);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const onPointerUp = (event) => {
|
|
93
|
+
const start = pointers.current.get(event.pointerId);
|
|
94
|
+
pointers.current.delete(event.pointerId);
|
|
95
|
+
if (pointers.current.size < 2) pinch.current = null;
|
|
96
|
+
const gesture = swipe.current;
|
|
97
|
+
if (pointers.current.size === 0) {
|
|
98
|
+
swipe.current = null;
|
|
99
|
+
setDragX(0);
|
|
100
|
+
}
|
|
101
|
+
if (!start || event.pointerType === "mouse") return;
|
|
102
|
+
if (gesture && !gesture.multi && view.scale === 1) {
|
|
103
|
+
const dx = event.clientX - gesture.x;
|
|
104
|
+
const dy = event.clientY - gesture.y;
|
|
105
|
+
if (Math.abs(dx) > SWIPE_PX && Math.abs(dx) > Math.abs(dy)) {
|
|
106
|
+
lastTap.current = 0;
|
|
107
|
+
go(dx < 0 ? 1 : -1);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const still = gesture && !gesture.multi && Math.hypot(event.clientX - gesture.x, event.clientY - gesture.y) < TAP_SLOP_PX;
|
|
112
|
+
if (!still) {
|
|
113
|
+
lastTap.current = 0;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
if (now - lastTap.current < 300) {
|
|
118
|
+
lastTap.current = 0;
|
|
119
|
+
toggleZoom();
|
|
120
|
+
} else {
|
|
121
|
+
lastTap.current = now;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsx(DialogPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
125
|
+
DialogPrimitive.Content,
|
|
126
|
+
{
|
|
127
|
+
className: productGalleryLightboxVariants(),
|
|
128
|
+
onKeyDown,
|
|
129
|
+
onCloseAutoFocus,
|
|
130
|
+
"aria-describedby": void 0,
|
|
131
|
+
"data-slot": "product-gallery-lightbox",
|
|
132
|
+
children: [
|
|
133
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 p-3 sm:p-4", children: [
|
|
134
|
+
/* @__PURE__ */ jsxs(DialogPrimitive.Title, { className: "text-body-2 font-semibold tabular-nums", children: [
|
|
135
|
+
labels.position(index, total),
|
|
136
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: image ? ` — ${image.alt}` : "" })
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
"button",
|
|
141
|
+
{
|
|
142
|
+
type: "button",
|
|
143
|
+
className: productGalleryLightboxButtonVariants(),
|
|
144
|
+
"aria-label": view.scale > 1 ? labels.zoomOut : labels.zoomIn,
|
|
145
|
+
onClick: toggleZoom,
|
|
146
|
+
children: view.scale > 1 ? /* @__PURE__ */ jsx(ZoomOut, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ZoomIn, { "aria-hidden": "true" })
|
|
147
|
+
}
|
|
148
|
+
),
|
|
149
|
+
/* @__PURE__ */ jsx(DialogPrimitive.Close, { className: productGalleryLightboxButtonVariants(), "aria-label": labels.close, children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" }) })
|
|
150
|
+
] })
|
|
151
|
+
] }),
|
|
152
|
+
/* @__PURE__ */ jsxs(
|
|
153
|
+
"div",
|
|
154
|
+
{
|
|
155
|
+
className: "relative min-h-0 flex-1 touch-none select-none overflow-hidden",
|
|
156
|
+
onDoubleClick: () => lastPointerType.current === "mouse" && toggleZoom(),
|
|
157
|
+
onWheel: (event) => zoomTo(view.scale - event.deltaY * 2e-3),
|
|
158
|
+
onPointerDown,
|
|
159
|
+
onPointerMove,
|
|
160
|
+
onPointerUp,
|
|
161
|
+
onPointerCancel: onPointerUp,
|
|
162
|
+
style: { cursor: view.scale > 1 ? "grab" : "zoom-in" },
|
|
163
|
+
"data-slot": "product-gallery-lightbox-stage",
|
|
164
|
+
children: [
|
|
165
|
+
image && /* @__PURE__ */ jsx(
|
|
166
|
+
"div",
|
|
167
|
+
{
|
|
168
|
+
className: `absolute inset-0 ease-out motion-reduce:transition-none ${dragX ? "" : "transition-transform duration-150"}`,
|
|
169
|
+
style: { transform: `translate(${view.x + dragX}px, ${view.y}px) scale(${view.scale})` },
|
|
170
|
+
children: renderImage(image, {
|
|
171
|
+
priority: false,
|
|
172
|
+
sizes: "100vw",
|
|
173
|
+
slot: "lightbox",
|
|
174
|
+
className: "object-contain"
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
),
|
|
178
|
+
/* @__PURE__ */ jsx(
|
|
179
|
+
"button",
|
|
180
|
+
{
|
|
181
|
+
type: "button",
|
|
182
|
+
className: `${productGalleryLightboxButtonVariants()} absolute left-3 top-1/2 hidden -translate-y-1/2 md:flex`,
|
|
183
|
+
"aria-label": labels.previous,
|
|
184
|
+
disabled: index === 0,
|
|
185
|
+
onClick: () => go(-1),
|
|
186
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
187
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" })
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
/* @__PURE__ */ jsx(
|
|
191
|
+
"button",
|
|
192
|
+
{
|
|
193
|
+
type: "button",
|
|
194
|
+
className: `${productGalleryLightboxButtonVariants()} absolute right-3 top-1/2 hidden -translate-y-1/2 md:flex`,
|
|
195
|
+
"aria-label": labels.next,
|
|
196
|
+
disabled: index >= total - 1,
|
|
197
|
+
onClick: () => go(1),
|
|
198
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
199
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
) }) });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { ProductGalleryLightbox };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ProductGallery/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { QuantityStepperProps } from './QuantityStepper.types';
|
|
3
|
+
/**
|
|
4
|
+
* − value + for a quantity. The buttons stop at `min` and `max` (e.g. the units in stock), and
|
|
5
|
+
* the value is announced when it changes. Arrow up/down on either button steps too.
|
|
6
|
+
*/
|
|
7
|
+
export declare const QuantityStepper: React.ForwardRefExoticComponent<QuantityStepperProps & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
//# sourceMappingURL=QuantityStepper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuantityStepper.d.ts","sourceRoot":"","sources":["../../../src/components/QuantityStepper/QuantityStepper.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,eAAe,6FAmD3B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Minus, Plus } from '@timmbr/icons';
|
|
5
|
+
import { cn } from '@timmbr/utils';
|
|
6
|
+
import { quantityStepperButtonVariants, quantityStepperValueVariants, quantityStepperVariants } from './QuantityStepper.styles.js';
|
|
7
|
+
|
|
8
|
+
const QuantityStepper = React.forwardRef(
|
|
9
|
+
({ value, onChange, min = 1, max = 99, size, disabled = false, labels, className, ...props }, ref) => {
|
|
10
|
+
const clamp = (next) => Math.min(Math.max(next, min), Math.max(min, max));
|
|
11
|
+
const step = (delta) => {
|
|
12
|
+
const next = clamp(value + delta);
|
|
13
|
+
if (next !== value) onChange(next);
|
|
14
|
+
};
|
|
15
|
+
const onKeyDown = (event) => {
|
|
16
|
+
if (event.key === "ArrowUp" || event.key === "ArrowRight") {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
step(1);
|
|
19
|
+
} else if (event.key === "ArrowDown" || event.key === "ArrowLeft") {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
step(-1);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return /* @__PURE__ */ jsxs(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
ref,
|
|
28
|
+
role: "group",
|
|
29
|
+
"aria-label": labels?.group ?? "Quantity",
|
|
30
|
+
"data-slot": "quantity-stepper",
|
|
31
|
+
className: cn(quantityStepperVariants({ size, disabled }), className),
|
|
32
|
+
onKeyDown: disabled ? void 0 : onKeyDown,
|
|
33
|
+
...props,
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx(
|
|
36
|
+
"button",
|
|
37
|
+
{
|
|
38
|
+
type: "button",
|
|
39
|
+
"aria-label": labels?.decrease ?? "Decrease quantity",
|
|
40
|
+
disabled: disabled || value <= min,
|
|
41
|
+
onClick: () => step(-1),
|
|
42
|
+
className: quantityStepperButtonVariants(),
|
|
43
|
+
children: /* @__PURE__ */ jsx(Minus, { "aria-hidden": "true", className: "size-4" })
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsx("output", { "aria-live": "polite", className: quantityStepperValueVariants(), "data-slot": "quantity-stepper-value", children: value }),
|
|
47
|
+
/* @__PURE__ */ jsx(
|
|
48
|
+
"button",
|
|
49
|
+
{
|
|
50
|
+
type: "button",
|
|
51
|
+
"aria-label": labels?.increase ?? "Increase quantity",
|
|
52
|
+
disabled: disabled || value >= max,
|
|
53
|
+
onClick: () => step(1),
|
|
54
|
+
className: quantityStepperButtonVariants(),
|
|
55
|
+
children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true", className: "size-4" })
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
QuantityStepper.displayName = "QuantityStepper";
|
|
64
|
+
|
|
65
|
+
export { QuantityStepper };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Bordered box: − value +, on a white surface with the 4px radius used by fields. */
|
|
2
|
+
export declare const quantityStepperVariants: (props?: ({
|
|
3
|
+
size?: "md" | "lg" | null | undefined;
|
|
4
|
+
disabled?: boolean | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
export declare const quantityStepperButtonVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
7
|
+
export declare const quantityStepperValueVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
8
|
+
//# sourceMappingURL=QuantityStepper.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuantityStepper.styles.d.ts","sourceRoot":"","sources":["../../../src/components/QuantityStepper/QuantityStepper.styles.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,eAAO,MAAM,uBAAuB;;;8EAenC,CAAC;AAEF,eAAO,MAAM,6BAA6B,oFAEzC,CAAC;AAEF,eAAO,MAAM,4BAA4B,oFAAoE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { cva } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
const quantityStepperVariants = cva(
|
|
4
|
+
"inline-flex shrink-0 items-center rounded-sm border border-grey-300 bg-grey-50 font-sans text-grey-950",
|
|
5
|
+
{
|
|
6
|
+
variants: {
|
|
7
|
+
size: {
|
|
8
|
+
md: "h-11",
|
|
9
|
+
lg: "h-[52px]"
|
|
10
|
+
},
|
|
11
|
+
disabled: {
|
|
12
|
+
true: "opacity-50",
|
|
13
|
+
false: ""
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: { size: "md", disabled: false }
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
const quantityStepperButtonVariants = cva(
|
|
20
|
+
"flex h-full w-10 items-center justify-center rounded-sm text-grey-500 transition-colors hover:text-grey-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:text-grey-300"
|
|
21
|
+
);
|
|
22
|
+
const quantityStepperValueVariants = cva("min-w-6 text-center text-body-1 font-semibold tabular-nums");
|
|
23
|
+
|
|
24
|
+
export { quantityStepperButtonVariants, quantityStepperValueVariants, quantityStepperVariants };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
export interface QuantityStepperLabels {
|
|
3
|
+
/** Accessible name of the whole control. */
|
|
4
|
+
group?: string;
|
|
5
|
+
decrease?: string;
|
|
6
|
+
increase?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface QuantityStepperProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
9
|
+
value: number;
|
|
10
|
+
onChange: (value: number) => void;
|
|
11
|
+
/** Defaults to 1. */
|
|
12
|
+
min?: number;
|
|
13
|
+
/** The + button disables here (e.g. units in stock). Defaults to 99. */
|
|
14
|
+
max?: number;
|
|
15
|
+
/** `md` matches the default button height (44px); `lg` matches a 52px buy button. */
|
|
16
|
+
size?: 'md' | 'lg';
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
labels?: QuantityStepperLabels;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=QuantityStepper.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuantityStepper.types.d.ts","sourceRoot":"","sources":["../../../src/components/QuantityStepper/QuantityStepper.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IAClG,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qBAAqB;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/QuantityStepper/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../../src/components/RangeSlider/RangeSlider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../../src/components/RangeSlider/RangeSlider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,eAAO,MAAM,WAAW,0FA2IvB,CAAC"}
|
|
@@ -25,6 +25,8 @@ const RangeSlider = React.forwardRef(
|
|
|
25
25
|
onValueCommit,
|
|
26
26
|
onApply,
|
|
27
27
|
onReset,
|
|
28
|
+
thumbLabels = ["Minimum value", "Maximum value"],
|
|
29
|
+
resetDisabled = false,
|
|
28
30
|
disabled,
|
|
29
31
|
...props
|
|
30
32
|
}, ref) => {
|
|
@@ -98,7 +100,7 @@ const RangeSlider = React.forwardRef(
|
|
|
98
100
|
SliderPrimitive.Thumb,
|
|
99
101
|
{
|
|
100
102
|
className: rangeSliderThumbVariants(),
|
|
101
|
-
"aria-label": index === 0 ?
|
|
103
|
+
"aria-label": thumbLabels[index === 0 ? 0 : 1]
|
|
102
104
|
},
|
|
103
105
|
index
|
|
104
106
|
))
|
|
@@ -111,8 +113,8 @@ const RangeSlider = React.forwardRef(
|
|
|
111
113
|
{
|
|
112
114
|
type: "button",
|
|
113
115
|
onClick: handleReset,
|
|
114
|
-
disabled,
|
|
115
|
-
className: "text-body-3 font-
|
|
116
|
+
disabled: disabled || resetDisabled,
|
|
117
|
+
className: "rounded-sm text-body-3 font-semibold text-foreground underline decoration-border underline-offset-4 transition-colors cursor-pointer hover:text-primary hover:decoration-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:no-underline disabled:opacity-40 disabled:hover:text-foreground",
|
|
116
118
|
children: resetLabel
|
|
117
119
|
}
|
|
118
120
|
),
|
|
@@ -64,5 +64,12 @@ export interface RangeSliderProps extends Omit<React.ComponentPropsWithoutRef<ty
|
|
|
64
64
|
* Callback fired when the Reset button is clicked.
|
|
65
65
|
*/
|
|
66
66
|
onReset?: () => void;
|
|
67
|
+
/**
|
|
68
|
+
* Accessible names of the two thumbs (translate these).
|
|
69
|
+
* @default ['Minimum value', 'Maximum value']
|
|
70
|
+
*/
|
|
71
|
+
thumbLabels?: [string, string];
|
|
72
|
+
/** Disables Reset (e.g. nothing to reset). */
|
|
73
|
+
resetDisabled?: boolean;
|
|
67
74
|
}
|
|
68
75
|
//# sourceMappingURL=RangeSlider.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.types.d.ts","sourceRoot":"","sources":["../../../src/components/RangeSlider/RangeSlider.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,MAAM,WAAW,gBACf,SAAQ,IAAI,CACR,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,EAC3D,OAAO,GAAG,cAAc,GAAG,eAAe,GAAG,eAAe,CAC7D,EACD,mBAAmB;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"RangeSlider.types.d.ts","sourceRoot":"","sources":["../../../src/components/RangeSlider/RangeSlider.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,MAAM,WAAW,gBACf,SAAQ,IAAI,CACR,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,EAC3D,OAAO,GAAG,cAAc,GAAG,eAAe,GAAG,eAAe,CAC7D,EACD,mBAAmB;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAU1D,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAEhE,eAAO,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAU1D,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAEhE,eAAO,MAAM,MAAM,uFAiIlB,CAAC;AAKF,eAAO,MAAM,UAAU,uCAAuB,CAAC;AAC/C,eAAO,MAAM,aAAa,8GAA0B,CAAC;AACrD,eAAO,MAAM,WAAW,0GAAwB,CAAC;AACjD,eAAO,MAAM,aAAa,2GAA0B,CAAC;AACrD,eAAO,MAAM,UAAU,wGAAuB,CAAC;AAC/C,eAAO,MAAM,WAAW,yGAAwB,CAAC;AACjD,eAAO,MAAM,WAAW,yGAAwB,CAAC;AACjD,eAAO,MAAM,eAAe,6GAA4B,CAAC"}
|
|
@@ -26,8 +26,12 @@ const Select = React.forwardRef(
|
|
|
26
26
|
onValueChange,
|
|
27
27
|
disabled,
|
|
28
28
|
children,
|
|
29
|
+
ariaLabel,
|
|
30
|
+
id: customId,
|
|
29
31
|
...props
|
|
30
32
|
}, ref) => {
|
|
33
|
+
const generatedId = React.useId();
|
|
34
|
+
const triggerId = customId ?? generatedId;
|
|
31
35
|
const isError = Boolean(error);
|
|
32
36
|
const errorMessage = typeof error === "string" ? error : void 0;
|
|
33
37
|
const globalMotion = useGlobalAnimation();
|
|
@@ -38,7 +42,14 @@ const Select = React.forwardRef(
|
|
|
38
42
|
className: cn("flex flex-col gap-1.5 w-full font-sans", containerClassName),
|
|
39
43
|
style,
|
|
40
44
|
children: [
|
|
41
|
-
label && /* @__PURE__ */ jsx(
|
|
45
|
+
label && /* @__PURE__ */ jsx(
|
|
46
|
+
"label",
|
|
47
|
+
{
|
|
48
|
+
htmlFor: triggerId,
|
|
49
|
+
className: "text-body-3 font-semibold text-foreground tracking-wide select-none",
|
|
50
|
+
children: label
|
|
51
|
+
}
|
|
52
|
+
),
|
|
42
53
|
/* @__PURE__ */ jsxs(
|
|
43
54
|
SelectPrimitive.Root,
|
|
44
55
|
{
|
|
@@ -52,6 +63,8 @@ const Select = React.forwardRef(
|
|
|
52
63
|
SelectPrimitive.Trigger,
|
|
53
64
|
{
|
|
54
65
|
ref,
|
|
66
|
+
id: triggerId,
|
|
67
|
+
"aria-label": ariaLabel,
|
|
55
68
|
"data-slot": "select-trigger",
|
|
56
69
|
"aria-invalid": isError ? "true" : void 0,
|
|
57
70
|
className: cn(
|
|
@@ -13,6 +13,10 @@ export interface SelectProps extends Omit<SelectPrimitive.SelectProps, 'children
|
|
|
13
13
|
* Field label displayed above the select trigger.
|
|
14
14
|
*/
|
|
15
15
|
label?: string;
|
|
16
|
+
/** Accessible name when there is no visible `label` (e.g. a compact "Sort by" control). */
|
|
17
|
+
ariaLabel?: string;
|
|
18
|
+
/** Id of the trigger (generated when omitted); the visible label points at it. */
|
|
19
|
+
id?: string;
|
|
16
20
|
/**
|
|
17
21
|
* Placeholder text shown when no value is selected.
|
|
18
22
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.types.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,EACnD,qBAAqB;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B"}
|
|
1
|
+
{"version":3,"file":"Select.types.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,CAAC,EACnD,qBAAqB;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { SnackbarProps } from './Snackbar.types';
|
|
3
|
+
/**
|
|
4
|
+
* One snackbar: message (and an optional second line), one inline action, a small close button.
|
|
5
|
+
* It leaves on its own after `duration` (paused while hovered, focused or touched), when swiped
|
|
6
|
+
* sideways or down, or when its action is used.
|
|
7
|
+
*/
|
|
8
|
+
export declare const Snackbar: React.ForwardRefExoticComponent<SnackbarProps & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
//# sourceMappingURL=Snackbar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Snackbar.d.ts","sourceRoot":"","sources":["../../../src/components/Snackbar/Snackbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAatD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,sFA+FpB,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { AlertCircle, AlertTriangle, CheckCircle, Info, X } from '@timmbr/icons';
|
|
5
|
+
import { cn } from '@timmbr/utils';
|
|
6
|
+
import { snackbarIconVariants, snackbarActionVariants, snackbarCloseVariants, snackbarVariants } from './Snackbar.styles.js';
|
|
7
|
+
|
|
8
|
+
const ICONS = {
|
|
9
|
+
default: Info,
|
|
10
|
+
info: Info,
|
|
11
|
+
success: CheckCircle,
|
|
12
|
+
warning: AlertTriangle,
|
|
13
|
+
destructive: AlertCircle
|
|
14
|
+
};
|
|
15
|
+
const SWIPE_DISMISS_PX = 48;
|
|
16
|
+
const Snackbar = React.forwardRef(
|
|
17
|
+
({ item, onDismiss, dismissLabel = "Dismiss", className, style, ...props }, ref) => {
|
|
18
|
+
const variant = item.variant ?? "default";
|
|
19
|
+
const Icon = ICONS[variant];
|
|
20
|
+
const duration = item.duration ?? (item.action ? 6e3 : 4e3);
|
|
21
|
+
const [paused, setPaused] = React.useState(false);
|
|
22
|
+
const remaining = React.useRef(duration);
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
remaining.current = duration;
|
|
25
|
+
}, [item, duration]);
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
if (!item.open || paused || duration === Infinity) return;
|
|
28
|
+
const started = Date.now();
|
|
29
|
+
const timer = setTimeout(onDismiss, remaining.current);
|
|
30
|
+
return () => {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
remaining.current -= Date.now() - started;
|
|
33
|
+
};
|
|
34
|
+
}, [item, paused, duration, onDismiss]);
|
|
35
|
+
const start = React.useRef(null);
|
|
36
|
+
const [drag, setDrag] = React.useState({ x: 0, y: 0 });
|
|
37
|
+
const onPointerDown = (event) => {
|
|
38
|
+
if (event.target.closest("button")) return;
|
|
39
|
+
start.current = { x: event.clientX, y: event.clientY };
|
|
40
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
41
|
+
setPaused(true);
|
|
42
|
+
};
|
|
43
|
+
const onPointerMove = (event) => {
|
|
44
|
+
if (!start.current) return;
|
|
45
|
+
setDrag({
|
|
46
|
+
x: event.clientX - start.current.x,
|
|
47
|
+
y: Math.max(0, event.clientY - start.current.y)
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
const onPointerEnd = () => {
|
|
51
|
+
if (!start.current) return;
|
|
52
|
+
start.current = null;
|
|
53
|
+
setPaused(false);
|
|
54
|
+
if (Math.abs(drag.x) > SWIPE_DISMISS_PX || drag.y > SWIPE_DISMISS_PX) onDismiss();
|
|
55
|
+
else setDrag({ x: 0, y: 0 });
|
|
56
|
+
};
|
|
57
|
+
const dragging = drag.x !== 0 || drag.y !== 0;
|
|
58
|
+
return /* @__PURE__ */ jsxs(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
ref,
|
|
62
|
+
"data-slot": "snackbar",
|
|
63
|
+
"data-variant": variant,
|
|
64
|
+
"data-state": item.open ? "open" : "closed",
|
|
65
|
+
className: cn(snackbarVariants({ open: item.open }), dragging && "transition-none", className),
|
|
66
|
+
style: {
|
|
67
|
+
...style,
|
|
68
|
+
...dragging && {
|
|
69
|
+
transform: `translate(${drag.x}px, ${drag.y}px)`,
|
|
70
|
+
opacity: Math.max(0.3, 1 - Math.max(Math.abs(drag.x), drag.y) / 200)
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
onMouseEnter: () => setPaused(true),
|
|
74
|
+
onMouseLeave: () => setPaused(false),
|
|
75
|
+
onFocus: () => setPaused(true),
|
|
76
|
+
onBlur: () => setPaused(false),
|
|
77
|
+
onPointerDown,
|
|
78
|
+
onPointerMove,
|
|
79
|
+
onPointerUp: onPointerEnd,
|
|
80
|
+
onPointerCancel: onPointerEnd,
|
|
81
|
+
...props,
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", className: snackbarIconVariants({ variant }) }),
|
|
84
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 py-1", children: [
|
|
85
|
+
/* @__PURE__ */ jsx("p", { className: "text-body-2 font-medium leading-snug", children: item.message }),
|
|
86
|
+
item.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-body-3 leading-snug text-grey-300", children: item.description })
|
|
87
|
+
] }),
|
|
88
|
+
item.action && /* @__PURE__ */ jsx(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
className: snackbarActionVariants(),
|
|
93
|
+
onClick: (event) => {
|
|
94
|
+
item.action?.onClick(event);
|
|
95
|
+
onDismiss();
|
|
96
|
+
},
|
|
97
|
+
children: item.action.label
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsx("button", { type: "button", "aria-label": dismissLabel, className: snackbarCloseVariants(), onClick: onDismiss, children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-4" }) })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
Snackbar.displayName = "Snackbar";
|
|
107
|
+
|
|
108
|
+
export { Snackbar };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bottom of the screen, above anything pinned there (pages set --toast-offset-bottom to a bar's
|
|
3
|
+
* height) and the home indicator. Full width minus margins on phones; a compact bar centred on
|
|
4
|
+
* wider screens.
|
|
5
|
+
*/
|
|
6
|
+
export declare const snackbarHostVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
7
|
+
/** Inverse surface (dark bar, light text), as on Android and most shopping apps. */
|
|
8
|
+
export declare const snackbarVariants: (props?: ({
|
|
9
|
+
open?: boolean | null | undefined;
|
|
10
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
|
+
export declare const snackbarIconVariants: (props?: ({
|
|
12
|
+
variant?: "default" | "destructive" | "success" | "warning" | "info" | null | undefined;
|
|
13
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
14
|
+
/** Text action: light primary on the dark bar (high contrast), no button chrome. */
|
|
15
|
+
export declare const snackbarActionVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
16
|
+
export declare const snackbarCloseVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
17
|
+
//# sourceMappingURL=Snackbar.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Snackbar.styles.d.ts","sourceRoot":"","sources":["../../../src/components/Snackbar/Snackbar.styles.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,oFAEhC,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,gBAAgB;;8EAW5B,CAAC;AAEF,eAAO,MAAM,oBAAoB;;8EAW/B,CAAC;AAEH,oFAAoF;AACpF,eAAO,MAAM,sBAAsB,oFAElC,CAAC;AAEF,eAAO,MAAM,qBAAqB,oFAEjC,CAAC"}
|