1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00
Nuno Góis 2023-04-18 10:56:15 +01:00 committed by GitHub
parent 35d354d2b6
commit 6c79c790a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 773 additions and 4 deletions

View File

@ -66,8 +66,10 @@
"chartjs-adapter-date-fns": "3.0.0",
"classnames": "2.3.2",
"copy-to-clipboard": "3.3.3",
"countries-and-timezones": "^3.4.0",
"cypress": "9.7.0",
"date-fns": "2.29.3",
"date-fns-tz": "^2.0.0",
"debounce": "1.2.1",
"deep-diff": "1.0.2",
"dequal": "2.0.3",
@ -92,7 +94,9 @@
"react-dropzone": "14.2.3",
"react-error-boundary": "3.1.4",
"react-hooks-global-state": "2.1.0",
"react-joyride": "^2.5.3",
"react-markdown": "^8.0.4",
"react-linkify": "^1.0.0-alpha",
"react-router-dom": "6.8.1",
"react-table": "7.8.0",
"react-test-renderer": "17.0.2",
@ -107,10 +111,7 @@
"vite-plugin-svgr": "2.4.0",
"vite-tsconfig-paths": "4.0.5",
"vitest": "0.28.5",
"whatwg-fetch": "3.6.2",
"countries-and-timezones": "^3.4.0",
"date-fns-tz": "^2.0.0",
"react-linkify": "^1.0.0-alpha"
"whatwg-fetch": "3.6.2"
},
"optionalDependencies": {
"orval": "^6.10.3"

View File

@ -20,6 +20,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import MaintenanceBanner from './maintenance/MaintenanceBanner';
import { styled } from '@mui/material';
import { InitialRedirect } from './InitialRedirect';
import { Demo } from './demo/Demo';
const StyledContainer = styled('div')(() => ({
'& ul': {
@ -100,6 +101,13 @@ export const App = () => {
<FeedbackNPS openUrl="http://feedback.unleash.run" />
<ConditionallyRender
condition={Boolean(
uiConfig.flags.demo
)}
show={<Demo />}
/>
<SplashPageRedirect />
</StyledContainer>
</>

View File

@ -0,0 +1,236 @@
import { Typography } from '@mui/material';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { Badge } from 'component/common/Badge/Badge';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useEffect, useState } from 'react';
import { Step } from 'react-joyride';
import { DemoTopics } from './DemoTopics/DemoTopics';
import { DemoSteps } from './DemoSteps/DemoSteps';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { createLocalStorage } from 'utils/createLocalStorage';
export interface ITutorialTopic {
title: string;
steps: Step[];
}
const defaultProgress = {
expanded: true,
run: false,
topic: 0,
steps: [0],
};
const { value: storedProgress, setValue: setStoredProgress } =
createLocalStorage('Tutorial:v1', defaultProgress);
const TOPICS: ITutorialTopic[] = [
{
title: 'Import',
steps: [
{
target: 'button[data-testid="IMPORT_BUTTON"]',
title: (
<Typography fontWeight="bold">
Import toggle configuration
</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
This is a cool feature that enables you to import
your toggle configuration. This is just an example
and not part of the final guide.
</Typography>
</>
),
disableBeacon: true,
},
],
},
{
title: 'New feature toggle',
steps: [
{
target: 'button[data-testid="NAVIGATE_TO_CREATE_FEATURE"]',
title: (
<Typography fontWeight="bold">
Add a new feature toggle
</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
You can use this button to add a new feature toggle.
This is just an example and not part of the final
guide.
</Typography>
</>
),
disableBeacon: true,
},
],
},
{
title: 'Enable/disable a feature toggle',
steps: [
{
target: '.MuiSwitch-sizeMedium',
title: (
<Typography fontWeight="bold">
Enable/disable a feature toggle
</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
The simplest way to use a feature toggle is to
enable or disable it for everyone (on/off).
</Typography>
<Badge
sx={{ marginTop: 2 }}
icon={<InfoOutlinedIcon />}
>
Look at the demo page when toggling!
</Badge>
</>
),
disableBeacon: true,
},
],
},
{
title: 'Community',
steps: [
{
target: 'a[href="https://twitter.com/getunleash"]',
title: (
<Typography fontWeight="bold">
Follow us on Twitter!
</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
Following us on Twitter is one of the easiest ways
of keeping up with us. This is just an example and
not part of the final guide.
</Typography>
</>
),
disableBeacon: true,
},
{
target: 'a[href="https://www.linkedin.com/company/getunleash"]',
title: (
<Typography fontWeight="bold">
Follow us on LinkedIn!
</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
You can also follow us LinkedIn. This is just an
example and not part of the final guide.
</Typography>
</>
),
disableBeacon: true,
},
{
target: 'a[href="https://github.com/Unleash/unleash"]',
title: (
<Typography fontWeight="bold">
Check out Unleash on GitHub!
</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
Unleash is open-source, check out the project on
GitHub. This is just an example and not part of the
final guide.
</Typography>
</>
),
disableBeacon: true,
},
{
target: 'a[href="https://slack.unleash.run"]',
title: (
<Typography fontWeight="bold">Join us on Slack!</Typography>
),
content: (
<>
<Typography variant="body2" color="text.secondary">
Join our community in Slack. This is just an example
and not part of the final guide.
</Typography>
</>
),
disableBeacon: true,
},
],
},
];
export const Demo = () => {
const { uiConfig } = useUiConfig();
const [loaded, setLoaded] = useState(false);
const [expanded, setExpanded] = useState(storedProgress.expanded ?? true);
const [run, setRun] = useState(storedProgress.run ?? false);
const [topic, setTopic] = useState(storedProgress.topic ?? 0);
const [steps, setSteps] = useState(storedProgress.steps ?? [0]);
useEffect(() => {
setTimeout(() => {
setLoaded(true);
}, 1000);
}, []);
useEffect(() => {
setStoredProgress({
expanded,
run,
topic,
steps,
});
}, [expanded, run, topic, steps]);
if (!uiConfig.flags.demo) return null;
return (
<>
<DemoTopics
expanded={expanded}
setExpanded={setExpanded}
steps={steps}
currentTopic={topic}
setCurrentTopic={(topic: number) => {
setTopic(topic);
setSteps(steps => {
const newSteps = [...steps];
newSteps[topic] = 0;
return newSteps;
});
}}
topics={TOPICS}
/>
<ConditionallyRender
condition={loaded}
show={
<DemoSteps
run={run}
setRun={setRun}
setExpanded={setExpanded}
steps={steps}
setSteps={setSteps}
topic={topic}
setTopic={setTopic}
topics={TOPICS}
/>
}
/>
</>
);
};

View File

@ -0,0 +1,232 @@
import Joyride, {
ACTIONS,
CallBackProps,
EVENTS,
STATUS,
TooltipRenderProps,
} from 'react-joyride';
import { Button, Typography, styled, useTheme } from '@mui/material';
import { ITutorialTopic } from '../Demo';
import { useEffect } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
const StyledTooltip = styled('div')(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
color: theme.palette.text.primary,
borderRadius: theme.shape.borderRadiusMedium,
width: '100%',
maxWidth: theme.spacing(45),
padding: theme.spacing(3),
}));
const StyledTooltipTitle = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
marginBottom: theme.spacing(1),
flexWrap: 'wrap',
}));
const StyledTooltipActions = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'space-between',
marginTop: theme.spacing(3),
'&&& button': {
'&:first-of-type': {
marginLeft: theme.spacing(-2),
},
fontSize: theme.fontSizes.smallBody,
},
}));
const StyledTooltipPrimaryActions = styled('div')(({ theme }) => ({
display: 'flex',
gap: theme.spacing(1),
}));
interface IDemoStepsProps {
run: boolean;
setRun: React.Dispatch<React.SetStateAction<boolean>>;
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
steps: number[];
setSteps: React.Dispatch<React.SetStateAction<number[]>>;
topic: number;
setTopic: React.Dispatch<React.SetStateAction<number>>;
topics: ITutorialTopic[];
}
export const DemoSteps = ({
run,
setRun,
setExpanded,
steps,
setSteps,
topic,
setTopic,
topics,
}: IDemoStepsProps) => {
const theme = useTheme();
const skip = () => {
setRun(false);
setTopic(-1);
setExpanded(false);
};
const back = () => {
if (steps[topic] === 0) {
setRun(false);
const newTopic = topic - 1;
setTopic(newTopic);
setSteps(steps => {
const newSteps = [...steps];
newSteps[newTopic] = topics[newTopic].steps.length - 1;
return newSteps;
});
} else {
setSteps(steps => {
const newSteps = [...steps];
newSteps[topic] = steps[topic] - 1;
return newSteps;
});
}
};
const joyrideCallback = (data: CallBackProps) => {
const { action, index, status, type, step } = data;
if (action === ACTIONS.UPDATE) {
const el = document.querySelector(step.target as string);
if (el) {
el.scrollIntoView({
block: 'center',
});
}
}
if (
([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND] as string[]).includes(
type
)
) {
const newStep = index + (action === ACTIONS.PREV ? -1 : 1);
setSteps(steps => {
const newSteps = [...steps];
newSteps[topic] = newStep;
return newSteps;
});
} else if (
([STATUS.FINISHED, STATUS.SKIPPED] as string[]).includes(status)
) {
setRun(false);
if (topic === topics.length - 1) {
setTopic(-1);
setExpanded(false);
} else {
const newTopic = topic + 1;
setTopic(newTopic);
setSteps(steps => {
const newSteps = [...steps];
newSteps[newTopic] = 0;
return newSteps;
});
}
}
};
useEffect(() => {
setRun(true);
}, [topic, steps]);
if (topic === -1) return null;
return (
<Joyride
run={run}
stepIndex={steps[topic]}
callback={joyrideCallback}
steps={topics[topic].steps}
disableScrolling
disableOverlayClose
spotlightClicks
floaterProps={{
disableAnimation: true,
styles: {
floater: {
filter: `drop-shadow(${theme.palette.primary.main} 0px 0px 3px)`,
},
},
}}
styles={{
options: {
arrowColor: theme.palette.background.paper,
zIndex: theme.zIndex.snackbar,
},
spotlight: {
borderRadius: theme.shape.borderRadiusMedium,
border: `2px solid ${theme.palette.primary.main}`,
outline: `2px solid ${theme.palette.secondary.border}`,
backgroundColor: 'transparent',
},
overlay: {
backgroundColor: 'transparent',
mixBlendMode: 'unset',
},
}}
tooltipComponent={({
step,
primaryProps,
tooltipProps,
}: TooltipRenderProps) => {
const { onClick } = primaryProps;
return (
<StyledTooltip {...tooltipProps}>
<StyledTooltipTitle>
{step.title}
<ConditionallyRender
condition={topics[topic].steps.length > 1}
show={
<Typography
variant="body2"
color="text.secondary"
flexShrink={0}
>
(step {steps[topic] + 1} of{' '}
{topics[topic].steps.length})
</Typography>
}
/>
</StyledTooltipTitle>
{step.content}
<StyledTooltipActions>
<Button variant="text" onClick={skip}>
Skip
</Button>
<StyledTooltipPrimaryActions>
<ConditionallyRender
condition={topic > 0 || steps[topic] > 0}
show={
<Button
variant="outlined"
onClick={back}
>
Back
</Button>
}
/>
<Button onClick={onClick} variant="contained">
{topic === topics.length - 1 &&
steps[topic] ===
topics[topic].steps.length - 1
? 'Finish'
: 'Next'}
</Button>
</StyledTooltipPrimaryActions>
</StyledTooltipActions>
</StyledTooltip>
);
}}
/>
);
};

View File

@ -0,0 +1,210 @@
import {
Accordion,
AccordionDetails,
AccordionSummary,
Button,
LinearProgress,
Typography,
linearProgressClasses,
styled,
} from '@mui/material';
import { CheckCircle, CircleOutlined, ExpandMore } from '@mui/icons-material';
import { ITutorialTopic } from '../Demo';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
const StyledAccordion = styled(Accordion)(({ theme }) => ({
position: 'fixed',
bottom: 0,
left: 0,
width: '100%',
maxWidth: theme.spacing(30),
zIndex: theme.zIndex.fab,
'&&&': {
borderRadius: 0,
borderTopLeftRadius: theme.shape.borderRadiusLarge,
borderTopRightRadius: theme.shape.borderRadiusLarge,
},
'&:before': {
display: 'none',
},
'& .expand-icon': {
position: 'absolute',
right: theme.spacing(2),
fontSize: theme.fontSizes.mainHeader,
transition: 'transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
},
'&.Mui-expanded .expand-icon': {
transform: 'rotate(180deg)',
},
}));
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
'& .MuiAccordionSummary-content': {
flexDirection: 'column',
alignItems: 'center',
},
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
borderTopLeftRadius: theme.shape.borderRadiusLarge,
borderTopRightRadius: theme.shape.borderRadiusLarge,
}));
const StyledExpandMoreIcon = styled(ExpandMore)(({ theme }) => ({
color: theme.palette.primary.contrastText,
}));
const StyledTitle = styled('div')({
display: 'flex',
alignItems: 'center',
});
const StyledSubtitle = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.smallerBody,
marginTop: theme.spacing(0.5),
marginBottom: theme.spacing(0.5),
}));
const StyledProgress = styled('div')(({ theme }) => ({
width: '100%',
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1.5),
}));
const StyledLinearProgress = styled(LinearProgress)(({ theme }) => ({
width: '100%',
height: theme.spacing(1),
borderRadius: theme.shape.borderRadius,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.primary.dark,
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.primary.contrastText,
},
}));
const StyledStep = styled('li', {
shouldForwardProp: prop => prop !== 'selected' && prop !== 'completed',
})<{ selected?: boolean; completed?: boolean }>(
({ theme, selected, completed }) => ({
padding: theme.spacing(1),
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
marginTop: theme.spacing(1),
borderRadius: theme.shape.borderRadius,
gap: theme.spacing(1),
backgroundColor: theme.palette.background.elevation2,
...(selected && {
backgroundColor: theme.palette.secondary.light,
fontWeight: theme.typography.fontWeightBold,
border: `1px solid ${theme.palette.primary.main}`,
}),
...(completed && {
backgroundColor: theme.palette.background.elevation1,
textDecoration: 'line-through',
}),
})
);
const StyledCheckCircle = styled(CheckCircle)(({ theme }) => ({
color: theme.palette.primary.main,
fontSize: theme.fontSizes.bodySize,
}));
const StyledCircleOutlined = styled(CircleOutlined)(({ theme }) => ({
color: theme.palette.neutral.main,
fontSize: theme.fontSizes.bodySize,
}));
const StyledStepIcon = styled(ExpandMore)(({ theme }) => ({
transform: 'rotate(-90deg)',
fontSize: theme.fontSizes.bodySize,
}));
const StyledButton = styled(Button)(({ theme }) => ({
width: '100%',
marginTop: theme.spacing(2),
'&&&': {
fontSize: theme.fontSizes.smallBody,
},
}));
interface IDemoTopicsProps {
expanded: boolean;
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
steps: number[];
currentTopic: number;
setCurrentTopic: (topic: number) => void;
topics: ITutorialTopic[];
}
export const DemoTopics = ({
expanded,
setExpanded,
steps,
currentTopic,
setCurrentTopic,
topics,
}: IDemoTopicsProps) => {
const completedSteps = steps.reduce((acc, step) => acc + (step || 0), 0);
const totalSteps = topics.flatMap(({ steps }) => steps).length;
const percentage = (completedSteps / totalSteps) * 100;
return (
<StyledAccordion
expanded={expanded}
onChange={() => setExpanded(expanded => !expanded)}
>
<StyledAccordionSummary>
<StyledTitle>
<Typography fontWeight="bold">Unleash tutorial</Typography>
<StyledExpandMoreIcon className="expand-icon" />
</StyledTitle>
<StyledSubtitle>
Complete all steps to finish tutorial
</StyledSubtitle>
<StyledProgress>
<Typography variant="body2">
{percentage.toFixed()}%
</Typography>
<StyledLinearProgress
variant="determinate"
value={percentage}
/>
</StyledProgress>
</StyledAccordionSummary>
<AccordionDetails>
<Typography variant="body2" paddingTop={1}>
The steps will guide you
</Typography>
{topics.map((topic, index) => {
const selected = currentTopic === index;
const completed = steps[index] === topic.steps.length;
return (
<StyledStep
key={topic.title}
onClick={() => setCurrentTopic(index)}
selected={selected}
completed={completed}
>
<ConditionallyRender
condition={completed}
show={<StyledCheckCircle />}
elseShow={<StyledCircleOutlined />}
/>
<Typography variant="body2" sx={{ flex: 1 }}>
{topic.title}
</Typography>
<StyledStepIcon />
</StyledStep>
);
})}
<StyledButton variant="outlined">
View demo link again
</StyledButton>
</AccordionDetails>
</StyledAccordion>
);
};

View File

@ -15,6 +15,8 @@ import { InstanceStatus } from 'component/common/InstanceStatus/InstanceStatus';
import { UIProviderContainer } from 'component/providers/UIProvider/UIProviderContainer';
import { MessageBanner } from 'component/common/MessageBanner/MessageBanner';
window.global ||= window;
ReactDOM.render(
<UIProviderContainer>
<AccessProvider>

View File

@ -1469,6 +1469,11 @@
resolved "https://registry.yarnpkg.com/@exodus/schemasafe/-/schemasafe-1.0.0-rc.9.tgz#56b9c6df627190f2dcda15f81f25d68826d9be4d"
integrity sha512-dGGHpb61hLwifAu7sotuHFDBw6GTdpG8aKC0fsK17EuTzMRvUrH7lEAr6LTJ+sx3AZYed9yZ77rltVDHyg2hRg==
"@gilbarbara/deep-equal@^0.1.1":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz#1a106721368dba5e7e9fb7e9a3a6f9efbd8df36d"
integrity sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
@ -4142,6 +4147,11 @@ deepmerge@^2.2.1:
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
defaults@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a"
@ -4921,6 +4931,11 @@ executable@^4.1.1:
dependencies:
pify "^2.2.0"
exenv@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
expect@^29.0.0:
version "29.3.1"
resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6"
@ -5793,6 +5808,16 @@ is-interactive@^1.0.0:
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
is-lite@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.8.2.tgz#26ab98b32aae8cc8b226593b9a641d2bf4bd3b6a"
integrity sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==
is-lite@^0.9.2:
version "0.9.2"
resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.9.2.tgz#4b19e9a26b7c99ed50f748bcf088db57893d0730"
integrity sha512-qZuxbaEiKLOKhX4sbHLfhFN9iA3YciuZLb37/DfXCpWnz8p7qNL2lwkpxYMXfjlS8eEEjpULPZxAUI8N6FYvYQ==
is-map@^2.0.1, is-map@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
@ -7313,6 +7338,11 @@ pony-cause@1.1.1, pony-cause@^1.0.0:
resolved "https://registry.yarnpkg.com/pony-cause/-/pony-cause-1.1.1.tgz#f795524f83bebbf1878bd3587b45f69143cbf3f9"
integrity sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==
popper.js@^1.16.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
postcss@^8.4.20, postcss@^8.4.21:
version "8.4.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
@ -7493,6 +7523,19 @@ react-error-boundary@3.1.4, react-error-boundary@^3.1.0:
dependencies:
"@babel/runtime" "^7.12.5"
react-floater@^0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/react-floater/-/react-floater-0.7.6.tgz#a98ee90e3d51200c6e6a564ff33496f3c0d7cfee"
integrity sha512-tt/15k/HpaShbtvWCwsQYLR+ebfUuYbl+oAUJ3DcEDkgYKeUcSkDey2PdAIERdVwzdFZANz47HbwoET2/Rduxg==
dependencies:
deepmerge "^4.2.2"
exenv "^1.2.2"
is-lite "^0.8.2"
popper.js "^1.16.0"
prop-types "^15.8.1"
react-proptype-conditional-require "^1.0.4"
tree-changes "^0.9.1"
react-hooks-global-state@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-hooks-global-state/-/react-hooks-global-state-2.1.0.tgz#91d1d85c6c920f2e8681579d71d552207c5fe4ac"
@ -7522,6 +7565,20 @@ react-linkify@^1.0.0-alpha:
dependencies:
linkify-it "^2.0.3"
tlds "^1.199.0"
react-joyride@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/react-joyride/-/react-joyride-2.5.3.tgz#3e753f80502a74abcc956babec4873d204345911"
integrity sha512-DKKvb/JAAsHm0x/RWO3WI6NOtTMHDso5v8MTauxTSz2dFs7Tu1rWg1BDBWmEMj6pUCvem7hblFbCiDAcvhs8tQ==
dependencies:
deepmerge "^4.2.2"
exenv "^1.2.2"
is-lite "^0.9.2"
prop-types "^15.8.1"
react-floater "^0.7.6"
react-is "^16.13.1"
scroll "^3.0.1"
scrollparent "^2.0.1"
tree-changes "^0.9.2"
react-markdown@^8.0.4:
version "8.0.4"
@ -7544,6 +7601,11 @@ react-markdown@^8.0.4:
unist-util-visit "^4.0.0"
vfile "^5.0.0"
react-proptype-conditional-require@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz#69c2d5741e6df5e08f230f36bbc2944ee1222555"
integrity sha512-nopsRn7KnGgazBe2c3H2+Kf+Csp6PGDRLiBkYEDMKY8o/EIgft/WnIm/OnAKTawZiLnJXHAqhpFBddvs6NiXlw==
react-refresh@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
@ -7920,6 +7982,16 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scroll@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/scroll/-/scroll-3.0.1.tgz#d5afb59fb3592ee3df31c89743e78b39e4cd8a26"
integrity sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==
scrollparent@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/scrollparent/-/scrollparent-2.0.1.tgz#715d5b9cc57760fb22bdccc3befb5bfe06b1a317"
integrity sha512-HSdN78VMvFCSGCkh0oYX/tY4R3P1DW61f8+TeZZ4j2VLgfwvw0bpRSOv4PCVKisktIwbzHCfZsx+rLbbDBqIBA==
semver@7.3.8, semver@^7.3.2, semver@^7.3.7:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
@ -8477,6 +8549,14 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tree-changes@^0.9.1, tree-changes@^0.9.2:
version "0.9.3"
resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.9.3.tgz#89433ab3b4250c2910d386be1f83912b7144efcc"
integrity sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==
dependencies:
"@gilbarbara/deep-equal" "^0.1.1"
is-lite "^0.8.2"
trim-lines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"