diff --git a/frontend/app/element/tabs.scss b/frontend/app/element/tabs.scss new file mode 100644 index 000000000..342e2fe1c --- /dev/null +++ b/frontend/app/element/tabs.scss @@ -0,0 +1,39 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +.tabs-container { + display: flex; + flex-direction: column; + font-family: Arial, sans-serif; + font-size: 12px; + + .tabs-list { + display: flex; + gap: 16px; + border: 1px solid rgb(from var(--main-text-color) r g b / 15%); + border-radius: 7px; + padding: 4px; + background: rgb(from var(--block-bg-color) r g b / 70%); + + .tab-item { + display: flex; + height: 24px; + padding: 0px 8px; + justify-content: center; + align-items: center; + cursor: pointer; + border-radius: 4px; + color: var(--main-text-color); + border: 1px solid transparent; + + &:hover { + background-color: rgb(from var(--main-bg-color) r g b / 60%); + } + + &.active { + border: 1px solid var(--success-color); + background: rgb(from var(--success-color) r g b / 15%); + } + } + } +} diff --git a/frontend/app/element/tabs.stories.tsx b/frontend/app/element/tabs.stories.tsx new file mode 100644 index 000000000..c8a299525 --- /dev/null +++ b/frontend/app/element/tabs.stories.tsx @@ -0,0 +1,47 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import type { Meta, StoryObj } from "@storybook/react"; +import { Tabs } from "./tabs"; + +const meta: Meta = { + title: "Elements/Tabs", + component: Tabs, + argTypes: {}, +}; + +export default meta; + +type Story = StoryObj; + +export const DefaultTabs: Story = { + render: () => { + const tabs = [ + { label: "Node 1", onClick: () => console.log("Node 1 Clicked") }, + { label: "Node 2", onClick: () => console.log("Node 2 Clicked") }, + { label: "Node 3", onClick: () => console.log("Node 3 Clicked") }, + ]; + + return ( +
+ +
+ ); + }, +}; + +export const TabsWithAlerts: Story = { + render: () => { + const tabs = [ + { label: "Node 1", onClick: () => alert("Node 1 Clicked") }, + { label: "Node 2", onClick: () => alert("Node 2 Clicked") }, + { label: "Node 3", onClick: () => alert("Node 3 Clicked") }, + ]; + + return ( +
+ +
+ ); + }, +}; diff --git a/frontend/app/element/tabs.tsx b/frontend/app/element/tabs.tsx new file mode 100644 index 000000000..607b88a3d --- /dev/null +++ b/frontend/app/element/tabs.tsx @@ -0,0 +1,55 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import React, { useState } from "react"; +import "./tabs.scss"; + +type Tab = { + label: string; + onClick: () => void; +}; + +type TabsProps = { + tabs: Tab[]; +}; + +const Tabs: React.FC = ({ tabs }) => { + const [activeIndex, setActiveIndex] = useState(0); + + const handleKeyDown = (event: React.KeyboardEvent, index: number) => { + if (event.key === "ArrowRight") { + setActiveIndex((prevIndex) => (prevIndex + 1) % tabs.length); + } else if (event.key === "ArrowLeft") { + setActiveIndex((prevIndex) => (prevIndex - 1 + tabs.length) % tabs.length); + } else if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + tabs[index].onClick(); + setActiveIndex(index); + } + }; + + return ( +
+
+ {tabs.map((tab, index) => ( +
{ + tab.onClick(); + setActiveIndex(index); + }} + onKeyDown={(e) => handleKeyDown(e, index)} + > + {tab.label} +
+ ))} +
+
+ ); +}; + +export { Tabs };