{"version":3,"sources":["../../../../src/browser/useMediaQuery/core.ts"],"sourcesContent":["import { observable, type Observable } from \"@legendapp/state\";\nimport {\n  createSupported,\n  get,\n  createObserve,\n  type DeepMaybeObservable,\n  type MaybeObservable,\n} from \"@usels/core\";\nimport { resolveWindowSource, type ConfigurableWindow } from \"@shared/configurable\";\nimport { createEventListener } from \"../useEventListener/core\";\nimport { createOpaque } from \"@usels/core/reactivity/useOpaque/core\";\n\n// ---------------------------------------------------------------------------\n// SSR helper\n// ---------------------------------------------------------------------------\n\nfunction pxValue(value: string): number {\n  const num = parseFloat(value);\n  const unit = value\n    .trim()\n    .replace(/^-?\\d+(?:\\.\\d+)?/, \"\")\n    .trim();\n  if (unit === \"px\" || unit === \"\") return num;\n  if (unit === \"em\" || unit === \"rem\") return num * 16;\n  return num;\n}\n\n/**\n * Statically evaluate a CSS media query against a known viewport width.\n *\n * Supports `min-width` / `max-width` / `not all` in comma-separated queries.\n * Used by the SSR path so server-rendered markup and the first client render\n * can agree on the initial value.\n */\nexport function evaluateSSRQuery(query: string, ssrWidth: number): boolean {\n  const queryStrings = query.split(\",\");\n  return queryStrings.some((queryString) => {\n    const not = queryString.includes(\"not all\");\n    const minWidth = queryString.match(/\\(\\s*min-width:\\s*(-?\\d+(?:\\.\\d*)?[a-z]+\\s*)\\)/);\n    const maxWidth = queryString.match(/\\(\\s*max-width:\\s*(-?\\d+(?:\\.\\d*)?[a-z]+\\s*)\\)/);\n    let res = Boolean(minWidth || maxWidth);\n    if (minWidth && res) res = ssrWidth >= pxValue(minWidth[1]);\n    if (maxWidth && res) res = ssrWidth <= pxValue(maxWidth[1]);\n    return not ? !res : res;\n  });\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UseMediaQueryOptions extends ConfigurableWindow {\n  /**\n   * Static viewport width used to evaluate `min-width` / `max-width` queries\n   * when `window.matchMedia` is unavailable (SSR).\n   */\n  ssrWidth?: number;\n}\n\nexport type UseMediaQueryReturn = Observable<boolean>;\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-agnostic reactive CSS media query tracker.\n *\n * Creates a `MediaQueryList` for the given query (reactive — accepts\n * `MaybeObservable<string>`) and subscribes to its `change` events via\n * `createEventListener`. Re-creates the `MediaQueryList` when the query or\n * the resolved window changes. Must be called inside a `useScope` factory.\n *\n * @param query - CSS media query (plain string or `Observable<string>`).\n * @param options - Optional `{ window?, ssrWidth? }`.\n * @returns `Observable<boolean>` that reflects `mql.matches` (or the SSR\n *          fallback when `matchMedia` is unavailable).\n */\nexport function createMediaQuery(\n  query: MaybeObservable<string>,\n  options?: DeepMaybeObservable<UseMediaQueryOptions>\n): UseMediaQueryReturn {\n  const opts$ = observable(options);\n  const win$ = resolveWindowSource(opts$.window as unknown as Observable<unknown>);\n\n  const isSupported$ = createSupported(() => {\n    const win = win$.get();\n    return !!win && \"matchMedia\" in win && typeof win.matchMedia === \"function\";\n  });\n\n  // Mount-time-only field — `ssrWidth` never changes during the hook lifetime.\n  const ssrWidth = opts$.peek()?.ssrWidth;\n\n  const matches$ = observable<boolean>(\n    typeof ssrWidth === \"number\" ? evaluateSSRQuery(get(query), ssrWidth) : false\n  );\n\n  // Reactive `MediaQueryList` holder. Uses `ObservableHint.opaque` so\n  // Legend-State does not deep-proxy the DOM object.\n  const mql$ = createOpaque<MediaQueryList>(null);\n\n  // Create / recreate the MediaQueryList whenever the query, window, or\n  // support flag changes. Previous MQL is simply dropped — `createEventListener`\n  // below reacts to `mql$` changes and tears down the old listener.\n  createObserve(() => {\n    const q = get(query);\n    const win = win$.get();\n    if (!isSupported$.get() || !win) {\n      mql$.set(null);\n      return;\n    }\n    const mql = (win as Window).matchMedia(q);\n    mql$.set(mql);\n    matches$.set(mql.matches);\n  });\n\n  // Subscribe to `change` events. `createEventListener` reacts to `mql$`\n  // mutations and re-binds the listener to the new `MediaQueryList` each\n  // time the query / window changes, tearing the previous one down.\n  createEventListener(\n    mql$,\n    \"change\",\n    (e: Event) => {\n      matches$.set((e as MediaQueryListEvent).matches);\n    },\n    { passive: true }\n  );\n\n  return matches$;\n}\n"],"mappings":"AAAA,SAAS,kBAAmC;AAC5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,2BAAoD;AAC7D,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAM7B,SAAS,QAAQ,OAAuB;AACtC,QAAM,MAAM,WAAW,KAAK;AAC5B,QAAM,OAAO,MACV,KAAK,EACL,QAAQ,oBAAoB,EAAE,EAC9B,KAAK;AACR,MAAI,SAAS,QAAQ,SAAS,GAAI,QAAO;AACzC,MAAI,SAAS,QAAQ,SAAS,MAAO,QAAO,MAAM;AAClD,SAAO;AACT;AASO,SAAS,iBAAiB,OAAe,UAA2B;AACzE,QAAM,eAAe,MAAM,MAAM,GAAG;AACpC,SAAO,aAAa,KAAK,CAAC,gBAAgB;AACxC,UAAM,MAAM,YAAY,SAAS,SAAS;AAC1C,UAAM,WAAW,YAAY,MAAM,gDAAgD;AACnF,UAAM,WAAW,YAAY,MAAM,gDAAgD;AACnF,QAAI,MAAM,QAAQ,YAAY,QAAQ;AACtC,QAAI,YAAY,IAAK,OAAM,YAAY,QAAQ,SAAS,CAAC,CAAC;AAC1D,QAAI,YAAY,IAAK,OAAM,YAAY,QAAQ,SAAS,CAAC,CAAC;AAC1D,WAAO,MAAM,CAAC,MAAM;AAAA,EACtB,CAAC;AACH;AAiCO,SAAS,iBACd,OACA,SACqB;AACrB,QAAM,QAAQ,WAAW,OAAO;AAChC,QAAM,OAAO,oBAAoB,MAAM,MAAwC;AAE/E,QAAM,eAAe,gBAAgB,MAAM;AACzC,UAAM,MAAM,KAAK,IAAI;AACrB,WAAO,CAAC,CAAC,OAAO,gBAAgB,OAAO,OAAO,IAAI,eAAe;AAAA,EACnE,CAAC;AAGD,QAAM,WAAW,MAAM,KAAK,GAAG;AAE/B,QAAM,WAAW;AAAA,IACf,OAAO,aAAa,WAAW,iBAAiB,IAAI,KAAK,GAAG,QAAQ,IAAI;AAAA,EAC1E;AAIA,QAAM,OAAO,aAA6B,IAAI;AAK9C,gBAAc,MAAM;AAClB,UAAM,IAAI,IAAI,KAAK;AACnB,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,CAAC,aAAa,IAAI,KAAK,CAAC,KAAK;AAC/B,WAAK,IAAI,IAAI;AACb;AAAA,IACF;AACA,UAAM,MAAO,IAAe,WAAW,CAAC;AACxC,SAAK,IAAI,GAAG;AACZ,aAAS,IAAI,IAAI,OAAO;AAAA,EAC1B,CAAC;AAKD;AAAA,IACE;AAAA,IACA;AAAA,IACA,CAAC,MAAa;AACZ,eAAS,IAAK,EAA0B,OAAO;AAAA,IACjD;AAAA,IACA,EAAE,SAAS,KAAK;AAAA,EAClB;AAEA,SAAO;AACT;","names":[]}