@botiverse/raft-ui 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/{inbox-item-Cgf9IT7y.mjs → inbox-item-CfS34Ssw.mjs} +9 -1
- package/dist/index.mjs +1 -9
- package/dist/wip.d.mts +103 -1
- package/dist/wip.mjs +386 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [0.27.0](https://github.com/botiverse/raft-design/compare/v0.26.0...v0.27.0) (2026-10-23)
|
|
2
|
+
|
|
3
|
+
### Design System
|
|
4
|
+
|
|
5
|
+
- **Refreshing a snapshot page by pulling**: on a phone, a page that shows a snapshot (Activity, Tasks, Members, Computers) refreshes when pulled down past 100px; while it refreshes the indicator holds at 60px, and it snaps back in 300ms without overshoot. Realtime pages (a conversation) are never pulled. On iOS a pull that passed the trigger stays armed; on Android, as in Material 3, pulling back above it takes it back, and crossing it gives a short vibration. The indicator is the Spinner on every platform. Every such page also reloads when it is opened, so the gesture is never the only way. `PullToRefresh` is experimental, from `@botiverse/raft-ui/wip`. ([#743](https://github.com/botiverse/raft-design/pull/743))
|
|
6
|
+
- Why: on a phone there was no way to ask a snapshot page for what is new without leaving it; the numbers match Raft's iOS app so the two feel the same.
|
|
7
|
+
- Affects: a rule for products; raft-ui adds `PullToRefresh` (wip).
|
|
8
|
+
- Docs: https://raft-design.botiverse.build/patterns/navigation/#refreshing
|
|
9
|
+
- **A right panel grows until the area left of it is 360px**: a thread, a profile or task side panel and the side cards have no fixed maximum width (640px, 720px before); they grow until the area beside them is down to its 360px minimum. ([#759](https://github.com/botiverse/raft-design/pull/759))
|
|
10
|
+
- Why: a fixed maximum stopped them at about half of a 1280px to 1440px window, though people wanted three quarters while they worked in them.
|
|
11
|
+
- Affects: a rule for products.
|
|
12
|
+
- Docs: https://raft-design.botiverse.build/foundations/spacing/
|
|
13
|
+
- **Dragging past the end snaps**: dragging the sidebar 40px past its minimum collapses it, and dragging a thread 40px past where the conversation is down to 360px puts it in focus mode, during the drag; dragging back undoes it, and the width before the snap comes back. ([#761](https://github.com/botiverse/raft-design/pull/761))
|
|
14
|
+
- Why: the jump is how people know they went past the end; 40px keeps a drag that only reaches it from snapping by accident.
|
|
15
|
+
- Affects: a rule for products.
|
|
16
|
+
- Docs: https://raft-design.botiverse.build/components/resizable/#dragging-past-the-end
|
|
17
|
+
|
|
1
18
|
## [0.26.0](https://github.com/botiverse/raft-design/compare/v0.25.0...v0.26.0) (2026-10-23)
|
|
2
19
|
|
|
3
20
|
### Design System
|
|
@@ -5521,6 +5521,14 @@ function Skeleton({ variant = "block", className, ...props }) {
|
|
|
5521
5521
|
});
|
|
5522
5522
|
}
|
|
5523
5523
|
//#endregion
|
|
5524
|
+
//#region src/components/virtual-list/hooks/use-latest-ref.ts
|
|
5525
|
+
/** A ref whose `current` is the value of the latest render. */
|
|
5526
|
+
function useLatestRef(value) {
|
|
5527
|
+
const ref = useRef(value);
|
|
5528
|
+
ref.current = value;
|
|
5529
|
+
return ref;
|
|
5530
|
+
}
|
|
5531
|
+
//#endregion
|
|
5524
5532
|
//#region src/components/inbox-item/inbox-item.recipe.ts
|
|
5525
5533
|
const inboxItem = tv$1({
|
|
5526
5534
|
slots: {
|
|
@@ -5930,4 +5938,4 @@ function InboxItemActions({ className, render, reveal, ...props }) {
|
|
|
5930
5938
|
});
|
|
5931
5939
|
}
|
|
5932
5940
|
//#endregion
|
|
5933
|
-
export {
|
|
5941
|
+
export { MessageTextSelectionToolbarSeparator as $, PopoverSeparator as $n, ListRowMeta as $r, MessageItemSearchMatch as $t, MessageList as A, TextLinkExternalIcon as Ai, PreviewCardTrigger as An, Panel as Ar, MessageItemBadgeSeparator as At, MessageQuotedPreviewHeader as B, useThemeScopeProps as Bi, MobileNavServerPopoverRowDescription as Bn, ScrollAreaScrollbar as Br, MessageItemInlineStatus as Bt, MessageListJumpPill as C, DialogSheetBack as Ci, PreviewCardFooter as Cn, DropdownMenuSeparator as Cr, MessageReactionTooltipContent as Ct, MessageListEndFade as D, PanelAction as Di, PreviewCardPortal as Dn, DropdownMenuTrigger as Dr, MessageReactionTooltipNames as Dt, MessageListDateDivider as E, DialogTrigger as Ei, PreviewCardPopup as En, DropdownMenuSubmenuTrigger as Er, MessageReactionTooltipNameText as Et, MessageQuotedPreviewAuthorDelegatedBy as F, Spinner as Fi, MobileNavServerPopoverDestinations as Fn, PanelScrollViewport as Fr, MessageItemDelegatedAvatar as Ft, MessageQuotedPreviewUnavailable as G, useStackedActions as Gi, PopoverBackdrop as Gn, PRESENCE_COLOR as Gr, MessageItemSenderGroup as Gt, MessageQuotedPreviewText as H, ThemeStateContext as Hi, Popover$1 as Hn, ScrollAreaViewport as Hr, MessageItemSender as Ht, MessageQuotedPreviewAuthorMeta as I, defaultNativeButton as Ii, MobileNavServerPopoverGroup as In, PanelSection as Ir, MessageItemDelegatedBy as It, MessageEmbedFooter as J, headEdges as Ji, PopoverContent as Jn, ListRowBottom as Jr, MessageItemBody as Jt, MessageEmbed as K, controlIconInset as Ki, PopoverBody as Kn, ListRow as Kr, MessageItemTime as Kt, MessageQuotedPreviewAuthorName as L, OverlayLayerProvider as Li, MobileNavServerPopoverList as Ln, ScrollArea$1 as Lr, MessageItemDelegatedByButton as Lt, MessageQuotedPreviewAttachment as M, DialogSheetReset as Mi, MobileNavServerPopover as Mn, PanelFooter as Mr, MessageItemSavedIndicator as Mt, MessageQuotedPreviewAttachments as N, Button$1 as Ni, MobileNavServerPopoverContent as Nn, PanelHeaderRoot as Nr, MessageItemToolbar as Nt, MessageListHistoryLimited as O, panelToggleAction as Oi, PreviewCardSeparator as On, Kbd as Or, MessageReactionTooltipSeparator as Ot, MessageQuotedPreviewAuthor as P, buttonVariants as Pi, MobileNavServerPopoverDestination as Pn, PanelScrollContent as Pr, MessageItemToolbarButton as Pt, MessageTextSelectionToolbarButton as Q, PopoverPortal as Qn, ListRowIcon as Qr, MessageItemFooter as Qt, MessageQuotedPreviewChannel as R, useParentOverlayKind as Ri, MobileNavServerPopoverRow as Rn, ScrollAreaContent as Rr, MessageItemDelegatedByName as Rt, MessageListJumpControls as S, DialogPortal as Si, PreviewCardEyebrow as Sn, DropdownMenuRadioItemIndicator as Sr, MessageReactionQuickRow as St, MessageListJumpPillGroup as T, DialogTitle as Ti, PreviewCardMeta as Tn, DropdownMenuSubmenu as Tr, MessageReactionTooltipName as Tt, MessageQuotedPreviewThread as U, tv$1 as Ui, PopoverActions as Un, Checkbox$1 as Ur, MessageItemSenderBadges as Ut, MessageQuotedPreviewLoading as V, useTheme as Vi, MobileNavServerPopoverRowLabel as Vn, ScrollAreaThumb as Vr, MessageItemMeta as Vt, MessageQuotedPreviewTimestamp as W, stackedActions as Wi, PopoverArrow as Wn, CheckboxIndicator as Wr, MessageItemSenderButton as Wt, messageEmbed as X, PopoverHeader as Xn, ListRowDescription as Xr, MessageItemEdited as Xt, MessageEmbedHeader as Y, decisionWidth as Yi, PopoverDescription as Yn, ListRowChevron as Yr, MessageItemContent as Yt, MessageTextSelectionToolbar as Z, PopoverPopup as Zn, ListRowGroup as Zr, MessageItemEmbedded as Zt, InboxItemTrailing as _, DialogFooter as _i, PreviewCardActions as _n, DropdownMenuLinkItem as _r, MessageReactionErrorIndicator as _t, InboxItemContext as a, TooltipPortal as ai, Avatar$1 as an, MobileNavItem as ar, MessageReferenceIcon as at, useLatestRef as b, DialogNav as bi, PreviewCardContent as bn, DropdownMenuRadioGroup as br, MessageReactionPickerStatic as bt, InboxItemLeading as c, Badge as ci, AvatarGroup as cn, DropdownMenu as cr, MessageReferencePending as ct, InboxItemMetadata as d, useMobileViewport as di, useOverflowTitle as dn, DropdownMenuContent as dr, MessageReferenceText as dt, ListRowTitle as ei, MessageItemAvatarSlot as en, PopoverTitle as er, MessageMultiSelect as et, InboxItemSelection as f, Dialog$1 as fi, measureContent as fn, DropdownMenuFooter as fr, MessageReferenceUnavailable as ft, InboxItemTitle as g, DialogDescription as gi, PreviewCard$1 as gn, DropdownMenuLabel as gr, MessageReactionCount as gt, InboxItemTimestamp as h, DialogContent as hi, readNow as hn, DropdownMenuItemLabel as hr, MessageReactionChip as ht, InboxItemContent as i, TooltipContent as ii, searchHit as in, useMouseOnlyPointerSensor as ir, MessageReferenceChip as it, MessageQuotedPreview as j, textLinkVariants as ji, MobileNavServerItem as jn, PanelBody as jr, MessageItemReadReceipt as jt, MessageListNotice as k, TextLink as ki, PreviewCardTitle as kn, KbdGroup as kr, MessageItemBadge as kt, InboxItemMarks as l, DialogCommit as li, AvatarGroupCount as ln, DropdownMenuCheckboxItem as lr, MessageReferenceReadMark as lt, InboxItemSubtitle as m, DialogClose as mi, observeTruncation as mn, DropdownMenuItemCount as mr, MessageReactionAddButton as mt, InboxItemActions as n, ListRowValue as ni, MessageItemGutter as nn, PopoverViewport as nr, MessageMultiSelectToolbar as nt, InboxItemHeading as o, TooltipProvider as oi, AvatarBadge as on, MobileNavLabel as or, MessageReferenceIndicator as ot, InboxItemSender as p, DialogBody as pi, measureElement as pn, DropdownMenuItem as pr, inlineChip as pt, MessageEmbedContent as q, headEdgePull as qi, PopoverClose as qn, ListRowBody as qr, MessageItemAttachments as qt, InboxItemBody as r, Tooltip$1 as ri, MessageItem as rn, useHoldThenMoveTouchSensor as rr, MessageMultiSelectToolbarActions as rt, InboxItemHoverTarget as s, TooltipTrigger as si, AvatarFallback as sn, MobileNavRoot as sr, MessageReferenceLabel as st, InboxItem as t, ListRowTop as ti, MessageItemContinuationTime as tn, PopoverTrigger as tr, MessageMultiSelectCheckbox as tt, InboxItemMessage as u, DialogSheetContent as ui, AvatarImage as un, DropdownMenuCheckboxItemIndicator as ur, MessageReferenceSuffix as ut, InboxItemTrigger as v, DialogFooterNote as vi, PreviewCardArrow as vn, DropdownMenuPopup as vr, MessageReactionGlyph as vt, MessageListJumpPillDismiss as w, DialogSurface as wi, PreviewCardHeader as wn, DropdownMenuShortcut as wr, MessageReactionTooltipMeta as wt, Skeleton as x, DialogOverlay as xi, PreviewCardDescription as xn, DropdownMenuRadioItem as xr, MessageReactionQuickButton as xt, inboxItem as y, DialogHeader as yi, PreviewCardBody as yn, DropdownMenuPortal as yr, MessageReactionPickerContent as yt, MessageQuotedPreviewContent as z, withOverlayLayer as zi, MobileNavServerPopoverRowBody as zn, ScrollAreaCorner as zr, MessageItemHeader as zt };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn } from "./cn.mjs";
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as MessageTextSelectionToolbarSeparator, $n as PopoverSeparator, $r as ListRowMeta, $t as MessageItemSearchMatch, A as MessageList, Ai as TextLinkExternalIcon, An as PreviewCardTrigger, Ar as Panel, At as MessageItemBadgeSeparator, B as MessageQuotedPreviewHeader, Bi as useThemeScopeProps, Br as ScrollAreaScrollbar, Bt as MessageItemInlineStatus, Cr as DropdownMenuSeparator, Ct as MessageReactionTooltipContent, D as MessageListEndFade, Di as PanelAction, Dn as PreviewCardPortal, Dr as DropdownMenuTrigger, Dt as MessageReactionTooltipNames, E as MessageListDateDivider, Ei as DialogTrigger, En as PreviewCardPopup, Er as DropdownMenuSubmenuTrigger, Et as MessageReactionTooltipNameText, Fi as Spinner, Fr as PanelScrollViewport, G as MessageQuotedPreviewUnavailable, Gi as useStackedActions, Gn as PopoverBackdrop, Gr as PRESENCE_COLOR, Gt as MessageItemSenderGroup, H as MessageQuotedPreviewText, Hi as ThemeStateContext, Hn as Popover, Hr as ScrollAreaViewport, Ht as MessageItemSender, I as MessageQuotedPreviewAuthorMeta, Ii as defaultNativeButton, Ir as PanelSection, J as MessageEmbedFooter, Ji as headEdges, Jn as PopoverContent, Jr as ListRowBottom, Jt as MessageItemBody, K as MessageEmbed, Ki as controlIconInset, Kr as ListRow, Kt as MessageItemTime, L as MessageQuotedPreviewAuthorName, Lr as ScrollArea, M as MessageQuotedPreviewAttachment, Mi as DialogSheetReset, Mr as PanelFooter, Mt as MessageItemSavedIndicator, N as MessageQuotedPreviewAttachments, Ni as Button, Nr as PanelHeaderRoot, Nt as MessageItemToolbar, O as MessageListHistoryLimited, Oi as panelToggleAction, On as PreviewCardSeparator, Or as Kbd, Ot as MessageReactionTooltipSeparator, P as MessageQuotedPreviewAuthor, Pi as buttonVariants, Pr as PanelScrollContent, Pt as MessageItemToolbarButton, Q as MessageTextSelectionToolbarButton, Qn as PopoverPortal, Qr as ListRowIcon, Qt as MessageItemFooter, R as MessageQuotedPreviewChannel, Ri as useParentOverlayKind, Rr as ScrollAreaContent, Si as DialogPortal, Sr as DropdownMenuRadioItemIndicator, St as MessageReactionQuickRow, Ti as DialogTitle, Tr as DropdownMenuSubmenu, Tt as MessageReactionTooltipName, U as MessageQuotedPreviewThread, Ui as tv, Ur as Checkbox, Ut as MessageItemSenderBadges, V as MessageQuotedPreviewLoading, Vi as useTheme, Vr as ScrollAreaThumb, Vt as MessageItemMeta, W as MessageQuotedPreviewTimestamp, Wi as stackedActions, Wn as PopoverArrow, Wr as CheckboxIndicator, Wt as MessageItemSenderButton, X as messageEmbed, Xn as PopoverHeader, Xr as ListRowDescription, Xt as MessageItemEdited, Y as MessageEmbedHeader, Yi as decisionWidth, Yn as PopoverDescription, Yr as ListRowChevron, Yt as MessageItemContent, Z as MessageTextSelectionToolbar, Zn as PopoverPopup, Zr as ListRowGroup, Zt as MessageItemEmbedded, _ as InboxItemTrailing, _i as DialogFooter, _t as MessageReactionErrorIndicator, a as InboxItemContext, ai as TooltipPortal, an as Avatar, ar as MobileNavItem, b as useLatestRef, bi as DialogNav, bn as PreviewCardContent, br as DropdownMenuRadioGroup, bt as MessageReactionPickerStatic, c as InboxItemLeading, ci as Badge, cn as AvatarGroup, cr as DropdownMenu, ct as MessageReferencePending, d as InboxItemMetadata, di as useMobileViewport, dn as useOverflowTitle, dr as DropdownMenuContent, dt as MessageReferenceText, ei as ListRowTitle, en as MessageItemAvatarSlot, er as PopoverTitle, et as MessageMultiSelect, f as InboxItemSelection, fi as Dialog, fr as DropdownMenuFooter, ft as MessageReferenceUnavailable, g as InboxItemTitle, gi as DialogDescription, gn as PreviewCard, gr as DropdownMenuLabel, gt as MessageReactionCount, h as InboxItemTimestamp, hi as DialogContent, hr as DropdownMenuItemLabel, ht as MessageReactionChip, i as InboxItemContent, ii as TooltipContent, in as searchHit, ir as useMouseOnlyPointerSensor, it as MessageReferenceChip, j as MessageQuotedPreview, ji as textLinkVariants, jr as PanelBody, jt as MessageItemReadReceipt, k as MessageListNotice, ki as TextLink, kn as PreviewCardTitle, kr as KbdGroup, kt as MessageItemBadge, l as InboxItemMarks, ln as AvatarGroupCount, lr as DropdownMenuCheckboxItem, m as InboxItemSubtitle, mi as DialogClose, mr as DropdownMenuItemCount, mt as MessageReactionAddButton, n as InboxItemActions, ni as ListRowValue, nn as MessageItemGutter, nr as PopoverViewport, nt as MessageMultiSelectToolbar, o as InboxItemHeading, oi as TooltipProvider, on as AvatarBadge, or as MobileNavLabel, ot as MessageReferenceIndicator, p as InboxItemSender, pi as DialogBody, pr as DropdownMenuItem, pt as inlineChip, q as MessageEmbedContent, qi as headEdgePull, qn as PopoverClose, qr as ListRowBody, qt as MessageItemAttachments, r as InboxItemBody, ri as Tooltip, rn as MessageItem, rr as useHoldThenMoveTouchSensor, rt as MessageMultiSelectToolbarActions, s as InboxItemHoverTarget, si as TooltipTrigger, sn as AvatarFallback, sr as MobileNavRoot, st as MessageReferenceLabel, t as InboxItem, ti as ListRowTop, tn as MessageItemContinuationTime, tr as PopoverTrigger, tt as MessageMultiSelectCheckbox, u as InboxItemMessage, un as AvatarImage, ur as DropdownMenuCheckboxItemIndicator, ut as MessageReferenceSuffix, v as InboxItemTrigger, vn as PreviewCardArrow, vr as DropdownMenuPopup, vt as MessageReactionGlyph, wi as DialogSurface, wn as PreviewCardHeader, wr as DropdownMenuShortcut, wt as MessageReactionTooltipMeta, x as Skeleton, xi as DialogOverlay, xn as PreviewCardDescription, xr as DropdownMenuRadioItem, xt as MessageReactionQuickButton, yi as DialogHeader, yr as DropdownMenuPortal, yt as MessageReactionPickerContent, z as MessageQuotedPreviewContent, zi as withOverlayLayer, zr as ScrollAreaCorner, zt as MessageItemHeader } from "./inbox-item-CfS34Ssw.mjs";
|
|
3
3
|
import { Dialog as Dialog$1 } from "@base-ui/react/dialog";
|
|
4
4
|
import { AlertTriangle, Bell, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, Copy, FileText, Image, Info, TriangleAlert, Upload, X, Zap } from "lucide-react";
|
|
5
5
|
import { Children, cloneElement, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
@@ -12973,14 +12973,6 @@ function useEventCallback(fn) {
|
|
|
12973
12973
|
return useCallback((...args) => fnRef.current(...args), []);
|
|
12974
12974
|
}
|
|
12975
12975
|
//#endregion
|
|
12976
|
-
//#region src/components/virtual-list/hooks/use-latest-ref.ts
|
|
12977
|
-
/** A ref whose `current` is the value of the latest render. */
|
|
12978
|
-
function useLatestRef(value) {
|
|
12979
|
-
const ref = useRef(value);
|
|
12980
|
-
ref.current = value;
|
|
12981
|
-
return ref;
|
|
12982
|
-
}
|
|
12983
|
-
//#endregion
|
|
12984
12976
|
//#region src/components/virtual-list/hooks/use-layout-effect-with-previous.ts
|
|
12985
12977
|
/**
|
|
12986
12978
|
* `useLayoutEffect(() => fn(previousDeps), deps)`: the layout-effect form of useMemoWithPrevious;
|
package/dist/wip.d.mts
CHANGED
|
@@ -604,4 +604,106 @@ export declare function OverlayLayerProvider({ kind, modal, children }: OverlayL
|
|
|
604
604
|
*/
|
|
605
605
|
export declare function useParentOverlayKind(): OverlayLayerKind | null;
|
|
606
606
|
//#endregion
|
|
607
|
-
|
|
607
|
+
//#region src/components/pull-to-refresh/pull-to-refresh-gesture.d.ts
|
|
608
|
+
/**
|
|
609
|
+
* The pull-to-refresh gesture as a pure state machine (pull-to-refresh.md,
|
|
610
|
+
* "Behavior"). `usePullToRefresh` feeds it the scroller's touch events; this
|
|
611
|
+
* file owns every decision, so the rules can be tested without a DOM.
|
|
612
|
+
*/
|
|
613
|
+
/** How far the indicator has to be pulled, after damping, for a release to refresh. */
|
|
614
|
+
export declare const PULL_TO_REFRESH_THRESHOLD = 100;
|
|
615
|
+
/**
|
|
616
|
+
* The furthest the indicator follows the finger. 40px past the threshold is
|
|
617
|
+
* enough to show the pull is armed and still going; any further and a long
|
|
618
|
+
* drag would push the list off a small phone's screen.
|
|
619
|
+
*/
|
|
620
|
+
export declare const PULL_TO_REFRESH_MAX_DISTANCE = 140;
|
|
621
|
+
/** The indicator's height while the refresh runs. */
|
|
622
|
+
export declare const PULL_TO_REFRESH_HOLD_DISTANCE = 60;
|
|
623
|
+
/**
|
|
624
|
+
* The rubber band: the indicator moves this fraction of the finger's travel,
|
|
625
|
+
* so it lags and the pull reads as resistance. 0.55 is iOS's own
|
|
626
|
+
* scroll-view constant.
|
|
627
|
+
*/
|
|
628
|
+
export declare const PULL_TO_REFRESH_DAMPING = 0.55;
|
|
629
|
+
/**
|
|
630
|
+
* How long the indicator takes to go back, to the hold height or to 0, once
|
|
631
|
+
* the finger lifts or the refresh ends: ease-out (`ease-slide`), with no
|
|
632
|
+
* overshoot. Instant with reduced motion.
|
|
633
|
+
*/
|
|
634
|
+
export declare const PULL_TO_REFRESH_SNAP_BACK_MS = 300;
|
|
635
|
+
type PullToRefreshState = "idle" | "pulling" | "refreshing";
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region src/components/pull-to-refresh/use-pull-to-refresh.d.ts
|
|
638
|
+
/** A primary pointer that is a finger: the only input pull to refresh answers. */
|
|
639
|
+
export declare const PULL_TO_REFRESH_POINTER_QUERY = "(pointer: coarse)";
|
|
640
|
+
/**
|
|
641
|
+
* How the pull looks. `default` (iOS and every other touch screen), as in
|
|
642
|
+
* the iOS app: the content moves down with the pull and the indicator sits in
|
|
643
|
+
* the space it opens. `android` follows Material 3: the indicator travels
|
|
644
|
+
* down over the content, which stays still, arming the pull gives a short
|
|
645
|
+
* vibration, and moving back above the threshold disarms it (no latch).
|
|
646
|
+
*/
|
|
647
|
+
type PullToRefreshPlatform = "default" | "android";
|
|
648
|
+
type UsePullToRefreshOptions = {
|
|
649
|
+
/**
|
|
650
|
+
* Reload the page's snapshot. The indicator holds until the promise
|
|
651
|
+
* settles. Handle a failure here (keep the content, show the same toast as
|
|
652
|
+
* a failed first load): a rejection only ends the refresh.
|
|
653
|
+
*/
|
|
654
|
+
onRefresh: () => Promise<unknown>;
|
|
655
|
+
/** Turn the gesture off, such as while the first load is still running or a search is empty. */
|
|
656
|
+
disabled?: boolean;
|
|
657
|
+
/** Pull distance, after damping, that arms the refresh. Default 100px. */
|
|
658
|
+
threshold?: number;
|
|
659
|
+
/** The furthest the indicator follows the finger. Default 140px. */
|
|
660
|
+
maxDistance?: number;
|
|
661
|
+
/** The indicator's height while refreshing. Default 60px. */
|
|
662
|
+
holdDistance?: number;
|
|
663
|
+
/** Override the detected platform, such as in a preview. */
|
|
664
|
+
platform?: PullToRefreshPlatform;
|
|
665
|
+
};
|
|
666
|
+
type PullToRefresh = {
|
|
667
|
+
/** `idle`, `pulling` while a finger pulls the indicator down, `refreshing` until `onRefresh` settles. */
|
|
668
|
+
state: PullToRefreshState;
|
|
669
|
+
/** The indicator's height in px. */
|
|
670
|
+
distance: number;
|
|
671
|
+
/** How far towards the threshold the pull is, from 0 to 1; 1 once armed and while refreshing. */
|
|
672
|
+
progress: number;
|
|
673
|
+
/** Which platform's look the indicator takes. */
|
|
674
|
+
platform: PullToRefreshPlatform;
|
|
675
|
+
};
|
|
676
|
+
/**
|
|
677
|
+
* Pull to refresh for a scroll container on a touch screen
|
|
678
|
+
* (pull-to-refresh.md). Pulling down from the top of `scrollRef` past the
|
|
679
|
+
* threshold calls `onRefresh`; render a `PullToRefreshIndicator` with the
|
|
680
|
+
* result as the scroller's first child.
|
|
681
|
+
*
|
|
682
|
+
* On Android it takes Material 3's look (`PullToRefreshPlatform`). It does
|
|
683
|
+
* nothing on a fine pointer. While it is on, the scroller gets
|
|
684
|
+
* `overscroll-behavior-y: contain`, so a browser tab's own pull to reload
|
|
685
|
+
* doesn't run as well. `scrollRef` is read when the hook turns on; a
|
|
686
|
+
* scroller that is replaced later needs the hook remounted (a `key`).
|
|
687
|
+
*/
|
|
688
|
+
export declare function usePullToRefresh(scrollRef: RefObject<HTMLElement | null>, { onRefresh, disabled, threshold, maxDistance, holdDistance, platform: platformOverride }: UsePullToRefreshOptions): PullToRefresh;
|
|
689
|
+
//#endregion
|
|
690
|
+
//#region src/components/pull-to-refresh/pull-to-refresh-indicator.d.ts
|
|
691
|
+
type PullToRefreshIndicatorProps = Omit<ComponentProps<"div">, "children"> & {
|
|
692
|
+
/** The result of `usePullToRefresh`. */
|
|
693
|
+
pull: PullToRefresh;
|
|
694
|
+
/**
|
|
695
|
+
* What a screen reader announces when the refresh starts, such as
|
|
696
|
+
* "Refreshing…". Product copy, so it comes from the consumer.
|
|
697
|
+
*/
|
|
698
|
+
label: string;
|
|
699
|
+
};
|
|
700
|
+
/**
|
|
701
|
+
* The pull's indicator, with the DS Spinner in it. Render it as the
|
|
702
|
+
* scroller's first child: by default it is the space the pull opens, so the
|
|
703
|
+
* content moves down with the pull; on Android it travels down over the
|
|
704
|
+
* content instead (Material 3). It holds a polite live region that announces
|
|
705
|
+
* `label` while refreshing.
|
|
706
|
+
*/
|
|
707
|
+
export declare function PullToRefreshIndicator({ pull, label, className, style, ...props }: PullToRefreshIndicatorProps): import("react").JSX.Element;
|
|
708
|
+
//#endregion
|
|
709
|
+
export { Dialog as AnnouncementDialog, DialogClose as AnnouncementDialogClose, DialogTrigger as AnnouncementDialogTrigger, type ComboboxSelectGroup, type ComboboxSelectMessages, type ComboboxSelectOption, type ComboboxSelectProps, type ComboboxSelectSelection, type ComboboxSelectValue, type CommentCardMetaProps, type CommentCardProps, type CommentCardQuoteProps, type CommentCardTriggerProps, type CommentEntryAuthorProps, type CommentEntryBodyProps, type CommentEntryHeaderProps, type CommentEntryProps, type CommentEntryTimeProps, type CommentMarginItemProps, type CommentMarginLayout, type CommentMarginLayoutItem, type CommentMarginLayoutOptions, type CommentMarginProps, type CommentMarkerProps, DialogCommit, type DialogCommitProps, DialogFooterNote, type DialogFooterNoteProps, DialogSheetBack, type DialogSheetBackProps, DialogSheetContent, type DialogSheetContentProps, DropdownMenuLinkItem, type DropdownMenuLinkItemProps, type EllipsisProps, type EllipsisState, type EllipsisToggleProps, type HoldThenMoveOptions, type InboxItemLeadingBadgeProps, type InboxItemSkeletonListProps, type InboxItemSkeletonProps, type ListRowSkeletonProps, MessageItemDelegatedAvatar, type MessageItemDelegatedAvatarProps, MessageItemDelegatedBy, MessageItemDelegatedByButton, type MessageItemDelegatedByButtonProps, MessageItemDelegatedByName, type MessageItemDelegatedByNameProps, type MessageItemDelegatedByProps, MessageListJumpControls, type MessageListJumpControlsProps, MessageListJumpPill, MessageListJumpPillDismiss, type MessageListJumpPillDismissProps, MessageListJumpPillGroup, type MessageListJumpPillGroupProps, type MessageListJumpPillProps, MessageQuotedPreviewAuthorDelegatedBy, type MessageQuotedPreviewAuthorDelegatedByProps, MessageReferenceIcon, type MessageReferenceIconProps, MessageReferenceReadMark, type MessageReferenceReadMarkProps, type MeterHeaderProps, type MeterIndicatorProps, type MeterLabelProps, type MeterProps, type MeterTrackProps, type MeterValueProps, MobileNavServerItem, type MobileNavServerItemProps, MobileNavServerPopover, MobileNavServerPopoverContent, type MobileNavServerPopoverContentProps, MobileNavServerPopoverDestination, type MobileNavServerPopoverDestinationProps, MobileNavServerPopoverDestinations, type MobileNavServerPopoverDestinationsProps, MobileNavServerPopoverGroup, type MobileNavServerPopoverGroupProps, MobileNavServerPopoverList, type MobileNavServerPopoverListProps, type MobileNavServerPopoverProps, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, type MobileNavServerPopoverRowBodyProps, MobileNavServerPopoverRowDescription, type MobileNavServerPopoverRowDescriptionProps, MobileNavServerPopoverRowLabel, type MobileNavServerPopoverRowLabelProps, type MobileNavServerPopoverRowProps, type OverlayLayerKind, type OverlayLayerProviderProps, PopoverActions, type PopoverActionsProps, PopoverBody, type PopoverBodyProps, PreviewCardActions, type PreviewCardActionsProps, PreviewCardBody, type PreviewCardBodyProps, PreviewCardEyebrow, type PreviewCardEyebrowProps, PreviewCardFooter, type PreviewCardFooterProps, PreviewCardMeta, type PreviewCardMetaProps, type PullToRefresh, type PullToRefreshIndicatorProps, type PullToRefreshPlatform, type PullToRefreshState, type UseCommentMarginFitsOptions, type UsePullToRefreshOptions };
|
package/dist/wip.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $r as
|
|
1
|
+
import { $r as ListRowMeta, Bi as useThemeScopeProps, Bn as MobileNavServerPopoverRowDescription, C as MessageListJumpPill, Ci as DialogSheetBack, Cn as PreviewCardFooter, Ei as DialogTrigger, F as MessageQuotedPreviewAuthorDelegatedBy, Fi as Spinner, Fn as MobileNavServerPopoverDestinations, Ft as MessageItemDelegatedAvatar, Ii as defaultNativeButton, In as MobileNavServerPopoverGroup, It as MessageItemDelegatedBy, Jr as ListRowBottom, Kn as PopoverBody, Kr as ListRow, Li as OverlayLayerProvider, Ln as MobileNavServerPopoverList, Lt as MessageItemDelegatedByButton, Mn as MobileNavServerPopover, Ni as Button$1, Nn as MobileNavServerPopoverContent, Pn as MobileNavServerPopoverDestination, Ri as useParentOverlayKind, Rn as MobileNavServerPopoverRow, Rt as MessageItemDelegatedByName, S as MessageListJumpControls, Sn as PreviewCardEyebrow, T as MessageListJumpPillGroup, Ti as DialogTitle, Tn as PreviewCardMeta, Ui as tv, Un as PopoverActions, Vi as useTheme, Vn as MobileNavServerPopoverRowLabel, Xr as ListRowDescription, _ as InboxItemTrailing, _n as PreviewCardActions, _r as DropdownMenuLinkItem, a as InboxItemContext, at as MessageReferenceIcon, b as useLatestRef, c as InboxItemLeading, dn as useOverflowTitle, ei as ListRowTitle, fi as Dialog, fn as measureContent, g as InboxItemTitle, h as InboxItemTimestamp, hi as DialogContent, hn as readNow, i as InboxItemContent, ii as TooltipContent, jn as MobileNavServerItem, ki as TextLink, li as DialogCommit, lt as MessageReferenceReadMark, mi as DialogClose, mn as observeTruncation, o as InboxItemHeading, pn as measureElement, qr as ListRowBody, r as InboxItemBody, ri as Tooltip, rr as useHoldThenMoveTouchSensor, si as TooltipTrigger, t as InboxItem, ti as ListRowTop, ui as DialogSheetContent, vi as DialogFooterNote, w as MessageListJumpPillDismiss, x as Skeleton, y as inboxItem, yn as PreviewCardBody, zn as MobileNavServerPopoverRowBody } from "./inbox-item-CfS34Ssw.mjs";
|
|
2
2
|
import { Check, ChevronDown, LoaderCircle, Plus, X } from "lucide-react";
|
|
3
3
|
import { createContext, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1750,4 +1750,388 @@ function MeterIndicator({ className, ...props }) {
|
|
|
1750
1750
|
});
|
|
1751
1751
|
}
|
|
1752
1752
|
//#endregion
|
|
1753
|
-
|
|
1753
|
+
//#region src/components/pull-to-refresh/pull-to-refresh-gesture.ts
|
|
1754
|
+
/**
|
|
1755
|
+
* The pull-to-refresh gesture as a pure state machine (pull-to-refresh.md,
|
|
1756
|
+
* "Behavior"). `usePullToRefresh` feeds it the scroller's touch events; this
|
|
1757
|
+
* file owns every decision, so the rules can be tested without a DOM.
|
|
1758
|
+
*/
|
|
1759
|
+
/** How far the indicator has to be pulled, after damping, for a release to refresh. */
|
|
1760
|
+
const PULL_TO_REFRESH_THRESHOLD = 100;
|
|
1761
|
+
/**
|
|
1762
|
+
* The furthest the indicator follows the finger. 40px past the threshold is
|
|
1763
|
+
* enough to show the pull is armed and still going; any further and a long
|
|
1764
|
+
* drag would push the list off a small phone's screen.
|
|
1765
|
+
*/
|
|
1766
|
+
const PULL_TO_REFRESH_MAX_DISTANCE = 140;
|
|
1767
|
+
/** The indicator's height while the refresh runs. */
|
|
1768
|
+
const PULL_TO_REFRESH_HOLD_DISTANCE = 60;
|
|
1769
|
+
/**
|
|
1770
|
+
* The rubber band: the indicator moves this fraction of the finger's travel,
|
|
1771
|
+
* so it lags and the pull reads as resistance. 0.55 is iOS's own
|
|
1772
|
+
* scroll-view constant.
|
|
1773
|
+
*/
|
|
1774
|
+
const PULL_TO_REFRESH_DAMPING = .55;
|
|
1775
|
+
/**
|
|
1776
|
+
* How long the indicator takes to go back, to the hold height or to 0, once
|
|
1777
|
+
* the finger lifts or the refresh ends: ease-out (`ease-slide`), with no
|
|
1778
|
+
* overshoot. Instant with reduced motion.
|
|
1779
|
+
*/
|
|
1780
|
+
const PULL_TO_REFRESH_SNAP_BACK_MS = 300;
|
|
1781
|
+
/** A finger held this long before it pulls is a long press (a menu, a drag), not a pull. */
|
|
1782
|
+
const PULL_TO_REFRESH_LONG_PRESS_MS = 500;
|
|
1783
|
+
/** How far the finger may drift while it could still be a long press. */
|
|
1784
|
+
const PULL_TO_REFRESH_TOLERANCE = 10;
|
|
1785
|
+
/** Where a touch must not start a pull: text entry, and anything the consumer marks. */
|
|
1786
|
+
const PULL_TO_REFRESH_IGNORE_SELECTOR = [
|
|
1787
|
+
"input",
|
|
1788
|
+
"textarea",
|
|
1789
|
+
"select",
|
|
1790
|
+
"[contenteditable=\"\"]",
|
|
1791
|
+
"[contenteditable=\"true\"]",
|
|
1792
|
+
"[contenteditable=\"plaintext-only\"]",
|
|
1793
|
+
"[data-pull-to-refresh-ignore]"
|
|
1794
|
+
].join(", ");
|
|
1795
|
+
const DEFAULT_PULL_GESTURE_CONFIG = {
|
|
1796
|
+
threshold: 100,
|
|
1797
|
+
maxDistance: 140,
|
|
1798
|
+
holdDistance: 60,
|
|
1799
|
+
latch: true
|
|
1800
|
+
};
|
|
1801
|
+
const IDLE_PULL_GESTURE = {
|
|
1802
|
+
phase: "idle",
|
|
1803
|
+
startX: 0,
|
|
1804
|
+
startY: 0,
|
|
1805
|
+
distance: 0,
|
|
1806
|
+
armed: false
|
|
1807
|
+
};
|
|
1808
|
+
/** How far the indicator is for a finger that has travelled `delta` px down. */
|
|
1809
|
+
function pullDistance(delta, maxDistance = 140) {
|
|
1810
|
+
if (delta <= 0) return 0;
|
|
1811
|
+
return Math.min(maxDistance, delta * PULL_TO_REFRESH_DAMPING);
|
|
1812
|
+
}
|
|
1813
|
+
/** How far towards the threshold the pull is, from 0 to 1. */
|
|
1814
|
+
function pullProgress(distance, threshold = 100) {
|
|
1815
|
+
if (threshold <= 0) return 1;
|
|
1816
|
+
return Math.min(1, Math.max(0, distance / threshold));
|
|
1817
|
+
}
|
|
1818
|
+
/**
|
|
1819
|
+
* Which of the spinner's 4 pixels is lit while pulling, clockwise from the
|
|
1820
|
+
* top left: one step per quarter of the threshold, the last from the
|
|
1821
|
+
* threshold on (the same walk as the native apps' indicator).
|
|
1822
|
+
*/
|
|
1823
|
+
function pullStep(progress) {
|
|
1824
|
+
return Math.min(3, Math.floor(Math.max(0, progress) * 4));
|
|
1825
|
+
}
|
|
1826
|
+
/** The state the indicator shows for a gesture. */
|
|
1827
|
+
function pullState(gesture) {
|
|
1828
|
+
if (gesture.phase === "refreshing") return "refreshing";
|
|
1829
|
+
if (gesture.phase !== "pulling") return "idle";
|
|
1830
|
+
return gesture.distance > 0 || gesture.armed ? "pulling" : "idle";
|
|
1831
|
+
}
|
|
1832
|
+
function step(gesture, preventDefault = false, refresh = false, armed = false) {
|
|
1833
|
+
return {
|
|
1834
|
+
gesture,
|
|
1835
|
+
preventDefault,
|
|
1836
|
+
refresh,
|
|
1837
|
+
armed
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
/** The next gesture for an event. */
|
|
1841
|
+
function stepPullGesture(gesture, event, config = DEFAULT_PULL_GESTURE_CONFIG) {
|
|
1842
|
+
if (gesture.phase === "refreshing") return step(event.type === "settle" ? IDLE_PULL_GESTURE : gesture);
|
|
1843
|
+
switch (event.type) {
|
|
1844
|
+
case "start":
|
|
1845
|
+
if (event.fingers !== 1 || !event.atTop || event.ignored) return step(IDLE_PULL_GESTURE);
|
|
1846
|
+
return step({
|
|
1847
|
+
...IDLE_PULL_GESTURE,
|
|
1848
|
+
phase: "tracking",
|
|
1849
|
+
startX: event.x,
|
|
1850
|
+
startY: event.y
|
|
1851
|
+
});
|
|
1852
|
+
case "move": {
|
|
1853
|
+
if (gesture.phase === "idle") return step(gesture);
|
|
1854
|
+
if (event.fingers !== 1) return step(IDLE_PULL_GESTURE);
|
|
1855
|
+
if (!event.atTop && !gesture.armed) return step(IDLE_PULL_GESTURE);
|
|
1856
|
+
const dx = event.x - gesture.startX;
|
|
1857
|
+
const dy = event.y - gesture.startY;
|
|
1858
|
+
if (gesture.phase === "tracking") {
|
|
1859
|
+
if (Math.abs(dx) > PULL_TO_REFRESH_TOLERANCE && Math.abs(dx) > dy) return step(IDLE_PULL_GESTURE);
|
|
1860
|
+
if (dy < -6) return step(IDLE_PULL_GESTURE);
|
|
1861
|
+
if (dy <= 6) return step(gesture);
|
|
1862
|
+
}
|
|
1863
|
+
const distance = pullDistance(dy, config.maxDistance);
|
|
1864
|
+
const armed = config.latch && gesture.armed || distance >= config.threshold;
|
|
1865
|
+
return step({
|
|
1866
|
+
...gesture,
|
|
1867
|
+
phase: "pulling",
|
|
1868
|
+
distance,
|
|
1869
|
+
armed
|
|
1870
|
+
}, armed || dy > 6, false, armed && !gesture.armed);
|
|
1871
|
+
}
|
|
1872
|
+
case "hold": return step(gesture.phase === "tracking" ? IDLE_PULL_GESTURE : gesture);
|
|
1873
|
+
case "end":
|
|
1874
|
+
if (gesture.phase === "pulling" && gesture.armed) return step({
|
|
1875
|
+
...gesture,
|
|
1876
|
+
phase: "refreshing",
|
|
1877
|
+
distance: config.holdDistance
|
|
1878
|
+
}, false, true);
|
|
1879
|
+
return step(IDLE_PULL_GESTURE);
|
|
1880
|
+
case "cancel":
|
|
1881
|
+
case "settle": return step(IDLE_PULL_GESTURE);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
//#endregion
|
|
1885
|
+
//#region src/components/pull-to-refresh/use-pull-to-refresh.ts
|
|
1886
|
+
/** A primary pointer that is a finger: the only input pull to refresh answers. */
|
|
1887
|
+
const PULL_TO_REFRESH_POINTER_QUERY = "(pointer: coarse)";
|
|
1888
|
+
/** The one platform check: Android, by its user agent (Client Hints first). */
|
|
1889
|
+
function detectPullToRefreshPlatform() {
|
|
1890
|
+
if (typeof navigator === "undefined") return "default";
|
|
1891
|
+
const hints = navigator.userAgentData;
|
|
1892
|
+
if (hints?.platform) return hints.platform === "Android" ? "android" : "default";
|
|
1893
|
+
return /\bAndroid\b/i.test(navigator.userAgent) ? "android" : "default";
|
|
1894
|
+
}
|
|
1895
|
+
function hapticTick() {
|
|
1896
|
+
if (typeof navigator !== "undefined" && typeof navigator.vibrate === "function") navigator.vibrate(10);
|
|
1897
|
+
}
|
|
1898
|
+
const subscribeNever = () => () => void 0;
|
|
1899
|
+
const defaultSnapshot = () => "default";
|
|
1900
|
+
function subscribeCoarse(onChange) {
|
|
1901
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => void 0;
|
|
1902
|
+
const media = window.matchMedia(PULL_TO_REFRESH_POINTER_QUERY);
|
|
1903
|
+
media.addEventListener("change", onChange);
|
|
1904
|
+
return () => media.removeEventListener("change", onChange);
|
|
1905
|
+
}
|
|
1906
|
+
function coarseSnapshot() {
|
|
1907
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
|
|
1908
|
+
}
|
|
1909
|
+
function ignoredTarget(target) {
|
|
1910
|
+
return target instanceof Element && target.closest(PULL_TO_REFRESH_IGNORE_SELECTOR) !== null;
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* Pull to refresh for a scroll container on a touch screen
|
|
1914
|
+
* (pull-to-refresh.md). Pulling down from the top of `scrollRef` past the
|
|
1915
|
+
* threshold calls `onRefresh`; render a `PullToRefreshIndicator` with the
|
|
1916
|
+
* result as the scroller's first child.
|
|
1917
|
+
*
|
|
1918
|
+
* On Android it takes Material 3's look (`PullToRefreshPlatform`). It does
|
|
1919
|
+
* nothing on a fine pointer. While it is on, the scroller gets
|
|
1920
|
+
* `overscroll-behavior-y: contain`, so a browser tab's own pull to reload
|
|
1921
|
+
* doesn't run as well. `scrollRef` is read when the hook turns on; a
|
|
1922
|
+
* scroller that is replaced later needs the hook remounted (a `key`).
|
|
1923
|
+
*/
|
|
1924
|
+
function usePullToRefresh(scrollRef, { onRefresh, disabled = false, threshold = DEFAULT_PULL_GESTURE_CONFIG.threshold, maxDistance = DEFAULT_PULL_GESTURE_CONFIG.maxDistance, holdDistance = DEFAULT_PULL_GESTURE_CONFIG.holdDistance, platform: platformOverride }) {
|
|
1925
|
+
const coarse = useSyncExternalStore(subscribeCoarse, coarseSnapshot, () => false);
|
|
1926
|
+
const detected = useSyncExternalStore(subscribeNever, detectPullToRefreshPlatform, defaultSnapshot);
|
|
1927
|
+
const platform = platformOverride ?? detected;
|
|
1928
|
+
const enabled = coarse && !disabled;
|
|
1929
|
+
const [gesture, setGesture] = useState(IDLE_PULL_GESTURE);
|
|
1930
|
+
const gestureRef = useRef(IDLE_PULL_GESTURE);
|
|
1931
|
+
const onRefreshRef = useLatestRef(onRefresh);
|
|
1932
|
+
useEffect(() => {
|
|
1933
|
+
const element = scrollRef.current;
|
|
1934
|
+
if (!enabled || !element) return;
|
|
1935
|
+
const config = {
|
|
1936
|
+
threshold,
|
|
1937
|
+
maxDistance,
|
|
1938
|
+
holdDistance,
|
|
1939
|
+
latch: platform !== "android"
|
|
1940
|
+
};
|
|
1941
|
+
let holdTimer;
|
|
1942
|
+
let live = true;
|
|
1943
|
+
const dispatch = (event, native) => {
|
|
1944
|
+
const next = stepPullGesture(gestureRef.current, event, config);
|
|
1945
|
+
if (next.preventDefault && native?.cancelable) native.preventDefault();
|
|
1946
|
+
if (next.gesture !== gestureRef.current) {
|
|
1947
|
+
gestureRef.current = next.gesture;
|
|
1948
|
+
setGesture(next.gesture);
|
|
1949
|
+
}
|
|
1950
|
+
if (next.gesture.phase !== "tracking") clearTimeout(holdTimer);
|
|
1951
|
+
if (next.armed && platform === "android") hapticTick();
|
|
1952
|
+
if (next.refresh) {
|
|
1953
|
+
const settle = () => {
|
|
1954
|
+
if (live) dispatch({ type: "settle" });
|
|
1955
|
+
};
|
|
1956
|
+
let result;
|
|
1957
|
+
try {
|
|
1958
|
+
result = Promise.resolve(onRefreshRef.current());
|
|
1959
|
+
} catch (error) {
|
|
1960
|
+
result = Promise.reject(error);
|
|
1961
|
+
}
|
|
1962
|
+
result.then(settle, settle);
|
|
1963
|
+
}
|
|
1964
|
+
};
|
|
1965
|
+
const atTop = () => element.scrollTop <= 0;
|
|
1966
|
+
const onTouchStart = (event) => {
|
|
1967
|
+
const touch = event.touches[0];
|
|
1968
|
+
dispatch({
|
|
1969
|
+
type: "start",
|
|
1970
|
+
x: touch?.clientX ?? 0,
|
|
1971
|
+
y: touch?.clientY ?? 0,
|
|
1972
|
+
fingers: event.touches.length,
|
|
1973
|
+
atTop: atTop(),
|
|
1974
|
+
ignored: ignoredTarget(event.target)
|
|
1975
|
+
});
|
|
1976
|
+
if (gestureRef.current.phase === "tracking") {
|
|
1977
|
+
clearTimeout(holdTimer);
|
|
1978
|
+
holdTimer = setTimeout(() => dispatch({ type: "hold" }), PULL_TO_REFRESH_LONG_PRESS_MS);
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
const onTouchMove = (event) => {
|
|
1982
|
+
if (event.defaultPrevented && gestureRef.current.phase === "tracking") {
|
|
1983
|
+
dispatch({ type: "cancel" });
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
const touch = event.touches[0];
|
|
1987
|
+
dispatch({
|
|
1988
|
+
type: "move",
|
|
1989
|
+
x: touch?.clientX ?? 0,
|
|
1990
|
+
y: touch?.clientY ?? 0,
|
|
1991
|
+
fingers: event.touches.length,
|
|
1992
|
+
atTop: atTop()
|
|
1993
|
+
}, event);
|
|
1994
|
+
};
|
|
1995
|
+
const onTouchEnd = () => dispatch({ type: "end" });
|
|
1996
|
+
const onCancel = () => dispatch({ type: "cancel" });
|
|
1997
|
+
const previousOverscroll = element.style.overscrollBehaviorY;
|
|
1998
|
+
element.style.overscrollBehaviorY = "contain";
|
|
1999
|
+
element.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
2000
|
+
element.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
2001
|
+
element.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
2002
|
+
element.addEventListener("touchcancel", onCancel, { passive: true });
|
|
2003
|
+
element.addEventListener("contextmenu", onCancel);
|
|
2004
|
+
element.addEventListener("dragstart", onCancel);
|
|
2005
|
+
return () => {
|
|
2006
|
+
live = false;
|
|
2007
|
+
clearTimeout(holdTimer);
|
|
2008
|
+
element.style.overscrollBehaviorY = previousOverscroll;
|
|
2009
|
+
element.removeEventListener("touchstart", onTouchStart);
|
|
2010
|
+
element.removeEventListener("touchmove", onTouchMove);
|
|
2011
|
+
element.removeEventListener("touchend", onTouchEnd);
|
|
2012
|
+
element.removeEventListener("touchcancel", onCancel);
|
|
2013
|
+
element.removeEventListener("contextmenu", onCancel);
|
|
2014
|
+
element.removeEventListener("dragstart", onCancel);
|
|
2015
|
+
gestureRef.current = IDLE_PULL_GESTURE;
|
|
2016
|
+
setGesture(IDLE_PULL_GESTURE);
|
|
2017
|
+
};
|
|
2018
|
+
}, [
|
|
2019
|
+
enabled,
|
|
2020
|
+
scrollRef,
|
|
2021
|
+
onRefreshRef,
|
|
2022
|
+
threshold,
|
|
2023
|
+
maxDistance,
|
|
2024
|
+
holdDistance,
|
|
2025
|
+
platform
|
|
2026
|
+
]);
|
|
2027
|
+
const state = pullState(gesture);
|
|
2028
|
+
const distance = state === "idle" ? 0 : gesture.distance;
|
|
2029
|
+
return {
|
|
2030
|
+
state,
|
|
2031
|
+
distance,
|
|
2032
|
+
progress: state === "refreshing" || gesture.armed ? 1 : pullProgress(distance, threshold),
|
|
2033
|
+
platform
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
//#endregion
|
|
2037
|
+
//#region src/components/pull-to-refresh/pull-to-refresh-indicator.tsx
|
|
2038
|
+
const pullStepStyle = /* @__PURE__ */ jsx("style", {
|
|
2039
|
+
href: "raft-ui-pull-to-refresh",
|
|
2040
|
+
precedence: "default",
|
|
2041
|
+
children: `
|
|
2042
|
+
[data-slot="pull-to-refresh-indicator"][data-pull-step] [data-spinner-pixel],
|
|
2043
|
+
[data-slot="pull-to-refresh-indicator"][data-state="idle"] [data-spinner-pixel] {
|
|
2044
|
+
animation: none;
|
|
2045
|
+
opacity: 0.2;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
[data-slot="pull-to-refresh-indicator"][data-pull-step="0"] [data-spinner-pixel]:nth-child(1),
|
|
2049
|
+
[data-slot="pull-to-refresh-indicator"][data-pull-step="1"] [data-spinner-pixel]:nth-child(2),
|
|
2050
|
+
[data-slot="pull-to-refresh-indicator"][data-pull-step="2"] [data-spinner-pixel]:nth-child(4),
|
|
2051
|
+
[data-slot="pull-to-refresh-indicator"][data-pull-step="3"] [data-spinner-pixel]:nth-child(3) {
|
|
2052
|
+
opacity: 1;
|
|
2053
|
+
}
|
|
2054
|
+
`
|
|
2055
|
+
});
|
|
2056
|
+
const SNAP_BACK = ["duration-300 ease-slide motion-reduce:transition-none", "data-[state=pulling]:transition-none"];
|
|
2057
|
+
const pullToRefreshIndicator = tv({
|
|
2058
|
+
slots: {
|
|
2059
|
+
root: ["flex w-full shrink-0 justify-center", "text-foreground-icon"],
|
|
2060
|
+
surface: ["flex items-center justify-center"],
|
|
2061
|
+
spinner: [
|
|
2062
|
+
"transition-opacity duration-300 ease-slide motion-reduce:transition-none",
|
|
2063
|
+
"in-data-[state=pulling]:transition-none",
|
|
2064
|
+
"in-data-[state=pulling]:opacity-(--pull-progress) in-data-[state=idle]:opacity-0"
|
|
2065
|
+
],
|
|
2066
|
+
status: ["sr-only"]
|
|
2067
|
+
},
|
|
2068
|
+
variants: { platform: {
|
|
2069
|
+
default: {
|
|
2070
|
+
root: [
|
|
2071
|
+
"relative items-center overflow-hidden",
|
|
2072
|
+
"transition-[height]",
|
|
2073
|
+
...SNAP_BACK
|
|
2074
|
+
],
|
|
2075
|
+
surface: ["size-full"]
|
|
2076
|
+
},
|
|
2077
|
+
android: {
|
|
2078
|
+
root: ["sticky top-0 z-10 h-0 overflow-visible"],
|
|
2079
|
+
surface: [
|
|
2080
|
+
"absolute inset-x-0 top-0 mx-auto size-10 rounded-md bg-layer-popover shadow-float ring-1 ring-line-seam",
|
|
2081
|
+
"translate-y-[calc(var(--pull-distance)-100%)]",
|
|
2082
|
+
"transition-[translate,opacity]",
|
|
2083
|
+
...SNAP_BACK,
|
|
2084
|
+
"data-[state=idle]:opacity-0"
|
|
2085
|
+
]
|
|
2086
|
+
}
|
|
2087
|
+
} },
|
|
2088
|
+
defaultVariants: { platform: "default" }
|
|
2089
|
+
});
|
|
2090
|
+
/**
|
|
2091
|
+
* The pull's indicator, with the DS Spinner in it. Render it as the
|
|
2092
|
+
* scroller's first child: by default it is the space the pull opens, so the
|
|
2093
|
+
* content moves down with the pull; on Android it travels down over the
|
|
2094
|
+
* content instead (Material 3). It holds a polite live region that announces
|
|
2095
|
+
* `label` while refreshing.
|
|
2096
|
+
*/
|
|
2097
|
+
function PullToRefreshIndicator({ pull, label, className, style, ...props }) {
|
|
2098
|
+
const { state, distance, progress, platform } = pull;
|
|
2099
|
+
const { root, surface, spinner, status } = pullToRefreshIndicator({ platform });
|
|
2100
|
+
const android = platform === "android";
|
|
2101
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2102
|
+
"data-slot": "pull-to-refresh-indicator",
|
|
2103
|
+
"data-state": state,
|
|
2104
|
+
"data-platform": platform,
|
|
2105
|
+
"data-pull-step": state === "pulling" ? pullStep(progress) : void 0,
|
|
2106
|
+
className: root({ className }),
|
|
2107
|
+
style: {
|
|
2108
|
+
...style,
|
|
2109
|
+
height: android ? void 0 : distance,
|
|
2110
|
+
["--pull-progress"]: progress,
|
|
2111
|
+
["--pull-distance"]: `${distance}px`
|
|
2112
|
+
},
|
|
2113
|
+
...props,
|
|
2114
|
+
children: [
|
|
2115
|
+
pullStepStyle,
|
|
2116
|
+
/* @__PURE__ */ jsx("div", {
|
|
2117
|
+
"data-slot": "pull-to-refresh-surface",
|
|
2118
|
+
"data-state": state,
|
|
2119
|
+
className: surface(),
|
|
2120
|
+
children: /* @__PURE__ */ jsx(Spinner, {
|
|
2121
|
+
"aria-hidden": "true",
|
|
2122
|
+
role: "presentation",
|
|
2123
|
+
"aria-label": void 0,
|
|
2124
|
+
className: spinner()
|
|
2125
|
+
})
|
|
2126
|
+
}),
|
|
2127
|
+
/* @__PURE__ */ jsx("span", {
|
|
2128
|
+
role: "status",
|
|
2129
|
+
"aria-live": "polite",
|
|
2130
|
+
className: status(),
|
|
2131
|
+
children: state === "refreshing" ? label : ""
|
|
2132
|
+
})
|
|
2133
|
+
]
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
//#endregion
|
|
2137
|
+
export { Dialog as AnnouncementDialog, AnnouncementDialogAction, AnnouncementDialogBody, DialogClose as AnnouncementDialogClose, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle, DialogTrigger as AnnouncementDialogTrigger, COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, ComboboxSelect, CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger, CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime, CommentMargin, CommentMarginItem, CommentMarker, DialogCommit, DialogFooterNote, DialogSheetBack, DialogSheetContent, DropdownMenuLinkItem, Ellipsis, EllipsisToggle, InboxItemLeadingBadge, InboxItemSkeleton, InboxItemSkeletonList, ListRowSkeleton, MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName, MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup, MessageQuotedPreviewAuthorDelegatedBy, MessageReferenceIcon, MessageReferenceReadMark, Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue, MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel, OverlayLayerProvider, PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_POINTER_QUERY, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD, PopoverActions, PopoverBody, PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta, PullToRefreshIndicator, resolveCommentMarginLayout, setCommentHighlights, useCommentMarginFits, useHoldThenMoveTouchSensor, useParentOverlayKind, usePullToRefresh, useTruncated };
|