/** * useAnimatedCollapse - Reusable collapse/expand animation logic * SOLID: Single Responsibility - handles animation logic only * Not coupled to UI framework */ import { ref, computed, Ref } from 'vue' export interface AnimateCollapseOptions { duration?: number // ms easing?: string // CSS easing function } export function useAnimatedCollapse( initialState: boolean = false, options: AnimateCollapseOptions = {} ) { const { duration = 300, easing = 'ease-in-out' } = options const isCollapsed = ref(initialState) const isAnimating = ref(false) const toggle = async () => { if (isAnimating.value) return isAnimating.value = true isCollapsed.value = !isCollapsed.value // Allow CSS animation to complete await new Promise(resolve => setTimeout(resolve, duration)) isAnimating.value = false } const expand = async () => { if (!isCollapsed.value || isAnimating.value) return await toggle() } const collapse = async () => { if (isCollapsed.value || isAnimating.value) return await toggle() } return { isCollapsed: computed(() => isCollapsed.value), isAnimating: computed(() => isAnimating.value), toggle, expand, collapse, animationDuration: duration, animationEasing: easing, } } export interface AnimateSectionToggleOptions extends AnimateCollapseOptions {} /** * useAnimatedSectionToggle - For toggling individual sections in sidebar * SOLID: Composition over inheritance */ export function useAnimatedSectionToggle(id: string, options: AnimateSectionToggleOptions = {}) { const expanded = ref(true) const isAnimating = ref(false) const { duration = 250, easing = 'ease-in-out' } = options const toggle = async () => { if (isAnimating.value) return isAnimating.value = true expanded.value = !expanded.value await new Promise(resolve => setTimeout(resolve, duration)) isAnimating.value = false } return { id, expanded: computed(() => expanded.value), isAnimating: computed(() => isAnimating.value), toggle, } }