Skip to content

Commit

Permalink
feat: add Makeswift site footer
Browse files Browse the repository at this point in the history
  • Loading branch information
fikrikarim committed Dec 23, 2024
1 parent c982db9 commit 434647d
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 1 deletion.
2 changes: 1 addition & 1 deletion core/components/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import {
import { useTranslations } from 'next-intl';
import { cache, JSX } from 'react';

import { Footer as FooterSection } from '@/vibes/soul/sections/footer';
import { LayoutQuery } from '~/app/[locale]/(default)/query';
import { getSessionCustomerAccessToken } from '~/auth';
import { client } from '~/client';
import { readFragment } from '~/client/graphql';
import { revalidate } from '~/client/revalidate-target';
import { logoTransformer } from '~/data-transformers/logo-transformer';
import { SiteFooter as FooterSection } from '~/lib/makeswift/components/site-footer/site-footer';

import { FooterFragment } from './fragment';
import { AmazonIcon } from './payment-icons/amazon';
Expand Down
1 change: 1 addition & 0 deletions core/lib/makeswift/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import './components/card-carousel/card-carousel.makeswift';
import './components/card/card.makeswift';
import './components/carousel/carousel.makeswift';
import './components/section/section.makeswift';
import './components/site-footer/site-footer.makeswift';
import './components/site-header/site-header.makeswift';
import './components/slideshow/slideshow.makeswift';
import './components/sticky-sidebar/sticky-sidebar.makeswift';
Expand Down
82 changes: 82 additions & 0 deletions core/lib/makeswift/components/site-footer/site-footer.client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use client';

import {
type ComponentPropsWithoutRef,
createContext,
forwardRef,
type PropsWithChildren,
type Ref,
useContext,
} from 'react';

import { Footer } from '@/vibes/soul/sections/footer';

import { mergeSections } from '../../utils/merge-sections';

type FooterProps = ComponentPropsWithoutRef<typeof Footer>;

// MakeswiftFooter does not support streamable sections
type ContextProps = Omit<FooterProps, 'sections'> & {
sections: Awaited<FooterProps['sections']>;
};

const PropsContext = createContext<ContextProps>({
sections: [],
});

export const PropsContextProvider = ({
value,
children,
}: PropsWithChildren<{ value: ContextProps }>) => (
<PropsContext.Provider value={value}>{children}</PropsContext.Provider>
);

interface Props {
logo?: {
show: boolean;
src?: string;
width?: number;
alt: string;
};
sections: Array<{
title: string;
links: Array<{
label: string;
link: { href: string };
}>;
}>;
copyright?: string;
}

function combineSections(
passedSections: ContextProps['sections'],
makeswiftSections: Props['sections'],
): ContextProps['sections'] {
return mergeSections(
passedSections,
makeswiftSections.map(({ title, links }) => ({
title,
links: links.map(({ label, link }) => ({ label, href: link.href })),
})),
(left, right) => ({ ...left, links: [...left.links, ...right.links] }),
);
}

export const MakeswiftFooter = forwardRef(
({ logo: _logo, sections, copyright }: Props, ref: Ref<HTMLDivElement>) => {
const passedProps = useContext(PropsContext);
const logoObject = _logo?.src ? { src: _logo.src, alt: _logo.alt } : passedProps.logo;
const logo = _logo?.show ? logoObject : undefined;

return (
<Footer
{...passedProps}
copyright={copyright ?? passedProps.copyright}
logo={logo}
logoWidth={_logo?.width ?? passedProps.logoWidth}
ref={ref}
sections={combineSections(passedProps.sections, sections)}
/>
);
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Checkbox, Image, Link, List, Number, Shape, TextInput } from '@makeswift/runtime/controls';

import { runtime } from '~/lib/makeswift/runtime';

import { MakeswiftFooter } from './site-footer.client';

export const COMPONENT_TYPE = 'catalyst-makeswift-footer';

runtime.registerComponent(MakeswiftFooter, {
type: COMPONENT_TYPE,
label: 'Site Footer',
hidden: true,
props: {
logo: Shape({
type: {
show: Checkbox({ label: 'Show logo', defaultValue: true }),
src: Image({ label: 'Logo' }),
width: Number({ label: 'Logo width', suffix: 'px' }),
alt: TextInput({ label: 'Alt text', defaultValue: 'Logo alt' }),
},
}),
sections: List({
label: 'Footer group',
type: Shape({
type: {
title: TextInput({ label: 'Heading', defaultValue: 'Heading' }),
links: List({
label: 'Links',
type: Shape({
type: {
label: TextInput({ label: 'Text', defaultValue: 'Text' }),
link: Link({ label: 'URL' }),
},
}),
getItemLabel: (item) => item?.label ?? 'Text',
}),
},
}),
getItemLabel: (item) => item?.title ?? 'Heading',
}),
copyright: TextInput({ label: 'Copyright text' }),
},
});
29 changes: 29 additions & 0 deletions core/lib/makeswift/components/site-footer/site-footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { MakeswiftComponent } from '@makeswift/runtime/next';
import { type ComponentPropsWithoutRef } from 'react';

import { Footer as VibesFooter } from '@/vibes/soul/sections/footer';
import { getComponentSnapshot } from '~/lib/makeswift/client';

import { PropsContextProvider } from './site-footer.client';
import { COMPONENT_TYPE } from './site-footer.makeswift';

type Props = ComponentPropsWithoutRef<typeof VibesFooter> & {
snapshotId?: string;
label?: string;
};

export const SiteFooter = async ({
snapshotId = 'site-footer',
label = 'Site Footer',
sections: _sections,
...props
}: Props) => {
const snapshot = await getComponentSnapshot(snapshotId);
const sections = await _sections;

return (
<PropsContextProvider value={{ ...props, sections }}>
<MakeswiftComponent label={label} snapshot={snapshot} type={COMPONENT_TYPE} />
</PropsContextProvider>
);
};
21 changes: 21 additions & 0 deletions core/lib/makeswift/utils/merge-sections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
interface Section {
title?: string;
}

export function mergeSections<S extends Section>(
defaultSections: S[],
overrides: S[],
mergeSection: (l: S, r: S) => S,
): S[] {
const defaultKeys = new Set(defaultSections.map((section) => section.title));
const overridesMap = new Map(overrides.map((section) => [section.title, section]));

return [
...defaultSections.map((section) => {
const override = overridesMap.get(section.title ?? '');

return override ? mergeSection(section, override) : section;
}),
...overrides.filter((section) => !defaultKeys.has(section.title ?? '')),
];
}

0 comments on commit 434647d

Please sign in to comment.