From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id D82981FF127 for ; Thu, 15 Oct 2026 14:40:50 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id A3A932134B; Thu, 15 Oct 2026 14:40:50 +0200 (CEST) From: Dominik Csapak To: yew-devel@lists.proxmox.com Subject: [PATCH yew-widget-toolkit v4 1/2] widget: data table: factor out cell rendering logic Date: Thu, 15 Oct 2026 14:38:27 +0200 Message-ID: <20261015124047.3536357-2-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261015124047.3536357-1-d.csapak@proxmox.com> References: <20261015124047.3536357-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.365 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: BPC3KWKTURTJ6WQRNNS5HUQFXBB3RVNY X-Message-ID-Hash: BPC3KWKTURTJ6WQRNNS5HUQFXBB3RVNY X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Yew framework devel list at Proxmox List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Move the loop that renders the visible cells of a row, including the colspan handling, into a separate function, so that it can be reused for rendering invisible sample rows to size the columns. No functional change intended. Signed-off-by: Dominik Csapak --- src/widget/data_table/mod.rs | 3 ++ src/widget/data_table/rendered_cell.rs | 59 +++++++++++++++++++++++ src/widget/data_table/row.rs | 66 +++++++++----------------- 3 files changed, 84 insertions(+), 44 deletions(-) create mode 100644 src/widget/data_table/rendered_cell.rs diff --git a/src/widget/data_table/mod.rs b/src/widget/data_table/mod.rs index 2ab03b6..ba0d0ff 100644 --- a/src/widget/data_table/mod.rs +++ b/src/widget/data_table/mod.rs @@ -29,6 +29,9 @@ pub use cell_configuration::CellConfiguration; mod cell_render_callback; pub use cell_render_callback::{DataTableCellRenderArgs, DataTableCellRenderer}; +mod rendered_cell; +pub(crate) use rendered_cell::render_row_cells; + mod header_render_callback; pub use header_render_callback::{ DataTableHeaderRenderArgs, DataTableHeaderRenderer, DataTableHeaderTableLink, diff --git a/src/widget/data_table/rendered_cell.rs b/src/widget/data_table/rendered_cell.rs new file mode 100644 index 0000000..42ab8e6 --- /dev/null +++ b/src/widget/data_table/rendered_cell.rs @@ -0,0 +1,59 @@ +use yew::Html; + +use crate::widget::data_table::{CellConfiguration, DataTableCellRenderArgs, DataTableColumn}; + +/// Contains the rendered cell contents plus meta info to properly show it in the data table +pub(crate) struct RenderedCell<'a, T: 'static> { + pub column_num: usize, + pub config: CellConfiguration, + pub content: Html, + pub column: &'a DataTableColumn, +} + +/// Calculates the necessary index and colspan properties and renders the cell for a row. +pub(crate) fn render_row_cells<'a, T>( + columns: &'a [DataTableColumn], + columns_hidden: &[bool], + mut make_args: impl FnMut(usize) -> DataTableCellRenderArgs<'a, T>, +) -> Vec> { + let mut res = Vec::with_capacity(columns.len()); + + let mut col_index = 0; + let mut column_num = 0; + + while let Some(column) = columns.get(column_num) { + if let Some(true) = columns_hidden.get(column_num) { + column_num += 1; + continue; + } + + let mut args = make_args(col_index); + + let content = column.apply_render(&mut args); + + let mut colspan = 1; + + for (attr_name, attr_value) in args.config.attributes.iter() { + if attr_name != "colspan" { + continue; + } + + match attr_value.parse::() { + Ok(n) if n > 0 => colspan = n, + _ => {} + } + } + + res.push(RenderedCell { + column_num, + config: args.config, + content, + column, + }); + + col_index += colspan; + column_num += colspan; + } + + res +} diff --git a/src/widget/data_table/row.rs b/src/widget/data_table/row.rs index 34bbbdf..9ce1f4d 100644 --- a/src/widget/data_table/row.rs +++ b/src/widget/data_table/row.rs @@ -13,7 +13,7 @@ use crate::widget::Container; use super::{ CellConfiguration, DataTableCellRenderArgs, DataTableColumn, DataTableRowRenderArgs, - DataTableRowRenderCallback, + DataTableRowRenderCallback, render_row_cells, }; /// DataTable row properties. @@ -119,15 +119,23 @@ impl Component for PwtDataTableRow { let minheight_cell_style = AttrValue::Rc(format!("vertical-align:top;height: {}px;", props.min_row_height).into()); - let mut col_index = 0; - let mut column_num = 0; - - while let Some(column) = props.columns.get(column_num) { - if let Some(true) = props.column_hidden.get(column_num) { - column_num += 1; - continue; + let rendered_cells = render_row_cells(&props.columns, &props.column_hidden, |col_index| { + DataTableCellRenderArgs { + selection: props.selection.clone(), + record: &props.record, + record_key: &props.record_key, + row_index: props.row_num, + column_index: col_index, + selected: props.selected, + config: (*props.cell_config).clone(), + is_expanded: props.is_expanded, + is_leaf: props.is_leaf, + level: props.level, } + }); + for cell in rendered_cells { + let column = cell.column; let vertical_align = props .vertical_align .to_owned() @@ -136,52 +144,22 @@ impl Component for PwtDataTableRow { let text_align = column.justify.to_owned(); let cell_active = match props.active_cell { - Some(active_cell) => active_cell == column_num, + Some(active_cell) => active_cell == cell.column_num, None => false, }; - let mut args = DataTableCellRenderArgs { - selection: props.selection.clone(), - record: &props.record, - record_key: &props.record_key, - row_index: props.row_num, - column_index: col_index, - selected: props.selected, - config: (*props.cell_config).clone(), - is_expanded: props.is_expanded, - is_leaf: props.is_leaf, - level: props.level, - }; - - let cell = column.apply_render(&mut args); - let mut td = Container::from_tag("td") - .class(args.config.class) + .class(cell.config.class) .class((cell_active && props.has_focus).then_some("cell-cursor")) - .styles(args.config.style) + .styles(cell.config.style) .style("vertical-align", vertical_align) .style("text-align", text_align) .attribute("role", "gridcell") - .attribute("data-column-num", column_num.to_string()) + .attribute("data-column-num", cell.column_num.to_string()) .attribute("tabindex", if cell_active { "0" } else { "-1" }) - .with_child(html! {
{cell}
}); - - let mut colspan = 1; - - for (attr_name, attr_value) in args.config.attributes.iter() { - if attr_name == "colspan" { - if let Ok(n) = attr_value.parse::() { - if n > 0 { - colspan = n - } - } - } - } - - td.as_std_props_mut().add_attributes(args.config.attributes); + .with_child(html! {
{cell.content}
}); - col_index += colspan; - column_num += colspan; + td.as_std_props_mut().add_attributes(cell.config.attributes); row.add_child(td); } -- 2.47.3