UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

488 lines (487 loc) 15.5 kB
/** * Theme accessor utility class */ export class ThemeAccessor { constructor(theme, cssPrefix = '--cms') { this.theme = theme; this.cssPrefix = cssPrefix; } /** * Get color value with fallback */ color(key, fallback) { const value = this.theme.colors?.[key]; const result = { value: value || fallback || '#000000', cssVar: `${this.cssPrefix}-color-${key}`, }; if (fallback !== undefined) { result.fallback = fallback; } return result; } /** * Get spacing value with fallback */ spacing(key, fallback) { const value = this.theme.spacing?.[key]; const result = { value: value || fallback || '0', cssVar: `${this.cssPrefix}-spacing-${key}`, }; if (fallback !== undefined) { result.fallback = fallback; } return result; } /** * Get typography value with fallback */ typography(category, key, fallback) { const categoryValues = this.theme.typography?.[category]; const value = categoryValues?.[key]; const result = { value: value || fallback || '', cssVar: `${this.cssPrefix}-${category}-${key}`, }; if (fallback !== undefined) { result.fallback = fallback; } return result; } /** * Get border radius value with fallback */ borderRadius(key, fallback) { const value = this.theme.borderRadius?.[key]; const result = { value: value || fallback || '0', cssVar: `${this.cssPrefix}-border-radius-${key}`, }; if (fallback !== undefined) { result.fallback = fallback; } return result; } /** * Get shadow value with fallback */ shadow(key, fallback) { const value = this.theme.shadows?.[key]; const result = { value: value || fallback || 'none', cssVar: `${this.cssPrefix}-shadow-${key}`, }; if (fallback !== undefined) { result.fallback = fallback; } return result; } /** * Get breakpoint value with fallback */ breakpoint(key, fallback) { const value = this.theme.breakpoints?.[key]; const result = { value: value || fallback || '0px', cssVar: `${this.cssPrefix}-breakpoint-${key}`, }; if (fallback !== undefined) { result.fallback = fallback; } return result; } /** * Get CSS custom property reference */ cssVar(category, key) { return `var(${this.cssPrefix}-${category}-${key})`; } /** * Get CSS custom property reference with fallback */ cssVarWithFallback(category, key, fallback) { return `var(${this.cssPrefix}-${category}-${key}, ${fallback})`; } } /** * CSS-in-JS Theme Adapter */ export class CssInJsAdapter { constructor(theme, options = {}) { this.theme = theme; this.options = { cssPrefix: '--cms', includeFallbacks: true, ...options, }; } /** * Convert CmsTheme to CSS-in-JS format */ toCssInJs() { return { colors: this.convertColors(), typography: this.convertTypography(), spacing: this.convertSpacing(), borderRadius: this.convertBorderRadius(), shadows: this.convertShadows(), breakpoints: this.convertBreakpoints(), zIndex: this.getDefaultZIndex(), }; } convertColors() { const colors = {}; if (this.theme.colors) { Object.entries(this.theme.colors).forEach(([key, value]) => { if (value) { colors[key] = this.applyTransformation(key, value); } }); } return colors; } convertTypography() { return { fontFamily: this.convertObject(this.theme.typography?.fontFamily), fontSize: this.convertObject(this.theme.typography?.fontSize), fontWeight: this.convertObject(this.theme.typography?.fontWeight), lineHeight: this.convertObject(this.theme.typography?.lineHeight), }; } convertSpacing() { return this.convertObject(this.theme.spacing); } convertBorderRadius() { return this.convertObject(this.theme.borderRadius); } convertShadows() { return this.convertObject(this.theme.shadows); } convertBreakpoints() { return this.convertObject(this.theme.breakpoints); } convertObject(obj) { const result = {}; if (obj && typeof obj === 'object') { Object.entries(obj).forEach(([key, value]) => { if (value && typeof value === 'string') { result[key] = this.applyTransformation(key, value); } }); } return result; } applyTransformation(key, value) { const transformation = this.options.transformations?.[key]; return transformation ? transformation(value) : value; } getDefaultZIndex() { return { auto: 0, behind: -1, base: 0, dropdown: 1000, sticky: 1020, fixed: 1030, modalBackdrop: 1040, modal: 1050, popover: 1060, tooltip: 1070, toast: 1080, }; } } /** * Tailwind CSS Theme Adapter */ export class TailwindAdapter { constructor(theme, options = {}) { this.theme = theme; this.options = options; } /** * Convert CmsTheme to Tailwind configuration format */ toTailwindConfig() { return { colors: this.convertColorsForTailwind(), fontFamily: this.convertFontFamilyForTailwind(), fontSize: this.convertFontSizeForTailwind(), fontWeight: this.convertFontWeightForTailwind(), spacing: this.convertSpacingForTailwind(), borderRadius: this.convertBorderRadiusForTailwind(), boxShadow: this.convertShadowsForTailwind(), screens: this.convertBreakpointsForTailwind(), zIndex: this.getDefaultZIndexForTailwind(), }; } convertColorsForTailwind() { const colors = {}; if (this.theme.colors) { Object.entries(this.theme.colors).forEach(([key, value]) => { if (value) { // Convert camelCase to kebab-case for Tailwind const tailwindKey = key.replace(/([A-Z])/g, '-$1').toLowerCase(); colors[tailwindKey] = value; } }); } return colors; } convertFontFamilyForTailwind() { const fontFamily = {}; if (this.theme.typography?.fontFamily) { fontFamily.sans = [this.theme.typography.fontFamily]; } if (this.theme.typography?.fontFamilyMono) { fontFamily.mono = [this.theme.typography.fontFamilyMono]; } return fontFamily; } convertFontSizeForTailwind() { const fontSize = {}; if (this.theme.typography?.fontSize) { Object.entries(this.theme.typography.fontSize).forEach(([key, value]) => { if (value) { // Add corresponding line height from theme if available const lineHeight = this.theme.typography?.lineHeight?.normal || '1.5'; fontSize[key] = [value, { lineHeight }]; } }); } return fontSize; } convertFontWeightForTailwind() { const fontWeight = {}; if (this.theme.typography?.fontWeight) { Object.entries(this.theme.typography.fontWeight).forEach(([key, value]) => { if (value) { fontWeight[key] = value; } }); } return fontWeight; } convertSpacingForTailwind() { const spacing = {}; if (this.theme.spacing) { Object.entries(this.theme.spacing).forEach(([key, value]) => { if (value) { spacing[key] = value; } }); } return spacing; } convertBorderRadiusForTailwind() { const borderRadius = {}; if (this.theme.borderRadius) { Object.entries(this.theme.borderRadius).forEach(([key, value]) => { if (value) { borderRadius[key] = value; } }); } return borderRadius; } convertShadowsForTailwind() { const boxShadow = {}; if (this.theme.shadows) { Object.entries(this.theme.shadows).forEach(([key, value]) => { if (value) { boxShadow[key] = value; } }); } return boxShadow; } convertBreakpointsForTailwind() { const screens = {}; if (this.theme.breakpoints) { Object.entries(this.theme.breakpoints).forEach(([key, value]) => { if (value) { screens[key] = value; } }); } return screens; } getDefaultZIndexForTailwind() { return { auto: 'auto', '0': '0', '10': '10', '20': '20', '30': '30', '40': '40', '50': '50', }; } } /** * CSS Variables Adapter */ export class CssVariablesAdapter { constructor(theme, options = {}) { this.theme = theme; this.options = { cssPrefix: '--cms', includeFallbacks: true, ...options, }; } /** * Convert CmsTheme to CSS custom properties */ toCssVariables() { const cssVars = {}; // Convert colors if (this.theme.colors) { Object.entries(this.theme.colors).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-color-${this.kebabCase(key)}`] = value; } }); } // Convert typography if (this.theme.typography) { if (this.theme.typography.fontFamily) { cssVars[`${this.options.cssPrefix}-font-family`] = this.theme.typography.fontFamily; } if (this.theme.typography.fontFamilyMono) { cssVars[`${this.options.cssPrefix}-font-family-mono`] = this.theme.typography.fontFamilyMono; } // Font sizes if (this.theme.typography.fontSize) { Object.entries(this.theme.typography.fontSize).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-font-size-${this.kebabCase(key)}`] = value; } }); } // Font weights if (this.theme.typography.fontWeight) { Object.entries(this.theme.typography.fontWeight).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-font-weight-${this.kebabCase(key)}`] = value; } }); } // Line heights if (this.theme.typography.lineHeight) { Object.entries(this.theme.typography.lineHeight).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-line-height-${this.kebabCase(key)}`] = value; } }); } } // Convert spacing if (this.theme.spacing) { Object.entries(this.theme.spacing).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-spacing-${this.kebabCase(key)}`] = value; } }); } // Convert border radius if (this.theme.borderRadius) { Object.entries(this.theme.borderRadius).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-border-radius-${this.kebabCase(key)}`] = value; } }); } // Convert shadows if (this.theme.shadows) { Object.entries(this.theme.shadows).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-shadow-${this.kebabCase(key)}`] = value; } }); } // Convert breakpoints if (this.theme.breakpoints) { Object.entries(this.theme.breakpoints).forEach(([key, value]) => { if (value) { cssVars[`${this.options.cssPrefix}-breakpoint-${this.kebabCase(key)}`] = value; } }); } return cssVars; } /** * Generate CSS string from variables */ toCssString(selector = ':root') { const variables = this.toCssVariables(); const declarations = Object.entries(variables) .map(([property, value]) => ` ${property}: ${value};`) .join('\n'); return `${selector} {\n${declarations}\n}`; } kebabCase(str) { return str.replace(/([A-Z])/g, '-$1').toLowerCase(); } } /** * Factory functions for creating theme adapters */ export function createThemeAccessor(theme, cssPrefix) { return new ThemeAccessor(theme, cssPrefix); } export function createCssInJsAdapter(theme, options) { return new CssInJsAdapter(theme, options); } export function createTailwindAdapter(theme, options) { return new TailwindAdapter(theme, options); } export function createCssVariablesAdapter(theme, options) { return new CssVariablesAdapter(theme, options); } /** * Utility functions for common theme operations */ export function getThemeValue(theme, path, fallback) { const keys = path.split('.'); let current = theme; for (const key of keys) { if (current && typeof current === 'object' && key in current) { current = current[key]; } else { return fallback; } } return typeof current === 'string' ? current : fallback; } export function hasThemeValue(theme, path) { return getThemeValue(theme, path) !== undefined; } export function mergeThemeTokens(base, override) { return { ...base, ...override, colors: { ...base.colors, ...override.colors }, typography: { ...base.typography, ...override.typography }, spacing: { ...base.spacing, ...override.spacing }, borderRadius: { ...base.borderRadius, ...override.borderRadius }, shadows: { ...base.shadows, ...override.shadows }, breakpoints: { ...base.breakpoints, ...override.breakpoints }, }; } export default { ThemeAccessor, CssInJsAdapter, TailwindAdapter, CssVariablesAdapter, createThemeAccessor, createCssInJsAdapter, createTailwindAdapter, createCssVariablesAdapter, getThemeValue, hasThemeValue, mergeThemeTokens, };