Skip to content

Commit

Permalink
feat: add preventDefaultOnDrag option to PanInput (#214)
Browse files Browse the repository at this point in the history
* feat: add preventDefaultOnDrag option to PanInput

* test: add dispatchDrag to test util

* test: add flick manual test

* docs: add preventDefaultOnDrag demo

* docs: update preventDefaultOnDrag demo

* demo: add clickable link to preventDefaultOnDrag demo

* demo: update demos

* demo: fix logo demo color in dark mode

* demo: fix demo deploy paths

* fix: fix preventDefaultOnDrag affecting touchstart
  • Loading branch information
malangfox committed Jul 13, 2023
1 parent 16b0a1c commit cc115a9
Show file tree
Hide file tree
Showing 16 changed files with 273 additions and 58 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{
"basePath": "packages/axes/dist",
"dists": [
"demo/dist"
"packages/demo/dist"
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"docs:build": "rm -rf ./packages/demo/docs/api && jsdoc-to-mdx -c ./jsdoc-to-mdx.json",
"demo:build": "npm run docs:build && npm run build --prefix packages/demo",
"demo:build-docusaurus": "npm run build --prefix demo",
"demo:deploy": "lerna-helper deploy --base @egjs/axes --remote upstream",
"demo:deploy-origin": "lerna-helper deploy --base @egjs/axes --remote origin",
"demo:deploy": "lerna-helper deploy --base @egjs/axes --src packages/demo/build/ --remote upstream",
"demo:deploy-origin": "lerna-helper deploy --base @egjs/axes --src packages/demo/build/ --remote origin",
"release": "lerna-helper release --base @egjs/axes --remote upstream --branch master",
"prepush": "npm run lint",
"commitmsg": "node config/validate-commit-msg.js"
Expand Down
6 changes: 5 additions & 1 deletion packages/axes/src/inputType/PanInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface PanInputOption {
thresholdAngle?: number;
threshold?: number;
preventClickOnDrag?: boolean;
preventDefaultOnDrag?: boolean;
iOSEdgeSwipeThreshold?: number;
releaseOnScroll?: boolean;
touchAction?: string;
Expand Down Expand Up @@ -95,6 +96,7 @@ export const getDirectionByAngle = (
* @param {Number} [thresholdAngle=45] The threshold value that determines whether user action is horizontal or vertical (0~90) <ko>사용자의 동작이 가로 방향인지 세로 방향인지 판단하는 기준 각도(0~90)</ko>
* @param {Number} [threshold=0] Minimal pan distance required before recognizing <ko>사용자의 Pan 동작을 인식하기 위해산 최소한의 거리</ko>
* @param {Boolean} [preventClickOnDrag=false] Whether to cancel the {@link https://developer.mozilla.org/en/docs/Web/API/Element/click_event click} event when the user finishes dragging more than 1 pixel <ko>사용자가 1픽셀 이상 드래그를 마쳤을 때 {@link https://developer.mozilla.org/ko/docs/Web/API/Element/click_event click} 이벤트 취소 여부</ko>
* @param {Boolean} [preventDefaultOnDrag=false] Whether to use the {@link https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault preventDefault} when the user starts dragging <ko>사용자가 드래그를 시작할 때 {@link https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault preventDefault} 실행 여부</ko>
* @param {Number} [iOSEdgeSwipeThreshold=30] Area (px) that can go to the next page when swiping the right edge in iOS safari <ko>iOS Safari에서 오른쪽 엣지를 스와이프 하는 경우 다음 페이지로 넘어갈 수 있는 영역(px)</ko>
* @param {String} [touchAction=null] Value that overrides the element's "touch-action" css property. If set to null, it is automatically set to prevent scrolling in the direction of the connected axis. <ko>엘리먼트의 "touch-action" CSS 속성을 덮어쓰는 값. 만약 null로 설정된 경우, 연결된 축 방향으로의 스크롤을 방지하게끔 자동으로 설정된다.</ko>
**/
Expand Down Expand Up @@ -149,6 +151,7 @@ export class PanInput implements InputType {
thresholdAngle: 45,
threshold: 0,
preventClickOnDrag: false,
preventDefaultOnDrag: false,
iOSEdgeSwipeThreshold: IOS_EDGE_THRESHOLD,
releaseOnScroll: false,
touchAction: null,
Expand Down Expand Up @@ -241,7 +244,7 @@ export class PanInput implements InputType {
}

protected _onPanstart(event: InputEventType) {
const { inputKey, inputButton } = this.options;
const { inputKey, inputButton, preventDefaultOnDrag } = this.options;
const activeEvent = this._activeEvent;
const panEvent = activeEvent.onEventStart(event, inputKey, inputButton);
if (
Expand All @@ -260,6 +263,7 @@ export class PanInput implements InputType {
this._atRightEdge =
IS_IOS_SAFARI && panEvent.center.x > window.innerWidth - edgeThreshold;
this._attachWindowEvent(activeEvent);
(preventDefaultOnDrag && panEvent.srcEvent.type !== "touchstart") && panEvent.srcEvent.preventDefault();
activeEvent.prevEvent = panEvent;
}
}
Expand Down
75 changes: 75 additions & 0 deletions packages/axes/test/manual/flick.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>egjs-axes</title>
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no">
<style>
.viewport {
overflow: hidden;
}
.camera {
display: flex;
}
.panel {
padding: 0 10px;
}
</style>
</head>
<body>
<div class="viewport">
<div class="camera">
<div class="panel">
<img
src="https://dummyimage.com/300x300/110010/fff&text=1"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/220020/fff&text=2"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/330030/fff&text=3"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/440040/fff&text=4"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/550050/fff&text=5"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/660060/fff&text=6"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/770070/fff&text=7"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/880080/fff&text=8"
/>
</div>
<div class="panel">
<img
src="https://dummyimage.com/300x300/990090/fff&text=9"
/>
</div>
</div>
</div>

<script src="../../../../node_modules/@egjs/component/dist/component.js"></script>
<script src="../../dist/axes.pkgd.js"></script>
<!-- <script src="../../dist/axes.pkgd.js"></script> -->
<script src="js/flick.js"></script>
</body>
</html>
20 changes: 20 additions & 0 deletions packages/axes/test/manual/js/flick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var viewport = document.querySelector(".viewport");
var camera = document.querySelector(".camera");
var input = new eg.Axes.PanInput(viewport, {
scale: [1, 1],
inputType: ["touch", "mouse"],
preventClickOnDrag: true,
preventDefaultOnDrag: true,
});
var axes = new eg.Axes(
{
x: {
range: [-1000, 0],
startPos: 0,
},
},
).on("change", function (e) {
camera.style.transform = "translateX(" + e.pos.x + "px)";
});

axes.connect(["x"], input);
33 changes: 33 additions & 0 deletions packages/axes/test/unit/inputType/PanInput.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,39 @@ describe("PanInput", () => {
});
});

describe("preventDefaultOnDrag", () => {
it("should use preventDefault at the start of a drag when preventDefaultOnDrag is true", () => {
// Given
const mouseDown = new MouseEvent("mousedown", { buttons: 1, cancelable: true });
input = new PanInput(el, {
inputType: ["touch", "mouse"],
preventDefaultOnDrag: true,
});
inst.connect(["x", "y"], input);

// When
el.dispatchEvent(mouseDown);
// Then
expect(mouseDown.defaultPrevented).to.be.true;
});

it("should apply changes in preventDefaultOnDrag option after connected", () => {
// Given
const mouseDown = new MouseEvent("mousedown", { buttons: 1, cancelable: true });
input = new PanInput(el, {
inputType: ["touch", "mouse"],
preventDefaultOnDrag: true,
});
inst.connect(["x", "y"], input);
input.options.preventDefaultOnDrag = false;

// When
el.dispatchEvent(mouseDown);
// Then
expect(mouseDown.defaultPrevented).to.be.false;
});
});

describe("threshold", () => {
it("should not trigger change event when moving below threshold", (done) => {
// Given
Expand Down
44 changes: 44 additions & 0 deletions packages/axes/test/unit/inputType/TestHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,50 @@ export default class TestHelper {
target.dispatchEvent(wheelEvent);
}

static dispatchDrag(target, from, to, options) {
const startRect = target.getBoundingClientRect();
const mousedown = new MouseEvent("mousedown", this.getMouseInit(startRect, from));
target.dispatchEvent(mousedown);

const count = Math.floor(options.duration / options.interval);
for (let i = 1; i <= count; ++i) {
this.dispatchMouseMove(target, this.getMouseInit(startRect, {
left: from.left + (to.left - from.left) / count * i,
top: from.top + (to.top - from.top) / count * i,
}), options.interval * i);
}
return new Promise(resolve => {
setTimeout(() => {
const mosueup = new MouseEvent("mouseup", this.getMouseInit(startRect, to));

target.dispatchEvent(mosueup);
resolve();
}, options.duration);
});
}

static dispatchMouseMove(target, moustInit, time) {
setTimeout(() => {
const mousemove = new MouseEvent("mousemove", moustInit);

target.dispatchEvent(mousemove);
}, time);
}

static getMouseInit(startRect, offsetRect) {
return {
buttons: 1,
screenX: startRect.left + offsetRect.left,
screenY: startRect.top + offsetRect.top,
clientX: startRect.left + offsetRect.left,
clientY: startRect.top + offsetRect.top,
offsetX: offsetRect.left,
offsetY: offsetRect.top,
bubbles: true,
cancelable: true,
};
}

static key(target, behavior, value, callback) {
if (target instanceof Element === false) {
return;
Expand Down
5 changes: 5 additions & 0 deletions packages/demo/src/css/demos/axesboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@
cursor: pointer;
margin-top: -15px;
}

.link {
position: absolute;
top: -40px;
}
9 changes: 9 additions & 0 deletions packages/demo/src/css/demos/logo.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
height: 50px;
}

html[data-theme='dark'] .item svg {
fill: white;
}

.item.light svg {
width: 60px;
margin-left: -5px;
Expand All @@ -34,4 +38,9 @@
position: absolute;
width: 100%;
height: 320px;
stroke: black;
}

html[data-theme='dark'] .line {
stroke: white;
}
27 changes: 27 additions & 0 deletions packages/demo/src/pages/Options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,33 @@ Whether to cancel the click event when the user finishes dragging more than 1 pi
</div>
</div>

### preventDefaultOnDrag
Whether to use the preventDefault when the user starts dragging.
:::info
Hint: These examples only have a x-axis. Try using the input direction similar to y-axis.
:::

<div className="columns">
<div className="column is-6">
<div>

```js
preventDefaultOnDrag: false
```
</div>
<AxesBoard panInputOptions={{ thresholdAngle: 45, preventDefaultOnDrag: false }} demoType={"preventDefaultOnDrag"}/>
</div>
<div className="column is-6">
<div>

```js
preventDefaultOnDrag: true
```
</div>
<AxesBoard panInputOptions={{ thresholdAngle: 45, preventDefaultOnDrag: true }} demoType={"preventDefaultOnDrag"}/>
</div>
</div>

### iOSEdgeSwipeThreshold
Area (px) that can go to the next page when swiping the right edge in iOS safari.

Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/pages/demos/axes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Axes, {
PinchInput,
MoveKeyInput,
WheelInput,
} from "../../../../axes/src/index";
} from "@egjs/axes";
import Icon from "../../../static/img/demos/axes/logo.svg";
import "../../css/demos/axes.css";

Expand Down
3 changes: 2 additions & 1 deletion packages/demo/src/pages/demos/axesboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ export default function AxesBoard({ axis, demoType, options, panInputOptions, pi
<div className="board" ref={board}>
<div className="info">x: {x} y: {y}</div>
<div className="target" ref={target} style={{ transform: `translate3d(${x}px, ${y}px, 0) scale(${zoom / 100})` }} onClick={() => { onClick(); }}>
{ demoType === "preventDefaultOnDrag" && <a className="link" href="https://www.naver.com/">Clickable Link</a> }
<img
draggable="false"
draggable={demoType === "preventDefaultOnDrag" ? true : false}
className="egjsicon"
src={
require(`@site/static/img/favicon.ico`)
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/pages/demos/bubble.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from "react";

import Axes, { PinchInput, WheelInput } from "../../../../axes/src/index";
import Axes, { PinchInput, WheelInput } from "@egjs/axes";
import "../../css/demos/bubble.css";

const Bubble = () => {
Expand Down
14 changes: 7 additions & 7 deletions packages/demo/src/pages/demos/logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ export default function Logo() {
<div id="x" className="item bold" style={{ transform: `translateX(${x.x}px) translateY(${x.y}px)` }}><XIcon /></div>
<div id="y" className="item" style={{ transform: `translateX(${y.x}px) translateY(${y.y}px)` }}><YIcon /></div>
<div id="z" className="item bold" style={{ transform: `translateX(${z.x}px) translateY(${z.y}px)` }}><ZIcon /></div>
<svg className="line"><line x1={ offsetX1 + square.x } y1={ offsetY + square.y } x2={ offsetX2 + x.x } y2={ offsetY + x.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + square.x } y1={ offsetY + square.y } x2={ offsetX2 + y.x } y2={ offsetY + y.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + triangle.x } y1={ offsetY + triangle.y } x2={ offsetX2 + x.x } y2={ offsetY + x.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + triangle.x } y1={ offsetY + triangle.y } x2={ offsetX2 + y.x } y2={ offsetY + y.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + triangle.x } y1={ offsetY + triangle.y } x2={ offsetX2 + z.x } y2={ offsetY + z.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + circle.x } y1={ offsetY + circle.y } x2={ offsetX2 + y.x } y2={ offsetY + y.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + circle.x } y1={ offsetY + circle.y } x2={ offsetX2 + z.x } y2={ offsetY + z.y } stroke="black" strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + square.x } y1={ offsetY + square.y } x2={ offsetX2 + x.x } y2={ offsetY + x.y } strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + square.x } y1={ offsetY + square.y } x2={ offsetX2 + y.x } y2={ offsetY + y.y } strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + triangle.x } y1={ offsetY + triangle.y } x2={ offsetX2 + x.x } y2={ offsetY + x.y } strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + triangle.x } y1={ offsetY + triangle.y } x2={ offsetX2 + y.x } y2={ offsetY + y.y } strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + triangle.x } y1={ offsetY + triangle.y } x2={ offsetX2 + z.x } y2={ offsetY + z.y } strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + circle.x } y1={ offsetY + circle.y } x2={ offsetX2 + y.x } y2={ offsetY + y.y } strokeWidth="4"/></svg>
<svg className="line"><line x1={ offsetX1 + circle.x } y1={ offsetY + circle.y } x2={ offsetX2 + z.x } y2={ offsetY + z.y } strokeWidth="4"/></svg>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/pages/demos/nestedaxes.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from "react";

import Axes, { PanInput, WheelInput } from "../../../../axes/src/index";
import Axes, { PanInput, WheelInput } from "@egjs/axes";
import "../../css/demos/nestedaxes.css";

const NestedAxes = () => {
Expand Down
Loading

0 comments on commit cc115a9

Please sign in to comment.