1 |
|
2 |
|
3 |
|
4 | import { q as now, p as pointerCoord } from './helpers.js';
|
5 |
|
6 | const startTapClick = (config) => {
|
7 | let lastTouch = -MOUSE_WAIT * 10;
|
8 | let lastActivated = 0;
|
9 | let scrollingEl;
|
10 | let activatableEle;
|
11 | let activeRipple;
|
12 | let activeDefer;
|
13 | const useRippleEffect = config.getBoolean('animated', true) && config.getBoolean('rippleEffect', true);
|
14 | const clearDefers = new WeakMap();
|
15 | const isScrolling = () => {
|
16 | return scrollingEl !== undefined && scrollingEl.parentElement !== null;
|
17 | };
|
18 |
|
19 | const onTouchStart = (ev) => {
|
20 | lastTouch = now(ev);
|
21 | pointerDown(ev);
|
22 | };
|
23 | const onTouchEnd = (ev) => {
|
24 | lastTouch = now(ev);
|
25 | pointerUp(ev);
|
26 | };
|
27 | const onMouseDown = (ev) => {
|
28 | const t = now(ev) - MOUSE_WAIT;
|
29 | if (lastTouch < t) {
|
30 | pointerDown(ev);
|
31 | }
|
32 | };
|
33 | const onMouseUp = (ev) => {
|
34 | const t = now(ev) - MOUSE_WAIT;
|
35 | if (lastTouch < t) {
|
36 | pointerUp(ev);
|
37 | }
|
38 | };
|
39 | const onContextMenu = (ev) => {
|
40 | pointerUp(ev);
|
41 | };
|
42 | const cancelActive = () => {
|
43 | clearTimeout(activeDefer);
|
44 | activeDefer = undefined;
|
45 | if (activatableEle) {
|
46 | removeActivated(false);
|
47 | activatableEle = undefined;
|
48 | }
|
49 | };
|
50 | const pointerDown = (ev) => {
|
51 | if (activatableEle || isScrolling()) {
|
52 | return;
|
53 | }
|
54 | scrollingEl = undefined;
|
55 | setActivatedElement(getActivatableTarget(ev), ev);
|
56 | };
|
57 | const pointerUp = (ev) => {
|
58 | setActivatedElement(undefined, ev);
|
59 | };
|
60 | const setActivatedElement = (el, ev) => {
|
61 |
|
62 | if (el && el === activatableEle) {
|
63 | return;
|
64 | }
|
65 | clearTimeout(activeDefer);
|
66 | activeDefer = undefined;
|
67 | const { x, y } = pointerCoord(ev);
|
68 |
|
69 | if (activatableEle) {
|
70 | if (clearDefers.has(activatableEle)) {
|
71 | throw new Error('internal error');
|
72 | }
|
73 | if (!activatableEle.classList.contains(ACTIVATED)) {
|
74 | addActivated(activatableEle, x, y);
|
75 | }
|
76 | removeActivated(true);
|
77 | }
|
78 |
|
79 | if (el) {
|
80 | const deferId = clearDefers.get(el);
|
81 | if (deferId) {
|
82 | clearTimeout(deferId);
|
83 | clearDefers.delete(el);
|
84 | }
|
85 | const delay = isInstant(el) ? 0 : ADD_ACTIVATED_DEFERS;
|
86 | el.classList.remove(ACTIVATED);
|
87 | activeDefer = setTimeout(() => {
|
88 | addActivated(el, x, y);
|
89 | activeDefer = undefined;
|
90 | }, delay);
|
91 | }
|
92 | activatableEle = el;
|
93 | };
|
94 | const addActivated = (el, x, y) => {
|
95 | lastActivated = Date.now();
|
96 | el.classList.add(ACTIVATED);
|
97 | const rippleEffect = useRippleEffect && getRippleEffect(el);
|
98 | if (rippleEffect && rippleEffect.addRipple) {
|
99 | removeRipple();
|
100 | activeRipple = rippleEffect.addRipple(x, y);
|
101 | }
|
102 | };
|
103 | const removeRipple = () => {
|
104 | if (activeRipple !== undefined) {
|
105 | activeRipple.then(remove => remove());
|
106 | activeRipple = undefined;
|
107 | }
|
108 | };
|
109 | const removeActivated = (smooth) => {
|
110 | removeRipple();
|
111 | const active = activatableEle;
|
112 | if (!active) {
|
113 | return;
|
114 | }
|
115 | const time = CLEAR_STATE_DEFERS - Date.now() + lastActivated;
|
116 | if (smooth && time > 0 && !isInstant(active)) {
|
117 | const deferId = setTimeout(() => {
|
118 | active.classList.remove(ACTIVATED);
|
119 | clearDefers.delete(active);
|
120 | }, CLEAR_STATE_DEFERS);
|
121 | clearDefers.set(active, deferId);
|
122 | }
|
123 | else {
|
124 | active.classList.remove(ACTIVATED);
|
125 | }
|
126 | };
|
127 | const doc = document;
|
128 | doc.addEventListener('ionScrollStart', ev => {
|
129 | scrollingEl = ev.target;
|
130 | cancelActive();
|
131 | });
|
132 | doc.addEventListener('ionScrollEnd', () => {
|
133 | scrollingEl = undefined;
|
134 | });
|
135 | doc.addEventListener('ionGestureCaptured', cancelActive);
|
136 | doc.addEventListener('touchstart', onTouchStart, true);
|
137 | doc.addEventListener('touchcancel', onTouchEnd, true);
|
138 | doc.addEventListener('touchend', onTouchEnd, true);
|
139 | doc.addEventListener('mousedown', onMouseDown, true);
|
140 | doc.addEventListener('mouseup', onMouseUp, true);
|
141 | doc.addEventListener('contextmenu', onContextMenu, true);
|
142 | };
|
143 | const getActivatableTarget = (ev) => {
|
144 | if (ev.composedPath) {
|
145 | const path = ev.composedPath();
|
146 | for (let i = 0; i < path.length - 2; i++) {
|
147 | const el = path[i];
|
148 | if (el.classList && el.classList.contains('ion-activatable')) {
|
149 | return el;
|
150 | }
|
151 | }
|
152 | }
|
153 | else {
|
154 | return ev.target.closest('.ion-activatable');
|
155 | }
|
156 | };
|
157 | const isInstant = (el) => {
|
158 | return el.classList.contains('ion-activatable-instant');
|
159 | };
|
160 | const getRippleEffect = (el) => {
|
161 | if (el.shadowRoot) {
|
162 | const ripple = el.shadowRoot.querySelector('ion-ripple-effect');
|
163 | if (ripple) {
|
164 | return ripple;
|
165 | }
|
166 | }
|
167 | return el.querySelector('ion-ripple-effect');
|
168 | };
|
169 | const ACTIVATED = 'ion-activated';
|
170 | const ADD_ACTIVATED_DEFERS = 200;
|
171 | const CLEAR_STATE_DEFERS = 200;
|
172 | const MOUSE_WAIT = 2500;
|
173 |
|
174 | export { startTapClick };
|