Skip to content

Commit

Permalink
Better animations for dialogs, animate web composer (#7703)
Browse files Browse the repository at this point in the history
* animation atoms, use for modals

* respect reduced motion

* simplify animtions

* fix atoms
  • Loading branch information
mozzius authored Feb 12, 2025
1 parent 521a764 commit 5d3e2e1
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 28 deletions.
20 changes: 20 additions & 0 deletions src/alf/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}
*/
Expand Down
24 changes: 10 additions & 14 deletions src/components/Dialog/index.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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>
Expand All @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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,
]}
Expand Down Expand Up @@ -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>
Expand Down
9 changes: 9 additions & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
33 changes: 19 additions & 14 deletions src/view/shell/Composer.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ 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 {
EmojiPicker,
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
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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={() => {
Expand All @@ -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}
Expand Down Expand Up @@ -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)`,
},
})

0 comments on commit 5d3e2e1

Please sign in to comment.