Skip to content
Open
Show file tree
Hide file tree
Changes from 18 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
9 changes: 7 additions & 2 deletions src/core/body/body.ctrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PathService } from '../path/path.service';
import { Fastdom } from '../services/fastdom';
import { GRID_PREFIX } from '../definition';
import { jobLine } from '../services/job.line';
import { ScrollService } from '../scroll/scroll.service';

const MOUSE_LEFT_BUTTON = 1;
const VERTICAL_SCROLL_CLASS = `${GRID_PREFIX}-scroll-vertical`;
Expand All @@ -12,6 +13,7 @@ export class BodyCtrl {
this.model = model;
this.view = view;
this.bag = bag;
this.scrollService = new ScrollService(model, table, bag, view);
this.table = table;
this.rangeStartCell = null;
this.scrollingJob = jobLine(100);
Expand Down Expand Up @@ -131,6 +133,7 @@ export class BodyCtrl {
if (startCell && endCell) {
this.navigate(endCell);
this.view.selection.selectRange(startCell, endCell, 'body');
this.scrollService.start(e, startCell);
}
}
}
Expand All @@ -147,8 +150,10 @@ export class BodyCtrl {
}

onMouseUp(e) {
const mode = this.selection.mode;
const edit = this.model.edit;
this.scrollService.stop();

const { mode } = this.selection;
const { edit } = this.model;

if (e.which === MOUSE_LEFT_BUTTON) {
const pathFinder = new PathService(this.bag.body);
Expand Down
10 changes: 10 additions & 0 deletions src/core/scroll/scroll.model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ export declare interface ScrollModel {
*/
cursor?: number;

/**
* Scroll velocity.
*/
velocity?: number;

/**
* Defines the distance from borders where scrolling should be initiated.
*/
offset?: number;

map: {
rowToView: (index: number) => number,
viewToRow: (index: number) => number,
Expand Down
2 changes: 2 additions & 0 deletions src/core/scroll/scroll.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export class ScrollModel {
this.top = 0;
this.left = 0;
this.cursor = 0;
this.velocity = 10;
this.offset = 50;

this.map = {
rowToView: identity,
Expand Down
10 changes: 10 additions & 0 deletions src/core/scroll/scroll.service.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Bag } from '../dom/bag';
import { Model } from '../infrastructure/model';
import { Table } from '../dom/table';

export declare class ScrollService {
constructor(model: Model, table: Table, bag: Bag, view: any);

start(): void;
stop(): void;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add scroll method

}
185 changes: 185 additions & 0 deletions src/core/scroll/scroll.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { AppError } from '../infrastructure/error';
import { jobLine } from '../services/job.line';
import { PathService } from '../path/path.service';

export class ScrollService {
constructor(model, table, bag, view) {
this.model = model;
this.table = table;
this.bag = bag;
this.view = view;
this.job = jobLine(0);
this.interval = null;
this.startCell = null;
this.mouseEvent = null;

const pathFinder = new PathService(bag.body);
model.scrollChanged.watch(e => {
if (this.interval) {
const path = this.getPath(this.mouseEvent);
const td = pathFinder.cell(path);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could it be that we do not find td?


this.navigate(td);
this.view.selection.selectRange(this.startCell, td, 'body');
}
});
}

canScroll(e) {
const rect = this.rect;
const offset = this.offset;

// check if mouse is not in areas which fire scrolling
const mouseNotOnTopSide = e.clientY > (rect.top + offset);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could me make positive booleans? like cursorIsOnTop?

const mouseNotOnBottomSide = e.clientY < (rect.bottom - offset);
const mouseNotOnLeftSide = e.clientX > (rect.left + offset);
const mouseNotOnRightSide = e.clientX < (rect.right - offset);

return !(mouseNotOnTopSide && mouseNotOnBottomSide && mouseNotOnLeftSide && mouseNotOnRightSide);
}

start(e, startCell) {
this.mouseEvent = e;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we calculate td here? not in scrollChanged event

this.startCell = startCell;

if (this.interval) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how it could happen on start?

if (!this.canScroll(e)) {
this.clearInterval();
return;
}
}

const direction = this.onEdgeOf(e);
if (direction && !this.interval) {
this.job(() => {
const interval = this.scroll(direction);
this.interval = interval();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we emulate interval with jobs?

})
}
}

scroll(direction) {
const { scroll } = this.model;
const scrollState = scroll();
const { velocity } = scrollState;
const scrolledToEnd = () => this.isScrolledToEnd(direction);

return () => setInterval(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try to use job

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setInterval is located in function, which is already wrapped in Job.
image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is to tricky, we need to make own interval like a jobLine, or emulate it with job

if (!scrolledToEnd()) {
switch (direction) {
case 'right':
case 'bottom': {
const course = direction === 'bottom' ? 'top' : 'left';
const origin = scrollState[course];
scroll({ [course]: origin + velocity });
break;
}
case 'left':
case 'top': {
const course = direction === 'top' ? 'top' : 'left';
const origin = scrollState[course];
scroll({ [course]: origin - velocity });
break;
}
default: {
throw new AppError('scroll.service', `doScroll: Wrong direction`);
}
}
}
}, 50);
}

isScrolledToEnd(direction) {
const body = this.body;

switch (direction) {
case 'top': {
return body.scrollTop === 0;
}
case 'bottom': {
return body.clientHeight === body.scrollHeight - body.scrollTop;
}
case 'left': {
return body.scrollLeft === 0;
}
case 'right': {
return body.scrollLeft === body.scrollWidth - body.clientWidth;
}
default: {
throw new AppError('scroll.service', `isScrolledToEnd: Wrong direction`);
}
}
}

onEdgeOf(e) {
const rect = this.rect;
const offset = this.offset;

if (e.clientY < (rect.top + offset) &&
e.clientX > (rect.left + offset) &&
e.clientX < (rect.right - offset)) {
return 'top';
}

if (e.clientY > (rect.bottom - offset) &&
e.clientX > (rect.left + offset) &&
e.clientX < (rect.right - offset)) {
return 'bottom';
}

if (e.clientX < (rect.left + offset) &&
e.clientY > (rect.top + offset) &&
e.clientY < (rect.bottom - offset)) {
return 'left';
}

if (e.clientX > (rect.right - offset) &&
e.clientY > (rect.top + offset) &&
e.clientY < (rect.bottom - offset)) {
return 'right';
}

return false;
}

stop() {
if(this.interval) {
this.clearInterval();
}
}

clearInterval() {
clearInterval(this.interval);
this.interval = null;
}

navigate(cell) {
const { focus } = this.view.nav;
if (focus.canExecute(cell)) {
focus.execute(cell);
}
}

getPath(e) {
const path = [];
let element = document.elementFromPoint(e.clientX, e.clientY);
while (element) {
path.push(element);
element = element.parentElement;
}

return path;
}

get rect() {
return this.table.view.rect(this.body);
}

get body() {
return this.table.view.markup.body;
}

get offset() {
return this.model.scroll().offset;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write unit tests

15 changes: 12 additions & 3 deletions src/lib/main/core/body/body-core.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, ElementRef, OnInit, NgZone, Input, ChangeDetectorRef } from '@angular/core';
import { Component, ElementRef, OnInit, NgZone, Input, ChangeDetectorRef, Inject } from '@angular/core';
import { EventListener } from 'ng2-qgrid/core/infrastructure/event.listener';
import { EventManager } from 'ng2-qgrid/core/infrastructure/event.manager';
import { ColumnView } from 'ng2-qgrid/core/scene/view/column.view';
Expand All @@ -12,7 +12,8 @@ import { TableCoreService } from '../table/table-core.service';

@Component({
selector: 'tbody[q-grid-core-body]',
templateUrl: './body-core.component.html'
templateUrl: './body-core.component.html',
providers: [{ provide: 'window', useValue: window }]
})
export class BodyCoreComponent extends NgComponent implements OnInit {
@Input() pin = 'body';
Expand All @@ -21,6 +22,7 @@ export class BodyCoreComponent extends NgComponent implements OnInit {
rowId: (index: number, row: any) => any;

constructor(
@Inject('window') private window: Window,
private element: ElementRef,
public $view: ViewCoreService,
public $table: TableCoreService,
Expand All @@ -39,6 +41,7 @@ export class BodyCoreComponent extends NgComponent implements OnInit {
const table = this.$table;
const ctrl = new BodyCtrl(model, view, this.root.table, this.root.bag);
const listener = new EventListener(element, new EventManager(this));
const windowListener = new EventListener(this.window, new EventManager(this));

this.zone.runOutsideAngular(() => {
listener.on('wheel', e => ctrl.onWheel(e));
Expand All @@ -56,7 +59,13 @@ export class BodyCoreComponent extends NgComponent implements OnInit {
});

listener.on('mousedown', ctrl.onMouseDown.bind(ctrl));
listener.on('mouseup', ctrl.onMouseUp.bind(ctrl));

windowListener.on('mouseup', (e) => {
const isActive = model.focus().isActive;
if (isActive) {
ctrl.onMouseUp(e);
}
});

const { id } = model.data();
this.rowId = id.row;
Expand Down
11 changes: 3 additions & 8 deletions src/lib/main/core/cell/cell-handler.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,9 @@ export class CellHandlerComponent implements OnInit, AfterViewInit {
}

get isMarkerVisible() {
const model = this.root.model;
const { column } = model.navigation();

if (column) {
const type = column.type;
return model.edit().method === 'batch';
}
const { model } = this.root;
const { method } = model.edit();

return false;
return method === 'batch';
}
}