|
// STATE MACHINE |
|
const states = { |
|
MOBILE_DISPLAY: `MOBILE_DISPLAY`, |
|
BUTTON_HIDDEN: `BUTTON_HIDDEN`, |
|
BUTTON_DISPLAY: `BUTTON_DISPLAY`, |
|
DESKTOP_DISPLAY: `DESKTOP_DISPLAY`, |
|
DESKTOP_HIDDEN: `DEKSTOP_HIDDEN`, |
|
}; |
|
|
|
const events = { |
|
SCROLL_DOWN: `SCROLL_DOWN`, |
|
SCROLL_UP: `SCROLL_UP`, |
|
BUTTON_CLICK: `BUTTON_CLICK`, |
|
CLOSE_CLICK: `CLOSE_CLICK`, |
|
TO_DESKTOP_WIDTH: `TO_DESKTOP_WIDTH`, |
|
TO_MOBILE_WIDTH: `TO_MOBILE_WIDTH`, |
|
}; |
|
|
|
const machine = { |
|
initial: states.BUTTON_DISPLAY, |
|
states: { |
|
[states.MOBILE_DISPLAY]: { |
|
on: { |
|
[events.CLOSE_CLICK]: states.BUTTON_DISPLAY, |
|
[events.TO_DESKTOP_WIDTH]: states.DESKTOP_DISPLAY, |
|
}, |
|
}, |
|
[states.BUTTON_HIDDEN]: { |
|
on: { |
|
[events.SCROLL_UP]: states.BUTTON_DISPLAY, |
|
[events.TO_DESKTOP_WIDTH]: states.DESKTOP_DISPLAY, |
|
}, |
|
}, |
|
[states.BUTTON_DISPLAY]: { |
|
on: { |
|
[events.SCROLL_DOWN]: states.BUTTON_HIDDEN, |
|
[events.BUTTON_CLICK]: states.MOBILE_DISPLAY, |
|
[events.TO_DESKTOP_WIDTH]: states.DESKTOP_DISPLAY, |
|
}, |
|
}, |
|
[states.DESKTOP_DISPLAY]: { |
|
on: { |
|
[events.SCROLL_DOWN]: states.DESKTOP_HIDDEN, |
|
[events.TO_MOBILE_WIDTH]: states.MOBILE_DISPLAY, |
|
}, |
|
}, |
|
[states.DESKTOP_HIDDEN]: { |
|
on: { |
|
[events.SCROLL_UP]: states.DESKTOP_DISPLAY, |
|
[events.TO_MOBILE_WIDTH]: states.BUTTON_DISPLAY, |
|
}, |
|
}, |
|
}, |
|
}; |
|
|
|
const stateTransition = (state: string, event: string) => |
|
machine.states[state].on[event] || state; |
|
|
|
const [state, send] = useReducer(stateTransition, machine.initial); |
|
|
|
// HANDLE EVENTS |
|
// Scroll |
|
// use ref to keep last value on re-render |
|
const previousScrollPosition: MutableRefObject<number> = useRef(0); |
|
|
|
useEffect(() => { |
|
function handleScroll() { |
|
const currentScroll = window.scrollY; |
|
if (previousScrollPosition.current < currentScroll) { |
|
send(events.SCROLL_DOWN); |
|
} else { |
|
send(events.SCROLL_UP); |
|
} |
|
previousScrollPosition.current = currentScroll; |
|
} |
|
// use passive option to optmise scrolling |
|
window.addEventListener(`scroll`, handleScroll, { passive: true }); |
|
|
|
// Window Size |
|
function handleResize(entries: any) { |
|
const { width }: { width: number } = entries[0].contentRect; |
|
if (width > 1000) { |
|
send(events.TO_DESKTOP_WIDTH); |
|
} else { |
|
send(events.TO_MOBILE_WIDTH); |
|
} |
|
} |
|
const resizeObserver = new ResizeObserver(handleResize); |
|
const body = document.querySelector(`body`); |
|
if (body) { |
|
resizeObserver.observe(body); |
|
} |
|
|
|
// remove event listener on unmount |
|
return () => { |
|
window.removeEventListener(`scroll`, handleScroll); |
|
if (body) { |
|
resizeObserver.unobserve(body); |
|
} |
|
}; |
|
}); |