accordion: Fix toggle accordion item. (#749)
Close #740 
This commit is contained in:
parent
26b59ea23e
commit
aadcf4e1df
3 changed files with 87 additions and 44 deletions
35
crates/story/examples/accordion.rs
Normal file
35
crates/story/examples/accordion.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use gpui::*;
|
||||
use story::{AccordionStory, Assets};
|
||||
|
||||
pub struct Example {
|
||||
root: Entity<AccordionStory>,
|
||||
}
|
||||
|
||||
impl Example {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let root = AccordionStory::view(window, cx);
|
||||
|
||||
Self { root }
|
||||
}
|
||||
|
||||
fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
cx.new(|cx| Self::new(window, cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Example {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().p_4().size_full().child(self.root.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = Application::new().with_assets(Assets);
|
||||
|
||||
app.run(move |cx| {
|
||||
story::init(cx);
|
||||
cx.activate(true);
|
||||
|
||||
story::create_new_window("Accordion Example", Example::view, cx);
|
||||
});
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ pub struct AccordionStory {
|
|||
size: Size,
|
||||
bordered: bool,
|
||||
disabled: bool,
|
||||
multiple: bool,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ impl AccordionStory {
|
|||
open_ixs: Vec::new(),
|
||||
size: Size::default(),
|
||||
disabled: false,
|
||||
multiple: false,
|
||||
focus_handle: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +107,16 @@ impl Render for AccordionStory {
|
|||
this.set_size(size, window, cx);
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Checkbox::new("multiple")
|
||||
.label("Multiple")
|
||||
.checked(self.multiple)
|
||||
.on_click(cx.listener(|this, checked, _, cx| {
|
||||
this.multiple = *checked;
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
|
||||
.child(
|
||||
Checkbox::new("disabled")
|
||||
.label("Disabled")
|
||||
|
|
@ -129,6 +141,7 @@ impl Render for AccordionStory {
|
|||
.bordered(self.bordered)
|
||||
.with_size(self.size)
|
||||
.disabled(self.disabled)
|
||||
.multiple(self.multiple)
|
||||
.item(|this|
|
||||
this.open(self.open_ixs.contains(&0))
|
||||
.icon(IconName::Info)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::Cell, rc::Rc, sync::Arc};
|
||||
use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, rems, AnyElement, App, Div, ElementId,
|
||||
|
|
@ -80,18 +80,8 @@ impl Sizable for Accordion {
|
|||
|
||||
impl RenderOnce for Accordion {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let mut open_ixs: Vec<usize> = Vec::new();
|
||||
let multiple = self.multiple;
|
||||
let state = Rc::new(Cell::new(None));
|
||||
|
||||
self.children
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(ix, accordion)| {
|
||||
if accordion.open {
|
||||
open_ixs.push(ix);
|
||||
}
|
||||
});
|
||||
let open_ixs = Rc::new(RefCell::new(HashSet::new()));
|
||||
let is_multiple = self.multiple;
|
||||
|
||||
self.base
|
||||
.id(self.id)
|
||||
|
|
@ -100,36 +90,37 @@ impl RenderOnce for Accordion {
|
|||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(ix, accordion)| {
|
||||
let state = Rc::clone(&state);
|
||||
if accordion.open {
|
||||
open_ixs.borrow_mut().insert(ix);
|
||||
}
|
||||
|
||||
accordion
|
||||
.index(ix)
|
||||
.with_size(self.size)
|
||||
.bordered(self.bordered)
|
||||
.when(self.disabled, |this| this.disabled(true))
|
||||
.on_toggle_click(move |_, _, _| {
|
||||
state.set(Some(ix));
|
||||
.disabled(self.disabled)
|
||||
.on_toggle_click({
|
||||
let open_ixs = Rc::clone(&open_ixs);
|
||||
move |open, _, _| {
|
||||
let mut open_ixs = open_ixs.borrow_mut();
|
||||
if *open {
|
||||
if !is_multiple {
|
||||
open_ixs.clear();
|
||||
}
|
||||
open_ixs.insert(ix);
|
||||
} else {
|
||||
open_ixs.remove(&ix);
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
.when_some(
|
||||
self.on_toggle_click.filter(|_| !self.disabled),
|
||||
move |this, on_toggle_click| {
|
||||
let open_ixs = Rc::clone(&open_ixs);
|
||||
this.on_click(move |_, window, cx| {
|
||||
let mut open_ixs = open_ixs.clone();
|
||||
if let Some(ix) = state.get() {
|
||||
if multiple {
|
||||
if let Some(pos) = open_ixs.iter().position(|&i| i == ix) {
|
||||
open_ixs.remove(pos);
|
||||
} else {
|
||||
open_ixs.push(ix);
|
||||
}
|
||||
} else {
|
||||
let was_open = open_ixs.iter().any(|&i| i == ix);
|
||||
open_ixs.clear();
|
||||
if !was_open {
|
||||
open_ixs.push(ix);
|
||||
}
|
||||
}
|
||||
}
|
||||
let open_ixs: Vec<usize> = open_ixs.borrow().iter().map(|&ix| ix).collect();
|
||||
|
||||
on_toggle_click(&open_ixs, window, cx);
|
||||
})
|
||||
|
|
@ -141,6 +132,7 @@ impl RenderOnce for Accordion {
|
|||
/// An Accordion is a vertically stacked list of items, each of which can be expanded to reveal the content associated with it.
|
||||
#[derive(IntoElement)]
|
||||
pub struct AccordionItem {
|
||||
index: usize,
|
||||
icon: Option<Icon>,
|
||||
title: AnyElement,
|
||||
content: AnyElement,
|
||||
|
|
@ -154,6 +146,7 @@ pub struct AccordionItem {
|
|||
impl AccordionItem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
index: 0,
|
||||
icon: None,
|
||||
title: SharedString::default().into_any_element(),
|
||||
content: SharedString::default().into_any_element(),
|
||||
|
|
@ -165,6 +158,11 @@ impl AccordionItem {
|
|||
}
|
||||
}
|
||||
|
||||
fn index(mut self, index: usize) -> Self {
|
||||
self.index = index;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
|
||||
self.icon = Some(icon.into());
|
||||
self
|
||||
|
|
@ -230,7 +228,7 @@ impl RenderOnce for AccordionItem {
|
|||
.text_size(text_size)
|
||||
.child(
|
||||
h_flex()
|
||||
.id("accordion-title")
|
||||
.id(self.index)
|
||||
.justify_between()
|
||||
.map(|this| match self.size {
|
||||
Size::XSmall => this.py_0().px_1p5(),
|
||||
|
|
@ -274,17 +272,14 @@ impl RenderOnce for AccordionItem {
|
|||
.xsmall()
|
||||
.text_color(cx.theme().muted_foreground),
|
||||
)
|
||||
})
|
||||
.when_some(
|
||||
self.on_toggle_click.filter(|_| !self.disabled),
|
||||
|this, on_toggle_click| {
|
||||
this.on_click({
|
||||
move |_, window, cx| {
|
||||
on_toggle_click(&!self.open, window, cx);
|
||||
}
|
||||
.when_some(self.on_toggle_click, |this, on_toggle_click| {
|
||||
this.on_click({
|
||||
move |_, window, cx| {
|
||||
on_toggle_click(&!self.open, window, cx);
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
),
|
||||
}),
|
||||
)
|
||||
.when(self.open, |this| {
|
||||
this.child(
|
||||
|
|
|
|||
Loading…
Reference in a new issue