import type { JSX, ValidComponent } from "@solidjs/web"; import { dynamic } from "@solidjs/web"; import { createMemo, omit } from "solid-js"; import { composeRefs, userRef } from "../../internals/compose-refs"; import { mergeRootProps } from "../../internals/merge-root-props"; import { resolveClassStyle } from "../../internals/resolve-class-style"; import type { BaseUIComponentProps } from "../../internals/types"; import type { SelectValue as SelectValueType } from "../select-root-context"; import { useSelectRootContext } from "../select-root-context"; import { hasNullItemLabel, resolveMultipleLabels, resolveSelectedLabel, } from "../utils/resolve-value-label"; export interface SelectValueState { value: SelectValueType; placeholder: boolean; } export interface SelectValueProps extends Omit, "children"> { as?: ValidComponent; children?: JSX.Element | ((value: Value) => JSX.Element) | undefined; placeholder?: JSX.Element | undefined; } export function SelectValue( props: SelectValueProps, ): JSX.Element { const root = useSelectRootContext(); const setValueRef = (el: HTMLElement | null): void => { root.valueRef.current = el; }; const hasSelectedValue = createMemo(() => { const v = root.value(); if (v == null) return false; if (root.multiple() && Array.isArray(v)) return v.length > 0; return true; }); const state = createMemo(() => ({ value: root.value(), placeholder: !hasSelectedValue(), })); const hasNullLabel = createMemo(() => { const childrenProp = props.children; if ( hasSelectedValue() || props.placeholder == null || childrenProp != null ) { return false; } return hasNullItemLabel(root.items()); }); const resolvedChildren = createMemo(() => { const childrenProp = props.children; const v = root.value(); if (typeof childrenProp === "function") { // `root.value()` is `unknown` at runtime; the `Value` generic is a // typed convenience for callers, so cast at this boundary. return (childrenProp as (value: Value) => JSX.Element)(v as Value); } if (childrenProp != null) return childrenProp as JSX.Element; if (!hasSelectedValue() && props.placeholder != null && !hasNullLabel()) { return props.placeholder; } if (Array.isArray(v)) { return resolveMultipleLabels(v, root.items(), root.itemToStringLabel()); } return resolveSelectedLabel(v, root.items(), root.itemToStringLabel()); }); const internal = { get children() { return resolvedChildren(); }, }; const userClassStyle = resolveClassStyle(props, () => state()); // Strip `placeholder` and `children` (children may be a formatter fn). const userProps = omit( props, "as", "class", "style", "attrs", "placeholder", "children", ); let merged: Record = mergeRootProps( internal, userClassStyle, ); merged = mergeRootProps(merged, userProps); const Tag = dynamic(() => props.as ?? "span"); return ; }