Skip to content

Commit d18da57

Browse files
committed
Touchpad improvements
1 parent 7353c3d commit d18da57

40 files changed

+443
-189
lines changed

docs/3rdpartylicenses.txt

+25
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,31 @@ MIT
6666
@angular/router
6767
MIT
6868

69+
hammerjs
70+
MIT
71+
The MIT License (MIT)
72+
73+
Copyright (C) 2011-2014 by Jorik Tangelder (Eight Media)
74+
75+
Permission is hereby granted, free of charge, to any person obtaining a copy
76+
of this software and associated documentation files (the "Software"), to deal
77+
in the Software without restriction, including without limitation the rights
78+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
79+
copies of the Software, and to permit persons to whom the Software is
80+
furnished to do so, subject to the following conditions:
81+
82+
The above copyright notice and this permission notice shall be included in
83+
all copies or substantial portions of the Software.
84+
85+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
86+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
87+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
88+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
89+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
90+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
91+
THE SOFTWARE.
92+
93+
6994
ngx-color-picker
7095
MIT
7196

docs/404.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
<link rel="stylesheet" href="styles.287147369392bd18245d.css"></head>
1717
<body>
1818
<app-root></app-root>
19-
<script src="runtime.7b63b9fd40098a2e8207.js" defer=""></script><script src="polyfills.94daefd414b8355106ab.js" defer=""></script><script src="main.4cf03309b70266d39ab8.js" defer=""></script></body>
19+
<script src="runtime.7b63b9fd40098a2e8207.js" defer=""></script><script src="polyfills.94daefd414b8355106ab.js" defer=""></script><script src="main.421f4ee253e95a2161f3.js" defer=""></script></body>
2020
</html>

docs/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
<link rel="stylesheet" href="styles.287147369392bd18245d.css"></head>
1717
<body>
1818
<app-root></app-root>
19-
<script src="runtime.7b63b9fd40098a2e8207.js" defer=""></script><script src="polyfills.94daefd414b8355106ab.js" defer=""></script><script src="main.4cf03309b70266d39ab8.js" defer=""></script></body>
19+
<script src="runtime.7b63b9fd40098a2e8207.js" defer=""></script><script src="polyfills.94daefd414b8355106ab.js" defer=""></script><script src="main.421f4ee253e95a2161f3.js" defer=""></script></body>
2020
</html>

docs/main.421f4ee253e95a2161f3.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/main.4cf03309b70266d39ab8.js

-1
This file was deleted.

package-lock.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@angular/platform-browser-dynamic": "~11.2.7",
2323
"@angular/router": "~11.2.7",
2424
"@ngneat/until-destroy": "^8.0.4",
25+
"hammerjs": "^2.0.8",
2526
"ngx-color-picker": "^11.0.0",
2627
"ngx-event-modifiers": "^1.0.1",
2728
"ngx-take-until-destroy": "^5.4.0",

src/app/app.module.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
99
import { MatTooltipModule } from '@angular/material/tooltip';
1010
import { MatSliderModule } from "@angular/material/slider"
1111

12-
import { BrowserModule, EVENT_MANAGER_PLUGINS } from '@angular/platform-browser';
12+
import { BrowserModule, EVENT_MANAGER_PLUGINS, HAMMER_LOADER } from '@angular/platform-browser';
1313
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
1414
import { DelayedHoverDirective } from "../shared/DelayedHoverDirective";
1515
import { ResizableTextAreaDirective } from "../shared/resizable-textarea";
@@ -51,6 +51,13 @@ import { LandingPageComponent } from './pages/landing-page/landing-page.componen
5151
import { PagesComponent } from './pages/pages.component';
5252
import { CDNComponent } from './board/components/cdn/cdn.component';
5353

54+
import * as Hammer from 'hammerjs';
55+
import {
56+
HammerModule, HammerGestureConfig, HAMMER_GESTURE_CONFIG}
57+
from '@angular/platform-browser';
58+
59+
60+
5461
@NgModule({
5562
declarations: [
5663
AppComponent,
@@ -99,15 +106,24 @@ import { CDNComponent } from './board/components/cdn/cdn.component';
99106
MatSnackBarModule,
100107
MatTooltipModule,
101108
MatSliderModule,
102-
ColorPickerModule
109+
ColorPickerModule,
110+
HammerModule
103111
],
104112
schemas: [CUSTOM_ELEMENTS_SCHEMA],
105113
providers: [
106114
{
107115
provide: EVENT_MANAGER_PLUGINS,
108116
useClass: VueEventModifiersPlugin,
109117
multi: true
110-
}
118+
},
119+
{
120+
provide: HAMMER_GESTURE_CONFIG,
121+
useClass: HammerGestureConfig,
122+
},
123+
{
124+
provide: HAMMER_LOADER,
125+
useValue: () => new Promise(() => {})
126+
}
111127
],
112128
bootstrap: [AppComponent]
113129
})

src/app/board/board.component.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
<div #board class="board infocus" id="board" oncontextmenu="return false;" (mousedown.self)="handleMousedown( $event )" (click.self)="handleSelfClick($event)" (click)="handleClick($event)">
1+
<div #board class="board infocus" id="board" oncontextmenu="return false;"
2+
(mousedown.self)="handleMousedown( $event )" (click.self)="handleSelfClick()" (click)="handleClick()"
3+
(touchstart.self)="handleMousedown($event); handleSelfClick()" (pinchout)="zoomIn(8)" (pinchin)="zoomOut(8)">
24
<template #conn></template>
35
<svg class="svg-canvas" id="svg-canvas" >
46
</svg>

src/app/board/board.component.ts

+88-36
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class BoardComponent implements AfterViewChecked {
5353
currentBoardId: string = UUID();
5454
isAllClientsSendingData = false;
5555
canToggleClientsSendingData = true;
56+
lastTouch: Touch;
5657

5758
@Input() isReadOnly = false;
5859
@Input() loadedSave: any;
@@ -119,7 +120,7 @@ export class BoardComponent implements AfterViewChecked {
119120
else
120121
this.zoomOut();
121122
}
122-
123+
123124
ngOnInit(): void {
124125
this.board = document.getElementById("board");
125126
this.board.style.width = `${this.placingService.boardWidth}px`;
@@ -128,8 +129,8 @@ export class BoardComponent implements AfterViewChecked {
128129

129130
this.updateBoardTransform();
130131

131-
window.addEventListener("resize", event => {
132-
event.preventDefault();
132+
this.board.addEventListener("resize", e => {
133+
e.preventDefault();
133134
});
134135

135136
if(!this.isReadOnly){ // These events will not be used in readonly board
@@ -153,7 +154,23 @@ export class BoardComponent implements AfterViewChecked {
153154
this.placingService.stopCreating();
154155
this.componentChanged();
155156
}
156-
})
157+
});
158+
window.addEventListener("touchstart",(e) => {this.lastTouch = e.touches[0];});
159+
window.addEventListener("touchmove", (e) => {this.lastTouch = e.touches[0];});
160+
161+
window.addEventListener("touchend",(e: any)=>{
162+
if(this.placingService.isCreating){
163+
let posX = (this.posX - this.placingService.boardWidth * (this.placingService.boardScale - 1) / 2);
164+
let posY = (this.posY - this.placingService.boardHeight * (this.placingService.boardScale - 1) / 2);
165+
var x = Math.max(Math.min((this.lastTouch.pageX - posX) / this.placingService.boardScale - 20, this.placingService.boardWidth), 0);
166+
var y = Math.max(Math.min((this.lastTouch.pageY - posY) / this.placingService.boardScale - 80 / this.placingService.boardScale, this.placingService.boardWidth), 0);
167+
let component = this.placingService.createComponent(this.placingService.creatingItem, x, y, this.placingService.creatingItemOptions);
168+
this.pushComponent(component);
169+
this.placingService.stopCreating();
170+
this.componentChanged();
171+
}
172+
return true;
173+
});
157174
this.placingService.componentChanged.subscribe(()=>{
158175
// Some component just got changed, change will be added for undo
159176
this.componentChanged();
@@ -352,73 +369,108 @@ export class BoardComponent implements AfterViewChecked {
352369
}, 1000);
353370
}
354371

355-
public handleMousedown(event: Event) {
356-
let e = event as MouseEvent;
357-
if(e.button == 0 && !this.isReadOnly){
358-
// Start selecting
359-
this.showContextMenu = false;
360-
this.showComponentContextMenu = false;
361-
this.selectionService.startSelecting(e, this.placingService.boardScale)
372+
public handleMousedown(e: Event) {
373+
if(e instanceof MouseEvent){
374+
if(e.button == 0 && !this.isReadOnly){
375+
// Start selecting
376+
this.showContextMenu = false;
377+
this.showComponentContextMenu = false;
378+
this.selectionService.startSelecting(e, this.placingService.boardScale)
379+
}
380+
else if(this.isReadOnly || e.button == 1 || e.button == 2){
381+
e.preventDefault();
382+
if(!this.placingService.canDrag())
383+
return;
384+
this.board.classList.add("moving")
385+
this.board.addEventListener( "mousemove", this.handleMousemove );
386+
window.addEventListener( "mouseup", this.handleMouseup );
387+
}
362388
}
363-
else if(this.isReadOnly || e.button == 1 || e.button == 2){
389+
else if(e instanceof TouchEvent){
364390
e.preventDefault();
365391
if(!this.placingService.canDrag())
366392
return;
393+
this.lastTouchMoveX = e.touches[0].clientX;
394+
this.lastTouchMoveY = e.touches[0].clientY;
367395
this.board.classList.add("moving")
368-
this.board.addEventListener( "mousemove", this.handleMousemove );
369-
window.addEventListener( "mouseup", this.handleMouseup );
396+
this.board.addEventListener( "touchmove", this.handleMousemove );
397+
window.addEventListener( "touchend", this.handleMouseup );
370398
}
399+
371400
}
372401

373-
public handleMousemove = ( event: MouseEvent ): void => {
374-
this.boardMoved = true;
375-
this.showContextMenu = false;
376-
this.showComponentContextMenu = false;
377-
this.posX += event.movementX;
378-
this.posY += event.movementY;
402+
lastTouchMoveX: number = 0;
403+
lastTouchMoveY: number = 0;
404+
405+
public handleMousemove = ( e: Event ): void => {
406+
if(e instanceof MouseEvent){
407+
this.boardMoved = true;
408+
this.showContextMenu = false;
409+
this.showComponentContextMenu = false;
410+
this.posX += e.movementX;
411+
this.posY += e.movementY;
412+
}
413+
else if(e instanceof TouchEvent){
414+
this.boardMoved = true;
415+
this.showContextMenu = false;
416+
this.showComponentContextMenu = false;
417+
this.posX += e.touches[0].clientX - this.lastTouchMoveX;
418+
this.posY += e.touches[0].clientY - this.lastTouchMoveY;
419+
this.lastTouchMoveX = e.touches[0].clientX;
420+
this.lastTouchMoveY = e.touches[0].clientY;
421+
}
379422

380423
this.updateBoardTransform();
381424
}
382425

383-
public handleMouseup = (e) : void => {
384-
if(e.button === 2 && !this.boardMoved && !this.isReadOnly){
385-
this.showContextMenu = true;
386-
this.showComponentContextMenu = false;
387-
this.contextMenuX = e.offsetX;
388-
this.contextMenuY = e.offsetY;
426+
public handleMouseup = (e: Event) : void => {
427+
if(e instanceof MouseEvent){
428+
if(e.button === 2 && !this.boardMoved && !this.isReadOnly){
429+
this.showContextMenu = true;
430+
this.showComponentContextMenu = false;
431+
this.contextMenuX = e.offsetX;
432+
this.contextMenuY = e.offsetY;
433+
}
434+
this.boardMoved = false;
435+
this.board.classList.remove("moving")
436+
this.board.removeEventListener( "mousemove", this.handleMousemove );
437+
window.removeEventListener( "mouseup", this.handleMouseup );
438+
}
439+
else if(e instanceof TouchEvent){
440+
this.boardMoved = false;
441+
this.board.classList.remove("moving")
442+
this.board.removeEventListener( "touchmove", this.handleMousemove );
443+
window.removeEventListener( "touchend", this.handleMouseup );
389444
}
390-
this.boardMoved = false;
391-
this.board.classList.remove("moving")
392-
this.board.removeEventListener( "mousemove", this.handleMousemove );
393-
window.removeEventListener( "mouseup", this.handleMouseup );
445+
394446
}
395447

396448
public updateBoardTransform(){
397449
this.board.style.transform = `translateX(${this.posX}px) translateY(${this.posY}px) scale(${this.placingService.boardScale})`;
398450
}
399451

400-
public handleClick = (event: MouseEvent) : void => {
452+
public handleClick = () : void => {
401453
if(this.placingService.isConnecting){
402454
this.placingService.stopConnecting();
403455
this.board.onmousemove = null;
404456
document.getElementsByClassName("svg-canvas")[0].innerHTML = "";
405457
}
406458
}
407459

408-
public handleSelfClick(event: Event){
460+
public handleSelfClick(){
409461
this.selectionService.clearSelection();
410462
this.selectionService.clearConnectionSelection();
411463
this.selectionService.clearCurrentConnectionSelections();
412464
this.selectionService.clearLineBreakSelection();
413465
}
414466

415-
zoomOut(){
416-
this.placingService.boardScale = Math.max(this.placingService.boardScale - 0.1,0.1) ;
467+
zoomOut(modifier: number = 1){
468+
this.placingService.boardScale = Math.max(this.placingService.boardScale - (0.1 / modifier),0.1) ;
417469
this.updateBoardTransform();
418470
}
419471

420-
zoomIn(){
421-
this.placingService.boardScale = Math.min(this.placingService.boardScale + 0.1,2);
472+
zoomIn(modifier: number = 1){
473+
this.placingService.boardScale = Math.min(this.placingService.boardScale + (0.1 / modifier),2);
422474
this.updateBoardTransform();
423475
}
424476

src/app/board/boardUI/boardUI.component.html

+5
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@
142142
</mat-menu>
143143
</li>
144144
</ul>
145+
<div class="mobile-fullscreen" *ngIf="isMobile">
146+
<button (click)="fullscreen.emit()">
147+
<span>Go fullscreen</span>
148+
</button>
149+
</div>
145150
</nav>
146151
<nav class="nav-second">
147152
<ul class="nav-list">

src/app/board/boardUI/boardUI.component.scss

+16
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,20 @@ input[type="file"] {
349349
font-weight: 600;
350350
font-size: 1em;
351351
}
352+
}
353+
.mobile-fullscreen{
354+
margin-left: 2em;
355+
display: flex;
356+
justify-content: center;
357+
align-items: center;
358+
font-size: 0.8rem;
359+
& button{
360+
background-color: var(--main);
361+
padding: 0.2em 0.4em;
362+
color: #fff;
363+
border-radius: 0.4em;
364+
&:hover{
365+
color: #fff;
366+
}
367+
}
352368
}

src/app/board/boardUI/boardUI.component.ts

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export class BoardUIComponent implements OnInit {
7575
exportPngPreview: HTMLCanvasElement;
7676
exportSvgPreview: SVGElement;
7777

78+
isMobile: boolean = false;
7879

7980
scaleControl: FormControl = new FormControl();
8081
scaleSelectList = [0.1, 0.5, 1, 1.5, 2];
@@ -180,6 +181,9 @@ export class BoardUIComponent implements OnInit {
180181
if(e.key === 'Delete')
181182
this.delComponent.emit();
182183
}
184+
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){
185+
this.isMobile = true;
186+
}
183187
}
184188

185189
showConfirmDialog(text: string, returnFunction){

src/app/board/componentmenu/componentmenu.component.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
</ng-template>
2020
</div>
2121
<div class="content">
22-
<div [style.backgroundColor]="item.color" class="section-item" *ngFor="let item of category.items" (mousedown)="dragItem(item)" (delayed-hover)="hoverItem(item)" delay="300" (mouseout)="leaveItem()">
22+
<div [style.backgroundColor]="item.color" class="section-item" *ngFor="let item of category.items"
23+
(mousedown)="dragItem(item)" (delayed-hover)="hoverItem(item)" delay="300" (mouseout)="leaveItem()"
24+
(touchstart)="dragItem(item)">
2325
<span class="caption">{{ item.mark }}</span>
2426
<img src="{{ item.imageUrl }}">
2527
<button *ngIf="!viewingService.isHelpersDisabled()" class="info" (click)="showInfo(item)"><i class="far fa-question-circle"></i></button>

0 commit comments

Comments
 (0)