Skip to content

Commit

Permalink
feat(tabs): adds tabs ctx and first props (intent and size)
Browse files Browse the repository at this point in the history
  • Loading branch information
soykje committed May 5, 2023
1 parent cf84ce6 commit 049945c
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 62 deletions.
14 changes: 13 additions & 1 deletion packages/components/tabs/src/Tabs.doc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ import { Tabs } from "@spark-ui/tabs"
}}
/>

## Variants
## Default

<Canvas of={stories.Default} />

## Intent

Use `intent` prop to set the color of active tab item.

<Canvas of={stories.Intent} />

## Size

Use `size` prop to set the size of items.

<Canvas of={stories.Size} />
83 changes: 48 additions & 35 deletions packages/components/tabs/src/Tabs.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import { Meta, StoryFn } from '@storybook/react'

import { Tabs } from '.'
import type { TabsRootProps } from './TabsRoot'

const meta = {
const meta: Meta<typeof Tabs> = {
title: 'Components/Tabs',
component: Tabs,
subcomponents: {
'Tabs.List': Tabs.List,
'Tabs.Trigger': Tabs.Trigger,
'Tabs.Content': Tabs.Content,
},
} as Meta<typeof Tabs>
}

export default meta

interface Tab {
title: string
value: string
}

const tabs: Tab[] = [
const tabs = [
{
title: 'Inbox',
value: 'tab1',
Expand All @@ -35,27 +26,49 @@ const tabs: Tab[] = [
},
]

export const Default: StoryFn = _args => (
<Tabs defaultValue="tab1">
<Tabs.List>
{tabs.map(({ title, value }) => (
<Tabs.Trigger key={value} value={value}>
<span>{title}</span>
</Tabs.Trigger>
))}
</Tabs.List>
{tabs.map(({ value }) => (
<Tabs.Content key={value} value={value}>
<span>
{
const invokeTabs = (customProps: TabsRootProps = {}) => {
return (
<Tabs defaultValue="tab1" {...customProps}>
<Tabs.List>
{tabs.map(({ title, value }) => (
<Tabs.Trigger key={value} value={value}>
<span>{title}</span>
</Tabs.Trigger>
))}
</Tabs.List>

{tabs.map(({ value }) => (
<Tabs.Content key={value} value={value}>
<span>
{
tab1: 'Your inbox is empty',
tab2: 'Make some coffee',
tab3: 'Order more coffee',
}[value]
}
</span>
</Tabs.Content>
))}
</Tabs>
{
tab1: 'Your inbox is empty',
tab2: 'Make some coffee',
tab3: 'Order more coffee',
}[value]
}
</span>
</Tabs.Content>
))}
</Tabs>
)
}

export const Default: StoryFn = _args => invokeTabs({ size: 'xs' })

export const Intent: StoryFn = _args => (
<div className="gap-lg flex flex-row">
{invokeTabs()}
{invokeTabs({ intent: 'secondary' })}
</div>
)

export const Size: StoryFn = _args => (
<div className="gap-lg flex flex-row">
{invokeTabs({ size: 'xs' })}

{invokeTabs({ size: 'sm' })}

{invokeTabs({ size: 'md' })}
</div>
)
36 changes: 36 additions & 0 deletions packages/components/tabs/src/Tabs.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable tailwindcss/no-custom-classname */
import { cva, type VariantProps } from 'class-variance-authority'

export const tabsTriggerVariants = cva(
[
'px-lg',
'border-b-sm border-outline',
'relative',
'after:content after:left-none after:right-none after:h-sz-2 after:absolute after:bottom-[-1px]',
'outline-none',
'focus-visible:ring-outline-high ring-inset focus-visible:border-none focus-visible:ring-2',
],
{
variants: {
intent: {
primary: ['spark-state-active:text-primary spark-state-active:after:bg-primary'],
secondary: ['spark-state-active:text-secondary spark-state-active:after:bg-secondary'],
},
size: {
xs: ['h-sz-32 text-caption'],
sm: ['h-sz-36 text-body-2'],
md: ['h-sz-40 text-body-1'],
},
},
defaultVariants: {
intent: 'primary',
size: 'md',
},
}
)

export type TabsTriggerVariantsProps = VariantProps<typeof tabsTriggerVariants>
// {
// intent?: 'primary'|'secondary'
// size?: 'xs'|'sm'|'md'
// }
17 changes: 17 additions & 0 deletions packages/components/tabs/src/TabsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createContext, useContext } from 'react'

import { TabsTriggerVariantsProps } from './Tabs.styles'

export type TabsContextInterface = TabsTriggerVariantsProps

export const TabsContext = createContext<TabsContextInterface>({})

export const useTabsContext = () => {
const context = useContext(TabsContext)

if (!context) {
throw Error('useTabsContext must be used within a TabsContext Provider')
}

return context
}
32 changes: 18 additions & 14 deletions packages/components/tabs/src/TabsRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,42 @@ import * as RadixTabs from '@radix-ui/react-tabs'
import { cva } from 'class-variance-authority'
import { forwardRef, type PropsWithChildren } from 'react'

import { TabsContext, type TabsContextInterface } from './TabsContext'

export const styles = cva(['flex', 'flex-col', 'px-sm', 'py-sm'])

export type TabsRootProps = PropsWithChildren<RadixTabs.TabsProps>
export type TabsRootProps = PropsWithChildren<Omit<RadixTabs.TabsProps, 'activationMode'>> &
TabsContextInterface

export const TabsRoot = forwardRef<HTMLDivElement, TabsRootProps>(
(
{
intent = 'primary',
size = 'md',
/**
* Default Radix Primitive values
* see https://www.radix-ui.com/docs/primitives/components/tabs#root
*/
asChild = false,
orientation = 'horizontal',
activationMode = 'automatic',
children,
...rest
},
ref
) => {
return (
<RadixTabs.Root
ref={ref}
className={styles()}
asChild={asChild}
orientation={orientation}
activationMode={activationMode}
{...rest}
>
{children}
</RadixTabs.Root>
<TabsContext.Provider value={{ intent, size }}>
<RadixTabs.Root
ref={ref}
className={styles()}
asChild={asChild}
orientation={orientation}
activationMode="automatic"
{...rest}
>
{children}
</RadixTabs.Root>
</TabsContext.Provider>
)
}
)

TabsRoot.displayName = 'Tabs.Root'
17 changes: 5 additions & 12 deletions packages/components/tabs/src/TabsTrigger.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import * as RadixTabs from '@radix-ui/react-tabs'
import { cva } from 'class-variance-authority'
import { forwardRef } from 'react'

export const styles = cva([
'px-lg h-sz-44',
'outline-none',
'text-body-2 text-on-surface',
'border-b-sm border-outline',
// radix states
'hover:text-outline',
'focus-visible:ring-outline-high ring-inset focus-visible:border-none focus-visible:ring-2',
'spark-state-active:text-primary spark-state-active:border-b-md spark-state-active:border-primary spark-state-active:mb-[1px]',
])
import { tabsTriggerVariants } from './Tabs.styles'
import { useTabsContext } from './TabsContext'

export interface TabsTriggerProps extends RadixTabs.TabsTriggerProps {
disabled?: boolean
Expand All @@ -31,10 +22,12 @@ export const TabsTrigger = forwardRef<HTMLButtonElement, TabsTriggerProps>(
},
ref
) => {
const { intent, size } = useTabsContext()

return (
<RadixTabs.Trigger
ref={ref}
className={styles()}
className={tabsTriggerVariants({ intent, size })}
asChild={asChild}
disabled={disabled}
{...rest}
Expand Down

0 comments on commit 049945c

Please sign in to comment.