├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── .vscode ├── extensions.json └── launch.json ├── README.md ├── package-lock.json ├── package.json ├── packages ├── astro-forms │ ├── Form.astro │ ├── README.md │ ├── index.mjs │ ├── package.json │ ├── themes │ │ ├── default │ │ │ ├── ArrayInput.astro │ │ │ ├── BooleanInput.astro │ │ │ ├── DateInput.astro │ │ │ ├── GenericInput.astro │ │ │ ├── NumberInput.astro │ │ │ ├── StringInput.astro │ │ │ ├── Submit.astro │ │ │ └── index.ts │ │ ├── index.ts │ │ └── tailwind │ │ │ ├── ArrayInput.astro │ │ │ ├── BooleanInput.astro │ │ │ ├── DateInput.astro │ │ │ ├── GenericInput.astro │ │ │ ├── InputLayout.astro │ │ │ ├── NumberInput.astro │ │ │ ├── README.md │ │ │ ├── StringInput.astro │ │ │ ├── Submit.astro │ │ │ ├── index.ts │ │ │ └── tailwind.config.cjs │ ├── types.ts │ └── util.ts └── demo │ ├── astro.config.mjs │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── pages │ │ └── index.astro │ └── util │ │ └── schema.ts │ └── tailwind.config.cjs ├── tsconfig.json └── yarn.lock /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | # Setup .npmrc file to publish to npm 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: '16.x' 14 | registry-url: 'https://registry.npmjs.org' 15 | # Defaults to the user or organization that owns the workflow file 16 | scope: '@octocat' 17 | - run: yarn 18 | - run: yarn upload 19 | env: 20 | NODE_AUTH_TOKEN: ${{ secrets.npm_token }} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # dependencies 5 | node_modules/ 6 | 7 | # logs 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | pnpm-debug.log* 12 | 13 | 14 | # environment variables 15 | .env 16 | .env.production 17 | 18 | # macOS-specific files 19 | .DS_Store 20 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Expose Astro dependencies for `pnpm` users 2 | shamefully-hoist=true 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 🚧 **Under Development** 🚧 2 | 3 | This component is under construction! It does work, however, bugs may be present. 4 | 5 | Pull requests are appreciated! 6 | 7 | # Astro Forms 8 | 9 | A Dynamic form component for Astro. 10 | 11 | Uses a JSON schema to generate a form. 12 | 13 | ## Themes 14 | 15 | There are currently two themes available: 16 | 17 | - `default` 18 | - `tailwind` [(View README)](/packages/astro-forms/themes/tailwind/README.md) 19 | 20 | ## Usage 21 | 22 | ### Installation 23 | 24 | ```bash 25 | yarn add astro-forms 26 | ``` 27 | 28 | or 29 | 30 | ```bash 31 | npm install astro-forms 32 | ``` 33 | 34 | ### Setup 35 | 36 | ```astro 37 | --- 38 | import { Form } from "astro-forms"; 39 | const schema = { 40 | type: "object", 41 | properties: { 42 | name: { 43 | type: "string", 44 | title: "Name", 45 | }, 46 | email: { 47 | type: "string", 48 | title: "Email", 49 | }, 50 | password: { 51 | type: "string", 52 | title: "Password", 53 | }, 54 | submit: { 55 | type: "submit", 56 | title: "Submit", 57 | }, 58 | }, 59 | }; // Or whatever you want it to be, must be an object. 60 | --- 61 | 62 |
63 | ``` 64 | 65 | ## Schema 66 | 67 | The JSON schema for this form component has a single type that can repeat itself. 68 | 69 | ```ts 70 | export type InputType = 71 | | "string" 72 | | "email" 73 | | "password" 74 | | "search" 75 | | "url" 76 | | "number" 77 | | "boolean" 78 | | "array" 79 | | "object" 80 | | "date" 81 | | "submit"; 82 | 83 | export interface FormSchema { 84 | type: InputType; 85 | title: string; 86 | subtitle?: string; 87 | properties: { 88 | [key: string]: FormSchema; 89 | }; 90 | items?: FormSchema; 91 | } 92 | ``` 93 | 94 | These types may be updated and will be marked as a breaking change if done so. 95 | 96 | Schema example: 97 | 98 | ```json 99 | { 100 | "type": "object", 101 | "title": "Form", 102 | "properties": { 103 | "name": { 104 | "type": "string", 105 | "title": "Name", 106 | "subtitle": "Alphanumeric characters only!!!!" 107 | }, 108 | "email": { 109 | "type": "string", 110 | "title": "Email", 111 | "subtitle": "Don't worry, we'll spam you every day!" 112 | }, 113 | "password": { 114 | "type": "password", 115 | "title": "Password", 116 | "subtitle": "We didn't invest much into security, choose wisely." 117 | }, 118 | "personalInfo": { 119 | "type": "object", 120 | "title": "Personal Info", 121 | "properties": { 122 | "age": { 123 | "type": "number", 124 | "title": "Age", 125 | "subtitle": "How old are you?" 126 | }, 127 | "birthday": { 128 | "type": "date", 129 | "title": "Birthday", 130 | "subtitle": "So we can send you a cake, duh." 131 | }, 132 | "misc": { 133 | "type": "object", 134 | "title": "Misc. Stuff", 135 | "properties": { 136 | "isScary": { 137 | "type": "boolean", 138 | "title": "Sus?", 139 | "subtitle": "Are you known by the FBI, CIA, or any governmental body?" 140 | } 141 | } 142 | }, 143 | "acceptsTerms": { 144 | "type": "boolean", 145 | "title": "Accept Terms of Service", 146 | "subtitle": "We're not responsible for your actions." 147 | } 148 | } 149 | }, 150 | "submit": { 151 | "type": "submit", 152 | "title": "Submit" 153 | } 154 | } 155 | } 156 | ``` 157 | 158 | Input names are tokenized and have a `.` delimiter, so they can be used with HTML forms with ease. 159 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "astro-forms-monorepo", 4 | "workspaces": [ 5 | "packages/*" 6 | ], 7 | "scripts": { 8 | "dev": "yarn workspace demo dev", 9 | "upload": "yarn workspace astro-forms publish" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/astro-forms/Form.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { GenericTheme } from "./themes"; 3 | import { Theme, FormSchema } from "./types"; 4 | import { flatten } from "./util"; 5 | 6 | const { schema, action, method, classes, theme, data, redirect } = Astro.props as { 7 | schema: FormSchema; 8 | action: string; 9 | method: "post" | "get"; 10 | classes: string; 11 | theme: Theme; 12 | data: Record; 13 | redirect: string; 14 | }; 15 | 16 | const components: Theme = { 17 | ...GenericTheme, 18 | ...theme, 19 | }; 20 | 21 | if (schema.type !== "object") { 22 | throw new Error("Initial form schema must be an object"); 23 | } 24 | 25 | const flattenedData = flatten(data); 26 | --- 27 | 28 | 29 | {redirect && ( 30 | 31 | )} 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/astro-forms/README.md: -------------------------------------------------------------------------------- 1 | 🚧 **Under Development** 🚧 2 | 3 | This component is under construction! It does work, however, bugs may be present. 4 | 5 | Pull requests are appreciated! 6 | 7 | # Astro Forms 8 | 9 | A Dynamic form component for Astro. 10 | 11 | Uses a JSON schema to generate a form. 12 | 13 | ## Themes 14 | 15 | There are currently two themes available: 16 | 17 | - `default` 18 | - `tailwind` [(View README)](/packages/astro-forms/themes/tailwind/README.md) 19 | 20 | ## Usage 21 | 22 | ### Installation 23 | 24 | ```bash 25 | yarn add astro-forms 26 | ``` 27 | 28 | or 29 | 30 | ```bash 31 | npm install astro-forms 32 | ``` 33 | 34 | ### Setup 35 | 36 | ```astro 37 | --- 38 | import { Form } from "astro-forms"; 39 | const schema = { 40 | type: "object", 41 | properties: { 42 | name: { 43 | type: "string", 44 | title: "Name", 45 | }, 46 | email: { 47 | type: "string", 48 | title: "Email", 49 | }, 50 | password: { 51 | type: "string", 52 | title: "Password", 53 | }, 54 | submit: { 55 | type: "submit", 56 | title: "Submit", 57 | }, 58 | }, 59 | }; // Or whatever you want it to be, must be an object. 60 | --- 61 | 62 |
63 | ``` 64 | 65 | ## Schema 66 | 67 | The JSON schema for this form component has a single type that can repeat itself. 68 | 69 | ```ts 70 | export type InputType = 71 | | "string" 72 | | "email" 73 | | "password" 74 | | "search" 75 | | "url" 76 | | "number" 77 | | "boolean" 78 | | "array" 79 | | "object" 80 | | "date" 81 | | "submit"; 82 | 83 | export interface FormSchema { 84 | type: InputType; 85 | title: string; 86 | subtitle?: string; 87 | properties: { 88 | [key: string]: FormSchema; 89 | }; 90 | items?: FormSchema; 91 | } 92 | ``` 93 | 94 | These types may be updated and will be marked as a breaking change if done so. 95 | 96 | Schema example: 97 | 98 | ```json 99 | { 100 | "type": "object", 101 | "title": "Form", 102 | "properties": { 103 | "name": { 104 | "type": "string", 105 | "title": "Name", 106 | "subtitle": "Alphanumeric characters only!!!!" 107 | }, 108 | "email": { 109 | "type": "string", 110 | "title": "Email", 111 | "subtitle": "Don't worry, we'll spam you every day!" 112 | }, 113 | "password": { 114 | "type": "password", 115 | "title": "Password", 116 | "subtitle": "We didn't invest much into security, choose wisely." 117 | }, 118 | "personalInfo": { 119 | "type": "object", 120 | "title": "Personal Info", 121 | "properties": { 122 | "age": { 123 | "type": "number", 124 | "title": "Age", 125 | "subtitle": "How old are you?" 126 | }, 127 | "birthday": { 128 | "type": "date", 129 | "title": "Birthday", 130 | "subtitle": "So we can send you a cake, duh." 131 | }, 132 | "misc": { 133 | "type": "object", 134 | "title": "Misc. Stuff", 135 | "properties": { 136 | "isScary": { 137 | "type": "boolean", 138 | "title": "Sus?", 139 | "subtitle": "Are you known by the FBI, CIA, or any governmental body?" 140 | } 141 | } 142 | }, 143 | "acceptsTerms": { 144 | "type": "boolean", 145 | "title": "Accept Terms of Service", 146 | "subtitle": "We're not responsible for your actions." 147 | } 148 | } 149 | }, 150 | "submit": { 151 | "type": "submit", 152 | "title": "Submit" 153 | } 154 | } 155 | } 156 | ``` 157 | 158 | Input names are tokenized and have a `.` delimiter, so they can be used with HTML forms with ease. 159 | -------------------------------------------------------------------------------- /packages/astro-forms/index.mjs: -------------------------------------------------------------------------------- 1 | export { default as Form } from "./Form.astro"; 2 | -------------------------------------------------------------------------------- /packages/astro-forms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-forms", 3 | "version": "1.0.11", 4 | "description": "Forms in Astro, made easy.", 5 | "main": "index.mjs", 6 | "homepage": "https://github.com/jackmerrill/astroforms#readme", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/jackmerrill/astroforms.git", 10 | "directory": "packages/astro-forms" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/jackmerrill/astroforms/issues" 14 | }, 15 | "author": "Jack Merrill", 16 | "license": "MPL-2.0", 17 | "keywords": [ 18 | "astro", 19 | "forms", 20 | "jsonform" 21 | ], 22 | "dependencies": { 23 | "@tailwindcss/forms": "^0.5.2", 24 | "tailwindcss": "^3.1.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/ArrayInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FormSchema } from "../../types"; 3 | import GenericInput from "./GenericInput.astro"; 4 | 5 | const { title, items } = Astro.props as { title: string; items: FormSchema }; 6 | --- 7 | 8 | 12 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/BooleanInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title, checked } = Astro.props as { title: string; checked: boolean }; 3 | --- 4 | 5 | 9 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/DateInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title, name } = Astro.props as { title: string; name: string }; 3 | --- 4 | 5 | 9 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/GenericInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Theme, FormSchema } from "../../types"; 3 | import { GetInput } from "."; 4 | 5 | const { schema, schemaKey, components } = Astro.props as { 6 | schema: FormSchema; 7 | schemaKey: string; 8 | components: Theme; 9 | }; 10 | 11 | const Inputs = []; 12 | const InputArgs = []; 13 | 14 | if (!schema.properties) { 15 | throw new Error("No properties found in schema"); 16 | } 17 | 18 | Object.entries(schema.properties).forEach(([key, value]) => { 19 | let inputKey; 20 | 21 | if (schemaKey) { 22 | inputKey = `${schemaKey}.${key}`; 23 | } else { 24 | inputKey = key; 25 | } 26 | 27 | let FormInput; 28 | let FormArgs: Record = { title: value.title, name: inputKey }; 29 | 30 | FormInput = GetInput(value.type); 31 | FormArgs = { 32 | ...FormArgs, 33 | schema: value, 34 | schemaKey: key, 35 | items: value.items, 36 | components, 37 | }; 38 | 39 | Inputs.push(FormInput); 40 | 41 | InputArgs.push({ 42 | key, 43 | value, 44 | ...FormArgs, 45 | }); 46 | }); 47 | --- 48 | 49 |
50 |

{schema.title}

51 | {Inputs.map((Input, index) => )} 52 |
53 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/NumberInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title, name } = Astro.props as { title: string; name: string }; 3 | --- 4 | 5 | 9 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/StringInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title, name } = Astro.props as { title: string; name: string }; 3 | --- 4 | 5 | 9 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/Submit.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title } = Astro.props as { title: string }; 3 | --- 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/default/index.ts: -------------------------------------------------------------------------------- 1 | import { InputType } from "../../types"; 2 | import StringInput from "./StringInput.astro"; 3 | import BooleanInput from "./BooleanInput.astro"; 4 | import ArrayInput from "./ArrayInput.astro"; 5 | import GenericInput from "./GenericInput.astro"; 6 | import DateInput from "./DateInput.astro"; 7 | import NumberInput from "./NumberInput.astro"; 8 | import Submit from "./Submit.astro"; 9 | 10 | export function GetInput(type: InputType) { 11 | switch (type) { 12 | case "string": 13 | return StringInput; 14 | case "number": 15 | return NumberInput; 16 | case "date": 17 | return DateInput; 18 | case "boolean": 19 | return BooleanInput; 20 | case "array": 21 | return ArrayInput; 22 | case "object": 23 | return GenericInput; 24 | case "submit": 25 | return Submit; 26 | default: 27 | return StringInput; 28 | } 29 | } 30 | 31 | export default { 32 | String: StringInput, 33 | Boolean: BooleanInput, 34 | Array: ArrayInput, 35 | Generic: GenericInput, 36 | Date: DateInput, 37 | Number: NumberInput, 38 | Submit: Submit, 39 | }; 40 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/index.ts: -------------------------------------------------------------------------------- 1 | import GenericTheme from "./default"; 2 | 3 | export { GenericTheme }; 4 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/ArrayInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | // THIS IS VERY EXPERIMENTAL. 3 | // IT IS NOT RECOMMENDED TO USE THIS IN PRODUCTION. 4 | // YOU CURRENTLY CANNOT ADD NEW INPUTS. 5 | 6 | import { GetInput } from "."; 7 | import { FormSchema } from "../../types"; 8 | import InputLayout from "./InputLayout.astro"; 9 | 10 | const { schema, name, data } = Astro.props as { schema: FormSchema; name: string, data: any }; 11 | 12 | const Input = GetInput(schema.items.type); 13 | 14 | const arrayData = Object.entries(data).map(([key, value]) => { 15 | if (key.includes(name)) { 16 | return value; 17 | } 18 | }).filter(Boolean); 19 | --- 20 | 21 | 33 | 34 | 35 | 38 |
39 | {arrayData.map((value, index) => ( 40 | 41 | ))} 42 |
43 | 47 |
-------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/BooleanInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FormSchema } from "../../types"; 3 | import InputLayout from "./InputLayout.astro"; 4 | 5 | const { schema, name, value } = Astro.props as { schema: FormSchema; name: string, value: any }; 6 | --- 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/DateInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FormSchema } from "../../types"; 3 | import InputLayout from "./InputLayout.astro"; 4 | 5 | const { schema, name, value } = Astro.props as { schema: FormSchema; name: string, value: any }; 6 | --- 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/GenericInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Theme, FormSchema } from "../../types"; 3 | import { GetInput } from "."; 4 | 5 | const { schema, schemaKey, components, data } = Astro.props as { 6 | schema: FormSchema; 7 | schemaKey: string; 8 | components: Theme; 9 | data: Record; 10 | }; 11 | 12 | const Inputs = []; 13 | const InputArgs = []; 14 | 15 | if (!schema.properties) { 16 | if (schema.type !== 'array') { 17 | throw new Error("No properties found in schema"); 18 | } 19 | 20 | if (!schema.items) { 21 | throw new Error("No items found in array schema"); 22 | } 23 | 24 | Inputs.push(components.Array) 25 | InputArgs.push({ 26 | schema: schema.items, 27 | schemaKey: schemaKey, 28 | components, 29 | data 30 | }) 31 | } else { 32 | Object.entries(schema.properties).forEach(([key, value]) => { 33 | let inputKey; 34 | 35 | if (schemaKey) { 36 | inputKey = `${schemaKey}.${key}`; 37 | } else { 38 | inputKey = key; 39 | } 40 | 41 | let FormInput; 42 | let FormArgs: Record = { title: value.title, name: inputKey }; 43 | 44 | FormInput = GetInput(value.type); 45 | FormArgs = { 46 | ...FormArgs, 47 | schema: value, 48 | schemaKey: key, 49 | items: value.items, 50 | components, 51 | subtitle: value.subtitle, 52 | data, 53 | value: data[inputKey] 54 | }; 55 | 56 | Inputs.push(FormInput); 57 | 58 | InputArgs.push({ 59 | key, 60 | value, 61 | ...FormArgs, 62 | }); 63 | }); 64 | 65 | } 66 | 67 | --- 68 | 69 |
70 |

{schema.title}

71 | {Inputs.map((Input, index) => )} 72 |
-------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/InputLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FormSchema } from "../../types"; 3 | 4 | const { schema, name } = Astro.props as { schema: FormSchema; name: string }; 5 | --- 6 | 7 |
8 | 10 |
11 | 12 |
13 | {schema.subtitle && ( 14 |

15 | {schema.subtitle} 16 |

17 | )} 18 |
-------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/NumberInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FormSchema } from "../../types"; 3 | import InputLayout from "./InputLayout.astro"; 4 | 5 | const { schema, name, value } = Astro.props as { schema: FormSchema; name: string, value: any }; 6 | --- 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/README.md: -------------------------------------------------------------------------------- 1 | # Tailwind Theme 2 | 3 | Prerequisites: 4 | 5 | - [Tailwind CSS](https://tailwindcss.com) v3 or greater 6 | - [@tailwindcss/forms](https://github.com/tailwindlabs/tailwindcss-forms) plugin 7 | 8 | Usage: 9 | 10 | ```astro 11 | --- 12 | import tailwind from "astro-forms/themes/tailwind"; 13 | import Form from "../components/Form/Form.astro"; 14 | --- 15 | 16 | 17 | ``` 18 | 19 | ## Important 20 | 21 | Make sure your Tailwind config file has the following: 22 | 23 | ```js 24 | module.exports = { 25 | content: [ 26 | "./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}", 27 | "./node_modules/astro-forms/themes/tailwind/*.astro", 28 | ], 29 | theme: { 30 | extend: {}, 31 | }, 32 | plugins: [], 33 | }; 34 | ``` 35 | 36 | You can either add the `@tailwindcss/forms` plugin to your Tailwind config file, or you may import the Tailwind config from the package. 37 | 38 | ```js 39 | module.exports = { 40 | content: [ 41 | "./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}", 42 | "./node_modules/astro-forms/themes/tailwind/*.astro", 43 | ], 44 | theme: { 45 | extend: {}, 46 | }, 47 | plugins: [require("@tailwindcss/forms")], 48 | }; 49 | 50 | // Or 51 | 52 | module.exports = { 53 | presets: [require("astro-forms/themes/tailwind/tailwind.config.cjs")], 54 | content: [ 55 | "./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}", 56 | "./node_modules/astro-forms/themes/tailwind/*.astro", 57 | ], 58 | theme: { 59 | extend: {}, 60 | }, 61 | plugins: [], 62 | }; 63 | ``` 64 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/StringInput.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { FormSchema } from "../../types"; 3 | import InputLayout from "./InputLayout.astro"; 4 | 5 | const { title, name, schema, value } = Astro.props as { 6 | title: string; 7 | name: string; 8 | schema: FormSchema; 9 | value: any; 10 | }; 11 | 12 | const getInputType = () => { 13 | switch (schema.type) { 14 | case "string": 15 | return "text"; 16 | case "password": 17 | return "password"; 18 | case "email": 19 | return "email"; 20 | case "search": 21 | return "search"; 22 | case "url": 23 | return "url"; 24 | default: 25 | return "text"; 26 | } 27 | }; 28 | --- 29 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/Submit.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { title } = Astro.props as { title: string }; 3 | --- 4 | 5 | 10 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/index.ts: -------------------------------------------------------------------------------- 1 | import { InputType } from "../../types"; 2 | import StringInput from "./StringInput.astro"; 3 | import BooleanInput from "./BooleanInput.astro"; 4 | import ArrayInput from "./ArrayInput.astro"; 5 | import GenericInput from "./GenericInput.astro"; 6 | import DateInput from "./DateInput.astro"; 7 | import NumberInput from "./NumberInput.astro"; 8 | import Submit from "./Submit.astro"; 9 | 10 | export function GetInput(type: InputType) { 11 | switch (type) { 12 | case "string": 13 | return StringInput; 14 | case "password": 15 | return StringInput; 16 | case "email": 17 | return StringInput; 18 | case "number": 19 | return NumberInput; 20 | case "date": 21 | return DateInput; 22 | case "boolean": 23 | return BooleanInput; 24 | case "array": 25 | return ArrayInput; 26 | case "object": 27 | return GenericInput; 28 | case "submit": 29 | return Submit; 30 | default: 31 | return StringInput; 32 | } 33 | } 34 | 35 | export default { 36 | String: StringInput, 37 | Boolean: BooleanInput, 38 | Array: ArrayInput, 39 | Generic: GenericInput, 40 | Date: DateInput, 41 | Number: NumberInput, 42 | Submit: Submit, 43 | }; 44 | -------------------------------------------------------------------------------- /packages/astro-forms/themes/tailwind/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [require("@tailwindcss/forms")], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/astro-forms/types.ts: -------------------------------------------------------------------------------- 1 | export type InputType = 2 | | "string" 3 | | "email" 4 | | "password" 5 | | "search" 6 | | "url" 7 | | "number" 8 | | "boolean" 9 | | "array" 10 | | "object" 11 | | "date" 12 | | "submit"; 13 | 14 | export type Input = string | number | boolean | Array | object | Date; 15 | 16 | export interface FormSchema { 17 | type: InputType; 18 | title: string; 19 | subtitle?: string; 20 | properties: { 21 | [key: string]: FormSchema; 22 | }; 23 | items?: FormSchema; 24 | } 25 | 26 | export interface Theme { 27 | String: any; 28 | Boolean: any; 29 | Array: any; 30 | Generic: any; 31 | Date: any; 32 | Number: any; 33 | Submit: any; 34 | } 35 | -------------------------------------------------------------------------------- /packages/astro-forms/util.ts: -------------------------------------------------------------------------------- 1 | export function flatten(data) { 2 | var result = {}; 3 | function recurse(cur, prop) { 4 | if (Object(cur) !== cur) { 5 | result[prop] = cur; 6 | } else if (Array.isArray(cur)) { 7 | for (var i = 0, l = cur.length; i < l; i++) 8 | recurse(cur[i], prop + "[" + i + "]"); 9 | if (l == 0) result[prop] = []; 10 | } else { 11 | var isEmpty = true; 12 | for (var p in cur) { 13 | isEmpty = false; 14 | recurse(cur[p], prop ? prop + "." + p : p); 15 | } 16 | if (isEmpty && prop) result[prop] = {}; 17 | } 18 | } 19 | recurse(data, ""); 20 | return result; 21 | } 22 | -------------------------------------------------------------------------------- /packages/demo/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | 3 | import tailwind from "@astrojs/tailwind"; 4 | 5 | // https://astro.build/config 6 | export default defineConfig({ 7 | integrations: [tailwind()], 8 | }); 9 | -------------------------------------------------------------------------------- /packages/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.0.1", 4 | "private": false, 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview" 10 | }, 11 | "devDependencies": { 12 | "@astrojs/tailwind": "^0.2.2", 13 | "@babel/core": "^7.18.6", 14 | "@babel/helper-validator-identifier": "7.18.6", 15 | "@babel/preset-env": "^7.18.6", 16 | "@babel/types": "^7.18.6", 17 | "astro": "^1.0.0-beta.64", 18 | "prettier": "^2.7.1", 19 | "prettier-plugin-astro": "^0.1.1" 20 | }, 21 | "dependencies": { 22 | "astro-forms": "^1.0.6" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackmerrill/AstroForms/12d103b231265108f99634680c5c69fdc52a89df/packages/demo/public/favicon.ico -------------------------------------------------------------------------------- /packages/demo/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import schema, { sampleData } from "../util/schema"; 3 | import { Form } from "../../../astro-forms"; 4 | import tailwind from "../../../astro-forms/themes/tailwind"; 5 | 6 | --- 7 | 8 | 9 | 10 | 11 | 12 | 13 | Astro Forms 14 | 15 | 16 | 17 |
18 |

19 | Astro Forms Example (TailwindCSS) 20 |

21 | 22 |
23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/demo/src/util/schema.ts: -------------------------------------------------------------------------------- 1 | export default JSON.parse(` 2 | { 3 | "type": "object", 4 | "title": "Form", 5 | "properties": { 6 | "name": { 7 | "type": "string", 8 | "title": "Name", 9 | "subtitle": "Alphanumeric characters only!!!!" 10 | }, 11 | "email": { 12 | "type": "string", 13 | "title": "Email", 14 | "subtitle": "Don't worry, we'll spam you every day!" 15 | }, 16 | "password": { 17 | "type": "password", 18 | "title": "Password", 19 | "subtitle": "We didn't invest much into security, choose wisely." 20 | }, 21 | "personalInfo": { 22 | "type": "object", 23 | "title": "Personal Info", 24 | "properties": { 25 | "age": { 26 | "type": "number", 27 | "title": "Age", 28 | "subtitle": "How old are you?" 29 | }, 30 | "birthday": { 31 | "type": "date", 32 | "title": "Birthday", 33 | "subtitle": "So we can send you a cake, duh." 34 | }, 35 | "scaryStuff": { 36 | "type": "object", 37 | "title": "Scary Stuff", 38 | "properties": { 39 | "isScary": { 40 | "type": "boolean", 41 | "title": "Is Scary?", 42 | "subtitle": "Are you really scary?" 43 | } 44 | } 45 | }, 46 | "acceptsTerms": { 47 | "type": "boolean", 48 | "title": "Accept Terms of Service", 49 | "subtitle": "We're not responsible for your actions." 50 | } 51 | } 52 | }, 53 | "randomArray": { 54 | "type": "array", 55 | "title": "Random Array", 56 | "subtitle": "This is a random array, don't worry about it.", 57 | "items": { 58 | "type": "string" 59 | } 60 | }, 61 | "arrayOfObjects": { 62 | "type": "array", 63 | "title": "Array of Objects", 64 | "subtitle": "This is an array of objects, don't worry about it.", 65 | "items": { 66 | "type": "object", 67 | "properties": { 68 | "key": { 69 | "type": "string", 70 | "title": "Key" 71 | }, 72 | "value": { 73 | "type": "string", 74 | "title": "Value" 75 | } 76 | } 77 | } 78 | }, 79 | "submit": { 80 | "type": "submit", 81 | "title": "Submit" 82 | } 83 | } 84 | } 85 | `); 86 | 87 | export const sampleData = { 88 | name: "John Doe", 89 | email: "johndoe@email.com", 90 | password: "123456", 91 | personalInfo: { 92 | age: 30, 93 | birthday: "2020-01-01", 94 | scaryStuff: { 95 | isScary: true, 96 | }, 97 | acceptsTerms: true, 98 | }, 99 | randomArray: ["a", "b", "c", "d", "e"], 100 | arrayOfObjects: [ 101 | { 102 | key: "a", 103 | value: "A", 104 | }, 105 | { 106 | key: "b", 107 | value: "B", 108 | }, 109 | ], 110 | }; 111 | -------------------------------------------------------------------------------- /packages/demo/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}", 5 | "./node_modules/astro-forms/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}", 6 | "../astro-forms/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}", 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [require("@tailwindcss/forms")], 12 | }; 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable top-level await, and other modern ESM features. 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | // Enable node-style module resolution, for things like npm package imports. 7 | "moduleResolution": "node", 8 | // Enable JSON imports. 9 | "resolveJsonModule": true, 10 | // Enable stricter transpilation for better output. 11 | "isolatedModules": true, 12 | // Add type definitions for our Vite runtime. 13 | "types": ["vite/client"] 14 | } 15 | } 16 | --------------------------------------------------------------------------------