Skip to content

Commit cccf9be

Browse files
committed
major changes on scroll and mobile support for some events
1 parent 25b1c05 commit cccf9be

16 files changed

+439
-96
lines changed

.prettierrc.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
trailingComma: 'es5',
3+
tabWidth: 2,
4+
singleQuote: true,
5+
};

.prettierrc.json

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"jsxBracketSameLine": false,
55
"jsxSingleQuote": false,
66
"quoteProps": "consistent",
7-
"printWidth": 180,
87
"semi": true,
98
"singleQuote": true,
109
"tabWidth": 2,

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@revolist/revogrid",
3-
"version": "4.4.0-next.12",
3+
"version": "4.4.0-next.20",
44
"description": "Virtual reactive data grid component - RevoGrid.",
55
"license": "MIT",
66
"directories": {
@@ -90,7 +90,7 @@
9090
"bootstrap": "^5.2.3"
9191
},
9292
"dependencies": {
93-
"@stencil/core": "^3.2.2",
93+
"@stencil/core": "^3.3.0",
9494
"lodash": "^4.17.21"
9595
}
9696
}

src/components.d.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,10 @@ export namespace Components {
397397
* Custom editors register
398398
*/
399399
"editors": Edition.Editors;
400+
/**
401+
* Is mobile view mode
402+
*/
403+
"isMobileDevice": boolean;
400404
/**
401405
* Last cell position
402406
*/
@@ -464,7 +468,7 @@ export namespace Components {
464468
* update on delta in case we don't know existing position or external change
465469
* @param e
466470
*/
467-
"changeScroll": (e: RevoGrid.ViewPortScrollEvent) => Promise<RevoGrid.ViewPortScrollEvent>;
471+
"changeScroll": (e: RevoGrid.ViewPortScrollEvent, silent?: boolean) => Promise<RevoGrid.ViewPortScrollEvent>;
468472
/**
469473
* Height of inner content
470474
*/
@@ -1162,6 +1166,10 @@ declare namespace LocalJSX {
11621166
* Custom editors register
11631167
*/
11641168
"editors"?: Edition.Editors;
1169+
/**
1170+
* Is mobile view mode
1171+
*/
1172+
"isMobileDevice"?: boolean;
11651173
/**
11661174
* Last cell position
11671175
*/
@@ -1287,7 +1295,14 @@ declare namespace LocalJSX {
12871295
"contentWidth"?: number;
12881296
"onResizeViewport"?: (event: RevogrViewportScrollCustomEvent<RevoGrid.ViewPortResizeEvent>) => void;
12891297
"onScrollViewport"?: (event: RevogrViewportScrollCustomEvent<RevoGrid.ViewPortScrollEvent>) => void;
1290-
"onScrollchange"?: (event: RevogrViewportScrollCustomEvent<{ type: RevoGrid.DimensionType; hasScroll: boolean }>) => void;
1298+
"onScrollchange"?: (event: RevogrViewportScrollCustomEvent<{
1299+
type: RevoGrid.DimensionType;
1300+
hasScroll: boolean;
1301+
}>) => void;
1302+
/**
1303+
* Silently scroll to coordinate Made to align negative coordinates for mobile devices
1304+
*/
1305+
"onSilentScroll"?: (event: RevogrViewportScrollCustomEvent<RevoGrid.ViewPortScrollEvent>) => void;
12911306
}
12921307
interface IntrinsicElements {
12931308
"revo-grid": RevoGrid;

src/components/overlay/autofill.service.tsx

+27-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import debounce from 'lodash/debounce';
22
import { DebouncedFunc } from 'lodash';
33

44
import { h } from '@stencil/core';
5-
import { CELL_HANDLER_CLASS } from '../../utils/consts';
5+
import { CELL_HANDLER_CLASS, MOBILE_CLASS } from '../../utils/consts';
66
import { Observable, Selection, RevoGrid, Edition } from '../../interfaces';
77
import { EventData, getCell, getCurrentCell, isAfterLast } from './selection.utils';
88
import { getRange } from '../../store/selection/selection.helpers';
@@ -69,16 +69,29 @@ export class AutoFillService {
6969
}
7070
return (
7171
<div
72-
class={CELL_HANDLER_CLASS}
73-
style={{ left: `${handlerStyle.right}px`, top: `${handlerStyle.bottom}px` }}
74-
onMouseDown={(e: MouseEvent) => {
75-
this.selectionStart(e.target as HTMLElement, this.sv.getData(), AutoFillType.autoFill);
76-
e.preventDefault();
72+
class={{
73+
[CELL_HANDLER_CLASS]: true,
74+
[MOBILE_CLASS]: true,
7775
}}
76+
style={{ left: `${handlerStyle.right}px`, top: `${handlerStyle.bottom}px` }}
77+
onMouseDown={(e: MouseEvent) => this.autoFillHandler(e)}
78+
onTouchStart={(e: TouchEvent) => this.autoFillHandler(e)}
7879
/>
7980
);
8081
}
8182

83+
private autoFillHandler(e: MouseEvent | TouchEvent, type = AutoFillType.autoFill) {
84+
let target: Element | null = null;
85+
if (e.target instanceof Element) {
86+
target = e.target;
87+
}
88+
if (!target) {
89+
return;
90+
}
91+
this.selectionStart(target, this.sv.getData(), type);
92+
e.preventDefault();
93+
}
94+
8295
get isAutoFill() {
8396
return !!this.autoFillType;
8497
}
@@ -113,7 +126,13 @@ export class AutoFillService {
113126
if (!this.autoFillInitial) {
114127
return;
115128
}
116-
const current = getCurrentCell({ x: getFromEvent(event, 'clientX'), y: getFromEvent(event, 'clientY') }, data);
129+
const x = getFromEvent(event, 'clientX', MOBILE_CLASS);
130+
const y = getFromEvent(event, 'clientY', MOBILE_CLASS);
131+
// skip touch
132+
if (x === null || y === null) {
133+
return;
134+
}
135+
const current = getCurrentCell({ x, y }, data);
117136

118137
// first time or direction equal to start(same as first time)
119138
if (!this.autoFillLast) {
@@ -146,7 +165,7 @@ export class AutoFillService {
146165
* Can be triggered from MouseDown selection on element
147166
* Or can be triggered on corner square drag
148167
*/
149-
selectionStart(target: HTMLElement, data: EventData, type = AutoFillType.selection) {
168+
selectionStart(target: Element, data: EventData, type = AutoFillType.selection) {
150169
/** Get cell by autofill element */
151170
const { top, left } = target.getBoundingClientRect();
152171
this.autoFillInitial = this.getFocus();

src/components/overlay/revogr-overlay-selection.tsx

+33-9
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import { AllDimensionType, ApplyFocusEvent, FocusRenderEvent, Edition, Observabl
44
import ColumnService from '../data/columnService';
55
import SelectionStoreService from '../../store/selection/selection.store.service';
66
import { codesLetter } from '../../utils/keyCodes';
7-
import { SELECTION_BORDER_CLASS } from '../../utils/consts';
7+
import { MOBILE_CLASS, SELECTION_BORDER_CLASS } from '../../utils/consts';
88
import { DataSourceState } from '../../store/dataSource/data.store';
99
import { isRangeSingleCell } from '../../store/selection/selection.helpers';
1010
import { getCurrentCell, getElStyle } from './selection.utils';
1111
import { isEditInput } from './editors/edit.utils';
1212
import { KeyboardService } from './keyboard.service';
1313
import { AutoFillService } from './autofill.service';
1414
import { ClipboardService } from './clipboard.service';
15-
import { getFromEvent } from '../../utils/events';
15+
import { getFromEvent, verifyTouchTarget } from '../../utils/events';
1616

1717
@Component({
1818
tag: 'revogr-overlay-selection',
@@ -80,11 +80,20 @@ export class OverlaySelection {
8080
* Custom editors register
8181
*/
8282
@Prop() editors: Edition.Editors;
83-
/** If true applys changes when cell closes if not Escape */
83+
/**
84+
* If true applys changes when cell closes if not Escape
85+
*/
8486
@Prop() applyChangesOnClose: boolean = false;
85-
/** Additional data to pass to renderer */
87+
/**
88+
* Additional data to pass to renderer
89+
*/
8690
@Prop() additionalData: any;
8791

92+
/**
93+
* Is mobile view mode
94+
*/
95+
@Prop() isMobileDevice: boolean;
96+
8897
// --------------------------------------------------------------------------
8998
//
9099
// Events
@@ -299,7 +308,11 @@ export class OverlaySelection {
299308

300309
private renderRange(range: Selection.RangeArea) {
301310
const style = getElStyle(range, this.dimensionRow.state, this.dimensionCol.state);
302-
return [<div class={SELECTION_BORDER_CLASS} style={style} />];
311+
return [
312+
<div class={SELECTION_BORDER_CLASS} style={style}>
313+
{this.isMobileDevice && <div class="range-handlers"><span class={MOBILE_CLASS}></span><span class={MOBILE_CLASS}></span></div>}
314+
</div>
315+
];
303316
}
304317

305318
private renderEditCell() {
@@ -389,6 +402,7 @@ export class OverlaySelection {
389402
}
390403
return (
391404
<Host
405+
class={{ mobile: this.isMobileDevice }}
392406
// run edit on dblclick
393407
onDblClick={(e: MouseEvent) => {
394408
// if dblclick prevented outside edit will not start
@@ -397,7 +411,7 @@ export class OverlaySelection {
397411
}
398412
}}
399413
onMouseDown={(e: MouseEvent) => this.onElementMouseDown(e)}
400-
onTouchStart={(e: TouchEvent) => this.onElementMouseDown(e)}>
414+
onTouchStart={(e: TouchEvent) => this.onElementMouseDown(e, true)}>
401415
{els}
402416
<slot name="data" />
403417
</Host>
@@ -454,7 +468,7 @@ export class OverlaySelection {
454468
return !e.defaultPrevented;
455469
}
456470

457-
protected onElementMouseDown(e: MouseEvent | TouchEvent) {
471+
protected onElementMouseDown(e: MouseEvent | TouchEvent, touch = false) {
458472
// Ignore focus if clicked input
459473
if (isEditInput(e.target as HTMLElement | undefined)) {
460474
return;
@@ -463,14 +477,24 @@ export class OverlaySelection {
463477
if (e.defaultPrevented) {
464478
return;
465479
}
480+
const x = getFromEvent(e, 'clientX');
481+
const y = getFromEvent(e, 'clientY');
482+
// skip touch
483+
if (x === null || y === null) {
484+
return;
485+
}
466486
// Regular cell click
467-
const focusCell = getCurrentCell({ x: getFromEvent(e, 'clientX'), y: getFromEvent(e, 'clientY') }, data);
487+
const focusCell = getCurrentCell({ x, y }, data);
468488
this.selectionStoreService.focus(focusCell, this.range && e.shiftKey);
469489

470490
// Initiate autofill selection
471491
if (this.range) {
472492
this.autoFillService.selectionStart(e.target as HTMLElement, data);
473-
e.preventDefault();
493+
if (!touch) {
494+
e.preventDefault();
495+
} else if (verifyTouchTarget((e as TouchEvent).touches[0], MOBILE_CLASS)) {
496+
e.preventDefault();
497+
}
474498
}
475499
}
476500

src/components/overlay/revogr-overlay-style.scss

+74-11
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,92 @@
1-
revogr-overlay-selection {
2-
display: block;
3-
position: relative;
4-
width: 100%;
5-
1+
@mixin autofill-handle($handler-size: 14px, $icon-size: 10px) {
62
.autofill-handle {
7-
$handler-size: 10px;
83
position: absolute;
94
width: $handler-size;
105
height: $handler-size;
11-
background: $selection-border;
126
margin-left: -$handler-size + 1;
137
margin-top: -$handler-size + 1;
14-
border: 1px solid white;
15-
box-sizing: border-box;
168
z-index: 10;
179
cursor: crosshair;
10+
11+
&::before {
12+
content: '';
13+
position: absolute;
14+
right: 0;
15+
bottom: 0;
16+
width: $icon-size;
17+
height: $icon-size;
18+
background: $selection-border;
19+
border: 1px solid white;
20+
box-sizing: border-box;
21+
}
22+
}
23+
}
24+
25+
revogr-overlay-selection {
26+
display: block;
27+
position: relative;
28+
width: 100%;
29+
30+
31+
@include autofill-handle;
32+
33+
&.mobile {
34+
@include autofill-handle(30px, 12px);
1835
}
1936

2037
.selection-border-range {
2138
position: absolute;
2239
pointer-events: none;
2340
z-index: 9;
24-
}
2541

26-
.selection-border-range {
42+
.range-handlers {
43+
height: 100%;
44+
background-color: transparent;
45+
width: calc(50% + (50% / 2));
46+
max-width: 50px;
47+
min-width: 20px;
48+
left: 50%;
49+
transform: translateX(-50%);
50+
position: absolute;
51+
52+
$btn-size: 20px;
53+
$handler-w: 15px;
54+
$handler-h: 5px;
55+
56+
> span {
57+
pointer-events: auto;
58+
height: $btn-size;
59+
width: $btn-size;
60+
position: absolute;
61+
left: 50%;
62+
transform: translateX(-50%);
63+
64+
65+
&:before, &:after {
66+
position: absolute;
67+
border-radius: 5px;
68+
width: $handler-w;
69+
height: $handler-h;
70+
left: 50%;
71+
transform: translateX(-50%);
72+
background-color: rgba(black, 20%);
73+
}
74+
&:first-child {
75+
top: -($handler-h + 2px);
76+
&:before{
77+
content: '';
78+
top: 0;
79+
}
80+
}
81+
&:last-child {
82+
bottom: -($handler-h + 2px);
83+
&:after{
84+
content: '';
85+
bottom: 0;
86+
}
87+
}
88+
}
89+
}
2790
@include selection();
2891
}
2992

0 commit comments

Comments
 (0)