mirror of
https://github.com/ershisan99/next-storybook.git
synced 2026-01-27 21:02:07 +00:00
initial commit
This commit is contained in:
17
src/assets/icons/chevron-down/index.tsx
Normal file
17
src/assets/icons/chevron-down/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
export const ChevronDown = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
||||
<g clipPath="url(#a)">
|
||||
<path
|
||||
fill="#BDC1C7"
|
||||
d="M5.51 9.46a1 1 0 0 1 1.64-.77l5.36 4.48 5.37-4.32A1 1 0 0 1 19.3 9a1 1 0 0 1-.15 1.46l-6 4.83a1 1 0 0 1-1.27 0l-6-5a1 1 0 0 1-.36-.83Z"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h24v24H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
28
src/components-controlled/select-controlled.tsx
Normal file
28
src/components-controlled/select-controlled.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Select, SelectProps } from "../components/select";
|
||||
import { Control, FieldValues, Path, useController } from "react-hook-form";
|
||||
|
||||
type SelectControlledProps<T extends FieldValues> = Omit<
|
||||
SelectProps,
|
||||
"onChange" | "value" | "name"
|
||||
> & {
|
||||
control: Control<T>;
|
||||
name: Path<T>;
|
||||
};
|
||||
|
||||
export const SelectControlled = <T extends FieldValues>({
|
||||
control,
|
||||
options,
|
||||
name,
|
||||
}: SelectControlledProps<T>) => {
|
||||
const {
|
||||
field: { value, onChange },
|
||||
fieldState: { error },
|
||||
} = useController({
|
||||
control,
|
||||
name,
|
||||
});
|
||||
|
||||
return (
|
||||
<Select options={options} onChange={onChange} value={value} name={name} />
|
||||
);
|
||||
};
|
||||
0
src/components/index.ts
Normal file
0
src/components/index.ts
Normal file
143
src/components/select/index.tsx
Normal file
143
src/components/select/index.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import * as React from "react";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { clsx } from "clsx";
|
||||
import { ChevronDown } from "../../assets/icons/chevron-down";
|
||||
import { FC, forwardRef } from "react";
|
||||
|
||||
const SelectRoot = SelectPrimitive.Root;
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group;
|
||||
|
||||
const SelectValue = SelectPrimitive.Value;
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
"flex h-10 w-full items-center border-dark-100 justify-between rounded-md border bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDown />
|
||||
</SelectPrimitive.Trigger>
|
||||
));
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
"animate-in fade-in-80 relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-100 bg-white text-slate-700 shadow-md dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SelectPrimitive.Viewport className="p-1">
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
));
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
"py-1.5 pr-2 pl-8 text-sm font-semibold text-slate-900 dark:text-slate-300",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm font-medium outline-none focus:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-700",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator></SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
));
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
"-mx-1 my-1 h-px bg-slate-100 dark:bg-slate-700 p-[11px]",
|
||||
className,
|
||||
props.title === "misha" && "bg-red-500"
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export type SelectProps = {
|
||||
options: { label: string; value: string }[];
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
placeholder?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Select: FC<SelectProps> = forwardRef(
|
||||
({ options, value, onChange, placeholder, name }, ref) => {
|
||||
return (
|
||||
<SelectRoot value={value} onValueChange={onChange} name={name}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{options.map((option) => {
|
||||
return (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</SelectContent>
|
||||
</SelectRoot>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export {
|
||||
SelectRoot,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
};
|
||||
86
src/components/select/select.stories.tsx
Normal file
86
src/components/select/select.stories.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Select } from "./";
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { SelectControlled } from "../../components-controlled/select-controlled";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
const meta: Meta<typeof Select> = {
|
||||
title: "Components/Select",
|
||||
component: Select,
|
||||
};
|
||||
|
||||
type Story = StoryObj<typeof Select>;
|
||||
const options = [
|
||||
{ label: "Male", value: "MALE" },
|
||||
{ label: "Female", value: "FEMALE" },
|
||||
{ label: "Cat", value: "CAT" },
|
||||
];
|
||||
export const Default: Story = {
|
||||
render: () => {
|
||||
const [value, setValue] = useState("light");
|
||||
console.log(value);
|
||||
return (
|
||||
<div className={"w-full h-64 grid place-items-center bg-dark-700"}>
|
||||
<Select
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
placeholder={"Select theme"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const formSchema = z.object({
|
||||
username: z.string().optional(),
|
||||
password: z.number(),
|
||||
email: z.string().email({}),
|
||||
gender: z.enum(["MALE", "FEMALE", "CAT"]),
|
||||
});
|
||||
|
||||
export type FormSchema = z.infer<typeof formSchema>;
|
||||
|
||||
export const Form = {
|
||||
render: () => {
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm<FormSchema>({
|
||||
mode: "onChange",
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
console.log(errors);
|
||||
return (
|
||||
<form
|
||||
className={"w-full h-64 grid place-items-center bg-dark-700"}
|
||||
onSubmit={handleSubmit((data) => {
|
||||
console.log(data);
|
||||
})}
|
||||
>
|
||||
<input {...register("username")} />
|
||||
|
||||
<input
|
||||
{...register("password", {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<input {...register("email")} />
|
||||
<SelectControlled options={options} control={control} name={"gender"} />
|
||||
|
||||
<button
|
||||
type={"submit"}
|
||||
className={"bg-light-300 text-dark-500 p-4 rounded"}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -1,7 +1,5 @@
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import { Inter } from 'next/font/google'
|
||||
import styles from '@/styles/Home.module.css'
|
||||
import {Inter} from 'next/font/google'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
@@ -10,114 +8,13 @@ export default function Home() {
|
||||
<>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<meta name="description" content="Gesnerated by create next app" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main className={styles.main}>
|
||||
<div className={styles.description}>
|
||||
<p>
|
||||
Get started by editing
|
||||
<code className={styles.code}>src/pages/index.tsx</code>
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{' '}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className={styles.vercelLogo}
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.center}>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
<div className={styles.thirteen}>
|
||||
<Image
|
||||
src="/thirteen.svg"
|
||||
alt="13"
|
||||
width={40}
|
||||
height={31}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={inter.className}>
|
||||
Docs <span>-></span>
|
||||
</h2>
|
||||
<p className={inter.className}>
|
||||
Find in-depth information about Next.js features and API.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={inter.className}>
|
||||
Learn <span>-></span>
|
||||
</h2>
|
||||
<p className={inter.className}>
|
||||
Learn about Next.js in an interactive course with quizzes!
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={inter.className}>
|
||||
Templates <span>-></span>
|
||||
</h2>
|
||||
<p className={inter.className}>
|
||||
Discover and deploy boilerplate example Next.js projects.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={inter.className}>
|
||||
Deploy <span>-></span>
|
||||
</h2>
|
||||
<p className={inter.className}>
|
||||
Instantly deploy your Next.js site to a shareable URL
|
||||
with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<main>
|
||||
<h1 className={'text-teal-600'}>Hello</h1>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,107 +1,3 @@
|
||||
:root {
|
||||
--max-width: 1100px;
|
||||
--border-radius: 12px;
|
||||
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
|
||||
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
|
||||
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
|
||||
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
|
||||
--primary-glow: conic-gradient(
|
||||
from 180deg at 50% 50%,
|
||||
#16abff33 0deg,
|
||||
#0885ff33 55deg,
|
||||
#54d6ff33 120deg,
|
||||
#0071ff33 160deg,
|
||||
transparent 360deg
|
||||
);
|
||||
--secondary-glow: radial-gradient(
|
||||
rgba(255, 255, 255, 1),
|
||||
rgba(255, 255, 255, 0)
|
||||
);
|
||||
|
||||
--tile-start-rgb: 239, 245, 249;
|
||||
--tile-end-rgb: 228, 232, 233;
|
||||
--tile-border: conic-gradient(
|
||||
#00000080,
|
||||
#00000040,
|
||||
#00000030,
|
||||
#00000020,
|
||||
#00000010,
|
||||
#00000010,
|
||||
#00000080
|
||||
);
|
||||
|
||||
--callout-rgb: 238, 240, 241;
|
||||
--callout-border-rgb: 172, 175, 176;
|
||||
--card-rgb: 180, 185, 188;
|
||||
--card-border-rgb: 131, 134, 135;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
|
||||
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
||||
--secondary-glow: linear-gradient(
|
||||
to bottom right,
|
||||
rgba(1, 65, 255, 0),
|
||||
rgba(1, 65, 255, 0),
|
||||
rgba(1, 65, 255, 0.3)
|
||||
);
|
||||
|
||||
--tile-start-rgb: 2, 13, 46;
|
||||
--tile-end-rgb: 2, 5, 19;
|
||||
--tile-border: conic-gradient(
|
||||
#ffffff80,
|
||||
#ffffff40,
|
||||
#ffffff30,
|
||||
#ffffff20,
|
||||
#ffffff10,
|
||||
#ffffff10,
|
||||
#ffffff80
|
||||
);
|
||||
|
||||
--callout-rgb: 20, 20, 20;
|
||||
--callout-border-rgb: 108, 108, 108;
|
||||
--card-rgb: 100, 100, 100;
|
||||
--card-border-rgb: 200, 200, 200;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
Reference in New Issue
Block a user