Better animations for dialogs, animate web composer (#7703)
* animation atoms, use for modals * respect reduced motion * simplify animtions * fix atoms
This commit is contained in:
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)`,
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user