diff --git a/rust/perspective-viewer/src/less/column-selector.less b/rust/perspective-viewer/src/less/column-selector.less index 9c3f0651c3..3b7651ddc0 100644 --- a/rust/perspective-viewer/src/less/column-selector.less +++ b/rust/perspective-viewer/src/less/column-selector.less @@ -70,12 +70,12 @@ .dragdrop-highlight { .column_selector_draggable.dragover, - .column-selector-column:not(:last-child):not(:nth-last-child(2)) + .column-selector-column:not(:last-child):not(:nth-last-child(2)):not(:nth-last-child(3)) .column_selector_draggable { border-bottom-color: var(--active--color) !important; } - .column-selector-column:not(:first-child):not([data-label]) + .column-selector-column:not(:first-child):not([data-label]):not(:nth-last-child(2)) .column_selector_draggable.dragover { border-top-color: rgb(0 0 0 / 5%) !important; } @@ -113,18 +113,18 @@ span.expression-delete-button { padding-right: 1.5px; padding-left: 5px; - + &::before { - content: var(--column-close--content, "close") + content: var(--column-close--content, "close"); } } span.expression-edit-button { padding-left: 1.5px; padding-right: 3px; - + &::before { - content: var(--column-menu--content, "menu") + content: var(--column-menu--content, "menu"); } } @@ -239,6 +239,21 @@ font-size: var(--column-selector--font-size, 14px); } + #active-columns.collapse { + .column-selector-column:nth-last-child(3) { + .column_selector_draggable { + border-color: transparent; + } + } + } + + #active-columns:not(.collapse).dragdrop-highlight { + .column-selector-column:not(:last-child):not(:nth-last-child(2)) + .column_selector_draggable { + border-bottom-color: var(--active--color) !important; + } + } + #active-columns, #sub-columns { flex: 0 1 auto; diff --git a/rust/perspective-viewer/src/rust/components/column_selector.rs b/rust/perspective-viewer/src/rust/components/column_selector.rs index 60713cbe7c..178eacbc04 100644 --- a/rust/perspective-viewer/src/rust/components/column_selector.rs +++ b/rust/perspective-viewer/src/rust/components/column_selector.rs @@ -167,6 +167,11 @@ impl Component for ColumnSelector { .and_then(|x| from_index.map(|from_index| from_index < x)) .unwrap_or_default() && is_to_empty + || from_index + .map(|from_index| { + from_index == config.columns.len() - 1 && to_index > from_index + }) + .unwrap_or_default() { ctx.props().dragdrop.drag_leave(DragTarget::Active); true @@ -271,7 +276,9 @@ impl Component for ColumnSelector { active_classes.push("dragdrop-highlight"); }; - if config.columns.len() != all_columns.len() + config.expressions.len() { + if config.columns.iter().filter(|x| x.is_some()).count() + != all_columns.len() + config.expressions.len() + { active_classes.push("collapse"); } diff --git a/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs b/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs index 1e9f29d94f..6df92e3ffc 100644 --- a/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs +++ b/rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs @@ -173,7 +173,8 @@ where let start_node = if scroll_top > named_col_section_height { max!( 0, - ((scroll_top / ctx.props().row_height).floor() as usize + (((scroll_top - named_col_section_height) / ctx.props().row_height).floor() + as usize + ctx.props().named_row_count) as isize, ) as usize } else { diff --git a/rust/perspective-viewer/src/rust/model/columns_iter_set.rs b/rust/perspective-viewer/src/rust/model/columns_iter_set.rs index c9eadd6e81..0baa9f9e18 100644 --- a/rust/perspective-viewer/src/rust/model/columns_iter_set.rs +++ b/rust/perspective-viewer/src/rust/model/columns_iter_set.rs @@ -63,27 +63,47 @@ impl<'a> ColumnsIteratorSet<'a> { .as_ref() .map_or(0, |x| x.len()); + let named_columns = self.renderer.metadata().names.clone().unwrap_or_default(); + let last_col = self.config.columns.last().and_then(|x| x.as_ref()); + let has_blank_tail = last_col.is_none() + || self.config.columns.len() < named_columns.len() + || self.config.columns.iter().filter(|x| x.is_some()).count() + == self + .session + .metadata() + .get_table_columns() + .map(|x| x.len()) + .unwrap_or_default() + + self.config.expressions.len(); + match &self.is_dragover_column { Some((to_index, from_column)) => { let is_to_swap = self.renderer.metadata().is_swap(*to_index); let to_column = self.config.columns.get(*to_index); let is_to_empty = to_column.map(|x| x.is_none()).unwrap_or_default(); - let is_from_required = self + let from_index = self .config .columns .iter() - .position(|x| x.as_ref() == Some(from_column)) + .position(|x| x.as_ref() == Some(from_column)); + + let is_from_required = from_index .and_then(|x| self.renderer.metadata().min.map(|y| x < y)) .unwrap_or_default(); - let is_from_swap = self - .config - .columns - .iter() - .position(|x| x.as_ref() == Some(from_column)) + let is_from_swap = from_index .map(|x| self.renderer.metadata().is_swap(x)) .unwrap_or_default(); + let is_dragover_last = *to_index == self.config.columns.len() + || (*to_index == self.config.columns.len() - 1 && from_index.is_some()); + + let tail = if has_blank_tail || is_dragover_last { + [].iter().cloned() + } else { + [Some(&None)].iter().cloned() + }; + if is_to_swap || is_from_required { let all_columns = self.config.columns.iter().filter_map(move |x| match x { Some(x) if x == from_column => { @@ -104,7 +124,10 @@ impl<'a> ColumnsIteratorSet<'a> { let after_cols = all_columns.skip(*to_index + 1).map(Some); self.to_active_column_state(Box::new( - before_cols.chain([None].iter().cloned()).chain(after_cols), + before_cols + .chain([None].iter().cloned()) + .chain(after_cols) + .chain(tail), )) } else { let to_offset = match to_column { @@ -131,11 +154,24 @@ impl<'a> ColumnsIteratorSet<'a> { let after_cols = all_columns.skip(to_offset).map(Some); self.to_active_column_state(Box::new( - before_cols.chain([None].iter().cloned()).chain(after_cols), + before_cols + .chain([None].iter().cloned()) + .chain(after_cols) + .chain(tail), )) } } - _ => self.to_active_column_state(Box::new(self.config.columns.iter().map(Some))), + _ => { + let tail = if has_blank_tail { + [].iter().cloned() + } else { + [Some(&None)].iter().cloned() + }; + + self.to_active_column_state(Box::new( + self.config.columns.iter().map(Some).chain(tail), + )) + } } } @@ -180,7 +216,6 @@ impl<'a> ColumnsIteratorSet<'a> { }; let col_set = self.config.columns.iter().collect::>(); - let mut filtered = values .filter_map(move |name| self.to_ordered_column(name, is_drag_active, &col_set)) .collect::>(); diff --git a/rust/perspective-viewer/test/results/results.json b/rust/perspective-viewer/test/results/results.json index 68b7d6b848..4aa04439ad 100644 --- a/rust/perspective-viewer/test/results/results.json +++ b/rust/perspective-viewer/test/results/results.json @@ -3,7 +3,7 @@ "superstore.html/doesn't leak elements.": "d0fd18b3d4d7c183c5ed155b4bf37972", "superstore.html/doesn't leak views when setting group by.": "54daaa4bbbe59f6ed4acc301ba871bab", "superstore.html/doesn't leak views when setting filters.": "6dfc1e505f1428424c3265f0236f22fc", - "__GIT_COMMIT__": "042876bc9248db7bd81b92f7d9422d9397fbc81f", + "__GIT_COMMIT__": "c641d251df413dba800a2a5bf330107a28bc1be6", "blank.html/Handles reloading with a schema.": "e58c62f6e0ff16dc4d753f99e0fc39c3", "superstore_shows_a_grid_without_any_settings_applied_": "ae1c4690d978598ca14c8669244ce604", "superstore_Responsive_Layout_shows_horizontal_columns_on_small_vertical_viewports_": "57ba3ad341cf8a0e4df6ab96715ff2a0", @@ -62,23 +62,23 @@ "superstore_save()_returns_the_current_config": "ded04b5d6cb96a3651578334f189b20e", "superstore_restore()_restores_a_config_from_save()": "ded04b5d6cb96a3651578334f189b20e", "superstore_restore()_fires_the__perspective-config-update__event": "ded04b5d6cb96a3651578334f189b20e", - "superstore_restore_fires_the__perspective-config-update__event": "06b1128d94ddb953f8c5b6adf49f4756", - "superstore_save_returns_the_current_config": "06b1128d94ddb953f8c5b6adf49f4756", - "superstore_restore_restores_a_config_from_save": "43141e5cf7c0554cce759d3f7ea16133", + "superstore_restore_fires_the__perspective-config-update__event": "87c349f24f967b3ca7710a350b26bc6f", + "superstore_save_returns_the_current_config": "87c349f24f967b3ca7710a350b26bc6f", + "superstore_restore_restores_a_config_from_save": "5bcbead8c4358e283301f2cae19af922", "Expressions_Click_on_add_column_button_opens_the_expression_UI_": "40bb9b2f39e1cd296752d42c694a4f05", "Expressions_Resetting_the_viewer_partially_should_not_delete_all_expressions": "e5a61cd8d6b3f02811ddcbacd4636889", "Expressions_Resetting_the_viewer_partially_when_expression_as_in_columns_field,_should_not_delete_all_expressions": "e5a61cd8d6b3f02811ddcbacd4636889", "superstore-all_restore__Bucket_by_year_": "e553e253c983516dea41eb584a889612", "superstore-all_restore__Plugin_config_color_mode_": "e15272040eea436c123116c69c11a3e8", - "dragdrop_superstore_drop_from_inactive_to_active_should_add": "d9647ef9ee823e5a755fd82f59bc17dd", - "dragdrop_superstore_drop_from_active_to_active_should_swap": "1bf37cc032feb970f7e13a8cfabd889e", + "dragdrop_superstore_drop_from_inactive_to_active_should_add": "fdd21108d1d0fe2cd4af227e80148244", + "dragdrop_superstore_drop_from_active_to_active_should_swap": "184362114cbed81e3738b7c6ec4eee4b", "dragdrop_column-selector-modes_drop_from_inactive_to_required_column_should_add": "cb444777492e047555dd2e7362342478", - "dragdrop_column-selector-modes_drop_from_required_to_required_should_swap": "fb99573ea6e099661c998768b7ba81a1", + "dragdrop_column-selector-modes_drop_from_required_to_required_should_swap": "44ef933dd1877da4e5a28e781b903ed7", "dragdrop_column-selector-modes_drop_from_required_to_empty_column_should_fail": "49315bb1d743aa90ed17ba765bfe62c6", "dragdrop_column-selector-modes_drop_from_inactive_to_empty_should_add": "4d64295b828cb74471cb5aaa8427fe97", - "dragdrop_column-selector-modes_drop_from_named_to_required_should_swap": "579b194ae62ed4efb1734a6e629705b6", - "dragdrop_column-selector-modes_drop_from_optional_to_empty_columns_should_move": "47e0f1e227cef782b9540c85d26192fd", - "dragdrop_column-selector-modes_dragover_from_named_to_required_columns_should_swap": "2ab795f33898c98faf0a7ffa4494975e", - "dragdrop_column-selector-modes_dragover_from_optional_to_empty_columns_should_move": "2aefefd091efd14886722404178e967d", - "dragdrop_column-selector-modes_dragover_from_optional_to_required_columns_should_swap": "fa41e3421003f99999ff3568f838bf08" + "dragdrop_column-selector-modes_drop_from_named_to_required_should_swap": "7f8a9471374f92b389db3ce95235f2fc", + "dragdrop_column-selector-modes_drop_from_optional_to_empty_columns_should_move": "5576999a454cff9b88ee67068ce87911", + "dragdrop_column-selector-modes_dragover_from_named_to_required_columns_should_swap": "052c1644607e755a3d1aeb341bb29434", + "dragdrop_column-selector-modes_dragover_from_optional_to_empty_columns_should_move": "3ace55714735edf6b242f2ee5a819cb8", + "dragdrop_column-selector-modes_dragover_from_optional_to_required_columns_should_swap": "93797970fabc6d024c7867c20ad4e47b" } \ No newline at end of file