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