@transferwise/components 0.0.0-experimental-1ce1b08 → 0.0.0-experimental-150c7ca
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/build/Prompt/Snackbar/Snackbar.js +4 -7
- package/build/Prompt/Snackbar/Snackbar.js.map +1 -1
- package/build/Prompt/Snackbar/Snackbar.mjs +4 -7
- package/build/Prompt/Snackbar/Snackbar.mjs.map +1 -1
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.js +33 -46
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.js.map +1 -1
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.mjs +34 -47
- package/build/Prompt/Snackbar/useSnackbarSwipeDismiss.mjs.map +1 -1
- package/build/types/Prompt/Snackbar/Snackbar.d.ts.map +1 -1
- package/build/types/Prompt/Snackbar/useSnackbarSwipeDismiss.d.ts +6 -8
- package/build/types/Prompt/Snackbar/useSnackbarSwipeDismiss.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Prompt/Snackbar/Snackbar.test.tsx +1 -69
- package/src/Prompt/Snackbar/Snackbar.tsx +4 -6
- package/src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts +39 -54
|
@@ -70,7 +70,7 @@ const Snackbar = ({
|
|
|
70
70
|
visible
|
|
71
71
|
});
|
|
72
72
|
const {
|
|
73
|
-
|
|
73
|
+
cancelSwipe,
|
|
74
74
|
containerRef,
|
|
75
75
|
handleClickCapture,
|
|
76
76
|
handlePointerCancel,
|
|
@@ -79,8 +79,7 @@ const Snackbar = ({
|
|
|
79
79
|
handlePointerUp
|
|
80
80
|
} = useSnackbarSwipeDismiss.default({
|
|
81
81
|
onDismissRequest,
|
|
82
|
-
onSwipeChange: setSwiping
|
|
83
|
-
visible
|
|
82
|
+
onSwipeChange: setSwiping
|
|
84
83
|
});
|
|
85
84
|
React.useEffect(() => {
|
|
86
85
|
if (!visible) {
|
|
@@ -149,10 +148,8 @@ const Snackbar = ({
|
|
|
149
148
|
},
|
|
150
149
|
unmountOnExit: true,
|
|
151
150
|
onEntered: handleEntered,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
onExited();
|
|
155
|
-
},
|
|
151
|
+
onExit: cancelSwipe,
|
|
152
|
+
onExited: onExited,
|
|
156
153
|
children: /*#__PURE__*/jsxRuntime.jsx(PrimitivePrompt.PrimitivePrompt, {
|
|
157
154
|
ref: surfaceRef,
|
|
158
155
|
className: "wds-Snackbar-container",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Snackbar.js","sources":["../../../src/Prompt/Snackbar/Snackbar.tsx"],"sourcesContent":["import { Tags } from '@transferwise/icons';\nimport { useEffect } from 'react';\nimport { useIntl } from 'react-intl';\nimport { CSSTransition } from 'react-transition-group';\n\nimport Button, { type ButtonReferenceType } from '../../Button';\nimport Body from '../../Body';\nimport { LiveRegion } from '../../common';\nimport type { ThemeDark, ThemeLight } from '../../common';\nimport { PrimitivePrompt } from '../PrimitivePrompt';\nimport ProcessIndicator from '../../ProcessIndicator';\nimport type { Sentiment } from '../../SentimentSurface';\nimport StatusIcon from '../../StatusIcon';\n\nimport { SNACKBAR_ENTER_DURATION, SNACKBAR_EXIT_DURATION } from './constants';\nimport messages from './Snackbar.messages';\nimport type { SnackbarAction, SnackbarDuration } from './Snackbar.types';\nimport useSnackbarAutoDismiss from './useSnackbarAutoDismiss';\nimport useSnackbarSwipeDismiss from './useSnackbarSwipeDismiss';\n\n/**\n * Props for the controlled Snackbar surface, including provider-managed lifecycle controls.\n * Use SnackbarCreationProps when creating notifications through SnackbarProvider/useSnackbar.\n */\nexport interface SnackbarProps {\n /** One optional callback or navigation action. Activating it dismisses after its callback runs. */\n action?: SnackbarAction;\n /** Changes for each new Snackbar so LiveRegion re-announces it. */\n announcementKey: number;\n /**\n * Controls auto-dismiss with a four-second, six-second or indefinite duration after entry finishes.\n * Takes precedence over the deprecated `timeout`.\n * When neither option is supplied, the default is 4000ms (previously 4500ms).\n * @default 'default'\n */\n duration?: SnackbarDuration;\n /**\n * Replaces the media with a progress indicator, uses neutral styling and prevents auto-dismiss.\n * Takes precedence over `media`, `sentiment`, `duration` and the deprecated `timeout`.\n * @default false\n */\n loading?: boolean;\n /** Overrides sentiment-derived media with content for the 24×24px media slot. */\n media?: React.ReactNode;\n /**\n * Called once after this Snackbar finishes exiting, including after an action (e.g. Undo)\n * or replacement by a newer Snackbar. The action callback runs first.\n * Not called for Snackbars discarded before being shown, such as earlier creates batched\n * before the first render.\n */\n onDismiss?: () => void;\n /** Requests an exit; the provider retains the surface until onExited confirms it has finished. */\n onDismissRequest: () => void;\n /** Called after the exit animation so the provider can unmount active or show its replacement. */\n onExited: () => void;\n /**\n * Controls the sentiment surface and default media.\n * @default 'neutral'\n */\n sentiment?: Sentiment;\n /** Message content. Empty strings or absent text dismiss without creating a blank replacement. */\n text: React.ReactNode;\n /** @deprecated Has no effect. Snackbar theming is based on ThemeProvider. */\n theme?: ThemeLight | ThemeDark;\n /**\n * @deprecated Use `duration` instead. Explicit millisecond values remain supported and are not\n * automatically remapped when no `duration` is supplied.\n * To migrate, use `default` for 0–5000ms, `extended` for values above 5000ms, and `indefinite`\n * for intentionally persistent notifications.\n */\n timeout?: number;\n /** @deprecated Has no effect when using SnackbarProvider, which manages Snackbar identity. */\n timestamp?: number;\n /** Controls entry/exit; the surface does not manage visibility itself. */\n visible: boolean;\n}\n\nexport const Snackbar = ({\n action,\n announcementKey,\n duration,\n loading = false,\n media,\n onDismissRequest,\n onExited,\n sentiment = 'neutral',\n text,\n timeout,\n visible,\n}: SnackbarProps) => {\n const { formatMessage } = useIntl();\n const {\n handleBlur,\n handleEntered,\n handleFocus,\n handleMouseEnter,\n handleMouseLeave,\n setSwiping,\n surfaceRef,\n } = useSnackbarAutoDismiss({\n duration,\n loading,\n onDismissRequest,\n timeout,\n visible,\n });\n const {\n clearSwipePosition,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n } = useSnackbarSwipeDismiss({ onDismissRequest, onSwipeChange: setSwiping, visible });\n\n useEffect(() => {\n if (!visible) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !event.defaultPrevented && !event.isComposing) {\n onDismissRequest();\n }\n };\n\n // Let document-level overlays consume Escape before this fallback handles it.\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [visible, onDismissRequest]);\n\n const handleActionClick: React.MouseEventHandler<ButtonReferenceType> = (event) => {\n try {\n action?.onClick?.(event);\n } finally {\n onDismissRequest();\n }\n };\n\n const renderMedia = () => {\n if (loading) {\n return (\n <ProcessIndicator\n className=\"wds-Snackbar__process-indicator\"\n data-testid=\"Snackbar_ProcessIndicator\"\n size=\"xs\"\n />\n );\n }\n\n if (media) {\n return <span className=\"wds-Snackbar__media-asset\">{media}</span>;\n }\n\n if (sentiment === 'proposition') {\n return (\n <span className=\"wds-Snackbar__media-asset\">\n <Tags aria-hidden size={24} />\n </span>\n );\n }\n\n return <StatusIcon sentiment={sentiment} size={24} />;\n };\n\n return (\n <div\n ref={containerRef}\n className=\"wds-Snackbar\"\n style={\n {\n '--Snackbar-enter-duration': `${SNACKBAR_ENTER_DURATION}ms`,\n '--Snackbar-exit-duration': `${SNACKBAR_EXIT_DURATION}ms`,\n } as React.CSSProperties\n }\n >\n <CSSTransition\n appear\n nodeRef={surfaceRef}\n in={visible}\n classNames=\"wds-Snackbar-container\"\n timeout={{\n appear: SNACKBAR_ENTER_DURATION,\n enter: SNACKBAR_ENTER_DURATION,\n exit: SNACKBAR_EXIT_DURATION,\n }}\n unmountOnExit\n onEntered={handleEntered}\n onExited={() => {\n clearSwipePosition();\n onExited();\n }}\n >\n <PrimitivePrompt\n ref={surfaceRef}\n className=\"wds-Snackbar-container\"\n sentiment={loading ? 'neutral' : sentiment}\n emphasis=\"elevated\"\n media={renderMedia()}\n dismissButtonPriority=\"minimal\"\n onDismiss={onDismissRequest}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onClickCapture={handleClickCapture}\n onDragStart={(event) => event.preventDefault()}\n onPointerDown={visible ? handlePointerDown : undefined}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerCancel}\n >\n <div className=\"wds-Snackbar__content\">\n <LiveRegion\n aria-live=\"polite\"\n announceOnChange={announcementKey}\n className=\"wds-Snackbar__message\"\n >\n <Body as=\"span\" className=\"wds-Snackbar__body\">\n {text}\n <span className=\"sr-only\"> {formatMessage(messages.escapeDismissHint)}</span>\n </Body>\n </LiveRegion>\n {action ? (\n <Button\n className=\"wds-Snackbar__action\"\n v2\n size=\"sm\"\n priority=\"secondary\"\n href={action.href}\n target={action.target}\n onClick={handleActionClick}\n >\n {action.label}\n </Button>\n ) : null}\n </div>\n </PrimitivePrompt>\n </CSSTransition>\n </div>\n );\n};\n\nexport default Snackbar;\n"],"names":["Snackbar","action","announcementKey","duration","loading","media","onDismissRequest","onExited","sentiment","text","timeout","visible","formatMessage","useIntl","handleBlur","handleEntered","handleFocus","handleMouseEnter","handleMouseLeave","setSwiping","surfaceRef","useSnackbarAutoDismiss","clearSwipePosition","containerRef","handleClickCapture","handlePointerCancel","handlePointerDown","handlePointerMove","handlePointerUp","useSnackbarSwipeDismiss","onSwipeChange","useEffect","undefined","handleKeyDown","event","key","defaultPrevented","isComposing","window","addEventListener","removeEventListener","handleActionClick","onClick","renderMedia","_jsx","ProcessIndicator","className","size","children","Tags","StatusIcon","ref","style","SNACKBAR_ENTER_DURATION","SNACKBAR_EXIT_DURATION","CSSTransition","appear","nodeRef","in","classNames","enter","exit","unmountOnExit","onEntered","PrimitivePrompt","emphasis","dismissButtonPriority","onDismiss","onMouseEnter","onMouseLeave","onFocus","onBlur","onClickCapture","onDragStart","preventDefault","onPointerDown","onPointerMove","onPointerUp","onPointerCancel","_jsxs","LiveRegion","announceOnChange","Body","as","messages","escapeDismissHint","Button","v2","priority","href","target","label"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Snackbar.js","sources":["../../../src/Prompt/Snackbar/Snackbar.tsx"],"sourcesContent":["import { Tags } from '@transferwise/icons';\nimport { useEffect } from 'react';\nimport { useIntl } from 'react-intl';\nimport { CSSTransition } from 'react-transition-group';\n\nimport Button, { type ButtonReferenceType } from '../../Button';\nimport Body from '../../Body';\nimport { LiveRegion } from '../../common';\nimport type { ThemeDark, ThemeLight } from '../../common';\nimport { PrimitivePrompt } from '../PrimitivePrompt';\nimport ProcessIndicator from '../../ProcessIndicator';\nimport type { Sentiment } from '../../SentimentSurface';\nimport StatusIcon from '../../StatusIcon';\n\nimport { SNACKBAR_ENTER_DURATION, SNACKBAR_EXIT_DURATION } from './constants';\nimport messages from './Snackbar.messages';\nimport type { SnackbarAction, SnackbarDuration } from './Snackbar.types';\nimport useSnackbarAutoDismiss from './useSnackbarAutoDismiss';\nimport useSnackbarSwipeDismiss from './useSnackbarSwipeDismiss';\n\n/**\n * Props for the controlled Snackbar surface, including provider-managed lifecycle controls.\n * Use SnackbarCreationProps when creating notifications through SnackbarProvider/useSnackbar.\n */\nexport interface SnackbarProps {\n /** One optional callback or navigation action. Activating it dismisses after its callback runs. */\n action?: SnackbarAction;\n /** Changes for each new Snackbar so LiveRegion re-announces it. */\n announcementKey: number;\n /**\n * Controls auto-dismiss with a four-second, six-second or indefinite duration after entry finishes.\n * Takes precedence over the deprecated `timeout`.\n * When neither option is supplied, the default is 4000ms (previously 4500ms).\n * @default 'default'\n */\n duration?: SnackbarDuration;\n /**\n * Replaces the media with a progress indicator, uses neutral styling and prevents auto-dismiss.\n * Takes precedence over `media`, `sentiment`, `duration` and the deprecated `timeout`.\n * @default false\n */\n loading?: boolean;\n /** Overrides sentiment-derived media with content for the 24×24px media slot. */\n media?: React.ReactNode;\n /**\n * Called once after this Snackbar finishes exiting, including after an action (e.g. Undo)\n * or replacement by a newer Snackbar. The action callback runs first.\n * Not called for Snackbars discarded before being shown, such as earlier creates batched\n * before the first render.\n */\n onDismiss?: () => void;\n /** Requests an exit; the provider retains the surface until onExited confirms it has finished. */\n onDismissRequest: () => void;\n /** Called after the exit animation so the provider can unmount active or show its replacement. */\n onExited: () => void;\n /**\n * Controls the sentiment surface and default media.\n * @default 'neutral'\n */\n sentiment?: Sentiment;\n /** Message content. Empty strings or absent text dismiss without creating a blank replacement. */\n text: React.ReactNode;\n /** @deprecated Has no effect. Snackbar theming is based on ThemeProvider. */\n theme?: ThemeLight | ThemeDark;\n /**\n * @deprecated Use `duration` instead. Explicit millisecond values remain supported and are not\n * automatically remapped when no `duration` is supplied.\n * To migrate, use `default` for 0–5000ms, `extended` for values above 5000ms, and `indefinite`\n * for intentionally persistent notifications.\n */\n timeout?: number;\n /** @deprecated Has no effect when using SnackbarProvider, which manages Snackbar identity. */\n timestamp?: number;\n /** Controls entry/exit; the surface does not manage visibility itself. */\n visible: boolean;\n}\n\nexport const Snackbar = ({\n action,\n announcementKey,\n duration,\n loading = false,\n media,\n onDismissRequest,\n onExited,\n sentiment = 'neutral',\n text,\n timeout,\n visible,\n}: SnackbarProps) => {\n const { formatMessage } = useIntl();\n const {\n handleBlur,\n handleEntered,\n handleFocus,\n handleMouseEnter,\n handleMouseLeave,\n setSwiping,\n surfaceRef,\n } = useSnackbarAutoDismiss({\n duration,\n loading,\n onDismissRequest,\n timeout,\n visible,\n });\n const {\n cancelSwipe,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n } = useSnackbarSwipeDismiss({ onDismissRequest, onSwipeChange: setSwiping });\n\n useEffect(() => {\n if (!visible) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !event.defaultPrevented && !event.isComposing) {\n onDismissRequest();\n }\n };\n\n // Let document-level overlays consume Escape before this fallback handles it.\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [visible, onDismissRequest]);\n\n const handleActionClick: React.MouseEventHandler<ButtonReferenceType> = (event) => {\n try {\n action?.onClick?.(event);\n } finally {\n onDismissRequest();\n }\n };\n\n const renderMedia = () => {\n if (loading) {\n return (\n <ProcessIndicator\n className=\"wds-Snackbar__process-indicator\"\n data-testid=\"Snackbar_ProcessIndicator\"\n size=\"xs\"\n />\n );\n }\n\n if (media) {\n return <span className=\"wds-Snackbar__media-asset\">{media}</span>;\n }\n\n if (sentiment === 'proposition') {\n return (\n <span className=\"wds-Snackbar__media-asset\">\n <Tags aria-hidden size={24} />\n </span>\n );\n }\n\n return <StatusIcon sentiment={sentiment} size={24} />;\n };\n\n return (\n <div\n ref={containerRef}\n className=\"wds-Snackbar\"\n style={\n {\n '--Snackbar-enter-duration': `${SNACKBAR_ENTER_DURATION}ms`,\n '--Snackbar-exit-duration': `${SNACKBAR_EXIT_DURATION}ms`,\n } as React.CSSProperties\n }\n >\n <CSSTransition\n appear\n nodeRef={surfaceRef}\n in={visible}\n classNames=\"wds-Snackbar-container\"\n timeout={{\n appear: SNACKBAR_ENTER_DURATION,\n enter: SNACKBAR_ENTER_DURATION,\n exit: SNACKBAR_EXIT_DURATION,\n }}\n unmountOnExit\n onEntered={handleEntered}\n onExit={cancelSwipe}\n onExited={onExited}\n >\n <PrimitivePrompt\n ref={surfaceRef}\n className=\"wds-Snackbar-container\"\n sentiment={loading ? 'neutral' : sentiment}\n emphasis=\"elevated\"\n media={renderMedia()}\n dismissButtonPriority=\"minimal\"\n onDismiss={onDismissRequest}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onClickCapture={handleClickCapture}\n onDragStart={(event) => event.preventDefault()}\n onPointerDown={visible ? handlePointerDown : undefined}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerCancel}\n >\n <div className=\"wds-Snackbar__content\">\n <LiveRegion\n aria-live=\"polite\"\n announceOnChange={announcementKey}\n className=\"wds-Snackbar__message\"\n >\n <Body as=\"span\" className=\"wds-Snackbar__body\">\n {text}\n <span className=\"sr-only\"> {formatMessage(messages.escapeDismissHint)}</span>\n </Body>\n </LiveRegion>\n {action ? (\n <Button\n className=\"wds-Snackbar__action\"\n v2\n size=\"sm\"\n priority=\"secondary\"\n href={action.href}\n target={action.target}\n onClick={handleActionClick}\n >\n {action.label}\n </Button>\n ) : null}\n </div>\n </PrimitivePrompt>\n </CSSTransition>\n </div>\n );\n};\n\nexport default Snackbar;\n"],"names":["Snackbar","action","announcementKey","duration","loading","media","onDismissRequest","onExited","sentiment","text","timeout","visible","formatMessage","useIntl","handleBlur","handleEntered","handleFocus","handleMouseEnter","handleMouseLeave","setSwiping","surfaceRef","useSnackbarAutoDismiss","cancelSwipe","containerRef","handleClickCapture","handlePointerCancel","handlePointerDown","handlePointerMove","handlePointerUp","useSnackbarSwipeDismiss","onSwipeChange","useEffect","undefined","handleKeyDown","event","key","defaultPrevented","isComposing","window","addEventListener","removeEventListener","handleActionClick","onClick","renderMedia","_jsx","ProcessIndicator","className","size","children","Tags","StatusIcon","ref","style","SNACKBAR_ENTER_DURATION","SNACKBAR_EXIT_DURATION","CSSTransition","appear","nodeRef","in","classNames","enter","exit","unmountOnExit","onEntered","onExit","PrimitivePrompt","emphasis","dismissButtonPriority","onDismiss","onMouseEnter","onMouseLeave","onFocus","onBlur","onClickCapture","onDragStart","preventDefault","onPointerDown","onPointerMove","onPointerUp","onPointerCancel","_jsxs","LiveRegion","announceOnChange","Body","as","messages","escapeDismissHint","Button","v2","priority","href","target","label"],"mappings":"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"}
|
|
@@ -68,7 +68,7 @@ const Snackbar = ({
|
|
|
68
68
|
visible
|
|
69
69
|
});
|
|
70
70
|
const {
|
|
71
|
-
|
|
71
|
+
cancelSwipe,
|
|
72
72
|
containerRef,
|
|
73
73
|
handleClickCapture,
|
|
74
74
|
handlePointerCancel,
|
|
@@ -77,8 +77,7 @@ const Snackbar = ({
|
|
|
77
77
|
handlePointerUp
|
|
78
78
|
} = useSnackbarSwipeDismiss({
|
|
79
79
|
onDismissRequest,
|
|
80
|
-
onSwipeChange: setSwiping
|
|
81
|
-
visible
|
|
80
|
+
onSwipeChange: setSwiping
|
|
82
81
|
});
|
|
83
82
|
useEffect(() => {
|
|
84
83
|
if (!visible) {
|
|
@@ -147,10 +146,8 @@ const Snackbar = ({
|
|
|
147
146
|
},
|
|
148
147
|
unmountOnExit: true,
|
|
149
148
|
onEntered: handleEntered,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
onExited();
|
|
153
|
-
},
|
|
149
|
+
onExit: cancelSwipe,
|
|
150
|
+
onExited: onExited,
|
|
154
151
|
children: /*#__PURE__*/jsx(PrimitivePrompt, {
|
|
155
152
|
ref: surfaceRef,
|
|
156
153
|
className: "wds-Snackbar-container",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Snackbar.mjs","sources":["../../../src/Prompt/Snackbar/Snackbar.tsx"],"sourcesContent":["import { Tags } from '@transferwise/icons';\nimport { useEffect } from 'react';\nimport { useIntl } from 'react-intl';\nimport { CSSTransition } from 'react-transition-group';\n\nimport Button, { type ButtonReferenceType } from '../../Button';\nimport Body from '../../Body';\nimport { LiveRegion } from '../../common';\nimport type { ThemeDark, ThemeLight } from '../../common';\nimport { PrimitivePrompt } from '../PrimitivePrompt';\nimport ProcessIndicator from '../../ProcessIndicator';\nimport type { Sentiment } from '../../SentimentSurface';\nimport StatusIcon from '../../StatusIcon';\n\nimport { SNACKBAR_ENTER_DURATION, SNACKBAR_EXIT_DURATION } from './constants';\nimport messages from './Snackbar.messages';\nimport type { SnackbarAction, SnackbarDuration } from './Snackbar.types';\nimport useSnackbarAutoDismiss from './useSnackbarAutoDismiss';\nimport useSnackbarSwipeDismiss from './useSnackbarSwipeDismiss';\n\n/**\n * Props for the controlled Snackbar surface, including provider-managed lifecycle controls.\n * Use SnackbarCreationProps when creating notifications through SnackbarProvider/useSnackbar.\n */\nexport interface SnackbarProps {\n /** One optional callback or navigation action. Activating it dismisses after its callback runs. */\n action?: SnackbarAction;\n /** Changes for each new Snackbar so LiveRegion re-announces it. */\n announcementKey: number;\n /**\n * Controls auto-dismiss with a four-second, six-second or indefinite duration after entry finishes.\n * Takes precedence over the deprecated `timeout`.\n * When neither option is supplied, the default is 4000ms (previously 4500ms).\n * @default 'default'\n */\n duration?: SnackbarDuration;\n /**\n * Replaces the media with a progress indicator, uses neutral styling and prevents auto-dismiss.\n * Takes precedence over `media`, `sentiment`, `duration` and the deprecated `timeout`.\n * @default false\n */\n loading?: boolean;\n /** Overrides sentiment-derived media with content for the 24×24px media slot. */\n media?: React.ReactNode;\n /**\n * Called once after this Snackbar finishes exiting, including after an action (e.g. Undo)\n * or replacement by a newer Snackbar. The action callback runs first.\n * Not called for Snackbars discarded before being shown, such as earlier creates batched\n * before the first render.\n */\n onDismiss?: () => void;\n /** Requests an exit; the provider retains the surface until onExited confirms it has finished. */\n onDismissRequest: () => void;\n /** Called after the exit animation so the provider can unmount active or show its replacement. */\n onExited: () => void;\n /**\n * Controls the sentiment surface and default media.\n * @default 'neutral'\n */\n sentiment?: Sentiment;\n /** Message content. Empty strings or absent text dismiss without creating a blank replacement. */\n text: React.ReactNode;\n /** @deprecated Has no effect. Snackbar theming is based on ThemeProvider. */\n theme?: ThemeLight | ThemeDark;\n /**\n * @deprecated Use `duration` instead. Explicit millisecond values remain supported and are not\n * automatically remapped when no `duration` is supplied.\n * To migrate, use `default` for 0–5000ms, `extended` for values above 5000ms, and `indefinite`\n * for intentionally persistent notifications.\n */\n timeout?: number;\n /** @deprecated Has no effect when using SnackbarProvider, which manages Snackbar identity. */\n timestamp?: number;\n /** Controls entry/exit; the surface does not manage visibility itself. */\n visible: boolean;\n}\n\nexport const Snackbar = ({\n action,\n announcementKey,\n duration,\n loading = false,\n media,\n onDismissRequest,\n onExited,\n sentiment = 'neutral',\n text,\n timeout,\n visible,\n}: SnackbarProps) => {\n const { formatMessage } = useIntl();\n const {\n handleBlur,\n handleEntered,\n handleFocus,\n handleMouseEnter,\n handleMouseLeave,\n setSwiping,\n surfaceRef,\n } = useSnackbarAutoDismiss({\n duration,\n loading,\n onDismissRequest,\n timeout,\n visible,\n });\n const {\n clearSwipePosition,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n } = useSnackbarSwipeDismiss({ onDismissRequest, onSwipeChange: setSwiping, visible });\n\n useEffect(() => {\n if (!visible) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !event.defaultPrevented && !event.isComposing) {\n onDismissRequest();\n }\n };\n\n // Let document-level overlays consume Escape before this fallback handles it.\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [visible, onDismissRequest]);\n\n const handleActionClick: React.MouseEventHandler<ButtonReferenceType> = (event) => {\n try {\n action?.onClick?.(event);\n } finally {\n onDismissRequest();\n }\n };\n\n const renderMedia = () => {\n if (loading) {\n return (\n <ProcessIndicator\n className=\"wds-Snackbar__process-indicator\"\n data-testid=\"Snackbar_ProcessIndicator\"\n size=\"xs\"\n />\n );\n }\n\n if (media) {\n return <span className=\"wds-Snackbar__media-asset\">{media}</span>;\n }\n\n if (sentiment === 'proposition') {\n return (\n <span className=\"wds-Snackbar__media-asset\">\n <Tags aria-hidden size={24} />\n </span>\n );\n }\n\n return <StatusIcon sentiment={sentiment} size={24} />;\n };\n\n return (\n <div\n ref={containerRef}\n className=\"wds-Snackbar\"\n style={\n {\n '--Snackbar-enter-duration': `${SNACKBAR_ENTER_DURATION}ms`,\n '--Snackbar-exit-duration': `${SNACKBAR_EXIT_DURATION}ms`,\n } as React.CSSProperties\n }\n >\n <CSSTransition\n appear\n nodeRef={surfaceRef}\n in={visible}\n classNames=\"wds-Snackbar-container\"\n timeout={{\n appear: SNACKBAR_ENTER_DURATION,\n enter: SNACKBAR_ENTER_DURATION,\n exit: SNACKBAR_EXIT_DURATION,\n }}\n unmountOnExit\n onEntered={handleEntered}\n onExited={() => {\n clearSwipePosition();\n onExited();\n }}\n >\n <PrimitivePrompt\n ref={surfaceRef}\n className=\"wds-Snackbar-container\"\n sentiment={loading ? 'neutral' : sentiment}\n emphasis=\"elevated\"\n media={renderMedia()}\n dismissButtonPriority=\"minimal\"\n onDismiss={onDismissRequest}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onClickCapture={handleClickCapture}\n onDragStart={(event) => event.preventDefault()}\n onPointerDown={visible ? handlePointerDown : undefined}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerCancel}\n >\n <div className=\"wds-Snackbar__content\">\n <LiveRegion\n aria-live=\"polite\"\n announceOnChange={announcementKey}\n className=\"wds-Snackbar__message\"\n >\n <Body as=\"span\" className=\"wds-Snackbar__body\">\n {text}\n <span className=\"sr-only\"> {formatMessage(messages.escapeDismissHint)}</span>\n </Body>\n </LiveRegion>\n {action ? (\n <Button\n className=\"wds-Snackbar__action\"\n v2\n size=\"sm\"\n priority=\"secondary\"\n href={action.href}\n target={action.target}\n onClick={handleActionClick}\n >\n {action.label}\n </Button>\n ) : null}\n </div>\n </PrimitivePrompt>\n </CSSTransition>\n </div>\n );\n};\n\nexport default Snackbar;\n"],"names":["Snackbar","action","announcementKey","duration","loading","media","onDismissRequest","onExited","sentiment","text","timeout","visible","formatMessage","useIntl","handleBlur","handleEntered","handleFocus","handleMouseEnter","handleMouseLeave","setSwiping","surfaceRef","useSnackbarAutoDismiss","clearSwipePosition","containerRef","handleClickCapture","handlePointerCancel","handlePointerDown","handlePointerMove","handlePointerUp","useSnackbarSwipeDismiss","onSwipeChange","useEffect","undefined","handleKeyDown","event","key","defaultPrevented","isComposing","window","addEventListener","removeEventListener","handleActionClick","onClick","renderMedia","_jsx","ProcessIndicator","className","size","children","Tags","StatusIcon","ref","style","SNACKBAR_ENTER_DURATION","SNACKBAR_EXIT_DURATION","CSSTransition","appear","nodeRef","in","classNames","enter","exit","unmountOnExit","onEntered","PrimitivePrompt","emphasis","dismissButtonPriority","onDismiss","onMouseEnter","onMouseLeave","onFocus","onBlur","onClickCapture","onDragStart","preventDefault","onPointerDown","onPointerMove","onPointerUp","onPointerCancel","_jsxs","LiveRegion","announceOnChange","Body","as","messages","escapeDismissHint","Button","v2","priority","href","target","label"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Snackbar.mjs","sources":["../../../src/Prompt/Snackbar/Snackbar.tsx"],"sourcesContent":["import { Tags } from '@transferwise/icons';\nimport { useEffect } from 'react';\nimport { useIntl } from 'react-intl';\nimport { CSSTransition } from 'react-transition-group';\n\nimport Button, { type ButtonReferenceType } from '../../Button';\nimport Body from '../../Body';\nimport { LiveRegion } from '../../common';\nimport type { ThemeDark, ThemeLight } from '../../common';\nimport { PrimitivePrompt } from '../PrimitivePrompt';\nimport ProcessIndicator from '../../ProcessIndicator';\nimport type { Sentiment } from '../../SentimentSurface';\nimport StatusIcon from '../../StatusIcon';\n\nimport { SNACKBAR_ENTER_DURATION, SNACKBAR_EXIT_DURATION } from './constants';\nimport messages from './Snackbar.messages';\nimport type { SnackbarAction, SnackbarDuration } from './Snackbar.types';\nimport useSnackbarAutoDismiss from './useSnackbarAutoDismiss';\nimport useSnackbarSwipeDismiss from './useSnackbarSwipeDismiss';\n\n/**\n * Props for the controlled Snackbar surface, including provider-managed lifecycle controls.\n * Use SnackbarCreationProps when creating notifications through SnackbarProvider/useSnackbar.\n */\nexport interface SnackbarProps {\n /** One optional callback or navigation action. Activating it dismisses after its callback runs. */\n action?: SnackbarAction;\n /** Changes for each new Snackbar so LiveRegion re-announces it. */\n announcementKey: number;\n /**\n * Controls auto-dismiss with a four-second, six-second or indefinite duration after entry finishes.\n * Takes precedence over the deprecated `timeout`.\n * When neither option is supplied, the default is 4000ms (previously 4500ms).\n * @default 'default'\n */\n duration?: SnackbarDuration;\n /**\n * Replaces the media with a progress indicator, uses neutral styling and prevents auto-dismiss.\n * Takes precedence over `media`, `sentiment`, `duration` and the deprecated `timeout`.\n * @default false\n */\n loading?: boolean;\n /** Overrides sentiment-derived media with content for the 24×24px media slot. */\n media?: React.ReactNode;\n /**\n * Called once after this Snackbar finishes exiting, including after an action (e.g. Undo)\n * or replacement by a newer Snackbar. The action callback runs first.\n * Not called for Snackbars discarded before being shown, such as earlier creates batched\n * before the first render.\n */\n onDismiss?: () => void;\n /** Requests an exit; the provider retains the surface until onExited confirms it has finished. */\n onDismissRequest: () => void;\n /** Called after the exit animation so the provider can unmount active or show its replacement. */\n onExited: () => void;\n /**\n * Controls the sentiment surface and default media.\n * @default 'neutral'\n */\n sentiment?: Sentiment;\n /** Message content. Empty strings or absent text dismiss without creating a blank replacement. */\n text: React.ReactNode;\n /** @deprecated Has no effect. Snackbar theming is based on ThemeProvider. */\n theme?: ThemeLight | ThemeDark;\n /**\n * @deprecated Use `duration` instead. Explicit millisecond values remain supported and are not\n * automatically remapped when no `duration` is supplied.\n * To migrate, use `default` for 0–5000ms, `extended` for values above 5000ms, and `indefinite`\n * for intentionally persistent notifications.\n */\n timeout?: number;\n /** @deprecated Has no effect when using SnackbarProvider, which manages Snackbar identity. */\n timestamp?: number;\n /** Controls entry/exit; the surface does not manage visibility itself. */\n visible: boolean;\n}\n\nexport const Snackbar = ({\n action,\n announcementKey,\n duration,\n loading = false,\n media,\n onDismissRequest,\n onExited,\n sentiment = 'neutral',\n text,\n timeout,\n visible,\n}: SnackbarProps) => {\n const { formatMessage } = useIntl();\n const {\n handleBlur,\n handleEntered,\n handleFocus,\n handleMouseEnter,\n handleMouseLeave,\n setSwiping,\n surfaceRef,\n } = useSnackbarAutoDismiss({\n duration,\n loading,\n onDismissRequest,\n timeout,\n visible,\n });\n const {\n cancelSwipe,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n } = useSnackbarSwipeDismiss({ onDismissRequest, onSwipeChange: setSwiping });\n\n useEffect(() => {\n if (!visible) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !event.defaultPrevented && !event.isComposing) {\n onDismissRequest();\n }\n };\n\n // Let document-level overlays consume Escape before this fallback handles it.\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [visible, onDismissRequest]);\n\n const handleActionClick: React.MouseEventHandler<ButtonReferenceType> = (event) => {\n try {\n action?.onClick?.(event);\n } finally {\n onDismissRequest();\n }\n };\n\n const renderMedia = () => {\n if (loading) {\n return (\n <ProcessIndicator\n className=\"wds-Snackbar__process-indicator\"\n data-testid=\"Snackbar_ProcessIndicator\"\n size=\"xs\"\n />\n );\n }\n\n if (media) {\n return <span className=\"wds-Snackbar__media-asset\">{media}</span>;\n }\n\n if (sentiment === 'proposition') {\n return (\n <span className=\"wds-Snackbar__media-asset\">\n <Tags aria-hidden size={24} />\n </span>\n );\n }\n\n return <StatusIcon sentiment={sentiment} size={24} />;\n };\n\n return (\n <div\n ref={containerRef}\n className=\"wds-Snackbar\"\n style={\n {\n '--Snackbar-enter-duration': `${SNACKBAR_ENTER_DURATION}ms`,\n '--Snackbar-exit-duration': `${SNACKBAR_EXIT_DURATION}ms`,\n } as React.CSSProperties\n }\n >\n <CSSTransition\n appear\n nodeRef={surfaceRef}\n in={visible}\n classNames=\"wds-Snackbar-container\"\n timeout={{\n appear: SNACKBAR_ENTER_DURATION,\n enter: SNACKBAR_ENTER_DURATION,\n exit: SNACKBAR_EXIT_DURATION,\n }}\n unmountOnExit\n onEntered={handleEntered}\n onExit={cancelSwipe}\n onExited={onExited}\n >\n <PrimitivePrompt\n ref={surfaceRef}\n className=\"wds-Snackbar-container\"\n sentiment={loading ? 'neutral' : sentiment}\n emphasis=\"elevated\"\n media={renderMedia()}\n dismissButtonPriority=\"minimal\"\n onDismiss={onDismissRequest}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onClickCapture={handleClickCapture}\n onDragStart={(event) => event.preventDefault()}\n onPointerDown={visible ? handlePointerDown : undefined}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerCancel}\n >\n <div className=\"wds-Snackbar__content\">\n <LiveRegion\n aria-live=\"polite\"\n announceOnChange={announcementKey}\n className=\"wds-Snackbar__message\"\n >\n <Body as=\"span\" className=\"wds-Snackbar__body\">\n {text}\n <span className=\"sr-only\"> {formatMessage(messages.escapeDismissHint)}</span>\n </Body>\n </LiveRegion>\n {action ? (\n <Button\n className=\"wds-Snackbar__action\"\n v2\n size=\"sm\"\n priority=\"secondary\"\n href={action.href}\n target={action.target}\n onClick={handleActionClick}\n >\n {action.label}\n </Button>\n ) : null}\n </div>\n </PrimitivePrompt>\n </CSSTransition>\n </div>\n );\n};\n\nexport default Snackbar;\n"],"names":["Snackbar","action","announcementKey","duration","loading","media","onDismissRequest","onExited","sentiment","text","timeout","visible","formatMessage","useIntl","handleBlur","handleEntered","handleFocus","handleMouseEnter","handleMouseLeave","setSwiping","surfaceRef","useSnackbarAutoDismiss","cancelSwipe","containerRef","handleClickCapture","handlePointerCancel","handlePointerDown","handlePointerMove","handlePointerUp","useSnackbarSwipeDismiss","onSwipeChange","useEffect","undefined","handleKeyDown","event","key","defaultPrevented","isComposing","window","addEventListener","removeEventListener","handleActionClick","onClick","renderMedia","_jsx","ProcessIndicator","className","size","children","Tags","StatusIcon","ref","style","SNACKBAR_ENTER_DURATION","SNACKBAR_EXIT_DURATION","CSSTransition","appear","nodeRef","in","classNames","enter","exit","unmountOnExit","onEntered","onExit","PrimitivePrompt","emphasis","dismissButtonPriority","onDismiss","onMouseEnter","onMouseLeave","onFocus","onBlur","onClickCapture","onDragStart","preventDefault","onPointerDown","onPointerMove","onPointerUp","onPointerCancel","_jsxs","LiveRegion","announceOnChange","Body","as","messages","escapeDismissHint","Button","v2","priority","href","target","label"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6EO,MAAMA,QAAQ,GAAGA,CAAC;EACvBC,MAAM;EACNC,eAAe;EACfC,QAAQ;AACRC,EAAAA,OAAO,GAAG,KAAK;EACfC,KAAK;EACLC,gBAAgB;EAChBC,QAAQ;AACRC,EAAAA,SAAS,GAAG,SAAS;EACrBC,IAAI;EACJC,OAAO;AACPC,EAAAA;AAAO,CACO,KAAI;EAClB,MAAM;AAAEC,IAAAA;GAAe,GAAGC,OAAO,EAAE;EACnC,MAAM;IACJC,UAAU;IACVC,aAAa;IACbC,WAAW;IACXC,gBAAgB;IAChBC,gBAAgB;IAChBC,UAAU;AACVC,IAAAA;GACD,GAAGC,sBAAsB,CAAC;IACzBlB,QAAQ;IACRC,OAAO;IACPE,gBAAgB;IAChBI,OAAO;AACPC,IAAAA;AACD,GAAA,CAAC;EACF,MAAM;IACJW,WAAW;IACXC,YAAY;IACZC,kBAAkB;IAClBC,mBAAmB;IACnBC,iBAAiB;IACjBC,iBAAiB;AACjBC,IAAAA;GACD,GAAGC,uBAAuB,CAAC;IAAEvB,gBAAgB;AAAEwB,IAAAA,aAAa,EAAEX;AAAU,GAAE,CAAC;AAE5EY,EAAAA,SAAS,CAAC,MAAK;IACb,IAAI,CAACpB,OAAO,EAAE;AACZ,MAAA,OAAOqB,SAAS;AAClB,IAAA;IAEA,MAAMC,aAAa,GAAIC,KAAoB,IAAI;AAC7C,MAAA,IAAIA,KAAK,CAACC,GAAG,KAAK,QAAQ,IAAI,CAACD,KAAK,CAACE,gBAAgB,IAAI,CAACF,KAAK,CAACG,WAAW,EAAE;AAC3E/B,QAAAA,gBAAgB,EAAE;AACpB,MAAA;IACF,CAAC;AAED;AACAgC,IAAAA,MAAM,CAACC,gBAAgB,CAAC,SAAS,EAAEN,aAAa,CAAC;IACjD,OAAO,MAAMK,MAAM,CAACE,mBAAmB,CAAC,SAAS,EAAEP,aAAa,CAAC;AACnE,EAAA,CAAC,EAAE,CAACtB,OAAO,EAAEL,gBAAgB,CAAC,CAAC;EAE/B,MAAMmC,iBAAiB,GAAkDP,KAAK,IAAI;IAChF,IAAI;AACFjC,MAAAA,MAAM,EAAEyC,OAAO,GAAGR,KAAK,CAAC;AAC1B,IAAA,CAAC,SAAS;AACR5B,MAAAA,gBAAgB,EAAE;AACpB,IAAA;EACF,CAAC;EAED,MAAMqC,WAAW,GAAGA,MAAK;AACvB,IAAA,IAAIvC,OAAO,EAAE;MACX,oBACEwC,GAAA,CAACC,gBAAgB,EAAA;AACfC,QAAAA,SAAS,EAAC,iCAAiC;AAC3C,QAAA,aAAA,EAAY,2BAA2B;AACvCC,QAAAA,IAAI,EAAC;AAAI,OAAA,CACT;AAEN,IAAA;AAEA,IAAA,IAAI1C,KAAK,EAAE;AACT,MAAA,oBAAOuC,GAAA,CAAA,MAAA,EAAA;AAAME,QAAAA,SAAS,EAAC,2BAA2B;AAAAE,QAAAA,QAAA,EAAE3C;AAAK,OAAO,CAAC;AACnE,IAAA;IAEA,IAAIG,SAAS,KAAK,aAAa,EAAE;AAC/B,MAAA,oBACEoC,GAAA,CAAA,MAAA,EAAA;AAAME,QAAAA,SAAS,EAAC,2BAA2B;QAAAE,QAAA,eACzCJ,GAAA,CAACK,IAAI,EAAA;UAAC,aAAA,EAAA,IAAW;AAACF,UAAAA,IAAI,EAAE;SAAG;AAC7B,OAAM,CAAC;AAEX,IAAA;IAEA,oBAAOH,GAAA,CAACM,UAAU,EAAA;AAAC1C,MAAAA,SAAS,EAAEA,SAAU;AAACuC,MAAAA,IAAI,EAAE;AAAG,MAAG;EACvD,CAAC;AAED,EAAA,oBACEH,GAAA,CAAA,KAAA,EAAA;AACEO,IAAAA,GAAG,EAAE5B,YAAa;AAClBuB,IAAAA,SAAS,EAAC,cAAc;AACxBM,IAAAA,KAAK,EACH;MACE,2BAA2B,EAAE,CAAA,EAAGC,uBAAuB,CAAA,EAAA,CAAI;MAC3D,0BAA0B,EAAE,GAAGC,sBAAsB,CAAA,EAAA;KAExD;IAAAN,QAAA,eAEDJ,GAAA,CAACW,aAAa,EAAA;MACZC,MAAM,EAAA,IAAA;AACNC,MAAAA,OAAO,EAAErC,UAAW;AACpBsC,MAAAA,EAAE,EAAE/C,OAAQ;AACZgD,MAAAA,UAAU,EAAC,wBAAwB;AACnCjD,MAAAA,OAAO,EAAE;AACP8C,QAAAA,MAAM,EAAEH,uBAAuB;AAC/BO,QAAAA,KAAK,EAAEP,uBAAuB;AAC9BQ,QAAAA,IAAI,EAAEP;OACN;MACFQ,aAAa,EAAA,IAAA;AACbC,MAAAA,SAAS,EAAEhD,aAAc;AACzBiD,MAAAA,MAAM,EAAE1C,WAAY;AACpBf,MAAAA,QAAQ,EAAEA,QAAS;MAAAyC,QAAA,eAEnBJ,GAAA,CAACqB,eAAe,EAAA;AACdd,QAAAA,GAAG,EAAE/B,UAAW;AAChB0B,QAAAA,SAAS,EAAC,wBAAwB;AAClCtC,QAAAA,SAAS,EAAEJ,OAAO,GAAG,SAAS,GAAGI,SAAU;AAC3C0D,QAAAA,QAAQ,EAAC,UAAU;QACnB7D,KAAK,EAAEsC,WAAW,EAAG;AACrBwB,QAAAA,qBAAqB,EAAC,SAAS;AAC/BC,QAAAA,SAAS,EAAE9D,gBAAiB;AAC5B+D,QAAAA,YAAY,EAAEpD,gBAAiB;AAC/BqD,QAAAA,YAAY,EAAEpD,gBAAiB;AAC/BqD,QAAAA,OAAO,EAAEvD,WAAY;AACrBwD,QAAAA,MAAM,EAAE1D,UAAW;AACnB2D,QAAAA,cAAc,EAAEjD,kBAAmB;AACnCkD,QAAAA,WAAW,EAAGxC,KAAK,IAAKA,KAAK,CAACyC,cAAc,EAAG;AAC/CC,QAAAA,aAAa,EAAEjE,OAAO,GAAGe,iBAAiB,GAAGM,SAAU;AACvD6C,QAAAA,aAAa,EAAElD,iBAAkB;AACjCmD,QAAAA,WAAW,EAAElD,eAAgB;AAC7BmD,QAAAA,eAAe,EAAEtD,mBAAoB;AAAAuB,QAAAA,QAAA,eAErCgC,IAAA,CAAA,KAAA,EAAA;AAAKlC,UAAAA,SAAS,EAAC,uBAAuB;UAAAE,QAAA,EAAA,cACpCJ,GAAA,CAACqC,UAAU,EAAA;AACT,YAAA,WAAA,EAAU,QAAQ;AAClBC,YAAAA,gBAAgB,EAAEhF,eAAgB;AAClC4C,YAAAA,SAAS,EAAC,uBAAuB;YAAAE,QAAA,eAEjCgC,IAAA,CAACG,IAAI,EAAA;AAACC,cAAAA,EAAE,EAAC,MAAM;AAACtC,cAAAA,SAAS,EAAC,oBAAoB;cAAAE,QAAA,EAAA,CAC3CvC,IAAI,eACLuE,IAAA,CAAA,MAAA,EAAA;AAAMlC,gBAAAA,SAAS,EAAC,SAAS;gBAAAE,QAAA,EAAA,CAAE,GAAA,EAACpC,aAAa,CAACyE,QAAQ,CAACC,iBAAiB,CAAC;AAAA,eAAO,CAC9E;aAAM;AACR,WAAY,CACZ,EAACrF,MAAM,gBACL2C,GAAA,CAAC2C,MAAM,EAAA;AACLzC,YAAAA,SAAS,EAAC,sBAAsB;YAChC0C,EAAE,EAAA,IAAA;AACFzC,YAAAA,IAAI,EAAC,IAAI;AACT0C,YAAAA,QAAQ,EAAC,WAAW;YACpBC,IAAI,EAAEzF,MAAM,CAACyF,IAAK;YAClBC,MAAM,EAAE1F,MAAM,CAAC0F,MAAO;AACtBjD,YAAAA,OAAO,EAAED,iBAAkB;YAAAO,QAAA,EAE1B/C,MAAM,CAAC2F;WACF,CAAC,GACP,IAAI;SACL;OACU;KACJ;AACjB,GAAK,CAAC;AAEV;;"}
|
|
@@ -8,39 +8,34 @@ var constants = require('./constants.js');
|
|
|
8
8
|
const DISMISS_CLASS = 'wds-Snackbar--swipe-dismiss';
|
|
9
9
|
const SETTLE_CLASS = 'wds-Snackbar--swipe-settle';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* Release dismisses at half the surface height; shorter
|
|
13
|
-
*
|
|
14
|
-
* follow-up click so dragging from the action cannot activate it.
|
|
11
|
+
* Follows one primary pointer's downward, mostly vertical movement without re-rendering.
|
|
12
|
+
* Release dismisses at half the surface height; shorter or cancelled gestures settle back.
|
|
13
|
+
* A drag consumes its follow-up click, so dragging from the action cannot activate it.
|
|
15
14
|
*/
|
|
16
15
|
const useSnackbarSwipeDismiss = ({
|
|
17
16
|
onDismissRequest,
|
|
18
|
-
onSwipeChange
|
|
19
|
-
visible
|
|
17
|
+
onSwipeChange
|
|
20
18
|
}) => {
|
|
21
19
|
const containerRef = React.useRef(null);
|
|
22
20
|
const pointerRef = React.useRef(null);
|
|
23
21
|
const suppressClick = React.useRef(false);
|
|
24
|
-
const
|
|
25
|
-
const pointer = pointerRef.current;
|
|
22
|
+
const endGesture = () => {
|
|
26
23
|
pointerRef.current = null;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
onSwipeChange(false);
|
|
32
|
-
}
|
|
33
|
-
suppressClick.current = false;
|
|
34
|
-
containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);
|
|
24
|
+
onSwipeChange(false);
|
|
25
|
+
};
|
|
26
|
+
const settle = () => {
|
|
27
|
+
containerRef.current?.classList.add(SETTLE_CLASS);
|
|
35
28
|
containerRef.current?.style.removeProperty('transform');
|
|
36
|
-
}
|
|
29
|
+
};
|
|
37
30
|
const handlePointerDown = event => {
|
|
38
31
|
if (event.button !== 0 || !event.isPrimary || pointerRef.current) {
|
|
39
32
|
return;
|
|
40
33
|
}
|
|
41
|
-
clearSwipePosition();
|
|
42
34
|
const target = event.target;
|
|
35
|
+
// Keeps move/up events coming when a mouse leaves the Snackbar mid-drag.
|
|
43
36
|
target.setPointerCapture?.(event.pointerId);
|
|
37
|
+
containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);
|
|
38
|
+
suppressClick.current = false;
|
|
44
39
|
pointerRef.current = {
|
|
45
40
|
id: event.pointerId,
|
|
46
41
|
target,
|
|
@@ -68,51 +63,43 @@ const useSnackbarSwipeDismiss = ({
|
|
|
68
63
|
if (pointer?.id !== event.pointerId) {
|
|
69
64
|
return;
|
|
70
65
|
}
|
|
66
|
+
endGesture();
|
|
71
67
|
const offsetY = event.clientY - pointer.startY;
|
|
72
68
|
const offsetX = event.clientX - pointer.startX;
|
|
73
69
|
const container = containerRef.current;
|
|
74
|
-
const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();
|
|
75
|
-
clearSwipePosition();
|
|
76
70
|
if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {
|
|
71
|
+
suppressClick.current = true;
|
|
77
72
|
if (container) {
|
|
78
73
|
// Include wrapper padding and the safe area, not only the surface, to clear the viewport.
|
|
79
74
|
container.classList.add(DISMISS_CLASS);
|
|
80
75
|
container.style.setProperty('transform', `translateY(${Math.max(offsetY, container.offsetHeight)}px)`);
|
|
81
76
|
}
|
|
82
|
-
suppressClick.current = true;
|
|
83
77
|
onDismissRequest();
|
|
84
78
|
} else {
|
|
85
|
-
// Capture retargets
|
|
79
|
+
// Capture retargets the click to the pressed control even when released outside it,
|
|
80
|
+
// unlike a native button; cancel it in that case.
|
|
81
|
+
const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();
|
|
86
82
|
suppressClick.current = !!bounds && (offsetX !== 0 || offsetY !== 0) && (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom);
|
|
87
|
-
|
|
83
|
+
settle();
|
|
88
84
|
}
|
|
89
85
|
};
|
|
90
|
-
const handlePointerCancel =
|
|
86
|
+
const handlePointerCancel = event => {
|
|
91
87
|
if (pointerRef.current?.id !== event.pointerId) {
|
|
92
88
|
return;
|
|
93
89
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
handlePointerCancel({
|
|
103
|
-
pointerId: pointerRef.current.id
|
|
104
|
-
});
|
|
90
|
+
endGesture();
|
|
91
|
+
settle();
|
|
92
|
+
};
|
|
93
|
+
/** Ends a gesture still in progress when the Snackbar starts exiting for another reason. */
|
|
94
|
+
const cancelSwipe = () => {
|
|
95
|
+
const pointer = pointerRef.current;
|
|
96
|
+
if (!pointer) {
|
|
97
|
+
return;
|
|
105
98
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
ownerDocument?.addEventListener('lostpointercapture', handlePointerCancel);
|
|
111
|
-
return () => {
|
|
112
|
-
ownerDocument?.removeEventListener('lostpointercapture', handlePointerCancel);
|
|
113
|
-
clearSwipePosition();
|
|
114
|
-
};
|
|
115
|
-
}, [clearSwipePosition, handlePointerCancel]);
|
|
99
|
+
pointer.target.releasePointerCapture?.(pointer.id);
|
|
100
|
+
endGesture();
|
|
101
|
+
settle();
|
|
102
|
+
};
|
|
116
103
|
const handleClickCapture = event => {
|
|
117
104
|
if (suppressClick.current && event.detail > 0) {
|
|
118
105
|
suppressClick.current = false;
|
|
@@ -121,7 +108,7 @@ const useSnackbarSwipeDismiss = ({
|
|
|
121
108
|
}
|
|
122
109
|
};
|
|
123
110
|
return {
|
|
124
|
-
|
|
111
|
+
cancelSwipe,
|
|
125
112
|
containerRef,
|
|
126
113
|
handleClickCapture,
|
|
127
114
|
handlePointerCancel,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSnackbarSwipeDismiss.js","sources":["../../../src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nimport { SNACKBAR_SWIPE_DISMISS_THRESHOLD } from './constants';\n\nconst DISMISS_CLASS = 'wds-Snackbar--swipe-dismiss';\nconst SETTLE_CLASS = 'wds-Snackbar--swipe-settle';\n\ntype SwipePointer = {\n id: number;\n target: Element;\n startX: number;\n startY: number;\n threshold: number;\n};\n\n/**\n * Captures one primary pointer and follows downward, mostly vertical movement without re-rendering.\n * Release dismisses at half the surface height; shorter/cancelled gestures settle back into place.\n * The outer wrapper moves independently of entry/exit motion, and a committed swipe consumes its\n * follow-up click so dragging from the action cannot activate it.\n */\nconst useSnackbarSwipeDismiss = ({\n onDismissRequest,\n onSwipeChange,\n visible,\n}: {\n onDismissRequest: () => void;\n /** Reports when a gesture starts and ends so auto-dismiss can pause beneath the pointer. */\n onSwipeChange: (swiping: boolean) => void;\n visible: boolean;\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const pointerRef = useRef<SwipePointer | null>(null);\n const suppressClick = useRef(false);\n\n const clearSwipePosition = useCallback(() => {\n const pointer = pointerRef.current;\n pointerRef.current = null;\n if (pointer) {\n if (pointer.target.hasPointerCapture?.(pointer.id)) {\n pointer.target.releasePointerCapture(pointer.id);\n }\n onSwipeChange(false);\n }\n suppressClick.current = false;\n containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);\n containerRef.current?.style.removeProperty('transform');\n }, [onSwipeChange]);\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0 || !event.isPrimary || pointerRef.current) {\n return;\n }\n\n clearSwipePosition();\n const target = event.target as Element;\n target.setPointerCapture?.(event.pointerId);\n pointerRef.current = {\n id: event.pointerId,\n target,\n startX: event.clientX,\n startY: event.clientY,\n threshold: event.currentTarget.offsetHeight * SNACKBAR_SWIPE_DISMISS_THRESHOLD,\n };\n onSwipeChange(true);\n };\n\n const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n if (offsetY > 0 && offsetY > Math.abs(offsetX)) {\n containerRef.current?.style.setProperty('transform', `translateY(${offsetY}px)`);\n } else {\n containerRef.current?.style.removeProperty('transform');\n }\n };\n\n const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n const container = containerRef.current;\n const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();\n clearSwipePosition();\n\n if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {\n if (container) {\n // Include wrapper padding and the safe area, not only the surface, to clear the viewport.\n container.classList.add(DISMISS_CLASS);\n container.style.setProperty(\n 'transform',\n `translateY(${Math.max(offsetY, container.offsetHeight)}px)`,\n );\n }\n suppressClick.current = true;\n onDismissRequest();\n } else {\n // Capture retargets clicks to the pressed control even when released outside it.\n suppressClick.current =\n !!bounds &&\n (offsetX !== 0 || offsetY !== 0) &&\n (event.clientX < bounds.left ||\n event.clientX > bounds.right ||\n event.clientY < bounds.top ||\n event.clientY > bounds.bottom);\n container?.classList.add(SETTLE_CLASS);\n }\n };\n\n const handlePointerCancel = useCallback(\n (event: Pick<PointerEvent, 'pointerId'>) => {\n if (pointerRef.current?.id !== event.pointerId) {\n return;\n }\n\n clearSwipePosition();\n containerRef.current?.classList.add(SETTLE_CLASS);\n },\n [clearSwipePosition],\n );\n\n useEffect(() => {\n if (visible) {\n clearSwipePosition();\n } else if (pointerRef.current) {\n // An exit starting mid-gesture (timeout, Escape, replacement) ends it without dismissing again.\n handlePointerCancel({ pointerId: pointerRef.current.id });\n }\n }, [visible, clearSwipePosition, handlePointerCancel]);\n\n useEffect(() => {\n // Removed descendants report capture loss on their document, not the Snackbar surface.\n const ownerDocument = containerRef.current?.ownerDocument;\n ownerDocument?.addEventListener('lostpointercapture', handlePointerCancel);\n return () => {\n ownerDocument?.removeEventListener('lostpointercapture', handlePointerCancel);\n clearSwipePosition();\n };\n }, [clearSwipePosition, handlePointerCancel]);\n\n const handleClickCapture = (event: React.MouseEvent<HTMLDivElement>) => {\n if (suppressClick.current && event.detail > 0) {\n suppressClick.current = false;\n event.preventDefault();\n event.stopPropagation();\n }\n };\n\n return {\n clearSwipePosition,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n };\n};\n\nexport default useSnackbarSwipeDismiss;\n"],"names":["DISMISS_CLASS","SETTLE_CLASS","useSnackbarSwipeDismiss","onDismissRequest","onSwipeChange","visible","containerRef","useRef","pointerRef","suppressClick","clearSwipePosition","useCallback","pointer","current","target","hasPointerCapture","id","releasePointerCapture","classList","remove","style","removeProperty","handlePointerDown","event","button","isPrimary","setPointerCapture","pointerId","startX","clientX","startY","clientY","threshold","currentTarget","offsetHeight","SNACKBAR_SWIPE_DISMISS_THRESHOLD","handlePointerMove","offsetY","offsetX","Math","abs","setProperty","handlePointerUp","container","bounds","closest","getBoundingClientRect","add","max","left","right","top","bottom","handlePointerCancel","useEffect","ownerDocument","addEventListener","removeEventListener","handleClickCapture","detail","preventDefault","stopPropagation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSnackbarSwipeDismiss.js","sources":["../../../src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts"],"sourcesContent":["import { useRef } from 'react';\n\nimport { SNACKBAR_SWIPE_DISMISS_THRESHOLD } from './constants';\n\nconst DISMISS_CLASS = 'wds-Snackbar--swipe-dismiss';\nconst SETTLE_CLASS = 'wds-Snackbar--swipe-settle';\n\ntype SwipePointer = {\n id: number;\n target: Element;\n startX: number;\n startY: number;\n threshold: number;\n};\n\n/**\n * Follows one primary pointer's downward, mostly vertical movement without re-rendering.\n * Release dismisses at half the surface height; shorter or cancelled gestures settle back.\n * A drag consumes its follow-up click, so dragging from the action cannot activate it.\n */\nconst useSnackbarSwipeDismiss = ({\n onDismissRequest,\n onSwipeChange,\n}: {\n onDismissRequest: () => void;\n /** Reports when a gesture starts and ends so auto-dismiss can pause beneath the pointer. */\n onSwipeChange: (swiping: boolean) => void;\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const pointerRef = useRef<SwipePointer | null>(null);\n const suppressClick = useRef(false);\n\n const endGesture = () => {\n pointerRef.current = null;\n onSwipeChange(false);\n };\n\n const settle = () => {\n containerRef.current?.classList.add(SETTLE_CLASS);\n containerRef.current?.style.removeProperty('transform');\n };\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0 || !event.isPrimary || pointerRef.current) {\n return;\n }\n\n const target = event.target as Element;\n // Keeps move/up events coming when a mouse leaves the Snackbar mid-drag.\n target.setPointerCapture?.(event.pointerId);\n containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);\n suppressClick.current = false;\n pointerRef.current = {\n id: event.pointerId,\n target,\n startX: event.clientX,\n startY: event.clientY,\n threshold: event.currentTarget.offsetHeight * SNACKBAR_SWIPE_DISMISS_THRESHOLD,\n };\n onSwipeChange(true);\n };\n\n const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n if (offsetY > 0 && offsetY > Math.abs(offsetX)) {\n containerRef.current?.style.setProperty('transform', `translateY(${offsetY}px)`);\n } else {\n containerRef.current?.style.removeProperty('transform');\n }\n };\n\n const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n endGesture();\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n const container = containerRef.current;\n if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {\n suppressClick.current = true;\n if (container) {\n // Include wrapper padding and the safe area, not only the surface, to clear the viewport.\n container.classList.add(DISMISS_CLASS);\n container.style.setProperty(\n 'transform',\n `translateY(${Math.max(offsetY, container.offsetHeight)}px)`,\n );\n }\n onDismissRequest();\n } else {\n // Capture retargets the click to the pressed control even when released outside it,\n // unlike a native button; cancel it in that case.\n const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();\n suppressClick.current =\n !!bounds &&\n (offsetX !== 0 || offsetY !== 0) &&\n (event.clientX < bounds.left ||\n event.clientX > bounds.right ||\n event.clientY < bounds.top ||\n event.clientY > bounds.bottom);\n settle();\n }\n };\n\n const handlePointerCancel = (event: React.PointerEvent<HTMLDivElement>) => {\n if (pointerRef.current?.id !== event.pointerId) {\n return;\n }\n\n endGesture();\n settle();\n };\n\n /** Ends a gesture still in progress when the Snackbar starts exiting for another reason. */\n const cancelSwipe = () => {\n const pointer = pointerRef.current;\n if (!pointer) {\n return;\n }\n\n pointer.target.releasePointerCapture?.(pointer.id);\n endGesture();\n settle();\n };\n\n const handleClickCapture = (event: React.MouseEvent<HTMLDivElement>) => {\n if (suppressClick.current && event.detail > 0) {\n suppressClick.current = false;\n event.preventDefault();\n event.stopPropagation();\n }\n };\n\n return {\n cancelSwipe,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n };\n};\n\nexport default useSnackbarSwipeDismiss;\n"],"names":["DISMISS_CLASS","SETTLE_CLASS","useSnackbarSwipeDismiss","onDismissRequest","onSwipeChange","containerRef","useRef","pointerRef","suppressClick","endGesture","current","settle","classList","add","style","removeProperty","handlePointerDown","event","button","isPrimary","target","setPointerCapture","pointerId","remove","id","startX","clientX","startY","clientY","threshold","currentTarget","offsetHeight","SNACKBAR_SWIPE_DISMISS_THRESHOLD","handlePointerMove","pointer","offsetY","offsetX","Math","abs","setProperty","handlePointerUp","container","max","bounds","closest","getBoundingClientRect","left","right","top","bottom","handlePointerCancel","cancelSwipe","releasePointerCapture","handleClickCapture","detail","preventDefault","stopPropagation"],"mappings":";;;;;;;AAIA,MAAMA,aAAa,GAAG,6BAA6B;AACnD,MAAMC,YAAY,GAAG,4BAA4B;AAUjD;;;;AAIG;AACH,MAAMC,uBAAuB,GAAGA,CAAC;EAC/BC,gBAAgB;AAChBC,EAAAA;AAAa,CAKd,KAAI;AACH,EAAA,MAAMC,YAAY,GAAGC,YAAM,CAAiB,IAAI,CAAC;AACjD,EAAA,MAAMC,UAAU,GAAGD,YAAM,CAAsB,IAAI,CAAC;AACpD,EAAA,MAAME,aAAa,GAAGF,YAAM,CAAC,KAAK,CAAC;EAEnC,MAAMG,UAAU,GAAGA,MAAK;IACtBF,UAAU,CAACG,OAAO,GAAG,IAAI;IACzBN,aAAa,CAAC,KAAK,CAAC;EACtB,CAAC;EAED,MAAMO,MAAM,GAAGA,MAAK;IAClBN,YAAY,CAACK,OAAO,EAAEE,SAAS,CAACC,GAAG,CAACZ,YAAY,CAAC;IACjDI,YAAY,CAACK,OAAO,EAAEI,KAAK,CAACC,cAAc,CAAC,WAAW,CAAC;EACzD,CAAC;EAED,MAAMC,iBAAiB,GAAIC,KAAyC,IAAI;AACtE,IAAA,IAAIA,KAAK,CAACC,MAAM,KAAK,CAAC,IAAI,CAACD,KAAK,CAACE,SAAS,IAAIZ,UAAU,CAACG,OAAO,EAAE;AAChE,MAAA;AACF,IAAA;AAEA,IAAA,MAAMU,MAAM,GAAGH,KAAK,CAACG,MAAiB;AACtC;AACAA,IAAAA,MAAM,CAACC,iBAAiB,GAAGJ,KAAK,CAACK,SAAS,CAAC;IAC3CjB,YAAY,CAACK,OAAO,EAAEE,SAAS,CAACW,MAAM,CAACvB,aAAa,EAAEC,YAAY,CAAC;IACnEO,aAAa,CAACE,OAAO,GAAG,KAAK;IAC7BH,UAAU,CAACG,OAAO,GAAG;MACnBc,EAAE,EAAEP,KAAK,CAACK,SAAS;MACnBF,MAAM;MACNK,MAAM,EAAER,KAAK,CAACS,OAAO;MACrBC,MAAM,EAAEV,KAAK,CAACW,OAAO;AACrBC,MAAAA,SAAS,EAAEZ,KAAK,CAACa,aAAa,CAACC,YAAY,GAAGC;KAC/C;IACD5B,aAAa,CAAC,IAAI,CAAC;EACrB,CAAC;EAED,MAAM6B,iBAAiB,GAAIhB,KAAyC,IAAI;AACtE,IAAA,MAAMiB,OAAO,GAAG3B,UAAU,CAACG,OAAO;AAClC,IAAA,IAAIwB,OAAO,EAAEV,EAAE,KAAKP,KAAK,CAACK,SAAS,EAAE;AACnC,MAAA;AACF,IAAA;IAEA,MAAMa,OAAO,GAAGlB,KAAK,CAACW,OAAO,GAAGM,OAAO,CAACP,MAAM;IAC9C,MAAMS,OAAO,GAAGnB,KAAK,CAACS,OAAO,GAAGQ,OAAO,CAACT,MAAM;AAC9C,IAAA,IAAIU,OAAO,GAAG,CAAC,IAAIA,OAAO,GAAGE,IAAI,CAACC,GAAG,CAACF,OAAO,CAAC,EAAE;AAC9C/B,MAAAA,YAAY,CAACK,OAAO,EAAEI,KAAK,CAACyB,WAAW,CAAC,WAAW,EAAE,CAAA,WAAA,EAAcJ,OAAO,CAAA,GAAA,CAAK,CAAC;AAClF,IAAA,CAAC,MAAM;MACL9B,YAAY,CAACK,OAAO,EAAEI,KAAK,CAACC,cAAc,CAAC,WAAW,CAAC;AACzD,IAAA;EACF,CAAC;EAED,MAAMyB,eAAe,GAAIvB,KAAyC,IAAI;AACpE,IAAA,MAAMiB,OAAO,GAAG3B,UAAU,CAACG,OAAO;AAClC,IAAA,IAAIwB,OAAO,EAAEV,EAAE,KAAKP,KAAK,CAACK,SAAS,EAAE;AACnC,MAAA;AACF,IAAA;AAEAb,IAAAA,UAAU,EAAE;IACZ,MAAM0B,OAAO,GAAGlB,KAAK,CAACW,OAAO,GAAGM,OAAO,CAACP,MAAM;IAC9C,MAAMS,OAAO,GAAGnB,KAAK,CAACS,OAAO,GAAGQ,OAAO,CAACT,MAAM;AAC9C,IAAA,MAAMgB,SAAS,GAAGpC,YAAY,CAACK,OAAO;AACtC,IAAA,IAAIyB,OAAO,IAAID,OAAO,CAACL,SAAS,IAAIM,OAAO,GAAGE,IAAI,CAACC,GAAG,CAACF,OAAO,CAAC,EAAE;MAC/D5B,aAAa,CAACE,OAAO,GAAG,IAAI;AAC5B,MAAA,IAAI+B,SAAS,EAAE;AACb;AACAA,QAAAA,SAAS,CAAC7B,SAAS,CAACC,GAAG,CAACb,aAAa,CAAC;AACtCyC,QAAAA,SAAS,CAAC3B,KAAK,CAACyB,WAAW,CACzB,WAAW,EACX,CAAA,WAAA,EAAcF,IAAI,CAACK,GAAG,CAACP,OAAO,EAAEM,SAAS,CAACV,YAAY,CAAC,KAAK,CAC7D;AACH,MAAA;AACA5B,MAAAA,gBAAgB,EAAE;AACpB,IAAA,CAAC,MAAM;AACL;AACA;AACA,MAAA,MAAMwC,MAAM,GAAGT,OAAO,CAACd,MAAM,CAACwB,OAAO,CAAC,WAAW,CAAC,EAAEC,qBAAqB,EAAE;MAC3ErC,aAAa,CAACE,OAAO,GACnB,CAAC,CAACiC,MAAM,KACPP,OAAO,KAAK,CAAC,IAAID,OAAO,KAAK,CAAC,CAAC,KAC/BlB,KAAK,CAACS,OAAO,GAAGiB,MAAM,CAACG,IAAI,IAC1B7B,KAAK,CAACS,OAAO,GAAGiB,MAAM,CAACI,KAAK,IAC5B9B,KAAK,CAACW,OAAO,GAAGe,MAAM,CAACK,GAAG,IAC1B/B,KAAK,CAACW,OAAO,GAAGe,MAAM,CAACM,MAAM,CAAC;AAClCtC,MAAAA,MAAM,EAAE;AACV,IAAA;EACF,CAAC;EAED,MAAMuC,mBAAmB,GAAIjC,KAAyC,IAAI;IACxE,IAAIV,UAAU,CAACG,OAAO,EAAEc,EAAE,KAAKP,KAAK,CAACK,SAAS,EAAE;AAC9C,MAAA;AACF,IAAA;AAEAb,IAAAA,UAAU,EAAE;AACZE,IAAAA,MAAM,EAAE;EACV,CAAC;AAED;EACA,MAAMwC,WAAW,GAAGA,MAAK;AACvB,IAAA,MAAMjB,OAAO,GAAG3B,UAAU,CAACG,OAAO;IAClC,IAAI,CAACwB,OAAO,EAAE;AACZ,MAAA;AACF,IAAA;IAEAA,OAAO,CAACd,MAAM,CAACgC,qBAAqB,GAAGlB,OAAO,CAACV,EAAE,CAAC;AAClDf,IAAAA,UAAU,EAAE;AACZE,IAAAA,MAAM,EAAE;EACV,CAAC;EAED,MAAM0C,kBAAkB,GAAIpC,KAAuC,IAAI;IACrE,IAAIT,aAAa,CAACE,OAAO,IAAIO,KAAK,CAACqC,MAAM,GAAG,CAAC,EAAE;MAC7C9C,aAAa,CAACE,OAAO,GAAG,KAAK;MAC7BO,KAAK,CAACsC,cAAc,EAAE;MACtBtC,KAAK,CAACuC,eAAe,EAAE;AACzB,IAAA;EACF,CAAC;EAED,OAAO;IACLL,WAAW;IACX9C,YAAY;IACZgD,kBAAkB;IAClBH,mBAAmB;IACnBlC,iBAAiB;IACjBiB,iBAAiB;AACjBO,IAAAA;GACD;AACH;;"}
|
|
@@ -1,42 +1,37 @@
|
|
|
1
|
-
import { useRef
|
|
1
|
+
import { useRef } from 'react';
|
|
2
2
|
import { SNACKBAR_SWIPE_DISMISS_THRESHOLD } from './constants.mjs';
|
|
3
3
|
|
|
4
4
|
const DISMISS_CLASS = 'wds-Snackbar--swipe-dismiss';
|
|
5
5
|
const SETTLE_CLASS = 'wds-Snackbar--swipe-settle';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* Release dismisses at half the surface height; shorter
|
|
9
|
-
*
|
|
10
|
-
* follow-up click so dragging from the action cannot activate it.
|
|
7
|
+
* Follows one primary pointer's downward, mostly vertical movement without re-rendering.
|
|
8
|
+
* Release dismisses at half the surface height; shorter or cancelled gestures settle back.
|
|
9
|
+
* A drag consumes its follow-up click, so dragging from the action cannot activate it.
|
|
11
10
|
*/
|
|
12
11
|
const useSnackbarSwipeDismiss = ({
|
|
13
12
|
onDismissRequest,
|
|
14
|
-
onSwipeChange
|
|
15
|
-
visible
|
|
13
|
+
onSwipeChange
|
|
16
14
|
}) => {
|
|
17
15
|
const containerRef = useRef(null);
|
|
18
16
|
const pointerRef = useRef(null);
|
|
19
17
|
const suppressClick = useRef(false);
|
|
20
|
-
const
|
|
21
|
-
const pointer = pointerRef.current;
|
|
18
|
+
const endGesture = () => {
|
|
22
19
|
pointerRef.current = null;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
onSwipeChange(false);
|
|
28
|
-
}
|
|
29
|
-
suppressClick.current = false;
|
|
30
|
-
containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);
|
|
20
|
+
onSwipeChange(false);
|
|
21
|
+
};
|
|
22
|
+
const settle = () => {
|
|
23
|
+
containerRef.current?.classList.add(SETTLE_CLASS);
|
|
31
24
|
containerRef.current?.style.removeProperty('transform');
|
|
32
|
-
}
|
|
25
|
+
};
|
|
33
26
|
const handlePointerDown = event => {
|
|
34
27
|
if (event.button !== 0 || !event.isPrimary || pointerRef.current) {
|
|
35
28
|
return;
|
|
36
29
|
}
|
|
37
|
-
clearSwipePosition();
|
|
38
30
|
const target = event.target;
|
|
31
|
+
// Keeps move/up events coming when a mouse leaves the Snackbar mid-drag.
|
|
39
32
|
target.setPointerCapture?.(event.pointerId);
|
|
33
|
+
containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);
|
|
34
|
+
suppressClick.current = false;
|
|
40
35
|
pointerRef.current = {
|
|
41
36
|
id: event.pointerId,
|
|
42
37
|
target,
|
|
@@ -64,51 +59,43 @@ const useSnackbarSwipeDismiss = ({
|
|
|
64
59
|
if (pointer?.id !== event.pointerId) {
|
|
65
60
|
return;
|
|
66
61
|
}
|
|
62
|
+
endGesture();
|
|
67
63
|
const offsetY = event.clientY - pointer.startY;
|
|
68
64
|
const offsetX = event.clientX - pointer.startX;
|
|
69
65
|
const container = containerRef.current;
|
|
70
|
-
const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();
|
|
71
|
-
clearSwipePosition();
|
|
72
66
|
if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {
|
|
67
|
+
suppressClick.current = true;
|
|
73
68
|
if (container) {
|
|
74
69
|
// Include wrapper padding and the safe area, not only the surface, to clear the viewport.
|
|
75
70
|
container.classList.add(DISMISS_CLASS);
|
|
76
71
|
container.style.setProperty('transform', `translateY(${Math.max(offsetY, container.offsetHeight)}px)`);
|
|
77
72
|
}
|
|
78
|
-
suppressClick.current = true;
|
|
79
73
|
onDismissRequest();
|
|
80
74
|
} else {
|
|
81
|
-
// Capture retargets
|
|
75
|
+
// Capture retargets the click to the pressed control even when released outside it,
|
|
76
|
+
// unlike a native button; cancel it in that case.
|
|
77
|
+
const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();
|
|
82
78
|
suppressClick.current = !!bounds && (offsetX !== 0 || offsetY !== 0) && (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom);
|
|
83
|
-
|
|
79
|
+
settle();
|
|
84
80
|
}
|
|
85
81
|
};
|
|
86
|
-
const handlePointerCancel =
|
|
82
|
+
const handlePointerCancel = event => {
|
|
87
83
|
if (pointerRef.current?.id !== event.pointerId) {
|
|
88
84
|
return;
|
|
89
85
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
handlePointerCancel({
|
|
99
|
-
pointerId: pointerRef.current.id
|
|
100
|
-
});
|
|
86
|
+
endGesture();
|
|
87
|
+
settle();
|
|
88
|
+
};
|
|
89
|
+
/** Ends a gesture still in progress when the Snackbar starts exiting for another reason. */
|
|
90
|
+
const cancelSwipe = () => {
|
|
91
|
+
const pointer = pointerRef.current;
|
|
92
|
+
if (!pointer) {
|
|
93
|
+
return;
|
|
101
94
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
ownerDocument?.addEventListener('lostpointercapture', handlePointerCancel);
|
|
107
|
-
return () => {
|
|
108
|
-
ownerDocument?.removeEventListener('lostpointercapture', handlePointerCancel);
|
|
109
|
-
clearSwipePosition();
|
|
110
|
-
};
|
|
111
|
-
}, [clearSwipePosition, handlePointerCancel]);
|
|
95
|
+
pointer.target.releasePointerCapture?.(pointer.id);
|
|
96
|
+
endGesture();
|
|
97
|
+
settle();
|
|
98
|
+
};
|
|
112
99
|
const handleClickCapture = event => {
|
|
113
100
|
if (suppressClick.current && event.detail > 0) {
|
|
114
101
|
suppressClick.current = false;
|
|
@@ -117,7 +104,7 @@ const useSnackbarSwipeDismiss = ({
|
|
|
117
104
|
}
|
|
118
105
|
};
|
|
119
106
|
return {
|
|
120
|
-
|
|
107
|
+
cancelSwipe,
|
|
121
108
|
containerRef,
|
|
122
109
|
handleClickCapture,
|
|
123
110
|
handlePointerCancel,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSnackbarSwipeDismiss.mjs","sources":["../../../src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nimport { SNACKBAR_SWIPE_DISMISS_THRESHOLD } from './constants';\n\nconst DISMISS_CLASS = 'wds-Snackbar--swipe-dismiss';\nconst SETTLE_CLASS = 'wds-Snackbar--swipe-settle';\n\ntype SwipePointer = {\n id: number;\n target: Element;\n startX: number;\n startY: number;\n threshold: number;\n};\n\n/**\n * Captures one primary pointer and follows downward, mostly vertical movement without re-rendering.\n * Release dismisses at half the surface height; shorter/cancelled gestures settle back into place.\n * The outer wrapper moves independently of entry/exit motion, and a committed swipe consumes its\n * follow-up click so dragging from the action cannot activate it.\n */\nconst useSnackbarSwipeDismiss = ({\n onDismissRequest,\n onSwipeChange,\n visible,\n}: {\n onDismissRequest: () => void;\n /** Reports when a gesture starts and ends so auto-dismiss can pause beneath the pointer. */\n onSwipeChange: (swiping: boolean) => void;\n visible: boolean;\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const pointerRef = useRef<SwipePointer | null>(null);\n const suppressClick = useRef(false);\n\n const clearSwipePosition = useCallback(() => {\n const pointer = pointerRef.current;\n pointerRef.current = null;\n if (pointer) {\n if (pointer.target.hasPointerCapture?.(pointer.id)) {\n pointer.target.releasePointerCapture(pointer.id);\n }\n onSwipeChange(false);\n }\n suppressClick.current = false;\n containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);\n containerRef.current?.style.removeProperty('transform');\n }, [onSwipeChange]);\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0 || !event.isPrimary || pointerRef.current) {\n return;\n }\n\n clearSwipePosition();\n const target = event.target as Element;\n target.setPointerCapture?.(event.pointerId);\n pointerRef.current = {\n id: event.pointerId,\n target,\n startX: event.clientX,\n startY: event.clientY,\n threshold: event.currentTarget.offsetHeight * SNACKBAR_SWIPE_DISMISS_THRESHOLD,\n };\n onSwipeChange(true);\n };\n\n const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n if (offsetY > 0 && offsetY > Math.abs(offsetX)) {\n containerRef.current?.style.setProperty('transform', `translateY(${offsetY}px)`);\n } else {\n containerRef.current?.style.removeProperty('transform');\n }\n };\n\n const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n const container = containerRef.current;\n const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();\n clearSwipePosition();\n\n if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {\n if (container) {\n // Include wrapper padding and the safe area, not only the surface, to clear the viewport.\n container.classList.add(DISMISS_CLASS);\n container.style.setProperty(\n 'transform',\n `translateY(${Math.max(offsetY, container.offsetHeight)}px)`,\n );\n }\n suppressClick.current = true;\n onDismissRequest();\n } else {\n // Capture retargets clicks to the pressed control even when released outside it.\n suppressClick.current =\n !!bounds &&\n (offsetX !== 0 || offsetY !== 0) &&\n (event.clientX < bounds.left ||\n event.clientX > bounds.right ||\n event.clientY < bounds.top ||\n event.clientY > bounds.bottom);\n container?.classList.add(SETTLE_CLASS);\n }\n };\n\n const handlePointerCancel = useCallback(\n (event: Pick<PointerEvent, 'pointerId'>) => {\n if (pointerRef.current?.id !== event.pointerId) {\n return;\n }\n\n clearSwipePosition();\n containerRef.current?.classList.add(SETTLE_CLASS);\n },\n [clearSwipePosition],\n );\n\n useEffect(() => {\n if (visible) {\n clearSwipePosition();\n } else if (pointerRef.current) {\n // An exit starting mid-gesture (timeout, Escape, replacement) ends it without dismissing again.\n handlePointerCancel({ pointerId: pointerRef.current.id });\n }\n }, [visible, clearSwipePosition, handlePointerCancel]);\n\n useEffect(() => {\n // Removed descendants report capture loss on their document, not the Snackbar surface.\n const ownerDocument = containerRef.current?.ownerDocument;\n ownerDocument?.addEventListener('lostpointercapture', handlePointerCancel);\n return () => {\n ownerDocument?.removeEventListener('lostpointercapture', handlePointerCancel);\n clearSwipePosition();\n };\n }, [clearSwipePosition, handlePointerCancel]);\n\n const handleClickCapture = (event: React.MouseEvent<HTMLDivElement>) => {\n if (suppressClick.current && event.detail > 0) {\n suppressClick.current = false;\n event.preventDefault();\n event.stopPropagation();\n }\n };\n\n return {\n clearSwipePosition,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n };\n};\n\nexport default useSnackbarSwipeDismiss;\n"],"names":["DISMISS_CLASS","SETTLE_CLASS","useSnackbarSwipeDismiss","onDismissRequest","onSwipeChange","visible","containerRef","useRef","pointerRef","suppressClick","clearSwipePosition","useCallback","pointer","current","target","hasPointerCapture","id","releasePointerCapture","classList","remove","style","removeProperty","handlePointerDown","event","button","isPrimary","setPointerCapture","pointerId","startX","clientX","startY","clientY","threshold","currentTarget","offsetHeight","SNACKBAR_SWIPE_DISMISS_THRESHOLD","handlePointerMove","offsetY","offsetX","Math","abs","setProperty","handlePointerUp","container","bounds","closest","getBoundingClientRect","add","max","left","right","top","bottom","handlePointerCancel","useEffect","ownerDocument","addEventListener","removeEventListener","handleClickCapture","detail","preventDefault","stopPropagation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSnackbarSwipeDismiss.mjs","sources":["../../../src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts"],"sourcesContent":["import { useRef } from 'react';\n\nimport { SNACKBAR_SWIPE_DISMISS_THRESHOLD } from './constants';\n\nconst DISMISS_CLASS = 'wds-Snackbar--swipe-dismiss';\nconst SETTLE_CLASS = 'wds-Snackbar--swipe-settle';\n\ntype SwipePointer = {\n id: number;\n target: Element;\n startX: number;\n startY: number;\n threshold: number;\n};\n\n/**\n * Follows one primary pointer's downward, mostly vertical movement without re-rendering.\n * Release dismisses at half the surface height; shorter or cancelled gestures settle back.\n * A drag consumes its follow-up click, so dragging from the action cannot activate it.\n */\nconst useSnackbarSwipeDismiss = ({\n onDismissRequest,\n onSwipeChange,\n}: {\n onDismissRequest: () => void;\n /** Reports when a gesture starts and ends so auto-dismiss can pause beneath the pointer. */\n onSwipeChange: (swiping: boolean) => void;\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const pointerRef = useRef<SwipePointer | null>(null);\n const suppressClick = useRef(false);\n\n const endGesture = () => {\n pointerRef.current = null;\n onSwipeChange(false);\n };\n\n const settle = () => {\n containerRef.current?.classList.add(SETTLE_CLASS);\n containerRef.current?.style.removeProperty('transform');\n };\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0 || !event.isPrimary || pointerRef.current) {\n return;\n }\n\n const target = event.target as Element;\n // Keeps move/up events coming when a mouse leaves the Snackbar mid-drag.\n target.setPointerCapture?.(event.pointerId);\n containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);\n suppressClick.current = false;\n pointerRef.current = {\n id: event.pointerId,\n target,\n startX: event.clientX,\n startY: event.clientY,\n threshold: event.currentTarget.offsetHeight * SNACKBAR_SWIPE_DISMISS_THRESHOLD,\n };\n onSwipeChange(true);\n };\n\n const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n if (offsetY > 0 && offsetY > Math.abs(offsetX)) {\n containerRef.current?.style.setProperty('transform', `translateY(${offsetY}px)`);\n } else {\n containerRef.current?.style.removeProperty('transform');\n }\n };\n\n const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {\n const pointer = pointerRef.current;\n if (pointer?.id !== event.pointerId) {\n return;\n }\n\n endGesture();\n const offsetY = event.clientY - pointer.startY;\n const offsetX = event.clientX - pointer.startX;\n const container = containerRef.current;\n if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {\n suppressClick.current = true;\n if (container) {\n // Include wrapper padding and the safe area, not only the surface, to clear the viewport.\n container.classList.add(DISMISS_CLASS);\n container.style.setProperty(\n 'transform',\n `translateY(${Math.max(offsetY, container.offsetHeight)}px)`,\n );\n }\n onDismissRequest();\n } else {\n // Capture retargets the click to the pressed control even when released outside it,\n // unlike a native button; cancel it in that case.\n const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();\n suppressClick.current =\n !!bounds &&\n (offsetX !== 0 || offsetY !== 0) &&\n (event.clientX < bounds.left ||\n event.clientX > bounds.right ||\n event.clientY < bounds.top ||\n event.clientY > bounds.bottom);\n settle();\n }\n };\n\n const handlePointerCancel = (event: React.PointerEvent<HTMLDivElement>) => {\n if (pointerRef.current?.id !== event.pointerId) {\n return;\n }\n\n endGesture();\n settle();\n };\n\n /** Ends a gesture still in progress when the Snackbar starts exiting for another reason. */\n const cancelSwipe = () => {\n const pointer = pointerRef.current;\n if (!pointer) {\n return;\n }\n\n pointer.target.releasePointerCapture?.(pointer.id);\n endGesture();\n settle();\n };\n\n const handleClickCapture = (event: React.MouseEvent<HTMLDivElement>) => {\n if (suppressClick.current && event.detail > 0) {\n suppressClick.current = false;\n event.preventDefault();\n event.stopPropagation();\n }\n };\n\n return {\n cancelSwipe,\n containerRef,\n handleClickCapture,\n handlePointerCancel,\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n };\n};\n\nexport default useSnackbarSwipeDismiss;\n"],"names":["DISMISS_CLASS","SETTLE_CLASS","useSnackbarSwipeDismiss","onDismissRequest","onSwipeChange","containerRef","useRef","pointerRef","suppressClick","endGesture","current","settle","classList","add","style","removeProperty","handlePointerDown","event","button","isPrimary","target","setPointerCapture","pointerId","remove","id","startX","clientX","startY","clientY","threshold","currentTarget","offsetHeight","SNACKBAR_SWIPE_DISMISS_THRESHOLD","handlePointerMove","pointer","offsetY","offsetX","Math","abs","setProperty","handlePointerUp","container","max","bounds","closest","getBoundingClientRect","left","right","top","bottom","handlePointerCancel","cancelSwipe","releasePointerCapture","handleClickCapture","detail","preventDefault","stopPropagation"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Snackbar.d.ts","sourceRoot":"","sources":["../../../../src/Prompt/Snackbar/Snackbar.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIzE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,mGAAmG;IACnG,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,mEAAmE;IACnE,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,kGAAkG;IAClG,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,kGAAkG;IAClG,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,kGAAkG;IAClG,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,QAAQ,GAAI,uHAYtB,aAAa,
|
|
1
|
+
{"version":3,"file":"Snackbar.d.ts","sourceRoot":"","sources":["../../../../src/Prompt/Snackbar/Snackbar.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIzE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,mGAAmG;IACnG,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,mEAAmE;IACnE,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,kGAAkG;IAClG,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,kGAAkG;IAClG,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,kGAAkG;IAClG,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,QAAQ,GAAI,uHAYtB,aAAa,gCAuJf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Release dismisses at half the surface height; shorter
|
|
4
|
-
*
|
|
5
|
-
* follow-up click so dragging from the action cannot activate it.
|
|
2
|
+
* Follows one primary pointer's downward, mostly vertical movement without re-rendering.
|
|
3
|
+
* Release dismisses at half the surface height; shorter or cancelled gestures settle back.
|
|
4
|
+
* A drag consumes its follow-up click, so dragging from the action cannot activate it.
|
|
6
5
|
*/
|
|
7
|
-
declare const useSnackbarSwipeDismiss: ({ onDismissRequest, onSwipeChange,
|
|
6
|
+
declare const useSnackbarSwipeDismiss: ({ onDismissRequest, onSwipeChange, }: {
|
|
8
7
|
onDismissRequest: () => void;
|
|
9
8
|
/** Reports when a gesture starts and ends so auto-dismiss can pause beneath the pointer. */
|
|
10
9
|
onSwipeChange: (swiping: boolean) => void;
|
|
11
|
-
visible: boolean;
|
|
12
10
|
}) => {
|
|
13
|
-
|
|
11
|
+
cancelSwipe: () => void;
|
|
14
12
|
containerRef: import("react").RefObject<HTMLDivElement>;
|
|
15
13
|
handleClickCapture: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
16
|
-
handlePointerCancel: (event:
|
|
14
|
+
handlePointerCancel: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
17
15
|
handlePointerDown: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
18
16
|
handlePointerMove: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
19
17
|
handlePointerUp: (event: React.PointerEvent<HTMLDivElement>) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSnackbarSwipeDismiss.d.ts","sourceRoot":"","sources":["../../../../src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts"],"names":[],"mappings":"AAeA
|
|
1
|
+
{"version":3,"file":"useSnackbarSwipeDismiss.d.ts","sourceRoot":"","sources":["../../../../src/Prompt/Snackbar/useSnackbarSwipeDismiss.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,QAAA,MAAM,uBAAuB,GAAI,sCAG9B;IACD,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,4FAA4F;IAC5F,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C;;;gCA2GoC,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC;iCArB/B,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC;+BAvEpC,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC;+BAoBlC,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC;6BAepC,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC;CA0EnE,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -770,7 +770,6 @@ describe('Snackbar swipe dismissal', () => {
|
|
|
770
770
|
|
|
771
771
|
await user.pointer({ keys: '[/MouseLeft]', coords: { clientX: 0, clientY: 200 } });
|
|
772
772
|
|
|
773
|
-
expect(capture.releasePointerCapture).toHaveBeenCalledWith(1);
|
|
774
773
|
expect(snackbarProps.onDismissRequest).toHaveBeenCalledTimes(1);
|
|
775
774
|
expect(container).toHaveStyle({ transform: 'translateY(200px)' });
|
|
776
775
|
});
|
|
@@ -892,7 +891,7 @@ describe('Snackbar swipe dismissal', () => {
|
|
|
892
891
|
expect(snackbarProps.onDismissRequest).not.toHaveBeenCalled();
|
|
893
892
|
});
|
|
894
893
|
|
|
895
|
-
it.each(['pointercancel'
|
|
894
|
+
it.each(['pointercancel'])('restores the position on %s', async (type) => {
|
|
896
895
|
const { surface, container, snackbarProps } = setupSwipe();
|
|
897
896
|
mockPointerCapture(surface);
|
|
898
897
|
await user.pointer([
|
|
@@ -911,59 +910,6 @@ describe('Snackbar swipe dismissal', () => {
|
|
|
911
910
|
expect(snackbarProps.onDismissRequest).not.toHaveBeenCalled();
|
|
912
911
|
});
|
|
913
912
|
|
|
914
|
-
it('recovers when a captured child is removed and capture loss fires on its document', async () => {
|
|
915
|
-
const { surface, container, rerender, snackbarProps } = setupSwipe({
|
|
916
|
-
media: <span>Drag me</span>,
|
|
917
|
-
});
|
|
918
|
-
const child = screen.getByText('Drag me');
|
|
919
|
-
mockPointerCapture(child);
|
|
920
|
-
await user.pointer([
|
|
921
|
-
{ keys: '[MouseLeft>]', target: child, coords: { clientX: 0, clientY: 0 } },
|
|
922
|
-
{ coords: { clientX: 0, clientY: 30 } },
|
|
923
|
-
]);
|
|
924
|
-
expect(container).toHaveStyle({ transform: 'translateY(30px)' });
|
|
925
|
-
|
|
926
|
-
// An unrelated pointer losing capture must not cancel the active gesture.
|
|
927
|
-
const unrelatedLoss = new Event('lostpointercapture', { bubbles: true });
|
|
928
|
-
Object.defineProperty(unrelatedLoss, 'pointerId', { value: 2 });
|
|
929
|
-
fireEvent(surface.ownerDocument, unrelatedLoss);
|
|
930
|
-
expect(container).toHaveStyle({ transform: 'translateY(30px)' });
|
|
931
|
-
|
|
932
|
-
rerender(<Snackbar {...snackbarProps} media={undefined} />);
|
|
933
|
-
expect(child).not.toBeInTheDocument();
|
|
934
|
-
const captureLoss = new Event('lostpointercapture', { bubbles: true });
|
|
935
|
-
Object.defineProperty(captureLoss, 'pointerId', { value: 1 });
|
|
936
|
-
fireEvent(surface.ownerDocument, captureLoss);
|
|
937
|
-
|
|
938
|
-
expect(container).toHaveStyle({ transform: '' });
|
|
939
|
-
expect(container).toHaveClass('wds-Snackbar--swipe-settle');
|
|
940
|
-
expect(snackbarProps.onDismissRequest).not.toHaveBeenCalled();
|
|
941
|
-
await user.pointer({ keys: '[/MouseLeft]', target: document.body });
|
|
942
|
-
await user.pointer([
|
|
943
|
-
{ keys: '[MouseLeft>]', target: surface, coords: { clientX: 0, clientY: 0 } },
|
|
944
|
-
{ coords: { clientX: 0, clientY: 50 } },
|
|
945
|
-
'[/MouseLeft]',
|
|
946
|
-
]);
|
|
947
|
-
expect(snackbarProps.onDismissRequest).toHaveBeenCalledTimes(1);
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
it('removes its document capture-loss listener on unmount', () => {
|
|
951
|
-
const addListener = jest.spyOn(document, 'addEventListener');
|
|
952
|
-
const removeListener = jest.spyOn(document, 'removeEventListener');
|
|
953
|
-
try {
|
|
954
|
-
const { unmount } = setupSwipe();
|
|
955
|
-
const listener = addListener.mock.calls.find(([type]) => type === 'lostpointercapture')?.[1];
|
|
956
|
-
expect(listener).toEqual(expect.any(Function));
|
|
957
|
-
|
|
958
|
-
unmount();
|
|
959
|
-
|
|
960
|
-
expect(removeListener).toHaveBeenCalledWith('lostpointercapture', listener);
|
|
961
|
-
} finally {
|
|
962
|
-
addListener.mockRestore();
|
|
963
|
-
removeListener.mockRestore();
|
|
964
|
-
}
|
|
965
|
-
});
|
|
966
|
-
|
|
967
913
|
it('ignores secondary pointers while retaining the primary gesture', async () => {
|
|
968
914
|
const { surface, container, snackbarProps } = setupSwipe();
|
|
969
915
|
const capture = mockPointerCapture(surface);
|
|
@@ -995,20 +941,6 @@ describe('Snackbar swipe dismissal', () => {
|
|
|
995
941
|
expect(snackbarProps.onDismissRequest).not.toHaveBeenCalled();
|
|
996
942
|
});
|
|
997
943
|
|
|
998
|
-
it('clears the drag state after the exit finishes', async () => {
|
|
999
|
-
const { surface, container, rerender, snackbarProps } = setupSwipe();
|
|
1000
|
-
await user.pointer([
|
|
1001
|
-
{ keys: '[MouseLeft>]', target: surface, coords: { clientX: 0, clientY: 0 } },
|
|
1002
|
-
{ coords: { clientX: 0, clientY: 50 } },
|
|
1003
|
-
'[/MouseLeft]',
|
|
1004
|
-
]);
|
|
1005
|
-
rerender(<Snackbar {...snackbarProps} visible={false} />);
|
|
1006
|
-
advanceTimers(SNACKBAR_EXIT_DURATION);
|
|
1007
|
-
expect(container).toHaveStyle({ transform: '' });
|
|
1008
|
-
expect(container).not.toHaveClass('wds-Snackbar--swipe-dismiss');
|
|
1009
|
-
expect(snackbarProps.onExited).toHaveBeenCalledTimes(1);
|
|
1010
|
-
});
|
|
1011
|
-
|
|
1012
944
|
it('keeps dragging across a render and uses the latest dismissal callback', async () => {
|
|
1013
945
|
const { surface, container, rerender, snackbarProps } = setupSwipe();
|
|
1014
946
|
await user.pointer([
|
|
@@ -105,14 +105,14 @@ export const Snackbar = ({
|
|
|
105
105
|
visible,
|
|
106
106
|
});
|
|
107
107
|
const {
|
|
108
|
-
|
|
108
|
+
cancelSwipe,
|
|
109
109
|
containerRef,
|
|
110
110
|
handleClickCapture,
|
|
111
111
|
handlePointerCancel,
|
|
112
112
|
handlePointerDown,
|
|
113
113
|
handlePointerMove,
|
|
114
114
|
handlePointerUp,
|
|
115
|
-
} = useSnackbarSwipeDismiss({ onDismissRequest, onSwipeChange: setSwiping
|
|
115
|
+
} = useSnackbarSwipeDismiss({ onDismissRequest, onSwipeChange: setSwiping });
|
|
116
116
|
|
|
117
117
|
useEffect(() => {
|
|
118
118
|
if (!visible) {
|
|
@@ -187,10 +187,8 @@ export const Snackbar = ({
|
|
|
187
187
|
}}
|
|
188
188
|
unmountOnExit
|
|
189
189
|
onEntered={handleEntered}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
onExited();
|
|
193
|
-
}}
|
|
190
|
+
onExit={cancelSwipe}
|
|
191
|
+
onExited={onExited}
|
|
194
192
|
>
|
|
195
193
|
<PrimitivePrompt
|
|
196
194
|
ref={surfaceRef}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import { SNACKBAR_SWIPE_DISMISS_THRESHOLD } from './constants';
|
|
4
4
|
|
|
@@ -14,47 +14,42 @@ type SwipePointer = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* Release dismisses at half the surface height; shorter
|
|
19
|
-
*
|
|
20
|
-
* follow-up click so dragging from the action cannot activate it.
|
|
17
|
+
* Follows one primary pointer's downward, mostly vertical movement without re-rendering.
|
|
18
|
+
* Release dismisses at half the surface height; shorter or cancelled gestures settle back.
|
|
19
|
+
* A drag consumes its follow-up click, so dragging from the action cannot activate it.
|
|
21
20
|
*/
|
|
22
21
|
const useSnackbarSwipeDismiss = ({
|
|
23
22
|
onDismissRequest,
|
|
24
23
|
onSwipeChange,
|
|
25
|
-
visible,
|
|
26
24
|
}: {
|
|
27
25
|
onDismissRequest: () => void;
|
|
28
26
|
/** Reports when a gesture starts and ends so auto-dismiss can pause beneath the pointer. */
|
|
29
27
|
onSwipeChange: (swiping: boolean) => void;
|
|
30
|
-
visible: boolean;
|
|
31
28
|
}) => {
|
|
32
29
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
33
30
|
const pointerRef = useRef<SwipePointer | null>(null);
|
|
34
31
|
const suppressClick = useRef(false);
|
|
35
32
|
|
|
36
|
-
const
|
|
37
|
-
const pointer = pointerRef.current;
|
|
33
|
+
const endGesture = () => {
|
|
38
34
|
pointerRef.current = null;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
suppressClick.current = false;
|
|
46
|
-
containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);
|
|
35
|
+
onSwipeChange(false);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const settle = () => {
|
|
39
|
+
containerRef.current?.classList.add(SETTLE_CLASS);
|
|
47
40
|
containerRef.current?.style.removeProperty('transform');
|
|
48
|
-
}
|
|
41
|
+
};
|
|
49
42
|
|
|
50
43
|
const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
|
|
51
44
|
if (event.button !== 0 || !event.isPrimary || pointerRef.current) {
|
|
52
45
|
return;
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
clearSwipePosition();
|
|
56
48
|
const target = event.target as Element;
|
|
49
|
+
// Keeps move/up events coming when a mouse leaves the Snackbar mid-drag.
|
|
57
50
|
target.setPointerCapture?.(event.pointerId);
|
|
51
|
+
containerRef.current?.classList.remove(DISMISS_CLASS, SETTLE_CLASS);
|
|
52
|
+
suppressClick.current = false;
|
|
58
53
|
pointerRef.current = {
|
|
59
54
|
id: event.pointerId,
|
|
60
55
|
target,
|
|
@@ -86,13 +81,12 @@ const useSnackbarSwipeDismiss = ({
|
|
|
86
81
|
return;
|
|
87
82
|
}
|
|
88
83
|
|
|
84
|
+
endGesture();
|
|
89
85
|
const offsetY = event.clientY - pointer.startY;
|
|
90
86
|
const offsetX = event.clientX - pointer.startX;
|
|
91
87
|
const container = containerRef.current;
|
|
92
|
-
const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();
|
|
93
|
-
clearSwipePosition();
|
|
94
|
-
|
|
95
88
|
if (offsetY >= pointer.threshold && offsetY > Math.abs(offsetX)) {
|
|
89
|
+
suppressClick.current = true;
|
|
96
90
|
if (container) {
|
|
97
91
|
// Include wrapper padding and the safe area, not only the surface, to clear the viewport.
|
|
98
92
|
container.classList.add(DISMISS_CLASS);
|
|
@@ -101,10 +95,11 @@ const useSnackbarSwipeDismiss = ({
|
|
|
101
95
|
`translateY(${Math.max(offsetY, container.offsetHeight)}px)`,
|
|
102
96
|
);
|
|
103
97
|
}
|
|
104
|
-
suppressClick.current = true;
|
|
105
98
|
onDismissRequest();
|
|
106
99
|
} else {
|
|
107
|
-
// Capture retargets
|
|
100
|
+
// Capture retargets the click to the pressed control even when released outside it,
|
|
101
|
+
// unlike a native button; cancel it in that case.
|
|
102
|
+
const bounds = pointer.target.closest('a, button')?.getBoundingClientRect();
|
|
108
103
|
suppressClick.current =
|
|
109
104
|
!!bounds &&
|
|
110
105
|
(offsetX !== 0 || offsetY !== 0) &&
|
|
@@ -112,40 +107,30 @@ const useSnackbarSwipeDismiss = ({
|
|
|
112
107
|
event.clientX > bounds.right ||
|
|
113
108
|
event.clientY < bounds.top ||
|
|
114
109
|
event.clientY > bounds.bottom);
|
|
115
|
-
|
|
110
|
+
settle();
|
|
116
111
|
}
|
|
117
112
|
};
|
|
118
113
|
|
|
119
|
-
const handlePointerCancel =
|
|
120
|
-
(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
const handlePointerCancel = (event: React.PointerEvent<HTMLDivElement>) => {
|
|
115
|
+
if (pointerRef.current?.id !== event.pointerId) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
endGesture();
|
|
120
|
+
settle();
|
|
121
|
+
};
|
|
124
122
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
useEffect(() => {
|
|
132
|
-
if (visible) {
|
|
133
|
-
clearSwipePosition();
|
|
134
|
-
} else if (pointerRef.current) {
|
|
135
|
-
// An exit starting mid-gesture (timeout, Escape, replacement) ends it without dismissing again.
|
|
136
|
-
handlePointerCancel({ pointerId: pointerRef.current.id });
|
|
123
|
+
/** Ends a gesture still in progress when the Snackbar starts exiting for another reason. */
|
|
124
|
+
const cancelSwipe = () => {
|
|
125
|
+
const pointer = pointerRef.current;
|
|
126
|
+
if (!pointer) {
|
|
127
|
+
return;
|
|
137
128
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
ownerDocument?.addEventListener('lostpointercapture', handlePointerCancel);
|
|
144
|
-
return () => {
|
|
145
|
-
ownerDocument?.removeEventListener('lostpointercapture', handlePointerCancel);
|
|
146
|
-
clearSwipePosition();
|
|
147
|
-
};
|
|
148
|
-
}, [clearSwipePosition, handlePointerCancel]);
|
|
129
|
+
|
|
130
|
+
pointer.target.releasePointerCapture?.(pointer.id);
|
|
131
|
+
endGesture();
|
|
132
|
+
settle();
|
|
133
|
+
};
|
|
149
134
|
|
|
150
135
|
const handleClickCapture = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
151
136
|
if (suppressClick.current && event.detail > 0) {
|
|
@@ -156,7 +141,7 @@ const useSnackbarSwipeDismiss = ({
|
|
|
156
141
|
};
|
|
157
142
|
|
|
158
143
|
return {
|
|
159
|
-
|
|
144
|
+
cancelSwipe,
|
|
160
145
|
containerRef,
|
|
161
146
|
handleClickCapture,
|
|
162
147
|
handlePointerCancel,
|