TooltipBox.js (7793B)
1 import {__} from '@wordpress/i18n'; 2 3 const { compose } = wp.compose; 4 const { withDispatch, withSelect } = wp.data; 5 const { useState, useEffect } = wp.element; 6 import {ModalManager} from '~redux-templates/modal-manager'; 7 import CONFIG from '../config'; 8 import helper from '../helper'; 9 const ARROW_BOX = 30; 10 const DEFAULT_BOX_WIDTH = 250; 11 const DEFAULT_BOX_HEIGHT = 300; 12 const DEFAULT_OFFSET_X = 0; 13 const DEFAULT_OFFSET_Y = 20; 14 const DEFAULT_ARROW_OFFSET_X = 20; 15 const DEFAULT_ARROW_OFFSET_Y = 20; 16 function TooltipBox(props) { 17 const { challengeStep, tooltipRect, isOpen, setChallengeStep, setChallengeFinalStatus, setChallengePassed, setChallengeListExpanded, setImportingTemplate } = props; 18 const [style, setStyle] = useState({}); 19 const [arrowStyle, setArrowStyle] = useState({}); 20 const [content, setContent] = useState(''); 21 const [wrapperClassname, setWrapperClassname] = useState(''); 22 23 const isVisible = () => { 24 return ((challengeStep >= 0 || challengeStep > CONFIG.totalStep) && isOpen); 25 } 26 27 const calculateWithStepInformation = () => { 28 const stepInformation = CONFIG.list[challengeStep]; 29 const boxWidth = (stepInformation.box && stepInformation.box.width) ? stepInformation.box.width : DEFAULT_BOX_WIDTH; 30 const boxHeight = (stepInformation.box && stepInformation.box.height) ? stepInformation.box.height : DEFAULT_BOX_HEIGHT; 31 const offsetX = stepInformation.offset ? stepInformation.offset.x :DEFAULT_OFFSET_X; 32 const offsetY = stepInformation.offset ? stepInformation.offset.y :DEFAULT_OFFSET_Y; 33 switch(stepInformation.direction) { 34 case 'right': 35 return [tooltipRect.left + offsetX, tooltipRect.top + offsetY - boxHeight / 2]; 36 case 'left': 37 return [tooltipRect.left + offsetX, tooltipRect.top + offsetY - boxHeight / 2]; 38 case 'top': 39 return [tooltipRect.left + offsetX - boxWidth / 2, tooltipRect.top + offsetY ]; 40 case 'bottom': 41 return [tooltipRect.left + offsetX - boxWidth / 2, tooltipRect.top - boxHeight + offsetY]; 42 default: 43 return [tooltipRect.left + offsetX, tooltipRect.top + offsetY]; 44 } 45 } 46 47 const calculateArrowOffset = () => { 48 const stepInformation = CONFIG.list[challengeStep]; 49 const boxWidth = (stepInformation.box && stepInformation.box.width) ? stepInformation.box.width : DEFAULT_BOX_WIDTH; 50 const boxHeight = (stepInformation.box && stepInformation.box.height) ? stepInformation.box.height : DEFAULT_BOX_HEIGHT; 51 const arrowOffsetX = (stepInformation.offset && isNaN(stepInformation.offset.arrowX) === false) ? stepInformation.offset.arrowX : DEFAULT_ARROW_OFFSET_X; 52 const arrowOffsetY = (stepInformation.offset && isNaN(stepInformation.offset.arrowY) === false) ? stepInformation.offset.arrowY : DEFAULT_ARROW_OFFSET_Y; 53 switch(stepInformation.direction) { 54 case 'top': 55 return [boxWidth / 2 + arrowOffsetX, arrowOffsetY]; 56 case 'bottom': 57 return [boxWidth / 2 + arrowOffsetX, arrowOffsetY]; 58 case 'left': 59 return [arrowOffsetX, arrowOffsetY + boxHeight / 2 - ARROW_BOX / 2]; 60 case 'right': 61 return [boxWidth + arrowOffsetX, arrowOffsetY + boxHeight / 2 - ARROW_BOX / 2]; 62 default: 63 return [arrowOffsetX, arrowOffsetY]; 64 } 65 } 66 // adjust position and content upon steps change 67 useEffect(() => { 68 if (isVisible() && tooltipRect) { 69 const stepInformation = CONFIG.list[challengeStep]; 70 if (stepInformation) { 71 const [boxLeft, boxTop] = calculateWithStepInformation(); 72 const [arrowOffsetX, arrowOffsetY] = calculateArrowOffset(); 73 setStyle({ 74 ...style, 75 display: 'block', 76 width: stepInformation.box ? stepInformation.box.width : DEFAULT_BOX_WIDTH, 77 left: boxLeft, 78 top: boxTop//tooltipRect.top + offsetY + PADDING_Y + ARROW_HEIGHT 79 }); 80 setContent(stepInformation.content); 81 setArrowStyle({ 82 ...arrowStyle, 83 display: 'block', 84 left: boxLeft + arrowOffsetX, // calculateLeftWithStepInformation(), 85 top: boxTop + arrowOffsetY // tooltipRect.top + offsetY + PADDING_Y 86 }); 87 } 88 } else { 89 setStyle({ ...style, display: 'none' }); 90 setArrowStyle({...arrowStyle, display: 'none'}); 91 } 92 }, [JSON.stringify(tooltipRect), challengeStep, isOpen]); 93 94 // update wrapper class name based on step change 95 useEffect(() => { 96 const stepInformation = CONFIG.list[challengeStep]; 97 if (stepInformation) { 98 switch(stepInformation.direction) { 99 case 'top': 100 setWrapperClassname('challenge-tooltip tooltipster-sidetip tooltipster-top'); 101 break; 102 case 'bottom': 103 setWrapperClassname('challenge-tooltip tooltipster-sidetip tooltipster-bottom'); 104 break; 105 case 'left': 106 setWrapperClassname('challenge-tooltip tooltipster-sidetip tooltipster-left'); 107 break; 108 case 'right': 109 setWrapperClassname('challenge-tooltip tooltipster-sidetip tooltipster-right'); 110 break; 111 default: 112 setWrapperClassname('challenge-tooltip tooltipster-sidetip tooltipster-left'); 113 } 114 115 } 116 }, [challengeStep]) 117 118 const toNextStep = () => { 119 if (challengeStep === CONFIG.totalStep - 1) { 120 // finalize challenge 121 ModalManager.show(); 122 setChallengeFinalStatus((helper.getSecondsLeft() > 0) ? 'success' : 'contact'); 123 setChallengeStep(CONFIG.beginningStep); 124 setChallengePassed(true); 125 setChallengeListExpanded(true); 126 setImportingTemplate(null); 127 } else 128 setChallengeStep(challengeStep + 1); 129 } 130 131 132 return ( 133 <div className={wrapperClassname}> 134 <div className="tooltipster-box" style={style}> 135 {content} 136 <div className="btn-row"> 137 <button className="challenge-done-btn" onClick={toNextStep}>{__('Next', redux_templates.i18n)}</button> 138 </div> 139 </div> 140 <div className="tooltipster-arrow" style={arrowStyle}> 141 <div className="tooltipster-arrow-uncropped"> 142 <div className="tooltipster-arrow-border"></div> 143 <div className="tooltipster-arrow-background"></div> 144 </div> 145 </div> 146 </div> 147 ); 148 } 149 150 151 export default compose([ 152 withDispatch((dispatch) => { 153 const { setChallengeStep, setChallengeFinalStatus, setChallengePassed, setChallengeListExpanded, setImportingTemplate } = dispatch('redux-templates/sectionslist'); 154 return { 155 setChallengeStep, 156 setChallengeFinalStatus, 157 setChallengePassed, 158 setChallengeListExpanded, 159 setImportingTemplate 160 }; 161 }), 162 163 withSelect((select, props) => { 164 const { getChallengeTooltipRect, getChallengeOpen, getChallengeStep, getChallengeFinalStatus } = select('redux-templates/sectionslist'); 165 return { 166 tooltipRect: getChallengeTooltipRect(), 167 isOpen: getChallengeOpen(), 168 challengeStep: getChallengeStep(), 169 finalStatus: getChallengeFinalStatus() 170 }; 171 }) 172 ])(TooltipBox);