Tabs component (#12)

* Add tgz files to gitignore

* Tabs element

* Remove duplicate util function
This commit is contained in:
Arnaud 2024-09-10 13:25:05 +02:00 committed by GitHub
parent bbe14a6f53
commit 66f8207be8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 130 additions and 30 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules node_modules
storybook-static storybook-static
dist dist
*.tgz

View File

@ -1,4 +1,4 @@
import { attributes } from "../../utils/attributes"; import { attributes } from "../utils/attributes";
import "./menu.css"; import "./menu.css";
import { LogoInverse } from "../Logo/LogoInverse"; import { LogoInverse } from "../Logo/LogoInverse";
import { ComponentType, useEffect } from "react"; import { ComponentType, useEffect } from "react";

View File

@ -1,4 +1,4 @@
import { classnames } from "../../utils/classnames"; import { classnames } from "../utils/classnames";
import "./networkIndicator.css"; import "./networkIndicator.css";
type Props = { type Props = {

View File

@ -0,0 +1,32 @@
import { ComponentType } from "react";
import "./tabs.css";
import { classnames } from "../utils/classnames";
type Props = {
tabs: {
label: string;
Icon?: ComponentType<{ size?: string }>;
}[];
onTabChange: (index: number) => void | Promise<void>;
tabIndex: number;
};
export function Tabs({ tabs, onTabChange, tabIndex }: Props) {
return (
<div className="tabs">
{tabs.map((tab, index) => (
<div
key={tab.label}
className={classnames(
["tabs-tab"],
["tabs-tab--active", tabIndex === index]
)}
onClick={() => onTabChange(index)}
>
{tab.Icon && <tab.Icon size={"1rem"} />}
<span>{tab.label}</span>
</div>
))}
</div>
);
}

View File

@ -0,0 +1,45 @@
.tabs {
display: flex;
margin-top: 1rem;
gap: 1rem;
position: relative;
}
.tabs-tab {
display: flex;
align-items: center;
gap: 0.25rem;
padding-bottom: 1rem;
cursor: pointer;
transition: 0.35s opacity;
z-index: 1;
}
.tabs-tab::after {
width: 100%;
background-color: var(--codex-background-light);
content: " ";
position: absolute;
height: 2px;
top: 11px;
top: 31px;
}
.tabs-tab:not(.files-headerTab--active) {
opacity: 0.7;
}
.tabs-tab:hover {
opacity: 0.85;
}
.tabs-tab--active {
border-bottom: 2px solid var(--codex-color-contrast);
}
.tabs-icon {
width: 1rem;
height: 1rem;
display: flex;
place-items: center;
}

View File

@ -32,3 +32,4 @@ export { NetworkIndicator } from "./components/NetworkIndicator/NetworkIndicator
export { Tooltip } from "./components/Tooltip/Tooltip"; export { Tooltip } from "./components/Tooltip/Tooltip";
export { Collapse } from "./components/Collapse/Collapse"; export { Collapse } from "./components/Collapse/Collapse";
export { Placeholder } from "./components/Placeholder/Placeholder"; export { Placeholder } from "./components/Placeholder/Placeholder";
export { Tabs } from "./components/Tabs/Tabs";

View File

@ -1,6 +0,0 @@
type Attributes = Record<string, unknown>;
export const attributes = (attributes: Attributes) =>
Object.keys(attributes)
.filter((key) => attributes[key] !== false)
.reduce((prev, key) => ({ ...prev, [key]: attributes[key] }), {});

View File

@ -1,14 +0,0 @@
export const PrettyBytes = (bytes: number) => {
const sizes = ["bytes", "KB", "MB", "GB", "TB"];
if (bytes == 0) {
return "0 b";
}
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString());
if (i == 0) {
return bytes + " " + sizes[i];
}
return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i];
};

View File

@ -1,7 +0,0 @@
export type Classname = [string, boolean?];
export const classnames = (...classnames: Classname[]) =>
classnames
.filter(([, visible = true]) => visible)
.map(([name]) => name)
.join(" ");

48
stories/Tabs.stories.tsx Normal file
View File

@ -0,0 +1,48 @@
import type { Meta } from "@storybook/react";
import { useState } from "react";
import { FilesIcon, Star } from "lucide-react";
import { Tabs } from "../src/components/Tabs/Tabs";
import { fn } from "@storybook/test";
const meta = {
title: "Components/Tabs",
component: Tabs,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {},
args: { onTabChange: fn() },
} satisfies Meta<typeof Tabs>;
export default meta;
const Template = (props: { onTabChange: () => void }) => {
const [tabIndex, setTabIndex] = useState(0);
const onTabChange = (index: number) => {
props.onTabChange();
setTabIndex(index);
};
return (
<div>
<Tabs
onTabChange={onTabChange}
tabIndex={tabIndex}
tabs={[
{
label: "All files",
Icon: () => <FilesIcon size={"1rem"}></FilesIcon>,
},
{
label: "Favorites",
Icon: () => <Star size={"1rem"}></Star>,
},
]}
/>
</div>
);
};
export const Default = Template.bind({});