diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index d4cb217cf..f29bdaf3c 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -22,7 +22,7 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 10.18.3 + version: 11.1.3 - name: Install Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/bump_publish.yml b/.github/workflows/bump_publish.yml index 84304b5a4..8ef3b1ead 100644 --- a/.github/workflows/bump_publish.yml +++ b/.github/workflows/bump_publish.yml @@ -52,7 +52,7 @@ jobs: - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 10.18.3 + version: 11.1.3 - name: Install Node.js uses: actions/setup-node@v4 diff --git a/apps/api/package.json b/apps/api/package.json index ef54d038c..560cc3e7b 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -15,11 +15,11 @@ }, "dependencies": { "@hono/zod-openapi": "^1.3.0", - "@hono/zod-validator": "^0.7.6", + "@hono/zod-validator": "^0.8.0", "@vitnode/core": "workspace:*", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", - "hono": "^4.12.16", + "hono": "^4.12.21", "next-intl": "^4.11.0", "react": "^19.2.5", "react-dom": "^19.2.5", @@ -36,9 +36,9 @@ "@vitnode/nodemailer": "workspace:*", "dotenv": "^17.4.2", "eslint": "^10.2.1", - "react-email": "^6.0.5", + "react-email": "^6.1.5", "tsc-alias": "^1.8.16", - "tsx": "^4.21.0", + "tsx": "^4.22.3", "typescript": "^6.0.3" } } diff --git a/apps/api/src/locales/@vitnode/core/en.json b/apps/api/src/locales/@vitnode/core/en.json index 5d2f42ed1..8fa1fee6c 100644 --- a/apps/api/src/locales/@vitnode/core/en.json +++ b/apps/api/src/locales/@vitnode/core/en.json @@ -1,6 +1,15 @@ { "core": { "global": { + "close": "Close", + "confirm": "Confirm", + "previous": "Previous", + "next": "Next", + "current_page": "Current page", + "go_to_page": "Go to page", + "previous_page": "Previous page", + "next_page": "Next page", + "remove": "Remove", "editor": { "undo": "Undo", "redo": "Redo", diff --git a/apps/docs/components.json b/apps/docs/components.json index 9a3fe27e5..58c323fd0 100644 --- a/apps/docs/components.json +++ b/apps/docs/components.json @@ -1,6 +1,6 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "base-nova", + "style": "radix-vega", "rsc": true, "tsx": true, "tailwind": { diff --git a/apps/docs/content/docs/dev/email/components/button.mdx b/apps/docs/content/docs/dev/email/components/button.mdx index 150a8a590..1d5f31012 100644 --- a/apps/docs/content/docs/dev/email/components/button.mdx +++ b/apps/docs/content/docs/dev/email/components/button.mdx @@ -5,15 +5,15 @@ description: A button or link component for emails. ## Preview -import { ImgDocs } from '@/components/fumadocs/img'; -import buttonPreviewImg from './button-preview.png'; +import { ImgDocs } from "@/components/fumadocs/img"; +import buttonPreviewImg from "./button-preview.png"; ## Usage ```ts -import { EmailButton } from '@vitnode/core/emails/ui/button'; +import { EmailButton } from "@vitnode/core/emails/ui/button"; ``` ```tsx @@ -22,24 +22,24 @@ import { EmailButton } from '@vitnode/core/emails/ui/button'; ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/content/docs/ui/auto-form.mdx b/apps/docs/content/docs/ui/auto-form.mdx index f83632f6b..417e8f7a8 100644 --- a/apps/docs/content/docs/ui/auto-form.mdx +++ b/apps/docs/content/docs/ui/auto-form.mdx @@ -15,6 +15,8 @@ import { AutoFormCheckbox } from "@vitnode/core/components/form/fields/checkbox" import { AutoFormInput } from "@vitnode/core/components/form/fields/input"; import { AutoFormSelect } from "@vitnode/core/components/form/fields/select"; import { AutoFormTextarea } from "@vitnode/core/components/form/fields/textarea"; +import { InputGroupAddon } from "@vitnode/core/components/ui/input-group"; +import { Search } from "lucide-react"; import { z } from "zod"; ``` @@ -29,6 +31,7 @@ const formSchema = z.object({ message: "You must accept the terms and conditions", }), description: z.string().min(10, "Description must be at least 10 characters"), + search: z.string().optional(), }); ``` @@ -84,6 +87,17 @@ const formSchema = z.object({ /> ), }, + { + id: "search", + component: props => ( + + + + + 12 results + + ), + }, ]} formSchema={formSchema} /> diff --git a/apps/docs/content/docs/ui/button.mdx b/apps/docs/content/docs/ui/button.mdx index 176ab8855..996682188 100644 --- a/apps/docs/content/docs/ui/button.mdx +++ b/apps/docs/content/docs/ui/button.mdx @@ -10,8 +10,8 @@ description: A button component for triggering actions in your application. ## Usage ```ts -import { Home } from 'lucide-react'; -import { Button } from '@vitnode/core/components/ui/button'; +import { Home } from "lucide-react"; +import { Button } from "@vitnode/core/components/ui/button"; ``` ```tsx @@ -23,19 +23,19 @@ import { Button } from '@vitnode/core/components/ui/button'; ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/content/docs/ui/checkbox.mdx b/apps/docs/content/docs/ui/checkbox.mdx index 3226157a3..0cc420a80 100644 --- a/apps/docs/content/docs/ui/checkbox.mdx +++ b/apps/docs/content/docs/ui/checkbox.mdx @@ -9,21 +9,21 @@ description: Toggle between checked and unchecked states. ## Usage -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; ```ts -import { z } from 'zod'; -import { AutoForm } from '@vitnode/core/components/form/auto-form'; -import { AutoFormCheckbox } from '@vitnode/core/components/form/fields/checkbox'; +import { z } from "zod"; +import { AutoForm } from "@vitnode/core/components/form/auto-form"; +import { AutoFormCheckbox } from "@vitnode/core/components/form/fields/checkbox"; ``` ```ts const formSchema = z.object({ acceptTerms: z.boolean().refine(val => val, { - message: 'You must accept the terms and conditions', + message: "You must accept the terms and conditions", }), }); ``` @@ -33,7 +33,7 @@ const formSchema = z.object({ formSchema={formSchema} fields={[ { - id: 'acceptTerms', + id: "acceptTerms", component: props => ( ```ts -import { Checkbox } from '@vitnode/core-frontend/components/ui/checkbox'; +import { Checkbox } from '@vitnode/core/components/ui/checkbox'; ``` ```tsx @@ -61,21 +61,21 @@ import { Checkbox } from '@vitnode/core-frontend/components/ui/checkbox'; ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/content/docs/ui/input-group.mdx b/apps/docs/content/docs/ui/input-group.mdx new file mode 100644 index 000000000..868f0aff7 --- /dev/null +++ b/apps/docs/content/docs/ui/input-group.mdx @@ -0,0 +1,113 @@ +--- +title: Input Group +description: Component used for grouping related input fields together +--- + +## Preview + + + +## Usage + +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; + + + +```ts +import { z } from 'zod'; +import { AutoForm } from '@vitnode/core/components/form/auto-form'; +import { AutoFormInput } from '@vitnode/core/components/form/fields/input'; +import { AutoFormTextarea } from '@vitnode/core/components/form/fields/textarea'; +import { InputGroupAddon, InputGroupText } from "@vitnode/core/components/ui/input-group"; +``` + +```ts +const formSchema = z.object({ + search: z.string().min(1, "Search is required"), + description: z + .string() + .max(500, "Description must be less than 500 characters"), +}); +``` + +```tsx + ( + + + + + 12 results + + ), + }, + { + id: "description", + component: props => ( + + + + {props.field.value?.toString().length ?? 0} of 500 characters + + + + ), + }, + ]} +/> +``` + + + + +```ts +import { + InputGroup, + InputGroupAddon, + InputGroupInput, + InputGroupText, + InputGroupTextarea, +} from "@vitnode/core/components/ui/input-group"; +import { Search } from "lucide-react"; +``` + +```tsx +
+ + + + + + 12 results + + + + + + + 0 of 500 characters + + + +
+``` + +
+
+ +## API Reference + +[Shadcn UI - Input Group](https://ui.shadcn.com/docs/components/radix/input-group#api-reference) diff --git a/apps/docs/content/docs/ui/input.mdx b/apps/docs/content/docs/ui/input.mdx index 78193dd0a..ad6bb31e4 100644 --- a/apps/docs/content/docs/ui/input.mdx +++ b/apps/docs/content/docs/ui/input.mdx @@ -9,7 +9,7 @@ description: Component used for collecting data from users ## Usage -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; @@ -21,8 +21,8 @@ import { AutoFormInput } from '@vitnode/core/components/form/fields/input'; ```ts const formSchema = z.object({ - username: z.string().min(3, 'Username must be at least 3 characters'), - email: z.email('Please enter a valid email address'), + username: z.string().min(3, "Username must be at least 3 characters"), + email: z.email("Please enter a valid email address"), }); ``` @@ -31,7 +31,7 @@ const formSchema = z.object({ formSchema={formSchema} fields={[ { - id: 'username', + id: "username", component: props => ( ( ```ts -import { Input } from '@vitnode/core-frontend/components/ui/input'; +import { Input } from '@vitnode/core/components/ui/input'; ``` ```tsx @@ -70,32 +70,32 @@ import { Input } from '@vitnode/core-frontend/components/ui/input'; ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/content/docs/ui/meta.json b/apps/docs/content/docs/ui/meta.json index ca3bbcc42..81e738b54 100644 --- a/apps/docs/content/docs/ui/meta.json +++ b/apps/docs/content/docs/ui/meta.json @@ -18,6 +18,7 @@ "combobox-async", "combobox", "input", + "input-group", "radio-group", "select", "switch", diff --git a/apps/docs/content/docs/ui/radio-group.mdx b/apps/docs/content/docs/ui/radio-group.mdx index 8796c0ed3..daeb4274e 100644 --- a/apps/docs/content/docs/ui/radio-group.mdx +++ b/apps/docs/content/docs/ui/radio-group.mdx @@ -21,7 +21,7 @@ import { AutoFormRadioGroup } from '@vitnode/core/components/form/fields/radio-g ```ts const formSchema = z.object({ - options: z.enum(["option1", "option2", "option3"]) + options: z.enum(["option1", "option2", "option3"]), }); ``` @@ -31,7 +31,7 @@ const formSchema = z.object({ fields={[ { id: "options", - component: (props) => ( + component: props => ( - ) - } + ), + }, ]} /> ``` @@ -61,29 +64,90 @@ const formSchema = z.object({ ```ts -import { Label } from '@vitnode/core-frontend/components/ui/label'; import { RadioGroup, RadioGroupItem, -} from '@vitnode/core-frontend/components/ui/radio-group'; +} from '@vitnode/core/components/ui/radio-group'; +import { Field, FieldLabel } from "@vitnode/core/components/ui/field" ``` ```tsx -
+ - -
-
+ + Option One + + This is the description for option one. It provides more information + about the option. + + + + + - -
+ + Option Two + + + + + + + Option Three + + This is the description for option three. It provides more information + about the option. + + +
```
+### variant="block" + +You can also use the `block` variant to display the radio buttons in a block style. + +```tsx + ( + + ), + }, + ]} +/> +``` + ## Props import { TypeTable } from "fumadocs-ui/components/type-table"; @@ -91,21 +155,29 @@ import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/content/docs/ui/select.mdx b/apps/docs/content/docs/ui/select.mdx index abe3cce37..4c24d5cf8 100644 --- a/apps/docs/content/docs/ui/select.mdx +++ b/apps/docs/content/docs/ui/select.mdx @@ -9,20 +9,20 @@ description: Choose an option from a list of options. ## Usage -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; ```ts -import { z } from 'zod'; -import { AutoForm } from '@vitnode/core/components/form/auto-form'; -import { AutoFormSelect } from '@vitnode/core/components/form/fields/select'; +import { z } from "zod"; +import { AutoForm } from "@vitnode/core/components/form/auto-form"; +import { AutoFormSelect } from "@vitnode/core/components/form/fields/select"; ``` ```ts const formSchema = z.object({ - options: z.enum(['option1', 'option2', 'option3']).default('option1'), + options: z.enum(["option1", "option2", "option3"]).default("option1"), }); ``` @@ -31,26 +31,26 @@ const formSchema = z.object({ formSchema={formSchema} fields={[ { - id: 'options', + id: "options", component: props => ( ), }, @@ -68,7 +68,7 @@ import { SelectItem, SelectTrigger, SelectValue, -} from '@vitnode/core-frontend/components/ui/select'; +} from '@vitnode/core/components/ui/select'; ``` ```tsx @@ -88,33 +88,33 @@ import { ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/content/docs/ui/switch.mdx b/apps/docs/content/docs/ui/switch.mdx index 412ca7bb4..c9b6197a8 100644 --- a/apps/docs/content/docs/ui/switch.mdx +++ b/apps/docs/content/docs/ui/switch.mdx @@ -9,21 +9,21 @@ description: Toggle between checked and unchecked states. ## Usage -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; ```ts -import { z } from 'zod'; -import { AutoForm } from '@vitnode/core/components/form/auto-form'; -import { AutoFormSwitch } from '@vitnode/core/components/form/fields/switch'; +import { z } from "zod"; +import { AutoForm } from "@vitnode/core/components/form/auto-form"; +import { AutoFormSwitch } from "@vitnode/core/components/form/fields/switch"; ``` ```ts const formSchema = z.object({ acceptTerms: z.boolean().refine(val => val, { - message: 'You must accept the terms and conditions', + message: "You must accept the terms and conditions", }), }); ``` @@ -33,7 +33,7 @@ const formSchema = z.object({ formSchema={formSchema} fields={[ { - id: 'acceptTerms', + id: "acceptTerms", component: props => ( ), @@ -46,7 +46,7 @@ const formSchema = z.object({ ```ts -import { Switch } from '@vitnode/core-frontend/components/ui/switch'; +import { Switch } from '@vitnode/core/components/ui/switch'; ``` ```tsx diff --git a/apps/docs/content/docs/ui/textarea.mdx b/apps/docs/content/docs/ui/textarea.mdx index 42559f631..41ebf0345 100644 --- a/apps/docs/content/docs/ui/textarea.mdx +++ b/apps/docs/content/docs/ui/textarea.mdx @@ -9,20 +9,20 @@ description: Input for multi-line text input. ## Usage -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; ```ts -import { z } from 'zod'; -import { AutoForm } from '@vitnode/core/components/form/auto-form'; -import { AutoFormTextarea } from '@vitnode/core/components/form/fields/textarea'; +import { z } from "zod"; +import { AutoForm } from "@vitnode/core/components/form/auto-form"; +import { AutoFormTextarea } from "@vitnode/core/components/form/fields/textarea"; ``` ```ts const formSchema = z.object({ - desc: z.string().min(10, 'Description must be at least 10 characters'), + desc: z.string().min(10, "Description must be at least 10 characters"), }); ``` @@ -31,7 +31,7 @@ const formSchema = z.object({ formSchema={formSchema} fields={[ { - id: 'desc', + id: "desc", component: props => ( ```ts -import { Textarea } from '@vitnode/core-frontend/components/ui/textarea'; +import { Textarea } from '@vitnode/core/components/ui/textarea'; ``` ```tsx @@ -61,26 +61,26 @@ import { Textarea } from '@vitnode/core-frontend/components/ui/textarea'; ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; diff --git a/apps/docs/package.json b/apps/docs/package.json index 81311561c..240abee3b 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -23,15 +23,15 @@ }, "dependencies": { "@hono/zod-openapi": "^1.3.0", - "@hono/zod-validator": "^0.7.6", + "@hono/zod-validator": "^0.8.0", "@vitnode/blog": "workspace:*", "@vitnode/core": "workspace:*", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", "fumadocs-core": "^16.8.5", - "fumadocs-mdx": "^14.3.2", + "fumadocs-mdx": "^15.0.6", "fumadocs-ui": "^16.8.5", - "hono": "^4.12.16", + "hono": "^4.12.21", "lucide-react": "^1.14.0", "motion": "^12.38.0", "next": "^16.2.4", @@ -41,6 +41,7 @@ "react-dom": "^19.2.5", "react-hook-form": "^7.74.0", "react-use": "^17.6.0", + "shadcn": "^4.7.0", "sonner": "^2.0.7" }, "devDependencies": { @@ -60,7 +61,7 @@ "class-variance-authority": "^0.7.1", "eslint": "^10.2.1", "postcss": "^8.5.12", - "react-email": "^6.0.5", + "react-email": "^6.1.5", "shiki": "^4.0.2", "tailwindcss": "^4.2.4", "tw-animate-css": "^1.4.0", diff --git a/apps/docs/src/app/global.css b/apps/docs/src/app/global.css index a3723c255..90890f9cd 100644 --- a/apps/docs/src/app/global.css +++ b/apps/docs/src/app/global.css @@ -1,7 +1,8 @@ @import "tailwindcss"; -@import "fumadocs-ui/css/shadcn.css"; +@import "fumadocs-ui/css/neutral.css"; @import "fumadocs-ui/css/preset.css"; @import "@vitnode/core/styles/tiptap.css"; +@import "shadcn/tailwind.css"; @import "tw-animate-css"; @@ -11,37 +12,37 @@ :root:not(.dark) { --background: oklch(1 0 0); - --foreground: oklch(0.141 0.005 285.823); + --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); - --card-foreground: oklch(0.141 0.005 285.823); + --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.141 0.005 285.823); - --primary: oklch(0.488 0.243 264.376); - --primary-foreground: oklch(0.97 0.014 254.604); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.51 0.16 262.61); + --primary-foreground: oklch(0.985 0 0); --secondary: oklch(0.967 0.001 286.375); --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.967 0.001 286.375); - --muted-foreground: oklch(0.552 0.016 285.938); - --accent: oklch(0.967 0.001 286.375); - --accent-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.92 0.004 286.32); - --input: oklch(0.92 0.004 286.32); - --ring: oklch(0.705 0.015 286.067); - --chart-1: oklch(0.871 0.006 286.286); - --chart-2: oklch(0.552 0.016 285.938); - --chart-3: oklch(0.442 0.017 285.786); - --chart-4: oklch(0.37 0.013 285.805); - --chart-5: oklch(0.274 0.006 286.033); - --radius: 0.625rem; + --warn: oklch(0.54 0.12 82.58); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-foreground: oklch(0.145 0 0); --sidebar-primary: oklch(0.546 0.245 262.881); --sidebar-primary-foreground: oklch(0.97 0.014 254.604); - --sidebar-accent: oklch(0.967 0.001 286.375); - --sidebar-accent-foreground: oklch(0.21 0.006 285.885); - --sidebar-border: oklch(0.92 0.004 286.32); - --sidebar-ring: oklch(0.705 0.015 286.067); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); --dev-color: oklch(0.6 0.18 50); --ui-color: oklch(0.65 0.18 170); @@ -49,37 +50,38 @@ } .dark { - --background: oklch(0.141 0.005 285.823); + --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); - --card: oklch(0.21 0.006 285.885); + --card: oklch(0.205 0 0); --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.21 0.006 285.885); + --popover: oklch(0.205 0 0); --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.424 0.199 265.638); - --primary-foreground: oklch(0.97 0.014 254.604); + --primary: oklch(0.6 0.18 262.65); + --primary-foreground: oklch(0.98 0 0); --secondary: oklch(0.274 0.006 286.033); --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.274 0.006 286.033); - --muted-foreground: oklch(0.705 0.015 286.067); - --accent: oklch(0.274 0.006 286.033); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.704 0.191 22.216); + --warn: oklch(0.76 0.18 81.84); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); - --ring: oklch(0.552 0.016 285.938); - --chart-1: oklch(0.871 0.006 286.286); - --chart-2: oklch(0.552 0.016 285.938); - --chart-3: oklch(0.442 0.017 285.786); - --chart-4: oklch(0.37 0.013 285.805); - --chart-5: oklch(0.274 0.006 286.033); - --sidebar: oklch(0.21 0.006 285.885); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --sidebar: oklch(0.205 0 0); --sidebar-foreground: oklch(0.985 0 0); --sidebar-primary: oklch(0.623 0.214 259.815); --sidebar-primary-foreground: oklch(0.97 0.014 254.604); - --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent: oklch(0.269 0 0); --sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.552 0.016 285.938); + --sidebar-ring: oklch(0.556 0 0); --dev-color: oklch(0.75 0.18 50); --ui-color: oklch(0.7 0.18 170); diff --git a/apps/docs/src/components/infinite-slider.tsx b/apps/docs/src/components/infinite-slider.tsx index 502b7b423..1964f6cd3 100644 --- a/apps/docs/src/components/infinite-slider.tsx +++ b/apps/docs/src/components/infinite-slider.tsx @@ -29,13 +29,12 @@ export function InfiniteSlider({ reverse = false, className, }: InfiniteSliderProps) { - // eslint-disable-next-line @eslint-react/no-unused-state const [currentSpeed, setCurrentSpeed] = React.useState(speed); const [ref, { width, height }] = useMeasure(); const translation = useMotionValue(0); - // eslint-disable-next-line @eslint-react/no-unused-state + const [isTransitioning, setIsTransitioning] = React.useState(false); - // eslint-disable-next-line @eslint-react/no-unused-state + const [key, setKey] = React.useState(0); React.useEffect(() => { diff --git a/apps/docs/src/examples/auto-form.tsx b/apps/docs/src/examples/auto-form.tsx index 6d9119d9e..3a8993858 100644 --- a/apps/docs/src/examples/auto-form.tsx +++ b/apps/docs/src/examples/auto-form.tsx @@ -5,6 +5,8 @@ import { AutoFormCheckbox } from "@vitnode/core/components/form/fields/checkbox" import { AutoFormInput } from "@vitnode/core/components/form/fields/input"; import { AutoFormSelect } from "@vitnode/core/components/form/fields/select"; import { AutoFormTextarea } from "@vitnode/core/components/form/fields/textarea"; +import { InputGroupAddon } from "@vitnode/core/components/ui/input-group"; +import { Search } from "lucide-react"; import { z } from "zod"; export default function AutoFormExample() { @@ -20,6 +22,7 @@ export default function AutoFormExample() { description: z .string() .min(10, "Description must be at least 10 characters"), + search: z.string().optional(), }); return ( @@ -45,6 +48,7 @@ export default function AutoFormExample() { id: "user_type", component: props => ( ), }, @@ -76,6 +79,17 @@ export default function AutoFormExample() { /> ), }, + { + id: "search", + component: props => ( + + + + + 12 results + + ), + }, ]} formSchema={formSchema} /> diff --git a/apps/docs/src/examples/button.tsx b/apps/docs/src/examples/button.tsx index b3b87f56c..be3b1d502 100644 --- a/apps/docs/src/examples/button.tsx +++ b/apps/docs/src/examples/button.tsx @@ -38,7 +38,7 @@ export default function ButtonExample() { aria-label="Delete" isLoading={isLoading} size="icon" - variant="destructiveGhost" + variant="destructive" > diff --git a/apps/docs/src/examples/input-group.tsx b/apps/docs/src/examples/input-group.tsx new file mode 100644 index 000000000..016d24696 --- /dev/null +++ b/apps/docs/src/examples/input-group.tsx @@ -0,0 +1,35 @@ +import { + InputGroup, + InputGroupAddon, + InputGroupInput, + InputGroupText, + InputGroupTextarea, +} from "@vitnode/core/components/ui/input-group"; +import { Search } from "lucide-react"; + +export default function InputGroupDemo() { + return ( +
+ + + + + + 12 results + + + + + + + 0 of 500 characters + + + +
+ ); +} diff --git a/apps/docs/src/examples/radio-group.tsx b/apps/docs/src/examples/radio-group.tsx index 265668984..bffa2feb9 100644 --- a/apps/docs/src/examples/radio-group.tsx +++ b/apps/docs/src/examples/radio-group.tsx @@ -6,7 +6,8 @@ import { z } from "zod"; export default function RadioGroupExample() { const formSchema = z.object({ - options: z.enum(["option1", "option2", "option3"]), + options: z.enum(["option1", "option2", "option3"]).default("option1"), + options_block: z.enum(["option1", "option2", "option3"]).default("option1"), }); return ( @@ -16,12 +17,14 @@ export default function RadioGroupExample() { id: "options", component: props => ( + ), + }, + { + id: "options_block", + component: props => ( + ), }, diff --git a/apps/docs/src/examples/select.tsx b/apps/docs/src/examples/select.tsx index 683375d08..aa82153f0 100644 --- a/apps/docs/src/examples/select.tsx +++ b/apps/docs/src/examples/select.tsx @@ -16,6 +16,7 @@ export default function SelectExample() { id: "options", component: props => ( ), }, diff --git a/apps/docs/src/locales/@vitnode/core/en.json b/apps/docs/src/locales/@vitnode/core/en.json index 5d2f42ed1..8fa1fee6c 100644 --- a/apps/docs/src/locales/@vitnode/core/en.json +++ b/apps/docs/src/locales/@vitnode/core/en.json @@ -1,6 +1,15 @@ { "core": { "global": { + "close": "Close", + "confirm": "Confirm", + "previous": "Previous", + "next": "Next", + "current_page": "Current page", + "go_to_page": "Go to page", + "previous_page": "Previous page", + "next_page": "Next page", + "remove": "Remove", "editor": { "undo": "Undo", "redo": "Redo", diff --git a/apps/docs/src/vitnode.config.ts b/apps/docs/src/vitnode.config.ts index ca8d7836d..708f35f2f 100644 --- a/apps/docs/src/vitnode.config.ts +++ b/apps/docs/src/vitnode.config.ts @@ -8,7 +8,7 @@ export const vitNodeConfig = buildConfig({ shortTitle: "VitNode", }, plugins: [blogPlugin()], - debug: true, + debug: false, i18n: { locales: [ { diff --git a/package.json b/package.json index 017477e1f..f50be9554 100644 --- a/package.json +++ b/package.json @@ -17,19 +17,19 @@ "test:e2e": "turbo test:e2e" }, "devDependencies": { - "@types/node": "^25.6.0", + "@types/node": "^25.9.0", "@vitnode/config": "workspace:*", "prettier": "^3.8.3", "prettier-plugin-tailwindcss": "^0.8.0", - "tsx": "^4.21.0", - "turbo": "^2.9.6", + "tsx": "^4.22.3", + "turbo": "^2.9.14", "typescript": "^6.0.3", - "zod": "^4.4.1" + "zod": "^4.4.3" }, "engines": { "node": ">=22" }, - "packageManager": "pnpm@10.18.3", + "packageManager": "pnpm@11.1.3", "workspaces": [ "apps/*", "packages/*", diff --git a/packages/config/package.json b/packages/config/package.json index 370f82979..b8272ec3b 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -53,7 +53,7 @@ "eslint-plugin-perfectionist": "^5.9.0", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-react-you-might-not-need-an-effect": "^0.9.3", + "eslint-plugin-react-you-might-not-need-an-effect": "^0.10.1", "prettier-plugin-tailwindcss": "^0.8.0", "typescript-eslint": "^8.59.1" } diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/global.css b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/global.css index 734f04a10..5a1390c82 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/global.css +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/global.css @@ -1,77 +1,79 @@ @import "tailwindcss"; @import "@vitnode/core/styles/tiptap.css"; @import "tw-animate-css"; +@import "shadcn/tailwind.css"; @source "../../node_modules/@vitnode/core/dist/src/components"; @source "../../node_modules/@vitnode/core/dist/src/views"; :root:not(.dark) { - --background: oklch(0.96 0.01 250); - --foreground: oklch(0.18 0.01 250); + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); - --card-foreground: oklch(0.22 0.01 250); + --card-foreground: oklch(0.145 0 0); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.22 0.01 250); + --popover-foreground: oklch(0.145 0 0); --primary: oklch(0.51 0.16 262.61); --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.94 0.02 254.94); - --secondary-foreground: oklch(0.25 0.01 250); - --muted: oklch(0.95 0.01 250); - --muted-foreground: oklch(0.53 0.01 250); - --accent: oklch(0.92 0.01 250); - --accent-foreground: oklch(0.25 0.01 250); - --destructive: oklch(0.6 0.2 24.45); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); --warn: oklch(0.54 0.12 82.58); - --border: oklch(0.9 0.01 250); - --input: oklch(0.9 0.01 250); - --ring: oklch(0.7 0.13 250); - --chart-1: oklch(0.65 0.13 250); - --chart-2: oklch(0.6 0.11 260); - --chart-3: oklch(0.45 0.09 250); - --chart-4: oklch(0.8 0.13 250); - --chart-5: oklch(0.75 0.13 250); - --sidebar: var(--card); - --sidebar-foreground: oklch(0.22 0.01 250); - --sidebar-primary: var(--primary); - --sidebar-primary-foreground: var(--primary-foreground); - --sidebar-accent: oklch(0.97 0.01 250); - --sidebar-accent-foreground: oklch(0.25 0.01 250); - --sidebar-border: oklch(0.91 0.01 250); - --sidebar-ring: oklch(0.7 0.13 250); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.546 0.245 262.881); + --sidebar-primary-foreground: oklch(0.97 0.014 254.604); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); } .dark { - --background: oklch(0.16 0.01 250); - --foreground: oklch(0.96 0.01 250); - --card: oklch(0.2 0.01 250); - --card-foreground: oklch(0.96 0.01 250); - --popover: oklch(0.22 0.01 250); - --popover-foreground: oklch(0.96 0.01 250); + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); --primary: oklch(0.6 0.18 262.65); --primary-foreground: oklch(0.98 0 0); - --secondary: oklch(0.2 0.01 250); - --secondary-foreground: oklch(0.96 0.01 250); - --muted: oklch(0.24 0.01 250); - --muted-foreground: oklch(0.7 0.01 250); - --accent: oklch(0.28 0.01 250); - --destructive: oklch(0.62 0.2 25.35); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); --warn: oklch(0.76 0.18 81.84); - --border: oklch(0.3 0.01 250); - --input: oklch(0.3 0.01 250); - --ring: oklch(0.54 0.13 250); - --chart-1: oklch(0.45 0.09 250); - --chart-2: oklch(0.6 0.11 260); - --chart-3: oklch(0.75 0.13 250); - --chart-4: oklch(0.8 0.13 250); - --chart-5: oklch(0.65 0.13 250); - --sidebar: var(--card); - --sidebar-foreground: oklch(0.96 0.01 250); - --sidebar-primary: var(--primary); - --sidebar-primary-foreground: var(--primary-foreground); - --sidebar-accent: oklch(0.23 0.01 250); - --sidebar-accent-foreground: oklch(0.96 0.01 250); - --sidebar-border: oklch(0.26 0.01 250); - --sidebar-ring: oklch(0.54 0.13 250); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.87 0 0); + --chart-2: oklch(0.556 0 0); + --chart-3: oklch(0.439 0 0); + --chart-4: oklch(0.371 0 0); + --chart-5: oklch(0.269 0 0); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.623 0.214 259.815); + --sidebar-primary-foreground: oklch(0.97 0.014 254.604); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); } :root { diff --git a/packages/create-vitnode-app/package.json b/packages/create-vitnode-app/package.json index 51d80f4c1..77890fe51 100644 --- a/packages/create-vitnode-app/package.json +++ b/packages/create-vitnode-app/package.json @@ -39,7 +39,7 @@ "commander": "^14.0.3", "ora": "^9.4.0", "picocolors": "^1.1.1", - "validate-npm-package-name": "^7.0.2" + "validate-npm-package-name": "^8.0.0" }, "devDependencies": { "@types/node": "^25.6.0", diff --git a/packages/create-vitnode-app/src/create/create-package-json.ts b/packages/create-vitnode-app/src/create/create-package-json.ts index 159da5622..35c21330c 100644 --- a/packages/create-vitnode-app/src/create/create-package-json.ts +++ b/packages/create-vitnode-app/src/create/create-package-json.ts @@ -131,6 +131,7 @@ const apiDeps = { react: versionsPackageJson.react, "react-dom": versionsPackageJson.reactDom, "use-intl": versionsPackageJson.useIntl, + shadcn: versionsPackageJson.shadcnUi, zod: versionsPackageJson.zod, }; diff --git a/packages/create-vitnode-app/src/create/package-versions.ts b/packages/create-vitnode-app/src/create/package-versions.ts index b80e164ff..d3d2991dc 100644 --- a/packages/create-vitnode-app/src/create/package-versions.ts +++ b/packages/create-vitnode-app/src/create/package-versions.ts @@ -6,7 +6,7 @@ export const versionsPackageJson = { turbo: "^2.9", typescript: "^6.0", - tsx: "^4.21", + tsx: "^4", tscAlias: "^1.8.16", eslint: "^10", prettier: "^3.8.3", @@ -32,7 +32,7 @@ export const versionsPackageJson = { hono: "^4.12", honoZodOpenapi: "^1.3", - honoZodValidator: "^0.7.6", + honoZodValidator: "^0.8", reactEmail: "^6.0", reactEmailComponents: "^1.0", zod: "^4.4", @@ -43,4 +43,5 @@ export const versionsPackageJson = { swcCli: "^0.8.1", swcCore: "^1.15", concurrently: "^9.2.1", + shadcnUi: "^4", }; diff --git a/packages/vitnode/components.json b/packages/vitnode/components.json index 797c749cb..68fa65fe9 100644 --- a/packages/vitnode/components.json +++ b/packages/vitnode/components.json @@ -1,6 +1,6 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "base-nova", + "style": "radix-vega", "rsc": true, "tsx": true, "tailwind": { diff --git a/packages/vitnode/package.json b/packages/vitnode/package.json index 4762347a5..e63cc9a1c 100644 --- a/packages/vitnode/package.json +++ b/packages/vitnode/package.json @@ -37,45 +37,45 @@ "zod": "^4.x.x" }, "devDependencies": { - "@hono/zod-openapi": "^1.3.0", - "@hono/zod-validator": "^0.7.6", + "@hono/zod-openapi": "^1.4.0", + "@hono/zod-validator": "^0.8.0", "@hookform/resolvers": "^5.2.2", "@react-email/components": "^1.0.12", "@swc/cli": "^0.8.1", - "@swc/core": "^1.15.32", + "@swc/core": "^1.15.33", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@types/node": "^25.6.0", + "@types/node": "^25.9.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "@vitest/coverage-v8": "^4.1.5", + "@vitejs/plugin-react": "^6.0.2", + "@vitest/coverage-v8": "^4.1.6", "@vitnode/config": "workspace:*", "chokidar": "^5.0.0", "concurrently": "^9.2.1", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", - "eslint": "^10.2.1", - "hono": "^4.12.16", - "jiti": "^2.6.1", + "eslint": "^10.4.0", + "hono": "^4.12.21", + "jiti": "^2.7.0", "jsdom": "^29.1.1", - "lucide-react": "^1.14.0", - "next": "^16.2.4", - "next-intl": "^4.11.0", - "react": "^19.2.5", - "react-dom": "^19.2.5", - "react-email": "^6.0.5", - "react-hook-form": "^7.74.0", + "lucide-react": "^1.16.0", + "next": "^16.2.6", + "next-intl": "^4.12.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "react-email": "^6.1.5", + "react-hook-form": "^7.76.0", "sonner": "^2.0.7", - "tailwindcss": "^4.2.4", - "tsc-alias": "^1.8.16", + "tailwindcss": "^4.3.0", + "tsc-alias": "^1.8.17", "tsup": "^8.5.1", - "tsx": "^4.21.0", + "tsx": "^4.22.3", "tw-animate-css": "^1.4.0", "typescript": "^6.0.3", - "vite": "^8.0.10", - "vitest": "^4.1.5", - "zod": "^4.4.1" + "vite": "^8.0.13", + "vitest": "^4.1.6", + "zod": "^4.4.3" }, "bin": { "vitnode": "./dist/scripts/scripts.js" @@ -109,31 +109,38 @@ }, "type": "module", "dependencies": { + "@base-ui/react": "^1.5.0", "@bprogress/next": "^3.2.12", "@dnd-kit/core": "^6.3.1", "@hono/swagger-ui": "^0.6.1", "@react-email/preview-server": "^5.2.10", - "@tanstack/react-query": "^5.100.6", - "@tiptap/extension-text-align": "^3.22.5", - "@tiptap/pm": "^3.22.5", - "@tiptap/react": "^3.22.5", - "@tiptap/starter-kit": "^3.22.5", + "@tanstack/react-query": "^5.100.11", + "@tiptap/extension-text-align": "^3.23.5", + "@tiptap/pm": "^3.23.5", + "@tiptap/react": "^3.23.5", + "@tiptap/starter-kit": "^3.23.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "cron-parser": "^5.5.0", + "date-fns": "^4.2.1", "dotenv": "^17.4.2", + "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", - "motion": "^12.38.0", + "motion": "^12.39.0", "next-themes": "^0.4.6", "postgres": "^3.4.9", "radix-ui": "^1.4.3", - "rate-limiter-flexible": "^11.0.1", - "react-scan": "^0.5.3", + "rate-limiter-flexible": "^11.1.0", + "react-day-picker": "^10.0.1", + "react-resizable-panels": "^4.11.1", + "react-scan": "^0.5.6", + "recharts": "3.8.1", "server-only": "^0.0.1", - "tailwind-merge": "^3.5.0", + "shadcn": "^4.7.0", + "tailwind-merge": "^3.6.0", "use-debounce": "^10.1.1", - "use-intl": "^4.11.0", + "use-intl": "^4.12.0", "vaul": "^1.1.2" } } diff --git a/packages/vitnode/src/components/date-format.tsx b/packages/vitnode/src/components/date-format.tsx index 5825d591c..8886de441 100644 --- a/packages/vitnode/src/components/date-format.tsx +++ b/packages/vitnode/src/components/date-format.tsx @@ -2,12 +2,7 @@ import { useFormatter, useNow } from "next-intl"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "./ui/tooltip"; +import { TooltipWithContent } from "./ui/tooltip"; export const DateFormat = ({ date, @@ -44,15 +39,9 @@ export const DateFormat = ({ // When date is < 7 days if (now.getTime() - dateToFormat.getTime() < 604800000) { return ( - - - - {format.relativeTime(dateToFormat, now)} - - - {fullDate} - - + + {format.relativeTime(dateToFormat, now)} + ); } diff --git a/packages/vitnode/src/components/form/auto-form.tsx b/packages/vitnode/src/components/form/auto-form.tsx index 06bd2c57b..0c085bc42 100644 --- a/packages/vitnode/src/components/form/auto-form.tsx +++ b/packages/vitnode/src/components/form/auto-form.tsx @@ -22,6 +22,7 @@ import { } from "../../lib/helpers/auto-form"; import { Button } from "../ui/button"; import { DialogClose, DialogFooter, useDialog } from "../ui/dialog"; +import { Field } from "../ui/field"; import { Form, FormField } from "../ui/form"; type ItemAutoFormProps< @@ -161,9 +162,12 @@ export function AutoForm< { + render={({ field, fieldState }) => { return ( - <> + {item.component({ field, description: @@ -193,7 +197,7 @@ export function AutoForm< : undefined, }, })} - + ); }} /> diff --git a/packages/vitnode/src/components/form/common/label.tsx b/packages/vitnode/src/components/form/common/label.tsx index 7a37e5d6e..5d24e39b4 100644 --- a/packages/vitnode/src/components/form/common/label.tsx +++ b/packages/vitnode/src/components/form/common/label.tsx @@ -1,26 +1,38 @@ -import { FormLabel } from "@/components/ui/form"; +import { useTranslations } from "next-intl"; + +import { FieldLabel } from "@/components/ui/field"; +import { useFormField } from "@/components/ui/form"; import { cn } from "@/lib/utils"; export const AutoFormLabel = ({ children, labelRight, className, + isOptional, ...props -}: React.ComponentProps & { +}: React.ComponentProps & { + isOptional?: boolean; labelRight?: React.ReactNode; }) => { + const t = useTranslations("core.global"); + const { formItemId } = useFormField(); + return ( - {children} + {isOptional && ( + {t("optional")} + )} {labelRight && {labelRight}} - + ); }; diff --git a/packages/vitnode/src/components/form/fields/checkbox.tsx b/packages/vitnode/src/components/form/fields/checkbox.tsx index 0c96090dd..a48788169 100644 --- a/packages/vitnode/src/components/form/fields/checkbox.tsx +++ b/packages/vitnode/src/components/form/fields/checkbox.tsx @@ -1,7 +1,7 @@ import type { ItemAutoFormComponentProps } from "../auto-form"; import { Checkbox } from "../../ui/checkbox"; -import { FormControl, FormItem, FormMessage } from "../../ui/form"; +import { FormControl, FormMessage } from "../../ui/form"; import { AutoFormDesc } from "../common/desc"; import { AutoFormLabel } from "../common/label"; @@ -15,7 +15,7 @@ export const AutoFormCheckbox = ({ }: ItemAutoFormComponentProps & Omit, "checked">) => { return ( - +
)} -
+ ); }; diff --git a/packages/vitnode/src/components/form/fields/combobox-async.tsx b/packages/vitnode/src/components/form/fields/combobox-async.tsx index 0a94bfdc4..8259ba8ff 100644 --- a/packages/vitnode/src/components/form/fields/combobox-async.tsx +++ b/packages/vitnode/src/components/form/fields/combobox-async.tsx @@ -13,7 +13,7 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; -import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; +import { FormControl, FormMessage } from "@/components/ui/form"; import { Popover, PopoverContent, @@ -110,7 +110,7 @@ export const AutoFormComboboxAsync = ({ }; return ( - +
{label && ( {label} @@ -153,6 +153,6 @@ export const AutoFormComboboxAsync = ({ {description && {description}} - +
); }; diff --git a/packages/vitnode/src/components/form/fields/combobox.tsx b/packages/vitnode/src/components/form/fields/combobox.tsx index 7e1e70094..80f61c17e 100644 --- a/packages/vitnode/src/components/form/fields/combobox.tsx +++ b/packages/vitnode/src/components/form/fields/combobox.tsx @@ -12,7 +12,7 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; -import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; +import { FormControl, FormMessage } from "@/components/ui/form"; import { Popover, PopoverContent, @@ -54,7 +54,7 @@ export const AutoFormCombobox = ({ }); return ( - +
{label && ( {label} @@ -116,6 +116,6 @@ export const AutoFormCombobox = ({ {description && {description}} - +
); }; diff --git a/packages/vitnode/src/components/form/fields/input.tsx b/packages/vitnode/src/components/form/fields/input.tsx index bb8554754..575a6b31e 100644 --- a/packages/vitnode/src/components/form/fields/input.tsx +++ b/packages/vitnode/src/components/form/fields/input.tsx @@ -1,6 +1,8 @@ +import { InputGroup, InputGroupInput } from "@/components/ui/input-group"; + import type { ItemAutoFormComponentProps } from "../auto-form"; -import { FormControl, FormItem, FormMessage } from "../../ui/form"; +import { FormControl, FormMessage } from "../../ui/form"; import { Input } from "../../ui/input"; import { AutoFormDesc } from "../common/desc"; import { AutoFormLabel } from "../common/label"; @@ -11,38 +13,65 @@ export const AutoFormInput = ({ description, otherProps: { isOptional, maxLength, minLength, pattern, type }, field, + children, ...props }: ItemAutoFormComponentProps & Omit, "value">) => { return ( - + <> {label && ( {label} )} - - { - field.onBlur(); - props.onBlur?.(e); - }} - onChange={e => { - field.onChange(e); - props.onChange?.(e); - }} - pattern={pattern} - type={type ?? "text"} - value={field.value ?? ""} - {...props} - /> - + + {children ? ( + + + { + field.onBlur(); + props.onBlur?.(e); + }} + onChange={e => { + field.onChange(e); + props.onChange?.(e); + }} + pattern={pattern} + type={type ?? "text"} + value={field.value ?? ""} + {...props} + /> + + {children} + + ) : ( + + { + field.onBlur(); + props.onBlur?.(e); + }} + onChange={e => { + field.onChange(e); + props.onChange?.(e); + }} + pattern={pattern} + type={type ?? "text"} + value={field.value ?? ""} + {...props} + /> + + )} {description && {description}} - + ); }; diff --git a/packages/vitnode/src/components/form/fields/radio-group.tsx b/packages/vitnode/src/components/form/fields/radio-group.tsx index 1b5aa0994..5963bed3f 100644 --- a/packages/vitnode/src/components/form/fields/radio-group.tsx +++ b/packages/vitnode/src/components/form/fields/radio-group.tsx @@ -1,11 +1,13 @@ import type React from "react"; import { - FormControl, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; + Field, + FieldContent, + FieldDescription, + FieldLabel, + FieldTitle, +} from "@/components/ui/field"; +import { FormControl, FormMessage } from "@/components/ui/form"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import type { ItemAutoFormComponentProps } from "../auto-form"; @@ -13,6 +15,13 @@ import type { ItemAutoFormComponentProps } from "../auto-form"; import { AutoFormDesc } from "../common/desc"; import { AutoFormLabel } from "../common/label"; +interface ItemAutoFormRadioGroupLabelsProps { + description?: string; + disabled?: boolean; + label: string; + value: string; +} + export const AutoFormRadioGroup = ({ label, labelRight, @@ -20,27 +29,32 @@ export const AutoFormRadioGroup = ({ description, otherProps: { enum: enumValues = [], isOptional }, labels = [], + variant = "default", ...props }: ItemAutoFormComponentProps & Omit, "value"> & { - labels?: { label: string; value: string }[]; + labels?: ItemAutoFormRadioGroupLabelsProps[]; + variant?: "blocks" | "default"; }) => { - const values: { label: string; value: string }[] = enumValues.map(value => { - const label = labels.find(l => l.value === value)?.label; + const values: ItemAutoFormRadioGroupLabelsProps[] = enumValues.map(value => { + const item = labels.find(l => l.value === value); return { value, - label: label ?? value, + label: item?.label ?? value, + description: item?.description, + disabled: item?.disabled, }; }); return ( - +
{label && ( {label} )} + {description && {description}} - {values.map(({ value, label }) => ( - - - - - {label} - - ))} + {values.map(({ value, label, description, disabled }) => + variant === "default" ? ( + + + + + + + {label} + + {description && ( + {description} + )} + + + ) : ( + + + + {label} + {description && ( + {description} + )} + + + + + + + ), + )} - {description && {description}} - +
); }; diff --git a/packages/vitnode/src/components/form/fields/select.tsx b/packages/vitnode/src/components/form/fields/select.tsx index 6597244fe..5063ed29e 100644 --- a/packages/vitnode/src/components/form/fields/select.tsx +++ b/packages/vitnode/src/components/form/fields/select.tsx @@ -2,7 +2,7 @@ import type React from "react"; import { useTranslations } from "next-intl"; -import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; +import { FormControl, FormMessage } from "@/components/ui/form"; import { Select, SelectContent, @@ -45,7 +45,7 @@ export const AutoFormSelect = ({ t("select_option"); return ( - + <> {label && ( {label} @@ -83,6 +83,6 @@ export const AutoFormSelect = ({ {description && {description}} - + ); }; diff --git a/packages/vitnode/src/components/form/fields/switch.tsx b/packages/vitnode/src/components/form/fields/switch.tsx index b9a901d7a..1fdf04509 100644 --- a/packages/vitnode/src/components/form/fields/switch.tsx +++ b/packages/vitnode/src/components/form/fields/switch.tsx @@ -1,4 +1,4 @@ -import { FormControl, FormItem } from "@/components/ui/form"; +import { FormControl } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; import type { ItemAutoFormComponentProps } from "../auto-form"; @@ -16,7 +16,7 @@ export const AutoFormSwitch = ({ }: ItemAutoFormComponentProps & Omit, "checked">) => { return ( - +
{(label ?? description) && (
{label && ( @@ -42,6 +42,6 @@ export const AutoFormSwitch = ({ {...props} /> - +
); }; diff --git a/packages/vitnode/src/components/form/fields/textarea.tsx b/packages/vitnode/src/components/form/fields/textarea.tsx index 8bfb8df63..19f6f494a 100644 --- a/packages/vitnode/src/components/form/fields/textarea.tsx +++ b/packages/vitnode/src/components/form/fields/textarea.tsx @@ -1,6 +1,7 @@ import type React from "react"; -import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; +import { FormControl, FormMessage } from "@/components/ui/form"; +import { InputGroup, InputGroupTextarea } from "@/components/ui/input-group"; import { Textarea } from "@/components/ui/textarea"; import type { ItemAutoFormComponentProps } from "../auto-form"; @@ -14,6 +15,7 @@ export const AutoFormTextarea = ({ labelRight, otherProps: { isOptional, maxLength, minLength }, field, + children, ...props }: ItemAutoFormComponentProps & Omit, "value"> & { @@ -21,32 +23,54 @@ export const AutoFormTextarea = ({ label?: React.ReactNode; }) => { return ( - + <> {label && ( {label} )} - -