From 8687ccd0288e36e9e007446187b912c104d5e45c Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 00:18:22 +0200 Subject: [PATCH 01/13] perf(context): split up context and memoize values and functions --- context/cookie/form.tsx | 45 ++++++---- context/cookie/index.tsx | 174 +++++++++++++-------------------------- context/cookie/menu.tsx | 51 ++++++++++++ context/dashboard.tsx | 10 +-- context/sidebar.tsx | 118 ++++++++++++++++++-------- 5 files changed, 222 insertions(+), 176 deletions(-) create mode 100644 context/cookie/menu.tsx diff --git a/context/cookie/form.tsx b/context/cookie/form.tsx index 3c5de8a..3ca7c74 100644 --- a/context/cookie/form.tsx +++ b/context/cookie/form.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ "use client"; -import { createContext, useContext, useEffect } from "react"; +import { createContext, useCallback, useContext, useEffect, useMemo } from "react"; import type { Control, FormState, @@ -25,7 +25,7 @@ import { useForm, useWatch } from "react-hook-form"; import { setCookie } from "@/lib/cookie/utils"; import type { CookieInputs } from "@/types/cookie"; -import { useCookieContext } from "."; +import { useCookieAPIContext, useCookieContext } from "."; export type CookieFormStore = { clearErrors: UseFormClearErrors; control: Control; @@ -67,29 +67,40 @@ export const useFormValues = () => { }; export default function CookieFormContextProvider({ children }: { children: React.ReactNode }) { - const cookieStore = useCookieContext(); + const { preferences, advertising, analytics } = useCookieContext(); + const { toggleSelectCookies } = useCookieAPIContext(); const form = useForm({ defaultValues: { necessary: true, - preferences: cookieStore.preferences, - advertising: cookieStore.advertising, - analytics: cookieStore.analytics, + preferences: preferences, + advertising: advertising, + analytics: analytics, }, }); - const onSubmit: SubmitHandler = (data) => { - setCookie(`1:${data.preferences ? 1 : 0}${data.analytics ? 1 : 0}${data.advertising ? 1 : 0}`); - cookieStore.toggleSelectCookies(data.preferences, data.analytics, data.advertising, true); - }; + const onSubmit: SubmitHandler = useCallback( + (data) => { + setCookie( + `1:${data.preferences ? 1 : 0}${data.analytics ? 1 : 0}${data.advertising ? 1 : 0}` + ); + toggleSelectCookies(data.preferences, data.analytics, data.advertising, true); + }, + [toggleSelectCookies] + ); useEffect(() => { - form.setValue("preferences", cookieStore.preferences); - form.setValue("analytics", cookieStore.analytics); - form.setValue("advertising", cookieStore.advertising); - }, [cookieStore.preferences, cookieStore.advertising, cookieStore.analytics]); + form.setValue("preferences", preferences); + form.setValue("analytics", analytics); + form.setValue("advertising", advertising); + }, [preferences, advertising, analytics]); + const data = useMemo( + () => ({ + ...form, + onSubmit, + }), + [form, onSubmit] + ); return ( <> - - {children} - + {children} ); } diff --git a/context/cookie/index.tsx b/context/cookie/index.tsx index 46cac10..3663d6c 100644 --- a/context/cookie/index.tsx +++ b/context/cookie/index.tsx @@ -1,17 +1,17 @@ "use client"; -import { useEffect, useState, createContext, useContext, useCallback } from "react"; +import { useEffect, createContext, useContext, useCallback, useReducer, useMemo } from "react"; import { getCookieConsentValue } from "@/lib/cookie/utils"; + export type CookieStore = { preferences: boolean; analytics: boolean; advertising: boolean; consent: boolean; - isChangeConsentMenuOpen: boolean; - isManageConsentMenuOpen: boolean; - toggleChangeConsentMenu: () => void; - toggleManageConsentMenu: () => void; +}; + +export type CookieAPIStore = { toggleSelectCookies: ( preferences: boolean, analytics: boolean, @@ -22,50 +22,35 @@ export type CookieStore = { denyAllCookies: () => void; }; -// type State = { -// preferences: boolean; -// analytics: boolean; -// advertising: boolean; -// consent: boolean; -// isChangeConsentMenuOpen: boolean; -// isManageConsentMenuOpen: boolean; -// }; - -// type Action = -// | { type: "TOGGLE_MENU"; menu: "isChangeConsentMenuOpen" | "isManageConsentMenuOpen" } -// | { type: "SET_COOKIES"; payload: Partial } -// | { type: "ACCEPT_ALL" } -// | { type: "DENY_ALL" }; - -// const cookieReducer = (state: State, action: Action) => { -// switch (action.type) { -// case "TOGGLE_MENU": -// return { ...state, [action.menu]: !state[action.menu] }; -// case "SET_COOKIES": -// return { ...state, ...action.payload }; -// case "ACCEPT_ALL": -// return { ...state, preferences: true, analytics: true, advertising: true, consent: true }; -// case "DENY_ALL": -// return { ...state, preferences: false, analytics: false, advertising: false, consent: false }; -// default: -// return state; -// } -// }; - -// function init() { -// const initValues = getCookieConsentValue(); -// return { -// preferences: initValues.preferences, -// analytics: initValues.analytics, -// advertising: initValues.advertising, -// consent: initValues.consent, -// isChangeConsentMenuOpen: false, -// isManageConsentMenuOpen: false, -// }; -// } +type State = { + preferences: boolean; + analytics: boolean; + advertising: boolean; + consent: boolean; +}; + +type Action = + | { type: "SET_COOKIES"; payload: Partial } + | { type: "ACCEPT_ALL" } + | { type: "DENY_ALL" }; + +const cookieReducer = (state: State, action: Action) => { + switch (action.type) { + case "SET_COOKIES": + return { ...state, ...action.payload }; + case "ACCEPT_ALL": + return { ...state, preferences: true, analytics: true, advertising: true, consent: true }; + case "DENY_ALL": + return { ...state, preferences: false, analytics: false, advertising: false, consent: false }; + default: + return state; + } +}; export const CookieContext = createContext(undefined); +export const CookieAPIContext = createContext(undefined); + export function useCookieContext() { const cookieStore = useContext(CookieContext); @@ -76,86 +61,45 @@ export function useCookieContext() { return cookieStore; } +export function useCookieAPIContext() { + const cookieAPIStore = useContext(CookieAPIContext); + + if (cookieAPIStore === undefined) { + throw new Error("useCookieContext must be used with a CookieContext provider"); + } + + return cookieAPIStore; +} + export default function CookieContextProvider({ children }: { children: React.ReactNode }) { - const [cookieSettings, setCookieSettings] = useState({ + const [cookieSettings, dispatch] = useReducer(cookieReducer, { preferences: false, analytics: false, advertising: false, consent: false, - isChangeConsentMenuOpen: false, - isManageConsentMenuOpen: false, }); - const toggleChangeConsentMenu = useCallback(() => { - setCookieSettings((prev) => ({ - ...prev, - isChangeConsentMenuOpen: !prev.isChangeConsentMenuOpen, - })); - }, []); - const toggleManageConsentMenu = useCallback(() => { - setCookieSettings((prev) => ({ - ...prev, - isManageConsentMenuOpen: !prev.isManageConsentMenuOpen, - })); - }, []); - const toggleSelectCookies = ( - preferences: boolean, - analytics: boolean, - advertising: boolean, - consent: boolean - ) => { - setCookieSettings({ - ...cookieSettings, - preferences, - analytics, - advertising, - consent, - }); - }; - const acceptAllCookies = useCallback(() => { - toggleSelectCookies(true, true, true, true); - }, []); - const denyAllCookies = useCallback(() => { - toggleSelectCookies(false, false, false, true); - }, []); + const toggleSelectCookies = useCallback( + (preferences: boolean, analytics: boolean, advertising: boolean, consent: boolean) => + dispatch({ type: "SET_COOKIES", payload: { preferences, analytics, advertising, consent } }), + [] + ); + const acceptAllCookies = useCallback(() => dispatch({ type: "ACCEPT_ALL" }), []); + const denyAllCookies = useCallback(() => dispatch({ type: "DENY_ALL" }), []); + useEffect(() => { const { preferences, advertising, analytics, consent } = getCookieConsentValue(); - setCookieSettings((prev) => ({ - ...prev, - preferences: preferences, - advertising: advertising, - analytics: analytics, - consent: consent, - })); + toggleSelectCookies(preferences, advertising, analytics, consent); }, []); - // const funcValue = useMemo( - // () => ({ - // toggleChangeConsentMenu, - // toggleManageConsentMenu, - // toggleSelectCookies, - // acceptAllCookies, - // denyAllCookies, - // }), - // [ - // toggleChangeConsentMenu, - // toggleManageConsentMenu, - // toggleSelectCookies, - // acceptAllCookies, - // denyAllCookies, - // ] - // ); + + const data = useMemo(() => ({ ...cookieSettings }), [cookieSettings]); + const api = useMemo( + () => ({ toggleSelectCookies, acceptAllCookies, denyAllCookies }), + [acceptAllCookies, toggleSelectCookies, denyAllCookies] + ); return ( <> - - {children} + + {children} ); diff --git a/context/cookie/menu.tsx b/context/cookie/menu.tsx new file mode 100644 index 0000000..1815a2e --- /dev/null +++ b/context/cookie/menu.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; +import { createContext, useContext, useCallback, useMemo, useRef } from "react"; + +export type CookieMenuAPIStore = { + toggleManageConsentMenu: () => void; + dialogRef: React.RefObject; +}; + +export const CookieMenuAPIContext = createContext(undefined); + +export function useCookieMenuAPIContext() { + const cookieMenuAPIStore = useContext(CookieMenuAPIContext); + + if (cookieMenuAPIStore === undefined) { + throw new Error("useCookieMenuAPIContext must be used with a CookieContext provider"); + } + + return cookieMenuAPIStore; +} + +export default function CookieMenuContextProvider({ children }: { children: React.ReactNode }) { + const dialogRef = useRef(null); + + const toggleManageConsentMenu = useCallback(() => { + if (!dialogRef.current) { + return; + } + if (dialogRef.current.hasAttribute("open")) { + dialogRef.current.close(); + enableBodyScroll(dialogRef.current); + return; + } + disableBodyScroll(dialogRef.current); + dialogRef.current.showModal(); + }, []); + + const api = useMemo( + () => ({ + toggleManageConsentMenu, + dialogRef, + }), + [toggleManageConsentMenu] + ); + return ( + <> + {children} + + ); +} diff --git a/context/dashboard.tsx b/context/dashboard.tsx index 95b89e2..df9a5d0 100644 --- a/context/dashboard.tsx +++ b/context/dashboard.tsx @@ -1,13 +1,7 @@ "use client"; -import { - type Dispatch, - type SetStateAction, - createContext, - useContext, - useEffect, - useState, -} from "react"; +import type { Dispatch, SetStateAction } from "react"; +import { createContext, useContext, useEffect, useState } from "react"; type DashboardStore = { isExpanded: boolean; diff --git a/context/sidebar.tsx b/context/sidebar.tsx index a57a905..ec240c4 100644 --- a/context/sidebar.tsx +++ b/context/sidebar.tsx @@ -1,34 +1,71 @@ "use client"; import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; -import { - type Dispatch, - type RefObject, - type SetStateAction, - createContext, - useContext, - useRef, - useState, -} from "react"; - -type SidebarStore = { - isExpanded: boolean; - setIsExpanded: Dispatch>; +import type { Dispatch, RefObject, SetStateAction } from "react"; +import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react"; + +type SidebarRefStore = { + mainMenuBtnRef: RefObject; + sidebarRef: RefObject; +}; + +type HeaderTopStore = { isAtTop: boolean; +}; + +type SidebarExpanded = { + isExpanded: boolean; +}; + +type HeaderAPIStore = { setIsAtTop: Dispatch>; toggle: () => void; - mainMenuBtnRef: RefObject; - sidebarRef: RefObject; }; -export const SidebarContext = createContext(undefined); -export function useSidebarContext() { - const sidebarStore = useContext(SidebarContext); +export const HeaderTopContext = createContext(undefined); +export const SidebarExpandedContext = createContext(undefined); +export const SidebarRefContext = createContext(undefined); +export const HeaderAPIContext = createContext(undefined); + +export function useHeaderAPIContext() { + const headerAPIStore = useContext(HeaderAPIContext); + + if (headerAPIStore === undefined) { + throw new Error("useHeaderAPIContext must be used with a HeaderAPIContext provider"); + } + + return headerAPIStore; +} + +export function useSidebarRefContext() { + const sidebarRefStore = useContext(SidebarRefContext); + + if (sidebarRefStore === undefined) { + throw new Error("useSidebarRefContext must be used with a SidebarRefContext provider"); + } + + return sidebarRefStore; +} + +export function useSidebarExpandedContext() { + const sidebarExpandedStore = useContext(SidebarExpandedContext); - if (sidebarStore === undefined) { - throw new Error("useSidebarContext must be used with a SidebarContext provider"); + if (sidebarExpandedStore === undefined) { + throw new Error( + "useSidebarExpandedContext must be used with a SidebarExpandedContext provider" + ); } - return sidebarStore; + return sidebarExpandedStore; +} + +export function useHeaderTopContext() { + const headerTopStore = useContext(HeaderTopContext); + + if (headerTopStore === undefined) { + throw new Error("useHeaderTopContext must be used with a HeaderTopContext provider"); + } + + return headerTopStore; } export default function SidebarContextProvider({ children }: { children: React.ReactNode }) { @@ -36,7 +73,7 @@ export default function SidebarContextProvider({ children }: { children: React.R const [isAtTop, setIsAtTop] = useState(true); const mainMenuBtnRef = useRef(null); const sidebarRef = useRef(null); - const toggle = () => { + const toggle = useCallback(() => { setIsExpanded(!isExpanded); if (sidebarRef.current) { if (!isExpanded) { @@ -45,22 +82,31 @@ export default function SidebarContextProvider({ children }: { children: React.R } enableBodyScroll(sidebarRef.current); } - }; + }, [isExpanded]); + + const api = useMemo( + () => ({ + setIsAtTop, + toggle, + }), + [toggle] + ); + const refs = useMemo( + () => ({ + mainMenuBtnRef, + sidebarRef, + }), + [] + ); return ( <> - - {children} - + + + + {children} + + + ); } From 396b3d2656cf7c6763a83a5e0f8201047ec0d0a3 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 00:21:54 +0200 Subject: [PATCH 02/13] build: add scrol area shadcn component --- components/ui/scroll-area.tsx | 46 ++++++++++++++++++ package.json | 2 + pnpm-lock.yaml | 92 +++++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 components/ui/scroll-area.tsx diff --git a/components/ui/scroll-area.tsx b/components/ui/scroll-area.tsx new file mode 100644 index 0000000..79019f6 --- /dev/null +++ b/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +"use client"; + +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)); +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName; + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)); +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName; + +export { ScrollArea, ScrollBar }; diff --git a/package.json b/package.json index cba1eb6..a25c42e 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4", @@ -82,6 +83,7 @@ "react-hook-form": "7.51.3", "react-icons": "^5.0.1", "react-intersection-observer": "^9.8.2", + "server-only": "^0.0.1", "tailwind-merge": "^2.2.2", "tailwindcss": "^3.4.3", "tailwindcss-animate": "^1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79b3651..ba283ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ dependencies: '@radix-ui/react-progress': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-scroll-area': + specifier: ^1.0.5 + version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-separator': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) @@ -146,6 +149,9 @@ dependencies: react-intersection-observer: specifier: ^9.8.2 version: 9.8.2(react-dom@18.2.0)(react@18.2.0) + server-only: + specifier: ^0.0.1 + version: 0.0.1 tailwind-merge: specifier: ^2.2.2 version: 2.2.2 @@ -1600,7 +1606,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 - dev: true /@babel/template@7.24.0: resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} @@ -2806,10 +2811,16 @@ packages: config-chain: 1.1.13 dev: true + /@radix-ui/number@1.0.1: + resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} + dependencies: + '@babel/runtime': 7.24.5 + dev: false + /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 dev: false /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0): @@ -2825,7 +2836,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.3.1 '@types/react-dom': 18.3.0 @@ -2898,7 +2909,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) @@ -2918,7 +2929,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 @@ -2931,7 +2942,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 dev: false @@ -2979,7 +2990,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 dev: false @@ -2997,7 +3008,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) @@ -3045,7 +3056,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 dev: false @@ -3063,7 +3074,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.2.0) @@ -3082,7 +3093,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@types/react': 18.3.1 react: 18.2.0 @@ -3122,7 +3133,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) @@ -3194,7 +3205,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) @@ -3224,7 +3235,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.3.1 '@types/react-dom': 18.3.0 @@ -3245,7 +3256,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@types/react': 18.3.1 @@ -3267,7 +3278,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.2.0) '@types/react': 18.3.1 '@types/react-dom': 18.3.0 @@ -3310,7 +3321,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) @@ -3326,6 +3337,35 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-scroll-area@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-b6PAgH4GQf9QEn8zbT2XUHpW5z8BzqEc7Kl11TwDrvuTrxlkcjTD5qa/bxgKr+nmuXKu4L/W5UZ4mlP/VG/5Gw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.5 + '@radix-ui/number': 1.0.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.3.1)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.3.1)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.2.0) + '@types/react': 18.3.1 + '@types/react-dom': 18.3.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} peerDependencies: @@ -3430,7 +3470,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 dev: false @@ -3444,7 +3484,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@types/react': 18.3.1 react: 18.2.0 @@ -3459,7 +3499,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@types/react': 18.3.1 react: 18.2.0 @@ -3474,7 +3514,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 dev: false @@ -3488,7 +3528,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@types/react': 18.3.1 react: 18.2.0 dev: false @@ -3502,7 +3542,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/rect': 1.0.1 '@types/react': 18.3.1 react: 18.2.0 @@ -3517,7 +3557,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.1)(react@18.2.0) '@types/react': 18.3.1 react: 18.2.0 @@ -3536,7 +3576,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.3.1 '@types/react-dom': 18.3.0 @@ -3547,7 +3587,7 @@ packages: /@radix-ui/rect@1.0.1: resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.5 dev: false /@rushstack/eslint-patch@1.10.1: From d9ce9b2bc14410d17c514bffd383e2082b634515 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 00:24:14 +0200 Subject: [PATCH 03/13] perf(cookie): manage rsc and client cmp, use native dialog, memoize --- components/cookies/manage/buttons.tsx | 36 +++++-- components/cookies/manage/controllers.tsx | 14 ++- components/cookies/manage/dialog.tsx | 30 ++++++ components/cookies/manage/index.tsx | 109 +++++++++++++++++----- components/cookies/manage/tabNav.tsx | 102 -------------------- components/cookies/manage/wrapper.tsx | 24 +++++ 6 files changed, 173 insertions(+), 142 deletions(-) create mode 100644 components/cookies/manage/dialog.tsx delete mode 100644 components/cookies/manage/tabNav.tsx create mode 100644 components/cookies/manage/wrapper.tsx diff --git a/components/cookies/manage/buttons.tsx b/components/cookies/manage/buttons.tsx index a2a886e..62b06cc 100644 --- a/components/cookies/manage/buttons.tsx +++ b/components/cookies/manage/buttons.tsx @@ -1,23 +1,25 @@ "use client"; +import { memo } from "react"; +import { FaWindowClose } from "react-icons/fa"; + import { Button } from "@/components/ui/button"; -import { useCookieContext } from "@/context/cookie"; import { useCookieFormContext } from "@/context/cookie/form"; +import { useCookieMenuAPIContext } from "@/context/cookie/menu"; export function DenyAll() { - const cookieStore = useCookieContext(); + const { toggleManageConsentMenu } = useCookieMenuAPIContext(); const { setValue } = useCookieFormContext(); return ( <> + + ); +}); diff --git a/components/cookies/manage/controllers.tsx b/components/cookies/manage/controllers.tsx index 0ac2082..47b1a49 100644 --- a/components/cookies/manage/controllers.tsx +++ b/components/cookies/manage/controllers.tsx @@ -1,5 +1,5 @@ "use client"; -import React from "react"; +import React, { memo } from "react"; import type { Path } from "react-hook-form"; import { Controller } from "react-hook-form"; @@ -7,7 +7,11 @@ import { useCookieFormContext } from "@/context/cookie/form"; import { cn } from "@/lib/utils"; import type { CookieInputs } from "@/types/cookie"; -export function ConsentTabController({ name }: { name: Path }) { +export const ConsentTabController = memo(function ConsentTabController({ + name, +}: { + name: Path; +}) { const { register, control, formState } = useCookieFormContext(); return ( @@ -54,9 +58,9 @@ export function ConsentTabController({ name }: { name: Path }) { /> ); -} +}); -export function DetailsTabController({ +export const DetailsTabController = memo(function DetailsTabController({ name, children, amount, @@ -135,4 +139,4 @@ export function DetailsTabController({ /> ); -} +}); diff --git a/components/cookies/manage/dialog.tsx b/components/cookies/manage/dialog.tsx new file mode 100644 index 0000000..67c7c8d --- /dev/null +++ b/components/cookies/manage/dialog.tsx @@ -0,0 +1,30 @@ +import { forwardRef } from "react"; + +import { cn } from "@/lib/utils"; + +import { CloseModal } from "./buttons"; + +type Props = { + children: React.ReactNode; +}; + +const Dialog = forwardRef(function Dialog({ children }, ref) { + return ( + + {children} + + + ); +}); +export default Dialog; diff --git a/components/cookies/manage/index.tsx b/components/cookies/manage/index.tsx index 68f1611..07ddbc4 100644 --- a/components/cookies/manage/index.tsx +++ b/components/cookies/manage/index.tsx @@ -1,32 +1,91 @@ -"use client"; -import TabNav from "@/components/cookies/manage/tabNav"; -import { useCookieContext } from "@/context/cookie"; -import { useCookieFormContext } from "@/context/cookie/form"; -import { cn } from "@/lib/utils"; +import Logo from "@/components/logo"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -export default function ManageWrapper() { - const cookie = useCookieContext(); - const { handleSubmit, onSubmit } = useCookieFormContext(); +import { AcceptAll, AcceptSelection, DenyAll } from "./buttons"; +import { ConsentTabController, DetailsTabController } from "./controllers"; +import ManageWrapper from "./wrapper"; + +export default function ManageConsent() { return ( <> -
-
- + + + + + + Consent + Details + About + + +
+

This website uses cookies

+

+ We use cookies to personalize content and marketing, deliver functionality through + social media, och for analyzing traffic. We share, with your consent, information + to our partners in social media, marketing, and analytics. They can then use this + data in combination with other data that you hae either shared or that they have + collected through your usage of their services. +

+
+ + + + +
+
+
+ +
+ + Necessary cookies help the website function. This includes navigation, and access + to secure areas of the site. This website will not function without these cookies. + + + Preference cookies are for remembering information that changes the way that the + website works or its appearance. This is primarily for preferences such as + language, theme, and more. + + + Analytics cookies help the owners of this website to understand what the visitors + are interacting with on the website. It also collects information about the + behavior of the users while preserving their anonimity. + + + Marketing cookies track the user's activity across the website. This helps + the owners of the website to recommend better content to the user. + +
+
+ +

What are cookies?

+

+ Cookies are small text files that are used by websites to improve the user + experience. +

+ Laws and guidelines such as GDPR allow us to store cookies on this device if they + are strictly necessary for the website to function. For all other cookies we will + need your explicit consent. +

+ This website uses only necessary cookies for now. However, we may later store more + cookies that help us improve the experience for our users. +

+ You can at any time withdraw your consent to all non-necessary cookies. +

+ Learn more about us, how you can contact us, and how we handle your personal data in + our Privacy Policy. +

+
+
+
+
+
+ + +
-
+ ); } diff --git a/components/cookies/manage/tabNav.tsx b/components/cookies/manage/tabNav.tsx deleted file mode 100644 index 1f5fa68..0000000 --- a/components/cookies/manage/tabNav.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import type { SubmitHandler, UseFormHandleSubmit } from "react-hook-form"; - -import Logo from "@/components/logo"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import type { CookieInputs } from "@/types/cookie"; - -import { AcceptAll, AcceptSelection, DenyAll } from "./buttons"; -import { ConsentTabController, DetailsTabController } from "./controllers"; - -export default function TabNav({ - handleSubmit, - onSubmit, -}: { - handleSubmit: UseFormHandleSubmit; - onSubmit: SubmitHandler; -}) { - return ( - <> -
- - - - Consent - Details - About - - -
-

This website uses cookies

-

- We use cookies to personalize content and marketing, deliver functionality through - social media, och for analyzing traffic. We share, with your consent, information to - our partners in social media, marketing, and analytics. They can then use this data - in combination with other data that you hae either shared or that they have - collected through your usage of their services. -

-
- - - - -
-
-
- -
- - Necessary cookies help the website function. This includes navigation, and access to - secure areas of the site. This website will not function without these cookies. - - - Preference cookies are for remembering information that changes the way that the - website works or its appearance. This is primarily for preferences such as language, - theme, and more. - - - Analytics cookies help the owners of this website to understand what the visitors - are interacting with on the website. It also collects information about the behavior - of the users while preserving their anonimity. - - - Marketing cookies track the user's activity across the website. This helps the - owners of the website to recommend better content to the user. - -
-
- -

What are cookies?

-

- Cookies are small text files that are used by websites to improve the user experience. -

- Laws and guidelines such as GDPR allow us to store cookies on this device if they are - strictly necessary for the website to function. For all other cookies we will need - your explicit consent. -

- This website uses only necessary cookies for now. However, we may later store more - cookies that help us improve the experience for our users. -

- You can at any time withdraw your consent to all non-necessary cookies. -

- Learn more about us, how you can contact us, and how we handle your personal data in - our Privacy Policy. -

-
-
-
-
- - - -
- - - ); -} diff --git a/components/cookies/manage/wrapper.tsx b/components/cookies/manage/wrapper.tsx new file mode 100644 index 0000000..69db2da --- /dev/null +++ b/components/cookies/manage/wrapper.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { useCookieFormContext } from "@/context/cookie/form"; +import { useCookieMenuAPIContext } from "@/context/cookie/menu"; + +import Dialog from "./dialog"; + +export default function ManageWrapper({ children }: { children: React.ReactNode }) { + const { dialogRef } = useCookieMenuAPIContext(); + const { handleSubmit, onSubmit } = useCookieFormContext(); + return ( + <> + +
+ {children} +
+
+ + ); +} From 4fedd09f6b2ca97ccdbdc01d683937d0b434afe1 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 00:26:58 +0200 Subject: [PATCH 04/13] perf: remove changeMenu, perf improe toast, a11y --- components/cookies/changeMenu.tsx | 103 --------------------------- components/cookies/index.tsx | 17 ++--- components/cookies/toast.tsx | 70 ------------------ components/cookies/toast/buttons.tsx | 80 +++++++++++++++++++++ components/cookies/toast/index.tsx | 22 ++++++ components/cookies/toast/wrapper.tsx | 32 +++++++++ 6 files changed, 143 insertions(+), 181 deletions(-) delete mode 100644 components/cookies/changeMenu.tsx delete mode 100644 components/cookies/toast.tsx create mode 100644 components/cookies/toast/buttons.tsx create mode 100644 components/cookies/toast/index.tsx create mode 100644 components/cookies/toast/wrapper.tsx diff --git a/components/cookies/changeMenu.tsx b/components/cookies/changeMenu.tsx deleted file mode 100644 index 29d3a08..0000000 --- a/components/cookies/changeMenu.tsx +++ /dev/null @@ -1,103 +0,0 @@ -"use client"; -import { FaWindowClose } from "react-icons/fa"; -import { FaCheck, FaLock } from "react-icons/fa6"; -import { MdClose } from "react-icons/md"; - -import { Button } from "@/components/ui/button"; -import { useCookieContext } from "@/context/cookie"; -import { resetCookieConsentValue } from "@/lib/cookie/utils"; -import { cn } from "@/lib/utils"; - -export default function ChangeMenu() { - const cookie = useCookieContext(); - return ( - <> -
-
-
- Cookies settings - -
-
- Current Consent -
    -
  • -
    - -
    - Necessary -
  • -
  • -
    -
    - - -
    -
    - Preferences -
  • -
  • -
    -
    - - -
    -
    - Analytics -
  • -
  • -
    -
    - - -
    -
    - Advertising -
  • -
-
-
- - -
-
-
- - ); -} diff --git a/components/cookies/index.tsx b/components/cookies/index.tsx index 37c5dbb..1d0f421 100644 --- a/components/cookies/index.tsx +++ b/components/cookies/index.tsx @@ -1,20 +1,21 @@ +import CookieContextProvider from "@/context/cookie"; import CookieFormContextProvider from "@/context/cookie/form"; import { cn } from "@/lib/utils"; -import ChangeMenu from "./changeMenu"; import ManageConsent from "./manage"; import CookieToast from "./toast"; export default function CookieTray() { return ( <> -
- - - - - -
+ +
+ + + + +
+
); } diff --git a/components/cookies/toast.tsx b/components/cookies/toast.tsx deleted file mode 100644 index 132d6ba..0000000 --- a/components/cookies/toast.tsx +++ /dev/null @@ -1,70 +0,0 @@ -"use client"; -import { useEffect, useState } from "react"; - -import { Button } from "@/components/ui/button"; -import { useCookieContext } from "@/context/cookie"; -import { env } from "@/env/client"; -import { setCookie } from "@/lib/cookie/utils"; -import { cn } from "@/lib/utils"; - -export default function CookieToast() { - const cookie = useCookieContext(); - const [initialVisible, setInitialVisible] = useState(false); - useEffect(() => { - setTimeout(() => { - setInitialVisible(true); - }, 500); - }); - return ( - <> -
-

- {env.NEXT_PUBLIC_SITE_NAME} uses cookies to improve the website and your user experience, - read more in the{" "} - - Privacy Policy - - . -

-
- - - -
-
- - ); -} diff --git a/components/cookies/toast/buttons.tsx b/components/cookies/toast/buttons.tsx new file mode 100644 index 0000000..a29f06f --- /dev/null +++ b/components/cookies/toast/buttons.tsx @@ -0,0 +1,80 @@ +"use client"; +import { memo } from "react"; + +import { Button } from "@/components/ui/button"; +import { useCookieAPIContext, useCookieContext } from "@/context/cookie"; +import { useCookieMenuAPIContext } from "@/context/cookie/menu"; +import { setCookie } from "@/lib/cookie/utils"; + +export const ManageCookies = memo(function ManageCookies() { + const { toggleManageConsentMenu } = useCookieMenuAPIContext(); + const { consent } = useCookieContext(); + return ( + <> + + + ); +}); + +export const DenyAll = memo(function DenyAll() { + const { toggleSelectCookies } = useCookieAPIContext(); + const { consent } = useCookieContext(); + return ( + <> + + + ); +}); + +export const AcceptAll = memo(function AcceptAll() { + const { toggleSelectCookies } = useCookieAPIContext(); + const { consent } = useCookieContext(); + return ( + <> + + + ); +}); + +export const PrivacyPolicy = memo(function PrivacyPolicy() { + const { consent } = useCookieContext(); + return ( + <> + + Privacy Policy + + + ); +}); diff --git a/components/cookies/toast/index.tsx b/components/cookies/toast/index.tsx new file mode 100644 index 0000000..0b6d102 --- /dev/null +++ b/components/cookies/toast/index.tsx @@ -0,0 +1,22 @@ +import { env } from "@/env/client"; + +import { AcceptAll, DenyAll, ManageCookies, PrivacyPolicy } from "./buttons"; +import Wrapper from "./wrapper"; + +export default function CookieToast() { + return ( + <> + +

+ {env.NEXT_PUBLIC_SITE_NAME} uses cookies to improve the website and your user experience, + read more in the . +

+
+ + + +
+
+ + ); +} diff --git a/components/cookies/toast/wrapper.tsx b/components/cookies/toast/wrapper.tsx new file mode 100644 index 0000000..3fa87f8 --- /dev/null +++ b/components/cookies/toast/wrapper.tsx @@ -0,0 +1,32 @@ +"use client"; +import { useEffect, useState } from "react"; + +import { useCookieContext } from "@/context/cookie"; +import { cn } from "@/lib/utils"; + +export default function Wrapper({ children }: { children: React.ReactNode }) { + const { consent } = useCookieContext(); + const [initialVisible, setInitialVisible] = useState(false); + useEffect(() => { + setTimeout(() => { + setInitialVisible(true); + }, 500); + }); + return ( + <> +
+ {children} +
+ + ); +} From f68b3378c4590acc4e09f077f0fcff4cb1134211 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 00:29:39 +0200 Subject: [PATCH 05/13] perf: swap out context in footer for fewer rerenders --- components/footer/btnCookie.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/footer/btnCookie.tsx b/components/footer/btnCookie.tsx index 3529fb1..0d0ba2c 100644 --- a/components/footer/btnCookie.tsx +++ b/components/footer/btnCookie.tsx @@ -2,16 +2,15 @@ import { memo } from "react"; import { Button } from "@/components/ui/button"; -import { useCookieContext } from "@/context/cookie"; +import { useCookieMenuAPIContext } from "@/context/cookie/menu"; function ButtonCookie() { - const { toggleChangeConsentMenu } = useCookieContext(); + const { toggleManageConsentMenu } = useCookieMenuAPIContext(); return ( <> ); diff --git a/components/header/util/wrapper.tsx b/components/header/util/wrapper.tsx index ca2aa86..5400585 100644 --- a/components/header/util/wrapper.tsx +++ b/components/header/util/wrapper.tsx @@ -2,11 +2,11 @@ import { useEffect } from "react"; import { useInView } from "react-intersection-observer"; -import { useSidebarContext } from "@/context/sidebar"; +import { useHeaderAPIContext } from "@/context/sidebar"; import { cn } from "@/lib/utils"; export default function HeaderWrapper({ children }: { children: React.ReactNode }) { - const { setIsAtTop } = useSidebarContext(); + const { setIsAtTop } = useHeaderAPIContext(); const { ref, inView } = useInView({ threshold: 0, initialInView: true, @@ -29,15 +29,7 @@ export default function HeaderWrapper({ children }: { children: React.ReactNode inView === true || undefined || null ? "" : "scrolled" )} > -
- {children} -
+ {children} ); From a99e1bae91078251f65a7be6c8ba82f3ec77b4cb Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 13:39:22 +0200 Subject: [PATCH 08/13] perf(header): remove unnecessary stylistic mounting logic Header looks perfectly fine without the animation. --- components/header/index.tsx | 31 ++++++++++++++++++++----- components/header/logo.tsx | 26 --------------------- components/header/util/wrapper.tsx | 36 ------------------------------ 3 files changed, 26 insertions(+), 67 deletions(-) delete mode 100644 components/header/logo.tsx delete mode 100644 components/header/util/wrapper.tsx diff --git a/components/header/index.tsx b/components/header/index.tsx index 92afcf8..8c26c3a 100644 --- a/components/header/index.tsx +++ b/components/header/index.tsx @@ -1,18 +1,27 @@ +import BaseLogo from "@/components/logo"; import SidebarContextProvider from "@/context/sidebar"; +import { env } from "@/env/client"; import { cn } from "@/lib/utils"; -import Logo from "./logo"; import Nav from "./menu"; import Sidebar from "./sidebar"; import JumpToContent from "./util/jumpToContent"; -import HeaderWrapper from "./util/wrapper"; export default function Header() { return ( <> - +
- +
); diff --git a/components/header/logo.tsx b/components/header/logo.tsx deleted file mode 100644 index 003b37e..0000000 --- a/components/header/logo.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; -import BaseLogo from "@/components/logo"; -import { useSidebarExpandedContext } from "@/context/sidebar"; -import { env } from "@/env/client"; -import { cn } from "@/lib/utils"; - -export default function Logo() { - const { isExpanded } = useSidebarExpandedContext(); - return ( - <> - - - / - {env.NEXT_PUBLIC_SITE_NAME} - - - ); -} diff --git a/components/header/util/wrapper.tsx b/components/header/util/wrapper.tsx deleted file mode 100644 index 5400585..0000000 --- a/components/header/util/wrapper.tsx +++ /dev/null @@ -1,36 +0,0 @@ -"use client"; -import { useEffect } from "react"; -import { useInView } from "react-intersection-observer"; - -import { useHeaderAPIContext } from "@/context/sidebar"; -import { cn } from "@/lib/utils"; - -export default function HeaderWrapper({ children }: { children: React.ReactNode }) { - const { setIsAtTop } = useHeaderAPIContext(); - const { ref, inView } = useInView({ - threshold: 0, - initialInView: true, - fallbackInView: false, - }); - - useEffect(() => { - setIsAtTop(inView); - }, [inView, setIsAtTop]); - return ( - <> -
-
- {children} -
- - ); -} From b965d2d67e84a27ce62bbe76dc2ff6e726555ced Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 13:46:18 +0200 Subject: [PATCH 09/13] perf(sidebar): remove unnecessary context and swap sidebar to dialog --- components/header/sidebar/index.tsx | 3 +- components/header/sidebar/util/backdrop.tsx | 12 +-- .../header/sidebar/util/innerWrapper.tsx | 36 +++------ context/sidebar.tsx | 77 +++++-------------- 4 files changed, 40 insertions(+), 88 deletions(-) diff --git a/components/header/sidebar/index.tsx b/components/header/sidebar/index.tsx index 40e3e3c..9ab185d 100644 --- a/components/header/sidebar/index.tsx +++ b/components/header/sidebar/index.tsx @@ -9,12 +9,13 @@ export default function Sidebar() { <>
- + {/* */}
); diff --git a/components/header/sidebar/util/backdrop.tsx b/components/header/sidebar/util/backdrop.tsx index b20f492..d74b4de 100644 --- a/components/header/sidebar/util/backdrop.tsx +++ b/components/header/sidebar/util/backdrop.tsx @@ -1,18 +1,18 @@ "use client"; -import { useHeaderAPIContext, useSidebarExpandedContext } from "@/context/sidebar"; +import { useHeaderAPIContext } from "@/context/sidebar"; import { cn } from "@/lib/utils"; export default function BackDrop() { - const { isExpanded } = useSidebarExpandedContext(); - const { toggle } = useHeaderAPIContext(); + const { toggleSidebarDialog } = useHeaderAPIContext(); return ( <>
); diff --git a/components/header/sidebar/util/innerWrapper.tsx b/components/header/sidebar/util/innerWrapper.tsx index 4d8d49a..7da6f91 100644 --- a/components/header/sidebar/util/innerWrapper.tsx +++ b/components/header/sidebar/util/innerWrapper.tsx @@ -1,44 +1,29 @@ "use client"; import { useState } from "react"; -import { - useHeaderAPIContext, - useHeaderTopContext, - useSidebarExpandedContext, - useSidebarRefContext, -} from "@/context/sidebar"; +import { useHeaderAPIContext, useSidebarRefContext } from "@/context/sidebar"; import { cn } from "@/lib/utils"; export default function InnerWrapper({ children }: { children: React.ReactNode }) { - const { isExpanded } = useSidebarExpandedContext(); - const { toggle } = useHeaderAPIContext(); - const { mainMenuBtnRef, sidebarRef } = useSidebarRefContext(); - const { isAtTop } = useHeaderTopContext(); + const { toggleSidebarDialog } = useHeaderAPIContext(); + const { sidebarRef } = useSidebarRefContext(); const [touchStart, setTouchStart] = useState(0); const [touchEnd, setTouchEnd] = useState(0); //127 - 72 = 55 / 2 = 22 - 23 return ( <> - + ); } diff --git a/context/sidebar.tsx b/context/sidebar.tsx index ec240c4..007c69c 100644 --- a/context/sidebar.tsx +++ b/context/sidebar.tsx @@ -1,15 +1,10 @@ "use client"; import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; -import type { Dispatch, RefObject, SetStateAction } from "react"; -import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react"; +import type { RefObject } from "react"; +import { createContext, useCallback, useContext, useMemo, useRef } from "react"; type SidebarRefStore = { - mainMenuBtnRef: RefObject; - sidebarRef: RefObject; -}; - -type HeaderTopStore = { - isAtTop: boolean; + sidebarRef: RefObject; }; type SidebarExpanded = { @@ -17,11 +12,9 @@ type SidebarExpanded = { }; type HeaderAPIStore = { - setIsAtTop: Dispatch>; - toggle: () => void; + toggleSidebarDialog: () => void; }; -export const HeaderTopContext = createContext(undefined); export const SidebarExpandedContext = createContext(undefined); export const SidebarRefContext = createContext(undefined); export const HeaderAPIContext = createContext(undefined); @@ -46,67 +39,39 @@ export function useSidebarRefContext() { return sidebarRefStore; } -export function useSidebarExpandedContext() { - const sidebarExpandedStore = useContext(SidebarExpandedContext); - - if (sidebarExpandedStore === undefined) { - throw new Error( - "useSidebarExpandedContext must be used with a SidebarExpandedContext provider" - ); - } - - return sidebarExpandedStore; -} - -export function useHeaderTopContext() { - const headerTopStore = useContext(HeaderTopContext); - - if (headerTopStore === undefined) { - throw new Error("useHeaderTopContext must be used with a HeaderTopContext provider"); - } - - return headerTopStore; -} - export default function SidebarContextProvider({ children }: { children: React.ReactNode }) { - const [isExpanded, setIsExpanded] = useState(false); - const [isAtTop, setIsAtTop] = useState(true); - const mainMenuBtnRef = useRef(null); - const sidebarRef = useRef(null); - const toggle = useCallback(() => { - setIsExpanded(!isExpanded); - if (sidebarRef.current) { - if (!isExpanded) { - disableBodyScroll(sidebarRef.current); - return; - } + const sidebarRef = useRef(null); + + const toggleSidebarDialog = useCallback(() => { + if (!sidebarRef.current) { + return; + } + if (sidebarRef.current.hasAttribute("open")) { + sidebarRef.current.close(); enableBodyScroll(sidebarRef.current); + return; } - }, [isExpanded]); + disableBodyScroll(sidebarRef.current); + sidebarRef.current.showModal(); + }, []); const api = useMemo( () => ({ - setIsAtTop, - toggle, + toggleSidebarDialog, }), - [toggle] + [toggleSidebarDialog] ); const refs = useMemo( () => ({ - mainMenuBtnRef, sidebarRef, }), [] ); return ( <> - - - - {children} - - - + + {children} + ); } From 9ba65a8ac1f5cb39724c2e51714e8dca47d428f7 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 13:48:46 +0200 Subject: [PATCH 10/13] refactor: change base dialog styles --- components/cookies/manage/dialog.tsx | 2 +- styles/base.css | 18 ++++++++++++++++-- styles/components.css | 7 ------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/components/cookies/manage/dialog.tsx b/components/cookies/manage/dialog.tsx index 67c7c8d..c363168 100644 --- a/components/cookies/manage/dialog.tsx +++ b/components/cookies/manage/dialog.tsx @@ -15,7 +15,7 @@ const Dialog = forwardRef(function Dialog({ children } className={cn( "hidden open:flex", "flex-col items-center rounded-2xl text-foreground", - "fixed left-0 top-0 z-[99] box-border overflow-hidden md:left-1/2 md:top-2/3 md:-translate-x-1/2 md:-translate-y-1/2", + "fixed left-0 top-0 z-[99] box-border overflow-hidden md:left-1/2 md:top-1/2 md:-translate-x-1/2 md:-translate-y-1/2", "h-auto max-h-[calc(100%-16px)] w-[calc(100%-16px)] max-w-[80rem] rounded-2xl bg-background pt-6 shadow transition-all md:min-h-[30rem]", "backdrop:fixed backdrop:bottom-[-100%] backdrop:left-[-100%] backdrop:right-[-100%] backdrop:top-[-100%] backdrop:z-40 backdrop:m-auto backdrop:h-[100vh] backdrop:w-full backdrop:overflow-hidden backdrop:bg-black backdrop:bg-opacity-50", "backdrop:!bg-no-repeat backdrop:object-cover backdrop:backdrop-blur backdrop:backdrop-brightness-50" diff --git a/styles/base.css b/styles/base.css index 896a094..d7962eb 100644 --- a/styles/base.css +++ b/styles/base.css @@ -147,8 +147,6 @@ html.dark { :root { --page-x-spacing: 3rem; --page-x-spacing2: 2rem; - --header-height: 8rem; - --header-height-scrolled: 4.5rem; } @media (width >= 64rem) { @@ -181,3 +179,19 @@ body { .pxPageTwo { @apply px-6 m:px-[--page-x-spacing2]; } + +dialog { + @apply m-0 block [inset-inline-end:auto] [inset-inline-start:auto]; +} + +dialog:-internal-dialog-in-top-layer { + @apply visible max-h-[100vh] max-w-[100vw]; +} + +#sidebarMenuNav:-internal-dialog-in-top-layer::backdrop { + @apply pointer-events-none hidden opacity-0; +} + +#sidebarMenuNav:modal { + @apply ![overlay:none]; +} diff --git a/styles/components.css b/styles/components.css index 1769d2f..c9a6b56 100644 --- a/styles/components.css +++ b/styles/components.css @@ -120,13 +120,6 @@ div[id="aboutTab"] { } } -header.scrolled { - height: var(--header-height-scrolled); - - @apply border-border; - @apply bg-background; -} - .menuBtn { --size: 1.5rem; --duration: 250ms; From ba40245bbdca68febc35a6c181ca04c5e170a798 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 13:52:36 +0200 Subject: [PATCH 11/13] refactor: change the styling of toast buttons on smaller screens --- components/cookies/toast/buttons.tsx | 3 +++ components/cookies/toast/index.tsx | 2 +- components/cookies/toast/wrapper.tsx | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/cookies/toast/buttons.tsx b/components/cookies/toast/buttons.tsx index a29f06f..94ba6de 100644 --- a/components/cookies/toast/buttons.tsx +++ b/components/cookies/toast/buttons.tsx @@ -18,6 +18,7 @@ export const ManageCookies = memo(function ManageCookies() { variant="link" tabIndex={consent ? -1 : 0} aria-label="Manage Cookies" + className="w-full md:w-auto" > Manage Cookies @@ -38,6 +39,7 @@ export const DenyAll = memo(function DenyAll() { variant="outline" tabIndex={consent ? -1 : 0} aria-label="Deny All" + className="w-full md:w-auto" > Deny All @@ -57,6 +59,7 @@ export const AcceptAll = memo(function AcceptAll() { }} tabIndex={consent ? -1 : 0} aria-label="Accept ALl" + className="w-full md:w-auto" > Accept All diff --git a/components/cookies/toast/index.tsx b/components/cookies/toast/index.tsx index 0b6d102..e6fcfc7 100644 --- a/components/cookies/toast/index.tsx +++ b/components/cookies/toast/index.tsx @@ -11,7 +11,7 @@ export default function CookieToast() { {env.NEXT_PUBLIC_SITE_NAME} uses cookies to improve the website and your user experience, read more in the .

-
+
diff --git a/components/cookies/toast/wrapper.tsx b/components/cookies/toast/wrapper.tsx index 3fa87f8..70f10b7 100644 --- a/components/cookies/toast/wrapper.tsx +++ b/components/cookies/toast/wrapper.tsx @@ -16,7 +16,7 @@ export default function Wrapper({ children }: { children: React.ReactNode }) { <>
Date: Wed, 8 May 2024 13:54:22 +0200 Subject: [PATCH 12/13] perf: extract static code, remove context, remove logout --- components/header/sidebar/nav/index.tsx | 25 +++++++---- components/header/sidebar/nav/logout.tsx | 40 ------------------ components/header/sidebar/nav/privacy.tsx | 36 +++++++--------- components/header/sidebar/nav/sidebarItem.tsx | 41 ++----------------- 4 files changed, 36 insertions(+), 106 deletions(-) delete mode 100644 components/header/sidebar/nav/logout.tsx diff --git a/components/header/sidebar/nav/index.tsx b/components/header/sidebar/nav/index.tsx index e378b64..ac0d786 100644 --- a/components/header/sidebar/nav/index.tsx +++ b/components/header/sidebar/nav/index.tsx @@ -1,6 +1,5 @@ -import { FaBoxArchive, FaFileLines } from "react-icons/fa6"; +import { FaBoxArchive, FaFileLines, FaPassport } from "react-icons/fa6"; -import LogOut from "./logout"; import Privacy from "./privacy"; import { SidebarItem } from "./sidebarItem"; export default function Nav() { @@ -10,20 +9,30 @@ export default function Nav() { {/* */}
  • - - + +
    + +
    + Archive
  • - - + +
    + +
    + Terms of Service
  • - + +
    + +
    + Privacy +
- ); diff --git a/components/header/sidebar/nav/logout.tsx b/components/header/sidebar/nav/logout.tsx deleted file mode 100644 index b291c44..0000000 --- a/components/header/sidebar/nav/logout.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"use client"; -import { type KeyboardEventHandler, useState } from "react"; -import { FaDoorOpen } from "react-icons/fa6"; - -import { useSidebarExpandedContext, useSidebarRefContext } from "@/context/sidebar"; -import { cn } from "@/lib/utils"; - -export default function LogOut() { - const [isLoggedIn] = useState(false); - const { isExpanded } = useSidebarExpandedContext(); - const { mainMenuBtnRef } = useSidebarRefContext(); - const trapFocus: KeyboardEventHandler = (e) => { - if (e.code === "Tab" && mainMenuBtnRef.current) { - isExpanded && mainMenuBtnRef.current.focus(); - } - }; - return ( - <> - {isLoggedIn && ( - <> - -
- -
- Log Out -
- - )} - - ); -} diff --git a/components/header/sidebar/nav/privacy.tsx b/components/header/sidebar/nav/privacy.tsx index 49d292e..69b6296 100644 --- a/components/header/sidebar/nav/privacy.tsx +++ b/components/header/sidebar/nav/privacy.tsx @@ -1,31 +1,25 @@ "use client"; -import { type KeyboardEventHandler, useState } from "react"; -import { FaPassport } from "react-icons/fa6"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; -import { useSidebarExpandedContext, useSidebarRefContext } from "@/context/sidebar"; +import { cn } from "@/lib/utils"; -import { SidebarItem } from "./sidebarItem"; +export default function Privacy({ children }: { children?: React.ReactNode }) { + const pathname = usePathname(); -export default function Privacy() { - const { isExpanded } = useSidebarExpandedContext(); - const { mainMenuBtnRef } = useSidebarRefContext(); - const [isLoggedIn] = useState(false); - const trapFocus: KeyboardEventHandler = (e) => { - if (e.code === "Tab" && !e.shiftKey && mainMenuBtnRef.current) { - e.preventDefault(); - isExpanded && mainMenuBtnRef.current.focus(); - } - }; return ( <> - - - + {children} + ); } diff --git a/components/header/sidebar/nav/sidebarItem.tsx b/components/header/sidebar/nav/sidebarItem.tsx index 247c3b7..a88bba1 100644 --- a/components/header/sidebar/nav/sidebarItem.tsx +++ b/components/header/sidebar/nav/sidebarItem.tsx @@ -1,40 +1,18 @@ "use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import { memo, type KeyboardEventHandler } from "react"; +import { memo } from "react"; -import { useHeaderAPIContext, useSidebarExpandedContext } from "@/context/sidebar"; import { cn } from "@/lib/utils"; export const SidebarItem = memo(function SidebarItem({ href, - classes = "", - text, - isLast = false, - onKeyDown, children, }: { href: string; - classes?: string; - text: string; - isLast?: boolean; - onKeyDown?: KeyboardEventHandler | undefined; children?: React.ReactNode; }) { - const { isExpanded } = useSidebarExpandedContext(); - const { toggle } = useHeaderAPIContext(); const pathname = usePathname(); - let attr = {}; - attr = { - tabIndex: isExpanded ? 0 : -1, - onClick: () => toggle(), - }; - if (isLast) { - attr = { - ...attr, - onKeyDown, - }; - } return ( <> -
{children}
- {text} - + {children} ); From a38bb858e578476406b84a3a9a0d89adcd2a86c0 Mon Sep 17 00:00:00 2001 From: rikhall1515 Date: Wed, 8 May 2024 13:58:13 +0200 Subject: [PATCH 13/13] refactor(sidebar): add sidebar to dialog, remove unneeded context This finalizes the change, and it is breaking with previous versions because previous context has been removed. BREAKING CHANGE: Previous context has been removed because it is no longer necessary. --- components/header/sidebar/topbar/index.tsx | 3 +- .../header/sidebar/topbar/menuButton.tsx | 28 +++++++++++++++++++ components/header/sidebar/util/menuButton.tsx | 24 ++++++---------- 3 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 components/header/sidebar/topbar/menuButton.tsx diff --git a/components/header/sidebar/topbar/index.tsx b/components/header/sidebar/topbar/index.tsx index 48ea45a..d7db65c 100644 --- a/components/header/sidebar/topbar/index.tsx +++ b/components/header/sidebar/topbar/index.tsx @@ -1,5 +1,6 @@ import { cn } from "@/lib/utils"; +import InnerMenuButton from "./menuButton"; import SearchButton from "./searchBtn"; export default function TopBar() { @@ -14,7 +15,7 @@ export default function TopBar() { )} > - {/* */} +
); diff --git a/components/header/sidebar/topbar/menuButton.tsx b/components/header/sidebar/topbar/menuButton.tsx new file mode 100644 index 0000000..c1287fb --- /dev/null +++ b/components/header/sidebar/topbar/menuButton.tsx @@ -0,0 +1,28 @@ +"use client"; +import { useHeaderAPIContext } from "@/context/sidebar"; +import { cn } from "@/lib/utils"; + +export default function InnerMenuButton() { + const { toggleSidebarDialog } = useHeaderAPIContext(); + return ( + <> + + + ); +} diff --git a/components/header/sidebar/util/menuButton.tsx b/components/header/sidebar/util/menuButton.tsx index 610b22a..d8875e8 100644 --- a/components/header/sidebar/util/menuButton.tsx +++ b/components/header/sidebar/util/menuButton.tsx @@ -1,33 +1,25 @@ "use client"; import { forwardRef } from "react"; -import { - useHeaderAPIContext, - useSidebarExpandedContext, - useSidebarRefContext, -} from "@/context/sidebar"; +import { useHeaderAPIContext } from "@/context/sidebar"; import { cn } from "@/lib/utils"; type Props = JSX.IntrinsicElements["button"]; type Ref = HTMLButtonElement; export const MenuButton = forwardRef(function MenuButton() { - const { isExpanded } = useSidebarExpandedContext(); - const { toggle } = useHeaderAPIContext(); - const { mainMenuBtnRef } = useSidebarRefContext(); + const { toggleSidebarDialog } = useHeaderAPIContext(); return ( <>