1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Shared builder traits (HeroGPUI extension, not a HeroUI v3 API).
//!
//! HeroUI v3 spells `isDisabled`, `size` and `isSelected` on every component
//! that has them, and this port keeps those spellings as **inherent** builder
//! methods — the parity audits read them there. These traits add a common
//! name on top, so generic code can configure any component that supports the
//! prop without naming its concrete type:
//!
//! ```
//! use herogpui_components::{Button, Disableable, Sizable, Size, Switch};
//!
//! fn compact<T: Disableable + Sizable<Size = Size>>(component: T, busy: bool) -> T {
//! component.is_disabled(busy).size(Size::Sm)
//! }
//!
//! let _button = compact(Button::new("save").label("Save"), true);
//! let _switch = compact(Switch::new("wifi"), false);
//! ```
//!
//! Every implementation delegates to the inherent builder of the same name,
//! so a call through the trait and a direct call are the same code path.
//! The pattern follows gpui-kit's `Disableable`/`Sizable`/`Selectable`
//! (Apache-2.0, Longbridge), rewritten for HeroUI's `is_*` naming.
use crate::*;
/// A component with v3's `isDisabled` prop.
pub trait Disableable: Sized {
/// `isDisabled` — whether the component is disabled.
fn is_disabled(self, disabled: bool) -> Self;
}
/// A component with a `size` prop. [`Sizable::Size`] is the component's own
/// scale: HeroUI's [`Size`] for most, a component-specific enum where v3
/// defines one (for example [`ModalSize`]).
pub trait Sizable: Sized {
/// The size scale this component accepts.
type Size;
/// `size` — the component's size step.
fn size(self, size: Self::Size) -> Self;
}
/// A component with v3's controlled `isSelected` prop.
pub trait Selectable: Sized {
/// `isSelected` — the controlled selection state.
fn is_selected(self, selected: bool) -> Self;
}
macro_rules! disableable {
($($ty:ident),* $(,)?) => {$(
impl Disableable for $ty {
fn is_disabled(self, disabled: bool) -> Self {
$ty::is_disabled(self, disabled)
}
}
)*};
}
macro_rules! sizable {
($($ty:ident => $size:ty),* $(,)?) => {$(
impl Sizable for $ty {
type Size = $size;
fn size(self, size: $size) -> Self {
$ty::size(self, size)
}
}
)*};
}
macro_rules! selectable {
($($ty:ident),* $(,)?) => {$(
impl Selectable for $ty {
fn is_selected(self, selected: bool) -> Self {
$ty::is_selected(self, selected)
}
}
)*};
}
disableable!(
AccordionItem,
Accordion,
Autocomplete,
Breadcrumbs,
Button,
ButtonGroup,
Calendar,
Checkbox,
CheckboxOption,
CheckboxGroup,
CloseButton,
ColorArea,
ColorField,
ColorPicker,
ColorSlider,
ColorSwatch,
ColorSwatchPicker,
ComboBox,
DateField,
DatePicker,
DateRangePicker,
Disclosure,
DisclosureGroup,
Label,
Input,
TextField,
SearchField,
InputGroup,
InputOTP,
Link,
ListBoxItem,
NumberField,
Pagination,
RadioOption,
RadioGroup,
RangeCalendar,
ResizablePanelGroup,
Select,
Slider,
Switch,
TabItem,
Tabs,
Tag,
TagGroup,
TextArea,
TimeField,
ToggleButton,
ToggleButtonGroup,
Tooltip,
TreeItem,
TreeView,
);
sizable!(
AlertDialog => AlertDialogSize,
Avatar => Size,
AvatarGroupCount => Size,
AvatarGroup => Size,
Badge => Size,
Button => Size,
ButtonGroup => Size,
Checkbox => CheckboxSize,
Chip => Size,
ColorSwatch => SizeXl,
ColorSwatchPicker => SizeXl,
Meter => Size,
Modal => ModalSize,
Pagination => Size,
ProgressBar => Size,
ProgressCircle => Size,
RadioGroup => RadioSize,
Slider => SliderSize,
Spinner => SpinnerSize,
Switch => Size,
Tabs => TabsSize,
TagGroup => Size,
ToggleButton => Size,
ToggleButtonGroup => Size,
);
selectable!(Checkbox, Switch, ToggleButton);