setup introjs tour

This commit is contained in:
saml33 2021-10-29 23:05:55 +11:00
parent 1e0c38799f
commit b8d34934d9
15 changed files with 428 additions and 87 deletions

View File

@ -5,6 +5,7 @@ import Button from './Button'
import useLocalStorageState from '../hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import Checkbox from './Checkbox'
import { SHOW_TOUR_KEY } from './IntroTips'
export const ALPHA_MODAL_KEY = 'mangoAlphaAccepted-3.06'
@ -18,11 +19,17 @@ const AlphaModal = ({
const { t } = useTranslation('common')
const [acceptRisks, setAcceptRisks] = useState(false)
const [, setAlphaAccepted] = useLocalStorageState(ALPHA_MODAL_KEY, false)
const [, setShowTips] = useLocalStorageState(SHOW_TOUR_KEY, false)
const handleAccept = () => {
const handleGetStarted = () => {
setAlphaAccepted(true)
}
const handleTakeTour = () => {
setAlphaAccepted(true)
setShowTips(true)
}
return (
<Modal isOpen={isOpen} onClose={onClose} hideClose>
<Modal.Header>
@ -71,9 +78,20 @@ const AlphaModal = ({
I understand and accept the risks
</Checkbox>
</div>
<div className={`mt-6 flex justify-center`}>
<Button disabled={!acceptRisks} onClick={handleAccept}>
Let&apos;s Go
<div className={`mt-6 flex justify-center space-x-4`}>
<Button
className="w-40"
disabled={!acceptRisks}
onClick={handleGetStarted}
>
Get Started
</Button>
<Button
className="w-40"
disabled={!acceptRisks}
onClick={handleTakeTour}
>
Take a Tour
</Button>
</div>
</Modal>

View File

@ -54,7 +54,7 @@ const ConnectWalletButton = () => {
<>
{connected && wallet?.publicKey ? (
<Menu>
<div className="relative">
<div className="relative" id="intro-step-1">
<Menu.Button className="bg-th-bkg-4 flex items-center justify-center rounded-full w-10 h-10 text-white focus:outline-none hover:bg-th-bkg-4 hover:text-th-fgd-3">
<ProfileIcon className="h-6 w-6" />
</Menu.Button>
@ -104,7 +104,10 @@ const ConnectWalletButton = () => {
</div>
</Menu>
) : (
<div className="bg-th-bkg-1 h-14 flex divide-x divide-th-bkg-3 justify-between">
<div
className="bg-th-bkg-1 h-14 flex divide-x divide-th-bkg-3 justify-between"
id="intro-step-0"
>
<button
onClick={handleWalletConect}
disabled={!wallet}

165
components/IntroTips.tsx Normal file
View File

@ -0,0 +1,165 @@
import React, { Component } from 'react'
import { Steps } from 'intro.js-react'
export const SHOW_TOUR_KEY = 'showTour'
interface Props {
showTour: boolean
connected: boolean
}
interface State {
steps: any
stepsEnabled: boolean
initialStep: number
}
class IntroTips extends Component<Props, State> {
steps: any
constructor(props) {
super(props)
this.state = {
stepsEnabled: true,
initialStep: 0,
steps: [
{
element: '#intro-step-0',
intro: (
<div>
<h4>Connect your wallet</h4>
<p>We&apos;ll show you around...</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
},
{
element: '#intro-step-1',
intro: (
<div>
<h4>Profile Menu</h4>
<p>
Access your Mango Accounts, copy your wallet address and
disconnect here.
</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
disableInteraction: true,
},
{
element: '#intro-step-2',
intro: (
<div>
<h4>Pick a theme</h4>
<p>Mango, Dark or Light (if you&apos;re that way inclined).</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
disableInteraction: true,
},
{
element: '#intro-step-3',
intro: (
<div>
<h4>English not your preferred language?</h4>
<p>No problem, change it here. More languages coming soon...</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
disableInteraction: true,
},
{
element: '#intro-step-4',
intro: (
<div>
<h4>Refresh your account data</h4>
<p>
Data is refreshed automatically but you can manually refresh
here.
</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
disableInteraction: true,
},
{
element: '#intro-step-5',
intro: (
<div>
<h4>Customize the layout</h4>
<p>
Unlock to re-arrange and re-size the trading panels to your
liking.
</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
disableInteraction: true,
},
{
element: '#intro-step-6',
intro: (
<div>
<h4>Perp position details</h4>
<p>
When you open a perp position, you&apos;ll see the details here.
If you&apos;re unfamiliar with how settling PnL works, we&apos;d
recommend reading up on it before you get started.
</p>
</div>
),
tooltipClass: 'intro-tooltip',
highlightClass: 'intro-highlight',
disableInteraction: true,
},
],
}
}
handleEndTour = () => {
localStorage.setItem('showTour', 'false')
this.setState({ stepsEnabled: false })
}
onBeforeChange = (nextStepIndex) => {
if (nextStepIndex === 1) {
this.steps.updateStepElement(nextStepIndex)
document.querySelector<HTMLElement>(
'.introjs-tooltipbuttons'
).style.display = 'block'
}
}
componentDidUpdate(prevProps) {
if (this.props.connected !== prevProps.connected) {
this.steps.introJs.nextStep()
}
}
render() {
const { initialStep, stepsEnabled, steps } = this.state
const { showTour } = this.props
return showTour ? (
<Steps
enabled={stepsEnabled}
steps={steps}
initialStep={initialStep}
onBeforeChange={this.onBeforeChange}
onExit={() => this.handleEndTour()}
options={{
skipLabel: 'Skip Tour',
exitOnOverlayClick: false,
}}
ref={(steps) => (this.steps = steps)}
/>
) : null
}
}
export default IntroTips

View File

@ -36,20 +36,24 @@ const LanguageSwitch = () => {
setSavedLanguage(e)
}
return mounted ? (
<DropMenu
button={
<div className="bg-th-bkg-4 flex items-center justify-center rounded-full w-8 h-8 text-th-fgd-1 focus:outline-none hover:text-th-primary">
<TranslateIcon className="h-4 w-4" />
</div>
}
value={savedLanguage}
onChange={(lang) => handleLangChange(lang)}
options={LANGS}
toolTipContent={t('change-language')}
/>
) : (
<div className="bg-th-bkg-3 rounded-full w-8 h-8" />
return (
<div id="intro-step-3">
{mounted ? (
<DropMenu
button={
<div className="bg-th-bkg-4 flex items-center justify-center rounded-full w-8 h-8 text-th-fgd-1 focus:outline-none hover:text-th-primary">
<TranslateIcon className="h-4 w-4" />
</div>
}
value={savedLanguage}
onChange={(lang) => handleLangChange(lang)}
options={LANGS}
toolTipContent={t('change-language')}
/>
) : (
<div className="bg-th-bkg-3 rounded-full w-8 h-8" />
)}
</div>
)
}

View File

@ -254,10 +254,14 @@ const MarketDetails = () => {
</div>
</div>
<div className="absolute right-4 bottom-0 sm:bottom-auto lg:right-6 flex items-center justify-end">
{!isMobile ? <UiLock /> : null}
{!isMobile && connected && mangoAccount ? (
<ManualRefresh className="pl-2" />
{!isMobile ? (
<div id="intro-step-5">
<UiLock />
</div>
) : null}
<div className="ml-2" id="intro-step-4">
{!isMobile && connected && mangoAccount ? <ManualRefresh /> : null}
</div>
</div>
</div>
)

View File

@ -152,7 +152,10 @@ export default function MarketPosition() {
return (
<>
<div className={!connected && !isMobile ? 'filter blur-sm' : null}>
<div
className={!connected && !isMobile ? 'filter blur-sm' : null}
id="intro-step-6"
>
{!isMobile ? (
<ElementTitle>
{marketConfig.name} {t('position')}

View File

@ -19,20 +19,24 @@ const ThemeSwitch = () => {
// When mounted on client, now we can show the UI
useEffect(() => setMounted(true), [])
return mounted ? (
<DropMenu
button={
<div className="bg-th-bkg-4 flex items-center justify-center rounded-full w-8 h-8 text-th-fgd-1 focus:outline-none hover:text-th-primary">
{THEMES.find((t) => t.name === theme).icon}
</div>
}
value={theme}
onChange={(theme) => setTheme(theme)}
options={THEMES}
toolTipContent={t('change-theme')}
/>
) : (
<div className="bg-th-bkg-3 rounded-full w-8 h-8" />
return (
<div id="intro-step-2">
{mounted ? (
<DropMenu
button={
<div className="bg-th-bkg-4 flex items-center justify-center rounded-full w-8 h-8 text-th-fgd-1 focus:outline-none hover:text-th-primary">
{THEMES.find((t) => t.name === theme).icon}
</div>
}
value={theme}
onChange={(theme) => setTheme(theme)}
options={THEMES}
toolTipContent={t('change-theme')}
/>
) : (
<div className="bg-th-bkg-3 rounded-full w-8 h-8" />
)}
</div>
)
}

View File

@ -47,7 +47,7 @@ const TVChartContainer = () => {
const selectedMarketName = selectedMarketConfig.name
const openOrders = useOpenOrders()
const actions = useMangoStore((s) => s.actions )
const actions = useMangoStore((s) => s.actions)
const connected = useMangoStore((s) => s.wallet.connected)
const selectedMarginAccount =
useMangoStore.getState().selectedMangoAccount.current
@ -149,7 +149,6 @@ const TVChartContainer = () => {
//eslint-disable-next-line
}, [selectedMarketConfig, theme, isMobile])
const handleCancelOrder = async (
order: Order | PerpOrder | PerpTriggerOrder,
market: Market | PerpMarket
@ -204,11 +203,10 @@ const TVChartContainer = () => {
actions.reloadMangoAccount()
actions.reloadOrders()
toggleOrderInProgress(false)
toggleMoveInProgress(false)
toggleMoveInProgress(false)
}
}
const handleModifyOrder = async (
order: Order | PerpOrder,
market: Market | PerpMarket,
@ -283,7 +281,6 @@ const TVChartContainer = () => {
}
}
function getLine(order, market) {
return tvWidgetRef.current
.chart()
@ -321,13 +318,9 @@ const TVChartContainer = () => {
tvWidgetRef.current.showConfirmDialog({
title: 'Modify Your Order?',
body: `Would you like to change your order from a
${order.size} ${market.config.baseSymbol} ${
order.side
} at $${currentOrderPrice}
${order.size} ${market.config.baseSymbol} ${order.side} at $${currentOrderPrice}
to a
${order.size} ${market.config.baseSymbol} LIMIT ${
order.side
} at $${updatedOrderPrice}?
${order.size} ${market.config.baseSymbol} LIMIT ${order.side} at $${updatedOrderPrice}?
`,
callback: (res) => {
if (res) {
@ -340,11 +333,10 @@ const TVChartContainer = () => {
},
})
}
} else {
} else {
tvWidgetRef.current.showNoticeDialog({
title: 'Advanced Order Type',
body:
'Advanced order types in the chart window may only be cancelled. If new conditions are required, please cancel this order and use the Advanced Trade Form.',
body: 'Advanced order types in the chart window may only be cancelled. If new conditions are required, please cancel this order and use the Advanced Trade Form.',
callback: () => {
this.setPrice(currentOrderPrice)
toggleMoveInProgress(false)
@ -358,9 +350,7 @@ const TVChartContainer = () => {
tvWidgetRef.current.showConfirmDialog({
title: 'Cancel Your Order?',
body: `Would you like to cancel your order for
${order.size} ${market.config.baseSymbol} ${
order.side
} at $${order.price}
${order.size} ${market.config.baseSymbol} ${order.side} at $${order.price}
`,
callback: (res) => {
if (res) {
@ -374,39 +364,94 @@ const TVChartContainer = () => {
.setPrice(order.price)
.setQuantity(order.size)
.setText(getLineText(order, market))
.setTooltip(order.perpTrigger?.clientOrderId ? `${order.orderType} Order #: ${order.orderId}` : `Order #: ${order.orderId}`)
.setBodyTextColor(theme === 'Dark' ? '#F2C94C' : theme === 'Light' ? '#FF9C24' : '#F2C94C')
.setQuantityTextColor(theme === 'Dark' ? '#F2C94C' : theme === 'Light' ? '#FF9C24' : '#F2C94C')
.setCancelButtonIconColor(theme === 'Dark' ? '#F2C94C' : theme === 'Light' ? '#FF9C24' : '#F2C94C')
.setBodyBorderColor(order.perpTrigger?.clientOrderId ? '#FF9C24' : order.side == 'buy' ? '#4BA53B' : '#AA2222')
.setQuantityBorderColor(order.perpTrigger?.clientOrderId ? '#FF9C24' : order.side == 'buy' ? '#4BA53B' : '#AA2222')
.setCancelButtonBorderColor(order.perpTrigger?.clientOrderId ? '#FF9C24' : order.side == 'buy' ? '#4BA53B' : '#AA2222')
.setBodyBackgroundColor(theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832')
.setQuantityBackgroundColor(theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832')
.setCancelButtonBackgroundColor(theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832')
.setTooltip(
order.perpTrigger?.clientOrderId
? `${order.orderType} Order #: ${order.orderId}`
: `Order #: ${order.orderId}`
)
.setBodyTextColor(
theme === 'Dark' ? '#F2C94C' : theme === 'Light' ? '#FF9C24' : '#F2C94C'
)
.setQuantityTextColor(
theme === 'Dark' ? '#F2C94C' : theme === 'Light' ? '#FF9C24' : '#F2C94C'
)
.setCancelButtonIconColor(
theme === 'Dark' ? '#F2C94C' : theme === 'Light' ? '#FF9C24' : '#F2C94C'
)
.setBodyBorderColor(
order.perpTrigger?.clientOrderId
? '#FF9C24'
: order.side == 'buy'
? '#4BA53B'
: '#AA2222'
)
.setQuantityBorderColor(
order.perpTrigger?.clientOrderId
? '#FF9C24'
: order.side == 'buy'
? '#4BA53B'
: '#AA2222'
)
.setCancelButtonBorderColor(
order.perpTrigger?.clientOrderId
? '#FF9C24'
: order.side == 'buy'
? '#4BA53B'
: '#AA2222'
)
.setBodyBackgroundColor(
theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832'
)
.setQuantityBackgroundColor(
theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832'
)
.setCancelButtonBackgroundColor(
theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832'
)
.setBodyFont('Lato, sans-serif')
.setQuantityFont('Lato, sans-serif')
.setLineColor(order.perpTrigger?.clientOrderId ? '#FF9C24' : order.side == 'buy' ? '#4BA53B' : '#AA2222')
.setLineColor(
order.perpTrigger?.clientOrderId
? '#FF9C24'
: order.side == 'buy'
? '#4BA53B'
: '#AA2222'
)
.setLineLength(3)
.setLineWidth(2)
.setLineStyle(1)
}
function getLineText(order, market) {
if (order.perpTrigger?.clientOrderId) {
const triggerPrice = order.perpTrigger.triggerPrice * Math.pow(10, market.config.baseDecimals - market.config.quoteDecimals)
const triggerPrice =
order.perpTrigger.triggerPrice *
Math.pow(10, market.config.baseDecimals - market.config.quoteDecimals)
if (order.side === 'buy') {
if (order.perpTrigger.triggerCondition === 'above') {
return (order.orderType === 'market' ? `Stop Loss ` : `Stop Limit `) + `(${order.orderType} ${order.side}) if price is ${order.perpTrigger.triggerCondition} ${usdFormatter(triggerPrice)}`
return (
(order.orderType === 'market' ? `Stop Loss ` : `Stop Limit `) +
`(${order.orderType} ${order.side}) if price is ${
order.perpTrigger.triggerCondition
} ${usdFormatter(triggerPrice)}`
)
} else {
return `Take Profit (${order.orderType} ${order.side}) if price is ${order.perpTrigger.triggerCondition} ${usdFormatter(triggerPrice)}`
return `Take Profit (${order.orderType} ${order.side}) if price is ${
order.perpTrigger.triggerCondition
} ${usdFormatter(triggerPrice)}`
}
} else {
if (order.perpTrigger.triggerCondition === 'below') {
return (order.orderType === 'market' ? `Stop Loss ` : `Stop Limit `) + `(${order.orderType} ${order.side}) if price is ${order.perpTrigger.triggerCondition} ${usdFormatter(triggerPrice)}`
return (
(order.orderType === 'market' ? `Stop Loss ` : `Stop Limit `) +
`(${order.orderType} ${order.side}) if price is ${
order.perpTrigger.triggerCondition
} ${usdFormatter(triggerPrice)}`
)
} else {
return `Take Profit (${order.orderType} ${order.side}) if price is ${order.perpTrigger.triggerCondition} ${usdFormatter(triggerPrice)}`
return `Take Profit (${order.orderType} ${order.side}) if price is ${
order.perpTrigger.triggerCondition
} ${usdFormatter(triggerPrice)}`
}
}
} else {
@ -414,7 +459,6 @@ const TVChartContainer = () => {
}
}
function deleteLines() {
tvWidgetRef.current.onChartReady(() => {
if (lines?.size > 0) {
@ -426,7 +470,6 @@ const TVChartContainer = () => {
return new Map()
}
function drawLines() {
const tempLines = new Map()
tvWidgetRef.current.onChartReady(() => {
@ -439,10 +482,14 @@ const TVChartContainer = () => {
return tempLines
}
useInterval(() => {
if (selectedMarginAccount && connected && !moveInProgress && !orderInProgress && openOrders?.length > 0) {
if (
selectedMarginAccount &&
connected &&
!moveInProgress &&
!orderInProgress &&
openOrders?.length > 0
) {
let matches = 0
let openOrdersInSelectedMarket = 0
lines?.forEach((value, key) => {
@ -460,23 +507,23 @@ const TVChartContainer = () => {
})
if (
lines?.size != openOrdersInSelectedMarket
|| matches != openOrdersInSelectedMarket
|| lines?.size > 0 && lines?.size != matches
|| lines?.size > 0 && !selectedMarginAccount
|| priceReset
lines?.size != openOrdersInSelectedMarket ||
matches != openOrdersInSelectedMarket ||
(lines?.size > 0 && lines?.size != matches) ||
(lines?.size > 0 && !selectedMarginAccount) ||
priceReset
) {
if (priceReset) { togglePriceReset(false) }
if (priceReset) {
togglePriceReset(false)
}
setLines(deleteLines())
setLines(drawLines())
}
} else if (lines?.size > 0 && !moveInProgress && !orderInProgress) {
setLines(deleteLines())
}
}, [100])
return <div id={defaultProps.containerId} className="tradingview-chart" />
}

View File

@ -62,4 +62,4 @@ const TradeType = ({
)
}
export default TradeType
export default TradeType

View File

@ -48,6 +48,8 @@
"dayjs": "^1.10.4",
"immer": "^9.0.1",
"immutable-tuple": "^0.4.10",
"intro.js": "^4.2.2",
"intro.js-react": "^0.5.0",
"lodash-es": "^4.17.21",
"next": "^11.1.0",
"next-i18next": "^8.9.0",

View File

@ -2,6 +2,7 @@ import Head from 'next/head'
import { ThemeProvider } from 'next-themes'
import '../node_modules/react-grid-layout/css/styles.css'
import '../node_modules/react-resizable/css/styles.css'
import 'intro.js/introjs.css'
import '../styles/index.css'
import useWallet from '../hooks/useWallet'
import useHydrateStore from '../hooks/useHydrateStore'

View File

@ -13,6 +13,7 @@ import useLocalStorageState from '../../hooks/useLocalStorageState'
import AlphaModal, { ALPHA_MODAL_KEY } from '../../components/AlphaModal'
import { PageBodyWrapper } from '../../components/styles'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import IntroTips, { SHOW_TOUR_KEY } from '../../components/IntroTips'
export async function getServerSideProps({ locale }) {
return {
@ -25,8 +26,10 @@ export async function getServerSideProps({ locale }) {
const PerpMarket = () => {
const [alphaAccepted] = useLocalStorageState(ALPHA_MODAL_KEY, false)
const [showTour] = useLocalStorageState(SHOW_TOUR_KEY, false)
const groupConfig = useMangoGroupConfig()
const setMangoStore = useMangoStore((s) => s.set)
const connected = useMangoStore((s) => s.wallet.connected)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
@ -61,6 +64,7 @@ const PerpMarket = () => {
return (
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all `}>
<IntroTips connected={connected} showTour={showTour} />
<TopBar />
<MarketSelect />
<PageBodyWrapper className="p-1 sm:px-2 sm:py-1 md:px-2 md:py-1">

View File

@ -321,3 +321,79 @@ input[type='number'] {
.react-swipeable-view-container > div[aria-hidden='true'] {
height: 0;
}
/* Intro Tour */
.introjs-tooltip {
@apply bg-transparent;
}
.introjs-arrow {
@apply border-0;
}
.intro-tooltip * {
@apply bg-th-bkg-1 font-body text-th-fgd-1;
}
.introjs-skipbutton {
@apply text-sm text-th-fgd-3;
}
.introjs-tooltip-header {
@apply rounded-t-md;
padding: 10px;
}
.introjs-tooltipbuttons {
@apply border-th-bkg-4 hidden font-bold rounded-b-md focus:outline-none;
}
.introjs-nextbutton {
@apply bg-th-bkg-4 border-th-bkg-4 flex h-4 items-center rounded-full text-sm text-th-fgd-1;
text-shadow: none;
}
.introjs-prevbutton {
@apply hidden;
}
.introjs-button:hover {
@apply bg-th-bkg-4 border-th-bkg-4 brightness-[1.15] text-th-fgd-1;
}
.introjs-button:focus {
@apply bg-th-bkg-4 border-th-bkg-4 shadow-none text-th-fgd-1;
}
.introjs-hidden {
@apply hidden;
}
.introjs-progressbar {
@apply bg-th-primary;
}
.intro-tooltip h4 {
@apply font-body font-bold mb-1 text-lg;
}
.intro-tooltip p {
@apply font-body text-sm text-th-fgd-3;
}
.introjs-bullets ul li a {
@apply bg-th-bkg-4;
}
.introjs-bullets ul li a:hover {
@apply bg-th-fgd-4;
}
.introjs-bullets ul li a.active {
@apply bg-th-primary;
}
.intro-highlight {
@apply border-0;
}

View File

@ -108,7 +108,7 @@ export function calculateTradePrice(
if (tradeType === 'Market') {
return calculateMarketPrice(orderBook, baseSize, side)
} else if (TRIGGER_ORDER_TYPES.includes(tradeType)) {
if( tradeType === 'Take Profit Limit' || tradeType === 'Stop Limit' ) {
if (tradeType === 'Take Profit Limit' || tradeType === 'Stop Limit') {
return Number(price)
} else {
return Number(triggerPrice)

View File

@ -4802,6 +4802,16 @@ internmap@^1.0.0:
resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
intro.js-react@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/intro.js-react/-/intro.js-react-0.5.0.tgz#d4e67a4f6e4726bee803222ff09d6deacb4598c8"
integrity sha512-tr9uhoy/aRgcdZxpqYC/TPSVlJ/Gt9Kx4mpI7clCdFa7c3SG8reJugIa1U5XnWlDLRNMc+e8egb8Jf186yvc1A==
intro.js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/intro.js/-/intro.js-4.2.2.tgz#9077549cc6ef697e78d18d1c05003b7471281e1a"
integrity sha512-Zgz2e8syCuttJ2vJlDOWCSWPUJBr7AOJkU5Ti3zcvXho+y//q0ixwoT+PkPLJWI7AX35IdgRcxAEWUrOAJYiNQ==
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"