Reputation: 195
I have this working Plunker Example, you can drag the mouse and select the cards.
My problem is that the code has alot of bugs and I want to do something similar to this Demo
Here something that I want to fix (img1 and img2 - the cards in img #2 were clicked before), another thing is that if you drag the mouse fast sometimes the cards will not be select.
Here is my component code
export class App {
private dragStart:number = 0;
private dragOver:number = 0;
public users:Array<{id?: number; name: string; admin: boolean;}> = [
{ name: 'Alexis Wursten', admin: false },
{ name: 'Janco Boscan', admin: true },
{ name: 'Noemi Iturralde', admin: false },
];
public added: string[] = [];
x1 = 0; y1 = 0; x2 = 0; y2 = 0;
@ViewChild('selector') selector: ElementRef;
constructor(private renderer: Renderer2) {
}
isRectangeVisible = false;
isMouseDown = false;
@HostListener('mousedown', ['$event'])
onMouseDown(ev) {
this.dragStart = ev.clientY;
this.isMouseDown = true;
}
@HostListener('document:mouseup', ['$event'])
onMouseUp(ev) {
this.dragStart = 0;
this.dragOver = 0;
this.renderer.setStyle(this.selector.nativeElement, 'display', 'none');
this.isRectangeVisible = false;
this.isMouseDown = false;
}
@HostListener('document:mousemove', ['$event'])
onMouseMove(ev) {
if(!this.isRectangeVisible && this.isMouseDown){
this.renderer.setStyle(this.selector.nativeElement, 'display', 'block');
this.x1 = ev.clientX;
this.y1 = ev.clientY;
this.isRectangeVisible = true;
}
this.x2 = ev.clientX;
this.y2 = ev.clientY;
this.reCalc();
}
reCalc() {
const x3 = Math.min(this.x1, this.x2);
const x4 = Math.max(this.x1, this.x2);
const y3 = Math.min(this.y1, this.y2);
const y4 = Math.max(this.y1, this.y2);
this.renderer.setStyle(this.selector.nativeElement, 'left', x3 + 'px');
this.renderer.setStyle(this.selector.nativeElement, 'top', y3 + 'px');
this.renderer.setStyle(this.selector.nativeElement, 'width', x4 - x3 + 'px');
this.renderer.setStyle(this.selector.nativeElement, 'height', y4 - y3 + 'px');
}
onSelecUser(item) {
if(this.added.indexOf(item.name)===-1) { // or compare by id
this.added = this.added.concat([item.name]);
}
else {
this.added = this.added.filter((x) => item.name!==x); // or compare by id
}
item.selected = !item.selected ? true : false;
}
onMouseOver(ev, item) {
if(ev.which!==1) {
return false;
}
ev.preventDefault();
if(ev.type==='mouseenter' && !item.selected) {
this.dragOver = ev.clientY - this.dragStart > 0 ? 1:-1;
this.onSelecUser(item);
return false;
}
if(ev.type==='mouseleave') {
if(this.dragOver===1 && ev.clientY < ev.target.offsetTop && item.selected) {
console.log('desel...', item);
this.onSelecUser(item);
return false;
}
if(this.dragOver===-1 && ev.clientY > ev.target.offsetTop && item.selected) {
console.log('desel...', item);
this.onSelecUser(item);
return false;
}
}
}
}
Thanks for read.
Upvotes: 1
Views: 437
Reputation: 2081
UPDATE #1: https://plnkr.co/edit/d9aTb0E0OKFfTSIAM0MY?p=preview
Added option to select/unselect a user by a click.
For that, no reset code needed.
@HostListener('mousedown', ['$event'])
onMouseDown(ev) {
this.dragStart = ev.clientY;
this.isMouseDown = true;
}
Only div's click handler changed.
(click)="onSelecPersona(user, !user.selected)"
INITIAL ANSWER: Here is modified code: https://plnkr.co/edit/QryFWtLQwNuGkrtzDehm?p=preview
It solves few issues:
(1) HTML selection: the "user-select" CSS should be on the "row" no on the "card" because the selection starts at the "row" boundaries
.row {
user-select: none;
-moz-user-select: none;
}
.card-content {
padding: 0;
}
(2) Handling of selected divs: initial implementation relies on mouse events on the user's div. That does not handle the case when "selector" rectangle never crosses "user" div's boundaries (i.e. goes around but still within selection boundaries). My implementation calculates an overlap of a "selector" and "user" divs to determine if a user selected.
<div class="card"
#ucard
[attr.id]="user.name"
[class.selected]="user.selected"
*ngFor="let user of users"
(click)="onSelecPersona(user, !user.selected)"
>
import {Component, NgModule, HostListener, Renderer2, ElementRef, ViewChild, ViewChildren } from '@angular/core'
...
@ViewChildren('ucard') components: QueryList<ElementRef>;
...
// return true if two HTML elements overlap
overlap(e1:ElementRef, e2:ElementRef){
var rect1 = e1.getBoundingClientRect();
var rect2 = e2.getBoundingClientRect();
return !(
rect1.top > rect2.bottom ||
rect1.right < rect2.left ||
rect1.bottom < rect2.top ||
rect1.left > rect2.right
);
}
// updates user selection based on the current "selector"
markSelected(){
this.components.forEach(it=> {
var overlaps: boolean = this.overlap(this.selector.nativeElement, it.nativeElement);
this.onSelecPersona(this.users.find(u=> u.name == it.nativeElement.id), overlaps);
});
}
Upvotes: 2