@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,67 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/swipe-actions/swipe-actions.d.ts
|
|
3
|
+
export interface SwipeAction {
|
|
4
|
+
/** Names the action: under its icon when revealed, and in the row's More actions menu. */
|
|
5
|
+
label: string;
|
|
6
|
+
icon: ReactNode;
|
|
7
|
+
/** The fill of the revealed action. Pair `destructive` with a label that names the destructive result. @default "neutral" */
|
|
8
|
+
tone?: "neutral" | "primary" | "destructive";
|
|
9
|
+
/** Runs for a full swipe, a tap on the revealed action, or the More actions menu. Remove the item here unless `keepRow` is set. */
|
|
10
|
+
onSelect: () => void;
|
|
11
|
+
/** The row springs home after the action instead of sliding away, for actions such as Mark as unread. */
|
|
12
|
+
keepRow?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface SwipeActionsProps {
|
|
15
|
+
/** Names the list. */
|
|
16
|
+
label: string;
|
|
17
|
+
/** `SwipeActionsRow`s, each with a stable `key`: a row that is removed slides out and the list closes the gap. */
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
/** Extra classes for the list's frame. Merged last. */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
/** One row. `leading` actions sit under the left edge and `trailing` ones under the right; the outermost on each side commits on a full swipe. */
|
|
23
|
+
export interface SwipeActionsRowProps {
|
|
24
|
+
/** Names the row in its menu button, for example the message subject. */
|
|
25
|
+
label: string;
|
|
26
|
+
leading?: SwipeAction[];
|
|
27
|
+
trailing?: SwipeAction[];
|
|
28
|
+
/** Lets a long swipe commit the outermost action without a tap. @default true */
|
|
29
|
+
fullSwipe?: boolean;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
/** Extra classes for the row's content. Merged last. */
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A list whose rows reveal actions on a horizontal swipe, the way a mail inbox does. Use
|
|
36
|
+
* it for short lists where people triage items quickly.
|
|
37
|
+
*
|
|
38
|
+
* Only one row stays open at a time; touching anywhere else or pressing Escape puts it
|
|
39
|
+
* away. Every row also has a More actions menu with the same actions, so keyboard,
|
|
40
|
+
* screen reader and single-pointer users never need the gesture. A row that is removed
|
|
41
|
+
* slides out and the list closes the gap; a row added later opens its height.
|
|
42
|
+
*
|
|
43
|
+
* Reduced motion: rows jump to where the finger leaves them, and removal is immediate.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* <SwipeActions label="Inbox">
|
|
47
|
+
* {mails.map((mail) => (
|
|
48
|
+
* <SwipeActionsRow key={mail.id} label={mail.subject} trailing={[archive, trash]}>
|
|
49
|
+
* {mail.subject}
|
|
50
|
+
* </SwipeActionsRow>
|
|
51
|
+
* ))}
|
|
52
|
+
* </SwipeActions>
|
|
53
|
+
*/
|
|
54
|
+
export declare function SwipeActions({ label, children, className }: SwipeActionsProps): import("react").JSX.Element;
|
|
55
|
+
/**
|
|
56
|
+
* One row of a `SwipeActions` list. Drag it sideways to reveal its actions: they share the
|
|
57
|
+
* revealed space, widen together past their resting width, and the outermost one stretches
|
|
58
|
+
* over the whole row for a full swipe (a tick of haptics on touch devices). A flick opens,
|
|
59
|
+
* commits or closes by its speed as well as its distance, and a row dragged past its last
|
|
60
|
+
* stop pulls back against elastic.
|
|
61
|
+
*
|
|
62
|
+
* The row's More actions button opens a menu of the same actions: the way to use them
|
|
63
|
+
* without a gesture.
|
|
64
|
+
*/
|
|
65
|
+
export declare function SwipeActionsRow({ label, leading, trailing, fullSwipe, children, className }: SwipeActionsRowProps): import("react").JSX.Element;
|
|
66
|
+
//#endregion
|
|
67
|
+
//# sourceMappingURL=swipe-actions.d.ts.map
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, cnAll } from "../lib/cn.js";
|
|
3
|
+
import { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from "../lib/motion.js";
|
|
4
|
+
import { MORPH, SMOOTH, SNAPPY, live, retarget } from "../lib/spring-tween.js";
|
|
5
|
+
import { Menu as Menu$1, MenuItem as MenuItem$1, MenuTrigger } from "../menu/menu.js";
|
|
6
|
+
import { useLeavingList } from "../lib/leaving-list.js";
|
|
7
|
+
import { Children, Fragment, createContext, isValidElement, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { Button } from "react-aria-components";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
//#region src/swipe-actions/swipe-actions.tsx
|
|
11
|
+
/** Width of one action while a row rests open. */
|
|
12
|
+
const ACTION = 76;
|
|
13
|
+
/** Movement before a press is read as a swipe or handed to the page as a scroll. */
|
|
14
|
+
const SLOP = 8;
|
|
15
|
+
/** Seconds of travel projected from the release velocity, roughly a 0.99 deceleration rate. */
|
|
16
|
+
const PROJECTION = .1;
|
|
17
|
+
/** Release speed in px/s that counts as a flick. */
|
|
18
|
+
const FLICK = 900;
|
|
19
|
+
/** The longest a leaving row's collapse takes, plus a margin, for the backstop that removes a row whose transition never ends. */
|
|
20
|
+
const COLLAPSE_BACKSTOP = 1300;
|
|
21
|
+
/** How long a committed row waits for its list to remove it before it comes back. */
|
|
22
|
+
const RESTORE_MS = 1400;
|
|
23
|
+
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
24
|
+
/** Past its last stop the row still follows, but every pixel costs more, like pulling against elastic. */
|
|
25
|
+
const rubberBand = (overshoot, dimension) => (1 - 1 / (overshoot * .55 / dimension + 1)) * dimension;
|
|
26
|
+
const sideOf = (value) => value > 0 ? "leading" : value < 0 ? "trailing" : null;
|
|
27
|
+
const GroupContext = createContext({
|
|
28
|
+
openId: null,
|
|
29
|
+
setOpenId: () => {},
|
|
30
|
+
rows: /* @__PURE__ */ new Map()
|
|
31
|
+
});
|
|
32
|
+
const RowStateContext = createContext(null);
|
|
33
|
+
function velocityOf(samples) {
|
|
34
|
+
const last = samples[samples.length - 1];
|
|
35
|
+
if (!last) return 0;
|
|
36
|
+
const first = samples.find((sample) => last[0] - sample[0] <= 80) ?? last;
|
|
37
|
+
const elapsed = (last[0] - first[0]) / 1e3;
|
|
38
|
+
return elapsed > 0 ? (last[1] - first[1]) / elapsed : 0;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A list whose rows reveal actions on a horizontal swipe, the way a mail inbox does. Use
|
|
42
|
+
* it for short lists where people triage items quickly.
|
|
43
|
+
*
|
|
44
|
+
* Only one row stays open at a time; touching anywhere else or pressing Escape puts it
|
|
45
|
+
* away. Every row also has a More actions menu with the same actions, so keyboard,
|
|
46
|
+
* screen reader and single-pointer users never need the gesture. A row that is removed
|
|
47
|
+
* slides out and the list closes the gap; a row added later opens its height.
|
|
48
|
+
*
|
|
49
|
+
* Reduced motion: rows jump to where the finger leaves them, and removal is immediate.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* <SwipeActions label="Inbox">
|
|
53
|
+
* {mails.map((mail) => (
|
|
54
|
+
* <SwipeActionsRow key={mail.id} label={mail.subject} trailing={[archive, trash]}>
|
|
55
|
+
* {mail.subject}
|
|
56
|
+
* </SwipeActionsRow>
|
|
57
|
+
* ))}
|
|
58
|
+
* </SwipeActions>
|
|
59
|
+
*/
|
|
60
|
+
function SwipeActions({ label, children, className }) {
|
|
61
|
+
const [openId, setOpenId] = useState(null);
|
|
62
|
+
const [rows] = useState(() => /* @__PURE__ */ new Map());
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!openId) return;
|
|
65
|
+
const onPointerDown = (event) => {
|
|
66
|
+
const row = rows.get(openId);
|
|
67
|
+
if (row && event.target instanceof Node && row.contains(event.target)) return;
|
|
68
|
+
setOpenId(null);
|
|
69
|
+
};
|
|
70
|
+
const onKeyDown = (event) => {
|
|
71
|
+
if (event.key === "Escape") setOpenId(null);
|
|
72
|
+
};
|
|
73
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
74
|
+
document.addEventListener("keydown", onKeyDown);
|
|
75
|
+
return () => {
|
|
76
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
77
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
78
|
+
};
|
|
79
|
+
}, [openId, rows]);
|
|
80
|
+
const group = useMemo(() => ({
|
|
81
|
+
openId,
|
|
82
|
+
setOpenId,
|
|
83
|
+
rows
|
|
84
|
+
}), [openId, rows]);
|
|
85
|
+
const items = Children.toArray(children).filter((child) => isValidElement(child));
|
|
86
|
+
const { list, done: leaveDone } = useLeavingList(items.map((node) => ({
|
|
87
|
+
id: String(node.key),
|
|
88
|
+
node
|
|
89
|
+
})));
|
|
90
|
+
const [initial] = useState(() => new Set(items.map((node) => String(node.key))));
|
|
91
|
+
const [ready, setReady] = useState(false);
|
|
92
|
+
useIsomorphicLayoutEffect(() => {
|
|
93
|
+
const frame = requestAnimationFrame(() => setReady(true));
|
|
94
|
+
return () => cancelAnimationFrame(frame);
|
|
95
|
+
}, []);
|
|
96
|
+
const done = useCallback((id) => {
|
|
97
|
+
initial.delete(id);
|
|
98
|
+
leaveDone(id);
|
|
99
|
+
}, [initial, leaveDone]);
|
|
100
|
+
return /* @__PURE__ */ jsx(GroupContext.Provider, {
|
|
101
|
+
value: group,
|
|
102
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
103
|
+
"data-slot": "swipe-actions",
|
|
104
|
+
className: cn("swipe-actions-surface rounded-panel border-(length:--border-width) border-border bg-surface font-sans text-foreground", className),
|
|
105
|
+
children: /* @__PURE__ */ jsx("ul", {
|
|
106
|
+
role: "list",
|
|
107
|
+
"aria-label": label,
|
|
108
|
+
tabIndex: -1,
|
|
109
|
+
className: "m-0 -mb-px list-none p-0 outline-none",
|
|
110
|
+
children: list.map(({ item, leaving }) => /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(RowStateContext.Provider, {
|
|
111
|
+
value: {
|
|
112
|
+
id: item.id,
|
|
113
|
+
leaving,
|
|
114
|
+
fresh: ready && !initial.has(item.id),
|
|
115
|
+
done
|
|
116
|
+
},
|
|
117
|
+
children: item.node
|
|
118
|
+
}) }, item.id))
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const layerTone = {
|
|
124
|
+
neutral: "bg-secondary text-secondary-foreground",
|
|
125
|
+
primary: "bg-primary text-primary-foreground",
|
|
126
|
+
destructive: "bg-destructive text-destructive-foreground"
|
|
127
|
+
};
|
|
128
|
+
const iconOf = (action) => /* @__PURE__ */ jsx("span", {
|
|
129
|
+
"aria-hidden": "true",
|
|
130
|
+
"data-swipe-icon": "",
|
|
131
|
+
className: "inline-flex w-4 text-text-secondary [&_svg]:size-4",
|
|
132
|
+
children: action.icon
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* One row of a `SwipeActions` list. Drag it sideways to reveal its actions: they share the
|
|
136
|
+
* revealed space, widen together past their resting width, and the outermost one stretches
|
|
137
|
+
* over the whole row for a full swipe (a tick of haptics on touch devices). A flick opens,
|
|
138
|
+
* commits or closes by its speed as well as its distance, and a row dragged past its last
|
|
139
|
+
* stop pulls back against elastic.
|
|
140
|
+
*
|
|
141
|
+
* The row's More actions button opens a menu of the same actions: the way to use them
|
|
142
|
+
* without a gesture.
|
|
143
|
+
*/
|
|
144
|
+
function SwipeActionsRow({ label, leading = [], trailing = [], fullSwipe = true, children, className }) {
|
|
145
|
+
const { openId, setOpenId, rows } = useContext(GroupContext);
|
|
146
|
+
const state = useContext(RowStateContext);
|
|
147
|
+
const leaving = state?.leaving ?? false;
|
|
148
|
+
const id = useId();
|
|
149
|
+
const rowRef = useRef(null);
|
|
150
|
+
const contentRef = useRef(null);
|
|
151
|
+
const parts = useRef(/* @__PURE__ */ new Map());
|
|
152
|
+
const motion = useRef({
|
|
153
|
+
x: live(0),
|
|
154
|
+
cover: live(0)
|
|
155
|
+
});
|
|
156
|
+
const covering = useRef(null);
|
|
157
|
+
const width = useRef(0);
|
|
158
|
+
const drag = useRef(null);
|
|
159
|
+
const armed = useRef(false);
|
|
160
|
+
const exiting = useRef(false);
|
|
161
|
+
const swallowClick = useRef(false);
|
|
162
|
+
const focusNeighbour = useRef(false);
|
|
163
|
+
const restoreTimer = useRef(0);
|
|
164
|
+
const [arrives] = useState(state?.fresh ?? false);
|
|
165
|
+
const latest = useRef({
|
|
166
|
+
leading,
|
|
167
|
+
trailing,
|
|
168
|
+
fullSwipe
|
|
169
|
+
});
|
|
170
|
+
useIsomorphicLayoutEffect(() => {
|
|
171
|
+
latest.current = {
|
|
172
|
+
leading,
|
|
173
|
+
trailing,
|
|
174
|
+
fullSwipe
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
const actionsOf = (side) => latest.current[side];
|
|
178
|
+
const openWidth = (side) => actionsOf(side).length * ACTION;
|
|
179
|
+
const outermost = (side) => side === "leading" ? 0 : actionsOf(side).length - 1;
|
|
180
|
+
const canCommit = (side) => latest.current.fullSwipe && actionsOf(side).length > 0;
|
|
181
|
+
const threshold = (side) => Math.max(openWidth(side) + 48, width.current * .56);
|
|
182
|
+
/** Writes the row and every revealed action from `x` and `cover`. Called every frame; no React render. */
|
|
183
|
+
function paint() {
|
|
184
|
+
const content = contentRef.current;
|
|
185
|
+
if (!content) return;
|
|
186
|
+
const value = motion.current.x.value;
|
|
187
|
+
const progress = motion.current.cover.value;
|
|
188
|
+
content.style.transform = value === 0 ? "" : `translate3d(${value}px, 0, 0)`;
|
|
189
|
+
for (const side of ["leading", "trailing"]) {
|
|
190
|
+
const direction = side === "leading" ? 1 : -1;
|
|
191
|
+
const count = actionsOf(side).length;
|
|
192
|
+
const covered = covering.current;
|
|
193
|
+
for (let index = 0; index < count; index++) {
|
|
194
|
+
const layer = parts.current.get(`${side}-${index}`);
|
|
195
|
+
if (!layer) continue;
|
|
196
|
+
const rank = side === "leading" ? count - 1 - index : index;
|
|
197
|
+
const coverRank = covered?.side === side ? side === "leading" ? count - 1 - covered.index : covered.index : null;
|
|
198
|
+
const revealed = Math.max(0, value * direction);
|
|
199
|
+
const segment = revealed / count;
|
|
200
|
+
const base = (count - rank) * revealed / count;
|
|
201
|
+
const inset = coverRank === rank ? base + progress * (revealed - base) : coverRank !== null && rank > coverRank ? base * (1 - progress) : base;
|
|
202
|
+
layer.layer.style.transform = `translate3d(${direction * inset}px, 0, 0)`;
|
|
203
|
+
const glyph = coverRank === rank ? segment / 2 + progress * (ACTION / 2 - segment / 2) : segment / 2;
|
|
204
|
+
if (layer.anchor) layer.anchor.style.transform = `translate3d(${-direction * glyph}px, 0, 0)`;
|
|
205
|
+
const reveal = (own) => coverRank === rank ? Math.max(own, progress) : coverRank === null ? own : own * (1 - progress);
|
|
206
|
+
const icon = reveal(clamp01((segment - ACTION * .25) / (ACTION * .55)));
|
|
207
|
+
const text = reveal(clamp01((segment - ACTION * .72) / (ACTION * .24)));
|
|
208
|
+
if (layer.icon) {
|
|
209
|
+
layer.icon.style.opacity = String(icon);
|
|
210
|
+
layer.icon.style.scale = String(.6 + .4 * icon);
|
|
211
|
+
}
|
|
212
|
+
if (layer.label) layer.label.style.opacity = String(text);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function stretchTo(value) {
|
|
217
|
+
retarget(motion.current.cover, value, MORPH, paint);
|
|
218
|
+
}
|
|
219
|
+
/** Springs the row to a stop. Under reduced motion it snaps. */
|
|
220
|
+
function settle(target) {
|
|
221
|
+
if (armed.current || target !== 0 && motion.current.cover.value !== 0) stretchTo(0);
|
|
222
|
+
armed.current = false;
|
|
223
|
+
retarget(motion.current.x, target, target === 0 ? SMOOTH : SNAPPY, paint, 0, () => {
|
|
224
|
+
if (target === 0) {
|
|
225
|
+
motion.current.cover.stop();
|
|
226
|
+
motion.current.cover.value = 0;
|
|
227
|
+
paint();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
if (target === 0) setOpenId((current) => current === id ? null : current);
|
|
231
|
+
else setOpenId(id);
|
|
232
|
+
}
|
|
233
|
+
/** The chosen action stretches over the whole row while the row leaves in its direction; the list then closes the gap. */
|
|
234
|
+
function commit(side, index) {
|
|
235
|
+
const action = actionsOf(side)[index];
|
|
236
|
+
if (!action || exiting.current) return;
|
|
237
|
+
const direction = side === "leading" ? 1 : -1;
|
|
238
|
+
armed.current = false;
|
|
239
|
+
covering.current = {
|
|
240
|
+
side,
|
|
241
|
+
index
|
|
242
|
+
};
|
|
243
|
+
stretchTo(1);
|
|
244
|
+
if (action.keepRow) {
|
|
245
|
+
action.onSelect();
|
|
246
|
+
settle(0);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
exiting.current = true;
|
|
250
|
+
const row = rowRef.current;
|
|
251
|
+
if (row) row.dataset.removing = "";
|
|
252
|
+
retarget(motion.current.x, direction * (width.current + 2), SMOOTH, paint);
|
|
253
|
+
setOpenId((current) => current === id ? null : current);
|
|
254
|
+
action.onSelect();
|
|
255
|
+
window.clearTimeout(restoreTimer.current);
|
|
256
|
+
restoreTimer.current = window.setTimeout(comeBack, RESTORE_MS);
|
|
257
|
+
}
|
|
258
|
+
function comeBack() {
|
|
259
|
+
if (!exiting.current) return;
|
|
260
|
+
exiting.current = false;
|
|
261
|
+
delete rowRef.current?.dataset.removing;
|
|
262
|
+
settle(0);
|
|
263
|
+
}
|
|
264
|
+
function arm(value, pointerType) {
|
|
265
|
+
const side = sideOf(value);
|
|
266
|
+
const limit = side ? threshold(side) : Infinity;
|
|
267
|
+
const next = !!side && canCommit(side) && Math.abs(value) > (armed.current ? limit - 20 : limit);
|
|
268
|
+
if (next === armed.current || !side) {
|
|
269
|
+
if (!side && armed.current) {
|
|
270
|
+
armed.current = false;
|
|
271
|
+
stretchTo(0);
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
armed.current = next;
|
|
276
|
+
if (next) {
|
|
277
|
+
covering.current = {
|
|
278
|
+
side,
|
|
279
|
+
index: outermost(side)
|
|
280
|
+
};
|
|
281
|
+
if (pointerType === "touch") navigator.vibrate?.(8);
|
|
282
|
+
}
|
|
283
|
+
stretchTo(next ? 1 : 0);
|
|
284
|
+
}
|
|
285
|
+
function constrain(raw) {
|
|
286
|
+
const side = sideOf(raw);
|
|
287
|
+
if (!side) return 0;
|
|
288
|
+
const dimension = width.current || 320;
|
|
289
|
+
const limit = actionsOf(side).length === 0 ? 0 : canCommit(side) ? dimension : openWidth(side);
|
|
290
|
+
const distance = Math.abs(raw);
|
|
291
|
+
return Math.sign(raw) * (distance <= limit ? distance : limit + rubberBand(distance - limit, dimension));
|
|
292
|
+
}
|
|
293
|
+
function release(velocity) {
|
|
294
|
+
const value = motion.current.x.value;
|
|
295
|
+
const side = sideOf(value);
|
|
296
|
+
if (!side || actionsOf(side).length === 0) {
|
|
297
|
+
settle(0);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const direction = side === "leading" ? 1 : -1;
|
|
301
|
+
const distance = Math.abs(value);
|
|
302
|
+
const outward = velocity * direction;
|
|
303
|
+
const open = openWidth(side);
|
|
304
|
+
const projected = distance + outward * PROJECTION;
|
|
305
|
+
const flung = distance > open && outward > FLICK && projected > threshold(side);
|
|
306
|
+
if (canCommit(side) && (armed.current && outward > -900 || flung)) {
|
|
307
|
+
commit(side, outermost(side));
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
settle(projected > open / 2 ? direction * open : 0);
|
|
311
|
+
}
|
|
312
|
+
function onPointerDown(event) {
|
|
313
|
+
swallowClick.current = false;
|
|
314
|
+
if (event.target.closest("[data-swipe-more]")) return;
|
|
315
|
+
if (exiting.current || !event.isPrimary || event.pointerType === "mouse" && event.button !== 0) return;
|
|
316
|
+
motion.current.x.stop();
|
|
317
|
+
drag.current = {
|
|
318
|
+
pointer: event.pointerId,
|
|
319
|
+
type: event.pointerType,
|
|
320
|
+
startX: event.clientX,
|
|
321
|
+
startY: event.clientY,
|
|
322
|
+
origin: motion.current.x.value,
|
|
323
|
+
locked: null,
|
|
324
|
+
samples: [[event.timeStamp, event.clientX]]
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function onPointerMove(event) {
|
|
328
|
+
const current = drag.current;
|
|
329
|
+
if (!current || current.pointer !== event.pointerId || current.locked === "y") return;
|
|
330
|
+
if (!current.locked) {
|
|
331
|
+
const dx = event.clientX - current.startX;
|
|
332
|
+
const dy = event.clientY - current.startY;
|
|
333
|
+
if (Math.hypot(dx, dy) < SLOP) return;
|
|
334
|
+
if (Math.abs(dy) > Math.abs(dx)) {
|
|
335
|
+
current.locked = "y";
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
current.locked = "x";
|
|
339
|
+
current.startX = event.clientX;
|
|
340
|
+
try {
|
|
341
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
342
|
+
} catch {}
|
|
343
|
+
if (rowRef.current) rowRef.current.dataset.dragging = "";
|
|
344
|
+
setOpenId(id);
|
|
345
|
+
}
|
|
346
|
+
const value = constrain(current.origin + event.clientX - current.startX);
|
|
347
|
+
motion.current.x.value = value;
|
|
348
|
+
paint();
|
|
349
|
+
current.samples.push([event.timeStamp, event.clientX]);
|
|
350
|
+
if (current.samples.length > 12) current.samples.shift();
|
|
351
|
+
arm(value, current.type);
|
|
352
|
+
}
|
|
353
|
+
function onPointerEnd(event) {
|
|
354
|
+
const current = drag.current;
|
|
355
|
+
if (!current || current.pointer !== event.pointerId) return;
|
|
356
|
+
drag.current = null;
|
|
357
|
+
if (current.locked === "x") {
|
|
358
|
+
swallowClick.current = true;
|
|
359
|
+
if (rowRef.current) delete rowRef.current.dataset.dragging;
|
|
360
|
+
current.samples.push([event.timeStamp, event.clientX]);
|
|
361
|
+
release(event.type === "pointercancel" ? 0 : velocityOf(current.samples));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (current.locked === null && Math.abs(motion.current.x.value) > .5) {
|
|
365
|
+
swallowClick.current = true;
|
|
366
|
+
settle(0);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function onClickCapture(event) {
|
|
370
|
+
if (!swallowClick.current) return;
|
|
371
|
+
swallowClick.current = false;
|
|
372
|
+
event.preventDefault();
|
|
373
|
+
event.stopPropagation();
|
|
374
|
+
}
|
|
375
|
+
/** After an action removed this row, focus goes to the next row's menu button, so a keyboard user keeps their place. */
|
|
376
|
+
function moveFocusToNeighbour() {
|
|
377
|
+
const row = rowRef.current;
|
|
378
|
+
if (!row) return;
|
|
379
|
+
const staying = (step) => {
|
|
380
|
+
let node = row[step];
|
|
381
|
+
while (node instanceof HTMLElement && ("removing" in node.dataset || "leaving" in node.dataset)) node = node[step];
|
|
382
|
+
return node;
|
|
383
|
+
};
|
|
384
|
+
((staying("nextElementSibling") ?? staying("previousElementSibling"))?.querySelector("[data-swipe-more]") ?? row.parentElement)?.focus({ preventScroll: true });
|
|
385
|
+
}
|
|
386
|
+
useEffect(() => {
|
|
387
|
+
const row = rowRef.current;
|
|
388
|
+
if (!row) return;
|
|
389
|
+
const { x, cover } = motion.current;
|
|
390
|
+
rows.set(id, row);
|
|
391
|
+
width.current = row.offsetWidth;
|
|
392
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
393
|
+
if (entry) width.current = entry.contentRect.width;
|
|
394
|
+
});
|
|
395
|
+
observer.observe(row);
|
|
396
|
+
return () => {
|
|
397
|
+
observer.disconnect();
|
|
398
|
+
rows.delete(id);
|
|
399
|
+
window.clearTimeout(restoreTimer.current);
|
|
400
|
+
x.stop();
|
|
401
|
+
cover.stop();
|
|
402
|
+
setOpenId((current) => current === id ? null : current);
|
|
403
|
+
};
|
|
404
|
+
}, [
|
|
405
|
+
id,
|
|
406
|
+
rows,
|
|
407
|
+
setOpenId
|
|
408
|
+
]);
|
|
409
|
+
useEffect(() => {
|
|
410
|
+
if (openId !== id && !exiting.current && drag.current?.locked !== "x" && motion.current.x.value !== 0) settle(0);
|
|
411
|
+
}, [openId]);
|
|
412
|
+
const wasLeaving = useRef(false);
|
|
413
|
+
const gone = useCallback(() => state?.done(state.id), [state]);
|
|
414
|
+
useExitBackstop(leaving, gone, COLLAPSE_BACKSTOP);
|
|
415
|
+
useIsomorphicLayoutEffect(() => {
|
|
416
|
+
if (leaving) {
|
|
417
|
+
window.clearTimeout(restoreTimer.current);
|
|
418
|
+
wasLeaving.current = true;
|
|
419
|
+
if (prefersReducedMotion()) gone();
|
|
420
|
+
} else if (wasLeaving.current) {
|
|
421
|
+
wasLeaving.current = false;
|
|
422
|
+
exiting.current = false;
|
|
423
|
+
delete rowRef.current?.dataset.removing;
|
|
424
|
+
settle(0);
|
|
425
|
+
}
|
|
426
|
+
}, [leaving, gone]);
|
|
427
|
+
const menuItems = [...leading.map((action, index) => ({
|
|
428
|
+
action,
|
|
429
|
+
side: "leading",
|
|
430
|
+
index
|
|
431
|
+
})), ...trailing.map((action, index) => ({
|
|
432
|
+
action,
|
|
433
|
+
side: "trailing",
|
|
434
|
+
index
|
|
435
|
+
}))];
|
|
436
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
437
|
+
ref: rowRef,
|
|
438
|
+
"data-slot": "swipe-actions-row",
|
|
439
|
+
"data-leaving": leaving || void 0,
|
|
440
|
+
"data-fresh": arrives || void 0,
|
|
441
|
+
onTransitionEnd: (event) => {
|
|
442
|
+
if (leaving && event.target === event.currentTarget && event.propertyName === "height") gone();
|
|
443
|
+
},
|
|
444
|
+
className: "swipe-actions-row",
|
|
445
|
+
style: { "--swipe-action-width": `${ACTION}px` },
|
|
446
|
+
children: [["leading", "trailing"].map((side) => (side === "leading" ? leading : trailing).map((action, index) => /* @__PURE__ */ jsx("button", {
|
|
447
|
+
type: "button",
|
|
448
|
+
tabIndex: -1,
|
|
449
|
+
"aria-hidden": "true",
|
|
450
|
+
"data-slot": "swipe-actions-action",
|
|
451
|
+
"data-side": side,
|
|
452
|
+
ref: (node) => {
|
|
453
|
+
const key = `${side}-${index}`;
|
|
454
|
+
if (!node) {
|
|
455
|
+
parts.current.delete(key);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
parts.current.set(key, {
|
|
459
|
+
layer: node,
|
|
460
|
+
anchor: node.querySelector("[data-part=anchor]"),
|
|
461
|
+
icon: node.querySelector("[data-part=icon]"),
|
|
462
|
+
label: node.querySelector("[data-part=label]")
|
|
463
|
+
});
|
|
464
|
+
},
|
|
465
|
+
onClick: () => commit(side, index),
|
|
466
|
+
style: { zIndex: (side === "leading" ? leading.length - 1 - index : index) + 1 },
|
|
467
|
+
className: cnAll("swipe-actions-layer border-0 p-0 font-sans", layerTone[action.tone ?? "neutral"]),
|
|
468
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
469
|
+
"data-part": "anchor",
|
|
470
|
+
className: "swipe-actions-anchor",
|
|
471
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
472
|
+
className: "swipe-actions-glyph",
|
|
473
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
474
|
+
"data-part": "icon",
|
|
475
|
+
className: "grid place-items-center opacity-0 [&_svg]:size-5",
|
|
476
|
+
children: action.icon
|
|
477
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
478
|
+
"data-part": "label",
|
|
479
|
+
className: "text-xs leading-(--leading-normal) font-medium whitespace-nowrap opacity-0",
|
|
480
|
+
children: action.label
|
|
481
|
+
})]
|
|
482
|
+
})
|
|
483
|
+
})
|
|
484
|
+
}, `${side}-${index}`))), /* @__PURE__ */ jsxs("div", {
|
|
485
|
+
ref: contentRef,
|
|
486
|
+
"data-slot": "swipe-actions-content",
|
|
487
|
+
onPointerDown,
|
|
488
|
+
onPointerMove,
|
|
489
|
+
onPointerUp: onPointerEnd,
|
|
490
|
+
onPointerCancel: onPointerEnd,
|
|
491
|
+
onClickCapture,
|
|
492
|
+
className: cn("swipe-actions-content bg-surface", className),
|
|
493
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
494
|
+
className: "min-w-0 flex-1",
|
|
495
|
+
children
|
|
496
|
+
}), menuItems.length > 0 ? /* @__PURE__ */ jsxs(MenuTrigger, {
|
|
497
|
+
onOpenChange: (open) => {
|
|
498
|
+
if (open || !focusNeighbour.current) return;
|
|
499
|
+
focusNeighbour.current = false;
|
|
500
|
+
requestAnimationFrame(() => requestAnimationFrame(moveFocusToNeighbour));
|
|
501
|
+
},
|
|
502
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
503
|
+
"data-swipe-more": "",
|
|
504
|
+
"aria-label": `More actions for ${label}`,
|
|
505
|
+
className: "grid size-8 flex-none cursor-pointer place-items-center rounded-full border-0 bg-transparent p-0 text-text-secondary outline-none motion-safe:transition-[background-color,color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle data-[hovered]:bg-muted data-[hovered]:text-foreground data-[pressed]:bg-muted data-[pressed]:text-foreground data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-(--color-ring)",
|
|
506
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
507
|
+
"aria-hidden": "true",
|
|
508
|
+
viewBox: "0 0 24 24",
|
|
509
|
+
fill: "currentColor",
|
|
510
|
+
className: "pointer-events-none size-4.5",
|
|
511
|
+
children: [
|
|
512
|
+
/* @__PURE__ */ jsx("circle", {
|
|
513
|
+
cx: "5",
|
|
514
|
+
cy: "12",
|
|
515
|
+
r: "1.75"
|
|
516
|
+
}),
|
|
517
|
+
/* @__PURE__ */ jsx("circle", {
|
|
518
|
+
cx: "12",
|
|
519
|
+
cy: "12",
|
|
520
|
+
r: "1.75"
|
|
521
|
+
}),
|
|
522
|
+
/* @__PURE__ */ jsx("circle", {
|
|
523
|
+
cx: "19",
|
|
524
|
+
cy: "12",
|
|
525
|
+
r: "1.75"
|
|
526
|
+
})
|
|
527
|
+
]
|
|
528
|
+
})
|
|
529
|
+
}), /* @__PURE__ */ jsx(Menu$1, {
|
|
530
|
+
placement: "bottom end",
|
|
531
|
+
offset: 6,
|
|
532
|
+
onAction: (key) => {
|
|
533
|
+
const found = menuItems.find((item) => `${item.side}-${item.index}` === key);
|
|
534
|
+
if (!found) return;
|
|
535
|
+
focusNeighbour.current = !found.action.keepRow;
|
|
536
|
+
commit(found.side, found.index);
|
|
537
|
+
},
|
|
538
|
+
children: menuItems.map(({ action, side, index }) => /* @__PURE__ */ jsxs(MenuItem$1, {
|
|
539
|
+
id: `${side}-${index}`,
|
|
540
|
+
textValue: action.label,
|
|
541
|
+
className: action.tone === "destructive" ? "[&_[data-swipe-icon]]:text-destructive-strong" : void 0,
|
|
542
|
+
children: [iconOf(action), action.label]
|
|
543
|
+
}, `${side}-${index}`))
|
|
544
|
+
})]
|
|
545
|
+
}) : null]
|
|
546
|
+
})]
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
//#endregion
|
|
550
|
+
export { SwipeActions, SwipeActionsRow };
|
|
551
|
+
|
|
552
|
+
//# sourceMappingURL=swipe-actions.js.map
|