-
Notifications
You must be signed in to change notification settings - Fork 27
ScrollService #344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
ScrollService #344
Changes from 14 commits
0f2713e
1c83c89
6c734e4
4496701
758ca97
35c0ae0
2ad0db8
07397f9
12094ab
a8d03cb
16281de
64d046c
039d931
50e16a2
b1793af
d5c271b
4b0c891
8b853fa
590b200
43e4f76
a7dc051
e0bee6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { Model } from '../infrastructure/model'; | ||
| import { Table } from '../dom/table'; | ||
|
|
||
| export declare class ScrollService { | ||
| constructor(model: Model, table: Table); | ||
|
|
||
| stop(): void; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add scroll method |
||
| invalidate(): void; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| 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.interval = null; | ||
| this.rect = null; | ||
| this.body = null; | ||
|
||
| this.job = jobLine(0); | ||
| this.startCell = null; | ||
| this.mousePosition = null; | ||
|
|
||
| const pathFinder = new PathService(bag.body); | ||
| model.scrollChanged.watch(e => { | ||
| if (this.interval) { | ||
| const path = this.getPath(this.mousePosition); | ||
| const td = pathFinder.cell(path); | ||
|
||
|
|
||
| this.navigate(td); | ||
| this.view.selection.selectRange(this.startCell, td, 'body'); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| canScroll(e) { | ||
| const table = this.rect; | ||
|
||
| const offset = this.offset; | ||
|
|
||
| return !(e.clientY < (table.bottom - offset) && | ||
|
||
| e.clientY > (table.top + offset) && | ||
| e.clientX > (table.left + offset) && | ||
| e.clientX < (table.right - offset)) | ||
| } | ||
|
|
||
| scroll(e, startCell) { | ||
|
||
| this.mousePosition = e; | ||
| this.startCell = startCell; | ||
|
|
||
| if (!this.body) { | ||
| this.invalidate(); | ||
| } | ||
|
|
||
| if (this.interval) { | ||
|
||
| if (!this.canScroll(e)) { | ||
| this.clearInterval(); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| const direction = this.onEdgeOf(e); | ||
| if (direction && !this.interval) { | ||
| this.job(() => { | ||
| const interval = this.doScroll(direction); | ||
| this.interval = interval(); | ||
|
||
| }) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| doScroll(direction) { | ||
|
||
| const { scroll } = this.model; | ||
| const scrollState = scroll(); | ||
| const { velocity } = scrollState; | ||
| const scrolledToEnd = () => this.isScrolledToEnd(direction); | ||
|
|
||
| return () => setInterval(() => { | ||
|
||
| 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 table = this.rect; | ||
| const offset = this.offset; | ||
|
|
||
| if (e.clientY < (table.top + offset) && | ||
| e.clientX > (table.left + offset) && | ||
| e.clientX < (table.right - offset)) { | ||
| return 'top'; | ||
| } | ||
|
|
||
| if (e.clientY > (table.bottom - offset) && | ||
| e.clientX > (table.left + offset) && | ||
| e.clientX < (table.right - offset)) { | ||
| return 'bottom'; | ||
| } | ||
|
|
||
| if (e.clientX < (table.left + offset) && | ||
| e.clientY > (table.top + offset) && | ||
| e.clientY < (table.bottom - offset)) { | ||
| return 'left'; | ||
| } | ||
|
|
||
| if (e.clientX > (table.right - offset) && | ||
| e.clientY > (table.top + offset) && | ||
| e.clientY < (table.bottom - offset)) { | ||
| return 'right'; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| invalidate() { | ||
| const { view } = this.table; | ||
|
|
||
| this.body = view.markup.body; | ||
| this.rect = view.rect(this.body); | ||
| } | ||
|
|
||
| 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 offset() { | ||
| return this.model.scroll().offset; | ||
| } | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Write unit tests |
||
| 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'; | ||
|
|
@@ -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'; | ||
|
|
@@ -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, | ||
|
|
@@ -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)); | ||
|
|
@@ -56,7 +59,14 @@ export class BodyCoreComponent extends NgComponent implements OnInit { | |
| }); | ||
|
|
||
| listener.on('mousedown', ctrl.onMouseDown.bind(ctrl)); | ||
| listener.on('mouseup', ctrl.onMouseUp.bind(ctrl)); | ||
|
|
||
| windowListener.on('resize', () => ctrl.resize()); | ||
|
||
| windowListener.on('mouseup', (e) => { | ||
| const isActive = model.focus().isActive; | ||
| if (isActive) { | ||
| ctrl.onMouseUp(e); | ||
| } | ||
| }); | ||
|
|
||
| const { id } = model.data(); | ||
| this.rowId = id.row; | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont use ng2-qgrid in core use relative path