├── .github └── workflows │ └── ci.yml ├── .gitignore ├── AGENTS.md ├── LICENSE.md ├── README.md ├── apps └── web │ ├── .gitignore │ ├── app │ ├── favicon.png │ ├── helpers.ts │ ├── logo.png │ ├── root.tsx │ ├── routes.ts │ ├── routes │ │ ├── conf │ │ │ ├── 01.spec.ts │ │ │ ├── 01.tsx │ │ │ ├── 02.tsx │ │ │ ├── 03.tsx │ │ │ ├── 04.tsx │ │ │ ├── 05.tsx │ │ │ ├── 06.tsx │ │ │ ├── 07.tsx │ │ │ ├── 08.tsx │ │ │ ├── 09.tsx │ │ │ ├── index.tsx │ │ │ ├── layout.tsx │ │ │ └── success.tsx │ │ ├── examples │ │ │ ├── array-of-objects.spec.ts │ │ │ ├── array-of-objects.tsx │ │ │ ├── array-of-strings.spec.ts │ │ │ ├── array-of-strings.tsx │ │ │ ├── async-validation-with-js.spec.ts │ │ │ ├── async-validation-without-js.spec.ts │ │ │ ├── async-validation.tsx │ │ │ ├── auto-complete.spec.ts │ │ │ ├── auto-complete.tsx │ │ │ ├── auto-generated-with-js.spec.ts │ │ │ ├── auto-generated-without-js.spec.ts │ │ │ ├── auto-generated.tsx │ │ │ ├── booleans.spec.ts │ │ │ ├── booleans.tsx │ │ │ ├── context.spec.ts │ │ │ ├── context.tsx │ │ │ ├── custom-input.spec.ts │ │ │ ├── custom-input.tsx │ │ │ ├── custom-response.spec.ts │ │ │ ├── custom-response.tsx │ │ │ ├── dates-with-js.spec.ts │ │ │ ├── dates-without-js.spec.ts │ │ │ ├── dates.tsx │ │ │ ├── dirty-indicator.spec.ts │ │ │ ├── dirty-indicator.tsx │ │ │ ├── dynamic-form.spec.ts │ │ │ ├── dynamic-form.tsx │ │ │ ├── edit-values.spec.ts │ │ │ ├── edit-values.tsx │ │ │ ├── empty-option-label.spec.ts │ │ │ ├── empty-option-label.tsx │ │ │ ├── enums.spec.ts │ │ │ ├── enums.tsx │ │ │ ├── error-indicator.spec.ts │ │ │ ├── error-indicator.tsx │ │ │ ├── field-error-clear-server.spec.ts │ │ │ ├── field-error-without-js.spec.ts │ │ │ ├── field-error.spec.ts │ │ │ ├── field-error.tsx │ │ │ ├── field-layout-with-js.spec.ts │ │ │ ├── field-layout-without-js.spec.ts │ │ │ ├── field-layout.tsx │ │ │ ├── field-with-children.spec.ts │ │ │ ├── field-with-children.tsx │ │ │ ├── form-with-children-with-js.spec.ts │ │ │ ├── form-with-children-without-js.spec.ts │ │ │ ├── form-with-children.tsx │ │ │ ├── global-error-with-js.spec.ts │ │ │ ├── global-error-without-js.spec.ts │ │ │ ├── global-error.tsx │ │ │ ├── hidden-field-with-js.spec.ts │ │ │ ├── hidden-field-without-js.spec.ts │ │ │ ├── hidden-field.tsx │ │ │ ├── imperative-submit.spec.ts │ │ │ ├── imperative-submit.tsx │ │ │ ├── index.ts │ │ │ ├── inline-checkboxes.spec.ts │ │ │ ├── inline-checkboxes.tsx │ │ │ ├── input-types.spec.ts │ │ │ ├── input-types.tsx │ │ │ ├── labels-options-etc.spec.ts │ │ │ ├── labels-options-etc.tsx │ │ │ ├── layout.tsx │ │ │ ├── multiple-forms.spec.ts │ │ │ ├── multiple-forms.tsx │ │ │ ├── numbers-with-js.spec.ts │ │ │ ├── numbers-without-js.spec.ts │ │ │ ├── numbers.tsx │ │ │ ├── on-blur.spec.ts │ │ │ ├── on-blur.tsx │ │ │ ├── on-change.spec.ts │ │ │ ├── on-change.tsx │ │ │ ├── on-submit.spec.ts │ │ │ ├── on-submit.tsx │ │ │ ├── radio-buttons.spec.ts │ │ │ ├── radio-buttons.tsx │ │ │ ├── redirect.spec.ts │ │ │ ├── redirect.tsx │ │ │ ├── required-indicator.spec.ts │ │ │ ├── required-indicator.tsx │ │ │ ├── strings-with-js.spec.ts │ │ │ ├── strings-without-js.spec.ts │ │ │ ├── strings.tsx │ │ │ ├── transform-values.spec.ts │ │ │ ├── transform-values.tsx │ │ │ ├── use-fetcher.spec.ts │ │ │ ├── use-fetcher.tsx │ │ │ ├── use-field.spec.ts │ │ │ ├── use-field.tsx │ │ │ ├── use-form-state.spec.ts │ │ │ ├── use-form-state.tsx │ │ │ ├── without-redirect.spec.ts │ │ │ ├── without-redirect.tsx │ │ │ ├── zod-effects-refinement.spec.ts │ │ │ ├── zod-effects.spec.ts │ │ │ └── zod-effects.tsx │ │ ├── get-started.spec.ts │ │ ├── get-started.tsx │ │ ├── home-with-js.spec.ts │ │ ├── home-without-js.spec.ts │ │ ├── home.tsx │ │ ├── success.spec.ts │ │ ├── success.tsx │ │ └── tests │ │ │ ├── accented-options.spec.ts │ │ │ ├── accented-options.tsx │ │ │ ├── fetcher-with-other-forms-error.spec.ts │ │ │ ├── fetcher-with-other-forms-error.tsx │ │ │ ├── field-with-radio-children-with-js.spec.ts │ │ │ ├── field-with-radio-children-without-js.spec.ts │ │ │ ├── field-with-radio-children.tsx │ │ │ ├── field-with-ref.spec.ts │ │ │ ├── field-with-ref.tsx │ │ │ ├── hidden-field-with-errors.spec.ts │ │ │ └── hidden-field-with-errors.tsx │ ├── seasoned-icon-dark.png │ ├── social.png │ ├── tailwind.css │ └── ui │ │ ├── base-button.tsx │ │ ├── button-link.tsx │ │ ├── button.tsx │ │ ├── checkbox.tsx │ │ ├── code.tsx │ │ ├── conf │ │ ├── label.tsx │ │ └── top-bar.tsx │ │ ├── error.tsx │ │ ├── errors.tsx │ │ ├── example.tsx │ │ ├── external-link.tsx │ │ ├── feature.tsx │ │ ├── field.tsx │ │ ├── global-loading.tsx │ │ ├── heading.tsx │ │ ├── icons │ │ └── github.tsx │ │ ├── input-wrapper.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── nav-link.tsx │ │ ├── pre.tsx │ │ ├── radio-group.tsx │ │ ├── radio.tsx │ │ ├── schema-form.tsx │ │ ├── secondary-button-link.tsx │ │ ├── secondary-button.tsx │ │ ├── select.tsx │ │ ├── sidebar-layout.tsx │ │ ├── sub-heading.tsx │ │ ├── submit-button.tsx │ │ ├── text-area.tsx │ │ └── top-bar.tsx │ ├── package.json │ ├── playwright.config.ts │ ├── react-router.config.ts │ ├── styles │ └── app.css │ ├── tailwind.config.ts │ ├── tests │ └── setup │ │ ├── example.ts │ │ └── tests.ts │ ├── tsconfig.json │ └── vite.config.ts ├── biome.json ├── package-lock.json ├── package.json ├── packages └── remix-forms │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ ├── children-traversal.test.tsx │ ├── children-traversal.ts │ ├── coerce-to-form.test.tsx │ ├── coerce-to-form.ts │ ├── coercions.test.ts │ ├── coercions.ts │ ├── create-field.test.tsx │ ├── create-field.tsx │ ├── default-render-field.test.tsx │ ├── default-render-field.tsx │ ├── hidden-errors.test.tsx │ ├── index.test.ts │ ├── index.ts │ ├── infer-label.test.ts │ ├── infer-label.ts │ ├── internal-mutations.test.ts │ ├── mutations.test.ts │ ├── mutations.ts │ ├── prelude.test.ts │ ├── prelude.ts │ ├── schema-form.test.tsx │ ├── schema-form.tsx │ ├── shape-info.test.ts │ └── shape-info.ts │ ├── tsconfig.json │ └── vitest.config.ts └── turbo.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | tests: 14 | name: tests 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: 📥 Restore cache 19 | uses: actions/cache@v3 20 | with: 21 | path: '**/node_modules' 22 | key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }}-v2 23 | - name: ⎔ Setup node 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: 20 27 | - name: Install dependencies 28 | run: npm ci 29 | - name: Lint 30 | run: npm run lint 31 | - name: Typecheck 32 | run: npm run tsc 33 | - name: Install Playwright 34 | run: npm run playwright:ci:install 35 | - name: Build 36 | run: npm run build 37 | - name: Run Playwright tests 38 | run: npm run test 39 | - uses: actions/upload-artifact@v4 40 | if: always() 41 | with: 42 | name: playwright-report 43 | path: ./**/playwright-report 44 | retention-days: 5 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | playwright-report/ 11 | 12 | build/ 13 | .netlify 14 | 15 | # misc 16 | .DS_Store 17 | *.pem 18 | 19 | # debug 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | .pnpm-debug.log* 24 | 25 | # local env files 26 | .env.local 27 | .env.development.local 28 | .env.test.local 29 | .env.production.local 30 | 31 | # turbo 32 | .turbo 33 | dist/ 34 | tsc/ 35 | *.tgz 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 Seasoned Desenvolvimento de Software LTDA. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Remix Forms 2 | 3 | This repository contains the Remix Forms source code. Please create issues as you encounter them. We appreciate the contribution! 4 | 5 | ## Documentation 6 | 7 | For documentation about Remix Forms, please visit [remix-forms.seasoned.cc](https://remix-forms.seasoned.cc). 8 | 9 | ## Development 10 | 11 | 1. Clone the repo 12 | 13 | 2. Install and run 14 | 15 | ```sh 16 | $ cd remix-forms 17 | $ npm i 18 | $ npm run dev 19 | ``` 20 | 21 | Note: we had issues running the turborepo `dev` command on Node 18. We recommend using Node 16 for development. 22 | 23 | This will run the website at http://localhost:5173. 24 | 25 | ### Repository structure 26 | 27 | This is a monorepo managed with **npm workspaces** and **Turborepo**. The two 28 | main workspaces are: 29 | 30 | - `apps/web` – the website and example app. 31 | - `packages/remix-forms` – the Remix Forms library. 32 | 33 | Workspace scripts are executed from the repo root using `npm run