diff --git a/README.md b/README.md index 3e1c939..bbd0b81 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # Introduction + English | [简体中文](./README.zh-CN.md) When we onboard new users or ship new features, how do we make sure that our audience knows what's in it and get them excited to use our apps? That's where an onboarding sequence comes into play. This React library `guide` offers an effective way to construct a smooth onboarding experience. ![mask](./public/light_mode.png) ![no mask](./public/dark_mode.png) + ## Installation ```javascript @@ -52,12 +54,12 @@ import Guide from 'byte-guide'; ### Component API's | props | definition | type | required | defalut value | -| :-------------------- | :------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------- | :------------------------------------------------------------ | +| :-------------------- | :------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------- | :------------------------------------------------------------ | --- | | steps | An array of info of each step of the onboarding sequence | IStep[] | ✓ | -- | | localKey | A unique key that will be stored in localStorage to indicate if the guide has finished | string | ✓ | -- | | expireDate | The expire date of the guide when it will not be displayed anymore | string,YYYY-mm-hh | | -- | | closable | If the guide can be closed before the last step. If false, the close button `x` will not be displayed on each modal. | bool | | true | -| closeEle | Customize the element that skips the guide | string, reactNode | | +| closeEle | Customize the element that skips the guide | string, reactNode | | | modalClassName | The class name of the modal | string | | -- | | maskClassName | The class name of the mask | string | | -- | | mask | Whether or not to display the mask | bool | | false | @@ -65,14 +67,15 @@ import Guide from 'byte-guide'; | hotspot | Whether or not to display the hotspot | bool | | false | | stepText | The custom text for the step info | (stepIndex, stepCount): string => {} | | (stepIndex, stepCount) => `Step ${stepIndex} of ${stepCount}` | | nextText | The custom text for the `Next Step` button | string | | Next | -| prevText | The custom text for the `Previous step` button | string | | Previous | -| showPreviousBtn | Whether or not to display the previous button | bool | | true | +| prevText | The custom text for the `Previous step` button | string | | Previous | +| showPreviousBtn | Whether or not to display the previous button | bool | | true | | okText | The custom text for the confirm button at the last step | string | | I know | | visible | If the guide is visible | bool | | true | | lang | The language of use | 'zh' | 'en' | 'ja' | | 'zh' | | step | The first step's number | number | | 0 | +| autoScroll | auto scroll to the guide on load | bool | | true | | afterStepChange | The callback function when the step changes | (nextIndex, nextStep): void=>{} | | -- | -| beforeStepChange | The callback function when the user is about to move to the next step | (stepIndex: number, step: IStep) => void | | -- | +| beforeStepChange | The callback function when the user is about to move to the next step | (stepIndex: number, step: IStep) => void | | -- | | | onClose | The callback function when the guide closes \*/ | | onClose?: () => void; | ():void=> {} | | -- | diff --git a/README.zh-CN.md b/README.zh-CN.md index 629883f..97aca11 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,8 +1,10 @@ # 使用指南 + [English](./README.md) | 简体中文 ## 简介 -基于react实现的新手引导组件,用于新功能引导,支持带蒙层/不带蒙层两种模式。 + +基于 react 实现的新手引导组件,用于新功能引导,支持带蒙层/不带蒙层两种模式。 ![带蒙层](./public/light_mode.png) ![不带蒙层](./public/dark_mode.png) @@ -44,7 +46,7 @@ import Guide from 'byte-guide' | localKey | 本地缓存 key,缓存是否展示过该引导页,需确保系统内 localKey 唯一性 | string | ✓ | -- | | expireDate | 过期时间,大于等于该时间都不展示引导页 | string,YYYY-mm-hh | | -- | | closable | 是否可以跳过引导 | bool | | true | -| closeEle | 自定义跳过引导的元素 | string, reactNode | | +| closeEle | 自定义跳过引导的元素 | string, reactNode | | | modalClassName | 弹窗类名 | string | | -- | | maskClassName | 蒙层类名 | string | | -- | | mask | 是否展示蒙层 | bool | | false | @@ -53,11 +55,12 @@ import Guide from 'byte-guide' | stepText | modal 的步骤信息文案 | (stepIndex, stepCount): string => {} | | (stepIndex, stepCount) => { return `第${stepIndex}步,共${stepCount}步`; } | | nextText | modal 的'下一步'按钮文案 | string | | 下一步 | | prevText | modal 的'上一步'按钮文案 | string | | 下一步 | -| showPreviousBtn | 是否显示'上一步'按钮 | bool | | true | +| showPreviousBtn | 是否显示'上一步'按钮 | bool | | true | | okText | modal 的确认按钮文案 | string | | 我知道了 | | visible | 控制 guide 显示隐藏,用于异步渲染 | bool | | true | | lang | 多语言 | 'zh' ,'en' , 'ja' | | 'zh' | | step | 初始步骤,步骤可受控,为-1 则不展示组件 | number | | 0 | +| autoScroll | 自动跳转到 Guide | bool | | true | | afterStepChange | 点击下一步的回调 | (nextIndex, nextStep): void=>{} | | -- | | beforeStepChange | 点击下一步之前的回调 | (stepIndex: number, step: IStep) => void | | -- | | onClose | 引导结束的回调 | ():void=> {} | | -- | diff --git a/src/Guide/index.tsx b/src/Guide/index.tsx index b6d4bb1..8768a34 100644 --- a/src/Guide/index.tsx +++ b/src/Guide/index.tsx @@ -11,7 +11,7 @@ import i18n from '../constant/lang'; import Mask from '../Mask'; import Modal from '../Modal'; import { IGuide } from '../typings/guide'; -import { CUSTOM_ELEMENT_CLASS } from '../constant/className' +import { CUSTOM_ELEMENT_CLASS } from '../constant/className'; const Guide: React.FC = (props) => { const { @@ -34,6 +34,7 @@ const Guide: React.FC = (props) => { nextText, okText, lang = 'zh', + autoScroll = true, showPreviousBtn = false, showSkipBtn = false, closeEle, @@ -55,12 +56,12 @@ const Guide: React.FC = (props) => { const anchorEl = useMemo(() => { if (stepIndex >= 0 && stepIndex < steps.length) { - const { targetPos, selector } = steps[stepIndex] + const { targetPos, selector } = steps[stepIndex]; - if (selector) return getAnchorEl(selector) + if (selector) return getAnchorEl(selector); if (targetPos) { - return getCusAnchorEl(targetPos) + return getCusAnchorEl(targetPos); } } return null; @@ -79,9 +80,10 @@ const Guide: React.FC = (props) => { /* To cater the cases of using iframe where the anchorEl * is not in the same window scope as the code running */ - const realWindow = useMemo(() => (anchorEl ? getWindow(anchorEl) : null), [ - anchorEl, - ]); + const realWindow = useMemo( + () => (anchorEl ? getWindow(anchorEl) : null), + [anchorEl], + ); const realDocument = useMemo( () => (anchorEl ? getDocumentElement(anchorEl as Element) : null), @@ -89,8 +91,11 @@ const Guide: React.FC = (props) => { ); const handleChange = (direction: number): void => { - const nextStepIndex = Math.min(Math.max(stepIndex + direction, 0), steps.length) - if (nextStepIndex === stepIndex) return + const nextStepIndex = Math.min( + Math.max(stepIndex + direction, 0), + steps.length, + ); + if (nextStepIndex === stepIndex) return; if (nextStepIndex === steps.length) handleClose(); else if (stepIndex >= 0) beforeStepChange?.(stepIndex, steps[stepIndex]); setStepIndex(nextStepIndex); @@ -104,9 +109,9 @@ const Guide: React.FC = (props) => { (realDocument as HTMLElement).style.overflow = initOverflowVal; } - const cusAnchor = document.querySelector(CUSTOM_ELEMENT_CLASS) + const cusAnchor = document.querySelector(CUSTOM_ELEMENT_CLASS); if (cusAnchor) { - document.body.removeChild(cusAnchor) + document.body.removeChild(cusAnchor); } setStepIndex(-1); @@ -209,6 +214,7 @@ const Guide: React.FC = (props) => { skipText={skipText} className={modalClassName} TEXT={i18nTEXT} + autoScroll={autoScroll} showPreviousBtn={showPreviousBtn} showSkipBtn={showSkipBtn} /> diff --git a/src/Modal/index.tsx b/src/Modal/index.tsx index 6301a20..74f601e 100644 --- a/src/Modal/index.tsx +++ b/src/Modal/index.tsx @@ -36,7 +36,8 @@ const Modal: React.FC = ({ showPreviousBtn, showSkipBtn, skipText, - closeEle + closeEle, + autoScroll, }) => { const stepInfo = steps[stepIndex]; @@ -57,15 +58,16 @@ const Modal: React.FC = ({ const [arrowStyle, setArrowStyle] = useState({}); const [hotspotStyle, setHotspotStyle] = useState({}); - const scrollContainer = useMemo(() => getScrollContainer(anchorEl), [ - anchorEl, - ]); + const scrollContainer = useMemo( + () => getScrollContainer(anchorEl), + [anchorEl], + ); const _okText = stepIndex !== steps.length - 1 ? nextText || TEXT('NEXT_STEP') : okText || TEXT('I_KNOW'); - const _prevText = prevText || TEXT('PREV_STEP') + const _prevText = prevText || TEXT('PREV_STEP'); const _skipText = skipText || TEXT('SKIP_STEP'); const _stepText = stepText || (TEXT('STEP_NUMBER') as (idx: number, len: number) => string); @@ -120,11 +122,11 @@ const Modal: React.FC = ({ if ( // Modal is below the viewport anchorPos.top - - scrollContainerTop + - anchorPos.height + - modalPos.height + - MARGIN >= - visibleHeight || + scrollContainerTop + + anchorPos.height + + modalPos.height + + MARGIN >= + visibleHeight || // Modal is above the viewport anchorPos.top <= modalPos.height + MARGIN ) { @@ -207,8 +209,9 @@ const Modal: React.FC = ({ onChange(1); } else if (visible) { focusedIdxRef.current = 0; - - handleScroll(); + if (autoScroll) { + handleScroll(); + } handleKeydown({ keyCode: 9 }); calculateStyle(); @@ -237,7 +240,9 @@ const Modal: React.FC = ({ )} {/* CLOSE BUTTON */} {closeEle ? ( -
{closeEle}
+
+ {closeEle} +
) : closable ? ( ) : null} @@ -255,7 +260,6 @@ const Modal: React.FC = ({ {_stepText(stepIndex + 1, steps.length)}
- {showSkipBtn && stepIndex != steps.length - 1 && (