mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
debloat & improve DraggableDrawer
This commit is contained in:
parent
fc36450b31
commit
cfcabc55d6
@ -1,15 +0,0 @@
|
|||||||
export function isDirectionTop(direction) {
|
|
||||||
return direction === "top";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDirectionBottom(direction) {
|
|
||||||
return direction === "bottom";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDirectionLeft(direction) {
|
|
||||||
return direction === "left";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDirectionRight(direction) {
|
|
||||||
return direction === "right";
|
|
||||||
}
|
|
@ -7,14 +7,7 @@ import Observer from "react-intersection-observer"
|
|||||||
import { css } from "@emotion/css"
|
import { css } from "@emotion/css"
|
||||||
import { createPortal } from "react-dom"
|
import { createPortal } from "react-dom"
|
||||||
|
|
||||||
import {
|
export default class DraggableDrawer extends Component {
|
||||||
isDirectionBottom,
|
|
||||||
isDirectionTop,
|
|
||||||
isDirectionLeft,
|
|
||||||
isDirectionRight,
|
|
||||||
} from "./helpers.js"
|
|
||||||
|
|
||||||
export default class Drawer extends Component {
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
open: PropTypes.bool.isRequired,
|
open: PropTypes.bool.isRequired,
|
||||||
children: PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
@ -28,13 +21,11 @@ export default class Drawer extends Component {
|
|||||||
inViewportChange: PropTypes.func,
|
inViewportChange: PropTypes.func,
|
||||||
allowClose: PropTypes.bool,
|
allowClose: PropTypes.bool,
|
||||||
notifyWillClose: PropTypes.func,
|
notifyWillClose: PropTypes.func,
|
||||||
direction: PropTypes.string,
|
|
||||||
modalElementClass: PropTypes.oneOfType([
|
modalElementClass: PropTypes.oneOfType([
|
||||||
PropTypes.object,
|
PropTypes.object,
|
||||||
PropTypes.string
|
PropTypes.string
|
||||||
]),
|
]),
|
||||||
containerOpacity: PropTypes.number,
|
containerOpacity: PropTypes.number,
|
||||||
containerElementClass: PropTypes.string,
|
|
||||||
getContainerRef: PropTypes.func,
|
getContainerRef: PropTypes.func,
|
||||||
getModalRef: PropTypes.func
|
getModalRef: PropTypes.func
|
||||||
}
|
}
|
||||||
@ -48,11 +39,9 @@ export default class Drawer extends Component {
|
|||||||
getContainerRef: () => { },
|
getContainerRef: () => { },
|
||||||
getModalRef: () => { },
|
getModalRef: () => { },
|
||||||
containerOpacity: 0.6,
|
containerOpacity: 0.6,
|
||||||
direction: "bottom",
|
|
||||||
parentElement: document.body,
|
parentElement: document.body,
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
dontApplyListeners: false,
|
dontApplyListeners: false,
|
||||||
containerElementClass: "",
|
|
||||||
modalElementClass: ""
|
modalElementClass: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +57,19 @@ export default class Drawer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DESKTOP_MODE = false
|
DESKTOP_MODE = false
|
||||||
DRAGGER_HEIGHT_SIZE = 100
|
|
||||||
MAX_NEGATIVE_SCROLL = 5
|
|
||||||
SCROLL_TO_CLOSE = 475
|
|
||||||
ALLOW_DRAWER_TRANSFORM = true
|
ALLOW_DRAWER_TRANSFORM = true
|
||||||
|
|
||||||
|
MAX_NEGATIVE_SCROLL = -50
|
||||||
|
PX_TO_CLOSE_FROM_BOTTOM = 200
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.DESKTOP_MODE = !app.isMobile
|
this.DESKTOP_MODE = !app.isMobile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.removeListeners()
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, nextState) {
|
componentDidUpdate(prevProps, nextState) {
|
||||||
// in the process of closing the drawer
|
// in the process of closing the drawer
|
||||||
if (!this.props.open && prevProps.open) {
|
if (!this.props.open && prevProps.open) {
|
||||||
@ -86,7 +79,7 @@ export default class Drawer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.drawer) {
|
if (this.drawer) {
|
||||||
this.getNegativeScroll(this.drawer)
|
this.setNegativeScroll(this.drawer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// in the process of opening the drawer
|
// in the process of opening the drawer
|
||||||
@ -97,12 +90,8 @@ export default class Drawer extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
attachListeners = (drawer) => {
|
||||||
this.removeListeners()
|
const { dontApplyListeners, getModalRef } = this.props
|
||||||
}
|
|
||||||
|
|
||||||
attachListeners = drawer => {
|
|
||||||
const { dontApplyListeners, getModalRef, direction } = this.props
|
|
||||||
const { listenersAttached } = this.state
|
const { listenersAttached } = this.state
|
||||||
|
|
||||||
// only attach listeners once as this function gets called every re-render
|
// only attach listeners once as this function gets called every re-render
|
||||||
@ -118,10 +107,6 @@ export default class Drawer extends Component {
|
|||||||
|
|
||||||
let position = 0
|
let position = 0
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
|
||||||
position = drawer.scrollWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ listenersAttached: true, position }, () => {
|
this.setState({ listenersAttached: true, position }, () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// trigger reflow so webkit browsers calculate height properly 😔
|
// trigger reflow so webkit browsers calculate height properly 😔
|
||||||
@ -134,7 +119,7 @@ export default class Drawer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isThumbInDraggerRange = (event) => {
|
isThumbInDraggerRange = (event) => {
|
||||||
return (event.touches[0].clientY - this.drawer.getBoundingClientRect().top) < this.DRAGGER_HEIGHT_SIZE
|
return (event.touches[0].clientY - this.drawer.getBoundingClientRect().top)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeListeners = () => {
|
removeListeners = () => {
|
||||||
@ -149,71 +134,70 @@ export default class Drawer extends Component {
|
|||||||
this.setState({ listenersAttached: false })
|
this.setState({ listenersAttached: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
tap = event => {
|
tap = (event) => {
|
||||||
const { pageY, pageX } = event.touches[0]
|
const { pageY } = event.touches[0]
|
||||||
const shouldIgnored = Boolean(event.target.getAttribute("ignore-dragger") || (window.getComputedStyle(event.target).getPropertyValue("--ignore-dragger") !== ""))
|
|
||||||
|
|
||||||
const start = isDirectionBottom(this.props.direction) || isDirectionTop(this.props.direction) ? pageY : pageX
|
if (!this.isThumbInDraggerRange(event)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if event.target has dragger argument
|
||||||
|
const inDraggerArea = !!event.target.closest("#dragger-area")
|
||||||
|
|
||||||
|
const start = pageY
|
||||||
|
|
||||||
// reset NEW_POSITION and MOVING_POSITION
|
// reset NEW_POSITION and MOVING_POSITION
|
||||||
this.NEW_POSITION = 0
|
this.NEW_POSITION = 0
|
||||||
this.MOVING_POSITION = 0
|
this.MOVING_POSITION = 0
|
||||||
|
|
||||||
this.setState({ ignore: shouldIgnored, onRange: this.isThumbInDraggerRange(event), thumb: start, start: start, touching: true })
|
this.setState({
|
||||||
|
ignore: !inDraggerArea,
|
||||||
|
onRange: this.isThumbInDraggerRange(event),
|
||||||
|
thumb: start,
|
||||||
|
start: start,
|
||||||
|
touching: true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
drag = event => {
|
drag = (event) => {
|
||||||
if (this.state.ignore) {
|
if (this.state.ignore) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const { direction } = this.props
|
event.preventDefault()
|
||||||
|
|
||||||
const { thumb, position } = this.state
|
const { thumb, position } = this.state
|
||||||
const { pageY, pageX } = event.touches[0]
|
const { pageY } = event.touches[0]
|
||||||
|
|
||||||
const movingPosition = isDirectionBottom(direction) || isDirectionTop(direction) ? pageY : pageX
|
const movingPosition = pageY
|
||||||
const delta = movingPosition - thumb
|
const delta = movingPosition - thumb
|
||||||
const newPosition = isDirectionBottom(direction) ? position + delta : position - delta
|
const newPosition = position + delta
|
||||||
|
|
||||||
if (newPosition > 0 && this.ALLOW_DRAWER_TRANSFORM) {
|
if (this.ALLOW_DRAWER_TRANSFORM) {
|
||||||
// stop android's pull to refresh behavior
|
// allow to drag negative scroll
|
||||||
event.preventDefault()
|
if (newPosition < this.MAX_NEGATIVE_SCROLL) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
this.props.onDrag({ newPosition })
|
this.props.onDrag({ newPosition })
|
||||||
|
|
||||||
// we set this, so we can access it in shouldWeCloseDrawer. Since setState is async, we're not guranteed we'll have the
|
|
||||||
// value in time
|
|
||||||
this.MOVING_POSITION = movingPosition
|
this.MOVING_POSITION = movingPosition
|
||||||
this.NEW_POSITION = newPosition
|
this.NEW_POSITION = newPosition
|
||||||
|
|
||||||
let positionThreshold = 0
|
if (this.shouldWeCloseDrawer()) {
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
|
||||||
positionThreshold = this.drawer.scrollWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newPosition < positionThreshold && this.shouldWeCloseDrawer()) {
|
|
||||||
this.props.notifyWillClose(true)
|
this.props.notifyWillClose(true)
|
||||||
} else {
|
} else {
|
||||||
this.props.notifyWillClose(false)
|
this.props.notifyWillClose(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// not at the bottom
|
this.setState({
|
||||||
if (this.NEGATIVE_SCROLL < newPosition) {
|
thumb: movingPosition,
|
||||||
this.setState({
|
position: newPosition,
|
||||||
thumb: movingPosition,
|
})
|
||||||
position:
|
|
||||||
positionThreshold > 0
|
|
||||||
? Math.min(newPosition, positionThreshold)
|
|
||||||
: newPosition
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
release = (event) => {
|
release = () => {
|
||||||
const { direction } = this.props
|
|
||||||
|
|
||||||
this.setState({ touching: false })
|
this.setState({ touching: false })
|
||||||
|
|
||||||
if (this.shouldWeCloseDrawer() && this.state.onRange) {
|
if (this.shouldWeCloseDrawer() && this.state.onRange) {
|
||||||
@ -221,34 +205,21 @@ export default class Drawer extends Component {
|
|||||||
} else {
|
} else {
|
||||||
let newPosition = 0
|
let newPosition = 0
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
|
||||||
newPosition = this.drawer.scrollWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ position: newPosition })
|
this.setState({ position: newPosition })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getNegativeScroll = (element) => {
|
setNegativeScroll = (element) => {
|
||||||
const { direction } = this.props
|
|
||||||
const size = this.getElementSize()
|
const size = this.getElementSize()
|
||||||
|
|
||||||
if (isDirectionBottom(direction) || isDirectionTop(direction)) {
|
this.NEGATIVE_SCROLL = size - element.scrollHeight - this.MAX_NEGATIVE_SCROLL
|
||||||
this.NEGATIVE_SCROLL = size - element.scrollHeight - this.MAX_NEGATIVE_SCROLL
|
|
||||||
} else {
|
|
||||||
this.NEGATIVE_SCROLL = size - element.scrollWidth - this.MAX_NEGATIVE_SCROLL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hideDrawer = () => {
|
hideDrawer = () => {
|
||||||
const { allowClose, direction } = this.props
|
const { allowClose } = this.props
|
||||||
|
|
||||||
let defaultPosition = 0
|
let defaultPosition = 0
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
|
||||||
defaultPosition = this.drawer.scrollWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowClose === false) {
|
if (allowClose === false) {
|
||||||
// if we aren't going to allow close, let's animate back to the default position
|
// if we aren't going to allow close, let's animate back to the default position
|
||||||
return this.setState({
|
return this.setState({
|
||||||
@ -269,67 +240,31 @@ export default class Drawer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shouldWeCloseDrawer = () => {
|
shouldWeCloseDrawer = () => {
|
||||||
const { start: touchStart } = this.state
|
if (this.MOVING_POSITION === 0) {
|
||||||
const { direction } = this.props
|
return false
|
||||||
|
|
||||||
let initialPosition = 0
|
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
|
||||||
initialPosition = this.drawer.scrollWidth
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.MOVING_POSITION === initialPosition) return false
|
const containerHeight = this.getElementSize()
|
||||||
|
const closeThreshold = containerHeight - this.PX_TO_CLOSE_FROM_BOTTOM
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
return (
|
||||||
return (
|
this.NEW_POSITION >= 0 &&
|
||||||
this.NEW_POSITION < initialPosition &&
|
this.MOVING_POSITION >= closeThreshold
|
||||||
this.MOVING_POSITION - touchStart > this.SCROLL_TO_CLOSE
|
)
|
||||||
)
|
|
||||||
} else if (isDirectionLeft(direction)) {
|
|
||||||
return (
|
|
||||||
this.NEW_POSITION >= initialPosition &&
|
|
||||||
touchStart - this.MOVING_POSITION > this.SCROLL_TO_CLOSE
|
|
||||||
)
|
|
||||||
} else if (isDirectionTop(direction)) {
|
|
||||||
return (
|
|
||||||
this.NEW_POSITION >= initialPosition &&
|
|
||||||
touchStart - this.MOVING_POSITION > this.SCROLL_TO_CLOSE
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
this.NEW_POSITION >= initialPosition &&
|
|
||||||
this.MOVING_POSITION - touchStart > this.SCROLL_TO_CLOSE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDrawerTransform = (value) => {
|
getDrawerTransform = (value) => {
|
||||||
const { direction } = this.props
|
return { transform: `translate3d(0, ${value}px, 0)` }
|
||||||
|
|
||||||
if (isDirectionBottom(direction)) {
|
|
||||||
return { transform: `translate3d(0, ${value}px, 0)` }
|
|
||||||
} else if (isDirectionTop(direction)) {
|
|
||||||
return { transform: `translate3d(0, -${value}px, 0)` }
|
|
||||||
} else if (isDirectionLeft(direction)) {
|
|
||||||
return { transform: `translate3d(-${Math.abs(value)}px, 0, 0)` }
|
|
||||||
} else if (isDirectionRight(direction)) {
|
|
||||||
return { transform: `translate3d(${value}px, 0, 0)` }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getElementSize = () => {
|
getElementSize = () => {
|
||||||
return isDirectionBottom(this.props.direction) || isDirectionTop(this.props.direction) ? window.innerHeight : window.innerWidth
|
return window.innerHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
getPosition(hiddenPosition) {
|
getPosition() {
|
||||||
const { position } = this.state
|
const { position } = this.state
|
||||||
const { direction } = this.props
|
|
||||||
|
|
||||||
if (isDirectionRight(direction)) {
|
return position
|
||||||
return hiddenPosition - position
|
|
||||||
} else {
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inViewportChange = (inView) => {
|
inViewportChange = (inView) => {
|
||||||
@ -360,46 +295,24 @@ export default class Drawer extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
containerElementClass,
|
|
||||||
containerOpacity,
|
containerOpacity,
|
||||||
dontApplyListeners,
|
|
||||||
id,
|
id,
|
||||||
getContainerRef,
|
getContainerRef,
|
||||||
getModalRef,
|
|
||||||
direction
|
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const open = this.state.open && this.props.open
|
const open = this.state.open && this.props.open
|
||||||
|
|
||||||
// If drawer isn't open or in the process of opening/closing, then remove it from the DOM
|
|
||||||
// also, if we're not client side we need to return early because createPortal is only
|
|
||||||
// a clientside method
|
|
||||||
|
|
||||||
// if ((!this.state.open && !this.props.open)) {
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
const { touching } = this.state
|
const { touching } = this.state
|
||||||
|
|
||||||
const springPreset = isDirectionLeft(direction) ? { damping: 17, stiffness: 120 } : { damping: 20, stiffness: 300 }
|
const springPreset = { damping: 20, stiffness: 300 }
|
||||||
const animationSpring = touching ? springPreset : presets.stiff
|
const animationSpring = touching ? springPreset : presets.stiff
|
||||||
const hiddenPosition = this.getElementSize()
|
const hiddenPosition = this.getElementSize()
|
||||||
const position = this.getPosition(hiddenPosition)
|
const position = this.getPosition(hiddenPosition)
|
||||||
|
|
||||||
// Style object for the container element
|
|
||||||
let containerStyle = {
|
let containerStyle = {
|
||||||
backgroundColor: `rgba(55, 56, 56, ${open ? containerOpacity : 0})`
|
backgroundColor: `rgba(55, 56, 56, ${open ? containerOpacity : 0})`
|
||||||
}
|
}
|
||||||
|
|
||||||
// If direction is right, we set the overflowX property to 'hidden' to hide the x scrollbar during
|
|
||||||
// the sliding animation
|
|
||||||
if (isDirectionRight(direction)) {
|
|
||||||
containerStyle = {
|
|
||||||
...containerStyle,
|
|
||||||
overflowX: "hidden"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<Motion
|
<Motion
|
||||||
style={{
|
style={{
|
||||||
@ -415,7 +328,7 @@ export default class Drawer extends Component {
|
|||||||
id={id}
|
id={id}
|
||||||
style={containerStyle}
|
style={containerStyle}
|
||||||
onMouseDown={this.onClickOutside}
|
onMouseDown={this.onClickOutside}
|
||||||
className={`${Container} ${containerElementClass} `}
|
className="draggable-drawer"
|
||||||
ref={getContainerRef}
|
ref={getContainerRef}
|
||||||
>
|
>
|
||||||
<Observer
|
<Observer
|
||||||
@ -424,11 +337,24 @@ export default class Drawer extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
className="draggable-drawer_body"
|
||||||
onClick={this.stopPropagation}
|
onClick={this.stopPropagation}
|
||||||
style={this.getDrawerTransform(translate)}
|
style={{
|
||||||
|
...this.props.bodyStyle,
|
||||||
|
...this.getDrawerTransform(translate),
|
||||||
|
}}
|
||||||
ref={this.attachListeners}
|
ref={this.attachListeners}
|
||||||
className={this.props.modalElementClass || ""}
|
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
className="dragger-area"
|
||||||
|
id="dragger-area"
|
||||||
|
dragger
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="dragger-indicator"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -440,22 +366,6 @@ export default class Drawer extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = css`
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 11;
|
|
||||||
transition: background-color 0.2s linear;
|
|
||||||
overflow-y: auto;
|
|
||||||
overscroll-behavior: none;
|
|
||||||
`
|
|
||||||
|
|
||||||
const HaveWeScrolled = css`
|
const HaveWeScrolled = css`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user