import type { JSX, ValidComponent } from "@solidjs/web"; import { dynamic } from "@solidjs/web"; import { createSignal, omit } from "solid-js"; import { userRef } from "../../internals/compose-refs"; import { type MaybeAccessor, readOption } from "../../internals/maybe-accessor"; import { mergeRootProps } from "../../internals/merge-root-props"; import { resolveClassStyle } from "../../internals/resolve-class-style"; import type { BaseUIComponentProps } from "../../internals/types"; import { VISUALLY_HIDDEN_STYLE } from "../../internals/visually-hidden-style"; import { createMeter } from "../create-meter.tsx"; import { MeterRootContext, type MeterRootContextValue, } from "../meter-root-context.ts"; export type MeterRootState = Record; export interface MeterRootProps extends Omit< BaseUIComponentProps<"div", MeterRootState>, | "role" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-labelledby" > { /** * Tag/component to render. Defaults to `"div"`. Replaces the * pre-spike `render` prop (axis G). */ as?: ValidComponent; "aria-valuetext"?: JSX.AriaAttributes["aria-valuetext"] | undefined; format?: MaybeAccessor; getAriaValueText?: | ((formattedValue: string, value: number) => string) | undefined; locale?: MaybeAccessor; max?: MaybeAccessor; min?: MaybeAccessor; value: MaybeAccessor; } /** * Groups all parts of the meter and provides the value for screen readers. * Renders a `
` (or `as` override). * * ยง1 wave 2: replaces `renderElement` with `dynamic(() => props.as ?? "div")`. * The slot factory `createMeter().root()` carries `role="meter"` and the * aria-valuemin/max/now/text getters; aria-labelledby is an inline reactive * getter on the wrapper's internal slot bag. */ export function MeterRoot(props: MeterRootProps): JSX.Element { const c = createMeter({ value: () => readOption(props.value), min: () => readOption(props.min), max: () => readOption(props.max), format: () => readOption(props.format), locale: () => readOption(props.locale), getAriaValueText: props.getAriaValueText, }); const value = c.value; const min = c.min; const max = c.max; const formattedValue = c.formattedValue; const [labelId, setLabelId] = createSignal(undefined, { ownedWrite: true, }); const contextValue: MeterRootContextValue = { value, min, max, formattedValue, setLabelId, }; const userClassStyle = resolveClassStyle(props, () => ({})); const userProps = omit( props, "as", "children", "class", "style", "attrs", "format", "getAriaValueText", "locale", "max", "min", "value", "aria-valuetext", ); const internalSlot = { get "aria-labelledby"(): string { return labelId() ?? ""; }, get "aria-valuetext"(): string { const provided = props["aria-valuetext"]; return typeof provided === "string" ? provided : c.ariaValueText(); }, }; let merged: Record = mergeRootProps(c.root(), internalSlot); merged = mergeRootProps(merged, userClassStyle); merged = mergeRootProps(merged, userProps); const Tag = dynamic(() => props.as ?? "div"); return ( {props.children} {/* force NVDA to read the label https://github.com/mui/base-ui/issues/4184 */} x ); }