Skip to content

Commit

Permalink
Merge pull request #20 from thomasKn/thomas/fv-228-header-settings
Browse files Browse the repository at this point in the history
Add header blur and sticky settings
  • Loading branch information
thomasKn authored Feb 1, 2024
2 parents dedd6ce + c1ed758 commit eca4b30
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 18 deletions.
83 changes: 67 additions & 16 deletions app/components/layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type {CSSProperties} from 'react';

import {Link} from '@remix-run/react';
import {vercelStegaCleanAll} from '@sanity/client/stega';
import {cx} from 'class-variance-authority';
import {LazyMotion, m, useTransform} from 'framer-motion';

import {useBoundedScroll} from '~/hooks/useBoundedScroll';
import {useLocalePath} from '~/hooks/useLocalePath';
import {useSanityRoot} from '~/hooks/useSanityRoot';
import {useSettingsCssVars} from '~/hooks/useSettingsCssVars';
Expand All @@ -13,31 +16,19 @@ import {CartCount} from './CartCount';
import {Logo} from './Logo';

export function Header() {
return <DesktopHeader />;
}

function DesktopHeader() {
const {data} = useSanityRoot();
const header = data?.header;
const logoWidth = header?.desktopLogoWidth
? `${header?.desktopLogoWidth}px`
: null;
const showSeparatorLine = header?.showSeparatorLine;
const homePath = useLocalePath({path: '/'});
const cssVars = useSettingsCssVars({
selector: 'header',
settings: header,
});
const homePath = useLocalePath({path: '/'});

return (
<header
className={cx([
'section-padding relative hidden bg-background text-foreground md:block',
headerVariants({
optional: showSeparatorLine ? 'separator-line' : null,
}),
])}
>
<HeaderWrapper>
<style dangerouslySetInnerHTML={{__html: cssVars}} />
<div className="container">
<div className="flex items-center justify-between">
Expand All @@ -53,11 +44,71 @@ function DesktopHeader() {
/>
</Link>
<div className="flex items-center gap-3">
<Navigation data={header?.menu} />
{/* Desktop navigation */}
<div className="hidden md:block">
<Navigation data={header?.menu} />
</div>
<CartCount />
</div>
</div>
</div>
</header>
</HeaderWrapper>
);
}

function HeaderWrapper(props: {children: React.ReactNode}) {
const {data} = useSanityRoot();
const header = data?.header;
const showSeparatorLine = header?.showSeparatorLine;
const blur = header?.blur;
const sticky = vercelStegaCleanAll(header?.sticky);

const headerClassName = cx([
'section-padding bg-background text-foreground',
(sticky === 'onScrollUp' || sticky === 'always') && 'sticky top-0 z-50',
blur &&
'bg-opacity-95 backdrop-blur supports-[backdrop-filter]:bg-opacity-85',
headerVariants({
optional: showSeparatorLine ? 'separator-line' : null,
}),
]);

return sticky === 'onScrollUp' ? (
<HeaderAnimation className={headerClassName}>
{props.children}
</HeaderAnimation>
) : (
<header className={headerClassName}>{props.children}</header>
);
}

function HeaderAnimation(props: {
children: React.ReactNode;
className: string;
}) {
// See Animated Sticky Header from Build UI (https://buildui.com/recipes/fixed-header)
const loadFeatures = async () =>
await import('../../lib/framerMotionFeatures').then((res) => res.default);
const {scrollYBoundedProgress} = useBoundedScroll(400);
const scrollYBoundedProgressDelayed = useTransform(
scrollYBoundedProgress,
[0, 0.75, 1],
[0, 0, 1],
);

const style = {
transform: useTransform(
scrollYBoundedProgressDelayed,
[0, 1],
['translateY(0)', 'translateY(-100%)'],
),
};

return (
<LazyMotion features={loadFeatures} strict>
<m.header className={props.className} style={style}>
{props.children}
</m.header>
</LazyMotion>
);
}
2 changes: 1 addition & 1 deletion app/components/ui/NavigationMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
const NavigationMenuItem = NavigationMenuPrimitive.Item;

const navigationMenuTriggerStyle = cva(
'group inline-flex h-8 w-max items-center justify-center rounded-md bg-background px-2 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
'group inline-flex h-8 w-max items-center justify-center rounded-md px-2 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
);

const NavigationMenuTrigger = forwardRef<
Expand Down
27 changes: 27 additions & 0 deletions app/hooks/useBoundedScroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {useMotionValue, useScroll, useTransform} from 'framer-motion';
import {useEffect} from 'react';

export function useBoundedScroll(threshold: number) {
const {scrollY} = useScroll();
const scrollYBounded = useMotionValue(0);
const scrollYBoundedProgress = useTransform(
scrollYBounded,
[0, threshold],
[0, 1],
);

useEffect(() => {
return scrollY.on('change', (current) => {
const previous = scrollY.getPrevious() ?? 0;
const diff = current - previous;
const newScrollYBounded = scrollYBounded.get() + diff;

scrollYBounded.set(clamp(newScrollYBounded, 0, threshold));
});
}, [threshold, scrollY, scrollYBounded]);

return {scrollYBounded, scrollYBoundedProgress};
}

const clamp = (number: number, min: number, max: number) =>
Math.min(Math.max(number, min), max);
4 changes: 3 additions & 1 deletion app/qroq/queries.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {q} from 'groqd';
import {q, z} from 'groqd';

import {FOOTERS_FRAGMENT} from './footers';
import {
Expand Down Expand Up @@ -143,6 +143,7 @@ export const HEADER_QUERY = q('*')
.deref()
.grab(COLOR_SCHEME_FRAGMENT),
autoRotateAnnoucements: q.boolean().nullable(),
blur: q.boolean().nullable(),
colorScheme: q('colorScheme').deref().grab(COLOR_SCHEME_FRAGMENT),
desktopLogoWidth: q.number().nullable(),
menu: MENU_FRAGMENT,
Expand All @@ -153,6 +154,7 @@ export const HEADER_QUERY = q('*')
})
.nullable(),
showSeparatorLine: q.boolean().nullable(),
sticky: z.enum(['none', 'always', 'onScrollUp']).nullable(),
})
.slice(0)
.nullable();
Expand Down
21 changes: 21 additions & 0 deletions studio/schemas/singletons/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@ export default defineType({
group: 'settings',
to: [{type: 'colorScheme'}],
}),
defineField({
name: 'blur',
title: 'Background blur',
type: 'boolean',
group: 'settings',
initialValue: false,
}),
defineField({
name: 'sticky',
title: 'Sticky header',
type: 'string',
group: 'settings',
options: {
list: [
{title: 'None', value: 'none'},
{title: 'On scroll up', value: 'onScrollUp'},
{title: 'Always', value: 'always'},
],
},
initialValue: 'none',
}),
defineField({
name: 'showSeparatorLine',
title: 'Show separator line',
Expand Down

0 comments on commit eca4b30

Please sign in to comment.