import type { JSX, ValidComponent } from "@solidjs/web"; import { dynamic } from "@solidjs/web"; import { omit, Show } from "solid-js"; import { mergeRootProps } from "../../internals/merge-root-props"; import { resolveClassStyle } from "../../internals/resolve-class-style"; import type { BaseUIComponentProps } from "../../internals/types"; import { type CheckboxRootState, useCheckboxRootContext, } from "../checkbox-root-context.ts"; export interface CheckboxIndicatorState extends CheckboxRootState {} export interface CheckboxIndicatorProps extends BaseUIComponentProps<"span", CheckboxIndicatorState> { /** * Tag/component to render. Defaults to `"span"`. Replaces the * pre-spike `render` prop (axis G). */ as?: ValidComponent; /** * Whether to keep the element in the DOM when the checkbox is not * checked. * @default false */ keepMounted?: boolean | undefined; } /** * Indicates whether the checkbox is ticked. Renders a `` (or `as` * override) by default. * * Solid 2.0-native rewire (§1 wave 1): replaces `renderElement` with * `dynamic(() => props.as ?? "span")` and emits the same `data-*` * projection (`data-checked`/`data-unchecked`/`data-indeterminate` plus * the field-state attrs) inline as getters reading from the root * context's state accessor. The conditional mount via `` is * preserved. */ export function CheckboxIndicator(props: CheckboxIndicatorProps): JSX.Element { const context = useCheckboxRootContext(); const state = context.state; const keepMounted = (): boolean => props.keepMounted ?? false; const shouldRender = (): boolean => keepMounted() || context.checked() || context.indeterminate(); const stateAttrs = { // `data-checked` / `data-unchecked` are suppressed when indeterminate // (matches the root projection — `data-indeterminate` owns that state). get "data-checked"(): "" | undefined { if (state().indeterminate) return undefined; return state().checked ? "" : undefined; }, get "data-unchecked"(): "" | undefined { if (state().indeterminate) return undefined; return state().checked ? undefined : ""; }, get "data-indeterminate"(): "" | undefined { return state().indeterminate ? "" : undefined; }, get "data-disabled"(): "" | undefined { return state().disabled ? "" : undefined; }, get "data-readonly"(): "" | undefined { return state().readOnly ? "" : undefined; }, get "data-required"(): "" | undefined { return state().required ? "" : undefined; }, get "data-valid"(): "" | undefined { return state().valid === true ? "" : undefined; }, get "data-invalid"(): "" | undefined { return state().valid === false ? "" : undefined; }, get "data-touched"(): "" | undefined { return state().touched ? "" : undefined; }, get "data-dirty"(): "" | undefined { return state().dirty ? "" : undefined; }, get "data-filled"(): "" | undefined { return state().filled ? "" : undefined; }, get "data-focused"(): "" | undefined { return state().focused ? "" : undefined; }, }; const userStyling = resolveClassStyle(props, () => state()); const userProps = omit(props, "class", "style", "attrs", "as", "keepMounted"); const styledUserProps = mergeRootProps(userStyling, userProps); const merged = mergeRootProps(stateAttrs, styledUserProps); const Tag = dynamic(() => props.as ?? "span"); return ( ); }