diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alf/atoms.ts | 20 | ||||
-rw-r--r-- | src/components/Dialog/index.web.tsx | 24 | ||||
-rw-r--r-- | src/style.css | 9 | ||||
-rw-r--r-- | src/view/shell/Composer.web.tsx | 33 |
4 files changed, 58 insertions, 28 deletions
diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index a7cf6cb3f..6982de75f 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -965,6 +965,26 @@ export const atoms = { transitionDelay: '50ms', }), + /* + * Animaations + */ + fade_in: web({ + animation: 'fadeIn ease-out 0.15s', + }), + fade_out: web({ + animation: 'fadeOut ease-out 0.15s', + }), + zoom_in: web({ + animation: 'zoomIn ease-out 0.1s', + }), + zoom_out: web({ + animation: 'zoomOut ease-out 0.1s', + }), + // special composite animation for dialogs + zoom_fade_in: web({ + animation: 'zoomIn ease-out 0.1s, fadeIn ease-out 0.1s', + }), + /** * {@link Layout.SCROLLBAR_OFFSET} */ diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index e45133dc5..9f4f8bb3f 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -15,6 +15,7 @@ import {FocusScope} from '@radix-ui/react-focus-scope' import {RemoveScrollBar} from 'react-remove-scroll-bar' import {logger} from '#/logger' +import {useA11y} from '#/state/a11y' import {useDialogStateControlContext} from '#/state/dialogs' import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' @@ -152,6 +153,7 @@ export function Inner({ const t = useTheme() const {close} = React.useContext(Context) const {gtMobile} = useBreakpoints() + const {reduceMotionEnabled} = useA11y() useFocusGuards() return ( <FocusScope loop asChild trapped> @@ -161,7 +163,7 @@ export function Inner({ aria-label={label} aria-labelledby={accessibilityLabelledBy} aria-describedby={accessibilityDescribedBy} - // @ts-ignore web only -prf + // @ts-expect-error web only -prf onClick={stopPropagation} onStartShouldSetResponder={_ => true} onTouchEnd={stopPropagation} @@ -177,10 +179,9 @@ export function Inner({ shadowColor: t.palette.black, shadowOpacity: t.name === 'light' ? 0.1 : 0.4, shadowRadius: 30, - // @ts-ignore web only - animation: 'fadeIn ease-out 0.1s', }, - flatten(style), + !reduceMotionEnabled && a.zoom_fade_in, + style, ])}> <DismissableLayer onInteractOutside={preventDefault} @@ -216,7 +217,7 @@ export const InnerFlatList = React.forwardRef< style={[ a.overflow_hidden, a.px_0, - // @ts-ignore web only -sfn + // @ts-expect-error web only -sfn {maxHeight: 'calc(-36px + 100vh)'}, webInnerStyle, ]} @@ -262,20 +263,15 @@ export function Handle() { function Backdrop() { const t = useTheme() + const {reduceMotionEnabled} = useA11y() return ( - <View - style={{ - opacity: 0.8, - }}> + <View style={{opacity: 0.8}}> <View style={[ a.fixed, a.inset_0, - { - backgroundColor: t.palette.black, - // @ts-ignore web only - animation: 'fadeIn ease-out 0.15s', - }, + {backgroundColor: t.palette.black}, + !reduceMotionEnabled && a.fade_in, ]} /> </View> diff --git a/src/style.css b/src/style.css index 96c51014f..770cb5e00 100644 --- a/src/style.css +++ b/src/style.css @@ -205,6 +205,15 @@ input:focus { } } +@keyframes zoomIn { + from { + transform: scale(0.95); + } + to { + transform: scale(1); + } +} + .force-no-clicks > *, .force-no-clicks * { pointer-events: none !important; diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx index 80a112705..b76e88372 100644 --- a/src/view/shell/Composer.web.tsx +++ b/src/view/shell/Composer.web.tsx @@ -5,6 +5,7 @@ import {useFocusGuards} from '@radix-ui/react-focus-guards' import {FocusScope} from '@radix-ui/react-focus-scope' import {RemoveScrollBar} from 'react-remove-scroll-bar' +import {useA11y} from '#/state/a11y' import {useModals} from '#/state/modals' import {ComposerOpts, useComposerState} from '#/state/shell/composer' import { @@ -12,7 +13,7 @@ import { EmojiPickerPosition, EmojiPickerState, } from '#/view/com/composer/text-input/web/EmojiPicker.web' -import {useBreakpoints, useTheme} from '#/alf' +import {atoms as a, flatten, useBreakpoints, useTheme} from '#/alf' import {ComposePost, useComposerCancelRef} from '../com/composer/Composer' const BOTTOM_BAR_HEIGHT = 61 @@ -41,6 +42,7 @@ function Inner({state}: {state: ComposerOpts}) { const {isModalActive} = useModals() const t = useTheme() const {gtMobile} = useBreakpoints() + const {reduceMotionEnabled} = useA11y() const [pickerState, setPickerState] = React.useState<EmojiPickerState>({ isOpen: false, pos: {top: 0, left: 0, right: 0, bottom: 0, nextFocusRef: null}, @@ -71,17 +73,15 @@ function Inner({state}: {state: ComposerOpts}) { <DismissableLayer role="dialog" aria-modal - style={{ - position: 'fixed', - top: 0, - left: 0, - width: '100%', - height: '100%', - backgroundColor: '#000c', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }} + style={flatten([ + {position: 'fixed'}, + a.inset_0, + {backgroundColor: '#000c'}, + a.flex, + a.flex_col, + a.align_center, + !reduceMotionEnabled && a.fade_in, + ])} onFocusOutside={evt => evt.preventDefault()} onInteractOutside={evt => evt.preventDefault()} onDismiss={() => { @@ -96,6 +96,11 @@ function Inner({state}: {state: ComposerOpts}) { !gtMobile && styles.containerMobile, t.atoms.bg, t.atoms.border_contrast_medium, + !reduceMotionEnabled && [ + a.zoom_fade_in, + {animationDelay: 0.1}, + {animationFillMode: 'backwards'}, + ], ]}> <ComposePost cancelRef={ref} @@ -123,14 +128,14 @@ const styles = StyleSheet.create({ borderRadius: 8, marginBottom: 0, borderWidth: 1, - // @ts-ignore web only + // @ts-expect-error web only maxHeight: 'calc(100% - (40px * 2))', overflow: 'hidden', }, containerMobile: { borderRadius: 0, marginBottom: BOTTOM_BAR_HEIGHT, - // @ts-ignore web only + // @ts-expect-error web only maxHeight: `calc(100% - ${BOTTOM_BAR_HEIGHT}px)`, }, }) |