Skip to content

Commit

Permalink
fix(lib): start selection inbetween select items
Browse files Browse the repository at this point in the history
This commit fixes the issue of not being able to start a selection if
mousedown occured on neither Host element nor select item element
but inbetween. The issue occurs if `SelectItem`s are not direct children
of the `SelectContainer`.

Fixes d3lm#144
  • Loading branch information
Philip Odermatt committed Jul 31, 2024
1 parent 75a05d4 commit 23c2665
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 2 deletions.
74 changes: 74 additions & 0 deletions cypress/integration/dragging.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { DEFAULT_CONFIG } from '../../projects/ngx-drag-to-select/src/lib/config';

import {
disableDragOverItems,
disableSelection,
disableSelectOnClick,
disableSelectOnDrag,
enableSelectMode,
getDesktopExample,
Expand Down Expand Up @@ -91,6 +93,78 @@ describe('Dragging', () => {
.dispatch('mouseup');
});
});

describe('Selection with dragOverItems set to false', () => {
it('should not start selection over items', () => {
disableDragOverItems().then(() => {
getDesktopExample().within(() => {
cy.getSelectItem(0)
.dispatch('mousedown', { button: 0 })
.getSelectItem(6, 'end')
.dispatch('mousemove')
.shouldSelect([1])
.getSelectBox()
.then(shouldBeInvisible)
.get('@end')
.dispatch('mouseup');
});
});
});

it('should start selection on element inbetween SelectContainer and SelectItem', () => {
disableDragOverItems().then(() => {
getDesktopExample().within(() => {
cy.get('mat-grid-list')
.as('end')
.scrollIntoView()
.trigger('mousedown', 210, 50, { button: 0 })
.getSelectItem(6)
.dispatch('mousemove')
.shouldSelect([2, 3, 6, 7])
.getSelectBox()
.then(shouldBeVisible)
.get('@end')
.dispatch('mouseup');
});
});
});
});

describe('Selection with selectOnClick set to false', () => {
it('should not start selection over items', () => {
disableSelectOnClick().then(() => {
getDesktopExample().within(() => {
cy.getSelectItem(0)
.dispatch('mousedown', { button: 0 })
.getSelectItem(6, 'end')
.dispatch('mousemove')
.shouldSelect([])
.getSelectBox()
.then(shouldBeInvisible)
.get('@end')
.dispatch('mouseup');
});
});
});

it('should start selection on element inbetween SelectContainer and SelectItem', () => {
disableSelectOnClick().then(() => {
getDesktopExample().within(() => {
cy.get('mat-grid-list')
.as('end')
.scrollIntoView()
.trigger('mousedown', 210, 50, { button: 0 })
.getSelectItem(6)
.dispatch('mousemove')
.shouldSelect([2, 3, 6, 7])
.getSelectBox()
.then(shouldBeVisible)
.get('@end')
.dispatch('mouseup');
});
});
});
});
});

describe('Keyboard Events', () => {
Expand Down
4 changes: 4 additions & 0 deletions cypress/support/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export const disableSelectOnDrag = () => {
return cy.get('[data-cy="selectOnDrag"]').click();
};

export const disableDragOverItems = () => {
return cy.get('[data-cy="dragOverItems"]').click();
};

export const disableSelectOnClick = () => {
return cy.get('[data-cy="selectOnClick"]').click();
};
Expand Down
23 changes: 21 additions & 2 deletions projects/ngx-drag-to-select/src/lib/select-container.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export class SelectContainerComponent implements AfterViewInit, OnDestroy, After

private _selectedItems$ = new BehaviorSubject<Array<any>>([]);
private _selectableItems: Array<SelectItemDirective> = [];
private _selectableItemsNative: Array<HTMLElement> = [];
private updateItems$ = new Subject<UpdateAction>();
private destroy$ = new Subject<void>();

Expand Down Expand Up @@ -163,7 +164,7 @@ export class SelectContainerComponent implements AfterViewInit, OnDestroy, After
const mousedown$ = fromEvent<MouseEvent>(this.host, 'mousedown').pipe(
filter((event) => event.button === 0), // only emit left mouse
filter(() => !this.disabled),
filter((event) => this.selectOnClick || event.target === this.host),
filter((event) => this.selectOnClick || this._isClickOutsideSelectableItem(event.target)),
tap((event) => this._onMouseDown(event)),
share()
);
Expand All @@ -172,7 +173,7 @@ export class SelectContainerComponent implements AfterViewInit, OnDestroy, After
filter((event) => !this.shortcuts.disableSelection(event)),
filter(() => !this.selectMode),
filter(() => !this.disableDrag),
filter((event) => this.dragOverItems || event.target === this.host),
filter((event) => this.dragOverItems || this._isClickOutsideSelectableItem(event.target)),
switchMap(() => mousemove$.pipe(takeUntil(mouseup$))),
share()
);
Expand Down Expand Up @@ -265,6 +266,7 @@ export class SelectContainerComponent implements AfterViewInit, OnDestroy, After

ngAfterContentInit() {
this._selectableItems = this.$selectableItems.toArray();
this._selectableItemsNative = this._selectableItems.map((directive) => directive.nativeElememnt);
}

selectAll() {
Expand Down Expand Up @@ -351,6 +353,7 @@ export class SelectContainerComponent implements AfterViewInit, OnDestroy, After
.subscribe(([items, selectedItems]: [QueryList<SelectItemDirective>, any[]]) => {
const newList = items.toArray();
this._selectableItems = newList;
this._selectableItemsNative = this._selectableItems.map((directive) => directive.nativeElememnt);
const newValues = newList.map((item) => item.value);
const removedItems = selectedItems.filter((item) => !newValues.includes(item));

Expand Down Expand Up @@ -680,4 +683,20 @@ export class SelectContainerComponent implements AfterViewInit, OnDestroy, After

return null;
}

private _isClickOutsideSelectableItem(element: EventTarget): boolean {
if (!(element instanceof HTMLElement)) {
return false;
}

if (element === this.host) {
return true;
}

if (this._selectableItemsNative.includes(element)) {
return false;
}

return this._isClickOutsideSelectableItem(element.parentElement);
}
}

0 comments on commit 23c2665

Please sign in to comment.