Skip to content

input_group: Add InputGroup component.#1674

Closed
sxhxliang wants to merge 5 commits intolongbridge:mainfrom
sxhxliang:feat/input_group
Closed

input_group: Add InputGroup component.#1674
sxhxliang wants to merge 5 commits intolongbridge:mainfrom
sxhxliang:feat/input_group

Conversation

@sxhxliang
Copy link
Copy Markdown

@sxhxliang sxhxliang commented Nov 24, 2025

  • Introduce InputGroup component with flexible layout and alignment options
  • Add supporting components: InputGroupAddon, InputGroupText, InputGroupInput, and InputGroupTextarea
  • Implement various examples including search, URL input, validation, and chat interfaces
  • Update story module to include and initialize InputGroupStory
  • Register InputGroup in the main UI module exports

Description

Add a new shadcn/ui InputGroup component that provides a flexible way to compose input fields with addons such as icons, buttons, and text labels.

Features:

  • InputGroup: Main container with support for disabled/invalid states, horizontal/vertical layouts
  • InputGroupAddon: Flexible addon container with alignment options (InlineStart, InlineEnd, BlockStart,
    BlockEnd)
  • InputGroupText: Simple text element for labels within addons
  • InputGroupInput: Styled input wrapper that integrates seamlessly with the group
  • InputGroupTextarea: Multi-line text input variant for chat-like interfaces

Examples included:

  • Basic search with icon and results count
  • URL input with protocol prefix
  • Username with validation badge
  • Search with clear button and loading state
  • Chat input with bottom toolbar

Screenshot

Before After
Windows/- image
macOS/- image

Break Changes

Describe any breaking changes introduced by this pull request. If none, remove this section.

  • Change 1

None

How to Test

  1. Run the story application:
    cargo run
  2. Navigate to the InputGroup story panel
  3. Verify the following examples render correctly:
    - Basic Search with Icon and Results
    - URL Input with Protocol Prefix
    - Username with Validation Icon
    - Search with Clear Button
    - Search with Loading State
    - Multiple Icons
    - With Action Button
    - Disabled State
    - Invalid/Error State
    - Chat Input with Toolbar

Checklist

  • I have read the CONTRIBUTING document and followed the guidelines.
  • Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate.
  • Passed cargo run for story tests related to the changes.
  • Tested macOS, Windows platforms performance (if the change is platform-specific)

…dons

- Introduce `InputGroup` component with flexible layout and alignment options
- Add supporting components: `InputGroupAddon`, `InputGroupText`,
  `InputGroupInput`, and `InputGroupTextarea`
- Implement various examples including search, URL input, validation,
  and chat interfaces
- Update story module to include and initialize InputGroupStory
- Register InputGroup in the main UI module exports
@sxhxliang sxhxliang marked this pull request as ready for review November 24, 2025 13:01
@huacnlee huacnlee changed the title feat(ui): add InputGroup component for composing input fields with ad… input_group: Add InputGroup component. Nov 24, 2025
Comment thread crates/story/src/input_group_story.rs Outdated
/// Create a standard InputGroup with max-width
fn example_group() -> InputGroup {
InputGroup::new().max_w_96()
}
Copy link
Copy Markdown
Member

@huacnlee huacnlee Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the example, we should keep the example simple and clean to understand.

Move these things to render method to as a nested layout, even it will duplicate code.

Comment thread crates/ui/src/input/input_group.rs Outdated
const DEFAULT_ADDON_PADDING: Pixels = px(12.);
const DEFAULT_TEXTAREA_HEIGHT: Pixels = px(80.);

// === Types ===
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all comments like this.

Comment thread crates/ui/src/input/input_group.rs Outdated

// === Constants ===

const DEFAULT_INPUT_GROUP_ID: &str = "input-group";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, and use InputGroup as the group id.

Comment thread crates/ui/src/input/input_group.rs Outdated
/// Marks the input group as invalid/error state.
///
/// When `true`, the border color changes to indicate an error.
pub fn invalid(mut self, invalid: bool) -> Self {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, we are not ready to introduce this feature, because danger not enough. So this API we need to consider more before add.

Please remove it, with replacement, user can be based on style for adding the invalid style.

Comment thread crates/ui/src/input/input_group.rs Outdated
fn default() -> Self {
Self::new()
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove default for InputGroup, just use new method.

Comment thread crates/ui/src/input/input_group.rs Outdated
let border = self.get_border_color(cx);

div()
.id(self.id.unwrap_or_else(|| DEFAULT_INPUT_GROUP_ID.into()))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the id is not required, just removed it. Here, this code is not correct.

Comment thread crates/ui/src/input/input_group.rs Outdated
div()
.id(self.id.unwrap_or_else(|| DEFAULT_INPUT_GROUP_ID.into()))
.w_full()
.min_w_0()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why need this? So, please review you self agian.

.when(cx.theme().shadow, |this| this.shadow_xs())
.when(self.disabled, |this| {
this.opacity(0.5).cursor_not_allowed()
})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabled style should not just change the opacity, please follow disabled style of the Input, Button.

pub fn align(mut self, align: InputGroupAlign) -> Self {
self.align = align;
self
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need add helper methods like inline_end, inline_start, block_start and block_end, and please order the method by name.

Comment thread crates/ui/src/input/input_group.rs Outdated
.text_sm()
.font_medium()
.cursor_text()
.pl(padding.0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use p(padding) instead.

Comment thread crates/ui/src/input/input_group.rs Outdated
/// Creates a text label with default styling.
///
/// This is a convenience method for quickly creating text elements.
pub fn label(text: impl Into<String>) -> impl IntoElement {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Into<SharedShared>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And why have this method? This is not for InputGroupText.

Comment thread crates/ui/src/input/input_group.rs Outdated
/// ```
#[derive(IntoElement)]
pub struct InputGroupInput {
state: gpui::Entity<crate::input::InputState>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean code, use Entity and InputState.

Comment thread crates/ui/src/input/input_group.rs Outdated
pub fn height(mut self, height: impl Into<Option<DefiniteLength>>) -> Self {
self.height = height.into();
self
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this method, use style instead, so user can use h method.

sxhxliang and others added 3 commits November 28, 2025 22:01
…tly inlined into

  render() method
  2. Remove comments - All // ===  style comments removed
  3. Remove custom group id - DEFAULT_INPUT_GROUP_ID constant and .id() method removed
  4. Remove invalid state - invalid field, invalid() method and related logic removed
  5. Remove Default trait - Default implementation for InputGroup removed
  6. Remove unnecessary id - No longer using fixed id
  7. Disabled styling - Changed to use muted background + overlay to fully block interactions
  8. Add helper methods - Added align(), block_end(), block_start(), inline_end(),
  inline_start() in alphabetical order
  9. Use IntoSharedString - InputGroupText::label() removed, no longer needed
  10. Remove InputGroupText::label - Method removed
  11. Use Entity/InputState - Simplified from gpui::Entity<crate::input::InputState> to
  Entity<InputState>
  12. Remove height method - InputGroupTextarea::height() removed, users directly use .h()
  style method
@sxhxliang
Copy link
Copy Markdown
Author

Review Comments Addressed

Thank you for the detailed review! I've addressed all the comments:

✅ Completed (12 items)

  1. Simplified examples - removed all helper functions, inlined directly in render()
  2. Removed section comments (// === Types ===, etc.)
  3. Removed DEFAULT_INPUT_GROUP_ID constant and custom id
  4. Removed invalid state and related API
  5. Removed Default trait implementation
  6. Fixed disabled style - now uses muted background + overlay to block all interactions (mouse, keyboard, scroll)
  7. Added helper methods: align(), block_end(), block_start(), inline_end(), inline_start() in alphabetical order
  8. Removed InputGroupText::label() method
  9. Simplified types: Entity<InputState> instead of full paths
  10. Removed InputGroupTextarea::height() method
  11. Updated all doc examples to reflect API changes
  12. Removed cursor_text() from addon to avoid wrong cursor affordance

⚠️ Two Items Need Discussion

1. Padding methods (line 241-252)
Current implementation uses individual pl/pr/pt/pb because different alignments need different padding values. A single p() method cannot handle tuples. Is this acceptable, or would you prefer a different approach?

2. min_w_0() (line 135)
The linter removed this, but it's needed to prevent flex overflow. Should I restore it with a comment, or handle differently?

All changes have been tested and compilation verified.

@huacnlee
Copy link
Copy Markdown
Member

huacnlee commented Feb 6, 2026

Sorry, I have consider about this PR more time. But I still not sure this is should be accept.

Because the Input is already have prefix, sufixx can support things like InputGroup. The example of InputGroup is pretty like the Input example.

So I just close this, untill when I have new mind.

@huacnlee huacnlee closed this Feb 6, 2026
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.

2 participants