// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file Field.tsx * @input Uses React, HTMLAttributes, ReactNode, FieldLabel, IconType * @output Exports Field component, FieldProps * @position Core implementation; consumed by index.ts, tested by Field.test.tsx * * SYNC: When modified, update these files to stay in sync: * - /packages/core/src/Field/Field.doc.mjs (props table, features, implementation notes) * - /packages/core/src/Field/Field.test.tsx (tests for new/changed behavior) * - /packages/core/src/Field/index.ts (exports if types change) * - /apps/storybook/stories/Field.stories.tsx (storybook stories) * - /packages/cli/assets/templates/blocks/components/Field/ (showcase blocks) */ import { type ReactNode, use } from 'react'; import * as stylex from "../style-runtime.js"; import type { BaseProps } from "../BaseProps.js"; import type { SizeValue } from "../utils/types.js"; import { FieldLabel } from "./FieldLabel.js"; import { FieldStatus } from "../FieldStatus/FieldStatus.js"; import type { FieldStatusVariant } from "../FieldStatus/FieldStatus.js"; import "../theme/tokens.stylex.js"; import { spacingVars, borderVars, sizeVars } from "../theme/tokens.stylex.js"; import type { IconType } from "../Icon/index.js"; import { mergeProps } from "../utils/index.js"; import { useDevWarning } from "../hooks/useDevWarning.js"; import { FormLayoutContext } from "../FormLayout/FormLayoutContext.js"; import { Text } from "../Text/index.js"; import { themeProps } from "../utils/themeProps.js"; const styles = { container: { k1xSpc: "x78zum5", kXwgrk: "xdt5ytf", kHBbk8: "xc8icb0", $$css: true as const }, containerGap: { kOIVth: "xzye2dw", $$css: true as const }, horizontalLabels: { k1xSpc: "xjp7ctv", $$css: true as const }, attachedStatusLayer: { kVAEAm: "x1n2onr6", kY2c9j: "x8knxv4", $$css: true as const } }; // Dynamic style for the consumer-controlled field width. Numbers are treated // as pixels by StyleX; strings (e.g. '100%') are used as-is. const dynamicStyles = { width: (width: SizeValue | null) => [{ kzqmXN: width != null ? "x5lhr3w" : width, $$css: true as const }, { "--x-width": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(width) }] }; export type { SizeValue } from "../utils/types.js"; export type FieldStatusType = 'warning' | 'error' | 'success'; export interface FieldStatusInput { /** * The type of status to display. */ type: FieldStatusType; /** * Optional message to display below the input. */ message?: string; /** * ID for the status message element (use for aria-describedby on the input). */ messageID?: string; } export interface FieldProps extends Omit, 'children'> { /** Ref forwarded to the root element */ ref?: React.Ref; /** * Label text for the field (always rendered for accessibility). */ label: string; /** * Whether to visually hide the label and description (still accessible to screen readers). * @default false */ isLabelHidden?: boolean; /** * Description text displayed between the label and input. * Hidden when isLabelHidden is true. */ description?: string; /** * ID of the input element this label points AT (used as the label's * `htmlFor`). This is the id of the *control*, not of the label element — * see `labelID` for the latter. */ inputID: string; /** * The `id` applied TO the label element itself (distinct from `inputID`, * which is the control the label points at). A grouping control * (radiogroup, checkbox group) references this via `aria-labelledby` to take * the label as its accessible name. Pair with `isGroupLabel`. */ labelID?: string; /** * When the field wraps a group of controls rather than a single input, set * this so the label renders as a non-`