import { createMemo } from "solid-js"; import { mergeElementStyles } from "./merge-root-props"; import type { ElementClass, ElementStyle, StylingAttrs } from "./types"; /** * Helper for the wave-3 `as=`/`dynamic()` element migration: resolve `class` / `style` * from a Base UI component's `props` against the live state accessor and * return a plain object with reactive getters suitable for `mergeRootProps`. * * Both keys may be raw values OR functions of state — the functions are * called inside the getter so they re-run on every reactive dependency * change. Returning `undefined` is fine; `mergeRootProps` treats undefined * as "absent" so internal defaults still win. */ export interface ClassStyleSource { class?: ElementClass | ((state: State) => ElementClass) | undefined; style?: ElementStyle | ((state: State) => ElementStyle) | undefined; attrs?: | StylingAttrs | ((state: State) => StylingAttrs | undefined) | undefined; } function composeClasses( legacy: ElementClass, attrs: ElementClass, ): ElementClass { if (legacy === undefined || legacy === false) return attrs; if (attrs === undefined || attrs === false) return legacy; return [legacy, attrs]; } export function resolveClassStyle( props: ClassStyleSource, getState: () => State, ): { class: ElementClass; style: ElementStyle; "data-style-src": string | undefined; } { // Resolve all state-aware styling inputs in one computation. Reading any // output observes the same state snapshot and does not independently invoke // class/style/attrs callbacks. const resolved = createMemo(() => { const state = getState(); const classValue = props.class; const styleValue = props.style; const attrsValue = props.attrs; const legacyClass = typeof classValue === "function" ? classValue(state) : classValue; const legacyStyle = typeof styleValue === "function" ? styleValue(state) : styleValue; const attrs = typeof attrsValue === "function" ? attrsValue(state) : attrsValue; return { class: composeClasses(legacyClass, attrs?.class), style: mergeElementStyles(legacyStyle, attrs?.style), "data-style-src": attrs?.["data-style-src"], }; }); return { get class(): ElementClass { return resolved().class; }, get style(): ElementStyle { return resolved().style; }, get "data-style-src"(): string | undefined { return resolved()["data-style-src"]; }, }; }