├── .flowconfig ├── .gitignore ├── README.md ├── demo ├── android.gif ├── demo.js └── ios.gif ├── index.d.ts ├── index.js ├── package.json └── src ├── IntroComponent.js ├── IntroManage.js ├── IntroModal.js ├── constants.js └── index.js /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*.web.js 5 | .*/*.android.js 6 | 7 | # Some modules have their own node_modules with overlap 8 | .*/node_modules/node-haste/.* 9 | 10 | # Ugh 11 | .*/node_modules/babel.* 12 | .*/node_modules/babylon.* 13 | .*/node_modules/invariant.* 14 | 15 | # Ignore react and fbjs where there are overlaps, but don't ignore 16 | # anything that react-native relies on 17 | .*/node_modules/fbjs/lib/Map.js 18 | .*/node_modules/fbjs/lib/Promise.js 19 | .*/node_modules/fbjs/lib/fetch.js 20 | .*/node_modules/fbjs/lib/ExecutionEnvironment.js 21 | .*/node_modules/fbjs/lib/isEmpty.js 22 | .*/node_modules/fbjs/lib/crc32.js 23 | .*/node_modules/fbjs/lib/ErrorUtils.js 24 | 25 | # Flow has a built-in definition for the 'react' module which we prefer to use 26 | # over the currently-untyped source 27 | .*/node_modules/react/react.js 28 | .*/node_modules/react/lib/React.js 29 | .*/node_modules/react/lib/ReactDOM.js 30 | 31 | # Ignore commoner tests 32 | .*/node_modules/commoner/test/.* 33 | 34 | # See https://github.com/facebook/flow/issues/442 35 | .*/react-tools/node_modules/commoner/lib/reader.js 36 | 37 | # Ignore jest 38 | .*/node_modules/jest-cli/.* 39 | 40 | # Ignore Website 41 | .*/website/.* 42 | 43 | [include] 44 | 45 | [libs] 46 | node_modules/react-native/Libraries/react-native/react-native-interface.js 47 | 48 | [options] 49 | module.system=haste 50 | 51 | munge_underscores=true 52 | 53 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 54 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub' 55 | 56 | suppress_type=$FlowIssue 57 | suppress_type=$FlowFixMe 58 | suppress_type=$FixMe 59 | 60 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 61 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 62 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 63 | 64 | [version] 65 | 0.21.0 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-intro 2 | A way for new feature introduction and step-by-step users guide for your react-native app 3 | 4 | # Install 5 | Run ```npm install react-native-intro --save``` in your Project dir 6 | 7 | # Usage 8 | 9 | 1. react-native-intro exports two APIs, default is ```Intro``` component, and the other one is ```intro``` function. see [demo](./demo/) get more informations. 10 | 11 | 2. Use ```Intro``` component wrap your components and pass some props to Intro. Maybe you need set the style props too; 12 | 13 | ``` 14 | import Intro, {IntroManage} from 'react-native-intro'; 15 | 16 | 19 | 20 | 21 | ..... 22 | 23 | componentDidMount() { 24 | 25 | // and start 26 | var myIntro = new IntroManage(); 27 | myIntro.start(); 28 | 29 | } 30 | 31 | ``` 32 | 33 | #Props 34 | ###content: string | ReactElement 35 | 36 | ###step: the step sort 37 | 38 | # Warning 39 | *This Component does not support your component Wrapped by `Redux connect` currently;* 40 | 41 | # screen shorts 42 | 43 | ![ios screen shoot](./demo/ios.gif) 44 | ![android screen shoot](./demo/android.gif) 45 | -------------------------------------------------------------------------------- /demo/android.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzheng644607/react-native-intro/a1d5a4e2865ee3390e19495682324433d010b7c7/demo/android.gif -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: liuyany.liu 3 | * @Date: 2017-02-27 19:21:32 4 | * @Last modified by: lyan 5 | * @Last modified time: 2017-03-02 20:54:00 6 | */ 7 | 8 | import { 9 | StyleSheet, 10 | View, 11 | TextInput, 12 | Image, 13 | Text, 14 | TouchableOpacity 15 | } from 'react-native'; 16 | 17 | import React, { 18 | Component 19 | } from 'react'; 20 | 21 | import Intro, { intro } from 'react-native-intro'; 22 | 23 | export default class Example extends Component { 24 | state = { 25 | value: 'hahah' 26 | } 27 | render() { 28 | return ( 29 | 30 | 35 | this.setState({value: v})} 38 | value={this.state.value}/> 39 | 43 | 45 | 46 | } 47 | step={2}> 48 | 呵呵呵呵 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 65 | 点我 66 | 67 | 68 | 69 | ); 70 | } 71 | 72 | componentDidMount() { 73 | this.intro = intro({group: 'test1'}); 74 | } 75 | 76 | _showModal() { 77 | this.intro.start(); 78 | } 79 | } 80 | 81 | const styles = StyleSheet.create({ 82 | container: { 83 | position: 'absolute', 84 | left: 0, 85 | top: 0, 86 | right: 0, 87 | bottom:0, 88 | }, 89 | content: { 90 | width: 200, 91 | height: 300, 92 | position: 'absolute', 93 | top: 100, 94 | left: 100, 95 | borderWidth: 1, 96 | borderColor: 'red' 97 | }, 98 | button: { 99 | width: 100, 100 | height: 44, 101 | position: 'absolute', 102 | bottom: 100 103 | }, 104 | }); 105 | -------------------------------------------------------------------------------- /demo/ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuzheng644607/react-native-intro/a1d5a4e2865ee3390e19495682324433d010b7c7/demo/ios.gif -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleProp, ViewStyle } from 'react-native'; 3 | import { ComponentClass, Component } from 'react'; 4 | 5 | interface IIntroComponentProps { 6 | /** 7 | * 分组 8 | */ 9 | group?: string; 10 | 11 | /** 12 | * 第几步 13 | */ 14 | step: number; 15 | 16 | /** 17 | * 展示内容 18 | */ 19 | content?: JSX.Element; 20 | 21 | /** 22 | * 样式 23 | */ 24 | style?: StyleProp; 25 | } 26 | 27 | export declare class Intro extends Component {} 28 | 29 | export interface IntroManageOptions { 30 | /** 31 | * 当前分组 32 | */ 33 | group?: string; 34 | 35 | /** 36 | * 循环 37 | */ 38 | loop?: boolean; 39 | 40 | /** 41 | * 初始step 索引 42 | */ 43 | startIndex?: number; 44 | 45 | /** 46 | * 是否显示步骤数, 默认true 47 | */ 48 | showStepNumber?: boolean; 49 | 50 | /** 51 | * 蒙层的样式,可以定义透明度,背景颜色等 52 | */ 53 | maskStyle?: StyleProp; 54 | 55 | /** 56 | * 高亮的内容区域是否可交互,用于防止用户点击在高亮内容上面 57 | */ 58 | touchable?: boolean; 59 | 60 | /** 61 | * 点击蒙层是否关闭 62 | */ 63 | maskClosable?: boolean; 64 | 65 | /** 66 | * 自定义渲染提示内容 67 | */ 68 | contentRender?: (content: JSX.Element, step: number) => JSX.Element; 69 | } 70 | 71 | export declare class IntroManage { 72 | constructor(opts: IntroManageOptions); 73 | /** 74 | * 开始 75 | */ 76 | start(): void; 77 | 78 | /** 79 | * 结束 80 | */ 81 | stop(): void; 82 | 83 | /** 84 | * next step 85 | */ 86 | next(): void; 87 | 88 | /** 89 | * previous step 90 | */ 91 | prev(): void; 92 | 93 | /** 94 | * go to step direct (index) 95 | * @param index 96 | */ 97 | private toStep(index: number): void; 98 | } 99 | 100 | export default Intro; 101 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: liuyany.liu 3 | * @Date: 2016-12-20 14:27:41 4 | * @Last modified by: lyan 5 | * @Last modified time: 2017-02-27 21:54:23 6 | */ 7 | 8 | import {IntroManage} from './src/IntroManage'; 9 | import {Intro} from './src/IntroComponent'; 10 | 11 | export default Intro; 12 | export { IntroManage } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-intro", 3 | "version": "0.0.2", 4 | "description": "A way for new feature introduction and step-by-step users guide for your react-native app", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/liuzheng644607/react-native-intro.git" 12 | }, 13 | "keywords": [ 14 | "react-native-intro", 15 | "intro", 16 | "react-native" 17 | ], 18 | "author": "lyan", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/liuzheng644607/react-native-intro/issues" 22 | }, 23 | "dependencies": { 24 | "react-native-root-siblings": "^1.1.2" 25 | }, 26 | "homepage": "https://github.com/liuzheng644607/react-native-intro#readme" 27 | } 28 | -------------------------------------------------------------------------------- /src/IntroComponent.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet, 3 | TouchableOpacity, 4 | View, 5 | Text, 6 | Dimensions, 7 | Animated 8 | } from "react-native"; 9 | 10 | import React, { Component } from "react"; 11 | import {groupMap, DEFAULT_GROUP} from './constants'; 12 | 13 | export class Intro extends Component { 14 | setNativeProps(obj) { 15 | this._refIntro.setNativeProps(obj); 16 | } 17 | 18 | componentDidMount() { 19 | const { group, step, content, disable } = this.props; 20 | 21 | if (!groupMap[group || DEFAULT_GROUP]) { 22 | groupMap[group || DEFAULT_GROUP] = {}; 23 | } 24 | 25 | let groupA = groupMap[group || DEFAULT_GROUP]; 26 | 27 | if (!groupA[step]) { 28 | groupA[step] = { 29 | content, 30 | disable, 31 | target: this, 32 | refTarget: this._refIntro, 33 | _index: 0 34 | }; 35 | } else { 36 | ++groupA[step]._index; 37 | } 38 | } 39 | 40 | measure() { 41 | this._refIntro.measure.apply(this._refIntro, arguments); 42 | } 43 | 44 | componentWillUnmount() { 45 | const { group, step, content, disable } = this.props; 46 | var groupA = groupMap[group || DEFAULT_GROUP][step]; 47 | 48 | if (groupA._index > 0) { 49 | --groupA._index; 50 | } else { 51 | delete groupMap[group || DEFAULT_GROUP][step]; 52 | if (Object.keys(groupMap[group || DEFAULT_GROUP]).length === 0) { 53 | delete groupMap[group && DEFAULT_GROUP]; 54 | } 55 | } 56 | } 57 | 58 | render() { 59 | this.html = ( 60 | (this._refIntro = c)} 63 | > 64 | {this.props.children} 65 | 66 | ); 67 | 68 | return this.html; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/IntroManage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: liuyany.liu 3 | * @Date: 2017-02-07 15:45:15 4 | * @Last modified by: lyan 5 | * @Last modified time: 2017-03-02 20:54:24 6 | */ 7 | 8 | import { 9 | StyleSheet, 10 | TouchableOpacity, 11 | View, 12 | Text, 13 | Dimensions, 14 | Animated 15 | } from "react-native"; 16 | import React, { cloneElement, Component } from "react"; 17 | import RootSiblings from "react-native-root-siblings"; 18 | import {IntroModal} from './IntroModal'; 19 | import {groupMap, DEFAULT_GROUP, DEFAULTOPTIONS} from './constants'; 20 | 21 | const offsetW = 4; 22 | 23 | export class IntroManage { 24 | constructor(opts = {}) { 25 | opts = this.opts = Object.assign({}, DEFAULTOPTIONS, opts); 26 | this.groupName = opts.group; 27 | this.introData = groupMap[this.groupName]; 28 | if (!this.introData) { 29 | return; 30 | } 31 | 32 | this.loop = opts.loop; 33 | 34 | this.stepArr = Object.keys(this.introData).sort(); 35 | this.stepLength = this.stepArr.length; 36 | this.index = opts.startIndex || 0; 37 | this.timer = null; 38 | this.stepTimer = null; 39 | 40 | this.refModal = null; 41 | this.sibling = null; 42 | } 43 | 44 | start = () => { 45 | if (!this.sibling) { 46 | this.sibling = new RootSiblings( 47 | ( 48 | (this.refModal = c)} 50 | next={this.next} 51 | prev={this.prev} 52 | stop={this.stop} 53 | contentRender={this.opts.contentRender} 54 | showStepNumber={this.opts.showStepNumber} 55 | maskStyle={this.opts.maskStyle} 56 | touchable={this.opts.touchable} 57 | maskClosable={this.opts.maskClosable} 58 | /> 59 | ) 60 | ); 61 | } 62 | this.cleartTimers(); 63 | stepTimer = setTimeout(() => { 64 | this.toStep(this.index); 65 | }); 66 | } 67 | 68 | stop = () => { 69 | this.cleartTimers(); 70 | if (this.sibling) { 71 | this.sibling.destroy(); 72 | this.sibling = null; 73 | } 74 | this.index = 0; 75 | } 76 | 77 | prev = () => { 78 | if (this.index <= 0) { 79 | if (this.loop) { 80 | this.index = this.stepLength; 81 | } else { 82 | return; 83 | } 84 | } 85 | 86 | this.index--; 87 | this.toStep(this.index); 88 | } 89 | 90 | next = () => { 91 | if (this.index >= this.stepLength - 1) { 92 | if (this.loop) { 93 | this.index = -1; 94 | } else { 95 | return; 96 | } 97 | } 98 | 99 | this.index++; 100 | this.toStep(this.index); 101 | } 102 | 103 | toStep = (index) => { 104 | this.index = index; 105 | const currentStep = this.introData[this.stepArr[index]]; 106 | const content = currentStep.content; 107 | 108 | const target = currentStep.target; 109 | const refTarget = currentStep.refTarget; 110 | 111 | let element = target.html; 112 | element = cloneElement(element, { 113 | style: [element.props.style] 114 | }); 115 | 116 | const refModal = this.refModal; 117 | 118 | this.refModal.innerElement = null; 119 | this.refModal.forceUpdate(); 120 | 121 | new Promise((resolve, reject) => { 122 | refTarget.measure((x, y, width, height, pageX, pageY) => { 123 | resolve({ x, y, width, height, pageX, pageY }); 124 | }, () => reject); 125 | }).then(res => { 126 | let obj = { 127 | width: res.width, 128 | height: res.height, 129 | left: res.pageX, 130 | top: res.pageY 131 | }; 132 | // hide the tooltip 133 | refModal.toggleTooltip(false); 134 | 135 | timer = setTimeout(() => { 136 | refModal.innerElement = element; 137 | refModal.currentStep = index + 1; 138 | refModal.content = 139 | typeof content === "string" ? ( 140 | 141 | {content} 142 | 143 | ) : ( 144 | content 145 | ); 146 | refModal.forceUpdate(() => { 147 | refModal.refContainer.setNativeProps({ 148 | style: obj 149 | }); 150 | }); 151 | refModal.toggleTooltip(true); 152 | }, 300); 153 | 154 | refModal.animateMove({ 155 | width: res.width + offsetW, 156 | height: res.height + offsetW, 157 | left: res.pageX - offsetW / 2, 158 | top: res.pageY - offsetW / 2 159 | }); 160 | }); 161 | } 162 | 163 | cleartTimers = () => { 164 | clearTimeout(this.stepTimer); 165 | clearTimeout(this.timer); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/IntroModal.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet, 3 | TouchableOpacity, 4 | View, 5 | Text, 6 | Dimensions, 7 | Animated, 8 | } from 'react-native'; 9 | 10 | import React, { Component } from 'react'; 11 | 12 | const { width, height } = Dimensions.get('window'); 13 | const [CLIENT_WIDTH, CLIENT_HEIGHT] = [width, height]; 14 | let zIndex = 999; 15 | 16 | export class IntroModal extends Component { 17 | static defaultProps = { 18 | showStepNumber: true, 19 | touchable: false, 20 | maskClosable: true, 21 | }; 22 | constructor(props) { 23 | super(props); 24 | 25 | this.innerElement = null; 26 | this.currentStep = 1; 27 | 28 | this._aniWidth = new Animated.Value(0); 29 | this._aniHeight = new Animated.Value(0); 30 | this._aniLeft = new Animated.Value(0); 31 | this._aniTop = new Animated.Value(0); 32 | 33 | this._aniOpacity = new Animated.Value(0); 34 | 35 | this._aniStepNumLeft = new Animated.Value(0); 36 | } 37 | 38 | animateMove(obj = {}) { 39 | var duration = 300; 40 | 41 | var stepNumLeft = obj.left - 12; 42 | 43 | if (stepNumLeft < 0) { 44 | stepNumLeft = obj.left + obj.width - 12; 45 | if (stepNumLeft > CLIENT_WIDTH - 24) { 46 | stepNumLeft = CLIENT_WIDTH - 24; 47 | } 48 | } 49 | 50 | Animated.parallel([ 51 | Animated.timing(this._aniWidth, { 52 | duration, 53 | toValue: obj.width, 54 | }), 55 | 56 | Animated.timing(this._aniHeight, { 57 | duration, 58 | toValue: obj.height, 59 | }), 60 | 61 | Animated.timing(this._aniLeft, { 62 | duration, 63 | toValue: obj.left, 64 | }), 65 | 66 | Animated.timing(this._aniTop, { 67 | duration, 68 | toValue: obj.top, 69 | }), 70 | 71 | Animated.timing(this._aniStepNumLeft, { 72 | duration, 73 | toValue: stepNumLeft, 74 | }), 75 | ]).start(); 76 | 77 | var centerPoint = { 78 | x: obj.left + obj.width / 2, 79 | y: obj.top + obj.height / 2, 80 | }; 81 | 82 | var relative2Left = centerPoint.x; 83 | var relative2Top = centerPoint.y; 84 | var relative2Bottom = Math.abs(centerPoint.y - CLIENT_HEIGHT); 85 | var relative2Right = Math.abs(centerPoint.x - CLIENT_WIDTH); 86 | 87 | var whereVerticalPlace = relative2Bottom > relative2Top ? 'bottom' : 'top'; 88 | var whereHorizontalPlace = 89 | relative2Left > relative2Right ? 'left' : 'right'; 90 | 91 | var margin = 13; 92 | var tooltip = {}; 93 | var arrow = {}; 94 | 95 | switch (whereVerticalPlace) { 96 | case 'bottom': 97 | tooltip.top = obj.top + obj.height + margin; 98 | arrow.borderBottomColor = '#fff'; 99 | arrow.top = tooltip.top - margin + 3; 100 | break; 101 | 102 | case 'top': 103 | tooltip.bottom = CLIENT_HEIGHT - obj.top + margin; 104 | arrow.borderTopColor = '#fff'; 105 | arrow.bottom = tooltip.bottom - margin + 3; 106 | break; 107 | default: 108 | // nothing todo 109 | } 110 | 111 | switch (whereHorizontalPlace) { 112 | case 'left': 113 | tooltip.right = Math.max(CLIENT_WIDTH - (obj.left + obj.width), 0); 114 | tooltip.right = 115 | tooltip.right === 0 ? tooltip.right + margin : tooltip.right; 116 | tooltip.maxWidth = CLIENT_WIDTH - tooltip.right - margin; 117 | arrow.right = tooltip.right + margin; 118 | break; 119 | 120 | case 'right': 121 | tooltip.left = Math.max(obj.left, 0); 122 | tooltip.left = 123 | tooltip.left === 0 ? tooltip.left + margin : tooltip.left; 124 | tooltip.maxWidth = CLIENT_WIDTH - tooltip.left - margin; 125 | arrow.left = tooltip.left + margin; 126 | break; 127 | default: 128 | // nothing todo 129 | } 130 | 131 | this.tooltip = tooltip; 132 | this.arrow = arrow; 133 | } 134 | 135 | toggleTooltip(isShow = true) { 136 | Animated.timing(this._aniOpacity, { 137 | toValue: isShow ? 1 : 0, 138 | duration: 200, 139 | }).start(); 140 | } 141 | 142 | render() { 143 | const { contentRender, showStepNumber, touchable, maskStyle, maskClosable } = this.props; 144 | return ( 145 | 146 | 151 | (this.refHilightBox = c)} 153 | style={[ 154 | styles.hilightBox, 155 | { position: 'absolute' }, 156 | { 157 | width: this._aniWidth, 158 | height: this._aniHeight, 159 | left: this._aniLeft, 160 | top: this._aniTop, 161 | }, 162 | ]} 163 | /> 164 | (this.refContainer = c)} 166 | style={[styles.modalContent]} 167 | > 168 | {this.innerElement} 169 | {/* mask the element */} 170 | {touchable ? null : } 171 | 172 | { 173 | showStepNumber ? ( 174 | 184 | {this.currentStep} 185 | 186 | ): null 187 | } 188 | 191 | {contentRender ? ( 192 | 199 | {contentRender(this.content, this.currentStep)} 200 | 201 | ) : ( 202 | 209 | {this.content || null} 210 | 211 | this.props.stop()} 220 | > 221 | 229 | OK 230 | 231 | 232 | this.props.prev()} 235 | > 236 | prev 237 | 238 | this.props.next()} 241 | > 242 | next 243 | 244 | 245 | 246 | )} 247 | 248 | ); 249 | } 250 | } 251 | 252 | const styles = StyleSheet.create({ 253 | container: { 254 | position: 'absolute', 255 | left: 0, 256 | top: 0, 257 | right: 0, 258 | bottom: 0, 259 | }, 260 | 261 | hilightBox: { 262 | position: 'absolute', 263 | backgroundColor: 'rgba(255,255,255,1)', 264 | borderRadius: 2, 265 | }, 266 | 267 | arrow: { 268 | position: 'absolute', 269 | borderColor: 'transparent', 270 | borderWidth: 5, 271 | }, 272 | 273 | up: { 274 | borderBottomColor: '#fff', 275 | }, 276 | 277 | down: { 278 | borderTopColor: '#fff', 279 | }, 280 | 281 | toolTip: { 282 | position: 'absolute', 283 | padding: 5, 284 | backgroundColor: '#fff', 285 | borderRadius: 3, 286 | overflow: 'hidden', 287 | }, 288 | 289 | stepNum: { 290 | position: 'absolute', 291 | width: 24, 292 | height: 24, 293 | borderWidth: 2, 294 | borderRadius: 12, 295 | borderColor: '#fff', 296 | backgroundColor: '#28a3ef', 297 | overflow: 'hidden', 298 | alignItems: 'center', 299 | justifyContent: 'center', 300 | }, 301 | 302 | stepNumText: { 303 | backgroundColor: 'rgba(255,255,255,0)', 304 | fontWeight: 'bold', 305 | color: '#fff', 306 | }, 307 | 308 | sibling: { 309 | position: 'absolute', 310 | left: 0, 311 | top: 0, 312 | right: 0, 313 | bottom: 0, 314 | backgroundColor: 'rgba(0,0,0,0.8)', 315 | }, 316 | 317 | introButton: { 318 | padding: 2, 319 | borderColor: '#28a3ef', 320 | borderWidth: 1, 321 | borderRadius: 2, 322 | }, 323 | 324 | buttonText: { 325 | fontSize: 12, 326 | textAlign: 'center', 327 | color: '#28a3ef', 328 | }, 329 | 330 | introBar: { 331 | marginTop: 10, 332 | flexDirection: 'row', 333 | justifyContent: 'flex-end', 334 | }, 335 | 336 | modalContent: { 337 | position: 'absolute', 338 | overflow: 'hidden', 339 | }, 340 | 341 | modal: { 342 | backgroundColor: 'rgba(0,0,0,0.5)', 343 | }, 344 | }); 345 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | groupMap = { 4 | groupA: { 5 | 1: { 6 | content: React.Element, 7 | disable: boolean, 8 | target: IntroComponent, 9 | refTarget: ref, 10 | _index: number, 11 | } 12 | } 13 | } 14 | */ 15 | export const groupMap = {}; 16 | 17 | export const DEFAULT_GROUP = "DEFAULT_GROUP"; 18 | 19 | export const DEFAULTOPTIONS = { 20 | group: DEFAULT_GROUP 21 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export {IntroManage} from './IntroManage'; 2 | import {Intro} from './IntroComponent'; 3 | 4 | export default Intro; --------------------------------------------------------------------------------