From e5178368d11a8b0bc65deb49f53b81d786e337da Mon Sep 17 00:00:00 2001 From: Kaung Htet Naing Date: Thu, 16 May 2024 12:30:57 +0700 Subject: [PATCH 1/2] add: Tab doc --- .github/docs/Tab.md | 62 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/docs/Tab.md diff --git a/.github/docs/Tab.md b/.github/docs/Tab.md new file mode 100644 index 0000000..d839efc --- /dev/null +++ b/.github/docs/Tab.md @@ -0,0 +1,62 @@ +# `Tab` + +The Tab component is a flexible and responsive tabbed navigation component that integrates. It uses @radix-ui/react-radio-group for the tab selection mechanism and includes support for route synchronization and dynamic content rendering. + +## Usage + +``` +import Tab from './Tab'; + +const options = [ + { label: 'Tab 1', value: 'tab1' }, + { label: 'Tab 2', value: 'tab2' }, + { label: 'Tab 3', value: 'tab3' }, +]; + +const panels = [ +
Content for Tab 1
, +
Content for Tab 2
, +
Content for Tab 3
, +]; + +function MyComponent() { + return ( + + {(value) => ( +
+ Selected Tab: {value} +
+ )} +
+ ); +} + +export default MyComponent; + +``` + +## Props + + +| Name | Type | Default | Description | +| :---------------------- | :------------------------------ | :---------- | :------------------------------------------------------------------------------------------------------- | +| | | | +| `name` | `string ` | `tab` | Name of the query parameter to use for syncing the selected tab with the URL. | +| `color` | `enum (primary)` | `primary` | The color variant to apply to the tabs. | +| `panels` | `ReactNode[] ` | `undefined` | An array of React nodes to be displayed as panels corresponding to each tab . | +| `options` | `array ` | `undefined` | An array of option objects, where each object should have a label and value property or string. | +| `shallow` | `boolean ` | `true` | Uses shallow routing to avoid a full page reload when changing tabs . | +| `variant` | `enum` | `undefined` | \_ | +| `$default` | `string` | `undefined` | The default selected tab value if no query parameter is present or no value is set. | +| `children` | `(value: string) => ReactNode` | `undefined` | A render function that receives the current tab value and returns React nodes to be rendered. | +| `className` | `string` | `undefined` | Additional class names to apply to the root element. | +| `syncRoute` | `boolean` | `true` | Syncs the selected tab with the URL query parameter. | +| `responsive` | `boolean` | `true` | The component adapts to responsive layouts, hiding tabs on small screens and showing a dropdown instead. | +| `removeQueriesOnChange` | `string[]` | `[]` | An array of query parameters to remove from the URL when the tab changes. | +| `...rest` | `HTMLAttribute` | `undefined` | HTML attributes that can be passed. | | + +## Notes + +- The Tab component integrates with Next.js routing to allow tab state synchronization via URL query parameters. +- It includes a responsive design, showing a dropdown on smaller screens when the responsive prop is true. +- The component supports dynamic content rendering via the children render function and the panels array. From 6c61ad4f9fbb1ba027c1b2743df8f3d44d710aaf Mon Sep 17 00:00:00 2001 From: sudoaugustin Date: Fri, 17 May 2024 19:46:05 +0630 Subject: [PATCH 2/2] update: docs --- .github/docs/Tab.md | 60 ++++++++++-------------- apps/web/components/Accordion.tsx | 5 +- apps/web/components/Tab.tsx | 77 ++++++++++++------------------- 3 files changed, 56 insertions(+), 86 deletions(-) diff --git a/.github/docs/Tab.md b/.github/docs/Tab.md index d839efc..1ccbfab 100644 --- a/.github/docs/Tab.md +++ b/.github/docs/Tab.md @@ -1,11 +1,11 @@ -# `Tab` +# [`Tab`](../../apps/web/components/Tab.tsx) -The Tab component is a flexible and responsive tabbed navigation component that integrates. It uses @radix-ui/react-radio-group for the tab selection mechanism and includes support for route synchronization and dynamic content rendering. +The Tab component is a flexible and responsive tabbed navigation component that integrates. It uses [`@radix-ui/react-radio-group`](https://www.radix-ui.com/primitives/docs/components/radio-group) for the tab selection mechanism and includes support for route synchronization with NextJS router and dynamic content rendering. ## Usage -``` -import Tab from './Tab'; +```tsx +import Tab from '@web/components/Tab'; const options = [ { label: 'Tab 1', value: 'tab1' }, @@ -19,44 +19,32 @@ const panels = [
Content for Tab 3
, ]; -function MyComponent() { - return ( - - {(value) => ( -
- Selected Tab: {value} -
- )} -
- ); -} +// Usage 1: Children as array + + {panels} + -export default MyComponent; +// Usage 2: Children as function + + {(value) => ( +
+ Selected Tab: {value} +
+ )} +
``` ## Props -| Name | Type | Default | Description | -| :---------------------- | :------------------------------ | :---------- | :------------------------------------------------------------------------------------------------------- | -| | | | -| `name` | `string ` | `tab` | Name of the query parameter to use for syncing the selected tab with the URL. | -| `color` | `enum (primary)` | `primary` | The color variant to apply to the tabs. | -| `panels` | `ReactNode[] ` | `undefined` | An array of React nodes to be displayed as panels corresponding to each tab . | -| `options` | `array ` | `undefined` | An array of option objects, where each object should have a label and value property or string. | -| `shallow` | `boolean ` | `true` | Uses shallow routing to avoid a full page reload when changing tabs . | -| `variant` | `enum` | `undefined` | \_ | -| `$default` | `string` | `undefined` | The default selected tab value if no query parameter is present or no value is set. | -| `children` | `(value: string) => ReactNode` | `undefined` | A render function that receives the current tab value and returns React nodes to be rendered. | -| `className` | `string` | `undefined` | Additional class names to apply to the root element. | +| Name | Type | Default | Description | +| :---------------------- | :------------------------------ | :---------- | :------------------------------------------------------------------------------------------------------- | +| `options` | [`Option[]`](../../apps/web/types/globals.d.ts#L7) | `[]` | An array of option objects, where each object should have a label and value property or string. | | +| `variant` | `enum` | `default` | The variant enum value will comes from `cva`. | +| `$default` | `string` | | The default selected tab value if no query parameter is present or no value is set. | +| `children` | `ReactNode[] \| ((value: string) => ReactNode)`| | A render function or React nodes that receives the current tab value and returns React nodes to be rendered.| | `syncRoute` | `boolean` | `true` | Syncs the selected tab with the URL query parameter. | -| `responsive` | `boolean` | `true` | The component adapts to responsive layouts, hiding tabs on small screens and showing a dropdown instead. | +| `shallow` | `boolean ` | `true` | Uses shallow routing to avoid a full page reload when changing tabs . +| `name` | `string ` | `tab` | Name of the query parameter to use for syncing the selected tab with the URL. | | `removeQueriesOnChange` | `string[]` | `[]` | An array of query parameters to remove from the URL when the tab changes. | -| `...rest` | `HTMLAttribute` | `undefined` | HTML attributes that can be passed. | | - -## Notes - -- The Tab component integrates with Next.js routing to allow tab state synchronization via URL query parameters. -- It includes a responsive design, showing a dropdown on smaller screens when the responsive prop is true. -- The component supports dynamic content rendering via the children render function and the panels array. diff --git a/apps/web/components/Accordion.tsx b/apps/web/components/Accordion.tsx index d25649c..d4ea68b 100644 --- a/apps/web/components/Accordion.tsx +++ b/apps/web/components/Accordion.tsx @@ -1,4 +1,3 @@ -import HTML from '@pkg/components/HTML' import * as RadixAccordion from '@radix-ui/react-accordion' import { cva, VariantProps } from 'class-variance-authority' import { ReactNode } from 'react' @@ -49,14 +48,14 @@ const contentClass = cva('overflow-hidden !transition-[grid-template-rows] !dura export default function Accordion({ items, color = 'default', classes, ...rest }: Props) { return ( - {items.map(({ title, content, children }) => ( + {items.map(({ title, content }) => (

{title}

{/* Replace with project based indicator icon */}
- {content} + {content}
))} diff --git a/apps/web/components/Tab.tsx b/apps/web/components/Tab.tsx index b77168b..0f6392c 100644 --- a/apps/web/components/Tab.tsx +++ b/apps/web/components/Tab.tsx @@ -2,67 +2,54 @@ import { cookOptions } from '@pkg/utils' import * as RadioGroup from '@radix-ui/react-radio-group' import { VariantProps, cva } from 'class-variance-authority' import { useRouter } from 'next/router' -import { Fragment, HTMLAttributes, ReactNode, useState } from 'react' -import Dropdown from './Dropdown' +import { Fragment, ReactNode, useState } from 'react' import ScrollArea from './ScrollArea' -type Props = Omit, 'children'> & - VariantProps & { - name?: string - options: Option[] - panels?: ReactNode[] - shallow?: boolean - $default?: string - children?: (value: string) => ReactNode - syncRoute?: boolean - responsive?: boolean - removeQueriesOnChange?: string[] - } +type Props = VariantProps & { + options: Option[] + children: ReactNode[] | ((value: string) => ReactNode) + $default?: string + name?: string + shallow?: boolean + syncRoute?: boolean + removeQueriesOnChange?: string[] +} const list = cva('', { variants: { - color: { - primary: '', + variant: { + default: '', }, - variant: {}, }, }) -const item = cva('flex-shrink-0 duration-150', { +const item = cva('', { variants: { - color: { - primary: '', + variant: { + default: '', }, - variant: {}, }, }) export default function Tab({ - name = 'tab', - color = 'primary', - panels, options, - shallow = true, - variant, + variant = 'default', $default, children, - className, + name = 'tab', + shallow = true, syncRoute = true, - responsive = true, removeQueriesOnChange = [], - ...rest }: Props) { const router = useRouter() const [value, setValue] = useState('') const $options = cookOptions(options) - const { query } = router - const queryValue = query[name] as string - const $value = (syncRoute ? queryValue : value) || $default || $options[0]?.value || '' - const activeIndex = $options.findIndex(option => option.value === $value) + const curValue = (syncRoute ? (router.query[name] as string) : value) || $default || $options[0].value const handleChange = (value: string) => { if (syncRoute) { + const { query } = router removeQueriesOnChange.forEach(element => delete query[element]) router.push({ query: { ...query, ...(value ? { [name]: value } : undefined) } }, undefined, { shallow }) } else { @@ -72,20 +59,16 @@ export default function Tab({ return ( -
- - - {$options.map(({ label, value }) => ( - - {label} - - ))} - - - -
- {children && children($value)} - {panels && panels[activeIndex]} + + + {$options.map(({ label, value }) => ( + + {label} + + ))} + + + {typeof children === 'function' ? children(curValue) : children[$options.findIndex(option => option.value === curValue)]}
) }