Skip to content

Commit

Permalink
fix(Tabs): support v4 (#433)
Browse files Browse the repository at this point in the history
* fix(Tabs): support v4

* remove comment
  • Loading branch information
yoshi6jp committed Nov 28, 2023
1 parent 858cd96 commit 2dfbf6f
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 214 deletions.
84 changes: 45 additions & 39 deletions .storybook/StoryLayout.tsx
@@ -1,6 +1,6 @@
import React, { ReactNode, useEffect, useMemo, useState } from 'react'
import Highlight, { defaultProps } from "prism-react-renderer"
import theme from "prism-react-renderer/themes/vsDark"
import Highlight, { defaultProps } from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/vsDark'

import { useGlobalTheme } from './theming'

Expand All @@ -17,28 +17,33 @@ type Props = {
}

const StoryLayout = ({ children, title, description, source }: Props) => {
const [tab, setTab] = useState('preview')
const globalTheme = useGlobalTheme()

useEffect(() => {
document.getElementsByTagName('html')[0].setAttribute('data-theme', globalTheme)
document
.getElementsByTagName('html')[0]
.setAttribute('data-theme', globalTheme)
}, [globalTheme])

const Code = () => useMemo(() => (
<Highlight {...defaultProps} theme={theme} code={source} language="jsx">
{({ tokens, getLineProps, getTokenProps }) => (
<pre slot="html">
{tokens.map((line, i) => (
<div {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span {...getTokenProps({ token, key })} />
const Code = () =>
useMemo(
() => (
<Highlight {...defaultProps} theme={theme} code={source} language="jsx">
{({ tokens, getLineProps, getTokenProps }) => (
<pre slot="html">
{tokens.map((line, i) => (
<div {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span {...getTokenProps({ token, key })} />
))}
</div>
))}
</div>
))}
</pre>
)}
</Highlight>
), [theme, source])
</pre>
)}
</Highlight>
),
[theme, source]
)

return (
<Theme dataTheme={globalTheme} className="w-full h-screen p-8 bg-base-100">
Expand All @@ -53,44 +58,45 @@ const StoryLayout = ({ children, title, description, source }: Props) => {
<p className="text-base-content">{description}</p>
<div className="my-4">
{/* Mobile view */}
<div className='block sm:hidden'>
<div className="block sm:hidden">
{children}
<CodeMockup className="w-full mb-8 mt-3">
<Code />
</CodeMockup>
</div>

{/* Desktop view */}
<div className='hidden sm:grid'>
<Tabs
className='z-10 -mb-px'
variant='lifted'
value={tab}
onChange={(tab) => setTab(tab === 'fullWidthClick' ? 'html' : tab)}
>
<Tabs.Tab value="preview" className="[--tab-bg:hsl(var(--b2))]">
Preview
</Tabs.Tab>
<Tabs.Tab value="html" className={tab === 'html' ? "[--tab-bg:hsl(var(--n))] [--tab-border-color:hsl(var(--n))] [--tab-color:hsl(var(--nc))]" : ""}>
HTML
</Tabs.Tab>
<Tabs.Tab value="fullWidthClick" className="mr-6 flex-1 cursor-default [--tab-border-color:transparent]" />
</Tabs>
<div className='rounded-b-box rounded-tr-box relative overflow-x-auto'>
{tab === 'preview' ? (
<div className="hidden sm:grid">
<Tabs className="z-10 -mb-px" variant="lifted">
<Tabs.RadioTab
name="content"
label="Preview"
defaultChecked={true}
className="checked:[--tab-bg:var(--fallback-b1,oklch(var(--b1)))] checked:!border-base-300
[--tab-border-color:transparent]"
contentClassName="overflow-x-auto"
>
<div
className="preview border-base-300 bg-base-100 rounded-se-box rounded-b-box
flex min-h-[6rem] min-w-[18rem] flex-wrap items-center justify-center gap-2
overflow-x-hidden overflow-y-hidden border bg-cover bg-top p-4"
>
{children}
</div>
) : (
</Tabs.RadioTab>
<Tabs.RadioTab
name="content"
label="HTML"
className="[--tab-border-color:transparent] [--tab-bg:var(--fallback-n,oklch(var(--n)))]
checked:[--tab-color:var(--fallback-nc,oklch(var(--nc)))]
checked:[--tab-border-color:var(--fallback-n,oklch(var(--n)))]"
contentClassName="overflow-x-auto"
>
<CodeMockup className="w-full mb-8">
<Code />
</CodeMockup>
)}
</div>
</Tabs.RadioTab>
</Tabs>
</div>
</div>
</div>
Expand Down
58 changes: 58 additions & 0 deletions src/Tabs/RadioTab.tsx
@@ -0,0 +1,58 @@
import React, { forwardRef, ReactNode } from 'react'
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'

export type RadioTabProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'type'
> & {
active?: boolean
disabled?: boolean
label: string
name: string
contentClassName?: string
}

const RadioTab = forwardRef<HTMLInputElement, RadioTabProps>(
(
{
children,
className,
active,
label,
disabled,
name,
contentClassName,
...props
},
ref
): JSX.Element => {
const classes = twMerge(
'tab',
className,
clsx({
'tab-active': active,
'tab-disabled': disabled,
})
)
const contentClasses = twMerge('tab-content', contentClassName)

return (
<>
<input
className={classes}
role="tab"
type="radio"
name={name}
disabled={disabled}
aria-label={label}
{...props}
ref={ref}
/>
<div className={contentClasses}>{children}</div>
</>
)
}
)

export default RadioTab
79 changes: 20 additions & 59 deletions src/Tabs/Tab.tsx
@@ -1,67 +1,28 @@
import React from 'react'
import React, { forwardRef } from 'react'
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'

import { ComponentSize } from '../types'

export type TabProps<T> = Omit<
React.AnchorHTMLAttributes<HTMLAnchorElement>,
'onClick'
> & {
value: T
activeValue?: T
onClick?: (value: T) => void
size?: ComponentSize
variant?: 'boxed' | 'bordered' | 'lifted'
export type TabProps = React.AnchorHTMLAttributes<HTMLAnchorElement> & {
active?: boolean
disabled?: boolean
}

const TabInner = <T extends string | number | undefined>(
{
children,
value,
activeValue,
onClick,
size,
variant,
disabled,
className,
style,
...props
}: TabProps<T>,
ref?: React.ForwardedRef<T>
): JSX.Element => {
const classes = twMerge(
'tab',
className,
clsx({
'tab-active': value != null && value === activeValue,
'tab-disabled': disabled,
'tab-lg': size === 'lg',
'tab-md': size === 'md',
'tab-sm': size === 'sm',
'tab-xs': size === 'xs',
'tab-bordered': variant === 'bordered',
'tab-lifted': variant === 'lifted',
})
)

return (
<a
role="tab"
{...props}
className={classes}
style={style}
onClick={() => onClick && onClick(value)}
>
{children}
</a>
)
}

// Make forwardRef work with generic component
const Tab = React.forwardRef(TabInner) as <T>(
props: TabProps<T> & { ref?: React.ForwardedRef<HTMLAnchorElement> }
) => ReturnType<typeof TabInner>
const Tab = forwardRef<HTMLAnchorElement, TabProps>(
({ children, className, active, disabled, ...props }, ref): JSX.Element => {
const classes = twMerge(
'tab',
className,
clsx({
'tab-active': active,
'tab-disabled': disabled,
})
)
return (
<a role="tab" {...props} ref={ref} className={classes}>
{children}
</a>
)
}
)

export default Tab

0 comments on commit 2dfbf6f

Please sign in to comment.