feat: setup full Next.js toolchain and core dependencies
- Add runtime deps: react-query, react-hook-form+zod, zustand, ky, openapi-fetch, better-auth, react-markdown, shiki, recharts, next-themes, nuqs, sonner, cmdk, vaul, embla-carousel-react, motion, date-fns, tailwind-merge, clsx, cva - Add dev tooling: vitest, testing-library, playwright, prettier(+tailwind plugin), husky, lint-staged, jsdom - Wire providers (QueryClient, Theme, NuqsAdapter, Tooltip, Sonner) into layout - Add vitest/playwright/prettier configs + lint-staged pre-commit hook - Add lib/query-client + lib/http (ky) helpers and sample unit test - Fix react-hooks/set-state-in-effect lint errors (use-mobile useSyncExternalStore, carousel async init) - Remove committed Next-generated types/ duplicates (regenerated under .next/types)
This commit is contained in:
1 parent
046f201e63
commit
8093dd2b7f
87 files changed
+8683
-305
No files matched your search
@@ -0,0 +1,315 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { Questionnaire as QuestionnairePrimitive } from "@shadcn/react/questionnaire";
|
||||
import { cn } from "cn";
|
||||
|
||||
import { buttonVariants, type Button } from "@/components/ui/button";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
|
||||
function Questionnaire({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Root>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Root
|
||||
data-slot="questionnaire"
|
||||
className={cn("flex w-full min-w-0 flex-col gap-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireProgress({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Progress>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Progress
|
||||
data-slot="questionnaire-progress"
|
||||
className={cn(
|
||||
"min-h-[1lh] w-fit min-w-[14ch] text-xs font-medium text-muted-foreground tabular-nums",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireItem({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Item>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Item
|
||||
data-slot="questionnaire-item"
|
||||
className={cn("flex min-w-0 flex-col gap-4 border-0 p-0 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Title>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Title
|
||||
data-slot="questionnaire-title"
|
||||
className={cn(
|
||||
"font-heading text-base leading-snug font-medium text-pretty [&:not(:has(~[data-slot=questionnaire-description]))]:mb-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Description>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Description
|
||||
data-slot="questionnaire-description"
|
||||
className={cn("text-sm text-pretty text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireChoices({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Choices>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Choices
|
||||
data-slot="questionnaire-choices"
|
||||
className={cn("group/questionnaire-choices grid min-w-0 gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireChoice({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Choice>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Choice
|
||||
data-slot="questionnaire-choice"
|
||||
className={cn(
|
||||
"group/questionnaire-choice relative flex min-h-11 cursor-pointer items-start gap-2.5 rounded-lg border border-input bg-transparent px-3 py-2.5 text-start text-sm transition-colors outline-none select-none hover:bg-muted/50 has-[>input:focus-visible]:border-ring has-[>input:focus-visible]:ring-3 has-[>input:focus-visible]:ring-ring/50 data-invalid:border-destructive dark:bg-input/20 data-checked:border-primary/40 data-checked:bg-muted dark:data-checked:bg-muted",
|
||||
"data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<QuestionnairePrimitive.ChoiceInput
|
||||
data-slot="questionnaire-choice-input"
|
||||
className="absolute inset-0 z-10 size-full cursor-pointer opacity-0"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
data-slot="questionnaire-choice-indicator"
|
||||
className="pointer-events-none relative flex size-4 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-[4px] border border-input group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[type=radio]/questionnaire-choice:rounded-full group-data-checked/questionnaire-choice:border-primary group-data-checked/questionnaire-choice:bg-primary group-data-checked/questionnaire-choice:text-primary-foreground dark:bg-input/30 dLine truncated
|
||||
>
|
||||
<span
|
||||
data-slot="questionnaire-choice-indicator-dot"
|
||||
className="hidden size-2 rounded-full bg-primary-foreground group-data-[type=checkbox]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block"
|
||||
/>
|
||||
<CheckIcon
|
||||
data-slot="questionnaire-choice-indicator-check"
|
||||
className="hidden size-3.5 group-data-[type=radio]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block"
|
||||
/>
|
||||
</span>
|
||||
<QuestionnairePrimitive.ChoiceLabel
|
||||
data-slot="questionnaire-choice-label"
|
||||
className="flex min-w-0 flex-1 flex-col gap-0.5 leading-snug"
|
||||
>
|
||||
{children}
|
||||
</QuestionnairePrimitive.ChoiceLabel>
|
||||
<QuestionnairePrimitive.ChoiceShortcut
|
||||
data-slot="questionnaire-choice-shortcut"
|
||||
className="pointer-events-none ms-auto hidden size-5 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-md border border-input bg-background font-mono text-[0.625rem] leading-none font-medium text-muted-foreground group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[shortcut]/questionnaire-choice:inline-flex"
|
||||
/>
|
||||
</QuestionnairePrimitive.Choice>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireChoiceDescription({ className, ...props }: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="questionnaire-choice-description"
|
||||
className={cn("text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireInput({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Input>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="questionnaire-input-wrapper"
|
||||
className="group/questionnaire-input relative w-full min-w-0"
|
||||
>
|
||||
<QuestionnairePrimitive.Input
|
||||
data-slot="questionnaire-input"
|
||||
className={cn(
|
||||
"h-8 min-h-11 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-[color,box-shadow,background-color] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 sm:min-h-0 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-Line truncated
|
||||
"selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireError({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Error>) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Error
|
||||
data-slot="questionnaire-error"
|
||||
className={cn("mt-2 text-sm text-destructive", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireActions({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="questionnaire-actions"
|
||||
className={cn(
|
||||
"grid min-h-11 w-full grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-2 sm:min-h-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnairePrevious({
|
||||
children,
|
||||
className,
|
||||
size = "default",
|
||||
variant = "outline",
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Previous> &
|
||||
Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Previous
|
||||
data-slot="questionnaire-previous"
|
||||
data-size={size}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
buttonVariants({ size, variant }),
|
||||
"col-start-1 row-start-1 min-h-11 justify-self-start sm:min-h-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? "Previous"}
|
||||
</QuestionnairePrimitive.Previous>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireSkip({
|
||||
children,
|
||||
className,
|
||||
size = "default",
|
||||
variant = "outline",
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Skip> &
|
||||
Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Skip
|
||||
data-slot="questionnaire-skip"
|
||||
data-size={size}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
buttonVariants({ size, variant }),
|
||||
"col-start-2 row-start-1 min-h-11 justify-self-end sm:min-h-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? "Skip"}
|
||||
</QuestionnairePrimitive.Skip>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireNext({
|
||||
children,
|
||||
className,
|
||||
size = "default",
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Next> &
|
||||
Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Next
|
||||
data-slot="questionnaire-next"
|
||||
data-size={size}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
buttonVariants({ size, variant }),
|
||||
"col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? "Next"}
|
||||
</QuestionnairePrimitive.Next>
|
||||
);
|
||||
}
|
||||
|
||||
function QuestionnaireSubmit({
|
||||
children,
|
||||
className,
|
||||
size = "default",
|
||||
variant = "default",
|
||||
...props
|
||||
}: React.ComponentProps<typeof QuestionnairePrimitive.Submit> &
|
||||
Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
|
||||
return (
|
||||
<QuestionnairePrimitive.Submit
|
||||
data-slot="questionnaire-submit"
|
||||
data-size={size}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
buttonVariants({ size, variant }),
|
||||
"col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? "Submit"}
|
||||
</QuestionnairePrimitive.Submit>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Questionnaire,
|
||||
QuestionnaireActions,
|
||||
QuestionnaireChoice,
|
||||
QuestionnaireChoiceDescription,
|
||||
QuestionnaireChoices,
|
||||
QuestionnaireDescription,
|
||||
QuestionnaireError,
|
||||
QuestionnaireInput,
|
||||
QuestionnaireItem,
|
||||
QuestionnaireNext,
|
||||
QuestionnairePrevious,
|
||||
QuestionnaireProgress,
|
||||
QuestionnaireSkip,
|
||||
QuestionnaireSubmit,
|
||||
QuestionnaireTitle,
|
||||
};
|
||||
Reference in new issue
Block a user