@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.
Files changed (178) hide show
  1. package/README.md +438 -0
  2. package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
  3. package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
  4. package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
  5. package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
  6. package/dist/components/ActionSheet/index.d.ts +3 -0
  7. package/dist/components/ActionSheet/index.js +2 -0
  8. package/dist/components/Alert/Alert.svelte +165 -0
  9. package/dist/components/Alert/Alert.svelte.d.ts +11 -0
  10. package/dist/components/Alert/index.d.ts +1 -0
  11. package/dist/components/Alert/index.js +1 -0
  12. package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
  13. package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
  14. package/dist/components/AspectRatio/index.d.ts +1 -0
  15. package/dist/components/AspectRatio/index.js +1 -0
  16. package/dist/components/Avatar/Avatar.svelte +147 -0
  17. package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
  18. package/dist/components/Avatar/AvatarGroup.svelte +153 -0
  19. package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
  20. package/dist/components/Avatar/index.d.ts +2 -0
  21. package/dist/components/Avatar/index.js +2 -0
  22. package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
  23. package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
  24. package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
  25. package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
  26. package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
  27. package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
  28. package/dist/components/BottomSheet/index.d.ts +3 -0
  29. package/dist/components/BottomSheet/index.js +3 -0
  30. package/dist/components/Box/Box.svelte +41 -0
  31. package/dist/components/Box/Box.svelte.d.ts +7 -0
  32. package/dist/components/Box/index.d.ts +1 -0
  33. package/dist/components/Box/index.js +1 -0
  34. package/dist/components/Card/Card.svelte +95 -0
  35. package/dist/components/Card/Card.svelte.d.ts +10 -0
  36. package/dist/components/Card/CardBody.svelte +32 -0
  37. package/dist/components/Card/CardBody.svelte.d.ts +7 -0
  38. package/dist/components/Card/CardFooter.svelte +34 -0
  39. package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
  40. package/dist/components/Card/CardHeader.svelte +67 -0
  41. package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
  42. package/dist/components/Card/index.d.ts +4 -0
  43. package/dist/components/Card/index.js +4 -0
  44. package/dist/components/Center/Center.svelte +28 -0
  45. package/dist/components/Center/Center.svelte.d.ts +8 -0
  46. package/dist/components/Center/index.d.ts +1 -0
  47. package/dist/components/Center/index.js +1 -0
  48. package/dist/components/Container/Container.svelte +58 -0
  49. package/dist/components/Container/Container.svelte.d.ts +10 -0
  50. package/dist/components/Container/index.d.ts +1 -0
  51. package/dist/components/Container/index.js +1 -0
  52. package/dist/components/Divider/Divider.svelte +38 -0
  53. package/dist/components/Divider/Divider.svelte.d.ts +9 -0
  54. package/dist/components/Divider/index.d.ts +1 -0
  55. package/dist/components/Divider/index.js +1 -0
  56. package/dist/components/EmptyState/EmptyState.svelte +164 -0
  57. package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
  58. package/dist/components/EmptyState/index.d.ts +1 -0
  59. package/dist/components/EmptyState/index.js +1 -0
  60. package/dist/components/FAB/FAB.svelte +242 -0
  61. package/dist/components/FAB/FAB.svelte.d.ts +9 -0
  62. package/dist/components/FAB/FABGroup.svelte +449 -0
  63. package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
  64. package/dist/components/FAB/index.d.ts +3 -0
  65. package/dist/components/FAB/index.js +2 -0
  66. package/dist/components/Grid/Grid.svelte +136 -0
  67. package/dist/components/Grid/Grid.svelte.d.ts +12 -0
  68. package/dist/components/Grid/GridItem.svelte +21 -0
  69. package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
  70. package/dist/components/Grid/index.d.ts +2 -0
  71. package/dist/components/Grid/index.js +2 -0
  72. package/dist/components/List/List.svelte +42 -0
  73. package/dist/components/List/List.svelte.d.ts +18 -0
  74. package/dist/components/List/ListItem.svelte +139 -0
  75. package/dist/components/List/ListItem.svelte.d.ts +36 -0
  76. package/dist/components/List/index.d.ts +2 -0
  77. package/dist/components/List/index.js +2 -0
  78. package/dist/components/Modal/Modal.svelte +204 -0
  79. package/dist/components/Modal/Modal.svelte.d.ts +7 -0
  80. package/dist/components/Modal/ModalBody.svelte +50 -0
  81. package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
  82. package/dist/components/Modal/ModalFooter.svelte +37 -0
  83. package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
  84. package/dist/components/Modal/ModalHeader.svelte +73 -0
  85. package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
  86. package/dist/components/Modal/index.d.ts +4 -0
  87. package/dist/components/Modal/index.js +4 -0
  88. package/dist/components/Popover/Popover.svelte +14 -0
  89. package/dist/components/Popover/Popover.svelte.d.ts +7 -0
  90. package/dist/components/Popover/PopoverContent.svelte +63 -0
  91. package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
  92. package/dist/components/Popover/PopoverTrigger.svelte +14 -0
  93. package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
  94. package/dist/components/Popover/index.d.ts +3 -0
  95. package/dist/components/Popover/index.js +3 -0
  96. package/dist/components/Progress/ProgressBar.svelte +86 -0
  97. package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
  98. package/dist/components/Progress/ProgressCircle.svelte +134 -0
  99. package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
  100. package/dist/components/Progress/Spinner.svelte +68 -0
  101. package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
  102. package/dist/components/Progress/index.d.ts +3 -0
  103. package/dist/components/Progress/index.js +3 -0
  104. package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
  105. package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
  106. package/dist/components/PullToRefresh/index.d.ts +1 -0
  107. package/dist/components/PullToRefresh/index.js +1 -0
  108. package/dist/components/SafeArea/SafeArea.svelte +33 -0
  109. package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
  110. package/dist/components/Select/Select.svelte +55 -12
  111. package/dist/components/Skeleton/Skeleton.svelte +59 -0
  112. package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
  113. package/dist/components/Skeleton/index.d.ts +1 -0
  114. package/dist/components/Skeleton/index.js +1 -0
  115. package/dist/components/Spacer/Spacer.svelte +56 -0
  116. package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
  117. package/dist/components/Spacer/index.d.ts +1 -0
  118. package/dist/components/Spacer/index.js +1 -0
  119. package/dist/components/Stack/Stack.svelte +117 -0
  120. package/dist/components/Stack/Stack.svelte.d.ts +13 -0
  121. package/dist/components/Stack/index.d.ts +1 -0
  122. package/dist/components/Stack/index.js +1 -0
  123. package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
  124. package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
  125. package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
  126. package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
  127. package/dist/components/SwipeActions/index.d.ts +2 -0
  128. package/dist/components/SwipeActions/index.js +2 -0
  129. package/dist/components/Switch/Switch.svelte +29 -9
  130. package/dist/components/Table/Table.svelte +175 -0
  131. package/dist/components/Table/Table.svelte.d.ts +38 -0
  132. package/dist/components/Table/TableBody.svelte +26 -0
  133. package/dist/components/Table/TableBody.svelte.d.ts +13 -0
  134. package/dist/components/Table/TableCell.svelte +85 -0
  135. package/dist/components/Table/TableCell.svelte.d.ts +28 -0
  136. package/dist/components/Table/TableHead.svelte +36 -0
  137. package/dist/components/Table/TableHead.svelte.d.ts +13 -0
  138. package/dist/components/Table/TableHeader.svelte +217 -0
  139. package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
  140. package/dist/components/Table/TableRow.svelte +92 -0
  141. package/dist/components/Table/TableRow.svelte.d.ts +28 -0
  142. package/dist/components/Table/index.d.ts +6 -0
  143. package/dist/components/Table/index.js +6 -0
  144. package/dist/components/Tag/Tag.svelte +189 -0
  145. package/dist/components/Tag/Tag.svelte.d.ts +13 -0
  146. package/dist/components/Tag/index.d.ts +1 -0
  147. package/dist/components/Tag/index.js +1 -0
  148. package/dist/components/Toast/Toast.svelte +241 -0
  149. package/dist/components/Toast/Toast.svelte.d.ts +18 -0
  150. package/dist/components/Toast/ToastContainer.svelte +110 -0
  151. package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
  152. package/dist/components/Toast/index.d.ts +3 -0
  153. package/dist/components/Toast/index.js +3 -0
  154. package/dist/components/Toast/toast.d.ts +13 -0
  155. package/dist/components/Toast/toast.js +55 -0
  156. package/dist/components/Tooltip/Tooltip.svelte +71 -0
  157. package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
  158. package/dist/components/Tooltip/index.d.ts +2 -0
  159. package/dist/components/Tooltip/index.js +1 -0
  160. package/dist/index.d.ts +29 -1
  161. package/dist/index.js +32 -0
  162. package/dist/styles/tokens.css +5 -0
  163. package/dist/types/data-display.d.ts +93 -0
  164. package/dist/types/data-display.js +1 -0
  165. package/dist/types/feedback.d.ts +92 -0
  166. package/dist/types/feedback.js +1 -0
  167. package/dist/types/index.d.ts +4 -0
  168. package/dist/types/layout.d.ts +57 -0
  169. package/dist/types/layout.js +1 -0
  170. package/dist/types/mobile.d.ts +91 -0
  171. package/dist/types/mobile.js +1 -0
  172. package/dist/utils/gestures.d.ts +219 -0
  173. package/dist/utils/gestures.js +492 -0
  174. package/dist/utils/haptics.d.ts +89 -0
  175. package/dist/utils/haptics.js +198 -0
  176. package/dist/utils/platform.d.ts +47 -0
  177. package/dist/utils/platform.js +156 -0
  178. 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
+ }