Merge pull request #416 from marcelines/feat/table-filters

[website]: Table filters
This commit is contained in:
marcelines 2023-06-27 14:08:48 +01:00 committed by GitHub
commit 2b6ef64fdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1373 additions and 162 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,2 +1,4 @@
export { Breadcrumbs } from './breadcrumbs/breadcrumbs'
export { EpicOverview } from './epic-overview'
export { SideBar } from './side-bar/side-bar'
export { TableIssues } from './table-issues/table-issues'

View File

@ -55,7 +55,7 @@ const FloatingMenu = (): JSX.Element => {
}}
className={cx([
'fixed left-1/2 top-1 z-10 flex -translate-x-1/2 flex-col items-center justify-between p-2 pb-0 lg:hidden',
'bg-blur-neutral-80/80 border-neutral-80/5 rounded-2xl border backdrop-blur-md',
'rounded-2xl border border-neutral-80/5 bg-blur-neutral-80/80 backdrop-blur-md',
' w-[calc(100%-24px)]',
' opacity-0 transition-opacity data-[visible=true]:opacity-100',
'z-10',
@ -69,8 +69,8 @@ const FloatingMenu = (): JSX.Element => {
}}
className={cx([
'fixed left-1/2 top-5 z-10 w-max min-w-[746px] -translate-x-1/2 overflow-hidden',
'bg-blur-neutral-80/80 border-neutral-80/5 rounded-2xl border backdrop-blur-md',
'md-lg:block hidden',
'rounded-2xl border border-neutral-80/5 bg-blur-neutral-80/80 backdrop-blur-md',
'hidden md-lg:block',
])}
>
<FloatingDesktop visible={visible} />

View File

@ -1,85 +0,0 @@
import { Avatar, Button, Tag, Text } from '@status-im/components'
import Link from 'next/link'
const issues = [
{
id: 5154,
title: 'Add support for encrypted communities',
status: 'Open',
},
{
id: 5155,
title: 'Add support for encrypted communities',
status: 'Open',
},
{
id: 4,
title: 'Add support for encrypted communities',
status: 'Open',
},
{
id: 4324,
title: 'Add support for encrypted communities',
status: 'Open',
},
{
id: 876,
title: 'Add support for encrypted communities',
status: 'Open',
},
]
export const TableIssues = () => {
return (
<div className="overflow-hidden rounded-2xl border border-neutral-10">
<div className="border-b border-neutral-10 bg-neutral-5 p-3">
<Text size={15} weight="medium">
784 Open
</Text>
</div>
<div className="divide-y divide-neutral-10">
{issues.map(issue => (
<Link
key={issue.id}
href={`https://github.com/status-im/status-react/issues/${issue.id}`}
className="flex items-center justify-between px-4 py-3 transition-colors duration-200 hover:bg-neutral-5"
>
<div className="flex flex-col">
<Text size={15} weight="medium">
{issue.title}
</Text>
<Text size={13} color="$neutral-50">
#9667 Opened 2 days ago by slaedjenic
</Text>
</div>
<div className="flex gap-3">
<div className="flex gap-1">
<Tag size={24} label="E:Syncing" color="$orange-50" />
<Tag size={24} label="E:Wallet" color="$green-50" />
<Tag size={24} label="Feature" color="$pink-50" />
<Tag size={24} label="Web" color="$purple-50" />
</div>
<Tag size={24} label="9435" />
<Avatar
type="user"
size={24}
name="jkbktl"
src="https://images.unsplash.com/photo-1552058544-f2b08422138a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1299&q=80"
/>
</div>
</Link>
))}
</div>
<div className="p-3">
<Button size={40} variant="outline">
Show more 10
</Button>
</div>
</div>
)
}

View File

@ -0,0 +1,48 @@
import { tokens } from '@status-im/components/src/tokens'
import type { ColorTokens } from '@tamagui/core'
// TypeGuard for ColorTokens
function isColorTokens(
value: `#${string}` | ColorTokens
): value is ColorTokens {
return typeof value === 'string' && value.startsWith('$')
}
const ColorCircle = ({
color: colorFromProps,
opacity = 40,
size = 16,
}: {
color: ColorTokens | `#${string}`
opacity?: number
size?: number
}) => {
if (!colorFromProps) {
return null
}
let color: ColorTokens | string = colorFromProps
if (isColorTokens(colorFromProps)) {
const colorToken = colorFromProps.replace(
'$',
''
) as keyof typeof tokens.color
color = tokens.color[colorToken]?.val || colorFromProps
}
return (
<div
className="rounded-full"
style={{
width: size,
height: size,
backgroundColor: `color-mix(in srgb, ${color} ${opacity}%, transparent)`,
border: `1px solid ${color}`,
}}
/>
)
}
export { ColorCircle }

View File

@ -0,0 +1,140 @@
import { cloneElement, useState } from 'react'
import { Avatar, Button, Input, Text } from '@status-im/components'
import { DropdownMenu } from '@status-im/components/src/dropdown-menu'
import { DropdownIcon, SearchIcon } from '@status-im/icons'
import Image from 'next/image'
import { useCurrentBreakpoint } from '@/hooks/use-current-breakpoint'
import { ColorCircle } from './components/color-circle'
import type { ColorTokens } from '@tamagui/core'
type Data = {
id: number
name: string
avatar?: string | React.ReactElement
color?: ColorTokens | `#${string}`
}
type Props = {
data: Data[]
label: string
placeholder?: string
}
const isAvatar = (value: unknown): value is string => {
return typeof value === 'string' && value !== null
}
const RenderIcon = (props: Data) => {
if (props.color) {
return <ColorCircle color={props.color} />
}
if (!props.avatar) {
return <></>
}
if (isAvatar(props.avatar)) {
return <Avatar src={props.avatar} size={16} name={props.name} type="user" />
}
return cloneElement(props.avatar) || <></>
}
const DropdownFilter = (props: Props) => {
const { data, label, placeholder } = props
const [filterText, setFilterText] = useState('')
// TODO - this will be improved by having a debounced search and use memoization
const filteredData = data.filter(label =>
label.name.toLowerCase().includes(filterText.toLowerCase())
)
const [selectedValues, setSelectedValues] = useState<number[]>([])
const [isOpen, setIsOpen] = useState(false)
const currentBreakpoint = useCurrentBreakpoint()
return (
<div>
<DropdownMenu onOpenChange={() => setIsOpen(!isOpen)}>
<Button
size={32}
variant="outline"
iconAfter={
<div
className={`transition-transform ${
isOpen ? 'rotate-180' : 'rotate-0'
}`}
>
<DropdownIcon size={20} />
</div>
}
>
{label}
</Button>
<DropdownMenu.Content
sideOffset={10}
align={currentBreakpoint === '2xl' ? 'end' : 'start'}
>
<div className="p-2 px-1">
<Input
placeholder={placeholder || 'Search'}
icon={<SearchIcon size={20} />}
size={32}
value={filterText}
onChangeText={setFilterText}
/>
</div>
{filteredData.map(filtered => {
return (
<DropdownMenu.CheckboxItem
key={filtered.id}
icon={<RenderIcon {...filtered} />}
label={filtered.name}
checked={selectedValues.includes(filtered.id)}
onSelect={() => {
if (selectedValues.includes(filtered.id)) {
setSelectedValues(
selectedValues.filter(id => id !== filtered.id)
)
} else {
setSelectedValues([...selectedValues, filtered.id])
}
}}
/>
)
})}
{filteredData.length === 0 && (
<div className="flex flex-col items-center justify-center p-2 py-1">
<Image
className="pb-3 invert"
alt="No results"
src={'/assets/filters/empty.png'}
width={80}
height={80}
/>
<div className="pb-[2px]">
<Text size={15} weight="semibold">
No options found
</Text>
</div>
<div className="text-center">
<Text size={13}>
We didn&apos;t find results that match your search
</Text>
</div>
</div>
)}
</DropdownMenu.Content>
</DropdownMenu>
</div>
)
}
export { DropdownFilter }
export type { Props as DropdownFilterProps }

View File

@ -0,0 +1,72 @@
import { useState } from 'react'
import { IconButton, Text } from '@status-im/components'
import { DropdownMenu } from '@status-im/components/src/dropdown-menu'
import { SortIcon } from '@status-im/icons'
import Image from 'next/image'
type Data = {
id: number
name: string
}
type Props = {
data: Data[]
}
const DropdownSort = (props: Props) => {
const { data } = props
const [selectedValue, setSelectedValue] = useState<number>()
return (
<div>
<DropdownMenu>
<div className="relative">
<IconButton icon={<SortIcon size={20} />} variant="outline" />
</div>
<DropdownMenu.Content sideOffset={10} align="end">
<div className="p-2">
<Text size={13} color="$neutral-80">
Sort by
</Text>
</div>
{data.map(option => {
return (
<DropdownMenu.Item
key={option.id}
label={option.name}
onSelect={() => {
setSelectedValue(option.id)
}}
selected={selectedValue === option.id}
/>
)
})}
{data.length === 0 && (
<div className="flex flex-col items-center justify-center p-2 py-1">
<Image
className="pb-3 invert"
alt="No results"
src={'/assets/filters/empty.png'}
width={80}
height={80}
/>
<div className="pb-[2px]">
<Text size={15} weight="semibold">
No options found
</Text>
</div>
<div className="text-center">
<Text size={13}>We didn&apos;t find any results</Text>
</div>
</div>
)}
</DropdownMenu.Content>
</DropdownMenu>
</div>
)
}
export { DropdownSort }
export type { Props as DropdownSortProps }

View File

@ -0,0 +1,3 @@
export { DropdownFilter } from './dropdown-filter'
export { DropdownSort } from './dropdown-sort'
export { Tabs } from './tabs'

View File

@ -0,0 +1,41 @@
import { useState } from 'react'
import { DoneIcon, OpenIcon } from '@status-im/icons'
const Tabs = (): JSX.Element => {
const [activeTab, setActiveTab] = useState<'open' | 'closed'>('open')
const isOpen = activeTab === 'open'
return (
<div className="flex">
<div className="flex items-center pr-4">
<button
className={`flex cursor-pointer flex-row items-center transition-colors ${
isOpen ? 'text-neutral-100' : 'text-neutral-50'
}`}
onClick={() => setActiveTab('open')}
>
<OpenIcon size={20} color={isOpen ? '$neutral-100' : '$neutral-50'} />
<span className="pl-1 text-[15px]">784 Open</span>
</button>
</div>
<div className="flex items-center pr-3">
<button
className={`flex cursor-pointer flex-row items-center transition-colors ${
!isOpen ? 'text-neutral-100' : 'text-neutral-50'
}`}
onClick={() => setActiveTab('closed')}
>
<DoneIcon
size={20}
color={!isOpen ? '$neutral-100' : '$neutral-50'}
/>
<span className="pl-1 text-[15px]">1012 Closed</span>
</button>
</div>
</div>
)
}
export { Tabs }

View File

@ -0,0 +1 @@
export { TableIssues } from './table-issues'

View File

@ -0,0 +1,324 @@
import { useState } from 'react'
import { Avatar, Button, Input, Tag, Text } from '@status-im/components'
import { ProfileIcon, SearchIcon } from '@status-im/icons'
import Link from 'next/link'
import { useCurrentBreakpoint } from '@/hooks/use-current-breakpoint'
import { DropdownFilter, DropdownSort, Tabs } from './filters'
import type { DropdownFilterProps } from './filters/dropdown-filter'
import type { DropdownSortProps } from './filters/dropdown-sort'
const issues = [
{
id: 5154,
title: 'Add support for encrypted communities',
status: 'open',
},
{
id: 5155,
title: 'Add support for encrypted communities',
status: 'open',
},
{
id: 4,
title: 'Add support for encrypted communities',
status: 'open',
},
{
id: 4324,
title: 'Add support for encrypted communities',
status: 'open',
},
{
id: 134,
title: 'Add support for encrypted communities',
status: 'closed',
},
{
id: 999,
title: 'Add support for encrypted communities',
status: 'closed',
},
{
id: 873,
title: 'Add support for encrypted communities',
status: 'open',
},
{
id: 123,
title: 'Add support for encrypted communities',
status: 'open',
},
]
const authors: DropdownFilterProps['data'] = [
{
id: 4,
name: 'marcelines',
avatar: 'https://avatars.githubusercontent.com/u/29401404?v=4',
},
{
id: 5,
name: 'prichodko',
avatar: 'https://avatars.githubusercontent.com/u/14926950?v=4',
},
{
id: 6,
name: 'felicio',
avatar: 'https://avatars.githubusercontent.com/u/13265126?v=4',
},
{
id: 7,
name: 'jkbktl',
avatar: 'https://avatars.githubusercontent.com/u/520927?v=4',
},
]
const epics: DropdownFilterProps['data'] = [
{
id: 4,
name: 'E:ActivityCenter',
color: '$orange-60',
},
{
id: 5,
name: 'E:Keycard',
color: '$purple-60',
},
{
id: 6,
name: 'E:Wallet',
color: '$pink-60',
},
{
id: 7,
name: 'E:Chat',
color: '$beige-60',
},
]
const labels: DropdownFilterProps['data'] = [
{
id: 4,
name: 'Mobile',
color: '$blue-60',
},
{
id: 5,
name: 'Frontend',
color: '$brown-50',
},
{
id: 6,
name: 'Backend',
color: '$red-60',
},
{
id: 7,
name: 'Desktop',
color: '$green-60',
},
]
const assignees: DropdownFilterProps['data'] = [
{
id: 1,
name: 'Unassigned',
avatar: <ProfileIcon size={16} />,
},
{
id: 4,
name: 'marcelines',
avatar: 'https://avatars.githubusercontent.com/u/29401404?v=4',
},
{
id: 5,
name: 'prichodko',
avatar: 'https://avatars.githubusercontent.com/u/14926950?v=4',
},
{
id: 6,
name: 'felicio',
avatar: 'https://avatars.githubusercontent.com/u/13265126?v=4',
},
{
id: 7,
name: 'jkbktl',
avatar: 'https://avatars.githubusercontent.com/u/520927?v=4',
},
]
const repositories: DropdownFilterProps['data'] = [
{
id: 1,
name: 'status-mobile',
},
{
id: 2,
name: 'status-desktop',
},
{
id: 3,
name: 'status-web',
},
{
id: 4,
name: 'status-go',
},
{
id: 5,
name: 'nwaku',
},
{
id: 6,
name: 'go-waku',
},
{
id: 7,
name: 'js-waku',
},
{
id: 8,
name: 'nimbus-eth2',
},
{
id: 9,
name: 'help.status.im',
},
]
const sortOptions: DropdownSortProps['data'] = [
{
id: 1,
name: 'Default',
},
{
id: 2,
name: 'Alphabetical',
},
{
id: 3,
name: 'Creation date',
},
{
id: 4,
name: 'Updated',
},
{
id: 5,
name: 'Completion',
},
]
const TableIssues = () => {
const [issuesSearchText, setIssuesSearchText] = useState('')
const currentBreakpoint = useCurrentBreakpoint()
return (
<div className="overflow-hidden rounded-2xl border border-neutral-10">
<div className="flex border-b border-neutral-10 bg-neutral-5 p-3">
<div className="flex w-full flex-col 2xl:flex-row 2xl:justify-between">
<Tabs />
<div className="flex-1">
<div className="flex items-center 2xl:justify-end">
<div className="flex w-full justify-between pt-4 2xl:justify-end 2xl:pt-0">
<div className="flex gap-2">
<div className="transition-all">
<Input
placeholder="Find Author"
icon={<SearchIcon size={20} />}
size={32}
value={issuesSearchText}
onChangeText={setIssuesSearchText}
variant="retractable"
direction={currentBreakpoint === '2xl' ? 'rtl' : 'ltr'}
/>
</div>
<DropdownFilter
data={authors}
label="Author"
placeholder="Find author"
/>
<DropdownFilter
data={epics}
label="Epics"
placeholder="Find epic"
/>
<DropdownFilter
data={labels}
label="Labels"
placeholder="Find label"
/>
<DropdownFilter
data={assignees}
label="Assignee"
placeholder="Find assignee"
/>
<DropdownFilter
data={repositories}
label="Repos"
placeholder="Find repo"
/>
</div>
<div className="pl-2">
<DropdownSort data={sortOptions} />
</div>
</div>
</div>
</div>
</div>
</div>
<div className="divide-y divide-neutral-10">
{issues.map(issue => (
<Link
key={issue.id}
href={`https://github.com/status-im/status-react/issues/${issue.id}`}
className="flex items-center justify-between px-4 py-3 transition-colors duration-200 hover:bg-neutral-5"
>
<div className="flex flex-col">
<Text size={15} weight="medium">
{issue.title}
</Text>
<Text size={13} color="$neutral-50">
#9667 Opened 2 days ago by slaedjenic
</Text>
</div>
<div className="flex gap-3">
<div className="flex gap-1">
<Tag size={24} label="E:Syncing" color="$orange-50" />
<Tag size={24} label="E:Wallet" color="$green-50" />
<Tag size={24} label="Feature" color="$pink-50" />
<Tag size={24} label="Web" color="$purple-50" />
</div>
<Tag size={24} label="9435" />
<Avatar
type="user"
size={24}
name="jkbktl"
src="https://images.unsplash.com/photo-1552058544-f2b08422138a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1299&q=80"
/>
</div>
</Link>
))}
</div>
<div className="p-3">
<Button size={40} variant="outline">
Show more 10
</Button>
</div>
</div>
)
}
export { TableIssues }

View File

@ -0,0 +1,46 @@
import { useEffect, useState } from 'react'
import defaultTheme from 'tailwindcss/defaultTheme'
// If we had custom breakpoints, we could use this to get the current breakpoint but we will use the default breakpoints for now
// import resolveConfig from 'tailwindcss/resolveConfig'
// import tailwindConfig from '../../tailwind.config'
// const fullConfig = resolveConfig(tailwindConfig)
type Breakpoint = keyof (typeof defaultTheme)['screens']
export function useCurrentBreakpoint(): Breakpoint {
const [breakpoint, setBreakpoint] = useState<Breakpoint>('sm')
useEffect(() => {
const handleResize = () => {
const screenWidth = window.innerWidth
const breakpoints = Object.entries(defaultTheme.screens) as [
Breakpoint,
string
][]
for (let i = breakpoints.length - 1; i >= 0; i--) {
const [breakpoint, minWidth] = breakpoints[i]
const convertedMinWidth = parseInt(minWidth, 10)
if (screenWidth >= convertedMinWidth) {
setBreakpoint(breakpoint)
return
}
}
}
handleResize()
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return breakpoint
}

View File

@ -1,6 +1,4 @@
import { Breadcrumbs } from '@/components'
import { EpicOverview } from '@/components/epic-overview'
import { TableIssues } from '@/components/table-issues'
import { Breadcrumbs, EpicOverview, TableIssues } from '@/components'
import { InsightsLayout } from '@/layouts/insights-layout'
import type { Page } from 'next'
@ -11,13 +9,14 @@ const EpicsDetailPage: Page = () => {
<div className="border-b border-neutral-10 px-5 py-3">
<Breadcrumbs />
</div>
<div className="px-10 py-6">
<div className="border-b border-neutral-10 px-10 py-6">
<EpicOverview
title="Communities protocol"
description="Detecting keycard reader removal for the beginning of each flow"
fullscreen
/>
</div>
<div className="border-b border-neutral-10 px-10 py-6">
<div role="separator" className="-mx-6 my-6 h-px bg-neutral-10" />
<TableIssues />

View File

@ -1,6 +1,6 @@
import { Text } from '@status-im/components'
import { TableIssues } from '@/components/table-issues'
import { TableIssues } from '@/components'
import { InsightsLayout } from '@/layouts/insights-layout'
import type { Page } from 'next'

View File

@ -1,4 +1,5 @@
import { Text } from '@status-im/components'
import { Shadow, Text } from '@status-im/components'
import { OpenIcon, UnlockedIcon } from '@status-im/icons'
import { Link } from '@/components/link'
import { InsightsLayout } from '@/layouts/insights-layout'
@ -30,6 +31,24 @@ const repos = [
issues: 10,
stars: 5,
},
{
name: 'nim-waku',
description: 'a free (libre) open source, mobile OS for Ethereum.',
issues: 10,
stars: 5,
},
{
name: 'go-waku',
description: 'a free (libre) open source, mobile OS for Ethereum.',
issues: 10,
stars: 5,
},
{
name: 'js-waku',
description: 'a free (libre) open source, mobile OS for Ethereum.',
issues: 10,
stars: 5,
},
{
name: 'nimbus-eth2',
description: 'a free (libre) open source, mobile OS for Ethereum.',
@ -55,30 +74,48 @@ const ReposPage: Page = () => {
<div className="grid grid-cols-3 gap-5">
{repos.map(repo => (
<Link
key={repo.name}
href={`https://github.com/status-im/${repo.name}`}
className="flex h-[124px] flex-col rounded-2xl border border-neutral-10 px-4 py-3 transition-colors duration-200 hover:border-neutral-40"
>
<Text size={15} weight="semibold">
{repo.name}
</Text>
<Text size={15} color="$neutral-50">
{repo.description}
</Text>
<Shadow key={repo.name}>
<Link
href={`https://github.com/status-im/${repo.name}`}
className="flex h-[124px] flex-col justify-between rounded-2xl border border-neutral-10 px-4 py-3 transition-colors duration-200 hover:border-neutral-40"
>
<div className="flex flex-col">
<Text size={15} weight="semibold">
{repo.name}
</Text>
<Text size={15} color="$neutral-50">
{repo.description}
</Text>
</div>
<div className="flex gap-3 pt-4">
<Text size={13} weight="medium" color="$neutral-50">
Public
</Text>
<Text size={13} weight="medium" color="$neutral-50">
42 Issues
</Text>
<Text size={13} weight="medium" color="$neutral-50">
32
</Text>
</div>
</Link>
<div className="flex gap-3 pt-4">
<div className="flex items-center">
<div className="pr-1">
<UnlockedIcon size={12} color="$neutral-50" />
</div>
<Text size={13} weight="medium" color="$neutral-100">
Public
</Text>
</div>
<div className="flex items-center">
<div className="pr-1">
<OpenIcon size={12} color="$neutral-50" />
</div>
<Text size={13} weight="medium" color="$neutral-100">
42 issues
</Text>
</div>
<div className="flex items-center">
<div className="pr-1">
<OpenIcon size={12} color="$neutral-50" />
</div>
<Text size={13} weight="medium" color="$neutral-100">
32
</Text>
</div>
</div>
</Link>
</Shadow>
))}
</div>
</div>

View File

@ -1,6 +1,4 @@
import { Breadcrumbs } from '@/components'
import { EpicOverview } from '@/components/epic-overview'
import { TableIssues } from '@/components/table-issues'
import { Breadcrumbs, EpicOverview, TableIssues } from '@/components'
import { InsightsLayout } from '@/layouts/insights-layout'
import type { Page } from 'next'
@ -11,13 +9,14 @@ const WorkstreamDetailPage: Page = () => {
<div className="border-b border-neutral-10 px-5 py-3">
<Breadcrumbs />
</div>
<div className="px-10 py-6">
<div className="border-b border-neutral-10 px-10 py-6">
<EpicOverview
title="Communities protocol"
description="Detecting keycard reader removal for the beginning of each flow"
fullscreen
/>
</div>
<div className="border-b border-neutral-10 px-10 py-6">
<div role="separator" className="-mx-6 my-6 h-px bg-neutral-10" />
<TableIssues />

View File

@ -31,6 +31,7 @@
},
"dependencies": {
"@radix-ui/react-accordion": "^1.1.1",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-popover": "^1.0.5",

View File

@ -0,0 +1,58 @@
import { useState } from 'react'
import { Checkbox } from './checkbox'
import type { Meta, StoryObj } from '@storybook/react'
const meta: Meta<typeof Checkbox> = {
component: Checkbox,
argTypes: {},
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=180-9685&t=tDEqIV09qddTZgXF-4',
},
},
}
type Story = StoryObj<typeof Checkbox>
const CheckBoxWithHookFilled = () => {
const [checked, setChecked] = useState(false)
return (
<Checkbox
id="checkbox"
selected={checked}
onCheckedChange={() => setChecked(!checked)}
variant="filled"
/>
)
}
const CheckBoxWithHookOutlined = () => {
const [checked, setChecked] = useState(false)
return (
<Checkbox
id="checkbox"
selected={checked}
onCheckedChange={() => setChecked(!checked)}
variant="outline"
/>
)
}
export const Filled: Story = {
render: () => {
return <CheckBoxWithHookFilled />
},
}
export const Outlined: Story = {
render: () => {
return <CheckBoxWithHookOutlined />
},
}
export default meta

View File

@ -0,0 +1,124 @@
import { Indicator as _Indicator, Root } from '@radix-ui/react-checkbox'
import { CheckIcon } from '@status-im/icons'
import { styled } from '@tamagui/core'
import type { GetVariants } from '../types'
import type { IconProps } from '@status-im/icons'
import type { ColorTokens } from '@tamagui/core'
type Variants = GetVariants<typeof Base>
interface Props {
selected?: boolean
onCheckedChange?: (checked: boolean) => void
id: string
size?: 32 | 20
variant?: Variants['variant']
}
const iconColor: Record<Variants['variant'], ColorTokens> = {
filled: '$neutral-50',
outline: '$white-100',
}
const iconSize: Record<Variants['size'], IconProps['size']> = {
32: 20,
20: 16,
}
const Checkbox = (props: Props) => {
const { id, selected, onCheckedChange, size = 20, variant = 'filled' } = props
return (
<Base
id={id}
selected={selected ? variant : undefined}
size={size}
onCheckedChange={onCheckedChange}
variant={variant}
checked={selected}
>
<Indicator>
<CheckIcon size={iconSize[size]} color={iconColor[variant]} />
</Indicator>
</Base>
)
}
export { Checkbox }
export type { Props as CheckboxProps }
const Base = styled(Root, {
name: 'Checkbox',
tag: 'span',
accessibilityRole: 'checkbox',
backgroundColor: 'transparent',
borderRadius: '$2',
cursor: 'pointer',
animation: 'fast',
height: 32,
width: 32,
borderWidth: 1,
variants: {
size: {
32: {
height: 32,
width: 32,
},
20: {
height: 20,
width: 20,
},
},
variant: {
filled: {
backgroundColor: '$neutral-20',
hoverStyle: { backgroundColor: '$neutral-30' },
pressStyle: { backgroundColor: '$neutral-30' },
},
outline: {
borderColor: '$neutral-20',
hoverStyle: { borderColor: '$neutral-30' },
pressStyle: { borderColor: '$neutral-30' },
},
},
selected: {
filled: {
hoverStyle: { backgroundColor: '$primary-60' },
pressStyle: { backgroundColor: '$primary-60' },
},
outline: {
backgroundColor: '$primary-50',
borderColor: '$primary-50',
hoverStyle: {
backgroundColor: '$primary-60',
},
pressStyle: {
backgroundColor: '$primary-60',
},
},
},
disabled: {
true: {
opacity: 0.3,
cursor: 'default',
},
},
} as const,
})
const Indicator = styled(_Indicator, {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
width: '100%',
})

View File

@ -0,0 +1 @@
export { Checkbox } from './checkbox'

View File

@ -1,6 +1,7 @@
import { cloneElement } from 'react'
import { cloneElement, forwardRef } from 'react'
import {
CheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
@ -8,8 +9,10 @@ import {
Root,
Trigger,
} from '@radix-ui/react-dropdown-menu'
import { styled } from '@tamagui/core'
import { CheckIcon } from '@status-im/icons'
import { Stack, styled } from '@tamagui/core'
import { Checkbox } from '../checkbox'
import { Text } from '../text'
interface Props {
@ -32,28 +35,64 @@ const DropdownMenu = (props: Props) => {
}
interface DropdownMenuItemProps {
icon: React.ReactElement
icon?: React.ReactElement
label: string
onSelect: () => void
selected?: boolean
danger?: boolean
}
const MenuItem = (props: DropdownMenuItemProps) => {
const { icon, label, onSelect, danger } = props
const { icon, label, onSelect, danger, selected } = props
const iconColor = danger ? '$danger-50' : '$neutral-50'
const textColor = danger ? '$danger-50' : '$neutral-100'
return (
<ItemBase onSelect={onSelect}>
{cloneElement(icon, { color: iconColor })}
<Text size={15} weight="medium" color={textColor}>
{label}
</Text>
<Stack flexDirection="row" gap={8} alignItems="center">
{icon && cloneElement(icon, { color: iconColor })}
<Text size={15} weight="medium" color={textColor}>
{label}
</Text>
</Stack>
{selected && <CheckIcon size={20} color={iconColor} />}
</ItemBase>
)
}
interface DropdownMenuCheckboxItemProps {
icon?: React.ReactElement
label: string
onSelect: () => void
checked?: boolean
danger?: boolean
}
const DropdownMenuCheckboxItem = forwardRef<
HTMLDivElement,
DropdownMenuCheckboxItemProps
>(function _DropdownMenuCheckboxItem(props, forwardedRef) {
const { checked, label, icon, onSelect } = props
const handleSelect = (event: Event) => {
event.preventDefault()
onSelect()
}
return (
<ItemBaseCheckbox {...props} ref={forwardedRef} onSelect={handleSelect}>
<Stack flexDirection="row" gap={8} alignItems="center">
{icon && cloneElement(icon)}
<Text size={15} weight="medium" color="$neutral-100">
{label}
</Text>
</Stack>
<Checkbox id={label} selected={checked} variant="outline" />
</ItemBaseCheckbox>
)
})
const Content = styled(DropdownMenuContent, {
name: 'DropdownMenuContent',
acceptsClassName: true,
@ -74,6 +113,32 @@ const ItemBase = styled(DropdownMenuItem, {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
height: 32,
paddingHorizontal: 8,
borderRadius: '$10',
cursor: 'pointer',
userSelect: 'none',
gap: 8,
hoverStyle: {
backgroundColor: '$neutral-5',
},
pressStyle: {
backgroundColor: '$neutral-10',
},
})
const ItemBaseCheckbox = styled(CheckboxItem, {
name: 'DropdownMenuCheckboxItem',
acceptsClassName: true,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
height: 32,
paddingHorizontal: 8,
borderRadius: '$10',
@ -104,6 +169,7 @@ const Separator = styled(DropdownMenuSeparator, {
DropdownMenu.Content = Content
DropdownMenu.Item = MenuItem
DropdownMenu.Separator = Separator
DropdownMenu.CheckboxItem = DropdownMenuCheckboxItem
export { DropdownMenu }
export type DropdownMenuProps = Omit<Props, 'children'>

View File

@ -1,6 +1,6 @@
import { cloneElement, forwardRef } from 'react'
import { Stack, styled } from 'tamagui'
import { Stack, styled } from '@tamagui/core'
import { usePressableColors } from '../hooks/use-pressable-colors'
@ -93,8 +93,8 @@ const Base = styled(Stack, {
outline: {
backgroundColor: 'transparent',
borderColor: '$neutral-20',
hoverStyle: { borderColor: '$neutral-30' },
borderColor: '$neutral-30',
hoverStyle: { borderColor: '$neutral-40' },
pressStyle: {
borderColor: '$neutral-20',
backgroundColor: '$neutral-10',

View File

@ -2,11 +2,13 @@ export * from './anchor-actions'
export * from './avatar'
export * from './button'
export * from './calendar'
export * from './checkbox'
export * from './community'
export * from './composer'
export * from './context-tag'
export * from './counter'
export * from './dividers'
export * from './dropdown-menu'
export * from './dynamic-button'
export * from './gap-messages'
export * from './icon-button'

View File

@ -1,3 +1,7 @@
import { useEffect, useState } from 'react'
import { SearchIcon } from '@status-im/icons'
import { Input } from './input'
import type { Meta, StoryObj } from '@storybook/react'
@ -14,7 +18,70 @@ type Story = StoryObj<typeof Input>
export const Primary: Story = {
args: {
placeholder: 'Type something...',
// children: 'Click me',
},
}
const InputSearch = () => {
const [value, setValue] = useState('')
// limit input to 100 characters just for demo purposes
useEffect(() => {
if (value.length > 100) {
setValue(value.slice(0, 100))
}
}, [value])
return (
<>
<Input
placeholder="Please type something..."
value={value}
onChangeText={setValue}
icon={<SearchIcon size={20} />}
onClear={() => setValue('')}
label="Search"
endLabel={`${value.length}/100`}
size={40}
button={{
label: 'Confirm',
onPress: () => alert('Confirmed!'),
}}
/>
</>
)
}
const InputSearchMinimzed = () => {
const [value, setValue] = useState('')
return (
<>
<Input
placeholder="Please type something..."
value={value}
onChangeText={setValue}
icon={<SearchIcon size={20} />}
onClear={() => setValue('')}
size={32}
direction="rtl"
variant="retractable"
/>
</>
)
}
export const Minimized: Story = {
render: () => <InputSearchMinimzed />,
}
export const CompleteExample: Story = {
render: () => <InputSearch />,
}
export const WithError: Story = {
args: {
placeholder: 'Type something...',
error: true,
},
}

View File

@ -1,62 +1,229 @@
import { setupReactNative, styled } from '@tamagui/core'
import { cloneElement, forwardRef, useRef, useState } from 'react'
import { composeRefs } from '@radix-ui/react-compose-refs'
import { ClearIcon } from '@status-im/icons'
import { setupReactNative, Stack, styled } from '@tamagui/core'
import { focusableInputHOC } from '@tamagui/focusable'
import { TextInput } from 'react-native'
import { Button } from '../button'
import { Text } from '../text'
import type { GetProps } from '@tamagui/core'
import type { Ref } from 'react'
setupReactNative({
TextInput,
})
export const InputFrame = styled(
type Props = GetProps<typeof InputFrame> & {
button?: {
label: string
onPress: () => void
}
endLabel?: string
icon?: React.ReactElement
label?: string
onClear?: () => void
variant?: 'default' | 'retractable'
size?: 40 | 32
error?: boolean
direction?: 'ltr' | 'rtl'
}
const _Input = (props: Props, ref: Ref<TextInput>) => {
const {
button,
color = '$neutral-50',
endLabel,
error,
icon,
label,
onClear,
size = 40,
placeholder,
value,
direction = 'ltr',
variant = 'default',
...rest
} = props
const [isMinimized, setIsMinimized] = useState(variant === 'retractable')
const isRetractable = variant === 'retractable'
const inputRef = useRef<TextInput>(null)
return (
<Stack flexDirection={direction === 'ltr' ? 'row' : 'row-reverse'}>
{Boolean(label || endLabel) && (
<Stack flexDirection="row" justifyContent="space-between" pb={8}>
{label && (
<Text size={13} color="$neutral-50" weight="medium">
{label}
</Text>
)}
{endLabel && (
<Text size={13} color="$neutral-50">
{endLabel}
</Text>
)}
</Stack>
)}
<InputContainer
size={size}
error={error}
minimized={isMinimized}
onPress={event => {
event.stopPropagation()
event.preventDefault()
if (isRetractable && isMinimized) {
setIsMinimized(false)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore ref is not inferred correctly here
inputRef?.current?.focus()
}
}}
>
{icon ? (
<Stack flexShrink={0}>{cloneElement(icon, { color })}</Stack>
) : null}
<InputBase
value={value}
placeholder={isMinimized && !value ? '' : placeholder}
flex={1}
ref={composeRefs(ref, inputRef)}
onBlur={() => {
if (!value && isRetractable && !isMinimized) {
setIsMinimized(true)
}
}}
{...rest}
/>
<Stack flexDirection="row" alignItems="center">
{Boolean(onClear) && !!value && (
<Stack
role="button"
accessibilityRole="button"
pr={4}
onPress={onClear}
cursor="pointer"
hoverStyle={{ opacity: 0.6 }}
animation="fast"
>
<ClearIcon size={20} />
</Stack>
)}
{button && (
<Button onPress={button.onPress} variant="outline" size={24}>
{button.label}
</Button>
)}
</Stack>
</InputContainer>
</Stack>
)
}
const Input = forwardRef(_Input)
export { Input }
export type { Props as InputProps }
const InputFrame = styled(
TextInput,
{
tag: 'input',
name: 'Input',
borderWidth: 1,
outlineWidth: 0,
borderColor: 'rgba(0, 200, 0, 1)',
borderColor: '$neutral-20',
paddingHorizontal: 30,
color: 'hsla(218, 51%, 7%, 1)',
color: '$neutral-100',
placeholderTextColor: '$placeHolderColor',
backgroundColor: 'transparent',
height: 32,
borderRadius: '$12',
animation: 'fast',
// this fixes a flex bug where it overflows container
minWidth: 0,
hoverStyle: {
borderColor: '$beigeHover',
},
focusStyle: {
borderColor: '$blueHover',
},
variants: {
blurred: {
true: {
placeholderTextColor: '$placeHolderColorBlurred',
},
},
},
defaultVariants: {
blurred: '$false',
},
} as const,
},
{
isInput: true,
}
)
export type InputProps = GetProps<typeof InputFrame>
const InputBase = focusableInputHOC(InputFrame)
export const Input = focusableInputHOC(InputFrame)
const InputContainer = styled(Stack, {
name: 'InputContainer',
tag: 'div',
flexDirection: 'row',
alignItems: 'center',
gap: 8,
borderWidth: 1,
borderColor: '$neutral-30',
paddingHorizontal: 12,
animation: 'fast',
width: '100%',
hoverStyle: {
borderColor: '$neutral-40',
},
focusStyle: {
borderColor: '$neutral-40',
},
pressStyle: {
borderColor: '$neutral-40',
},
variants: {
size: {
40: {
height: 40,
paddingHorizontal: 16,
borderRadius: '$12',
},
32: {
height: 32,
paddingHorizontal: 8,
borderRadius: '$10',
},
},
minimized: {
true: {
width: 32,
paddingHorizontal: 0,
paddingLeft: 5,
cursor: 'pointer',
},
},
error: {
true: {
borderColor: '$danger-50-opa-40',
},
},
disabled: {
true: {
opacity: 0.3,
cursor: 'default',
},
},
} as const,
})

View File

@ -42,7 +42,7 @@ export const Default: Story = {
<Tag label="New tag" size={24} disabled selected />
<Tag label="New tag" size={24} color="#FF7D46" />
<Tag label="New tag #7140FD" size={24} color="#BA34F5" />
<Tag label="New tag #BA34F5" size={24} color="#BA34F5" />
<Tag label="New tag #7140FD" size={24} color="#7140FD" icon="🧙‍♂️" />
<Tag

View File

@ -4167,6 +4167,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-accordion@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.1.1.tgz#fa1ab1b5c6a29aa75aefaf306a9e72fe3a482dbc"
@ -4191,6 +4198,21 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.2"
"@radix-ui/react-checkbox@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
integrity sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "1.0.1"
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-context" "1.0.1"
"@radix-ui/react-presence" "1.0.1"
"@radix-ui/react-primitive" "1.0.3"
"@radix-ui/react-use-controllable-state" "1.0.1"
"@radix-ui/react-use-previous" "1.0.1"
"@radix-ui/react-use-size" "1.0.1"
"@radix-ui/react-collapsible@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.0.2.tgz#0583470c7caa8cd1ab6f606416288d19b3baf777"
@ -4224,6 +4246,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-context@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.0.tgz#f38e30c5859a9fb5e9aa9a9da452ee3ed9e0aee0"
@ -4231,6 +4260,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-context@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-dialog@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.3.tgz#a715bf30f35fcd80476c0a07fcc073c1968e6d3e"
@ -4412,6 +4448,15 @@
"@radix-ui/react-compose-refs" "1.0.0"
"@radix-ui/react-use-layout-effect" "1.0.0"
"@radix-ui/react-presence@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-use-layout-effect" "1.0.1"
"@radix-ui/react-primitive@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz#54e22f49ca59ba88d8143090276d50b93f8a7053"
@ -4420,6 +4465,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-slot" "1.0.1"
"@radix-ui/react-primitive@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-slot" "1.0.2"
"@radix-ui/react-roving-focus@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.3.tgz#0b4f4f9bd509f4510079e9e0734a734fd17cdce3"
@ -4444,6 +4497,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.0"
"@radix-ui/react-slot@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-tabs@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.3.tgz#8b4158160a7c6633c893c74641e929d2708e709a"
@ -4511,6 +4572,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-controllable-state@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f"
@ -4519,6 +4587,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "1.0.0"
"@radix-ui/react-use-controllable-state@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286"
integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-callback-ref" "1.0.1"
"@radix-ui/react-use-escape-keydown@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz#09ab6455ab240b4f0a61faf06d4e5132c4d639f6"
@ -4542,6 +4618,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-previous@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.0.tgz#e48a69c3a7d8078a967084038df66d0d181c56ac"
@ -4549,6 +4632,13 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-previous@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66"
integrity sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-previous@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-0.1.1.tgz#0226017f72267200f6e832a7103760e96a6db5d0"
@ -4572,6 +4662,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "1.0.0"
"@radix-ui/react-use-size@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "1.0.1"
"@radix-ui/react-visually-hidden@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.2.tgz#29b117a59ef09a984bdad12cb98d81e8350be450"