Message reactions, add Tabs, update Dialog and Tooltip (#361)
* add Tabs component * update Tooltip styling * add reactions dialog * use IconButton and simplify ReactButton * add PressableTrigger to dialog * update css reset * update pressable type * fix text story name * update dynamic button props * add counter to TabsTrigger * fix casing in USerListProps * make Dialog.Content customizable * update dialogs
This commit is contained in:
parent
6ed390b0dc
commit
7c7e8f78ab
|
@ -71,6 +71,7 @@ button,
|
|||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
all: unset;
|
||||
}
|
||||
/*
|
||||
8. Avoid text overflows
|
||||
|
|
|
@ -71,6 +71,7 @@ button,
|
|||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
all: unset;
|
||||
}
|
||||
/*
|
||||
8. Avoid text overflows
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"@radix-ui/react-dialog": "^1.0.3",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.4",
|
||||
"@radix-ui/react-popover": "^1.0.5",
|
||||
"@radix-ui/react-tabs": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.5",
|
||||
"@status-im/icons": "*",
|
||||
"@tamagui/animations-css": "1.7.7",
|
||||
|
|
|
@ -17,7 +17,7 @@ const Counter = (props: Props) => {
|
|||
|
||||
return (
|
||||
<Base type={type}>
|
||||
<Text size={11} color={textColor[type]}>
|
||||
<Text size={11} weight="medium" color={textColors[type]}>
|
||||
{value > 99 ? '99+' : value}
|
||||
</Text>
|
||||
</Base>
|
||||
|
@ -31,7 +31,7 @@ const Base = styled(View, {
|
|||
backgroundColor: '$primary-50',
|
||||
paddingHorizontal: 3,
|
||||
paddingVertical: 0,
|
||||
borderRadius: '6px', // TODO: use tokens when fixed its definition
|
||||
borderRadius: 6, // TODO: use tokens when fixed its definition
|
||||
height: 16,
|
||||
minWidth: 16,
|
||||
maxWidth: 28,
|
||||
|
@ -39,6 +39,8 @@ const Base = styled(View, {
|
|||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexBasis: 'fit-content',
|
||||
borderWidth: 1,
|
||||
borderColor: 'transparent',
|
||||
|
||||
variants: {
|
||||
type: {
|
||||
|
@ -54,13 +56,12 @@ const Base = styled(View, {
|
|||
outline: {
|
||||
backgroundColor: 'transparent',
|
||||
borderColor: '$neutral-20',
|
||||
borderWidth: '1px',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const textColor: Record<NonNullable<Props['type']>, ColorTokens> = {
|
||||
const textColors: Record<NonNullable<Props['type']>, ColorTokens> = {
|
||||
default: '$white-100',
|
||||
secondary: '$neutral-100',
|
||||
outline: '$neutral-100',
|
||||
|
|
|
@ -24,7 +24,10 @@ export const Default: Story = {
|
|||
render: () => (
|
||||
<Dialog>
|
||||
<Button>Trigger</Button>
|
||||
<Dialog.Content>test</Dialog.Content>
|
||||
|
||||
<Dialog.Content borderRadius={16} width={400}>
|
||||
test
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { forwardRef } from 'react'
|
||||
import { cloneElement, forwardRef } from 'react'
|
||||
|
||||
import { Content, Overlay, Portal, Root, Trigger } from '@radix-ui/react-dialog'
|
||||
import { Stack, styled, useMedia } from 'tamagui'
|
||||
|
||||
import { Sheet } from '../sheet'
|
||||
import {
|
||||
Close,
|
||||
Content,
|
||||
Overlay,
|
||||
Portal,
|
||||
Root,
|
||||
Trigger,
|
||||
} from '@radix-ui/react-dialog'
|
||||
|
||||
import type { DialogTriggerProps } from '@radix-ui/react-dialog'
|
||||
import type { Ref } from 'react'
|
||||
import type React from 'react'
|
||||
|
||||
|
@ -15,74 +20,61 @@ interface Props {
|
|||
press?: 'normal' | 'long'
|
||||
}
|
||||
|
||||
const Wrapper = styled(Stack, {
|
||||
position: 'absolute',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
})
|
||||
|
||||
// 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 { children, open, onOpenChange, press = 'normal' } = props
|
||||
|
||||
const [trigger, content] = children
|
||||
|
||||
const media = useMedia()
|
||||
// const media = useMedia()
|
||||
|
||||
if (media.sm) {
|
||||
return (
|
||||
<Sheet>
|
||||
{trigger}
|
||||
{content}
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
// if (media.sm) {
|
||||
// return (
|
||||
// <Sheet>
|
||||
// {trigger}
|
||||
// {content}
|
||||
// </Sheet>
|
||||
// )
|
||||
// }
|
||||
|
||||
return (
|
||||
<Root open={open} onOpenChange={onOpenChange}>
|
||||
{/* TRIGGER */}
|
||||
<Trigger asChild>{trigger}</Trigger>
|
||||
<Trigger asChild>
|
||||
<PressableTrigger press={press}>{trigger}</PressableTrigger>
|
||||
</Trigger>
|
||||
|
||||
{/* CONTENT */}
|
||||
<Portal>
|
||||
<Wrapper>
|
||||
<Overlay
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
}}
|
||||
/>
|
||||
{content}
|
||||
</Wrapper>
|
||||
<Overlay
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
}}
|
||||
/>
|
||||
{content}
|
||||
</Portal>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
const PressableTrigger = forwardRef(function _PressableTrigger(
|
||||
props: DialogTriggerProps & {
|
||||
press: Props['press']
|
||||
children: React.ReactElement
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const { children, press, onClick, ...triggerProps } = props
|
||||
const handler = press === 'normal' ? 'onPress' : 'onLongPress'
|
||||
|
||||
return cloneElement(children, { ref, ...triggerProps, [handler]: onClick })
|
||||
})
|
||||
|
||||
interface DialogContentProps {
|
||||
// title: string
|
||||
// description?: string
|
||||
children: React.ReactNode
|
||||
// action: string
|
||||
// onAction: (close: VoidFunction) => void
|
||||
// onCancel?: () => void
|
||||
borderRadius: 8 | 12 | 16
|
||||
width: number
|
||||
initialFocusRef?: React.RefObject<HTMLElement>
|
||||
}
|
||||
|
||||
|
@ -96,25 +88,25 @@ const DialogContent = (props: DialogContentProps, ref: Ref<HTMLDivElement>) => {
|
|||
}
|
||||
}
|
||||
|
||||
const media = useMedia()
|
||||
// const media = useMedia()
|
||||
|
||||
if (media.sm) {
|
||||
return <Sheet.Content>{children}</Sheet.Content>
|
||||
}
|
||||
// if (media.sm) {
|
||||
// return <Sheet.Content>{children}</Sheet.Content>
|
||||
// }
|
||||
|
||||
return (
|
||||
<Content
|
||||
ref={ref}
|
||||
onOpenAutoFocus={handleOpenAutoFocus}
|
||||
// TODO: use tamagui components
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
padding: 8,
|
||||
width: 400,
|
||||
borderRadius: 8,
|
||||
position: 'fixed',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: props.borderRadius,
|
||||
width: props.width,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -124,4 +116,4 @@ const DialogContent = (props: DialogContentProps, ref: Ref<HTMLDivElement>) => {
|
|||
|
||||
Dialog.Content = forwardRef(DialogContent)
|
||||
|
||||
export { Dialog }
|
||||
export { Close, Dialog }
|
||||
|
|
|
@ -1 +1 @@
|
|||
export { Dialog } from './dialog'
|
||||
export { Close, Dialog } from './dialog'
|
||||
|
|
|
@ -6,7 +6,6 @@ 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<typeof DynamicButton> = {
|
||||
title: 'DynamicButton',
|
||||
component: DynamicButton,
|
||||
args: {},
|
||||
argTypes: {},
|
||||
|
|
|
@ -6,10 +6,9 @@ import { Stack, styled } from '@tamagui/core'
|
|||
import { Shadow } from '../shadow'
|
||||
import { Text } from '../text'
|
||||
|
||||
import type { GetVariants } from '../types'
|
||||
import type { ColorTokens, StackProps } from '@tamagui/core'
|
||||
import type { GetVariants, PressableProps } from '../types'
|
||||
import type { ColorTokens } from '@tamagui/core'
|
||||
import type { Ref } from 'react'
|
||||
import type { PressableProps } from 'react-native'
|
||||
|
||||
type Variants = GetVariants<typeof Button>
|
||||
|
||||
|
@ -27,7 +26,7 @@ const DynamicButton = (props: Props, ref: Ref<HTMLButtonElement>) => {
|
|||
return (
|
||||
<Shadow variant="$2" borderRadius={999}>
|
||||
<Button
|
||||
{...(pressableProps as unknown as StackProps)} // TODO: Tamagui has incorrect types for PressableProps
|
||||
{...(pressableProps as unknown as object)} // TODO: Tamagui has incorrect types for PressableProps
|
||||
ref={ref}
|
||||
type={type}
|
||||
iconOnly={showCount === false}
|
||||
|
@ -51,8 +50,8 @@ export type { Props as DynamicButtonProps }
|
|||
|
||||
const Button = styled(Stack, {
|
||||
name: 'DynamicButton',
|
||||
accessibilityRole: 'button',
|
||||
tag: 'button',
|
||||
accessibilityRole: 'button',
|
||||
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useState } from 'react'
|
||||
|
||||
import type { PressableProps } from '../types'
|
||||
import type { ColorTokens } from 'tamagui'
|
||||
import type { MouseEvent } from 'react-native'
|
||||
import type { ColorTokens, TamaguiComponentPropsBase } from 'tamagui'
|
||||
|
||||
type Config = {
|
||||
default: ColorTokens
|
||||
|
@ -12,8 +13,9 @@ type Config = {
|
|||
|
||||
type Return = {
|
||||
color: ColorTokens
|
||||
// FIXME: use PressableProps instead TamaguiComponentPropsBase, fix necessary in Tamagui
|
||||
pressableProps: Pick<
|
||||
PressableProps,
|
||||
TamaguiComponentPropsBase,
|
||||
'onHoverIn' | 'onHoverOut' | 'onPressIn' | 'onPressOut'
|
||||
>
|
||||
}
|
||||
|
@ -49,11 +51,11 @@ export const usePressableColors = (
|
|||
color: styles[key],
|
||||
pressableProps: {
|
||||
onHoverIn: event => {
|
||||
props.onHoverIn?.(event)
|
||||
props.onHoverIn?.(event as unknown as MouseEvent)
|
||||
setHovered(true)
|
||||
},
|
||||
onHoverOut: event => {
|
||||
props.onHoverOut?.(event)
|
||||
props.onHoverOut?.(event as unknown as MouseEvent)
|
||||
setHovered(false)
|
||||
},
|
||||
onPressIn: event => {
|
||||
|
|
|
@ -5,7 +5,6 @@ import { Stack, styled } from 'tamagui'
|
|||
import { usePressableColors } from '../hooks/use-pressable-colors'
|
||||
|
||||
import type { GetVariants, PressableProps } from '../types'
|
||||
import type { StackProps } from '@tamagui/core'
|
||||
import type { Ref } from 'react'
|
||||
|
||||
type Variants = GetVariants<typeof Base>
|
||||
|
@ -41,8 +40,8 @@ const IconButton = (props: Props, ref: Ref<HTMLButtonElement>) => {
|
|||
|
||||
return (
|
||||
<Base
|
||||
{...(buttonProps as unknown as StackProps)} // TODO: Tamagui has incorrect types for PressableProps
|
||||
{...(pressableProps as unknown as StackProps)} // TODO: Tamagui has incorrect types for PressableProps
|
||||
{...(buttonProps as unknown as object)} // TODO: Tamagui has incorrect types for PressableProps
|
||||
{...pressableProps}
|
||||
ref={ref}
|
||||
variant={blur ? undefined : variant}
|
||||
active={blur ? undefined : selected ? variant : undefined}
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
import {
|
||||
AngryIcon,
|
||||
LaughIcon,
|
||||
LoveIcon,
|
||||
SadIcon,
|
||||
ThumbsDownIcon,
|
||||
ThumbsUpIcon,
|
||||
} from '@status-im/icons/reactions'
|
||||
import { XStack } from 'tamagui'
|
||||
|
||||
import { IconButton } from '../../icon-button'
|
||||
import { Popover } from '../../popover'
|
||||
import { ReactButton } from '../../react-button'
|
||||
|
||||
import type { PopoverProps } from '../../popover'
|
||||
import type { ReactionsType } from '../types'
|
||||
|
@ -12,6 +20,15 @@ type Props = Omit<PopoverProps, 'children'> & {
|
|||
onOpenChange?: PopoverProps['onOpenChange']
|
||||
}
|
||||
|
||||
export const REACTIONS_ICONS = {
|
||||
love: <LoveIcon />,
|
||||
laugh: <LaughIcon />,
|
||||
'thumbs-up': <ThumbsUpIcon />,
|
||||
'thumbs-down': <ThumbsDownIcon />,
|
||||
sad: <SadIcon />,
|
||||
angry: <AngryIcon />,
|
||||
} as const
|
||||
|
||||
export const ReactionPopover = (props: Props) => {
|
||||
const { children, reactions, onOpenChange, ...popoverProps } = props
|
||||
|
||||
|
@ -21,40 +38,34 @@ export const ReactionPopover = (props: Props) => {
|
|||
|
||||
<Popover.Content>
|
||||
<XStack space={2} padding={2}>
|
||||
<ReactButton
|
||||
<IconButton
|
||||
icon={REACTIONS_ICONS['love']}
|
||||
variant="ghost"
|
||||
size={32}
|
||||
icon="love"
|
||||
selected={reactions['love']?.has('me')}
|
||||
/>
|
||||
<ReactButton
|
||||
<IconButton
|
||||
icon={REACTIONS_ICONS['thumbs-up']}
|
||||
variant="ghost"
|
||||
size={32}
|
||||
icon="thumbs-up"
|
||||
selected={reactions['thumbs-up']?.has('me')}
|
||||
/>
|
||||
<ReactButton
|
||||
<IconButton
|
||||
icon={REACTIONS_ICONS['thumbs-down']}
|
||||
variant="ghost"
|
||||
size={32}
|
||||
icon="thumbs-down"
|
||||
selected={reactions['thumbs-down']?.has('me')}
|
||||
/>
|
||||
<ReactButton
|
||||
<IconButton
|
||||
icon={REACTIONS_ICONS['laugh']}
|
||||
variant="ghost"
|
||||
size={32}
|
||||
icon="laugh"
|
||||
selected={reactions.laugh?.has('me')}
|
||||
/>
|
||||
<ReactButton
|
||||
<IconButton
|
||||
icon={REACTIONS_ICONS['sad']}
|
||||
variant="ghost"
|
||||
size={32}
|
||||
icon="sad"
|
||||
selected={reactions.sad?.has('me')}
|
||||
/>
|
||||
<ReactButton
|
||||
<IconButton
|
||||
icon={REACTIONS_ICONS['angry']}
|
||||
variant="ghost"
|
||||
size={32}
|
||||
icon="angry"
|
||||
selected={reactions.angry?.has('me')}
|
||||
/>
|
||||
</XStack>
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import { useMemo } from 'react'
|
||||
|
||||
import { Stack } from '@tamagui/web'
|
||||
|
||||
import { Dialog } from '../../dialog'
|
||||
import { REACTIONS_ICONS } from '../../react-button/react-button'
|
||||
import { Tabs } from '../../tabs'
|
||||
import { UserList } from '../../user-list'
|
||||
|
||||
import type { UserListProps } from '../../user-list'
|
||||
import type { ReactionsType, ReactionType } from '../types'
|
||||
|
||||
type Props = {
|
||||
initialReactionType: ReactionType
|
||||
reactions: ReactionsType
|
||||
}
|
||||
|
||||
export const ReactionsDialog = (props: Props) => {
|
||||
const { initialReactionType, reactions } = props
|
||||
|
||||
const users: UserListProps['users'] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
]
|
||||
}, [reactions])
|
||||
|
||||
return (
|
||||
<Dialog.Content width={352} borderRadius={12}>
|
||||
<Tabs defaultValue={initialReactionType}>
|
||||
<Stack padding={16}>
|
||||
<Tabs.List size={24}>
|
||||
{Object.entries(reactions).map(([reaction, value]) => {
|
||||
const Icon = REACTIONS_ICONS[reaction as keyof ReactionsType]
|
||||
return (
|
||||
<Tabs.Trigger key={reaction} value={reaction} icon={<Icon />}>
|
||||
{value.size.toString()}
|
||||
</Tabs.Trigger>
|
||||
)
|
||||
})}
|
||||
</Tabs.List>
|
||||
</Stack>
|
||||
<Stack padding={8} paddingTop={0}>
|
||||
{Object.entries(reactions).map(([reaction]) => {
|
||||
return (
|
||||
<Tabs.Content key={reaction} value={reaction}>
|
||||
<UserList users={users} />
|
||||
</Tabs.Content>
|
||||
)
|
||||
})}
|
||||
</Stack>
|
||||
</Tabs>
|
||||
</Dialog.Content>
|
||||
)
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
import { XStack } from 'tamagui'
|
||||
import { createElement, useState } from 'react'
|
||||
|
||||
import { Stack, XStack } from 'tamagui'
|
||||
|
||||
import { Dialog } from '../../dialog'
|
||||
import { ReactButton } from '../../react-button'
|
||||
import { REACTIONS_ICONS } from '../../react-button/react-button'
|
||||
import { Text } from '../../text'
|
||||
import { Tooltip } from '../../tooltip/tooltip'
|
||||
import { UserList } from '../../user-list'
|
||||
import { ReactionPopover } from './reaction-popover'
|
||||
import { ReactionsDialog } from './reactions-dialog'
|
||||
|
||||
import type { ReactButtonProps } from '../../react-button'
|
||||
import type { ReactionsType, ReactionType } from '../types'
|
||||
|
||||
type Props = {
|
||||
|
@ -24,13 +27,11 @@ export const Reactions = (props: Props) => {
|
|||
|
||||
return (
|
||||
<XStack space={6} flexWrap="wrap">
|
||||
{Object.entries(reactions).map(([type, value]) => (
|
||||
{Object.keys(reactions).map(type => (
|
||||
<Reaction
|
||||
key={type}
|
||||
size="compact"
|
||||
icon={type as ReactionType}
|
||||
count={value.size}
|
||||
selected={value.has('me')}
|
||||
type={type as ReactionType}
|
||||
reactions={reactions}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
@ -40,70 +41,67 @@ export const Reactions = (props: Props) => {
|
|||
align="start"
|
||||
sideOffset={8}
|
||||
>
|
||||
<ReactButton size="compact" icon="add" selected={false} />
|
||||
<ReactButton type="add" />
|
||||
</ReactionPopover>
|
||||
</XStack>
|
||||
)
|
||||
}
|
||||
|
||||
const Reaction = (props: ReactButtonProps) => {
|
||||
type ReactionProps = {
|
||||
type: ReactionType
|
||||
reactions: ReactionsType
|
||||
}
|
||||
|
||||
const Reaction = (props: ReactionProps) => {
|
||||
const { type, reactions } = props
|
||||
|
||||
const value = reactions[type]!
|
||||
const icon = REACTIONS_ICONS[type]
|
||||
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Dialog press="long">
|
||||
<Dialog press="long" open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<Tooltip
|
||||
side="bottom"
|
||||
sideOffset={4}
|
||||
content={
|
||||
<>
|
||||
You, Mr Gandalf, Ariana Perlona and
|
||||
<button>3 more</button> reacted with {'[ICON]'}
|
||||
</>
|
||||
<Stack
|
||||
tag="button"
|
||||
cursor="pointer"
|
||||
onPress={() => setDialogOpen(true)}
|
||||
>
|
||||
<Text size={13} weight="medium" color="$neutral-100">
|
||||
You, Mr Gandalf, Ariana Perlona
|
||||
</Text>
|
||||
<Stack flexDirection="row" alignItems="center" gap={4}>
|
||||
<Text size={13} weight="medium" color="$neutral-100">
|
||||
and
|
||||
</Text>
|
||||
<Stack
|
||||
backgroundColor="$turquoise-50-opa-10"
|
||||
borderRadius={6}
|
||||
paddingHorizontal={4}
|
||||
>
|
||||
<Text size={13} weight="medium" color="$turquoise-50">
|
||||
3 more
|
||||
</Text>
|
||||
</Stack>
|
||||
<Text size={13} weight="medium" color="$neutral-100">
|
||||
reacted with
|
||||
</Text>
|
||||
{createElement(icon)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<ReactButton {...props} />
|
||||
</Tooltip>
|
||||
|
||||
<Dialog.Content>
|
||||
<UserList
|
||||
users={[
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
{
|
||||
name: 'Pedro',
|
||||
src: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80',
|
||||
address: 'zQ3...9d4Gs0',
|
||||
indicator: 'online',
|
||||
},
|
||||
]}
|
||||
<ReactButton
|
||||
type={type as ReactionType}
|
||||
count={value.size}
|
||||
selected={value.has('me')}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
</Tooltip>
|
||||
<ReactionsDialog initialReactionType={type} reactions={reactions} />
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { useState } from 'react'
|
||||
|
||||
import { CloseIcon, PinIcon } from '@status-im/icons/20'
|
||||
import { styled } from '@tamagui/core'
|
||||
import { Pressable, View } from 'react-native'
|
||||
import { Stack } from '@tamagui/core'
|
||||
import { Pressable } from 'react-native'
|
||||
|
||||
import { Banner } from '../banner'
|
||||
import { Button } from '../button'
|
||||
import { ContextTag } from '../context-tag'
|
||||
import { Dialog } from '../dialog'
|
||||
import { Close, Dialog } from '../dialog'
|
||||
import { Message } from '../messages'
|
||||
import { Text } from '../text'
|
||||
|
||||
|
@ -19,78 +17,39 @@ type Props = {
|
|||
|
||||
const PinnedMessage = (props: Props) => {
|
||||
const { messages } = props
|
||||
const [isDetailVisible, setIsDetailVisible] = useState(false)
|
||||
|
||||
return messages.length > 0 ? (
|
||||
<Dialog open={isDetailVisible}>
|
||||
<Pressable onPress={() => setIsDetailVisible(true)}>
|
||||
return (
|
||||
<Dialog>
|
||||
<Pressable>
|
||||
<Banner count={messages.length} icon={<PinIcon />}>
|
||||
{messages[0].text}
|
||||
</Banner>
|
||||
</Pressable>
|
||||
|
||||
<Base>
|
||||
<Button
|
||||
variant="grey"
|
||||
onPress={() => setIsDetailVisible(false)}
|
||||
size={32}
|
||||
icon={<CloseIcon />}
|
||||
/>
|
||||
<DialogHeader>
|
||||
<Text size={27} weight="semibold">
|
||||
Pinned Messages
|
||||
</Text>
|
||||
<ContextTag
|
||||
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.seadn.io%2Fgae%2FFG0QJ00fN3c_FWuPeUr9-T__iQl63j9hn5d6svW8UqOmia5zp3lKHPkJuHcvhZ0f_Pd6P2COo9tt9zVUvdPxG_9BBw%3Fw%3D500%26auto%3Dformat&f=1&nofb=1&ipt=c177cd71d8d0114080cfc6efd3f9e098ddaeb1b347919bd3089bf0aacb003b3e&ipo=images"
|
||||
label={['Rarible', '# random']}
|
||||
/>
|
||||
</DialogHeader>
|
||||
<DialogContent>
|
||||
<Dialog.Content width={480} borderRadius={16}>
|
||||
<Stack padding={16} alignItems="flex-start">
|
||||
<Close asChild>
|
||||
<Button variant="grey" size={32} icon={<CloseIcon />} />
|
||||
</Close>
|
||||
<Stack paddingTop={24} gap={8} alignItems="flex-start">
|
||||
<Text size={27} weight="semibold">
|
||||
Pinned Messages
|
||||
</Text>
|
||||
<ContextTag
|
||||
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.seadn.io%2Fgae%2FFG0QJ00fN3c_FWuPeUr9-T__iQl63j9hn5d6svW8UqOmia5zp3lKHPkJuHcvhZ0f_Pd6P2COo9tt9zVUvdPxG_9BBw%3Fw%3D500%26auto%3Dformat&f=1&nofb=1&ipt=c177cd71d8d0114080cfc6efd3f9e098ddaeb1b347919bd3089bf0aacb003b3e&ipo=images"
|
||||
label={['Rarible', '# random']}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack padding={8}>
|
||||
{messages.map(message => (
|
||||
<Message key={message.id} {...message} pinned={false} />
|
||||
))}
|
||||
</DialogContent>
|
||||
</Base>
|
||||
</Stack>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
) : null
|
||||
)
|
||||
}
|
||||
|
||||
export { PinnedMessage }
|
||||
export type { Props as PinnedMessageProps }
|
||||
|
||||
const Base = styled(View, {
|
||||
position: 'relative',
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
borderRadius: 16,
|
||||
alignSelf: 'center',
|
||||
alignItems: 'flex-start',
|
||||
maxWidth: 480,
|
||||
backgroundColor: '$neutral-5',
|
||||
zIndex: 100,
|
||||
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
backgroundColor: '$neutral-5',
|
||||
},
|
||||
},
|
||||
pinned: {
|
||||
true: {
|
||||
backgroundColor: '$blue-50-opa-5',
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
})
|
||||
|
||||
const DialogHeader = styled(View, {
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-start',
|
||||
paddingVertical: 16,
|
||||
space: 11,
|
||||
})
|
||||
|
||||
const DialogContent = styled(View, {
|
||||
alignItems: 'stretch',
|
||||
justifyContent: 'flex-start',
|
||||
})
|
||||
|
|
|
@ -6,62 +6,24 @@ 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<typeof ReactButton> = {
|
||||
title: 'ReactButton',
|
||||
component: ReactButton,
|
||||
args: {},
|
||||
argTypes: {},
|
||||
render: args => (
|
||||
<XStack space={4}>
|
||||
<ReactButton {...args} icon="laugh" />
|
||||
<ReactButton {...args} icon="love" />
|
||||
<ReactButton {...args} icon="sad" />
|
||||
<ReactButton {...args} icon="thumbs-up" />
|
||||
<ReactButton {...args} icon="thumbs-down" />
|
||||
<ReactButton {...args} icon="angry" />
|
||||
<ReactButton {...args} type="laugh" />
|
||||
<ReactButton {...args} type="love" />
|
||||
<ReactButton {...args} type="sad" />
|
||||
<ReactButton {...args} type="thumbs-up" />
|
||||
<ReactButton {...args} type="thumbs-down" />
|
||||
<ReactButton {...args} type="angry" />
|
||||
<ReactButton {...args} type="add" />
|
||||
</XStack>
|
||||
),
|
||||
}
|
||||
|
||||
type Story = StoryObj<typeof ReactButton>
|
||||
|
||||
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 const Default: Story = {}
|
||||
|
||||
export default meta
|
||||
|
|
|
@ -9,18 +9,16 @@ import {
|
|||
ThumbsDownIcon,
|
||||
ThumbsUpIcon,
|
||||
} from '@status-im/icons/reactions'
|
||||
import { Stack, styled } from '@tamagui/core'
|
||||
import { styled } from '@tamagui/core'
|
||||
import { Stack } from '@tamagui/web'
|
||||
|
||||
import { Text } from '../text'
|
||||
|
||||
import type { GetVariants } from '../types'
|
||||
import type { StackProps } from '@tamagui/core'
|
||||
import type { ReactionType } from '../messages/types'
|
||||
import type { PressableProps } from '../types'
|
||||
import type { Ref } from 'react'
|
||||
import type { PressableProps } from 'react-native'
|
||||
|
||||
type Variants = GetVariants<typeof Button>
|
||||
|
||||
export const REACTIONS = {
|
||||
export const REACTIONS_ICONS = {
|
||||
love: LoveIcon,
|
||||
laugh: LaughIcon,
|
||||
'thumbs-up': ThumbsUpIcon,
|
||||
|
@ -31,10 +29,7 @@ export const REACTIONS = {
|
|||
} as const
|
||||
|
||||
type Props = PressableProps & {
|
||||
icon: keyof typeof REACTIONS
|
||||
variant?: Variants['variant']
|
||||
size?: Variants['size']
|
||||
// FIXME: use aria-selected
|
||||
type: ReactionType
|
||||
selected?: boolean
|
||||
count?: number
|
||||
// FIXME: update to latest RN
|
||||
|
@ -43,25 +38,17 @@ type Props = PressableProps & {
|
|||
}
|
||||
|
||||
const ReactButton = (props: Props, ref: Ref<HTMLButtonElement>) => {
|
||||
const {
|
||||
icon,
|
||||
variant = 'outline',
|
||||
size = 40,
|
||||
count,
|
||||
...pressableProps
|
||||
} = props
|
||||
const { type, count, ...pressableProps } = props
|
||||
|
||||
const Icon = REACTIONS[icon]
|
||||
const Icon = REACTIONS_ICONS[type]
|
||||
|
||||
const selected =
|
||||
props.selected || props['aria-expanded'] || props['aria-selected']
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...(pressableProps as unknown as StackProps)} // TODO: Tamagui has incorrect types for PressableProps
|
||||
{...(pressableProps as unknown as object)}
|
||||
ref={ref}
|
||||
variant={variant}
|
||||
size={size}
|
||||
selected={selected}
|
||||
>
|
||||
<Icon color="$neutral-100" />
|
||||
|
@ -81,6 +68,7 @@ export type { Props as ReactButtonProps }
|
|||
|
||||
const Button = styled(Stack, {
|
||||
name: 'ReactButton',
|
||||
tag: 'button',
|
||||
accessibilityRole: 'button',
|
||||
|
||||
cursor: 'pointer',
|
||||
|
@ -93,50 +81,24 @@ const Button = styled(Stack, {
|
|||
animation: 'fast',
|
||||
space: 4,
|
||||
|
||||
borderRadius: 8,
|
||||
minWidth: 36,
|
||||
height: 24,
|
||||
paddingHorizontal: 8,
|
||||
|
||||
borderColor: '$neutral-20',
|
||||
hoverStyle: { borderColor: '$neutral-30' },
|
||||
pressStyle: {
|
||||
backgroundColor: '$neutral-10',
|
||||
borderColor: '$neutral-20',
|
||||
},
|
||||
|
||||
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,
|
||||
})
|
||||
|
|
|
@ -3,11 +3,11 @@ import { Stack } from '@tamagui/core'
|
|||
import { DividerLabel } from '../dividers'
|
||||
import { UserList } from '../user-list'
|
||||
|
||||
import type { USerListProps } from '../user-list'
|
||||
import type { UserListProps } from '../user-list'
|
||||
|
||||
type GroupProps = {
|
||||
label: string
|
||||
users: USerListProps['users']
|
||||
users: UserListProps['users']
|
||||
}
|
||||
|
||||
const Group = (props: GroupProps) => {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { Tabs, type TabsProps } from './tabs'
|
|
@ -0,0 +1,72 @@
|
|||
import { PlaceholderIcon } from '@status-im/icons/20'
|
||||
|
||||
import { Text } from '../text'
|
||||
import { Tabs } from './tabs'
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import type { ComponentProps } from 'react'
|
||||
|
||||
const meta: Meta<typeof Tabs> = {
|
||||
component: Tabs,
|
||||
argTypes: {},
|
||||
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=57-13214&t=q5DFi3jlBAcdghLy-11',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type Story = StoryObj<{ size: 24 | 32; icon: boolean; count: boolean }>
|
||||
|
||||
export const Default: Story = {
|
||||
name: 'Default',
|
||||
args: {
|
||||
size: 24,
|
||||
icon: false,
|
||||
},
|
||||
argTypes: {
|
||||
size: {
|
||||
control: 'select',
|
||||
options: [24, 32] satisfies ComponentProps<typeof Tabs.List>['size'][],
|
||||
},
|
||||
icon: {
|
||||
control: 'boolean',
|
||||
},
|
||||
count: {
|
||||
control: 'boolean',
|
||||
},
|
||||
},
|
||||
render(args) {
|
||||
const icon = args.icon ? <PlaceholderIcon /> : undefined
|
||||
const count = args.count ? 8 : undefined
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="1">
|
||||
<Tabs.List size={args.size}>
|
||||
<Tabs.Trigger value="1" icon={icon} count={count}>
|
||||
Tab 1
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="2" icon={icon}>
|
||||
Tab 2
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="3" icon={icon}>
|
||||
Tab 3
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value="1">
|
||||
<Text size={15}>Content 1</Text>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="2">
|
||||
<Text size={15}>Content 2</Text>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="3">
|
||||
<Text size={15}>Content 3</Text>
|
||||
</Tabs.Content>
|
||||
</Tabs>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
|
@ -0,0 +1,164 @@
|
|||
import { Children, cloneElement, forwardRef } from 'react'
|
||||
|
||||
import { Content, List, Root, Trigger } from '@radix-ui/react-tabs'
|
||||
import { Stack } from '@tamagui/web'
|
||||
import { styled } from 'tamagui'
|
||||
|
||||
import { Counter } from '../counter'
|
||||
import { usePressableColors } from '../hooks/use-pressable-colors'
|
||||
import { Text } from '../text'
|
||||
|
||||
import type { TextProps } from '../text'
|
||||
import type { GetVariants } from '../types'
|
||||
import type { Ref } from 'react'
|
||||
import type { View } from 'react-native'
|
||||
|
||||
type Variants = GetVariants<typeof TriggerBase>
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode[]
|
||||
defaultValue: string
|
||||
value?: string
|
||||
onValueChange?: (value: string) => void
|
||||
}
|
||||
|
||||
const Tabs = (props: Props) => {
|
||||
const { children, defaultValue, value, onValueChange } = props
|
||||
|
||||
return (
|
||||
<Root
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
>
|
||||
{children}
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
type ListProps = {
|
||||
children: React.ReactElement[]
|
||||
size: Variants['size']
|
||||
}
|
||||
|
||||
const TabsList = (props: ListProps) => {
|
||||
const { children } = props
|
||||
|
||||
return (
|
||||
<List asChild>
|
||||
<Stack flexDirection="row" gap={8}>
|
||||
{Children.map(children, child => (
|
||||
<Trigger asChild value={child.props.value}>
|
||||
{cloneElement(child, { size: props.size })}
|
||||
</Trigger>
|
||||
))}
|
||||
</Stack>
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
||||
type TriggerProps = {
|
||||
value: string
|
||||
children: string
|
||||
icon?: React.ReactElement
|
||||
count?: number
|
||||
}
|
||||
|
||||
// TODO: Add counter
|
||||
const TabsTrigger = (props: TriggerProps, ref: Ref<View>) => {
|
||||
const { icon = null, children, count, ...triggerProps } = props
|
||||
|
||||
// props coming from parent List and Trigger, not passed by the user (line 52)
|
||||
const providedProps = props as TriggerProps & {
|
||||
size: 24 | 32
|
||||
'aria-selected': boolean
|
||||
}
|
||||
|
||||
const { color, pressableProps } = usePressableColors(
|
||||
{
|
||||
default: '$neutral-100',
|
||||
hover: '$neutral-100',
|
||||
press: '$neutral-100',
|
||||
active: '$white-100',
|
||||
},
|
||||
providedProps
|
||||
)
|
||||
|
||||
const { size, 'aria-selected': selected } = providedProps
|
||||
|
||||
const textSize = triggerTextSizes[size]
|
||||
|
||||
return (
|
||||
<TriggerBase
|
||||
{...triggerProps}
|
||||
{...pressableProps}
|
||||
ref={ref}
|
||||
size={size}
|
||||
active={selected}
|
||||
>
|
||||
{icon && cloneElement(icon, { size: iconSizes[size] })}
|
||||
<Text size={textSize} weight="medium" color={color}>
|
||||
{children}
|
||||
</Text>
|
||||
{count && (
|
||||
<Stack marginRight={-4}>
|
||||
<Counter type="secondary" value={count} />
|
||||
</Stack>
|
||||
)}
|
||||
</TriggerBase>
|
||||
)
|
||||
}
|
||||
|
||||
Tabs.List = TabsList
|
||||
Tabs.Trigger = forwardRef(TabsTrigger)
|
||||
Tabs.Content = Content
|
||||
|
||||
export { Tabs }
|
||||
export type { Props as TabsProps }
|
||||
|
||||
const TriggerBase = styled(Stack, {
|
||||
tag: 'button',
|
||||
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
32: {
|
||||
height: 32,
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 12,
|
||||
gap: 6,
|
||||
},
|
||||
24: {
|
||||
height: 24,
|
||||
borderRadius: 8,
|
||||
paddingHorizontal: 8,
|
||||
gap: 4,
|
||||
},
|
||||
},
|
||||
active: {
|
||||
true: {
|
||||
backgroundColor: '$neutral-50',
|
||||
},
|
||||
false: {
|
||||
backgroundColor: '$neutral-10',
|
||||
hoverStyle: {
|
||||
backgroundColor: '$neutral-20',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const triggerTextSizes: Record<Variants['size'], TextProps['size']> = {
|
||||
'32': 15,
|
||||
'24': 13,
|
||||
}
|
||||
|
||||
// FIXME: icons will accept size as number
|
||||
const iconSizes: Record<Variants['size'], number> = {
|
||||
'32': 16,
|
||||
'24': 12,
|
||||
}
|
|
@ -5,7 +5,8 @@ import { Text } from './text'
|
|||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Text',
|
||||
title: 'text',
|
||||
|
||||
args: {
|
||||
children: 'The quick brown fox jumped over the lazy dog.',
|
||||
},
|
||||
|
@ -19,8 +20,7 @@ const meta: Meta = {
|
|||
},
|
||||
}
|
||||
|
||||
export const TextStory: StoryObj<typeof Text> = {
|
||||
name: 'TextNew',
|
||||
export const Default: StoryObj<typeof Text> = {
|
||||
render: args => (
|
||||
<Stack gap={24}>
|
||||
<Stack gap={8}>
|
||||
|
|
|
@ -8,8 +8,9 @@ import {
|
|||
TooltipProvider,
|
||||
Trigger,
|
||||
} from '@radix-ui/react-tooltip'
|
||||
import { Stack } from 'tamagui'
|
||||
import { useTheme } from 'tamagui'
|
||||
|
||||
import { Shadow } from '../shadow'
|
||||
import { Text } from '../text'
|
||||
|
||||
import type { TooltipContentProps } from '@radix-ui/react-tooltip'
|
||||
|
@ -37,6 +38,8 @@ const Tooltip = (props: Props, ref: Ref<HTMLButtonElement>) => {
|
|||
...triggerProps
|
||||
} = props
|
||||
|
||||
const theme = useTheme() // not ideal
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Root delayDuration={delayDuration}>
|
||||
|
@ -52,24 +55,22 @@ const Tooltip = (props: Props, ref: Ref<HTMLButtonElement>) => {
|
|||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
>
|
||||
<Stack
|
||||
backgroundColor="$neutral-95"
|
||||
<Shadow
|
||||
variant="$3"
|
||||
backgroundColor="$white-100"
|
||||
paddingVertical={6}
|
||||
paddingHorizontal={12}
|
||||
borderRadius={8}
|
||||
shadowRadius={30}
|
||||
shadowOffset="0px 8px"
|
||||
shadowColor="rgba(9, 16, 28, 0.12)"
|
||||
>
|
||||
{typeof content === 'string' ? (
|
||||
<Text size={13} weight="medium" color="$white-100">
|
||||
<Text size={13} weight="medium" color="$neutral-100">
|
||||
{content}
|
||||
</Text>
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
<Arrow width={11} height={5} />
|
||||
</Stack>
|
||||
<Arrow width={11} height={5} fill={theme.background.val} />
|
||||
</Shadow>
|
||||
</Content>
|
||||
</Portal>
|
||||
</Root>
|
||||
|
|
|
@ -17,7 +17,7 @@ type PressableProps = {
|
|||
delayHoverIn?: NativePressableProps['delayHoverIn']
|
||||
delayHoverOut?: NativePressableProps['delayHoverOut']
|
||||
delayLongPress?: NativePressableProps['delayLongPress']
|
||||
disabled?: NativePressableProps['disabled']
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export type MapVariant<
|
||||
|
|
|
@ -49,4 +49,4 @@ const UserList = (props: Props) => {
|
|||
}
|
||||
|
||||
export { UserList }
|
||||
export type { Props as USerListProps }
|
||||
export type { Props as UserListProps }
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -3541,6 +3541,21 @@
|
|||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
|
||||
"@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"
|
||||
integrity sha512-4CkF/Rx1GcrusI/JZ1Rvyx4okGUs6wEenWA0RG/N+CwkRhTy7t54y7BLsWUXrAz/GRbBfHQg/Odfs/RoW0CiRA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "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-presence" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.2"
|
||||
"@radix-ui/react-roving-focus" "1.0.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.0"
|
||||
|
||||
"@radix-ui/react-tooltip@^1.0.5":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.5.tgz#fe20274aeac874db643717fc7761d5a8abdd62d1"
|
||||
|
|
Loading…
Reference in New Issue