同一页面上相关面板之间切换的组件。

演示

import { SpriteIcon } from "~/components/sprite-icon";
import { Tabs, TabsList, TabsTab, TabsIndicator, TabsPanel } from "@resolid/react-ui";

export default function App() {
  return (
    <Tabs defaultValue="auto" className="w-auto rounded-md border border-bd-normal">
      <TabsList className="gap-1 border-b border-bd-normal p-1">
        <TabsIndicator className="-bottom-px h-0.5 w-(--wv) bg-bg-primary-emphasis" />
        <TabsTab value="auto" className="rounded-md px-3">
          自动
        </TabsTab>
        <TabsTab value="light" className="rounded-md px-3">
          亮色
        </TabsTab>
        <TabsTab value="dark" className="rounded-md px-3">
          暗色
        </TabsTab>
      </TabsList>
      <TabsPanel value="auto" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="auto" size="3rem" />
      </TabsPanel>
      <TabsPanel value="light" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="sun" size="3rem" />
      </TabsPanel>
      <TabsPanel value="dark" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="moon" size="3rem" />
      </TabsPanel>
    </Tabs>
  );
}

使用

import { Tabs, TabsList, TabsTab, TabsIndicator, TabsPanel } from "@resolid/react-ui";
  • Tabs: 将选项卡和相应面板分组。
  • TabsList: 分组单个选项卡按钮。
  • TabsTab: 单个交互式选项卡按钮,可切换相应的面板。
  • TabsIndicator: 突出显示当前活动选项卡的指示器。
  • TabsPanel: 当“对应”选项卡处于活动状态时显示的面板。

选项卡默认只有必需的样式,你可以通过 TabsTabsListTabsTabTabsPanelclassName 自定义。

<Tabs>
  <TabsList>
    <TabsTab />
  </TabsList>
  <TabsPanel />
</Tabs>

特点

  • 遵循 WAI ARIA Tabs 设计模式。
  • 完整的键盘导航。
  • 支持水平/垂直方向。
  • 可以是受控的,也可以是不受控的。

举例

垂直

import { SpriteIcon } from "~/components/sprite-icon";
import { Tabs, TabsList, TabsIndicator, TabsTab, TabsPanel } from "@resolid/react-ui";

export default function App() {
  return (
    <Tabs
      className="w-auto rounded-md border border-bd-normal"
      orientation="vertical"
      defaultValue="light"
    >
      <TabsList className="border-e border-bd-normal p-1">
        <TabsIndicator className="-z-1 h-(--hv) w-(--wv) rounded-md bg-bg-subtle" />
        <TabsTab value="auto" className="rounded-md px-3">
          自动
        </TabsTab>
        <TabsTab value="light" className="rounded-md px-3">
          亮色
        </TabsTab>
        <TabsTab value="dark" className="rounded-md px-3">
          暗色
        </TabsTab>
      </TabsList>
      <TabsPanel value="auto" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="auto" size="3rem" />
      </TabsPanel>
      <TabsPanel value="light" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="sun" size="3rem" />
      </TabsPanel>
      <TabsPanel value="dark" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="moon" size="3rem" />
      </TabsPanel>
    </Tabs>
  );
}

禁用

import { SpriteIcon } from "~/components/sprite-icon";
import { Tabs, TabsList, TabsTab, TabsIndicator, TabsPanel } from "@resolid/react-ui";

export default function App() {
  return (
    <Tabs className="w-auto rounded-md border border-bd-normal" defaultValue="auto">
      <TabsList className="border-b border-bd-normal p-1">
        <TabsIndicator className="-bottom-px h-0.5 w-(--wv) bg-bg-primary-emphasis" />
        <TabsTab value="auto" className="rounded-md px-3">
          自动
        </TabsTab>
        <TabsTab value="light" disabled className="rounded-md px-3">
          亮色
        </TabsTab>
        <TabsTab value="dark" className="rounded-md px-3">
          暗色
        </TabsTab>
      </TabsList>
      <TabsPanel value="auto" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="auto" size="3rem" />
      </TabsPanel>
      <TabsPanel value="light" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="sun" size="3rem" />
      </TabsPanel>
      <TabsPanel value="dark" className="flex items-center justify-center p-16 text-bg-muted">
        <SpriteIcon name="moon" size="3rem" />
      </TabsPanel>
    </Tabs>
  );
}

动态

这是如何动态添加和删除选项卡的示例。

选项卡-1
import { useState } from "react";
import {
  Tabs,
  TabsList,
  TabsTab,
  TabsIndicator,
  TabsPanel,
  Button,
  CloseButton,
} from "@resolid/react-ui";

const items = [
  { id: "1", title: "选项卡", content: "选项卡-1" },
  { id: "2", title: "选项卡", content: "选项卡-2" },
  { id: "3", title: "选项卡", content: "选项卡-3" },
];

export default function App() {
  const [tabs, setTabs] = useState(items);
  const [selected, setSelected] = useState(items[0].id);

  const addTab = () => {
    const uid = Math.random().toString(36).substring(2, 15);

    setTabs([
      ...tabs,
      {
        id: uid,
        title: `选项卡`,
        content: `选项卡-${uid}`,
      },
    ]);

    setSelected(uid);
  };

  const removeTab = (id) => {
    if (tabs.length > 1) {
      const tabIndex = tabs.findIndex((tab) => tab.id === id);
      const newTabs = [...tabs.slice(0, tabIndex), ...tabs.slice(tabIndex + 1)];

      setTabs(newTabs);

      if (id === selected) {
        setSelected(newTabs[Math.max(tabIndex - 1, 0)].id);
      }
    }
  };

  return (
    <Tabs
      value={selected}
      onChange={setSelected}
      className="w-auto min-w-90 rounded-md border border-bd-normal"
    >
      <TabsList className="border-b border-bd-normal p-1">
        <TabsIndicator className="-bottom-px h-0.5 w-(--wv) bg-bg-primary-emphasis" />
        {tabs.map((tab) => {
          return (
            <TabsTab
              render={(props) => <div {...props} />}
              key={tab.id}
              value={tab.id}
              className="inline-flex items-center rounded-md ps-3 pe-1 select-none"
            >
              {tab.title}
              <CloseButton
                size="1em"
                className="ms-2"
                onClick={(e) => {
                  e.stopPropagation();
                  removeTab(tab.id);
                }}
              />
            </TabsTab>
          );
        })}
        <Button
          variant="ghost"
          color="neutral"
          className="ms-1.5 px-1.5 py-0.5"
          noPadding
          onClick={addTab}
        >
          增加
        </Button>
      </TabsList>
      {tabs.map((tab) => {
        return (
          <TabsPanel key={tab.id} value={tab.id} className="p-4">
            {tab.content}
          </TabsPanel>
        );
      })}
    </Tabs>
  );
}

属性

Tabs

属性
value

受控值

类型string默认值必须false
属性
defaultValue

默认值

类型string默认值必须false
属性
onChange

onChange 回调

类型(value: string) => void默认值必须false
属性
orientation

方向

类型"horizontal" | "vertical"默认值"horizontal"必须false

TabsTab

属性
value

将内容与触发器关联起来的唯一值

类型string默认值必须true
属性
disabled

是否禁用

类型boolean默认值false必须false

TabsPanel

属性
value

将内容与触发器关联起来的唯一值

类型string默认值必须true

建议更改此页面