import type { Theme, BuiltInThemeName, ThemeMode, ThemeVariant } from '../types/theme.types'; import type { EventBus } from '../event-bus/event-bus'; /** * Coordinates the two independent theming axes: * * 1. **Mode** (`light` / `dark`) — the full color palette, injected as design * tokens (CSS custom properties) scoped to the grid's container element. * 2. **Variant** (`ion` / `neon` / …) — a cosmetic skin applied as a CSS * class on that same container. Variant stylesheets only override structural * and accent concerns, so any variant composes with either mode. * * Mode tokens are additionally mirrored onto `:root` so any element outside both * the container and the portal host still resolves a sensible palette. * * The `data-pg-mode` / `data-pg-variant` **attributes** are not mirrored there * when the instance has a container. Both are used ancestor-rooted in CSS — * `[data-pg-mode="dark"] .pg-cell--in-selection`, * `[data-pg-variant="quantum"] .pg-context-menu` — so a copy on * `` makes every such rule match *every* grid on the page instead of the * one it belongs to. A light grid beside a dark one took the dark selection * tints and flash colours; a classic grid's menu was skinned by whichever * variant stylesheet was concatenated last. Only a manager driving the document * root itself (no container) still mirrors, where there is nothing else to hang * the attributes on and no second grid to mis-target. * * What overlays resolve instead is the **portal host**: a `display: contents` * element in ``, one per grid, carrying that grid's scope id, mode, * variant and variant class. Built-in overlays are appended into their owner's * host (see `overlay-portal.ts`), so both the ancestor-rooted rules above and * the scoped token stylesheet reach them, per instance. * * Per-instance chrome is unaffected either way: those rules stay scoped to * `.pg--theme .pg-grid`. */ export declare class ThemeManager { private readonly eventBus; /** Per-instance stylesheet carrying the active mode's tokens. */ private readonly injector; /** Mirrors mode tokens onto `:root` for portaled elements. */ private readonly rootInjector; private readonly registry; private activeMode; private activeVariant; private scopeEl; /** * This instance's portal host in ``, created with the scope id and kept * in step with the active mode/variant. Null until a scope element is known, * since the host is identified by that scope. See `overlay-portal.ts`. */ private portalHost; /** Scope id stamped on {@link scopeEl} and {@link portalHost}; needed to unregister. */ private scopeId; /** Names of inline token overrides applied via {@link applyTokenOverrides}, for clean removal. */ private readonly tokenOverrideKeys; /** * Notified whenever the active variant changes, so subsystems that are not * CSS-driven can follow the skin. Currently drives the per-variant icon pack * swap (see `IconThemeController`). * * Injected rather than imported so the theme layer stays free of any * dependency on the icon layer. */ private onVariantChange; /** Monotonic source for unique per-instance scope ids. */ private static scopeSeq; constructor(eventBus: EventBus); /** Register (or replace) a mode theme so it can be resolved by name. */ registerTheme(theme: Theme): void; /** * Registers the callback notified on every variant change, including the * initial one applied at construction. Pass `null` to unhook (see * `GridCore.destroy`, which must break the cycle). */ setVariantChangeHandler(handler: ((variant: ThemeVariant | 'none') => void) | null): void; /** * Apply a color mode (light/dark) by injecting its tokens. Colors cascade * from the container down to every cell; the same tokens are mirrored to * `:root` for portaled UI. Does not touch the active variant. */ applyMode(modeOrTheme: ThemeMode | Theme, scopeEl?: HTMLElement): void; /** * Apply (or clear) the cosmetic variant skin. Swaps the `pg--theme` * class on the scope container; passing `'none'` removes any active variant. */ applyVariant(variant: ThemeVariant | 'none', scopeEl?: HTMLElement): void; /** * Overlay a subset of design-token CSS variables directly on the scope * container as inline properties — used by the AI Theme Engine for instant, * rebuild-free preview/apply. Inline properties win over the scoped mode * stylesheet in the cascade, so these override the active mode without * touching it. The same variables are mirrored onto `document.documentElement` * so portaled menus/overlays (appended to ``) pick them up too, and onto * this instance's portal host so overlays that *are* scoped to it — which win * over the root mirror — preview the override as well. * * @param vars - Map of full `--pg-*` variable names to values. * @param scopeEl - Optional container; defaults to the current scope element. */ applyTokenOverrides(vars: Readonly>, scopeEl?: HTMLElement): void; /** * Remove every inline token override applied via {@link applyTokenOverrides}, * reverting the grid to its active mode/variant styling. */ clearTokenOverrides(scopeEl?: HTMLElement): void; /** * Backward-compatible entry point for the deprecated `theme` option / API. * Normalizes a legacy value (e.g. `'dark'`, `'ion'`, `'pg-ion-theme'`, * `'ion-dark'`) onto the mode/variant axes and applies it. * * @deprecated Prefer {@link applyMode} + {@link applyVariant}. */ applyTheme(nameOrTheme: BuiltInThemeName | string | Theme, scopeEl?: HTMLElement): void; /** The active mode theme (light/dark). Never null after construction. */ getActiveTheme(): Theme; /** The active mode as a plain string. */ getActiveMode(): ThemeMode; /** The active variant, or `'none'` when no skin is applied. */ getActiveVariant(): ThemeVariant | 'none'; /** * This grid's portal host — the ``-level element carrying its scope id, * mode and variant, into which overlays should be appended so they resolve * this instance's theme rather than the shared document root. * * Prefer `portalHostFor(anchorEl)` from `overlay-portal.ts` at call sites that * have a triggering element: it resolves the owner from the DOM and needs no * reference to the manager. Use this accessor for long-lived layers that have * no per-open anchor, such as the toast layer. * * Returns `null` until a mode has been applied, since the host is keyed by the * scope id minted at that point. */ getPortalHost(): HTMLElement | null; getTheme(name: string): Theme | undefined; getAllThemes(): Theme[]; /** Toggle between light and dark mode, preserving the active variant. */ toggleDarkMode(): void; isDarkMode(): boolean; destroy(): void; /** * Resolve a mode name or Theme object to a concrete light/dark Theme. */ private resolveModeTheme; /** * Lazily creates this instance's portal host and registers it under `scopeId`. * Idempotent — subsequent calls return the existing host. * * The host carries the scope id so the mode-token stylesheet injected by * {@link applyMode} matches it, and is styled `display: contents` so it adds no * box, no layout and no containing block. See `overlay-portal.ts`. */ private ensurePortalHost; /** Ensure the scope element carries a stable id used to target its tokens. */ private ensureScopeId; /** Map a legacy `theme` string onto the mode/variant axes. */ private static parseLegacyTheme; } //# sourceMappingURL=theme-manager.d.ts.map