@aspect-ops/exon-ui 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +438 -0
- package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
- package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
- package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
- package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
- package/dist/components/ActionSheet/index.d.ts +3 -0
- package/dist/components/ActionSheet/index.js +2 -0
- package/dist/components/Alert/Alert.svelte +165 -0
- package/dist/components/Alert/Alert.svelte.d.ts +11 -0
- package/dist/components/Alert/index.d.ts +1 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
- package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
- package/dist/components/AspectRatio/index.d.ts +1 -0
- package/dist/components/AspectRatio/index.js +1 -0
- package/dist/components/Avatar/Avatar.svelte +147 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
- package/dist/components/Avatar/AvatarGroup.svelte +153 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Avatar/index.js +2 -0
- package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
- package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
- package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
- package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/index.d.ts +3 -0
- package/dist/components/BottomSheet/index.js +3 -0
- package/dist/components/Box/Box.svelte +41 -0
- package/dist/components/Box/Box.svelte.d.ts +7 -0
- package/dist/components/Box/index.d.ts +1 -0
- package/dist/components/Box/index.js +1 -0
- package/dist/components/Card/Card.svelte +95 -0
- package/dist/components/Card/Card.svelte.d.ts +10 -0
- package/dist/components/Card/CardBody.svelte +32 -0
- package/dist/components/Card/CardBody.svelte.d.ts +7 -0
- package/dist/components/Card/CardFooter.svelte +34 -0
- package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
- package/dist/components/Card/CardHeader.svelte +67 -0
- package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
- package/dist/components/Card/index.d.ts +4 -0
- package/dist/components/Card/index.js +4 -0
- package/dist/components/Center/Center.svelte +28 -0
- package/dist/components/Center/Center.svelte.d.ts +8 -0
- package/dist/components/Center/index.d.ts +1 -0
- package/dist/components/Center/index.js +1 -0
- package/dist/components/Container/Container.svelte +58 -0
- package/dist/components/Container/Container.svelte.d.ts +10 -0
- package/dist/components/Container/index.d.ts +1 -0
- package/dist/components/Container/index.js +1 -0
- package/dist/components/Divider/Divider.svelte +38 -0
- package/dist/components/Divider/Divider.svelte.d.ts +9 -0
- package/dist/components/Divider/index.d.ts +1 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/EmptyState/EmptyState.svelte +164 -0
- package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
- package/dist/components/EmptyState/index.d.ts +1 -0
- package/dist/components/EmptyState/index.js +1 -0
- package/dist/components/FAB/FAB.svelte +242 -0
- package/dist/components/FAB/FAB.svelte.d.ts +9 -0
- package/dist/components/FAB/FABGroup.svelte +449 -0
- package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
- package/dist/components/FAB/index.d.ts +3 -0
- package/dist/components/FAB/index.js +2 -0
- package/dist/components/Grid/Grid.svelte +136 -0
- package/dist/components/Grid/Grid.svelte.d.ts +12 -0
- package/dist/components/Grid/GridItem.svelte +21 -0
- package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
- package/dist/components/Grid/index.d.ts +2 -0
- package/dist/components/Grid/index.js +2 -0
- package/dist/components/List/List.svelte +42 -0
- package/dist/components/List/List.svelte.d.ts +18 -0
- package/dist/components/List/ListItem.svelte +139 -0
- package/dist/components/List/ListItem.svelte.d.ts +36 -0
- package/dist/components/List/index.d.ts +2 -0
- package/dist/components/List/index.js +2 -0
- package/dist/components/Modal/Modal.svelte +204 -0
- package/dist/components/Modal/Modal.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalBody.svelte +50 -0
- package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalFooter.svelte +37 -0
- package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalHeader.svelte +73 -0
- package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
- package/dist/components/Modal/index.d.ts +4 -0
- package/dist/components/Modal/index.js +4 -0
- package/dist/components/Popover/Popover.svelte +14 -0
- package/dist/components/Popover/Popover.svelte.d.ts +7 -0
- package/dist/components/Popover/PopoverContent.svelte +63 -0
- package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
- package/dist/components/Popover/PopoverTrigger.svelte +14 -0
- package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
- package/dist/components/Popover/index.d.ts +3 -0
- package/dist/components/Popover/index.js +3 -0
- package/dist/components/Progress/ProgressBar.svelte +86 -0
- package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
- package/dist/components/Progress/ProgressCircle.svelte +134 -0
- package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
- package/dist/components/Progress/Spinner.svelte +68 -0
- package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
- package/dist/components/Progress/index.d.ts +3 -0
- package/dist/components/Progress/index.js +3 -0
- package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
- package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
- package/dist/components/PullToRefresh/index.d.ts +1 -0
- package/dist/components/PullToRefresh/index.js +1 -0
- package/dist/components/SafeArea/SafeArea.svelte +33 -0
- package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
- package/dist/components/Select/Select.svelte +55 -12
- package/dist/components/Skeleton/Skeleton.svelte +59 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
- package/dist/components/Skeleton/index.d.ts +1 -0
- package/dist/components/Skeleton/index.js +1 -0
- package/dist/components/Spacer/Spacer.svelte +56 -0
- package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
- package/dist/components/Spacer/index.d.ts +1 -0
- package/dist/components/Spacer/index.js +1 -0
- package/dist/components/Stack/Stack.svelte +117 -0
- package/dist/components/Stack/Stack.svelte.d.ts +13 -0
- package/dist/components/Stack/index.d.ts +1 -0
- package/dist/components/Stack/index.js +1 -0
- package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
- package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
- package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
- package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
- package/dist/components/SwipeActions/index.d.ts +2 -0
- package/dist/components/SwipeActions/index.js +2 -0
- package/dist/components/Switch/Switch.svelte +29 -9
- package/dist/components/Table/Table.svelte +175 -0
- package/dist/components/Table/Table.svelte.d.ts +38 -0
- package/dist/components/Table/TableBody.svelte +26 -0
- package/dist/components/Table/TableBody.svelte.d.ts +13 -0
- package/dist/components/Table/TableCell.svelte +85 -0
- package/dist/components/Table/TableCell.svelte.d.ts +28 -0
- package/dist/components/Table/TableHead.svelte +36 -0
- package/dist/components/Table/TableHead.svelte.d.ts +13 -0
- package/dist/components/Table/TableHeader.svelte +217 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
- package/dist/components/Table/TableRow.svelte +92 -0
- package/dist/components/Table/TableRow.svelte.d.ts +28 -0
- package/dist/components/Table/index.d.ts +6 -0
- package/dist/components/Table/index.js +6 -0
- package/dist/components/Tag/Tag.svelte +189 -0
- package/dist/components/Tag/Tag.svelte.d.ts +13 -0
- package/dist/components/Tag/index.d.ts +1 -0
- package/dist/components/Tag/index.js +1 -0
- package/dist/components/Toast/Toast.svelte +241 -0
- package/dist/components/Toast/Toast.svelte.d.ts +18 -0
- package/dist/components/Toast/ToastContainer.svelte +110 -0
- package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
- package/dist/components/Toast/index.d.ts +3 -0
- package/dist/components/Toast/index.js +3 -0
- package/dist/components/Toast/toast.d.ts +13 -0
- package/dist/components/Toast/toast.js +55 -0
- package/dist/components/Tooltip/Tooltip.svelte +71 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
- package/dist/components/Tooltip/index.d.ts +2 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/index.d.ts +29 -1
- package/dist/index.js +32 -0
- package/dist/styles/tokens.css +5 -0
- package/dist/types/data-display.d.ts +93 -0
- package/dist/types/data-display.js +1 -0
- package/dist/types/feedback.d.ts +92 -0
- package/dist/types/feedback.js +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/layout.d.ts +57 -0
- package/dist/types/layout.js +1 -0
- package/dist/types/mobile.d.ts +91 -0
- package/dist/types/mobile.js +1 -0
- package/dist/utils/gestures.d.ts +219 -0
- package/dist/utils/gestures.js +492 -0
- package/dist/utils/haptics.d.ts +89 -0
- package/dist/utils/haptics.js +198 -0
- package/dist/utils/platform.d.ts +47 -0
- package/dist/utils/platform.js +156 -0
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Touch gesture utilities for mobile-friendly interactions
|
|
3
|
+
* Supports swipe, drag, and pinch gestures with velocity tracking
|
|
4
|
+
*/
|
|
5
|
+
/** Direction of a swipe gesture */
|
|
6
|
+
export type SwipeDirection = 'left' | 'right' | 'up' | 'down';
|
|
7
|
+
/** Options for swipe gesture detection */
|
|
8
|
+
export interface SwipeOptions {
|
|
9
|
+
/** Minimum distance in pixels to trigger swipe (default: 50) */
|
|
10
|
+
threshold?: number;
|
|
11
|
+
/** Maximum time in ms for swipe to be valid (default: 300) */
|
|
12
|
+
maxTime?: number;
|
|
13
|
+
/** Callback when swipe is detected */
|
|
14
|
+
onSwipe?: (direction: SwipeDirection, velocity: number) => void;
|
|
15
|
+
/** Callback for swipe left */
|
|
16
|
+
onSwipeLeft?: (velocity: number) => void;
|
|
17
|
+
/** Callback for swipe right */
|
|
18
|
+
onSwipeRight?: (velocity: number) => void;
|
|
19
|
+
/** Callback for swipe up */
|
|
20
|
+
onSwipeUp?: (velocity: number) => void;
|
|
21
|
+
/** Callback for swipe down */
|
|
22
|
+
onSwipeDown?: (velocity: number) => void;
|
|
23
|
+
}
|
|
24
|
+
/** Current state of a drag gesture */
|
|
25
|
+
export interface DragState {
|
|
26
|
+
/** Whether drag is currently active */
|
|
27
|
+
isDragging: boolean;
|
|
28
|
+
/** Starting X position */
|
|
29
|
+
startX: number;
|
|
30
|
+
/** Starting Y position */
|
|
31
|
+
startY: number;
|
|
32
|
+
/** Current X position */
|
|
33
|
+
currentX: number;
|
|
34
|
+
/** Current Y position */
|
|
35
|
+
currentY: number;
|
|
36
|
+
/** Change in X from start */
|
|
37
|
+
deltaX: number;
|
|
38
|
+
/** Change in Y from start */
|
|
39
|
+
deltaY: number;
|
|
40
|
+
/** Horizontal velocity in pixels/ms */
|
|
41
|
+
velocityX: number;
|
|
42
|
+
/** Vertical velocity in pixels/ms */
|
|
43
|
+
velocityY: number;
|
|
44
|
+
}
|
|
45
|
+
/** Options for drag gesture tracking */
|
|
46
|
+
export interface DragOptions {
|
|
47
|
+
/** Minimum distance to start drag (default: 0) */
|
|
48
|
+
threshold?: number;
|
|
49
|
+
/** Callback on drag start */
|
|
50
|
+
onDragStart?: (state: DragState) => void;
|
|
51
|
+
/** Callback during drag */
|
|
52
|
+
onDrag?: (state: DragState) => void;
|
|
53
|
+
/** Callback on drag end */
|
|
54
|
+
onDragEnd?: (state: DragState) => void;
|
|
55
|
+
}
|
|
56
|
+
/** Current state of a pinch gesture */
|
|
57
|
+
export interface PinchState {
|
|
58
|
+
/** Whether pinch is currently active */
|
|
59
|
+
isPinching: boolean;
|
|
60
|
+
/** Initial distance between touch points */
|
|
61
|
+
initialDistance: number;
|
|
62
|
+
/** Current distance between touch points */
|
|
63
|
+
currentDistance: number;
|
|
64
|
+
/** Scale factor (current / initial) */
|
|
65
|
+
scale: number;
|
|
66
|
+
/** Center X position between fingers */
|
|
67
|
+
centerX: number;
|
|
68
|
+
/** Center Y position between fingers */
|
|
69
|
+
centerY: number;
|
|
70
|
+
/** Rotation angle in degrees (if fingers rotate) */
|
|
71
|
+
rotation: number;
|
|
72
|
+
}
|
|
73
|
+
/** Options for pinch gesture detection */
|
|
74
|
+
export interface PinchOptions {
|
|
75
|
+
/** Minimum scale change to trigger callback (default: 0.01) */
|
|
76
|
+
scaleThreshold?: number;
|
|
77
|
+
/** Callback on pinch start */
|
|
78
|
+
onPinchStart?: (state: PinchState) => void;
|
|
79
|
+
/** Callback during pinch */
|
|
80
|
+
onPinch?: (state: PinchState) => void;
|
|
81
|
+
/** Callback on pinch end */
|
|
82
|
+
onPinchEnd?: (state: PinchState) => void;
|
|
83
|
+
}
|
|
84
|
+
/** Handler interface returned by gesture creators */
|
|
85
|
+
export interface GestureHandler {
|
|
86
|
+
start: (e: TouchEvent) => void;
|
|
87
|
+
move: (e: TouchEvent) => void;
|
|
88
|
+
end: (e: TouchEvent) => void;
|
|
89
|
+
cancel?: (e: TouchEvent) => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates a swipe gesture handler
|
|
93
|
+
* Detects horizontal and vertical swipes with velocity
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```svelte
|
|
97
|
+
* <script>
|
|
98
|
+
* const swipe = createSwipeHandler({
|
|
99
|
+
* threshold: 50,
|
|
100
|
+
* onSwipe: (direction) => console.log('Swiped:', direction)
|
|
101
|
+
* });
|
|
102
|
+
* </script>
|
|
103
|
+
*
|
|
104
|
+
* <div ontouchstart={swipe.start} ontouchmove={swipe.move} ontouchend={swipe.end}>
|
|
105
|
+
* Swipe me
|
|
106
|
+
* </div>
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export declare function createSwipeHandler(options?: SwipeOptions): GestureHandler;
|
|
110
|
+
/**
|
|
111
|
+
* Creates a drag gesture handler
|
|
112
|
+
* Tracks position and velocity during drag
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```svelte
|
|
116
|
+
* <script>
|
|
117
|
+
* let x = 0, y = 0;
|
|
118
|
+
* const drag = createDragHandler({
|
|
119
|
+
* onDrag: (state) => {
|
|
120
|
+
* x = state.deltaX;
|
|
121
|
+
* y = state.deltaY;
|
|
122
|
+
* }
|
|
123
|
+
* });
|
|
124
|
+
* </script>
|
|
125
|
+
*
|
|
126
|
+
* <div
|
|
127
|
+
* style="transform: translate({x}px, {y}px)"
|
|
128
|
+
* ontouchstart={drag.start}
|
|
129
|
+
* ontouchmove={drag.move}
|
|
130
|
+
* ontouchend={drag.end}
|
|
131
|
+
* >
|
|
132
|
+
* Drag me
|
|
133
|
+
* </div>
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function createDragHandler(options?: DragOptions): GestureHandler;
|
|
137
|
+
/**
|
|
138
|
+
* Creates a pinch gesture handler
|
|
139
|
+
* Detects scale changes from two-finger pinch
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```svelte
|
|
143
|
+
* <script>
|
|
144
|
+
* let scale = 1;
|
|
145
|
+
* const pinch = createPinchHandler({
|
|
146
|
+
* onPinch: (state) => {
|
|
147
|
+
* scale = state.scale;
|
|
148
|
+
* }
|
|
149
|
+
* });
|
|
150
|
+
* </script>
|
|
151
|
+
*
|
|
152
|
+
* <div
|
|
153
|
+
* style="transform: scale({scale})"
|
|
154
|
+
* ontouchstart={pinch.start}
|
|
155
|
+
* ontouchmove={pinch.move}
|
|
156
|
+
* ontouchend={pinch.end}
|
|
157
|
+
* >
|
|
158
|
+
* Pinch to zoom
|
|
159
|
+
* </div>
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function createPinchHandler(options?: PinchOptions): GestureHandler;
|
|
163
|
+
/** Action options for swipe */
|
|
164
|
+
export interface SwipeActionOptions extends SwipeOptions {
|
|
165
|
+
/** Prevent default touch behavior */
|
|
166
|
+
preventDefault?: boolean;
|
|
167
|
+
}
|
|
168
|
+
/** Action options for drag */
|
|
169
|
+
export interface DragActionOptions extends DragOptions {
|
|
170
|
+
/** Prevent default touch behavior */
|
|
171
|
+
preventDefault?: boolean;
|
|
172
|
+
}
|
|
173
|
+
/** Action options for pinch */
|
|
174
|
+
export interface PinchActionOptions extends PinchOptions {
|
|
175
|
+
/** Prevent default touch behavior */
|
|
176
|
+
preventDefault?: boolean;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Svelte action for swipe gestures
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```svelte
|
|
183
|
+
* <div use:swipeAction={{ onSwipeLeft: () => console.log('Left!') }}>
|
|
184
|
+
* Swipe me
|
|
185
|
+
* </div>
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
export declare function swipeAction(node: HTMLElement, options?: SwipeActionOptions): {
|
|
189
|
+
update(newOptions: SwipeActionOptions): void;
|
|
190
|
+
destroy(): void;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Svelte action for drag gestures
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```svelte
|
|
197
|
+
* <div use:dragAction={{ onDrag: (state) => console.log(state.deltaX, state.deltaY) }}>
|
|
198
|
+
* Drag me
|
|
199
|
+
* </div>
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export declare function dragAction(node: HTMLElement, options?: DragActionOptions): {
|
|
203
|
+
update(newOptions: DragActionOptions): void;
|
|
204
|
+
destroy(): void;
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Svelte action for pinch gestures
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```svelte
|
|
211
|
+
* <div use:pinchAction={{ onPinch: (state) => console.log('Scale:', state.scale) }}>
|
|
212
|
+
* Pinch to zoom
|
|
213
|
+
* </div>
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export declare function pinchAction(node: HTMLElement, options?: PinchActionOptions): {
|
|
217
|
+
update(newOptions: PinchActionOptions): void;
|
|
218
|
+
destroy(): void;
|
|
219
|
+
};
|
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Touch gesture utilities for mobile-friendly interactions
|
|
3
|
+
* Supports swipe, drag, and pinch gestures with velocity tracking
|
|
4
|
+
*/
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Utility Functions
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/** Calculate distance between two points */
|
|
9
|
+
function getDistance(x1, y1, x2, y2) {
|
|
10
|
+
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
|
11
|
+
}
|
|
12
|
+
/** Calculate angle between two points in degrees */
|
|
13
|
+
function getAngle(x1, y1, x2, y2) {
|
|
14
|
+
return Math.atan2(y2 - y1, x2 - x1) * (180 / Math.PI);
|
|
15
|
+
}
|
|
16
|
+
/** Calculate velocity (pixels per millisecond) */
|
|
17
|
+
function calculateVelocity(distance, time) {
|
|
18
|
+
return time > 0 ? distance / time : 0;
|
|
19
|
+
}
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Swipe Handler
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* Creates a swipe gesture handler
|
|
25
|
+
* Detects horizontal and vertical swipes with velocity
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```svelte
|
|
29
|
+
* <script>
|
|
30
|
+
* const swipe = createSwipeHandler({
|
|
31
|
+
* threshold: 50,
|
|
32
|
+
* onSwipe: (direction) => console.log('Swiped:', direction)
|
|
33
|
+
* });
|
|
34
|
+
* </script>
|
|
35
|
+
*
|
|
36
|
+
* <div ontouchstart={swipe.start} ontouchmove={swipe.move} ontouchend={swipe.end}>
|
|
37
|
+
* Swipe me
|
|
38
|
+
* </div>
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function createSwipeHandler(options = {}) {
|
|
42
|
+
const { threshold = 50, maxTime = 300, onSwipe, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown } = options;
|
|
43
|
+
let startX = 0;
|
|
44
|
+
let startY = 0;
|
|
45
|
+
let startTime = 0;
|
|
46
|
+
let tracking = false;
|
|
47
|
+
return {
|
|
48
|
+
start(e) {
|
|
49
|
+
if (e.touches.length !== 1)
|
|
50
|
+
return;
|
|
51
|
+
const touch = e.touches[0];
|
|
52
|
+
startX = touch.clientX;
|
|
53
|
+
startY = touch.clientY;
|
|
54
|
+
startTime = Date.now();
|
|
55
|
+
tracking = true;
|
|
56
|
+
},
|
|
57
|
+
move(_e) {
|
|
58
|
+
// Swipe doesn't need move tracking, but included for API consistency
|
|
59
|
+
},
|
|
60
|
+
end(e) {
|
|
61
|
+
if (!tracking)
|
|
62
|
+
return;
|
|
63
|
+
tracking = false;
|
|
64
|
+
const touch = e.changedTouches[0];
|
|
65
|
+
const endX = touch.clientX;
|
|
66
|
+
const endY = touch.clientY;
|
|
67
|
+
const endTime = Date.now();
|
|
68
|
+
const diffX = endX - startX;
|
|
69
|
+
const diffY = endY - startY;
|
|
70
|
+
const elapsed = endTime - startTime;
|
|
71
|
+
// Check if swipe was quick enough
|
|
72
|
+
if (elapsed > maxTime)
|
|
73
|
+
return;
|
|
74
|
+
const absX = Math.abs(diffX);
|
|
75
|
+
const absY = Math.abs(diffY);
|
|
76
|
+
// Determine if horizontal or vertical swipe
|
|
77
|
+
if (absX > absY && absX > threshold) {
|
|
78
|
+
// Horizontal swipe
|
|
79
|
+
const velocity = calculateVelocity(absX, elapsed);
|
|
80
|
+
const direction = diffX > 0 ? 'right' : 'left';
|
|
81
|
+
onSwipe?.(direction, velocity);
|
|
82
|
+
if (direction === 'left')
|
|
83
|
+
onSwipeLeft?.(velocity);
|
|
84
|
+
if (direction === 'right')
|
|
85
|
+
onSwipeRight?.(velocity);
|
|
86
|
+
}
|
|
87
|
+
else if (absY > absX && absY > threshold) {
|
|
88
|
+
// Vertical swipe
|
|
89
|
+
const velocity = calculateVelocity(absY, elapsed);
|
|
90
|
+
const direction = diffY > 0 ? 'down' : 'up';
|
|
91
|
+
onSwipe?.(direction, velocity);
|
|
92
|
+
if (direction === 'up')
|
|
93
|
+
onSwipeUp?.(velocity);
|
|
94
|
+
if (direction === 'down')
|
|
95
|
+
onSwipeDown?.(velocity);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
cancel() {
|
|
99
|
+
tracking = false;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Drag Handler
|
|
105
|
+
// ============================================================================
|
|
106
|
+
/**
|
|
107
|
+
* Creates a drag gesture handler
|
|
108
|
+
* Tracks position and velocity during drag
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```svelte
|
|
112
|
+
* <script>
|
|
113
|
+
* let x = 0, y = 0;
|
|
114
|
+
* const drag = createDragHandler({
|
|
115
|
+
* onDrag: (state) => {
|
|
116
|
+
* x = state.deltaX;
|
|
117
|
+
* y = state.deltaY;
|
|
118
|
+
* }
|
|
119
|
+
* });
|
|
120
|
+
* </script>
|
|
121
|
+
*
|
|
122
|
+
* <div
|
|
123
|
+
* style="transform: translate({x}px, {y}px)"
|
|
124
|
+
* ontouchstart={drag.start}
|
|
125
|
+
* ontouchmove={drag.move}
|
|
126
|
+
* ontouchend={drag.end}
|
|
127
|
+
* >
|
|
128
|
+
* Drag me
|
|
129
|
+
* </div>
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function createDragHandler(options = {}) {
|
|
133
|
+
const { threshold = 0, onDragStart, onDrag, onDragEnd } = options;
|
|
134
|
+
let state = {
|
|
135
|
+
isDragging: false,
|
|
136
|
+
startX: 0,
|
|
137
|
+
startY: 0,
|
|
138
|
+
currentX: 0,
|
|
139
|
+
currentY: 0,
|
|
140
|
+
deltaX: 0,
|
|
141
|
+
deltaY: 0,
|
|
142
|
+
velocityX: 0,
|
|
143
|
+
velocityY: 0
|
|
144
|
+
};
|
|
145
|
+
let lastX = 0;
|
|
146
|
+
let lastY = 0;
|
|
147
|
+
let lastTime = 0;
|
|
148
|
+
let dragStarted = false;
|
|
149
|
+
let tracking = false;
|
|
150
|
+
function resetState() {
|
|
151
|
+
state = {
|
|
152
|
+
isDragging: false,
|
|
153
|
+
startX: 0,
|
|
154
|
+
startY: 0,
|
|
155
|
+
currentX: 0,
|
|
156
|
+
currentY: 0,
|
|
157
|
+
deltaX: 0,
|
|
158
|
+
deltaY: 0,
|
|
159
|
+
velocityX: 0,
|
|
160
|
+
velocityY: 0
|
|
161
|
+
};
|
|
162
|
+
dragStarted = false;
|
|
163
|
+
tracking = false;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
start(e) {
|
|
167
|
+
if (e.touches.length !== 1)
|
|
168
|
+
return;
|
|
169
|
+
const touch = e.touches[0];
|
|
170
|
+
state.startX = touch.clientX;
|
|
171
|
+
state.startY = touch.clientY;
|
|
172
|
+
state.currentX = touch.clientX;
|
|
173
|
+
state.currentY = touch.clientY;
|
|
174
|
+
lastX = touch.clientX;
|
|
175
|
+
lastY = touch.clientY;
|
|
176
|
+
lastTime = Date.now();
|
|
177
|
+
tracking = true;
|
|
178
|
+
},
|
|
179
|
+
move(e) {
|
|
180
|
+
if (!tracking || e.touches.length !== 1)
|
|
181
|
+
return;
|
|
182
|
+
const touch = e.touches[0];
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
const elapsed = now - lastTime;
|
|
185
|
+
state.currentX = touch.clientX;
|
|
186
|
+
state.currentY = touch.clientY;
|
|
187
|
+
state.deltaX = state.currentX - state.startX;
|
|
188
|
+
state.deltaY = state.currentY - state.startY;
|
|
189
|
+
// Calculate velocity
|
|
190
|
+
if (elapsed > 0) {
|
|
191
|
+
state.velocityX = (touch.clientX - lastX) / elapsed;
|
|
192
|
+
state.velocityY = (touch.clientY - lastY) / elapsed;
|
|
193
|
+
}
|
|
194
|
+
lastX = touch.clientX;
|
|
195
|
+
lastY = touch.clientY;
|
|
196
|
+
lastTime = now;
|
|
197
|
+
// Check threshold for drag start
|
|
198
|
+
const distance = getDistance(state.startX, state.startY, state.currentX, state.currentY);
|
|
199
|
+
if (!dragStarted && distance >= threshold) {
|
|
200
|
+
dragStarted = true;
|
|
201
|
+
state.isDragging = true;
|
|
202
|
+
onDragStart?.({ ...state });
|
|
203
|
+
}
|
|
204
|
+
if (dragStarted) {
|
|
205
|
+
onDrag?.({ ...state });
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
end(_e) {
|
|
209
|
+
if (!tracking)
|
|
210
|
+
return;
|
|
211
|
+
if (dragStarted) {
|
|
212
|
+
state.isDragging = false;
|
|
213
|
+
onDragEnd?.({ ...state });
|
|
214
|
+
}
|
|
215
|
+
resetState();
|
|
216
|
+
},
|
|
217
|
+
cancel() {
|
|
218
|
+
if (dragStarted) {
|
|
219
|
+
state.isDragging = false;
|
|
220
|
+
onDragEnd?.({ ...state });
|
|
221
|
+
}
|
|
222
|
+
resetState();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// Pinch Handler
|
|
228
|
+
// ============================================================================
|
|
229
|
+
/**
|
|
230
|
+
* Creates a pinch gesture handler
|
|
231
|
+
* Detects scale changes from two-finger pinch
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```svelte
|
|
235
|
+
* <script>
|
|
236
|
+
* let scale = 1;
|
|
237
|
+
* const pinch = createPinchHandler({
|
|
238
|
+
* onPinch: (state) => {
|
|
239
|
+
* scale = state.scale;
|
|
240
|
+
* }
|
|
241
|
+
* });
|
|
242
|
+
* </script>
|
|
243
|
+
*
|
|
244
|
+
* <div
|
|
245
|
+
* style="transform: scale({scale})"
|
|
246
|
+
* ontouchstart={pinch.start}
|
|
247
|
+
* ontouchmove={pinch.move}
|
|
248
|
+
* ontouchend={pinch.end}
|
|
249
|
+
* >
|
|
250
|
+
* Pinch to zoom
|
|
251
|
+
* </div>
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export function createPinchHandler(options = {}) {
|
|
255
|
+
const { scaleThreshold = 0.01, onPinchStart, onPinch, onPinchEnd } = options;
|
|
256
|
+
let state = {
|
|
257
|
+
isPinching: false,
|
|
258
|
+
initialDistance: 0,
|
|
259
|
+
currentDistance: 0,
|
|
260
|
+
scale: 1,
|
|
261
|
+
centerX: 0,
|
|
262
|
+
centerY: 0,
|
|
263
|
+
rotation: 0
|
|
264
|
+
};
|
|
265
|
+
let initialAngle = 0;
|
|
266
|
+
let pinchStarted = false;
|
|
267
|
+
let lastScale = 1;
|
|
268
|
+
function resetState() {
|
|
269
|
+
state = {
|
|
270
|
+
isPinching: false,
|
|
271
|
+
initialDistance: 0,
|
|
272
|
+
currentDistance: 0,
|
|
273
|
+
scale: 1,
|
|
274
|
+
centerX: 0,
|
|
275
|
+
centerY: 0,
|
|
276
|
+
rotation: 0
|
|
277
|
+
};
|
|
278
|
+
pinchStarted = false;
|
|
279
|
+
lastScale = 1;
|
|
280
|
+
}
|
|
281
|
+
function getTouchData(e) {
|
|
282
|
+
if (e.touches.length < 2)
|
|
283
|
+
return null;
|
|
284
|
+
const touch1 = e.touches[0];
|
|
285
|
+
const touch2 = e.touches[1];
|
|
286
|
+
const distance = getDistance(touch1.clientX, touch1.clientY, touch2.clientX, touch2.clientY);
|
|
287
|
+
const centerX = (touch1.clientX + touch2.clientX) / 2;
|
|
288
|
+
const centerY = (touch1.clientY + touch2.clientY) / 2;
|
|
289
|
+
const angle = getAngle(touch1.clientX, touch1.clientY, touch2.clientX, touch2.clientY);
|
|
290
|
+
return { distance, centerX, centerY, angle };
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
start(e) {
|
|
294
|
+
const data = getTouchData(e);
|
|
295
|
+
if (!data)
|
|
296
|
+
return;
|
|
297
|
+
state.initialDistance = data.distance;
|
|
298
|
+
state.currentDistance = data.distance;
|
|
299
|
+
state.centerX = data.centerX;
|
|
300
|
+
state.centerY = data.centerY;
|
|
301
|
+
state.scale = 1;
|
|
302
|
+
state.rotation = 0;
|
|
303
|
+
initialAngle = data.angle;
|
|
304
|
+
pinchStarted = true;
|
|
305
|
+
state.isPinching = true;
|
|
306
|
+
onPinchStart?.({ ...state });
|
|
307
|
+
},
|
|
308
|
+
move(e) {
|
|
309
|
+
if (!pinchStarted) {
|
|
310
|
+
// Check if pinch just started (second finger added)
|
|
311
|
+
const data = getTouchData(e);
|
|
312
|
+
if (data) {
|
|
313
|
+
state.initialDistance = data.distance;
|
|
314
|
+
state.currentDistance = data.distance;
|
|
315
|
+
state.centerX = data.centerX;
|
|
316
|
+
state.centerY = data.centerY;
|
|
317
|
+
state.scale = 1;
|
|
318
|
+
state.rotation = 0;
|
|
319
|
+
initialAngle = data.angle;
|
|
320
|
+
pinchStarted = true;
|
|
321
|
+
state.isPinching = true;
|
|
322
|
+
onPinchStart?.({ ...state });
|
|
323
|
+
}
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const data = getTouchData(e);
|
|
327
|
+
if (!data)
|
|
328
|
+
return;
|
|
329
|
+
state.currentDistance = data.distance;
|
|
330
|
+
state.centerX = data.centerX;
|
|
331
|
+
state.centerY = data.centerY;
|
|
332
|
+
// Calculate scale
|
|
333
|
+
if (state.initialDistance > 0) {
|
|
334
|
+
state.scale = state.currentDistance / state.initialDistance;
|
|
335
|
+
}
|
|
336
|
+
// Calculate rotation
|
|
337
|
+
state.rotation = data.angle - initialAngle;
|
|
338
|
+
// Only trigger callback if scale changed significantly
|
|
339
|
+
if (Math.abs(state.scale - lastScale) >= scaleThreshold) {
|
|
340
|
+
lastScale = state.scale;
|
|
341
|
+
onPinch?.({ ...state });
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
end(e) {
|
|
345
|
+
// Check if we still have 2 fingers
|
|
346
|
+
if (e.touches.length >= 2)
|
|
347
|
+
return;
|
|
348
|
+
if (pinchStarted) {
|
|
349
|
+
state.isPinching = false;
|
|
350
|
+
onPinchEnd?.({ ...state });
|
|
351
|
+
}
|
|
352
|
+
resetState();
|
|
353
|
+
},
|
|
354
|
+
cancel() {
|
|
355
|
+
if (pinchStarted) {
|
|
356
|
+
state.isPinching = false;
|
|
357
|
+
onPinchEnd?.({ ...state });
|
|
358
|
+
}
|
|
359
|
+
resetState();
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Svelte action for swipe gestures
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```svelte
|
|
368
|
+
* <div use:swipeAction={{ onSwipeLeft: () => console.log('Left!') }}>
|
|
369
|
+
* Swipe me
|
|
370
|
+
* </div>
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
export function swipeAction(node, options = {}) {
|
|
374
|
+
const { preventDefault = false, ...swipeOptions } = options;
|
|
375
|
+
const handler = createSwipeHandler(swipeOptions);
|
|
376
|
+
function handleStart(e) {
|
|
377
|
+
if (preventDefault)
|
|
378
|
+
e.preventDefault();
|
|
379
|
+
handler.start(e);
|
|
380
|
+
}
|
|
381
|
+
function handleMove(e) {
|
|
382
|
+
if (preventDefault)
|
|
383
|
+
e.preventDefault();
|
|
384
|
+
handler.move(e);
|
|
385
|
+
}
|
|
386
|
+
function handleEnd(e) {
|
|
387
|
+
handler.end(e);
|
|
388
|
+
}
|
|
389
|
+
node.addEventListener('touchstart', handleStart, { passive: !preventDefault });
|
|
390
|
+
node.addEventListener('touchmove', handleMove, { passive: !preventDefault });
|
|
391
|
+
node.addEventListener('touchend', handleEnd);
|
|
392
|
+
node.addEventListener('touchcancel', handler.cancel);
|
|
393
|
+
return {
|
|
394
|
+
update(newOptions) {
|
|
395
|
+
const { preventDefault: _newPrevent = false, ...newSwipeOptions } = newOptions;
|
|
396
|
+
Object.assign(options, newOptions);
|
|
397
|
+
Object.assign(swipeOptions, newSwipeOptions);
|
|
398
|
+
},
|
|
399
|
+
destroy() {
|
|
400
|
+
node.removeEventListener('touchstart', handleStart);
|
|
401
|
+
node.removeEventListener('touchmove', handleMove);
|
|
402
|
+
node.removeEventListener('touchend', handleEnd);
|
|
403
|
+
node.removeEventListener('touchcancel', handler.cancel);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Svelte action for drag gestures
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```svelte
|
|
412
|
+
* <div use:dragAction={{ onDrag: (state) => console.log(state.deltaX, state.deltaY) }}>
|
|
413
|
+
* Drag me
|
|
414
|
+
* </div>
|
|
415
|
+
* ```
|
|
416
|
+
*/
|
|
417
|
+
export function dragAction(node, options = {}) {
|
|
418
|
+
const { preventDefault = true, ...dragOptions } = options;
|
|
419
|
+
const handler = createDragHandler(dragOptions);
|
|
420
|
+
function handleStart(e) {
|
|
421
|
+
if (preventDefault)
|
|
422
|
+
e.preventDefault();
|
|
423
|
+
handler.start(e);
|
|
424
|
+
}
|
|
425
|
+
function handleMove(e) {
|
|
426
|
+
if (preventDefault)
|
|
427
|
+
e.preventDefault();
|
|
428
|
+
handler.move(e);
|
|
429
|
+
}
|
|
430
|
+
function handleEnd(e) {
|
|
431
|
+
handler.end(e);
|
|
432
|
+
}
|
|
433
|
+
node.addEventListener('touchstart', handleStart, { passive: !preventDefault });
|
|
434
|
+
node.addEventListener('touchmove', handleMove, { passive: !preventDefault });
|
|
435
|
+
node.addEventListener('touchend', handleEnd);
|
|
436
|
+
node.addEventListener('touchcancel', handler.cancel);
|
|
437
|
+
return {
|
|
438
|
+
update(newOptions) {
|
|
439
|
+
Object.assign(options, newOptions);
|
|
440
|
+
Object.assign(dragOptions, newOptions);
|
|
441
|
+
},
|
|
442
|
+
destroy() {
|
|
443
|
+
node.removeEventListener('touchstart', handleStart);
|
|
444
|
+
node.removeEventListener('touchmove', handleMove);
|
|
445
|
+
node.removeEventListener('touchend', handleEnd);
|
|
446
|
+
node.removeEventListener('touchcancel', handler.cancel);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Svelte action for pinch gestures
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```svelte
|
|
455
|
+
* <div use:pinchAction={{ onPinch: (state) => console.log('Scale:', state.scale) }}>
|
|
456
|
+
* Pinch to zoom
|
|
457
|
+
* </div>
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export function pinchAction(node, options = {}) {
|
|
461
|
+
const { preventDefault = true, ...pinchOptions } = options;
|
|
462
|
+
const handler = createPinchHandler(pinchOptions);
|
|
463
|
+
function handleStart(e) {
|
|
464
|
+
if (preventDefault)
|
|
465
|
+
e.preventDefault();
|
|
466
|
+
handler.start(e);
|
|
467
|
+
}
|
|
468
|
+
function handleMove(e) {
|
|
469
|
+
if (preventDefault)
|
|
470
|
+
e.preventDefault();
|
|
471
|
+
handler.move(e);
|
|
472
|
+
}
|
|
473
|
+
function handleEnd(e) {
|
|
474
|
+
handler.end(e);
|
|
475
|
+
}
|
|
476
|
+
node.addEventListener('touchstart', handleStart, { passive: !preventDefault });
|
|
477
|
+
node.addEventListener('touchmove', handleMove, { passive: !preventDefault });
|
|
478
|
+
node.addEventListener('touchend', handleEnd);
|
|
479
|
+
node.addEventListener('touchcancel', handler.cancel);
|
|
480
|
+
return {
|
|
481
|
+
update(newOptions) {
|
|
482
|
+
Object.assign(options, newOptions);
|
|
483
|
+
Object.assign(pinchOptions, newOptions);
|
|
484
|
+
},
|
|
485
|
+
destroy() {
|
|
486
|
+
node.removeEventListener('touchstart', handleStart);
|
|
487
|
+
node.removeEventListener('touchmove', handleMove);
|
|
488
|
+
node.removeEventListener('touchend', handleEnd);
|
|
489
|
+
node.removeEventListener('touchcancel', handler.cancel);
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|