Tabs component (#12)
* Add tgz files to gitignore * Tabs element * Remove duplicate util function
This commit is contained in:
parent
bbe14a6f53
commit
66f8207be8
|
@ -1,3 +1,4 @@
|
|||
node_modules
|
||||
storybook-static
|
||||
dist
|
||||
*.tgz
|
|
@ -1,4 +1,4 @@
|
|||
import { attributes } from "../../utils/attributes";
|
||||
import { attributes } from "../utils/attributes";
|
||||
import "./menu.css";
|
||||
import { LogoInverse } from "../Logo/LogoInverse";
|
||||
import { ComponentType, useEffect } from "react";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { classnames } from "../../utils/classnames";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import "./networkIndicator.css";
|
||||
|
||||
type Props = {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -32,3 +32,4 @@ export { NetworkIndicator } from "./components/NetworkIndicator/NetworkIndicator
|
|||
export { Tooltip } from "./components/Tooltip/Tooltip";
|
||||
export { Collapse } from "./components/Collapse/Collapse";
|
||||
export { Placeholder } from "./components/Placeholder/Placeholder";
|
||||
export { Tabs } from "./components/Tabs/Tabs";
|
||||
|
|
|
@ -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] }), {});
|
|
@ -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];
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
export type Classname = [string, boolean?];
|
||||
|
||||
export const classnames = (...classnames: Classname[]) =>
|
||||
classnames
|
||||
.filter(([, visible = true]) => visible)
|
||||
.map(([name]) => name)
|
||||
.join(" ");
|
|
@ -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({});
|
Loading…
Reference in New Issue