@seekalfred/primitives 0.6.0 → 0.7.1
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/README.md +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from "../lib/motion.js";
|
|
4
|
+
import { Button } from "../button/button.js";
|
|
5
|
+
import { useCallback, useId, useRef, useState } from "react";
|
|
6
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/card-stack/card-stack.tsx
|
|
8
|
+
/** Cards rendered in the stack. The last one waits invisibly one step behind and fades in as it rises. */
|
|
9
|
+
const VISIBLE = 4;
|
|
10
|
+
/** Degrees of rotation per pixel of horizontal travel. */
|
|
11
|
+
const ROTATE = .065;
|
|
12
|
+
/** A release faster than this (px/s) throws the card even when it has only moved a little. */
|
|
13
|
+
const FLICK = 500;
|
|
14
|
+
/** Drag distance at which the cards behind have fully risen. */
|
|
15
|
+
const LIFT_DISTANCE = 160;
|
|
16
|
+
/** Vertical travel is not a decision, so past a small free zone it resists like a rubber band. */
|
|
17
|
+
const FREE_Y = 48;
|
|
18
|
+
const STRETCH_Y = 140;
|
|
19
|
+
/** The stamp is fully shown at 96px of travel and starts at 16px. */
|
|
20
|
+
const STAMP_FROM = 16;
|
|
21
|
+
/** A thrown card is gone this long after it starts (the fade finishes well before); the backstop for a transition that never ends. */
|
|
22
|
+
const FLIGHT_BACKSTOP_MS = 1500;
|
|
23
|
+
const resistY = (raw) => {
|
|
24
|
+
const distance = Math.abs(raw);
|
|
25
|
+
return distance <= FREE_Y ? raw : Math.sign(raw) * (FREE_Y + (1 - 1 / ((distance - FREE_Y) * .55 / STRETCH_Y + 1)) * STRETCH_Y);
|
|
26
|
+
};
|
|
27
|
+
const unresistY = (shown) => {
|
|
28
|
+
const distance = Math.abs(shown);
|
|
29
|
+
if (distance <= FREE_Y) return shown;
|
|
30
|
+
const stretch = Math.min(distance - FREE_Y, 139);
|
|
31
|
+
return Math.sign(shown) * (FREE_Y + (1 / (1 - stretch / STRETCH_Y) - 1) * STRETCH_Y / .55);
|
|
32
|
+
};
|
|
33
|
+
const clampUnit = (value) => Math.min(1, Math.max(0, value));
|
|
34
|
+
/** Where the card is now, mid-flight included: the computed translate, in px. */
|
|
35
|
+
function readOffset(el) {
|
|
36
|
+
const value = getComputedStyle(el).translate;
|
|
37
|
+
if (!value || value === "none") return {
|
|
38
|
+
x: 0,
|
|
39
|
+
y: 0
|
|
40
|
+
};
|
|
41
|
+
const [x, y] = value.split(" ").map(parseFloat);
|
|
42
|
+
return {
|
|
43
|
+
x: x || 0,
|
|
44
|
+
y: y || 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function velocityOf(samples, now) {
|
|
48
|
+
const recent = samples.filter((sample) => now - sample.t <= 80);
|
|
49
|
+
const first = recent[0];
|
|
50
|
+
const last = recent[recent.length - 1];
|
|
51
|
+
if (!first || !last || first === last || now - last.t > 60) return {
|
|
52
|
+
x: 0,
|
|
53
|
+
y: 0
|
|
54
|
+
};
|
|
55
|
+
const seconds = (last.t - first.t) / 1e3;
|
|
56
|
+
return {
|
|
57
|
+
x: (last.x - first.x) / seconds,
|
|
58
|
+
y: (last.y - first.y) / seconds
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const iconProps = {
|
|
62
|
+
width: 16,
|
|
63
|
+
height: 16,
|
|
64
|
+
viewBox: "0 0 24 24",
|
|
65
|
+
fill: "none",
|
|
66
|
+
stroke: "currentColor",
|
|
67
|
+
strokeWidth: 1.75,
|
|
68
|
+
strokeLinecap: "round",
|
|
69
|
+
strokeLinejoin: "round",
|
|
70
|
+
"aria-hidden": true
|
|
71
|
+
};
|
|
72
|
+
const CheckIcon = () => /* @__PURE__ */ jsx("svg", {
|
|
73
|
+
...iconProps,
|
|
74
|
+
children: /* @__PURE__ */ jsx("path", { d: "M5 12.5 10 17.5 19 7" })
|
|
75
|
+
});
|
|
76
|
+
const CloseIcon = () => /* @__PURE__ */ jsx("svg", {
|
|
77
|
+
...iconProps,
|
|
78
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6 6 18" })
|
|
79
|
+
});
|
|
80
|
+
const UndoIcon = () => /* @__PURE__ */ jsx("svg", {
|
|
81
|
+
...iconProps,
|
|
82
|
+
children: /* @__PURE__ */ jsx("path", { d: "M4 12a8 8 0 1 0 2.5-5.8M4 4v4.5h4.5" })
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* A control that stays focusable when it has nothing to do: React Aria drops `aria-disabled` and a disabled button would
|
|
86
|
+
* drop focus to the page when the stack runs out, so the attribute is set by hand and the press is guarded.
|
|
87
|
+
*/
|
|
88
|
+
function Control({ disabled, onPress, ...rest }) {
|
|
89
|
+
const ref = useRef(null);
|
|
90
|
+
useIsomorphicLayoutEffect(() => {
|
|
91
|
+
if (disabled) ref.current?.setAttribute("aria-disabled", "true");
|
|
92
|
+
else ref.current?.removeAttribute("aria-disabled");
|
|
93
|
+
}, [disabled]);
|
|
94
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
95
|
+
...rest,
|
|
96
|
+
ref,
|
|
97
|
+
className: "aria-disabled:cursor-default aria-disabled:opacity-40",
|
|
98
|
+
onPress: () => {
|
|
99
|
+
if (!disabled) onPress();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Renders a custom empty state; the reset handler travels as a prop so render never touches the stack's refs. */
|
|
104
|
+
function EmptySlot({ render, onReset }) {
|
|
105
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: render(onReset) });
|
|
106
|
+
}
|
|
107
|
+
const HOME_TRANSITION = (delay) => `translate var(--duration-smooth) var(--ease-smooth) ${delay}ms, rotate var(--duration-smooth) var(--ease-smooth) ${delay}ms, opacity var(--duration-quick) var(--ease-enter) ${delay}ms`;
|
|
108
|
+
const SPRING_BACK = "translate var(--duration-morph) var(--ease-morph), rotate var(--duration-morph) var(--ease-morph)";
|
|
109
|
+
const THROW = "translate var(--duration-calm) var(--ease-calm), rotate var(--duration-calm) var(--ease-calm), opacity 220ms var(--ease-settle) 120ms";
|
|
110
|
+
const REDUCED_FADE = "opacity 150ms linear";
|
|
111
|
+
function StackCard({ id, depth, decision, returnFrom, interactive, zIndex, label, labels, stage, fling, onRelease, onThrown, onGone, children }) {
|
|
112
|
+
const card = useRef(null);
|
|
113
|
+
const tilt = useRef(returnFrom?.tilt ?? 1);
|
|
114
|
+
const drag = useRef(null);
|
|
115
|
+
const settled = useRef(!returnFrom);
|
|
116
|
+
const wasDecided = useRef(false);
|
|
117
|
+
const lift = (value) => stage.current?.style.setProperty("--lift", String(value));
|
|
118
|
+
/** Writes the card's place and what follows from it (its tilt and its stamps) straight to the DOM: a drag never re-renders. */
|
|
119
|
+
const place = (el, x, y) => {
|
|
120
|
+
el.style.translate = `${x}px ${y}px`;
|
|
121
|
+
el.style.rotate = `${x * ROTATE * tilt.current}deg`;
|
|
122
|
+
el.style.setProperty("--keep", String(clampUnit((x - STAMP_FROM) / 80)));
|
|
123
|
+
el.style.setProperty("--pass", String(clampUnit((-16 - x) / 80)));
|
|
124
|
+
};
|
|
125
|
+
useIsomorphicLayoutEffect(() => {
|
|
126
|
+
const el = card.current;
|
|
127
|
+
if (!el) return;
|
|
128
|
+
const reduced = prefersReducedMotion();
|
|
129
|
+
if (decision) {
|
|
130
|
+
drag.current = null;
|
|
131
|
+
el.removeAttribute("data-dragging");
|
|
132
|
+
wasDecided.current = true;
|
|
133
|
+
const direction = decision === "right" ? 1 : -1;
|
|
134
|
+
const { x: cx, y: cy } = readOffset(el);
|
|
135
|
+
const velocity = fling(id) ?? {
|
|
136
|
+
x: direction * 900,
|
|
137
|
+
y: -120
|
|
138
|
+
};
|
|
139
|
+
const speed = Math.hypot(velocity.x, velocity.y);
|
|
140
|
+
let ux = velocity.x;
|
|
141
|
+
let uy = velocity.y;
|
|
142
|
+
if (speed < 200 || Math.sign(ux) !== direction) {
|
|
143
|
+
ux = Math.abs(cx) > 8 ? cx : direction;
|
|
144
|
+
uy = Math.abs(cx) > 8 ? cy * .5 : -.12;
|
|
145
|
+
}
|
|
146
|
+
const length = Math.hypot(ux, uy) || 1;
|
|
147
|
+
ux /= length;
|
|
148
|
+
uy /= length;
|
|
149
|
+
if (Math.abs(ux) < .45) {
|
|
150
|
+
ux = direction * .45;
|
|
151
|
+
uy = Math.sign(uy || -1) * Math.sqrt(.7975);
|
|
152
|
+
}
|
|
153
|
+
const reach = el.offsetWidth * 1.6;
|
|
154
|
+
const exit = {
|
|
155
|
+
x: cx + ux * reach,
|
|
156
|
+
y: cy + uy * reach,
|
|
157
|
+
tilt: tilt.current
|
|
158
|
+
};
|
|
159
|
+
onThrown(id, exit);
|
|
160
|
+
el.style.setProperty(direction > 0 ? "--keep" : "--pass", "1");
|
|
161
|
+
if (reduced) el.style.transition = REDUCED_FADE;
|
|
162
|
+
else {
|
|
163
|
+
el.style.transition = THROW;
|
|
164
|
+
place(el, exit.x, exit.y);
|
|
165
|
+
}
|
|
166
|
+
el.style.opacity = "0";
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!wasDecided.current && settled.current) return;
|
|
170
|
+
wasDecided.current = false;
|
|
171
|
+
const delay = returnFrom?.delay ?? 0;
|
|
172
|
+
if (!settled.current && !reduced && returnFrom) {
|
|
173
|
+
el.style.transition = "none";
|
|
174
|
+
place(el, returnFrom.x, returnFrom.y);
|
|
175
|
+
el.style.opacity = "0";
|
|
176
|
+
el.offsetWidth;
|
|
177
|
+
}
|
|
178
|
+
settled.current = true;
|
|
179
|
+
if (reduced) {
|
|
180
|
+
el.style.transition = `${REDUCED_FADE} ${delay}ms`;
|
|
181
|
+
place(el, 0, 0);
|
|
182
|
+
} else {
|
|
183
|
+
el.style.transition = HOME_TRANSITION(delay);
|
|
184
|
+
place(el, 0, 0);
|
|
185
|
+
}
|
|
186
|
+
el.style.opacity = "1";
|
|
187
|
+
}, [decision]);
|
|
188
|
+
const gone = useCallback(() => onGone(id), [onGone, id]);
|
|
189
|
+
useExitBackstop(decision !== null, gone, FLIGHT_BACKSTOP_MS);
|
|
190
|
+
function down(event) {
|
|
191
|
+
if (!interactive || event.button !== 0 || !event.isPrimary) return;
|
|
192
|
+
if (event.target instanceof Element && event.target.closest("button, a, input, select, textarea, [role='button']")) return;
|
|
193
|
+
const el = event.currentTarget;
|
|
194
|
+
const now = readOffset(el);
|
|
195
|
+
el.style.transition = "none";
|
|
196
|
+
if (Math.abs(now.x) < 2) {
|
|
197
|
+
const rect = el.getBoundingClientRect();
|
|
198
|
+
tilt.current = event.clientY < rect.top + rect.height / 2 ? 1 : -1;
|
|
199
|
+
}
|
|
200
|
+
place(el, now.x, now.y);
|
|
201
|
+
drag.current = {
|
|
202
|
+
ox: event.clientX - now.x,
|
|
203
|
+
oy: event.clientY - unresistY(now.y),
|
|
204
|
+
samples: [{
|
|
205
|
+
t: event.timeStamp,
|
|
206
|
+
x: event.clientX,
|
|
207
|
+
y: event.clientY
|
|
208
|
+
}]
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
el.setPointerCapture(event.pointerId);
|
|
212
|
+
} catch {}
|
|
213
|
+
el.setAttribute("data-dragging", "");
|
|
214
|
+
}
|
|
215
|
+
function move(event) {
|
|
216
|
+
const state = drag.current;
|
|
217
|
+
if (!state || !interactive) return;
|
|
218
|
+
const x = event.clientX - state.ox;
|
|
219
|
+
const y = resistY(event.clientY - state.oy);
|
|
220
|
+
place(event.currentTarget, x, y);
|
|
221
|
+
lift(clampUnit(Math.hypot(x, y * .5) / LIFT_DISTANCE));
|
|
222
|
+
state.samples.push({
|
|
223
|
+
t: event.timeStamp,
|
|
224
|
+
x: event.clientX,
|
|
225
|
+
y: event.clientY
|
|
226
|
+
});
|
|
227
|
+
if (state.samples.length > 12) state.samples.shift();
|
|
228
|
+
}
|
|
229
|
+
function up(event) {
|
|
230
|
+
const state = drag.current;
|
|
231
|
+
drag.current = null;
|
|
232
|
+
const el = event.currentTarget;
|
|
233
|
+
el.removeAttribute("data-dragging");
|
|
234
|
+
if (el.hasPointerCapture(event.pointerId)) el.releasePointerCapture(event.pointerId);
|
|
235
|
+
if (!state || !interactive) return;
|
|
236
|
+
const velocity = event.type === "pointercancel" ? {
|
|
237
|
+
x: 0,
|
|
238
|
+
y: 0
|
|
239
|
+
} : velocityOf(state.samples, event.timeStamp);
|
|
240
|
+
if (onRelease(id, readOffset(el), velocity, el.offsetWidth)) return;
|
|
241
|
+
el.style.transition = prefersReducedMotion() ? "none" : SPRING_BACK;
|
|
242
|
+
place(el, 0, 0);
|
|
243
|
+
lift(0);
|
|
244
|
+
}
|
|
245
|
+
return /* @__PURE__ */ jsx("div", {
|
|
246
|
+
className: "card-stack-slot",
|
|
247
|
+
style: {
|
|
248
|
+
"--depth": depth,
|
|
249
|
+
zIndex
|
|
250
|
+
},
|
|
251
|
+
"aria-hidden": interactive ? void 0 : true,
|
|
252
|
+
ref: (node) => {
|
|
253
|
+
if (!node) return;
|
|
254
|
+
if (interactive) node.removeAttribute("inert");
|
|
255
|
+
else node.setAttribute("inert", "");
|
|
256
|
+
},
|
|
257
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
258
|
+
ref: card,
|
|
259
|
+
className: "card-stack-card",
|
|
260
|
+
"data-top": interactive ? "" : void 0,
|
|
261
|
+
"data-flying": decision ? "" : void 0,
|
|
262
|
+
role: interactive ? "group" : void 0,
|
|
263
|
+
"aria-label": interactive ? label : void 0,
|
|
264
|
+
onPointerDown: down,
|
|
265
|
+
onPointerMove: move,
|
|
266
|
+
onPointerUp: up,
|
|
267
|
+
onPointerCancel: up,
|
|
268
|
+
onTransitionEnd: (event) => {
|
|
269
|
+
if (decision && event.target === event.currentTarget && event.propertyName === "opacity") gone();
|
|
270
|
+
},
|
|
271
|
+
children: [
|
|
272
|
+
/* @__PURE__ */ jsxs("div", {
|
|
273
|
+
className: "card-stack-face",
|
|
274
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
275
|
+
className: "card-stack-shade",
|
|
276
|
+
"aria-hidden": "true"
|
|
277
|
+
})]
|
|
278
|
+
}),
|
|
279
|
+
/* @__PURE__ */ jsxs("span", {
|
|
280
|
+
className: "card-stack-stamp",
|
|
281
|
+
"data-side": "right",
|
|
282
|
+
"aria-hidden": "true",
|
|
283
|
+
children: [/* @__PURE__ */ jsx(CheckIcon, {}), labels.right]
|
|
284
|
+
}),
|
|
285
|
+
/* @__PURE__ */ jsxs("span", {
|
|
286
|
+
className: "card-stack-stamp",
|
|
287
|
+
"data-side": "left",
|
|
288
|
+
"aria-hidden": "true",
|
|
289
|
+
children: [/* @__PURE__ */ jsx(CloseIcon, {}), labels.left]
|
|
290
|
+
})
|
|
291
|
+
]
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function CardStack({ items, getKey, getLabel, renderCard, onDecide, onUndo, onReset, labels = {
|
|
296
|
+
left: "Pass",
|
|
297
|
+
right: "Keep"
|
|
298
|
+
}, outcomes, label = "Cards", renderEmpty, className }) {
|
|
299
|
+
const hintId = useId();
|
|
300
|
+
const stage = useRef(null);
|
|
301
|
+
const [position, setPosition] = useState(0);
|
|
302
|
+
const [history, setHistory] = useState([]);
|
|
303
|
+
const [flying, setFlying] = useState([]);
|
|
304
|
+
const [returning, setReturning] = useState({});
|
|
305
|
+
const [message, setMessage] = useState("");
|
|
306
|
+
const exits = useRef({});
|
|
307
|
+
const flings = useRef(/* @__PURE__ */ new Map());
|
|
308
|
+
const top = items[position];
|
|
309
|
+
const remaining = Math.max(0, items.length - position);
|
|
310
|
+
function decide(decision) {
|
|
311
|
+
if (!top) return;
|
|
312
|
+
const key = getKey(top);
|
|
313
|
+
stage.current?.style.setProperty("--lift", "0");
|
|
314
|
+
setPosition(position + 1);
|
|
315
|
+
setHistory([...history, {
|
|
316
|
+
key,
|
|
317
|
+
decision
|
|
318
|
+
}]);
|
|
319
|
+
setFlying((current) => [...current, key]);
|
|
320
|
+
setReturning({});
|
|
321
|
+
const left = remaining - 1;
|
|
322
|
+
setMessage(`${getLabel(top)}${outcomes ? ` ${outcomes[decision]}` : `: ${labels[decision]}`}. ${left ? `${left} left.` : "All cards reviewed."}`);
|
|
323
|
+
onDecide?.(top, decision);
|
|
324
|
+
}
|
|
325
|
+
function undo() {
|
|
326
|
+
const last = history[history.length - 1];
|
|
327
|
+
const item = last && items.find((entry) => getKey(entry) === last.key);
|
|
328
|
+
if (!last || !item) return;
|
|
329
|
+
const exit = exits.current[last.key];
|
|
330
|
+
setPosition(position - 1);
|
|
331
|
+
setHistory(history.slice(0, -1));
|
|
332
|
+
setFlying((current) => current.filter((key) => key !== last.key));
|
|
333
|
+
setReturning(exit ? { [last.key]: {
|
|
334
|
+
...exit,
|
|
335
|
+
delay: 0
|
|
336
|
+
} } : {});
|
|
337
|
+
setMessage(`${getLabel(item)} restored.`);
|
|
338
|
+
onUndo?.(item, last.decision);
|
|
339
|
+
}
|
|
340
|
+
function reset() {
|
|
341
|
+
if (!history.length) return;
|
|
342
|
+
const count = Math.min(VISIBLE, items.length);
|
|
343
|
+
const back = {};
|
|
344
|
+
items.slice(0, count).forEach((item, index) => {
|
|
345
|
+
const key = getKey(item);
|
|
346
|
+
const exit = exits.current[key];
|
|
347
|
+
if (exit && index < position) back[key] = {
|
|
348
|
+
...exit,
|
|
349
|
+
delay: (count - 1 - index) * 60
|
|
350
|
+
};
|
|
351
|
+
});
|
|
352
|
+
setPosition(0);
|
|
353
|
+
setHistory([]);
|
|
354
|
+
setFlying([]);
|
|
355
|
+
setReturning(back);
|
|
356
|
+
setMessage("All cards are back in the stack.");
|
|
357
|
+
onReset?.();
|
|
358
|
+
}
|
|
359
|
+
function release(key, offset, velocity, width) {
|
|
360
|
+
const direction = Math.sign(offset.x);
|
|
361
|
+
if (!direction || !top || getKey(top) !== key) return false;
|
|
362
|
+
const flick = Math.abs(velocity.x) > FLICK && Math.sign(velocity.x) === direction && Math.abs(offset.x) > 12;
|
|
363
|
+
const far = Math.abs(offset.x) > Math.min(width * .35, 140) && !(Math.sign(velocity.x) === -direction && Math.abs(velocity.x) > 300);
|
|
364
|
+
if (!flick && !far) return false;
|
|
365
|
+
flings.current.set(key, velocity);
|
|
366
|
+
decide(direction > 0 ? "right" : "left");
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
function onKeyDown(event) {
|
|
370
|
+
if (event.target !== event.currentTarget) return;
|
|
371
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
372
|
+
event.preventDefault();
|
|
373
|
+
decide(event.key === "ArrowRight" ? "right" : "left");
|
|
374
|
+
} else if (event.key === "Backspace" || (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "z") {
|
|
375
|
+
event.preventDefault();
|
|
376
|
+
undo();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const decided = new Map(history.map((entry) => [entry.key, entry.decision]));
|
|
380
|
+
const windowKeys = new Set(items.slice(position, position + VISIBLE).map(getKey));
|
|
381
|
+
const rendered = items.filter((item) => windowKeys.has(getKey(item)) || flying.includes(getKey(item)));
|
|
382
|
+
const empty = position >= items.length && flying.length === 0;
|
|
383
|
+
const takeFling = useCallback((key) => {
|
|
384
|
+
const velocity = flings.current.get(key);
|
|
385
|
+
flings.current.delete(key);
|
|
386
|
+
return velocity;
|
|
387
|
+
}, []);
|
|
388
|
+
const recordExit = useCallback((key, exit) => {
|
|
389
|
+
exits.current[key] = exit;
|
|
390
|
+
}, []);
|
|
391
|
+
const finish = useCallback((key) => setFlying((current) => current.filter((entry) => entry !== key)), []);
|
|
392
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
393
|
+
"data-slot": "card-stack",
|
|
394
|
+
className: cn("card-stack", className),
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ jsxs("div", {
|
|
397
|
+
ref: stage,
|
|
398
|
+
className: "card-stack-stage",
|
|
399
|
+
role: "application",
|
|
400
|
+
"aria-roledescription": "card stack",
|
|
401
|
+
"aria-label": label,
|
|
402
|
+
"aria-describedby": hintId,
|
|
403
|
+
tabIndex: 0,
|
|
404
|
+
onKeyDown,
|
|
405
|
+
children: [rendered.map((item) => {
|
|
406
|
+
const id = getKey(item);
|
|
407
|
+
const index = items.indexOf(item);
|
|
408
|
+
const decision = decided.get(id) ?? null;
|
|
409
|
+
const depth = decision ? 0 : index - position;
|
|
410
|
+
return /* @__PURE__ */ jsx(StackCard, {
|
|
411
|
+
id,
|
|
412
|
+
depth,
|
|
413
|
+
decision,
|
|
414
|
+
returnFrom: returning[id],
|
|
415
|
+
interactive: !decision && depth === 0,
|
|
416
|
+
zIndex: decision ? 20 + flying.indexOf(id) : VISIBLE - depth,
|
|
417
|
+
label: `${getLabel(item)}, ${index + 1} of ${items.length}`,
|
|
418
|
+
labels,
|
|
419
|
+
stage,
|
|
420
|
+
fling: takeFling,
|
|
421
|
+
onRelease: release,
|
|
422
|
+
onThrown: recordExit,
|
|
423
|
+
onGone: finish,
|
|
424
|
+
children: renderCard(item)
|
|
425
|
+
}, id);
|
|
426
|
+
}), empty ? /* @__PURE__ */ jsx("div", {
|
|
427
|
+
className: "card-stack-empty",
|
|
428
|
+
children: renderEmpty ? /* @__PURE__ */ jsx(EmptySlot, {
|
|
429
|
+
render: renderEmpty,
|
|
430
|
+
onReset: reset
|
|
431
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
|
|
432
|
+
className: "m-0 text-base font-medium",
|
|
433
|
+
children: "All cards reviewed"
|
|
434
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
435
|
+
variant: "outline",
|
|
436
|
+
onPress: reset,
|
|
437
|
+
startIcon: /* @__PURE__ */ jsx(UndoIcon, {}),
|
|
438
|
+
children: "Start over"
|
|
439
|
+
})] })
|
|
440
|
+
}) : null]
|
|
441
|
+
}),
|
|
442
|
+
/* @__PURE__ */ jsxs("div", {
|
|
443
|
+
className: "flex flex-wrap justify-center gap-2",
|
|
444
|
+
children: [
|
|
445
|
+
/* @__PURE__ */ jsx(Control, {
|
|
446
|
+
variant: "outline",
|
|
447
|
+
disabled: !top,
|
|
448
|
+
onPress: () => decide("left"),
|
|
449
|
+
startIcon: /* @__PURE__ */ jsx(CloseIcon, {}),
|
|
450
|
+
children: labels.left
|
|
451
|
+
}),
|
|
452
|
+
/* @__PURE__ */ jsx(Control, {
|
|
453
|
+
variant: "ghost",
|
|
454
|
+
disabled: !history.length,
|
|
455
|
+
onPress: undo,
|
|
456
|
+
startIcon: /* @__PURE__ */ jsx(UndoIcon, {}),
|
|
457
|
+
children: "Undo"
|
|
458
|
+
}),
|
|
459
|
+
/* @__PURE__ */ jsx(Control, {
|
|
460
|
+
variant: "primary",
|
|
461
|
+
disabled: !top,
|
|
462
|
+
onPress: () => decide("right"),
|
|
463
|
+
startIcon: /* @__PURE__ */ jsx(CheckIcon, {}),
|
|
464
|
+
children: labels.right
|
|
465
|
+
})
|
|
466
|
+
]
|
|
467
|
+
}),
|
|
468
|
+
/* @__PURE__ */ jsxs("p", {
|
|
469
|
+
id: hintId,
|
|
470
|
+
className: "sr-only",
|
|
471
|
+
children: [
|
|
472
|
+
"Drag the top card, or use the left and right arrow keys to ",
|
|
473
|
+
labels.left.toLowerCase(),
|
|
474
|
+
" or",
|
|
475
|
+
" ",
|
|
476
|
+
labels.right.toLowerCase(),
|
|
477
|
+
" it. Backspace undoes the last decision."
|
|
478
|
+
]
|
|
479
|
+
}),
|
|
480
|
+
/* @__PURE__ */ jsx("p", {
|
|
481
|
+
className: "sr-only",
|
|
482
|
+
role: "status",
|
|
483
|
+
"aria-live": "polite",
|
|
484
|
+
children: message
|
|
485
|
+
})
|
|
486
|
+
]
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
//#endregion
|
|
490
|
+
export { CardStack };
|
|
491
|
+
|
|
492
|
+
//# sourceMappingURL=card-stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card-stack.js","names":[],"sources":["../../src/card-stack/card-stack.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useId,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n type RefObject,\n type SVGProps,\n} from \"react\";\n\nimport { Button, type ButtonProps } from \"../button/button.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\n\nexport type CardDecision = \"left\" | \"right\";\n\n/**\n * CardStack: a deck of cards reviewed one at a time. Drag or flick the top card\n * left or right, or use the buttons and the arrow keys. The cards behind peek\n * out below it and rise as it leaves; a card in the hand tips the way it is\n * moving and shows a stamp (the action it will take); let go early and it springs\n * home, let go far enough, or fast enough, and it is thrown. Undo brings the\n * last card back from where it left, and Start over deals the deck again.\n *\n * Use it for quick triage where each item deserves a moment of attention:\n * shortlisting destinations, candidates or design options. Use a list or a\n * table when people need to compare items side by side.\n *\n * Keyboard: the stack takes one tab stop; Left and Right decide the top card,\n * Backspace (or Ctrl or Cmd with Z) undoes. Every decision is announced politely\n * and the buttons do the same, so nothing needs a drag. Reduced motion fades the\n * card out and back instead of throwing it.\n */\nexport interface CardStackProps<T> {\n items: T[];\n /** A stable key for an item. */\n getKey: (item: T) => string;\n /** A short name for the card, used in its accessible label and announcements, such as \"Lisbon\". */\n getLabel: (item: T) => string;\n /** The card's content. It fills the card, whose size the stack sets. */\n renderCard: (item: T) => ReactNode;\n /** Called when a card is decided. */\n onDecide?: (item: T, decision: CardDecision) => void;\n /** Called when a decision is undone. */\n onUndo?: (item: T, decision: CardDecision) => void;\n /** Called when the deck is dealt again. */\n onReset?: () => void;\n /** Action names for each side, used on the buttons and the stamps that appear while dragging. @default { left: \"Pass\", right: \"Keep\" } */\n labels?: { left: string; right: string };\n /** Past tense of each action for announcements, such as \"skipped\" and \"shortlisted\". Without it a decision reads \"Lisbon: Keep\". */\n outcomes?: { left: string; right: string };\n /** Accessible name for the stack. @default \"Cards\" */\n label?: string;\n /** Shown once every card has been decided. Receives a function that brings all cards back. */\n renderEmpty?: (reset: () => void) => ReactNode;\n /** Extra classes for the stack. Merged last. */\n className?: string;\n}\n\ninterface Point {\n x: number;\n y: number;\n}\ntype Exit = Point & { tilt: number };\ntype Return = Exit & { delay: number };\n\n/** Cards rendered in the stack. The last one waits invisibly one step behind and fades in as it rises. */\nconst VISIBLE = 4;\n/** Degrees of rotation per pixel of horizontal travel. */\nconst ROTATE = 0.065;\n/** A release faster than this (px/s) throws the card even when it has only moved a little. */\nconst FLICK = 500;\n/** Drag distance at which the cards behind have fully risen. */\nconst LIFT_DISTANCE = 160;\n/** Vertical travel is not a decision, so past a small free zone it resists like a rubber band. */\nconst FREE_Y = 48;\nconst STRETCH_Y = 140;\n/** The stamp is fully shown at 96px of travel and starts at 16px. */\nconst STAMP_FROM = 16;\nconst STAMP_TO = 96;\n/** A thrown card is gone this long after it starts (the fade finishes well before); the backstop for a transition that never ends. */\nconst FLIGHT_BACKSTOP_MS = 1500;\n\nconst resistY = (raw: number) => {\n const distance = Math.abs(raw);\n return distance <= FREE_Y\n ? raw\n : Math.sign(raw) *\n (FREE_Y + (1 - 1 / (((distance - FREE_Y) * 0.55) / STRETCH_Y + 1)) * STRETCH_Y);\n};\nconst unresistY = (shown: number) => {\n const distance = Math.abs(shown);\n if (distance <= FREE_Y) return shown;\n const stretch = Math.min(distance - FREE_Y, STRETCH_Y - 1);\n return Math.sign(shown) * (FREE_Y + ((1 / (1 - stretch / STRETCH_Y) - 1) * STRETCH_Y) / 0.55);\n};\nconst clampUnit = (value: number) => Math.min(1, Math.max(0, value));\n\n/** Where the card is now, mid-flight included: the computed translate, in px. */\nfunction readOffset(el: HTMLElement): Point {\n const value = getComputedStyle(el).translate;\n if (!value || value === \"none\") return { x: 0, y: 0 };\n const [x, y] = value.split(\" \").map(parseFloat);\n return { x: x || 0, y: y || 0 };\n}\n\nfunction velocityOf(samples: { t: number; x: number; y: number }[], now: number): Point {\n const recent = samples.filter((sample) => now - sample.t <= 80);\n const first = recent[0];\n const last = recent[recent.length - 1];\n if (!first || !last || first === last || now - last.t > 60) return { x: 0, y: 0 };\n const seconds = (last.t - first.t) / 1000;\n return { x: (last.x - first.x) / seconds, y: (last.y - first.y) / seconds };\n}\n\nconst iconProps: SVGProps<SVGSVGElement> = {\n width: 16,\n height: 16,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 1.75,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n \"aria-hidden\": true,\n};\nconst CheckIcon = () => (\n <svg {...iconProps}>\n <path d=\"M5 12.5 10 17.5 19 7\" />\n </svg>\n);\nconst CloseIcon = () => (\n <svg {...iconProps}>\n <path d=\"M6 6l12 12M18 6 6 18\" />\n </svg>\n);\nconst UndoIcon = () => (\n <svg {...iconProps}>\n <path d=\"M4 12a8 8 0 1 0 2.5-5.8M4 4v4.5h4.5\" />\n </svg>\n);\n\n/**\n * A control that stays focusable when it has nothing to do: React Aria drops `aria-disabled` and a disabled button would\n * drop focus to the page when the stack runs out, so the attribute is set by hand and the press is guarded.\n */\nfunction Control({\n disabled,\n onPress,\n ...rest\n}: { disabled: boolean; onPress: () => void } & Omit<ButtonProps, \"onPress\" | \"isDisabled\">) {\n const ref = useRef<HTMLButtonElement>(null);\n useIsomorphicLayoutEffect(() => {\n if (disabled) ref.current?.setAttribute(\"aria-disabled\", \"true\");\n else ref.current?.removeAttribute(\"aria-disabled\");\n }, [disabled]);\n return (\n <Button\n {...rest}\n ref={ref}\n className=\"aria-disabled:cursor-default aria-disabled:opacity-40\"\n onPress={() => {\n if (!disabled) onPress();\n }}\n />\n );\n}\n\n/** Renders a custom empty state; the reset handler travels as a prop so render never touches the stack's refs. */\nfunction EmptySlot({\n render,\n onReset,\n}: {\n render: (reset: () => void) => ReactNode;\n onReset: () => void;\n}) {\n return <>{render(onReset)}</>;\n}\n\nconst HOME_TRANSITION = (delay: number) =>\n `translate var(--duration-smooth) var(--ease-smooth) ${delay}ms, rotate var(--duration-smooth) var(--ease-smooth) ${delay}ms, opacity var(--duration-quick) var(--ease-enter) ${delay}ms`;\nconst SPRING_BACK =\n \"translate var(--duration-morph) var(--ease-morph), rotate var(--duration-morph) var(--ease-morph)\";\nconst THROW =\n \"translate var(--duration-calm) var(--ease-calm), rotate var(--duration-calm) var(--ease-calm), opacity 220ms var(--ease-settle) 120ms\";\nconst REDUCED_FADE = \"opacity 150ms linear\";\n\ninterface CardProps {\n id: string;\n depth: number;\n decision: CardDecision | null;\n returnFrom?: Return;\n interactive: boolean;\n zIndex: number;\n label: string;\n labels: { left: string; right: string };\n stage: RefObject<HTMLDivElement | null>;\n fling: (id: string) => Point | undefined;\n onRelease: (id: string, offset: Point, velocity: Point, width: number) => boolean;\n onThrown: (id: string, exit: Exit) => void;\n onGone: (id: string) => void;\n children: ReactNode;\n}\n\nfunction StackCard({\n id,\n depth,\n decision,\n returnFrom,\n interactive,\n zIndex,\n label,\n labels,\n stage,\n fling,\n onRelease,\n onThrown,\n onGone,\n children,\n}: CardProps) {\n const card = useRef<HTMLDivElement>(null);\n // Grabbing the top half tips the card the way the hand moves; grabbing the bottom half tips it the other way.\n const tilt = useRef(returnFrom?.tilt ?? 1);\n const drag = useRef<{\n ox: number;\n oy: number;\n samples: { t: number; x: number; y: number }[];\n } | null>(null);\n const settled = useRef(!returnFrom);\n const wasDecided = useRef(false);\n\n const lift = (value: number) => stage.current?.style.setProperty(\"--lift\", String(value));\n /** Writes the card's place and what follows from it (its tilt and its stamps) straight to the DOM: a drag never re-renders. */\n const place = (el: HTMLElement, x: number, y: number) => {\n el.style.translate = `${x}px ${y}px`;\n el.style.rotate = `${x * ROTATE * tilt.current}deg`;\n el.style.setProperty(\"--keep\", String(clampUnit((x - STAMP_FROM) / (STAMP_TO - STAMP_FROM))));\n el.style.setProperty(\"--pass\", String(clampUnit((-STAMP_FROM - x) / (STAMP_TO - STAMP_FROM))));\n };\n\n // Leaving the stack (a decision) and coming back (undo, or start over) are the only things that move a card by themselves.\n useIsomorphicLayoutEffect(() => {\n const el = card.current;\n if (!el) return;\n const reduced = prefersReducedMotion();\n if (decision) {\n // A key or button can decide the card mid-drag; the throw takes over from the finger.\n drag.current = null;\n el.removeAttribute(\"data-dragging\");\n wasDecided.current = true;\n const direction = decision === \"right\" ? 1 : -1;\n const { x: cx, y: cy } = readOffset(el);\n // Buttons and keys throw with a default flick; a drag keeps the finger's velocity.\n const velocity = fling(id) ?? { x: direction * 900, y: -120 };\n const speed = Math.hypot(velocity.x, velocity.y);\n // Leave along the flick when it heads the chosen way, otherwise along the drag, otherwise straight out.\n let ux = velocity.x;\n let uy = velocity.y;\n if (speed < 200 || Math.sign(ux) !== direction) {\n ux = Math.abs(cx) > 8 ? cx : direction;\n uy = Math.abs(cx) > 8 ? cy * 0.5 : -0.12;\n }\n const length = Math.hypot(ux, uy) || 1;\n ux /= length;\n uy /= length;\n if (Math.abs(ux) < 0.45) {\n ux = direction * 0.45;\n uy = Math.sign(uy || -1) * Math.sqrt(1 - 0.45 * 0.45);\n }\n // Far enough to clear a typical preview or card column; the fade finishes before the spring does.\n const reach = el.offsetWidth * 1.6;\n const exit = { x: cx + ux * reach, y: cy + uy * reach, tilt: tilt.current };\n onThrown(id, exit);\n el.style.setProperty(direction > 0 ? \"--keep\" : \"--pass\", \"1\");\n if (reduced) {\n el.style.transition = REDUCED_FADE;\n } else {\n el.style.transition = THROW;\n place(el, exit.x, exit.y);\n }\n el.style.opacity = \"0\";\n return;\n }\n // Undo, or a first mount from where the card left: come back from there, catching any flight in progress.\n if (!wasDecided.current && settled.current) return;\n wasDecided.current = false;\n const delay = returnFrom?.delay ?? 0;\n if (!settled.current && !reduced && returnFrom) {\n el.style.transition = \"none\";\n place(el, returnFrom.x, returnFrom.y);\n el.style.opacity = \"0\";\n void el.offsetWidth;\n }\n settled.current = true;\n if (reduced) {\n el.style.transition = `${REDUCED_FADE} ${delay}ms`;\n place(el, 0, 0);\n } else {\n el.style.transition = HOME_TRANSITION(delay);\n place(el, 0, 0);\n }\n el.style.opacity = \"1\";\n // Only the decision drives this effect; the return position is read once at that moment.\n }, [decision]);\n\n const gone = useCallback(() => onGone(id), [onGone, id]);\n useExitBackstop(decision !== null, gone, FLIGHT_BACKSTOP_MS);\n\n function down(event: ReactPointerEvent<HTMLDivElement>) {\n if (!interactive || event.button !== 0 || !event.isPrimary) return;\n if (\n event.target instanceof Element &&\n event.target.closest(\"button, a, input, select, textarea, [role='button']\")\n )\n return;\n const el = event.currentTarget;\n // Freeze wherever the card is (it may be mid-way home), then follow the finger.\n const now = readOffset(el);\n el.style.transition = \"none\";\n if (Math.abs(now.x) < 2) {\n const rect = el.getBoundingClientRect();\n tilt.current = event.clientY < rect.top + rect.height / 2 ? 1 : -1;\n }\n place(el, now.x, now.y);\n drag.current = {\n ox: event.clientX - now.x,\n oy: event.clientY - unresistY(now.y),\n samples: [{ t: event.timeStamp, x: event.clientX, y: event.clientY }],\n };\n try {\n el.setPointerCapture(event.pointerId);\n } catch {\n // A pointer that is no longer active cannot be captured; the drag then follows the card only while the pointer is over it.\n }\n el.setAttribute(\"data-dragging\", \"\");\n }\n function move(event: ReactPointerEvent<HTMLDivElement>) {\n const state = drag.current;\n if (!state || !interactive) return;\n const x = event.clientX - state.ox;\n const y = resistY(event.clientY - state.oy);\n place(event.currentTarget, x, y);\n // The cards behind rise as this one is dragged away.\n lift(clampUnit(Math.hypot(x, y * 0.5) / LIFT_DISTANCE));\n state.samples.push({ t: event.timeStamp, x: event.clientX, y: event.clientY });\n if (state.samples.length > 12) state.samples.shift();\n }\n function up(event: ReactPointerEvent<HTMLDivElement>) {\n const state = drag.current;\n drag.current = null;\n const el = event.currentTarget;\n el.removeAttribute(\"data-dragging\");\n if (el.hasPointerCapture(event.pointerId)) el.releasePointerCapture(event.pointerId);\n if (!state || !interactive) return;\n const velocity =\n event.type === \"pointercancel\" ? { x: 0, y: 0 } : velocityOf(state.samples, event.timeStamp);\n const offset = readOffset(el);\n if (onRelease(id, offset, velocity, el.offsetWidth)) return;\n // Below the threshold the card springs home.\n el.style.transition = prefersReducedMotion() ? \"none\" : SPRING_BACK;\n place(el, 0, 0);\n lift(0);\n }\n\n return (\n <div\n className=\"card-stack-slot\"\n style={{ \"--depth\": depth, zIndex } as CSSProperties}\n aria-hidden={interactive ? undefined : true}\n ref={(node) => {\n if (!node) return;\n if (interactive) node.removeAttribute(\"inert\");\n else node.setAttribute(\"inert\", \"\");\n }}\n >\n <div\n ref={card}\n className=\"card-stack-card\"\n data-top={interactive ? \"\" : undefined}\n data-flying={decision ? \"\" : undefined}\n role={interactive ? \"group\" : undefined}\n aria-label={interactive ? label : undefined}\n onPointerDown={down}\n onPointerMove={move}\n onPointerUp={up}\n onPointerCancel={up}\n onTransitionEnd={(event) => {\n if (decision && event.target === event.currentTarget && event.propertyName === \"opacity\")\n gone();\n }}\n >\n <div className=\"card-stack-face\">\n {children}\n <span className=\"card-stack-shade\" aria-hidden=\"true\" />\n </div>\n <span className=\"card-stack-stamp\" data-side=\"right\" aria-hidden=\"true\">\n <CheckIcon />\n {labels.right}\n </span>\n <span className=\"card-stack-stamp\" data-side=\"left\" aria-hidden=\"true\">\n <CloseIcon />\n {labels.left}\n </span>\n </div>\n </div>\n );\n}\n\nexport function CardStack<T>({\n items,\n getKey,\n getLabel,\n renderCard,\n onDecide,\n onUndo,\n onReset,\n labels = { left: \"Pass\", right: \"Keep\" },\n outcomes,\n label = \"Cards\",\n renderEmpty,\n className,\n}: CardStackProps<T>) {\n const hintId = useId();\n const stage = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState(0);\n const [history, setHistory] = useState<{ key: string; decision: CardDecision }[]>([]);\n const [flying, setFlying] = useState<string[]>([]);\n const [returning, setReturning] = useState<Record<string, Return>>({});\n const [message, setMessage] = useState(\"\");\n // Where each thrown card left the screen, so undo and start over can bring it back from there. Only those handlers read it,\n // so it lives in a ref: recording an exit must not re-render the stack in the very frame the throw starts.\n const exits = useRef<Record<string, Exit>>({});\n const flings = useRef(new Map<string, Point>());\n const top = items[position];\n const remaining = Math.max(0, items.length - position);\n\n function decide(decision: CardDecision) {\n if (!top) return;\n const key = getKey(top);\n stage.current?.style.setProperty(\"--lift\", \"0\");\n setPosition(position + 1);\n setHistory([...history, { key, decision }]);\n setFlying((current) => [...current, key]);\n setReturning({});\n const left = remaining - 1;\n setMessage(\n `${getLabel(top)}${outcomes ? ` ${outcomes[decision]}` : `: ${labels[decision]}`}. ${left ? `${left} left.` : \"All cards reviewed.\"}`,\n );\n onDecide?.(top, decision);\n }\n\n function undo() {\n const last = history[history.length - 1];\n const item = last && items.find((entry) => getKey(entry) === last.key);\n if (!last || !item) return;\n const exit = exits.current[last.key];\n setPosition(position - 1);\n setHistory(history.slice(0, -1));\n setFlying((current) => current.filter((key) => key !== last.key));\n setReturning(exit ? { [last.key]: { ...exit, delay: 0 } } : {});\n setMessage(`${getLabel(item)} restored.`);\n onUndo?.(item, last.decision);\n }\n\n function reset() {\n if (!history.length) return;\n // The deck deals itself back: the card that will sit deepest returns first, the top card last.\n const count = Math.min(VISIBLE, items.length);\n const back: Record<string, Return> = {};\n items.slice(0, count).forEach((item, index) => {\n const key = getKey(item);\n const exit = exits.current[key];\n if (exit && index < position) back[key] = { ...exit, delay: (count - 1 - index) * 60 };\n });\n setPosition(0);\n setHistory([]);\n setFlying([]);\n setReturning(back);\n setMessage(\"All cards are back in the stack.\");\n onReset?.();\n }\n\n function release(key: string, offset: Point, velocity: Point, width: number) {\n const direction = Math.sign(offset.x);\n if (!direction || !top || getKey(top) !== key) return false;\n const flick =\n Math.abs(velocity.x) > FLICK &&\n Math.sign(velocity.x) === direction &&\n Math.abs(offset.x) > 12;\n const far =\n Math.abs(offset.x) > Math.min(width * 0.35, 140) &&\n !(Math.sign(velocity.x) === -direction && Math.abs(velocity.x) > 300);\n if (!flick && !far) return false;\n flings.current.set(key, velocity);\n decide(direction > 0 ? \"right\" : \"left\");\n return true;\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.target !== event.currentTarget) return;\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n decide(event.key === \"ArrowRight\" ? \"right\" : \"left\");\n } else if (\n event.key === \"Backspace\" ||\n ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"z\")\n ) {\n event.preventDefault();\n undo();\n }\n }\n\n const decided = new Map(history.map((entry) => [entry.key, entry.decision]));\n const windowKeys = new Set(items.slice(position, position + VISIBLE).map(getKey));\n const rendered = items.filter(\n (item) => windowKeys.has(getKey(item)) || flying.includes(getKey(item)),\n );\n const empty = position >= items.length && flying.length === 0;\n const takeFling = useCallback((key: string) => {\n const velocity = flings.current.get(key);\n flings.current.delete(key);\n return velocity;\n }, []);\n const recordExit = useCallback((key: string, exit: Exit) => {\n exits.current[key] = exit;\n }, []);\n const finish = useCallback(\n (key: string) => setFlying((current) => current.filter((entry) => entry !== key)),\n [],\n );\n\n return (\n <div data-slot=\"card-stack\" className={cn(\"card-stack\", className)}>\n <div\n ref={stage}\n className=\"card-stack-stage\"\n // A keyboard widget: a focusable group is not allowed, an application is.\n role=\"application\"\n aria-roledescription=\"card stack\"\n aria-label={label}\n aria-describedby={hintId}\n tabIndex={0}\n onKeyDown={onKeyDown}\n >\n {rendered.map((item) => {\n const id = getKey(item);\n const index = items.indexOf(item);\n const decision = decided.get(id) ?? null;\n const depth = decision ? 0 : index - position;\n return (\n <StackCard\n key={id}\n id={id}\n depth={depth}\n decision={decision}\n returnFrom={returning[id]}\n interactive={!decision && depth === 0}\n zIndex={decision ? 20 + flying.indexOf(id) : VISIBLE - depth}\n label={`${getLabel(item)}, ${index + 1} of ${items.length}`}\n labels={labels}\n stage={stage}\n fling={takeFling}\n onRelease={release}\n onThrown={recordExit}\n onGone={finish}\n >\n {renderCard(item)}\n </StackCard>\n );\n })}\n {empty ? (\n <div className=\"card-stack-empty\">\n {renderEmpty ? (\n <EmptySlot render={renderEmpty} onReset={reset} />\n ) : (\n <>\n <p className=\"m-0 text-base font-medium\">All cards reviewed</p>\n <Button variant=\"outline\" onPress={reset} startIcon={<UndoIcon />}>\n Start over\n </Button>\n </>\n )}\n </div>\n ) : null}\n </div>\n <div className=\"flex flex-wrap justify-center gap-2\">\n {/* aria-disabled keeps focus on the button when the stack runs out, instead of dropping it to the page. */}\n <Control\n variant=\"outline\"\n disabled={!top}\n onPress={() => decide(\"left\")}\n startIcon={<CloseIcon />}\n >\n {labels.left}\n </Control>\n <Control variant=\"ghost\" disabled={!history.length} onPress={undo} startIcon={<UndoIcon />}>\n Undo\n </Control>\n <Control\n variant=\"primary\"\n disabled={!top}\n onPress={() => decide(\"right\")}\n startIcon={<CheckIcon />}\n >\n {labels.right}\n </Control>\n </div>\n <p id={hintId} className=\"sr-only\">\n Drag the top card, or use the left and right arrow keys to {labels.left.toLowerCase()} or{\" \"}\n {labels.right.toLowerCase()} it. Backspace undoes the last decision.\n </p>\n <p className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {message}\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/carousel/carousel.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Carousel: a row of slides people browse by swiping, a trackpad, dragging
|
|
5
|
+
* with a mouse, the arrow keys, or the controls below it. Use it for a short,
|
|
6
|
+
* ordered set of peers such as products or case studies, where the neighbours
|
|
7
|
+
* stay in view as a hint that there is more.
|
|
8
|
+
*
|
|
9
|
+
* The slides sit on a native scroll-snap track, so touch and trackpad swipes
|
|
10
|
+
* are the browser's own. The active slide is centred; its neighbours shrink and
|
|
11
|
+
* dim with their distance, and each page indicator stretches into a pill as the
|
|
12
|
+
* track nears its slide, all written straight from the scroll position without a
|
|
13
|
+
* re-render. A mouse can drag the track, and a flick carries on to the next
|
|
14
|
+
* slide. Arrow keys, Home and End move between slides from the track or the
|
|
15
|
+
* indicators. `interval` adds a play control that rotates through the slides;
|
|
16
|
+
* rotation pauses on hover, keyboard focus and touch, while the carousel is off
|
|
17
|
+
* screen or the tab is hidden, and is unavailable under reduced motion (where
|
|
18
|
+
* scrolling also jumps instead of gliding).
|
|
19
|
+
*/
|
|
20
|
+
export interface CarouselProps {
|
|
21
|
+
/** Names the carousel for assistive technology, such as "From the Harbour suites". */
|
|
22
|
+
label: string;
|
|
23
|
+
/** One child per slide. */
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
/** Controlled active slide. Pair with `onIndexChange`. */
|
|
26
|
+
index?: number;
|
|
27
|
+
/** @default 0 */
|
|
28
|
+
defaultIndex?: number;
|
|
29
|
+
onIndexChange?: (index: number) => void;
|
|
30
|
+
/** Width of one slide as a CSS length; `cqw` is a share of the carousel's width. @default "min(80cqw, 340px)" */
|
|
31
|
+
slideSize?: string;
|
|
32
|
+
/** Names each slide and its indicator. @default (index, count) => `${index + 1} of ${count}` */
|
|
33
|
+
slideLabel?: (index: number, count: number) => string;
|
|
34
|
+
/** Milliseconds per slide. Adds a play control that rotates through the slides. */
|
|
35
|
+
interval?: number;
|
|
36
|
+
/** Starts rotating on mount. Off by default, so motion begins with the viewer. @default false */
|
|
37
|
+
autoplay?: boolean;
|
|
38
|
+
/** Names of the controls, for translation. */
|
|
39
|
+
labels?: {
|
|
40
|
+
/** @default "Previous slide" */
|
|
41
|
+
previous?: string;
|
|
42
|
+
/** @default "Next slide" */
|
|
43
|
+
next?: string;
|
|
44
|
+
/** @default "Start slide rotation" */
|
|
45
|
+
play?: string;
|
|
46
|
+
/** @default "Pause slide rotation" */
|
|
47
|
+
pause?: string;
|
|
48
|
+
/** Names the group of indicators. @default "Choose a slide" */
|
|
49
|
+
slides?: string;
|
|
50
|
+
};
|
|
51
|
+
/** Extra classes for the carousel. Merged last. */
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare const Carousel: import("react").ForwardRefExoticComponent<CarouselProps & import("react").RefAttributes<HTMLElement>>;
|
|
55
|
+
//#endregion
|
|
56
|
+
//# sourceMappingURL=carousel.d.ts.map
|