diff --git a/timeline-chart/package.json b/timeline-chart/package.json index 202624f..764113c 100644 --- a/timeline-chart/package.json +++ b/timeline-chart/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@types/lodash.throttle": "^4.1.4", + "keyboard-key": "1.1.0", "lodash.throttle": "^4.1.1", "pixi.js": "^5.0.0", "rimraf": "latest" diff --git a/timeline-chart/src/layer/time-graph-chart-cursors.ts b/timeline-chart/src/layer/time-graph-chart-cursors.ts index 8c6a7a8..d0a2199 100644 --- a/timeline-chart/src/layer/time-graph-chart-cursors.ts +++ b/timeline-chart/src/layer/time-graph-chart-cursors.ts @@ -1,4 +1,5 @@ import * as PIXI from "pixi.js" +import * as keyboardKey from "keyboard-key" import { TimeGraphCursor } from "../components/time-graph-cursor"; import { TimelineChart } from "../time-graph-model"; @@ -26,13 +27,13 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer { this.stage.interactive = true; document.addEventListener('keydown', (event: KeyboardEvent) => { this.shiftKeyDown = event.shiftKey; - if (event.keyCode === 37) { + if (event.keyCode === keyboardKey.ArrowLeft) { this.navigateOrSelectLeft(); - } else if (event.keyCode === 39) { + } else if (event.keyCode === keyboardKey.ArrowRight) { this.navigateOrSelectRight(); - } else if(event.keyCode === 38){ + } else if (event.keyCode === keyboardKey.ArrowUp) { this.navigateUp(); - } else if(event.keyCode === 40){ + } else if (event.keyCode === keyboardKey.ArrowDown) { this.navigateDown(); } }); @@ -136,16 +137,16 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer { } } - protected navigateDown(){ + protected navigateDown() { const rows = this.chartLayer.getRowModels(); let selectedRow = this.rowController.selectedRow; const idx = rows.findIndex(row => row === selectedRow); - if(idx < rows.length){ + if (idx < rows.length) { this.chartLayer.selectRow(rows[idx + 1]); } selectedRow = this.rowController.selectedRow; const state = selectedRow.states.find(state => { - if(this.unitController.selectionRange){ + if (this.unitController.selectionRange) { return state.range.start <= this.unitController.selectionRange.start && state.range.end > this.unitController.selectionRange.start; } return false; @@ -153,16 +154,16 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer { state && this.chartLayer.selectRowElement(state); } - protected navigateUp(){ + protected navigateUp() { const rows = this.chartLayer.getRowModels(); let selectedRow = this.rowController.selectedRow; const idx = rows.findIndex(row => row === selectedRow); - if(idx > 0){ + if (idx > 0) { this.chartLayer.selectRow(rows[idx - 1]); } selectedRow = this.rowController.selectedRow; const state = selectedRow.states.find(state => { - if(this.unitController.selectionRange){ + if (this.unitController.selectionRange) { return state.range.start <= this.unitController.selectionRange.start && state.range.end > this.unitController.selectionRange.start; } return false; @@ -200,7 +201,7 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer { if (!this.firstCursor) { this.firstCursor = new TimeGraphCursor(firstCursorOptions); this.addChild(this.firstCursor); - }else{ + } else { this.firstCursor.update(firstCursorOptions); } if (secondCursorPosition !== firstCursorPosition) { @@ -215,10 +216,10 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer { if (!this.secondCursor) { this.secondCursor = new TimeGraphCursor(secondCursorOptions); this.addChild(this.secondCursor); - }else{ + } else { this.secondCursor.update(secondCursorOptions); } - } else if(!!this.secondCursor){ + } else if (!!this.secondCursor) { this.removeChild(this.secondCursor); delete this.secondCursor; } diff --git a/timeline-chart/src/layer/time-graph-chart.ts b/timeline-chart/src/layer/time-graph-chart.ts index db1ef25..b94a8bf 100644 --- a/timeline-chart/src/layer/time-graph-chart.ts +++ b/timeline-chart/src/layer/time-graph-chart.ts @@ -21,6 +21,13 @@ export interface TimeGraphChartProviders { rowStyleProvider?: (row: TimelineChart.TimeGraphRowModel) => TimeGraphRowStyle | undefined } +export const keyBoardNavs: Record> = { + "zoomin": ['w', 'i'], + "zoomout": ['s', 'k'], + "panleft": ['a', 'j'], + "panright": ['d', 'l'] +} + export type TimeGraphRowStyleHook = (row: TimelineChart.TimeGraphRowModel) => TimeGraphRowStyle | undefined; export class TimeGraphChart extends TimeGraphChartLayer { @@ -50,38 +57,103 @@ export class TimeGraphChart extends TimeGraphChartLayer { protected afterAddToContainer() { this.shiftKeyDown = false; this.ctrlKeyDown = false; + + let mousePositionX = 1; + let mousePositionY = 1; + const moveLeft = -1; + const moveRight = 1; + const horizontalDelta = 3; + let triggerKeyEvent = false; + + const moveTimeGraph = (direction: number) => { + const moveFactor = horizontalDelta / this.stateController.zoomFactor; + let start = this.unitController.viewRange.start + (moveFactor * direction); + let end = this.unitController.viewRange.end + (moveFactor * direction); + if (start < 0) { + end = end - start; + start = 0; + } + if (end > this.unitController.absoluteRange) { + start = start - end + this.unitController.absoluteRange; + end = this.unitController.absoluteRange; + } + this.unitController.viewRange = { + start, + end + } + } + const adjustZoom = (zoomPosition: number, deltaLength: number) => { + let newViewRangeLength = this.unitController.viewRangeLength; + let xOffset = 0; + + newViewRangeLength += deltaLength; + xOffset = ((zoomPosition / this.unitController.viewRangeLength) * deltaLength); + let start = this.unitController.viewRange.start - xOffset; + if (start < 0) { + start = 0; + } + let end = start + newViewRangeLength; + if (end > this.unitController.absoluteRange) { + end = this.unitController.absoluteRange; + } + this.unitController.viewRange = { + start, + end + } + }; + + document.addEventListener('mousemove', (event: MouseEvent) => { + mousePositionX = event.offsetX; + mousePositionY = event.offsetY; + }) + document.addEventListener('keydown', (event: KeyboardEvent) => { this.shiftKeyDown = event.shiftKey; this.ctrlKeyDown = event.ctrlKey; + let keyPressed = event.key; + + if (triggerKeyEvent) { + if (keyBoardNavs['zoomin'].indexOf(keyPressed) >= 0) { + const zoomPosition = (mousePositionX / this.stateController.zoomFactor); + const deltaLength = -(mousePositionY / this.stateController.zoomFactor); + adjustZoom(zoomPosition, deltaLength); + + } else if (keyBoardNavs['zoomout'].indexOf(keyPressed) >= 0) { + const zoomPosition = (mousePositionX / this.stateController.zoomFactor); + const deltaLength = (mousePositionY / this.stateController.zoomFactor); + adjustZoom(zoomPosition, deltaLength); + + } else if (keyBoardNavs['panleft'].indexOf(keyPressed) >= 0) { + moveTimeGraph(moveLeft); + + } else if (keyBoardNavs['panright'].indexOf(keyPressed) >= 0) { + moveTimeGraph(moveRight); + } + + event.preventDefault(); + } + }); + document.addEventListener('keyup', (event: KeyboardEvent) => { this.shiftKeyDown = event.shiftKey; this.ctrlKeyDown = event.ctrlKey; }); + + this.stage.addListener('mouseover', (event: MouseEvent) => { + triggerKeyEvent = true; + }); + + this.stage.addListener('mouseout', (event: MouseEvent) => { + triggerKeyEvent = false; + }); + const mw = (ev: WheelEvent) => { if (this.ctrlKeyDown) { - let newViewRangeLength = this.unitController.viewRangeLength; - let xOffset = 0; - let moveX = false; const zoomPosition = (ev.offsetX / this.stateController.zoomFactor); const deltaLength = (ev.deltaY / this.stateController.zoomFactor); - newViewRangeLength += deltaLength; - xOffset = ((zoomPosition / this.unitController.viewRangeLength) * deltaLength); - let start = this.unitController.viewRange.start - xOffset; - if (start < 0) { - start = 0; - } - let end = start + newViewRangeLength; - if (end > this.unitController.absoluteRange) { - end = this.unitController.absoluteRange; - if (moveX) { - start = end - newViewRangeLength; - } - } - this.unitController.viewRange = { - start, - end - } + adjustZoom(zoomPosition, deltaLength); + } else if (this.shiftKeyDown) { let newViewRangeLength = this.unitController.viewRangeLength; let xOffset = 0; diff --git a/yarn.lock b/yarn.lock index dc5eecb..26d0249 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2955,6 +2955,11 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +keyboard-key@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keyboard-key/-/keyboard-key-1.1.0.tgz#6f2e8e37fa11475bb1f1d65d5174f1b35653f5b7" + integrity sha512-qkBzPTi3rlAKvX7k0/ub44sqOfXeLc/jcnGGmj5c7BJpU8eDrEVPyhCvNYAaoubbsLm9uGWwQJO1ytQK1a9/dQ== + killable@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"