From 84ec4922922af32f9f95ace4a6b3657f760d01d9 Mon Sep 17 00:00:00 2001 From: Pavel <14926950+prichodko@users.noreply.github.com> Date: Tue, 14 Feb 2023 17:36:38 +0100 Subject: [PATCH] Add more components, message actions & reactions (#339) * hide sidebar on small screen * add reply component * add radix dependencies * add dropdown menu component * add popover component * add tooltip component * add react button component * add reaction popover * update chat message actions * add basic dialog and sheet components * add ref to Button component * add chat message reactions * add reply and reactions to chat message * remove console.log * add dropdown menu to topbar * add ref and support aria in IconButton * yarn.lock * add stylesheet reset to storybook * add pinned state to message * remove extraneous component * add all button variants * fix button icons * use IconButton in actions * remove erroneous character * add reply to composer + simplify * use correct button in reply --- apps/vite/src/app.tsx | 9 +- apps/vite/styles/app.css | 18 ++ packages/components/.storybook/preview.tsx | 2 + packages/components/.storybook/reset.css | 100 ++++++++ packages/components/package.json | 3 + .../components/src/button/button.stories.tsx | 64 ++++- packages/components/src/button/button.tsx | 100 ++++++-- packages/components/src/button/index.tsx | 2 +- packages/components/src/composer/composer.tsx | 59 +++-- .../components/src/dialog/dialog.stories.tsx | 32 +++ packages/components/src/dialog/dialog.tsx | 116 +++++++++ packages/components/src/dialog/index.tsx | 1 + .../dropdown-menu/dropdown-menu.stories.tsx | 85 +++++++ .../src/dropdown-menu/dropdown-menu.tsx | 108 +++++++++ .../components/src/dropdown-menu/index.tsx | 1 + .../src/icon-button/icon-button.tsx | 29 +-- packages/components/src/input/input.tsx | 2 +- .../components/src/messages/chat-message.tsx | 89 ------- .../messages/components/actions.stories.tsx | 10 + .../src/messages/components/actions.tsx | 118 ++++++++- .../messages/components/reaction-popover.tsx | 64 +++++ .../messages/components/reactions.stories.tsx | 10 + .../src/messages/components/reactions.tsx | 175 +++++++------- packages/components/src/messages/index.tsx | 76 ++++-- ...ssages.stories.tsx => message.stories.tsx} | 47 +++- packages/components/src/messages/message.tsx | 127 ++++++++++ packages/components/src/messages/types.ts | 12 + packages/components/src/popover/index.tsx | 1 + .../src/popover/popover.stories.tsx | 32 +++ packages/components/src/popover/popover.tsx | 51 ++++ .../components/src/react-button/index.tsx | 1 + .../src/react-button/react-button.stories.tsx | 67 ++++++ .../src/react-button/react-button.tsx | 141 +++++++++++ packages/components/src/reply/index.tsx | 1 + .../components/src/reply/reply.stories.tsx | 84 +++++++ packages/components/src/reply/reply.tsx | 97 ++++++++ packages/components/src/sheet/index.tsx | 1 + .../components/src/sheet/sheet.stories.tsx | 32 +++ packages/components/src/sheet/sheet.tsx | 79 ++++++ packages/components/src/tooltip/index.tsx | 1 + .../components/src/tooltip/tooltip.native.tsx | 3 + .../src/tooltip/tooltip.stories.tsx | 30 +++ packages/components/src/tooltip/tooltip.tsx | 83 +++++++ packages/components/src/topbar/topbar.tsx | 49 +++- packages/icons/vite.config.ts | 1 - yarn.lock | 224 +++++++++++++++++- 46 files changed, 2134 insertions(+), 303 deletions(-) create mode 100644 packages/components/.storybook/reset.css create mode 100644 packages/components/src/dialog/dialog.stories.tsx create mode 100644 packages/components/src/dialog/dialog.tsx create mode 100644 packages/components/src/dialog/index.tsx create mode 100644 packages/components/src/dropdown-menu/dropdown-menu.stories.tsx create mode 100644 packages/components/src/dropdown-menu/dropdown-menu.tsx create mode 100644 packages/components/src/dropdown-menu/index.tsx delete mode 100644 packages/components/src/messages/chat-message.tsx create mode 100644 packages/components/src/messages/components/reaction-popover.tsx rename packages/components/src/messages/{messages.stories.tsx => message.stories.tsx} (74%) create mode 100644 packages/components/src/messages/message.tsx create mode 100644 packages/components/src/messages/types.ts create mode 100644 packages/components/src/popover/index.tsx create mode 100644 packages/components/src/popover/popover.stories.tsx create mode 100644 packages/components/src/popover/popover.tsx create mode 100644 packages/components/src/react-button/index.tsx create mode 100644 packages/components/src/react-button/react-button.stories.tsx create mode 100644 packages/components/src/react-button/react-button.tsx create mode 100644 packages/components/src/reply/index.tsx create mode 100644 packages/components/src/reply/reply.stories.tsx create mode 100644 packages/components/src/reply/reply.tsx create mode 100644 packages/components/src/sheet/index.tsx create mode 100644 packages/components/src/sheet/sheet.stories.tsx create mode 100644 packages/components/src/sheet/sheet.tsx create mode 100644 packages/components/src/tooltip/index.tsx create mode 100644 packages/components/src/tooltip/tooltip.native.tsx create mode 100644 packages/components/src/tooltip/tooltip.stories.tsx create mode 100644 packages/components/src/tooltip/tooltip.tsx diff --git a/apps/vite/src/app.tsx b/apps/vite/src/app.tsx index b6d560f8..44b332bb 100644 --- a/apps/vite/src/app.tsx +++ b/apps/vite/src/app.tsx @@ -88,12 +88,9 @@ function App() { - +
+ +
{showMembers && ( diff --git a/apps/vite/styles/app.css b/apps/vite/styles/app.css index ab34016c..3bcc1fd1 100644 --- a/apps/vite/styles/app.css +++ b/apps/vite/styles/app.css @@ -17,6 +17,7 @@ body, } #main { + position: relative; display: grid; grid-template-rows: 56px 1fr 100px; height: 100vh; @@ -45,7 +46,24 @@ body, margin-top: -56px; } +#composer { + position: absolute; + bottom: 0; + left: 0; + right: 0; +} + #members { width: 352px; overflow: auto; } + +@media screen and (max-width: 768px) { + #app { + grid-template-columns: 1fr; + } + + #sidebar { + display: none; + } +} diff --git a/packages/components/.storybook/preview.tsx b/packages/components/.storybook/preview.tsx index a0134e88..7056e3e4 100644 --- a/packages/components/.storybook/preview.tsx +++ b/packages/components/.storybook/preview.tsx @@ -3,6 +3,8 @@ import { TamaguiProvider } from '@tamagui/core' import { config } from '../src' import { Parameters, Decorator } from '@storybook/react' +import './reset.css' + export const parameters: Parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { diff --git a/packages/components/.storybook/reset.css b/packages/components/.storybook/reset.css new file mode 100644 index 00000000..c793f03b --- /dev/null +++ b/packages/components/.storybook/reset.css @@ -0,0 +1,100 @@ +/* + 1. Use a more-intuitive box-sizing model. +*/ +*, +*::before, +*::after { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #fff; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +/* + 2. Remove default margin +*/ +* { + margin: 0; +} + +/* + 3. Allow percentage-based heights in the application +*/ +html, +body { + height: 100vh; + width: 100vw; + overflow: hidden; + overscroll-behavior-y: none; /* not working on Safari */ +} +/* + Typographic tweaks! + 4. Add accessible line-height + 5. Improve text rendering +*/ +body { + line-height: 1.5; + -webkit-font-smoothing: antialiased; + padding: 0; + -webkit-overflow-scrolling: touch; +} +/* + 6. Improve media defaults +*/ +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} +/* + 7. Remove built-in form typography styles +*/ +input, +button, +textarea, +select { + font: inherit; +} +/* + 8. Avoid text overflows +*/ +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} +/* + 9. Create a root stacking context +*/ +#root, +#__next { + isolation: isolate; +} + +/* + 10. Remove user selection on buttons +*/ +button { + user-select: none; +} diff --git a/packages/components/package.json b/packages/components/package.json index b1c25268..7745ce0e 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -22,6 +22,9 @@ "react-native-web": "^0.18.0" }, "dependencies": { + "@radix-ui/react-dropdown-menu": "^2.0.2", + "@radix-ui/react-popover": "^1.0.3", + "@radix-ui/react-tooltip": "^1.0.3", "@status-im/icons": "*", "@tamagui/animations-css": "1.0.15", "@tamagui/animations-react-native": "1.0.15", diff --git a/packages/components/src/button/button.stories.tsx b/packages/components/src/button/button.stories.tsx index 3f7d115c..91b93de8 100644 --- a/packages/components/src/button/button.stories.tsx +++ b/packages/components/src/button/button.stories.tsx @@ -1,3 +1,6 @@ +import { action } from '@storybook/addon-actions' +import { Stack } from 'tamagui' + import { Button } from './button' import type { Meta, StoryObj } from '@storybook/react' @@ -5,18 +8,25 @@ import type { Meta, StoryObj } from '@storybook/react' // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction const meta: Meta = { component: Button, - argTypes: {}, + args: { + onPress: action('press'), + }, + argTypes: { + disabled: { + defaultValue: false, + }, + }, + decorators: [ + Story => ( + + + + ), + ], } type Story = StoryObj -// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args -export const Primary: Story = { - args: { - children: 'Click me', - }, -} - const icon = ( ) +// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args +export const Primary: Story = { + args: { + children: 'Click me', + }, +} + +export const PrimaryDisabled: Story = { + args: { + children: 'Click me', + disabled: true, + }, +} + +export const PrimaryFullWidth: Story = { + args: { + children: 'Click me', + width: 'full', + }, +} + export const Primary32: Story = { - name: 'Primary/32px', + name: 'Primary / 32', args: { size: 32, children: 'Click me', }, } +export const Primary24: Story = { + name: 'Primary / 24', + args: { + size: 24, + children: 'Click me', + }, +} + export const PrimaryIconBefore: Story = { name: 'Primary icon before', args: { @@ -85,4 +124,11 @@ export const Ghost: Story = { }, } +export const Danger: Story = { + args: { + type: 'danger', + children: 'Click me', + }, +} + export default meta diff --git a/packages/components/src/button/button.tsx b/packages/components/src/button/button.tsx index e1edb05b..9601118e 100644 --- a/packages/components/src/button/button.tsx +++ b/packages/components/src/button/button.tsx @@ -1,24 +1,29 @@ +import { forwardRef } from 'react' + import { Stack, styled } from '@tamagui/core' import { Paragraph } from '../typography' import type { GetProps } from '@tamagui/core' -// import { Pressable } from 'react-native' +import type { Ref } from 'react' const Base = styled(Stack, { name: 'Button', accessibilityRole: 'button', - borderRadius: 12, display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', paddingHorizontal: 16, paddingTop: 7, paddingBottom: 9, - cursor: 'pointer', - alignItems: 'center', - animation: 'fast', + cursor: 'pointer', userSelect: 'none', + animation: 'fast', + borderWidth: 1, + borderColor: 'transparent', variants: { type: { @@ -28,9 +33,19 @@ const Base = styled(Stack, { pressStyle: { backgroundColor: '$primaryHover' }, }, positive: { - backgroundColor: '$success', - hoverStyle: { backgroundColor: '$successHover' }, - pressStyle: { backgroundColor: '$successHover' }, + backgroundColor: '$success-50', + hoverStyle: { backgroundColor: '$success-60' }, + pressStyle: { backgroundColor: '$success-50' }, + }, + grey: { + backgroundColor: '$neutral-10', + hoverStyle: { backgroundColor: '$neutral-20' }, + pressStyle: { backgroundColor: '$neutral-30' }, + }, + darkGrey: { + backgroundColor: '$neutral-20', + hoverStyle: { backgroundColor: '$neutral-30' }, + pressStyle: { backgroundColor: '$neutral-40' }, }, outline: { borderWidth: 1, @@ -43,23 +58,47 @@ const Base = styled(Stack, { hoverStyle: { backgroundColor: '$neutral-10' }, pressStyle: { backgroundColor: '$neutral-20' }, }, + danger: { + backgroundColor: '$danger', + hoverStyle: { backgroundColor: '$danger-60' }, + pressStyle: { backgroundColor: '$danger' }, + }, + }, + + disabled: { + true: { + opacity: 0.3, + cursor: 'default', + }, }, size: { 40: { + minHeight: 40, + borderRadius: 12, paddingHorizontal: 16, paddingTop: 7, paddingBottom: 9, }, 32: { + minHeight: 32, + borderRadius: 10, paddingHorizontal: 16, paddingTop: 4, paddingBottom: 6, }, + 24: { + minHeight: 24, + borderRadius: 8, + paddingHorizontal: 8, + paddingTop: 2, + paddingBottom: 4, + }, }, iconOnly: { true: { + space: 0, paddingHorizontal: 8, }, }, @@ -67,11 +106,10 @@ const Base = styled(Stack, { }) const ButtonText = styled(Paragraph, { - textAlign: 'center', - weight: 'medium', display: 'flex', alignItems: 'center', space: 4, + weight: 'medium', variants: { type: { @@ -81,12 +119,33 @@ const ButtonText = styled(Paragraph, { positive: { color: '$white-100', }, + grey: { + color: '$neutral-100', + }, + darkGrey: { + color: '$neutral-100', + }, outline: { color: '$neutral-100', }, ghost: { color: '$neutral-100', }, + danger: { + color: '$white-100', + }, + }, + + size: { + 40: { + variant: 'normal', + }, + 32: { + variant: 'normal', + }, + 24: { + variant: 'smaller', + }, }, } as const, }) @@ -95,19 +154,18 @@ type BaseProps = GetProps type Props = BaseProps & { children?: string - icon?: React.ReactNode type?: BaseProps['type'] size?: BaseProps['size'] + disabled?: boolean + icon?: React.ReactNode iconAfter?: React.ReactNode - onPress?: () => void } -const Button = (props: Props) => { +const Button = (props: Props, ref: Ref) => { const { type = 'primary', size = 40, children, - onPress, icon, iconAfter, ...rest @@ -116,14 +174,8 @@ const Button = (props: Props) => { const iconOnly = !children && Boolean(icon) return ( - - + + {icon} {children} {iconAfter} @@ -132,5 +184,7 @@ const Button = (props: Props) => { ) } -export { Button } +const _Button = forwardRef(Button) + +export { _Button as Button } export type { Props as ButtonProps } diff --git a/packages/components/src/button/index.tsx b/packages/components/src/button/index.tsx index db783b10..f872a184 100644 --- a/packages/components/src/button/index.tsx +++ b/packages/components/src/button/index.tsx @@ -1 +1 @@ -export { Button } from './button' +export { type ButtonProps, Button } from './button' diff --git a/packages/components/src/composer/composer.tsx b/packages/components/src/composer/composer.tsx index c1f2f4f4..5cad4911 100644 --- a/packages/components/src/composer/composer.tsx +++ b/packages/components/src/composer/composer.tsx @@ -11,30 +11,19 @@ import { Stack, XStack, YStack } from 'tamagui' import { IconButton } from '../icon-button' import { Input } from '../input' +import { Reply } from '../reply' -import type { GetProps } from '@tamagui/core' -import type { ViewProps } from 'react-native' +interface Props { + isBlurred: boolean + reply: boolean +} -type BaseProps = GetProps - -const Composer = ( - props: Omit & { - placeholderTextColor?: BaseProps['backgroundColor'] - iconOptionsColor?: BaseProps['backgroundColor'] - isBlurred?: boolean - style?: ViewProps['style'] - } -) => { - const { backgroundColor, isBlurred, style: styleFromProps, ...rest } = props - const style = styleFromProps ? Object.assign(styleFromProps) : {} +const Composer = (props: Props) => { + const { isBlurred, reply } = props const [isFocused, setIsFocused] = useState(false) - const applyVariantStyles: - | { - blurred: boolean - } - | undefined = isBlurred && !isFocused ? { blurred: true } : undefined + const iconButtonBlurred = isBlurred && !isFocused return ( + {reply && ( + + { + console.log('close') + }} + /> + + )} + diff --git a/packages/components/src/dialog/dialog.stories.tsx b/packages/components/src/dialog/dialog.stories.tsx new file mode 100644 index 00000000..32117f79 --- /dev/null +++ b/packages/components/src/dialog/dialog.stories.tsx @@ -0,0 +1,32 @@ +import { Button } from '../button' +import { Dialog } from './dialog' + +import type { Meta, StoryObj } from '@storybook/react' + +// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction +const meta: Meta = { + title: 'Web/Dialog', + // component: Sheet, + argTypes: {}, + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Web?node-id=611%3A36006&t=Gyy71OAckl3b2TWj-4', + }, + }, +} + +type Story = StoryObj + +// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args +export const Default: Story = { + args: {}, + render: () => ( + + + test + + ), +} + +export default meta diff --git a/packages/components/src/dialog/dialog.tsx b/packages/components/src/dialog/dialog.tsx new file mode 100644 index 00000000..2f73427b --- /dev/null +++ b/packages/components/src/dialog/dialog.tsx @@ -0,0 +1,116 @@ +import { forwardRef } from 'react' + +import { Content, Overlay, Portal, Root, Trigger } from '@radix-ui/react-dialog' +import { useMedia } from 'tamagui' + +import { Sheet } from '../sheet' + +import type { Ref } from 'react' +import type React from 'react' + +interface Props { + children: [React.ReactElement, React.ReactElement] + open?: boolean + onOpenChange?: (open: boolean) => void + press?: 'normal' | 'long' +} + +// const DialogTrigger = ( +// props: DialogTriggerProps & { +// press: Props['press'] +// children: React.ReactElement +// } +// ) => { +// const { children, press, onClick, type, ...triggerProps } = props +// const handler = press === 'normal' ? 'onPress' : 'onLongPress' + +// // console.log('dialog', press, onClick, { ...triggerProps, [handler]: onClick }) +// return cloneElement(children, { ref, ...triggerProps, [handler]: onClick }) +// } + +// TODO: allow customization of press duration +const Dialog = (props: Props) => { + const { children, open, onOpenChange /* press = 'normal' */ } = props + + const [trigger, content] = children + + const media = useMedia() + + if (media.sm) { + return ( + + {trigger} + {content} + + ) + } + + return ( + + {/* TRIGGER */} + {trigger} + + {/* CONTENT */} + + + {content} + + + ) +} + +interface DialogContentProps { + // title: string + // description?: string + children: React.ReactNode + // action: string + // onAction: (close: VoidFunction) => void + // onCancel?: () => void + initialFocusRef?: React.RefObject +} + +const DialogContent = (props: DialogContentProps, ref: Ref) => { + const { children, initialFocusRef } = props + + const handleOpenAutoFocus = (event: Event) => { + if (initialFocusRef?.current) { + event.preventDefault() + initialFocusRef.current.focus() + } + } + + const media = useMedia() + + if (media.sm) { + return {children} + } + + return ( + + {children} + + ) +} + +Dialog.Content = forwardRef(DialogContent) + +export { Dialog } diff --git a/packages/components/src/dialog/index.tsx b/packages/components/src/dialog/index.tsx new file mode 100644 index 00000000..afbb355f --- /dev/null +++ b/packages/components/src/dialog/index.tsx @@ -0,0 +1 @@ +export { Dialog } from './dialog' diff --git a/packages/components/src/dropdown-menu/dropdown-menu.stories.tsx b/packages/components/src/dropdown-menu/dropdown-menu.stories.tsx new file mode 100644 index 00000000..d2f23c04 --- /dev/null +++ b/packages/components/src/dropdown-menu/dropdown-menu.stories.tsx @@ -0,0 +1,85 @@ +import { + CopyIcon, + DeleteIcon, + EditIcon, + ForwardIcon, + LinkIcon, + PinIcon, + ReplyIcon, +} from '@status-im/icons/20' +import { action } from '@storybook/addon-actions' + +import { Button } from '../button' +import { DropdownMenu } from './dropdown-menu' + +import type { Meta, StoryObj } from '@storybook/react' + +const meta: Meta = { + title: 'Web/dropdown menu', + component: DropdownMenu, + argTypes: {}, + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Web?node-id=1931%3A31188&t=rOKELbVkzya48FJE-0', + }, + }, +} + +type Story = StoryObj + +export const Default: Story = { + args: {}, + + render: args => { + return ( + + + + + } + label="Edit message" + onSelect={action('edit')} + /> + } + label="Reply" + onSelect={action('reply')} + /> + } + label="Copy text" + onSelect={action('copy')} + /> + } + label="Pin to the channel" + onSelect={action('pin')} + /> + } + label="Forward" + onSelect={action('forward')} + /> + } + label="Share link to message" + onSelect={action('share')} + /> + + + + } + label="Delete message" + danger + onSelect={action('delete')} + /> + + + ) + }, +} + +export default meta diff --git a/packages/components/src/dropdown-menu/dropdown-menu.tsx b/packages/components/src/dropdown-menu/dropdown-menu.tsx new file mode 100644 index 00000000..cd1280a6 --- /dev/null +++ b/packages/components/src/dropdown-menu/dropdown-menu.tsx @@ -0,0 +1,108 @@ +import { cloneElement } from 'react' + +import { + DropdownMenuContent, + DropdownMenuItem as RadixDropdownMenuItem, + DropdownMenuSeparator, + // Label, + Portal, + Root, + Trigger, +} from '@radix-ui/react-dropdown-menu' +import { Stack, styled } from 'tamagui' + +import { Paragraph } from '../typography' + +const Content = styled(DropdownMenuContent, { + name: 'DropdownMenuContent', + acceptsClassName: true, + + width: 352, + padding: 8, + borderRadius: 16, + backgroundColor: '$white-100', + + shadowRadius: 30, + shadowOffset: '0px 8px', + shadowColor: 'rgba(9, 16, 28, 0.12)', +}) + +const Item = styled(RadixDropdownMenuItem, { + name: 'DropdownMenuItem', + acceptsClassName: true, + + display: 'flex', + alignItems: 'center', + padding: 8, + borderRadius: 12, + cursor: 'pointer', + userSelect: 'none', + + hoverStyle: { + backgroundColor: '$neutral-5', + }, + + pressStyle: { + backgroundColor: '$neutral-10', + }, +}) + +const Separator = styled(DropdownMenuSeparator, { + name: 'DropdownMenuSeparator', + acceptsClassName: true, + + height: 1, + backgroundColor: '$neutral-10', + marginVertical: 8, + marginLeft: -8, + marginRight: -8, +}) + +interface Props { + children: [React.ReactElement, React.ReactElement] + modal?: false + onOpenChange?: (open: boolean) => void +} + +const DropdownMenu = (props: Props) => { + const { children, onOpenChange, modal } = props + + const [trigger, content] = children + + return ( + + {trigger} + {content} + + ) +} + +interface DropdownMenuItemProps { + icon: React.ReactElement + label: string + onSelect: () => void + danger?: boolean +} + +const DropdownMenuItem = (props: DropdownMenuItemProps) => { + const { icon, label, onSelect, danger } = props + + const iconColor = danger ? '$danger-50' : '$neutral-50' + const textColor = danger ? '$danger-50' : '$neutral-100' + + return ( + + {cloneElement(icon, { color: iconColor })} + + {label} + + + ) +} + +DropdownMenu.Content = Content +DropdownMenu.Item = DropdownMenuItem +DropdownMenu.Separator = Separator + +export { DropdownMenu } +export type { Props as DropdownMenuProps } diff --git a/packages/components/src/dropdown-menu/index.tsx b/packages/components/src/dropdown-menu/index.tsx new file mode 100644 index 00000000..6b8b76cf --- /dev/null +++ b/packages/components/src/dropdown-menu/index.tsx @@ -0,0 +1 @@ +export { DropdownMenu } from './dropdown-menu' diff --git a/packages/components/src/icon-button/icon-button.tsx b/packages/components/src/icon-button/icon-button.tsx index 5a16eaff..86dc78f2 100644 --- a/packages/components/src/icon-button/icon-button.tsx +++ b/packages/components/src/icon-button/icon-button.tsx @@ -1,8 +1,8 @@ -import { cloneElement } from 'react' +import { cloneElement, forwardRef } from 'react' import { Stack, styled } from '@tamagui/core' -import type React from 'react' +import type { Ref } from 'react' const Base = styled(Stack, { name: 'IconButton', @@ -108,6 +108,9 @@ interface Props { disabled?: boolean // FIXME: enforce aria-label for accessibility // 'aria-label'?: string + // FIXME: update to latest RN + 'aria-expanded'?: boolean + 'aria-selected'?: boolean } const iconColor = { @@ -165,26 +168,22 @@ const getSelectedVariant = ({ return variant } -const IconButton = (props: Props) => { - const { - icon, - selected, - blurred, - onPress, - variant = 'default', - disabled, - } = props +const IconButton = (props: Props, ref: Ref) => { + const { icon, blurred, variant = 'default', ...rest } = props + + const selected = + props.selected || props['aria-expanded'] || props['aria-selected'] const state = getStateForIconColor({ blurred, selected }) const selectedVariant = getSelectedVariant({ selected, variant, blurred }) return ( {cloneElement(icon, { color: iconColor[variant][state], @@ -194,5 +193,7 @@ const IconButton = (props: Props) => { ) } -export { IconButton } +const _IconButton = forwardRef(IconButton) + +export { _IconButton as IconButton } export type { Props as IconButtonProps } diff --git a/packages/components/src/input/input.tsx b/packages/components/src/input/input.tsx index e367b682..6b5b8a64 100644 --- a/packages/components/src/input/input.tsx +++ b/packages/components/src/input/input.tsx @@ -24,7 +24,7 @@ export const InputFrame = styled( backgroundColor: 'transparent', - height: 40, + height: 32, borderRadius: 12, animation: 'fast', diff --git a/packages/components/src/messages/chat-message.tsx b/packages/components/src/messages/chat-message.tsx deleted file mode 100644 index ab5cf4d4..00000000 --- a/packages/components/src/messages/chat-message.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react' - -import { Stack, Unspaced, XStack, YStack } from 'tamagui' - -import { Author } from '../author/author' -import { Avatar } from '../avatar' -import { Image } from '../image' -import { Paragraph } from '../typography' -import { Actions } from './components/actions' -import { Reactions } from './components/reactions' - -interface Props { - text?: React.ReactNode - images?: Array<{ url: string }> - reactions?: [] -} - -const ChatMessage = (props: Props) => { - const { text, images, reactions } = props - - const [hovered, setHovered] = React.useState(false) - - return ( - setHovered(true)} - onHoverOut={() => setHovered(false)} - > - {hovered && ( - - { - console.log('clicked') - }} - /> - - )} - - - - - - - {text && ( - - {text} - - )} - - {images?.map(image => ( - - - - ))} - - {reactions && ( - - - - )} - - - ) -} - -export { ChatMessage } diff --git a/packages/components/src/messages/components/actions.stories.tsx b/packages/components/src/messages/components/actions.stories.tsx index 712f6c93..ea827e53 100644 --- a/packages/components/src/messages/components/actions.stories.tsx +++ b/packages/components/src/messages/components/actions.stories.tsx @@ -1,11 +1,21 @@ import { Actions } from './actions' +import type { ReactionsType } from '../types' import type { Meta, StoryObj } from '@storybook/react' +const reactions: ReactionsType = { + love: new Set(['me', '1', '2', '3']), + 'thumbs-up': new Set(['me', '1', '2', '3']), + 'thumbs-down': new Set(['me', '1', '2', '3']), +} + // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction const meta: Meta = { title: 'Messages/actions', component: Actions, + args: { + reactions, + }, argTypes: {}, parameters: { layout: 'centered', diff --git a/packages/components/src/messages/components/actions.tsx b/packages/components/src/messages/components/actions.tsx index 8cd7f093..85d4d552 100644 --- a/packages/components/src/messages/components/actions.tsx +++ b/packages/components/src/messages/components/actions.tsx @@ -1,18 +1,47 @@ -import { AddReactionIcon, OptionsIcon, ReplyIcon } from '@status-im/icons/20' +import { useEffect } from 'react' + +import { + AddReactionIcon, + CopyIcon, + DeleteIcon, + EditIcon, + ForwardIcon, + LinkIcon, + OptionsIcon, + PinIcon, + ReplyIcon, +} from '@status-im/icons/20' import { Stack } from 'tamagui' -import { Button } from '../../button' +import { DropdownMenu } from '../../dropdown-menu' +import { IconButton } from '../../icon-button' +import { ReactionPopover } from './reaction-popover' + +import type { ReactionsType } from '../types' interface Props { - onClick: VoidFunction + reactions: ReactionsType + onOpenChange: (open: boolean) => void + onReplyPress: VoidFunction + // onEditPress: VoidFunction + // onDeletePress: VoidFunction } -export const Actions = (_props: Props) => { + +export const Actions = (props: Props) => { + const { reactions, onOpenChange, onReplyPress } = props + + useEffect(() => { + return () => onOpenChange(false) + }, [onOpenChange]) + return ( { shadowColor="rgba(9, 16, 28, 0.08)" zIndex={10} > - } borderRadius={0} /> - } borderRadius={0} /> - } borderRadius={0} /> + {/* REACTION */} + + } /> + + + {/* REPLY */} + } + onPress={onReplyPress} + /> + + {/* EDIT */} + {/* } + onPress={onEditPress} + /> */} + + {/* DELETE */} + {/* } + onPress={onDeletePress} + /> */} + + {/* OPTIONS MENU */} + + } /> + + } + label="Edit message" + onSelect={() => console.log('edit')} + /> + } + label="Reply" + onSelect={() => console.log('reply')} + /> + } + label="Copy text" + onSelect={() => console.log('copy')} + /> + } + label="Pin to the channel" + onSelect={() => console.log('pin')} + /> + } + label="Forward" + onSelect={() => console.log('forward')} + /> + } + label="Share link to message" + onSelect={() => console.log('share')} + /> + + + + } + label="Delete message" + danger + onSelect={() => console.log('delete')} + /> + + ) } diff --git a/packages/components/src/messages/components/reaction-popover.tsx b/packages/components/src/messages/components/reaction-popover.tsx new file mode 100644 index 00000000..7d793565 --- /dev/null +++ b/packages/components/src/messages/components/reaction-popover.tsx @@ -0,0 +1,64 @@ +import { XStack } from 'tamagui' + +import { Popover } from '../../popover' +import { ReactButton } from '../../react-button' + +import type { PopoverProps } from '../../popover' +import type { ReactionsType } from '../types' + +type Props = Omit & { + children: React.ReactElement + reactions: ReactionsType + onOpenChange?: PopoverProps['onOpenChange'] +} + +export const ReactionPopover = (props: Props) => { + const { children, reactions, onOpenChange, ...popoverProps } = props + + return ( + + {children} + + + + + + + + + + + + + ) +} diff --git a/packages/components/src/messages/components/reactions.stories.tsx b/packages/components/src/messages/components/reactions.stories.tsx index 5761436e..fd54447e 100644 --- a/packages/components/src/messages/components/reactions.stories.tsx +++ b/packages/components/src/messages/components/reactions.stories.tsx @@ -1,11 +1,21 @@ import { Reactions } from './reactions' +import type { ReactionsType } from '../types' import type { Meta, StoryObj } from '@storybook/react' +const reactions: ReactionsType = { + love: new Set(['me', '1', '2', '3']), + 'thumbs-up': new Set(['me', '1', '2', '3']), + 'thumbs-down': new Set(['me', '1', '2', '3']), +} + // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction const meta: Meta = { title: 'messages/reactions', component: Reactions, + args: { + reactions, + }, argTypes: {}, parameters: { design: { diff --git a/packages/components/src/messages/components/reactions.tsx b/packages/components/src/messages/components/reactions.tsx index 52a4ba22..e84c7313 100644 --- a/packages/components/src/messages/components/reactions.tsx +++ b/packages/components/src/messages/components/reactions.tsx @@ -1,94 +1,109 @@ -import { cloneElement } from 'react' - -import { AddReactionIcon } from '@status-im/icons/20' -import { - // AngryIcon, - LaughIcon, - LoveIcon, - SadIcon, - ThumbsDownIcon, - ThumbsUpIcon, -} from '@status-im/icons/reactions' -import { Stack, styled } from '@tamagui/core' import { XStack } from 'tamagui' -import { Paragraph } from '../../typography' +import { Dialog } from '../../dialog' +import { ReactButton } from '../../react-button' +import { Tooltip } from '../../tooltip/tooltip' +import { UserList } from '../../user-list' +import { ReactionPopover } from './reaction-popover' -import type React from 'react' - -// import { Pressable } from 'react-native' - -const ReactButton = styled(Stack, { - name: 'ReactButton', - accessibilityRole: 'button', - - cursor: 'pointer', - userSelect: 'none', - borderRadius: 8, - display: 'flex', - flexDirection: 'row', - space: 4, - alignItems: 'center', - justifyContent: 'center', - flexShrink: 0, - minWidth: 36, - height: 24, - paddingHorizontal: 8, - borderWidth: 1, - borderColor: '$neutral-20', - - hoverStyle: { - borderColor: '$neutral-30', - }, - - variants: { - selected: { - true: { - backgroundColor: '$neutral-30', - borderColor: '$neutral-30', - }, - }, - } as const, -}) - -type ReactionButtonProps = { - icon: React.ReactElement - count?: number - selected?: boolean -} - -const ReactionButton = (props: ReactionButtonProps) => { - const { count, selected, icon } = props - - return ( - - {cloneElement(icon, { color: '$neutral-100' })} - {count && ( - - {count} - - )} - - ) -} +import type { ReactButtonProps } from '../../react-button' +import type { ReactionsType, ReactionType } from '../types' type Props = { - reactions?: any[] + reactions: ReactionsType } export const Reactions = (props: Props) => { const { reactions } = props + const hasReaction = Object.values(reactions).some(value => value.size > 0) + + if (hasReaction === false) { + return null + } + return ( - - } selected /> - } /> - } /> - } /> - } /> - {/* FIX TAMAGUI BUG */} - {/* } /> */} - } /> + + {Object.entries(reactions).map(([type, value]) => ( + + ))} + + + + ) } + +const Reaction = (props: ReactButtonProps) => { + return ( + + + You, Mr Gandalf, Ariana Perlona and + reacted with {'[ICON]'} + + } + > + + + + + + + + ) +} diff --git a/packages/components/src/messages/index.tsx b/packages/components/src/messages/index.tsx index 04146085..a08371bf 100644 --- a/packages/components/src/messages/index.tsx +++ b/packages/components/src/messages/index.tsx @@ -1,50 +1,82 @@ -import { ChatMessage } from './chat-message' +import { Message } from './message' -export * from './chat-message' +import type { ReactionsType } from './types' + +export * from './message' + +const reactions: ReactionsType = { + love: new Set(['me', '1']), + 'thumbs-up': new Set(['3']), + 'thumbs-down': new Set(['me', '1', '2', '3']), +} export const Messages = () => { return ( <> - - - - + + + - - - - - - - - - + + + - ) diff --git a/packages/components/src/messages/messages.stories.tsx b/packages/components/src/messages/message.stories.tsx similarity index 74% rename from packages/components/src/messages/messages.stories.tsx rename to packages/components/src/messages/message.stories.tsx index 7d4ddadf..8cab05ef 100644 --- a/packages/components/src/messages/messages.stories.tsx +++ b/packages/components/src/messages/message.stories.tsx @@ -1,12 +1,25 @@ -import { ChatMessage } from './chat-message' +import { Message } from './message' +import type { ReactionsType } from './types' import type { Meta, StoryObj } from '@storybook/react' // More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction -const meta: Meta = { - // title: 'Messages', - component: ChatMessage, - argTypes: {}, +const meta: Meta = { + title: 'messages', + component: Message, + args: { + reactions: {}, + }, + argTypes: { + pinned: { + type: 'boolean', + defaultValue: false, + }, + reply: { + type: 'boolean', + defaultValue: false, + }, + }, parameters: { design: { type: 'figma', @@ -15,9 +28,13 @@ const meta: Meta = { }, } -type Story = StoryObj +type Story = StoryObj -const reactions = ['123'] +const reactions: ReactionsType = { + love: new Set(['me', '1', '2', '3']), + 'thumbs-up': new Set(['me', '1', '2', '3']), + 'thumbs-down': new Set(['me', '1', '2', '3']), +} // More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args export const Text: Story = { @@ -34,6 +51,22 @@ export const TextWithReactions: Story = { }, } +export const TextWithReply: Story = { + name: 'Text + Reply', + args: { + text: 'This is a simple message.', + reply: true, + }, +} + +export const TextPinned: Story = { + name: 'Text + Pinned', + args: { + text: 'This is a simple message.', + pinned: true, + }, +} + export const LongText: Story = { name: 'Long text', args: { diff --git a/packages/components/src/messages/message.tsx b/packages/components/src/messages/message.tsx new file mode 100644 index 00000000..548a1af9 --- /dev/null +++ b/packages/components/src/messages/message.tsx @@ -0,0 +1,127 @@ +import React from 'react' + +import { PinIcon } from '@status-im/icons/16' +import { Stack, Unspaced, XStack, YStack } from 'tamagui' + +import { Author } from '../author/author' +import { Avatar } from '../avatar' +import { Image } from '../image' +import { Reply } from '../reply' +import { Paragraph } from '../typography' +import { Actions } from './components/actions' +import { Reactions } from './components/reactions' + +import type { ReactionsType } from './types' + +interface Props { + text?: React.ReactNode + images?: Array<{ url: string }> + reactions: ReactionsType + reply?: boolean + pinned?: boolean +} + +const Message = (props: Props) => { + const { text, images, reactions, reply, pinned } = props + + const [hovered, setHovered] = React.useState(false) + const [actionsOpen, setActionsOpen] = React.useState(false) + + const active = actionsOpen || hovered + // + + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + > + {active && ( + + { + console.log('reply') + }} + /> + + )} + + {reply && ( + + + + )} + + {pinned && ( + + + + Steve + + + )} + + + + + + + + {text && ( + + {text} + + )} + + {images?.map(image => ( + + + + ))} + + {reactions && ( + + + + )} + + + + ) +} + +export { Message } diff --git a/packages/components/src/messages/types.ts b/packages/components/src/messages/types.ts new file mode 100644 index 00000000..72ea0f11 --- /dev/null +++ b/packages/components/src/messages/types.ts @@ -0,0 +1,12 @@ +export type ReactionType = + | 'love' + | 'laugh' + | 'thumbs-up' + | 'thumbs-down' + | 'sad' + | 'angry' + | 'add' + +export type ReactionsType = { + [key in ReactionType]?: Set +} diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx new file mode 100644 index 00000000..3d3237ee --- /dev/null +++ b/packages/components/src/popover/index.tsx @@ -0,0 +1 @@ +export { type PopoverProps, Popover } from './popover' diff --git a/packages/components/src/popover/popover.stories.tsx b/packages/components/src/popover/popover.stories.tsx new file mode 100644 index 00000000..003018e5 --- /dev/null +++ b/packages/components/src/popover/popover.stories.tsx @@ -0,0 +1,32 @@ +import { Button } from '../button' +import { Popover } from './popover' + +import type { Meta, StoryObj } from '@storybook/react' + +// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction +const meta: Meta = { + // title: 'Messages', + component: Popover, + argTypes: {}, + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Web?node-id=611%3A36006&t=Gyy71OAckl3b2TWj-4', + }, + }, +} + +type Story = StoryObj + +// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args +export const Default: Story = { + args: {}, + render: args => ( + + + some content + + ), +} + +export default meta diff --git a/packages/components/src/popover/popover.tsx b/packages/components/src/popover/popover.tsx new file mode 100644 index 00000000..5e24ff32 --- /dev/null +++ b/packages/components/src/popover/popover.tsx @@ -0,0 +1,51 @@ +import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover' +import { Stack } from 'tamagui' + +import type { PopoverContentProps } from '@radix-ui/react-popover' +import type { FunctionComponent } from 'react' + +interface Props { + children: [React.ReactElement, React.ReactElement] + onOpenChange?: (open: boolean) => void + modal?: false + side?: PopoverContentProps['side'] + sideOffset?: PopoverContentProps['sideOffset'] + align?: PopoverContentProps['align'] + alignOffset?: PopoverContentProps['alignOffset'] +} + +const Popover = (props: Props) => { + const { children, onOpenChange, modal, ...contentProps } = props + + const [trigger, content] = children + + return ( + + {trigger} + + {content} + + + ) +} + +const PopoverContent: FunctionComponent = props => { + const { children } = props + + return ( + + {children} + + ) +} + +Popover.Content = PopoverContent + +export { Popover as Popover } +export type { Props as PopoverProps } diff --git a/packages/components/src/react-button/index.tsx b/packages/components/src/react-button/index.tsx new file mode 100644 index 00000000..792f9ae8 --- /dev/null +++ b/packages/components/src/react-button/index.tsx @@ -0,0 +1 @@ +export { type ReactButtonProps, ReactButton } from './react-button' diff --git a/packages/components/src/react-button/react-button.stories.tsx b/packages/components/src/react-button/react-button.stories.tsx new file mode 100644 index 00000000..b1c6e04b --- /dev/null +++ b/packages/components/src/react-button/react-button.stories.tsx @@ -0,0 +1,67 @@ +import { XStack } from 'tamagui' + +import { ReactButton } from './react-button' + +import type { Meta, StoryObj } from '@storybook/react' + +// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction +const meta: Meta = { + title: 'ReactButton', + component: ReactButton, + args: {}, + argTypes: {}, + render: args => ( + + + + + + + + + ), +} + +type Story = StoryObj + +export const Outline: Story = { + name: 'Outline / 40px', + args: { variant: 'outline' }, +} + +export const OutlineSelected: Story = { + name: 'Outline / 40px / selected', + args: { variant: 'outline', selected: true }, +} + +export const Outline32: Story = { + name: 'Outline / 32px', + args: { variant: 'outline', size: 32 }, +} + +export const Outline32Selected: Story = { + name: 'Outline / 32px', + args: { variant: 'outline', size: 32, selected: true }, +} + +export const Ghost: Story = { + name: 'Ghost / 40px', + args: { variant: 'ghost' }, +} + +export const GhostSelected: Story = { + name: 'Ghost / 40px / selected', + args: { variant: 'ghost', selected: true }, +} + +export const Ghost32: Story = { + name: 'Ghost / 32px', + args: { variant: 'ghost', size: 32 }, +} + +export const Ghost32Selected: Story = { + name: 'Ghost / 32px', + args: { variant: 'ghost', size: 32, selected: true }, +} + +export default meta diff --git a/packages/components/src/react-button/react-button.tsx b/packages/components/src/react-button/react-button.tsx new file mode 100644 index 00000000..57636abf --- /dev/null +++ b/packages/components/src/react-button/react-button.tsx @@ -0,0 +1,141 @@ +import { forwardRef } from 'react' + +import { AddReactionIcon } from '@status-im/icons/20' +import { + AngryIcon, + LaughIcon, + LoveIcon, + SadIcon, + ThumbsDownIcon, + ThumbsUpIcon, +} from '@status-im/icons/reactions' +import { Stack, styled } from '@tamagui/core' + +import { Paragraph } from '../typography' + +import type { GetProps } from '@tamagui/core' +import type { Ref } from 'react' +import type { PressableProps } from 'react-native' + +const Button = styled(Stack, { + name: 'ReactButton', + accessibilityRole: 'button', + + cursor: 'pointer', + userSelect: 'none', + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderWidth: 1, + animation: 'fast', + space: 4, + + variants: { + variant: { + outline: { + borderColor: '$neutral-10', + hoverStyle: { borderColor: '$neutral-30' }, + pressStyle: { + backgroundColor: '$neutral-10', + borderColor: '$neutral-20', + }, + }, + + ghost: { + borderColor: 'transparent', + hoverStyle: { backgroundColor: '$neutral-10' }, + pressStyle: { backgroundColor: '$neutral-20' }, + }, + }, + + selected: { + true: { + backgroundColor: '$neutral-10', + borderColor: '$neutral-30', + }, + }, + + size: { + 40: { + borderRadius: 12, + width: 40, + height: 40, + }, + + 32: { + borderRadius: 10, + width: 32, + height: 32, + }, + + compact: { + borderRadius: 8, + minWidth: 36, + height: 24, + paddingHorizontal: 8, + }, + }, + } as const, +}) + +type ButtonProps = GetProps + +export const REACTIONS = { + love: LoveIcon, + laugh: LaughIcon, + 'thumbs-up': ThumbsUpIcon, + 'thumbs-down': ThumbsDownIcon, + sad: SadIcon, + angry: AngryIcon, + add: AddReactionIcon, +} as const + +interface Props extends PressableProps { + icon: keyof typeof REACTIONS + variant?: ButtonProps['variant'] + size?: ButtonProps['size'] + // FIXME: use aria-selected + selected?: boolean + count?: number + // FIXME: update to latest RN + 'aria-expanded'?: boolean + 'aria-selected'?: boolean +} + +const ReactButton = (props: Props, ref: Ref) => { + const { + icon, + variant = 'outline', + size = 40, + count, + ...pressableProps + } = props + + const Icon = REACTIONS[icon] + + const selected = + props.selected || props['aria-expanded'] || props['aria-selected'] + + return ( + + ) +} + +const _ReactButton = forwardRef(ReactButton) + +export { _ReactButton as ReactButton } +export type { Props as ReactButtonProps } diff --git a/packages/components/src/reply/index.tsx b/packages/components/src/reply/index.tsx new file mode 100644 index 00000000..c13fa7cd --- /dev/null +++ b/packages/components/src/reply/index.tsx @@ -0,0 +1 @@ +export { type ReplyProps, Reply } from './reply' diff --git a/packages/components/src/reply/reply.stories.tsx b/packages/components/src/reply/reply.stories.tsx new file mode 100644 index 00000000..6cecf480 --- /dev/null +++ b/packages/components/src/reply/reply.stories.tsx @@ -0,0 +1,84 @@ +import { action } from '@storybook/addon-actions' + +import { Reply } from './reply' + +import type { Meta, StoryObj } from '@storybook/react' + +const meta: Meta = { + component: Reply, + argTypes: {}, + args: { + name: 'Alisher Yakupov', + src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixid=Mnw0MDAxMTJ8MHwxfHNlYXJjaHw0fHxhdmF0YXJ8ZW58MHx8fHwxNjc1MjU4NTkw&ixlib=rb-4.0.3', + }, + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=3173%3A55936&t=QgRAQPXVREVsrDg7-11', + }, + }, +} + +type Story = StoryObj + +export const Text: Story = { + args: { + type: 'text', + onClose: undefined, + }, +} + +export const TextClose: Story = { + name: 'Text + Close', + args: { + ...Text.args, + onClose: action('close'), + }, +} + +export const Image: Story = { + args: { + type: 'image', + onClose: undefined, + }, +} + +export const ImageClose: Story = { + name: 'Image + Close', + args: { + ...Image.args, + onClose: action('close'), + }, +} + +export const GIF: Story = { + args: { + type: 'gif', + onClose: undefined, + }, +} + +export const GIFClose: Story = { + name: 'GIF + Close', + args: { + ...GIF.args, + onClose: action('close'), + }, +} + +export const Deleted: Story = { + args: { + type: 'deleted', + onClose: undefined, + }, +} + +export const DeletedClose: Story = { + name: 'Deleted + Close', + args: { + type: 'deleted', + onClose: action('close'), + }, +} + +export default meta diff --git a/packages/components/src/reply/reply.tsx b/packages/components/src/reply/reply.tsx new file mode 100644 index 00000000..5f88d45f --- /dev/null +++ b/packages/components/src/reply/reply.tsx @@ -0,0 +1,97 @@ +import { CloseIcon } from '@status-im/icons/12' +import { SadIcon } from '@status-im/icons/16' +import { Path, Svg } from 'react-native-svg' +import { Stack, Unspaced, XStack } from 'tamagui' + +import { Avatar } from '../avatar' +import { Button } from '../button' +import { Paragraph } from '../typography' + +interface Props { + type: 'text' | 'gif' | 'image' | 'deleted' + onClose?: VoidFunction + name: string + src: string +} + +// FIXME: This should accept message or message ID and render the message accordingly +const Reply = (props: Props) => { + const { type, name, onClose, src } = props + + const content = + type !== 'deleted' ? ( + + + + + + + + + + + {name} + + + + {type === 'text' && 'What is the meaning of life? '} + {type === 'gif' && 'GIF'} + {type === 'image' && '5 photos'} + + + ) : ( + + + + + + + + + + + Message deleted + + + ) + + return ( + + {content} + + {/* FIXME: This should be regular button with size 24 */} + {onClose && ( + } + onPress={onClose} + /> + )} + + ) +} + +const Connector = () => ( + + + +) + +export { Reply } +export type { Props as ReplyProps } diff --git a/packages/components/src/sheet/index.tsx b/packages/components/src/sheet/index.tsx new file mode 100644 index 00000000..7af56847 --- /dev/null +++ b/packages/components/src/sheet/index.tsx @@ -0,0 +1 @@ +export { Sheet } from './sheet' diff --git a/packages/components/src/sheet/sheet.stories.tsx b/packages/components/src/sheet/sheet.stories.tsx new file mode 100644 index 00000000..aa1ed673 --- /dev/null +++ b/packages/components/src/sheet/sheet.stories.tsx @@ -0,0 +1,32 @@ +import { Button } from '../button' +import { Sheet } from './sheet' + +import type { Meta, StoryObj } from '@storybook/react' + +// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction +const meta: Meta = { + // title: 'Messages', + component: Sheet, + argTypes: {}, + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Web?node-id=611%3A36006&t=Gyy71OAckl3b2TWj-4', + }, + }, +} + +type Story = StoryObj + +// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args +export const Default: Story = { + args: {}, + render: args => ( + + + hello + + ), +} + +export default meta diff --git a/packages/components/src/sheet/sheet.tsx b/packages/components/src/sheet/sheet.tsx new file mode 100644 index 00000000..3c151d60 --- /dev/null +++ b/packages/components/src/sheet/sheet.tsx @@ -0,0 +1,79 @@ +import { Content, Overlay, Portal, Root, Trigger } from '@radix-ui/react-dialog' + +import type React from 'react' + +interface Props { + children: [React.ReactElement, React.ReactElement] + open?: boolean + onOpenChange?: (open: boolean) => void + press?: 'normal' | 'long' +} + +const Sheet = (props: Props) => { + const { children, open, onOpenChange } = props + + const [trigger, content] = children + + return ( + + {/* TRIGGER */} + {trigger} + + {/* CONTENT */} + + + {content} + + + ) +} + +interface DialogContentProps { + children: React.ReactNode + initialFocusRef?: React.RefObject +} + +const SheetContent = (props: DialogContentProps) => { + const { children, initialFocusRef } = props + + const handleOpenAutoFocus = (event: Event) => { + if (initialFocusRef?.current) { + event.preventDefault() + initialFocusRef.current.focus() + } + } + + return ( + + {children} + + ) +} + +Sheet.Content = SheetContent + +export { Sheet } diff --git a/packages/components/src/tooltip/index.tsx b/packages/components/src/tooltip/index.tsx new file mode 100644 index 00000000..6519ab08 --- /dev/null +++ b/packages/components/src/tooltip/index.tsx @@ -0,0 +1 @@ +export { type TooltipProps, Tooltip } from './tooltip' diff --git a/packages/components/src/tooltip/tooltip.native.tsx b/packages/components/src/tooltip/tooltip.native.tsx new file mode 100644 index 00000000..fad0ae51 --- /dev/null +++ b/packages/components/src/tooltip/tooltip.native.tsx @@ -0,0 +1,3 @@ +import type { PropsWithChildren } from 'react' + +export const Tooltip = ({ children }: PropsWithChildren) => children diff --git a/packages/components/src/tooltip/tooltip.stories.tsx b/packages/components/src/tooltip/tooltip.stories.tsx new file mode 100644 index 00000000..97e02370 --- /dev/null +++ b/packages/components/src/tooltip/tooltip.stories.tsx @@ -0,0 +1,30 @@ +import { Button } from '../button' +import { Tooltip } from './tooltip' + +import type { Meta, StoryObj } from '@storybook/react' + +const meta: Meta = { + component: Tooltip, + argTypes: {}, + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Web?node-id=15032%3A174184&t=PHVNitU0s0KwOi8L-0', + }, + }, +} + +type Story = StoryObj + +export const Default: Story = { + args: { + content: 'Sebastian Vettel reacted with a heart', + }, + render: args => ( + + + + ), +} + +export default meta diff --git a/packages/components/src/tooltip/tooltip.tsx b/packages/components/src/tooltip/tooltip.tsx new file mode 100644 index 00000000..7dbe1acb --- /dev/null +++ b/packages/components/src/tooltip/tooltip.tsx @@ -0,0 +1,83 @@ +import { forwardRef } from 'react' + +import { + Arrow, + Content, + Portal, + Root, + TooltipProvider, + Trigger, +} from '@radix-ui/react-tooltip' +import { Stack } from 'tamagui' + +import { Paragraph } from '../typography' + +import type { TooltipContentProps } from '@radix-ui/react-tooltip' +import type { Ref } from 'react' + +interface Props { + children: React.ReactElement + content: React.ReactNode + delayDuration?: number + side?: TooltipContentProps['side'] + sideOffset?: TooltipContentProps['sideOffset'] + align?: TooltipContentProps['align'] + alignOffset?: TooltipContentProps['alignOffset'] +} + +const Tooltip = (props: Props, ref: Ref) => { + const { + children, + content, + delayDuration, + side, + sideOffset, + align, + alignOffset, + ...triggerProps + } = props + + return ( + + + + {children} + + + + + + {typeof content === 'string' ? ( + + {content} + + ) : ( + content + )} + + + + + + + ) +} + +const _Tooltip = forwardRef(Tooltip) + +export { _Tooltip as Tooltip } +export type { Props as TooltipProps } diff --git a/packages/components/src/topbar/topbar.tsx b/packages/components/src/topbar/topbar.tsx index ad1bfb2d..b5b69746 100644 --- a/packages/components/src/topbar/topbar.tsx +++ b/packages/components/src/topbar/topbar.tsx @@ -1,13 +1,21 @@ import { Divider, IconButton, Paragraph } from '@status-im/components' import { ArrowLeftIcon, + CommunitiesIcon, + DeleteIcon, + DownloadIcon, LockedIcon, MembersIcon, + MutedIcon, OptionsIcon, + ShareIcon, + UpToDateIcon, } from '@status-im/icons/20' import { Stack } from '@tamagui/core' import { BlurView } from 'expo-blur' +import { DropdownMenu } from '../dropdown-menu' + import type { GetProps, StackProps } from '@tamagui/core' type BaseProps = GetProps @@ -104,7 +112,46 @@ const Topbar = (props: Props) => { blurred={isBlurred} /> - } blurred={isBlurred} /> + + } /> + + + } + label="View channel members and details" + onSelect={() => console.log('click')} + /> + } + label="Mute channel" + onSelect={() => console.log('click')} + /> + } + label="Mark as read" + onSelect={() => console.log('click')} + /> + } + label="Fetch messages" + onSelect={() => console.log('click')} + /> + } + label="Share link to the channel" + onSelect={() => console.log('click')} + /> + + + + } + label="Clear history" + onSelect={() => console.log('click')} + danger + /> + + diff --git a/packages/icons/vite.config.ts b/packages/icons/vite.config.ts index 4db1b71d..8480e869 100644 --- a/packages/icons/vite.config.ts +++ b/packages/icons/vite.config.ts @@ -23,7 +23,6 @@ export default defineConfig(({ mode }) => { './src/reactions/index.ts', ], fileName(format, entryName) { - console.log('fileName > format, entryName', format, entryName) // const [name] = entryName.split('/') return `icons-${entryName}.${format}.js` }, diff --git a/yarn.lock b/yarn.lock index 2e31731c..7ad53f79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3612,6 +3612,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.0" +"@radix-ui/react-arrow@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.1.tgz#5246adf79e97f89e819af68da51ddcf349ecf1c4" + integrity sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-checkbox@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-0.1.5.tgz#3a6bd54ba1720c8e5c03852acf460e35dfbe9da3" @@ -3665,6 +3673,17 @@ "@radix-ui/react-primitive" "1.0.0" "@radix-ui/react-slot" "1.0.0" +"@radix-ui/react-collection@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.1.tgz#259506f97c6703b36291826768d3c1337edd1de5" + integrity sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-slot" "1.0.1" + "@radix-ui/react-compose-refs@0.1.0", "@radix-ui/react-compose-refs@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz#cff6e780a0f73778b976acff2c2a5b6551caab95" @@ -3758,6 +3777,18 @@ "@radix-ui/react-use-callback-ref" "1.0.0" "@radix-ui/react-use-escape-keydown" "1.0.0" +"@radix-ui/react-dismissable-layer@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.2.tgz#f04d1061bddf00b1ca304148516b9ddc62e45fb2" + integrity sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-escape-keydown" "1.0.2" + "@radix-ui/react-dropdown-menu@^0.1.6": version "0.1.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz#3203229788cd57e552c9f19dcc7008e2b545919c" @@ -3772,6 +3803,20 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-use-controllable-state" "0.1.0" +"@radix-ui/react-dropdown-menu@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.2.tgz#758ca7733dc79b3a6523d2d5a8d33970ec7ece1b" + integrity sha512-r0kN0fstrSi+uAdK2GkLxnnbhqVBy/9Q4o4PvGOYipW0BldQlYBMSmZprvCNj2i2mAATx16kvzIn12GnaGjbMw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-id" "1.0.0" + "@radix-ui/react-menu" "2.0.2" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-use-controllable-state" "1.0.0" + "@radix-ui/react-focus-guards@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-0.1.0.tgz#ba3b6f902cba7826569f8edc21ff8223dece7def" @@ -3779,6 +3824,13 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-focus-guards@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz#339c1c69c41628c1a5e655f15f7020bf11aa01fa" + integrity sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-focus-scope@0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-0.1.4.tgz#c830724e212d42ffaaa81aee49533213d09b47df" @@ -3789,6 +3841,16 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-use-callback-ref" "0.1.0" +"@radix-ui/react-focus-scope@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.1.tgz#faea8c25f537c5a5c38c50914b63722db0e7f951" + integrity sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-id@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-0.1.5.tgz#010d311bedd5a2884c1e9bb6aaaa4e6cc1d1d3b8" @@ -3840,6 +3902,31 @@ aria-hidden "^1.1.1" react-remove-scroll "^2.4.0" +"@radix-ui/react-menu@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.2.tgz#54d4e040407962af95ff3c66612749661a504de7" + integrity sha512-H5dtBi/k3tc45IMd2Pu+Q2PyONFlsYJ5sWUlflSs8BQRghh5GhJHLRuB1yb88VOywuzzvGkaR/HUJJ65Jf2POA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-collection" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-direction" "1.0.0" + "@radix-ui/react-dismissable-layer" "1.0.2" + "@radix-ui/react-focus-guards" "1.0.0" + "@radix-ui/react-focus-scope" "1.0.1" + "@radix-ui/react-id" "1.0.0" + "@radix-ui/react-popper" "1.1.0" + "@radix-ui/react-portal" "1.0.1" + "@radix-ui/react-presence" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-roving-focus" "1.0.2" + "@radix-ui/react-slot" "1.0.1" + "@radix-ui/react-use-callback-ref" "1.0.0" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.5" + "@radix-ui/react-popover@^0.1.6": version "0.1.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-0.1.6.tgz#788e969239d9c55239678e615ab591b6b7ba5cdc" @@ -3861,6 +3948,28 @@ aria-hidden "^1.1.1" react-remove-scroll "^2.4.0" +"@radix-ui/react-popover@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.0.3.tgz#65ae2ee1fca2d7fd750308549eb8e0857c6160fe" + integrity sha512-YwedSukfWsyJs3/yP3yXUq44k4/JBe3jqU63Z8v2i19qZZ3dsx32oma17ztgclWPNuqp3A+Xa9UiDlZHyVX8Vg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-dismissable-layer" "1.0.2" + "@radix-ui/react-focus-guards" "1.0.0" + "@radix-ui/react-focus-scope" "1.0.1" + "@radix-ui/react-id" "1.0.0" + "@radix-ui/react-popper" "1.1.0" + "@radix-ui/react-portal" "1.0.1" + "@radix-ui/react-presence" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-slot" "1.0.1" + "@radix-ui/react-use-controllable-state" "1.0.0" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.5" + "@radix-ui/react-popper@0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-0.1.4.tgz#dfc055dcd7dfae6a2eff7a70d333141d15a5d029" @@ -3892,6 +4001,23 @@ "@radix-ui/react-use-size" "1.0.0" "@radix-ui/rect" "1.0.0" +"@radix-ui/react-popper@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.0.tgz#2be7e4c0cd4581f54277ca33a981c9037d2a8e60" + integrity sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@floating-ui/react-dom" "0.7.2" + "@radix-ui/react-arrow" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-layout-effect" "1.0.0" + "@radix-ui/react-use-rect" "1.0.0" + "@radix-ui/react-use-size" "1.0.0" + "@radix-ui/rect" "1.0.0" + "@radix-ui/react-portal@0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-0.1.4.tgz#17bdce3d7f1a9a0b35cb5e935ab8bc562441a7d2" @@ -3909,6 +4035,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.0" +"@radix-ui/react-portal@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.1.tgz#169c5a50719c2bb0079cf4c91a27aa6d37e5dd33" + integrity sha512-NY2vUWI5WENgAT1nfC6JS7RU5xRYBfjZVLq0HmgEN1Ezy3rk/UruMV4+Rd0F40PEaFC5SrLS1ixYvcYIQrb4Ig== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-presence@0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-0.1.2.tgz#9f11cce3df73cf65bc348e8b76d891f0d54c1fe3" @@ -3943,6 +4077,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.0" +"@radix-ui/react-primitive@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz#c1ebcce283dd2f02e4fbefdaa49d1cb13dbc990a" + integrity sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-slot" "1.0.1" + "@radix-ui/react-roving-focus@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-0.1.5.tgz#cc48d17a36b56f253d54905b0fd60ee134cb97ee" @@ -3974,6 +4116,22 @@ "@radix-ui/react-use-callback-ref" "1.0.0" "@radix-ui/react-use-controllable-state" "1.0.0" +"@radix-ui/react-roving-focus@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.2.tgz#d8ac2e3b8006697bdfc2b0eb06bef7e15b6245de" + integrity sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-collection" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-direction" "1.0.0" + "@radix-ui/react-id" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-controllable-state" "1.0.0" + "@radix-ui/react-separator@^0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-0.1.4.tgz#383ad0f82b364d9982a978d752084af3598e4090" @@ -3998,6 +4156,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.0" +"@radix-ui/react-slot@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.1.tgz#e7868c669c974d649070e9ecbec0b367ee0b4d81" + integrity sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-tabs@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.0.tgz#135c67f1f2bd9ada69a3f6e38dd897d459af5fe5" @@ -4073,6 +4239,25 @@ "@radix-ui/react-use-controllable-state" "1.0.0" "@radix-ui/react-visually-hidden" "1.0.0" +"@radix-ui/react-tooltip@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.3.tgz#a8c7e7b2b542cdfe7e94122af79079f7de3f90ff" + integrity sha512-cmc9qV4KpgqdXVTn1K8KN8MnuSXvw+E719pKwyvpCGrQ+0AA2qTjcIL3uxCj4jc4k3sDR36RF7R3H7N5hPybBQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-dismissable-layer" "1.0.2" + "@radix-ui/react-id" "1.0.0" + "@radix-ui/react-popper" "1.1.0" + "@radix-ui/react-portal" "1.0.1" + "@radix-ui/react-presence" "1.0.0" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/react-slot" "1.0.1" + "@radix-ui/react-use-controllable-state" "1.0.0" + "@radix-ui/react-visually-hidden" "1.0.1" + "@radix-ui/react-use-body-pointer-events@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.1.tgz#63e7fd81ca7ffd30841deb584cd2b7f460df2597" @@ -4134,6 +4319,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.0" +"@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" + integrity sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-layout-effect@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz#ebf71bd6d2825de8f1fbb984abf2293823f0f223" @@ -4202,6 +4395,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.0" +"@radix-ui/react-visually-hidden@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.1.tgz#9a4ac4fc97ae8d72a10e727f16b3121b5f0aa469" + integrity sha512-K1hJcCMfWfiYUibRqf3V8r5Drpyf7rh44jnrwAbdvI5iCCijilBBeyQv9SKidYNZIopMdCyR9FnIjkHxHN0FcQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.1" + "@radix-ui/rect@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-0.1.1.tgz#95b5ba51f469bea6b1b841e2d427e17e37d38419" @@ -14437,6 +14638,7 @@ node-fetch-native@^1.0.1: node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.x.x: version "2.6.7" + uid "1b5d62978f2ed07b99444f64f0df39f960a6d34d" resolved "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz#1b5d62978f2ed07b99444f64f0df39f960a6d34d" node-forge@^1.1.0, node-forge@^1.2.1, node-forge@^1.3.1: @@ -15883,6 +16085,17 @@ react-remove-scroll-bar@^2.3.3: react-style-singleton "^2.2.1" tslib "^2.0.0" +react-remove-scroll@2.5.5, react-remove-scroll@^2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77" + integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw== + dependencies: + react-remove-scroll-bar "^2.3.3" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + react-remove-scroll@^2.4.0: version "2.4.4" resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.4.4.tgz#2dfff377cf17efc00de39dad51c143fc7a1b9e3e" @@ -15894,17 +16107,6 @@ react-remove-scroll@^2.4.0: use-callback-ref "^1.2.3" use-sidecar "^1.0.1" -react-remove-scroll@^2.5.5: - version "2.5.5" - resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77" - integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw== - dependencies: - react-remove-scroll-bar "^2.3.3" - react-style-singleton "^2.2.1" - tslib "^2.1.0" - use-callback-ref "^1.3.0" - use-sidecar "^1.1.2" - react-router-dom@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d"