Skip to content

Commit

Permalink
Merge pull request #14 from thomasKn/thomas/fv-215-annoucement-bar
Browse files Browse the repository at this point in the history
Annoucement bar
  • Loading branch information
thomasKn authored Jan 30, 2024
2 parents 354d90e + 7867d11 commit 931cbdb
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 5 deletions.
1 change: 0 additions & 1 deletion .prettierignore

This file was deleted.

114 changes: 114 additions & 0 deletions app/components/layout/AnnoucementBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type {TypeFromSelection} from 'groqd';

import {cx} from 'class-variance-authority';
import Autoplay from 'embla-carousel-autoplay';
import {useMemo} from 'react';

import type {ANNOUCEMENT_BAR_FRAGMENT} from '~/qroq/fragments';

import {useSanityRoot} from '~/hooks/useSanityRoot';
import {useSettingsCssVars} from '~/hooks/useSettingsCssVars';

import {Icon} from '../icons/Icon';
import {SanityInternalLink} from '../sanity/link/SanityInternalLink';
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from '../ui/Carousel';

type AnnoucementBarProps = TypeFromSelection<typeof ANNOUCEMENT_BAR_FRAGMENT>;

export function AnnouncementBar() {
const {data} = useSanityRoot();
const header = data?.header;
const annoucementBar = header?.annoucementBar;
const plugins = useMemo(
() =>
header?.autoRotateAnnoucements
? [Autoplay({delay: 5000, stopOnMouseEnter: true})]
: [],
[header],
);

const cssVars = useSettingsCssVars({
selector: `#announcement-bar`,
settings: {
colorScheme: header?.annoucementBarColorScheme!,
customCss: null,
hide: null,
padding: null,
},
});

const isActive = annoucementBar?.length! > 1;

return (
<section className="bg-background text-foreground" id="announcement-bar">
<div className="container">
<style dangerouslySetInnerHTML={{__html: cssVars}} />
<Carousel opts={{active: isActive}} plugins={plugins}>
<CarouselContent className="relative">
{annoucementBar?.map((item) => (
<CarouselItem key={item._key}>
<Item _key={item._key} link={item.link} text={item.text} />
</CarouselItem>
))}
</CarouselContent>
{isActive && (
<>
<CarouselPrevious />
<CarouselNext />
</>
)}
</Carousel>
</div>
</section>
);
}

function Item(props: AnnoucementBarProps) {
if (!props.text) return null;

const className = cx('flex w-full justify-center py-3 text-center');

return props.link ? (
<SanityInternalLink
className={cx(['group', className])}
data={{
_key: props.link.slug.current,
_type: 'internalLink',
anchor: null,
link: props.link,
name: props.text,
}}
>
<p className="flex items-center text-sm underline-offset-4 group-hover:underline">
<span className="relative z-[2] block bg-background pr-2">
{props.text}
</span>
<span className="-translate-x-[2px] transition-transform group-hover:translate-x-0">
<ArrowRight />
</span>
</p>
</SanityInternalLink>
) : (
<p className={className}>{props.text}</p>
);
}

function ArrowRight() {
return (
<Icon className="size-4" fill="none" viewBox="0 0 14 10">
<title>Arrow Right</title>
<path
clipRule="evenodd"
d="M8.537.808a.5.5 0 01.817-.162l4 4a.5.5 0 010 .708l-4 4a.5.5 0 11-.708-.708L11.793 5.5H1a.5.5 0 010-1h10.793L8.646 1.354a.5.5 0 01-.109-.546z"
fill="currentColor"
fillRule="evenodd"
></path>
</Icon>
);
}
2 changes: 2 additions & 0 deletions app/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {useSanityPreviewMode} from '~/hooks/useSanityPreviewMode';

import {TailwindIndicator} from '../TailwindIndicator';
import {TogglePreviewMode} from '../sanity/TogglePreviewMode';
import {AnnouncementBar} from './AnnoucementBar';
import {Footer} from './Footer';
import {Header} from './Header';

Expand All @@ -33,6 +34,7 @@ export function Layout({children = null}: LayoutProps) {
storefrontApiVersion={env?.PUBLIC_STOREFRONT_API_VERSION!}
storefrontToken={env?.PUBLIC_STOREFRONT_API_TOKEN!}
>
<AnnouncementBar />
<Header />
<main>{children}</main>
<Footer />
Expand Down
4 changes: 3 additions & 1 deletion app/components/sections/CarouselSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function CarouselSection(
100 / slidesPerViewDesktop
}vw, (min-width: 768px) 50vw, 100vw`
: '(min-width: 768px) 50vw, 100vw';
const isActive = slides?.length! > 1;

return (
<div className="container">
Expand All @@ -53,6 +54,7 @@ export function CarouselSection(
{slides && slides?.length > 0 && (
<Carousel
opts={{
active: isActive,
loop: loop || false,
}}
plugins={plugins}
Expand All @@ -68,7 +70,7 @@ export function CarouselSection(
</CarouselItem>
))}
</CarouselContent>
{arrows && (
{arrows && isActive && (
<div className="hidden md:block">
<CarouselPrevious />
<CarouselNext />
Expand Down
25 changes: 24 additions & 1 deletion app/qroq/fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {Selection} from 'groqd';

import {q} from 'groqd';

import {LINKS_LIST_SELECTION} from './links';
import {LINK_REFERENCE_FRAGMENT, LINKS_LIST_SELECTION} from './links';

/*
|--------------------------------------------------------------------------
Expand Down Expand Up @@ -79,6 +79,29 @@ export const COLOR_SCHEME_FRAGMENT = {
primaryForeground: q('primaryForeground').grab(COLOR_FRAGMENT).nullable(),
} satisfies Selection;

/*
|--------------------------------------------------------------------------
| Annoucement Bar Fragment
|--------------------------------------------------------------------------
*/
export const ANNOUCEMENT_BAR_FRAGMENT = {
_key: q.string(),
link: LINK_REFERENCE_FRAGMENT,
text: q.string().nullable(),
} satisfies Selection;

export const ANNOUCEMENT_BAR_ARRAY_FRAGMENT = q(
`coalesce(
annoucementBar[_key == $language][0].value[],
annoucementBar[_key == $defaultLanguage][0].value[],
)[]`,
{isArray: true},
)
.select({
'_type == "announcementBar"': ANNOUCEMENT_BAR_FRAGMENT,
})
.nullable();

/*
|--------------------------------------------------------------------------
| Settings Fragments
Expand Down
6 changes: 6 additions & 0 deletions app/qroq/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {q} from 'groqd';

import {FOOTERS_FRAGMENT} from './footers';
import {
ANNOUCEMENT_BAR_ARRAY_FRAGMENT,
COLOR_SCHEME_FRAGMENT,
FONT_FRAGMENT,
MENU_FRAGMENT,
Expand Down Expand Up @@ -137,6 +138,11 @@ export const SETTINGS_QUERY = q('*')
export const HEADER_QUERY = q('*')
.filter("_type == 'header'")
.grab({
annoucementBar: ANNOUCEMENT_BAR_ARRAY_FRAGMENT,
annoucementBarColorScheme: q('annoucementBarColorScheme')
.deref()
.grab(COLOR_SCHEME_FRAGMENT),
autoRotateAnnoucements: q.boolean().nullable(),
colorScheme: q('colorScheme').deref().grab(COLOR_SCHEME_FRAGMENT),
desktopLogoWidth: q.number().nullable(),
menu: MENU_FRAGMENT,
Expand Down
4 changes: 2 additions & 2 deletions prettier.config.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
arrowParens: 'always',
singleQuote: true,
bracketSpacing: false,
trailingComma: 'all',
plugins: ['prettier-plugin-tailwindcss'],
singleQuote: true,
trailingComma: 'all',
};
1 change: 1 addition & 0 deletions studio/sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default defineConfig({
'text',
'slug',
'headerNavigation',
'announcementBar',
'productRichtext',
'richtext',
],
Expand Down
2 changes: 2 additions & 0 deletions studio/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import productTemplate from './documents/productTemplate';
import collectionTemplate from './documents/collectionTemplate';
import collectionBanner from './objects/sections/collectionBanner';
import collectionProductGrid from './objects/sections/collectionProductGrid';
import announcementBar from './objects/global/announcementBar';

const singletons = [home, header, footer, settings, themeContent];
const documents = [
Expand Down Expand Up @@ -78,6 +79,7 @@ const objects = [
seo,
sectionSettings,
headerNavigation,
announcementBar,
inventory,
options,
placeholderString,
Expand Down
22 changes: 22 additions & 0 deletions studio/schemas/objects/global/announcementBar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {Megaphone} from 'lucide-react';
import {defineField} from 'sanity';
import {internalLinkField} from './headerNavigation';

export default defineField({
type: 'array',
name: 'announcementBar',
of: [
{
type: 'object',
name: 'announcementBar',
icon: Megaphone,
fields: [
defineField({
name: 'text',
type: 'string',
}),
internalLinkField,
],
},
],
});
21 changes: 21 additions & 0 deletions studio/schemas/singletons/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ const GROUPS = [
title: 'Navigation',
default: true,
},
{
name: 'announcementBar',
title: 'Announcement Bar',
},
{
name: 'settings',
title: 'Settings',
Expand All @@ -18,6 +22,23 @@ export default defineType({
__experimental_formPreviewTitle: false,
groups: GROUPS,
fields: [
defineField({
name: 'annoucementBar',
group: 'announcementBar',
type: 'internationalizedArrayAnnouncementBar',
}),
defineField({
name: 'annoucementBarColorScheme',
type: 'reference',
group: 'announcementBar',
to: [{type: 'colorScheme'}],
}),
defineField({
name: 'autoRotateAnnoucements',
type: 'boolean',
group: 'announcementBar',
initialValue: false,
}),
defineField({
name: 'menu',
group: 'navigation',
Expand Down

0 comments on commit 931cbdb

Please sign in to comment.