cronixui 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/package.json +71 -71
- package/packages/flutter/.qwen/settings.json +7 -0
- package/packages/flutter/pubspec.yaml +20 -20
- package/packages/go/cronixui/cronixui.go +926 -926
- package/packages/python/README.md +142 -0
- package/packages/python/cronixui/__init__.py +15 -6
- package/packages/python/cronixui/__pycache__/__init__.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/accordion.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/alert.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/avatar.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/badge.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/button.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/card.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/command_palette.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/core.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/dropdown.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/form.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/layout.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/list.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/loading.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/modal.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/nav.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/pagination.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/progress.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/search.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/table.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/tabs.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/toast.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/toggle.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/tokens.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/tooltip.cpython-314.pyc +0 -0
- package/packages/python/cronixui/alert.py +119 -36
- package/packages/python/cronixui/avatar.py +129 -22
- package/packages/python/cronixui/badge.py +161 -24
- package/packages/python/cronixui/button.py +96 -27
- package/packages/python/cronixui/card.py +206 -33
- package/packages/python/cronixui/core.py +212 -23
- package/packages/python/cronixui/form.py +552 -141
- package/packages/python/cronixui/layout.py +358 -96
- package/packages/python/cronixui/list.py +140 -37
- package/packages/python/cronixui/loading.py +107 -17
- package/packages/python/cronixui/progress.py +189 -47
- package/packages/python/cronixui/table.py +118 -31
- package/packages/python/cronixui/tooltip.py +117 -15
- package/packages/react/src/components/Accordion.tsx +82 -82
- package/packages/react/src/components/Button.tsx +47 -47
- package/packages/react/src/components/Card.tsx +69 -69
- package/packages/react/src/components/CommandPalette.tsx +131 -131
- package/packages/react/src/components/Dropdown.tsx +88 -88
- package/packages/react/src/components/FileInput.tsx +86 -86
- package/packages/react/src/components/FormGroup.tsx +36 -36
- package/packages/react/src/components/List.tsx +55 -55
- package/packages/react/src/components/Pagination.tsx +107 -107
- package/packages/react/src/components/Progress.tsx +49 -49
- package/packages/react/src/components/Search.tsx +95 -95
- package/packages/react/src/components/Sidebar.tsx +64 -64
- package/packages/react/src/components/Stack.tsx +69 -69
- package/packages/react/src/components/Table.tsx +90 -90
- package/packages/react/src/components/Toast.tsx +134 -134
- package/packages/react/src/components/Typography.tsx +66 -66
- package/packages/react/src/index.ts +40 -40
- package/packages/react/src/styles.css +2039 -2039
- package/packages/rust/cronixui/src/components/avatar.rs +85 -85
- package/packages/rust/cronixui/src/components/breadcrumb.rs +58 -58
- package/packages/rust/cronixui/src/components/card.rs +259 -259
- package/packages/rust/cronixui/src/components/command_palette.rs +254 -254
- package/packages/rust/cronixui/src/components/dropdown.rs +179 -179
- package/packages/rust/cronixui/src/components/file_input.rs +74 -74
- package/packages/rust/cronixui/src/components/mod.rs +51 -51
- package/packages/rust/cronixui/src/components/search.rs +185 -185
- package/packages/rust/cronixui/src/components/skeleton.rs +63 -63
- package/packages/rust/cronixui/src/components/table.rs +56 -56
- package/packages/rust/cronixui/src/lib.rs +128 -128
- package/packages/web/dist/cronixui.css +97 -93
- package/packages/web/dist/cronixui.min.css +1 -1
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
//! Search component
|
|
2
|
-
|
|
3
|
-
use egui::*;
|
|
4
|
-
use crate::{colors::*, tokens::*};
|
|
5
|
-
|
|
6
|
-
pub struct SearchItem {
|
|
7
|
-
pub title: String,
|
|
8
|
-
pub subtitle: Option<String>,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
impl SearchItem {
|
|
12
|
-
pub fn new(title: impl Into<String>) -> Self {
|
|
13
|
-
Self { title: title.into(), subtitle: None }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
pub fn subtitle(mut self, subtitle: impl Into<String>) -> Self {
|
|
17
|
-
self.subtitle = Some(subtitle.into());
|
|
18
|
-
self
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
pub struct Search {
|
|
23
|
-
pub items: Vec<SearchItem>,
|
|
24
|
-
pub query: String,
|
|
25
|
-
id: Id,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
impl Search {
|
|
29
|
-
pub fn new(id: impl Into<Id>) -> Self {
|
|
30
|
-
Self { items: Vec::new(), query: String::new(), id: id.into() }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
pub fn item(mut self, item: SearchItem) -> Self {
|
|
34
|
-
self.items.push(item);
|
|
35
|
-
self
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
pub fn set_items(&mut self, items: Vec<SearchItem>) {
|
|
39
|
-
self.items = items;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
pub fn filter(&self) -> Vec<&SearchItem> {
|
|
43
|
-
if self.query.is_empty() {
|
|
44
|
-
return self.items.iter().collect();
|
|
45
|
-
}
|
|
46
|
-
self.items
|
|
47
|
-
.iter()
|
|
48
|
-
.filter(|item| item.title.to_lowercase().contains(&self.query.to_lowercase()))
|
|
49
|
-
.collect()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/// Render the search UI with text input and results list
|
|
53
|
-
pub fn show(&mut self, ui: &mut Ui) -> Option<usize> {
|
|
54
|
-
let colors = Colors::default();
|
|
55
|
-
let mut selected = None;
|
|
56
|
-
|
|
57
|
-
// Search input field
|
|
58
|
-
ui.horizontal(|ui| {
|
|
59
|
-
ui.label("🔍");
|
|
60
|
-
let response = ui.add(
|
|
61
|
-
TextEdit::singleline(&mut self.query)
|
|
62
|
-
.hint_text("Search...")
|
|
63
|
-
.desired_width(f32::INFINITY)
|
|
64
|
-
.font(FontId::new(tokens::FONT_SIZE_BASE, FontFamily::Proportional)),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
if response.has_focus() {
|
|
68
|
-
// Show results dropdown when focused
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Results list
|
|
73
|
-
let results = self.filter();
|
|
74
|
-
if !results.is_empty() {
|
|
75
|
-
egui::Frame::none()
|
|
76
|
-
.fill(colors.surface)
|
|
77
|
-
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
78
|
-
.rounding(Rounding::same(tokens::RADIUS))
|
|
79
|
-
.inner_margin(SPACE_2)
|
|
80
|
-
.show(ui, |ui| {
|
|
81
|
-
for (idx, item) in results.iter().enumerate() {
|
|
82
|
-
let response = ui.horizontal(|ui| {
|
|
83
|
-
ui.label(
|
|
84
|
-
RichText::new(&item.title)
|
|
85
|
-
.size(tokens::FONT_SIZE_BASE)
|
|
86
|
-
.color(colors.text),
|
|
87
|
-
);
|
|
88
|
-
if let Some(subtitle) = &item.subtitle {
|
|
89
|
-
ui.label(
|
|
90
|
-
RichText::new(subtitle)
|
|
91
|
-
.size(tokens::FONT_SIZE_SM)
|
|
92
|
-
.color(colors.text_muted),
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if response.response.clicked() {
|
|
98
|
-
// Find original index
|
|
99
|
-
if let Some(orig_idx) = self.items.iter().position(|i| std::ptr::eq(i, *item)) {
|
|
100
|
-
selected = Some(orig_idx);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
} else if !self.query.is_empty() {
|
|
106
|
-
ui.label(
|
|
107
|
-
RichText::new("No results found")
|
|
108
|
-
.size(tokens::FONT_SIZE_SM)
|
|
109
|
-
.color(colors.text_muted),
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
selected
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
impl Default for Search {
|
|
118
|
-
fn default() -> Self {
|
|
119
|
-
Self::new("default_search")
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/// Functional search helper
|
|
124
|
-
pub fn search(ui: &mut Ui, query: &mut String, items: &[SearchItem]) -> Vec<&SearchItem> {
|
|
125
|
-
let colors = Colors::default();
|
|
126
|
-
|
|
127
|
-
ui.add(
|
|
128
|
-
TextEdit::singleline(query)
|
|
129
|
-
.hint_text("Search...")
|
|
130
|
-
.desired_width(f32::INFINITY),
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if query.is_empty() {
|
|
134
|
-
return items.iter().collect();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
items
|
|
138
|
-
.iter()
|
|
139
|
-
.filter(|item| item.title.to_lowercase().contains(&query.to_lowercase()))
|
|
140
|
-
.collect()
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/// Render search results list
|
|
144
|
-
pub fn search_results(ui: &mut Ui, results: &[&SearchItem]) -> Option<usize> {
|
|
145
|
-
let colors = Colors::default();
|
|
146
|
-
let mut clicked = None;
|
|
147
|
-
|
|
148
|
-
if !results.is_empty() {
|
|
149
|
-
egui::Frame::none()
|
|
150
|
-
.fill(colors.surface)
|
|
151
|
-
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
152
|
-
.rounding(Rounding::same(tokens::RADIUS))
|
|
153
|
-
.inner_margin(SPACE_2)
|
|
154
|
-
.show(ui, |ui| {
|
|
155
|
-
for (idx, item) in results.iter().enumerate() {
|
|
156
|
-
let response = ui.horizontal(|ui| {
|
|
157
|
-
ui.label(
|
|
158
|
-
RichText::new(&item.title)
|
|
159
|
-
.size(tokens::FONT_SIZE_BASE)
|
|
160
|
-
.color(colors.text),
|
|
161
|
-
);
|
|
162
|
-
if let Some(subtitle) = &item.subtitle {
|
|
163
|
-
ui.label(
|
|
164
|
-
RichText::new(subtitle)
|
|
165
|
-
.size(tokens::FONT_SIZE_SM)
|
|
166
|
-
.color(colors.text_muted),
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
if response.response.clicked() {
|
|
172
|
-
clicked = Some(idx);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
} else {
|
|
177
|
-
ui.label(
|
|
178
|
-
RichText::new("No results found")
|
|
179
|
-
.size(tokens::FONT_SIZE_SM)
|
|
180
|
-
.color(colors.text_muted),
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
clicked
|
|
185
|
-
}
|
|
1
|
+
//! Search component
|
|
2
|
+
|
|
3
|
+
use egui::*;
|
|
4
|
+
use crate::{colors::*, tokens::*};
|
|
5
|
+
|
|
6
|
+
pub struct SearchItem {
|
|
7
|
+
pub title: String,
|
|
8
|
+
pub subtitle: Option<String>,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
impl SearchItem {
|
|
12
|
+
pub fn new(title: impl Into<String>) -> Self {
|
|
13
|
+
Self { title: title.into(), subtitle: None }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pub fn subtitle(mut self, subtitle: impl Into<String>) -> Self {
|
|
17
|
+
self.subtitle = Some(subtitle.into());
|
|
18
|
+
self
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub struct Search {
|
|
23
|
+
pub items: Vec<SearchItem>,
|
|
24
|
+
pub query: String,
|
|
25
|
+
id: Id,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl Search {
|
|
29
|
+
pub fn new(id: impl Into<Id>) -> Self {
|
|
30
|
+
Self { items: Vec::new(), query: String::new(), id: id.into() }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn item(mut self, item: SearchItem) -> Self {
|
|
34
|
+
self.items.push(item);
|
|
35
|
+
self
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn set_items(&mut self, items: Vec<SearchItem>) {
|
|
39
|
+
self.items = items;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub fn filter(&self) -> Vec<&SearchItem> {
|
|
43
|
+
if self.query.is_empty() {
|
|
44
|
+
return self.items.iter().collect();
|
|
45
|
+
}
|
|
46
|
+
self.items
|
|
47
|
+
.iter()
|
|
48
|
+
.filter(|item| item.title.to_lowercase().contains(&self.query.to_lowercase()))
|
|
49
|
+
.collect()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Render the search UI with text input and results list
|
|
53
|
+
pub fn show(&mut self, ui: &mut Ui) -> Option<usize> {
|
|
54
|
+
let colors = Colors::default();
|
|
55
|
+
let mut selected = None;
|
|
56
|
+
|
|
57
|
+
// Search input field
|
|
58
|
+
ui.horizontal(|ui| {
|
|
59
|
+
ui.label("🔍");
|
|
60
|
+
let response = ui.add(
|
|
61
|
+
TextEdit::singleline(&mut self.query)
|
|
62
|
+
.hint_text("Search...")
|
|
63
|
+
.desired_width(f32::INFINITY)
|
|
64
|
+
.font(FontId::new(tokens::FONT_SIZE_BASE, FontFamily::Proportional)),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if response.has_focus() {
|
|
68
|
+
// Show results dropdown when focused
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Results list
|
|
73
|
+
let results = self.filter();
|
|
74
|
+
if !results.is_empty() {
|
|
75
|
+
egui::Frame::none()
|
|
76
|
+
.fill(colors.surface)
|
|
77
|
+
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
78
|
+
.rounding(Rounding::same(tokens::RADIUS))
|
|
79
|
+
.inner_margin(SPACE_2)
|
|
80
|
+
.show(ui, |ui| {
|
|
81
|
+
for (idx, item) in results.iter().enumerate() {
|
|
82
|
+
let response = ui.horizontal(|ui| {
|
|
83
|
+
ui.label(
|
|
84
|
+
RichText::new(&item.title)
|
|
85
|
+
.size(tokens::FONT_SIZE_BASE)
|
|
86
|
+
.color(colors.text),
|
|
87
|
+
);
|
|
88
|
+
if let Some(subtitle) = &item.subtitle {
|
|
89
|
+
ui.label(
|
|
90
|
+
RichText::new(subtitle)
|
|
91
|
+
.size(tokens::FONT_SIZE_SM)
|
|
92
|
+
.color(colors.text_muted),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if response.response.clicked() {
|
|
98
|
+
// Find original index
|
|
99
|
+
if let Some(orig_idx) = self.items.iter().position(|i| std::ptr::eq(i, *item)) {
|
|
100
|
+
selected = Some(orig_idx);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
} else if !self.query.is_empty() {
|
|
106
|
+
ui.label(
|
|
107
|
+
RichText::new("No results found")
|
|
108
|
+
.size(tokens::FONT_SIZE_SM)
|
|
109
|
+
.color(colors.text_muted),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
selected
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
impl Default for Search {
|
|
118
|
+
fn default() -> Self {
|
|
119
|
+
Self::new("default_search")
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Functional search helper
|
|
124
|
+
pub fn search(ui: &mut Ui, query: &mut String, items: &[SearchItem]) -> Vec<&SearchItem> {
|
|
125
|
+
let colors = Colors::default();
|
|
126
|
+
|
|
127
|
+
ui.add(
|
|
128
|
+
TextEdit::singleline(query)
|
|
129
|
+
.hint_text("Search...")
|
|
130
|
+
.desired_width(f32::INFINITY),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if query.is_empty() {
|
|
134
|
+
return items.iter().collect();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
items
|
|
138
|
+
.iter()
|
|
139
|
+
.filter(|item| item.title.to_lowercase().contains(&query.to_lowercase()))
|
|
140
|
+
.collect()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Render search results list
|
|
144
|
+
pub fn search_results(ui: &mut Ui, results: &[&SearchItem]) -> Option<usize> {
|
|
145
|
+
let colors = Colors::default();
|
|
146
|
+
let mut clicked = None;
|
|
147
|
+
|
|
148
|
+
if !results.is_empty() {
|
|
149
|
+
egui::Frame::none()
|
|
150
|
+
.fill(colors.surface)
|
|
151
|
+
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
152
|
+
.rounding(Rounding::same(tokens::RADIUS))
|
|
153
|
+
.inner_margin(SPACE_2)
|
|
154
|
+
.show(ui, |ui| {
|
|
155
|
+
for (idx, item) in results.iter().enumerate() {
|
|
156
|
+
let response = ui.horizontal(|ui| {
|
|
157
|
+
ui.label(
|
|
158
|
+
RichText::new(&item.title)
|
|
159
|
+
.size(tokens::FONT_SIZE_BASE)
|
|
160
|
+
.color(colors.text),
|
|
161
|
+
);
|
|
162
|
+
if let Some(subtitle) = &item.subtitle {
|
|
163
|
+
ui.label(
|
|
164
|
+
RichText::new(subtitle)
|
|
165
|
+
.size(tokens::FONT_SIZE_SM)
|
|
166
|
+
.color(colors.text_muted),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if response.response.clicked() {
|
|
172
|
+
clicked = Some(idx);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
ui.label(
|
|
178
|
+
RichText::new("No results found")
|
|
179
|
+
.size(tokens::FONT_SIZE_SM)
|
|
180
|
+
.color(colors.text_muted),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
clicked
|
|
185
|
+
}
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
//! Skeleton loading placeholder
|
|
2
|
-
|
|
3
|
-
use egui::*;
|
|
4
|
-
|
|
5
|
-
use crate::{colors::*, tokens::*};
|
|
6
|
-
|
|
7
|
-
pub struct Skeleton {
|
|
8
|
-
pub width: f32,
|
|
9
|
-
pub height: f32,
|
|
10
|
-
pub variant: SkeletonVariant,
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
#[derive(Clone, Copy)]
|
|
14
|
-
pub enum SkeletonVariant {
|
|
15
|
-
Text,
|
|
16
|
-
Title,
|
|
17
|
-
Avatar,
|
|
18
|
-
Block,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
impl Skeleton {
|
|
22
|
-
pub fn text() -> Self {
|
|
23
|
-
Self { width: 100.0, height: 14.0, variant: SkeletonVariant::Text }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
pub fn title() -> Self {
|
|
27
|
-
Self { width: 150.0, height: 20.0, variant: SkeletonVariant::Title }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
pub fn avatar() -> Self {
|
|
31
|
-
Self { width: 40.0, height: 40.0, variant: SkeletonVariant::Avatar }
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
pub fn block(width: f32, height: f32) -> Self {
|
|
35
|
-
Self { width, height, variant: SkeletonVariant::Block }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
pub fn width(mut self, width: f32) -> Self {
|
|
39
|
-
self.width = width;
|
|
40
|
-
self
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
pub fn height(mut self, height: f32) -> Self {
|
|
44
|
-
self.height = height;
|
|
45
|
-
self
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
pub fn show(&self, ui: &mut Ui) -> Response {
|
|
49
|
-
let colors = Colors::default();
|
|
50
|
-
|
|
51
|
-
let rounding = match self.variant {
|
|
52
|
-
SkeletonVariant::Avatar => Rounding::same(self.width / 2.0),
|
|
53
|
-
SkeletonVariant::Block => Rounding::same(tokens::RADIUS),
|
|
54
|
-
_ => Rounding::same(tokens::RADIUS_SM),
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
let (rect, response) = ui.allocate_at_least(vec2(self.width, self.height), Sense::hover());
|
|
58
|
-
ui.painter().rect_filled(rect, rounding, colors.surface_3);
|
|
59
|
-
ui.painter().rect_stroke(rect, rounding, Stroke::new(1.0, colors.border));
|
|
60
|
-
|
|
61
|
-
response
|
|
62
|
-
}
|
|
63
|
-
}
|
|
1
|
+
//! Skeleton loading placeholder
|
|
2
|
+
|
|
3
|
+
use egui::*;
|
|
4
|
+
|
|
5
|
+
use crate::{colors::*, tokens::*};
|
|
6
|
+
|
|
7
|
+
pub struct Skeleton {
|
|
8
|
+
pub width: f32,
|
|
9
|
+
pub height: f32,
|
|
10
|
+
pub variant: SkeletonVariant,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[derive(Clone, Copy)]
|
|
14
|
+
pub enum SkeletonVariant {
|
|
15
|
+
Text,
|
|
16
|
+
Title,
|
|
17
|
+
Avatar,
|
|
18
|
+
Block,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl Skeleton {
|
|
22
|
+
pub fn text() -> Self {
|
|
23
|
+
Self { width: 100.0, height: 14.0, variant: SkeletonVariant::Text }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn title() -> Self {
|
|
27
|
+
Self { width: 150.0, height: 20.0, variant: SkeletonVariant::Title }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn avatar() -> Self {
|
|
31
|
+
Self { width: 40.0, height: 40.0, variant: SkeletonVariant::Avatar }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn block(width: f32, height: f32) -> Self {
|
|
35
|
+
Self { width, height, variant: SkeletonVariant::Block }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn width(mut self, width: f32) -> Self {
|
|
39
|
+
self.width = width;
|
|
40
|
+
self
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub fn height(mut self, height: f32) -> Self {
|
|
44
|
+
self.height = height;
|
|
45
|
+
self
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn show(&self, ui: &mut Ui) -> Response {
|
|
49
|
+
let colors = Colors::default();
|
|
50
|
+
|
|
51
|
+
let rounding = match self.variant {
|
|
52
|
+
SkeletonVariant::Avatar => Rounding::same(self.width / 2.0),
|
|
53
|
+
SkeletonVariant::Block => Rounding::same(tokens::RADIUS),
|
|
54
|
+
_ => Rounding::same(tokens::RADIUS_SM),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let (rect, response) = ui.allocate_at_least(vec2(self.width, self.height), Sense::hover());
|
|
58
|
+
ui.painter().rect_filled(rect, rounding, colors.surface_3);
|
|
59
|
+
ui.painter().rect_stroke(rect, rounding, Stroke::new(1.0, colors.border));
|
|
60
|
+
|
|
61
|
+
response
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
//! Table component
|
|
2
|
-
|
|
3
|
-
use egui::*;
|
|
4
|
-
use crate::{colors::*, tokens::*};
|
|
5
|
-
|
|
6
|
-
pub struct Table<'a> {
|
|
7
|
-
headers: Vec<&'a str>,
|
|
8
|
-
rows: Vec<Vec<String>>,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
impl<'a> Table<'a> {
|
|
12
|
-
pub fn new(headers: impl IntoIterator<Item = &'a str>) -> Self {
|
|
13
|
-
Self { headers: headers.into_iter().collect(), rows: Vec::new() }
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
pub fn row(mut self, cells: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
|
17
|
-
self.rows.push(cells.into_iter().map(|s| s.into()).collect());
|
|
18
|
-
self
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
pub fn show(&self, ui: &mut Ui) {
|
|
22
|
-
let colors = Colors::default();
|
|
23
|
-
|
|
24
|
-
// Render headers
|
|
25
|
-
ui.horizontal(|ui| {
|
|
26
|
-
for header in &self.headers {
|
|
27
|
-
egui::Frame::none()
|
|
28
|
-
.fill(colors.surface_2)
|
|
29
|
-
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
30
|
-
.rounding(Rounding::same(tokens::RADIUS_SM))
|
|
31
|
-
.inner_margin(vec2(tokens::SPACE_2, tokens::SPACE_1))
|
|
32
|
-
.show(ui, |ui| {
|
|
33
|
-
ui.strong(*header);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
ui.add_space(tokens::SPACE_2);
|
|
39
|
-
|
|
40
|
-
// Render rows
|
|
41
|
-
for row in &self.rows {
|
|
42
|
-
ui.horizontal(|ui| {
|
|
43
|
-
for cell in row {
|
|
44
|
-
egui::Frame::none()
|
|
45
|
-
.fill(colors.surface)
|
|
46
|
-
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
47
|
-
.rounding(Rounding::same(tokens::RADIUS_SM))
|
|
48
|
-
.inner_margin(vec2(tokens::SPACE_2, tokens::SPACE_1))
|
|
49
|
-
.show(ui, |ui| {
|
|
50
|
-
ui.label(cell);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
//! Table component
|
|
2
|
+
|
|
3
|
+
use egui::*;
|
|
4
|
+
use crate::{colors::*, tokens::*};
|
|
5
|
+
|
|
6
|
+
pub struct Table<'a> {
|
|
7
|
+
headers: Vec<&'a str>,
|
|
8
|
+
rows: Vec<Vec<String>>,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
impl<'a> Table<'a> {
|
|
12
|
+
pub fn new(headers: impl IntoIterator<Item = &'a str>) -> Self {
|
|
13
|
+
Self { headers: headers.into_iter().collect(), rows: Vec::new() }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pub fn row(mut self, cells: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
|
17
|
+
self.rows.push(cells.into_iter().map(|s| s.into()).collect());
|
|
18
|
+
self
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn show(&self, ui: &mut Ui) {
|
|
22
|
+
let colors = Colors::default();
|
|
23
|
+
|
|
24
|
+
// Render headers
|
|
25
|
+
ui.horizontal(|ui| {
|
|
26
|
+
for header in &self.headers {
|
|
27
|
+
egui::Frame::none()
|
|
28
|
+
.fill(colors.surface_2)
|
|
29
|
+
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
30
|
+
.rounding(Rounding::same(tokens::RADIUS_SM))
|
|
31
|
+
.inner_margin(vec2(tokens::SPACE_2, tokens::SPACE_1))
|
|
32
|
+
.show(ui, |ui| {
|
|
33
|
+
ui.strong(*header);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
ui.add_space(tokens::SPACE_2);
|
|
39
|
+
|
|
40
|
+
// Render rows
|
|
41
|
+
for row in &self.rows {
|
|
42
|
+
ui.horizontal(|ui| {
|
|
43
|
+
for cell in row {
|
|
44
|
+
egui::Frame::none()
|
|
45
|
+
.fill(colors.surface)
|
|
46
|
+
.stroke(egui::Stroke::new(1.0, colors.border))
|
|
47
|
+
.rounding(Rounding::same(tokens::RADIUS_SM))
|
|
48
|
+
.inner_margin(vec2(tokens::SPACE_2, tokens::SPACE_1))
|
|
49
|
+
.show(ui, |ui| {
|
|
50
|
+
ui.label(cell);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|