Skip to content

Commit

Permalink
Feat/tabs v1 (#222)
Browse files Browse the repository at this point in the history
* feat: add exports

* feat: add init tabs code

* chore: add usage

* chore: deprecate old tabs code

* docs: add mdx docs

* chore: remove unwanted comments

* chore: remove comment

* fix: add nowrap to long text

* revert

* fix: add disabled state

* fix: mdx incorrect font size and other fixex
  • Loading branch information
paanSinghCoder authored Jan 10, 2025
1 parent 4d53fcf commit f966e50
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 74 deletions.
209 changes: 138 additions & 71 deletions apps/www/content/primitives/components/tabs.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Tabs
description: A set of layered sections of content—known as tab panels—that are displayed one at a time.
description: A set of layered sections of content that display one panel at a time.
radix:
link: https://www.radix-ui.com/docs/primitives/components/tabs
api: https://www.radix-ui.com/docs/primitives/components/tabs#api-reference
Expand All @@ -9,89 +9,156 @@ radix:
## Preview

<Preview>
<Flex
style={{
flexDirection: "column",
alignItems: "center",
gap: "8px",
}}
>
<Tabs defaultValue="tab-one">
<Tabs.List>
<Tabs.Trigger value="tab-one">General</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three">Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
</Tabs.List>

</Tabs>

<Tabs defaultValue="tab-one">
<Tabs.List underline>
<Tabs.Trigger value="tab-one">General</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three">Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
</Tabs.List>
</Tabs>
<Tabs defaultValue="tab-one">
<Tabs.List elevated>
<Tabs.Trigger value="tab-one">General</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three">Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
</Tabs.List>
</Tabs>
<Flex direction="row" gap="large" style={{ width: "100%", fontSize: "12px" }}>
<Tabs.Root defaultValue="tab-one">
<Tabs.List>
<Tabs.Trigger value="tab-one" icon={<InfoCircledIcon />}>Hoisting</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three" icon={<InfoCircledIcon />}>Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab-one">
<Text>General settings content</Text>
</Tabs.Content>
<Tabs.Content value="tab-two">
<Text>Hosting configuration content</Text>
</Tabs.Content>
<Tabs.Content value="tab-three">
<Text>Editor preferences content</Text>
</Tabs.Content>
<Tabs.Content value="tab-four">
<Text>Billing information content</Text>
</Tabs.Content>
<Tabs.Content value="tab-five">
<Text>SEO settings content</Text>
</Tabs.Content>
</Tabs.Root>
</Flex>
</Preview>

## Usage

The Tabs component provides a way to organize content into multiple sections, displaying one section at a time.

## Installation

Install the component from your command line.

<LiveProvider>
<LiveEditor code={`npm install @raystack/apsara`} border />
<LiveEditor code={`npm install @raystack/apsara`} border language="shell" />
</LiveProvider>

## Anatomy

Import all parts and piece them together.

<LiveProvider>
<LiveEditor code={`
import { Tabs } from '@raystack/apsara'
<LiveEditor code={`import { Tabs } from '@raystack/apsara/v1'
<Tabs defaultValue="tab-one">
<Tabs.Root defaultValue="tab1">
<Tabs.List>
<Tabs.Trigger value="tab-one">General</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three">Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
<Tabs.Trigger value="tab1">Tab One</Tabs.Trigger>
<Tabs.Trigger value="tab2">Tab Two</Tabs.Trigger>
</Tabs.List>
</Tabs>
<Tabs defaultValue="tab-one">
<Tabs.List underline>
<Tabs.Trigger value="tab-one">General</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three">Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
<Tabs.Content value="tab1">Tab one content</Tabs.Content>
<Tabs.Content value="tab2">Tab two content</Tabs.Content>
</Tabs.Root>`} border/>
</LiveProvider>

## Tabs Props

The Tabs component is composed of several parts, each with their own props:

### Root Props

- `defaultValue`: The initial active tab value. If not passed, no tab will be selected by default.
- `value`: Controlled active tab value.
- `onValueChange`: Callback when active tab changes.
- `className`: Additional CSS class names.

### List Props

- `className`: Additional CSS class names

### Trigger Props

- `value`: Unique identifier for the tab (required)
- `icon`: Optional icon element to display
- `disabled`: Whether the tab is disabled
- `className`: Additional CSS class names

### Content Props

- `value`: Matching identifier for the tab (required)
- `className`: Additional CSS class names

## Examples

### Basic Tabs

<Playground
scope={{ Tabs }}
tabs={[
{
name: "Basic",
code: `
<Tabs.Root defaultValue="tab1">
<Tabs.List>
<Tabs.Trigger value="tab1">Account</Tabs.Trigger>
<Tabs.Trigger value="tab2">Password</Tabs.Trigger>
<Tabs.Trigger value="tab3">Settings</Tabs.Trigger>
</Tabs.List>
</Tabs>
<Tabs defaultValue="tab-one">
<Tabs.List elevated>
<Tabs.Trigger value="tab-one">General</Tabs.Trigger>
<Tabs.Trigger value="tab-two">Hosting</Tabs.Trigger>
<Tabs.Trigger value="tab-three">Editor</Tabs.Trigger>
<Tabs.Trigger value="tab-four">Billing</Tabs.Trigger>
<Tabs.Trigger value="tab-five">SEO</Tabs.Trigger>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs.Root>`,
},
]}
/>

### With Icons

<Playground
scope={{ Tabs, InfoCircledIcon }}
tabs={[
{
name: "With Icons",
code: `
<div style={{ width: "100px" }}>
<Tabs.Root defaultValue="tab1">
<Tabs.List>
<Tabs.Trigger value="tab1">Home</Tabs.Trigger>
<Tabs.Trigger value="tab2" icon={<InfoCircledIcon />} />
</Tabs.List>
<Tabs.Content value="tab1">Home</Tabs.Content>
<Tabs.Content value="tab2">Info</Tabs.Content>
</Tabs.Root>
</div>`,
},
]}
/>

### With Disabled Tab

<Playground
scope={{ Tabs }}
tabs={[
{
name: "Disabled",
code: `
<Tabs.Root defaultValue="tab1">
<Tabs.List>
<Tabs.Trigger value="tab1">Active</Tabs.Trigger>
<Tabs.Trigger value="tab2" disabled>Disabled</Tabs.Trigger>
</Tabs.List>
</Tabs>
`} border />
</LiveProvider>
<Tabs.Content value="tab1">Active tab content</Tabs.Content>
<Tabs.Content value="tab2">Disabled tab content</Tabs.Content>
</Tabs.Root>`,
},
]}
/>

## Accessibility

Tabs follow the [WAI-ARIA Tabs Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/). They include the following accessibility features:

- Keyboard navigation between tabs using arrow keys
- Proper ARIA roles, states, and properties
- Focus management for tab panels
37 changes: 35 additions & 2 deletions apps/www/examples/shield-ts/assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
useTable
} from "@raystack/apsara";

import { toast, ToastContainer, Avatar, AvatarGroup, Button, Spinner, DropdownMenu, Breadcrumb, Chip, Flex, Text, Checkbox, InputField, Badge, Radio } from "@raystack/apsara/v1";
import { toast, ToastContainer, Avatar, AvatarGroup, Button, Spinner, DropdownMenu, Breadcrumb, Chip, Flex, Text, Checkbox, InputField, Badge, Radio, Tabs } from "@raystack/apsara/v1";

import { getData, Payment } from "./data";
import { ApsaraColumnDef } from "@raystack/apsara/table/datatables.types";
Expand Down Expand Up @@ -207,7 +207,40 @@ const AssetsHeader = () => {
justify="between"
style={{ width: "100%", padding: "4px", paddingTop: "48px" }}
>
<Flex gap="extra-large" align="center">
<Flex gap="extra-large" align="center" style={{ width: "100%" }}>
<Tabs.Root defaultValue="general">
<Tabs.List>
<Tabs.Trigger value="general" icon={<HomeIcon />}>
Home
</Tabs.Trigger>
<Tabs.Trigger value="hosting" disabled>
Hosting
</Tabs.Trigger>
<Tabs.Trigger value="editor" icon={<InfoCircledIcon />} disabled />
<Tabs.Trigger value="billing">
Billing
</Tabs.Trigger>
<Tabs.Trigger value="seo">
SEO
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="general">
<Text>General settings content</Text>
</Tabs.Content>
<Tabs.Content value="hosting">
<Text>Hosting configuration content</Text>
</Tabs.Content>
<Tabs.Content value="editor">
<Text>Editor preferences content</Text>
</Tabs.Content>
<Tabs.Content value="billing">
<Text>Billing information content</Text>
</Tabs.Content>
<Tabs.Content value="seo">
<Text>SEO settings content</Text>
</Tabs.Content>
</Tabs.Root>

{/* <Text style={{ fontWeight: 500 }}>Assets</Text> */}
{/* <Spinner size={3} />
<div>
Expand Down
2 changes: 1 addition & 1 deletion apps/www/utils/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const primitivesRoutes = [
},
{ title: "Switch", slug: "docs/primitives/components/switch", newBadge: true },
{ title: "Slider", slug: "docs/primitives/components/slider", newBadge: true },
{ title: "Tabs", slug: "docs/primitives/components/tabs" },
{ title: "Tabs", slug: "docs/primitives/components/tabs", newBadge: true },
{ title: "Table", slug: "docs/primitives/components/table" },
{
title: "Text",
Expand Down
37 changes: 37 additions & 0 deletions packages/raystack/tabs/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ import React, { ComponentPropsWithoutRef } from "react";
import styles from "./tabs.module.css";

const root = cva(styles.root);

/**
* @deprecated Use Tabs from '@raystack/apsara/v1' instead.
*/
export interface TabsRootProps
extends ComponentPropsWithoutRef<typeof TabsPrimitive.Root>,
VariantProps<typeof root> {}


/**
* @deprecated Use TabsRoot from '@raystack/apsara/v1' instead.
*/
const TabsRoot = ({ className, ...props }: TabsRootProps) => (
<TabsPrimitive.Root className={root({ className })} {...props} />
);

TabsRoot.displayName = TabsPrimitive.Root.displayName;


const tablist = cva(styles.tablist, {
variants: {
underline: {
Expand All @@ -24,10 +33,18 @@ const tablist = cva(styles.tablist, {
},
},
});

/**
* @deprecated Use TabsListProps from '@raystack/apsara/v1' instead.
*/
export interface TabsListProps
extends ComponentPropsWithoutRef<typeof TabsPrimitive.List>,
VariantProps<typeof tablist> {}


/**
* @deprecated Use TabsList from '@raystack/apsara/v1' instead.
*/
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
TabsListProps
Expand All @@ -40,28 +57,48 @@ const TabsList = React.forwardRef<
));
TabsList.displayName = TabsPrimitive.List.displayName;


const content = cva(styles.content);

/**
* @deprecated Use ContentProps from '@raystack/apsara/v1' instead.
*/
export interface ContentProps
extends ComponentPropsWithoutRef<typeof TabsPrimitive.Content>,
VariantProps<typeof content> {}


/**
* @deprecated Use Tabs from '@raystack/apsara/v1' instead.
*/
const TabsContent = ({ className, ...props }: ContentProps) => (
<TabsPrimitive.Content className={content({ className })} {...props} />
);

TabsContent.displayName = TabsPrimitive.Content.displayName;


const trigger = cva(styles.trigger);

/**
* @deprecated Use Tabs from '@raystack/apsara/v1' instead.
*/
export interface TabsTriggerProps
extends ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>,
VariantProps<typeof trigger> {}

/**
* @deprecated Use Tabs from '@raystack/apsara/v1' instead.
*/
const TabsTrigger = ({ className, ...props }: TabsTriggerProps) => (
<TabsPrimitive.Trigger className={trigger({ className })} {...props} />
);

TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;

/**
* @deprecated Use Tabs from '@raystack/apsara/v1' instead.
*/
export const Tabs = Object.assign(TabsRoot, {
Trigger: TabsTrigger,
Content: TabsContent,
Expand Down
1 change: 1 addition & 0 deletions packages/raystack/v1/components/tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Tabs } from "./tabs";
Loading

0 comments on commit f966e50

Please sign in to comment.