Skip to main content

material_ui_rs/widget/component/
data_table.rs

1//! Material 3 data table constructors with token-backed layout defaults.
2
3use iced_widget::core::text as core_text;
4use iced_widget::core::{Length, Padding, alignment};
5use iced_widget::table::{self as iced_table, Column, Table};
6use iced_widget::text;
7use iced_widget::{Container, Text};
8
9use super::absolute_line_height;
10use crate::{Theme, text as text_style, tokens};
11
12const CELL_HORIZONTAL_PADDING: f32 = 16.0;
13pub const COMPACT_NUMERIC_COLUMN_WIDTH: f32 = 88.0;
14
15pub fn standard<'a, 'b, T, Message, Renderer>(
16    columns: impl IntoIterator<Item = Column<'a, 'b, T, Message, Theme, Renderer>>,
17    rows: impl IntoIterator<Item = T>,
18) -> Table<'a, Message, Theme, Renderer>
19where
20    T: Clone,
21    Renderer: iced_widget::core::Renderer,
22{
23    iced_table::table(columns, rows)
24        .width(Length::Fill)
25        .padding(0.0)
26        .separator(tokens::component::data_table::OUTLINE_WIDTH)
27}
28
29pub fn column<'a, 'b, T, F, Message, Renderer>(
30    header: impl text::IntoFragment<'a>,
31    view: impl Fn(T) -> F + 'b,
32) -> Column<'a, 'b, T, Message, Theme, Renderer>
33where
34    T: 'a,
35    F: text::IntoFragment<'a> + 'a,
36    Message: 'a,
37    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
38{
39    iced_table::column(header_cell(header), move |row| body_cell(view(row)))
40        .width(Length::Fill)
41        .align_y(alignment::Vertical::Center)
42}
43
44pub fn weighted_column<'a, 'b, T, F, Message, Renderer>(
45    weight: u16,
46    header: impl text::IntoFragment<'a>,
47    view: impl Fn(T) -> F + 'b,
48) -> Column<'a, 'b, T, Message, Theme, Renderer>
49where
50    T: 'a,
51    F: text::IntoFragment<'a> + 'a,
52    Message: 'a,
53    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
54{
55    column(header, view).width(Length::FillPortion(weight))
56}
57
58pub fn numeric_column<'a, 'b, T, F, Message, Renderer>(
59    header: impl text::IntoFragment<'a>,
60    view: impl Fn(T) -> F + 'b,
61) -> Column<'a, 'b, T, Message, Theme, Renderer>
62where
63    T: 'a,
64    F: text::IntoFragment<'a> + 'a,
65    Message: 'a,
66    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
67{
68    iced_table::column(header_cell_right(header), move |row| {
69        body_cell_right(view(row))
70    })
71    .width(Length::Fill)
72    .align_x(alignment::Horizontal::Right)
73    .align_y(alignment::Vertical::Center)
74}
75
76pub fn compact_numeric_column<'a, 'b, T, F, Message, Renderer>(
77    header: impl text::IntoFragment<'a>,
78    view: impl Fn(T) -> F + 'b,
79) -> Column<'a, 'b, T, Message, Theme, Renderer>
80where
81    T: 'a,
82    F: text::IntoFragment<'a> + 'a,
83    Message: 'a,
84    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
85{
86    numeric_column(header, view).width(Length::Fixed(COMPACT_NUMERIC_COLUMN_WIDTH))
87}
88
89pub fn header_cell<'a, Message, Renderer>(
90    label: impl text::IntoFragment<'a>,
91) -> Container<'a, Message, Theme, Renderer>
92where
93    Message: 'a,
94    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
95{
96    header_cell_aligned(label, alignment::Horizontal::Left)
97}
98
99pub fn header_cell_right<'a, Message, Renderer>(
100    label: impl text::IntoFragment<'a>,
101) -> Container<'a, Message, Theme, Renderer>
102where
103    Message: 'a,
104    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
105{
106    header_cell_aligned(label, alignment::Horizontal::Right)
107}
108
109pub fn body_cell<'a, Message, Renderer>(
110    label: impl text::IntoFragment<'a>,
111) -> Container<'a, Message, Theme, Renderer>
112where
113    Message: 'a,
114    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
115{
116    body_cell_aligned(label, alignment::Horizontal::Left)
117}
118
119pub fn body_cell_right<'a, Message, Renderer>(
120    label: impl text::IntoFragment<'a>,
121) -> Container<'a, Message, Theme, Renderer>
122where
123    Message: 'a,
124    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
125{
126    body_cell_aligned(label, alignment::Horizontal::Right)
127}
128
129fn header_cell_aligned<'a, Message, Renderer>(
130    label: impl text::IntoFragment<'a>,
131    align_x: alignment::Horizontal,
132) -> Container<'a, Message, Theme, Renderer>
133where
134    Message: 'a,
135    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
136{
137    let label_text = tokens::typography::LABEL_LARGE;
138
139    cell(
140        Text::new(label)
141            .size(label_text.size)
142            .line_height(absolute_line_height(label_text.line_height))
143            .style(text_style::surface_variant),
144        tokens::component::data_table::HEADER_CONTAINER_HEIGHT,
145        align_x,
146    )
147}
148
149fn body_cell_aligned<'a, Message, Renderer>(
150    label: impl text::IntoFragment<'a>,
151    align_x: alignment::Horizontal,
152) -> Container<'a, Message, Theme, Renderer>
153where
154    Message: 'a,
155    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
156{
157    let body_text = tokens::typography::BODY_MEDIUM;
158
159    cell(
160        Text::new(label)
161            .size(body_text.size)
162            .line_height(absolute_line_height(body_text.line_height))
163            .style(text_style::surface),
164        tokens::component::data_table::ROW_ITEM_CONTAINER_HEIGHT,
165        align_x,
166    )
167}
168
169fn cell<'a, Message, Renderer>(
170    content: Text<'a, Theme, Renderer>,
171    height: f32,
172    align_x: alignment::Horizontal,
173) -> Container<'a, Message, Theme, Renderer>
174where
175    Message: 'a,
176    Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
177{
178    Container::new(content)
179        .width(Length::Shrink)
180        .height(Length::Fixed(height))
181        .padding(Padding::from([0.0, CELL_HORIZONTAL_PADDING]))
182        .align_x(align_x)
183        .align_y(alignment::Vertical::Center)
184}