import type { TemplateResult } from "lit-html"; import type { MatesRef } from "../Mutables/ref/ref"; import type { ClassesInput } from "./classesDirective"; import type { AttrMap } from "./resolveAttrValue"; import type { StyleMap } from "./styleDirective"; /** Any value accepted by the `data()` method — serialised via String(). */ type DataMap = Record; /** Subset of DOM event names + a loose string for custom events. */ type EventName = keyof HTMLElementEventMap | (string & {}); /** Event listener options forwarded to addEventListener. */ type ListenerOptions = boolean | AddEventListenerOptions; /** ARIA attribute map — keys are aria-* names (with or without the "aria-" prefix). */ type AriaMap = Record; /** Scroll options for .scroll(). */ type ScrollToOptions = { top?: number; left?: number; behavior?: ScrollBehavior; }; /** Measurement result returned by .measure(). */ export interface Measurements { width: number; height: number; top: number; left: number; right: number; bottom: number; x: number; y: number; } /** * Fluent chain returned by `$(element)`. * * Every method returns `this` so calls can be chained: * * ```ts * $(el) * .attr({ "aria-label": "Close", disabled: isDisabled }) * .style({ color: "red", fontSize: "14px" }) * .classes(["btn", [isActive, "btn-active"]]) * .on("click", () => handleClick()) * .text("Hello"); * ``` */ export interface DollarChain { /** * Set / remove attributes on the element. * * - `true` → sets attribute with empty string value (boolean attribute) * - `false` → removes the attribute * - `null` / `undefined` → removes the attribute * - string / number → sets the attribute to that value * * ```ts * $(el).attr({ disabled: true, "aria-label": "Close", tabindex: 0 }); * ``` */ attr(attrs: AttrMap): this; /** * Mutate the currently-applied attributes in-place via a draft object. * * The callback receives a **mutable draft** — a plain `Record` * pre-populated with every attribute that was set by a previous `.attr()` or * `.updateAttr()` call on this element. Modify it freely: add keys, change * values, delete keys. Whatever the draft looks like when the callback returns * is applied to the element — removed keys cause the corresponding attribute * to be removed from the DOM. * * No return value is needed (or used). This is intentionally different from * `.attr()`, which replaces the whole map. `.updateAttr()` is for surgical, * read-modify-write updates where you only want to touch a few keys. * * ```ts * // Toggle aria-expanded based on its current value * $(el).updateAttr((draft) => { * draft["aria-expanded"] = draft["aria-expanded"] === "true" ? "false" : "true"; * }); * * // Increment a counter attribute * $(el).updateAttr((draft) => { * draft["data-step"] = String(Number(draft["data-step"] ?? 0) + 1); * }); * * // Conditionally add/remove attributes * $(el).updateAttr((draft) => { * if (isLoading) { * draft["disabled"] = ""; * draft["aria-busy"] = "true"; * } else { * delete draft["disabled"]; * delete draft["aria-busy"]; * } * }); * * // Chain freely — reads the live state at call time * $(el) * .attr({ "data-loading": "true", tabindex: -1 }) * .updateAttr((draft) => { draft["aria-busy"] = draft["data-loading"]; }); * ``` */ updateAttr(fn: (draft: Record) => void): this; /** * Apply inline styles to the element. * Accepts camelCase CSS property names (type-safe via csstype) or * kebab-case / custom property strings. * * Previously applied styles that are absent on a subsequent call are * automatically removed. * * ```ts * $(el).style({ color: "red", fontSize: "14px", "--gap": "8px" }); * ``` */ style(styles: StyleMap): this; /** * Declaratively manage the element's class list. * * Accepts: * - an array of `ClassEntry` items (strings, conditionals, ternaries) * - an object map `{ className: condition }` * - a plain space-separated string * * Classes applied by a previous `classes()` call that are absent in the * next call are removed automatically. * * ```ts * $(el).classes(["btn", [isActive, "btn-active"], [isError, "btn-error", "btn-ok"]]); * ``` */ classes(input: ClassesInput): this; /** * Add one or more space-separated class names to the element. * Does not remove any existing classes. * * ```ts * $(el).addClass("active highlighted"); * ``` */ addClass(names: string): this; /** * Remove one or more space-separated class names from the element. * * ```ts * $(el).removeClass("active highlighted"); * ``` */ removeClass(names: string): this; /** * Toggle one or more space-separated class names on the element. * Optionally accepts a `force` boolean: * - `true` → always add * - `false` → always remove * - omitted → toggle * * ```ts * $(el).toggleClass("open"); * $(el).toggleClass("open", isOpen); * ``` */ toggleClass(names: string, force?: boolean): this; /** * Render a lit-html template inside the element. * Accepts a factory function that returns a `TemplateResult` from `html\`…\``. * Uses lit-html's `render()` under the hood, so the template is efficiently * patched on subsequent calls. * * The factory must return a value — bare `html\`…\`` without a return is not * valid here. * * ```ts * $(el).html(() => html`${userName}`); * ``` */ html(templateResult: TemplateResult): this; /** * Set the element's `textContent`. * Any existing child nodes are replaced. * * ```ts * $(el).text("Hello, world!"); * ``` */ text(content: string | number): this; /** * Attach a DOM event listener to the element. * Subsequent calls with the same `event` name replace the previous listener * (the old one is removed before the new one is added). * * ```ts * $(el).on("click", (e) => handleClick(e)); * $(el).on("input", (e) => handleInput(e), { passive: true }); * ``` */ on(event: K, handler: K extends keyof HTMLElementEventMap ? (e: HTMLElementEventMap[K]) => void : (e: Event) => void, options?: ListenerOptions): this; /** * Set `data-*` attributes on the element. * Keys are used as-is if they start with `data-`, otherwise `data-` is * prepended automatically. * `null` / `undefined` values remove the corresponding attribute. * * ```ts * $(el).data({ id: 7, status: "active" }); * // → data-id="7" data-status="active" * ``` */ data(map: DataMap): this; /** The underlying element. */ readonly el: Element; /** * Focus the element, with an optional `FocusOptions` object. * * ```ts * $(inputEl).focus(); * $(inputEl).focus({ preventScroll: true }); * ``` */ focus(options?: FocusOptions): this; /** * Remove focus from the element. * * ```ts * $(el).blur(); * ``` */ blur(): this; /** * Scroll the element to a specific position, or pass a `behavior` string * (`"smooth"` | `"instant"` | `"auto"`) as a shorthand for smooth scrolling * to the top. * * ```ts * $(el).scroll({ top: 0, behavior: "smooth" }); * $(el).scroll("smooth"); // shorthand — scrolls to top smoothly * ``` */ scroll(options?: ScrollToOptions | ScrollBehavior): this; /** * Scroll the element into the viewport. * Delegates to the native `scrollIntoView()`. * * ```ts * $(el).scrollIntoView(); * $(el).scrollIntoView({ behavior: "smooth", block: "start" }); * ``` */ scrollIntoView(options?: ScrollIntoViewOptions | boolean): this; /** * Read the element's bounding rect via `getBoundingClientRect()` and return * the measurements synchronously. The chain continues normally — the * measurements are only available through the callback. * * ```ts * $(el).measure(({ width, height, top }) => { * console.log("size:", width, height, "top:", top); * }); * ``` */ measure(callback: (rect: Measurements) => void): this; /** * Remove a previously registered listener for `event`. * If no `handler` is provided, removes ALL listeners registered via `.on()` * for that event name. * * ```ts * $(el).off("click"); // remove all click listeners * $(el).off("click", myHandler); // remove specific listener * ``` */ off(event: EventName, handler?: EventListener): this; /** * Add a one-time event listener that automatically removes itself after * firing once. Accepts the same options as `.on()`. * * ```ts * $(el).once("transitionend", () => el.remove()); * ``` */ once(event: K, handler: K extends keyof HTMLElementEventMap ? (e: HTMLElementEventMap[K]) => void : (e: Event) => void, options?: ListenerOptions): this; /** * Attach a delegated event listener on this element that fires only when * the event target matches `selector`. Useful for lists and dynamic children. * * ```ts * $(listEl).delegate("click", "li.item", (e, target) => { * console.log("item clicked:", target.dataset.id); * }); * ``` */ delegate(event: K, selector: string, handler: (e: K extends keyof HTMLElementEventMap ? HTMLElementEventMap[K] : Event, matchedTarget: Element) => void, options?: ListenerOptions): this; /** * Set `aria-*` attributes. Keys may include or omit the `aria-` prefix. * `null` / `undefined` / `false` removes the attribute. * * ```ts * $(el).aria({ label: "Close dialog", hidden: false, expanded: isOpen }); * // → aria-label="Close dialog" aria-expanded="true/false" * ``` */ aria(map: AriaMap): this; } /** * Fluent DOM utility — wrap any element and chain imperative DOM operations. * * ```ts * import { $ } from "mates"; * * $(buttonEl) * .attr({ disabled: isDisabled, "aria-pressed": isActive }) * .style({ opacity: [isDisabled, "0.5", "1"] }) * .classes(["btn", [isActive, "btn-active"]]) * .on("click", handleClick) * .text("Click me"); * ``` * * ### Methods * * | Method | Description | * |--------|-------------| * | `.attr(map)` | Set / remove attributes. Supports boolean, conditional `[cond, val]` and ternary `[cond, t, f]` values. | * | `.style(map)` | Apply inline styles (camelCase or kebab-case). Previously applied styles removed automatically. | * | `.classes(input)` | Manage class list via array, object map, or string. Stale classes removed automatically. | * | `.addClass(names)` | Add space-separated class names. | * | `.removeClass(names)` | Remove space-separated class names. | * | `.toggleClass(names, force?)` | Toggle space-separated class names. | * | `.html(factory)` | Render a lit-html template inside the element via `() => html\`…\``. | * | `.text(content)` | Set `textContent`. | * | `.on(event, handler, opts?)` | Add a DOM event listener. Re-calling with the same event replaces the previous listener. | * | `.updateAttr(fn)` | Mutate current attrs via a draft object — add/change/delete keys, no return needed. | * | `.off(event, handler?)` | Remove a listener added by `.on()`. Omit handler to remove all for that event. | * | `.once(event, handler, opts?)` | One-time listener — auto-removes itself after firing. | * | `.delegate(event, selector, handler)` | Delegated listener — fires only when target matches `selector`. | * | `.props(map)` | Set JS properties directly on the element (for custom elements). | * | `.data(map)` | Set `data-*` attributes. `null`/`undefined` removes the attribute. | * | `.aria(map)` | Set `aria-*` attributes. Keys may omit the `aria-` prefix. `false`/`null` removes. | * | `.cssVar(map)` | Set CSS custom properties (`--var`). Keys may omit `--`. `null` removes. | * | `.show()` | Remove `display:none` inline style. | * | `.hide()` | Set `display:none` inline style. | * | `.toggle(force?)` | Toggle visibility. Optional `force` boolean overrides auto-detection. | * | `.focus(opts?)` | Focus the element. | * | `.blur()` | Blur the element. | * | `.scroll(opts?)` | Scroll element to position. Pass `"smooth"` as shorthand for smooth-scroll-to-top. | * | `.scrollIntoView(opts?)` | Scroll element into the viewport. | * | `.measure(cb)` | Read `getBoundingClientRect()` synchronously inside a callback, keeps chain alive. | * | `.getAttr(name)` | Read a single attribute value. Returns `null` if absent. | * | `.clone(deep?)` | Clone the element and return a new `DollarChain` wrapping it. | * * Accepts either a raw `Element` **or** a `MatesRef` created by `ref()` / * `createRef()`. When a `MatesRef` is passed and its `.value` is `undefined` * (the element has not mounted yet, or has already been removed from the DOM), * a descriptive error is thrown pointing to the correct lifecycle hooks. * * ```ts * // ✅ Inside onMount / onPaint / onUpdate — element is guaranteed to exist * const inputRef = ref(); * * onMount(() => { * $(inputRef).focus(); * }); * * onUpdate(() => { * $(inputRef).updateAttr((d) => { d["aria-invalid"] = hasError ? "true" : "false"; }); * }); * ``` * * @param elementOrRef - A DOM `Element` or a `MatesRef`. * @returns A `DollarChain` instance bound to the element. * @throws If a `MatesRef` is passed whose `.value` is `undefined`. */ export declare function $(elementOrRef: T | MatesRef): DollarChain; export {}; //# sourceMappingURL=$.d.ts.map