Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Continue work on hving resize handle on whole column
  • Loading branch information
vnbaaij committed May 13, 2025
commit 602845ede00669e7f71f0437ed00790f386915b3
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div style="height: 400px; max-width: 800px; overflow-y: scroll;">
<FluentDataGrid @ref="grid" Items=@items Virtualize="true" DisplayMode="DataGridDisplayMode.Table" Style="width: 100%;" ItemSize="54" GenerateHeader="@GenerateHeaderOption.Sticky">
<FluentDataGrid @ref="grid" Items=@items Virtualize="true" ResizableColumns="true" DisplayMode="DataGridDisplayMode.Table" Style="width: 100%;" ItemSize="54" GenerateHeader="@GenerateHeaderOption.Sticky">
<ChildContent>
<PropertyColumn Width="25%" Property="@(c => c.Item1)" Sortable="true" />
<PropertyColumn Width="25%" Property="@(c => c.Item2)" />
Expand All @@ -26,6 +26,25 @@
</FluentSwitch>
<FluentButton OnClick="SimulateDataLoading">Simulate data loading</FluentButton>

<div style="height: 400px; max-width: 800px; overflow-y: scroll;">
<FluentDataGrid Items=@items Virtualize="true" ResizableColumns="true" DisplayMode="DataGridDisplayMode.Grid" Style="width: 100%;" ItemSize="54" GenerateHeader="@GenerateHeaderOption.Sticky">
<ChildContent>
<PropertyColumn Width="25%" Property="@(c => c.Item1)" Sortable="true" />
<PropertyColumn Width="25%" Property="@(c => c.Item2)" />
<PropertyColumn Width="25%" Property="@(c => c.Item3)" Align="Align.Center" />
<PropertyColumn Width="25%" Property="@(c => c.Item4)" Align="Align.End" />
</ChildContent>
<EmptyContent>
<FluentIcon Value="@(new Icons.Filled.Size24.Crown())" Color="@Color.Accent" />&nbsp; Nothing to see here. Carry on!
</EmptyContent>
<LoadingContent>
<FluentStack Orientation="Orientation.Vertical" HorizontalAlignment="HorizontalAlignment.Center">
Loading...<br />
<FluentProgress Width="240px" />
</FluentStack>
</LoadingContent>
</FluentDataGrid>
</div>

@code {
FluentDataGrid<SampleGridData>? grid;
Expand Down Expand Up @@ -63,14 +82,14 @@
}
}

private async Task SimulateDataLoading()

Check warning on line 85 in examples/Demo/Shared/Pages/DataGrid/Examples/DataGridVirtualize.razor

View workflow job for this annotation

GitHub Actions / Build and Deploy Demo site

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 85 in examples/Demo/Shared/Pages/DataGrid/Examples/DataGridVirtualize.razor

View workflow job for this annotation

GitHub Actions / Build and Deploy Demo site

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 85 in examples/Demo/Shared/Pages/DataGrid/Examples/DataGridVirtualize.razor

View workflow job for this annotation

GitHub Actions / Build and Deploy Demo site

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
_clearItems = false;

items = null;
grid?.SetLoadingState(true);

await Task.Delay(1500);
//await Task.Delay(1500);

items = GenerateSampleGridData(5000);
grid?.SetLoadingState(false);
Expand Down
15 changes: 13 additions & 2 deletions src/Core/Components/DataGrid/Columns/ColumnBase.razor
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,24 @@
else
{
string? tooltip = Tooltip ? (HeaderTooltip ?? Title) : null;
string? wdelta = "20px";
string? wdelta = "10px";
string? align;

if (Grid.ResizeType is not null || ColumnOptions is not null)
{
wdelta = "56px";
}
<div style="display: flex;">

// determine align string based on Align value
align = Align switch
{
Align.Start => "flex-start",
Align.Center => "center",
Align.End => "flex-end",
_ => "flex-start"
};

<div style="display: flex; justify-content: @align;">
@if (Align == Align.Start || Align == Align.Center)
{
@if (Grid.ResizeType is not null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.col-title {
padding: 0.4rem 0.8rem;
padding: 6px 16px;
user-select: none;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Core/Components/DataGrid/FluentDataGrid.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
::deep .resize-handle {
position: absolute;
top: 6px;
right: -1px;
right: 0;
left: unset;
bottom: 0;
height: 30px;
Expand Down
197 changes: 125 additions & 72 deletions src/Core/Components/DataGrid/FluentDataGrid.razor.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,108 +160,153 @@ export function checkColumnPopupPosition(gridElement, selector) {

export function enableColumnResizing(gridElement) {
const columns = [];
let min = 75;
let headerBeingResized;
let resizeHandle;

const headers = gridElement.querySelectorAll('.column-header.resizable');

if (headers.length === 0) {
return;
}

headers.forEach(header => {
const isRTL = getComputedStyle(gridElement).direction === 'rtl';
const isGrid = gridElement.classList.contains('grid')

let tableHeight = gridElement.offsetHeight;
if (tableHeight < 70) {
//get the aria rowcount attribute
const rowCount = gridElement.getAttribute('aria-rowcount');
if (rowCount) {
const rowHeight = gridElement.querySelector('thead tr th').offsetHeight;
// and multiply by the itemsize (== height of the header cells)
tableHeight = rowCount * rowHeight;
}
}

headers.forEach((header) => {
columns.push({
header,
size: `minmax(${minWidth}px,auto)`,
size: `${header.clientWidth}px`,
});

const onPointerMove = (e) => requestAnimationFrame(() => {
if (!headerBeingResized) {
return;
}
gridElement.style.tableLayout = "fixed";
const div = createDiv(tableHeight, isRTL);
header.appendChild(div);
header.style.position = 'relative';
setListeners(div, isRTL);
});

const horizontalScrollOffset = document.documentElement.scrollLeft;
let width;
let initialWidths;
if (gridElement.style.gridTemplateColumns) {
initialWidths = gridElement.style.gridTemplateColumns;
} else {
initialWidths = columns.map(({ size }) => size).join(' ');

if (document.body.dir === '' || document.body.dir === 'ltr') {
width = (horizontalScrollOffset + e.clientX) - headerBeingResized.getClientRects()[0].x;
}
else {
width = headerBeingResized.getClientRects()[0].x + headerBeingResized.clientWidth - (horizontalScrollOffset + e.clientX);
}
if (isGrid) {
gridElement.style.gridTemplateColumns = initialWidths;
}
}

const column = columns.find(({ header }) => header === headerBeingResized);
column.size = Math.max(minWidth, width) + 'px';
const id = gridElement.id;
grids.push({
id,
columns,
initialWidths,
});

columns.forEach((column) => {
if (column.size.startsWith('minmax')) {
column.size = parseInt(column.header.clientWidth, 10) + 'px';
}
});
function setListeners(div, isRTL) {
let pageX, curCol, curColWidth;

div.addEventListener('pointerdown', function (e) {
curCol = e.target.parentElement;
pageX = e.pageX;

const padding = paddingDiff(curCol);

gridElement.style.gridTemplateColumns = columns
.map(({ size }) => size)
.join(' ');
curColWidth = curCol.offsetWidth - padding;
});

const onPointerUp = (e) => {
div.addEventListener('pointerover', function (e) {
e.target.style.borderInlineEnd = '2px solid var(--neutral-stroke-focus)';
});

window.removeEventListener('pointermove', onPointerMove);
window.removeEventListener('pointerup', onPointerUp);
window.removeEventListener('pointercancel', onPointerUp);
window.removeEventListener('pointerleave', onPointerUp);
div.addEventListener('pointerup', removeBorder);
div.addEventListener('pointercancel', removeBorder);
div.addEventListener('pointerleave', removeBorder);

headerBeingResized.classList.remove('header-being-resized');
headerBeingResized = null;
document.addEventListener('pointermove', (e) =>
requestAnimationFrame(() => {
gridElement.style.tableLayout = 'fixed';

if (e.target.hasPointerCapture(e.pointerId)) {
e.target.releasePointerCapture(e.pointerId);
}
};
if (curCol) {
const diffX = isRTL ? pageX - e.pageX : e.pageX - pageX;
const column = columns.find(({ header }) => header === curCol);

const initResize = ({ target, pointerId }) => {
headerBeingResized = target.parentNode;
headerBeingResized.classList.add('header-being-resized');
column.size = parseInt(Math.max(minWidth, curColWidth + diffX), 10) + 'px';

columns.forEach((col) => {
if (col.size.startsWith('minmax')) {
col.size = parseInt(col.header.clientWidth, 10) + 'px';
}
});

window.addEventListener('pointermove', onPointerMove);
window.addEventListener('pointerup', onPointerUp);
window.addEventListener('pointercancel', onPointerUp);
window.addEventListener('pointerleave', onPointerUp);
if (isGrid) {
gridElement.style.gridTemplateColumns = columns
.map(({ size }) => size)
.join(' ');
}
else {
curCol.style.width = column.size;
}
}
})
);

if (resizeHandle) {
resizeHandle.setPointerCapture(pointerId);
}
};
document.addEventListener('pointerup', function () {
curCol = undefined;
curColWidth = undefined;
pageX = undefined;
});
}

header.querySelector('.resize-handle').addEventListener('pointerdown', initResize);
function createDiv(height, isRTL) {
const div = document.createElement('div');
div.style.top = '5px';
div.style.position = 'absolute';
div.style.cursor = 'col-resize';
div.style.userSelect = 'none';
div.style.height = height + 'px';
div.style.width = '5px';

if (isRTL) {
div.style.left = '0px';
div.style.right = 'unset';
} else {
div.style.left = 'unset';
div.style.right = '0px';
}
return div;
}

});
function paddingDiff(col) {
if (getStyleVal(col, 'box-sizing') === 'border-box') {
return 0;
}

let initialWidths;
if (gridElement.style.gridTemplateColumns) {
initialWidths = gridElement.style.gridTemplateColumns;
const padLeft = getStyleVal(col, 'padding-left');
const padRight = getStyleVal(col, 'padding-right');
return parseInt(padLeft) + parseInt(padRight);
}
else {
initialWidths = columns
.map(({ header, size }) => size)
.join(' ');

gridElement.style.gridTemplateColumns = initialWidths;
function getStyleVal(elm, css) {
return window.getComputedStyle(elm, null).getPropertyValue(css);
}

let id = gridElement.id;
grids.push({
id,
columns,
initialWidths
});
function removeBorder(e) {
e.target.style.borderInlineEnd = '';
}
}

export function resetColumnWidths(gridElement) {


export function resetColumnWidths(gridElement) {
const isGrid = gridElement.classList.contains('grid');
const grid = grids.find(({ id }) => id === gridElement.id);
if (!grid) {
return;
Expand All @@ -270,11 +315,19 @@ export function resetColumnWidths(gridElement) {
const columnsWidths = grid.initialWidths.split(' ');

grid.columns.forEach((column, index) => {
column.size = columnsWidths[index];
if (isGrid) {
column.size = columnsWidths[index];
} else {
column.header.style.width = columnsWidths[index];
}
});

gridElement.style.gridTemplateColumns = grid.initialWidths;
gridElement.dispatchEvent(new CustomEvent('closecolumnresize', { bubbles: true }));
if (isGrid) {
gridElement.style.gridTemplateColumns = grid.initialWidths;
}
gridElement.dispatchEvent(
new CustomEvent('closecolumnresize', { bubbles: true })
);
gridElement.focus();
}

Expand Down Expand Up @@ -308,7 +361,7 @@ export function resizeColumnDiscrete(gridElement, column, change) {
}
else {
if (column.size.startsWith('minmax')) {
column.size = parseInt(column.header.clientWidth, 10) + 'px';
column.size = parseInt(column.header.clientWidth, 10) + 'px';
}
}
columns.push(column.size);
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public partial class FluentDataGridCell<TGridItem> : FluentComponentBase
.AddStyle("padding-top", "calc(var(--design-unit) * 2.5px)", Column is SelectColumn<TGridItem> && (Grid.RowSize == DataGridRowSize.Medium || Owner.RowType == DataGridRowType.Header))
.AddStyle("padding-top", "calc(var(--design-unit) * 1.5px)", Column is SelectColumn<TGridItem> && Grid.RowSize == DataGridRowSize.Small && Owner.RowType == DataGridRowType.Default)
.AddStyle("width", Column?.Width, !string.IsNullOrEmpty(Column?.Width) && Grid.DisplayMode == DataGridDisplayMode.Table)
.AddStyle("height", $"{Grid.ItemSize:0}px", () => !Grid.EffectiveLoadingValue && Grid.Virtualize && Owner.RowType == DataGridRowType.Default)
.AddStyle("height", $"{Grid.ItemSize:0}px", () => !Grid.EffectiveLoadingValue && Grid.Virtualize)
.AddStyle("height", $"{(int)Grid.RowSize}px", () => !Grid.EffectiveLoadingValue && !Grid.Virtualize && !Grid.MultiLine && (Grid.Items is not null || Grid.ItemsProvider is not null))
.AddStyle("height", "100%", Grid.MultiLine)
.AddStyle("min-height", "44px", Owner.RowType != DataGridRowType.Default)
Expand Down
20 changes: 14 additions & 6 deletions src/Core/Components/DataGrid/FluentDataGridCell.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ th, td {
}

td {
padding: calc((var(--design-unit) + var(--focus-stroke-width) - var(--stroke-width)) * 1px) calc(((var(--design-unit) * 5) + var(--focus-stroke-width) - var(--stroke-width))* 1px);
padding: 6px 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
Expand All @@ -20,10 +20,15 @@ td {
text-align: end;
}

td.grid-cell-placeholder:after {
content: '\2026'; /*horizontal ellipsis*/
opacity: 0.75;
}
th.col-justify-end > div {
justify-content: flex-end;
}


td.grid-cell-placeholder:after {
content: '\2026'; /*horizontal ellipsis*/
opacity: 0.75;
}


.empty-content-cell,
Expand Down Expand Up @@ -86,11 +91,14 @@ td {
margin-inline-start: 2px;
}

.col-justify-start ::deep .col-title {
text-align: left;
}

.col-justify-center ::deep .col-title {
text-align: center;
}

.col-justify-end ::deep .col-title {
text-align: end;
margin-inline-end: calc(var(--design-unit) * 4px);
}
Loading