Skip to content

input_group: Add composable input groups - #3042

Open
suxiaoshao wants to merge 5 commits into
longbridge:mainfrom
suxiaoshao:codex/2863-input-group
Open

input_group: Add composable input groups#3042
suxiaoshao wants to merge 5 commits into
longbridge:mainfrom
suxiaoshao:codex/2863-input-group

Conversation

@suxiaoshao

Copy link
Copy Markdown
Contributor

Closes #2863

Description

Add composable input groups for multiple inline addons, block toolbars, and textareas that share one frame. Existing Input::prefix and Input::suffix remain available for simple standalone adornments.

Composition and reuse boundary

The reusable unit is the retained InputState / TextareaState and its native editing engine. InputGroup does not compose or wrap the styled Input component. It owns its layout, border, background, and focus/error treatment, while the existing state continues to own text, selection, undo, IME, focus, and textarea scrolling/auto-grow. A private InputControl adapter shares theme projection, native menus, accessibility, and content-type integration with standalone inputs without sharing their visual frame. There are no gpui-base changes.

The public API keeps the composable parts and fluent construction of shadcn/ui: InputGroup, InputGroupInput, InputGroupTextarea, InputGroupAddon, InputGroupButton, and InputGroupText. Rust uses a typed .input(...) slot for one editor and ordered .addon(...) / .child(...) composition:

InputGroup::new("message")
    .input(InputGroupTextarea::new(&self.message).aria_label("Message"))
    .addon(InputGroupAddon::new("message-header")
        .align(InputGroupAddonAlignment::BlockStart)
        .child(InputGroupText::new().child("New message")))
    .addon(InputGroupAddon::new("message-actions")
        .align(InputGroupAddonAlignment::BlockEnd)
        .child(InputGroupButton::new("send").label("Send")
            .on_click(cx.listener(|this, _, window, cx| this.send(window, cx)))))

Each part owns its presentation. Default styling follows the base-nova composition with GPUI Component theme tokens. Editor, label, icon, and semantic-state refinements expose stable customization points. The group coordinates disabled/read-only behavior and addon click-to-focus while preserving native button actions.

The same six parts are registered in the JavaScript shell, with retained state bindings and immediate style declarations. Native and JavaScript Stories cover the compositions and working actions; English and Chinese documentation explain ownership, usage, and styling.

Breaking Changes

None; existing input APIs are unchanged. The following is an optional change when a simple prefix grows into a shared-frame composition:

- Input::new(&query).prefix(Icon::new(IconName::Search))
+ InputGroup::new("search")
+     .input(InputGroupInput::new(&query))
+     .addon(InputGroupAddon::new("search-icon")
+         .child(Icon::new(IconName::Search)))

How to Test

Validated on macOS after rebasing onto the latest upstream main:

cargo fmt --all --check
cargo test -p gpui-component --features test-support --lib input_group --locked
cargo test -p gpui-component --features test-support --lib input::input::tests --locked
cargo test -p gpui-component-shell --test input_group_host --test story_gallery_host --locked
cargo clippy -p gpui-component -p gpui-component-story -p gpui-component-shell -p gpui-shell --locked -- --deny warnings
cargo build -p gpui-component-story -p gpui-component-shell --locked
node examples/js_story/fixtures/verify-coverage.mjs
npm --prefix crates/component-shell/tests/types test

The 25 focused regressions cover native editing, focus routing, embedded actions, disabled/read-only behavior, accessibility, selection/IME geometry, textarea padding and scrolling, shell state callbacks, and Story materialization. TypeScript positive/negative fixtures and runtime validation pass. JavaScript Story coverage tracks 72 routes and 73 catalog surfaces.

The native Input Group Story was also exercised on macOS for editing, focus, custom internal styles, and submission. Windows, Linux, and WebAssembly were not manually tested.

Checklist

  • Read the contributing guide and kept this PR scoped to InputGroup.
  • Reviewed the AI-assisted implementation and its focused regression coverage.
  • Built and ran the affected native Story and validated the JavaScript Story.

Implementation, examples, documentation, and tests were developed with Codex assistance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Add an InputGroup component

1 participant