This commit is contained in:
Sanster 2022-04-12 20:58:57 +08:00
parent e570e85e64
commit aa411c7524
25 changed files with 769 additions and 22 deletions

View File

@ -17,6 +17,7 @@
"project": "./tsconfig.json"
},
"rules": {
"react/jsx-props-no-spreading": 0,
"import/no-unresolved": 0,
"react/jsx-no-bind": "off",
"react/jsx-filename-extension": [

View File

@ -5,6 +5,7 @@
"proxy": "http://localhost:8080",
"dependencies": {
"@heroicons/react": "^1.0.4",
"@radix-ui/react-switch": "^0.1.5",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",

View File

@ -23,3 +23,11 @@ header {
gap: 12px;
justify-self: end;
}
.header-icons {
display: flex;
justify-content: center;
align-items: center;
gap: 6px;
justify-self: end;
}

View File

@ -6,6 +6,7 @@ import Button from '../shared/Button'
import Shortcuts from '../Shortcuts/Shortcuts'
import useResolution from '../../hooks/useResolution'
import { ThemeChanger } from './ThemeChanger'
import SettingIcon from '../Setting/SettingIcon'
const Header = () => {
const [file, setFile] = useRecoilState(fileState)
@ -26,7 +27,11 @@ const Header = () => {
</Button>
</div>
<div className="header-icons-wrapper">
<div style={{ visibility: file ? 'visible' : 'hidden' }}>
<div
className="header-icons"
style={{ visibility: file ? 'visible' : 'hidden' }}
>
<SettingIcon />
<Shortcuts />
</div>
<ThemeChanger />

View File

@ -0,0 +1,137 @@
import React, { ReactNode } from 'react'
import { useRecoilState } from 'recoil'
import { settingState } from '../../store/Atoms'
import NumberInput from '../shared/NumberInput'
import Selector from '../shared/Selector'
import SettingBlock from './SettingBlock'
export enum HDStrategy {
ORIGINAL = 'Original',
REISIZE = 'Resize',
CROP = 'Crop',
}
interface PixelSizeInputProps {
title: string
value: string
onValue: (val: string) => void
}
function PixelSizeInputSetting(props: PixelSizeInputProps) {
const { title, value, onValue } = props
return (
<SettingBlock
title={title}
input={
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '8px',
}}
>
<NumberInput
style={{ width: '80px' }}
value={`${value}`}
onValue={onValue}
/>
<span>pixel</span>
</div>
}
/>
)
}
function HDSettingBlock() {
const [setting, setSettingState] = useRecoilState(settingState)
const onStrategyChange = (value: HDStrategy) => {
setSettingState(old => {
return { ...old, hdStrategy: value }
})
}
const onResizeLimitChange = (value: string) => {
setSettingState(old => {
return { ...old, hdStrategyResizeLimit: value }
})
}
const onCropTriggerSizeChange = (value: string) => {
setSettingState(old => {
return { ...old, hdStrategyCropTrigerSize: value }
})
}
const renderOriginalOptionDesc = () => {
return (
<div>
Use the original resolution of the picture, suitable for picture size
below 2K, of course you can try it on higher resolution pictures
</div>
)
}
const renderResizeOptionDesc = () => {
return (
<div>
<div>
Resize the longer side of the image to a specific size(keep ratio),
then do inpainting on the entire resized image.
</div>
<PixelSizeInputSetting
title="Size limit"
value={`${setting.hdStrategyResizeLimit}`}
onValue={onResizeLimitChange}
/>
</div>
)
}
const renderCropOptionDesc = () => {
return (
<div>
<div>
Crop masking area from the original image to do inpainting, and paste
the result back. Mainly for performance and memory reasons on high
resolution image.
</div>
<PixelSizeInputSetting
title="Trigger size"
value={`${setting.hdStrategyCropTrigerSize}`}
onValue={onCropTriggerSizeChange}
/>
</div>
)
}
const renderHDStrategyOptionDesc = (): ReactNode => {
switch (setting.hdStrategy) {
case HDStrategy.ORIGINAL:
return renderOriginalOptionDesc()
case HDStrategy.CROP:
return renderCropOptionDesc()
case HDStrategy.REISIZE:
return renderResizeOptionDesc()
default:
return renderOriginalOptionDesc()
}
}
return (
<SettingBlock
title="High Resolution Strategy"
input={
<Selector
options={Object.values(HDStrategy)}
onChange={val => onStrategyChange(val as HDStrategy)}
/>
}
optionDesc={renderHDStrategyOptionDesc()}
/>
)
}
export default HDSettingBlock

View File

@ -0,0 +1,19 @@
import React, { ReactNode } from 'react'
import { useRecoilState } from 'recoil'
import { Switch, SwitchThumb } from '../shared/Switch'
import SettingBlock from './SettingBlock'
function SavePathSettingBlock() {
return (
<SettingBlock
title="Download image at same folder of origin image"
input={
<Switch defaultChecked>
<SwitchThumb />
</Switch>
}
/>
)
}
export default SavePathSettingBlock

View File

@ -0,0 +1,19 @@
@use '../../styles/Mixins/' as *;
@import './SettingBlock.scss';
.modal-setting {
grid-area: main-content;
background-color: var(--modal-bg);
color: var(--modal-text-color);
box-shadow: 0px 0px 20px rgb(0, 0, 40, 0.2);
min-height: 450px;
width: 700px;
@include mobile {
display: grid;
width: 100%;
height: auto;
margin-top: -11rem;
animation: slideDown 0.2s ease-out;
}
}

View File

@ -0,0 +1,31 @@
.setting-block {
display: flex;
flex-direction: column;
margin-top: 12px;
.option-desc {
margin-top: 12px;
border: 1px solid var(--border-color);
border-radius: 0.3rem;
padding: 2rem;
}
}
.setting-block-content {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12rem;
}
.setting-block-content-title {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.setting-block-desc {
font-size: 1rem;
margin-top: 8px;
color: var(--text-color-gray);
}

View File

@ -0,0 +1,27 @@
import React, { ReactNode } from 'react'
interface SettingBlockProps {
title: string
desc?: string
input: ReactNode
optionDesc?: ReactNode
className?: string
}
function SettingBlock(props: SettingBlockProps) {
const { title, desc, input, optionDesc, className } = props
return (
<div className={`setting-block ${className}`}>
<div className="setting-block-content">
<div className="setting-block-content-title">
<span>{title}</span>
{desc && <span className="setting-block-desc">{desc}</span>}
</div>
{input}
</div>
{optionDesc && <div className="option-desc">{optionDesc}</div>}
</div>
)
}
export default SettingBlock

View File

@ -0,0 +1,46 @@
import React from 'react'
import { useRecoilState } from 'recoil'
import { settingState } from '../../store/Atoms'
import Button from '../shared/Button'
const SettingIcon = () => {
const [setting, setSettingState] = useRecoilState(settingState)
const onClick = () => {
setSettingState({ ...setting, show: !setting.show })
}
return (
<div>
<Button
onClick={onClick}
style={{ border: 0 }}
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
role="img"
width="28"
height="28"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
}
/>
</div>
)
}
export default SettingIcon

View File

@ -0,0 +1,29 @@
import React from 'react'
import { useRecoilState } from 'recoil'
import { settingState } from '../../store/Atoms'
import Modal from '../shared/Modal'
import HDSettingBlock from './HDSettingBlock'
import SavePathSettingBlock from './SavePathSettingBlock'
export default function SettingModal() {
const [setting, setSettingState] = useRecoilState(settingState)
const onClose = () => {
setSettingState(old => {
return { ...old, show: false }
})
}
return (
<Modal
onClose={onClose}
title="Settings"
className="modal-setting"
show={setting.show}
>
<SavePathSettingBlock />
<HDSettingBlock />
</Modal>
)
}

View File

@ -1,5 +1,5 @@
import React, { ReactNode } from 'react'
import { useSetRecoilState } from 'recoil'
import { useRecoilState } from 'recoil'
import { shortcutsState } from '../../store/Atoms'
import Modal from '../shared/Modal'
@ -19,13 +19,8 @@ function ShortCut(props: Shortcut) {
)
}
interface ShortcutsModalProps {
show: boolean
}
export default function ShortcutsModal(props: ShortcutsModalProps) {
const { show } = props
const setShortcutState = useSetRecoilState(shortcutsState)
export default function ShortcutsModal() {
const [shortcutsShow, setShortcutState] = useRecoilState(shortcutsState)
const shortcutStateHandler = () => {
setShortcutState(false)
@ -36,7 +31,7 @@ export default function ShortcutsModal(props: ShortcutsModalProps) {
onClose={shortcutStateHandler}
title="Hotkeys"
className="modal-shortcuts"
show={show}
show={shortcutsShow}
>
<div className="shortcut-options">
<ShortCut content="Enable multi-stroke mask drawing">

View File

@ -3,17 +3,18 @@ import { useRecoilValue } from 'recoil'
import Editor from './Editor/Editor'
import { shortcutsState } from '../store/Atoms'
import ShortcutsModal from './Shortcuts/ShortcutsModal'
import SettingModal from './Setting/SettingModal'
interface WorkspaceProps {
file: File
}
const Workspace = ({ file }: WorkspaceProps) => {
const shortcutVisbility = useRecoilValue(shortcutsState)
return (
<>
<Editor file={file} />
<ShortcutsModal show={shortcutVisbility} />
<SettingModal />
<ShortcutsModal />
</>
)
}

View File

@ -1,6 +1,6 @@
import { XIcon } from '@heroicons/react/outline'
import React, { ReactNode, useRef } from 'react'
import { useClickAway, useKey } from 'react-use'
import { useClickAway, useKey, useKeyPress, useKeyPressEvent } from 'react-use'
import Button from './Button'
export interface ModalProps {
@ -19,8 +19,8 @@ export default function Modal(props: ModalProps) {
onClose?.()
})
useKey('Escape', onClose, {
event: 'keydown',
useKeyPressEvent('Escape', e => {
onClose?.()
})
return (
@ -30,7 +30,7 @@ export default function Modal(props: ModalProps) {
>
<div ref={ref} className={`modal ${className}`}>
<div className="modal-header">
<h3>{title}</h3>
<h2>{title}</h2>
<Button icon={<XIcon />} onClick={onClose} />
</div>
{children}

View File

@ -0,0 +1,12 @@
.number-input {
all: unset;
flex: 1 0 auto;
border-radius: 0.5rem;
padding: 0.2rem 0.8rem;
line-height: 1;
outline: 1px solid var(--border-color);
&:focus-visible {
outline: 1px solid var(--yellow-accent);
}
}

View File

@ -0,0 +1,31 @@
import React, { FormEvent, InputHTMLAttributes } from 'react'
interface NumberInputProps extends InputHTMLAttributes<HTMLInputElement> {
value: string
onValue?: (val: string) => void
}
const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
(props: NumberInputProps, forwardedRef) => {
const { value, onValue, ...itemProps } = props
const handleOnInput = (evt: FormEvent<HTMLInputElement>) => {
const target = evt.target as HTMLInputElement
const val = target.value.replace(/\D/g, '')
onValue?.(val)
}
return (
<input
value={value}
onInput={handleOnInput}
className="number-input"
{...itemProps}
ref={forwardedRef}
type="text"
/>
)
}
)
export default NumberInput

View File

@ -0,0 +1,74 @@
@use '../../styles/Mixins' as *;
.selector {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
.selector-main {
@include accented-display(var(white));
width: 100%;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
outline: none;
gap: 8px;
padding: 0.2rem 0.8rem;
border: 1px solid var(--border-color);
color: var(--options-text-color);
svg {
width: 1rem;
height: 1rem;
margin-top: 0.25rem;
}
}
.selector-options {
@include accented-display(var(--btn-primary-bg));
width: 100%;
padding: 0;
display: grid;
justify-self: center;
position: absolute;
cursor: pointer;
top: 3rem;
color: var(--options-text-color);
background-color: var(--page-bg);
border: 1px solid var(--border-color);
border-radius: 0.6rem;
@include mobile {
bottom: 11.5rem;
}
.selector-option {
display: flex;
align-items: center;
user-select: none;
padding: 0.5rem 0.8rem;
&:first-of-type {
border-top-right-radius: 0.5rem;
border-top-left-radius: 0.5rem;
}
&:last-of-type {
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
}
&:hover {
background-color: var(--yellow-accent);
color: var(--btn-text-hover-color);
}
}
}

View File

@ -0,0 +1,87 @@
import React, { MutableRefObject, useCallback, useRef, useState } from 'react'
import { useClickAway, useKeyPressEvent } from 'react-use'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/outline'
type SelectorChevronDirection = 'up' | 'down'
type SelectorProps = {
minWidth?: number
chevronDirection?: SelectorChevronDirection
options: string[]
onChange: (value: string) => void
}
const selectorDefaultProps = {
minWidth: 128,
chevronDirection: 'down',
}
function Selector(props: SelectorProps) {
const { minWidth, chevronDirection, options, onChange } = props
const [showOptions, setShowOptions] = useState<boolean>(false)
const [index, setIndex] = useState<number>(0)
const selectorRef = useRef<HTMLDivElement | null>(null)
const showOptionsHandler = () => {
// console.log(selectorRef.current?.focus)
// selectorRef?.current?.focus()
setShowOptions(currentShowOptionsState => !currentShowOptionsState)
}
useClickAway(selectorRef, () => {
setShowOptions(false)
})
// TODO: how to prevent Modal close?
// useKeyPressEvent('Escape', (e: KeyboardEvent) => {
// if (showOptions === true) {
// console.log(`selector ${e}`)
// e.preventDefault()
// e.stopPropagation()
// setShowOptions(false)
// }
// })
const onOptionClick = (e: any, newIndex: number) => {
const currentRes = e.target.textContent.split('x')
onChange(currentRes[0])
setShowOptions(false)
setIndex(newIndex)
}
return (
<div className="selector" ref={selectorRef} style={{ minWidth }}>
<div
className="selector-main"
role="button"
onClick={showOptionsHandler}
aria-hidden="true"
>
<p>{options[index]}</p>
<div className="selector-icon">
{chevronDirection === 'up' ? <ChevronUpIcon /> : <ChevronDownIcon />}
</div>
</div>
{showOptions && (
<div className="selector-options">
{options.map((val, _index) => (
<div
className="selector-option"
role="button"
tabIndex={0}
key={val}
onClick={e => onOptionClick(e, _index)}
aria-hidden="true"
>
{val}
</div>
))}
</div>
)}
</div>
)
}
Selector.defaultProps = selectorDefaultProps
export default Selector

View File

@ -0,0 +1,36 @@
.switch-root {
all: 'unset';
width: 42px;
height: 25px;
background-color: var(--switch-root-background-color);
border-radius: 9999px;
border: none;
position: relative;
transition: background-color 100ms;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
&:focus-visible {
outline: none;
}
}
.switch-root[data-state='checked'] {
background-color: var(--yellow-accent);
}
.switch-thumb {
display: block;
width: 17px;
height: 17px;
background-color: var(--switch-thumb-color);
border-radius: 9999px;
transition: transform 100ms;
transform: translateX(4px);
will-change: transform;
}
.switch-thumb[data-state='checked'] {
transform: translateX(21px);
background-color: var(--switch-thumb-checked-color);
outline: 1px solid rgb(100, 100, 120, 0.5);
}

View File

@ -0,0 +1,34 @@
import React from 'react'
import * as SwitchPrimitive from '@radix-ui/react-switch'
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitive.Root>,
React.ComponentProps<typeof SwitchPrimitive.Root>
>((props, forwardedRef) => {
const { className, ...itemProps } = props
return (
<SwitchPrimitive.Root
{...itemProps}
ref={forwardedRef}
className={`switch-root ${className}`}
/>
)
})
const SwitchThumb = React.forwardRef<
React.ElementRef<typeof SwitchPrimitive.Thumb>,
React.ComponentProps<typeof SwitchPrimitive.Thumb>
>((props, forwardedRef) => {
const { className, ...itemProps } = props
return (
<SwitchPrimitive.Thumb
{...itemProps}
ref={forwardedRef}
className={`switch-thumb ${className}`}
/>
)
})
export { Switch, SwitchThumb }

View File

@ -1,4 +1,5 @@
import { atom } from 'recoil'
import { HDStrategy } from '../components/Setting/HDSettingBlock'
export const fileState = atom<File | undefined>({
key: 'fileState',
@ -9,3 +10,20 @@ export const shortcutsState = atom<boolean>({
key: 'shortcutsState',
default: false,
})
export interface Setting {
show: boolean
hdStrategy: HDStrategy
hdStrategyResizeLimit: string
hdStrategyCropTrigerSize: string
}
export const settingState = atom<Setting>({
key: 'settingsState',
default: {
show: false,
hdStrategy: HDStrategy.ORIGINAL,
hdStrategyResizeLimit: '2048',
hdStrategyCropTrigerSize: '2048',
},
})

View File

@ -19,11 +19,20 @@
--modal-hotkey-border-color: rgb(0, 0, 0);
--model-mask-bg: rgba(209, 213, 219, 0.4);
// Text
--text-color: #040404;
--text-color-gray: rgb(107, 111, 118);
// Shared
--btn-primary-bg: rgb(210, 210, 220);
--btn-text-color: black;
--btn-text-hover-color: black;
--btn-text-color: var(--text-color);
--btn-text-hover-color: #040404;
--btn-border-color: rgb(100, 100, 120);
--btn-primary-hover-bg: var(--yellow-accent);
--animation-pulsing-bg: rgb(255, 255, 255, 0.5);
// switch
--switch-root-background-color: rgb(223, 225, 228);
--switch-thumb-color: var(--page-bg);
--switch-thumb-checked-color: var(--page-bg);
}

View File

@ -3,7 +3,7 @@
// Theme
--page-bg: #040404;
--page-text-color: #F9F9F9;
--page-text-color: #f9f9f9;
--yellow-accent: #ffcc00;
--link-color: var(--yellow-accent);
--border-color: rgb(100, 100, 120);
@ -17,14 +17,23 @@
--modal-bg: var(--page-bg);
--modal-text-color: var(--page-text-color);
// --modal-hotkey-bg: rgb(60, 60, 90);
--modal-hotkey-border-color: var(--page-text-color);;
--modal-hotkey-border-color: var(--page-text-color);
--model-mask-bg: rgba(76, 76, 87, 0.4);
// Text
--text-color: white;
--text-color-gray: rgb(138, 143, 152);
// Shared
--btn-primary-bg: rgb(140, 140, 180);
--btn-text-color: white;
--btn-text-color: var(--text-color);
--btn-text-hover-color: var(--page-bg);
--btn-border-color: var(--yellow-accent);
--btn-primary-hover-bg: var(--yellow-accent);
--animation-pulsing-bg: rgb(240, 240, 255);
// switch
--switch-root-background-color: rgb(60, 63, 68);
--switch-thumb-color: rgb(31, 32, 35);
--switch-thumb-checked-color: white;
}

View File

@ -11,11 +11,15 @@
@use '../components/Header/Header';
@use '../components/Header/ThemeChanger';
@use '../components/Shortcuts/Shortcuts';
@use '../components/Setting/Setting.scss';
// Shared
@use '../components/FileSelect/FileSelect';
@use '../components/shared/Button';
@use '../components/shared/Modal';
@use '../components/shared/Selector';
@use '../components/shared/Switch';
@use '../components/shared/NumberInput';
// Main CSS
*,

View File

@ -1164,6 +1164,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.13.10":
version "7.17.9"
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.10.4", "@babel/template@^7.15.4", "@babel/template@^7.3.3":
version "7.15.4"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz"
@ -1537,6 +1544,113 @@
schema-utils "^2.6.5"
source-map "^0.7.3"
"@radix-ui/primitive@0.1.0":
version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-0.1.0.tgz#6206b97d379994f0d1929809db035733b337e543"
integrity sha512-tqxZKybwN5Fa3VzZry4G6mXAAb9aAqKmPtnVbZpL0vsBwvOHTBwsjHVPXylocYLwEtBY9SCe665bYnNB515uoA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs@0.1.0":
version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz#cff6e780a0f73778b976acff2c2a5b6551caab95"
integrity sha512-eyclbh+b77k+69Dk72q3694OHrn9B3QsoIRx7ywX341U9RK1ThgQjMFZoPtmZNQTksXHLNEiefR8hGVeFyInGg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-context@0.1.1":
version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-0.1.1.tgz#06996829ea124d9a1bc1dbe3e51f33588fab0875"
integrity sha512-PkyVX1JsLBioeu0jB9WvRpDBBLtLZohVDT3BB5CTSJqActma8S8030P57mWZb4baZifMvN7KKWPAA40UmWKkQg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-id@0.1.5":
version "0.1.5"
resolved "https://registry.npmmirror.com/@radix-ui/react-id/-/react-id-0.1.5.tgz#010d311bedd5a2884c1e9bb6aaaa4e6cc1d1d3b8"
integrity sha512-IPc4H/63bes0IZ1GJJozSEkSWcDyhNGtKFWUpJ+XtaLyQ1X3x7Mf6fWwWhDcpqlYEP+5WtAvfqcyEsyjP+ZhBQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "0.1.0"
"@radix-ui/react-label@0.1.5":
version "0.1.5"
resolved "https://registry.npmmirror.com/@radix-ui/react-label/-/react-label-0.1.5.tgz#12cd965bfc983e0148121d4c99fb8e27a917c45c"
integrity sha512-Au9+n4/DhvjR0IHhvZ1LPdx/OW+3CGDie30ZyCkbSHIuLp4/CV4oPPGBwJ1vY99Jog3zyQhsGww9MXj8O9Aj/A==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-id" "0.1.5"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-primitive@0.1.4":
version "0.1.4"
resolved "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz#6c233cf08b0cb87fecd107e9efecb3f21861edc1"
integrity sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-slot" "0.1.2"
"@radix-ui/react-slot@0.1.2":
version "0.1.2"
resolved "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-0.1.2.tgz#e6f7ad9caa8ce81cc8d532c854c56f9b8b6307c8"
integrity sha512-ADkqfL+agEzEguU3yS26jfB50hRrwf7U4VTwAOZEmi/g+ITcBWe12yM46ueS/UCIMI9Py+gFUaAdxgxafFvY2Q==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-switch@^0.1.5":
version "0.1.5"
resolved "https://registry.npmmirror.com/@radix-ui/react-switch/-/react-switch-0.1.5.tgz#071ffa19a17a47fdc5c5e6f371bd5901c9fef2f4"
integrity sha512-ITtslJPK+Yi34iNf7K9LtsPaLD76oRIVzn0E8JpEO5HW8gpRBGb2NNI9mxKtEB30TVqIcdjdL10AmuIfOMwjtg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "0.1.0"
"@radix-ui/react-compose-refs" "0.1.0"
"@radix-ui/react-context" "0.1.1"
"@radix-ui/react-label" "0.1.5"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-use-previous" "0.1.1"
"@radix-ui/react-use-size" "0.1.1"
"@radix-ui/react-use-callback-ref@0.1.0":
version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-0.1.0.tgz#934b6e123330f5b3a6b116460e6662cbc663493f"
integrity sha512-Va041McOFFl+aV+sejvl0BS2aeHx86ND9X/rVFmEFQKTXCp6xgUK0NGUAGcgBlIjnJSbMYPGEk1xKSSlVcN2Aw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-controllable-state@0.1.0":
version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-0.1.0.tgz#4fced164acfc69a4e34fb9d193afdab973a55de1"
integrity sha512-zv7CX/PgsRl46a52Tl45TwqwVJdmqnlQEQhaYMz/yBOD2sx2gCkCFSoF/z9mpnYWmS6DTLNTg5lIps3fV6EnXg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "0.1.0"
"@radix-ui/react-use-layout-effect@0.1.0":
version "0.1.0"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz#ebf71bd6d2825de8f1fbb984abf2293823f0f223"
integrity sha512-+wdeS51Y+E1q1Wmd+1xSSbesZkpVj4jsg0BojCbopWvgq5iBvixw5vgemscdh58ep98BwUbsFYnrywFhV9yrVg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-previous@0.1.1":
version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-previous/-/react-use-previous-0.1.1.tgz#0226017f72267200f6e832a7103760e96a6db5d0"
integrity sha512-O/ZgrDBr11dR8rhO59ED8s5zIXBRFi8MiS+CmFGfi7MJYdLbfqVOmQU90Ghf87aifEgWe6380LA69KBneaShAg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-size@0.1.1":
version "0.1.1"
resolved "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-0.1.1.tgz#f6b75272a5d41c3089ca78c8a2e48e5f204ef90f"
integrity sha512-pTgWM5qKBu6C7kfKxrKPoBI2zZYZmp2cSXzpUiGM3qEBQlMLtYhaY2JXdXUCxz+XmD1YEjc8oRwvyfsD4AG4WA==
dependencies:
"@babel/runtime" "^7.13.10"
"@rollup/plugin-node-resolve@^7.1.1":
version "7.1.3"
resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz"