diff --git a/packages/lsd-react/package.json b/packages/lsd-react/package.json index e4b831f..6e98b06 100644 --- a/packages/lsd-react/package.json +++ b/packages/lsd-react/package.json @@ -20,7 +20,8 @@ "clsx": "^1.2.1", "lodash": "^4.17.21", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-use": "^17.4.0" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.19.6", diff --git a/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx b/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx index 4f82b63..a460fc0 100644 --- a/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx +++ b/packages/lsd-react/src/components/CSSBaseline/CSSBaseline.tsx @@ -1,14 +1,26 @@ import { Global, SerializedStyles } from '@emotion/react' import React, { useMemo } from 'react' import { ButtonStyles } from '../Button/Button.styles' +import { DropdownStyles } from '../Dropdown/Dropdown.styles' +import { DropdownItemStyles } from '../DropdownItem/DropdownItem.styles' import { LsdIconStyles } from '../Icons/LsdIcon/LsdIcon.styles' +import { ListBoxStyles } from '../ListBox/ListBox.styles' import { TabItemStyles } from '../TabItem/TabItem.styles' import { TabsStyles } from '../Tabs/Tabs.styles' import { defaultThemes, Theme, withTheme } from '../Theme' import { TypographyStyles } from '../Typography/Typography.styles' const componentStyles: Array | SerializedStyles> = - [ButtonStyles, TypographyStyles, LsdIconStyles, TabItemStyles, TabsStyles] + [ + ButtonStyles, + TypographyStyles, + LsdIconStyles, + TabItemStyles, + TabsStyles, + ListBoxStyles, + DropdownStyles, + DropdownItemStyles, + ] export const CSSBaseline: React.FC<{ theme?: Theme }> = ({ theme = defaultThemes.light, diff --git a/packages/lsd-react/src/components/Dropdown/Dropdown.classes.ts b/packages/lsd-react/src/components/Dropdown/Dropdown.classes.ts new file mode 100644 index 0000000..45bed11 --- /dev/null +++ b/packages/lsd-react/src/components/Dropdown/Dropdown.classes.ts @@ -0,0 +1,20 @@ +export const dropdownClasses = { + root: `lsd-dropdown`, + + trigger: `lsd-dropdown-trigger`, + triggerLabel: `lsd-dropdown-trigger__label`, + triggerIcons: `lsd-dropdown-trigger-icons`, + triggerIcon: `lsd-dropdown-trigger-icons__icon`, + triggerMenuIcon: `lsd-dropdown-trigger-icons__menu-icon`, + + supportingText: 'lsd-dropdown__supporting-text', + + listBox: 'lsd-dropdown-list-box', + + open: 'lsd-dropdown--open', + error: 'lsd-dropdown--error', + disabled: 'lsd-dropdown--disabled', + small: `lsd-dropdown--small`, + medium: `lsd-dropdown--medium`, + large: `lsd-dropdown--large`, +} diff --git a/packages/lsd-react/src/components/Dropdown/Dropdown.stories.tsx b/packages/lsd-react/src/components/Dropdown/Dropdown.stories.tsx new file mode 100644 index 0000000..5f9cbf2 --- /dev/null +++ b/packages/lsd-react/src/components/Dropdown/Dropdown.stories.tsx @@ -0,0 +1,27 @@ +import { Meta, Story } from '@storybook/react' +import { Dropdown, DropdownProps } from './Dropdown' + +export default { + title: 'Dropdown', + component: Dropdown, +} as Meta + +export const Root: Story = (args) => ( +
+ +
+) + +Root.args = { + size: 'large', + label: 'Choose an option', + supportingText: '', + disabled: false, + error: false, + multi: false, + onChange: undefined, + options: new Array(16).fill(null).map((value, index) => ({ + value: `${index}`, + name: `Option ${index + 1}`, + })), +} diff --git a/packages/lsd-react/src/components/Dropdown/Dropdown.styles.ts b/packages/lsd-react/src/components/Dropdown/Dropdown.styles.ts new file mode 100644 index 0000000..1a144d2 --- /dev/null +++ b/packages/lsd-react/src/components/Dropdown/Dropdown.styles.ts @@ -0,0 +1,104 @@ +import { css } from '@emotion/react' +import { dropdownItemClasses } from '../DropdownItem/DropdownItem.classes' +import { dropdownClasses } from './Dropdown.classes' + +export const DropdownStyles = css` + .${dropdownClasses.root} { + } + + .${dropdownClasses.root}:not(.${dropdownClasses.disabled}):not( + .${dropdownClasses.error} + ) { + .${dropdownClasses.trigger} { + &:hover, + &:focus { + .${dropdownClasses.triggerLabel} { + text-decoration: underline; + } + } + } + } + + .${dropdownClasses.trigger} { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 10px 14px 10px 18px; + + cursor: pointer; + background: none; + border: 1px solid rgb(var(--lsd-border-primary)); + + &:focus { + outline: none; + } + } + + .${dropdownClasses.triggerLabel} { + cursor: inherit; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .${dropdownClasses.triggerIcons} { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; + min-width: 60px; + } + + .${dropdownClasses.triggerIcon} { + margin-right: 8px; + } + + .${dropdownClasses.triggerMenuIcon} { + } + + .${dropdownClasses.supportingText} { + margin: 6px 14px; + } + + .${dropdownClasses.error} { + .${dropdownClasses.triggerLabel} { + text-decoration: line-through; + } + } + + .${dropdownClasses.disabled} { + .${dropdownClasses.trigger} { + opacity: 0.34; + cursor: initial; + } + } + + .${dropdownClasses.listBox} { + max-height: 400px; + overflow: auto; + border: 1px solid rgb(var(--lsd-border-primary)); + border-top: 0; + + & .${dropdownItemClasses.root} { + border: 0; + + &:not(:last-child) { + border-bottom: 1px solid rgb(var(--lsd-border-primary)); + } + } + } + + .${dropdownClasses.small} { + .${dropdownClasses.trigger} { + padding: 6px 10px; + } + } + + .${dropdownClasses.medium} { + .${dropdownClasses.trigger} { + padding: 6px 12px; + } + } +` diff --git a/packages/lsd-react/src/components/Dropdown/Dropdown.tsx b/packages/lsd-react/src/components/Dropdown/Dropdown.tsx new file mode 100644 index 0000000..a1f9614 --- /dev/null +++ b/packages/lsd-react/src/components/Dropdown/Dropdown.tsx @@ -0,0 +1,147 @@ +import clsx from 'clsx' +import React, { useEffect, useRef, useState } from 'react' +import { SelectOption, useSelect } from '../../utils/useSelect' +import { DropdownItem } from '../DropdownItem' +import { ArrowDownIcon, ArrowUpIcon, ErrorIcon } from '../Icons' +import { ListBox } from '../ListBox' +import { Portal } from '../PortalProvider/Portal' +import { Typography } from '../Typography' +import { dropdownClasses } from './Dropdown.classes' + +export type DropdownOption = SelectOption + +export type DropdownProps = Omit< + React.HTMLAttributes, + 'label' | 'disabled' | 'value' | 'onChange' +> & { + label: string + error?: boolean + disabled?: boolean + supportingText?: string + size?: 'small' | 'medium' | 'large' + + multi?: boolean + options?: DropdownOption[] + value?: string | string[] + onChange?: (value: string | string[]) => void +} + +export const Dropdown: React.FC & { + classes: typeof dropdownClasses +} = ({ + label, + size = 'large', + error = false, + disabled = false, + supportingText, + + value = [], + onChange, + options = [], + multi = false, + ...props +}) => { + const ref = useRef(null) + const [open, setOpen] = useState(false) + + const { select, isSelected, selected } = useSelect(options, value, { + multi, + onChange, + onDone: () => { + setOpen(false) + }, + }) + + const onTrigger = () => { + !disabled && setOpen((value) => !value) + } + + useEffect(() => { + if (disabled && open) setOpen(false) + }, [open, disabled]) + + return ( +
+ + + {supportingText && ( + + {supportingText} + + )} + + + setOpen(false)} + className={dropdownClasses.listBox} + > + {options.map((opt) => ( + e.key === 'Enter' && select(opt)} + /> + ))} + + +
+ ) +} + +Dropdown.classes = dropdownClasses diff --git a/packages/lsd-react/src/components/Dropdown/index.ts b/packages/lsd-react/src/components/Dropdown/index.ts new file mode 100644 index 0000000..5cda7b9 --- /dev/null +++ b/packages/lsd-react/src/components/Dropdown/index.ts @@ -0,0 +1 @@ +export * from './Dropdown' diff --git a/packages/lsd-react/src/components/DropdownItem/DropdownItem.classes.ts b/packages/lsd-react/src/components/DropdownItem/DropdownItem.classes.ts new file mode 100644 index 0000000..f245bf1 --- /dev/null +++ b/packages/lsd-react/src/components/DropdownItem/DropdownItem.classes.ts @@ -0,0 +1,13 @@ +export const dropdownItemClasses = { + root: `lsd-dropdown-item`, + icon: `lsd-dropdown-item__icon`, + label: `lsd-dropdown-item__label`, + + error: 'lsd-dropdown-item--error', + disabled: 'lsd-dropdown-item--disabled', + selected: 'lsd-dropdown-item--selected', + withIcon: 'lsd-dropdown-item--with-icon', + small: `lsd-dropdown-item--small`, + medium: `lsd-dropdown-item--medium`, + large: `lsd-dropdown-item--large`, +} diff --git a/packages/lsd-react/src/components/DropdownItem/DropdownItem.stories.tsx b/packages/lsd-react/src/components/DropdownItem/DropdownItem.stories.tsx new file mode 100644 index 0000000..c227b5f --- /dev/null +++ b/packages/lsd-react/src/components/DropdownItem/DropdownItem.stories.tsx @@ -0,0 +1,29 @@ +import { Meta, Story } from '@storybook/react' +import { DropdownItem, DropdownItemProps } from './DropdownItem' + +export default { + title: 'DropdownItem', + component: DropdownItem, + argTypes: { + size: { + type: { + name: 'enum', + value: ['small', 'medium', 'large'], + }, + }, + }, +} as Meta + +export const Root: Story = (args) => ( +
+ +
+) + +Root.args = { + label: 'label', + size: 'large', + selected: false, + withIcon: false, + disabled: false, +} diff --git a/packages/lsd-react/src/components/DropdownItem/DropdownItem.styles.ts b/packages/lsd-react/src/components/DropdownItem/DropdownItem.styles.ts new file mode 100644 index 0000000..0852eba --- /dev/null +++ b/packages/lsd-react/src/components/DropdownItem/DropdownItem.styles.ts @@ -0,0 +1,55 @@ +import { css } from '@emotion/react' +import { dropdownItemClasses } from './DropdownItem.classes' + +export const DropdownItemStyles = css` + .${dropdownItemClasses.root} { + width: 100%; + box-sizing: border-box; + + display: flex; + flex-direction: row; + align-items: center; + + padding: 10px 14px; + border: 1px solid rgb(var(--lsd-border-primary)); + + :not(.${dropdownItemClasses.disabled}) { + cursor: pointer; + + &:hover, + &:focus { + outline: none; + .${dropdownItemClasses.label} { + text-decoration: underline; + } + } + } + } + + .${dropdownItemClasses.label} { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .${dropdownItemClasses.disabled} { + opacity: 0.34; + } + + .${dropdownItemClasses.icon} { + margin-right: 18px; + } + + .${dropdownItemClasses.small} { + padding: 6px 10px; + } + + .${dropdownItemClasses.medium} { + padding: 6px 12px; + } + + .${dropdownItemClasses.large} { + padding: 10px 14px; + } +` diff --git a/packages/lsd-react/src/components/DropdownItem/DropdownItem.tsx b/packages/lsd-react/src/components/DropdownItem/DropdownItem.tsx new file mode 100644 index 0000000..2a78c13 --- /dev/null +++ b/packages/lsd-react/src/components/DropdownItem/DropdownItem.tsx @@ -0,0 +1,62 @@ +import clsx from 'clsx' +import React from 'react' +import { CheckboxFilledIcon, CheckboxIcon, LsdIconProps } from '../Icons' +import { Typography } from '../Typography' +import { dropdownItemClasses } from './DropdownItem.classes' + +export type DropdownItemProps = React.HTMLAttributes & { + label: string + selected?: boolean + withIcon?: boolean + disabled?: boolean + size: 'small' | 'medium' | 'large' +} + +export const DropdownItem: React.FC & { + classes: typeof dropdownItemClasses +} = ({ + label, + size = 'large', + withIcon, + selected, + disabled, + className, + ...props +}) => { + const iconProps: LsdIconProps = { + color: 'primary', + className: dropdownItemClasses.icon, + } + + return ( +
+ {withIcon && + (selected ? ( + + ) : ( + + ))} + + {label} + +
+ ) +} + +DropdownItem.classes = dropdownItemClasses diff --git a/packages/lsd-react/src/components/DropdownItem/index.ts b/packages/lsd-react/src/components/DropdownItem/index.ts new file mode 100644 index 0000000..f30de1e --- /dev/null +++ b/packages/lsd-react/src/components/DropdownItem/index.ts @@ -0,0 +1 @@ +export * from './DropdownItem' diff --git a/packages/lsd-react/src/components/ListBox/ListBox.classes.ts b/packages/lsd-react/src/components/ListBox/ListBox.classes.ts new file mode 100644 index 0000000..f851330 --- /dev/null +++ b/packages/lsd-react/src/components/ListBox/ListBox.classes.ts @@ -0,0 +1,6 @@ +export const listBoxClasses = { + root: `lsd-list-box`, + list: 'lsd-list-box-list', + + open: 'lsd-list-box--open', +} diff --git a/packages/lsd-react/src/components/ListBox/ListBox.styles.ts b/packages/lsd-react/src/components/ListBox/ListBox.styles.ts new file mode 100644 index 0000000..87a7f30 --- /dev/null +++ b/packages/lsd-react/src/components/ListBox/ListBox.styles.ts @@ -0,0 +1,21 @@ +import { css } from '@emotion/react' +import { listBoxClasses } from './ListBox.classes' + +export const ListBoxStyles = css` + .${listBoxClasses.root} { + position: absolute; + top: 0; + left: 0; + opacity: 0; + visibility: hidden; + margin: 0; + padding: 0; + box-sizing: border-box; + background: rgb(var(--lsd-surface-primary)); + } + + .${listBoxClasses.open} { + opacity: 1; + visibility: visible; + } +` diff --git a/packages/lsd-react/src/components/ListBox/ListBox.tsx b/packages/lsd-react/src/components/ListBox/ListBox.tsx new file mode 100644 index 0000000..9228ec2 --- /dev/null +++ b/packages/lsd-react/src/components/ListBox/ListBox.tsx @@ -0,0 +1,61 @@ +import clsx from 'clsx' +import React, { useEffect, useRef, useState } from 'react' +import { useClickAway } from 'react-use' +import { listBoxClasses } from './ListBox.classes' + +export type ListBoxProps = Omit< + React.HTMLAttributes, + 'label' +> & { + open?: boolean + label?: string + onClose?: () => void + handleRef: React.RefObject +} + +export const ListBox: React.FC & { + classes: typeof listBoxClasses +} = ({ open, label, handleRef, onClose, children, ...props }) => { + const ref = useRef(null) + const [style, setStyle] = useState({}) + + useClickAway(ref, (event) => { + if (!open || event.composedPath().includes(handleRef.current!)) return + + onClose && onClose() + }) + + const updateStyle = () => { + const { width, height, top, left } = + handleRef.current!.getBoundingClientRect() + + setStyle({ + left, + width, + top: top + height, + }) + } + + useEffect(() => { + updateStyle() + }, [open]) + + return ( +
    + {children} +
+ ) +} + +ListBox.classes = listBoxClasses diff --git a/packages/lsd-react/src/components/ListBox/index.ts b/packages/lsd-react/src/components/ListBox/index.ts new file mode 100644 index 0000000..8469f16 --- /dev/null +++ b/packages/lsd-react/src/components/ListBox/index.ts @@ -0,0 +1 @@ +export * from './ListBox' diff --git a/packages/lsd-react/src/components/PortalProvider/Portal.tsx b/packages/lsd-react/src/components/PortalProvider/Portal.tsx new file mode 100644 index 0000000..c4947cb --- /dev/null +++ b/packages/lsd-react/src/components/PortalProvider/Portal.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import { createPortal } from 'react-dom' +import { useCanUsePortal } from './PortalContext' +import { usePortal } from './usePortal' + +export type PortalProps = React.PropsWithChildren & { + id: string +} + +export const Portal: React.FC = ({ id, children }) => { + const canUse = useCanUsePortal() + if (!canUse) return <> + + return {children} +} + +const PortalContent: React.FC = ({ id, children }) => { + const element = usePortal({ parentId: 'lsd-presentation' }) + + if (!element) return <> + + return createPortal(children, element, id) +} diff --git a/packages/lsd-react/src/components/PortalProvider/PortalContext.ts b/packages/lsd-react/src/components/PortalProvider/PortalContext.ts new file mode 100644 index 0000000..301eab4 --- /dev/null +++ b/packages/lsd-react/src/components/PortalProvider/PortalContext.ts @@ -0,0 +1,12 @@ +import React, { useContext } from 'react' + +export type PortalContextType = { + initialized?: boolean +} + +export const PortalContext = React.createContext({ + initialized: false, +}) + +export const useCanUsePortal = (): boolean => + useContext(PortalContext)?.initialized ?? false diff --git a/packages/lsd-react/src/components/PortalProvider/PortalProvider.tsx b/packages/lsd-react/src/components/PortalProvider/PortalProvider.tsx new file mode 100644 index 0000000..5bf0ef4 --- /dev/null +++ b/packages/lsd-react/src/components/PortalProvider/PortalProvider.tsx @@ -0,0 +1,29 @@ +import React, { useEffect, useState } from 'react' +import { PortalContext } from './PortalContext' + +export type PortalProviderProps = React.PropsWithChildren + +export const PortalProvider: React.FC = ({ children }) => { + const [initialized, setInitialized] = useState(false) + + useEffect(() => { + if (typeof window === 'undefined') return + + const body = document.querySelector('body')! + const container = document.createElement('div') + container.id = 'lsd-presentation' + body.appendChild(container) + + setInitialized(true) + + return () => { + body.removeChild(container) + } + }, []) + + return ( + + {children} + + ) +} diff --git a/packages/lsd-react/src/components/PortalProvider/index.ts b/packages/lsd-react/src/components/PortalProvider/index.ts new file mode 100644 index 0000000..2cc8363 --- /dev/null +++ b/packages/lsd-react/src/components/PortalProvider/index.ts @@ -0,0 +1 @@ +export * from './PortalProvider' diff --git a/packages/lsd-react/src/components/PortalProvider/usePortal.ts b/packages/lsd-react/src/components/PortalProvider/usePortal.ts new file mode 100644 index 0000000..7a5e846 --- /dev/null +++ b/packages/lsd-react/src/components/PortalProvider/usePortal.ts @@ -0,0 +1,28 @@ +import { useEffect, useRef } from 'react' + +interface Props { + parentId: string +} + +export const usePortal = ({ parentId }: Props) => { + const elementRef = useRef() + + if (typeof window !== 'undefined' && !elementRef.current) { + elementRef.current = document.createElement('div') + } + + useEffect(() => { + if (typeof window === 'undefined' || !elementRef.current) return + document.getElementById(parentId)?.appendChild(elementRef.current) + + return () => { + try { + document + .getElementById(parentId) + ?.removeChild(elementRef.current as Node) + } catch (error) {} + } + }, [parentId, elementRef.current]) + + return elementRef.current +} diff --git a/packages/lsd-react/src/components/Theme/ThemeProvider.tsx b/packages/lsd-react/src/components/Theme/ThemeProvider.tsx index a964611..c7da4f4 100644 --- a/packages/lsd-react/src/components/Theme/ThemeProvider.tsx +++ b/packages/lsd-react/src/components/Theme/ThemeProvider.tsx @@ -1,6 +1,7 @@ import { Global, ThemeProvider as EmotionThemeProvider } from '@emotion/react' import React from 'react' import { CSSBaseline } from '../CSSBaseline' +import { PortalProvider } from '../PortalProvider' import { ResizeObserverProvider } from '../ResizeObserver' import { Theme } from './types' @@ -14,11 +15,13 @@ export const ThemeProvider: React.FC = ({ }) => { return ( - - {children} - - - + + + {children} + + + + ) } diff --git a/packages/lsd-react/src/index.ts b/packages/lsd-react/src/index.ts index 8602ce1..d728ac5 100644 --- a/packages/lsd-react/src/index.ts +++ b/packages/lsd-react/src/index.ts @@ -1,5 +1,8 @@ export * from './components/Button' +export * from './components/Dropdown' +export * from './components/DropdownItem' export * from './components/Icons' +export * from './components/ListBox' export * from './components/TabItem' export * from './components/Tabs' export * from './components/Theme' diff --git a/packages/lsd-react/src/utils/useSelect.ts b/packages/lsd-react/src/utils/useSelect.ts new file mode 100644 index 0000000..ca22e48 --- /dev/null +++ b/packages/lsd-react/src/utils/useSelect.ts @@ -0,0 +1,60 @@ +import { useEffect, useMemo, useState } from 'react' +import { pairs } from './object.utils' + +export type SelectOption = { + name: string + value: string +} + +export const useSelect = ( + options: SelectOption[], + value: string | string[], + { + onDone, + onChange, + multi = false, + }: { + multi?: boolean + onDone?: (value: string | string[]) => void + onChange?: (value: string | string[]) => void + } = {}, +) => { + const dict = useMemo( + () => Object.fromEntries(options.map((opt) => [opt.value, opt])), + [options], + ) + + const [val, setVal] = useState( + Array.isArray(value) ? value : value ? [value] : [], + ) + + const selection = useMemo(() => pairs(val, () => true), [val]) + const selected = useMemo(() => val.map((value) => dict[value]), [val, dict]) + + useEffect(() => { + if (onChange) setVal(Array.isArray(value) ? value : value ? [value] : []) + }, [value, onChange]) + + const getKey = (option: string | SelectOption) => + typeof option === 'string' ? option : option.value + + const isSelected = (option: string | SelectOption) => + !!selection[getKey(option)] + + const doSelect = (option: string | SelectOption) => { + const key = getKey(option) + const newVal = multi + ? selection[key] + ? val.filter((i) => i !== key) + : [...val, key] + : [key] + onChange ? onChange(multi ? newVal : newVal[0]) : setVal(newVal) + if (!multi && onDone) onDone(multi ? newVal : newVal[0]) + } + + return { + selected, + isSelected, + select: doSelect, + } +} diff --git a/yarn.lock b/yarn.lock index c453084..310310d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1105,7 +1105,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": version "7.20.13" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz" integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== @@ -4257,6 +4257,11 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/js-cookie@^2.2.6": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": version "7.0.11" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" @@ -4790,6 +4795,11 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" +"@xobotyi/scrollbar-width@^1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" @@ -6715,6 +6725,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== +copy-to-clipboard@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + core-js-compat@^3.25.1, core-js-compat@^3.8.1: version "3.27.2" resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz" @@ -6872,6 +6889,13 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +css-in-js-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" + integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== + dependencies: + hyphenate-style-name "^1.0.3" + css-loader@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz" @@ -6918,6 +6942,14 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + css-what@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" @@ -6928,7 +6960,7 @@ cssesc@^3.0.0: resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -csstype@^3.0.2: +csstype@^3.0.2, csstype@^3.0.6: version "3.1.1" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== @@ -8205,6 +8237,21 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-loops@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75" + integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== + +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + +fastest-stable-stringify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" + integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== + fastq@^1.6.0: version "1.15.0" resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" @@ -9417,6 +9464,11 @@ husky@^8.0.3: resolved "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== +hyphenate-style-name@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -9564,6 +9616,14 @@ inline-style-parser@0.1.1: resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +inline-style-prefixer@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz#4290ed453ab0e4441583284ad86e41ad88384f44" + integrity sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg== + dependencies: + css-in-js-utils "^3.1.0" + fast-loops "^1.1.3" + inquirer@8.2.5, inquirer@^8.2.4: version "8.2.5" resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz" @@ -10229,6 +10289,11 @@ jju@~1.4.0: resolved "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-sdsl@^4.1.4: version "4.3.0" resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz" @@ -11033,6 +11098,11 @@ mdast-util-to-string@^1.0.0: resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + mdurl@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" @@ -11453,6 +11523,20 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nano-css@^5.3.1: + version "5.3.5" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.5.tgz#3075ea29ffdeb0c7cb6d25edb21d8f7fa8e8fe8e" + integrity sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg== + dependencies: + css-tree "^1.1.2" + csstype "^3.0.6" + fastest-stable-stringify "^2.0.2" + inline-style-prefixer "^6.0.0" + rtl-css-js "^1.14.0" + sourcemap-codec "^1.4.8" + stacktrace-js "^2.0.2" + stylis "^4.0.6" + nanoid@^3.3.1, nanoid@^3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" @@ -13130,6 +13214,31 @@ react-refresh@^0.14.0: resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz" integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^17.4.0: + version "17.4.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.4.0.tgz#cefef258b0a6c534a5c8021c2528ac6e1a4cdc6d" + integrity sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.3.1" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + react@18.2.0, react@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" @@ -13481,6 +13590,11 @@ require-from-string@^2.0.2: resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-alpn@^1.0.0: version "1.2.1" resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" @@ -13625,6 +13739,13 @@ rsvp@^4.8.4: resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +rtl-css-js@^1.14.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.1.tgz#4b48b4354b0ff917a30488d95100fbf7219a3e80" + integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg== + dependencies: + "@babel/runtime" "^7.1.2" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" @@ -13754,6 +13875,11 @@ schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" +screenfull@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" @@ -13844,6 +13970,11 @@ set-blocking@^2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" @@ -14093,6 +14224,11 @@ source-map-url@^0.4.0: resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== + source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" @@ -14196,11 +14332,35 @@ stable@^0.1.8: resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-generator@^2.0.5: + version "2.0.10" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" + integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== + dependencies: + stackframe "^1.3.4" + stackframe@^1.3.4: version "1.3.4" resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== +stacktrace-gps@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" + integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== + dependencies: + source-map "0.5.6" + stackframe "^1.3.4" + +stacktrace-js@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + state-toggle@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz" @@ -14482,7 +14642,7 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" -stylis@4.1.3: +stylis@4.1.3, stylis@^4.0.6: version "4.1.3" resolved "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz" integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== @@ -14676,6 +14836,11 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +throttle-debounce@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" + integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== + through2@^2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" @@ -14772,6 +14937,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" @@ -14822,6 +14992,11 @@ ts-dedent@^2.0.0, ts-dedent@^2.2.0: resolved "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + ts-morph@17.0.1: version "17.0.1" resolved "https://registry.npmjs.org/ts-morph/-/ts-morph-17.0.1.tgz"