Skip to content

Commit

Permalink
feat: segmented control - tabs implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Avinash Verma committed Dec 5, 2024
1 parent a35772f commit c3a7420
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 55 deletions.
4 changes: 3 additions & 1 deletion lib/src/components/segmented-control/SegmentedControl.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SegmentedControlContent } from './SegmentedControlContent'
import { SegmentedControlDescription } from './SegmentedControlDescription'
import { SegmentedControlHeading } from './SegmentedControlHeading'
import { SegmentedControlIcon } from './SegmentedControlIcon'
Expand All @@ -9,5 +10,6 @@ export const SegmentedControl = {
Item: SegmentedControlItem,
Heading: SegmentedControlHeading,
Description: SegmentedControlDescription,
Icon: SegmentedControlIcon
Icon: SegmentedControlIcon,
Content: SegmentedControlContent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as React from 'react'

import { Tabs } from '../tabs'

export const SegmentedControlContent = Tabs.Content
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import * as React from 'react'

import type { SegmentedControlRootProps } from './SegmentedControlRoot'

export type SegmentedControlTheme = 'primary' | 'marsh'

interface SegmentedControlContextValue {
size: SegmentedControlRootProps['size']
theme: SegmentedControlTheme
}

export interface SegmentedControlProviderProps
Expand All @@ -16,11 +19,12 @@ const SegmentedControlContext =

export const SegmentedControlProvider = ({
size,
theme,
children
}: SegmentedControlProviderProps): JSX.Element => {
const value = React.useMemo<SegmentedControlContextValue>(
() => ({ size }),
[size]
() => ({ size, theme }),
[size, theme]
)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useSegmentedControl } from './SegmentedControlContext'
const StyledText = styled(Text, {
fontFamily: '$body',
color: '$textSubtle',
fontWeight: 400,
variants: {
size: {
sm: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { useSegmentedControl } from './SegmentedControlContext'

const StyledHeading = styled(Heading, {
fontFamily: '$body',
fontWeight: 600,
color: '$textBold',
variants: {
size: {
sm: {
Expand Down
90 changes: 62 additions & 28 deletions lib/src/components/segmented-control/SegmentedControlItem.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
import * as React from 'react'

import { styled } from '../../stitches'
import { ToggleGroup } from '../toggle-group'
import { Flex } from '../flex'
import { Tabs } from '../tabs'
import { useSegmentedControl } from './SegmentedControlContext'

const StyledItem = styled(ToggleGroup.Item, {
const StyledItem = styled(Tabs.Trigger, {
bg: 'transparent',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
border: 'none',
borderRadius: '$2 !important',
boxShadow: 'none',
borderRadius: '$2',
overflow: 'hidden',
boxShadow: 'none !important',
p: 0,
variants: {
theme: {
primary: {
'&[data-state=inactive]:hover': {
bg: '$primary300'
}
},
marsh: {
'&[data-state=inactive]:hover': {
bg: '$marsh300'
}
}
}
},
'& > div': { display: 'none' },
'&[data-state=active]': {
bg: 'white',
boxShadow: 'none',
'& :is(h1,h2,h3,h4,h5,h6)': {
fontWeight: 600,
color: '$textBold'
},
'&:hover': {
bg: 'white'
}
},
'&[data-state=inactive]': {
'& :is(h1,h2,h3,h4,h5,h6)': {
fontWeight: 400,
color: '$grey900'
}
},
'&[disabled]': {
opacity: 0.3
}
})

const StyledContainer = styled(Flex, {
variants: {
size: {
sm: {
Expand All @@ -30,29 +68,25 @@ const StyledItem = styled(ToggleGroup.Item, {
maxWidth: '306px'
}
}
},
'&[data-state=on]': {
bg: 'white',
boxShadow: 'none'
},
'&:hover[data-state=off]': {
bg: '$primary300'
},
'$:focus': {
boxShadow: '$primary'
},
'&[disabled]': {
opacity: 0.3
},
'&:active': {
bg: '$primary100'
}
})

export const SegmentedControlItem = (
props: Omit<React.ComponentProps<typeof StyledItem>, 'size'>
): JSX.Element => {
const { size } = useSegmentedControl()
export const SegmentedControlItem = ({
children,
...props
}: Omit<React.ComponentProps<typeof StyledItem>, 'size'>): JSX.Element => {
const { size, theme } = useSegmentedControl()

return <StyledItem {...props} size={size} />
return (
<StyledItem {...props} theme={theme}>
<StyledContainer
size={size}
direction="column"
align="center"
justify="center"
>
{children}
</StyledContainer>
</StyledItem>
)
}
56 changes: 34 additions & 22 deletions lib/src/components/segmented-control/SegmentedControlRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import * as React from 'react'

import { styled } from '../../stitches'
import { ToggleGroup } from '../toggle-group'
import { SegmentedControlProvider } from './SegmentedControlContext'
import { Tabs } from '../tabs'
import { SegmentedControlContent } from './SegmentedControlContent'
import {
SegmentedControlProvider,
SegmentedControlTheme
} from './SegmentedControlContext'
import { SegmentedControlItem } from './SegmentedControlItem'

const StyledRoot = styled(ToggleGroup.Root, {
bg: '$primary200',
p: '$1',
borderRadius: '$3',
const StyledTabsRoot = styled(Tabs, {
width: 'unset',
'& > div': { border: 'none' },
variants: {
size: {
sm: {},
Expand All @@ -17,29 +21,37 @@ const StyledRoot = styled(ToggleGroup.Root, {
}
})

export type SegmentedControlRootProps = Omit<
React.ComponentProps<typeof StyledRoot>,
'gap' | 'type' | 'wrap'
>
export type SegmentedControlRootProps = React.ComponentProps<
typeof StyledTabsRoot
> & {
theme?: SegmentedControlTheme
}

export const SegmentedControlRoot = ({
size,
children,
theme = 'primary',
...props
}: SegmentedControlRootProps): JSX.Element => {
const tabTriggers = React.Children.toArray(children).filter(
(child) =>
React.isValidElement(child) && child.type === SegmentedControlItem
)
const tabContents = React.Children.toArray(children).filter(
(child) =>
React.isValidElement(child) && child.type === SegmentedControlContent
)

return (
<SegmentedControlProvider size={size}>
<StyledRoot
{...props}
type="single"
wrap={false}
gap={null}
hasGap
size={size}
orientation="horizontal"
>
{children}
</StyledRoot>
<SegmentedControlProvider size={size} theme={theme}>
<StyledTabsRoot {...props} size={size}>
<Tabs.TriggerList
css={{ bg: `$${theme}200`, p: '$1', borderRadius: '$3' }}
>
{tabTriggers}
</Tabs.TriggerList>
{tabContents}
</StyledTabsRoot>
</SegmentedControlProvider>
)
}

0 comments on commit c3a7420

Please sign in to comment.