chrore: Refactor the disabled API for NumberInput, DatePicker, OtpInput, Calandar. (#1102)
- Added `disabled` method to OtpInput.
- Added `disabled` method to DatePicker.
- Added `disabled_matcher` method to CalendarState.
## Break Changes
- The `InputState` has been removed `disabled` and `set_disabled`
method, the disabled state should assign from TextInput element.
```diff
- let state = InputState::new("input1").disabled(true)
+ let state = InputState::new("input1");
+ TextInput::new(&state).disabled(true)
```
- Renamed `set_disabled` method to `set_disabled_matcher` from
`CalendarState`.
- Removed `set_disabled` method from `DatePickerState`, use
`disabled_matcher` method in `DatePicker` element.
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
parent
c07bc4aaad
commit
7487662343
9 changed files with 167 additions and 82 deletions
|
|
@ -13,6 +13,7 @@ pub struct CalendarStory {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
calendar: Entity<CalendarState>,
|
calendar: Entity<CalendarState>,
|
||||||
calendar_wide: Entity<CalendarState>,
|
calendar_wide: Entity<CalendarState>,
|
||||||
|
calendar_with_disabled_matcher: Entity<CalendarState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Story for CalendarStory {
|
impl super::Story for CalendarStory {
|
||||||
|
|
@ -37,10 +38,13 @@ impl CalendarStory {
|
||||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let calendar = cx.new(|cx| CalendarState::new(window, cx));
|
let calendar = cx.new(|cx| CalendarState::new(window, cx));
|
||||||
let calendar_wide = cx.new(|cx| CalendarState::new(window, cx));
|
let calendar_wide = cx.new(|cx| CalendarState::new(window, cx));
|
||||||
|
let calendar_with_disabled_matcher =
|
||||||
|
cx.new(|cx| CalendarState::new(window, cx).disabled_matcher(vec![0, 3, 6]));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
calendar,
|
calendar,
|
||||||
calendar_wide,
|
calendar_wide,
|
||||||
|
calendar_with_disabled_matcher,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,5 +70,10 @@ impl Render for CalendarStory {
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(Calendar::new(&self.calendar_wide).number_of_months(3)),
|
.child(Calendar::new(&self.calendar_wide).number_of_months(3)),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
section("With Disabled matcher (Sundays, Wednesdays, Saturdays)")
|
||||||
|
.max_w_md()
|
||||||
|
.child(Calendar::new(&self.calendar_with_disabled_matcher)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,18 +45,17 @@ impl DatePickerStory {
|
||||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let now = chrono::Local::now().naive_local().date();
|
let now = chrono::Local::now().naive_local().date();
|
||||||
let date_picker = cx.new(|cx| {
|
let date_picker = cx.new(|cx| {
|
||||||
let mut picker = DatePickerState::new(window, cx);
|
let mut picker = DatePickerState::new(window, cx).disabled_matcher(vec![0, 6]);
|
||||||
picker.set_date(now, window, cx);
|
picker.set_date(now, window, cx);
|
||||||
picker.set_disabled(vec![0, 6], window, cx);
|
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
let date_picker_large = cx.new(|cx| {
|
let date_picker_large = cx.new(|cx| {
|
||||||
let mut picker = DatePickerState::new(window, cx).date_format("%Y-%m-%d");
|
let mut picker = DatePickerState::new(window, cx)
|
||||||
picker.set_disabled(
|
.date_format("%Y-%m-%d")
|
||||||
calendar::Matcher::range(Some(now), now.checked_add_days(Days::new(7))),
|
.disabled_matcher(calendar::Matcher::range(
|
||||||
window,
|
Some(now),
|
||||||
cx,
|
now.checked_add_days(Days::new(7)),
|
||||||
);
|
));
|
||||||
picker.set_date(
|
picker.set_date(
|
||||||
now.checked_sub_days(Days::new(1)).unwrap_or_default(),
|
now.checked_sub_days(Days::new(1)).unwrap_or_default(),
|
||||||
window,
|
window,
|
||||||
|
|
@ -65,22 +64,15 @@ impl DatePickerStory {
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
let date_picker_small = cx.new(|cx| {
|
let date_picker_small = cx.new(|cx| {
|
||||||
let mut picker = DatePickerState::new(window, cx);
|
let mut picker = DatePickerState::new(window, cx).disabled_matcher(
|
||||||
picker.set_disabled(
|
|
||||||
calendar::Matcher::interval(Some(now), now.checked_add_days(Days::new(5))),
|
calendar::Matcher::interval(Some(now), now.checked_add_days(Days::new(5))),
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
);
|
||||||
picker.set_date(now, window, cx);
|
picker.set_date(now, window, cx);
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
let data_picker_custom = cx.new(|cx| {
|
let data_picker_custom = cx.new(|cx| {
|
||||||
let mut picker = DatePickerState::new(window, cx);
|
let mut picker = DatePickerState::new(window, cx)
|
||||||
picker.set_disabled(
|
.disabled_matcher(calendar::Matcher::custom(|date| date.day0() < 5));
|
||||||
calendar::Matcher::custom(|date| date.day0() < 5),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
picker.set_date(now, window, cx);
|
picker.set_date(now, window, cx);
|
||||||
picker
|
picker
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::{section, Tab, TabPrev};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
button::{Button, ButtonVariants},
|
button::{Button, ButtonVariants},
|
||||||
input::{InputEvent, InputState, MaskPattern, NumberInput, NumberInputEvent, StepAction},
|
input::{InputEvent, InputState, MaskPattern, NumberInput, NumberInputEvent, StepAction},
|
||||||
v_flex, ActiveTheme, FocusableCycle, IconName, Sizable,
|
v_flex, ActiveTheme, Disableable, FocusableCycle, IconName, Sizable,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONTEXT: &str = "NumberInputStory";
|
const CONTEXT: &str = "NumberInputStory";
|
||||||
|
|
@ -29,6 +29,7 @@ pub struct NumberInputStory {
|
||||||
number_input3_value: f64,
|
number_input3_value: f64,
|
||||||
number_input4: Entity<InputState>,
|
number_input4: Entity<InputState>,
|
||||||
number_input4_value: f64,
|
number_input4_value: f64,
|
||||||
|
disabled_input: Entity<InputState>,
|
||||||
|
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +89,12 @@ impl NumberInputStory {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let disabled_input = cx.new(|cx| {
|
||||||
|
InputState::new(window, cx)
|
||||||
|
.default_value("100")
|
||||||
|
.placeholder("Disabled input")
|
||||||
|
});
|
||||||
|
|
||||||
let _subscriptions = vec![
|
let _subscriptions = vec![
|
||||||
cx.subscribe_in(&number_input1, window, Self::on_input_event),
|
cx.subscribe_in(&number_input1, window, Self::on_input_event),
|
||||||
cx.subscribe_in(&number_input1, window, Self::on_number_input_event),
|
cx.subscribe_in(&number_input1, window, Self::on_number_input_event),
|
||||||
|
|
@ -97,6 +104,8 @@ impl NumberInputStory {
|
||||||
cx.subscribe_in(&number_input3, window, Self::on_number_input_event),
|
cx.subscribe_in(&number_input3, window, Self::on_number_input_event),
|
||||||
cx.subscribe_in(&number_input4, window, Self::on_input_event),
|
cx.subscribe_in(&number_input4, window, Self::on_input_event),
|
||||||
cx.subscribe_in(&number_input4, window, Self::on_number_input_event),
|
cx.subscribe_in(&number_input4, window, Self::on_number_input_event),
|
||||||
|
cx.subscribe_in(&disabled_input, window, Self::on_input_event),
|
||||||
|
cx.subscribe_in(&disabled_input, window, Self::on_number_input_event),
|
||||||
];
|
];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -108,6 +117,7 @@ impl NumberInputStory {
|
||||||
number_input3_value: 0.0,
|
number_input3_value: 0.0,
|
||||||
number_input4,
|
number_input4,
|
||||||
number_input4_value: 0.0,
|
number_input4_value: 0.0,
|
||||||
|
disabled_input,
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -238,6 +248,11 @@ impl Render for NumberInputStory {
|
||||||
.max_w_md()
|
.max_w_md()
|
||||||
.child(NumberInput::new(&self.number_input1)),
|
.child(NumberInput::new(&self.number_input1)),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
section("Disabled")
|
||||||
|
.max_w_md()
|
||||||
|
.child(NumberInput::new(&self.disabled_input).disabled(true)),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Small Size with suffix").max_w_md().child(
|
section("Small Size with suffix").max_w_md().child(
|
||||||
NumberInput::new(&self.number_input2)
|
NumberInput::new(&self.number_input2)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use gpui_component::{
|
||||||
checkbox::Checkbox,
|
checkbox::Checkbox,
|
||||||
h_flex,
|
h_flex,
|
||||||
input::{InputEvent, OtpInput, OtpState},
|
input::{InputEvent, OtpInput, OtpState},
|
||||||
v_flex, FocusableCycle, Sizable, StyledExt,
|
v_flex, Disableable as _, FocusableCycle, Sizable, StyledExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{section, Tab, TabPrev};
|
use crate::{section, Tab, TabPrev};
|
||||||
|
|
@ -28,6 +28,7 @@ pub struct OtpInputStory {
|
||||||
otp_state_small: Entity<OtpState>,
|
otp_state_small: Entity<OtpState>,
|
||||||
otp_state_large: Entity<OtpState>,
|
otp_state_large: Entity<OtpState>,
|
||||||
otp_state_sized: Entity<OtpState>,
|
otp_state_sized: Entity<OtpState>,
|
||||||
|
otp_state_disabled: Entity<OtpState>,
|
||||||
|
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +89,11 @@ impl OtpInputStory {
|
||||||
.masked(true)
|
.masked(true)
|
||||||
.default_value("654321")
|
.default_value("654321")
|
||||||
}),
|
}),
|
||||||
|
otp_state_disabled: cx.new(|cx| {
|
||||||
|
OtpState::new(6, window, cx)
|
||||||
|
.masked(true)
|
||||||
|
.default_value("123456")
|
||||||
|
}),
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,17 +108,20 @@ impl OtpInputStory {
|
||||||
|
|
||||||
fn toggle_opt_masked(&mut self, _: &bool, window: &mut Window, cx: &mut Context<Self>) {
|
fn toggle_opt_masked(&mut self, _: &bool, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.otp_masked = !self.otp_masked;
|
self.otp_masked = !self.otp_masked;
|
||||||
self.otp_state.update(cx, |input, cx| {
|
self.otp_state.update(cx, |state, cx| {
|
||||||
input.set_masked(self.otp_masked, window, cx)
|
state.set_masked(self.otp_masked, window, cx)
|
||||||
});
|
});
|
||||||
self.otp_state_small.update(cx, |input, cx| {
|
self.otp_state_small.update(cx, |state, cx| {
|
||||||
input.set_masked(self.otp_masked, window, cx)
|
state.set_masked(self.otp_masked, window, cx)
|
||||||
});
|
});
|
||||||
self.otp_state_large.update(cx, |input, cx| {
|
self.otp_state_large.update(cx, |state, cx| {
|
||||||
input.set_masked(self.otp_masked, window, cx)
|
state.set_masked(self.otp_masked, window, cx)
|
||||||
});
|
});
|
||||||
self.otp_state_sized.update(cx, |input, cx| {
|
self.otp_state_sized.update(cx, |state, cx| {
|
||||||
input.set_masked(self.otp_masked, window, cx)
|
state.set_masked(self.otp_masked, window, cx)
|
||||||
|
});
|
||||||
|
self.otp_state_disabled.update(cx, |state, cx| {
|
||||||
|
state.set_masked(self.otp_masked, window, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,5 +171,8 @@ impl Render for OtpInputStory {
|
||||||
.with_size(px(55.)),
|
.with_size(px(55.)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
section("Disabled").child(OtpInput::new(&self.otp_state_disabled).disabled(true)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use gpui::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
button::{Button, ButtonVariants as _},
|
button::{Button, ButtonVariants as _},
|
||||||
h_flex, ActiveTheme, IconName, Sizable, Size, StyleSized, StyledExt as _,
|
h_flex, ActiveTheme, Disableable, IconName, Sizable, Size, StyleSized, StyledExt as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{InputState, TextInput};
|
use super::{InputState, TextInput};
|
||||||
|
|
@ -30,6 +30,7 @@ pub struct NumberInput {
|
||||||
prefix: Option<AnyElement>,
|
prefix: Option<AnyElement>,
|
||||||
suffix: Option<AnyElement>,
|
suffix: Option<AnyElement>,
|
||||||
appearance: bool,
|
appearance: bool,
|
||||||
|
disabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NumberInput {
|
impl NumberInput {
|
||||||
|
|
@ -42,6 +43,7 @@ impl NumberInput {
|
||||||
prefix: None,
|
prefix: None,
|
||||||
suffix: None,
|
suffix: None,
|
||||||
appearance: true,
|
appearance: true,
|
||||||
|
disabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,6 +86,13 @@ impl NumberInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Disableable for NumberInput {
|
||||||
|
fn disabled(mut self, disabled: bool) -> Self {
|
||||||
|
self.disabled = disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl InputState {
|
impl InputState {
|
||||||
fn on_action_increment(&mut self, _: &Increment, window: &mut Window, cx: &mut Context<Self>) {
|
fn on_action_increment(&mut self, _: &Increment, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.on_number_input_step(StepAction::Increment, window, cx);
|
self.on_number_input_step(StepAction::Increment, window, cx);
|
||||||
|
|
@ -142,6 +151,7 @@ impl RenderOnce for NumberInput {
|
||||||
.border_1()
|
.border_1()
|
||||||
.rounded(cx.theme().radius)
|
.rounded(cx.theme().radius)
|
||||||
})
|
})
|
||||||
|
.when(self.disabled, |this| this.bg(cx.theme().muted))
|
||||||
.when(focused, |this| this.focused_border(cx))
|
.when(focused, |this| this.focused_border(cx))
|
||||||
.child(
|
.child(
|
||||||
Button::new("minus")
|
Button::new("minus")
|
||||||
|
|
@ -149,6 +159,7 @@ impl RenderOnce for NumberInput {
|
||||||
.with_size(self.size.smaller())
|
.with_size(self.size.smaller())
|
||||||
.icon(IconName::Minus)
|
.icon(IconName::Minus)
|
||||||
.compact()
|
.compact()
|
||||||
|
.disabled(self.disabled)
|
||||||
.on_click({
|
.on_click({
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
move |_, window, cx| {
|
move |_, window, cx| {
|
||||||
|
|
@ -159,6 +170,7 @@ impl RenderOnce for NumberInput {
|
||||||
.child(
|
.child(
|
||||||
TextInput::new(&self.state)
|
TextInput::new(&self.state)
|
||||||
.appearance(false)
|
.appearance(false)
|
||||||
|
.disabled(self.disabled)
|
||||||
.px(px(2.))
|
.px(px(2.))
|
||||||
.gap_0()
|
.gap_0()
|
||||||
.when_some(self.prefix, |this, prefix| this.prefix(prefix))
|
.when_some(self.prefix, |this, prefix| this.prefix(prefix))
|
||||||
|
|
@ -170,6 +182,7 @@ impl RenderOnce for NumberInput {
|
||||||
.with_size(self.size.smaller())
|
.with_size(self.size.smaller())
|
||||||
.icon(IconName::Plus)
|
.icon(IconName::Plus)
|
||||||
.compact()
|
.compact()
|
||||||
|
.disabled(self.disabled)
|
||||||
.on_click({
|
.on_click({
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
move |_, window, cx| {
|
move |_, window, cx| {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use gpui::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{blink_cursor::BlinkCursor, InputEvent};
|
use super::{blink_cursor::BlinkCursor, InputEvent};
|
||||||
use crate::{h_flex, v_flex, ActiveTheme, Icon, IconName, Sizable, Size};
|
use crate::{h_flex, v_flex, ActiveTheme, Disableable, Icon, IconName, Sizable, Size};
|
||||||
|
|
||||||
pub struct OtpState {
|
pub struct OtpState {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
|
|
@ -183,6 +183,7 @@ pub struct OtpInput {
|
||||||
state: Entity<OtpState>,
|
state: Entity<OtpState>,
|
||||||
number_of_groups: usize,
|
number_of_groups: usize,
|
||||||
size: Size,
|
size: Size,
|
||||||
|
disabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OtpInput {
|
impl OtpInput {
|
||||||
|
|
@ -192,6 +193,7 @@ impl OtpInput {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
number_of_groups: 2,
|
number_of_groups: 2,
|
||||||
size: Size::Medium,
|
size: Size::Medium,
|
||||||
|
disabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +203,12 @@ impl OtpInput {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Disableable for OtpInput {
|
||||||
|
fn disabled(mut self, disabled: bool) -> Self {
|
||||||
|
self.disabled = disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Sizable for OtpInput {
|
impl Sizable for OtpInput {
|
||||||
fn with_size(mut self, size: impl Into<crate::Size>) -> Self {
|
fn with_size(mut self, size: impl Into<crate::Size>) -> Self {
|
||||||
self.size = size.into();
|
self.size = size.into();
|
||||||
|
|
@ -248,6 +255,10 @@ impl RenderOnce for OtpInput {
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().input)
|
.border_color(cx.theme().input)
|
||||||
.bg(cx.theme().background)
|
.bg(cx.theme().background)
|
||||||
|
.when(self.disabled, |this| {
|
||||||
|
this.bg(cx.theme().muted)
|
||||||
|
.text_color(cx.theme().muted_foreground)
|
||||||
|
})
|
||||||
.when(is_input_focused, |this| this.border_color(cx.theme().ring))
|
.when(is_input_focused, |this| this.border_color(cx.theme().ring))
|
||||||
.when(cx.theme().shadow, |this| this.shadow_xs())
|
.when(cx.theme().shadow, |this| this.shadow_xs())
|
||||||
.items_center()
|
.items_center()
|
||||||
|
|
@ -271,6 +282,9 @@ impl RenderOnce for OtpInput {
|
||||||
this.child(
|
this.child(
|
||||||
Icon::new(IconName::Asterisk)
|
Icon::new(IconName::Asterisk)
|
||||||
.text_color(cx.theme().secondary_foreground)
|
.text_color(cx.theme().secondary_foreground)
|
||||||
|
.when(self.disabled, |this| {
|
||||||
|
this.text_color(cx.theme().muted_foreground)
|
||||||
|
})
|
||||||
.with_size(text_size),
|
.with_size(text_size),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -294,7 +308,9 @@ impl RenderOnce for OtpInput {
|
||||||
v_flex()
|
v_flex()
|
||||||
.id(("otp-input", self.state.entity_id()))
|
.id(("otp-input", self.state.entity_id()))
|
||||||
.track_focus(&self.state.read(cx).focus_handle)
|
.track_focus(&self.state.read(cx).focus_handle)
|
||||||
.on_key_down(window.listener_for(&self.state, OtpState::on_key_down))
|
.when(!self.disabled, |this| {
|
||||||
|
this.on_key_down(window.listener_for(&self.state, OtpState::on_key_down))
|
||||||
|
})
|
||||||
.items_center()
|
.items_center()
|
||||||
.child(
|
.child(
|
||||||
h_flex().items_center().gap_5().children(
|
h_flex().items_center().gap_5().children(
|
||||||
|
|
|
||||||
|
|
@ -723,24 +723,11 @@ impl InputState {
|
||||||
/// Set with disabled mode.
|
/// Set with disabled mode.
|
||||||
///
|
///
|
||||||
/// See also: [`Self::set_disabled`], [`Self::is_disabled`].
|
/// See also: [`Self::set_disabled`], [`Self::is_disabled`].
|
||||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
pub(crate) fn disabled(mut self, disabled: bool) -> Self {
|
||||||
self.disabled = disabled;
|
self.disabled = disabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the disabled state of the input field.
|
|
||||||
///
|
|
||||||
/// See also: [`Self::disabled`], [`Self::is_disabled`].
|
|
||||||
pub fn set_disabled(&mut self, disabled: bool, _: &mut Window, cx: &mut Context<Self>) {
|
|
||||||
self.disabled = disabled;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return is the input field is disabled.
|
|
||||||
pub fn is_disabled(&self) -> bool {
|
|
||||||
self.disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set with password masked state.
|
/// Set with password masked state.
|
||||||
pub fn masked(mut self, masked: bool) -> Self {
|
pub fn masked(mut self, masked: bool) -> Self {
|
||||||
self.masked = masked;
|
self.masked = masked;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, rc::Rc};
|
||||||
|
|
||||||
use chrono::{Datelike, Local, NaiveDate};
|
use chrono::{Datelike, Local, NaiveDate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
|
@ -248,6 +248,7 @@ impl Matcher {
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Calendar {
|
pub struct Calendar {
|
||||||
|
id: ElementId,
|
||||||
size: Size,
|
size: Size,
|
||||||
state: Entity<CalendarState>,
|
state: Entity<CalendarState>,
|
||||||
style: StyleRefinement,
|
style: StyleRefinement,
|
||||||
|
|
@ -267,7 +268,7 @@ pub struct CalendarState {
|
||||||
today: NaiveDate,
|
today: NaiveDate,
|
||||||
/// Number of the months view to show.
|
/// Number of the months view to show.
|
||||||
number_of_months: usize,
|
number_of_months: usize,
|
||||||
disabled: Option<Matcher>,
|
pub(crate) disabled_matcher: Option<Rc<Matcher>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CalendarState {
|
impl CalendarState {
|
||||||
|
|
@ -283,11 +284,29 @@ impl CalendarState {
|
||||||
year_page: 0,
|
year_page: 0,
|
||||||
today,
|
today,
|
||||||
number_of_months: 1,
|
number_of_months: 1,
|
||||||
disabled: None,
|
disabled_matcher: None,
|
||||||
}
|
}
|
||||||
.year_range((today.year() - 50, today.year() + 50))
|
.year_range((today.year() - 50, today.year() + 50))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the disabled matcher of the calendar state.
|
||||||
|
pub fn disabled_matcher(mut self, matcher: impl Into<Matcher>) -> Self {
|
||||||
|
self.disabled_matcher = Some(Rc::new(matcher.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the disabled matcher of the calendar.
|
||||||
|
///
|
||||||
|
/// The disabled matcher will be used to disable the days that match the matcher.
|
||||||
|
pub fn set_disabled_matcher(
|
||||||
|
&mut self,
|
||||||
|
disabled: impl Into<Matcher>,
|
||||||
|
_: &mut Window,
|
||||||
|
_: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.disabled_matcher = Some(Rc::new(disabled.into()));
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the date of the calendar.
|
/// Set the date of the calendar.
|
||||||
///
|
///
|
||||||
/// When you set a range date, the mode will be automatically set to `Mode::Range`.
|
/// When you set a range date, the mode will be automatically set to `Mode::Range`.
|
||||||
|
|
@ -295,9 +314,9 @@ impl CalendarState {
|
||||||
let date = date.into();
|
let date = date.into();
|
||||||
|
|
||||||
let invalid = self
|
let invalid = self
|
||||||
.disabled
|
.disabled_matcher
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |disabled| disabled.date_matched(&date));
|
.map_or(false, |matcher| matcher.date_matched(&date));
|
||||||
|
|
||||||
if invalid {
|
if invalid {
|
||||||
return;
|
return;
|
||||||
|
|
@ -356,13 +375,6 @@ impl CalendarState {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the disabled matcher of the calendar.
|
|
||||||
///
|
|
||||||
/// The disabled matcher will be used to disable the days that match the matcher.
|
|
||||||
pub fn set_disabled(&mut self, disabled: Matcher, _: &mut Window, _: &mut Context<Self>) {
|
|
||||||
self.disabled = Some(disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get year and month by offset month.
|
/// Get year and month by offset month.
|
||||||
fn offset_year_month(&self, offset_month: usize) -> (i32, u32) {
|
fn offset_year_month(&self, offset_month: usize) -> (i32, u32) {
|
||||||
let mut month = self.current_month as i32 + offset_month as i32;
|
let mut month = self.current_month as i32 + offset_month as i32;
|
||||||
|
|
@ -497,6 +509,7 @@ impl Render for CalendarState {
|
||||||
impl Calendar {
|
impl Calendar {
|
||||||
pub fn new(state: &Entity<CalendarState>) -> Self {
|
pub fn new(state: &Entity<CalendarState>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id: ("calendar", state.entity_id()).into(),
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
style: StyleRefinement::default(),
|
style: StyleRefinement::default(),
|
||||||
|
|
@ -527,7 +540,7 @@ impl Calendar {
|
||||||
let date = *d;
|
let date = *d;
|
||||||
let is_today = *d == state.today;
|
let is_today = *d == state.today;
|
||||||
let disabled = state
|
let disabled = state
|
||||||
.disabled
|
.disabled_matcher
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |disabled| disabled.matched(&date));
|
.map_or(false, |disabled| disabled.matched(&date));
|
||||||
|
|
||||||
|
|
@ -914,10 +927,12 @@ impl RenderOnce for Calendar {
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
let view_mode = self.state.read(cx).view_mode;
|
let view_mode = self.state.read(cx).view_mode;
|
||||||
let number_of_months = self.number_of_months;
|
let number_of_months = self.number_of_months;
|
||||||
self.state
|
self.state.update(cx, |state, _| {
|
||||||
.update(cx, |state, _| state.number_of_months = number_of_months);
|
state.number_of_months = number_of_months;
|
||||||
|
});
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
|
.id(self.id.clone())
|
||||||
.track_focus(&self.state.read(cx).focus_handle)
|
.track_focus(&self.state.read(cx).focus_handle)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().border)
|
.border_color(cx.theme().border)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
anchored, deferred, div, prelude::FluentBuilder as _, px, App, AppContext, Context, ElementId,
|
anchored, deferred, div, prelude::FluentBuilder as _, px, App, AppContext, Context, ElementId,
|
||||||
|
|
@ -12,7 +14,8 @@ use crate::{
|
||||||
button::{Button, ButtonVariants as _},
|
button::{Button, ButtonVariants as _},
|
||||||
h_flex,
|
h_flex,
|
||||||
input::clear_button,
|
input::clear_button,
|
||||||
v_flex, ActiveTheme, Icon, IconName, Sizable, Size, StyleSized as _, StyledExt as _,
|
v_flex, ActiveTheme, Disableable, Icon, IconName, Sizable, Size, StyleSized as _,
|
||||||
|
StyledExt as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::calendar::{Calendar, CalendarEvent, CalendarState, Date, Matcher};
|
use super::calendar::{Calendar, CalendarEvent, CalendarState, Date, Matcher};
|
||||||
|
|
@ -64,6 +67,7 @@ pub struct DatePickerState {
|
||||||
calendar: Entity<CalendarState>,
|
calendar: Entity<CalendarState>,
|
||||||
date_format: SharedString,
|
date_format: SharedString,
|
||||||
number_of_months: usize,
|
number_of_months: usize,
|
||||||
|
disabled_matcher: Option<Rc<Matcher>>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,6 +120,7 @@ impl DatePickerState {
|
||||||
open: false,
|
open: false,
|
||||||
date_format: "%Y/%m/%d".into(),
|
date_format: "%Y/%m/%d".into(),
|
||||||
number_of_months: 1,
|
number_of_months: 1,
|
||||||
|
disabled_matcher: None,
|
||||||
_subscriptions,
|
_subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,15 +159,17 @@ impl DatePickerState {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the disabled match for the calendar.
|
||||||
|
pub fn disabled_matcher(mut self, disabled: impl Into<Matcher>) -> Self {
|
||||||
|
self.disabled_matcher = Some(Rc::new(disabled.into()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the disabled matcher of the date picker.
|
/// Set the disabled matcher of the date picker.
|
||||||
pub fn set_disabled(
|
fn set_canlendar_disabled_matcher(&mut self, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
&mut self,
|
let matcher = self.disabled_matcher.clone();
|
||||||
disabled: impl Into<Matcher>,
|
self.calendar.update(cx, |state, _| {
|
||||||
window: &mut Window,
|
state.disabled_matcher = matcher;
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) {
|
|
||||||
self.calendar.update(cx, |view, cx| {
|
|
||||||
view.set_disabled(disabled.into(), window, cx);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,6 +246,7 @@ pub struct DatePicker {
|
||||||
number_of_months: usize,
|
number_of_months: usize,
|
||||||
presets: Option<Vec<DateRangePreset>>,
|
presets: Option<Vec<DateRangePreset>>,
|
||||||
appearance: bool,
|
appearance: bool,
|
||||||
|
disabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sizable for DatePicker {
|
impl Sizable for DatePicker {
|
||||||
|
|
@ -259,6 +267,13 @@ impl Styled for DatePicker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Disableable for DatePicker {
|
||||||
|
fn disabled(mut self, disabled: bool) -> Self {
|
||||||
|
self.disabled = disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Render for DatePickerState {
|
impl Render for DatePickerState {
|
||||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl gpui::IntoElement {
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl gpui::IntoElement {
|
||||||
Empty
|
Empty
|
||||||
|
|
@ -277,6 +292,7 @@ impl DatePicker {
|
||||||
number_of_months: 2,
|
number_of_months: 2,
|
||||||
presets: None,
|
presets: None,
|
||||||
appearance: true,
|
appearance: true,
|
||||||
|
disabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +329,10 @@ impl DatePicker {
|
||||||
|
|
||||||
impl RenderOnce for DatePicker {
|
impl RenderOnce for DatePicker {
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
|
self.state.update(cx, |state, cx| {
|
||||||
|
state.set_canlendar_disabled_matcher(window, cx);
|
||||||
|
});
|
||||||
|
|
||||||
// This for keep focus border style, when click on the popup.
|
// This for keep focus border style, when click on the popup.
|
||||||
let is_focused = self.focus_handle(cx).contains_focused(window, cx);
|
let is_focused = self.focus_handle(cx).contains_focused(window, cx);
|
||||||
let state = self.state.read(cx);
|
let state = self.state.read(cx);
|
||||||
|
|
@ -352,11 +372,15 @@ impl RenderOnce for DatePicker {
|
||||||
.rounded(cx.theme().radius)
|
.rounded(cx.theme().radius)
|
||||||
.when(cx.theme().shadow, |this| this.shadow_xs())
|
.when(cx.theme().shadow, |this| this.shadow_xs())
|
||||||
.when(is_focused, |this| this.focused_border(cx))
|
.when(is_focused, |this| this.focused_border(cx))
|
||||||
|
.when(self.disabled, |this| {
|
||||||
|
this.bg(cx.theme().muted)
|
||||||
|
.text_color(cx.theme().muted_foreground)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.input_text_size(self.size)
|
.input_text_size(self.size)
|
||||||
.input_size(self.size)
|
.input_size(self.size)
|
||||||
.when(!state.open, |this| {
|
.when(!state.open && !self.disabled, |this| {
|
||||||
this.on_click(
|
this.on_click(
|
||||||
window.listener_for(&self.state, DatePickerState::toggle_calendar),
|
window.listener_for(&self.state, DatePickerState::toggle_calendar),
|
||||||
)
|
)
|
||||||
|
|
@ -368,17 +392,19 @@ impl RenderOnce for DatePicker {
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(div().w_full().overflow_hidden().child(display_title))
|
.child(div().w_full().overflow_hidden().child(display_title))
|
||||||
.when(show_clean, |this| {
|
.when(!self.disabled, |this| {
|
||||||
this.child(clear_button(cx).on_click(
|
this.when(show_clean, |this| {
|
||||||
window.listener_for(&self.state, DatePickerState::clean),
|
this.child(clear_button(cx).on_click(
|
||||||
))
|
window.listener_for(&self.state, DatePickerState::clean),
|
||||||
})
|
))
|
||||||
.when(!show_clean, |this| {
|
})
|
||||||
this.child(
|
.when(!show_clean, |this| {
|
||||||
Icon::new(IconName::Calendar)
|
this.child(
|
||||||
.xsmall()
|
Icon::new(IconName::Calendar)
|
||||||
.text_color(cx.theme().muted_foreground),
|
.xsmall()
|
||||||
)
|
.text_color(cx.theme().muted_foreground),
|
||||||
|
)
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue