From b7e0173b36d69c28ba9b8e592ae6e4d85c3b5d6e Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Tue, 30 Nov 2021 10:14:43 +0000 Subject: [PATCH 01/32] First draft adding typewriter sounds --- .../JournalyEditor/JournalyEditor.tsx | 18 +++++++++++++++++- .../public/static/sounds/typewriter-sound.wav | Bin 0 -> 75668 bytes 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 packages/web/public/static/sounds/typewriter-sound.wav diff --git a/packages/web/components/JournalyEditor/JournalyEditor.tsx b/packages/web/components/JournalyEditor/JournalyEditor.tsx index a32640429..e94bbbe70 100644 --- a/packages/web/components/JournalyEditor/JournalyEditor.tsx +++ b/packages/web/components/JournalyEditor/JournalyEditor.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useCallback } from 'react' +import React, { useEffect, useMemo, useCallback, useRef } from 'react' import { createEditor, Editor, Descendant } from 'slate' import { Slate, withReact } from 'slate-react' import { withHistory } from 'slate-history' @@ -64,6 +64,21 @@ const JournalyEditor = ({ return pipe(withReact(createEditor()), ...withPlugins) }, []) + const typewriterSound = useRef( + typeof Audio !== 'undefined' + ? new Audio('https://journaly-sound-effects.s3.us-east-2.amazonaws.com/typewriter-sound.wav') + : undefined, + ) + + const playTypewriterSound = (sound: React.MutableRefObject) => { + // Always rewind audio to beginning before playing + // This enables rapic replays even if the file was still playing + if (sound.current) { + sound.current.currentTime = 0 + sound.current.play() + } + } + useEffect(() => { ;(slateRef as React.MutableRefObject).current = editor }, [editor]) @@ -81,6 +96,7 @@ const JournalyEditor = ({ spellCheck onKeyDown={[ (event: React.KeyboardEvent) => { + playTypewriterSound(typewriterSound) Object.entries(HOTKEYS).forEach(([hotkey, mark]) => { // Convert React keyboard event to native keyboard event if (isHotkey(hotkey, (event as unknown) as KeyboardEvent)) { diff --git a/packages/web/public/static/sounds/typewriter-sound.wav b/packages/web/public/static/sounds/typewriter-sound.wav new file mode 100644 index 0000000000000000000000000000000000000000..bf749443331eeb47f923279ee8d1e90706707606 GIT binary patch literal 75668 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 vBklc7%B}8mvg{z|9 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 literal 0 HcmV?d00001 From eaabe749a57e627d4393e0c24859f89cf76d20cf Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Tue, 30 Nov 2021 11:10:42 +0000 Subject: [PATCH 02/32] Build SwitchToggle Component --- .../components/SwitchToggle/SwitchToggle.tsx | 65 +++++++++++++++++++ packages/web/components/SwitchToggle/index.ts | 1 + 2 files changed, 66 insertions(+) create mode 100644 packages/web/components/SwitchToggle/SwitchToggle.tsx create mode 100644 packages/web/components/SwitchToggle/index.ts diff --git a/packages/web/components/SwitchToggle/SwitchToggle.tsx b/packages/web/components/SwitchToggle/SwitchToggle.tsx new file mode 100644 index 000000000..01eb8d908 --- /dev/null +++ b/packages/web/components/SwitchToggle/SwitchToggle.tsx @@ -0,0 +1,65 @@ +import theme from '@/theme' +import React from 'react' + +type SwitchToggleProps = { + isToggled: boolean + onToggle: () => void +} + +const SwitchToggle: React.FC = ({ isToggled, onToggle }) => { + console.log(isToggled) + return ( + + ) +} + +export default SwitchToggle diff --git a/packages/web/components/SwitchToggle/index.ts b/packages/web/components/SwitchToggle/index.ts new file mode 100644 index 000000000..528d5305f --- /dev/null +++ b/packages/web/components/SwitchToggle/index.ts @@ -0,0 +1 @@ +export { default } from './SwitchToggle' From a62f92d838c3adc3ef81740dd3af1557e543aa72 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Tue, 30 Nov 2021 11:47:48 +0000 Subject: [PATCH 03/32] Update SwitchToggle sizing & colors --- .../components/SwitchToggle/SwitchToggle.tsx | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/web/components/SwitchToggle/SwitchToggle.tsx b/packages/web/components/SwitchToggle/SwitchToggle.tsx index 01eb8d908..c7475fb1a 100644 --- a/packages/web/components/SwitchToggle/SwitchToggle.tsx +++ b/packages/web/components/SwitchToggle/SwitchToggle.tsx @@ -16,8 +16,8 @@ const SwitchToggle: React.FC = ({ isToggled, onToggle }) => { .switch { position: relative; display: inline-block; - width: 60px; - height: 34px; + width: 40px; + height: 24px; } .switch input { @@ -33,29 +33,25 @@ const SwitchToggle: React.FC = ({ isToggled, onToggle }) => { left: 0; right: 0; bottom: 0; - background-color: ${theme.colors.gray300}; - transition: background 0.4s; - border-radius: 24px; + background-color: ${theme.colors.gray800}; + border-radius: 26px; } .slider::before { position: absolute; content: ''; - height: 26px; - width: 26px; + height: 16px; + width: 16px; left: 4px; bottom: 4px; background-color: ${theme.colors.white}; - transition: transform 0.4s; + transition: all 0.2s; border-radius: 15px; } - input:checked + .slider { - background-color: ${theme.colors.blueLight}; - } - input:checked + .slider::before { - transform: translateX(26px); + transform: translateX(16px); + background-color: ${theme.colors.blueLight}; } `} From cb0221fa49e7e5ccad2c48fd811fd457f42078b1 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Tue, 30 Nov 2021 11:48:04 +0000 Subject: [PATCH 04/32] Implement typewriter sound mechanism to editor --- .../JournalyEditor/JournalyEditor.tsx | 40 ++++++++++--------- .../JournalyEditor/Toolbar/Toolbar.tsx | 21 +++++++++- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/web/components/JournalyEditor/JournalyEditor.tsx b/packages/web/components/JournalyEditor/JournalyEditor.tsx index e94bbbe70..094ab2bc3 100644 --- a/packages/web/components/JournalyEditor/JournalyEditor.tsx +++ b/packages/web/components/JournalyEditor/JournalyEditor.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useCallback, useRef } from 'react' +import React, { useEffect, useMemo, useCallback, useRef, useState } from 'react' import { createEditor, Editor, Descendant } from 'slate' import { Slate, withReact } from 'slate-react' import { withHistory } from 'slate-history' @@ -10,13 +10,8 @@ import PostBodyStyles from '@/components/PostBodyStyles' import Toolbar from './Toolbar' import RenderElement from './RenderElement' import RenderLeaf from './RenderLeaf' -import { - withLinks, - withImages, - toggleMark, - options, - MarkType, -} from './helpers' +import { withLinks, withImages, toggleMark, options, MarkType } from './helpers' +import SwitchToggle from '../SwitchToggle' /** * The Journaly Rich Text Editor @@ -39,6 +34,7 @@ type JournalyEditorProps = { slateRef: React.RefObject allowInlineImages: boolean disabled?: boolean + playTypewriterSounds?: boolean } const plugins = [TablePlugin(options)] @@ -52,10 +48,7 @@ const JournalyEditor = ({ const renderElement = useCallback((props) => , []) const renderLeaf = useCallback((props) => , []) const editor = useMemo(() => { - const withPlugins: ((ed: Editor) => Editor)[] = [ - withHistory, - withLinks, - ] + const withPlugins: ((ed: Editor) => Editor)[] = [withHistory, withLinks] if (allowInlineImages) { withPlugins.push(withImages) @@ -64,19 +57,26 @@ const JournalyEditor = ({ return pipe(withReact(createEditor()), ...withPlugins) }, []) + const [playTypewriterSounds, setPlayTypewriterSounds] = useState(false) + const typewriterSound = useRef( typeof Audio !== 'undefined' ? new Audio('https://journaly-sound-effects.s3.us-east-2.amazonaws.com/typewriter-sound.wav') : undefined, ) - const playTypewriterSound = (sound: React.MutableRefObject) => { + const handlePlayTypewriterSound = ( + sound: React.MutableRefObject, + ) => { // Always rewind audio to beginning before playing // This enables rapic replays even if the file was still playing - if (sound.current) { + console.log('play', playTypewriterSounds) + if (sound.current && playTypewriterSounds) { + console.log('TAP!') sound.current.currentTime = 0 sound.current.play() } + return } useEffect(() => { @@ -87,7 +87,11 @@ const JournalyEditor = ({
setValue(value)}> - + setPlayTypewriterSounds(!playTypewriterSounds)} + /> { - playTypewriterSound(typewriterSound) + handlePlayTypewriterSound(typewriterSound) Object.entries(HOTKEYS).forEach(([hotkey, mark]) => { // Convert React keyboard event to native keyboard event - if (isHotkey(hotkey, (event as unknown) as KeyboardEvent)) { + if (isHotkey(hotkey, event as unknown as KeyboardEvent)) { event.preventDefault() toggleMark(editor, mark) } @@ -124,7 +128,7 @@ const JournalyEditor = ({ min-height: 200px; } - .editor-container > :global([contenteditable="true"]) { + .editor-container > :global([contenteditable='true']) { flex: 1; padding-top: 15px; } diff --git a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx index 00fe61614..720637529 100644 --- a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx +++ b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx @@ -29,12 +29,19 @@ import ToggleMarkButton from './ToggleMarkButton' import ToolbarButton from './ToolbarButton' import InsertImageButton from './InsertImageButton' import { options, isTableActive } from '../helpers' +import SwitchToggle from '@/components/SwitchToggle' type ToolbarProps = { allowInlineImages: boolean + playTypewriterSounds: boolean + onTogglePlayTypewriterSounds: () => void } -const Toolbar = ({ allowInlineImages }: ToolbarProps) => { +const Toolbar = ({ + allowInlineImages, + playTypewriterSounds, + onTogglePlayTypewriterSounds, +}: ToolbarProps) => { const editor = useSlate() const isEditorFocused = useFocused() const toolbarRef = useRef(null) @@ -147,6 +154,11 @@ const Toolbar = ({ allowInlineImages }: ToolbarProps) => { ) : ( tableIcon )} + +
+ Typewriter Sounds + +
-
) From 2258b1e8e2c40f11a83a7091240d26416032651a Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Mon, 10 Jan 2022 08:08:24 +0000 Subject: [PATCH 16/32] Debugging progress with position sticky --- packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx index c9eff35e1..e0e391bb7 100644 --- a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx +++ b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx @@ -176,6 +176,7 @@ const Toolbar = ({ .editor-toolbar-container { position: sticky; top: 200px; + top: calc(${headerHeight} + ${fixedDistanceFromTop}px); } .editor-toolbar { @@ -186,8 +187,6 @@ const Toolbar = ({ border-bottom: 2px solid #eee; z-index: 1; gap: 10px; - position: sticky; - top: 100px; } .is-fixed .editor-toolbar { From c714e263e9a9b80e8290048c297211f2f9e80c75 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Sat, 15 Jan 2022 17:32:05 +0000 Subject: [PATCH 17/32] getWindowSize hook --- packages/web/hooks/useGetWindowSize.ts | 45 ++++++++++++++++++++++++++ packages/web/hooks/useWindowSize.ts | 34 ------------------- 2 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 packages/web/hooks/useGetWindowSize.ts delete mode 100644 packages/web/hooks/useWindowSize.ts diff --git a/packages/web/hooks/useGetWindowSize.ts b/packages/web/hooks/useGetWindowSize.ts new file mode 100644 index 000000000..8b583e1fd --- /dev/null +++ b/packages/web/hooks/useGetWindowSize.ts @@ -0,0 +1,45 @@ +import { useState, useEffect } from 'react' + +/** + * A simple hook to get dimensions of window. + * Handles tracking and returning updated values on resizing. + * + * Example Usage: + * const windowSize = useGetWindowSize() + * + * if (windowSize.width === someBreakpoint) { + * // Show mobile view + * } + */ + +type WindowSize = { + width: number | null + height: number | null +} + +const useWindowSize = () => { + // Make sure we're client-side / not server-side + const isClient = typeof window === 'object' + + const [windowSize, setWindowSize] = useState({ + width: isClient ? window.innerWidth : null, + height: isClient ? window.innerHeight : null, + }) + + const handleResize = () => { + setWindowSize({ + width: window.innerWidth, + height: window.innerHeight, + }) + } + + useEffect(() => { + if (!isClient) return + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) + + return windowSize +} + +export default useWindowSize diff --git a/packages/web/hooks/useWindowSize.ts b/packages/web/hooks/useWindowSize.ts deleted file mode 100644 index f02e4cab4..000000000 --- a/packages/web/hooks/useWindowSize.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useState, useEffect } from 'react' - -type WindowSize = { - width: number - height: number -} - -const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity): WindowSize => { - const isClient = typeof window === 'object' - - const getSize = (): WindowSize => { - return { - width: isClient ? window.innerWidth : initialWidth, - height: isClient ? window.innerHeight : initialHeight, - } - } - - const [windowSize, setWindowSize] = useState(getSize) - - useEffect((): (() => void) | void => { - if (!isClient) return - - function handleResize() { - setWindowSize(getSize()) - } - - window.addEventListener('resize', handleResize) - return () => window.removeEventListener('resize', handleResize) - }, []) - - return windowSize -} - -export default useWindowSize From f717b8eeded24c78bd60c95324bd61b4d3912c65 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Sat, 15 Jan 2022 17:32:31 +0000 Subject: [PATCH 18/32] Remove hopefully obsolete rules to enable position sticky toolbar --- packages/web/components/Layouts/DashboardLayout.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/web/components/Layouts/DashboardLayout.tsx b/packages/web/components/Layouts/DashboardLayout.tsx index 1861d672b..2ba11309b 100644 --- a/packages/web/components/Layouts/DashboardLayout.tsx +++ b/packages/web/components/Layouts/DashboardLayout.tsx @@ -41,7 +41,6 @@ const DashboardLayout: React.FC = ({ children, pad = 'always' }) => { position: relative; height: 100%; width: 100%; - overflow: hidden; } .dashboard-container { @@ -62,7 +61,6 @@ const DashboardLayout: React.FC = ({ children, pad = 'always' }) => { .dashboard-container { height: 100%; - overflow-y: auto; transition: margin-left ${navConstants.transitionDuration}ms ease-in-out; } From 633131e90a476ebb6ef716d6fa1bb6c37e6527fc Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Sat, 15 Jan 2022 20:27:30 +0000 Subject: [PATCH 19/32] Create useIntersectionObserver hook --- .../web/hooks/userIntersectionObserver.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 packages/web/hooks/userIntersectionObserver.ts diff --git a/packages/web/hooks/userIntersectionObserver.ts b/packages/web/hooks/userIntersectionObserver.ts new file mode 100644 index 000000000..cd59a5ab1 --- /dev/null +++ b/packages/web/hooks/userIntersectionObserver.ts @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react' + +/** + * A hook to unable using an Intersection Observer. + * + * Wraps the logic in a try/catch because some browsers don't support IntersectionObserver + * and this would throw an error, rendering the app unusable for those users. + * * Currently only supports observing one node per usage, but could be modified to handle more. + * + * @param ref : Current code expects a div element + * @param rootMargin : String + * : CSS Margin syntax - sets an invisible box around the root + * : Example: '0px 0px 0px 0px' + * @returns Boolean : true if the DOM element crosses the threshold, otherwise false + */ + +type IntersectionObserverOptions = { + root?: HTMLElement | null + rootMargin?: string + threshold?: number +} + +const useIntersectionObserver = ({ + root = null, + rootMargin = '0px', + threshold = 0, +}: IntersectionObserverOptions) => { + const [observedElementRef, setObservedElementRef] = useState(null) + const [isIntersecting, setIsIntersecting] = useState(false) + + useEffect(() => { + try { + const observer = new IntersectionObserver( + // Destrucure first entry since we only observe one node at a time + ([entry]) => { + setIsIntersecting(entry.isIntersecting) + }, + { root, rootMargin, threshold }, + ) + + if (observedElementRef) { + observer.observe(observedElementRef) + } + + return () => { + if (observedElementRef) { + observer.disconnect() + } + } + } catch (e) {} + }, [observedElementRef, root, rootMargin, threshold]) + + return [setObservedElementRef, isIntersecting] +} + +export default useIntersectionObserver From 94b6661182091cea8eb870542c89a4a0743c62a5 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Sat, 15 Jan 2022 20:31:03 +0000 Subject: [PATCH 20/32] A very long and mostly failed day of playing with Intersection Observers --- .../JournalyEditor/Toolbar/Toolbar.tsx | 344 ++++++++---------- 1 file changed, 160 insertions(+), 184 deletions(-) diff --git a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx index e0e391bb7..e8581097b 100644 --- a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx +++ b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react' +import React from 'react' import classNames from 'classnames' import { Popup } from 'reactjs-popup' import { @@ -13,7 +13,6 @@ import { useSlate, useFocused } from 'slate-react' import theme from '@/theme' -import { headerHeight } from '@/components/Dashboard/dashboardConstants' import { navConstants } from '@/components/Dashboard/Nav' import FormatBoldIcon from '@/components/Icons/FormatBoldIcon' import FormatItalicIcon from '@/components/Icons/FormatItalicIcon' @@ -32,6 +31,8 @@ import InsertImageButton from './InsertImageButton' import { options, isTableActive } from '../helpers' import SwitchToggle from '@/components/SwitchToggle' import { useTranslation } from '@/config/i18n' +import useGetWindowSize from '@/hooks/useGetWindowSize' +import useIntersectionObserver from '@/hooks/userIntersectionObserver' type ToolbarProps = { allowInlineImages: boolean @@ -48,202 +49,177 @@ const Toolbar = ({ const editor = useSlate() const isEditorFocused = useFocused() - const toolbarRef = useRef(null) - const [isFixed, setIsFixed] = useState(false) - // Save the initial distance that the toolbar is from the top of the page - const originalElementOffsetTop = toolbarRef?.current?.offsetTop as number - // Save height of toolbar so that when it's fixed and removed from document flow, - // the parent takes the same height and surrounding content won't collapse - const fixedDistanceFromTop = 20 - const toolbarClasses = classNames('editor-toolbar-container') - // const toolbarClasses = classNames('editor-toolbar-container', { 'is-fixed': isFixed }) + const stickyToolbarOffset = 20 const isTableActivated = isEditorFocused && isTableActive(editor) const tableIcon = ( ) - const handleScroll = () => { - if (toolbarRef.current) { - if (!isFixed && window.pageYOffset >= toolbarRef.current.offsetTop - fixedDistanceFromTop) { - setIsFixed(true) - return - } - - if (isFixed && window.pageYOffset + fixedDistanceFromTop < originalElementOffsetTop) { - setIsFixed(false) - return - } - } - } - - useEffect(() => { - window.addEventListener('scroll', handleScroll) - - return () => { - window.removeEventListener('scroll', handleScroll) - } - }, [isFixed, originalElementOffsetTop]) + + const windowSize = useGetWindowSize() + + let toolbarStickyOffset = + windowSize.width && windowSize.width < navConstants.mobileBreakpoint + ? -stickyToolbarOffset - 72 + : -stickyToolbarOffset + + const [toolbarObserverRef, toolbarShouldFloat] = useIntersectionObserver({ + rootMargin: `0px 0px ${toolbarStickyOffset}px 0px`, + }) + + const toolbarClasses = classNames('editor-toolbar-container', { 'is-fixed': toolbarShouldFloat }) return ( -
-
-
- - - - - - - - - - - - - - - -
-
- - - - - - - - - - - - - - {isTableActivated ? ( - {tableIcon}} - position="bottom center" - on={['hover', 'focus']} - closeOnDocumentClick - className="editor-toolbar-popover" - > - - - - - +
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + {isTableActivated ? ( + {tableIcon}} + position="bottom center" + on={['hover', 'focus']} + closeOnDocumentClick + className="editor-toolbar-popover" + > + + + + + + + ) : ( + tableIcon + )} +
+
+
+ {t('typewriterSounds')} + - - ) : ( - tableIcon - )} -
-
-
- {t('typewriterSounds')} - +
-
- -
+ + .editor-toolbar-popover-item { + width: 100%; + justify-content: left; + padding-left: 7px; + } + + .editor-toolbar-popover-item:hover { + font-weight: 600; + } + + .toolbar-row { + display: flex; + gap: 10px; + } + `} +
+ ) } From 2d0e208769dda0d0b000d9b5b83c4beb8aa9a8c6 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Sat, 15 Jan 2022 21:30:12 +0000 Subject: [PATCH 21/32] Make things finally work --- packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx index e8581097b..ce5ceb8cb 100644 --- a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx +++ b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx @@ -65,10 +65,10 @@ const Toolbar = ({ : -stickyToolbarOffset const [toolbarObserverRef, toolbarShouldFloat] = useIntersectionObserver({ - rootMargin: `0px 0px ${toolbarStickyOffset}px 0px`, + rootMargin: `${toolbarStickyOffset}px 0px 0px 0px`, }) - const toolbarClasses = classNames('editor-toolbar-container', { 'is-fixed': toolbarShouldFloat }) + const toolbarClasses = classNames('editor-toolbar-container', { 'is-fixed': !toolbarShouldFloat }) return ( <> From 92d3646c02e76f02eebaa79ae10279bb1ab70071 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Fri, 21 Jan 2022 15:25:26 +0000 Subject: [PATCH 22/32] Address PR feedback --- .../JournalyEditor/Toolbar/Toolbar.tsx | 10 +++---- packages/web/hooks/useGetWindowSize.ts | 27 +++++++++---------- .../web/hooks/userIntersectionObserver.ts | 2 +- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx index ce5ceb8cb..91cac799b 100644 --- a/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx +++ b/packages/web/components/JournalyEditor/Toolbar/Toolbar.tsx @@ -49,7 +49,6 @@ const Toolbar = ({ const editor = useSlate() const isEditorFocused = useFocused() - const stickyToolbarOffset = 20 const isTableActivated = isEditorFocused && isTableActive(editor) const tableIcon = ( @@ -59,10 +58,9 @@ const Toolbar = ({ const windowSize = useGetWindowSize() - let toolbarStickyOffset = - windowSize.width && windowSize.width < navConstants.mobileBreakpoint - ? -stickyToolbarOffset - 72 - : -stickyToolbarOffset + // This is to accounnt for the nav which appears at the top on mobile and has a height of 72px + const toolbarStickyOffset = + windowSize.width && windowSize.width < navConstants.mobileBreakpoint ? -92 : -20 const [toolbarObserverRef, toolbarShouldFloat] = useIntersectionObserver({ rootMargin: `${toolbarStickyOffset}px 0px 0px 0px`, @@ -162,7 +160,7 @@ const Toolbar = ({