From 4cb077e7b949ff47461c44020013f39a2aaba612 Mon Sep 17 00:00:00 2001 From: andrzej Date: Fri, 14 Jun 2024 11:25:18 +0200 Subject: [PATCH] radix select boxes working --- package-lock.json | 35 ++ package.json | 1 + src/@/components/ui/toast.tsx | 129 ++++++++ src/@/components/ui/toaster.tsx | 35 ++ src/@/components/ui/use-toast.ts | 194 +++++++++++ src/app/layout.tsx | 6 +- src/app/submission/create/page.tsx | 1 + src/app/tailwind.css | 306 +++++++++++++++--- .../ui/forms/{demo.tsx => Checkboxdemo.tsx} | 0 src/app/ui/forms/selectDemo.tsx | 86 +++++ src/app/ui/forms/sub.tsx | 87 +++-- 11 files changed, 816 insertions(+), 64 deletions(-) create mode 100644 src/@/components/ui/toast.tsx create mode 100644 src/@/components/ui/toaster.tsx create mode 100644 src/@/components/ui/use-toast.ts rename src/app/ui/forms/{demo.tsx => Checkboxdemo.tsx} (100%) create mode 100644 src/app/ui/forms/selectDemo.tsx diff --git a/package-lock.json b/package-lock.json index 9aab49a..cd64a05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-toast": "^1.1.5", "@tanstack/react-table": "^8.17.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -1076,6 +1077,40 @@ } } }, + "node_modules/@radix-ui/react-toast": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz", + "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@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-dismissable-layer": "1.0.5", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@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-visually-hidden": "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/react-use-callback-ref": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", diff --git a/package.json b/package.json index f5319dd..913ff23 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-toast": "^1.1.5", "@tanstack/react-table": "^8.17.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", diff --git a/src/@/components/ui/toast.tsx b/src/@/components/ui/toast.tsx new file mode 100644 index 0000000..521b94b --- /dev/null +++ b/src/@/components/ui/toast.tsx @@ -0,0 +1,129 @@ +"use client" + +import * as React from "react" +import * as ToastPrimitives from "@radix-ui/react-toast" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const ToastProvider = ToastPrimitives.Provider + +const ToastViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastViewport.displayName = ToastPrimitives.Viewport.displayName + +const toastVariants = cva( + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + { + variants: { + variant: { + default: "border bg-background text-foreground", + destructive: + "destructive group border-destructive bg-destructive text-destructive-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Toast = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => { + return ( + + ) +}) +Toast.displayName = ToastPrimitives.Root.displayName + +const ToastAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastAction.displayName = ToastPrimitives.Action.displayName + +const ToastClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +ToastClose.displayName = ToastPrimitives.Close.displayName + +const ToastTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastTitle.displayName = ToastPrimitives.Title.displayName + +const ToastDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastDescription.displayName = ToastPrimitives.Description.displayName + +type ToastProps = React.ComponentPropsWithoutRef + +type ToastActionElement = React.ReactElement + +export { + type ToastProps, + type ToastActionElement, + ToastProvider, + ToastViewport, + Toast, + ToastTitle, + ToastDescription, + ToastClose, + ToastAction, +} diff --git a/src/@/components/ui/toaster.tsx b/src/@/components/ui/toaster.tsx new file mode 100644 index 0000000..e223385 --- /dev/null +++ b/src/@/components/ui/toaster.tsx @@ -0,0 +1,35 @@ +"use client" + +import { + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, +} from "@/components/ui/toast" +import { useToast } from "@/components/ui/use-toast" + +export function Toaster() { + const { toasts } = useToast() + + return ( + + {toasts.map(function ({ id, title, description, action, ...props }) { + return ( + +
+ {title && {title}} + {description && ( + {description} + )} +
+ {action} + +
+ ) + })} + +
+ ) +} diff --git a/src/@/components/ui/use-toast.ts b/src/@/components/ui/use-toast.ts new file mode 100644 index 0000000..02e111d --- /dev/null +++ b/src/@/components/ui/use-toast.ts @@ -0,0 +1,194 @@ +"use client" + +// Inspired by react-hot-toast library +import * as React from "react" + +import type { + ToastActionElement, + ToastProps, +} from "@/components/ui/toast" + +const TOAST_LIMIT = 1 +const TOAST_REMOVE_DELAY = 1000000 + +type ToasterToast = ToastProps & { + id: string + title?: React.ReactNode + description?: React.ReactNode + action?: ToastActionElement +} + +const actionTypes = { + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", +} as const + +let count = 0 + +function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER + return count.toString() +} + +type ActionType = typeof actionTypes + +type Action = + | { + type: ActionType["ADD_TOAST"] + toast: ToasterToast + } + | { + type: ActionType["UPDATE_TOAST"] + toast: Partial + } + | { + type: ActionType["DISMISS_TOAST"] + toastId?: ToasterToast["id"] + } + | { + type: ActionType["REMOVE_TOAST"] + toastId?: ToasterToast["id"] + } + +interface State { + toasts: ToasterToast[] +} + +const toastTimeouts = new Map>() + +const addToRemoveQueue = (toastId: string) => { + if (toastTimeouts.has(toastId)) { + return + } + + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId) + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }) + }, TOAST_REMOVE_DELAY) + + toastTimeouts.set(toastId, timeout) +} + +export const reducer = (state: State, action: Action): State => { + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + } + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t + ), + } + + case "DISMISS_TOAST": { + const { toastId } = action + + // ! Side effects ! - This could be extracted into a dismissToast() action, + // but I'll keep it here for simplicity + if (toastId) { + addToRemoveQueue(toastId) + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id) + }) + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { + ...t, + open: false, + } + : t + ), + } + } + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + } + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + } + } +} + +const listeners: Array<(state: State) => void> = [] + +let memoryState: State = { toasts: [] } + +function dispatch(action: Action) { + memoryState = reducer(memoryState, action) + listeners.forEach((listener) => { + listener(memoryState) + }) +} + +type Toast = Omit + +function toast({ ...props }: Toast) { + const id = genId() + + const update = (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props, id }, + }) + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) + + dispatch({ + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss() + }, + }, + }) + + return { + id: id, + dismiss, + update, + } +} + +function useToast() { + const [state, setState] = React.useState(memoryState) + + React.useEffect(() => { + listeners.push(setState) + return () => { + const index = listeners.indexOf(setState) + if (index > -1) { + listeners.splice(index, 1) + } + } + }, [state]) + + return { + ...state, + toast, + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), + } +} + +export { useToast, toast } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0a5808a..3099749 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; +import { Toaster } from "@/components/ui/toaster"; import "./tailwind.css"; const inter = Inter({ subsets: ["latin"] }); @@ -20,7 +21,10 @@ export default function RootLayout({ - {children} + {children} + + + ); } diff --git a/src/app/submission/create/page.tsx b/src/app/submission/create/page.tsx index 2b19026..f9fb2e1 100644 --- a/src/app/submission/create/page.tsx +++ b/src/app/submission/create/page.tsx @@ -1,6 +1,7 @@ "use server" import { getPubs, getResponses, getStories } from "app/lib/get"; import SubmissionForm from "app/ui/forms/sub"; +import { SelectForm } from "app/ui/forms/selectDemo"; export default async function Page() { const stories = await getStories() diff --git a/src/app/tailwind.css b/src/app/tailwind.css index 07e1e36..d534ecb 100644 --- a/src/app/tailwind.css +++ b/src/app/tailwind.css @@ -600,6 +600,14 @@ body { } } +.pointer-events-auto { + pointer-events: auto; +} + +.fixed { + position: fixed; +} + .absolute { position: absolute; } @@ -612,10 +620,26 @@ body { left: 0.5rem; } +.right-2 { + right: 0.5rem; +} + +.top-0 { + top: 0px; +} + +.top-2 { + top: 0.5rem; +} + .z-50 { z-index: 50; } +.z-\[100\] { + z-index: 100; +} + .-mx-1 { margin-left: -0.25rem; margin-right: -0.25rem; @@ -631,6 +655,10 @@ body { margin-bottom: 0.25rem; } +.mb-4 { + margin-bottom: 1rem; +} + .ml-2 { margin-left: 0.5rem; } @@ -639,18 +667,14 @@ body { margin-left: auto; } -.mt-4 { - margin-top: 1rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - .mt-2 { margin-top: 0.5rem; } +.mt-4 { + margin-top: 1rem; +} + .flex { display: flex; } @@ -667,6 +691,10 @@ body { display: table; } +.grid { + display: grid; +} + .h-10 { height: 2.5rem; } @@ -703,18 +731,26 @@ body { height: 2.25rem; } -.h-px { - height: 1px; -} - .h-\[var\(--radix-select-trigger-height\)\] { height: var(--radix-select-trigger-height); } +.h-px { + height: 1px; +} + +.h-8 { + height: 2rem; +} + .max-h-96 { max-height: 24rem; } +.max-h-screen { + max-height: 100vh; +} + .w-10 { width: 2.5rem; } @@ -735,12 +771,16 @@ body { width: 1rem; } +.w-\[340px\] { + width: 340px; +} + .w-full { width: 100%; } -.w-\[340px\] { - width: 340px; +.w-2\/3 { + width: 66.666667%; } .min-w-\[8rem\] { @@ -777,6 +817,10 @@ body { flex-direction: row; } +.flex-col-reverse { + flex-direction: column-reverse; +} + .items-start { align-items: flex-start; } @@ -797,12 +841,28 @@ body { justify-content: space-between; } +.gap-1 { + gap: 0.25rem; +} + .space-x-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.5rem * var(--tw-space-x-reverse)); margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); +} + .space-y-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); @@ -815,16 +875,16 @@ body { margin-bottom: calc(2rem * var(--tw-space-y-reverse)); } -.space-x-3 > :not([hidden]) ~ :not([hidden]) { +.space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; - margin-right: calc(0.75rem * var(--tw-space-x-reverse)); - margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); } -.space-y-0 > :not([hidden]) ~ :not([hidden]) { +.space-y-6 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; - margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0px * var(--tw-space-y-reverse)); + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); } .overflow-auto { @@ -867,6 +927,10 @@ body { border-color: hsl(var(--primary)); } +.border-destructive { + border-color: hsl(var(--destructive)); +} + .bg-background { background-color: hsl(var(--background)); } @@ -900,6 +964,10 @@ body { background-color: rgb(2 6 23 / var(--tw-bg-opacity)); } +.bg-transparent { + background-color: transparent; +} + .fill-current { fill: currentColor; } @@ -912,6 +980,10 @@ body { padding: 1rem; } +.p-6 { + padding: 1.5rem; +} + .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; @@ -965,6 +1037,10 @@ body { padding-right: 0.5rem; } +.pr-8 { + padding-right: 2rem; +} + .text-left { text-align: left; } @@ -982,6 +1058,11 @@ body { line-height: 2.25rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + .text-sm { font-size: 0.875rem; line-height: 1.25rem; @@ -992,11 +1073,6 @@ body { line-height: 1rem; } -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} - .font-black { font-weight: 900; } @@ -1005,14 +1081,14 @@ body { font-weight: 500; } -.font-semibold { - font-weight: 600; -} - .font-normal { font-weight: 400; } +.font-semibold { + font-weight: 600; +} + .capitalize { text-transform: capitalize; } @@ -1025,6 +1101,10 @@ body { letter-spacing: 0.1em; } +.text-current { + color: currentColor; +} + .text-destructive { color: hsl(var(--destructive)); } @@ -1053,15 +1133,19 @@ body { color: hsl(var(--secondary-foreground)); } -.text-current { - color: currentColor; -} - .text-white { --tw-text-opacity: 1; color: rgb(255 255 255 / var(--tw-text-opacity)); } +.text-foreground { + color: hsl(var(--foreground)); +} + +.text-foreground\/50 { + color: hsl(var(--foreground) / 0.5); +} + .underline { text-decoration-line: underline; } @@ -1070,12 +1154,20 @@ body { text-underline-offset: 4px; } +.opacity-50 { + opacity: 0.5; +} + .opacity-60 { opacity: 0.6; } -.opacity-50 { - opacity: 0.5; +.opacity-0 { + opacity: 0; +} + +.opacity-90 { + opacity: 0.9; } .shadow-lg { @@ -1113,6 +1205,18 @@ body { transition-duration: 150ms; } +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + @keyframes enter { from { opacity: var(--tw-enter-opacity, 1); @@ -1172,10 +1276,18 @@ body { background-color: hsl(var(--secondary) / 0.8); } +.hover\:bg-secondary:hover { + background-color: hsl(var(--secondary)); +} + .hover\:text-accent-foreground:hover { color: hsl(var(--accent-foreground)); } +.hover\:text-foreground:hover { + color: hsl(var(--foreground)); +} + .hover\:underline:hover { text-decoration-line: underline; } @@ -1188,6 +1300,10 @@ body { color: hsl(var(--accent-foreground)); } +.focus\:opacity-100:focus { + opacity: 1; +} + .focus\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; @@ -1238,6 +1354,49 @@ body { opacity: 0.5; } +.group:hover .group-hover\:opacity-100 { + opacity: 1; +} + +.group.destructive .group-\[\.destructive\]\:border-muted\/40 { + border-color: hsl(var(--muted) / 0.4); +} + +.group.destructive .group-\[\.destructive\]\:text-red-300 { + --tw-text-opacity: 1; + color: rgb(252 165 165 / var(--tw-text-opacity)); +} + +.group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover { + border-color: hsl(var(--destructive) / 0.3); +} + +.group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover { + background-color: hsl(var(--destructive)); +} + +.group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover { + color: hsl(var(--destructive-foreground)); +} + +.group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover { + --tw-text-opacity: 1; + color: rgb(254 242 242 / var(--tw-text-opacity)); +} + +.group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus { + --tw-ring-color: hsl(var(--destructive)); +} + +.group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity)); +} + +.group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus { + --tw-ring-offset-color: #dc2626; +} + .peer:disabled ~ .peer-disabled\:cursor-not-allowed { cursor: not-allowed; } @@ -1270,6 +1429,25 @@ body { 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-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel] { + --tw-translate-x: 0px; + 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-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end] { + --tw-translate-x: var(--radix-toast-swipe-end-x); + 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-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move] { + --tw-translate-x: var(--radix-toast-swipe-move-x); + 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\=checked\]\:bg-primary[data-state=checked] { + background-color: hsl(var(--primary)); +} + .data-\[state\=open\]\:bg-accent[data-state=open] { background-color: hsl(var(--accent)); } @@ -1278,10 +1456,6 @@ body { background-color: hsl(var(--muted)); } -.data-\[state\=checked\]\:bg-primary[data-state=checked] { - background-color: hsl(var(--primary)); -} - .data-\[state\=checked\]\:text-primary-foreground[data-state=checked] { color: hsl(var(--primary-foreground)); } @@ -1290,6 +1464,10 @@ body { opacity: 0.5; } +.data-\[swipe\=move\]\:transition-none[data-swipe=move] { + transition-property: none; +} + .data-\[state\=open\]\:animate-in[data-state=open] { animation-name: enter; animation-duration: 150ms; @@ -1310,6 +1488,16 @@ body { --tw-exit-translate-y: initial; } +.data-\[swipe\=end\]\:animate-out[data-swipe=end] { + animation-name: exit; + animation-duration: 150ms; + --tw-exit-opacity: initial; + --tw-exit-scale: initial; + --tw-exit-rotate: initial; + --tw-exit-translate-x: initial; + --tw-exit-translate-y: initial; +} + .data-\[state\=closed\]\:fade-out-0[data-state=closed] { --tw-exit-opacity: 0; } @@ -1318,6 +1506,10 @@ body { --tw-enter-opacity: 0; } +.data-\[state\=closed\]\:fade-out-80[data-state=closed] { + --tw-exit-opacity: 0.8; +} + .data-\[state\=closed\]\:zoom-out-95[data-state=closed] { --tw-exit-scale: .95; } @@ -1342,6 +1534,42 @@ body { --tw-enter-translate-y: 0.5rem; } +.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed] { + --tw-exit-translate-x: 100%; +} + +.data-\[state\=open\]\:slide-in-from-top-full[data-state=open] { + --tw-enter-translate-y: -100%; +} + +@media (min-width: 640px) { + .sm\:bottom-0 { + bottom: 0px; + } + + .sm\:right-0 { + right: 0px; + } + + .sm\:top-auto { + top: auto; + } + + .sm\:flex-col { + flex-direction: column; + } + + .data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open] { + --tw-enter-translate-y: 100%; + } +} + +@media (min-width: 768px) { + .md\:max-w-\[420px\] { + max-width: 420px; + } +} + .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]) { padding-right: 0px; } diff --git a/src/app/ui/forms/demo.tsx b/src/app/ui/forms/Checkboxdemo.tsx similarity index 100% rename from src/app/ui/forms/demo.tsx rename to src/app/ui/forms/Checkboxdemo.tsx diff --git a/src/app/ui/forms/selectDemo.tsx b/src/app/ui/forms/selectDemo.tsx new file mode 100644 index 0000000..e589d74 --- /dev/null +++ b/src/app/ui/forms/selectDemo.tsx @@ -0,0 +1,86 @@ +"use client" + +import Link from "next/link" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { toast } from "@/components/ui/use-toast" + +const FormSchema = z.object({ + email: z + .string({ + required_error: "Please select an email to display.", + }) + .email(), +}) + +export function SelectForm() { + const form = useForm>({ + resolver: zodResolver(FormSchema), + }) + + function onSubmit(data: z.infer) { + toast({ + title: "You submitted the following values:", + description: ( +
+					{JSON.stringify(data, null, 2)}
+				
+ ), + }) + } + + + return ( +
+ + ( + + Email + + + You can manage email addresses in your{" "} + email settings. + + + + )} + /> + + + + ) +} + diff --git a/src/app/ui/forms/sub.tsx b/src/app/ui/forms/sub.tsx index 62f9f2d..2e66892 100644 --- a/src/app/ui/forms/sub.tsx +++ b/src/app/ui/forms/sub.tsx @@ -4,6 +4,7 @@ import { z } from "zod" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { Button } from "@/components/ui/button" +import { toast } from "@/components/ui/use-toast" import { Form, FormItem, @@ -22,19 +23,21 @@ import { } 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() +const FormSchema = z.object({ + storyId: z.string(), + pubId: z.string(), + // submitted: z.date(), + // responded: z.date(), + // responseId: z.string() }) export default function SubmissionForm({ stories, pubs, responses }) { - + const form = useForm>({ + resolver: zodResolver(FormSchema), + }) const storiesSelectItems = stories.map(e => ( - + {e.title} )) @@ -50,43 +53,79 @@ export default function SubmissionForm({ stories, pubs, responses }) { )) - // 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) { + function onSubmit(values: z.infer) { // Do something with the form values. // ✅ This will be type-safe and validated. + toast({ + title: "You submitted the following values:", + description: ( +
+					{JSON.stringify(values, null, 2)}
+				
+ ), + }) console.log(values) } + + function onErrors(errors) { + toast({ + title: "You have errors", + description: ( +
+					{JSON.stringify(errors, null, 2)}
+				
+ ), + }) + console.log(JSON.stringify(errors)) + } + return (
- + ( Story - - + +

{stories?.find(e => e.id === Number(field.value))?.title ?? null}

+
{storiesSelectItems} - This is the story you submitted, yeah? + The piece you submitted + +
+ )} + /> + ( + + Publication + + The market you sent it to )}