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