Compare commits

...
Author SHA1 Message Date
Felicio Mununga 49c3cf834b Update README.md 2024-10-17 15:41:39 +02:00
marcelinesandPavel 4953fe79fe add <SegmentedControl /> (#591)
* f: adds segmented control

* f: fix story and adds type variant

* f: change size type to string

* f

* f: changeset

* f

* f

* f: adds all variants in story and fixes some issues

* f: use toogle group approach

* f: changes from review

* add context consumer to tabs

* cleanup

* rename

* simplify stories

---------

Co-authored-by: Pavel <14926950+prichodko@users.noreply.github.com>
2024-10-17 15:37:54 +02:00
pavel b15925815e fix <DropdownMenu.Item /> onSelect prop (#624)
* fix `<DropdownMenu.Item />` onSelect prop

* Create angry-geese-ring.md

* f
2024-10-17 15:13:18 +02:00
Marko Burčul 51d61895a4 jenkinsfile: revert check for changed files (#622)
Signed-off-by: markoburcul <marko@status.im>
2024-10-17 12:07:19 +02:00
Felicio Mununga 7f611bd199 Update README.md (#615) 2024-10-17 10:49:49 +02:00
github-actions[bot]andgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> 1b05730c3d Release (#618)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-16 12:20:30 +02:00
pavel bf143681ff add type="account" to <ContextTag /> (#619)
* u

* Create lemon-houses-develop.md
2024-10-16 10:55:24 +02:00
marcelines e9979fdad2 fix <Button /> icon only colors (#616)
* f: adds compound variants for missing cases

* f: changeset
2024-10-15 21:50:24 +02:00
Marko Burčul f1fa827d42 jenkinsfile: filter stage execution based on path (#613)
Signed-off-by: markoburcul <marko@status.im>
2024-10-11 16:55:32 +02:00
github-actions[bot]andgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> b7be3d762b Release (#610)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-11 14:19:49 +02:00
pavel 7f21132f0c fix <Shortcut /> (#609)
* f

* f

* Create early-apes-judge.md
2024-10-11 13:32:32 +02:00
github-actions[bot]andgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> d48508b162 Release (#606)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-11 11:43:43 +02:00
Felicio Mununga 3ad28eaeb6 chore: add changeset (#605) 2024-10-11 11:36:10 +02:00
Felicio Mununga a87d6260d7 chore: Remove flex-1 class from button component (#602) 2024-10-11 11:26:30 +02:00
Marko Burčul 6286027559 jenkinsfile: fix paths for all steps (#597)
* jenkinsfile: fix paths for all steps

Use newest jenkins lib tag which adds the entryPoint as an argument to the nix shell function.

Referenced issue: https://github.com/status-im/status-web/issues/590

Signed-off-by: markoburcul <marko@status.im>
2024-10-10 17:47:34 +02:00
github-actions[bot]andgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> 6bf4d487f3 Release (#599)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-10 16:15:51 +02:00
pavel a3f9252b90 fix blur button icon color (#598)
* add

* f

* f

* Create serious-carpets-promise.md
2024-10-10 16:04:16 +02:00
github-actions[bot]andgithub-actions[bot] <github-actions[bot]@users.noreply.github.com> aeee77e9b8 Release (#596)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-10 12:40:35 +02:00
Felicio Mununga 8f1980eaa7 Update tailwind.config.ts (#595)
* Update tailwind.config.ts

* Update tailwind.config.ts

* Create poor-coats-behave.md
2024-10-10 12:32:11 +02:00
21 changed files with 690 additions and 62 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@status-im/components": patch
---
fix `<DropdownMenu.Item />` onSelect prop
+5
View File
@@ -0,0 +1,5 @@
---
'@status-im/components': patch
---
adds segmented control component
+7 -7
View File
@@ -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) | [![npm version](https://img.shields.io/npm/v/@status-im/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) | [![npm version](https://img.shields.io/npm/v/@status-im/js)](https://www.npmjs.com/package/@status-im/js) | Libary for Waku protocol integration and blockchain interactions. |
| [`@status-im/icons`](./packages/icons) | [![npm version](https://img.shields.io/npm/v/@status-im/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) | [![npm version](https://img.shields.io/npm/v/@status-im/colors.svg)](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) | [![npm version](https://img.shields.io/npm/v/@status-im/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) | [![npm version](https://img.shields.io/npm/v/@status-im/js)](https://www.npmjs.com/package/@status-im/js) | Libary for Waku protocol integration and blockchain interactions. |
| [`@status-im/icons`](./packages/icons) | [![npm version](https://img.shields.io/npm/v/@status-im/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) | [![npm version](https://img.shields.io/npm/v/@status-im/colors.svg)](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) | [![npm version](https://img.shields.io/npm/v/@status-im/eslint-config.svg)](https://www.npmjs.com/package/@status-im/eslint-config) | Shared ESLint configuration for consistent code style across projects. |
## Apps
+85 -21
View File
@@ -1,5 +1,7 @@
#!/usr/bin/env groovy
library 'status-jenkins-lib@v1.9.1'
library 'status-jenkins-lib@v1.9.11'
def changesDetected = false
pipeline {
agent { label 'linux' }
@@ -27,47 +29,109 @@ pipeline {
}
stages {
stage('Check Changed Files') {
when {
changeset pattern: "apps/connector/**", comparator: "GLOB"
}
steps {
script {
changesDetected = true
}
}
}
stage('Install') {
steps { script {
nix.shell('yarn install --frozen-lockfile', pure: false)
} }
when {
expression { changesDetected }
}
steps {
dir("${env.WORKSPACE}/apps/connector") {
script {
nix.shell(
'yarn install --frozen-lockfile',
pure: false,
entryPoint: "${env.WORKSPACE}/apps/connector/shell.nix"
)
}
}
}
}
stage('Build') {
steps { script {
nix.shell('yarn build:chrome', pure: false)
} }
when {
expression { changesDetected }
}
steps {
dir("${env.WORKSPACE}/apps/connector") {
script {
nix.shell(
'yarn build:chrome',
pure: false,
entryPoint: "${env.WORKSPACE}/apps/connector/shell.nix"
)
}
}
}
}
stage('Zip') {
when {
expression { changesDetected }
}
steps {
zip(
zipFile: env.ZIP_NAME,
dir: 'build/chrome-mv3-prod',
archive: false,
)
dir("${env.WORKSPACE}/apps/connector") {
zip(
zipFile: env.ZIP_NAME,
dir: 'build/chrome-mv3-prod',
archive: false,
)
}
}
}
stage('Archive') {
when {
expression { changesDetected }
}
steps {
archiveArtifacts(
artifacts: env.ZIP_NAME,
fingerprint: true,
)
dir("${env.WORKSPACE}/apps/connector") {
archiveArtifacts(
artifacts: env.ZIP_NAME,
fingerprint: true,
)
}
}
}
stage('Upload') {
steps { script {
env.PKG_URL = s5cmd.upload(env.ZIP_NAME)
} }
when {
expression { changesDetected }
}
steps {
dir("${env.WORKSPACE}/apps/connector") {
script {
env.PKG_URL = s5cmd.upload(env.ZIP_NAME)
}
}
}
}
}
post {
success { script { github.notifyPR(true) } }
failure { script { github.notifyPR(false) } }
success {
script {
if(changesDetected) {
github.notifyPR(true)
}
}
}
failure {
script {
if(changesDetected) {
github.notifyPR(false)
}
}
}
cleanup { cleanWs() }
}
}
-4
View File
@@ -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.
+31
View File
@@ -1,5 +1,36 @@
# @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
- 7f21132: fix `<Shortcut />`icon prop and symbol centering
## 1.0.5
### Patch Changes
- 3ad28ea: chore: Remove flex-1 class from button component (#602)
## 1.0.4
### Patch Changes
- a3f9252: fix `<Button />` icon color for blur variant
## 1.0.3
### Patch Changes
- 8f1980e: Update fontSize in tailwind.config.ts
## 1.0.2
### Patch Changes
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@status-im/components",
"version": "1.0.2",
"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>
))}
+37 -5
View File
@@ -76,7 +76,7 @@ function Button(
{iconBefore}
</span>
)}
<span className="flex-1 whitespace-nowrap">{children}</span>
<span className="whitespace-nowrap">{children}</span>
{iconAfter && (
<span className={iconStyles({ size, placement: 'after', variant })}>
{iconAfter}
@@ -143,16 +143,18 @@ const iconStyles = cva({
variant: {
primary: ['text-blur-white/70'],
positive: ['text-blur-white/70'],
grey: ['text-neutral-50'],
darkGrey: [
grey: [
'text-neutral-50',
'blur:text-neutral-80/40',
'dark:text-neutral-40',
'blur:dark:text-neutral-80/40',
'blur:dark:text-white-40',
],
darkGrey: ['text-neutral-40'],
outline: [
'text-neutral-50',
'blur:text-neutral-80/40',
'dark:text-neutral-40',
'blur:dark:text-neutral-80/40',
'blur:dark:text-white-40',
],
ghost: ['text-neutral-50'],
danger: ['text-blur-white/70'],
@@ -191,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'
@@ -9,7 +9,7 @@ const variants = ['primary', 'secondary', 'gray'] as const
// eslint-disable-next-line react/display-name
const renderVariant = (variant: (typeof variants)[number]) => (props: any) => (
<div className="flex items-center gap-2">
<Shortcut {...props} variant={variant} icon={CommandIcon} />
<Shortcut {...props} variant={variant} icon={<CommandIcon />} />
<Shortcut {...props} variant={variant} symbol="K" />
</div>
)
@@ -1,13 +1,14 @@
import { forwardRef } from 'react'
import { cloneElement, forwardRef } from 'react'
import { match, P } from 'ts-pattern'
import { cva } from '../utils/variants'
import type { IconElement } from '../types'
import type { VariantProps } from '../utils/variants'
const styles = cva({
base: 'flex size-4 flex-shrink-0 items-center justify-center rounded-6 border',
base: 'grid size-4 flex-shrink-0 place-items-center rounded-6 border',
variants: {
variant: {
primary: [
@@ -32,7 +33,7 @@ const styles = cva({
type Props = VariantProps<typeof styles> &
(
| {
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>
icon: IconElement
symbol?: never
}
| {
@@ -42,15 +43,17 @@ type Props = VariantProps<typeof styles> &
)
const Shortcut = (props: Props, ref: React.Ref<HTMLDivElement>) => {
const { variant = 'primary', ...rest } = props
const { variant = 'primary' } = props
return (
<div className={styles({ variant })} ref={ref} {...rest}>
<div className={styles({ variant })} ref={ref}>
{match(props)
.with({ symbol: P.string }, ({ symbol }) => (
<span className="text-11 font-medium">{symbol}</span>
))
.with({ icon: P._ }, ({ icon: Icon }) => <Icon className="size-3" />)
.with({ icon: P._ }, ({ icon }) =>
cloneElement(icon, { className: 'size-3' }),
)
.exhaustive()}
</div>
)
+12 -2
View File
@@ -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 })}>
+56 -8
View File
@@ -34,14 +34,62 @@ export default {
},
fontSize: {
88: ['5.5rem', { lineHeight: '5.25rem', letterSpacing: '-0.1155rem' }],
64: ['4rem', { lineHeight: '4.25rem', letterSpacing: '-0.08rem' }],
40: ['2.5rem', { lineHeight: '2.75rem', letterSpacing: '-0.05rem' }],
27: ['1.6875rem', { lineHeight: '2rem', letterSpacing: '0rem' }],
19: ['1.1875rem', { lineHeight: '1.75rem', letterSpacing: '0rem' }],
15: ['0.9375rem', { lineHeight: '1.36rem', letterSpacing: '0rem' }],
13: ['0.8125rem', { lineHeight: '1.1375rem', letterSpacing: '0rem' }],
11: ['0.6875rem', { lineHeight: '1', letterSpacing: '0rem' }],
88: [
'5.5rem',
{
lineHeight: '5.25rem',
letterSpacing: '-0.1155rem',
},
],
64: [
'4rem',
{
lineHeight: '4.25rem',
letterSpacing: '-0.08rem',
},
],
40: [
'2.5rem',
{
lineHeight: '2.75rem',
letterSpacing: '-0.05rem',
},
],
27: [
'1.6875rem',
{
lineHeight: '2rem',
letterSpacing: '-0.0354375rem',
},
],
19: [
'1.1875rem',
{
lineHeight: '1.75rem',
letterSpacing: '-0.019rem',
},
],
15: [
'0.9375rem',
{
lineHeight: '1.359375rem',
letterSpacing: '-0.0084375rem',
},
],
13: [
'0.8125rem',
{
lineHeight: '1.1375rem',
letterSpacing: '-0.0024375rem',
},
],
11: [
'0.6875rem',
{
lineHeight: '0.97625rem',
letterSpacing: '-0.0034375rem',
},
],
},
opacity: {},
+50 -3
View File
@@ -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"