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
|
node_modules
|
||||||
storybook-static
|
storybook-static
|
||||||
dist
|
dist
|
||||||
|
*.tgz
|
|
@ -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";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { classnames } from "../../utils/classnames";
|
import { classnames } from "../utils/classnames";
|
||||||
import "./networkIndicator.css";
|
import "./networkIndicator.css";
|
||||||
|
|
||||||
type Props = {
|
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 { 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";
|
||||||
|
|
|
@ -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