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 )