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,
|
size: Size,
|
||||||
bordered: bool,
|
bordered: bool,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
|
multiple: bool,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@ impl AccordionStory {
|
||||||
open_ixs: Vec::new(),
|
open_ixs: Vec::new(),
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
multiple: false,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +107,16 @@ impl Render for AccordionStory {
|
||||||
this.set_size(size, window, cx);
|
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(
|
.child(
|
||||||
Checkbox::new("disabled")
|
Checkbox::new("disabled")
|
||||||
.label("Disabled")
|
.label("Disabled")
|
||||||
|
|
@ -129,6 +141,7 @@ impl Render for AccordionStory {
|
||||||
.bordered(self.bordered)
|
.bordered(self.bordered)
|
||||||
.with_size(self.size)
|
.with_size(self.size)
|
||||||
.disabled(self.disabled)
|
.disabled(self.disabled)
|
||||||
|
.multiple(self.multiple)
|
||||||
.item(|this|
|
.item(|this|
|
||||||
this.open(self.open_ixs.contains(&0))
|
this.open(self.open_ixs.contains(&0))
|
||||||
.icon(IconName::Info)
|
.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::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, rems, AnyElement, App, Div, ElementId,
|
div, prelude::FluentBuilder as _, rems, AnyElement, App, Div, ElementId,
|
||||||
|
|
@ -80,18 +80,8 @@ impl Sizable for Accordion {
|
||||||
|
|
||||||
impl RenderOnce for Accordion {
|
impl RenderOnce for Accordion {
|
||||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||||
let mut open_ixs: Vec<usize> = Vec::new();
|
let open_ixs = Rc::new(RefCell::new(HashSet::new()));
|
||||||
let multiple = self.multiple;
|
let is_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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.base
|
self.base
|
||||||
.id(self.id)
|
.id(self.id)
|
||||||
|
|
@ -100,36 +90,37 @@ impl RenderOnce for Accordion {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(ix, accordion)| {
|
.map(|(ix, accordion)| {
|
||||||
let state = Rc::clone(&state);
|
if accordion.open {
|
||||||
|
open_ixs.borrow_mut().insert(ix);
|
||||||
|
}
|
||||||
|
|
||||||
accordion
|
accordion
|
||||||
|
.index(ix)
|
||||||
.with_size(self.size)
|
.with_size(self.size)
|
||||||
.bordered(self.bordered)
|
.bordered(self.bordered)
|
||||||
.when(self.disabled, |this| this.disabled(true))
|
.disabled(self.disabled)
|
||||||
.on_toggle_click(move |_, _, _| {
|
.on_toggle_click({
|
||||||
state.set(Some(ix));
|
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(
|
.when_some(
|
||||||
self.on_toggle_click.filter(|_| !self.disabled),
|
self.on_toggle_click.filter(|_| !self.disabled),
|
||||||
move |this, on_toggle_click| {
|
move |this, on_toggle_click| {
|
||||||
|
let open_ixs = Rc::clone(&open_ixs);
|
||||||
this.on_click(move |_, window, cx| {
|
this.on_click(move |_, window, cx| {
|
||||||
let mut open_ixs = open_ixs.clone();
|
let open_ixs: Vec<usize> = open_ixs.borrow().iter().map(|&ix| ix).collect();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on_toggle_click(&open_ixs, window, cx);
|
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.
|
/// An Accordion is a vertically stacked list of items, each of which can be expanded to reveal the content associated with it.
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct AccordionItem {
|
pub struct AccordionItem {
|
||||||
|
index: usize,
|
||||||
icon: Option<Icon>,
|
icon: Option<Icon>,
|
||||||
title: AnyElement,
|
title: AnyElement,
|
||||||
content: AnyElement,
|
content: AnyElement,
|
||||||
|
|
@ -154,6 +146,7 @@ pub struct AccordionItem {
|
||||||
impl AccordionItem {
|
impl AccordionItem {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
index: 0,
|
||||||
icon: None,
|
icon: None,
|
||||||
title: SharedString::default().into_any_element(),
|
title: SharedString::default().into_any_element(),
|
||||||
content: 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 {
|
pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
|
||||||
self.icon = Some(icon.into());
|
self.icon = Some(icon.into());
|
||||||
self
|
self
|
||||||
|
|
@ -230,7 +228,7 @@ impl RenderOnce for AccordionItem {
|
||||||
.text_size(text_size)
|
.text_size(text_size)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("accordion-title")
|
.id(self.index)
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
Size::XSmall => this.py_0().px_1p5(),
|
Size::XSmall => this.py_0().px_1p5(),
|
||||||
|
|
@ -274,17 +272,14 @@ impl RenderOnce for AccordionItem {
|
||||||
.xsmall()
|
.xsmall()
|
||||||
.text_color(cx.theme().muted_foreground),
|
.text_color(cx.theme().muted_foreground),
|
||||||
)
|
)
|
||||||
})
|
.when_some(self.on_toggle_click, |this, on_toggle_click| {
|
||||||
.when_some(
|
|
||||||
self.on_toggle_click.filter(|_| !self.disabled),
|
|
||||||
|this, on_toggle_click| {
|
|
||||||
this.on_click({
|
this.on_click({
|
||||||
move |_, window, cx| {
|
move |_, window, cx| {
|
||||||
on_toggle_click(&!self.open, window, cx);
|
on_toggle_click(&!self.open, window, cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
),
|
}),
|
||||||
)
|
)
|
||||||
.when(self.open, |this| {
|
.when(self.open, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue