Description
Elements like FAB and Snackbar render on a different plane above the content in z-axis. Many other elements such as bottom sheets, modals etc. need to be rendered above content too. It'll be useful to have a single Portal component to handle these cases.
To determine which elements exist on which plane, we need to refer to their elevation - https://material.google.com/material-design/elevation-shadows.html#elevation-shadows-elevation-android
Use cases
- Elements with same elevation should be grouped in the same layer along the z-axis (e.g.
FAB, Snackbar)
- Grouped elements should move together, e.g. when a
Snackbar slides in from button, the FAB should move up
Detailed design
Props
The Portal component will accept two props, elevation and layered.
The elevation prop determines the elevation of the layer in the z-axis. There will be predefined elevations for internal use according to material design guidelines:
const Elevations = {
DIALOG: 24, // Dialogs and Pickers
DRAWER: 16, // Drawer, Bottom sheet
FAB_RAISED: 12, // Floating action button (raised)
CARD_RAISED: 8, // Card (raised), Button (raised), Bottom navigation, Menu
FAB_RESTING: 6, // Floating action button, Snackbar
APPBAR: 4, // Floating action button, Snackbar
CARD_RESTING: 2, // Card, Button, Search bar
}
This is not the full list of elevations, rather the ones we need. Naming is up-to-debate. This will be internal for now, so we can change them later.
The layered prop is a boolean which determines whether the item is grouped with other layered items in a single layer. Grouped items are rendered under the same View and positioned using flexbox, so items will appear one below another. When the layered prop is false or not passed, the items will be positioned absolutely instead. Non-layered items will render below layered elements.
Usage
<Portal elevation={Elevations.FAB_RESTING} layered>
<View style={styles.snackbar}>
Hey! I'm a `Snackbar` :)
</View>
</Portal>
Animations
The main motivation behind this is animations, i.e. when you show a new Snackbar, it should slide in from the bottom and move everything else up (FAB). The easiest way seems to be to handle the animation inside Portal. This is how it'll work:
- On initial render, render everything normally.
- When a new element is rendered later, render it offscreen and measure the
height ({ position: 'absolute', left: 0, right: 0, bottom: -9999 }).
- Now render the element normally, but also apply a
translateY of height to all the elements. This should have no visual difference.
- Animate the
translateY to 0 so everything moves up.
- When the element is removed, animate the
translateY to height so everything moves down.
- Now remove that element and reset the
translateY to 0
- If new elements are added/removed mid-transition, it'll wait till the transition is complete to reflect the changes.
This approach assumes that all the elements in a layer are going to use the same slide up/slide down animations for appearing/disappearing.
Description
Elements like FAB and Snackbar render on a different plane above the content in z-axis. Many other elements such as bottom sheets, modals etc. need to be rendered above content too. It'll be useful to have a single Portal component to handle these cases.
To determine which elements exist on which plane, we need to refer to their elevation - https://material.google.com/material-design/elevation-shadows.html#elevation-shadows-elevation-android
Use cases
FAB,Snackbar)Snackbarslides in from button, theFABshould move upDetailed design
Props
The
Portalcomponent will accept two props,elevationandlayered.The
elevationprop determines the elevation of the layer in the z-axis. There will be predefined elevations for internal use according to material design guidelines:This is not the full list of elevations, rather the ones we need. Naming is up-to-debate. This will be internal for now, so we can change them later.
The
layeredprop is abooleanwhich determines whether the item is grouped with other layered items in a single layer. Grouped items are rendered under the sameViewand positioned using flexbox, so items will appear one below another. When thelayeredprop isfalseor not passed, the items will be positioned absolutely instead. Non-layered items will render below layered elements.Usage
Animations
The main motivation behind this is animations, i.e. when you show a new
Snackbar, it should slide in from the bottom and move everything else up (FAB). The easiest way seems to be to handle the animation insidePortal. This is how it'll work:height({ position: 'absolute', left: 0, right: 0, bottom: -9999 }).translateYofheightto all the elements. This should have no visual difference.translateYto0so everything moves up.translateYtoheightso everything moves down.translateYto0This approach assumes that all the elements in a layer are going to use the same slide up/slide down animations for appearing/disappearing.