I can select rows and columns
This commit is contained in:
@@ -1477,8 +1477,87 @@ function initDataGrid(gridId) {
|
|||||||
updateDatagridSelection(gridId)
|
updateDatagridSelection(gridId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function initDataGridMouseOver(gridId) {
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize DataGrid hover effects using event delegation.
|
||||||
|
*
|
||||||
|
* Optimizations:
|
||||||
|
* - Event delegation: 1 listener instead of N×2 (where N = number of cells)
|
||||||
|
* - Row mode: O(1) via class toggle on parent row
|
||||||
|
* - Column mode: RAF batching + cached cells for efficient class removal
|
||||||
|
* - Works with HTMX swaps: listener on stable parent, querySelectorAll finds new cells
|
||||||
|
* - No mouseout: hover selection stays visible when leaving the table
|
||||||
|
*
|
||||||
|
* @param {string} gridId - The DataGrid instance ID
|
||||||
|
*/
|
||||||
|
function initDataGridMouseOver(gridId) {
|
||||||
|
const table = document.getElementById(`t_${gridId}`);
|
||||||
|
if (!table) {
|
||||||
|
console.error(`Table with id "t_${gridId}" not found.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = document.getElementById(`tw_${gridId}`);
|
||||||
|
const selectionModeDiv = document.getElementById(`tsm_${gridId}`);
|
||||||
|
|
||||||
|
// Track hover state
|
||||||
|
let currentHoverRow = null;
|
||||||
|
let currentHoverColId = null;
|
||||||
|
let currentHoverColCells = null;
|
||||||
|
|
||||||
|
table.addEventListener('mouseover', (e) => {
|
||||||
|
// Skip hover during scrolling
|
||||||
|
if (wrapper?.hasAttribute('mf-no-hover')) return;
|
||||||
|
|
||||||
|
const cell = e.target.closest('.dt2-cell');
|
||||||
|
if (!cell) return;
|
||||||
|
|
||||||
|
const selectionMode = selectionModeDiv?.getAttribute('selection-mode');
|
||||||
|
|
||||||
|
if (selectionMode === 'row') {
|
||||||
|
const rowElement = cell.parentElement;
|
||||||
|
if (rowElement !== currentHoverRow) {
|
||||||
|
if (currentHoverRow) {
|
||||||
|
currentHoverRow.classList.remove('dt2-hover-row');
|
||||||
|
}
|
||||||
|
rowElement.classList.add('dt2-hover-row');
|
||||||
|
currentHoverRow = rowElement;
|
||||||
|
}
|
||||||
|
} else if (selectionMode === 'column') {
|
||||||
|
const colId = cell.dataset.col;
|
||||||
|
|
||||||
|
// Skip if same column
|
||||||
|
if (colId === currentHoverColId) return;
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
// Remove old column highlight
|
||||||
|
if (currentHoverColCells) {
|
||||||
|
currentHoverColCells.forEach(c => c.classList.remove('dt2-hover-column'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query and add new column highlight
|
||||||
|
currentHoverColCells = table.querySelectorAll(`.dt2-cell[data-col="${colId}"]`);
|
||||||
|
currentHoverColCells.forEach(c => c.classList.add('dt2-hover-column'));
|
||||||
|
|
||||||
|
currentHoverColId = colId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clean up when leaving the table entirely
|
||||||
|
table.addEventListener('mouseout', (e) => {
|
||||||
|
if (!table.contains(e.relatedTarget)) {
|
||||||
|
if (currentHoverRow) {
|
||||||
|
currentHoverRow.classList.remove('dt2-hover-row');
|
||||||
|
currentHoverRow = null;
|
||||||
|
}
|
||||||
|
if (currentHoverColCells) {
|
||||||
|
currentHoverColCells.forEach(c => c.classList.remove('dt2-hover-column'));
|
||||||
|
currentHoverColCells = null;
|
||||||
|
currentHoverColId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1638,6 +1717,7 @@ function initDataGridScrollbars(gridId) {
|
|||||||
dragStartY = e.clientY;
|
dragStartY = e.clientY;
|
||||||
dragStartScrollTop = cachedBodyScrollTop;
|
dragStartScrollTop = cachedBodyScrollTop;
|
||||||
wrapper.setAttribute("mf-no-tooltip", "");
|
wrapper.setAttribute("mf-no-tooltip", "");
|
||||||
|
wrapper.setAttribute("mf-no-hover", "");
|
||||||
}, {signal});
|
}, {signal});
|
||||||
|
|
||||||
// Horizontal scrollbar mousedown
|
// Horizontal scrollbar mousedown
|
||||||
@@ -1646,6 +1726,7 @@ function initDataGridScrollbars(gridId) {
|
|||||||
dragStartX = e.clientX;
|
dragStartX = e.clientX;
|
||||||
dragStartScrollLeft = cachedTableScrollLeft;
|
dragStartScrollLeft = cachedTableScrollLeft;
|
||||||
wrapper.setAttribute("mf-no-tooltip", "");
|
wrapper.setAttribute("mf-no-tooltip", "");
|
||||||
|
wrapper.setAttribute("mf-no-hover", "");
|
||||||
}, {signal});
|
}, {signal});
|
||||||
|
|
||||||
// Consolidated mousemove listener
|
// Consolidated mousemove listener
|
||||||
@@ -1684,9 +1765,11 @@ function initDataGridScrollbars(gridId) {
|
|||||||
if (isDraggingVertical) {
|
if (isDraggingVertical) {
|
||||||
isDraggingVertical = false;
|
isDraggingVertical = false;
|
||||||
wrapper.removeAttribute("mf-no-tooltip");
|
wrapper.removeAttribute("mf-no-tooltip");
|
||||||
|
wrapper.removeAttribute("mf-no-hover");
|
||||||
} else if (isDraggingHorizontal) {
|
} else if (isDraggingHorizontal) {
|
||||||
isDraggingHorizontal = false;
|
isDraggingHorizontal = false;
|
||||||
wrapper.removeAttribute("mf-no-tooltip");
|
wrapper.removeAttribute("mf-no-tooltip");
|
||||||
|
wrapper.removeAttribute("mf-no-hover");
|
||||||
}
|
}
|
||||||
}, {signal});
|
}, {signal});
|
||||||
|
|
||||||
@@ -1694,8 +1777,20 @@ function initDataGridScrollbars(gridId) {
|
|||||||
let rafScheduledWheel = false;
|
let rafScheduledWheel = false;
|
||||||
let pendingWheelDeltaX = 0;
|
let pendingWheelDeltaX = 0;
|
||||||
let pendingWheelDeltaY = 0;
|
let pendingWheelDeltaY = 0;
|
||||||
|
let wheelEndTimeout = null;
|
||||||
|
|
||||||
const handleWheelScrolling = (event) => {
|
const handleWheelScrolling = (event) => {
|
||||||
|
// Disable hover and tooltip during wheel scroll
|
||||||
|
wrapper.setAttribute("mf-no-hover", "");
|
||||||
|
wrapper.setAttribute("mf-no-tooltip", "");
|
||||||
|
|
||||||
|
// Clear previous timeout and re-enable after 150ms of no wheel events
|
||||||
|
if (wheelEndTimeout) clearTimeout(wheelEndTimeout);
|
||||||
|
wheelEndTimeout = setTimeout(() => {
|
||||||
|
wrapper.removeAttribute("mf-no-hover");
|
||||||
|
wrapper.removeAttribute("mf-no-tooltip");
|
||||||
|
}, 150);
|
||||||
|
|
||||||
// Accumulate wheel deltas
|
// Accumulate wheel deltas
|
||||||
pendingWheelDeltaX += event.deltaX;
|
pendingWheelDeltaX += event.deltaX;
|
||||||
pendingWheelDeltaY += event.deltaY;
|
pendingWheelDeltaY += event.deltaY;
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ class DataGrid(MultipleInstance):
|
|||||||
df = self._df.copy()
|
df = self._df.copy()
|
||||||
df = self._apply_sort(df) # need to keep the real type to sort
|
df = self._apply_sort(df) # need to keep the real type to sort
|
||||||
df = self._apply_filter(df)
|
df = self._apply_filter(df)
|
||||||
|
self._state.ns_total_rows = len(df)
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
@@ -533,7 +534,8 @@ class DataGrid(MultipleInstance):
|
|||||||
return Div(
|
return Div(
|
||||||
*[Div(selection_type=s_type, element_id=f"{elt_id}") for s_type, elt_id in selected],
|
*[Div(selection_type=s_type, element_id=f"{elt_id}") for s_type, elt_id in selected],
|
||||||
id=f"tsm_{self._id}",
|
id=f"tsm_{self._id}",
|
||||||
selection_mode=f"{self._state.selection.selection_mode}",
|
#selection_mode=f"{self._state.selection.selection_mode}",
|
||||||
|
selection_mode=f"column",
|
||||||
**extra_attr,
|
**extra_attr,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user