{"version":3,"file":"forty-cdk-breakpoints.mjs","sources":["../../../projects/forty-cdk/breakpoints/src/breakpoints-defaults.ts","../../../projects/forty-cdk/breakpoints/src/breakpoints.ts","../../../projects/forty-cdk/breakpoints/src/forty-cdk-breakpoints.ts"],"sourcesContent":["import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * A breakpoint map: each named breakpoint paired with its `min-width`\n * threshold in CSS pixels. Names are arbitrary — `injectBreakpoints` derives\n * its query methods (`up`, `down`, `between`, `only`) from whatever keys this\n * map declares, sorted ascending by threshold.\n */\nexport type BreakpointMap = Record<string, number>;\n\n/**\n * The default breakpoint map, mirroring Tailwind CSS's `sm`–`2xl` scale. Used\n * as the library fallback when no `provideForBreakpointsDefaults` is in scope,\n * and the source of the typed breakpoint names when the consumer has not\n * augmented {@link BreakpointRegistry}.\n */\nexport const forBreakpointsTailwind = {\n  sm: 640,\n  md: 768,\n  lg: 1024,\n  xl: 1280,\n  '2xl': 1536,\n} as const satisfies BreakpointMap;\n\n/** The breakpoint names from the default Tailwind scale. */\nexport type TailwindBreakpointName = keyof typeof forBreakpointsTailwind;\n\n/** Resolved shape stored against {@link FOR_BREAKPOINTS_DEFAULTS}. */\nexport interface ForBreakpointsDefaults {\n  /** The active breakpoint map for the current injector scope. */\n  breakpoints: BreakpointMap;\n}\n\n/**\n * Library fallback for breakpoints defaults, read at the root injector when no\n * consumer has called `provideForBreakpointsDefaults`. Exported for the shared\n * defaults contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_BREAKPOINTS_FALLBACK_DEFAULTS: ForBreakpointsDefaults = {\n  breakpoints: forBreakpointsTailwind,\n};\n\nconst { token, provideDefaults } = createDefaults<ForBreakpointsDefaults>(\n  'FOR_BREAKPOINTS_DEFAULTS',\n  FOR_BREAKPOINTS_FALLBACK_DEFAULTS,\n);\n\n/**\n * Token holding the resolved breakpoint map for the current injector scope.\n * The library always provides a fully-populated value (the Tailwind fallback\n * at the root, or the map from the nearest `provideForBreakpointsDefaults`).\n */\nexport const FOR_BREAKPOINTS_DEFAULTS = token;\n\n/**\n * Configures the breakpoint map for this injector scope so `injectBreakpoints`\n * can be called with no arguments. Provide it once in your application config:\n *\n * ```ts\n * providers: [\n *   provideForBreakpointsDefaults({ mobile: 0, tablet: 640, laptop: 1024, desktop: 1280 }),\n * ]\n * ```\n *\n * Providing it again on a component injector replaces the map for that subtree\n * only (the nearest scope wins; the map is replaced wholesale, never merged\n * key-by-key). Omit it entirely to use {@link forBreakpointsTailwind}.\n */\nexport function provideForBreakpointsDefaults(breakpoints: BreakpointMap): Provider[] {\n  return provideDefaults({ breakpoints });\n}\n","import { Injector, computed, inject, runInInjectionContext, type Signal } from '@angular/core';\n\nimport { fortyError, injectMediaQuery } from 'forty-cdk/core';\nimport { FOR_BREAKPOINTS_DEFAULTS, type TailwindBreakpointName } from './breakpoints-defaults';\n\n/**\n * Extension point for typing custom breakpoint names. Augment it via module\n * declaration merging so `injectBreakpoints` autocompletes your own names\n * instead of the default Tailwind scale:\n *\n * ```ts\n * import type { appBreakpoints } from './breakpoints';\n *\n * declare module 'forty-cdk' {\n *   interface BreakpointRegistry extends Record<keyof typeof appBreakpoints, true> {}\n * }\n * ```\n *\n * When left empty (no augmentation), {@link BreakpointName} falls back to the\n * keys of {@link forBreakpointsTailwind}.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface BreakpointRegistry {}\n\n/**\n * The set of valid breakpoint names. Resolves to the augmented\n * {@link BreakpointRegistry} keys when present, otherwise the Tailwind scale\n * (`'sm' | 'md' | 'lg' | 'xl' | '2xl'`).\n */\nexport type BreakpointName = [keyof BreakpointRegistry] extends [never]\n  ? TailwindBreakpointName\n  : keyof BreakpointRegistry & string;\n\n/** Reactive handle returned by {@link injectBreakpoints}. */\nexport interface ForBreakpoints<K extends string = BreakpointName> {\n  /**\n   * Matches the named breakpoint and wider — `(min-width: <threshold>px)`.\n   * @param name A breakpoint declared in the active map.\n   */\n  up(name: K): Signal<boolean>;\n  /**\n   * Matches narrower than the named breakpoint —\n   * `(max-width: <threshold - 0.02>px)`. The 0.02px back-off keeps `up(name)`\n   * and `down(name)` from both matching at the exact threshold.\n   * @param name A breakpoint declared in the active map.\n   */\n  down(name: K): Signal<boolean>;\n  /**\n   * Matches from `min` (inclusive) up to but not including `max`.\n   * @param min Lower breakpoint, inclusive.\n   * @param max Upper breakpoint, exclusive.\n   */\n  between(min: K, max: K): Signal<boolean>;\n  /**\n   * Matches only the named breakpoint's own band — from its threshold up to\n   * but not including the next-larger breakpoint (open-ended for the largest).\n   * @param name A breakpoint declared in the active map.\n   */\n  only(name: K): Signal<boolean>;\n  /**\n   * The largest breakpoint whose `min-width` currently matches, or `null` when\n   * the viewport is narrower than the smallest breakpoint (and on the server).\n   */\n  readonly active: Signal<K | null>;\n  /**\n   * Escape hatch for an arbitrary media query string (orientation, pointer,\n   * `prefers-color-scheme`, …) that the named helpers don't cover.\n   * @param query Any valid media query.\n   */\n  matches(query: string): Signal<boolean>;\n}\n\n/**\n * A signal-first, zoneless, SSR-safe viewport breakpoint observer. Reads the\n * breakpoint map from the ambient {@link FOR_BREAKPOINTS_DEFAULTS} token\n * (configured with `provideForBreakpointsDefaults`, or the Tailwind fallback),\n * so call sites never repeat the breakpoint set:\n *\n * ```ts\n * private bp = injectBreakpoints();\n * protected isDesktop = this.bp.up('desktop');\n * protected active = this.bp.active;\n * ```\n *\n * Each query method returns a `Signal<boolean>` backed by a cached\n * `MediaQueryList`. The returned handle captures the calling injection\n * context, so its methods can be invoked lazily from `computed()` or a\n * template — not only during construction. Listeners are torn down with the\n * injector. `active`'s per-breakpoint queries are materialized eagerly at call\n * time, so reading `active` never attaches a listener from inside a reactive\n * computation or after teardown. On the server (or where `matchMedia` is\n * unavailable) every signal reads `false` and `active` reads `null`.\n *\n * Must be called from an injection context.\n *\n * @returns A {@link ForBreakpoints} handle of reactive query methods.\n */\nexport function injectBreakpoints(): ForBreakpoints {\n  const injector = inject(Injector);\n  const map = inject(FOR_BREAKPOINTS_DEFAULTS).breakpoints;\n  const names = Object.keys(map).sort((a, b) => map[a]! - map[b]!);\n  const cache = new Map<string, Signal<boolean>>();\n\n  const observe = (query: string): Signal<boolean> => {\n    let result = cache.get(query);\n    if (result === undefined) {\n      result = runInInjectionContext(injector, () => injectMediaQuery(query));\n      cache.set(query, result);\n    }\n    return result;\n  };\n\n  const thresholdOf = (name: string): number => {\n    const value = map[name];\n    if (value === undefined) {\n      throw fortyError({\n        code: 'FORCDK-BREAKPOINTS-001',\n        message: `Unknown breakpoint \"${name}\".`,\n        cause: `The active breakpoint map defines: ${names.join(', ') || '(none)'}.`,\n        fix: 'Use one of the names above, or register yours with provideForBreakpointsDefaults().',\n      });\n    }\n    return value;\n  };\n\n  const up = (name: string): Signal<boolean> => observe(`(min-width: ${thresholdOf(name)}px)`);\n\n  const down = (name: string): Signal<boolean> =>\n    observe(`(max-width: ${thresholdOf(name) - 0.02}px)`);\n\n  const between = (min: string, max: string): Signal<boolean> =>\n    observe(`(min-width: ${thresholdOf(min)}px) and (max-width: ${thresholdOf(max) - 0.02}px)`);\n\n  const only = (name: string): Signal<boolean> => {\n    const lower = thresholdOf(name);\n    const index = names.indexOf(name);\n    const next = index < names.length - 1 ? thresholdOf(names[index + 1]!) : null;\n    return next === null\n      ? observe(`(min-width: ${lower}px)`)\n      : observe(`(min-width: ${lower}px) and (max-width: ${next - 0.02}px)`);\n  };\n\n  const activeCandidates = names.map((name) => ({\n    name,\n    matches: observe(`(min-width: ${map[name]}px)`),\n  }));\n\n  const active = computed<BreakpointName | null>(() => {\n    let current: string | null = null;\n    for (const candidate of activeCandidates) {\n      if (candidate.matches()) {\n        current = candidate.name;\n      }\n    }\n    return current as BreakpointName | null;\n  });\n\n  return { up, down, between, only, active, matches: (query) => observe(query) };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}