Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49c3cf834b | ||
|
|
4953fe79fe | ||
|
|
b15925815e | ||
|
|
51d61895a4 | ||
|
|
7f611bd199 | ||
|
|
1b05730c3d | ||
|
|
bf143681ff | ||
|
|
e9979fdad2 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@status-im/components": patch
|
||||
---
|
||||
|
||||
fix `<DropdownMenu.Item />` onSelect prop
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@status-im/components': patch
|
||||
---
|
||||
|
||||
adds segmented control component
|
||||
@@ -6,13 +6,13 @@ This monorepo contains packages for building web applications in the Status ecos
|
||||
|
||||
## Packages
|
||||
|
||||
| Name | `npm` | Description |
|
||||
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`@status-im/components`](./packages/components) | [](https://www.npmjs.com/package/@status-im/components) | Component library built with Radix UI, React Aria, Tailwind CSS. |
|
||||
| [`@status-im/js`](./packages/status-js) | [](https://www.npmjs.com/package/@status-im/js) | Libary for Waku protocol integration and blockchain interactions. |
|
||||
| [`@status-im/icons`](./packages/icons) | [](https://www.npmjs.com/package/@status-im/icons) | Auto-generated icon library based on our [design system](https://www.figma.com/design/qLLuMLfpGxK9OfpIavwsmK/Iconset?node-id=3239-987&node-type=frame&t=0h8iIiZ3Sf0g4MRV-11). |
|
||||
| [`@status-im/colors`](./packages/colors) | [](https://www.npmjs.com/package/@status-im/colors) | Auto-generated color palette based on our [design system](https://www.figma.com/design/v98g9ZiaSHYUdKWrbFg9eM/Foundations?node-id=619-5995&node-type=canvas&m=dev). |
|
||||
| [`@status-im/eslint-config`](./packages/eslint-config) | | Shared ESLint configuration for consistent code style across projects. |
|
||||
| Name | `npm` | Description |
|
||||
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`@status-im/components`](./packages/components) | [](https://www.npmjs.com/package/@status-im/components) | Component library built with Radix UI, React Aria, Tailwind CSS. |
|
||||
| [`@status-im/js`](./packages/status-js) | [](https://www.npmjs.com/package/@status-im/js) | Libary for Waku protocol integration and blockchain interactions. |
|
||||
| [`@status-im/icons`](./packages/icons) | [](https://www.npmjs.com/package/@status-im/icons) | Auto-generated icon library based on our [design system](https://www.figma.com/design/qLLuMLfpGxK9OfpIavwsmK/Iconset?node-id=3239-987&node-type=frame&t=0h8iIiZ3Sf0g4MRV-11). |
|
||||
| [`@status-im/colors`](./packages/colors) | [](https://www.npmjs.com/package/@status-im/colors) | Auto-generated color palette based on our [design system](https://www.figma.com/design/v98g9ZiaSHYUdKWrbFg9eM/Foundations?node-id=619-5995&node-type=canvas&m=dev). |
|
||||
| [`@status-im/eslint-config`](./packages/eslint-config) | [](https://www.npmjs.com/package/@status-im/eslint-config) | Shared ESLint configuration for consistent code style across projects. |
|
||||
|
||||
## Apps
|
||||
|
||||
|
||||
Vendored
+7
-18
@@ -30,25 +30,14 @@ pipeline {
|
||||
|
||||
stages {
|
||||
stage('Check Changed Files') {
|
||||
steps {
|
||||
script {
|
||||
// Get the list of changed files
|
||||
def changedFiles = sh(
|
||||
script: "git diff --name-only origin/main...HEAD",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
// Check if the changes are relevant
|
||||
def isRelevant = changedFiles.split('\n').any { file ->
|
||||
file.startsWith('apps/connector/')
|
||||
}
|
||||
|
||||
// If no relevant changes, skip the build
|
||||
if (isRelevant) {
|
||||
changesDetected = true
|
||||
}
|
||||
}
|
||||
when {
|
||||
changeset pattern: "apps/connector/**", comparator: "GLOB"
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
changesDetected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Install') {
|
||||
|
||||
@@ -86,7 +86,3 @@ https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox#w_what-are-my-opt
|
||||
Firefox Developer Edition > Tools > Add-ons and Themes > click on gear icon (Tools for all add-ons) > Install Add-on From File... > select build (build/firefox-mv3-prod.zip)
|
||||
|
||||
> Note: Does not reload automatically, requires build and load on change.
|
||||
|
||||
## Testing
|
||||
|
||||
Download latest build from last merged PR or build from source. To use the extension see the load steps from [Development](#development) section.
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @status-im/components
|
||||
|
||||
## 1.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e9979fd: updates icon color for missing compound variants
|
||||
- bf14368: add `type="account"` to `<ContextTag />`
|
||||
|
||||
## 1.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@status-im/components",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"license": "MPL-2.0",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
@@ -50,6 +50,7 @@
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@status-im/colors": "*",
|
||||
"@status-im/icons": "*",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
@@ -1,3 +1,7 @@
|
||||
import { Fragment } from 'react'
|
||||
|
||||
import { PlaceholderIcon } from '@status-im/icons/20'
|
||||
|
||||
import { Button } from './button'
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
@@ -51,7 +55,22 @@ const meta: Meta<
|
||||
).map(variant => (
|
||||
<div key={variant} className="flex items-center gap-4">
|
||||
{(['40', '32', '24'] as const).map(size => (
|
||||
<Button key={size} {...args} variant={variant} size={size} />
|
||||
<Fragment key={size}>
|
||||
<Button {...args} variant={variant} size={size} />
|
||||
{/* With icon */}
|
||||
<Button
|
||||
{...args}
|
||||
size={size}
|
||||
variant={variant}
|
||||
iconBefore={<PlaceholderIcon />}
|
||||
/>
|
||||
<Button
|
||||
aria-label="Button with icon only"
|
||||
size={size}
|
||||
variant={variant}
|
||||
icon={<PlaceholderIcon />}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -193,11 +193,41 @@ const iconStyles = cva({
|
||||
placement: 'after',
|
||||
className: '-mr-0.5',
|
||||
},
|
||||
{
|
||||
variant: 'grey',
|
||||
iconOnly: true,
|
||||
className: '!text-neutral-100 dark:!text-white-100',
|
||||
},
|
||||
{
|
||||
variant: 'outline',
|
||||
iconOnly: true,
|
||||
className: '!text-neutral-100 dark:!text-white-100',
|
||||
},
|
||||
{
|
||||
variant: 'darkGrey',
|
||||
iconOnly: true,
|
||||
className: '!text-neutral-100 dark:!text-white-100',
|
||||
},
|
||||
{
|
||||
variant: 'ghost',
|
||||
iconOnly: true,
|
||||
className: '!text-neutral-100 dark:!text-white-100',
|
||||
},
|
||||
{
|
||||
variant: 'primary',
|
||||
iconOnly: true,
|
||||
className: '!text-white-100',
|
||||
},
|
||||
{
|
||||
variant: 'positive',
|
||||
iconOnly: true,
|
||||
className: '!text-white-100',
|
||||
},
|
||||
{
|
||||
variant: 'danger',
|
||||
iconOnly: true,
|
||||
className: '!text-white-100',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ const ContextTagVariant = (props: Props) => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ContextTag {...props} type="account" label="Account name" emoji="🐷" />
|
||||
<ContextTag
|
||||
{...props}
|
||||
type="icon"
|
||||
|
||||
@@ -3,6 +3,8 @@ import { cloneElement, forwardRef } from 'react'
|
||||
import { cva, cx } from 'cva'
|
||||
import { match } from 'ts-pattern'
|
||||
|
||||
import { Avatar } from '../avatar'
|
||||
|
||||
import type { IconElement } from '../types'
|
||||
import type { VariantProps } from 'cva'
|
||||
|
||||
@@ -15,10 +17,10 @@ type Props = {
|
||||
| { type: 'community'; label: string; icon: IconElement }
|
||||
| { type: 'token'; label: string; icon: IconElement }
|
||||
| { type: 'network'; label: string; icon: IconElement }
|
||||
| { type: 'account'; label: string; emoji: string }
|
||||
| { type: 'icon'; label: string; icon: IconElement }
|
||||
)
|
||||
// | { type: 'user'; user: { name: string; src: string } }
|
||||
// | { type: 'account'; account: { name: string; emoji: string } }
|
||||
// | {
|
||||
// type: 'group'
|
||||
// group: {
|
||||
@@ -36,7 +38,7 @@ type Props = {
|
||||
|
||||
const baseStyles = cva({
|
||||
base: [
|
||||
'inline-flex w-fit cursor-default items-center rounded-full',
|
||||
'inline-flex w-fit cursor-default items-center',
|
||||
'font-medium text-neutral-100 dark:text-white-100',
|
||||
'bg-neutral-10 blur:bg-neutral-80/5 dark:bg-neutral-90 blur:dark:bg-white-5',
|
||||
],
|
||||
@@ -50,6 +52,13 @@ const baseStyles = cva({
|
||||
selected: {
|
||||
true: 'border border-customisation-50',
|
||||
},
|
||||
rounded: {
|
||||
'8': 'rounded-8',
|
||||
full: 'rounded-full',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
rounded: 'full',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -116,6 +125,23 @@ const ContextTag = (props: Props, ref: React.Ref<HTMLDivElement>) => {
|
||||
</div>
|
||||
)
|
||||
})
|
||||
.with({ type: 'account' }, ({ label, emoji }) => {
|
||||
return (
|
||||
<div {...rest} ref={ref} className={baseStyles({ size, rounded: '8' })}>
|
||||
<span className={iconStyles({ size, rounded: '6', offset: size })}>
|
||||
<Avatar
|
||||
type="account"
|
||||
size={match(size)
|
||||
.with('32', () => '28' as const)
|
||||
.otherwise(() => '20' as const)}
|
||||
name={label}
|
||||
emoji={emoji}
|
||||
/>
|
||||
</span>
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
.with({ type: 'icon' }, ({ icon, label }) => {
|
||||
return (
|
||||
<div {...rest} ref={ref} className={baseStyles({ size })}>
|
||||
|
||||
@@ -123,7 +123,6 @@ Search.displayName = 'Search'
|
||||
type ItemProps = DropdownMenu.DropdownMenuItemProps & {
|
||||
icon?: IconElement
|
||||
label: string
|
||||
onSelect: () => void
|
||||
selected?: boolean
|
||||
danger?: boolean
|
||||
external?: boolean
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * as SegmentedControl from './segmented-control'
|
||||
@@ -0,0 +1,130 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import { PlaceholderIcon } from '@status-im/icons/20'
|
||||
|
||||
import { SegmentedControl } from './'
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
type RootProps = React.ComponentPropsWithoutRef<typeof SegmentedControl.Root>
|
||||
type ItemProps = React.ComponentPropsWithoutRef<typeof SegmentedControl.Item>
|
||||
|
||||
const SegmentedControlVariant = (
|
||||
props: Omit<ItemProps, 'value'> & {
|
||||
count: number
|
||||
size: RootProps['size']
|
||||
variant: RootProps['variant']
|
||||
},
|
||||
) => {
|
||||
const [value, setValue] = useState('0')
|
||||
|
||||
const { count, variant, size, ...itemProps } = props
|
||||
|
||||
return (
|
||||
<SegmentedControl.Root
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
variant={variant}
|
||||
size={size}
|
||||
>
|
||||
{Array(count)
|
||||
.fill(null)
|
||||
.map((_, index) => {
|
||||
return (
|
||||
<SegmentedControl.Item
|
||||
{...(itemProps as ItemProps)}
|
||||
key={index}
|
||||
value={index.toString()}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</SegmentedControl.Root>
|
||||
)
|
||||
}
|
||||
|
||||
const SegmentedControlGroup = (
|
||||
props: Omit<RootProps, 'value' | 'onValueChange'>,
|
||||
) => {
|
||||
const { variant = 'grey' } = props
|
||||
|
||||
return (
|
||||
<div className="inline-flex flex-col gap-12">
|
||||
<div className="flex flex-col gap-6">
|
||||
<SegmentedControlVariant count={5} size="32" variant={variant}>
|
||||
Tab
|
||||
</SegmentedControlVariant>
|
||||
|
||||
<SegmentedControlVariant
|
||||
count={5}
|
||||
size="32"
|
||||
variant={variant}
|
||||
icon={<PlaceholderIcon />}
|
||||
>
|
||||
Tab
|
||||
</SegmentedControlVariant>
|
||||
|
||||
<SegmentedControlVariant count={2} size="32" variant={variant}>
|
||||
Tab
|
||||
</SegmentedControlVariant>
|
||||
<SegmentedControlVariant
|
||||
count={2}
|
||||
size="32"
|
||||
variant={variant}
|
||||
icon={<PlaceholderIcon />}
|
||||
>
|
||||
Tab
|
||||
</SegmentedControlVariant>
|
||||
|
||||
<SegmentedControlVariant
|
||||
count={5}
|
||||
size="32"
|
||||
variant={variant}
|
||||
icon={<PlaceholderIcon />}
|
||||
aria-label="placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Components/Segmented Control',
|
||||
render: args => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-5">
|
||||
<div className="flex flex-wrap gap-5">
|
||||
<div className="rounded-[24px] bg-white-100 p-12 dark:bg-neutral-90">
|
||||
<SegmentedControlGroup {...args} />
|
||||
</div>
|
||||
|
||||
<div className="inline-flex flex-col gap-12 rounded-[24px] bg-neutral-5 p-12 dark:bg-neutral-95">
|
||||
<SegmentedControlGroup {...args} variant="darkGrey" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
data-background="blur"
|
||||
className="relative inline-flex flex-col gap-12 overflow-hidden rounded-[24px] p-12"
|
||||
>
|
||||
<div className="absolute left-0 top-0 z-10 size-full bg-blur-white/70 backdrop-blur-[20px] dark:bg-blur-neutral-80/80" />
|
||||
{/* Background image */}
|
||||
<div className="absolute left-0 top-0 size-full bg-[url(./assets/background-blur.png)] bg-cover bg-center bg-no-repeat" />
|
||||
<div className="relative z-10">
|
||||
<SegmentedControlGroup {...args} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
type Story = StoryObj
|
||||
|
||||
export const Light: Story = {}
|
||||
export const Dark: Story = {
|
||||
parameters: {
|
||||
backgrounds: { default: 'dark' },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
@@ -0,0 +1,210 @@
|
||||
import {
|
||||
cloneElement,
|
||||
createContext,
|
||||
forwardRef,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
import * as ToggleGroup from '@radix-ui/react-toggle-group'
|
||||
import { cva } from 'cva'
|
||||
|
||||
import type { IconElement } from '../types'
|
||||
import type { VariantProps } from 'cva'
|
||||
|
||||
type Variants = VariantProps<typeof rootStyles>
|
||||
|
||||
const SegmentedControlContext = createContext<
|
||||
Pick<Variants, 'variant' | 'size'>
|
||||
>({})
|
||||
|
||||
function useSegmentedControlContext() {
|
||||
const context = useContext(SegmentedControlContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useSegmentedControlContext must be used within a <SegmentedControl.Root />',
|
||||
)
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
type RootProps = Omit<ToggleGroup.ToggleGroupSingleProps, 'type'> & {
|
||||
value: string
|
||||
onValueChange: (value: string) => void
|
||||
variant?: Variants['variant']
|
||||
size?: Variants['size']
|
||||
}
|
||||
|
||||
const rootStyles = cva({
|
||||
base: 'relative flex flex-1 items-center justify-center gap-0.5 rounded-10 p-0.5',
|
||||
variants: {
|
||||
variant: {
|
||||
grey: 'bg-neutral-10 blur:bg-neutral-80/5 blur:backdrop-blur-[20px] dark:bg-neutral-80 blur:dark:bg-white-5',
|
||||
darkGrey: 'bg-neutral-20 dark:bg-neutral-90',
|
||||
},
|
||||
size: {
|
||||
'24': 'h-6',
|
||||
'32': 'h-8',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const activeSegmentStyles = cva({
|
||||
base: 'pointer-events-none absolute inset-y-0.5 left-0 flex-1 rounded-8 transition-all duration-200 ease-out',
|
||||
variants: {
|
||||
variant: {
|
||||
grey: 'bg-neutral-50 blur:bg-neutral-80/60 dark:bg-neutral-60 blur:dark:bg-white-20',
|
||||
darkGrey: 'bg-neutral-50 dark:bg-neutral-60',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const Root = forwardRef<
|
||||
React.ElementRef<typeof ToggleGroup.Root>,
|
||||
RootProps
|
||||
>((props, ref) => {
|
||||
const {
|
||||
children,
|
||||
variant = 'grey',
|
||||
size = '32',
|
||||
value,
|
||||
onValueChange,
|
||||
...rootProps
|
||||
} = props
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null)
|
||||
useImperativeHandle(ref, () => rootRef.current!)
|
||||
|
||||
const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({})
|
||||
|
||||
useEffect(() => {
|
||||
const activeButton =
|
||||
rootRef.current!.querySelector<HTMLButtonElement>('[data-state="on"]')!
|
||||
|
||||
if (activeButton) {
|
||||
setIndicatorStyle({
|
||||
width: activeButton.offsetWidth,
|
||||
transform: `translateX(${activeButton.offsetLeft}px)`,
|
||||
})
|
||||
}
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<SegmentedControlContext.Provider
|
||||
value={useMemo(() => ({ size, variant }), [size, variant])}
|
||||
>
|
||||
<ToggleGroup.Root
|
||||
{...rootProps}
|
||||
ref={rootRef}
|
||||
type="single"
|
||||
className={rootStyles({ size, variant })}
|
||||
value={value}
|
||||
onValueChange={value => {
|
||||
// Ensuring there is always a value
|
||||
// @see https://www.radix-ui.com/primitives/docs/components/toggle-group#ensuring-there-is-always-a-value
|
||||
if (value) {
|
||||
onValueChange(value)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={activeSegmentStyles({ variant })}
|
||||
style={indicatorStyle}
|
||||
/>
|
||||
{children}
|
||||
</ToggleGroup.Root>
|
||||
</SegmentedControlContext.Provider>
|
||||
)
|
||||
})
|
||||
|
||||
Root.displayName = 'Root'
|
||||
|
||||
/**
|
||||
* Item
|
||||
*/
|
||||
|
||||
const itemStyles = cva({
|
||||
base: [
|
||||
'group relative z-10 flex flex-1 select-none items-center justify-center gap-1 whitespace-nowrap rounded-8 bg-transparent font-medium transition-all duration-300 ease-out',
|
||||
'text-neutral-100 data-[state="on"]:text-white-100 dark:text-white-100',
|
||||
],
|
||||
|
||||
variants: {
|
||||
variant: {
|
||||
grey: [
|
||||
'data-[state="off"]:hover:bg-neutral-20 data-[state="off"]:blur:hover:bg-neutral-80/5 data-[state="off"]:dark:hover:bg-neutral-70 data-[state="off"]:dark:blur:hover:bg-white-5',
|
||||
],
|
||||
darkGrey: [
|
||||
'data-[state="off"]:hover:bg-neutral-30 data-[state="off"]:dark:hover:bg-neutral-80',
|
||||
],
|
||||
},
|
||||
size: {
|
||||
'24': 'h-6 px-2 text-13',
|
||||
'32': 'h-7 px-3 text-15',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const iconStyles = cva({
|
||||
base: [
|
||||
'size-5 text-neutral-50 group-data-[state="on"]:text-white-100 dark:text-white-40',
|
||||
],
|
||||
variants: {
|
||||
iconOnly: {
|
||||
true: '',
|
||||
false: '-ml-0.5',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
type ItemProps = Omit<
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroup.Item>,
|
||||
'children'
|
||||
> &
|
||||
(
|
||||
| {
|
||||
icon?: IconElement
|
||||
children: React.ReactNode
|
||||
}
|
||||
| {
|
||||
icon: IconElement
|
||||
children?: never
|
||||
'aria-label': string
|
||||
}
|
||||
)
|
||||
|
||||
export const Item = forwardRef<
|
||||
React.ElementRef<typeof ToggleGroup.Item>,
|
||||
ItemProps
|
||||
>((props, ref) => {
|
||||
const { icon, children, ...itemProps } = props
|
||||
|
||||
const { size, variant } = useSegmentedControlContext()
|
||||
|
||||
const iconOnly = children ? false : true
|
||||
|
||||
return (
|
||||
<ToggleGroup.Item
|
||||
{...itemProps}
|
||||
ref={ref}
|
||||
className={itemStyles({ size, variant })}
|
||||
>
|
||||
{icon && (
|
||||
<>
|
||||
{cloneElement(icon, {
|
||||
className: iconStyles({ iconOnly }),
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{children}
|
||||
</ToggleGroup.Item>
|
||||
)
|
||||
})
|
||||
|
||||
Item.displayName = 'Item'
|
||||
@@ -26,6 +26,16 @@ type RootProps = React.ComponentProps<typeof Tabs.Root> & {
|
||||
|
||||
const TabsContext = createContext<Pick<RootProps, 'size' | 'variant'>>({})
|
||||
|
||||
function useTabsContext() {
|
||||
const context = useContext(TabsContext)
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useTabsContext must be used within a <Tabs.Root />')
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
export const Root = (props: RootProps) => {
|
||||
const { size = '32', variant = 'grey', ...rootProps } = props
|
||||
|
||||
@@ -42,7 +52,7 @@ export const List = forwardRef<
|
||||
React.ElementRef<typeof Tabs.List>,
|
||||
React.ComponentPropsWithoutRef<typeof Tabs.List>
|
||||
>((props, ref) => {
|
||||
const { size } = useContext(TabsContext)!
|
||||
const { size } = useTabsContext()
|
||||
|
||||
return (
|
||||
<Tabs.List
|
||||
@@ -74,7 +84,7 @@ export const Trigger = forwardRef<
|
||||
>((props, ref) => {
|
||||
const { children, ...rest } = props
|
||||
|
||||
const { size, variant } = useContext(TabsContext)!
|
||||
const { size, variant } = useTabsContext()
|
||||
|
||||
return (
|
||||
<Tabs.Trigger {...rest} ref={ref} className={tabStyles({ variant, size })}>
|
||||
|
||||
@@ -2528,6 +2528,28 @@
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
"@radix-ui/react-visually-hidden" "1.1.0"
|
||||
|
||||
"@radix-ui/react-toggle-group@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.0.tgz#28714c4d1ff4961a8fd259b1feef58b4cac92f80"
|
||||
integrity sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.0"
|
||||
"@radix-ui/react-context" "1.1.0"
|
||||
"@radix-ui/react-direction" "1.1.0"
|
||||
"@radix-ui/react-primitive" "2.0.0"
|
||||
"@radix-ui/react-roving-focus" "1.1.0"
|
||||
"@radix-ui/react-toggle" "1.1.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-toggle@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz#1f7697b82917019330a16c6f96f649f46b4606cf"
|
||||
integrity sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.0"
|
||||
"@radix-ui/react-primitive" "2.0.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-tooltip@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz#c42db2ffd7dcc6ff3d65407c8cb70490288f518d"
|
||||
@@ -11291,7 +11313,16 @@ string-argv@^0.3.1:
|
||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
||||
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@@ -11375,7 +11406,14 @@ string.prototype.trimstart@^1.0.6:
|
||||
define-properties "^1.1.4"
|
||||
es-abstract "^1.20.4"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@@ -12334,7 +12372,7 @@ word-wrap@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@@ -12352,6 +12390,15 @@ wrap-ansi@^6.2.0:
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
||||
Reference in New Issue
Block a user