From c13e56779ab8df4b21660866b98d0f42677fabc9 Mon Sep 17 00:00:00 2001 From: Michael Brusegard <56915010+michaelbrusegard@users.noreply.github.com> Date: Tue, 9 Jan 2024 08:51:48 +0100 Subject: [PATCH 01/10] feat(reusable-styled-form-component): create base files for new react example This commit adds the base files needed to start developing an example of a reusable styled form component. --- .../.eslintrc.cjs | 15 + .../reusable-styled-form-component/.gitignore | 27 ++ .../reusable-styled-form-component/README.md | 6 + .../reusable-styled-form-component/index.html | 14 + .../package.json | 26 ++ .../postcss.config.js | 6 + .../src/index.css | 3 + .../src/index.tsx | 13 + .../src/utils.ts | 10 + .../tailwind.config.js | 8 + .../tsconfig.json | 9 + pnpm-lock.yaml | 285 +++++++++++++++++- 12 files changed, 408 insertions(+), 14 deletions(-) create mode 100644 examples/react/reusable-styled-form-component/.eslintrc.cjs create mode 100644 examples/react/reusable-styled-form-component/.gitignore create mode 100644 examples/react/reusable-styled-form-component/README.md create mode 100644 examples/react/reusable-styled-form-component/index.html create mode 100644 examples/react/reusable-styled-form-component/package.json create mode 100644 examples/react/reusable-styled-form-component/postcss.config.js create mode 100644 examples/react/reusable-styled-form-component/src/index.css create mode 100644 examples/react/reusable-styled-form-component/src/index.tsx create mode 100644 examples/react/reusable-styled-form-component/src/utils.ts create mode 100644 examples/react/reusable-styled-form-component/tailwind.config.js create mode 100644 examples/react/reusable-styled-form-component/tsconfig.json diff --git a/examples/react/reusable-styled-form-component/.eslintrc.cjs b/examples/react/reusable-styled-form-component/.eslintrc.cjs new file mode 100644 index 000000000..75ee331c1 --- /dev/null +++ b/examples/react/reusable-styled-form-component/.eslintrc.cjs @@ -0,0 +1,15 @@ +// @ts-check + +/** @type {import('eslint').Linter.Config} */ +const config = { + extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json', + }, + rules: { + 'react/no-children-prop': 'off', + }, +} + +module.exports = config diff --git a/examples/react/reusable-styled-form-component/.gitignore b/examples/react/reusable-styled-form-component/.gitignore new file mode 100644 index 000000000..4673b022e --- /dev/null +++ b/examples/react/reusable-styled-form-component/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +pnpm-lock.yaml +yarn.lock +package-lock.json + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/react/reusable-styled-form-component/README.md b/examples/react/reusable-styled-form-component/README.md new file mode 100644 index 000000000..1cf889265 --- /dev/null +++ b/examples/react/reusable-styled-form-component/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run dev` diff --git a/examples/react/reusable-styled-form-component/index.html b/examples/react/reusable-styled-form-component/index.html new file mode 100644 index 000000000..87450a047 --- /dev/null +++ b/examples/react/reusable-styled-form-component/index.html @@ -0,0 +1,14 @@ + + + + + + TanStack Reusable Styled Form Component + + + + +
+ + + diff --git a/examples/react/reusable-styled-form-component/package.json b/examples/react/reusable-styled-form-component/package.json new file mode 100644 index 000000000..f4ad65d66 --- /dev/null +++ b/examples/react/reusable-styled-form-component/package.json @@ -0,0 +1,26 @@ +{ + "name": "@tanstack/reusable-styled-form-component", + "version": "0.0.1", + "main": "src/index.tsx", + "scripts": { + "dev": "vite --port=3001", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-form": "<1.0.0", + "@tanstack/zod-form-adapter": "<1.0.0", + "cva": "1.0.0-beta.1", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "zod": "^3.21.4" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.16", + "postcss": "8.4.32", + "tailwind-merge": "^2.2.0", + "tailwindcss": "^3.4.1", + "vite": "^5.0.10" + } +} diff --git a/examples/react/reusable-styled-form-component/postcss.config.js b/examples/react/reusable-styled-form-component/postcss.config.js new file mode 100644 index 000000000..33ad091d2 --- /dev/null +++ b/examples/react/reusable-styled-form-component/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/examples/react/reusable-styled-form-component/src/index.css b/examples/react/reusable-styled-form-component/src/index.css new file mode 100644 index 000000000..b5c61c956 --- /dev/null +++ b/examples/react/reusable-styled-form-component/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/examples/react/reusable-styled-form-component/src/index.tsx b/examples/react/reusable-styled-form-component/src/index.tsx new file mode 100644 index 000000000..abe414dc5 --- /dev/null +++ b/examples/react/reusable-styled-form-component/src/index.tsx @@ -0,0 +1,13 @@ +import * as React from 'react' +import { createRoot } from 'react-dom/client' +import { z } from 'zod' + +export default function App() { + return ( +
+ ) +} + +const rootElement = document.getElementById('root')! + +createRoot(rootElement).render() diff --git a/examples/react/reusable-styled-form-component/src/utils.ts b/examples/react/reusable-styled-form-component/src/utils.ts new file mode 100644 index 000000000..cf0dec80d --- /dev/null +++ b/examples/react/reusable-styled-form-component/src/utils.ts @@ -0,0 +1,10 @@ +import { type VariantProps, defineConfig } from 'cva'; +import { twMerge } from 'tailwind-merge'; + +const { cva, cx, compose } = defineConfig({ + hooks: { + onComplete: (className) => twMerge(className), + }, +}); + +export { cva, cx, compose, type VariantProps }; diff --git a/examples/react/reusable-styled-form-component/tailwind.config.js b/examples/react/reusable-styled-form-component/tailwind.config.js new file mode 100644 index 000000000..7038f8440 --- /dev/null +++ b/examples/react/reusable-styled-form-component/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./index.html', './src/**/*.{tsx,ts}'], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/examples/react/reusable-styled-form-component/tsconfig.json b/examples/react/reusable-styled-form-component/tsconfig.json new file mode 100644 index 000000000..666a0ea71 --- /dev/null +++ b/examples/react/reusable-styled-form-component/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "jsx": "react", + "noEmit": true, + "strict": true, + "esModuleInterop": true, + "lib": ["DOM", "DOM.Iterable", "ES2020"] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1ac2f11d..d8b927997 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,7 +61,7 @@ importers: version: 3.6.1(@typescript-eslint/parser@6.4.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + version: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.0)(eslint@8.56.0) eslint-plugin-react: specifier: ^7.33.2 version: 7.33.2(eslint@8.56.0) @@ -148,6 +148,46 @@ importers: specifier: ^5 version: 5.2.2 + examples/react/reusable-styled-form-component: + dependencies: + '@tanstack/react-form': + specifier: <1.0.0 + version: link:../../../packages/react-form + '@tanstack/zod-form-adapter': + specifier: <1.0.0 + version: link:../../../packages/zod-form-adapter + cva: + specifier: 1.0.0-beta.1 + version: 1.0.0-beta.1(typescript@5.2.2) + react: + specifier: ^18.0.0 + version: 18.2.0 + react-dom: + specifier: ^18.0.0 + version: 18.2.0(react@18.2.0) + zod: + specifier: ^3.21.4 + version: 3.22.4 + devDependencies: + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.2.1(vite@5.0.10) + autoprefixer: + specifier: ^10.4.16 + version: 10.4.16(postcss@8.4.32) + postcss: + specifier: 8.4.32 + version: 8.4.32 + tailwind-merge: + specifier: ^2.2.0 + version: 2.2.0 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.1 + vite: + specifier: ^5.0.10 + version: 5.0.10(@types/node@18.19.4) + examples/react/simple: dependencies: '@tanstack/react-form': @@ -634,6 +674,11 @@ packages: resolution: {integrity: sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==} dev: true + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: true + /@ampproject/remapping@2.2.1: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} @@ -4828,12 +4873,15 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: false /appdirsjs@1.2.7: resolution: {integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==} dev: false + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -5031,6 +5079,22 @@ packages: when-exit: 2.1.2 dev: true + /autoprefixer@10.4.16(postcss@8.4.32): + resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.22.2 + caniuse-lite: 1.0.30001572 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.32 + postcss-value-parser: 4.2.0 + dev: true + /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -5342,7 +5406,6 @@ packages: /camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - dev: false /camelcase-keys@8.0.2: resolution: {integrity: sha512-qMKdlOfsjlezMqxkUGGMaWWs17i2HoL15tM+wtx8ld4nLrUwU58TFdvyGOz/piNP842KeO8yXvggVQSdQ828NA==} @@ -5423,6 +5486,21 @@ packages: get-func-name: 2.0.2 dev: true + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /ci-info@2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} dev: false @@ -5541,6 +5619,11 @@ packages: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: false + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + /commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -5683,7 +5766,6 @@ packages: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true - dev: false /cssstyle@3.0.0: resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==} @@ -5703,6 +5785,18 @@ packages: is-git-repository: 1.1.1 dev: true + /cva@1.0.0-beta.1(typescript@5.2.2): + resolution: {integrity: sha512-gznFqTgERU9q4wg7jfgqtt34+RUt9S5t0xDAAEuDwQEAXEgjdDkKXpLLNjwSxsB4Ln/sqWJEH7yhE8Ny0mxd0w==} + peerDependencies: + typescript: '>= 4.5.5 < 6' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + clsx: 2.0.0 + typescript: 5.2.2 + dev: false + /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true @@ -5905,6 +5999,10 @@ packages: resolution: {integrity: sha512-/oD3At60ZfhgzpofJtyClNTrIACyMdRe+ih0YiHzAniN0IZnLdLpEzgR6RtGs3kowxUkTnvV/4t1FBxXMUdusQ==} dev: true + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + /diff-sequences@26.6.2: resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==} engines: {node: '>= 10.14.2'} @@ -5922,6 +6020,10 @@ packages: path-type: 4.0.0 dev: true + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -6243,7 +6345,7 @@ packages: eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.0)(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) eslint-plugin-react: 7.33.2(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) @@ -6283,7 +6385,7 @@ packages: enhanced-resolve: 5.15.0 eslint: 8.56.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.0)(eslint@8.56.0) fast-glob: 3.3.1 get-tsconfig: 4.7.0 is-core-module: 2.13.1 @@ -6306,7 +6408,7 @@ packages: enhanced-resolve: 5.15.0 eslint: 8.56.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.0)(eslint@8.56.0) fast-glob: 3.3.1 get-tsconfig: 4.7.0 is-core-module: 2.13.1 @@ -6378,7 +6480,7 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-typescript@3.6.0)(eslint@8.56.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -6870,6 +6972,10 @@ packages: mime-types: 2.1.35 dev: true + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: true + /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -7917,6 +8023,11 @@ packages: supports-color: 8.1.1 dev: false + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: true + /jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} dev: true @@ -8171,6 +8282,16 @@ packages: resolve: 1.22.4 dev: true + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /lilconfig@3.0.0: + resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} + engines: {node: '>=14'} + dev: true + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -8842,6 +8963,14 @@ packages: resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} dev: true + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -8979,7 +9108,11 @@ packages: /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: false + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true /npm-bundled@2.0.1: resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} @@ -9103,6 +9236,11 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + /object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} dev: true @@ -9468,6 +9606,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + /pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -9483,7 +9626,6 @@ packages: /pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - dev: false /pkg-dir@3.0.0: resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} @@ -9500,6 +9642,18 @@ packages: pathe: 1.1.1 dev: true + /postcss-import@15.1.0(postcss@8.4.32): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.32 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.4 + dev: true + /postcss-js@4.0.1(postcss@8.4.32): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} @@ -9508,7 +9662,23 @@ packages: dependencies: camelcase-css: 2.0.1 postcss: 8.4.32 - dev: false + + /postcss-load-config@4.0.2(postcss@8.4.32): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.0.0 + postcss: 8.4.32 + yaml: 2.3.4 + dev: true /postcss-mixins@9.0.4(postcss@8.4.32): resolution: {integrity: sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==} @@ -9531,7 +9701,6 @@ packages: dependencies: postcss: 8.4.32 postcss-selector-parser: 6.0.15 - dev: false /postcss-preset-mantine@1.12.2(postcss@8.4.32): resolution: {integrity: sha512-a7W/lDSeMg/LeOKb/PNKp+pVzYSSxxWFHGihnwRBEGzeY0qCZnPluH5KsJMfxqbElcf5zoiXZIyElzI/0KmgjA==} @@ -9549,7 +9718,6 @@ packages: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - dev: false /postcss-simple-vars@7.0.1(postcss@8.4.32): resolution: {integrity: sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==} @@ -9560,6 +9728,10 @@ packages: postcss: 8.4.32 dev: false + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + /postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -9904,6 +10076,12 @@ packages: dependencies: loose-envify: 1.4.0 + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + /read-pkg-up@9.1.0: resolution: {integrity: sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -9942,6 +10120,13 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + /readline@1.3.0: resolution: {integrity: sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==} dev: false @@ -10741,6 +10926,20 @@ packages: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} dev: false + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 10.3.10 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + /sudo-prompt@9.2.1: resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==} dev: false @@ -10785,6 +10984,43 @@ packages: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} dev: false + /tailwind-merge@2.2.0: + resolution: {integrity: sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==} + dependencies: + '@babel/runtime': 7.23.7 + dev: true + + /tailwindcss@3.4.1: + resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.32 + postcss-import: 15.1.0(postcss@8.4.32) + postcss-js: 4.0.1(postcss@8.4.32) + postcss-load-config: 4.0.2(postcss@8.4.32) + postcss-nested: 6.0.1(postcss@8.4.32) + postcss-selector-parser: 6.0.15 + resolve: 1.22.4 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + dev: true + /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -10837,6 +11073,19 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + /throat@5.0.0: resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} dev: false @@ -11021,6 +11270,10 @@ packages: typescript: 5.3.3 dev: true + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true + /tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} dependencies: @@ -11137,7 +11390,6 @@ packages: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true - dev: true /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} @@ -11902,6 +12154,11 @@ packages: engines: {node: '>= 14'} dev: false + /yaml@2.3.4: + resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} + engines: {node: '>= 14'} + dev: true + /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} From 303cabf25af35050132a7da4b3e34cc877459a16 Mon Sep 17 00:00:00 2001 From: Michael Brusegard <56915010+michaelbrusegard@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:39:03 +0100 Subject: [PATCH 02/10] chore: update text field component --- .../reusable-styled-form-component/index.html | 2 +- .../package.json | 1 + .../src/Form.tsx | 127 ++++++++++++++++++ .../src/Input.tsx | 28 ++++ .../src/Label.tsx | 23 ++++ .../src/index.tsx | 38 +++++- .../src/variants.ts | 23 ++++ pnpm-lock.yaml | 11 ++ 8 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 examples/react/reusable-styled-form-component/src/Form.tsx create mode 100644 examples/react/reusable-styled-form-component/src/Input.tsx create mode 100644 examples/react/reusable-styled-form-component/src/Label.tsx create mode 100644 examples/react/reusable-styled-form-component/src/variants.ts diff --git a/examples/react/reusable-styled-form-component/index.html b/examples/react/reusable-styled-form-component/index.html index 87450a047..fa8314fd9 100644 --- a/examples/react/reusable-styled-form-component/index.html +++ b/examples/react/reusable-styled-form-component/index.html @@ -6,7 +6,7 @@ TanStack Reusable Styled Form Component - +
diff --git a/examples/react/reusable-styled-form-component/package.json b/examples/react/reusable-styled-form-component/package.json index f4ad65d66..b3f5386bf 100644 --- a/examples/react/reusable-styled-form-component/package.json +++ b/examples/react/reusable-styled-form-component/package.json @@ -8,6 +8,7 @@ "preview": "vite preview" }, "dependencies": { + "@heroicons/react": "^2.1.1", "@tanstack/react-form": "<1.0.0", "@tanstack/zod-form-adapter": "<1.0.0", "cva": "1.0.0-beta.1", diff --git a/examples/react/reusable-styled-form-component/src/Form.tsx b/examples/react/reusable-styled-form-component/src/Form.tsx new file mode 100644 index 000000000..f9b74a0c5 --- /dev/null +++ b/examples/react/reusable-styled-form-component/src/Form.tsx @@ -0,0 +1,127 @@ +import * as React from 'react' +import { type FormApi, type FormOptions, useForm } from '@tanstack/react-form' +import { ExclamationCircleIcon } from '@heroicons/react/20/solid' +import { zodValidator } from '@tanstack/zod-form-adapter' +import { cx } from './utils' +import { Input } from './Input' +import { Label } from './Label' + +type FormProps = any + +type FormFieldProps = any + +type FormFieldTextProps = any + +type FormFieldCheckboxProps = any + +type FormFieldSelectProps = any + +type FormComponentProps = React.ForwardRefExoticComponent & { + Field: FormFieldProps & { + Text: React.ForwardRefExoticComponent + Checkbox: React.ForwardRefExoticComponent + Select: React.ForwardRefExoticComponent + } +} + +type FormContextProps = FormApi, typeof zodValidator> + +const FormContext = React.createContext( + {} as FormContextProps, +) + +const Form = React.forwardRef( + ({ className, formOptions, ...props }, ref) => { + const HeadlessForm = useForm({ + validatorAdapter: zodValidator, + ...formOptions, + }) + + return ( + + +
{ + e.preventDefault() + e.stopPropagation() + void HeadlessForm.handleSubmit() + }} + ref={ref} + {...props} + /> + + + ) + }, +) as FormComponentProps + +Form.displayName = 'Form' + +Form.Field = ({ ...props }: FormFieldProps) => { + const HeadlessForm = React.useContext(FormContext) + return +} + +Form.Field.displayName = 'Form.Field' + +Form.Field.Text = React.forwardRef( + ({ className, field, label, ...props }, ref) => { + const formMessageId = `${field.name}-message` + return ( +
+ 0} + type="text" + id={field.name} + name={field.name} + placeholder={label} + value={field.state.value} + onBlur={field.handleBlur} + onChange={(e) => field.handleChange(e.target.value)} + aria-describedby={ + field.state.meta.errors.length > 0 ? `${formMessageId}` : '' + } + aria-invalid={field.state.meta.errors.length > 0} + ref={ref} + {...props} + /> + + 0 && + 'peer-focus:opacity-100 text-red-500', + )} + /> + 0 && + 'peer-focus:opacity-100 text-red-500', + )} + aria-hidden={field.state.meta.errors.length > 0 ? 'false' : 'true'} + > + {field.state.meta.touchedErrors} + +
+ ) + }, +) + +Form.Field.Text.displayName = 'Form.Field.Text' + +export { Form, type FormProps, type FormFieldProps } diff --git a/examples/react/reusable-styled-form-component/src/Input.tsx b/examples/react/reusable-styled-form-component/src/Input.tsx new file mode 100644 index 000000000..3389dad32 --- /dev/null +++ b/examples/react/reusable-styled-form-component/src/Input.tsx @@ -0,0 +1,28 @@ +import * as React from 'react' +import { type inputVariantProps, inputVariants } from './variants' + +type InputProps = React.InputHTMLAttributes & + Omit & { + invalid?: boolean + } + +const Input = React.forwardRef( + ({ className, type, invalid = false, ...props }, ref) => { + return ( + + ) + }, +) + +Input.displayName = 'Input' + +export { Input, inputVariants, type InputProps } diff --git a/examples/react/reusable-styled-form-component/src/Label.tsx b/examples/react/reusable-styled-form-component/src/Label.tsx new file mode 100644 index 000000000..0a5deecb3 --- /dev/null +++ b/examples/react/reusable-styled-form-component/src/Label.tsx @@ -0,0 +1,23 @@ +import * as React from 'react' +import { cx } from './utils' + +type LabelProps = React.LabelHTMLAttributes + +const Label = React.forwardRef( + ({ className, ...props }, ref) => { + return ( +