UNPKG

goobs-frontend

Version:

A comprehensive React-based libary for building modern web applications

785 lines (752 loc) 23.5 kB
// -------------------------------------------------------------------------- // ACCORDION THEME SYSTEM // -------------------------------------------------------------------------- import React from 'react' export interface AccordionTheme { container: { background: string border: string borderRadius: string boxShadow: string backdropFilter: string backgroundImage?: string } containerHover: { transform: string boxShadow: string borderColor?: string } containerExpanded: { boxShadow: string background: string borderColor?: string backgroundImage?: string } summary: { background: string color: string fontFamily: string fontSize: string fontWeight: string | number letterSpacing: string textShadow?: string borderBottom: string minHeight: string height?: string maxHeight?: string whiteSpace?: string overflow?: string textOverflow?: string minWidth?: string } summaryHover: { backgroundColor: string color: string transform?: string textShadow?: string } summaryExpanded: { backgroundColor: string borderBottomColor: string color: string fontWeight: string | number textShadow?: string } details: { background: string borderTop: string color: string fontFamily: string fontSize: string lineHeight: string | number backdropFilter: string } icon: { color: string filter?: string } iconHover: { color: string transform: string filter?: string } iconExpanded: { transform: string color: string filter?: string } transition: string // Mobile responsive overrides mobile: { borderRadius: string boxShadow: string summaryPadding: string summaryMinHeight: string detailsPadding: string summaryFontSize: string } // Tablet responsive overrides tablet: { detailsPadding: string } // Desktop responsive overrides desktop: { detailsPadding: string } } export interface AccordionStyles { // Theme selection theme?: 'light' | 'dark' | 'sacred' // Container styling backgroundColor?: string borderColor?: string borderRadius?: string borderWidth?: string boxShadow?: string backdropFilter?: string backgroundImage?: string // Hover states hoverBackgroundColor?: string hoverBorderColor?: string hoverBoxShadow?: string hoverTransform?: string // Expanded states expandedBackgroundColor?: string expandedBorderColor?: string expandedBoxShadow?: string expandedBackgroundImage?: string // Summary styling summaryBackgroundColor?: string summaryColor?: string summaryFontFamily?: string summaryFontSize?: string summaryFontWeight?: string | number summaryLetterSpacing?: string summaryTextShadow?: string summaryBorderBottom?: string summaryWhiteSpace?: string summaryOverflow?: string summaryTextOverflow?: string // Summary hover summaryHoverBackgroundColor?: string summaryHoverColor?: string summaryHoverTransform?: string summaryHoverTextShadow?: string // Summary expanded summaryExpandedBackgroundColor?: string summaryExpandedBorderBottomColor?: string summaryExpandedColor?: string summaryExpandedFontWeight?: string | number summaryExpandedTextShadow?: string // Details styling detailsBackgroundColor?: string detailsBorderTop?: string detailsColor?: string detailsFontFamily?: string detailsFontSize?: string detailsLineHeight?: string | number detailsBackdropFilter?: string // Icon styling iconColor?: string iconFilter?: string iconHoverColor?: string iconHoverTransform?: string iconHoverFilter?: string iconExpandedTransform?: string iconExpandedColor?: string iconExpandedFilter?: string // Layout and spacing padding?: string summaryPadding?: string detailsPadding?: string margin?: string marginBottom?: string minHeight?: string summaryMinHeight?: string // Transitions transitionDuration?: string transitionEasing?: string // States disabled?: boolean outline?: boolean // Dimensions width?: string maxWidth?: string minWidth?: string height?: string maxHeight?: string // Navigation level (for indentation) level?: number levelIndentBase?: number levelIndentIncrement?: number } export const accordionThemes: Record< 'light' | 'dark' | 'sacred', AccordionTheme > = { light: { container: { background: 'white', border: 'none', borderRadius: '8px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.1)', backdropFilter: 'none', }, containerHover: { transform: 'translateY(-1px)', boxShadow: '0px 3px 6px rgba(0, 0, 0, 0.15)', }, containerExpanded: { boxShadow: '0px 3px 8px rgba(0, 0, 0, 0.12)', background: '#fafafa', }, summary: { background: '#f5f7fa', color: 'inherit', fontFamily: 'merriweather', fontSize: '14px', fontWeight: 500, letterSpacing: 'normal', borderBottom: 'none', minHeight: '32px', height: '32px', maxHeight: '32px', whiteSpace: 'nowrap', minWidth: 'fit-content', }, summaryHover: { backgroundColor: '#e8f0fe', color: 'inherit', }, summaryExpanded: { backgroundColor: '#e3f2fd', borderBottomColor: 'rgba(0, 0, 0, 0.12)', color: 'inherit', fontWeight: 500, }, details: { background: 'white', borderTop: '1px solid rgba(0, 0, 0, 0.08)', color: 'inherit', fontFamily: 'inherit', fontSize: '16px', lineHeight: 1.6, backdropFilter: 'none', }, icon: { color: '#000000', filter: 'none', }, iconHover: { color: '#000000', transform: 'scale(1.1)', filter: 'none', }, iconExpanded: { transform: 'rotate(180deg)', color: '#000000', filter: 'none', }, transition: 'all 0.2s ease', mobile: { borderRadius: '6px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.1)', summaryPadding: '4px 16px', summaryMinHeight: '32px', detailsPadding: '12px 16px', summaryFontSize: '14px', }, tablet: { detailsPadding: '14px 18px', }, desktop: { detailsPadding: '16px 24px', }, }, dark: { container: { background: 'rgba(31, 41, 55, 0.95)', border: 'none', borderRadius: '8px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.3)', backdropFilter: 'blur(8px)', }, containerHover: { transform: 'translateY(-1px)', boxShadow: '0px 3px 6px rgba(0, 0, 0, 0.25)', }, containerExpanded: { boxShadow: '0px 3px 8px rgba(0, 0, 0, 0.3)', background: 'rgba(30, 58, 138, 0.2)', }, summary: { background: 'rgba(17, 24, 39, 0.5)', color: 'rgb(243, 244, 246)', fontFamily: 'merriweather', fontSize: '14px', fontWeight: 500, letterSpacing: 'normal', borderBottom: 'none', minHeight: '32px', height: '32px', maxHeight: '32px', whiteSpace: 'nowrap', minWidth: 'fit-content', }, summaryHover: { backgroundColor: 'rgba(30, 58, 138, 0.3)', color: 'rgb(96, 165, 250)', }, summaryExpanded: { backgroundColor: 'rgba(30, 58, 138, 0.4)', borderBottomColor: 'rgba(96, 165, 250, 0.3)', color: 'rgb(96, 165, 250)', fontWeight: 500, }, details: { background: 'rgba(17, 24, 39, 0.8)', borderTop: '1px solid rgba(75, 85, 99, 0.5)', color: 'rgb(209, 213, 219)', fontFamily: 'inherit', fontSize: '16px', lineHeight: 1.6, backdropFilter: 'blur(4px)', }, icon: { color: 'rgb(156, 163, 175)', filter: 'none', }, iconHover: { color: 'rgb(96, 165, 250)', transform: 'scale(1.1)', filter: 'none', }, iconExpanded: { transform: 'rotate(180deg)', color: 'rgb(96, 165, 250)', filter: 'none', }, transition: 'all 0.2s ease', mobile: { borderRadius: '6px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.3)', summaryPadding: '4px 16px', summaryMinHeight: '32px', detailsPadding: '12px 16px', summaryFontSize: '14px', }, tablet: { detailsPadding: '14px 18px', }, desktop: { detailsPadding: '16px 24px', }, }, sacred: { container: { background: '#0a0a0a', border: '1px solid rgba(255, 215, 0, 0.3)', borderRadius: '8px', boxShadow: '0 0 15px rgba(255, 215, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.3)', backdropFilter: 'blur(8px)', backgroundImage: ` linear-gradient(rgba(255, 215, 0, 0.02), rgba(255, 215, 0, 0.02)), radial-gradient(circle at top right, rgba(255, 215, 0, 0.08) 0%, transparent 50%) `, }, containerHover: { transform: 'translateY(-1px)', boxShadow: '0 0 25px rgba(255, 215, 0, 0.4), 0 4px 8px rgba(0, 0, 0, 0.4)', }, containerExpanded: { boxShadow: '0 0 30px rgba(255, 215, 0, 0.5), 0 6px 12px rgba(0, 0, 0, 0.5)', background: '#0a0a0a', borderColor: '#FFD700', backgroundImage: ` linear-gradient(rgba(255, 215, 0, 0.02), rgba(255, 215, 0, 0.02)), radial-gradient(circle at top right, rgba(255, 215, 0, 0.08) 0%, transparent 50%) `, }, summary: { background: 'transparent', color: 'rgba(255, 215, 0, 0.9)', fontFamily: '"Cinzel", serif', fontSize: '14px', fontWeight: 600, letterSpacing: '0.05em', textShadow: '0 0 8px rgba(255, 215, 0, 0.6)', borderBottom: 'none', minHeight: '32px', height: '32px', maxHeight: '32px', whiteSpace: 'nowrap', minWidth: 'fit-content', }, summaryHover: { backgroundColor: 'rgba(255, 215, 0, 0.1)', color: '#FFD700', textShadow: '0 0 8px rgba(255, 215, 0, 0.6)', }, summaryExpanded: { backgroundColor: 'rgba(255, 215, 0, 0.05)', borderBottomColor: 'rgba(255, 215, 0, 0.3)', color: '#FFD700', fontWeight: 600, textShadow: '0 0 8px rgba(255, 215, 0, 0.6)', }, details: { background: 'transparent', borderTop: '1px solid rgba(255, 215, 0, 0.2)', color: 'rgba(255, 215, 0, 0.8)', fontFamily: 'inherit', fontSize: '16px', lineHeight: 1.6, backdropFilter: 'blur(2px)', }, icon: { color: '#FFD700', filter: 'drop-shadow(0 0 4px rgba(255, 215, 0, 0.5))', }, iconHover: { color: '#FFD700', transform: 'scale(1.1)', filter: 'drop-shadow(0 0 8px rgba(255, 215, 0, 0.8))', }, iconExpanded: { transform: 'rotate(180deg)', color: '#FFD700', filter: 'drop-shadow(0 0 8px rgba(255, 215, 0, 0.8))', }, transition: 'all 0.3s ease', mobile: { borderRadius: '6px', boxShadow: '0 0 10px rgba(255, 215, 0, 0.2)', summaryPadding: '4px 16px', summaryMinHeight: '32px', detailsPadding: '12px 16px', summaryFontSize: '14px', }, tablet: { detailsPadding: '14px 18px', }, desktop: { detailsPadding: '24px 32px', }, }, } // Helper function to get computed theme with custom style overrides export const getAccordionTheme = (styles?: AccordionStyles): AccordionTheme => { const theme = styles?.theme || 'light' const baseTheme = accordionThemes[theme] if (!styles) { return baseTheme } return { ...baseTheme, container: { background: styles.backgroundColor || baseTheme.container.background, border: styles.borderColor ? `${styles.borderWidth || '1px'} solid ${styles.borderColor}` : baseTheme.container.border, borderRadius: styles.borderRadius || baseTheme.container.borderRadius, boxShadow: styles.boxShadow || baseTheme.container.boxShadow, backdropFilter: styles.backdropFilter || baseTheme.container.backdropFilter, backgroundImage: styles.backgroundImage || baseTheme.container.backgroundImage, }, containerHover: { transform: styles.hoverTransform || baseTheme.containerHover.transform, boxShadow: styles.hoverBoxShadow || baseTheme.containerHover.boxShadow, borderColor: styles.hoverBorderColor || baseTheme.containerHover.borderColor, }, containerExpanded: { boxShadow: styles.expandedBoxShadow || baseTheme.containerExpanded.boxShadow, background: styles.expandedBackgroundColor || baseTheme.containerExpanded.background, borderColor: styles.expandedBorderColor || baseTheme.containerExpanded.borderColor, backgroundImage: styles.expandedBackgroundImage || baseTheme.containerExpanded.backgroundImage, }, summary: { background: styles.summaryBackgroundColor || baseTheme.summary.background, color: styles.summaryColor || baseTheme.summary.color, fontFamily: styles.summaryFontFamily || baseTheme.summary.fontFamily, fontSize: styles.summaryFontSize || baseTheme.summary.fontSize, fontWeight: styles.summaryFontWeight || baseTheme.summary.fontWeight, letterSpacing: styles.summaryLetterSpacing || baseTheme.summary.letterSpacing, textShadow: styles.summaryTextShadow || baseTheme.summary.textShadow, borderBottom: styles.summaryBorderBottom || baseTheme.summary.borderBottom, minHeight: styles.summaryMinHeight || baseTheme.summary.minHeight, height: styles.height || baseTheme.summary.height, maxHeight: styles.maxHeight || baseTheme.summary.maxHeight, whiteSpace: styles.summaryWhiteSpace || baseTheme.summary.whiteSpace, overflow: styles.summaryOverflow || baseTheme.summary.overflow, textOverflow: styles.summaryTextOverflow || baseTheme.summary.textOverflow, minWidth: styles.minWidth || baseTheme.summary.minWidth, }, summaryHover: { backgroundColor: styles.summaryHoverBackgroundColor || baseTheme.summaryHover.backgroundColor, color: styles.summaryHoverColor || baseTheme.summaryHover.color, transform: styles.summaryHoverTransform || baseTheme.summaryHover.transform, textShadow: styles.summaryHoverTextShadow || baseTheme.summaryHover.textShadow, }, summaryExpanded: { backgroundColor: styles.summaryExpandedBackgroundColor || baseTheme.summaryExpanded.backgroundColor, borderBottomColor: styles.summaryExpandedBorderBottomColor || baseTheme.summaryExpanded.borderBottomColor, color: styles.summaryExpandedColor || baseTheme.summaryExpanded.color, fontWeight: styles.summaryExpandedFontWeight || baseTheme.summaryExpanded.fontWeight, textShadow: styles.summaryExpandedTextShadow || baseTheme.summaryExpanded.textShadow, }, details: { background: styles.detailsBackgroundColor || baseTheme.details.background, borderTop: styles.detailsBorderTop || baseTheme.details.borderTop, color: styles.detailsColor || baseTheme.details.color, fontFamily: styles.detailsFontFamily || baseTheme.details.fontFamily, fontSize: styles.detailsFontSize || baseTheme.details.fontSize, lineHeight: styles.detailsLineHeight || baseTheme.details.lineHeight, backdropFilter: styles.detailsBackdropFilter || baseTheme.details.backdropFilter, }, icon: { color: styles.iconColor || baseTheme.icon.color, filter: styles.iconFilter || baseTheme.icon.filter, }, iconHover: { color: styles.iconHoverColor || baseTheme.iconHover.color, transform: styles.iconHoverTransform || baseTheme.iconHover.transform, filter: styles.iconHoverFilter || baseTheme.iconHover.filter, }, iconExpanded: { transform: styles.iconExpandedTransform || baseTheme.iconExpanded.transform, color: styles.iconExpandedColor || baseTheme.iconExpanded.color, filter: styles.iconExpandedFilter || baseTheme.iconExpanded.filter, }, transition: styles.transitionDuration ? `all ${styles.transitionDuration} ${styles.transitionEasing || 'cubic-bezier(0.4, 0, 0.2, 1)'}` : baseTheme.transition, } } // Helper function to get responsive styles const getResponsiveStyles = ( theme: AccordionTheme, breakpoint: 'mobile' | 'tablet' | 'desktop' ): Partial<React.CSSProperties> => { const responsive = theme[breakpoint] switch (breakpoint) { case 'mobile': return { borderRadius: (responsive as any).borderRadius, boxShadow: (responsive as any).boxShadow, } case 'tablet': case 'desktop': return {} default: return {} } } // Helper function to get responsive summary styles const getResponsiveSummaryStyles = ( theme: AccordionTheme, breakpoint: 'mobile' | 'tablet' | 'desktop' ): Partial<React.CSSProperties> => { const responsive = theme[breakpoint] switch (breakpoint) { case 'mobile': return { padding: (responsive as any).summaryPadding, minHeight: (responsive as any).summaryMinHeight, fontSize: (responsive as any).summaryFontSize, } default: return {} } } // Helper function to get responsive details styles const getResponsiveDetailsStyles = ( theme: AccordionTheme, breakpoint: 'mobile' | 'tablet' | 'desktop' ): Partial<React.CSSProperties> => { const responsive = theme[breakpoint] return { padding: responsive.detailsPadding, } } // Main style generator function export const getAccordionStyles = ( styles?: AccordionStyles, isHovered?: boolean, isExpanded?: boolean, isDisabled?: boolean ) => { const themeConfig = getAccordionTheme(styles) const isSacredTheme = styles?.theme === 'sacred' // Calculate level-based indentation const level = styles?.level || 0 const indentBase = styles?.levelIndentBase || 0 const indentIncrement = styles?.levelIndentIncrement || 6 const levelIndent = indentBase + level * indentIncrement // Base container styles const containerStyle: React.CSSProperties = { marginBottom: styles?.marginBottom || '8px', margin: styles?.margin, width: styles?.width || '100%', maxWidth: styles?.maxWidth, minWidth: styles?.minWidth, borderRadius: themeConfig.container.borderRadius, overflow: 'hidden', position: 'relative', transition: themeConfig.transition, backgroundColor: themeConfig.container.background, border: themeConfig.container.border, boxShadow: themeConfig.container.boxShadow, backdropFilter: themeConfig.container.backdropFilter, backgroundImage: themeConfig.container.backgroundImage, ...(isHovered && !isDisabled && { transform: themeConfig.containerHover.transform, boxShadow: themeConfig.containerHover.boxShadow, borderColor: themeConfig.containerHover.borderColor, }), ...(isExpanded && !isDisabled && { boxShadow: themeConfig.containerExpanded.boxShadow, background: themeConfig.containerExpanded.background, borderColor: themeConfig.containerExpanded.borderColor, backgroundImage: themeConfig.containerExpanded.backgroundImage, }), ...(isDisabled && { opacity: isSacredTheme ? 0.6 : 0.8, backgroundColor: isSacredTheme ? 'rgba(0, 0, 0, 0.8)' : '#f8f8f8', borderColor: isSacredTheme ? 'rgba(255, 215, 0, 0.1)' : undefined, }), ...(styles?.outline === false && { border: 'none', boxShadow: 'none' }), } // Base summary styles with level-based indentation const summaryStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: styles?.summaryPadding || `4px 16px 4px ${levelIndent}px`, height: '32px', maxHeight: '32px', minWidth: 'fit-content', transition: themeConfig.transition, position: 'relative', backgroundColor: themeConfig.summary.background, color: themeConfig.summary.color, fontFamily: themeConfig.summary.fontFamily, fontWeight: themeConfig.summary.fontWeight, fontSize: themeConfig.summary.fontSize, letterSpacing: themeConfig.summary.letterSpacing, cursor: isDisabled ? 'not-allowed' : 'pointer', minHeight: themeConfig.summary.minHeight, borderBottom: themeConfig.summary.borderBottom, textShadow: themeConfig.summary.textShadow, whiteSpace: 'nowrap', ...(isHovered && !isDisabled && { backgroundColor: themeConfig.summaryHover.backgroundColor, color: themeConfig.summaryHover.color, transform: themeConfig.summaryHover.transform, textShadow: themeConfig.summaryHover.textShadow, }), ...(isExpanded && !isDisabled && { backgroundColor: themeConfig.summaryExpanded.backgroundColor, borderBottom: `1px solid ${themeConfig.summaryExpanded.borderBottomColor}`, color: themeConfig.summaryExpanded.color, fontWeight: themeConfig.summaryExpanded.fontWeight, textShadow: themeConfig.summaryExpanded.textShadow, }), ...(isDisabled && { opacity: 1, color: isSacredTheme ? 'rgba(255, 215, 0, 0.3)' : '#666', cursor: 'not-allowed', }), } // Base details styles const detailsStyle: React.CSSProperties = { padding: styles?.detailsPadding || '16px', position: 'relative', backgroundColor: themeConfig.details.background, borderTop: themeConfig.details.borderTop, color: themeConfig.details.color, fontFamily: themeConfig.details.fontFamily, fontSize: themeConfig.details.fontSize, lineHeight: themeConfig.details.lineHeight, backdropFilter: themeConfig.details.backdropFilter, ...(styles?.outline === false && { borderTop: 'none' }), } // Base icon styles const iconStyle: React.CSSProperties = { width: '24px', height: '24px', transition: themeConfig.transition, color: themeConfig.icon.color, filter: themeConfig.icon.filter, ...(isHovered && !isDisabled && { color: themeConfig.iconHover.color, transform: themeConfig.iconHover.transform, filter: themeConfig.iconHover.filter, }), ...(isExpanded && !isDisabled && { transform: themeConfig.iconExpanded.transform, color: themeConfig.iconExpanded.color, filter: themeConfig.iconExpanded.filter, }), ...(isDisabled && { color: isSacredTheme ? 'rgba(255, 215, 0, 0.3)' : '#999', }), } return { container: containerStyle, summary: summaryStyle, details: detailsStyle, icon: iconStyle, responsive: { mobile: { container: getResponsiveStyles(themeConfig, 'mobile'), summary: getResponsiveSummaryStyles(themeConfig, 'mobile'), details: getResponsiveDetailsStyles(themeConfig, 'mobile'), }, tablet: { container: getResponsiveStyles(themeConfig, 'tablet'), summary: getResponsiveSummaryStyles(themeConfig, 'tablet'), details: getResponsiveDetailsStyles(themeConfig, 'tablet'), }, desktop: { container: getResponsiveStyles(themeConfig, 'desktop'), summary: getResponsiveSummaryStyles(themeConfig, 'desktop'), details: getResponsiveDetailsStyles(themeConfig, 'desktop'), }, }, } }