JGuang

表单字段

Field · Astryx

交互预览
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

表单字段 · Field

表单字段用于表单输入场景。下面的官方示例展示原生用法与状态,属性名保持上游 API。

安装与前置条件

npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0

需要 React 19+,按顺序导入基础 CSS 和主题,并设置 Theme Provider。参见开始使用。

示例与使用方式

  • 表单字段 · 综合展示 1:首先体验这个示例,再查看代码理解组件的组合方式。
  • 表单字段 · 综合展示 2:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
  • 表单字段 · 基础用法 3:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
  • 表单字段 · 交互示例 4:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
  • 表单字段 · 样式变化 / 状态 / 样式 5:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。
  • 表单字段 · 交互示例 6:可交互查看这一用法的行为,复制代码时保留它实际使用的属性与导入。

API 与上游详细参考

优先使用 0.7.1 官方中文说明;上游尚未提供中文的条目保留英文。属性名、类型及默认值保持原生 API,不把示例状态名称当成组件属性。

Field

Field is a low-level wrapper for custom, native, or third-party controls that do not already provide field label, description, and status UI. Use it when you need the Field shell around a control you own; use styled Astryx inputs like TextInput, Typeahead, and Select directly when they already expose label, description, and validation props.

属性类型默认值说明
label *stringLabel text for the field (always rendered for accessibility).
inputID *stringID for the input element (used for the label htmlFor attribute).
labelIDstringID applied to the label element itself for group accessibility.
isGroupLabelbooleanfalseRenders the label as a span for control groups (radiogroup, checkbox list).
children *ReactNodeThe input or control to render.
isLabelHiddenbooleanfalseVisually hide the label (still accessible to screen readers).
isDisabledbooleanfalseWhether the associated input is disabled. Propagates disabled styling to the label.
descriptionstringDescription text displayed between the label and input.
descriptionIDstringID for the description element (use for aria-describedby on the input).
isOptionalbooleanfalseWhether the field is optional (mutually exclusive with isRequired).
isRequiredbooleanfalseWhether the field is required (mutually exclusive with isOptional).
labelIconIconTypeIcon to display before the label text. See astryx docs icons for valid semantic names.
labelTooltipstringTooltip text to display in an info icon at the end of the label.
status{type: 'warning' | 'error' | 'success', message?: string, messageID?: string}Status indicator with type and optional message. When message is set, displays a colored status box. messageID is for wiring aria-describedby on the input.
statusVariant'attached' | 'detached''attached'How the status message renders relative to the input. Attached overlaps the input border; detached floats below.
widthSizeValueWidth of the field (number = pixels, string used as-is, e.g. "100%"). Sizes the whole field (label, control, and status) so they stay aligned. Prefer this over setting width via xstyle/className/style, which only size the inner control box. Without a width, a row can shrink the whole field below the natural width of its control, so filter bars shrink instead of overflowing.
refReact.Ref<HTMLDivElement>Ref forwarded to the root element.
xstyleStyleXStylesStyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value: not an inline style object like style={{}}.
classNamestringCSS class name(s) appended to the root element. Prefer xstyle for StyleX deduplication.
styleReact.CSSPropertiesInline styles applied to the root element. Takes priority over StyleX inline styles.

使用建议

  • Wrap custom controls, native inputs, or third-party widgets that need labeling, helper text, optional/required indicators, or validation status.
  • Always provide a label for accessibility, even if visually hidden with isLabelHidden.
  • Use inputID and descriptionID to connect the label and description to the inner control with htmlFor and aria-describedby.
  • 避免:Nest Field around styled inputs such as TextInput, Typeahead, Select, DateInput, or TextArea; those components already render their own Field shell.
  • 避免:Use the attached status variant on non-bordered controls such as sliders, switches, or checkboxes; use detached so the message does not overlap the control.
  • 避免:Set both isOptional and isRequired on the same field.
  • 避免:Hide the label without providing an alternative way for the user to understand the field purpose.

Filter bar

import {useState} from 'react';
import {HStack} from '@astryxdesign/core/HStack';
import {Icon} from '@astryxdesign/core/Icon';
import {Selector} from '@astryxdesign/core/Selector';
import {StackItem} from '@astryxdesign/core/Stack';
import {TextInput} from '@astryxdesign/core/TextInput';

// Every Field-based input (TextInput, Selector, DateInput, ...) can be shrunk
// by its row, so a filter bar never pushes the page sideways. Let the row wrap
// so that on a phone the search box keeps a full line and the selectors move
// below it; on wider screens everything stays on one line, with the search box
// taking the free space.
function OrdersFilterBar() {
  const [query, setQuery] = useState('');
  const [status, setStatus] = useState('Awaiting fulfillment');
  const [region, setRegion] = useState('All regions');
  const [sort, setSort] = useState('Newest first');
  return (
    <HStack gap={2} wrap="wrap" vAlign="center">
      <StackItem size="fill">
        <TextInput
          label="Search orders"
          isLabelHidden
          placeholder="Search by order or customer"
          startIcon={<Icon icon="search" />}
          value={query}
          onChange={setQuery}
        />
      </StackItem>
      <Selector
        label="Status"
        isLabelHidden
        options={['All statuses', 'Awaiting fulfillment', 'Shipped', 'Delivered', 'Returned']}
        value={status}
        onChange={setStatus}
      />
      <Selector
        label="Region"
        isLabelHidden
        options={['All regions', 'North America', 'Europe', 'Asia Pacific']}
        value={region}
        onChange={setRegion}
      />
      <Selector
        label="Sort"
        isLabelHidden
        options={['Newest first', 'Oldest first', 'Highest total']}
        value={sort}
        onChange={setSort}
      />
    </HStack>
  );
}

Wrap a custom control

function CustomSliderField() {
  return (
    <Field
      label="Confidence"
      inputID="confidence-slider"
      description="Choose how strict the review should be."
      descriptionID="confidence-help"
      status={{type: 'success', message: 'Recommended default'}}
      statusVariant="detached">
      <input
        id="confidence-slider"
        type="range"
        min={0}
        max={100}
        defaultValue={60}
        aria-describedby="confidence-help"
      />
    </Field>
  );
}

主题 API

{
  "targets": [
    {
      "className": "astryx-field",
      "visualProps": [
        "layout"
      ]
    },
    {
      "className": "astryx-field-label"
    },
    {
      "className": "astryx-field-status",
      "visualProps": [
        "type",
        "variant"
      ]
    },
    {
      "className": "astryx-input-status-icon",
      "visualProps": [
        "size",
        "status"
      ]
    },
    {
      "className": "astryx-input-clear-button"
    },
    {
      "className": "astryx-input-clear-icon"
    }
  ],
  "vars": [
    {
      "name": "--_field-radius",
      "description": "Border radius of input fields",
      "default": "var(--radius-element)",
      "private": true
    },
    {
      "name": "--_field-status-overlap",
      "description": "Amount an attached FieldStatus extends behind the lower half of the control. Set from the rendered control size.",
      "default": "calc(var(--size-element-md) / 2)",
      "private": true
    },
    {
      "name": "--_input-clear-hit-inset",
      "description": "Outset of the clear (✕) button's invisible hit area, applied to a ::after overlay. 0 on a fine pointer; negative on a coarse one, which grows the 20px button to the 24px touch target without changing what is drawn.",
      "default": "0px",
      "private": true
    },
    {
      "name": "--_input-clear-hit-content",
      "description": "Whether the clear (✕) button's invisible hit overlay exists. `none` on a fine pointer, so no ::after is generated and hover still reaches the glyph; `\"\"` on a coarse one, where the overlay provides the 24px touch target.",
      "default": "none",
      "private": true
    }
  ],
  "derived": [
    {
      "property": "borderRadius",
      "vars": [
        "--_field-radius"
      ]
    }
  ]
}

FieldLabel

FieldStatus

组件结构

  • Label: Text identifying the field. Always rendered for accessibility, optionally hidden visually.
  • Description: Helper text between the label and input explaining what to enter.
  • Control slot: A custom, native, or third-party control that does not already render a field shell.
  • Status message: Inline validation feedback showing error, warning, or success with a message.
  • Optional/Required indicator: Badge next to the label showing whether the field is optional or required.
  • Label tooltip: Info icon at the end of the label with a tooltip explaining the field.

Field Label

独立的标签组件,支持可选/必填指示器和工具提示。

属性类型默认值说明
label *string标签文本。
inputID *string此标签关联的输入框 ID。
isLabelHiddenbooleanfalse视觉隐藏标签。
isDisabledbooleanfalse关联的输入框是否禁用。
isOptionalbooleanfalse显示"Optional"指示器。
isRequiredbooleanfalse显示"Required"指示器。
labelIconIconType标签文本前的图标。
labelTooltipstring标签末尾信息图标的工具提示文本。