@ngrok/mantle 0.63.1 → 0.63.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/hooks.js CHANGED
@@ -1,2 +1,2 @@
1
- import{a as Q}from"./chunk-GLSHD37P.js";import{a as M}from"./chunk-6J7D73WA.js";import{a as y,b as T,c as g}from"./chunk-SMYI7SUP.js";import{a as b}from"./chunk-KMNACVH6.js";import{useSyncExternalStore as x}from"react";var h=["2xl","xl","lg","md","sm","xs","2xs"],R=["default",...h];function C(){return x(v,S,()=>"default")}function L(e){return x(E(e),W(e),()=>!1)}var r={"2xl":"(min-width: 96rem)",xl:"(min-width: 80rem)",lg:"(min-width: 64rem)",md:"(min-width: 48rem)",sm:"(min-width: 40rem)",xs:"(min-width: 30rem)","2xs":"(min-width: 22.5rem)"},o={"2xl":"(max-width: 95.99rem)",xl:"(max-width: 79.99rem)",lg:"(max-width: 63.99rem)",md:"(max-width: 47.99rem)",sm:"(max-width: 39.99rem)",xs:"(max-width: 29.99rem)","2xs":"(max-width: 22.49rem)"},u=null,c=null;function p(){return u||(u={"2xl":window.matchMedia(r["2xl"]),xl:window.matchMedia(r.xl),lg:window.matchMedia(r.lg),md:window.matchMedia(r.md),sm:window.matchMedia(r.sm),xs:window.matchMedia(r.xs),"2xs":window.matchMedia(r["2xs"])}),u}function k(e){return c||(c={"2xl":window.matchMedia(o["2xl"]),xl:window.matchMedia(o.xl),lg:window.matchMedia(o.lg),md:window.matchMedia(o.md),sm:window.matchMedia(o.sm),xs:window.matchMedia(o.xs),"2xs":window.matchMedia(o["2xs"])}),c[e]}var m="default",d=new Set,s=!1;function B(){let e=p();for(let t of h)if(e[t].matches)return t;return"default"}var l=!1;function w(){l||(l=!0,requestAnimationFrame(()=>{l=!1;let e=B();if(e!==m){m=e;for(let t of d)t()}}))}function v(e){if(d.add(e),!s){s=!0;let t=p();m=B();for(let n of Object.values(t))n.addEventListener("change",w)}return e(),()=>{if(d.delete(e),d.size===0&&s){s=!1;let t=p();for(let n of Object.values(t))n.removeEventListener("change",w)}}}function S(){return m}function E(e){return t=>{let n=k(e),i=!1,a=()=>{i||(i=!0,requestAnimationFrame(()=>{i=!1,t()}))};return n.addEventListener("change",a),()=>{n.removeEventListener("change",a)}}}function W(e){return()=>k(e).matches}import{useEffect as q,useMemo as I,useRef as j}from"react";function f(e){let t=j(e);return q(()=>{t.current=e}),I(()=>((...n)=>t.current?.(...n)),[])}import{useCallback as O,useEffect as P,useRef as A}from"react";function F(e,t){let n=f(e),i=A(0);return P(()=>()=>window.clearTimeout(i.current),[]),O((...a)=>{window.clearTimeout(i.current),i.current=window.setTimeout(()=>n(...a),t.waitMs)},[n,t.waitMs])}import{useMemo as D}from"react";var _=(e="mantle")=>D(()=>$(e),[e]);function $(e="mantle"){return[e.trim()||"mantle",z()].join("-")}function z(){return Math.random().toString(36).substring(2,9)}export{R as breakpoints,C as useBreakpoint,f as useCallbackRef,Q as useCopyToClipboard,F as useDebouncedCallback,L as useIsBelowBreakpoint,b as useIsHydrated,y as useIsomorphicLayoutEffect,M as useMatchesMediaQuery,T as usePrefersReducedMotion,_ as useRandomStableId,g as useScrollBehavior};
1
+ import{a as R}from"./chunk-GLSHD37P.js";import{a as g}from"./chunk-6J7D73WA.js";import{a as Q,b as C,c as v}from"./chunk-SMYI7SUP.js";import{a as y}from"./chunk-KMNACVH6.js";import{useSyncExternalStore as B}from"react";var M=["2xl","xl","lg","md","sm","xs","2xs"],L=["default",...M];function S(){return B(W,q,()=>"default")}function E(e){return B(I(e),j(e),()=>!1)}var i={"2xl":"(min-width: 96rem)",xl:"(min-width: 80rem)",lg:"(min-width: 64rem)",md:"(min-width: 48rem)",sm:"(min-width: 40rem)",xs:"(min-width: 30rem)","2xs":"(min-width: 22.5rem)"},o={"2xl":"(max-width: 95.99rem)",xl:"(max-width: 79.99rem)",lg:"(max-width: 63.99rem)",md:"(max-width: 47.99rem)",sm:"(max-width: 39.99rem)",xs:"(max-width: 29.99rem)","2xs":"(max-width: 22.49rem)"},u=null,c=null;function p(){return u||(u={"2xl":window.matchMedia(i["2xl"]),xl:window.matchMedia(i.xl),lg:window.matchMedia(i.lg),md:window.matchMedia(i.md),sm:window.matchMedia(i.sm),xs:window.matchMedia(i.xs),"2xs":window.matchMedia(i["2xs"])}),u}function b(e){return c||(c={"2xl":window.matchMedia(o["2xl"]),xl:window.matchMedia(o.xl),lg:window.matchMedia(o.lg),md:window.matchMedia(o.md),sm:window.matchMedia(o.sm),xs:window.matchMedia(o.xs),"2xs":window.matchMedia(o["2xs"])}),c[e]}var m="default",d=new Set,s=!1;function T(){let e=p();for(let t of M)if(e[t].matches)return t;return"default"}var l=!1;function x(){l||(l=!0,requestAnimationFrame(()=>{l=!1;let e=T();if(e!==m){m=e;for(let t of d)t()}}))}function W(e){if(d.add(e),!s){s=!0;let t=p();m=T();for(let n of Object.values(t))n.addEventListener("change",x)}return e(),()=>{if(d.delete(e),d.size===0&&s){s=!1;let t=p();for(let n of Object.values(t))n.removeEventListener("change",x)}}}function q(){return m}var h=new Map;function I(e){let t=h.get(e);return t||(t=n=>{let r=b(e),a=!1,f=()=>{a||(a=!0,requestAnimationFrame(()=>{a=!1,n()}))};return r.addEventListener("change",f),()=>{r.removeEventListener("change",f)}},h.set(e,t),t)}var k=new Map;function j(e){let t=k.get(e);return t||(t=()=>b(e).matches,k.set(e,t),t)}import{useEffect as O,useMemo as P,useRef as A}from"react";function w(e){let t=A(e);return O(()=>{t.current=e}),P(()=>((...n)=>t.current?.(...n)),[])}import{useCallback as F,useEffect as D,useRef as _}from"react";function $(e,t){let n=w(e),r=_(0);return D(()=>()=>window.clearTimeout(r.current),[]),F((...a)=>{window.clearTimeout(r.current),r.current=window.setTimeout(()=>n(...a),t.waitMs)},[n,t.waitMs])}import{useMemo as z}from"react";var G=(e="mantle")=>z(()=>H(e),[e]);function H(e="mantle"){return[e.trim()||"mantle",U()].join("-")}function U(){return Math.random().toString(36).substring(2,9)}export{L as breakpoints,S as useBreakpoint,w as useCallbackRef,R as useCopyToClipboard,$ as useDebouncedCallback,E as useIsBelowBreakpoint,y as useIsHydrated,Q as useIsomorphicLayoutEffect,g as useMatchesMediaQuery,C as usePrefersReducedMotion,G as useRandomStableId,v as useScrollBehavior};
2
2
  //# sourceMappingURL=hooks.js.map
package/dist/hooks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/use-breakpoint.tsx","../src/hooks/use-callback-ref.tsx","../src/hooks/use-debounced-callback.tsx","../src/hooks/use-random-stable-id.tsx"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n/**\n * Tailwind CSS breakpoints in descending order (largest → smallest).\n *\n * These correspond to Tailwind’s default `theme.screens` config and are used\n * to determine the current viewport size.\n *\n * @see https://tailwindcss.com/docs/screens\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\nconst tailwindBreakpoints = [\"2xl\", \"xl\", \"lg\", \"md\", \"sm\", \"xs\", \"2xs\"] as const;\n\n/**\n * A valid Tailwind CSS breakpoint identifier.\n *\n * @example\n * const bp: TailwindBreakpoint = \"md\"; // ≥48rem (768px)\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\ntype TailwindBreakpoint = (typeof tailwindBreakpoints)[number];\n\n/**\n * Mantle’s breakpoint set, extending Tailwind’s with `\"default\"`.\n *\n * `\"default\"` represents the base (0px and up) viewport,\n * useful for defining fallbacks or mobile-first styles.\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\nconst breakpoints = [\"default\", ...tailwindBreakpoints] as const;\n\n/**\n * A valid Mantle breakpoint identifier.\n *\n * Includes Tailwind’s standard breakpoints plus `\"default\"` for 0px+.\n *\n * @example\n * const bp: Breakpoint = \"default\"; // ≥0px\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\ntype Breakpoint = (typeof breakpoints)[number];\n\n/**\n * React hook that returns the current breakpoint based on the viewport width.\n *\n * Uses a singleton subscription to a set of min-width media queries and returns\n * the largest matching breakpoint. Designed for React 18+ with\n * `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The current breakpoint that matches the viewport width.\n *\n * @example\n * const breakpoint = useBreakpoint();\n * if (breakpoint === \"lg\") {\n * // Do something for large screens and above\n * }\n */\nfunction useBreakpoint(): Breakpoint {\n\treturn useSyncExternalStore(\n\t\tsubscribeToBreakpointChanges,\n\t\tgetCurrentBreakpointSnapshot,\n\t\t() => \"default\", // SSR fallback\n\t);\n}\n\n/**\n * React hook that returns true if the current viewport width is below the specified breakpoint.\n *\n * This hook uses `window.matchMedia` with a max-width media query and leverages\n * `useSyncExternalStore` to stay compliant with React's concurrent rendering model.\n *\n * @param {TailwindBreakpoint} breakpoint - The breakpoint to check against (e.g., \"md\", \"lg\").\n *\n * @returns {boolean} `true` if the viewport width is below the breakpoint, otherwise `false`.\n *\n * @example\n * // Check if viewport is below medium (768px)\n * const isBelowMd = useIsBelowBreakpoint(\"md\");\n */\nfunction useIsBelowBreakpoint(breakpoint: TailwindBreakpoint): boolean {\n\treturn useSyncExternalStore(\n\t\tcreateBelowBreakpointSubscribe(breakpoint),\n\t\tcreateBelowBreakpointGetSnapshot(breakpoint),\n\t\t() => false, // SSR fallback - assume desktop\n\t);\n}\n\nexport {\n\t//,\n\tbreakpoints,\n\tuseBreakpoint,\n\tuseIsBelowBreakpoint,\n};\n\nexport type {\n\t//,\n\tBreakpoint,\n\tTailwindBreakpoint,\n};\n\n/**\n * A CSS media query string representing a minimum width in `rem` units.\n *\n * @example\n * const query: MinWidthQuery = \"(min-width: 48rem)\";\n *\n * @private\n */\ntype MinWidthQuery = `(min-width: ${number}rem)`;\n\n/**\n * A CSS media query string representing a maximum width in `rem` units.\n *\n * @example\n * const query: MaxWidthQuery = \"(max-width: 47.99rem)\";\n *\n * @private\n */\ntype MaxWidthQuery = `(max-width: ${number}rem)`;\n\n/**\n * Precomputed min-width media query strings for each Tailwind breakpoint.\n *\n * Using constants avoids template string work in hot paths and ensures type\n * safety against the `MinWidthQuery` template literal type.\n *\n * @remarks\n * These are expressed in `rem`. If your CSS breakpoints are in `px`, consider\n * aligning units to avoid JS/CSS drift when `html{font-size}` changes.\n *\n * @private\n */\nconst breakpointQueries = {\n\t\"2xl\": \"(min-width: 96rem)\" as const,\n\txl: \"(min-width: 80rem)\" as const,\n\tlg: \"(min-width: 64rem)\" as const,\n\tmd: \"(min-width: 48rem)\" as const,\n\tsm: \"(min-width: 40rem)\" as const,\n\txs: \"(min-width: 30rem)\" as const,\n\t\"2xs\": \"(min-width: 22.5rem)\" as const,\n} as const satisfies Record<TailwindBreakpoint, MinWidthQuery>;\n\n/**\n * Precomputed max-width media query strings used by `useIsBelowBreakpoint`.\n *\n * The `-0.01rem` offset avoids overlap at exact boundaries.\n *\n * @private\n */\nconst belowBreakpointQueries = {\n\t\"2xl\": \"(max-width: 95.99rem)\" as const, // 96 - 0.01\n\txl: \"(max-width: 79.99rem)\" as const, // 80 - 0.01\n\tlg: \"(max-width: 63.99rem)\" as const, // 64 - 0.01\n\tmd: \"(max-width: 47.99rem)\" as const, // 48 - 0.01\n\tsm: \"(max-width: 39.99rem)\" as const, // 40 - 0.01\n\txs: \"(max-width: 29.99rem)\" as const, // 30 - 0.01\n\t\"2xs\": \"(max-width: 22.49rem)\" as const, // 22.5 - 0.01\n} as const satisfies Record<TailwindBreakpoint, MaxWidthQuery>;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for min-width queries.\n *\n * Initialized on first access to remain SSR-safe (no `window` at import time).\n *\n * @private\n */\nlet minWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for max-width queries.\n *\n * Used by `useIsBelowBreakpoint`. Also SSR-safe by lazy access.\n *\n * @private\n */\nlet maxWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` objects for min-width queries.\n *\n * @returns A record of `MediaQueryList` keyed by Tailwind breakpoint.\n * @private\n */\nfunction getMinWidthMQLs(): Record<TailwindBreakpoint, MediaQueryList> {\n\tif (!minWidthMQLs) {\n\t\tminWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(breakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(breakpointQueries.xl),\n\t\t\tlg: window.matchMedia(breakpointQueries.lg),\n\t\t\tmd: window.matchMedia(breakpointQueries.md),\n\t\t\tsm: window.matchMedia(breakpointQueries.sm),\n\t\t\txs: window.matchMedia(breakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(breakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn minWidthMQLs;\n}\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` for a specific max-width breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"md\").\n * @returns The corresponding `MediaQueryList`.\n * @private\n */\nfunction getMaxWidthMQL(breakpoint: TailwindBreakpoint): MediaQueryList {\n\tif (!maxWidthMQLs) {\n\t\tmaxWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(belowBreakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(belowBreakpointQueries.xl),\n\t\t\tlg: window.matchMedia(belowBreakpointQueries.lg),\n\t\t\tmd: window.matchMedia(belowBreakpointQueries.md),\n\t\t\tsm: window.matchMedia(belowBreakpointQueries.sm),\n\t\t\txs: window.matchMedia(belowBreakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(belowBreakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn maxWidthMQLs[breakpoint];\n}\n\n/**\n * Current breakpoint value used by the singleton store backing `useBreakpoint`.\n *\n * Initialized to `\"default\"` and updated on media-query change events.\n *\n * @private\n */\nlet currentBreakpointValue: Breakpoint = \"default\";\n\n/**\n * Set of component listeners subscribed to the singleton breakpoint store.\n *\n * Each listener is invoked when the current breakpoint value changes.\n *\n * @private\n */\nconst breakpointListeners = new Set<() => void>();\n\n/**\n * Flag indicating whether global media-query listeners are currently attached.\n *\n * Prevents duplicate registrations and enables full teardown when unused.\n *\n * @private\n */\nlet breakpointSubscriptionActive = false;\n\n/**\n * Compute the current breakpoint by checking cached min-width MQLs\n * from largest to smallest.\n *\n * @returns {Breakpoint} The largest matching breakpoint, or `\"default\"`.\n * @private\n */\nfunction getCurrentBreakpoint(): Breakpoint {\n\tconst mqls = getMinWidthMQLs();\n\tfor (const breakpoint of tailwindBreakpoints) {\n\t\tif (mqls[breakpoint].matches) {\n\t\t\treturn breakpoint;\n\t\t}\n\t}\n\treturn \"default\";\n}\n\n/**\n * Update the current breakpoint value and notify all listeners.\n *\n * Uses `requestAnimationFrame` to coalesce rapid resize events and minimize\n * re-renders during active window resizing.\n *\n * @private\n */\nlet breakpointUpdatePending = false;\nfunction updateCurrentBreakpoint() {\n\tif (!breakpointUpdatePending) {\n\t\tbreakpointUpdatePending = true;\n\t\trequestAnimationFrame(() => {\n\t\t\tbreakpointUpdatePending = false;\n\t\t\tconst newBreakpoint = getCurrentBreakpoint();\n\t\t\tif (newBreakpoint !== currentBreakpointValue) {\n\t\t\t\tcurrentBreakpointValue = newBreakpoint;\n\t\t\t\tfor (const listener of breakpointListeners) {\n\t\t\t\t\tlistener();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * Subscribe a component to breakpoint changes (singleton pattern).\n *\n * Ensures only one set of MQL listeners exists app-wide. Also reconciles the\n * `useSyncExternalStore` initial snapshot/subscribe race by invoking the\n * subscriber once on mount.\n *\n * @param callback - Listener invoked when the breakpoint value may have changed.\n * @returns Cleanup function to unsubscribe the listener.\n * @private\n */\nfunction subscribeToBreakpointChanges(callback: () => void) {\n\tbreakpointListeners.add(callback);\n\n\t// Attach global listeners once\n\tif (!breakpointSubscriptionActive) {\n\t\tbreakpointSubscriptionActive = true;\n\t\tconst mqls = getMinWidthMQLs();\n\n\t\t// Initialize current value synchronously\n\t\tcurrentBreakpointValue = getCurrentBreakpoint();\n\n\t\t// Attach listeners to all breakpoint MQLs\n\t\tfor (const mql of Object.values(mqls)) {\n\t\t\tmql.addEventListener(\"change\", updateCurrentBreakpoint);\n\t\t}\n\t}\n\n\t// Reconcile initial getSnapshot vs subscribe ordering\n\tcallback();\n\n\t// Cleanup\n\treturn () => {\n\t\tbreakpointListeners.delete(callback);\n\n\t\t// Tear down global listeners when no one is listening\n\t\tif (breakpointListeners.size === 0 && breakpointSubscriptionActive) {\n\t\t\tbreakpointSubscriptionActive = false;\n\t\t\tconst mqls = getMinWidthMQLs();\n\t\t\tfor (const mql of Object.values(mqls)) {\n\t\t\t\tmql.removeEventListener(\"change\", updateCurrentBreakpoint);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Return the current breakpoint value from the singleton store.\n *\n * Used as the `getSnapshot` for `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The latest computed breakpoint.\n * @private\n */\nfunction getCurrentBreakpointSnapshot(): Breakpoint {\n\treturn currentBreakpointValue;\n}\n\n/**\n * Factory to create a `subscribe` function for a specific \"below\" breakpoint.\n *\n * Uses a cached `MediaQueryList` and rAF-throttled change handler to avoid\n * bursty updates during resize.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A `subscribe` function suitable for `useSyncExternalStore`.\n * @private\n */\nfunction createBelowBreakpointSubscribe(breakpoint: TailwindBreakpoint) {\n\treturn (callback: () => void) => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\n\t\t// rAF throttle the change callback during active resize\n\t\tlet pending = false;\n\t\tconst onChange = () => {\n\t\t\tif (!pending) {\n\t\t\t\tpending = true;\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tpending = false;\n\t\t\t\t\tcallback();\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", onChange);\n\t\treturn () => {\n\t\t\tmediaQuery.removeEventListener(\"change\", onChange);\n\t\t};\n\t};\n}\n\n/**\n * Factory to create a `getSnapshot` function for a specific \"below\" breakpoint.\n *\n * Uses the cached `MediaQueryList` for the target breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A function that returns `true` when the viewport is below the breakpoint.\n * @private\n */\nfunction createBelowBreakpointGetSnapshot(breakpoint: TailwindBreakpoint) {\n\treturn () => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\t\treturn mediaQuery.matches;\n\t};\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\n/**\n * Returns a memoized callback that will always refer to the latest callback\n * passed to the hook.\n *\n * This is useful when you want to pass a callback which may or may not be\n * memoized (have a stable identity) to a child component that will be updated\n * without causing the child component to re-render.\n */\nfunction useCallbackRef<T extends (...args: unknown[]) => unknown>(callback: T | undefined): T {\n\tconst callbackRef = useRef(callback);\n\n\tuseEffect(() => {\n\t\tcallbackRef.current = callback;\n\t});\n\n\treturn useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);\n}\n\nexport {\n\t//,\n\tuseCallbackRef,\n};\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useCallbackRef } from \"./use-callback-ref.js\";\n\ntype Options = {\n\t/**\n\t * The delay in milliseconds to wait before calling the callback.\n\t */\n\twaitMs: number;\n};\n\n/**\n * Create a debounced version of a callback function.\n *\n * It allows you to delay the execution of a function until a certain period of\n * inactivity has passed (the `options.waitMs`), which can be useful for limiting rapid\n * invocations of a function (like in search inputs or button clicks)\n *\n * Note: The debounced callback will always refer to the latest callback passed\n * even without memoization, so it's stable and safe to use in dependency arrays.\n */\nfunction useDebouncedCallback<T extends (...args: unknown[]) => unknown>(\n\tcallbackFn: T,\n\toptions: Options,\n) {\n\tconst stableCallbackFn = useCallbackRef(callbackFn);\n\tconst debounceTimerRef = useRef(0);\n\tuseEffect(() => () => window.clearTimeout(debounceTimerRef.current), []);\n\n\treturn useCallback(\n\t\t(...args: Parameters<T>) => {\n\t\t\twindow.clearTimeout(debounceTimerRef.current);\n\t\t\tdebounceTimerRef.current = window.setTimeout(() => stableCallbackFn(...args), options.waitMs);\n\t\t},\n\t\t[stableCallbackFn, options.waitMs],\n\t);\n}\n\nexport {\n\t//,\n\tuseDebouncedCallback,\n};\n","import { useMemo } from \"react\";\n\n/**\n * Hook to generate a random, stable id.\n * This is similar to `useId`, but generates a stable id client side which can also\n * be used for css selectors and element ids.\n */\nconst useRandomStableId = (prefix = \"mantle\") => useMemo(() => randomStableId(prefix), [prefix]);\n\nexport {\n\t//,\n\tuseRandomStableId,\n};\n\nfunction randomStableId(prefix = \"mantle\") {\n\tconst _prefix = prefix.trim() || \"mantle\";\n\treturn [_prefix, randomPostfix()].join(\"-\");\n}\n\nfunction randomPostfix() {\n\treturn Math.random().toString(36).substring(2, 9);\n}\n"],"mappings":"8KAAA,OAAS,wBAAAA,MAA4B,QAmBrC,IAAMC,EAAsB,CAAC,MAAO,KAAM,KAAM,KAAM,KAAM,KAAM,KAAK,EAmCjEC,EAAc,CAAC,UAAW,GAAGD,CAAmB,EAqCtD,SAASE,GAA4B,CACpC,OAAOH,EACNI,EACAC,EACA,IAAM,SACP,CACD,CAgBA,SAASC,EAAqBC,EAAyC,CACtE,OAAOP,EACNQ,EAA+BD,CAAU,EACzCE,EAAiCF,CAAU,EAC3C,IAAM,EACP,CACD,CA+CA,IAAMG,EAAoB,CACzB,MAAO,qBACP,GAAI,qBACJ,GAAI,qBACJ,GAAI,qBACJ,GAAI,qBACJ,GAAI,qBACJ,MAAO,sBACR,EASMC,EAAyB,CAC9B,MAAO,wBACP,GAAI,wBACJ,GAAI,wBACJ,GAAI,wBACJ,GAAI,wBACJ,GAAI,wBACJ,MAAO,uBACR,EASIC,EAAkE,KASlEC,EAAkE,KAQtE,SAASC,GAA8D,CACtE,OAAKF,IACJA,EAAe,CACd,MAAO,OAAO,WAAWF,EAAkB,KAAK,CAAC,EACjD,GAAI,OAAO,WAAWA,EAAkB,EAAE,EAC1C,GAAI,OAAO,WAAWA,EAAkB,EAAE,EAC1C,GAAI,OAAO,WAAWA,EAAkB,EAAE,EAC1C,GAAI,OAAO,WAAWA,EAAkB,EAAE,EAC1C,GAAI,OAAO,WAAWA,EAAkB,EAAE,EAC1C,MAAO,OAAO,WAAWA,EAAkB,KAAK,CAAC,CAClD,GAEME,CACR,CASA,SAASG,EAAeC,EAAgD,CACvE,OAAKH,IACJA,EAAe,CACd,MAAO,OAAO,WAAWF,EAAuB,KAAK,CAAC,EACtD,GAAI,OAAO,WAAWA,EAAuB,EAAE,EAC/C,GAAI,OAAO,WAAWA,EAAuB,EAAE,EAC/C,GAAI,OAAO,WAAWA,EAAuB,EAAE,EAC/C,GAAI,OAAO,WAAWA,EAAuB,EAAE,EAC/C,GAAI,OAAO,WAAWA,EAAuB,EAAE,EAC/C,MAAO,OAAO,WAAWA,EAAuB,KAAK,CAAC,CACvD,GAEME,EAAaG,CAAU,CAC/B,CASA,IAAIC,EAAqC,UASnCC,EAAsB,IAAI,IAS5BC,EAA+B,GASnC,SAASC,GAAmC,CAC3C,IAAMC,EAAOP,EAAgB,EAC7B,QAAWE,KAAcM,EACxB,GAAID,EAAKL,CAAU,EAAE,QACpB,OAAOA,EAGT,MAAO,SACR,CAUA,IAAIO,EAA0B,GAC9B,SAASC,GAA0B,CAC7BD,IACJA,EAA0B,GAC1B,sBAAsB,IAAM,CAC3BA,EAA0B,GAC1B,IAAME,EAAgBL,EAAqB,EAC3C,GAAIK,IAAkBR,EAAwB,CAC7CA,EAAyBQ,EACzB,QAAWC,KAAYR,EACtBQ,EAAS,CAEX,CACD,CAAC,EAEH,CAaA,SAASC,EAA6BC,EAAsB,CAI3D,GAHAV,EAAoB,IAAIU,CAAQ,EAG5B,CAACT,EAA8B,CAClCA,EAA+B,GAC/B,IAAME,EAAOP,EAAgB,EAG7BG,EAAyBG,EAAqB,EAG9C,QAAWS,KAAO,OAAO,OAAOR,CAAI,EACnCQ,EAAI,iBAAiB,SAAUL,CAAuB,CAExD,CAGA,OAAAI,EAAS,EAGF,IAAM,CAIZ,GAHAV,EAAoB,OAAOU,CAAQ,EAG/BV,EAAoB,OAAS,GAAKC,EAA8B,CACnEA,EAA+B,GAC/B,IAAME,EAAOP,EAAgB,EAC7B,QAAWe,KAAO,OAAO,OAAOR,CAAI,EACnCQ,EAAI,oBAAoB,SAAUL,CAAuB,CAE3D,CACD,CACD,CAUA,SAASM,GAA2C,CACnD,OAAOb,CACR,CAYA,SAASc,EAA+Bf,EAAgC,CACvE,OAAQY,GAAyB,CAChC,IAAMI,EAAajB,EAAeC,CAAU,EAGxCiB,EAAU,GACRC,EAAW,IAAM,CACjBD,IACJA,EAAU,GACV,sBAAsB,IAAM,CAC3BA,EAAU,GACVL,EAAS,CACV,CAAC,EAEH,EAEA,OAAAI,EAAW,iBAAiB,SAAUE,CAAQ,EACvC,IAAM,CACZF,EAAW,oBAAoB,SAAUE,CAAQ,CAClD,CACD,CACD,CAWA,SAASC,EAAiCnB,EAAgC,CACzE,MAAO,IACaD,EAAeC,CAAU,EAC1B,OAEpB,CC5aA,OAAS,aAAAoB,EAAW,WAAAC,EAAS,UAAAC,MAAc,QAU3C,SAASC,EAA0DC,EAA4B,CAC9F,IAAMC,EAAcH,EAAOE,CAAQ,EAEnC,OAAAJ,EAAU,IAAM,CACfK,EAAY,QAAUD,CACvB,CAAC,EAEMH,EAAQ,KAAO,IAAIK,IAASD,EAAY,UAAU,GAAGC,CAAI,GAAS,CAAC,CAAC,CAC5E,CClBA,OAAS,eAAAC,EAAa,aAAAC,EAAW,UAAAC,MAAc,QAoB/C,SAASC,EACRC,EACAC,EACC,CACD,IAAMC,EAAmBC,EAAeH,CAAU,EAC5CI,EAAmBC,EAAO,CAAC,EACjC,OAAAC,EAAU,IAAM,IAAM,OAAO,aAAaF,EAAiB,OAAO,EAAG,CAAC,CAAC,EAEhEG,EACN,IAAIC,IAAwB,CAC3B,OAAO,aAAaJ,EAAiB,OAAO,EAC5CA,EAAiB,QAAU,OAAO,WAAW,IAAMF,EAAiB,GAAGM,CAAI,EAAGP,EAAQ,MAAM,CAC7F,EACA,CAACC,EAAkBD,EAAQ,MAAM,CAClC,CACD,CCnCA,OAAS,WAAAQ,MAAe,QAOxB,IAAMC,EAAoB,CAACC,EAAS,WAAaF,EAAQ,IAAMG,EAAeD,CAAM,EAAG,CAACA,CAAM,CAAC,EAO/F,SAASE,EAAeC,EAAS,SAAU,CAE1C,MAAO,CADSA,EAAO,KAAK,GAAK,SAChBC,EAAc,CAAC,EAAE,KAAK,GAAG,CAC3C,CAEA,SAASA,GAAgB,CACxB,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,EAAG,CAAC,CACjD","names":["useSyncExternalStore","tailwindBreakpoints","breakpoints","useBreakpoint","subscribeToBreakpointChanges","getCurrentBreakpointSnapshot","useIsBelowBreakpoint","breakpoint","createBelowBreakpointSubscribe","createBelowBreakpointGetSnapshot","breakpointQueries","belowBreakpointQueries","minWidthMQLs","maxWidthMQLs","getMinWidthMQLs","getMaxWidthMQL","breakpoint","currentBreakpointValue","breakpointListeners","breakpointSubscriptionActive","getCurrentBreakpoint","mqls","tailwindBreakpoints","breakpointUpdatePending","updateCurrentBreakpoint","newBreakpoint","listener","subscribeToBreakpointChanges","callback","mql","getCurrentBreakpointSnapshot","createBelowBreakpointSubscribe","mediaQuery","pending","onChange","createBelowBreakpointGetSnapshot","useEffect","useMemo","useRef","useCallbackRef","callback","callbackRef","args","useCallback","useEffect","useRef","useDebouncedCallback","callbackFn","options","stableCallbackFn","useCallbackRef","debounceTimerRef","useRef","useEffect","useCallback","args","useMemo","useRandomStableId","prefix","randomStableId","randomStableId","prefix","randomPostfix"]}
1
+ {"version":3,"sources":["../src/hooks/use-breakpoint.tsx","../src/hooks/use-callback-ref.tsx","../src/hooks/use-debounced-callback.tsx","../src/hooks/use-random-stable-id.tsx"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n/**\n * Tailwind CSS breakpoints in descending order (largest → smallest).\n *\n * These correspond to Tailwind’s default `theme.screens` config and are used\n * to determine the current viewport size.\n *\n * @see https://tailwindcss.com/docs/screens\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\nconst tailwindBreakpoints = [\"2xl\", \"xl\", \"lg\", \"md\", \"sm\", \"xs\", \"2xs\"] as const;\n\n/**\n * A valid Tailwind CSS breakpoint identifier.\n *\n * @example\n * const bp: TailwindBreakpoint = \"md\"; // ≥48rem (768px)\n *\n * @example\n * \"2xl\" // ≥96rem (1536px)\n * \"xl\" // ≥80rem (1280px)\n * \"lg\" // ≥64rem (1024px)\n * \"md\" // ≥48rem (768px)\n * \"sm\" // ≥40rem (640px)\n * \"xs\" // ≥30rem (480px)\n * \"2xs\" // ≥22.5rem (360px)\n */\ntype TailwindBreakpoint = (typeof tailwindBreakpoints)[number];\n\n/**\n * Mantle’s breakpoint set, extending Tailwind’s with `\"default\"`.\n *\n * `\"default\"` represents the base (0px and up) viewport,\n * useful for defining fallbacks or mobile-first styles.\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\nconst breakpoints = [\"default\", ...tailwindBreakpoints] as const;\n\n/**\n * A valid Mantle breakpoint identifier.\n *\n * Includes Tailwind’s standard breakpoints plus `\"default\"` for 0px+.\n *\n * @example\n * const bp: Breakpoint = \"default\"; // ≥0px\n *\n * @example\n * \"default\" // ≥0rem (0px)\n * \"2xs\" // ≥22.5rem (360px)\n * \"xs\" // ≥30rem (480px)\n * \"sm\" // ≥40rem (640px)\n * \"md\" // ≥48rem (768px)\n * \"lg\" // ≥64rem (1024px)\n * \"xl\" // ≥80rem (1280px)\n * \"2xl\" // ≥96rem (1536px)\n */\ntype Breakpoint = (typeof breakpoints)[number];\n\n/**\n * React hook that returns the current breakpoint based on the viewport width.\n *\n * Uses a singleton subscription to a set of min-width media queries and returns\n * the largest matching breakpoint. Designed for React 18+ with\n * `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The current breakpoint that matches the viewport width.\n *\n * @example\n * const breakpoint = useBreakpoint();\n * if (breakpoint === \"lg\") {\n * // Do something for large screens and above\n * }\n */\nfunction useBreakpoint(): Breakpoint {\n\treturn useSyncExternalStore(\n\t\tsubscribeToBreakpointChanges,\n\t\tgetCurrentBreakpointSnapshot,\n\t\t() => \"default\", // SSR fallback\n\t);\n}\n\n/**\n * React hook that returns true if the current viewport width is below the specified breakpoint.\n *\n * This hook uses `window.matchMedia` with a max-width media query and leverages\n * `useSyncExternalStore` to stay compliant with React's concurrent rendering model.\n *\n * @param {TailwindBreakpoint} breakpoint - The breakpoint to check against (e.g., \"md\", \"lg\").\n *\n * @returns {boolean} `true` if the viewport width is below the breakpoint, otherwise `false`.\n *\n * @example\n * // Check if viewport is below medium (768px)\n * const isBelowMd = useIsBelowBreakpoint(\"md\");\n */\nfunction useIsBelowBreakpoint(breakpoint: TailwindBreakpoint): boolean {\n\treturn useSyncExternalStore(\n\t\tcreateBelowBreakpointSubscribe(breakpoint),\n\t\tcreateBelowBreakpointGetSnapshot(breakpoint),\n\t\t() => false, // SSR fallback - assume desktop\n\t);\n}\n\nexport {\n\t//,\n\tbreakpoints,\n\tuseBreakpoint,\n\tuseIsBelowBreakpoint,\n};\n\nexport type {\n\t//,\n\tBreakpoint,\n\tTailwindBreakpoint,\n};\n\n/**\n * A CSS media query string representing a minimum width in `rem` units.\n *\n * @example\n * const query: MinWidthQuery = \"(min-width: 48rem)\";\n *\n * @private\n */\ntype MinWidthQuery = `(min-width: ${number}rem)`;\n\n/**\n * A CSS media query string representing a maximum width in `rem` units.\n *\n * @example\n * const query: MaxWidthQuery = \"(max-width: 47.99rem)\";\n *\n * @private\n */\ntype MaxWidthQuery = `(max-width: ${number}rem)`;\n\n/**\n * Precomputed min-width media query strings for each Tailwind breakpoint.\n *\n * Using constants avoids template string work in hot paths and ensures type\n * safety against the `MinWidthQuery` template literal type.\n *\n * @remarks\n * These are expressed in `rem`. If your CSS breakpoints are in `px`, consider\n * aligning units to avoid JS/CSS drift when `html{font-size}` changes.\n *\n * @private\n */\nconst breakpointQueries = {\n\t\"2xl\": \"(min-width: 96rem)\" as const,\n\txl: \"(min-width: 80rem)\" as const,\n\tlg: \"(min-width: 64rem)\" as const,\n\tmd: \"(min-width: 48rem)\" as const,\n\tsm: \"(min-width: 40rem)\" as const,\n\txs: \"(min-width: 30rem)\" as const,\n\t\"2xs\": \"(min-width: 22.5rem)\" as const,\n} as const satisfies Record<TailwindBreakpoint, MinWidthQuery>;\n\n/**\n * Precomputed max-width media query strings used by `useIsBelowBreakpoint`.\n *\n * The `-0.01rem` offset avoids overlap at exact boundaries.\n *\n * @private\n */\nconst belowBreakpointQueries = {\n\t\"2xl\": \"(max-width: 95.99rem)\" as const, // 96 - 0.01\n\txl: \"(max-width: 79.99rem)\" as const, // 80 - 0.01\n\tlg: \"(max-width: 63.99rem)\" as const, // 64 - 0.01\n\tmd: \"(max-width: 47.99rem)\" as const, // 48 - 0.01\n\tsm: \"(max-width: 39.99rem)\" as const, // 40 - 0.01\n\txs: \"(max-width: 29.99rem)\" as const, // 30 - 0.01\n\t\"2xs\": \"(max-width: 22.49rem)\" as const, // 22.5 - 0.01\n} as const satisfies Record<TailwindBreakpoint, MaxWidthQuery>;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for min-width queries.\n *\n * Initialized on first access to remain SSR-safe (no `window` at import time).\n *\n * @private\n */\nlet minWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Lazily-initialized cache of `MediaQueryList` objects for max-width queries.\n *\n * Used by `useIsBelowBreakpoint`. Also SSR-safe by lazy access.\n *\n * @private\n */\nlet maxWidthMQLs: Record<TailwindBreakpoint, MediaQueryList> | null = null;\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` objects for min-width queries.\n *\n * @returns A record of `MediaQueryList` keyed by Tailwind breakpoint.\n * @private\n */\nfunction getMinWidthMQLs(): Record<TailwindBreakpoint, MediaQueryList> {\n\tif (!minWidthMQLs) {\n\t\tminWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(breakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(breakpointQueries.xl),\n\t\t\tlg: window.matchMedia(breakpointQueries.lg),\n\t\t\tmd: window.matchMedia(breakpointQueries.md),\n\t\t\tsm: window.matchMedia(breakpointQueries.sm),\n\t\t\txs: window.matchMedia(breakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(breakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn minWidthMQLs;\n}\n\n/**\n * Get (and lazily create) the cached `MediaQueryList` for a specific max-width breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"md\").\n * @returns The corresponding `MediaQueryList`.\n * @private\n */\nfunction getMaxWidthMQL(breakpoint: TailwindBreakpoint): MediaQueryList {\n\tif (!maxWidthMQLs) {\n\t\tmaxWidthMQLs = {\n\t\t\t\"2xl\": window.matchMedia(belowBreakpointQueries[\"2xl\"]),\n\t\t\txl: window.matchMedia(belowBreakpointQueries.xl),\n\t\t\tlg: window.matchMedia(belowBreakpointQueries.lg),\n\t\t\tmd: window.matchMedia(belowBreakpointQueries.md),\n\t\t\tsm: window.matchMedia(belowBreakpointQueries.sm),\n\t\t\txs: window.matchMedia(belowBreakpointQueries.xs),\n\t\t\t\"2xs\": window.matchMedia(belowBreakpointQueries[\"2xs\"]),\n\t\t};\n\t}\n\treturn maxWidthMQLs[breakpoint];\n}\n\n/**\n * Current breakpoint value used by the singleton store backing `useBreakpoint`.\n *\n * Initialized to `\"default\"` and updated on media-query change events.\n *\n * @private\n */\nlet currentBreakpointValue: Breakpoint = \"default\";\n\n/**\n * Set of component listeners subscribed to the singleton breakpoint store.\n *\n * Each listener is invoked when the current breakpoint value changes.\n *\n * @private\n */\nconst breakpointListeners = new Set<() => void>();\n\n/**\n * Flag indicating whether global media-query listeners are currently attached.\n *\n * Prevents duplicate registrations and enables full teardown when unused.\n *\n * @private\n */\nlet breakpointSubscriptionActive = false;\n\n/**\n * Compute the current breakpoint by checking cached min-width MQLs\n * from largest to smallest.\n *\n * @returns {Breakpoint} The largest matching breakpoint, or `\"default\"`.\n * @private\n */\nfunction getCurrentBreakpoint(): Breakpoint {\n\tconst mqls = getMinWidthMQLs();\n\tfor (const breakpoint of tailwindBreakpoints) {\n\t\tif (mqls[breakpoint].matches) {\n\t\t\treturn breakpoint;\n\t\t}\n\t}\n\treturn \"default\";\n}\n\n/**\n * Update the current breakpoint value and notify all listeners.\n *\n * Uses `requestAnimationFrame` to coalesce rapid resize events and minimize\n * re-renders during active window resizing.\n *\n * @private\n */\nlet breakpointUpdatePending = false;\nfunction updateCurrentBreakpoint() {\n\tif (!breakpointUpdatePending) {\n\t\tbreakpointUpdatePending = true;\n\t\trequestAnimationFrame(() => {\n\t\t\tbreakpointUpdatePending = false;\n\t\t\tconst newBreakpoint = getCurrentBreakpoint();\n\t\t\tif (newBreakpoint !== currentBreakpointValue) {\n\t\t\t\tcurrentBreakpointValue = newBreakpoint;\n\t\t\t\tfor (const listener of breakpointListeners) {\n\t\t\t\t\tlistener();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * Subscribe a component to breakpoint changes (singleton pattern).\n *\n * Ensures only one set of MQL listeners exists app-wide. Also reconciles the\n * `useSyncExternalStore` initial snapshot/subscribe race by invoking the\n * subscriber once on mount.\n *\n * @param callback - Listener invoked when the breakpoint value may have changed.\n * @returns Cleanup function to unsubscribe the listener.\n * @private\n */\nfunction subscribeToBreakpointChanges(callback: () => void) {\n\tbreakpointListeners.add(callback);\n\n\t// Attach global listeners once\n\tif (!breakpointSubscriptionActive) {\n\t\tbreakpointSubscriptionActive = true;\n\t\tconst mqls = getMinWidthMQLs();\n\n\t\t// Initialize current value synchronously\n\t\tcurrentBreakpointValue = getCurrentBreakpoint();\n\n\t\t// Attach listeners to all breakpoint MQLs\n\t\tfor (const mql of Object.values(mqls)) {\n\t\t\tmql.addEventListener(\"change\", updateCurrentBreakpoint);\n\t\t}\n\t}\n\n\t// Reconcile initial getSnapshot vs subscribe ordering\n\tcallback();\n\n\t// Cleanup\n\treturn () => {\n\t\tbreakpointListeners.delete(callback);\n\n\t\t// Tear down global listeners when no one is listening\n\t\tif (breakpointListeners.size === 0 && breakpointSubscriptionActive) {\n\t\t\tbreakpointSubscriptionActive = false;\n\t\t\tconst mqls = getMinWidthMQLs();\n\t\t\tfor (const mql of Object.values(mqls)) {\n\t\t\t\tmql.removeEventListener(\"change\", updateCurrentBreakpoint);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Return the current breakpoint value from the singleton store.\n *\n * Used as the `getSnapshot` for `useSyncExternalStore`.\n *\n * @returns {Breakpoint} The latest computed breakpoint.\n * @private\n */\nfunction getCurrentBreakpointSnapshot(): Breakpoint {\n\treturn currentBreakpointValue;\n}\n\n/**\n * Cached `subscribe` functions keyed by breakpoint.\n *\n * Without caching, `useSyncExternalStore` receives a new function reference on\n * every render, causing it to tear down and re-attach the MQL listener each\n * time — the primary source of resize sluggishness.\n *\n * @private\n */\nconst belowBreakpointSubscribeCache = new Map<\n\tTailwindBreakpoint,\n\t(callback: () => void) => () => void\n>();\n\n/**\n * Get (or create and cache) a `subscribe` function for a specific \"below\" breakpoint.\n *\n * Uses a cached `MediaQueryList` and rAF-throttled change handler to avoid\n * bursty updates during resize.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A stable `subscribe` function suitable for `useSyncExternalStore`.\n * @private\n */\nfunction createBelowBreakpointSubscribe(breakpoint: TailwindBreakpoint) {\n\tlet cached = belowBreakpointSubscribeCache.get(breakpoint);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tcached = (callback: () => void) => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\n\t\t// rAF throttle the change callback during active resize\n\t\tlet pending = false;\n\t\tconst onChange = () => {\n\t\t\tif (!pending) {\n\t\t\t\tpending = true;\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tpending = false;\n\t\t\t\t\tcallback();\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", onChange);\n\t\treturn () => {\n\t\t\tmediaQuery.removeEventListener(\"change\", onChange);\n\t\t};\n\t};\n\n\tbelowBreakpointSubscribeCache.set(breakpoint, cached);\n\treturn cached;\n}\n\n/**\n * Cached `getSnapshot` functions keyed by breakpoint.\n *\n * Ensures `useSyncExternalStore` receives a referentially stable function,\n * preventing unnecessary subscription churn.\n *\n * @private\n */\nconst belowBreakpointSnapshotCache = new Map<TailwindBreakpoint, () => boolean>();\n\n/**\n * Get (or create and cache) a `getSnapshot` function for a specific \"below\" breakpoint.\n *\n * Uses the cached `MediaQueryList` for the target breakpoint.\n *\n * @param breakpoint - Tailwind breakpoint identifier (e.g., \"lg\").\n * @returns A stable function that returns `true` when the viewport is below the breakpoint.\n * @private\n */\nfunction createBelowBreakpointGetSnapshot(breakpoint: TailwindBreakpoint) {\n\tlet cached = belowBreakpointSnapshotCache.get(breakpoint);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tcached = () => {\n\t\tconst mediaQuery = getMaxWidthMQL(breakpoint);\n\t\treturn mediaQuery.matches;\n\t};\n\n\tbelowBreakpointSnapshotCache.set(breakpoint, cached);\n\treturn cached;\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\n/**\n * Returns a memoized callback that will always refer to the latest callback\n * passed to the hook.\n *\n * This is useful when you want to pass a callback which may or may not be\n * memoized (have a stable identity) to a child component that will be updated\n * without causing the child component to re-render.\n */\nfunction useCallbackRef<T extends (...args: unknown[]) => unknown>(callback: T | undefined): T {\n\tconst callbackRef = useRef(callback);\n\n\tuseEffect(() => {\n\t\tcallbackRef.current = callback;\n\t});\n\n\treturn useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);\n}\n\nexport {\n\t//,\n\tuseCallbackRef,\n};\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useCallbackRef } from \"./use-callback-ref.js\";\n\ntype Options = {\n\t/**\n\t * The delay in milliseconds to wait before calling the callback.\n\t */\n\twaitMs: number;\n};\n\n/**\n * Create a debounced version of a callback function.\n *\n * It allows you to delay the execution of a function until a certain period of\n * inactivity has passed (the `options.waitMs`), which can be useful for limiting rapid\n * invocations of a function (like in search inputs or button clicks)\n *\n * Note: The debounced callback will always refer to the latest callback passed\n * even without memoization, so it's stable and safe to use in dependency arrays.\n */\nfunction useDebouncedCallback<T extends (...args: unknown[]) => unknown>(\n\tcallbackFn: T,\n\toptions: Options,\n) {\n\tconst stableCallbackFn = useCallbackRef(callbackFn);\n\tconst debounceTimerRef = useRef(0);\n\tuseEffect(() => () => window.clearTimeout(debounceTimerRef.current), []);\n\n\treturn useCallback(\n\t\t(...args: Parameters<T>) => {\n\t\t\twindow.clearTimeout(debounceTimerRef.current);\n\t\t\tdebounceTimerRef.current = window.setTimeout(() => stableCallbackFn(...args), options.waitMs);\n\t\t},\n\t\t[stableCallbackFn, options.waitMs],\n\t);\n}\n\nexport {\n\t//,\n\tuseDebouncedCallback,\n};\n","import { useMemo } from \"react\";\n\n/**\n * Hook to generate a random, stable id.\n * This is similar to `useId`, but generates a stable id client side which can also\n * be used for css selectors and element ids.\n */\nconst useRandomStableId = (prefix = \"mantle\") => useMemo(() => randomStableId(prefix), [prefix]);\n\nexport {\n\t//,\n\tuseRandomStableId,\n};\n\nfunction randomStableId(prefix = \"mantle\") {\n\tconst _prefix = prefix.trim() || \"mantle\";\n\treturn [_prefix, randomPostfix()].join(\"-\");\n}\n\nfunction randomPostfix() {\n\treturn Math.random().toString(36).substring(2, 9);\n}\n"],"mappings":"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","names":["useSyncExternalStore","tailwindBreakpoints","breakpoints","useBreakpoint","subscribeToBreakpointChanges","getCurrentBreakpointSnapshot","useIsBelowBreakpoint","breakpoint","createBelowBreakpointSubscribe","createBelowBreakpointGetSnapshot","breakpointQueries","belowBreakpointQueries","minWidthMQLs","maxWidthMQLs","getMinWidthMQLs","getMaxWidthMQL","breakpoint","currentBreakpointValue","breakpointListeners","breakpointSubscriptionActive","getCurrentBreakpoint","mqls","tailwindBreakpoints","breakpointUpdatePending","updateCurrentBreakpoint","newBreakpoint","listener","subscribeToBreakpointChanges","callback","mql","getCurrentBreakpointSnapshot","belowBreakpointSubscribeCache","createBelowBreakpointSubscribe","cached","mediaQuery","pending","onChange","belowBreakpointSnapshotCache","createBelowBreakpointGetSnapshot","useEffect","useMemo","useRef","useCallbackRef","callback","callbackRef","args","useCallback","useEffect","useRef","useDebouncedCallback","callbackFn","options","stableCallbackFn","useCallbackRef","debounceTimerRef","useRef","useEffect","useCallback","args","useMemo","useRandomStableId","prefix","randomStableId","randomStableId","prefix","randomPostfix"]}
package/dist/mantle.css CHANGED
@@ -5,6 +5,9 @@
5
5
  @source "../src";
6
6
  @source "../dist";
7
7
 
8
+ /**
9
+ MARK: FONT FACES
10
+ */
8
11
  @font-face {
9
12
  font-family: "Roobert";
10
13
  src:
@@ -57,6 +60,9 @@
57
60
  font-style: italic;
58
61
  }
59
62
 
63
+ /**
64
+ MARK: THEME
65
+ */
60
66
  @theme {
61
67
  --color-white: oklch(100% 0 0);
62
68
  --color-black: oklch(0% 0 0);
@@ -1309,6 +1315,61 @@ MARK: DARK HICON DEFS
1309
1315
  --navigation-shadow: #000;
1310
1316
  }
1311
1317
 
1318
+ /**
1319
+ MARK: CUSTOM VARIANTS
1320
+ */
1321
+ @custom-variant light (&:where(.light, .light *, [data-theme="light"], [data-applied-theme="light"]));
1322
+ @custom-variant dark (&:where(.dark, .dark *, [data-theme="dark"], [data-applied-theme="dark"]));
1323
+ @custom-variant high-contrast (&:where(.light-high-contrast, .light-high-contrast *, [data-theme="light-high-contrast"], [data-applied-theme="light-high-contrast"]));
1324
+ @custom-variant dark-high-contrast (&:where(.dark-high-contrast, .dark-high-contrast *, [data-theme="dark-high-contrast"], [data-applied-theme="dark-high-contrast"]));
1325
+
1326
+ @custom-variant aria-collapsed (&[aria-expanded="false"]);
1327
+ @custom-variant aria-invalid (&[aria-invalid="true"]);
1328
+ @custom-variant aria-unchecked (&[aria-checked="false"]);
1329
+
1330
+ @custom-variant data-active-item (&[data-active-item~="true"]);
1331
+ @custom-variant data-disabled (&[data-disabled]);
1332
+ @custom-variant data-drag-over (&[data-drag-over="true"]);
1333
+ @custom-variant data-highlighted (&[data-highlighted]);
1334
+ @custom-variant data-orientation-horizontal (&[data-orientation="horizontal"]);
1335
+ @custom-variant data-orientation-vertical (&[data-orientation="vertical"]);
1336
+ @custom-variant data-side-bottom (&[data-side="bottom"]);
1337
+ @custom-variant data-side-left (&[data-side="left"]);
1338
+ @custom-variant data-side-right (&[data-side="right"]);
1339
+ @custom-variant data-side-top (&[data-side="top"]);
1340
+ @custom-variant data-state-active (&[data-state~=active]);
1341
+ @custom-variant data-state-checked (&[data-state~=checked]);
1342
+ @custom-variant data-state-closed (&[data-state~=closed]);
1343
+ @custom-variant data-state-idle (&[data-state~=idle]);
1344
+ @custom-variant data-state-inactive (&[data-state~=inactive]);
1345
+ @custom-variant data-state-indeterminate (&[data-state~=indeterminate]);
1346
+ @custom-variant data-state-open (&[data-state~=open]);
1347
+ @custom-variant data-state-pending (&[data-state~=pending]);
1348
+ @custom-variant data-state-selected (&[data-state~=selected]);
1349
+ @custom-variant data-state-submitting (&[data-state~=submitting]);
1350
+ @custom-variant data-state-unchecked (&[data-state~=unchecked]);
1351
+ @custom-variant data-validation-error (&[data-validation="error"]);
1352
+ @custom-variant data-validation-success (&[data-validation="success"]);
1353
+ @custom-variant data-validation-warning (&[data-validation="warning"]);
1354
+
1355
+ @custom-variant where (:where(&));
1356
+
1357
+ @custom-variant hover-hover {
1358
+ @media (hover: hover) {
1359
+ & {
1360
+ @slot;
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ @custom-variant hover-none {
1366
+ @media (hover: none) {
1367
+ & {
1368
+ @slot;
1369
+ }
1370
+ }
1371
+ }
1372
+
1312
1373
  @layer base {
1313
1374
  button:not(:disabled),
1314
1375
  [role="button"]:not(:disabled) {
@@ -1321,6 +1382,9 @@ MARK: DARK HICON DEFS
1321
1382
  }
1322
1383
  }
1323
1384
 
1385
+ /**
1386
+ MARK: UTILITIES
1387
+ */
1324
1388
  @utility cursor-inherit {
1325
1389
  cursor: inherit;
1326
1390
  }
@@ -1424,7 +1488,9 @@ MARK: DARK HICON DEFS
1424
1488
  animation-duration: --value([length]);
1425
1489
  }
1426
1490
 
1427
- /* Prism styles */
1491
+ /*
1492
+ MARK: PRISM SYNTAX HIGHLIGHT
1493
+ */
1428
1494
  .token.comment,
1429
1495
  .token.prolog,
1430
1496
  .token.doctype,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngrok/mantle",
3
- "version": "0.63.1",
3
+ "version": "0.63.2",
4
4
  "description": "mantle is ngrok's UI library and design system.",
5
5
  "homepage": "https://mantle.ngrok.com",
6
6
  "license": "MIT",