Punk-Queue
view release on metacpan or search on metacpan
lib/Punk/Plugin/Queue/assets/funky/js/07-media-query.js view on Meta::CPAN
/**
* Funky.MediaQuery - Centralized Media Query Manager
*
* Provides shared matchMedia listeners with named breakpoints.
* Multiple components can subscribe to the same query, sharing one listener.
*
* NAMING CONVENTION:
* - PubSub events: funky:mediaquery:change, funky:mediaquery:{breakpoint}
*
* Usage:
* Funky.MediaQuery.subscribe({
* breakpoint: 'mobile',
* namespace: 'my-component',
* onChange: function(matches) { console.log('Mobile:', matches); }
* });
*
* Funky.MediaQuery.matches('mobile'); // true/false
* Funky.MediaQuery.unsubscribe('my-component');
*
* @version 1.0.4
*/
(function(window) {
'use strict';
// Ensure Funky registry exists
if (!window.Funky || !window.Funky.register) {
console.error('[Funky.MediaQuery] Registry not found. Load namespace.js first.');
return;
}
// Prevent double registration
if (window.Funky.isRegistered && window.Funky.isRegistered('MediaQuery')) {
return;
}
var Funky = window.Funky;
// =========================================================================
// Predefined Breakpoints
// =========================================================================
var BREAKPOINTS = {
'mobile': '(max-width: 767px)',
'tablet': '(max-width: 1023px)',
'tablet-only': '(min-width: 768px) and (max-width: 1023px)',
'desktop': '(min-width: 1024px)',
'large': '(min-width: 1200px)',
'xlarge': '(min-width: 1440px)',
'portrait': '(orientation: portrait)',
'landscape': '(orientation: landscape)',
'touch': '(pointer: coarse)',
'mouse': '(pointer: fine)',
'hover': '(hover: hover)',
'reduced-motion': '(prefers-reduced-motion: reduce)',
'dark-mode': '(prefers-color-scheme: dark)',
'light-mode': '(prefers-color-scheme: light)',
'high-contrast': '(prefers-contrast: more)'
};
// =========================================================================
// State
// =========================================================================
// Map of query string -> { mql: MediaQueryList, handlers: [{namespace, onChange}], listener: Function }
var queries = {};
// Map of namespace -> query string (for quick unsubscribe lookup)
var namespaceMap = {};
// =========================================================================
// Internal Functions
// =========================================================================
/**
* Resolve breakpoint name or query option to query string
* @param {Object} options - Subscribe options
* @returns {string|null} Query string
*/
function resolveQuery(options) {
if (options.query) {
return options.query;
}
if (options.breakpoint && BREAKPOINTS[options.breakpoint]) {
return BREAKPOINTS[options.breakpoint];
}
console.warn('[MediaQuery] Unknown breakpoint:', options.breakpoint);
return null;
}
/**
* Find breakpoint name for a query string
* @param {string} query - Query string
* @returns {string|null} Breakpoint name or null
*/
function findBreakpointName(query) {
for (var name in BREAKPOINTS) {
if (BREAKPOINTS.hasOwnProperty(name) && BREAKPOINTS[name] === query) {
return name;
}
}
return null;
}
/**
* Get or create MediaQueryList for a query
* @param {string} query - Media query string
( run in 0.568 second using v1.01-cache-2.11-cpan-b16cb0d3907 )