diff --git a/src/components/button/Button.styles.ts b/src/components/button/Button.styles.ts new file mode 100644 index 0000000..1110ef6 --- /dev/null +++ b/src/components/button/Button.styles.ts @@ -0,0 +1,29 @@ +import styled from "styled-components"; +import { ButtonProps } from "./Button"; + +export const StyledButton = styled.button` + line-height: 1; + cursor: pointer; + font-weight: 700; + font-size: ${(props) => + props.size === "small" + ? "16px" + : props.size === "medium" + ? "16px" + : "20px"}; + border-radius: 10px; + display: inline-block; + color: ${(props) => (props.primary ? "#fff" : "#000")}; + ${(props) => + props.primary && `background: ${props.bg ? props.bg : "#FF5655"}`}; + ${(props) => props.outline && `background: transparent`}; + border: ${(props) => (props.outline ? "2px solid" : "none")}; + ${(props) => + props.outline && `border-color: ${props.bg ? props.bg : "#000"}`}; + padding: ${(props) => + props.size === "small" + ? "10px 18px" + : props.size === "medium" + ? "14px 20px" + : "14px 24px"}; +`; diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index d6a45b1..29284be 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -1,5 +1,5 @@ import React, { MouseEventHandler } from "react"; -import styled from "styled-components"; +import { StyledButton } from "./Button.styles"; export type ButtonProps = { text?: string; @@ -11,33 +11,6 @@ export type ButtonProps = { bg?: string; }; -const StyledButton = styled.button` - line-height: 1; - cursor: pointer; - font-weight: 700; - font-size: ${(props) => - props.size === "small" - ? "16px" - : props.size === "medium" - ? "16px" - : "20px"}; - border-radius: 10px; - display: inline-block; - color: ${(props) => (props.primary ? "#fff" : "#000")}; - ${(props) => - props.primary && `background: ${props.bg ? props.bg : "#FF5655"}`}; - ${(props) => props.outline && `background: transparent`}; - border: ${(props) => (props.outline ? "2px solid" : "none")}; - ${(props) => - props.outline && `border-color: ${props.bg ? props.bg : "#000"}`}; - padding: ${(props) => - props.size === "small" - ? "10px 18px" - : props.size === "medium" - ? "14px 20px" - : "14px 24px"}; -`; - const Button: React.FC = ({ size, primary, diff --git a/src/components/index.ts b/src/components/index.ts index 9e40e28..47b8ed9 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,3 +1,4 @@ export * from "./button"; export * from "./input"; export * from "./navbar"; +export * from "./sidebar"; diff --git a/src/components/input/Input.styles.ts b/src/components/input/Input.styles.ts new file mode 100644 index 0000000..e02580f --- /dev/null +++ b/src/components/input/Input.styles.ts @@ -0,0 +1,100 @@ +import styled from "styled-components"; +import { InputProps } from "./Input"; + +/* + Styled Input for the primary variant + */ +export const StyledInput = styled.input` + padding: ${(props) => (props.p ? props.p : "10px 14px")}; + width: ${(props) => props.width || "100%"}; + max-width: ${(props) => props.maxWidth}; + outline: none; + border: ${(props) => (props.border ? props.border.width : "1px")} solid + ${(props) => (props.border ? props.border.color : "#eee")}; + border-radius: ${(props) => (props.border ? props.border.radius : "4px")}; + font-size: ${(props) => + props.fontsProperty ? props.fontsProperty.size : "16px"}; + font-family: ${(props) => + props.fontsProperty ? props.fontsProperty.family : "system-ui"}; + font-weight: ${(props) => + props.fontsProperty ? props.fontsProperty.weight : "500"}; +`; + +/* + Styled Container for the float variant + */ +export const StyledFloatContainer = styled.div` + position: relative; + width: ${(props) => (props.width ? props.width : "100%")}; + max-width: ${(props) => props.maxWidth || "100%"}; + margin-bottom: 1.5rem; + font-family: ${(props) => + props.fontsProperty ? props.fontsProperty.family : "system-ui"}; +`; + +/* + Styled Input for the float variant + */ +export const StyledFloatInput = styled.input` + padding: ${(props) => (props.p ? props.p : "10px 14px")}; + width: ${(props) => + props.p ? `calc(100% - ${2 * props.p}px)` : "calc(100% - 28px)"}; + outline: none; + border: ${(props) => (props.border ? props.border.width : "1px")} solid + ${(props) => (props.border ? props.border.color : "#eee")}; + border-radius: ${(props) => (props.border ? props.border.radius : "4px")}; + font-size: ${(props) => + props.fontsProperty ? props.fontsProperty.size : "16px"}; + font-family: ${(props) => + props.fontsProperty ? props.fontsProperty.family : "system-ui"}; + font-weight: ${(props) => + props.fontsProperty ? props.fontsProperty.weight : "500"}; + transition: border-color 0.3s; + &:focus { + border-color: #007bff; + } + &: + focus + label, + ${({ hasValue }) => + hasValue && + ` + & + label { + top: -0.75rem; + font-size: 0.75rem; + color: #007bff; + } + `}; +`; + +export const StyledFloatLabel = styled.label` + position: absolute; + padding: ${(props) => (props.p ? props.p : "10px 14px")}; + transform: translateY(-50%); + transition: all 0.3s; + pointer-events: none; + font-size: ${(props) => + props.fontsProperty ? props.fontsProperty.size : "16px"}; + font-family: ${(props) => + props.fontsProperty ? props.fontsProperty.family : "system-ui"}; + font-weight: ${(props) => + props.fontsProperty ? props.fontsProperty.weight : "500"}; + ${StyledFloatInput}:focus + & { + top: -0.75rem; + left: -14px; + font-size: 0.75rem; + color: #007bff; + } + ${({ hasValue }) => + hasValue + ? ` + top: -0.75rem; + left: -14px; + font-size: 0.75rem; + color: #007bff; + ` + : ` + top: 50%; + left: 0; + color: #999; + `}; +`; diff --git a/src/components/input/Input.tsx b/src/components/input/Input.tsx index 8274095..eafbe1e 100644 --- a/src/components/input/Input.tsx +++ b/src/components/input/Input.tsx @@ -1,5 +1,10 @@ import React from "react"; -import styled from "styled-components"; +import { + StyledFloatContainer, + StyledFloatInput, + StyledFloatLabel, + StyledInput, +} from "./Input.styles"; /** * Input Component @@ -74,101 +79,3 @@ const Input: React.FC = ({ }; export default Input; - -/* - Styled Input for the primary variant - */ -const StyledInput = styled.input` - padding: ${(props) => (props.p ? props.p : "10px 14px")}; - width: ${(props) => props.width || "100%"}; - max-width: ${(props) => props.maxWidth}; - outline: none; - border: ${(props) => (props.border ? props.border.width : "1px")} solid - ${(props) => (props.border ? props.border.color : "#eee")}; - border-radius: ${(props) => (props.border ? props.border.radius : "4px")}; - font-size: ${(props) => - props.fontsProperty ? props.fontsProperty.size : "16px"}; - font-family: ${(props) => - props.fontsProperty ? props.fontsProperty.family : "system-ui"}; - font-weight: ${(props) => - props.fontsProperty ? props.fontsProperty.weight : "500"}; -`; - -/* - Styled Container for the float variant - */ -const StyledFloatContainer = styled.div` - position: relative; - width: ${(props) => (props.width ? props.width : "100%")}; - max-width: ${(props) => props.maxWidth || "100%"}; - margin-bottom: 1.5rem; - font-family: ${(props) => - props.fontsProperty ? props.fontsProperty.family : "system-ui"}; -`; - -/* - Styled Input for the float variant - */ -const StyledFloatInput = styled.input` - padding: ${(props) => (props.p ? props.p : "10px 14px")}; - width: ${(props) => - props.p ? `calc(100% - ${2 * props.p}px)` : "calc(100% - 28px)"}; - outline: none; - border: ${(props) => (props.border ? props.border.width : "1px")} solid - ${(props) => (props.border ? props.border.color : "#eee")}; - border-radius: ${(props) => (props.border ? props.border.radius : "4px")}; - font-size: ${(props) => - props.fontsProperty ? props.fontsProperty.size : "16px"}; - font-family: ${(props) => - props.fontsProperty ? props.fontsProperty.family : "system-ui"}; - font-weight: ${(props) => - props.fontsProperty ? props.fontsProperty.weight : "500"}; - transition: border-color 0.3s; - &:focus { - border-color: #007bff; - } - &: - focus + label, - ${({ hasValue }) => - hasValue && - ` - & + label { - top: -0.75rem; - font-size: 0.75rem; - color: #007bff; - } - `}; -`; - -const StyledFloatLabel = styled.label` - position: absolute; - padding: ${(props) => (props.p ? props.p : "10px 14px")}; - transform: translateY(-50%); - transition: all 0.3s; - pointer-events: none; - font-size: ${(props) => - props.fontsProperty ? props.fontsProperty.size : "16px"}; - font-family: ${(props) => - props.fontsProperty ? props.fontsProperty.family : "system-ui"}; - font-weight: ${(props) => - props.fontsProperty ? props.fontsProperty.weight : "500"}; - ${StyledFloatInput}:focus + & { - top: -0.75rem; - left: -14px; - font-size: 0.75rem; - color: #007bff; - } - ${({ hasValue }) => - hasValue - ? ` - top: -0.75rem; - left: -14px; - font-size: 0.75rem; - color: #007bff; - ` - : ` - top: 50%; - left: 0; - color: #999; - `}; -`; diff --git a/src/components/navbar/Navbar.styles.ts b/src/components/navbar/Navbar.styles.ts new file mode 100644 index 0000000..3d2371f --- /dev/null +++ b/src/components/navbar/Navbar.styles.ts @@ -0,0 +1,95 @@ +import styled from "styled-components"; +import { NavbarProps } from "./Navbar"; + +export const StyledNavbar = styled.nav` + z-index: 999; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: ${(props) => + props.p ? `${props.p}px` : `${props.py || 10}px ${props.px || 30}px`}; + width: calc( + 100% - + ${(props) => + props.p + ? `${2 * props.p}px` + : props.px + ? `${2 * props.px}px` + : "60px"} + ); + .nav-logo__container, + .nav-links__container { + display: flex; + align-items: center; + gap: 10px; + } + .hamburger-open .line1 { + transform: rotate(45deg); + } + .hamburger-open .line2 { + transform: scaleY(0); + } + .hamburger-open .line3 { + transform: rotate(-45deg); + } + .nav-phone__toggle { + position: absolute; + height: 100%; + width: 100%; + top: calc( + ${(props) => + props.fontsProperty && props.fontsProperty.size + ? props.fontsProperty.size + : "30px" + + ` + ${props.p ? 2 * props.p : props.py ? 2 * props.py : 20}px` + + " - 2px"} + ); + left: 0; + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + justify-content: center; + z-index: 990; + } + img { + height: 30px; + } + a { + text-decoration: none; + color: ${(props) => props.color || "#000"}; + font-size: ${(props) => props.fontsProperty?.size || "16px"}; + font-family: ${(props) => props.fontsProperty?.family || "system-ui"}; + font-weight: ${(props) => `${props.fontsProperty?.weight || 500}`}; + } +`; + +export const StyledMenu = styled.div` + display: block; + height: 20px; + width: 25px; + z-index: 2; + display: flex; + flex-direction: column; + justify-content: space-between; + cursor: pointer; + .line { + display: block; + height: 3px; + width: 100%; + border-radius: 10px; + background: #0e2431; + } + .line1 { + transform-origin: 0% 0%; + transition: transform 0.3s ease-in-out; + } + .line2 { + transition: transform 0.1s ease-in-out; + } + .line3 { + transform-origin: 0% 100%; + transition: transform 0.3s ease-in-out; + } +`; diff --git a/src/components/navbar/Navbar.tsx b/src/components/navbar/Navbar.tsx index 8a4bd5f..c77fd91 100644 --- a/src/components/navbar/Navbar.tsx +++ b/src/components/navbar/Navbar.tsx @@ -1,6 +1,6 @@ import React from "react"; -import styled from "styled-components"; import { disableScrolling, enableScrolling } from "../../utils/ScrollHandler"; +import { StyledMenu, StyledNavbar } from "./Navbar.styles"; export type NavbarProps = { p?: number; @@ -118,96 +118,3 @@ const Navbar: React.FC = ({ }; export default Navbar; - -const StyledNavbar = styled.nav` - z-index: 999; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: ${(props) => - props.p ? `${props.p}px` : `${props.py || 10}px ${props.px || 30}px`}; - width: calc( - 100% - - ${(props) => - props.p - ? `${2 * props.p}px` - : props.px - ? `${2 * props.px}px` - : "60px"} - ); - .nav-logo__container, - .nav-links__container { - display: flex; - align-items: center; - gap: 10px; - } - .hamburger-open .line1 { - transform: rotate(45deg); - } - .hamburger-open .line2 { - transform: scaleY(0); - } - .hamburger-open .line3 { - transform: rotate(-45deg); - } - .nav-phone__toggle { - position: absolute; - height: 100%; - width: 100%; - top: calc( - ${(props) => - props.fontsProperty && props.fontsProperty.size - ? props.fontsProperty.size - : "30px" + - ` + ${props.p ? 2 * props.p : props.py ? 2 * props.py : 20}px` + - " - 2px"} - ); - left: 0; - display: flex; - flex-direction: column; - gap: 10px; - align-items: center; - justify-content: center; - z-index: 990; - } - img { - height: 30px; - } - a { - text-decoration: none; - color: ${(props) => props.color || "#000"}; - font-size: ${(props) => props.fontsProperty?.size || "16px"}; - font-family: ${(props) => props.fontsProperty?.family || "system-ui"}; - font-weight: ${(props) => `${props.fontsProperty?.weight || 500}`}; - } -`; - -const StyledMenu = styled.div` - display: block; - height: 20px; - width: 25px; - z-index: 2; - display: flex; - flex-direction: column; - justify-content: space-between; - cursor: pointer; - .line { - display: block; - height: 3px; - width: 100%; - border-radius: 10px; - background: #0e2431; - } - .line1 { - transform-origin: 0% 0%; - transition: transform 0.3s ease-in-out; - } - .line2 { - transition: transform 0.1s ease-in-out; - } - .line3 { - transform-origin: 0% 100%; - transition: transform 0.3s ease-in-out; - } -`; diff --git a/src/components/sidebar/Sidebar.styles.ts b/src/components/sidebar/Sidebar.styles.ts new file mode 100644 index 0000000..056be23 --- /dev/null +++ b/src/components/sidebar/Sidebar.styles.ts @@ -0,0 +1,51 @@ +import { SidebarProps } from "./Sidebar"; +import styled from "styled-components"; + +export const StyledSidebar = styled.div` + position: fixed; + height: 100vh; + min-width: 120px; + left: 0; + top: 0; + background: ${(props) => props.bg || "#eee"}; + padding: ${(props) => props.p || "30px 10px"}; + font-family: system-ui; +`; + +export const StyledLogo = styled.a` + font-weight: 600; + display: flex; + align-items: center; + font-size: 24px; + text-decoration: none; + color: ${(props) => props.color || "#000"}; +`; + +export const StyledList = styled.ul` + list-style: none; + margin-left: 0px; + padding-left: 0px; +`; + +export const StyledLinkList = styled.li` + width: calc(100% - 8px); + transition: 0.3s all; + padding: 4px; + margin-top: 2px; + &:hover { + background: ${(props) => props.linkHighlight || "royalblue"}; + } +`; + +export const StyledHeading = styled.li` + text-transform: capitalize; + font-weight: 600; + margin-top: 10px; +`; + +export const StyledAnchor = styled.a` + text-decoration: none; + width: 100%; + height: 100%; + color: ${(props) => props.color || "#000"}; +`; diff --git a/src/components/sidebar/Sidebar.tsx b/src/components/sidebar/Sidebar.tsx new file mode 100644 index 0000000..0076eaa --- /dev/null +++ b/src/components/sidebar/Sidebar.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { + StyledAnchor, + StyledHeading, + StyledLinkList, + StyledList, + StyledLogo, + StyledSidebar, +} from "./Sidebar.styles"; + +export type SidebarProps = { + p?: string; + bg?: string; + color?: string; + linkHighlight?: string; + title?: string; + links?: { + category: string; + href: string; + name: string; + }[][]; +}; + +const Sidebar: React.FC = ({ ...props }: SidebarProps) => { + const tempLinks = [ + [ + { + category: "menu", + href: "/", + name: "Dashboard", + }, + { + category: "menu", + href: "/", + name: "Menu", + }, + ], + [ + { + category: "settings", + href: "/", + name: "settings", + }, + { + category: "settings", + href: "/", + name: "profile", + }, + ], + ]; + + return ( + + ); +}; + +export default Sidebar; diff --git a/src/components/sidebar/__docs__/Example.tsx b/src/components/sidebar/__docs__/Example.tsx new file mode 100644 index 0000000..014f23d --- /dev/null +++ b/src/components/sidebar/__docs__/Example.tsx @@ -0,0 +1,19 @@ +import React, { FC } from "react"; +import Sidebar, { SidebarProps } from "../Sidebar"; + +const Example: FC = () => { + return ( +
+ +
+ ); +}; + +export default Example; diff --git a/src/components/sidebar/__docs__/Sidebar.mdx b/src/components/sidebar/__docs__/Sidebar.mdx new file mode 100644 index 0000000..dccfa24 --- /dev/null +++ b/src/components/sidebar/__docs__/Sidebar.mdx @@ -0,0 +1,52 @@ +import { Canvas, Meta } from "@storybook/blocks"; +import Example from "./Example.tsx"; +import * as Sidebar from "./Sidebar.stories.tsx"; + + + +# Sidebar + +Sidebar component with different props. + +#### Example + + + +## Usage + +```ts +import {Sidebar} from "elementique"; + +const Example = () => { + return ( + + ); +}; + +export default Example; +``` + +#### Arguments + +- **type** - +- **logo** - +- **title** - +- **links** - +- **fontsProperty** - +- **className** - +- **bgColor** - \ No newline at end of file diff --git a/src/components/sidebar/__docs__/Sidebar.stories.tsx b/src/components/sidebar/__docs__/Sidebar.stories.tsx new file mode 100644 index 0000000..416d2d4 --- /dev/null +++ b/src/components/sidebar/__docs__/Sidebar.stories.tsx @@ -0,0 +1,14 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import Example from "./Example"; + +const meta: Meta = { + title: "Sidebar", + component: Example, +}; + +export default meta; +type Story = StoryObj; + +export const Standard: Story = { + args: {}, +}; diff --git a/src/components/sidebar/__test__/Sidebar.test.tsx b/src/components/sidebar/__test__/Sidebar.test.tsx new file mode 100644 index 0000000..4024baf --- /dev/null +++ b/src/components/sidebar/__test__/Sidebar.test.tsx @@ -0,0 +1,14 @@ +import "@testing-library/jest-dom"; + +import React from "react"; +import { describe, expect, it } from "vitest"; +import { render, screen } from "@testing-library/react"; +import Sidebar from "../Sidebar"; + +describe("Navbar component", () => { + it("should render Navbar component correctly", () => { + render(); + const sidebar = screen.getByRole("navigation"); + expect(sidebar).toBeInTheDocument(); + }); +}); diff --git a/src/components/sidebar/index.tsx b/src/components/sidebar/index.tsx new file mode 100644 index 0000000..c3c6406 --- /dev/null +++ b/src/components/sidebar/index.tsx @@ -0,0 +1 @@ +export { default as Sidebar } from "./Sidebar";