UNPKG

@sandlada/mdc

Version:

@sandlada/mdc is an open source component library that follows the Material Design 3 design specifications.

100 lines 3.8 kB
/** * @license * Copyright 2026 Kai-Orion & Sandlada * SPDX-License-Identifier: MIT */ import { type ReactiveController, type ReactiveControllerHost } from 'lit'; /** * Configuration options for {@link MeasuredDimensionController}. */ export interface MeasuredDimensionOptions { /** * Returns the target element whose natural size is observed and animated. * May return `null` before the element has been rendered. */ target: () => HTMLElement | null; /** * The CSS dimension to animate. * @default 'width' */ dimension?: 'width' | 'height'; /** * Animation duration in milliseconds. * @default 300 */ duration?: number; /** * CSS easing function for the animation. * @default 'cubic-bezier(0.2, 0, 0.2, 1)' */ easing?: string; } /** * A `ReactiveController` that animates a single CSS dimension (width or * height) of a target element whenever its natural measured size changes. * * ### Trigger sources * - **Light DOM mutations** (e.g. slotted text content changes, slot child * additions / removals): detected via a `MutationObserver` on the host. * Measurement is deferred to the next animation frame so that any concurrent * Lit re-renders (e.g. `slotchange` → `hasIcon`) complete before we read * the natural size. * - **Lit re-renders** (e.g. `size`, `hasIcon`, `hasLabel` class changes): * detected via `hostUpdated()`. Skipped when a mutation rAF is already * pending to avoid animating twice for the same visual change. * * ### Animation * Uses the Web Animations API (`element.animate()`) instead of inline CSS * transitions, which allows mid-flight interruption without jank: when a new * change arrives while an animation is running, the controller reads the * current compositor position via `getBoundingClientRect()`, cancels the * old animation (so the natural size becomes reliable again), then starts a * new animation from that visual position to the new natural size. * * @example * ```ts * private readonly sizeController = new MeasuredDimensionController(this, { * target: () => this.labelElement, * dimension: 'width', * duration: 250, * }) * ``` */ export declare class MeasuredDimensionController implements ReactiveController { private readonly host; private readonly options; /** Last known settled natural size of the target element. */ private naturalSize; /** Currently running WAAPI animation, if any. */ private currentAnimation; private mutationObserver; /** Non-null when a measurement rAF is already queued (coalescing guard). */ private rafId; constructor(host: ReactiveControllerHost & Element, options: MeasuredDimensionOptions); hostConnected(): void; hostDisconnected(): void; /** * Handles Lit re-render triggered size changes (e.g. `size` property, * shadow DOM structural changes like `has-icon` / `has-label` classes). * Defers to any already-queued mutation rAF to avoid double-animating. */ hostUpdated(): void; /** * MutationObserver callback for light DOM changes (slotted text / children). * Defers to the next animation frame so that Lit re-renders triggered by * the same mutation (e.g. slotchange → `hasIcon = true`) complete before * we read the natural size. */ private readonly onMutation; /** * Captures the visual from-size, cancels any in-progress animation so * that the natural size is reliable, then starts a new animation if the * natural size has changed. */ private measure; private animate; private readNaturalSize; private cancelAnimation; private cancelPendingRaf; } //# sourceMappingURL=measured-dimension-controller.d.ts.map