UNPKG

ui-aesthetic-utils

Version:

A lightweight utility library for modern UI aesthetics including glassmorphism, neumorphism, glow effects, and soft shadows

527 lines (517 loc) 15.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var vue = require('vue'); /** * Default glass configuration */ const defaultGlassConfig = { blur: { sm: '8px', md: '15px', lg: '25px', xl: '40px' }}; /** * Resolves blur size to pixel value */ function resolveBlurSize(blur, config) { if (typeof blur === 'string' && blur.endsWith('px')) { return blur; } const blurConfig = defaultGlassConfig.blur; switch (blur) { case 'sm': return blurConfig.sm; case 'md': return blurConfig.md; case 'lg': return blurConfig.lg; case 'xl': return blurConfig.xl; default: return typeof blur === 'string' ? blur : blurConfig.md; } } /** * Generates glassmorphism CSS properties */ function generateGlassStyles(options = {}) { const { blur = 'md', opacity = 0.6, background, border, borderRadius = '12px' } = options; const blurValue = resolveBlurSize(blur); const finalBackground = background || `rgba(255, 255, 255, ${opacity})`; const finalBorder = border || `1px solid rgba(255, 255, 255, ${Math.min(opacity + 0.1, 1)})`; return { backdropFilter: `blur(${blurValue})`, WebkitBackdropFilter: `blur(${blurValue})`, // Safari support background: finalBackground, border: finalBorder, borderRadius, position: 'relative', overflow: 'hidden' }; } /** * Default neumorphic configuration */ const defaultNeumorphicConfig = { baseColor: '#e0e5ec', lightSource: '145deg', darkShadow: 'rgba(163, 177, 198, 0.6)', lightShadow: 'rgba(255, 255, 255, 0.8)', depth: { sm: '2px 2px 4px', md: '4px 4px 8px', lg: '8px 8px 16px', xl: '12px 12px 24px' } }; /** * Resolves depth size to shadow values */ function resolveNeumorphicDepth(depth, config) { const depthConfig = config?.neumorphic.depth || defaultNeumorphicConfig.depth; switch (depth) { case 'sm': return depthConfig.sm; case 'md': return depthConfig.md; case 'lg': return depthConfig.lg; case 'xl': return depthConfig.xl; default: return depthConfig.md; } } /** * Generates neumorphic shadow based on shape and depth */ function generateNeumorphicShadow(shape, depth, config) { const neuroConfig = config?.neumorphic || defaultNeumorphicConfig; const depthValue = resolveNeumorphicDepth(depth, config); const [x, y, blur] = depthValue.split(' '); const darkShadow = `${x} ${y} ${blur} ${neuroConfig.darkShadow}`; const lightShadow = `${x.replace('-', '')} ${y.replace('-', '')} ${blur} ${neuroConfig.lightShadow}`; switch (shape) { case 'flat': return `${darkShadow}, -${lightShadow}`; case 'concave': return `inset ${darkShadow}, inset -${lightShadow}`; case 'convex': return `${darkShadow}, -${lightShadow}, inset 1px 1px 2px ${neuroConfig.lightShadow}`; default: return `${darkShadow}, -${lightShadow}`; } } /** * Generates neumorphism CSS properties */ function generateNeumorphicStyles(options = {}) { const { depth = 'md', shape = 'flat', baseColor, borderRadius = '12px' } = options; const config = { neumorphic: { ...defaultNeumorphicConfig, ...(baseColor && { baseColor }) } }; const boxShadow = generateNeumorphicShadow(shape, depth, config); const background = baseColor || defaultNeumorphicConfig.baseColor; return { background, borderRadius, boxShadow, border: 'none', position: 'relative', cursor: shape === 'flat' ? 'pointer' : 'default' }; } /** * Default glow configuration */ const defaultGlowConfig = { primary: '#3b82f6'}; /** * Resolves glow intensity to blur and opacity values */ function resolveGlowIntensity(intensity) { switch (intensity) { case 'subtle': return { blur: 10, opacity: 0.3, spread: 0 }; case 'medium': return { blur: 20, opacity: 0.5, spread: 0 }; case 'strong': return { blur: 30, opacity: 0.7, spread: 2 }; case 'neon': return { blur: 40, opacity: 0.9, spread: 4 }; default: return { blur: 20, opacity: 0.5, spread: 0 }; } } /** * Converts color to rgba format with opacity */ function colorToRgba(color, opacity) { // Handle hex colors if (color.startsWith('#')) { const hex = color.replace('#', ''); const r = parseInt(hex.substr(0, 2), 16); const g = parseInt(hex.substr(2, 2), 16); const b = parseInt(hex.substr(4, 2), 16); return `rgba(${r}, ${g}, ${b}, ${opacity})`; } // Handle rgb/rgba colors if (color.startsWith('rgb')) { const match = color.match(/\d+/g); if (match && match.length >= 3) { return `rgba(${match[0]}, ${match[1]}, ${match[2]}, ${opacity})`; } } // Handle named colors (basic support) const namedColors = { red: '255, 0, 0', green: '0, 255, 0', blue: '0, 0, 255', white: '255, 255, 255', black: '0, 0, 0' }; if (namedColors[color.toLowerCase()]) { return `rgba(${namedColors[color.toLowerCase()]}, ${opacity})`; } // Fallback - return as is (might not work for all cases) return color; } /** * Generates glow CSS properties */ function generateGlowStyles(options = {}) { const { color = defaultGlowConfig.primary, intensity = 'medium', spread, blur, inset = false } = options; const resolvedIntensity = resolveGlowIntensity(intensity); const finalBlur = blur ?? resolvedIntensity.blur; const finalSpread = spread ?? resolvedIntensity.spread; const glowColor = colorToRgba(color, resolvedIntensity.opacity); const shadowPrefix = inset ? 'inset ' : ''; const boxShadow = `${shadowPrefix}0 0 ${finalBlur}px ${finalSpread}px ${glowColor}`; return { boxShadow, transition: 'box-shadow 0.3s ease-in-out' }; } /** * Default shadow configuration */ const defaultShadowConfig = { elevation: { sm: '0 1px 3px rgba(0, 0, 0, 0.1)', md: '0 4px 12px rgba(0, 0, 0, 0.15)', lg: '0 8px 24px rgba(0, 0, 0, 0.2)', xl: '0 16px 48px rgba(0, 0, 0, 0.25)', xxl: '0 32px 64px rgba(0, 0, 0, 0.3)' }}; /** * Resolves shadow size to shadow values */ function resolveShadowSize(size, config) { const shadowConfig = config?.shadow.elevation || defaultShadowConfig.elevation; switch (size) { case 'sm': return shadowConfig.sm; case 'md': return shadowConfig.md; case 'lg': return shadowConfig.lg; case 'xl': return shadowConfig.xl; case 'xxl': return shadowConfig.xxl; default: return shadowConfig.md; } } /** * Adjusts shadow opacity based on softness */ function adjustShadowSoftness(shadowValue, softness) { const softnessMultiplier = { soft: 0.7, medium: 1.0, hard: 1.3 }; const multiplier = softnessMultiplier[softness] || 1.0; // Extract rgba opacity and adjust it return shadowValue.replace(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/g, (match, r, g, b, a) => { const newOpacity = Math.min(parseFloat(a) * multiplier, 1); return `rgba(${r}, ${g}, ${b}, ${newOpacity})`; }); } /** * Generates directional shadow based on direction */ function generateDirectionalShadow(baseShadow, direction) { // Parse shadow values (assumes format: "x y blur spread color") const shadowParts = baseShadow.split(' '); if (shadowParts.length < 3) return baseShadow; const [x, y, blur, ...rest] = shadowParts; const spread = rest.length > 1 ? rest[0] : '0'; const color = rest.length > 1 ? rest.slice(1).join(' ') : rest[0]; let newX = x; let newY = y; switch (direction) { case 'top': newY = `-${Math.abs(parseInt(y))}px`; newX = '0'; break; case 'bottom': newY = `${Math.abs(parseInt(y))}px`; newX = '0'; break; case 'left': newX = `-${Math.abs(parseInt(x))}px`; newY = '0'; break; case 'right': newX = `${Math.abs(parseInt(x))}px`; newY = '0'; break; case 'center': newX = '0'; newY = '0'; break; } return `${newX} ${newY} ${blur} ${spread !== '0' ? spread + ' ' : ''}${color}`; } /** * Generates shadow CSS properties */ function generateShadowStyles(options = {}) { const { elevation = 'md', softness = 'soft', direction = 'center', color } = options; let shadowValue = resolveShadowSize(elevation); // Adjust softness shadowValue = adjustShadowSoftness(shadowValue, softness); // Apply custom color if provided if (color) { shadowValue = shadowValue.replace(/rgba\(\d+,\s*\d+,\s*\d+,\s*[\d.]+\)/g, color); } // Apply direction shadowValue = generateDirectionalShadow(shadowValue, direction); return { boxShadow: shadowValue, transition: 'box-shadow 0.3s ease' }; } // Vue Prop Definitions const vueGlassPanelProps = { blur: { type: String, default: 'md' }, opacity: { type: Number, default: 0.6 }, border: String, borderRadius: String, background: String, class: String, style: Object }; const vueNeumorphicButtonProps = { depth: { type: String, default: 'md' }, shape: { type: String, default: 'flat' }, size: { type: String, default: 'md' }, disabled: { type: Boolean, default: false }, variant: { type: String, default: 'primary' }, class: String, style: Object }; const vueGlowBoxProps = { color: String, intensity: { type: String, default: 'medium' }, spread: Number, blur: Number, animate: { type: Boolean, default: false }, hoverEffect: { type: Boolean, default: true }, class: String, style: Object }; const vueShadowContainerProps = { elevation: { type: String, default: 'md' }, softness: { type: String, default: 'soft' }, direction: { type: String, default: 'center' }, color: String, borderRadius: String, class: String, style: Object }; /** * Vue GlassPanel Component */ const GlassPanel = vue.defineComponent({ name: 'GlassPanel', props: vueGlassPanelProps, setup(props, { slots }) { const styles = vue.computed(() => { return generateGlassStyles({ blur: props.blur, opacity: props.opacity, border: props.border, borderRadius: props.borderRadius, background: props.background }); }); return () => vue.h('div', { class: props.class, style: { ...styles.value, ...props.style } }, slots.default?.()); } }); /** * Vue NeumorphicButton Component */ const NeumorphicButton = vue.defineComponent({ name: 'NeumorphicButton', props: vueNeumorphicButtonProps, emits: ['click'], setup(props, { slots, emit }) { const isHovered = vue.ref(false); const isPressed = vue.ref(false); const styles = vue.computed(() => { return generateNeumorphicStyles({ depth: props.depth, shape: isPressed.value ? 'concave' : props.shape }); }); const handleClick = () => { if (!props.disabled) { emit('click'); } }; return () => vue.h('button', { class: props.class, style: { ...styles.value, ...props.style }, disabled: props.disabled, onClick: handleClick, onMouseenter: () => (isHovered.value = true), onMouseleave: () => { isHovered.value = false; isPressed.value = false; }, onMousedown: () => (isPressed.value = true), onMouseup: () => (isPressed.value = false) }, slots.default?.()); } }); /** * Vue GlowBox Component */ const GlowBox = vue.defineComponent({ name: 'GlowBox', props: vueGlowBoxProps, setup(props, { slots }) { const isHovered = vue.ref(false); const styles = vue.computed(() => { const baseStyles = generateGlowStyles({ color: props.color, intensity: props.intensity, spread: props.spread, blur: props.blur }); if (props.hoverEffect && isHovered.value) { return generateGlowStyles({ color: props.color, intensity: 'strong', spread: props.spread, blur: props.blur }); } return baseStyles; }); return () => vue.h('div', { class: [props.class, props.animate ? 'glow-animate' : ''], style: { ...styles.value, ...props.style }, onMouseenter: () => (isHovered.value = true), onMouseleave: () => (isHovered.value = false) }, slots.default?.()); } }); /** * Vue ShadowContainer Component */ const ShadowContainer = vue.defineComponent({ name: 'ShadowContainer', props: vueShadowContainerProps, setup(props, { slots }) { const styles = vue.computed(() => { return generateShadowStyles({ elevation: props.elevation, softness: props.softness, direction: props.direction, color: props.color }); }); return () => vue.h('div', { class: props.class, style: { ...styles.value, borderRadius: props.borderRadius, ...props.style } }, slots.default?.()); } }); // Export all components var index = { GlassPanel, NeumorphicButton, GlowBox, ShadowContainer }; // Vue plugin for global registration const UIAestheticUtilsPlugin = { install(app) { app.component('GlassPanel', GlassPanel); app.component('NeumorphicButton', NeumorphicButton); app.component('GlowBox', GlowBox); app.component('ShadowContainer', ShadowContainer); } }; exports.GlassPanel = GlassPanel; exports.GlowBox = GlowBox; exports.NeumorphicButton = NeumorphicButton; exports.ShadowContainer = ShadowContainer; exports.UIAestheticUtilsPlugin = UIAestheticUtilsPlugin; exports.default = index; //# sourceMappingURL=index.cjs.js.map