@vyuh/sanity-schema-marketing
Version:
The Sanity schema package for the Marketing content blocks
186 lines (175 loc) • 4.9 kB
text/typescript
import { ContentDescriptor, ContentSchemaBuilder } from '@vyuh/sanity-schema-core';
import { TbChartBar as Icon } from 'react-icons/tb';
import { defineField, defineType } from 'sanity';
/**
* Stats section schema for marketing pages
* Based on common patterns from Tailwind UI stats sections
*/
/**
* Content descriptor for stats content items
*/
export class StatsDescriptor extends ContentDescriptor {
static readonly schemaName = 'marketing.stats';
constructor(props: Partial<StatsDescriptor>) {
super(StatsDescriptor.schemaName, props);
}
}
/**
* Default layout schema for stats content items
*/
export const defaultStatsLayout = defineType({
name: `${StatsDescriptor.schemaName}.layout.default`,
title: 'Default',
type: 'object',
icon: Icon,
fields: [
defineField({
name: 'variant',
title: 'Variant',
type: 'string',
description: 'The style variant for the stats section',
options: {
list: [
{ title: 'Simple', value: 'simple' },
{ title: 'With description', value: 'with-description' },
],
},
initialValue: 'simple',
validation: (Rule) => Rule.required(),
}),
],
preview: {
select: {
variant: 'variant',
},
prepare({ variant }) {
const variantDisplay = variant
? variant
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
: 'Simple';
return {
title: `Stats Layout: ${variantDisplay}`,
subtitle: 'Default',
media: Icon,
};
},
},
});
export class StatsSchemaBuilder extends ContentSchemaBuilder {
private schema = defineType({
name: StatsDescriptor.schemaName,
title: 'Stats Section',
type: 'object',
icon: Icon,
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
description: 'The main title for the stats section',
}),
defineField({
name: 'subtitle',
title: 'Subtitle',
type: 'text',
description: 'A supporting text that appears with the title',
}),
defineField({
name: 'description',
title: 'Description',
type: 'text',
description:
'Detailed description for variants that include descriptions',
}),
defineField({
name: 'image',
title: 'Image',
type: 'image',
description: 'Image for variants that include an image',
options: {
hotspot: true,
},
}),
defineField({
name: 'stats',
title: 'Statistics',
type: 'array',
of: [
{
type: 'object',
fields: [
defineField({
name: 'value',
title: 'Value',
type: 'string',
description: 'The statistic value (e.g., "35K" or "99.9%")',
validation: (Rule) => Rule.required(),
}),
defineField({
name: 'label',
title: 'Label',
type: 'string',
description: 'Label describing the statistic',
validation: (Rule) => Rule.required(),
}),
defineField({
name: 'description',
title: 'Description',
type: 'text',
description: 'Optional longer description of the statistic',
}),
defineField({
name: 'icon',
title: 'Icon',
type: 'string',
description: 'Optional icon name from your icon library',
}),
],
preview: {
select: {
value: 'value',
label: 'label',
icon: 'icon',
},
prepare({ value, label, icon }) {
return {
title: `Stat: ${value || 'Untitled'}`,
subtitle: label,
media: icon ? Icon : undefined,
};
},
},
},
],
validation: (Rule) => Rule.required().min(1),
}),
defineField({
name: 'action',
title: 'Action',
type: 'vyuh.action',
description: 'Optional call-to-action button',
}),
],
preview: {
select: {
title: 'title',
stats: 'stats',
},
prepare({ title, stats = [] }) {
return {
title: `Stats: ${title || 'Untitled'}`,
subtitle: `${stats.length} stat${stats.length === 1 ? '' : 's'}`,
media: Icon,
};
},
},
});
constructor() {
super(StatsDescriptor.schemaName);
}
build(descriptors: ContentDescriptor[]) {
return this.schema;
}
}