@thelol3882/sapar 0.0.0-stage → 0.3.0
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/LICENSE +21 -0
- package/README.md +380 -2
- package/dist/adapter-Y9yB3-q3.d.ts +14 -0
- package/dist/adapter-fFZPsINL.d.ts +14 -0
- package/dist/chunk-7B66QEMY.js +536 -0
- package/dist/chunk-7B66QEMY.js.map +1 -0
- package/dist/chunk-FM64UDKJ.js +893 -0
- package/dist/chunk-FM64UDKJ.js.map +1 -0
- package/dist/chunk-VC6XLWM3.js +176 -0
- package/dist/chunk-VC6XLWM3.js.map +1 -0
- package/dist/chunk-ZBVQGMR4.js +583 -0
- package/dist/chunk-ZBVQGMR4.js.map +1 -0
- package/dist/cli/index.js +1335 -0
- package/dist/core/index.d.ts +273 -0
- package/dist/core/index.js +71 -0
- package/dist/core/index.js.map +1 -0
- package/dist/expo/index.d.ts +189 -0
- package/dist/expo/index.js +39 -0
- package/dist/expo/index.js.map +1 -0
- package/dist/expo-router/index.d.ts +18 -0
- package/dist/expo-router/index.js +46 -0
- package/dist/expo-router/index.js.map +1 -0
- package/dist/next/index.d.ts +18 -0
- package/dist/next/index.js +49 -0
- package/dist/next/index.js.map +1 -0
- package/dist/presentation-CClsPRT6.d.ts +12 -0
- package/dist/react/index.d.ts +35 -0
- package/dist/react/index.js +29 -0
- package/dist/react/index.js.map +1 -0
- package/dist/rtk/index.d.ts +751 -0
- package/dist/rtk/index.js +114 -0
- package/dist/rtk/index.js.map +1 -0
- package/dist/stepCount-CTYYFWiV.d.ts +57 -0
- package/dist/types-CHD2pB5X.d.ts +192 -0
- package/dist/web/index.d.ts +112 -0
- package/dist/web/index.js +34 -0
- package/dist/web/index.js.map +1 -0
- package/package.json +147 -3
- package/templates/expo/AnnouncementModal.tsx +102 -0
- package/templates/expo/TourCard.tsx +112 -0
- package/templates/expo/WhatsNewButton.tsx +87 -0
- package/templates/expo/WhatsNewPanel.tsx +167 -0
- package/templates/mantine/AnnouncementModal.tsx +66 -0
- package/templates/mantine/TourCard.tsx +84 -0
- package/templates/mantine/WhatsNewButton.tsx +62 -0
- package/templates/mantine/WhatsNewPanel.tsx +129 -0
- package/templates/tailwind/AnnouncementModal.tsx +100 -0
- package/templates/tailwind/TourCard.tsx +69 -0
- package/templates/tailwind/WhatsNewButton.tsx +56 -0
- package/templates/tailwind/WhatsNewPanel.tsx +174 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultAnnounceStep,
|
|
3
|
+
defaultProgress,
|
|
4
|
+
useSaparAdapter,
|
|
5
|
+
useTourStep
|
|
6
|
+
} from "./chunk-VC6XLWM3.js";
|
|
7
|
+
import {
|
|
8
|
+
resolvePresentation,
|
|
9
|
+
spotlightPath
|
|
10
|
+
} from "./chunk-FM64UDKJ.js";
|
|
11
|
+
|
|
12
|
+
// src/expo/adapter.ts
|
|
13
|
+
import { AccessibilityInfo, Dimensions } from "react-native";
|
|
14
|
+
|
|
15
|
+
// src/expo/measure.ts
|
|
16
|
+
function toRect(x, y, width, height) {
|
|
17
|
+
return width > 0 && height > 0 ? { x, y, width, height } : null;
|
|
18
|
+
}
|
|
19
|
+
function measureInWindow(instance) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const onMeasured = (...measurement) => resolve(toRect(...measurement));
|
|
22
|
+
instance.measureInWindow(onMeasured);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function measureLayout(instance, ancestor) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const onMeasured = (...measurement) => resolve(toRect(...measurement));
|
|
28
|
+
instance.measureLayout(ancestor, onMeasured, () => resolve(null));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/expo/registry.ts
|
|
33
|
+
var anchors = /* @__PURE__ */ new Map();
|
|
34
|
+
var waiters = /* @__PURE__ */ new Map();
|
|
35
|
+
var pressListeners = /* @__PURE__ */ new Map();
|
|
36
|
+
function addToSet(sets, key, item) {
|
|
37
|
+
const set = sets.get(key) ?? /* @__PURE__ */ new Set();
|
|
38
|
+
sets.set(key, set);
|
|
39
|
+
set.add(item);
|
|
40
|
+
return () => {
|
|
41
|
+
set.delete(item);
|
|
42
|
+
if (set.size === 0 && sets.get(key) === set) sets.delete(key);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function registerAnchor(key, anchor) {
|
|
46
|
+
anchors.set(key, anchor);
|
|
47
|
+
[...waiters.get(key) ?? []].forEach((resolve) => resolve(anchor));
|
|
48
|
+
return () => {
|
|
49
|
+
if (anchors.get(key) === anchor) anchors.delete(key);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getAnchor(key) {
|
|
53
|
+
return anchors.get(key) ?? null;
|
|
54
|
+
}
|
|
55
|
+
function waitForAnchor(key, timeoutMs) {
|
|
56
|
+
const present = getAnchor(key);
|
|
57
|
+
if (present) return Promise.resolve(present);
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
const stopWaiting = addToSet(waiters, key, settle);
|
|
60
|
+
const timer = setTimeout(() => settle(null), timeoutMs);
|
|
61
|
+
function settle(anchor) {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
stopWaiting();
|
|
64
|
+
resolve(anchor);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function subscribeToPress(key, listener) {
|
|
69
|
+
return addToSet(pressListeners, key, listener);
|
|
70
|
+
}
|
|
71
|
+
function notifyPress(key) {
|
|
72
|
+
[...pressListeners.get(key) ?? []].forEach((listener) => listener());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/expo/scrollIntoView.ts
|
|
76
|
+
var SCROLL_SETTLE_MS = 350;
|
|
77
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
78
|
+
function centeredOffset({ offsetY, height, viewportHeight }) {
|
|
79
|
+
return Math.max(0, offsetY - (viewportHeight - height) / 2);
|
|
80
|
+
}
|
|
81
|
+
async function scrollAnchorIntoView({ instance, container }, reducedMotion) {
|
|
82
|
+
const measurement = await container?.measure(instance);
|
|
83
|
+
if (!container || !measurement) return;
|
|
84
|
+
const animated = !reducedMotion;
|
|
85
|
+
container.scrollToOffset(centeredOffset(measurement), animated);
|
|
86
|
+
if (animated) await delay(SCROLL_SETTLE_MS);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/expo/stores.ts
|
|
90
|
+
var asyncStorageStore = (storage) => ({
|
|
91
|
+
get: (key) => storage.getItem(key),
|
|
92
|
+
set: (key, value) => storage.setItem(key, value),
|
|
93
|
+
remove: (key) => storage.removeItem(key)
|
|
94
|
+
});
|
|
95
|
+
var mmkvStore = (mmkv) => ({
|
|
96
|
+
get: (key) => mmkv.getString(key) ?? null,
|
|
97
|
+
set: (key, value) => mmkv.set(key, value),
|
|
98
|
+
remove: (key) => "remove" in mmkv ? mmkv.remove(key) : mmkv.delete(key)
|
|
99
|
+
});
|
|
100
|
+
function appSessionStore() {
|
|
101
|
+
const values = /* @__PURE__ */ new Map();
|
|
102
|
+
return {
|
|
103
|
+
get: (key) => values.get(key) ?? null,
|
|
104
|
+
set: (key, value) => void values.set(key, value)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/expo/adapter.ts
|
|
109
|
+
var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
110
|
+
function trackReducedMotion() {
|
|
111
|
+
let enabled = false;
|
|
112
|
+
void AccessibilityInfo.isReduceMotionEnabled().then((value) => {
|
|
113
|
+
enabled = value;
|
|
114
|
+
});
|
|
115
|
+
AccessibilityInfo.addEventListener("reduceMotionChanged", (value) => {
|
|
116
|
+
enabled = value;
|
|
117
|
+
});
|
|
118
|
+
return () => enabled;
|
|
119
|
+
}
|
|
120
|
+
function createHandle(key) {
|
|
121
|
+
return {
|
|
122
|
+
key,
|
|
123
|
+
measure: async () => {
|
|
124
|
+
const anchor = getAnchor(key);
|
|
125
|
+
return anchor ? measureInWindow(anchor.instance) : null;
|
|
126
|
+
},
|
|
127
|
+
onPress: (listener) => subscribeToPress(key, listener)
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function createExpoAdapter(options) {
|
|
131
|
+
const { compactBreakpointPx = 0 } = options;
|
|
132
|
+
return {
|
|
133
|
+
platform: "native",
|
|
134
|
+
async resolveAnchor(key, { timeoutMs }) {
|
|
135
|
+
const anchor = await waitForAnchor(key, timeoutMs);
|
|
136
|
+
return anchor ? createHandle(key) : null;
|
|
137
|
+
},
|
|
138
|
+
async scrollIntoView(handle, { reducedMotion }) {
|
|
139
|
+
const anchor = getAnchor(handle.key);
|
|
140
|
+
if (anchor) await scrollAnchorIntoView(anchor, reducedMotion);
|
|
141
|
+
},
|
|
142
|
+
currentRoute: options.currentRoute,
|
|
143
|
+
async navigate(route) {
|
|
144
|
+
await options.navigate(route);
|
|
145
|
+
await nextFrame();
|
|
146
|
+
},
|
|
147
|
+
prefersReducedMotion: trackReducedMotion(),
|
|
148
|
+
isCompact: () => Dimensions.get("window").width < compactBreakpointPx,
|
|
149
|
+
session: options.session ?? appSessionStore(),
|
|
150
|
+
now: Date.now
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/expo/useTourAnchor.ts
|
|
155
|
+
import { useCallback as useCallback2 } from "react";
|
|
156
|
+
|
|
157
|
+
// src/expo/assignRef.ts
|
|
158
|
+
function assignRef(ref, value) {
|
|
159
|
+
if (typeof ref === "function") ref(value);
|
|
160
|
+
else if (ref) ref.current = value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/expo/scrollContainer.tsx
|
|
164
|
+
import { createContext, forwardRef, useCallback, useContext, useMemo, useRef } from "react";
|
|
165
|
+
import { ScrollView } from "react-native";
|
|
166
|
+
import { jsx } from "react/jsx-runtime";
|
|
167
|
+
var SCROLL_EVENT_THROTTLE_MS = 16;
|
|
168
|
+
var ScrollContainerContext = createContext(null);
|
|
169
|
+
function useTourScrollContainer() {
|
|
170
|
+
return useContext(ScrollContainerContext);
|
|
171
|
+
}
|
|
172
|
+
function createScrollEmitter() {
|
|
173
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
174
|
+
let pendingFrame = null;
|
|
175
|
+
return {
|
|
176
|
+
subscribe(listener) {
|
|
177
|
+
listeners.add(listener);
|
|
178
|
+
return () => void listeners.delete(listener);
|
|
179
|
+
},
|
|
180
|
+
emit() {
|
|
181
|
+
if (pendingFrame !== null) return;
|
|
182
|
+
pendingFrame = requestAnimationFrame(() => {
|
|
183
|
+
pendingFrame = null;
|
|
184
|
+
[...listeners].forEach((listener) => listener());
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function createScrollContainer(getScrollView, emitter) {
|
|
190
|
+
return {
|
|
191
|
+
async measure(instance) {
|
|
192
|
+
const scrollView = getScrollView();
|
|
193
|
+
const content = scrollView?.getInnerViewRef();
|
|
194
|
+
if (!scrollView || !content) return null;
|
|
195
|
+
const [layout, viewport] = await Promise.all([measureLayout(instance, content), measureInWindow(scrollView)]);
|
|
196
|
+
return layout && viewport ? { offsetY: layout.y, height: layout.height, viewportHeight: viewport.height } : null;
|
|
197
|
+
},
|
|
198
|
+
scrollToOffset: (offsetY, animated) => getScrollView()?.scrollTo({ y: offsetY, animated }),
|
|
199
|
+
subscribeToScroll: emitter.subscribe
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
var TourScrollView = forwardRef(function TourScrollView2(props, forwardedRef) {
|
|
203
|
+
const { onScroll } = props;
|
|
204
|
+
const scrollViewRef = useRef(null);
|
|
205
|
+
const emitter = useMemo(createScrollEmitter, []);
|
|
206
|
+
const container = useMemo(() => createScrollContainer(() => scrollViewRef.current, emitter), [emitter]);
|
|
207
|
+
const handleScroll = useCallback(
|
|
208
|
+
(event) => {
|
|
209
|
+
emitter.emit();
|
|
210
|
+
onScroll?.(event);
|
|
211
|
+
},
|
|
212
|
+
[emitter, onScroll]
|
|
213
|
+
);
|
|
214
|
+
const setScrollView = useCallback(
|
|
215
|
+
(node) => {
|
|
216
|
+
scrollViewRef.current = node;
|
|
217
|
+
assignRef(forwardedRef, node);
|
|
218
|
+
},
|
|
219
|
+
[forwardedRef]
|
|
220
|
+
);
|
|
221
|
+
return /* @__PURE__ */ jsx(ScrollContainerContext.Provider, { value: container, children: /* @__PURE__ */ jsx(ScrollView, { scrollEventThrottle: SCROLL_EVENT_THROTTLE_MS, ...props, onScroll: handleScroll, ref: setScrollView }) });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// src/expo/useTourAnchor.ts
|
|
225
|
+
function useTourAnchor(key, { ref: hostRef, onTouchEnd: hostOnTouchEnd } = {}) {
|
|
226
|
+
const container = useTourScrollContainer();
|
|
227
|
+
const ref = useCallback2(
|
|
228
|
+
(instance) => {
|
|
229
|
+
assignRef(hostRef, instance);
|
|
230
|
+
if (!instance) return void 0;
|
|
231
|
+
const unregister = registerAnchor(key, { instance, container });
|
|
232
|
+
return () => {
|
|
233
|
+
unregister();
|
|
234
|
+
assignRef(hostRef, null);
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
[key, container, hostRef]
|
|
238
|
+
);
|
|
239
|
+
const onTouchEnd = useCallback2(
|
|
240
|
+
(event) => {
|
|
241
|
+
hostOnTouchEnd?.(event);
|
|
242
|
+
notifyPress(key);
|
|
243
|
+
},
|
|
244
|
+
[key, hostOnTouchEnd]
|
|
245
|
+
);
|
|
246
|
+
return { ref, collapsable: false, onTouchEnd };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/expo/cardPosition.ts
|
|
250
|
+
var OPPOSITE_SIDE = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
251
|
+
var clamp = (value, min, max) => Math.max(min, Math.min(value, max));
|
|
252
|
+
function spaceOnSide(side, { anchor, window, insets, offset }) {
|
|
253
|
+
switch (side) {
|
|
254
|
+
case "bottom":
|
|
255
|
+
return window.height - insets.bottom - (anchor.y + anchor.height) - offset;
|
|
256
|
+
case "top":
|
|
257
|
+
return anchor.y - insets.top - offset;
|
|
258
|
+
case "right":
|
|
259
|
+
return window.width - insets.right - (anchor.x + anchor.width) - offset;
|
|
260
|
+
case "left":
|
|
261
|
+
return anchor.x - insets.left - offset;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
var preferredSide = (placement) => placement === "auto" ? "bottom" : placement;
|
|
265
|
+
function findSideWithRoom(input) {
|
|
266
|
+
const preferred = preferredSide(input.placement);
|
|
267
|
+
const needed = preferred === "top" || preferred === "bottom" ? input.card.height : input.card.width;
|
|
268
|
+
const hasRoom = (side) => spaceOnSide(side, input) >= needed;
|
|
269
|
+
return [preferred, OPPOSITE_SIDE[preferred]].find(hasRoom) ?? null;
|
|
270
|
+
}
|
|
271
|
+
function positionBesideAnchor(side, { anchor, card, offset }) {
|
|
272
|
+
const centeredX = anchor.x + (anchor.width - card.width) / 2;
|
|
273
|
+
const centeredY = anchor.y + (anchor.height - card.height) / 2;
|
|
274
|
+
switch (side) {
|
|
275
|
+
case "bottom":
|
|
276
|
+
return { x: centeredX, y: anchor.y + anchor.height + offset };
|
|
277
|
+
case "top":
|
|
278
|
+
return { x: centeredX, y: anchor.y - offset - card.height };
|
|
279
|
+
case "right":
|
|
280
|
+
return { x: anchor.x + anchor.width + offset, y: centeredY };
|
|
281
|
+
case "left":
|
|
282
|
+
return { x: anchor.x - offset - card.width, y: centeredY };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function placeCard(input) {
|
|
286
|
+
const { anchor, card, window } = input;
|
|
287
|
+
if (!anchor) return { x: (window.width - card.width) / 2, y: (window.height - card.height) / 2, fits: true };
|
|
288
|
+
const anchored = { ...input, anchor };
|
|
289
|
+
const side = findSideWithRoom(anchored);
|
|
290
|
+
return { ...positionBesideAnchor(side ?? preferredSide(input.placement), anchored), fits: side !== null };
|
|
291
|
+
}
|
|
292
|
+
function computeCardPosition(input) {
|
|
293
|
+
const { card, window, insets } = input;
|
|
294
|
+
const { x, y, fits } = placeCard(input);
|
|
295
|
+
return {
|
|
296
|
+
x: clamp(x, insets.left, window.width - insets.right - card.width),
|
|
297
|
+
y: clamp(y, insets.top, window.height - insets.bottom - card.height),
|
|
298
|
+
fits
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/expo/useAnchorRect.ts
|
|
303
|
+
import { useEffect, useState } from "react";
|
|
304
|
+
import { useWindowDimensions } from "react-native";
|
|
305
|
+
var SETTLE_POLL_COUNT = 5;
|
|
306
|
+
var SETTLE_POLL_INTERVAL_MS = 120;
|
|
307
|
+
var sameRect = (a, b) => a === b || !!a && !!b && a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
308
|
+
function useAnchorRect(handle) {
|
|
309
|
+
const { width, height } = useWindowDimensions();
|
|
310
|
+
const [measured, setMeasured] = useState(null);
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
if (!handle) {
|
|
313
|
+
setMeasured(null);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
let cancelled = false;
|
|
317
|
+
async function measure(current) {
|
|
318
|
+
const rect = await current.measure();
|
|
319
|
+
if (cancelled) return;
|
|
320
|
+
setMeasured(
|
|
321
|
+
(previous) => previous?.handle === current && sameRect(previous.rect, rect) ? previous : { handle: current, rect }
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
void measure(handle);
|
|
325
|
+
const stopFollowingScroll = getAnchor(handle.key)?.container?.subscribeToScroll(() => void measure(handle));
|
|
326
|
+
let polls = 0;
|
|
327
|
+
const timer = setInterval(() => {
|
|
328
|
+
void measure(handle);
|
|
329
|
+
if (++polls >= SETTLE_POLL_COUNT) clearInterval(timer);
|
|
330
|
+
}, SETTLE_POLL_INTERVAL_MS);
|
|
331
|
+
return () => {
|
|
332
|
+
cancelled = true;
|
|
333
|
+
clearInterval(timer);
|
|
334
|
+
stopFollowingScroll?.();
|
|
335
|
+
};
|
|
336
|
+
}, [handle, width, height]);
|
|
337
|
+
return handle && measured?.handle === handle ? measured.rect : null;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/expo/TourPopover.tsx
|
|
341
|
+
import { useCallback as useCallback3, useState as useState3 } from "react";
|
|
342
|
+
import { StyleSheet, useWindowDimensions as useWindowDimensions3, View } from "react-native";
|
|
343
|
+
|
|
344
|
+
// src/expo/useIsCompact.ts
|
|
345
|
+
import { useWindowDimensions as useWindowDimensions2 } from "react-native";
|
|
346
|
+
function useIsCompact() {
|
|
347
|
+
useWindowDimensions2();
|
|
348
|
+
return useSaparAdapter()?.isCompact() ?? false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/expo/useKeyboardHeight.ts
|
|
352
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
353
|
+
import { Keyboard } from "react-native";
|
|
354
|
+
function useKeyboardHeight() {
|
|
355
|
+
const [height, setHeight] = useState2(0);
|
|
356
|
+
useEffect2(() => {
|
|
357
|
+
const show = ({ endCoordinates }) => setHeight(endCoordinates.height);
|
|
358
|
+
const hide = () => setHeight(0);
|
|
359
|
+
const subscriptions = [
|
|
360
|
+
Keyboard.addListener("keyboardWillShow", show),
|
|
361
|
+
Keyboard.addListener("keyboardDidShow", show),
|
|
362
|
+
Keyboard.addListener("keyboardWillHide", hide),
|
|
363
|
+
Keyboard.addListener("keyboardDidHide", hide)
|
|
364
|
+
];
|
|
365
|
+
return () => subscriptions.forEach((subscription) => subscription.remove());
|
|
366
|
+
}, []);
|
|
367
|
+
return height;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/expo/TourPopover.tsx
|
|
371
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
372
|
+
import { createElement } from "react";
|
|
373
|
+
var DEFAULT_OFFSET_PX = 12;
|
|
374
|
+
var KEYBOARD_GAP_PX = 8;
|
|
375
|
+
var DEFAULT_INSETS = { top: 8, right: 8, bottom: 8, left: 8 };
|
|
376
|
+
var styles = StyleSheet.create({
|
|
377
|
+
centered: { ...StyleSheet.absoluteFill, justifyContent: "center", alignItems: "center" },
|
|
378
|
+
sheet: { position: "absolute", left: 0, right: 0, bottom: 0 },
|
|
379
|
+
// Positioned by transform so the card is measured at its natural width wherever it ends up.
|
|
380
|
+
anchored: { position: "absolute", top: 0, left: 0 },
|
|
381
|
+
hidden: { opacity: 0 }
|
|
382
|
+
});
|
|
383
|
+
var paddingFor = ({ top, right, bottom, left }) => ({
|
|
384
|
+
paddingTop: top,
|
|
385
|
+
paddingRight: right,
|
|
386
|
+
paddingBottom: bottom,
|
|
387
|
+
paddingLeft: left
|
|
388
|
+
});
|
|
389
|
+
function Sheet({ insets, style, children }) {
|
|
390
|
+
return /* @__PURE__ */ jsx2(View, { pointerEvents: "box-none", style: [styles.sheet, paddingFor({ ...insets, top: 0 }), style], children });
|
|
391
|
+
}
|
|
392
|
+
function CenteredCard({ insets, style, children }) {
|
|
393
|
+
return /* @__PURE__ */ jsx2(View, { pointerEvents: "box-none", style: [styles.centered, paddingFor(insets), style], children });
|
|
394
|
+
}
|
|
395
|
+
function AnchoredCard({ position, onLayout, style, children }) {
|
|
396
|
+
const transform = position ? [{ translateX: position.x }, { translateY: position.y }] : void 0;
|
|
397
|
+
return /* @__PURE__ */ jsx2(View, { onLayout, style: [styles.anchored, { transform }, !position && styles.hidden, style], children });
|
|
398
|
+
}
|
|
399
|
+
function Popover({ rect, placement, offset, presentation, insets, ...appearance }) {
|
|
400
|
+
const compact = useIsCompact();
|
|
401
|
+
const window = useWindowDimensions3();
|
|
402
|
+
const keyboardHeight = useKeyboardHeight();
|
|
403
|
+
const [card, setCard] = useState3(null);
|
|
404
|
+
const handleLayout = useCallback3(({ nativeEvent: { layout } }) => {
|
|
405
|
+
setCard(
|
|
406
|
+
(previous) => previous?.width === layout.width && previous.height === layout.height ? previous : { width: layout.width, height: layout.height }
|
|
407
|
+
);
|
|
408
|
+
}, []);
|
|
409
|
+
const bottom = keyboardHeight > 0 ? Math.max(insets.bottom, keyboardHeight + KEYBOARD_GAP_PX) : insets.bottom;
|
|
410
|
+
const clearedInsets = { ...insets, bottom };
|
|
411
|
+
const position = card && computeCardPosition({ anchor: rect, card, window, placement, offset, insets: clearedInsets });
|
|
412
|
+
switch (resolvePresentation({ presentation, compact, fits: position?.fits })) {
|
|
413
|
+
case "sheet":
|
|
414
|
+
return /* @__PURE__ */ jsx2(Sheet, { ...appearance, insets: clearedInsets });
|
|
415
|
+
case "anchored":
|
|
416
|
+
return rect ? /* @__PURE__ */ jsx2(AnchoredCard, { ...appearance, position, onLayout: handleLayout }) : /* @__PURE__ */ jsx2(CenteredCard, { ...appearance, insets: clearedInsets });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function TourPopover({ offset = DEFAULT_OFFSET_PX, insets, presentation = "auto", ...rest }) {
|
|
420
|
+
const tourStep = useTourStep();
|
|
421
|
+
const rect = useAnchorRect(tourStep?.handle ?? null);
|
|
422
|
+
if (!tourStep) return null;
|
|
423
|
+
return /* @__PURE__ */ createElement(
|
|
424
|
+
Popover,
|
|
425
|
+
{
|
|
426
|
+
...rest,
|
|
427
|
+
key: `${tourStep.tour.key}:${tourStep.step.key}`,
|
|
428
|
+
placement: tourStep.step.placement,
|
|
429
|
+
offset,
|
|
430
|
+
presentation,
|
|
431
|
+
insets: { ...DEFAULT_INSETS, ...insets },
|
|
432
|
+
rect
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/expo/TourSpotlight.tsx
|
|
438
|
+
import { StyleSheet as StyleSheet2, useWindowDimensions as useWindowDimensions4 } from "react-native";
|
|
439
|
+
import Svg, { Path } from "react-native-svg";
|
|
440
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
441
|
+
var DEFAULT_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
442
|
+
var DEFAULT_RADIUS = 8;
|
|
443
|
+
function Spotlight({ handle, padding, onClickOutside, color = DEFAULT_COLOR, radius = DEFAULT_RADIUS, style }) {
|
|
444
|
+
const rect = useAnchorRect(handle);
|
|
445
|
+
const window = useWindowDimensions4();
|
|
446
|
+
return /* @__PURE__ */ jsx3(
|
|
447
|
+
Svg,
|
|
448
|
+
{
|
|
449
|
+
width: window.width,
|
|
450
|
+
height: window.height,
|
|
451
|
+
style: [StyleSheet2.absoluteFill, style],
|
|
452
|
+
pointerEvents: "box-none",
|
|
453
|
+
accessibilityElementsHidden: true,
|
|
454
|
+
importantForAccessibility: "no-hide-descendants",
|
|
455
|
+
children: /* @__PURE__ */ jsx3(Path, { d: spotlightPath({ rect, padding, radius, bounds: window }), fill: color, fillRule: "evenodd", onPress: onClickOutside })
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
function TourSpotlight({ onClickOutside, ...rest }) {
|
|
460
|
+
const tourStep = useTourStep();
|
|
461
|
+
if (!tourStep) return null;
|
|
462
|
+
return /* @__PURE__ */ jsx3(
|
|
463
|
+
Spotlight,
|
|
464
|
+
{
|
|
465
|
+
...rest,
|
|
466
|
+
handle: tourStep.handle,
|
|
467
|
+
padding: tourStep.step.padding,
|
|
468
|
+
onClickOutside: onClickOutside ?? tourStep.skip
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/expo/useHardwareBackSkip.ts
|
|
474
|
+
import { useEffect as useEffect3 } from "react";
|
|
475
|
+
import { BackHandler } from "react-native";
|
|
476
|
+
function useHardwareBackSkip(skip) {
|
|
477
|
+
useEffect3(() => {
|
|
478
|
+
if (!skip) return;
|
|
479
|
+
const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
|
|
480
|
+
skip();
|
|
481
|
+
return true;
|
|
482
|
+
});
|
|
483
|
+
return () => subscription.remove();
|
|
484
|
+
}, [skip]);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/expo/TourOverlay.tsx
|
|
488
|
+
import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
489
|
+
function TourOverlay({ spotlightColor, spotlightStyle, popoverStyle, onClickOutside, insets, presentation, children }) {
|
|
490
|
+
const tourStep = useTourStep();
|
|
491
|
+
useHardwareBackSkip(tourStep?.skip);
|
|
492
|
+
if (!tourStep) return null;
|
|
493
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
494
|
+
/* @__PURE__ */ jsx4(TourSpotlight, { color: spotlightColor, style: spotlightStyle, onClickOutside }),
|
|
495
|
+
/* @__PURE__ */ jsx4(TourPopover, { style: popoverStyle, insets, presentation, children: typeof children === "function" ? children(tourStep) : children })
|
|
496
|
+
] });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/expo/TourStep.tsx
|
|
500
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect4 } from "react";
|
|
501
|
+
import {
|
|
502
|
+
AccessibilityInfo as AccessibilityInfo2,
|
|
503
|
+
Pressable,
|
|
504
|
+
Text,
|
|
505
|
+
View as View2
|
|
506
|
+
} from "react-native";
|
|
507
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
508
|
+
var TourStepContext = createContext2(null);
|
|
509
|
+
function useStepState() {
|
|
510
|
+
const tourStep = useContext2(TourStepContext);
|
|
511
|
+
if (!tourStep) throw new Error("TourStep parts must be rendered inside <TourStep.Root>.");
|
|
512
|
+
return tourStep;
|
|
513
|
+
}
|
|
514
|
+
function Dialog({ tourStep, announceStep = defaultAnnounceStep, children, ...rest }) {
|
|
515
|
+
const { step, position, total } = tourStep;
|
|
516
|
+
useEffect4(() => AccessibilityInfo2.announceForAccessibility(announceStep({ position, total, title: step.title })), [step.key]);
|
|
517
|
+
return /* @__PURE__ */ jsx5(TourStepContext.Provider, { value: tourStep, children: /* @__PURE__ */ jsx5(View2, { ...rest, role: "dialog", accessibilityViewIsModal: false, children }) });
|
|
518
|
+
}
|
|
519
|
+
function Root(props) {
|
|
520
|
+
const tourStep = useTourStep();
|
|
521
|
+
return tourStep ? /* @__PURE__ */ jsx5(Dialog, { ...props, tourStep }) : null;
|
|
522
|
+
}
|
|
523
|
+
function Title({ children, ...rest }) {
|
|
524
|
+
const { step } = useStepState();
|
|
525
|
+
return /* @__PURE__ */ jsx5(Text, { role: "heading", ...rest, children: children ?? step.title });
|
|
526
|
+
}
|
|
527
|
+
function Body({ children, ...rest }) {
|
|
528
|
+
const { step } = useStepState();
|
|
529
|
+
return /* @__PURE__ */ jsx5(Text, { ...rest, children: children ?? step.body });
|
|
530
|
+
}
|
|
531
|
+
function Progress({ children, ...rest }) {
|
|
532
|
+
const { position, total } = useStepState();
|
|
533
|
+
const count = { position, total };
|
|
534
|
+
const content = typeof children === "function" ? children(count) : children ?? defaultProgress(count);
|
|
535
|
+
return /* @__PURE__ */ jsx5(Text, { ...rest, children: content });
|
|
536
|
+
}
|
|
537
|
+
function Button({ children, textStyle, ...rest }) {
|
|
538
|
+
return /* @__PURE__ */ jsx5(Pressable, { role: "button", ...rest, children: /* @__PURE__ */ jsx5(Text, { style: textStyle, children }) });
|
|
539
|
+
}
|
|
540
|
+
function runThen(handler, action) {
|
|
541
|
+
return (event) => {
|
|
542
|
+
handler?.(event);
|
|
543
|
+
void action();
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function Next({ doneLabel = "Done", nextLabel = "Next", showOnPress = false, children, onPress, ...rest }) {
|
|
547
|
+
const tourStep = useStepState();
|
|
548
|
+
if (tourStep.step.advanceOn === "press" && !showOnPress) return null;
|
|
549
|
+
return /* @__PURE__ */ jsx5(Button, { ...rest, onPress: runThen(onPress, tourStep.next), children: children ?? (tourStep.isLast ? doneLabel : nextLabel) });
|
|
550
|
+
}
|
|
551
|
+
function Back({ children = "Back", onPress, ...rest }) {
|
|
552
|
+
const tourStep = useStepState();
|
|
553
|
+
if (tourStep.isFirst) return null;
|
|
554
|
+
return /* @__PURE__ */ jsx5(Button, { ...rest, onPress: runThen(onPress, tourStep.previous), children });
|
|
555
|
+
}
|
|
556
|
+
function Skip({ children = "Skip", onPress, ...rest }) {
|
|
557
|
+
const { skip } = useStepState();
|
|
558
|
+
return /* @__PURE__ */ jsx5(Button, { ...rest, onPress: runThen(onPress, skip), children });
|
|
559
|
+
}
|
|
560
|
+
function Close({ children = "\xD7", ...rest }) {
|
|
561
|
+
return /* @__PURE__ */ jsx5(Skip, { accessibilityLabel: "Close tour", ...rest, children });
|
|
562
|
+
}
|
|
563
|
+
var TourStep = { Root, Title, Body, Progress, Next, Back, Skip, Close };
|
|
564
|
+
|
|
565
|
+
export {
|
|
566
|
+
registerAnchor,
|
|
567
|
+
getAnchor,
|
|
568
|
+
waitForAnchor,
|
|
569
|
+
asyncStorageStore,
|
|
570
|
+
mmkvStore,
|
|
571
|
+
appSessionStore,
|
|
572
|
+
createExpoAdapter,
|
|
573
|
+
useTourScrollContainer,
|
|
574
|
+
TourScrollView,
|
|
575
|
+
useTourAnchor,
|
|
576
|
+
computeCardPosition,
|
|
577
|
+
useAnchorRect,
|
|
578
|
+
TourPopover,
|
|
579
|
+
TourSpotlight,
|
|
580
|
+
TourOverlay,
|
|
581
|
+
TourStep
|
|
582
|
+
};
|
|
583
|
+
//# sourceMappingURL=chunk-ZBVQGMR4.js.map
|