@korsolutions/guidon 1.0.3 → 1.0.6
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/dist/commonjs/components/GuidonTooltip.js +17 -3
- package/dist/commonjs/components/GuidonTooltip.js.map +1 -1
- package/dist/commonjs/hooks/index.js +6 -0
- package/dist/commonjs/hooks/index.js.map +1 -1
- package/dist/commonjs/hooks/useGuidonRef.js +95 -8
- package/dist/commonjs/hooks/useGuidonRef.js.map +1 -1
- package/dist/commonjs/index.js +6 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/store.js +23 -2
- package/dist/commonjs/store.js.map +1 -1
- package/dist/module/components/GuidonTooltip.js +17 -3
- package/dist/module/components/GuidonTooltip.js.map +1 -1
- package/dist/module/hooks/index.js +1 -1
- package/dist/module/hooks/index.js.map +1 -1
- package/dist/module/hooks/useGuidonRef.js +95 -10
- package/dist/module/hooks/useGuidonRef.js.map +1 -1
- package/dist/module/index.js +1 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/store.js +23 -2
- package/dist/module/store.js.map +1 -1
- package/dist/typescript/commonjs/components/GuidonTooltip.d.ts.map +1 -1
- package/dist/typescript/commonjs/hooks/index.d.ts +1 -1
- package/dist/typescript/commonjs/hooks/index.d.ts.map +1 -1
- package/dist/typescript/commonjs/hooks/useGuidonRef.d.ts +5 -1
- package/dist/typescript/commonjs/hooks/useGuidonRef.d.ts.map +1 -1
- package/dist/typescript/commonjs/index.d.ts +2 -2
- package/dist/typescript/commonjs/index.d.ts.map +1 -1
- package/dist/typescript/commonjs/store.d.ts.map +1 -1
- package/dist/typescript/module/components/GuidonTooltip.d.ts.map +1 -1
- package/dist/typescript/module/hooks/index.d.ts +1 -1
- package/dist/typescript/module/hooks/index.d.ts.map +1 -1
- package/dist/typescript/module/hooks/useGuidonRef.d.ts +5 -1
- package/dist/typescript/module/hooks/useGuidonRef.d.ts.map +1 -1
- package/dist/typescript/module/index.d.ts +2 -2
- package/dist/typescript/module/index.d.ts.map +1 -1
- package/dist/typescript/module/store.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/GuidonTooltip.tsx +17 -3
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useGuidonRef.ts +139 -18
- package/src/index.ts +2 -5
- package/src/store.ts +41 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, type RefObject } from
|
|
2
|
-
import { Platform } from
|
|
3
|
-
import { useGuidonStore } from
|
|
4
|
-
import type { TargetMeasurements, GuidonStore, GuidonStep } from
|
|
1
|
+
import { useCallback, useEffect, useRef, type RefObject } from "react";
|
|
2
|
+
import { Platform } from "react-native";
|
|
3
|
+
import { useGuidonStore } from "../store";
|
|
4
|
+
import type { TargetMeasurements, GuidonStore, GuidonStep } from "../types";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Element type that can be measured
|
|
@@ -10,7 +10,7 @@ import type { TargetMeasurements, GuidonStore, GuidonStep } from '../types';
|
|
|
10
10
|
type MeasurableElement = {
|
|
11
11
|
getBoundingClientRect?: () => DOMRect;
|
|
12
12
|
measureInWindow?: (
|
|
13
|
-
callback: (x: number, y: number, width: number, height: number) => void
|
|
13
|
+
callback: (x: number, y: number, width: number, height: number) => void,
|
|
14
14
|
) => void;
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -38,21 +38,21 @@ type MeasurableElement = {
|
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
40
|
export function useGuidonRef<T extends MeasurableElement>(
|
|
41
|
-
targetId: string
|
|
41
|
+
targetId: string,
|
|
42
42
|
): RefObject<T | null> {
|
|
43
43
|
const ref = useRef<T>(null);
|
|
44
44
|
const measurementFrameRef = useRef<number | null>(null);
|
|
45
45
|
|
|
46
46
|
const registerTarget = useGuidonStore(
|
|
47
|
-
(state: GuidonStore) => state.registerTarget
|
|
47
|
+
(state: GuidonStore) => state.registerTarget,
|
|
48
48
|
);
|
|
49
49
|
const unregisterTarget = useGuidonStore(
|
|
50
|
-
(state: GuidonStore) => state.unregisterTarget
|
|
50
|
+
(state: GuidonStore) => state.unregisterTarget,
|
|
51
51
|
);
|
|
52
52
|
const isActive = useGuidonStore((state: GuidonStore) => state.isActive);
|
|
53
53
|
const config = useGuidonStore((state: GuidonStore) => state.config);
|
|
54
54
|
const currentStepIndex = useGuidonStore(
|
|
55
|
-
(state: GuidonStore) => state.currentStepIndex
|
|
55
|
+
(state: GuidonStore) => state.currentStepIndex,
|
|
56
56
|
);
|
|
57
57
|
|
|
58
58
|
// Check if this target is needed anywhere in the walkthrough
|
|
@@ -67,10 +67,10 @@ export function useGuidonRef<T extends MeasurableElement>(
|
|
|
67
67
|
const measureElement = useCallback(() => {
|
|
68
68
|
if (!ref.current || !isTargetNeeded) return;
|
|
69
69
|
|
|
70
|
-
if (Platform.OS ===
|
|
70
|
+
if (Platform.OS === "web") {
|
|
71
71
|
// Web measurement using getBoundingClientRect
|
|
72
72
|
const element = ref.current as unknown as HTMLElement;
|
|
73
|
-
if (element && typeof element.getBoundingClientRect ===
|
|
73
|
+
if (element && typeof element.getBoundingClientRect === "function") {
|
|
74
74
|
const rect = element.getBoundingClientRect();
|
|
75
75
|
const measurements: TargetMeasurements = {
|
|
76
76
|
x: rect.left + window.scrollX,
|
|
@@ -84,11 +84,11 @@ export function useGuidonRef<T extends MeasurableElement>(
|
|
|
84
84
|
// Native measurement using measureInWindow
|
|
85
85
|
const nativeRef = ref.current as unknown as {
|
|
86
86
|
measureInWindow: (
|
|
87
|
-
cb: (x: number, y: number, w: number, h: number) => void
|
|
87
|
+
cb: (x: number, y: number, w: number, h: number) => void,
|
|
88
88
|
) => void;
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
if (nativeRef && typeof nativeRef.measureInWindow ===
|
|
91
|
+
if (nativeRef && typeof nativeRef.measureInWindow === "function") {
|
|
92
92
|
nativeRef.measureInWindow((x, y, width, height) => {
|
|
93
93
|
if (width > 0 && height > 0) {
|
|
94
94
|
const measurements: TargetMeasurements = { x, y, width, height };
|
|
@@ -128,18 +128,18 @@ export function useGuidonRef<T extends MeasurableElement>(
|
|
|
128
128
|
|
|
129
129
|
// Web: handle scroll and resize
|
|
130
130
|
useEffect(() => {
|
|
131
|
-
if (Platform.OS !==
|
|
131
|
+
if (Platform.OS !== "web" || !isTargetNeeded) return;
|
|
132
132
|
|
|
133
133
|
const handleScrollOrResize = () => {
|
|
134
134
|
measureElement();
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
-
window.addEventListener(
|
|
138
|
-
window.addEventListener(
|
|
137
|
+
window.addEventListener("scroll", handleScrollOrResize, true);
|
|
138
|
+
window.addEventListener("resize", handleScrollOrResize);
|
|
139
139
|
|
|
140
140
|
return () => {
|
|
141
|
-
window.removeEventListener(
|
|
142
|
-
window.removeEventListener(
|
|
141
|
+
window.removeEventListener("scroll", handleScrollOrResize, true);
|
|
142
|
+
window.removeEventListener("resize", handleScrollOrResize);
|
|
143
143
|
};
|
|
144
144
|
}, [isTargetNeeded, measureElement]);
|
|
145
145
|
|
|
@@ -152,3 +152,124 @@ export function useGuidonRef<T extends MeasurableElement>(
|
|
|
152
152
|
|
|
153
153
|
return ref;
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
export function useGuidon() {
|
|
157
|
+
const register = useGuidonRegister();
|
|
158
|
+
return { register };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function useGuidonRegister() {
|
|
162
|
+
const elementsRef = useRef<Map<string, MeasurableElement>>(new Map());
|
|
163
|
+
const rafRef = useRef<Map<string, number>>(new Map());
|
|
164
|
+
|
|
165
|
+
const registerTarget = useGuidonStore(
|
|
166
|
+
(state: GuidonStore) => state.registerTarget,
|
|
167
|
+
);
|
|
168
|
+
const unregisterTarget = useGuidonStore(
|
|
169
|
+
(state: GuidonStore) => state.unregisterTarget,
|
|
170
|
+
);
|
|
171
|
+
const isActive = useGuidonStore((state) => state.isActive);
|
|
172
|
+
const config = useGuidonStore((state) => state.config);
|
|
173
|
+
const currentStepIndex = useGuidonStore((state) => state.currentStepIndex);
|
|
174
|
+
|
|
175
|
+
const measure = useCallback(
|
|
176
|
+
(targetId: string, node: MeasurableElement | null) => {
|
|
177
|
+
if (!node || !isActive || !config) return;
|
|
178
|
+
|
|
179
|
+
const isTargetUsed = config.steps.some(
|
|
180
|
+
(step) => step.targetId === targetId,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
if (!isTargetUsed) return;
|
|
184
|
+
|
|
185
|
+
if (Platform.OS === "web") {
|
|
186
|
+
const el = node as unknown as HTMLElement;
|
|
187
|
+
|
|
188
|
+
if (!el?.getBoundingClientRect) return;
|
|
189
|
+
|
|
190
|
+
const rect = el.getBoundingClientRect();
|
|
191
|
+
|
|
192
|
+
registerTarget(targetId, {
|
|
193
|
+
x: rect.left + window.scrollX,
|
|
194
|
+
y: rect.top + window.scrollY,
|
|
195
|
+
width: rect.width,
|
|
196
|
+
height: rect.height,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const native = node as unknown as {
|
|
203
|
+
measureInWindow?: (
|
|
204
|
+
cb: (x: number, y: number, w: number, h: number) => void,
|
|
205
|
+
) => void;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
native?.measureInWindow?.((x, y, width, height) => {
|
|
209
|
+
if (width > 0 && height > 0) {
|
|
210
|
+
registerTarget(targetId, { x, y, width, height });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
[isActive, config, registerTarget],
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const register = useCallback(
|
|
218
|
+
(targetId: string) => {
|
|
219
|
+
return (node: MeasurableElement | null) => {
|
|
220
|
+
if (node) {
|
|
221
|
+
elementsRef.current.set(targetId, node);
|
|
222
|
+
|
|
223
|
+
const raf = requestAnimationFrame(() => {
|
|
224
|
+
measure(targetId, node);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
rafRef.current.set(targetId, raf);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
elementsRef.current.delete(targetId);
|
|
231
|
+
unregisterTarget(targetId);
|
|
232
|
+
|
|
233
|
+
const raf = rafRef.current.get(targetId);
|
|
234
|
+
if (raf) cancelAnimationFrame(raf);
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
[measure, unregisterTarget],
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (!isActive || !config) return;
|
|
242
|
+
|
|
243
|
+
const step = config.steps[currentStepIndex];
|
|
244
|
+
if (!step?.targetId) return;
|
|
245
|
+
|
|
246
|
+
const targetId = step.targetId;
|
|
247
|
+
|
|
248
|
+
const node = elementsRef.current.get(targetId);
|
|
249
|
+
if (!node) return;
|
|
250
|
+
|
|
251
|
+
requestAnimationFrame(() => {
|
|
252
|
+
measure(targetId, node);
|
|
253
|
+
});
|
|
254
|
+
}, [currentStepIndex, isActive, config, measure]);
|
|
255
|
+
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (Platform.OS !== "web" || !isActive) return;
|
|
258
|
+
|
|
259
|
+
const handler = () => {
|
|
260
|
+
elementsRef.current.forEach((node, id) => {
|
|
261
|
+
measure(id, node);
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
window.addEventListener("scroll", handler, true);
|
|
266
|
+
window.addEventListener("resize", handler);
|
|
267
|
+
|
|
268
|
+
return () => {
|
|
269
|
+
window.removeEventListener("scroll", handler, true);
|
|
270
|
+
window.removeEventListener("resize", handler);
|
|
271
|
+
};
|
|
272
|
+
}, [isActive, measure]);
|
|
273
|
+
|
|
274
|
+
return register;
|
|
275
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ export {
|
|
|
8
8
|
} from "./components";
|
|
9
9
|
|
|
10
10
|
// Hooks
|
|
11
|
-
export { useGuidonRef } from "./hooks";
|
|
11
|
+
export { useGuidonRef, useGuidon } from "./hooks";
|
|
12
12
|
|
|
13
13
|
// Store and API
|
|
14
14
|
export {
|
|
@@ -51,7 +51,4 @@ export {
|
|
|
51
51
|
createCompositeAdapter,
|
|
52
52
|
} from "./persistence/adapters";
|
|
53
53
|
|
|
54
|
-
export {
|
|
55
|
-
useGuidonPersistence,
|
|
56
|
-
useShouldShowGuidon,
|
|
57
|
-
} from "./persistence/hooks";
|
|
54
|
+
export { useGuidonPersistence, useShouldShowGuidon } from "./persistence/hooks";
|
package/src/store.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
2
|
import { useShallow } from "zustand/react/shallow";
|
|
3
|
-
import type {
|
|
4
|
-
GuidonConfig,
|
|
5
|
-
GuidonStore,
|
|
6
|
-
TargetMeasurements,
|
|
7
|
-
} from "./types";
|
|
3
|
+
import type { GuidonConfig, GuidonStore, TargetMeasurements } from "./types";
|
|
8
4
|
|
|
9
5
|
const initialState = {
|
|
10
6
|
config: null,
|
|
@@ -27,8 +23,20 @@ export const useGuidonStore = create<GuidonStore>((set, get) => ({
|
|
|
27
23
|
|
|
28
24
|
start: () => {
|
|
29
25
|
const { config } = get();
|
|
30
|
-
|
|
26
|
+
console.log("[Guidon] start() called", {
|
|
27
|
+
hasConfig: !!config,
|
|
28
|
+
stepCount: config?.steps.length ?? 0,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!config || config.steps.length === 0) {
|
|
32
|
+
console.log("[Guidon] start() early return - no config or no steps");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
31
35
|
|
|
36
|
+
console.log(
|
|
37
|
+
"[Guidon] starting walkthrough with steps:",
|
|
38
|
+
config.steps.map((s) => s.id),
|
|
39
|
+
);
|
|
32
40
|
set({ isActive: true, currentStepIndex: 0, isCompleted: false });
|
|
33
41
|
|
|
34
42
|
// Call onStepEnter for the first step
|
|
@@ -41,22 +49,45 @@ export const useGuidonStore = create<GuidonStore>((set, get) => ({
|
|
|
41
49
|
|
|
42
50
|
next: () => {
|
|
43
51
|
const { config, currentStepIndex, isActive } = get();
|
|
44
|
-
|
|
52
|
+
console.log("[Guidon] next() called", {
|
|
53
|
+
hasConfig: !!config,
|
|
54
|
+
currentStepIndex,
|
|
55
|
+
isActive,
|
|
56
|
+
totalSteps: config?.steps.length ?? 0,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!config || !isActive) {
|
|
60
|
+
console.log("[Guidon] next() early return - config or isActive is falsy");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
45
63
|
|
|
46
64
|
const currentStep = config.steps[currentStepIndex];
|
|
65
|
+
console.log("[Guidon] current step:", currentStep?.id ?? "undefined");
|
|
47
66
|
currentStep?.onStepExit?.();
|
|
48
67
|
|
|
49
68
|
if (currentStepIndex < config.steps.length - 1) {
|
|
50
69
|
const nextIndex = currentStepIndex + 1;
|
|
51
70
|
const nextStep = config.steps[nextIndex];
|
|
52
71
|
|
|
72
|
+
console.log(
|
|
73
|
+
"[Guidon] advancing to step",
|
|
74
|
+
nextIndex,
|
|
75
|
+
nextStep?.id ?? "undefined",
|
|
76
|
+
);
|
|
53
77
|
set({ currentStepIndex: nextIndex });
|
|
54
78
|
|
|
55
79
|
nextStep?.onStepEnter?.();
|
|
56
80
|
if (nextStep) {
|
|
81
|
+
console.log(
|
|
82
|
+
"[Guidon] advancing to step",
|
|
83
|
+
nextIndex,
|
|
84
|
+
nextStep?.id ?? "undefined",
|
|
85
|
+
);
|
|
86
|
+
|
|
57
87
|
config.onStepChange?.(nextIndex, nextStep);
|
|
58
88
|
}
|
|
59
89
|
} else {
|
|
90
|
+
console.log("[Guidon] last step reached, completing walkthrough");
|
|
60
91
|
// Last step completed
|
|
61
92
|
get().complete();
|
|
62
93
|
}
|
|
@@ -278,13 +309,11 @@ export const useGuidonProgress = () =>
|
|
|
278
309
|
percentage: state.config
|
|
279
310
|
? ((state.currentStepIndex + 1) / state.config.steps.length) * 100
|
|
280
311
|
: 0,
|
|
281
|
-
}))
|
|
312
|
+
})),
|
|
282
313
|
);
|
|
283
314
|
|
|
284
315
|
export const useTargetMeasurements = (targetId: string) =>
|
|
285
|
-
useGuidonStore(
|
|
286
|
-
(state: GuidonStore) => state.targetMeasurements[targetId],
|
|
287
|
-
);
|
|
316
|
+
useGuidonStore((state: GuidonStore) => state.targetMeasurements[targetId]);
|
|
288
317
|
|
|
289
318
|
/**
|
|
290
319
|
* Hook to check if the guidon is waiting for a target element to mount
|
|
@@ -308,7 +337,7 @@ export const useWaitingState = () =>
|
|
|
308
337
|
targetId: hasMeasurements ? null : targetId,
|
|
309
338
|
message: currentStep?.waitingMessage ?? null,
|
|
310
339
|
};
|
|
311
|
-
})
|
|
340
|
+
}),
|
|
312
341
|
);
|
|
313
342
|
|
|
314
343
|
/**
|