Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x | import type { Attachment } from 'svelte/attachments';
/**
* Calls the callback when an upward swipe is detected on the attached node.
*/
export function onSwipe(
// TODO: other directions
direction: 'up',
callback: () => void,
{ minDistance = 30 }: { minDistance?: number } = {}
): Attachment<HTMLElement> {
Iif (direction !== 'up') {
throw new Error(`Unsupported swipe direction: ${direction}`);
}
return (node) => {
let touchStartX: number | undefined;
let touchStartY: number | undefined;
const onTouchStart = (event: TouchEvent) => {
const touch = event.touches[0];
Iif (!touch) return;
touchStartX = touch.clientX;
touchStartY = touch.clientY;
};
const onTouchEnd = (event: TouchEvent) => {
Iif (touchStartX === undefined || touchStartY === undefined) return;
const touch = event.changedTouches[0];
Iif (!touch) return;
const deltaX = touch.clientX - touchStartX;
const deltaY = touch.clientY - touchStartY;
touchStartX = undefined;
touchStartY = undefined;
const mostlyVertical = Math.abs(deltaY) > Math.abs(deltaX);
const isSwipeUp = deltaY < -minDistance;
Eif (mostlyVertical && isSwipeUp) {
callback();
}
};
const onTouchCancel = () => {
touchStartX = undefined;
touchStartY = undefined;
};
node.addEventListener('touchstart', onTouchStart, { passive: true });
node.addEventListener('touchend', onTouchEnd, { passive: true });
node.addEventListener('touchcancel', onTouchCancel, { passive: true });
return () => {
node.removeEventListener('touchstart', onTouchStart);
node.removeEventListener('touchend', onTouchEnd);
node.removeEventListener('touchcancel', onTouchCancel);
};
};
}
if (import.meta.vitest) {
const { describe, it, expect, vi } = import.meta.vitest;
describe('onSwipe', () => {
it('should call callback on upward swipe', () => {
const callback = vi.fn();
const node = document.createElement('div');
const detach = onSwipe('up', callback)(node);
// Simulate touch events
const touchStartEvent = new TouchEvent('touchstart', {
touches: [{ identifier: 0, target: node, clientX: 100, clientY: 100 }],
});
const touchEndEvent = new TouchEvent('touchend', {
changedTouches: [{ identifier: 0, target: node, clientX: 100, clientY: 50 }],
});
node.dispatchEvent(touchStartEvent);
node.dispatchEvent(touchEndEvent);
expect(callback).toHaveBeenCalled();
detach();
});
});
}
export function onLongPress(timeout: number, callback: () => void): Attachment<HTMLElement> {
return (node) => {
let pressTimer: ReturnType<typeof setTimeout> | null = null;
const ondown = () => {
pressTimer = setTimeout(() => {
callback();
}, timeout);
};
const onup = () => {
Eif (pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
// node.addEventListener('mousedown', onMouseDown);
// node.addEventListener('mouseup', onMouseUp);
// node.addEventListener('mouseleave', onMouseUp);
node.addEventListener('pointerdown', ondown);
node.addEventListener('pointerup', onup);
node.addEventListener('pointerleave', onup);
return () => {
// node.removeEventListener('mousedown', onMouseDown);
// node.removeEventListener('mouseup', onMouseUp);
// node.removeEventListener('mouseleave', onMouseUp);
node.removeEventListener('pointerdown', ondown);
node.removeEventListener('pointerup', onup);
node.removeEventListener('pointerleave', onup);
};
};
}
if (import.meta.vitest) {
const { describe, it, expect, vi } = import.meta.vitest;
describe('onLongPress', () => {
it('should call callback after long press', async () => {
const callback = vi.fn();
const node = document.createElement('div');
const detach = onLongPress(500, callback)(node);
node.dispatchEvent(new PointerEvent('pointerdown'));
await new Promise((r) => setTimeout(r, 600)); // Wait longer than the timeout
node.dispatchEvent(new PointerEvent('pointerup'));
expect(callback).toHaveBeenCalled();
detach();
});
it('should not call callback if press is released early', async () => {
const callback = vi.fn();
const node = document.createElement('div');
const detach = onLongPress(500, callback)(node);
node.dispatchEvent(new PointerEvent('pointerdown'));
await new Promise((r) => setTimeout(r, 300)); // Wait less than the timeout
node.dispatchEvent(new PointerEvent('pointerup'));
expect(callback).not.toHaveBeenCalled();
detach();
});
});
}
|