Skip to content

Commit

Permalink
Merge pull request #81 from buildheadless/templates
Browse files Browse the repository at this point in the history
Add collection and product templates
  • Loading branch information
thomasKn authored Jan 12, 2024
2 parents 3730984 + a3835e2 commit de704ed
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 36 deletions.
7 changes: 3 additions & 4 deletions templates/hydrogen-theme/app/hooks/useSettingsCssVars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ const fallbackScheme = {
},
};

export function useSettingsCssVars({
settings,
}: {
settings?: CmsSectionSettings | HeaderQuery | SettingsQuery;
export function useSettingsCssVars(props: {
settings?: CmsSectionSettings | HeaderQuery;
}): CSSProperties & {
'--backgroundColor'?: string;
'--outlineButton'?: string;
Expand All @@ -66,6 +64,7 @@ export function useSettingsCssVars({
'--primaryButtonLabel'?: string;
'--textColor'?: string;
} {
const {settings} = props;
const sanityRoot = useSanityRoot();
const defaultColorScheme = sanityRoot?.data?.defaultColorScheme;

Expand Down
1 change: 0 additions & 1 deletion templates/hydrogen-theme/app/qroq/fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ export const COLOR_SCHEME_FRAGMENT = {
|--------------------------------------------------------------------------
*/
export const SETTINGS_FRAGMENT = {
colorScheme: q('colorScheme').deref().grab(COLOR_SCHEME_FRAGMENT),
favicon: q('favicon').grab(IMAGE_FRAGMENT).nullable(),
logo: q('logo').grab(IMAGE_FRAGMENT).nullable(),
socialMedia: q('socialMedia').grab({
Expand Down
27 changes: 23 additions & 4 deletions templates/hydrogen-theme/app/qroq/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ export const PRODUCT_QUERY = q('*')
.filter(`_type == "product" && store.slug.current == $productHandle`)
.grab({
_type: q.literal('product'),
sections: PRODUCT_SECTIONS_FRAGMENT,
store: q('store').grab({
gid: q.string(),
}),
template: q('template').deref().grab({
sections: PRODUCT_SECTIONS_FRAGMENT,
}),
})
.slice(0)
.nullable();
Expand All @@ -67,10 +69,25 @@ export const FONTS_QUERY = q('*')
.nullable();

export const DEFAULT_COLOR_SCHEME_QUERY = q('*')
.filter("_type == 'colorScheme'")
.filter("_type == 'colorScheme' && default == true")
.grab(COLOR_SCHEME_FRAGMENT)
.order('_createdAt asc')
.slice(0);
.slice(0)
.nullable();

export const DEFAULT_PRODUCT_TEMPLATE = q('*')
.filter("_type == 'productTemplate' && default == true")
.grab({
_type: q.literal('productTemplate'),
name: q.string().nullable(),
sections: PRODUCT_SECTIONS_FRAGMENT,
})
.slice(0)
.nullable();

export const DEFAULT_COLLECTION_TEMPLATE = q('*')
.filter("_type == 'collectionTemplate' && default == true")
.slice(0)
.nullable();

export const SETTINGS_QUERY = q('*')
.filter("_type == 'settings'")
Expand Down Expand Up @@ -112,7 +129,9 @@ export const THEME_CONTENT_QUERY = q('*')

export const ROOT_QUERY = q('')
.grab({
defaultCollectionTemplate: DEFAULT_COLLECTION_TEMPLATE,
defaultColorScheme: DEFAULT_COLOR_SCHEME_QUERY,
defaultProductTemplate: DEFAULT_PRODUCT_TEMPLATE,
fonts: FONTS_QUERY,
footer: FOOTER_QUERY,
header: HEADER_QUERY,
Expand Down
7 changes: 1 addition & 6 deletions templates/hydrogen-theme/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import type {HydrogenSession} from './lib/hydrogen.session.server';
import favicon from '../public/favicon.svg';
import {Fonts} from './components/Fonts';
import {useLocale} from './hooks/useLocale';
import {useSanityRoot} from './hooks/useSanityRoot';
import {useSanityThemeContent} from './hooks/useSanityThemeContent';
import {useSettingsCssVars} from './hooks/useSettingsCssVars';
import {generateFontsPreloadLinks} from './lib/fonts';
import {sanityPreviewPayload} from './lib/sanity/sanity.payload.server';
Expand Down Expand Up @@ -141,10 +139,7 @@ export async function loader({context}: LoaderFunctionArgs) {
export default function App() {
const nonce = useNonce();
const locale = useLocale();
const themeSettings = useSanityRoot()?.data?.settings;
const cssVars = useSettingsCssVars({
settings: themeSettings,
});
const cssVars = useSettingsCssVars({});

return (
<html lang={locale?.language}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import invariant from 'tiny-invariant';
import {CmsSection} from '~/components/CmsSection';
import {PRODUCT_QUERY, VARIANTS_QUERY} from '~/graphql/queries';
import {useSanityData} from '~/hooks/useSanityData';
import {useSanityRoot} from '~/hooks/useSanityRoot';
import {resolveShopifyPromises} from '~/lib/resolveShopifyPromises';
import {sanityPreviewPayload} from '~/lib/sanity/sanity.payload.server';
import {PRODUCT_QUERY as CMS_PRODUCT_QUERY} from '~/qroq/queries';
Expand Down Expand Up @@ -91,11 +92,13 @@ export async function loader({context, params, request}: LoaderFunctionArgs) {

export default function Product() {
const {cmsProduct} = useLoaderData<typeof loader>();
const {data: rootData} = useSanityRoot();
const {data, encodeDataAttribute} = useSanityData(cmsProduct);

// Todo => Add a template mechanism to CMS products so we can attach a same template to multiple products
return data?.sections && data.sections.length > 0
? data.sections.map((section) => (
const template = data?.template || rootData?.defaultProductTemplate;

return template?.sections && template.sections.length > 0
? template.sections.map((section) => (
<CmsSection
data={section}
encodeDataAttribute={encodeDataAttribute}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ export default defineType({
type: 'proxyString',
options: {field: 'store.slug.current'},
}),
// Sections
// Template
defineField({
name: 'sections',
type: 'sections',
name: 'template',
description:
'Select a template to use for this collection. If no template is selected, the default template will be used.',
type: 'reference',
to: [{type: 'collectionTemplate'}],
group: 'editorial',
}),
// Vector
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {StringRule, ValidationContext, defineField, defineType} from 'sanity';
import {validateDefaultStatus} from '../../utils/setAsDefaultValidation';
import {LayoutTemplate} from 'lucide-react';

export default defineType({
name: 'collectionTemplate',
type: 'document',
__experimental_formPreviewTitle: false,
fields: [
defineField({
name: 'name',
title: 'Template name',
type: 'string',
validation: (Rule: StringRule) => Rule.required(),
}),
defineField({
name: 'default',
title: 'Set as default template',
type: 'boolean',
validation: (Rule) =>
Rule.required().custom(async (value, context: ValidationContext) =>
validateDefaultStatus(value, context),
),
initialValue: false,
}),
],
preview: {
select: {
title: 'name',
subtitle: 'default',
},
prepare({title, subtitle}) {
return {
title,
subtitle: subtitle ? 'Default template' : undefined,
media: LayoutTemplate,
};
},
},
});
17 changes: 15 additions & 2 deletions templates/hydrogen-theme/studio/schemas/documents/color.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {StringRule, defineField, defineType} from 'sanity';
import {StringRule, ValidationContext, defineField, defineType} from 'sanity';
import {IconPalette} from '../../components/icons/Palette';
import {ColorSchemeMedia} from '../../components/ColorScheme';
import {validateDefaultStatus} from '../../utils/setAsDefaultValidation';

export default defineType({
name: 'colorScheme',
Expand All @@ -11,12 +12,14 @@ export default defineType({
preview: {
select: {
title: 'name',
subtitle: 'default',
background: 'background',
text: 'text',
},
prepare({title, background, text}: any) {
prepare({title, subtitle, background, text}: any) {
return {
title,
subtitle: subtitle ? 'Default template' : undefined,
media: ColorSchemeMedia({background, text}),
};
},
Expand All @@ -28,6 +31,16 @@ export default defineType({
type: 'string',
validation: (Rule: StringRule) => Rule.required(),
}),
defineField({
name: 'default',
title: 'Set as default template',
type: 'boolean',
validation: (Rule) =>
Rule.required().custom(async (value, context: ValidationContext) =>
validateDefaultStatus(value, context),
),
initialValue: false,
}),
defineField({
name: 'background',
title: 'Background',
Expand Down
9 changes: 6 additions & 3 deletions templates/hydrogen-theme/studio/schemas/documents/product.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ export default defineType({
return !parent?.store || (isActive && !isDeleted);
},
}),
// Sections
// Template
defineField({
name: 'sections',
type: 'productSections',
name: 'template',
description:
'Select a template to use for this product. If no template is selected, the default template will be used.',
type: 'reference',
to: [{type: 'productTemplate'}],
group: 'editorial',
}),
// Title (proxy)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {StringRule, ValidationContext, defineField, defineType} from 'sanity';
import {LayoutTemplate} from 'lucide-react';
import {validateDefaultStatus} from '../../utils/setAsDefaultValidation';

export default defineType({
name: 'productTemplate',
type: 'document',
__experimental_formPreviewTitle: false,
fields: [
defineField({
name: 'name',
title: 'Template name',
type: 'string',
validation: (Rule: StringRule) => Rule.required(),
}),
defineField({
name: 'default',
title: 'Set as default template',
type: 'boolean',
validation: (Rule) =>
Rule.required().custom(async (value, context: ValidationContext) =>
validateDefaultStatus(value, context),
),
initialValue: false,
}),
defineField({
name: 'sections',
type: 'productSections',
}),
],
preview: {
select: {
title: 'name',
subtitle: 'default',
},
prepare({title, subtitle}) {
return {
title,
subtitle: subtitle ? 'Default template' : undefined,
media: LayoutTemplate,
};
},
},
});
13 changes: 12 additions & 1 deletion templates/hydrogen-theme/studio/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,20 @@ import relatedProductsSection from './objects/sections/relatedProductsSection';
import carouselSection from './objects/sections/carouselSection';
import richtextSection from './objects/sections/richtextSection';
import richtext from './objects/global/richtext';
import productTemplate from './documents/productTemplate';
import collectionTemplate from './documents/collectionTemplate';

const singletons = [home, header, footer, settings, themeContent];
const documents = [page, color, collection, product, blogPost, productVariant];
const documents = [
page,
color,
collection,
product,
productTemplate,
collectionTemplate,
blogPost,
productVariant,
];
const sections = [
imageBannerSection,
featuredCollection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ export default defineType({
type: 'document',
__experimental_formPreviewTitle: false,
fields: [
defineField({
name: 'colorScheme',
title: 'Default Color Scheme',
description: 'The default color scheme used throughout the site.',
type: 'reference',
to: [{type: 'colorScheme'}],
}),
defineField({
title: 'Logo',
name: 'logo',
Expand Down
22 changes: 20 additions & 2 deletions templates/hydrogen-theme/studio/structure/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {DefaultDocumentNodeResolver, StructureResolver} from 'sanity/structure';

import {SINGLETONS, singleton} from './singletons';
import {IconPage} from '../components/icons/Page';

import {IconBlog} from '../components/icons/Blog';
import {products} from './productStructure';
import {collections} from './collectionStructure';
import {LayoutTemplate, PanelsTopLeft} from 'lucide-react';

export const defaultDocumentNode: DefaultDocumentNodeResolver = (S) => {
return S.document().views([S.view.form()]);
Expand All @@ -15,7 +16,7 @@ export const structure: StructureResolver = (S, context) => {
.title('Content')
.items([
singleton(S, SINGLETONS.home),
S.documentTypeListItem('page').icon(IconPage),
S.documentTypeListItem('page').icon(PanelsTopLeft),
products(S, context),
collections(S, context),
S.documentTypeListItem('blogPost').icon(IconBlog),
Expand All @@ -24,6 +25,23 @@ export const structure: StructureResolver = (S, context) => {
singleton(S, SINGLETONS.footer),
S.divider(),
singleton(S, SINGLETONS.settings),
S.listItem()
.title('Templates')
.icon(LayoutTemplate)
.child(
S.list()
.title('Templates')
.items([
S.listItem()
.title('Products')
.icon(false)
.child(S.documentTypeList('productTemplate')),
S.listItem()
.title('Collections')
.icon(false)
.child(S.documentTypeList('collectionTemplate')),
]),
),
S.documentTypeListItem('colorScheme').showIcon(true),
singleton(S, SINGLETONS.typography),
singleton(S, SINGLETONS.themeContent),
Expand Down
Loading

0 comments on commit de704ed

Please sign in to comment.