/*! * Mode.js — TypeScript Definitions * MIT License * Copyright (c) 2025 Silvio Corigliano * https://github.com/microdom/mode.js */ // --------------------------------------------------------------------------- // Primitive types // --------------------------------------------------------------------------- /** Any value accepted as a DOM selector by µ */ export type ModeSelector = string | HTMLElement | NodeList | HTMLElement[]; /** The value held internally by µ after selection / command execution */ export type ModeTarget = HTMLElement | NodeList | HTMLElement[] | null; /** Generic DOM event handler */ export type ModeEventHandler = (event: Event) => void; /** * The atom protocol: any object exposing `get()` and `sub()` is a reactive * source. Passing one as a helper value binds it — µ keeps that helper in sync * with the atom (see `bindAtom`), instead of writing once. */ export type Atom = { get(): T; sub(fn: (v: T) => void, runNow?: boolean): () => void; }; /** A helper value that may be a plain value or a reactive atom of it. */ export type Bindable = T | Atom; /** Content accepted by `text` / `html`: strings, finite numbers, bigints, or an atom of those. */ export type ModeContent = Bindable; // --------------------------------------------------------------------------- // `on` command // --------------------------------------------------------------------------- /** * Delegated listener: attaches `fn` to `tg` rather than to the current * selection. Useful for event delegation inside a container. */ export interface ModeDelegatedListener { fn: ModeEventHandler; tg: HTMLElement | NodeList | HTMLElement[]; } /** * Config object for the `on` command. * * @example * µ('#btn', { on: { e: 'click', fn: (e) => console.log(e) } }) * * @example * // Delegated listener * µ('#list', { on: { e: 'click', fn: handler, l: { fn: childHandler, tg: µ('.item') } } }) */ export interface ModeOnConfig { /** Event name or list of event names */ e: string | string[]; /** Handler attached to the current selection */ fn: ModeEventHandler; /** Optional delegated listener(s) attached to a specific target element */ l?: ModeDelegatedListener | ModeDelegatedListener[]; } // --------------------------------------------------------------------------- // `css` command // --------------------------------------------------------------------------- /** * Map of camelCase CSS properties to their string values. * * @example * µ('.box', { css: { backgroundColor: 'red', display: 'none' } }) */ export type ModeCSSCommand = { [K in keyof CSSStyleDeclaration]?: string; } & Record; // --------------------------------------------------------------------------- // `classes` / `clss` command // --------------------------------------------------------------------------- /** * Keys map to `classList` method names; values are the class name(s) to act on. * * @example * µ('.el', { classes: { add: 'active', remove: ['foo', 'bar'] } }) */ export type ModeClassOperation = 'add' | 'remove' | 'toggle' | 'replace' | 'contains'; export type ModeClassesCommand = Partial>; // --------------------------------------------------------------------------- // `attr` command // --------------------------------------------------------------------------- /** * Attribute manipulation. Keys are operations; values are attribute names / * values depending on the operation. * * @example * µ('input', { attr: { set: { placeholder: 'Type here…' } } }) * µ('input', { attr: { del: 'disabled' } }) */ export interface ModeAttrCommand { /** Set an attribute: `{ attrName: attrValue }` */ set?: Record; /** Get the value of an attribute by name */ get?: string; /** Remove an attribute by name */ del?: string; /** Check whether an attribute exists by name */ has?: string; /** Toggle a boolean attribute by name */ tog?: string; /** List all attribute names on the element */ list?: true; } // --------------------------------------------------------------------------- // Commands object (second argument to µ) // --------------------------------------------------------------------------- /** * All commands that µ can execute on the current selection when passed as * the second argument. * * Every key is optional; they are processed in the order they appear in the * object (JavaScript property iteration order). * * A dispatch-key value of `null` or the string `'get'` triggers read mode * (e.g. `{ html: 'get' }`, `{ offset: null }`). The sentinel applies only at * this top level — nested option objects are untouched, so `attr: { method: * 'get' }` still SETS `method="get"`. To write the literal string `'get'` as * content, use the DOM property directly (`el.textContent = 'get'`). * * Animation commands (Mode Move) return Promises; when an object contains * several, µ dispatches all of them (serialized per element via the queue) and * returns a single Promise that resolves when the whole choreography ends. */ export interface ModeCommands { /** * Set `innerHTML` of the selection. Finite numbers and bigints are coerced to * strings (NaN/±Infinity are ignored). An `Atom` keeps it updated. `null` / * `'get'` read the first element's innerHTML. */ html?: ModeContent | null | 'get'; /** * Set `textContent` of the selection. Finite numbers and bigints are coerced * to strings (NaN/±Infinity are ignored). An `Atom` keeps it updated. `null` / * `'get'` read the first element's textContent. */ text?: ModeContent | null | 'get'; /** * Get/set a form control's `.value` (checkbox/radio → `.checked` with a * boolean). An `Atom` makes it two-way: atom → control and input/change → * atom. `null` / `'get'` read the current value. */ value?: Bindable | boolean | null | 'get'; /** * Insert content into the selection. * - HTML string → appended via `insertAdjacentHTML` * - `HTMLElement` → appended via `appendChild` */ insert?: string | HTMLElement; /** Attach one or more event listeners to the selection */ on?: ModeOnConfig | ModeOnConfig[]; /** Iterate over each element in the selection */ each?: (element: HTMLElement) => void; /** * Replace the current selection with the return value of `fn`. * Useful for chaining custom transformations. */ proceed?: (current: ModeTarget) => ModeTarget; /** * Find a descendant within the current selection. * - Plain selector → `querySelector` (first match per element) * - `/selector/` syntax → `querySelectorAll` (all matches) */ find?: string; /** Apply one or more CSS style properties (camelCase keys). An `Atom` keeps them updated. */ css?: ModeCSSCommand | Atom; /** Manipulate CSS classes via `classList` methods. An `Atom` keeps them updated. */ classes?: ModeClassesCommand | Atom; /** Alias for `classes` */ clss?: ModeClassesCommand | Atom; /** Manipulate element attributes. An `Atom` keeps them updated. */ attr?: ModeAttrCommand | Atom; /** Move selection to the previous sibling, optionally filtered by a selector */ prev?: string; /** Move selection to the next sibling, optionally filtered by a selector */ next?: string; /** Collect all ancestor elements up to (but not including) `document`, optionally filtered */ parents?: string; /** Collect direct child `HTMLElement` nodes, optionally filtered by a selector */ childs?: string; /** Insert `element` immediately after the current (single) element */ after?: HTMLElement; /** Return filtered `children` of a single element */ children?: string; /** * From a NodeList / Array, select the last element. * Pass a selector string to get the last element that matches. */ last?: string; /** * From a NodeList / Array, select the first element. * Pass a selector string to get the first element that matches. */ first?: string; /** Get all siblings of the current element including itself, optionally filtered */ siblingsAll?: string; /** Get all siblings of the current element excluding itself, optionally filtered */ siblings?: string; /** Filter a NodeList / Array to elements matching a selector */ filter?: string; /** Wrap the current element(s) in a (deep-cloned) copy of `element` */ wrap?: HTMLElement; // ------------------------------------------------------------------------- // Mode Move extension commands (available once `µ._ext` is installed — see // the `move` entry point). All operate on the current selection; the // element is supplied by µ's factory, so params never carry an `el`. // ------------------------------------------------------------------------- /** Collapse height to zero, then hide. */ slideUp?: MoveAnimParams; /** Expand from zero to natural height. */ slideDown?: MoveAnimParams; /** slideDown when hidden, slideUp when visible. */ slideToggle?: MoveAnimParams; /** Fade opacity in (sets display first). */ fadeIn?: MoveAnimParams; /** Fade opacity out, then hide. */ fadeOut?: MoveAnimParams; /** Tween opacity to an explicit value. */ fadeTo?: MoveFadeToParams; /** fadeIn when hidden, fadeOut when visible. */ fadeToggle?: MoveAnimParams; /** Reveal via opacity tween. */ show?: MoveAnimParams; /** Hide via opacity tween. */ hide?: MoveAnimParams; /** show when hidden, hide when visible. */ toggle?: MoveAnimParams; /** rAF tween of arbitrary numeric CSS properties. */ animate?: MoveAnimateParams; /** Cancel the running animation (optionally clearing the queue). */ stop?: MoveStopParams; /** Pause the running animation; resume continues from here. */ pause?: MoveControlParams; /** Resume a paused animation. */ resume?: MoveControlParams; /** Queue a no-op delay before the next animation. */ delay?: MoveAnimParams; /** Getter — `{ top, left }` relative to the document. Pass `null` or `'get'`. */ offset?: null | 'get'; /** Getter — `{ top, left }` relative to the offset parent. Pass `null` or `'get'`. */ pos?: null | 'get'; } // --------------------------------------------------------------------------- // µ.ax — AJAX helper // --------------------------------------------------------------------------- export interface ModeAjaxOptions { url: string; method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | (string & {}); /** Sent as JSON body (POST/PUT/PATCH) or query-string params (GET) */ data?: Record | FormData; headers?: Record; /** Request timeout in milliseconds */ timeout?: number; } export interface ModeAjaxResponse { response: T; status: number; xhr: XMLHttpRequest; } export interface ModeAjaxError { error: string; status: number; xhr: XMLHttpRequest; } // --------------------------------------------------------------------------- // Mode Move extension types (installed via µ._ext — see the `move` export) // --------------------------------------------------------------------------- /** Duration: milliseconds, or the named presets `'fast'` (200) / `'slow'` (600). */ export type MoveDuration = number | 'fast' | 'slow'; /** Easing functions available to `animate`. */ export type MoveEasing = 'linear' | 'swing' | 'easeIn' | 'easeOut' | 'easeInOut'; /** Common params for slide / fade / show / hide / delay. */ export interface MoveAnimParams { /** Duration (default 400ms). */ t?: MoveDuration; /** Callback fired after the animation completes. */ fn?: () => void; /** `display` value to apply when revealing (fadeIn/show). */ display?: string; } /** Params for `fadeTo` — tween opacity to a target value. */ export interface MoveFadeToParams extends MoveAnimParams { /** Target opacity (0–1). */ opacity: number; } /** Params for `animate` — rAF tween of numeric CSS properties. */ export interface MoveAnimateParams { /** Map of CSS property → target numeric value (px assumed unless unitless). */ props: Record; t?: MoveDuration; easing?: MoveEasing; fn?: () => void; } /** Params for `stop`. */ export interface MoveStopParams { /** Also clear the queued animations. */ clear?: boolean; fn?: () => void; } /** Params for `pause` / `resume`. */ export interface MoveControlParams { fn?: () => void; } /** Return value of the `offset` and `pos` getters. */ export interface ModeMoveOffset { top: number; left: number; } /** * The animation method table returned by the Mode Move installer for a given * element. Merged into µ's command dispatch via `µ._ext`. */ export interface ModeMoveMethods { slideUp(p?: MoveAnimParams): Promise; slideDown(p?: MoveAnimParams): Promise; slideToggle(p?: MoveAnimParams): Promise; fadeIn(p?: MoveAnimParams): Promise; fadeOut(p?: MoveAnimParams): Promise; fadeTo(p: MoveFadeToParams): Promise; fadeToggle(p?: MoveAnimParams): Promise; show(p?: MoveAnimParams): Promise; hide(p?: MoveAnimParams): Promise; toggle(p?: MoveAnimParams): Promise; animate(p: MoveAnimateParams): Promise; stop(p?: MoveStopParams): void; pause(p?: MoveControlParams): void; resume(p?: MoveControlParams): void; delay(p?: MoveAnimParams): Promise; offset(): ModeMoveOffset; pos(): ModeMoveOffset; } /** The Mode Move installer factory assigned to `µ._ext`. */ export type ModeMoveInstaller = (el: HTMLElement) => ModeMoveMethods; // --------------------------------------------------------------------------- // Main function declaration // --------------------------------------------------------------------------- /** * µ — Mode.js DOM selection and manipulation. * * **Selector forms:** * - `'#id'` / `'.class'` / `'tag'` → `document.querySelector` → `HTMLElement | null` * - `'/selector/'` (slash-wrapped) → `document.querySelectorAll` → `NodeList` * - `''` (angle-bracket-wrapped) → creates and returns a new element * - `HTMLElement` / `NodeList` / `HTMLElement[]` → returned as-is * - `[selector, contextEl]` → `querySelector` scoped to `contextEl` * - `null` → returns `null` * * **Commands (second argument):** * When a `ModeCommands` object is provided, all listed operations are executed * against the resolved selection and the (possibly transformed) selection is * returned. * * @example * // Simple selection * const el = µ('#app') as HTMLElement * * @example * // Chained commands * µ('.card', { css: { opacity: '0.5' }, classes: { add: 'disabled' } }) * * @example * // NodeList via slash syntax * µ('/li/', { each: (el) => console.log(el.textContent) }) */ declare function µ(selector: ModeSelector | null, commands?: ModeCommands): ModeTarget; // --------------------------------------------------------------------------- // Static methods on µ // --------------------------------------------------------------------------- declare namespace µ { /** * Create a `CustomEvent` (with detail) or a plain `Event`. * * @param name - Event type string (e.g. `'my:event'`) * @param detail - Payload attached to `event.detail` (produces a CustomEvent) * @param bubbles - Whether the event bubbles (default `true`) * * @example * const ev = µ.e('item:selected', { id: 42 }) * µ.d(ev, listEl) */ function e(name: string, detail?: unknown, bubbles?: boolean): CustomEvent | Event; /** * Shorthand for `element.addEventListener(event, handler)`. * * @example * µ.l(document, 'keydown', (e) => console.log(e.key)) */ function l( element: HTMLElement | Document | Window | EventTarget, event: string, handler: ModeEventHandler, ): void; /** * Dispatch `event` on `element`. * * @example * µ.d(µ.e('refresh'), containerEl) */ function d(event: Event | CustomEvent, element?: HTMLElement | Document | EventTarget): void; /** * Call `preventDefault()` + `stopPropagation()` on the event, then return * `event.target` (or the closest ancestor matching `selector`). * * @example * btn.addEventListener('click', (e) => { * const target = µ.t(e, '.card') // returns closest .card ancestor * }) */ function t(event: Event, selector?: string): HTMLElement; /** * Convert an object or array-like value. * * - No `type` → `Object.entries(obj)` → `[key, value][]` * - `'array'` → `Array.from(obj)` * - `'keys'` → `Object.keys(obj)` * - `'values'` → `Object.values(obj)` * - `'entries'`→ `Object.entries(obj)` * * @example * µ.a({ color: 'red', size: 'lg' }) // → [['color','red'],['size','lg']] * µ.a(document.querySelectorAll('li'), 'array') // → HTMLElement[] */ function a( obj: object | ArrayLike, type?: 'array' | 'keys' | 'values' | 'entries', ): unknown[]; /** Alias for `µ.a` */ const ar: typeof µ.a; /** * Promise-based XHR wrapper. * * - GET requests: `data` is serialized as query-string parameters * - POST/PUT/PATCH: `data` is sent as JSON (or raw FormData) * - Automatically parses JSON responses * * @example * const { response } = await µ.ax({ url: '/api/items', method: 'GET' }) * * @example * await µ.ax({ * url: '/api/items', * method: 'POST', * data: { name: 'Widget' }, * headers: { 'X-CSRF-Token': token }, * }) */ function ax(options: ModeAjaxOptions): Promise>; /** * Extension hook. An installer factory assigned here is called by µ on every * invocation with the resolved element; its returned methods are merged into * the command dispatch table. The Mode Move suite registers itself here. * * @example * import install from '@microdom/mode/move' * µ._ext = install * µ('#box', { slideUp: { t: 300 } }) */ let _ext: ModeMoveInstaller | undefined; } // --------------------------------------------------------------------------- // Global augmentation (window.µ) // --------------------------------------------------------------------------- declare global { interface Window { µ: typeof µ; } } // --------------------------------------------------------------------------- // Exports // --------------------------------------------------------------------------- /** * `String.raw` — a tagged-template alias for authoring raw HTML strings with * editor highlighting. µ itself takes plain strings; this is only a naming * convenience (escape sequences are NOT processed, so write real newlines). */ export declare const html: typeof String.raw; export default µ; export { µ };