Skip to content

Commit

Permalink
Make context positing easier
Browse files Browse the repository at this point in the history
  • Loading branch information
electerious committed Apr 25, 2020
1 parent 76e34dc commit a819226
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 113 deletions.
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"url": "https://github.com/electerious/Ackee.git"
},
"funding": {
"type" : "paypal",
"url" : "https://paypal.me/electerious"
"type": "paypal",
"url": "https://paypal.me/electerious"
},
"scripts": {
"start": "node src/index.js",
Expand All @@ -45,7 +45,7 @@
"is-url": "^1.2.4",
"micro": "^9.3.4",
"microrouter": "^3.1.3",
"mongoose": "^5.9.7",
"mongoose": "^5.9.10",
"node-fetch": "^2.6.0",
"node-schedule": "^1.3.2",
"normalize-url": "^5.0.0",
Expand All @@ -55,8 +55,9 @@
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-error-boundary": "^1.2.5",
"react-fast-compare": "^3.0.1",
"react-redux": "^7.2.0",
"react-use": "^14.1.0",
"react-use": "^14.2.0",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"redux-thunk": "^2.3.0",
Expand All @@ -70,8 +71,8 @@
},
"devDependencies": {
"@electerious/eslint-config": "^1.3.2",
"ava": "3.6.0",
"coveralls": "^3.0.11",
"ava": "3.7.1",
"coveralls": "^3.1.0",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-react": "^7.19.0",
Expand Down
24 changes: 10 additions & 14 deletions src/ui/scripts/components/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@ import PropTypes from 'prop-types'
import classNames from 'classnames'
import { useClickAway } from 'react-use'

import runWhenDefined from '../utils/runWhenDefined'
import useMeasure from '../utils/useMeasure'

export const BUTTON = Symbol()
export const SEPARATOR = Symbol()

const toPixel = (num) => `${ Math.round(num) }px`

const Context = (props) => {

const ref = useRef()
const measurement = useMeasure(props.targetRef)
const measurement = useMeasure(props.targetRef, ref)

useClickAway(ref, props.onAwayClick, [ 'click' ])

const x = measurement == null ? undefined : toPixel(props.x(measurement))
const y = measurement == null ? undefined : toPixel(props.y(measurement))

return createPortal(
h('div', {
ref,
Expand All @@ -26,12 +30,8 @@ const Context = (props) => {
'visible': measurement != null
}),
style: {
'--top': runWhenDefined(props.top, measurement),
'--right': runWhenDefined(props.right, measurement),
'--bottom': runWhenDefined(props.bottom, measurement),
'--left': runWhenDefined(props.left, measurement),
'--x': props.x,
'--y': props.y
'--x': x,
'--y': y
}
},
props.items.map((item, index) => {
Expand Down Expand Up @@ -68,12 +68,8 @@ Context.propTypes = {
targetRef: PropTypes.shape({
current: PropTypes.instanceOf(Element)
}),
top: PropTypes.func,
right: PropTypes.func,
bottom: PropTypes.func,
left: PropTypes.func,
x: PropTypes.string,
y: PropTypes.string,
x: PropTypes.func.isRequired,
y: PropTypes.func.isRequired,
floating: PropTypes.bool,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
onItemClick: PropTypes.func.isRequired,
Expand Down
19 changes: 14 additions & 5 deletions src/ui/scripts/components/Filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ const labels = {
[languages.LANGUAGES_SORTING_RECENT]: 'Recent'
}

const calculateX = (measurement) => {

return measurement.target.relative.x + measurement.target.width / 2 - measurement.element.width / 2

}

const calculateY = (measurement) => {

return measurement.target.relative.y - measurement.element.height - 10

}

const FilterItem = (props) => {

const ref = useRef()
Expand All @@ -82,11 +94,8 @@ const FilterItem = (props) => {
),
active === true && h(Context, {
targetRef: ref,
// Manually calculated works better on mobile when element is sticky to the bottom
bottom: () => `calc(4vh + 51px)`,
left: (measurement) => `${ measurement.left + measurement.width / 2 }px`,
x: '-50%',
y: '-10px',
x: calculateX,
y: calculateY,
floating: true,
items: props.items,
onItemClick: close,
Expand Down
22 changes: 19 additions & 3 deletions src/ui/scripts/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ import IconArrowDown from './icons/IconArrowDown'
const BUTTON = Symbol()
const DROPDOWN = Symbol()

const calculateX = (measurement) => {

return Math.min(
// Ensure that the context stays on the screen
measurement.body.width - measurement.element.width - 10,
// Ensure that the context is pinned to the target
measurement.target.absolute.x + measurement.target.width - measurement.element.width
)

}

const calculateY = (measurement) => {

return measurement.target.absolute.y + measurement.target.height + 10

}

const Spinner = (props) => {

return (
Expand Down Expand Up @@ -80,9 +97,8 @@ const Dropdown = (props) => {
),
active === true && h(Context, {
targetRef: ref,
top: (measurement) => `${ measurement.bottom - measurement.scrollY }px`,
right: (measurement) => `${ Math.max(10, measurement.scrollWidth - measurement.right) }px`,
y: '10px',
x: calculateX,
y: calculateY,
items: props.items,
onItemClick: close,
onAwayClick: close
Expand Down
10 changes: 0 additions & 10 deletions src/ui/scripts/utils/runWhenDefined.js

This file was deleted.

79 changes: 50 additions & 29 deletions src/ui/scripts/utils/useMeasure.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,69 @@
import React, { useState } from 'react'
import { useInterval } from 'react-use'
import { useRafLoop } from 'react-use'
import isEqual from 'react-fast-compare'

const getMeasurement = (targetRef, elementRef) => {

const scrollingBoundingClientRect = document.scrollingElement.getBoundingClientRect()
const targetBoundingClientRect = targetRef.current.getBoundingClientRect()
const elementBoundingClientRect = elementRef.current.getBoundingClientRect()

const body = {
width: scrollingBoundingClientRect.width,
height: scrollingBoundingClientRect.height,
relative: {
x: scrollingBoundingClientRect.left,
y: scrollingBoundingClientRect.top
},
absolute: {
x: scrollingBoundingClientRect.left,
y: scrollingBoundingClientRect.top
}
}

const getMeasurement = (ref) => {
const target = {
width: targetBoundingClientRect.width,
height: targetBoundingClientRect.height,
relative: {
x: targetBoundingClientRect.left,
y: targetBoundingClientRect.top
},
absolute: {
x: targetBoundingClientRect.left + scrollingBoundingClientRect.left * -1,
y: targetBoundingClientRect.top + scrollingBoundingClientRect.top * -1
}
}

const documentBoundingClientRect = document.scrollingElement.getBoundingClientRect()
const refBoundingClientRect = ref.current.getBoundingClientRect()
const element = {
width: elementBoundingClientRect.width,
height: elementBoundingClientRect.height
}

return {
top: refBoundingClientRect.top,
right: refBoundingClientRect.right,
bottom: refBoundingClientRect.bottom,
left: refBoundingClientRect.left,
width: refBoundingClientRect.width,
height: refBoundingClientRect.height,
x: refBoundingClientRect.x,
y: refBoundingClientRect.y,
scrollWidth: documentBoundingClientRect.width,
scrollHeight: documentBoundingClientRect.height,
scrollX: documentBoundingClientRect.x,
scrollY: documentBoundingClientRect.y
body,
target,
element
}

}

const shouldStateUpdate = (prevState = {}, nextState = {}) => {

return Object.keys(nextState).some((key) => prevState[key] !== nextState[key])

}

export default (ref) => {
export default (targetRef, elementRef) => {

const [ measurement, setMeasurement ] = useState()

useInterval(() => {
useRafLoop(() => {

if (targetRef.current == null) return
if (elementRef.current == null) return

if (ref.current == null) return
const nextMeasurement = getMeasurement(targetRef, elementRef)
const needsStateUpdate = isEqual(measurement || {}, nextMeasurement) === false

const nextMeasurement = getMeasurement(ref)
const needUpdate = shouldStateUpdate(measurement, nextMeasurement)
if (needsStateUpdate === false) return

if (needUpdate === true) setMeasurement(nextMeasurement)
setMeasurement(nextMeasurement)

}, 10)
}, true)

return measurement

Expand Down
15 changes: 8 additions & 7 deletions src/ui/styles/_context.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
.context {

position: absolute;
display: none;
top: var(--top);
right: var(--right);
bottom: var(--bottom);
left: var(--left);
display: grid;
top: 0;
left: 0;
padding: $gutter/2 0;
min-width: 170px;
background: rgba($context, .98);
border-radius: $radius;
transform: translate(var(--x, 0), var(--y, 0));
transform: translate(var(--x), var(--y));
z-index: 3;
opacity: 0;
pointer-events: none;

@supports (backdrop-filter: blur(10px)) {
background: $context;
Expand All @@ -29,7 +29,8 @@
}

&.visible {
display: grid;
opacity: 1;
pointer-events: all;
}

&__button {
Expand Down
Loading

0 comments on commit a819226

Please sign in to comment.