router config changes
This commit is contained in:
@@ -2,20 +2,22 @@ import { AppShell } from '@mantine/core';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import Header from './header';
|
||||
import Navbar from './navbar';
|
||||
import useHeaderConfig from '../hooks/use-header-config';
|
||||
import Pullable from './pullable';
|
||||
import useVisualViewportSize from '../hooks/use-visual-viewport-size';
|
||||
import useRouterConfig from '../hooks/use-router-config';
|
||||
import Page from '@/components/page';
|
||||
|
||||
const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
const headerConfig = useHeaderConfig();
|
||||
const { header } = useRouterConfig();
|
||||
const viewport = useVisualViewportSize();
|
||||
const [scrollPosition, setScrollPosition] = useState({ x: 0, y: 0 });
|
||||
const { withPadding } = useRouterConfig();
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
id='app-shell'
|
||||
layout='alt'
|
||||
header={{ height: 60, collapsed: headerConfig.collapsed }}
|
||||
header={{ height: 60, collapsed: header.collapsed }}
|
||||
navbar={{
|
||||
width: { base: 0, sm: 100, md: 200, lg: 300 },
|
||||
breakpoint: 'sm',
|
||||
@@ -31,7 +33,7 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
mah='100dvh'
|
||||
style={{ top: viewport.top }} //, transition: 'top 0.1s ease-in-out' }}
|
||||
>
|
||||
<Header scrollPosition={scrollPosition} {...headerConfig} />
|
||||
<Header scrollPosition={scrollPosition} {...header} />
|
||||
<AppShell.Main
|
||||
pos='relative'
|
||||
h='100%'
|
||||
@@ -41,7 +43,9 @@ const Layout: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
style={{ transition: 'none' }}
|
||||
>
|
||||
<Pullable scrollPosition={scrollPosition} onScrollPositionChange={setScrollPosition}>
|
||||
{children}
|
||||
<Page noPadding={!withPadding}>
|
||||
{children}
|
||||
</Page>
|
||||
</Pullable>
|
||||
</AppShell.Main>
|
||||
<Navbar />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ActionIcon, Box, Button, Flex, ScrollArea } from "@mantine/core";
|
||||
import { PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import useAppShellHeight from "@/hooks/use-appshell-height";
|
||||
import useRefreshConfig from "@/features/core/hooks/use-refresh-config";
|
||||
import { ArrowClockwiseIcon, SpinnerIcon } from "@phosphor-icons/react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import useRouterConfig from "../hooks/use-router-config";
|
||||
|
||||
const THRESHOLD = 80;
|
||||
|
||||
@@ -20,20 +20,20 @@ const Pullable: React.FC<PullableProps> = ({ children, scrollPosition, onScrollP
|
||||
const height = useAppShellHeight();
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [scrolling, setScrolling] = useState(false);
|
||||
const { toRefresh } = useRefreshConfig();
|
||||
const { refresh } = useRouterConfig();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const scrollY = useMemo(() => scrollPosition.y < 0 && scrolling ? Math.abs(scrollPosition.y) : 0, [scrollPosition.y, scrolling]);
|
||||
|
||||
const onTrigger = useCallback(async () => {
|
||||
setIsRefreshing(true);
|
||||
if (toRefresh.length > 0) {
|
||||
if (refresh.length > 0) {
|
||||
// TODO: Remove this after testing - or does the delay help ux?
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await queryClient.refetchQueries({ queryKey: toRefresh, exact: true});
|
||||
await queryClient.refetchQueries({ queryKey: refresh, exact: true});
|
||||
}
|
||||
setIsRefreshing(false);
|
||||
}, [toRefresh]);
|
||||
}, [refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isRefreshing && scrollY > THRESHOLD) {
|
||||
@@ -43,7 +43,7 @@ const Pullable: React.FC<PullableProps> = ({ children, scrollPosition, onScrollP
|
||||
|
||||
const iconOpacity = useMemo(() => {
|
||||
if (isRefreshing) return 1;
|
||||
if (toRefresh.length === 0) return 0;
|
||||
if (refresh.length === 0) return 0;
|
||||
const clampedValue = Math.max(5, Math.min(THRESHOLD, scrollY));
|
||||
|
||||
const min = 5;
|
||||
@@ -111,7 +111,7 @@ const Pullable: React.FC<PullableProps> = ({ children, scrollPosition, onScrollP
|
||||
>
|
||||
<Box pt='1rem'pb='0.285rem' mih={height} style={{ boxSizing: 'content-box' }}>
|
||||
{ /* TODO: Remove this debug button */}
|
||||
<ActionIcon style={{ zIndex: 1000 }} pos='absolute' top={8} left='calc(50% - 24px)' onClick={onTrigger} variant='filled' color='var(--mantine-color-dimmed)'>
|
||||
<ActionIcon display={!!refresh.length ? 'unset' : 'none' } style={{ zIndex: 1000 }} pos='absolute' top={8} left='calc(50% - 24px)' onClick={onTrigger} variant='filled' color='var(--mantine-color-dimmed)'>
|
||||
<ArrowClockwiseIcon />
|
||||
</ActionIcon>
|
||||
{children}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { isMatch, useMatches } from "@tanstack/react-router";
|
||||
import { HeaderConfig } from "../types/header-config";
|
||||
|
||||
export const defaultHeaderConfig: HeaderConfig = {
|
||||
title: 'FLXN',
|
||||
withBackButton: false,
|
||||
collapsed: false,
|
||||
}
|
||||
|
||||
const useHeaderConfig = () => {
|
||||
const matches = useMatches();
|
||||
|
||||
const matchesWithHeader = matches.filter((match) =>
|
||||
isMatch(match, 'loaderData.header'),
|
||||
)
|
||||
|
||||
const config = matchesWithHeader.reduce((acc, match) => {
|
||||
return {
|
||||
...acc,
|
||||
...match?.loaderData?.header,
|
||||
}
|
||||
}, defaultHeaderConfig) as HeaderConfig;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export default useHeaderConfig;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { isMatch, useMatches } from "@tanstack/react-router";
|
||||
|
||||
export const defaultRefreshConfig: { toRefresh: string[] } = {
|
||||
toRefresh: [],
|
||||
}
|
||||
|
||||
const useRefreshConfig = () => {
|
||||
const matches = useMatches();
|
||||
|
||||
const matchesWithRefresh = matches.filter((match) =>
|
||||
isMatch(match, 'loaderData.refresh'),
|
||||
)
|
||||
|
||||
const config = matchesWithRefresh.reduce((acc, match) => {
|
||||
return {
|
||||
...acc,
|
||||
...match?.loaderData?.refresh,
|
||||
}
|
||||
}, defaultRefreshConfig) as { toRefresh: string[] };
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export default useRefreshConfig;
|
||||
40
src/features/core/hooks/use-router-config.ts
Normal file
40
src/features/core/hooks/use-router-config.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useMatches } from "@tanstack/react-router";
|
||||
import { HeaderConfig } from "../types/header-config";
|
||||
|
||||
export const defaultHeaderConfig: HeaderConfig = {
|
||||
title: 'FLXN',
|
||||
withBackButton: false,
|
||||
collapsed: false,
|
||||
}
|
||||
|
||||
const useRouterConfig = () => {
|
||||
const matches = useMatches();
|
||||
|
||||
const matchesWithHeader = matches.filter((match) =>
|
||||
match?.loaderData && 'header' in match.loaderData
|
||||
);
|
||||
|
||||
const headerConfig = matchesWithHeader.reduce((acc, match) => {
|
||||
const loaderData = match?.loaderData;
|
||||
if (loaderData && typeof loaderData === 'object' && 'header' in loaderData) {
|
||||
const header = loaderData.header;
|
||||
if (header && typeof header === 'object') {
|
||||
return {
|
||||
...acc,
|
||||
...header,
|
||||
}
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, defaultHeaderConfig);
|
||||
|
||||
const current = matches[matches.length - 1]?.loaderData;
|
||||
|
||||
return {
|
||||
header: headerConfig,
|
||||
refresh: current && typeof current === 'object' && 'refresh' in current ? current.refresh : [],
|
||||
withPadding: current && typeof current === 'object' && 'withPadding' in current ? current.withPadding : true
|
||||
};
|
||||
}
|
||||
|
||||
export default useRouterConfig;
|
||||
Reference in New Issue
Block a user