From e014db96c65e8bf356d0a5654d199d4c22e0bb0d Mon Sep 17 00:00:00 2001 From: TimBryanDev Date: Tue, 26 Sep 2023 14:26:07 +0100 Subject: [PATCH 1/5] :sparkles: create hook for local storage --- lib/hooks/useLocalStorage.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/hooks/useLocalStorage.ts diff --git a/lib/hooks/useLocalStorage.ts b/lib/hooks/useLocalStorage.ts new file mode 100644 index 000000000..a33da977d --- /dev/null +++ b/lib/hooks/useLocalStorage.ts @@ -0,0 +1,28 @@ +import { useState } from "react"; + +export const useLocalStorage = (key: string, defaultValue: unknown) => { + const [localStorageValue, setLocalStorageValue] = useState(() => { + try { + // Try and retrieve any previously recorded values + const value = localStorage.getItem(key); + if (value) { + return JSON.parse(value); + } + + // Else, record the defaultValue and return it instead + localStorage.setItem(key, JSON.stringify(defaultValue)); + return defaultValue; + } catch (error) { + // Fallback to the defaultValue if we can't retrieve a previous value + localStorage.setItem(key, JSON.stringify(defaultValue)); + return defaultValue; + } + }); + + const setLocalStorageStateValue = (value: unknown) => { + localStorage.setItem(key, JSON.stringify(value)); + setLocalStorageValue(value); + }; + + return [localStorageValue, setLocalStorageStateValue]; +}; From 46e6e2932e886da874b786810d3223e4f562e975 Mon Sep 17 00:00:00 2001 From: TimBryanDev Date: Tue, 26 Sep 2023 14:28:21 +0100 Subject: [PATCH 2/5] :sparkles: remember last resized position --- lib/Resizable/index.tsx | 26 +++++++++++++++++++----- stories/components/Resizable.stories.tsx | 13 ++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/Resizable/index.tsx b/lib/Resizable/index.tsx index 3d7e8815f..c876693e3 100644 --- a/lib/Resizable/index.tsx +++ b/lib/Resizable/index.tsx @@ -1,17 +1,26 @@ import type { PropsWithChildren } from "react"; import React, { useCallback, useEffect, useRef } from "react"; import { keepValueWithinRange, toPixels } from "../helpers"; +import { useLocalStorage } from "../hooks/useLocalStorage"; + +const LOCALSTORAGE_KEY = "RESIZE_POSITION"; export interface ResizableProps { containerWidth?: number | string; initialWidth?: number | string; minResizableWidth?: number | string; maxResizableWidth?: number | string; + rememberPosition?: boolean; useGutterOffset?: boolean; } export function Resizable(props: PropsWithChildren) { - const { children, initialWidth = "50%", useGutterOffset = false } = props; + const { + children, + initialWidth = "50%", + rememberPosition = false, + useGutterOffset = false, + } = props; const containerWidth: number = toPixels( props.containerWidth ?? document.body.offsetWidth ); @@ -30,14 +39,21 @@ export function Resizable(props: PropsWithChildren) { startWidth: 0, }); + const [lastPosition, setLastPosition] = useLocalStorage( + LOCALSTORAGE_KEY, + toPixels(initialWidth) + ); + const getWidth = () => toPixels(resizeWrapperRef.current?.style.width || 0); const setWidth = (value: number) => { if (resizeWrapperRef.current === null) return; - resizeWrapperRef.current.style.width = `${ - keepValueWithinRange(value, minWidth, maxWidth) - gutterSize - }px`; + const newWidth = + keepValueWithinRange(value, minWidth, maxWidth) - gutterSize; + + resizeWrapperRef.current.style.width = `${newWidth}px`; + setLastPosition(newWidth); }; const doDrag = (evt: MouseEvent) => { @@ -85,7 +101,7 @@ export function Resizable(props: PropsWithChildren) { }; useEffect(() => { - setWidth(toPixels(initialWidth)); + setWidth(rememberPosition ? lastPosition : toPixels(initialWidth)); // remember to remove global listeners on dismount return () => stopDrag(); diff --git a/stories/components/Resizable.stories.tsx b/stories/components/Resizable.stories.tsx index 6a871c3b4..bcc297755 100644 --- a/stories/components/Resizable.stories.tsx +++ b/stories/components/Resizable.stories.tsx @@ -121,6 +121,19 @@ export function Resizable() { + + +
+ +
+

Move me - refresh the page - I should be where you left me!

+
+
+
+
); } From 682db103481cb4c49a93a1c0511fd5c64ce06f76 Mon Sep 17 00:00:00 2001 From: TimBryanDev Date: Tue, 26 Sep 2023 14:33:38 +0100 Subject: [PATCH 3/5] :sparkles: record positions uniquely --- lib/Resizable/index.tsx | 6 +++--- stories/components/Resizable.stories.tsx | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Resizable/index.tsx b/lib/Resizable/index.tsx index c876693e3..22f7b7ab3 100644 --- a/lib/Resizable/index.tsx +++ b/lib/Resizable/index.tsx @@ -3,10 +3,9 @@ import React, { useCallback, useEffect, useRef } from "react"; import { keepValueWithinRange, toPixels } from "../helpers"; import { useLocalStorage } from "../hooks/useLocalStorage"; -const LOCALSTORAGE_KEY = "RESIZE_POSITION"; - export interface ResizableProps { containerWidth?: number | string; + id?: string; initialWidth?: number | string; minResizableWidth?: number | string; maxResizableWidth?: number | string; @@ -17,6 +16,7 @@ export interface ResizableProps { export function Resizable(props: PropsWithChildren) { const { children, + id, initialWidth = "50%", rememberPosition = false, useGutterOffset = false, @@ -40,7 +40,7 @@ export function Resizable(props: PropsWithChildren) { }); const [lastPosition, setLastPosition] = useLocalStorage( - LOCALSTORAGE_KEY, + `RESIZE_POSITION_${id ?? ""}`, toPixels(initialWidth) ); diff --git a/stories/components/Resizable.stories.tsx b/stories/components/Resizable.stories.tsx index bcc297755..e825a1850 100644 --- a/stories/components/Resizable.stories.tsx +++ b/stories/components/Resizable.stories.tsx @@ -30,9 +30,11 @@ export function Resizable() { }} > {/* eslint-disable-next-line no-use-before-define */} @@ -127,7 +129,7 @@ export function Resizable() { description="If you resize me, I'll remember where you left me when you refresh or return to the item - coz I'm nice like that!" >
- +

Move me - refresh the page - I should be where you left me!

From 1c6beb3d474178e4682ebe4d15a588210fb5abaf Mon Sep 17 00:00:00 2001 From: TimBryanDev Date: Tue, 26 Sep 2023 15:01:02 +0100 Subject: [PATCH 4/5] :recycle: remove superfluous calls to toPixels for initialWidth --- lib/Resizable/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Resizable/index.tsx b/lib/Resizable/index.tsx index 22f7b7ab3..8bfc43075 100644 --- a/lib/Resizable/index.tsx +++ b/lib/Resizable/index.tsx @@ -17,10 +17,10 @@ export function Resizable(props: PropsWithChildren) { const { children, id, - initialWidth = "50%", rememberPosition = false, useGutterOffset = false, } = props; + const initialWidth = toPixels(props.initialWidth ?? "50%"); const containerWidth: number = toPixels( props.containerWidth ?? document.body.offsetWidth ); @@ -41,10 +41,10 @@ export function Resizable(props: PropsWithChildren) { const [lastPosition, setLastPosition] = useLocalStorage( `RESIZE_POSITION_${id ?? ""}`, - toPixels(initialWidth) + initialWidth ); - const getWidth = () => toPixels(resizeWrapperRef.current?.style.width || 0); + const getWidth = () => toPixels(resizeWrapperRef.current?.style.width ?? 0); const setWidth = (value: number) => { if (resizeWrapperRef.current === null) return; @@ -101,7 +101,7 @@ export function Resizable(props: PropsWithChildren) { }; useEffect(() => { - setWidth(rememberPosition ? lastPosition : toPixels(initialWidth)); + setWidth(rememberPosition ? lastPosition : initialWidth); // remember to remove global listeners on dismount return () => stopDrag(); From 48478436fa162b35ba523216a7f1ce707d65e882 Mon Sep 17 00:00:00 2001 From: TimBryanDev Date: Fri, 29 Sep 2023 10:13:48 +0100 Subject: [PATCH 5/5] :recycle: rename and refactor out comments --- lib/hooks/useLocalStorage.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/hooks/useLocalStorage.ts b/lib/hooks/useLocalStorage.ts index a33da977d..504eb891a 100644 --- a/lib/hooks/useLocalStorage.ts +++ b/lib/hooks/useLocalStorage.ts @@ -3,17 +3,15 @@ import { useState } from "react"; export const useLocalStorage = (key: string, defaultValue: unknown) => { const [localStorageValue, setLocalStorageValue] = useState(() => { try { - // Try and retrieve any previously recorded values - const value = localStorage.getItem(key); - if (value) { - return JSON.parse(value); + const previousValue = localStorage.getItem(key); + + if (previousValue === null) { + localStorage.setItem(key, JSON.stringify(defaultValue)); + return defaultValue; } - // Else, record the defaultValue and return it instead - localStorage.setItem(key, JSON.stringify(defaultValue)); - return defaultValue; + return JSON.parse(previousValue); } catch (error) { - // Fallback to the defaultValue if we can't retrieve a previous value localStorage.setItem(key, JSON.stringify(defaultValue)); return defaultValue; }