UNPKG

muspe-cli

Version:

MusPE Advanced Framework v2.1.3 - Mobile User-friendly Simple Progressive Engine with Enhanced CLI Tools, Specialized E-Commerce Templates, Material Design 3, Progressive Enhancement, Mobile Optimizations, Performance Analysis, and Enterprise-Grade Develo

546 lines (454 loc) 13.9 kB
// MusPE Composable Hooks System - React-like hooks with Vue-like composables class MusPEHooks { constructor() { this.currentInstance = null; this.hookIndex = 0; this.hooks = []; } // Set current component instance setCurrentInstance(instance) { this.currentInstance = instance; this.hookIndex = 0; } // Get current hook index and increment getHookIndex() { return this.hookIndex++; } // Reset for next render reset() { this.hookIndex = 0; } } const hookSystem = new MusPEHooks(); // useState hook - React-like state management function useState(initialValue) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useState can only be used inside components'); } // Initialize hook if first time if (!instance.hooks) instance.hooks = []; if (!instance.hooks[index]) { instance.hooks[index] = { type: 'state', value: ref(typeof initialValue === 'function' ? initialValue() : initialValue) }; } const hook = instance.hooks[index]; const setState = (newValue) => { if (typeof newValue === 'function') { hook.value.value = newValue(hook.value.value); } else { hook.value.value = newValue; } // Trigger re-render if (instance.update) { instance.update(); } }; return [hook.value.value, setState]; } // useEffect hook - React-like side effects function useEffect(callback, dependencies) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useEffect can only be used inside components'); } if (!instance.hooks) instance.hooks = []; const hook = instance.hooks[index]; const depsChanged = !hook || !dependencies || !hook.dependencies || dependencies.some((dep, i) => dep !== hook.dependencies[i]); if (!hook) { instance.hooks[index] = { type: 'effect', callback, dependencies: dependencies ? [...dependencies] : null, cleanup: null }; } else if (depsChanged) { // Cleanup previous effect if (hook.cleanup && typeof hook.cleanup === 'function') { hook.cleanup(); } hook.callback = callback; hook.dependencies = dependencies ? [...dependencies] : null; hook.cleanup = null; } // Run effect if dependencies changed if (depsChanged) { const currentHook = instance.hooks[index]; // Schedule effect to run after render setTimeout(() => { if (currentHook.callback) { const cleanup = currentHook.callback(); if (typeof cleanup === 'function') { currentHook.cleanup = cleanup; } } }, 0); } } // useRef hook - React-like refs function useRef(initialValue) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useRef can only be used inside components'); } if (!instance.hooks) instance.hooks = []; if (!instance.hooks[index]) { instance.hooks[index] = { type: 'ref', current: initialValue }; } return instance.hooks[index]; } // useComputed hook - Vue-like computed values function useComputed(computeFn) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useComputed can only be used inside components'); } if (!instance.hooks) instance.hooks = []; if (!instance.hooks[index]) { instance.hooks[index] = { type: 'computed', value: computed(computeFn) }; } return instance.hooks[index].value; } // useWatch hook - Vue-like watchers function useWatch(source, callback, options) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useWatch can only be used inside components'); } if (!instance.hooks) instance.hooks = []; if (!instance.hooks[index]) { instance.hooks[index] = { type: 'watch', watcher: watch(source, callback, options) }; } return instance.hooks[index].watcher; } // useMemo hook - React-like memoization function useMemo(factory, dependencies) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useMemo can only be used inside components'); } if (!instance.hooks) instance.hooks = []; const hook = instance.hooks[index]; const depsChanged = !hook || !dependencies || !hook.dependencies || dependencies.some((dep, i) => dep !== hook.dependencies[i]); if (!hook || depsChanged) { const value = factory(); instance.hooks[index] = { type: 'memo', value, dependencies: dependencies ? [...dependencies] : null }; return value; } return hook.value; } // useCallback hook - React-like callback memoization function useCallback(callback, dependencies) { return useMemo(() => callback, dependencies); } // useReducer hook - React-like state reducer function useReducer(reducer, initialState, init) { const instance = hookSystem.currentInstance; const index = hookSystem.getHookIndex(); if (!instance) { throw new Error('useReducer can only be used inside components'); } if (!instance.hooks) instance.hooks = []; if (!instance.hooks[index]) { const initialValue = init ? init(initialState) : initialState; instance.hooks[index] = { type: 'reducer', state: ref(initialValue), reducer }; } const hook = instance.hooks[index]; const dispatch = (action) => { const newState = hook.reducer(hook.state.value, action); hook.state.value = newState; // Trigger re-render if (instance.update) { instance.update(); } }; return [hook.state.value, dispatch]; } // Custom hooks for mobile-specific functionality // useTouch hook - Touch gesture handling function useTouch() { const touchState = reactive({ isTouch: false, touches: [], startTime: 0, startPosition: { x: 0, y: 0 }, currentPosition: { x: 0, y: 0 }, velocity: { x: 0, y: 0 } }); const handlers = { onTouchStart: (e) => { touchState.isTouch = true; touchState.touches = Array.from(e.touches); touchState.startTime = Date.now(); touchState.startPosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; touchState.currentPosition = { ...touchState.startPosition }; }, onTouchMove: (e) => { if (!touchState.isTouch) return; touchState.touches = Array.from(e.touches); const newPosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; const deltaTime = Date.now() - touchState.startTime; const deltaX = newPosition.x - touchState.currentPosition.x; const deltaY = newPosition.y - touchState.currentPosition.y; touchState.velocity = { x: deltaX / deltaTime, y: deltaY / deltaTime }; touchState.currentPosition = newPosition; }, onTouchEnd: (e) => { touchState.isTouch = false; touchState.touches = []; } }; return { touchState, handlers }; } // useSwipe hook - Swipe gesture detection function useSwipe(onSwipe, options = {}) { const { threshold = 50, timeout = 500 } = options; const { touchState, handlers } = useTouch(); useEffect(() => { if (!touchState.isTouch && touchState.startTime > 0) { const deltaTime = Date.now() - touchState.startTime; const deltaX = touchState.currentPosition.x - touchState.startPosition.x; const deltaY = touchState.currentPosition.y - touchState.startPosition.y; if (deltaTime < timeout) { if (Math.abs(deltaX) > threshold && Math.abs(deltaX) > Math.abs(deltaY)) { const direction = deltaX > 0 ? 'right' : 'left'; onSwipe(direction, { deltaX, deltaY, deltaTime, velocity: touchState.velocity }); } else if (Math.abs(deltaY) > threshold && Math.abs(deltaY) > Math.abs(deltaX)) { const direction = deltaY > 0 ? 'down' : 'up'; onSwipe(direction, { deltaX, deltaY, deltaTime, velocity: touchState.velocity }); } } } }, [touchState.isTouch]); return handlers; } // useGeolocation hook - Device geolocation function useGeolocation(options = {}) { const [position, setPosition] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const getCurrentPosition = useCallback(() => { if (!navigator.geolocation) { setError(new Error('Geolocation is not supported')); return; } setLoading(true); navigator.geolocation.getCurrentPosition( (pos) => { setPosition({ latitude: pos.coords.latitude, longitude: pos.coords.longitude, accuracy: pos.coords.accuracy, timestamp: pos.timestamp }); setLoading(false); setError(null); }, (err) => { setError(err); setLoading(false); }, options ); }, [options]); useEffect(() => { getCurrentPosition(); }, [getCurrentPosition]); return { position, error, loading, getCurrentPosition }; } // useDeviceOrientation hook - Device orientation function useDeviceOrientation() { const [orientation, setOrientation] = useState({ alpha: 0, beta: 0, gamma: 0 }); useEffect(() => { const handleOrientation = (event) => { setOrientation({ alpha: event.alpha, beta: event.beta, gamma: event.gamma }); }; if ('DeviceOrientationEvent' in window) { window.addEventListener('deviceorientation', handleOrientation); return () => { window.removeEventListener('deviceorientation', handleOrientation); }; } }, []); return orientation; } // useNetworkStatus hook - Network connectivity function useNetworkStatus() { const [isOnline, setIsOnline] = useState(navigator.onLine); const [connection, setConnection] = useState(null); useEffect(() => { const handleOnline = () => setIsOnline(true); const handleOffline = () => setIsOnline(false); window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); // Get connection info if available if ('connection' in navigator) { setConnection(navigator.connection); const handleConnectionChange = () => { setConnection({ ...navigator.connection }); }; navigator.connection.addEventListener('change', handleConnectionChange); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); navigator.connection.removeEventListener('change', handleConnectionChange); }; } return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []); return { isOnline, connection }; } // useLocalStorage hook - LocalStorage with reactivity function useLocalStorage(key, defaultValue) { const [value, setValue] = useState(() => { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : defaultValue; } catch (error) { console.error('Error reading from localStorage:', error); return defaultValue; } }); const setStoredValue = useCallback((newValue) => { try { setValue(newValue); localStorage.setItem(key, JSON.stringify(newValue)); } catch (error) { console.error('Error writing to localStorage:', error); } }, [key]); return [value, setStoredValue]; } // useAnimation hook - Animation utilities function useAnimation() { const animationsRef = useRef(new Set()); const animate = useCallback((element, keyframes, options = {}) => { const animation = element.animate(keyframes, { duration: 300, easing: 'ease-out', fill: 'forwards', ...options }); animationsRef.current.add(animation); animation.addEventListener('finish', () => { animationsRef.current.delete(animation); }); return animation; }, []); const cancelAll = useCallback(() => { animationsRef.current.forEach(animation => { if (animation.cancel) animation.cancel(); }); animationsRef.current.clear(); }, []); // Cleanup on unmount useEffect(() => { return () => { cancelAll(); }; }, [cancelAll]); return { animate, cancelAll, activeAnimations: animationsRef.current }; } // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = { hookSystem, useState, useEffect, useRef, useComputed, useWatch, useMemo, useCallback, useReducer, useTouch, useSwipe, useGeolocation, useDeviceOrientation, useNetworkStatus, useLocalStorage, useAnimation }; } // Make available globally if (typeof window !== 'undefined') { window.useState = useState; window.useEffect = useEffect; window.useRef = useRef; window.useComputed = useComputed; window.useWatch = useWatch; window.useMemo = useMemo; window.useCallback = useCallback; window.useReducer = useReducer; window.useTouch = useTouch; window.useSwipe = useSwipe; window.useGeolocation = useGeolocation; window.useDeviceOrientation = useDeviceOrientation; window.useNetworkStatus = useNetworkStatus; window.useLocalStorage = useLocalStorage; window.useAnimation = useAnimation; }