Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion docs
Submodule docs updated 275 files
137 changes: 132 additions & 5 deletions e2e/reordering.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ test.describe('reordering', () => {

await mountGrid(page, { columns, source });

const dragHandle = mainDataRows(page).first().locator('[data-rgCol="0"] .revo-draggable');
const dragHandle = mainDataRows(page)
.first()
.locator('[data-rgCol="0"] .revo-draggable');
const targetRow = mainDataRows(page).nth(2);
const handleBox = await dragHandle.boundingBox();
const targetBox = await targetRow.boundingBox();

expect(handleBox).not.toBeNull();
expect(targetBox).not.toBeNull();

await page.mouse.move(handleBox!.x + handleBox!.width / 2, handleBox!.y + handleBox!.height / 2);
await page.mouse.move(
handleBox!.x + handleBox!.width / 2,
handleBox!.y + handleBox!.height / 2,
);
await page.mouse.down();
await page.mouse.move(
targetBox!.x + targetBox!.width / 2,
Expand Down Expand Up @@ -77,15 +82,26 @@ test.describe('reordering', () => {
};
}) as EventListener);
});
const from = page.locator('revo-grid revogr-header .header-rgRow.actual-rgRow .rgHeaderCell').nth(2);
const to = page.locator('revo-grid revogr-header .header-rgRow.actual-rgRow .rgHeaderCell').first();
const from = page
.locator(
'revo-grid revogr-header .header-rgRow.actual-rgRow .rgHeaderCell',
)
.nth(2);
const to = page
.locator(
'revo-grid revogr-header .header-rgRow.actual-rgRow .rgHeaderCell',
)
.first();
const fromBox = await from.boundingBox();
const toBox = await to.boundingBox();

expect(fromBox).not.toBeNull();
expect(toBox).not.toBeNull();

await page.mouse.move(fromBox!.x + fromBox!.width / 2, fromBox!.y + fromBox!.height / 2);
await page.mouse.move(
fromBox!.x + fromBox!.width / 2,
fromBox!.y + fromBox!.height / 2,
);
await page.mouse.down();
await page.mouse.move(toBox!.x + 4, toBox!.y + toBox!.height / 2, {
steps: 14,
Expand All @@ -103,4 +119,115 @@ test.describe('reordering', () => {
type: 'rgCol',
});
});

test('does not sort when a sortable header drag drops on the same column', async ({
page,
}) => {
const source = [
{ id: 1, name: 'Charlie', role: 'Engineer' },
{ id: 2, name: 'Alice', role: 'Designer' },
{ id: 3, name: 'Bob', role: 'Manager' },
];

const columns = buildColumns([
{ prop: 'id', name: 'ID', sortable: true },
{ prop: 'name', name: 'Name', sortable: true },
{ prop: 'role', name: 'Role', sortable: true },
]);

await mountGrid(page, {
columns,
source,
canMoveColumns: true,
});

const headers = page.locator(
'revo-grid revogr-header .header-rgRow.actual-rgRow .rgHeaderCell',
);
const nameHeader = headers.nth(1);
const headerBox = await nameHeader.boundingBox();

expect(headerBox).not.toBeNull();

const centerX = headerBox!.x + headerBox!.width / 2;
const centerY = headerBox!.y + headerBox!.height / 2;

await page.mouse.move(centerX, centerY);
await page.mouse.down();
await page.mouse.move(centerX + 20, centerY, { steps: 6 });
await page.waitForTimeout(20);
await page.mouse.move(centerX, centerY, { steps: 6 });
await page.mouse.up();

await expect
.poll(() => visibleHeaderTexts(page))
.toEqual(['ID', 'Name', 'Role']);
await expect
.poll(() => visibleColumnValues(page, 1))
.toEqual(['Charlie', 'Alice', 'Bob']);

await nameHeader.click();

await expect
.poll(() => visibleColumnValues(page, 1))
.toEqual(['Alice', 'Bob', 'Charlie']);
});

test('places right-to-left column drag indicator at the insertion edge', async ({
page,
}) => {
const source = [{ a: 'a1', b: 'b1', c: 'c1', d: 'd1' }];

const columns = buildColumns([
{ prop: 'a', name: 'A' },
{ prop: 'b', name: 'B' },
{ prop: 'c', name: 'C' },
{ prop: 'd', name: 'D' },
]);

await mountGrid(page, {
columns,
source,
canMoveColumns: true,
colSize: 100,
});

const headers = page.locator(
'revo-grid revogr-header .header-rgRow.actual-rgRow .rgHeaderCell',
);
const from = headers.nth(3);
const to = headers.nth(1);
const fromBox = await from.boundingBox();
const toBox = await to.boundingBox();

expect(fromBox).not.toBeNull();
expect(toBox).not.toBeNull();

await page.mouse.move(
fromBox!.x + fromBox!.width / 2,
fromBox!.y + fromBox!.height / 2,
);
await page.mouse.down();
await page.mouse.move(
toBox!.x + toBox!.width / 2,
toBox!.y + toBox!.height / 2,
{
steps: 20,
},
);

const indicator = page.locator('revo-grid > .drag-position-y');
await expect
.poll(async () => {
const box = await indicator.boundingBox();
return Math.abs((box?.x ?? Number.POSITIVE_INFINITY) - toBox!.x);
})
.toBeLessThanOrEqual(1);

await page.mouse.up();

await expect
.poll(() => visibleHeaderTexts(page))
.toEqual(['A', 'D', 'B', 'C']);
});
});
2 changes: 1 addition & 1 deletion formats/date
2 changes: 1 addition & 1 deletion formats/number
2 changes: 1 addition & 1 deletion packages/vue2
2 changes: 1 addition & 1 deletion packages/vue3
107 changes: 85 additions & 22 deletions src/plugins/moveColumn/column.drag.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,17 @@ declare global {

export class ColumnMovePlugin extends BasePlugin {
protected moveFunc = debounce((e: MouseEvent) => this.doMove(e), 5);
protected preventHeaderClickAfterDrag = (event: Event) => {
if (!this.preventNextHeaderClick) {
return;
}
this.preventNextHeaderClick = false;
event.preventDefault();
};
protected staticDragData: StaticData | null = null;
protected dragData: ColumnDragEventData | null = null;
protected columnDragMoved = false;
protected preventNextHeaderClick = false;
readonly orderUi: ColumnOrderHandler;
readonly localSubscriptions: LocalSubscriptions = {};

Expand All @@ -109,12 +118,18 @@ export class ColumnMovePlugin extends BasePlugin {
};

this.addEventListener(COLUMN_CLICK, ({ detail }) => this.dragStart(detail));
this.revogrid.addEventListener(
'beforeheaderclick',
this.preventHeaderClickAfterDrag,
{ capture: true },
);
}

dragStart({ event, data }: DragStartEventDetails) {
if (event.defaultPrevented) {
return;
}
this.preventNextHeaderClick = false;
const { defaultPrevented } = dispatch(
this.revogrid,
COLUMN_DRAG_START_EVENT,
Expand All @@ -130,7 +145,9 @@ export class ColumnMovePlugin extends BasePlugin {
mouseup.target.addEventListener('mouseup', mouseup.callback);

const dataEl = (event.target as HTMLElement).closest('revogr-header');
const scrollEl = (event.target as HTMLElement).closest('revogr-viewport-scroll');
const scrollEl = (event.target as HTMLElement).closest(
'revogr-viewport-scroll',
);
if (!dataEl || !scrollEl) {
return;
}
Expand All @@ -143,11 +160,11 @@ export class ColumnMovePlugin extends BasePlugin {
const cols = this.getDimension(data.pin || 'rgCol');
const gridRect = this.revogrid.getBoundingClientRect();
const elRect = dataEl.getBoundingClientRect();
const startItem = getItemByPosition(cols, getLeftRelative(
event.x,
gridRect.left,
elRect.left - gridRect.left,
) + (cols.renderOffset || 0));
const startItem = getItemByPosition(
cols,
getLeftRelative(event.x, gridRect.left, elRect.left - gridRect.left) +
(cols.renderOffset || 0),
);

this.staticDragData = {
startPos: event.x,
Expand Down Expand Up @@ -193,13 +210,24 @@ export class ColumnMovePlugin extends BasePlugin {
return;
}
this.orderUi.showHandler(
rgCol.end - (this.staticDragData.cols.renderOffset || 0) + dragData.scrollOffset,
dragData.gridRect.width
getColumnDragPosition(
rgCol,
this.staticDragData.startItem,
this.staticDragData.cols.renderOffset || 0,
dragData.scrollOffset,
),
dragData.gridRect.width,
);
}
}

move(e: MouseEvent) {
if (
this.staticDragData &&
Math.abs(this.staticDragData.startPos - e.x) > 10
) {
this.columnDragMoved = true;
}
dispatch(this.revogrid, COLUMN_DRAG_MOVE_EVENT, e);
// then do move
this.moveFunc(e);
Expand All @@ -208,6 +236,7 @@ export class ColumnMovePlugin extends BasePlugin {
this.clearOrder();
}
onMouseUp(e: MouseEvent) {
const suppressClick = this.columnDragMoved;
// apply new positions
if (this.dragData && this.staticDragData) {
let relativePos = getLeftRelative(
Expand All @@ -228,40 +257,56 @@ export class ColumnMovePlugin extends BasePlugin {
const newItems = [...store.get('items')];

// prevent position change if needed
const { defaultPrevented: stopDrag } = dispatch<BeforeColumnDragEndEventData>(
this.revogrid,
BEFORE_COLUMN_DRAG_END_EVENT,
{
...this.staticDragData,
startPosition: this.staticDragData.startItem,
newPosition,
newItem: source[newItems[this.staticDragData.startItem.itemIndex]],
},
);
const { defaultPrevented: stopDrag } =
dispatch<BeforeColumnDragEndEventData>(
this.revogrid,
BEFORE_COLUMN_DRAG_END_EVENT,
{
...this.staticDragData,
startPosition: this.staticDragData.startItem,
newPosition,
newItem: source[newItems[this.staticDragData.startItem.itemIndex]],
},
);
if (!stopDrag) {
const prevItems = [...newItems];
// todo: if move item out of group remove item from group
const toMove = newItems.splice(this.staticDragData.startItem.itemIndex, 1);
const toMove = newItems.splice(
this.staticDragData.startItem.itemIndex,
1,
);
newItems.splice(newPosition.itemIndex, 0, ...toMove);
store.set('items', newItems);
this.providers.dimension.updateSizesPositionByNewDataIndexes(this.dragData.type, newItems, prevItems);
this.providers.dimension.updateSizesPositionByNewDataIndexes(
this.dragData.type,
newItems,
prevItems,
);
}
dispatch(
this.revogrid,
COLUMN_DRAG_END_EVENT,
this.getData(this.staticDragData, newItems, source),
);
}
if (suppressClick) {
this.preventNextHeaderClick = !!(e.target as HTMLElement).closest(
'revogr-header',
);
}
this.clearOrder();
}

protected clearLocalSubscriptions() {
each(this.localSubscriptions, ({ target, callback }, key) => target.removeEventListener(key, callback));
each(this.localSubscriptions, ({ target, callback }, key) =>
target.removeEventListener(key, callback),
);
}

clearOrder() {
this.staticDragData = null;
this.dragData = null;
this.columnDragMoved = false;
this.clearLocalSubscriptions();
this.orderUi.stop(this.revogrid);
}
Expand All @@ -271,6 +316,11 @@ export class ColumnMovePlugin extends BasePlugin {
clearSubscriptions() {
super.clearSubscriptions();
this.clearLocalSubscriptions();
this.revogrid.removeEventListener(
'beforeheaderclick',
this.preventHeaderClickAfterDrag,
{ capture: true },
);
}

protected getData(
Expand Down Expand Up @@ -298,7 +348,20 @@ export class ColumnMovePlugin extends BasePlugin {
export function getLeftRelative(
absoluteX: number,
gridPos: number,
offset: number
offset: number,
): number {
return absoluteX - gridPos - offset;
}

export function getColumnDragPosition(
targetItem: PositionItem,
startItem: PositionItem,
renderOffset: number,
scrollOffset: number,
): number {
const insertionEdge =
startItem.itemIndex > targetItem.itemIndex
? targetItem.start
: targetItem.end;
return insertionEdge - renderOffset + scrollOffset;
}
Loading