goobs-frontend
Version:
A comprehensive React-based libary for building modern web applications
568 lines (559 loc) • 15 kB
text/typescript
/**
* @fileoverview Switch component theme system with light, dark, and sacred themes.
*/
import React from 'react'
import { TRANSITIONS, SHADOWS } from './shared'
export interface SwitchTheme {
/** Main container styling */
container: React.CSSProperties
/** Container when disabled */
containerDisabled: React.CSSProperties
/** Track (background) styling */
track: React.CSSProperties
/** Track without outline */
trackNoOutline: React.CSSProperties
/** Track when checked */
trackChecked: React.CSSProperties
/** Track when focused */
trackFocused: React.CSSProperties
/** Track when disabled */
trackDisabled: React.CSSProperties
/** Thumb (switch handle) styling */
thumb: React.CSSProperties
/** Thumb when checked */
thumbChecked: React.CSSProperties
/** Thumb when disabled */
thumbDisabled: React.CSSProperties
/** Input element styling */
input: React.CSSProperties
/** Input when disabled */
inputDisabled: React.CSSProperties
/** Sacred glyph styling */
glyph: React.CSSProperties
/** Sacred glyph left position */
glyphLeft: React.CSSProperties
/** Sacred glyph right position */
glyphRight: React.CSSProperties
/** Sacred glyph when visible */
glyphVisible: React.CSSProperties
/** Sacred shimmer effect */
shimmer: React.CSSProperties
/** Left label styling */
leftLabel: React.CSSProperties
/** Right label styling */
rightLabel: React.CSSProperties
}
export interface SwitchStyles {
/** Theme selection: light, dark, or sacred */
theme?: 'light' | 'dark' | 'sacred'
/** Whether to show outline */
outline?: boolean
/** Whether the switch is disabled */
disabled?: boolean
/** Whether the switch is checked */
checked?: boolean
/** Custom track width */
trackWidth?: string
/** Custom track height */
trackHeight?: string
/** Custom track background color */
trackBackground?: string
/** Custom track border color */
trackBorderColor?: string
/** Custom track border radius */
trackBorderRadius?: string
/** Custom thumb size */
thumbSize?: string
/** Custom thumb background color */
thumbBackground?: string
/** Custom thumb border color */
thumbBorderColor?: string
/** Custom label color */
labelColor?: string
/** Custom label font family */
labelFontFamily?: string
/** Custom label font size */
labelFontSize?: string
/** Custom label font weight */
labelFontWeight?: string | number
/** Custom transition duration */
transitionDuration?: string
/** Custom checked track color */
checkedTrackColor?: string
/** Custom checked thumb color */
checkedThumbColor?: string
/** Custom hover effects */
hoverEffects?: boolean
/** Custom focus effects */
focusEffects?: boolean
/** Custom sacred glyph left */
sacredGlyphLeft?: string
/** Custom sacred glyph right */
sacredGlyphRight?: string
}
const lightTheme: SwitchTheme = {
container: {
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
cursor: 'pointer',
gap: '8px',
transition: TRANSITIONS.medium,
},
containerDisabled: {
cursor: 'not-allowed',
opacity: 0.6,
},
track: {
position: 'relative',
width: '48px',
height: '24px',
backgroundColor: 'rgba(156, 163, 175, 0.3)',
borderRadius: '12px',
transition: TRANSITIONS.medium,
border: '1px solid rgba(156, 163, 175, 0.2)',
backdropFilter: 'blur(4px)',
overflow: 'hidden',
},
trackNoOutline: {
border: 'none',
},
trackChecked: {
backgroundColor: 'rgb(59, 130, 246)',
borderColor: 'transparent',
boxShadow: '0 0 20px rgba(59, 130, 246, 0.3)',
},
trackFocused: {
boxShadow: '0 0 0 3px rgba(59, 130, 246, 0.2)',
},
trackDisabled: {
backgroundColor: 'rgba(156, 163, 175, 0.2)',
borderColor: 'rgba(156, 163, 175, 0.1)',
},
thumb: {
position: 'absolute',
top: '2px',
left: '2px',
width: '20px',
height: '20px',
backgroundColor: 'white',
borderRadius: '50%',
transition: TRANSITIONS.medium,
boxShadow: SHADOWS.light.small,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '10px',
color: '#6B7280',
},
thumbChecked: {
transform: 'translateX(24px)',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.3)',
color: '#3B82F6',
},
thumbDisabled: {
backgroundColor: 'rgba(249, 250, 251, 0.8)',
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.1)',
color: '#9CA3AF',
},
input: {
position: 'absolute',
opacity: 0,
width: '100%',
height: '100%',
margin: 0,
cursor: 'pointer',
},
inputDisabled: {
cursor: 'not-allowed',
},
glyph: {
display: 'none',
},
glyphLeft: {
display: 'none',
},
glyphRight: {
display: 'none',
},
glyphVisible: {
display: 'none',
},
shimmer: {
display: 'none',
},
leftLabel: {
fontSize: '14px',
fontWeight: 500,
color: 'rgb(55, 65, 81)',
fontFamily: 'Inter, system-ui, sans-serif',
userSelect: 'none',
transition: TRANSITIONS.medium,
},
rightLabel: {
fontSize: '14px',
fontWeight: 500,
color: 'rgb(55, 65, 81)',
fontFamily: 'Inter, system-ui, sans-serif',
userSelect: 'none',
transition: TRANSITIONS.medium,
},
}
const darkTheme: SwitchTheme = {
container: {
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
cursor: 'pointer',
gap: '8px',
transition: TRANSITIONS.medium,
},
containerDisabled: {
cursor: 'not-allowed',
opacity: 0.6,
},
track: {
position: 'relative',
width: '48px',
height: '24px',
backgroundColor: 'rgba(75, 85, 99, 0.4)',
borderRadius: '12px',
transition: TRANSITIONS.medium,
border: '1px solid rgba(75, 85, 99, 0.3)',
backdropFilter: 'blur(4px)',
overflow: 'hidden',
},
trackNoOutline: {
border: 'none',
},
trackChecked: {
backgroundColor: 'rgb(59, 130, 246)',
borderColor: 'transparent',
boxShadow: '0 0 20px rgba(59, 130, 246, 0.4)',
},
trackFocused: {
boxShadow: '0 0 0 3px rgba(59, 130, 246, 0.3)',
},
trackDisabled: {
backgroundColor: 'rgba(75, 85, 99, 0.2)',
borderColor: 'rgba(75, 85, 99, 0.1)',
},
thumb: {
position: 'absolute',
top: '2px',
left: '2px',
width: '20px',
height: '20px',
backgroundColor: '#F3F4F6',
borderRadius: '50%',
transition: TRANSITIONS.medium,
boxShadow: SHADOWS.dark.small,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '10px',
color: '#4B5563',
},
thumbChecked: {
transform: 'translateX(24px)',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.4)',
color: '#3B82F6',
},
thumbDisabled: {
backgroundColor: 'rgba(107, 114, 128, 0.8)',
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.2)',
color: '#6B7280',
},
input: {
position: 'absolute',
opacity: 0,
width: '100%',
height: '100%',
margin: 0,
cursor: 'pointer',
},
inputDisabled: {
cursor: 'not-allowed',
},
glyph: {
display: 'none',
},
glyphLeft: {
display: 'none',
},
glyphRight: {
display: 'none',
},
glyphVisible: {
display: 'none',
},
shimmer: {
display: 'none',
},
leftLabel: {
fontSize: '14px',
fontWeight: 500,
color: 'rgb(209, 213, 219)',
fontFamily: 'Inter, system-ui, sans-serif',
userSelect: 'none',
transition: TRANSITIONS.medium,
},
rightLabel: {
fontSize: '14px',
fontWeight: 500,
color: 'rgb(209, 213, 219)',
fontFamily: 'Inter, system-ui, sans-serif',
userSelect: 'none',
transition: TRANSITIONS.medium,
},
}
const sacredTheme: SwitchTheme = {
container: {
position: 'relative',
display: 'inline-flex',
alignItems: 'center',
cursor: 'pointer',
gap: '12px',
transition: TRANSITIONS.premium,
},
containerDisabled: {
cursor: 'not-allowed',
opacity: 0.6,
},
track: {
position: 'relative',
width: '56px',
height: '28px',
backgroundColor: 'rgba(10, 10, 10, 0.9)',
borderRadius: '14px',
transition: TRANSITIONS.premium,
border: '2px solid rgba(255, 215, 0, 0.3)',
backdropFilter: 'blur(8px)',
overflow: 'hidden',
backgroundImage: `
radial-gradient(circle at 25% 50%, rgba(255, 215, 0, 0.05) 0%, transparent 50%),
radial-gradient(circle at 75% 50%, rgba(255, 215, 0, 0.05) 0%, transparent 50%)
`,
},
trackNoOutline: {
border: 'none',
boxShadow: 'none',
},
trackChecked: {
backgroundColor: 'rgba(10, 10, 10, 0.9)',
borderColor: '#FFD700',
boxShadow: SHADOWS.sacred.medium,
backgroundImage: `
radial-gradient(circle at 25% 50%, rgba(255, 215, 0, 0.1) 0%, transparent 50%),
radial-gradient(circle at 75% 50%, rgba(255, 215, 0, 0.1) 0%, transparent 50%)
`,
},
trackFocused: {
boxShadow: '0 0 0 4px rgba(255, 215, 0, 0.3)',
},
trackDisabled: {
backgroundColor: 'rgba(10, 10, 10, 0.6)',
borderColor: 'rgba(255, 215, 0, 0.1)',
boxShadow: 'none',
},
thumb: {
position: 'absolute',
top: '2px',
left: '2px',
width: '24px',
height: '24px',
backgroundColor: 'rgba(10, 10, 10, 0.9)',
borderRadius: '50%',
transition: TRANSITIONS.premium,
border: '1px solid rgba(255, 215, 0, 0.4)',
boxShadow: '0 0 15px rgba(255, 215, 0, 0.3)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '12px',
color: '#FFD700',
backgroundImage: `
radial-gradient(circle at center, rgba(255, 215, 0, 0.1) 0%, transparent 70%)
`,
},
thumbChecked: {
transform: 'translateX(28px)',
borderColor: '#FFD700',
boxShadow:
'0 0 25px rgba(255, 215, 0, 0.6), 0 0 50px rgba(255, 215, 0, 0.3)',
backgroundImage: `
radial-gradient(circle at center, rgba(255, 215, 0, 0.2) 0%, transparent 70%)
`,
},
thumbDisabled: {
backgroundColor: 'rgba(10, 10, 10, 0.6)',
borderColor: 'rgba(255, 215, 0, 0.2)',
boxShadow: 'none',
color: 'rgba(255, 215, 0, 0.3)',
},
input: {
position: 'absolute',
opacity: 0,
width: '100%',
height: '100%',
margin: 0,
cursor: 'pointer',
},
inputDisabled: {
cursor: 'not-allowed',
},
glyph: {
position: 'absolute',
fontSize: '8px',
color: 'rgba(255, 215, 0, 0.3)',
transition: TRANSITIONS.premium,
pointerEvents: 'none',
animation: 'sacredSwitchFloat 3s ease-in-out infinite',
},
glyphLeft: {
left: '-16px',
top: '50%',
transform: 'translateY(-50%)',
},
glyphRight: {
right: '-16px',
top: '50%',
transform: 'translateY(-50%)',
},
glyphVisible: {
opacity: 0.6,
},
shimmer: {
position: 'absolute',
top: '0',
left: '-100%',
width: '100%',
height: '100%',
background:
'linear-gradient(90deg, transparent, rgba(255, 215, 0, 0.2), transparent)',
animation: 'sacredSwitchShimmer 2s ease-in-out infinite',
borderRadius: 'inherit',
},
leftLabel: {
fontSize: '14px',
fontWeight: 500,
color: '#FFD700',
fontFamily: 'Cinzel, serif',
userSelect: 'none',
transition: TRANSITIONS.premium,
textShadow: '0 0 5px rgba(255, 215, 0, 0.3)',
},
rightLabel: {
fontSize: '14px',
fontWeight: 500,
color: '#FFD700',
fontFamily: 'Cinzel, serif',
userSelect: 'none',
transition: TRANSITIONS.premium,
textShadow: '0 0 5px rgba(255, 215, 0, 0.3)',
},
}
export const getSwitchStyles = (
styles?: SwitchStyles,
isFocused?: boolean,
isHovered?: boolean,
isChecked?: boolean,
isDisabled?: boolean
): SwitchTheme => {
const baseTheme =
styles?.theme === 'light'
? lightTheme
: styles?.theme === 'sacred'
? sacredTheme
: darkTheme
const customStyles: SwitchTheme = {
container: {
...baseTheme.container,
...(isDisabled && baseTheme.containerDisabled),
},
containerDisabled: baseTheme.containerDisabled,
track: {
...baseTheme.track,
...(styles?.trackWidth && { width: styles.trackWidth }),
...(styles?.trackHeight && { height: styles.trackHeight }),
...(styles?.trackBackground && {
backgroundColor: styles.trackBackground,
}),
...(styles?.trackBorderColor && { borderColor: styles.trackBorderColor }),
...(styles?.trackBorderRadius && {
borderRadius: styles.trackBorderRadius,
}),
...(styles?.outline === false && baseTheme.trackNoOutline),
...(isChecked && {
...baseTheme.trackChecked,
...(styles?.checkedTrackColor && {
backgroundColor: styles.checkedTrackColor,
}),
}),
...(isFocused &&
styles?.focusEffects !== false &&
baseTheme.trackFocused),
...(isDisabled && baseTheme.trackDisabled),
},
trackNoOutline: baseTheme.trackNoOutline,
trackChecked: baseTheme.trackChecked,
trackFocused: baseTheme.trackFocused,
trackDisabled: baseTheme.trackDisabled,
thumb: {
...baseTheme.thumb,
...(styles?.thumbSize && {
width: styles.thumbSize,
height: styles.thumbSize,
}),
...(styles?.thumbBackground && {
backgroundColor: styles.thumbBackground,
}),
...(styles?.thumbBorderColor && { borderColor: styles.thumbBorderColor }),
...(isChecked && {
...baseTheme.thumbChecked,
...(styles?.checkedThumbColor && {
backgroundColor: styles.checkedThumbColor,
}),
}),
...(isDisabled && baseTheme.thumbDisabled),
},
thumbChecked: baseTheme.thumbChecked,
thumbDisabled: baseTheme.thumbDisabled,
input: {
...baseTheme.input,
...(isDisabled && baseTheme.inputDisabled),
},
inputDisabled: baseTheme.inputDisabled,
glyph: {
...baseTheme.glyph,
...(styles?.transitionDuration && {
transition: `all ${styles.transitionDuration} ease`,
}),
},
glyphLeft: {
...baseTheme.glyphLeft,
...(isHovered && baseTheme.glyphVisible),
},
glyphRight: {
...baseTheme.glyphRight,
...(isHovered && baseTheme.glyphVisible),
},
glyphVisible: baseTheme.glyphVisible,
shimmer: baseTheme.shimmer,
leftLabel: {
...baseTheme.leftLabel,
...(styles?.labelColor && { color: styles.labelColor }),
...(styles?.labelFontFamily && { fontFamily: styles.labelFontFamily }),
...(styles?.labelFontSize && { fontSize: styles.labelFontSize }),
...(styles?.labelFontWeight && { fontWeight: styles.labelFontWeight }),
},
rightLabel: {
...baseTheme.rightLabel,
...(styles?.labelColor && { color: styles.labelColor }),
...(styles?.labelFontFamily && { fontFamily: styles.labelFontFamily }),
...(styles?.labelFontSize && { fontSize: styles.labelFontSize }),
...(styles?.labelFontWeight && { fontWeight: styles.labelFontWeight }),
},
}
return customStyles
}