From b604ed48daa77cc37f13283852371b75b43138f3 Mon Sep 17 00:00:00 2001 From: andrzej Date: Thu, 13 Jun 2024 21:52:44 +0200 Subject: [PATCH] still trying to get shadcn selects to work dynamically --- package-lock.json | 123 ++++++++++++++++++++++ package.json | 1 + src/@/components/ui/select.tsx | 160 +++++++++++++++++++++++++++++ src/app/lib/get.ts | 3 + src/app/story/create/page.tsx | 6 +- src/app/submission/create/page.tsx | 7 +- src/app/tailwind.css | 66 ++++++++++++ src/app/ui/forms/story.tsx | 129 +++++++++++++++++++++-- src/app/ui/forms/sub.tsx | 111 +++++++++++++++++--- 9 files changed, 580 insertions(+), 26 deletions(-) create mode 100644 src/@/components/ui/select.tsx diff --git a/package-lock.json b/package-lock.json index 6112cc9..9aab49a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,10 @@ "dependencies": { "@hookform/resolvers": "^3.6.0", "@prisma/client": "^5.15.0", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@tanstack/react-table": "^8.17.3", "class-variance-authority": "^0.7.0", @@ -555,6 +557,14 @@ "@prisma/debug": "5.15.0" } }, + "node_modules/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, "node_modules/@radix-ui/primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", @@ -586,6 +596,36 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz", + "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", @@ -975,6 +1015,49 @@ } } }, + "node_modules/@radix-ui/react-select": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz", + "integrity": "sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -1063,6 +1146,23 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", @@ -1099,6 +1199,29 @@ } } }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", diff --git a/package.json b/package.json index 4a9bf37..f5319dd 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@tanstack/react-table": "^8.17.3", "class-variance-authority": "^0.7.0", diff --git a/src/@/components/ui/select.tsx b/src/@/components/ui/select.tsx new file mode 100644 index 0000000..cbe5a36 --- /dev/null +++ b/src/@/components/ui/select.tsx @@ -0,0 +1,160 @@ +"use client" + +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { Check, ChevronDown, ChevronUp } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +} diff --git a/src/app/lib/get.ts b/src/app/lib/get.ts index 578e2ba..797b286 100644 --- a/src/app/lib/get.ts +++ b/src/app/lib/get.ts @@ -9,4 +9,7 @@ export async function getPubs() { export async function getGenres() { return prisma.genre.findMany() } +export async function getResponses() { + return prisma.response.findMany() +} diff --git a/src/app/story/create/page.tsx b/src/app/story/create/page.tsx index 1e2bf0b..0165968 100644 --- a/src/app/story/create/page.tsx +++ b/src/app/story/create/page.tsx @@ -1,5 +1,7 @@ +import { getGenres } from "app/lib/get"; import StoryForm from "app/ui/forms/story"; -export default function Page() { - return +export default async function Page() { + const genres = await getGenres() + return } diff --git a/src/app/submission/create/page.tsx b/src/app/submission/create/page.tsx index 05f2d1c..2b19026 100644 --- a/src/app/submission/create/page.tsx +++ b/src/app/submission/create/page.tsx @@ -1,5 +1,10 @@ "use server" +import { getPubs, getResponses, getStories } from "app/lib/get"; import SubmissionForm from "app/ui/forms/sub"; + export default async function Page() { - return + const stories = await getStories() + const pubs = await getPubs() + const responses = await getResponses() + return } diff --git a/src/app/tailwind.css b/src/app/tailwind.css index 77f8741..07e1e36 100644 --- a/src/app/tailwind.css +++ b/src/app/tailwind.css @@ -707,6 +707,14 @@ body { height: 1px; } +.h-\[var\(--radix-select-trigger-height\)\] { + height: var(--radix-select-trigger-height); +} + +.max-h-96 { + max-height: 24rem; +} + .w-10 { width: 2.5rem; } @@ -739,6 +747,10 @@ body { min-width: 8rem; } +.min-w-\[var\(--radix-select-trigger-width\)\] { + min-width: var(--radix-select-trigger-width); +} + .max-w-sm { max-width: 24rem; } @@ -781,6 +793,10 @@ body { justify-content: center; } +.justify-between { + justify-content: space-between; +} + .space-x-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.5rem * var(--tw-space-x-reverse)); @@ -1058,6 +1074,10 @@ body { opacity: 0.6; } +.opacity-50 { + opacity: 0.5; +} + .shadow-lg { --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); @@ -1168,6 +1188,25 @@ body { color: hsl(var(--accent-foreground)); } +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-ring:focus { + --tw-ring-color: hsl(var(--ring)); +} + +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} + .focus-visible\:outline-none:focus-visible { outline: 2px solid transparent; outline-offset: 2px; @@ -1211,6 +1250,26 @@ body { pointer-events: none; } +.data-\[side\=bottom\]\:translate-y-1[data-side=bottom] { + --tw-translate-y: 0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.data-\[side\=left\]\:-translate-x-1[data-side=left] { + --tw-translate-x: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.data-\[side\=right\]\:translate-x-1[data-side=right] { + --tw-translate-x: 0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.data-\[side\=top\]\:-translate-y-1[data-side=top] { + --tw-translate-y: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .data-\[state\=open\]\:bg-accent[data-state=open] { background-color: hsl(var(--accent)); } @@ -1287,6 +1346,13 @@ body { padding-right: 0px; } +.\[\&\>span\]\:line-clamp-1>span { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + .\[\&\>tr\]\:last\:border-b-0:last-child>tr { border-bottom-width: 0px; } diff --git a/src/app/ui/forms/story.tsx b/src/app/ui/forms/story.tsx index 9833ae3..3bf8da6 100644 --- a/src/app/ui/forms/story.tsx +++ b/src/app/ui/forms/story.tsx @@ -1,12 +1,121 @@ +"use client" + +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Genre } from "@prisma/client" +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" import { Input } from "@/components/ui/input" -import GenreCheckboxes from "./genreCheckboxes" -export default async function StoryForm() { - return
- - - - - - - +import { Checkbox } from "@/components/ui/checkbox" + +const formSchema = z.object({ + title: z.string().min(2).max(50), + word_count: z.number(), + genres: z.object({ id: z.number(), name: z.string() }).array() +}) + + +export default function StoryForm({ genres }) { + // 1. Define your form. + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + title: "", + word_count: 0, + genres: genres + }, + }) + // 2. Define a submit handler. + function onSubmit(values: z.infer) { + // Do something with the form values. + // ✅ This will be type-safe and validated. + console.log(values) + } + return ( +
+ + ( + + Title + + + + + + )} + /> + ( + + Word count + + + + + )} + /> + ( + +
+ Genres + genres baby +
+ {genres.map((item) => ( + { + return ( + + + { + return checked + ? field.onChange([...field.value, item.id]) + : field.onChange( + field.value?.filter( + (value) => value !== item.id + ) + ) + }} + /> + + + {item.name} + + + ) + }} + /> + ))} +
+ )} + + /> + + + + ) } diff --git a/src/app/ui/forms/sub.tsx b/src/app/ui/forms/sub.tsx index 518691f..62f9f2d 100644 --- a/src/app/ui/forms/sub.tsx +++ b/src/app/ui/forms/sub.tsx @@ -1,15 +1,100 @@ -import PubsDropdown from "./pubsDropdown"; -import ResponseDropdown from "./responseDropdown"; -import StoryDropdown from "./storyDropdown"; +"use client" -export default async function SubmissionForm() { - return
- - - - - - - - +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Button } from "@/components/ui/button" +import { + Form, + FormItem, + FormLabel, + FormField, + FormControl, + FormDescription, + FormMessage +} from "@/components/ui/form" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { ItemText, SelectItemIndicator, SelectItemText } from "@radix-ui/react-select" + +const formSchema = z.object({ + storyId: z.coerce.number(), + pubId: z.number(), + submitted: z.date(), + responded: z.date(), + responseId: z.number() +}) + + +export default function SubmissionForm({ stories, pubs, responses }) { + + const storiesSelectItems = stories.map(e => ( + + {e.title} + + )) + const pubsSelectItems = pubs.map(e => ( + + {e.title} + + )) + + const reponsesSelectItems = responses.map(e => ( + + {e.response} + + )) + + // 1. Define your form. + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + storyId: stories[0].id, + pubId: pubs[0].id, + submitted: new Date(), + responded: null, + responseId: responses[0].id + }, + }) + // 2. Define a submit handler. + function onSubmit(values: z.infer) { + // Do something with the form values. + // ✅ This will be type-safe and validated. + console.log(values) + } + return ( +
+ + ( + + Story + + This is the story you submitted, yeah? + + + )} + /> + + + + + + ) }