input, select, date_picker: Refactor cleanable to have argument and default to false. (#1506)
https://github.com/longbridge/gpui-component/pull/1502#issuecomment-3485354324 ## Break Change - The `DatePicker` and `Select` has changed `cleanable` default from `true` to `false`. - Updated `Input`, `DatePicker` and `Select` the `cleanable` to have a argument. ```diff - pub fn cleanable(mut self) + pub fn cleanable(mut self, cleanable: bool) ``` --------- Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
parent
522f3ced43
commit
a4e93132f1
12 changed files with 36 additions and 175 deletions
|
|
@ -1,12 +1,12 @@
|
||||||
use chrono::{Datelike, Days, Duration, Utc};
|
use chrono::{Datelike, Days, Duration, Utc};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, App, AppContext, Context, Entity, Focusable, IntoElement, ParentElement as _, Render,
|
App, AppContext, Context, Entity, Focusable, IntoElement, ParentElement as _, Render,
|
||||||
Styled as _, Subscription, Window,
|
Styled as _, Subscription, Window, div, px,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
calendar,
|
ActiveTheme as _, Sizable as _, calendar,
|
||||||
date_picker::{DatePicker, DatePickerEvent, DatePickerState, DateRangePreset},
|
date_picker::{DatePicker, DatePickerEvent, DatePickerState, DateRangePreset},
|
||||||
v_flex, ActiveTheme as _, Sizable as _,
|
v_flex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::section;
|
use crate::section;
|
||||||
|
|
@ -172,7 +172,7 @@ impl Render for DatePickerStory {
|
||||||
.child(
|
.child(
|
||||||
section("Normal").max_w_128().child(
|
section("Normal").max_w_128().child(
|
||||||
DatePicker::new(&self.date_picker)
|
DatePicker::new(&self.date_picker)
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.presets(presets),
|
.presets(presets),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -195,7 +195,7 @@ impl Render for DatePickerStory {
|
||||||
section("Date Range").max_w_128().child(
|
section("Date Range").max_w_128().child(
|
||||||
DatePicker::new(&self.date_range_picker)
|
DatePicker::new(&self.date_range_picker)
|
||||||
.number_of_months(2)
|
.number_of_months(2)
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.presets(range_presets.clone()),
|
.presets(range_presets.clone()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -203,7 +203,7 @@ impl Render for DatePickerStory {
|
||||||
section("Default Range Mode").max_w_128().child(
|
section("Default Range Mode").max_w_128().child(
|
||||||
DatePicker::new(&self.default_range_mode_picker)
|
DatePicker::new(&self.default_range_mode_picker)
|
||||||
.placeholder("Range mode picker")
|
.placeholder("Range mode picker")
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.presets(range_presets.clone()),
|
.presets(range_presets.clone()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -217,8 +217,7 @@ impl Render for DatePickerStory {
|
||||||
div().w_full().bg(cx.theme().secondary).child(
|
div().w_full().bg(cx.theme().secondary).child(
|
||||||
DatePicker::new(&self.without_appearance_picker)
|
DatePicker::new(&self.without_appearance_picker)
|
||||||
.appearance(false)
|
.appearance(false)
|
||||||
.placeholder("Without appearance")
|
.placeholder("Without appearance"),
|
||||||
.cleanable(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -153,32 +153,32 @@ impl Render for InputStory {
|
||||||
.child(
|
.child(
|
||||||
section("Normal Input")
|
section("Normal Input")
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(Input::new(&self.input1).cleanable())
|
.child(Input::new(&self.input1).cleanable(true))
|
||||||
.child(Input::new(&self.input2)),
|
.child(Input::new(&self.input2)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Input State")
|
section("Input State")
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(Input::new(&self.disabled_input).disabled(true))
|
.child(Input::new(&self.disabled_input).disabled(true))
|
||||||
.child(Input::new(&self.mask_input).mask_toggle().cleanable()),
|
.child(Input::new(&self.mask_input).mask_toggle().cleanable(true)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Prefix and Suffix")
|
section("Prefix and Suffix")
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(
|
.child(
|
||||||
Input::new(&self.prefix_input1)
|
Input::new(&self.prefix_input1)
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.prefix(Icon::new(IconName::Search).small()),
|
.prefix(Icon::new(IconName::Search).small()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Input::new(&self.both_input1)
|
Input::new(&self.both_input1)
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.prefix(div().child(Icon::new(IconName::Search).small()))
|
.prefix(div().child(Icon::new(IconName::Search).small()))
|
||||||
.suffix(Button::new("info").ghost().icon(IconName::Info).xsmall()),
|
.suffix(Button::new("info").ghost().icon(IconName::Info).xsmall()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
Input::new(&self.suffix_input1)
|
Input::new(&self.suffix_input1)
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.suffix(Button::new("info").ghost().icon(IconName::Info).xsmall()),
|
.suffix(Button::new("info").ghost().icon(IconName::Info).xsmall()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -219,13 +219,13 @@ impl Render for InputStory {
|
||||||
.child(
|
.child(
|
||||||
section("Input Size")
|
section("Input Size")
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(Input::new(&self.large_input).large().cleanable())
|
.child(Input::new(&self.large_input).large())
|
||||||
.child(Input::new(&self.small_input).small().cleanable()),
|
.child(Input::new(&self.small_input).small()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Cleanable and ESC to clean")
|
section("Cleanable and ESC to clean")
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(Input::new(&self.input_esc).cleanable()),
|
.child(Input::new(&self.input_esc).cleanable(true)),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Focused Input")
|
section("Focused Input")
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ impl Render for LabelStory {
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_x_3()
|
.gap_x_3()
|
||||||
.child(Input::new(&self.highlights_input).cleanable().w_1_3())
|
.child(Input::new(&self.highlights_input).w_1_3())
|
||||||
.child(
|
.child(
|
||||||
Checkbox::new("prefix")
|
Checkbox::new("prefix")
|
||||||
.label("Prefix")
|
.label("Prefix")
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ impl Render for Gallery {
|
||||||
.child(
|
.child(
|
||||||
Input::new(&self.search_input)
|
Input::new(&self.search_input)
|
||||||
.appearance(false)
|
.appearance(false)
|
||||||
.cleanable(),
|
.cleanable(true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ impl Render for SelectStory {
|
||||||
section("Select").max_w_128().child(
|
section("Select").max_w_128().child(
|
||||||
Select::new(&self.country_select)
|
Select::new(&self.country_select)
|
||||||
.search_placeholder("Search country by name or code")
|
.search_placeholder("Search country by name or code")
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.disabled(self.disabled),
|
.disabled(self.disabled),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,9 @@ impl Input {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set true to show the clear button when the input field is not empty.
|
/// Set whether to show the clear button when the input field is not empty, default is false.
|
||||||
pub fn cleanable(mut self) -> Self {
|
pub fn cleanable(mut self, cleanable: bool) -> Self {
|
||||||
self.cleanable = true;
|
self.cleanable = cleanable;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -635,7 +635,7 @@ where
|
||||||
Icon::new(IconName::Search)
|
Icon::new(IconName::Search)
|
||||||
.text_color(cx.theme().muted_foreground),
|
.text_color(cx.theme().muted_foreground),
|
||||||
)
|
)
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
.p_0()
|
.p_0()
|
||||||
.appearance(false),
|
.appearance(false),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ impl Default for SelectOptions {
|
||||||
style: StyleRefinement::default(),
|
style: StyleRefinement::default(),
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
icon: None,
|
icon: None,
|
||||||
cleanable: true,
|
cleanable: false,
|
||||||
placeholder: None,
|
placeholder: None,
|
||||||
title_prefix: None,
|
title_prefix: None,
|
||||||
empty: None,
|
empty: None,
|
||||||
|
|
@ -962,9 +962,9 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set true to show the clear button when the input field is not empty.
|
/// Set whether to show the clear button when the input field is not empty, default is false.
|
||||||
pub fn cleanable(mut self) -> Self {
|
pub fn cleanable(mut self, cleanable: bool) -> Self {
|
||||||
self.options.cleanable = true;
|
self.options.cleanable = cleanable;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ impl DatePicker {
|
||||||
Self {
|
Self {
|
||||||
id: ("date-picker", state.entity_id()).into(),
|
id: ("date-picker", state.entity_id()).into(),
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
cleanable: true,
|
cleanable: false,
|
||||||
placeholder: None,
|
placeholder: None,
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
style: StyleRefinement::default(),
|
style: StyleRefinement::default(),
|
||||||
|
|
@ -318,9 +318,9 @@ impl DatePicker {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set true to show the clear button when the input field is not empty.
|
/// Set whether to show the clear button when the input field is not empty, default is false.
|
||||||
pub fn cleanable(mut self) -> Self {
|
pub fn cleanable(mut self, cleanable: bool) -> Self {
|
||||||
self.cleanable = true;
|
self.cleanable = cleanable;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ DatePicker::new(&date_picker)
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
DatePicker::new(&date_picker)
|
DatePicker::new(&date_picker)
|
||||||
.cleanable() // Shows clear button when date is selected
|
.cleanable(true) // Show clear button when date is selected
|
||||||
```
|
```
|
||||||
|
|
||||||
### Different Sizes
|
### Different Sizes
|
||||||
|
|
@ -360,91 +360,6 @@ DatePicker::new(&date_picker)
|
||||||
.presets(quarterly_presets)
|
.presets(quarterly_presets)
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### DatePickerState
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| ---------------------------- | ------------------------------------------- |
|
|
||||||
| `new(window, cx)` | Create a single date picker state |
|
|
||||||
| `range(window, cx)` | Create a date range picker state |
|
|
||||||
| `date_format(format)` | Set display format (default: "%Y/%m/%d") |
|
|
||||||
| `number_of_months(count)` | Set calendar months to display (default: 1) |
|
|
||||||
| `disabled_matcher(matcher)` | Set date disabling rules |
|
|
||||||
| `date()` | Get current selected date |
|
|
||||||
| `set_date(date, window, cx)` | Set selected date programmatically |
|
|
||||||
| `focus_handle(cx)` | Get focus handle |
|
|
||||||
|
|
||||||
### DatePicker
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| ------------------------- | --------------------------------------- |
|
|
||||||
| `new(state)` | Create date picker with state entity |
|
|
||||||
| `placeholder(text)` | Set placeholder text |
|
|
||||||
| `cleanable()` | Show clear button when date selected |
|
|
||||||
| `presets(presets)` | Set quick selection presets |
|
|
||||||
| `number_of_months(count)` | Set months to display (overrides state) |
|
|
||||||
| `appearance(bool)` | Enable/disable default styling |
|
|
||||||
| `disabled(bool)` | Set disabled state |
|
|
||||||
| `large()` | Large size |
|
|
||||||
| `small()` | Small size |
|
|
||||||
|
|
||||||
### DateRangePreset
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| -------------------------- | ------------------------- |
|
|
||||||
| `single(label, date)` | Create single date preset |
|
|
||||||
| `range(label, start, end)` | Create date range preset |
|
|
||||||
|
|
||||||
### Date
|
|
||||||
|
|
||||||
| Variant | Description |
|
|
||||||
| --------------------------------------------- | --------------------- |
|
|
||||||
| `Single(Option<NaiveDate>)` | Single date selection |
|
|
||||||
| `Range(Option<NaiveDate>, Option<NaiveDate>)` | Date range selection |
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| -------------------- | ----------------------------------------- |
|
|
||||||
| `format(format_str)` | Format date(s) using chrono format string |
|
|
||||||
| `is_some()` | Check if date is selected |
|
|
||||||
| `is_complete()` | Check if selection is complete |
|
|
||||||
|
|
||||||
### Matcher
|
|
||||||
|
|
||||||
| Variant | Description |
|
|
||||||
| --------------------------- | ------------------------------------------------ |
|
|
||||||
| `DayOfWeek(Vec<u32>)` | Disable specific weekdays (0=Sunday, 6=Saturday) |
|
|
||||||
| `Interval(IntervalMatcher)` | Disable dates outside interval |
|
|
||||||
| `Range(RangeMatcher)` | Disable dates within range |
|
|
||||||
| `Custom(fn)` | Custom disable function |
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| ------------------------- | ----------------------- |
|
|
||||||
| `interval(before, after)` | Create interval matcher |
|
|
||||||
| `range(from, to)` | Create range matcher |
|
|
||||||
| `custom(fn)` | Create custom matcher |
|
|
||||||
|
|
||||||
### DatePickerEvent
|
|
||||||
|
|
||||||
| Event | Description |
|
|
||||||
| -------------- | ---------------------- |
|
|
||||||
| `Change(Date)` | Date selection changed |
|
|
||||||
|
|
||||||
## Format Strings
|
|
||||||
|
|
||||||
Common date format patterns using chrono format specifiers:
|
|
||||||
|
|
||||||
| Format | Example Output |
|
|
||||||
| ------------------- | ------------------------- |
|
|
||||||
| `%Y-%m-%d` | 2023-12-25 |
|
|
||||||
| `%m/%d/%Y` | 12/25/2023 |
|
|
||||||
| `%d/%m/%Y` | 25/12/2023 |
|
|
||||||
| `%B %d, %Y` | December 25, 2023 |
|
|
||||||
| `%b %d, %Y` | Dec 25, 2023 |
|
|
||||||
| `%d %b %Y` | 25 Dec 2023 |
|
|
||||||
| `%A, %B %d, %Y` | Monday, December 25, 2023 |
|
|
||||||
| `%Y 年 %m 月 %d 日` | 2023 年 12 月 25 日 |
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Event Date Picker
|
### Event Date Picker
|
||||||
|
|
@ -462,7 +377,7 @@ let event_date = cx.new(|cx| {
|
||||||
|
|
||||||
DatePicker::new(&event_date)
|
DatePicker::new(&event_date)
|
||||||
.placeholder("Choose event date")
|
.placeholder("Choose event date")
|
||||||
.cleanable()
|
.cleanable(true)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Booking System Date Range
|
### Booking System Date Range
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ Input::new(&input)
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
Input::new(&input)
|
Input::new(&input)
|
||||||
.cleanable() // Shows clear button when input has value
|
.cleanable(true) // Show clear button when input has value
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Prefix and Suffix
|
### With Prefix and Suffix
|
||||||
|
|
@ -60,7 +60,6 @@ use gpui_component::{Icon, IconName};
|
||||||
// With prefix icon
|
// With prefix icon
|
||||||
Input::new(&input)
|
Input::new(&input)
|
||||||
.prefix(Icon::new(IconName::Search).small())
|
.prefix(Icon::new(IconName::Search).small())
|
||||||
.cleanable()
|
|
||||||
|
|
||||||
// With suffix button
|
// With suffix button
|
||||||
Input::new(&input)
|
Input::new(&input)
|
||||||
|
|
@ -75,7 +74,6 @@ Input::new(&input)
|
||||||
Input::new(&input)
|
Input::new(&input)
|
||||||
.prefix(Icon::new(IconName::Search).small())
|
.prefix(Icon::new(IconName::Search).small())
|
||||||
.suffix(Button::new("btn").ghost().icon(IconName::Info).xsmall())
|
.suffix(Button::new("btn").ghost().icon(IconName::Info).xsmall())
|
||||||
.cleanable()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Password Input (Masked)
|
### Password Input (Masked)
|
||||||
|
|
@ -113,7 +111,7 @@ let input = cx.new(|cx|
|
||||||
.clean_on_escape() // Clear input when ESC is pressed
|
.clean_on_escape() // Clear input when ESC is pressed
|
||||||
);
|
);
|
||||||
|
|
||||||
Input::new(&input).cleanable()
|
Input::new(&input)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Input Validation
|
### Input Validation
|
||||||
|
|
@ -195,55 +193,6 @@ div()
|
||||||
.child(Input::new(&input).appearance(false))
|
.child(Input::new(&input).appearance(false))
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### InputState
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| ---------------------------- | -------------------------------------- |
|
|
||||||
| `new(window, cx)` | Create a new input state |
|
|
||||||
| `placeholder(str)` | Set placeholder text |
|
|
||||||
| `default_value(str)` | Set initial value |
|
|
||||||
| `masked(bool)` | Enable password masking |
|
|
||||||
| `clean_on_escape()` | Clear on ESC key |
|
|
||||||
| `validate(fn)` | Set validation function |
|
|
||||||
| `pattern(regex)` | Set regex pattern for validation |
|
|
||||||
| `mask_pattern(pattern)` | Set input mask pattern |
|
|
||||||
| `value()` | Get current value |
|
|
||||||
| `unmask_value()` | Get unmasked value (for masked inputs) |
|
|
||||||
| `set_value(str, window, cx)` | Set input value programmatically |
|
|
||||||
| `focus_handle(cx)` | Get focus handle |
|
|
||||||
|
|
||||||
### Input
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| ------------------ | ---------------------------------------- |
|
|
||||||
| `new(state)` | Create input with state entity |
|
|
||||||
| `prefix(el)` | Add prefix element |
|
|
||||||
| `suffix(el)` | Add suffix element |
|
|
||||||
| `cleanable()` | Show clear button |
|
|
||||||
| `mask_toggle()` | Show password toggle (for masked inputs) |
|
|
||||||
| `disabled(bool)` | Set disabled state |
|
|
||||||
| `appearance(bool)` | Enable/disable default styling |
|
|
||||||
| `large()` | Large size |
|
|
||||||
| `small()` | Small size |
|
|
||||||
|
|
||||||
### InputEvent
|
|
||||||
|
|
||||||
| Event | Description |
|
|
||||||
| -------------------------- | ----------------- |
|
|
||||||
| `Change` | Value changed |
|
|
||||||
| `PressEnter { secondary }` | Enter key pressed |
|
|
||||||
| `Focus` | Input focused |
|
|
||||||
| `Blur` | Input lost focus |
|
|
||||||
|
|
||||||
### MaskPattern
|
|
||||||
|
|
||||||
| Pattern | Description |
|
|
||||||
| -------------------------------- | ---------------------------------------------------- |
|
|
||||||
| String | Pattern string (9=digit, A=letter, #=digit required) |
|
|
||||||
| `Number { separator, fraction }` | Number with thousands separator |
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Search Input
|
### Search Input
|
||||||
|
|
@ -256,7 +205,6 @@ let search = cx.new(|cx|
|
||||||
|
|
||||||
Input::new(&search)
|
Input::new(&search)
|
||||||
.prefix(Icon::new(IconName::Search).small())
|
.prefix(Icon::new(IconName::Search).small())
|
||||||
.cleanable()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Currency Input
|
### Currency Input
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ Select::new(&state).disabled(true)
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
Select::new(&state)
|
Select::new(&state)
|
||||||
.cleanable() // Shows clear button when item is selected
|
.cleanable(true) // Show clear button when item is selected
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Appearance
|
### Custom Appearance
|
||||||
|
|
@ -257,7 +257,6 @@ let state = cx.new(|cx| {
|
||||||
Select::new(&state)
|
Select::new(&state)
|
||||||
.placeholder("Select language...")
|
.placeholder("Select language...")
|
||||||
.title_prefix("Language: ")
|
.title_prefix("Language: ")
|
||||||
.cleanable()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Country/Region Selector
|
### Country/Region Selector
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue