mirror of
https://github.com/OHTU-German-learning-website/OHTU-German-learning-website.git
synced 2026-10-12 19:13:51 +03:00
Generated
+10
-1
@@ -16,7 +16,8 @@
|
||||
"pg": "^8.13.1",
|
||||
"qs": "^6.14.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
@@ -9034,6 +9035,14 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.24.2",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
|
||||
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"pg": "^8.13.1",
|
||||
"qs": "^6.14.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
|
||||
+33
-33
@@ -10,12 +10,12 @@
|
||||
|
||||
/* foreground colors (text, icons, etc.) */
|
||||
--fg1: hsl(240, 40%, 5%);
|
||||
--fg2: hsl(240, 40%, 9%);
|
||||
--fg3: hsl(240, 40%, 13%);
|
||||
--fg4: hsl(240, 40%, 17%);
|
||||
--fg5: hsl(240, 40%, 21%);
|
||||
--fg6: hsl(240, 40%, 25%);
|
||||
--fg7: hsl(240, 40%, 29%);
|
||||
--fg2: hsl(240, 40%, 11%);
|
||||
--fg3: hsl(240, 40%, 17%);
|
||||
--fg4: hsl(240, 40%, 23%);
|
||||
--fg5: hsl(240, 40%, 29%);
|
||||
--fg6: hsl(240, 40%, 35%);
|
||||
--fg7: hsl(240, 40%, 41%);
|
||||
|
||||
--tertiary1: hsl(351, 90%, 90%);
|
||||
--tertiary2: hsl(351, 90%, 80%);
|
||||
@@ -68,9 +68,9 @@
|
||||
--radius-xs: 2px;
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 16px;
|
||||
--radius-xl: 24px;
|
||||
--radius-2xl: 32px;
|
||||
--radius-lg: 10px;
|
||||
--radius-xl: 16px;
|
||||
--radius-2xl: 24px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -85,32 +85,32 @@
|
||||
--bg7: hsl(240, 40%, 29%);
|
||||
|
||||
--fg1: hsl(240, 40%, 98%);
|
||||
--fg2: hsl(240, 40%, 95%);
|
||||
--fg3: hsl(240, 40%, 92%);
|
||||
--fg4: hsl(240, 40%, 89%);
|
||||
--fg5: hsl(240, 40%, 86%);
|
||||
--fg6: hsl(240, 40%, 83%);
|
||||
--fg7: hsl(240, 40%, 80%);
|
||||
--fg2: hsl(240, 40%, 92%);
|
||||
--fg3: hsl(240, 40%, 86%);
|
||||
--fg4: hsl(240, 40%, 80%);
|
||||
--fg5: hsl(240, 40%, 74%);
|
||||
--fg6: hsl(240, 40%, 68%);
|
||||
--fg7: hsl(240, 40%, 62%);
|
||||
|
||||
--tertiary1: hsl(351, 90%, 30%);
|
||||
--tertiary2: hsl(351, 90%, 40%);
|
||||
--tertiary3: hsl(351, 90%, 50%);
|
||||
--tertiary4: hsl(351, 90%, 60%);
|
||||
--tertiary5: hsl(351, 90%, 70%);
|
||||
--tertiary6: hsl(351, 90%, 80%);
|
||||
--tertiary7: hsl(351, 90%, 85%);
|
||||
--tertiary8: hsl(351, 90%, 90%);
|
||||
--tertiary9: hsl(351, 90%, 95%);
|
||||
--tertiary1: hsl(351, 40%, 30%);
|
||||
--tertiary2: hsl(351, 40%, 40%);
|
||||
--tertiary3: hsl(351, 40%, 50%);
|
||||
--tertiary4: hsl(351, 40%, 60%);
|
||||
--tertiary5: hsl(351, 40%, 70%);
|
||||
--tertiary6: hsl(351, 40%, 80%);
|
||||
--tertiary7: hsl(351, 40%, 85%);
|
||||
--tertiary8: hsl(351, 40%, 90%);
|
||||
--tertiary9: hsl(351, 40%, 95%);
|
||||
|
||||
--secondary1: hsl(249, 90%, 30%);
|
||||
--secondary2: hsl(249, 90%, 40%);
|
||||
--secondary3: hsl(249, 90%, 50%);
|
||||
--secondary4: hsl(249, 90%, 60%);
|
||||
--secondary5: hsl(249, 90%, 70%);
|
||||
--secondary6: hsl(249, 90%, 80%);
|
||||
--secondary7: hsl(249, 90%, 85%);
|
||||
--secondary8: hsl(249, 90%, 90%);
|
||||
--secondary9: hsl(249, 90%, 95%);
|
||||
--secondary1: hsl(249, 60%, 30%);
|
||||
--secondary2: hsl(249, 60%, 40%);
|
||||
--secondary3: hsl(249, 60%, 50%);
|
||||
--secondary4: hsl(249, 60%, 60%);
|
||||
--secondary5: hsl(249, 60%, 70%);
|
||||
--secondary6: hsl(249, 60%, 80%);
|
||||
--secondary7: hsl(249, 60%, 85%);
|
||||
--secondary8: hsl(249, 60%, 90%);
|
||||
--secondary9: hsl(249, 60%, 95%);
|
||||
|
||||
--primary1: hsl(161, 35%, 30%);
|
||||
--primary2: hsl(161, 35%, 40%);
|
||||
|
||||
@@ -3,6 +3,8 @@ import Image from "next/image";
|
||||
import styles from "./page.module.css";
|
||||
import useQuery from "@/shared/hooks/useQuery";
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button/button";
|
||||
import { Dropdown } from "@/components/ui/dropdown/dropdown";
|
||||
export default function Home() {
|
||||
const [mode, setMode] = useState("normal");
|
||||
|
||||
@@ -11,6 +13,41 @@ export default function Home() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<main className={styles.main}>
|
||||
<Button>Default</Button>
|
||||
<Button variant="primary" size="sm">
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="secondary" size="md" width="fit">
|
||||
Secondary
|
||||
</Button>
|
||||
<Button variant="tertiary" size="lg" width="full">
|
||||
Tertiary
|
||||
</Button>
|
||||
<Button variant="none" size="xl">
|
||||
None
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" width="fit">
|
||||
Outline
|
||||
</Button>
|
||||
<Dropdown
|
||||
options={[
|
||||
{
|
||||
label: "Option 1",
|
||||
value: "option1",
|
||||
},
|
||||
{
|
||||
label: "Option 2",
|
||||
value: "option2",
|
||||
},
|
||||
{
|
||||
label: "Option 3",
|
||||
value: "option3",
|
||||
disabled: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Button>Dropdown</Button>
|
||||
</Dropdown>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"use client";
|
||||
import styles from "./button.module.css";
|
||||
|
||||
export const Button = ({
|
||||
children,
|
||||
variant = "primary",
|
||||
size = "md",
|
||||
width,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={[
|
||||
styles.baseButton,
|
||||
styles[variant],
|
||||
styles[size],
|
||||
styles[width],
|
||||
className,
|
||||
].join(" ")}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
.baseButton {
|
||||
background-color: var(--bg1);
|
||||
color: var(--fg1);
|
||||
border: 1px solid var(--fg1);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.1s ease-in-out,
|
||||
border-color 0.1s ease-in-out,
|
||||
transform 0.1s ease-in-out,
|
||||
opacity 0.1s ease-in-out;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:active {
|
||||
transform: scale(0.99);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.baseButton.sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
width: 8rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--font-sm);
|
||||
}
|
||||
|
||||
.baseButton.md {
|
||||
padding: 0.5rem 1rem;
|
||||
width: 12rem;
|
||||
font-size: var(--font-md);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.baseButton.lg {
|
||||
padding: 0.75rem 1.5rem;
|
||||
width: 16rem;
|
||||
font-size: var(--font-lg);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.baseButton.xl {
|
||||
padding: 1rem 2rem;
|
||||
width: 20rem;
|
||||
font-size: var(--font-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.baseButton.full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.baseButton.fit {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.baseButton.primary {
|
||||
background-color: var(--primary1);
|
||||
color: var(--fg1);
|
||||
border: 1px solid var(--primary5);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary2);
|
||||
border: 1px solid var(--primary6);
|
||||
}
|
||||
}
|
||||
|
||||
.baseButton.secondary {
|
||||
background-color: var(--secondary1);
|
||||
color: var(--fg1);
|
||||
border: 1px solid var(--secondary5);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--secondary2);
|
||||
border: 1px solid var(--secondary6);
|
||||
}
|
||||
}
|
||||
|
||||
.baseButton.tertiary {
|
||||
background-color: var(--tertiary1);
|
||||
color: var(--fg1);
|
||||
border: 1px solid var(--tertiary5);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tertiary2);
|
||||
border: 1px solid var(--tertiary6);
|
||||
}
|
||||
}
|
||||
|
||||
.baseButton.none {
|
||||
background-color: transparent;
|
||||
color: var(--fg1);
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.baseButton.outline {
|
||||
background-color: transparent;
|
||||
color: var(--fg1);
|
||||
border: 1px solid var(--fg7);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--bg2);
|
||||
border: 1px solid var(--fg2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
"use client";
|
||||
import styles from "./dropdown.module.css";
|
||||
import { DropdownMenu as _DropdownMenu } from "radix-ui";
|
||||
import { z } from "zod";
|
||||
import { useSchema } from "@/shared/hooks/useSchema";
|
||||
|
||||
const optionSchema = z.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
value: z.string(),
|
||||
disabled: z.boolean().optional(),
|
||||
})
|
||||
);
|
||||
|
||||
export const Dropdown = ({
|
||||
children,
|
||||
options,
|
||||
onSelect,
|
||||
contentProps,
|
||||
...props
|
||||
}) => {
|
||||
const validatedOptions = useSchema(options, optionSchema);
|
||||
|
||||
const handleSelect = (value) => {
|
||||
onSelect?.(value);
|
||||
};
|
||||
|
||||
const _contentProps = {
|
||||
sideOffset: 8,
|
||||
...contentProps,
|
||||
};
|
||||
|
||||
const renderOptions = () => {
|
||||
return validatedOptions.map((option) => (
|
||||
<_DropdownMenu.Item
|
||||
key={option.value}
|
||||
className={styles.item}
|
||||
onClick={() => handleSelect(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</_DropdownMenu.Item>
|
||||
));
|
||||
};
|
||||
return (
|
||||
<_DropdownMenu.Root {...props}>
|
||||
<_DropdownMenu.Trigger asChild>{children}</_DropdownMenu.Trigger>
|
||||
<_DropdownMenu.Portal className={styles.portal}>
|
||||
<_DropdownMenu.Content className={styles.content} {..._contentProps}>
|
||||
{renderOptions()}
|
||||
</_DropdownMenu.Content>
|
||||
</_DropdownMenu.Portal>
|
||||
</_DropdownMenu.Root>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
.content {
|
||||
background-color: var(--bg1);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--u-xs);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 12rem;
|
||||
border: 1px solid var(--fg5);
|
||||
box-shadow: 0 0 4 4px #000000dd;
|
||||
&:focus-within {
|
||||
outline: none;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
cursor: pointer;
|
||||
padding: 4px 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
&:hover {
|
||||
background-color: var(--bg3);
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.portal {
|
||||
z-index: 1000;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { useMemo } from "react";
|
||||
export const useSchema = (value, schema) => {
|
||||
return useMemo(() => schema.parse(value), [value, schema]);
|
||||
};
|
||||
Reference in New Issue
Block a user