react-guide-step 0.1.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.
@@ -0,0 +1,6 @@
1
+ import type { GuideStep } from '../types';
2
+ /**
3
+ * Preloads all images declared in steps[].preloadImages when the guide
4
+ * becomes active, and tracks whether the *current* step's images are ready.
5
+ */
6
+ export declare function usePreloadImages(steps: GuideStep[], currentStep: number, isActive: boolean): boolean;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Hook that scrolls a target element into the viewport if needed.
3
+ */
4
+ export declare function useScrollIntoView(behavior?: ScrollBehavior): (el: HTMLElement | null) => void;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hook that waits for an element to appear in the DOM using MutationObserver.
3
+ * Returns the element once it's found, or null while waiting.
4
+ */
5
+ export declare function useWaitForElement(target: string | HTMLElement | undefined, enabled: boolean): {
6
+ element: HTMLElement | null;
7
+ isWaiting: boolean;
8
+ };
@@ -0,0 +1,4 @@
1
+ export { GuideProvider } from './context/GuideProvider';
2
+ export { useGuide } from './hooks/useGuide';
3
+ export { DEFAULT_THEME } from './styles/theme';
4
+ export type { GuideStep, GuideOptions, GuideTheme, GuideController, GuideLabels, Placement, StepRenderProps, } from './types';
@@ -0,0 +1,171 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("react/jsx-runtime"),s=require("react"),U=require("react-dom"),W=s.createContext(null);function N(e){return e instanceof HTMLElement?e:document.querySelector(e)}function F(e){return e.getBoundingClientRect()}function G(...e){return e.filter(Boolean).join(" ")}const X=({targetElement:e,highlightPadding:t=4,allowInteraction:n=!1,onMaskClick:r,placement:i})=>{if(i==="center")return p.jsx("div",{className:"rgs-spotlight",style:{top:0,left:0,width:0,height:0},onClick:r});const u=e?(()=>{const a=F(e);return{top:a.top-t,left:a.left-t,width:a.width+t*2,height:a.height+t*2}})():{top:0,left:0,width:0,height:0};return p.jsxs(p.Fragment,{children:[p.jsx("div",{className:"rgs-overlay",onClick:r}),p.jsx("div",{className:G("rgs-spotlight",n&&"rgs-spotlight--interactive"),style:u})]})},B=10,Y=8,K={top:"bottom",bottom:"top",left:"right",right:"left"};function Z(e){const t=e.split("-")[0];return{top:"bottom",bottom:"top",left:"right",right:"left",center:"top"}[t]||"top"}function J(e,t){const n=Z(t),r=Y,i={position:"absolute",width:0,height:0,borderStyle:"solid",borderTopWidth:r,borderBottomWidth:r,borderLeftWidth:r,borderRightWidth:r,borderTopColor:"transparent",borderBottomColor:"transparent",borderLeftColor:"transparent",borderRightColor:"transparent"};switch(n){case"bottom":return{...i,left:e.x-r,bottom:-r,borderBottomWidth:0,borderTopColor:"var(--rgs-bg)"};case"top":return{...i,left:e.x-r,top:-r,borderTopWidth:0,borderBottomColor:"var(--rgs-bg)"};case"right":return{...i,top:e.y-r,right:-r,borderRightWidth:0,borderLeftColor:"var(--rgs-bg)"};case"left":return{...i,top:e.y-r,left:-r,borderLeftWidth:0,borderRightColor:"var(--rgs-bg)"}}}const Q=({arrowPos:e,placement:t})=>{const n=J(e,t);return p.jsx("div",{className:"rgs-arrow",style:n})},ee={next:"Next",prev:"Previous",skip:"Skip",finish:"Finish",stepOf:"{current} of {total}"};function te(e){return{...ee,...e}}const re=({step:e,stepIndex:t,totalSteps:n,position:r,tooltipRef:i,labels:o,showSkip:u=!0,onNext:a,onPrev:f,onSkip:c})=>{const[l,h]=s.useState(!1),[g,w]=s.useState(r),[x,d]=s.useState(e),[E,C]=s.useState(t),R=s.useRef(t),T=s.useRef();s.useEffect(()=>{const y=R.current!==t;if(R.current=t,T.current&&clearTimeout(T.current),!y){r&&(w(r),d(e),C(t),l||requestAnimationFrame(()=>h(!0)));return}return h(!1),T.current=setTimeout(()=>{d(e),C(t),w(r),requestAnimationFrame(()=>h(!0))},200),()=>{T.current&&clearTimeout(T.current)}},[t,r,e]);const A=E===0,S=E===n-1,_=x.showArrow!==!1&&(g==null?void 0:g.resolvedPlacement)!=="center",j=g?{left:g.x,top:g.y}:{left:-9999,top:-9999},b=o.stepOf.replace("{current}",String(E+1)).replace("{total}",String(n)),m=_&&g&&p.jsx(Q,{arrowPos:g.arrow,placement:g.resolvedPlacement}),v={ref:i,className:G("rgs-tooltip",l&&"rgs-tooltip--visible"),style:j,role:"dialog","aria-modal":!0,"aria-label":x.title||"Guide step",tabIndex:-1};if(x.customRender){const y={step:x,stepIndex:E,totalSteps:n,next:a,prev:f,skip:c,finish:a};return p.jsxs("div",{...v,children:[x.customRender(y),m]})}return p.jsxs("div",{...v,children:[x.title&&p.jsx("div",{className:"rgs-tooltip__title",children:x.title}),x.content&&p.jsx("div",{className:"rgs-tooltip__content",children:x.content}),p.jsxs("div",{className:"rgs-tooltip__footer",children:[p.jsx("span",{className:"rgs-tooltip__counter",children:b}),p.jsxs("div",{className:"rgs-tooltip__actions",children:[u&&!S&&p.jsx("button",{className:"rgs-btn rgs-btn--text",onClick:c,type:"button",children:o.skip}),!A&&p.jsx("button",{className:"rgs-btn rgs-btn--default",onClick:f,type:"button",children:o.prev}),p.jsx("button",{className:"rgs-btn rgs-btn--primary",onClick:a,type:"button",children:S?o.finish:o.next})]})]}),m]})};function I(e){return e.split("-")[0]}function V(e){return e.split("-")[1]||"center"}function O(e,t,n,r){const i=I(n),o=V(n);let u=0,a=0;switch(i){case"top":a=e.y-t.height-r;break;case"bottom":a=e.y+e.height+r;break;case"left":u=e.x-t.width-r;break;case"right":u=e.x+e.width+r;break}if(i==="top"||i==="bottom")switch(o){case"start":u=e.x;break;case"end":u=e.x+e.width-t.width;break;default:u=e.x+e.width/2-t.width/2;break}else switch(o){case"start":a=e.y;break;case"end":a=e.y+e.height-t.height;break;default:a=e.y+e.height/2-t.height/2;break}return{x:u,y:a}}function M(e,t,n){const r=window.innerWidth,i=window.innerHeight;switch(n){case"top":return e.y<0;case"bottom":return e.y+t.height>i;case"left":return e.x<0;case"right":return e.x+t.width>r;default:return!1}}function ne(e){const t=I(e),n=V(e),r=K[t]||t;return n==="center"?r:`${r}-${n}`}function $(e,t,n=8){const r=window.innerWidth,i=window.innerHeight;return{x:Math.max(n,Math.min(e.x,r-t.width-n)),y:Math.max(n,Math.min(e.y,i-t.height-n))}}function se(e,t,n){const r=I(n);if(r==="top"||r==="bottom"){const i=e.x+e.width/2-t.x;return{x:Math.max(12,Math.min(i,t.width-12)),y:r==="top"?t.height:0}}else{const i=e.y+e.height/2-t.y,o=Math.max(12,Math.min(i,t.height-12));return{x:r==="left"?t.width:0,y:o}}}function oe(e,t,n="bottom",r=B){if(n==="center"){const c={x:e.x+e.width/2-t.width/2,y:e.y+e.height/2-t.height/2},l=$(c,t);return{x:l.x,y:l.y,resolvedPlacement:"center",arrow:{x:0,y:0}}}const i=I(n);let o=O(e,t,n,r),u=n;if(M(o,t,i)){const c=ne(n),l=I(c),h=O(e,t,c,r);M(h,t,l)||(o=h,u=c)}const a=$(o,t),f=se(e,{...a,width:t.width,height:t.height},u);return{x:a.x,y:a.y,resolvedPlacement:u,arrow:f}}function ie({targetElement:e,placement:t,enabled:n,highlightPadding:r=0}){const[i,o]=s.useState(null),u=s.useRef(null),a=s.useRef(0),f=s.useCallback(()=>{if(!e||!u.current)return;const c=F(e),l=F(u.current),h=oe(c,l,t,B+r);o(h)},[e,t,r]);return s.useEffect(()=>{if(!n){o(null);return}if(!e)return;f(),a.current=requestAnimationFrame(f);const c=()=>{cancelAnimationFrame(a.current),a.current=requestAnimationFrame(f)};window.addEventListener("scroll",c,!0),window.addEventListener("resize",c);let l;return l=new ResizeObserver(c),u.current&&l.observe(u.current),l.observe(e),()=>{cancelAnimationFrame(a.current),window.removeEventListener("scroll",c,!0),window.removeEventListener("resize",c),l==null||l.disconnect()}},[n,e,f]),{position:i,tooltipRef:u,updatePosition:f}}function ce({enabled:e,onNext:t,onPrev:n,onSkip:r}){s.useEffect(()=>{if(!e)return;const i=o=>{switch(o.key){case"ArrowRight":case"Enter":o.preventDefault(),t();break;case"ArrowLeft":o.preventDefault(),n();break;case"Escape":o.preventDefault(),r();break}};return document.addEventListener("keydown",i),()=>document.removeEventListener("keydown",i)},[e,t,n,r])}function ae(e,t){const[n,r]=s.useState(null),[i,o]=s.useState(!1);return s.useEffect(()=>{if(!t||!e){r(null),o(!1);return}const u=N(e);if(u){r(u),o(!1);return}if(o(!0),typeof e!="string"){o(!1);return}const a=new MutationObserver(()=>{const f=document.querySelector(e);f&&(r(f),o(!1),a.disconnect())});return a.observe(document.body,{childList:!0,subtree:!0}),()=>a.disconnect()},[e,t]),{element:n,isWaiting:i}}function le(e="smooth"){return s.useCallback(n=>{if(!n)return;const r=n.getBoundingClientRect();r.top>=0&&r.left>=0&&r.bottom<=window.innerHeight&&r.right<=window.innerWidth||n.scrollIntoView({behavior:e,block:"center",inline:"center"})},[e])}function ue(e,t,n){const r=s.useRef(new Map),[i,o]=s.useState(!0),u=s.useRef(t),a=s.useRef(e);u.current=t,a.current=e;const f=s.useCallback(()=>{var h;const c=(h=a.current[u.current])==null?void 0:h.preloadImages;if(!c||c.length===0){o(!0);return}const l=c.every(g=>r.current.get(g)===!0);o(l)},[]);return s.useEffect(()=>{var l;if(!n){r.current.clear();return}const c=new Set;for(const h of e)(l=h.preloadImages)==null||l.forEach(g=>c.add(g));for(const h of c){if(r.current.has(h))continue;r.current.set(h,!1);const g=new Image;g.src=h;const w=()=>{r.current.set(h,!0),f()};g.onload=w,g.onerror=w}f()},[n,e,f]),s.useEffect(()=>{f()},[t,f]),i}const q={primaryColor:"#1677ff",textColor:"#333333",bgColor:"#ffffff",overlayColor:"rgba(0, 0, 0, 0.5)",borderRadius:8,zIndex:99999,animationDuration:150};function D(e){return{...q,...e}}function de(e){return`
2
+ --rgs-primary: ${e.primaryColor};
3
+ --rgs-text: ${e.textColor};
4
+ --rgs-bg: ${e.bgColor};
5
+ --rgs-overlay: ${e.overlayColor};
6
+ --rgs-radius: ${e.borderRadius}px;
7
+ --rgs-z: ${e.zIndex};
8
+ --rgs-duration: ${e.animationDuration}ms;
9
+ `}const fe=()=>{const e=s.useContext(W),{controller:t,options:n}=e,{steps:r,maskClosable:i=!1,showSkip:o=!0,keyboardNavigation:u=!0,scrollBehavior:a="smooth",labels:f}=n,{isActive:c,currentStep:l,next:h,prev:g,stop:w}=t,x=te(f),d=r[l],E=!!(d!=null&&d.waitForElement),{element:C,isWaiting:R}=ae(d==null?void 0:d.target,c&&E),[T,A]=s.useState(null);s.useEffect(()=>{if(!c||!d||E){A(null);return}const k=N(d.target);A(k||null)},[c,l,d,E]);const S=E?C:T,_=ue(r,l,c),j=le(a);s.useEffect(()=>{S&&j(S)},[S,j]);const{position:b,tooltipRef:m}=ie({targetElement:S,placement:(d==null?void 0:d.placement)||"bottom",enabled:c&&!!S,highlightPadding:d==null?void 0:d.highlightPadding});s.useEffect(()=>{c&&b&&m.current&&m.current.focus()},[c,l,b,m]);const v=s.useCallback(()=>{var k;(k=n.onSkip)==null||k.call(n,l),w()},[n,l,w]);ce({enabled:c&&u,onNext:h,onPrev:g,onSkip:v});const y=s.useCallback(()=>{i&&v()},[i,v]),L=s.useRef(null),[z,P]=s.useState(!1);return s.useLayoutEffect(()=>{if(c){const k=document.createElement("div");return k.className="rgs-portal",k.setAttribute("aria-live","polite"),k.style.zIndex=String(D(n.theme).zIndex),document.body.appendChild(k),L.current=k,P(!0),()=>{k.remove(),L.current=null,P(!1)}}else P(!1)},[c,n.theme]),!c||!d||!z||!L.current?null:U.createPortal(p.jsxs(p.Fragment,{children:[p.jsx(X,{targetElement:S,highlightPadding:d.highlightPadding,allowInteraction:d.allowInteraction,onMaskClick:y,placement:d.placement}),R||!_?p.jsx("div",{className:"rgs-waiting",children:"Loading..."}):p.jsx(re,{step:d,stepIndex:l,totalSteps:r.length,position:b,tooltipRef:m,labels:x,showSkip:o,onNext:h,onPrev:g,onSkip:v})]}),L.current)},pe=()=>s.useContext(W)?p.jsx(fe,{}):null,H="rgs";function he(e,t){const n=`${H}-${e}`;let r=document.getElementById(n);r||(r=document.createElement("style"),r.id=n,r.setAttribute("data-rgs",""),document.head.appendChild(r)),r.textContent=t}function ge(e){const t=`${H}-${e}`,n=document.getElementById(t);n&&n.remove()}const me=`
10
+ .rgs-portal {
11
+ position: fixed;
12
+ inset: 0;
13
+ z-index: var(--rgs-z);
14
+ pointer-events: none;
15
+ }
16
+
17
+ .rgs-portal > * {
18
+ pointer-events: auto;
19
+ }
20
+
21
+ /* Spotlight overlay — uses box-shadow for the mask */
22
+ .rgs-spotlight {
23
+ position: fixed;
24
+ box-shadow: 0 0 0 9999px var(--rgs-overlay);
25
+ border-radius: 4px;
26
+ z-index: var(--rgs-z);
27
+ pointer-events: none;
28
+ }
29
+
30
+ .rgs-spotlight--interactive {
31
+ pointer-events: auto;
32
+ }
33
+
34
+ /* Clickable overlay behind everything to detect mask clicks */
35
+ .rgs-overlay {
36
+ position: fixed;
37
+ inset: 0;
38
+ z-index: calc(var(--rgs-z) - 1);
39
+ cursor: default;
40
+ }
41
+
42
+ /* Tooltip container */
43
+ .rgs-tooltip {
44
+ position: fixed;
45
+ z-index: calc(var(--rgs-z) + 1);
46
+ background: var(--rgs-bg);
47
+ color: var(--rgs-text);
48
+ border-radius: var(--rgs-radius);
49
+ box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
50
+ 0 3px 6px -4px rgba(0, 0, 0, 0.12),
51
+ 0 9px 28px 8px rgba(0, 0, 0, 0.05);
52
+ padding: 16px;
53
+ min-width: 280px;
54
+ max-width: 420px;
55
+ opacity: 0;
56
+ transition: opacity var(--rgs-duration) ease;
57
+ outline: none;
58
+ }
59
+
60
+ .rgs-tooltip--visible {
61
+ opacity: 1;
62
+ }
63
+
64
+ .rgs-tooltip__title {
65
+ font-size: 16px;
66
+ font-weight: 600;
67
+ margin: 0 0 8px 0;
68
+ line-height: 1.4;
69
+ }
70
+
71
+ .rgs-tooltip__content {
72
+ font-size: 14px;
73
+ line-height: 1.6;
74
+ margin: 0 0 16px 0;
75
+ color: var(--rgs-text);
76
+ opacity: 0.85;
77
+ }
78
+
79
+ .rgs-tooltip__footer {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: space-between;
83
+ gap: 8px;
84
+ }
85
+
86
+ .rgs-tooltip__counter {
87
+ font-size: 12px;
88
+ color: var(--rgs-text);
89
+ opacity: 0.45;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ .rgs-tooltip__actions {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 8px;
97
+ margin-left: auto;
98
+ }
99
+
100
+ /* Buttons */
101
+ .rgs-btn {
102
+ display: inline-flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ padding: 4px 16px;
106
+ font-size: 14px;
107
+ line-height: 1.5;
108
+ border-radius: calc(var(--rgs-radius) - 2px);
109
+ cursor: pointer;
110
+ border: 1px solid transparent;
111
+ transition: all 0.2s ease;
112
+ white-space: nowrap;
113
+ font-family: inherit;
114
+ }
115
+
116
+ .rgs-btn:focus-visible {
117
+ outline: 2px solid var(--rgs-primary);
118
+ outline-offset: 2px;
119
+ }
120
+
121
+ .rgs-btn--primary {
122
+ background: var(--rgs-primary);
123
+ color: #fff;
124
+ border-color: var(--rgs-primary);
125
+ }
126
+
127
+ .rgs-btn--primary:hover {
128
+ opacity: 0.85;
129
+ }
130
+
131
+ .rgs-btn--default {
132
+ background: transparent;
133
+ color: var(--rgs-text);
134
+ border-color: #d9d9d9;
135
+ }
136
+
137
+ .rgs-btn--default:hover {
138
+ color: var(--rgs-primary);
139
+ border-color: var(--rgs-primary);
140
+ }
141
+
142
+ .rgs-btn--text {
143
+ background: transparent;
144
+ color: var(--rgs-text);
145
+ opacity: 0.65;
146
+ padding: 4px 8px;
147
+ }
148
+
149
+ .rgs-btn--text:hover {
150
+ opacity: 1;
151
+ }
152
+
153
+ /* Arrow (styles applied inline, this is a fallback) */
154
+ .rgs-arrow {
155
+ position: absolute;
156
+ }
157
+
158
+ /* Waiting state */
159
+ .rgs-waiting {
160
+ position: fixed;
161
+ top: 50%;
162
+ left: 50%;
163
+ transform: translate(-50%, -50%);
164
+ z-index: calc(var(--rgs-z) + 1);
165
+ color: var(--rgs-bg);
166
+ font-size: 14px;
167
+ }
168
+ `,be=({guide:e,options:t,children:n})=>{s.useEffect(()=>{const i=D(t.theme),u=`
169
+ .rgs-portal { ${de(i)} }
170
+ ${me}
171
+ `;return he("base",u),()=>ge("base")},[t.theme]);const r=s.useMemo(()=>({controller:e,options:t}),[e,t]);return p.jsxs(W.Provider,{value:r,children:[n,p.jsx(pe,{})]})},xe="rgs:";function ve(e){if(!e)return!1;try{return localStorage.getItem(e)==="done"}catch{return!1}}function ye(e){const t=e?`${xe}${e}`:null,[n,r]=s.useState(()=>ve(t)),i=s.useCallback(()=>{if(t){try{localStorage.setItem(t,"done")}catch{}r(!0)}},[t]),o=s.useCallback(()=>{if(t){try{localStorage.removeItem(t)}catch{}r(!1)}},[t]);return{isCompleted:n,markCompleted:i,reset:o}}function we(e,t){switch(t.type){case"START":return{...e,isActive:!0,currentStep:t.step,targetElement:null,isWaiting:!1};case"STOP":return{isActive:!1,currentStep:0,targetElement:null,isWaiting:!1};case"GO_TO":return{...e,currentStep:t.step,targetElement:null,isWaiting:!1};default:return e}}const Ee={isActive:!1,currentStep:0,targetElement:null,isWaiting:!1};function Se(e){const{steps:t,onComplete:n,onSkip:r,onStepChange:i,initialStep:o=0,persistKey:u,autoStart:a=!1}=e,[f,c]=s.useReducer(we,Ee),{isCompleted:l,markCompleted:h}=ye(u),g=s.useRef(!1),w=s.useRef(f);w.current=f;const x=s.useRef(t);x.current=t;const d=s.useRef(i);d.current=i;const E=s.useRef(n);E.current=n,s.useEffect(()=>{a&&!l&&t.length>0&&c({type:"START",step:o})},[a,l,t.length,o]);const C=s.useCallback(async b=>{var m;if(!g.current){g.current=!0;try{const v=x.current[w.current.currentStep],y=x.current[b];v!=null&&v.afterLeave&&await v.afterLeave(),y!=null&&y.beforeEnter&&await y.beforeEnter(),c({type:"GO_TO",step:b}),(m=d.current)==null||m.call(d,b)}catch(v){console.error("[react-guide-step] Error during step transition:",v)}finally{g.current=!1}}},[]),R=s.useCallback(()=>{if(l)return;const b=o,m=x.current[b];(async()=>{var y;try{m!=null&&m.beforeEnter&&await m.beforeEnter(),c({type:"START",step:b}),(y=d.current)==null||y.call(d,b)}catch(L){console.error("[react-guide-step] Error in beforeEnter:",L)}})()},[l,o]),T=s.useCallback(()=>{c({type:"STOP"})},[]),A=s.useCallback(()=>{const{isActive:b,currentStep:m}=w.current;if(!b)return;const v=m+1;if(v>=x.current.length){const y=x.current[m];(async()=>{var z;try{y!=null&&y.afterLeave&&await y.afterLeave(),c({type:"STOP"}),h(),(z=E.current)==null||z.call(E)}catch(P){console.error("[react-guide-step] Error in afterLeave:",P),c({type:"STOP"})}})()}else C(v)},[C,h]),S=s.useCallback(()=>{const{isActive:b,currentStep:m}=w.current;!b||m<=0||C(m-1)},[C]),_=s.useCallback(b=>{const{isActive:m}=w.current;!m||b<0||b>=x.current.length||C(b)},[C]);return s.useMemo(()=>({start:R,stop:T,next:A,prev:S,goTo:_,isActive:f.isActive,currentStep:f.currentStep,totalSteps:t.length,isCompleted:l}),[R,T,A,S,_,f.isActive,f.currentStep,t.length,l])}exports.DEFAULT_THEME=q;exports.GuideProvider=be;exports.useGuide=Se;