mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 22:34:56 +00:00
ORGANIZED INTO 4 MAIN CATEGORIES: - react/ React TypeScript components (145 components + Python bindings) - qml/ QML desktop components (104+ QML components) - python/ Python package implementations - legacy/ Utilities, contexts, and migration-in-progress code SUPPORTING FOLDERS (kept as-is): - icons/ 421 SVG icons - theming/ Material Design 3 theme system - styles/ SCSS modules and utilities - scss/ SCSS preprocessor files - docs/ Documentation files STRUCTURE IMPROVEMENTS: ✅ All code preserved (nothing deleted) ✅ Clear separation by implementation type ✅ Better navigation and discoverability ✅ Easy to find what you need ✅ Professional organization DOCUMENTATION: - Added STRUCTURE.md explaining the new layout - Updated folder organization with clear purpose - Maintained all original functionality All files reorganized while keeping full functionality intact. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
105 lines
2.5 KiB
JavaScript
105 lines
2.5 KiB
JavaScript
'use client'
|
|
|
|
import { useState, useEffect, useCallback } from 'react'
|
|
|
|
/**
|
|
* useMediaQuery - React hook for CSS media queries
|
|
* @param {string} query - Media query string (e.g., '(min-width: 600px)')
|
|
* @param {object} options - Options object
|
|
* @returns {boolean} - Whether the media query matches
|
|
*/
|
|
export function useMediaQuery(query, options = {}) {
|
|
const {
|
|
defaultMatches = false,
|
|
matchOnSSR = defaultMatches,
|
|
noSsr = false,
|
|
} = options
|
|
|
|
const getMatches = useCallback(() => {
|
|
// For SSR, return the default
|
|
if (typeof window === 'undefined') {
|
|
return matchOnSSR
|
|
}
|
|
return window.matchMedia(query).matches
|
|
}, [query, matchOnSSR])
|
|
|
|
const [matches, setMatches] = useState(() => {
|
|
if (noSsr) {
|
|
return getMatches()
|
|
}
|
|
return defaultMatches
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') return
|
|
|
|
const mediaQueryList = window.matchMedia(query)
|
|
|
|
// Set initial value
|
|
setMatches(mediaQueryList.matches)
|
|
|
|
// Create event listener
|
|
const listener = (event) => {
|
|
setMatches(event.matches)
|
|
}
|
|
|
|
// Add listener (modern browsers use addEventListener)
|
|
if (mediaQueryList.addEventListener) {
|
|
mediaQueryList.addEventListener('change', listener)
|
|
} else {
|
|
// Fallback for older browsers
|
|
mediaQueryList.addListener(listener)
|
|
}
|
|
|
|
return () => {
|
|
if (mediaQueryList.removeEventListener) {
|
|
mediaQueryList.removeEventListener('change', listener)
|
|
} else {
|
|
mediaQueryList.removeListener(listener)
|
|
}
|
|
}
|
|
}, [query])
|
|
|
|
return matches
|
|
}
|
|
|
|
// Convenience hooks for common breakpoints (matching MUI defaults)
|
|
export function useMediaQueryUp(breakpoint) {
|
|
const breakpoints = {
|
|
xs: 0,
|
|
sm: 600,
|
|
md: 900,
|
|
lg: 1200,
|
|
xl: 1536,
|
|
}
|
|
const width = breakpoints[breakpoint] || breakpoint
|
|
return useMediaQuery(`(min-width: ${width}px)`)
|
|
}
|
|
|
|
export function useMediaQueryDown(breakpoint) {
|
|
const breakpoints = {
|
|
xs: 0,
|
|
sm: 600,
|
|
md: 900,
|
|
lg: 1200,
|
|
xl: 1536,
|
|
}
|
|
const width = breakpoints[breakpoint] || breakpoint
|
|
return useMediaQuery(`(max-width: ${width - 0.05}px)`)
|
|
}
|
|
|
|
export function useMediaQueryBetween(start, end) {
|
|
const breakpoints = {
|
|
xs: 0,
|
|
sm: 600,
|
|
md: 900,
|
|
lg: 1200,
|
|
xl: 1536,
|
|
}
|
|
const startWidth = breakpoints[start] || start
|
|
const endWidth = breakpoints[end] || end
|
|
return useMediaQuery(`(min-width: ${startWidth}px) and (max-width: ${endWidth - 0.05}px)`)
|
|
}
|
|
|
|
export default useMediaQuery
|