diff --git a/.vscode/settings.json b/.vscode/settings.json index 3139e12..07f71c8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,7 +33,8 @@ "editor.defaultFormatter": "biomejs.biome" }, "editor.codeActionsOnSave": { - "source.organizeImports.biome": "explicit" + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit" }, "editor.defaultFormatter": "biomejs.biome", "editor.formatOnPaste": true, diff --git a/lefthook.yml b/lefthook.yml index 6addd4b..487006d 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -2,9 +2,9 @@ pre-commit: commands: check: glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" - run: pnpx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} + run: pnpm biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} stage_fixed: true registry: - glob: "src/registry/new-york/**/*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" - run: pnpx shadcn@latest build + glob: "registry.json" + run: pnpm shadcn build && git add public/r/* stage_fixed: true diff --git a/package.json b/package.json index 5c364eb..769c236 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,13 @@ "lint": "biome lint", "check": "biome check --fix --unsafe", "registry:build": "shadcn build", + "registry:watch": "tsx watch src/scripts/blocks.ts", "pre-commit": "pnpm run check && pnpm run registry:build" }, "dependencies": { + "@radix-ui/react-checkbox": "^1.3.1", "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-popover": "^1.1.13", "@radix-ui/react-radio-group": "^1.3.6", "@radix-ui/react-separator": "^1.1.6", "@radix-ui/react-slot": "^1.1.2", @@ -43,8 +46,8 @@ "zod": "^3.24.2" }, "devDependencies": { - "@tanstack/react-router-devtools": "^1.120.3", "@biomejs/biome": "1.9.4", + "@tanstack/react-router-devtools": "^1.120.3", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.2.0", "@types/node": "^22.13.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3afb3a0..48629ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,15 @@ importers: .: dependencies: + '@radix-ui/react-checkbox': + specifier: ^1.3.1 + version: 1.3.1(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-label': specifier: ^2.1.2 version: 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popover': + specifier: ^1.1.13 + version: 1.1.13(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-radio-group': specifier: ^1.3.6 version: 1.3.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -992,6 +998,21 @@ packages: '@fastify/busboy@3.1.1': resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==} + '@floating-ui/core@1.7.0': + resolution: {integrity: sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==} + + '@floating-ui/dom@1.7.0': + resolution: {integrity: sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==} + + '@floating-ui/react-dom@2.1.2': + resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@inquirer/confirm@5.1.8': resolution: {integrity: sha512-dNLWCYZvXDjO3rnQfk2iuJNL4Ivwz/T2+C3+WnNfJKsNGSuOs3wAo2F6e0p946gtSAk31nZMfW+MRmYaplPKsg==} engines: {node: '>=18'} @@ -1233,6 +1254,32 @@ packages: '@radix-ui/primitive@1.1.2': resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + '@radix-ui/react-arrow@1.1.6': + resolution: {integrity: sha512-2JMfHJf/eVnwq+2dewT3C0acmCWD3XiVA1Da+jTDqo342UlU13WvXtqHhG+yJw5JeQmu4ue2eMy6gcEArLBlcw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.1': + resolution: {integrity: sha512-xTaLKAO+XXMPK/BpVTSaAAhlefmvMSACjIhK9mGsImvX2ljcTDm8VGR1CuS1uYcNdR5J+oiOhoJZc5un6bh3VQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collection@1.1.6': resolution: {integrity: sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ==} peerDependencies: @@ -1282,6 +1329,41 @@ packages: '@types/react': optional: true + '@radix-ui/react-dismissable-layer@1.1.9': + resolution: {integrity: sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.6': + resolution: {integrity: sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: @@ -1304,6 +1386,45 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popover@1.1.13': + resolution: {integrity: sha512-84uqQV3omKDR076izYgcha6gdpN8m3z6w/AeJ83MSBJYVG/AbOHdLjAgsPZkeC/kt+k64moXFCnio8BbqXszlw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.6': + resolution: {integrity: sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.8': + resolution: {integrity: sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.1.4': resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} peerDependencies: @@ -1427,6 +1548,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-layout-effect@1.1.1': resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: @@ -1445,6 +1575,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-size@1.1.1': resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: @@ -1454,6 +1593,9 @@ packages: '@types/react': optional: true + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@remix-run/node@2.16.3': resolution: {integrity: sha512-6FLMNPUwVk/XMZs83li5LvXz0UGG1MXE+6p6rIh29COFASOPEy8tlhu5OSnkMWlGrnaGfjejlSr3lWmdiE864Q==} engines: {node: '>=18.0.0'} @@ -2345,6 +2487,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + engines: {node: '>=10'} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -2869,6 +3015,9 @@ packages: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + detective-amd@5.0.2: resolution: {integrity: sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==} engines: {node: '>=14'} @@ -3241,6 +3390,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-own-enumerable-keys@1.0.0: resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} engines: {node: '>=14.16'} @@ -4547,12 +4700,42 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.6.3: + resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-resizable-panels@3.0.1: resolution: {integrity: sha512-6ruCEyw0iqXRcXEktPQn1HL553DNhrdLisCyEdSpzhkmo9bPqZxskJZ+aGeFqJ1qPvIWxuAiag82kvLSb2JZTQ==} peerDependencies: react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react@19.1.0: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} @@ -5310,6 +5493,26 @@ packages: urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + use-sync-external-store@1.5.0: resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: @@ -6276,6 +6479,23 @@ snapshots: '@fastify/busboy@3.1.1': {} + '@floating-ui/core@1.7.0': + dependencies: + '@floating-ui/utils': 0.2.9 + + '@floating-ui/dom@1.7.0': + dependencies: + '@floating-ui/core': 1.7.0 + '@floating-ui/utils': 0.2.9 + + '@floating-ui/react-dom@2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/dom': 1.7.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@floating-ui/utils@0.2.9': {} + '@inquirer/confirm@5.1.8(@types/node@22.13.14)': dependencies: '@inquirer/core': 10.1.9(@types/node@22.13.14) @@ -6571,6 +6791,31 @@ snapshots: '@radix-ui/primitive@1.1.2': {} + '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + + '@radix-ui/react-checkbox@1.3.1(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.12)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-collection@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) @@ -6607,6 +6852,36 @@ snapshots: optionalDependencies: '@types/react': 19.0.12 + '@radix-ui/react-dismissable-layer@1.1.9(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.0.12)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.0.12)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.0.12 + + '@radix-ui/react-focus-scope@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.12)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-id@1.1.1(@types/react@19.0.12)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.12)(react@19.1.0) @@ -6623,6 +6898,57 @@ snapshots: '@types/react': 19.0.12 '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-popover@1.1.13(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.12)(react@19.1.0) + aria-hidden: 1.2.4 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.6.3(@types/react@19.0.12)(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + + '@radix-ui/react-popper@1.2.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.12)(react@19.1.0) + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + + '@radix-ui/react-portal@1.1.8(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.12)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.1.0) @@ -6730,6 +7056,13 @@ snapshots: optionalDependencies: '@types/react': 19.0.12 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.0.12)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.12)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.0.12 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.0.12)(react@19.1.0)': dependencies: react: 19.1.0 @@ -6742,6 +7075,13 @@ snapshots: optionalDependencies: '@types/react': 19.0.12 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.0.12)(react@19.1.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + optionalDependencies: + '@types/react': 19.0.12 + '@radix-ui/react-use-size@1.1.1(@types/react@19.0.12)(react@19.1.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.12)(react@19.1.0) @@ -6749,6 +7089,8 @@ snapshots: optionalDependencies: '@types/react': 19.0.12 + '@radix-ui/rect@1.1.1': {} + '@remix-run/node@2.16.3(typescript@5.8.2)': dependencies: '@remix-run/server-runtime': 2.16.3(typescript@5.8.2) @@ -8054,6 +8396,10 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.4: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -8522,6 +8868,8 @@ snapshots: detect-libc@2.0.3: {} + detect-node-es@1.1.0: {} + detective-amd@5.0.2: dependencies: ast-module-types: 5.0.0 @@ -8970,6 +9318,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-own-enumerable-keys@1.0.0: {} get-port-please@3.1.2: {} @@ -10477,11 +10827,38 @@ snapshots: react-refresh@0.14.2: {} + react-remove-scroll-bar@2.3.8(@types/react@19.0.12)(react@19.1.0): + dependencies: + react: 19.1.0 + react-style-singleton: 2.2.3(@types/react@19.0.12)(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.12 + + react-remove-scroll@2.6.3(@types/react@19.0.12)(react@19.1.0): + dependencies: + react: 19.1.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.0.12)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.0.12)(react@19.1.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.0.12)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.0.12)(react@19.1.0) + optionalDependencies: + '@types/react': 19.0.12 + react-resizable-panels@3.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + react-style-singleton@2.2.3(@types/react@19.0.12)(react@19.1.0): + dependencies: + get-nonce: 1.0.1 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.12 + react@19.1.0: {} read-package-up@11.0.0: @@ -11320,6 +11697,21 @@ snapshots: urlpattern-polyfill@8.0.2: {} + use-callback-ref@1.3.3(@types/react@19.0.12)(react@19.1.0): + dependencies: + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.12 + + use-sidecar@1.1.3(@types/react@19.0.12)(react@19.1.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.12 + use-sync-external-store@1.5.0(react@19.1.0): dependencies: react: 19.1.0 diff --git a/public/r/basic-info.json b/public/r/basic-info.json index 57c17cf..561eebb 100644 --- a/public/r/basic-info.json +++ b/public/r/basic-info.json @@ -5,17 +5,19 @@ "title": "Basic Info", "description": "A basic info form component built with TanStack Form.", "dependencies": [ - "@tanstack/react-form" + "zod" ], "registryDependencies": [ - "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json" + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input", + "textarea" ], "files": [ { - "path": "src/registry/new-york/blocks/user/basic-info.tsx", - "content": "import { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { useCallback } from \"react\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n email: z.string().email({\n message: \"Please enter a valid email address.\",\n }),\n age: z.number().min(8, {\n message: \"Age must be at least 18 years.\",\n }),\n bio: z.string().max(160, {\n message: \"Bio must not exceed 160 characters.\",\n }),\n});\n\nfunction BasicInfoForm() {\n const form = useAppForm({\n validators: { onBlur: FormSchema },\n defaultValues: {\n username: \"\",\n email: \"\",\n age: 0,\n bio: \"\",\n },\n onSubmit: ({ formApi, value }) => {\n formApi.reset();\n toast.success(Username: {value.username});\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n return (\n \n \n
\n (\n \n Username\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n This is your public display name.\n \n \n \n )}\n />\n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n Enter your email address for account verification.\n \n \n \n )}\n />\n (\n \n Age\n \n field.handleChange(Number(e.target.value))}\n onBlur={field.handleBlur}\n />\n \n \n Must be at least 18 years old.\n \n \n \n )}\n />\n (\n \n Bio\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n A brief description about yourself (optional).\n \n \n \n )}\n />\n
\n \n \n
\n );\n}\n\nexport default BasicInfoForm;\n", - "type": "registry:block", - "target": "components/forms/user/basic-info.tsx" + "path": "src/registry/new-york/blocks/basic-info.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useCallback } from \"react\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n email: z.string().email({\n message: \"Please enter a valid email address.\",\n }),\n age: z.number().min(8, {\n message: \"Age must be at least 18 years.\",\n }),\n bio: z.string().max(160, {\n message: \"Bio must not exceed 160 characters.\",\n }),\n});\n\nfunction BasicInfoForm() {\n const form = useAppForm({\n validators: { onBlur: FormSchema },\n defaultValues: {\n username: \"\",\n email: \"\",\n age: 0,\n bio: \"\",\n },\n onSubmit: ({ formApi, value }) => {\n formApi.reset();\n toast.success(Username: {value.username});\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n return (\n \n \n
\n (\n \n Username\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n This is your public display name.\n \n \n \n )}\n />\n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n Enter your email address for account verification.\n \n \n \n )}\n />\n (\n \n Age\n \n field.handleChange(Number(e.target.value))}\n onBlur={field.handleBlur}\n />\n \n \n Must be at least 18 years old.\n \n \n \n )}\n />\n (\n \n Bio\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n A brief description about yourself (optional).\n \n \n \n )}\n />\n
\n \n \n
\n );\n}\n\nBasicInfoForm.displayName = \"BasicInfoForm\";\n\nexport default BasicInfoForm;\n", + "type": "registry:block" } ], "categories": [ diff --git a/public/r/login-form.json b/public/r/login-form.json new file mode 100644 index 0000000..4da5c11 --- /dev/null +++ b/public/r/login-form.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "login-form", + "type": "registry:block", + "title": "Login Form", + "description": "A login form built with TanStack Form.", + "registryDependencies": [ + "https://fatahchan.github.io/shadcn-tanstack-form/r/tanstack-form.json" + ], + "files": [ + { + "path": "src/registry/new-york/blocks/auth/login-form.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { cn } from \"@/lib/utils\";\nimport {\n type ComponentProps,\n type FormHTMLAttributes,\n useCallback,\n} from \"react\";\nimport * as z from \"zod\";\n\nconst loginFormSchema = z.object({\n email: z.string(),\n password: z.string().min(1),\n});\n\ninterface LoginFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nexport default function LoginForm({\n onSubmit,\n defaultValues,\n className,\n ...props\n}: LoginFormProps) {\n const form = useAppForm({\n defaultValues: {\n email: defaultValues?.email ?? \"\",\n password: defaultValues?.password ?? \"\",\n },\n validators: { onBlur: loginFormSchema },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n return (\n \n \n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n This is your email\n \n \n )}\n />\n\n (\n \n Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n This is your password\n \n \n \n )}\n />\n \n \n \n );\n}\n", + "type": "registry:block" + } + ] +} \ No newline at end of file diff --git a/public/r/login.json b/public/r/login.json new file mode 100644 index 0000000..d6ace19 --- /dev/null +++ b/public/r/login.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "login", + "type": "registry:block", + "title": "Login", + "description": "A login form component built with TanStack Form.", + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input" + ], + "files": [ + { + "path": "src/registry/new-york/blocks/login.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { cn } from \"@/lib/utils\";\nimport { type FormHTMLAttributes, useCallback } from \"react\";\nimport * as z from \"zod\";\n\nconst loginFormSchema = z.object({\n email: z.string(),\n password: z.string().min(1),\n});\n\ninterface LoginFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nfunction LoginForm({\n onSubmit,\n\n defaultValues,\n className,\n ...props\n}: LoginFormProps) {\n const form = useAppForm({\n defaultValues: {\n email: defaultValues?.email ?? \"\",\n password: defaultValues?.password ?? \"\",\n },\n validators: { onBlur: loginFormSchema },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n return (\n \n \n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n This is your email\n \n \n )}\n />\n\n (\n \n Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n This is your password\n \n \n \n )}\n />\n \n \n \n );\n}\n\nLoginForm.displayName = \"LoginForm\";\n\nexport default LoginForm;\n", + "type": "registry:block" + } + ], + "categories": [ + "forms", + "auth" + ] +} \ No newline at end of file diff --git a/public/r/shipping-info.json b/public/r/shipping-info.json new file mode 100644 index 0000000..23f0c25 --- /dev/null +++ b/public/r/shipping-info.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "shipping-info", + "type": "registry:block", + "title": "Shipping Info", + "description": "A shipping information form component built with TanStack Form.", + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input" + ], + "files": [ + { + "path": "src/registry/new-york/blocks/shipping-info.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { useCallback } from \"react\";\nimport type { FormHTMLAttributes } from \"react\";\nimport { z } from \"zod\";\n\nconst shippingSchema = z.object({\n fullName: z.string().min(2, {\n message: \"Full name must be at least 2 characters long.\",\n }),\n addressLine1: z.string().min(5, {\n message: \"Address must be at least 5 characters long.\",\n }),\n addressLine2: z.string(),\n city: z.string().min(2, {\n message: \"City must be at least 2 characters long.\",\n }),\n state: z.string().min(2, {\n message: \"State must be at least 2 characters long.\",\n }),\n postalCode: z.string().min(4, {\n message: \"Please enter a valid postal code.\",\n }),\n phone: z.string().min(8, {\n message: \"Please enter a valid phone number.\",\n }),\n});\n\ninterface ShippingFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nfunction ShippingForm({\n onSubmit,\n defaultValues,\n className,\n ...props\n}: ShippingFormProps) {\n const form = useAppForm({\n defaultValues: {\n fullName: defaultValues?.fullName ?? \"\",\n addressLine1: defaultValues?.addressLine1 ?? \"\",\n addressLine2: defaultValues?.addressLine2 ?? \"\",\n city: defaultValues?.city ?? \"\",\n state: defaultValues?.state ?? \"\",\n postalCode: defaultValues?.postalCode ?? \"\",\n phone: defaultValues?.phone ?? \"\",\n },\n validators: { onBlur: shippingSchema },\n onSubmit: ({ value }) => {\n onSubmit(value);\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n\n return (\n \n \n (\n \n Full Name\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Address Line 1\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Address Line 2 (Optional)\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n
\n (\n \n City\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n State\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n
\n\n
\n (\n \n Postal Code\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Phone Number\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n
\n\n \n \n
\n );\n}\n\nShippingForm.displayName = \"ShippingForm\";\nShippingForm.__CATEGORIES = [\"forms\", \"checkout\"];\nShippingForm.__TITLE = \"Shipping Info\";\nShippingForm.__DESCRIPTION =\n \"A shipping information form component built with TanStack Form.\";\nShippingForm.__DEPENDENCIES = [\"zod\"];\nShippingForm.__REGISTRY_DEPENDENCIES = [\n \"https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json\",\n \"button\",\n \"input\",\n];\n\nexport default ShippingForm;\n", + "type": "registry:block" + } + ], + "categories": [ + "forms", + "checkout" + ] +} \ No newline at end of file diff --git a/public/r/sign-up.json b/public/r/sign-up.json new file mode 100644 index 0000000..1b42db8 --- /dev/null +++ b/public/r/sign-up.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "sign-up", + "type": "registry:block", + "title": "Sign Up", + "description": "A sign up form component built with TanStack Form.", + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input" + ], + "files": [ + { + "path": "src/registry/new-york/blocks/sign-up.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { useCallback } from \"react\";\nimport type { FormHTMLAttributes } from \"react\";\nimport { z } from \"zod\";\n\nconst signUpSchema = z\n .object({\n email: z.string().email({\n message: \"Please enter a valid email address.\",\n }),\n password: z.string().min(8, {\n message: \"Password must be at least 8 characters long.\",\n }),\n confirmPassword: z.string(),\n fullName: z.string().min(2, {\n message: \"Full name must be at least 2 characters long.\",\n }),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: \"Passwords do not match\",\n path: [\"confirmPassword\"],\n });\n\ninterface SignUpFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nfunction SignUpForm({\n onSubmit,\n defaultValues,\n className,\n ...props\n}: SignUpFormProps) {\n const form = useAppForm({\n defaultValues: {\n email: defaultValues?.email ?? \"\",\n password: defaultValues?.password ?? \"\",\n confirmPassword: defaultValues?.confirmPassword ?? \"\",\n fullName: defaultValues?.fullName ?? \"\",\n },\n validators: { onBlur: signUpSchema },\n onSubmit: ({ value }) => {\n onSubmit(value);\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n\n return (\n \n \n (\n \n Full Name\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Confirm Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n \n \n \n );\n}\n\nSignUpForm.displayName = \"SignUpForm\";\n\nexport default SignUpForm;\n", + "type": "registry:block" + } + ], + "categories": [ + "forms", + "auth" + ] +} \ No newline at end of file diff --git a/public/r/tanstack-form.json b/public/r/tanstack-form.json index 51ee118..0aeffd8 100644 --- a/public/r/tanstack-form.json +++ b/public/r/tanstack-form.json @@ -4,12 +4,6 @@ "type": "registry:ui", "title": "TanStack Form", "description": "A form component built with TanStack Form.", - "dependencies": [ - "@tanstack/react-form" - ], - "registryDependencies": [ - "label" - ], "files": [ { "path": "src/registry/new-york/tanstack-form/tanstack-form.tsx", diff --git a/registry.json b/registry.json index fc3f4ae..33b735f 100644 --- a/registry.json +++ b/registry.json @@ -1,37 +1,129 @@ { - "$schema": "https://ui.shadcn.com/schema/registry.json", - "name": "shadcn-tanstack-form", - "homepage": "https://fatahchan.github.io/shadcn-tanstack-form/", - "items": [ + "$schema": "https://ui.shadcn.com/schema/registry.json", + "name": "shadcn-tanstack-form", + "homepage": "https://fatahchan.github.io/shadcn-tanstack-form/", + "items": [ + { + "name": "tanstack-form", + "type": "registry:ui", + "title": "TanStack Form", + "description": "A form component built with TanStack Form.", + "files": [ { - "name": "tanstack-form", - "type": "registry:ui", - "title": "TanStack Form", - "description": "A form component built with TanStack Form.", - "files": [ - { - "path": "src/registry/new-york/tanstack-form/tanstack-form.tsx", - "type": "registry:ui" - } - ], - "dependencies": ["@tanstack/react-form"], - "registryDependencies": ["label"] - }, - { - "name": "basic-info", - "type": "registry:block", - "title": "Basic Info", - "description": "A basic info form component built with TanStack Form.", - "categories": ["forms", "user"], - "files": [ - { - "path": "src/registry/new-york/blocks/user/basic-info.tsx", - "type": "registry:block", - "target": "components/forms/user/basic-info.tsx" - } - ], - "registryDependencies": ["https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", "textarea"] - } - ] - } - \ No newline at end of file + "name": "tanstack-form", + "type": "registry:ui", + "title": "TanStack Form", + "path": "src/registry/new-york/tanstack-form/tanstack-form.tsx", + "description": "A form component built with TanStack Form.", + "dependencies": [ + "@tanstack/react-form" + ], + "registryDependencies": [ + "label" + ] + } + ] + }, + { + "name": "basic-info", + "type": "registry:block", + "title": "Basic Info", + "description": "A basic info form component built with TanStack Form.", + "files": [ + { + "type": "registry:block", + "path": "src/registry/new-york/blocks/basic-info.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useCallback } from \"react\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n email: z.string().email({\n message: \"Please enter a valid email address.\",\n }),\n age: z.number().min(8, {\n message: \"Age must be at least 18 years.\",\n }),\n bio: z.string().max(160, {\n message: \"Bio must not exceed 160 characters.\",\n }),\n});\n\nfunction BasicInfoForm() {\n const form = useAppForm({\n validators: { onBlur: FormSchema },\n defaultValues: {\n username: \"\",\n email: \"\",\n age: 0,\n bio: \"\",\n },\n onSubmit: ({ formApi, value }) => {\n formApi.reset();\n toast.success(Username: {value.username});\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n return (\n \n \n
\n (\n \n Username\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n This is your public display name.\n \n \n \n )}\n />\n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n Enter your email address for account verification.\n \n \n \n )}\n />\n (\n \n Age\n \n field.handleChange(Number(e.target.value))}\n onBlur={field.handleBlur}\n />\n \n \n Must be at least 18 years old.\n \n \n \n )}\n />\n (\n \n Bio\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n A brief description about yourself (optional).\n \n \n \n )}\n />\n
\n \n \n
\n );\n}\n\nBasicInfoForm.displayName = \"BasicInfoForm\";\n\nexport default BasicInfoForm;\n" + } + ], + "categories": [ + "forms", + "user" + ], + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input", + "textarea" + ] + }, + { + "name": "login", + "type": "registry:block", + "title": "Login", + "description": "A login form component built with TanStack Form.", + "files": [ + { + "type": "registry:block", + "path": "src/registry/new-york/blocks/login.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { cn } from \"@/lib/utils\";\nimport { type FormHTMLAttributes, useCallback } from \"react\";\nimport * as z from \"zod\";\n\nconst loginFormSchema = z.object({\n email: z.string(),\n password: z.string().min(1),\n});\n\ninterface LoginFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nfunction LoginForm({\n onSubmit,\n\n defaultValues,\n className,\n ...props\n}: LoginFormProps) {\n const form = useAppForm({\n defaultValues: {\n email: defaultValues?.email ?? \"\",\n password: defaultValues?.password ?? \"\",\n },\n validators: { onBlur: loginFormSchema },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n return (\n \n \n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n This is your email\n \n \n )}\n />\n\n (\n \n Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n This is your password\n \n \n \n )}\n />\n \n \n \n );\n}\n\nLoginForm.displayName = \"LoginForm\";\n\nexport default LoginForm;\n" + } + ], + "categories": [ + "forms", + "auth" + ], + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input" + ] + }, + { + "name": "shipping-info", + "type": "registry:block", + "title": "Shipping Info", + "description": "A shipping information form component built with TanStack Form.", + "files": [ + { + "type": "registry:block", + "path": "src/registry/new-york/blocks/shipping-info.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { useCallback } from \"react\";\nimport type { FormHTMLAttributes } from \"react\";\nimport { z } from \"zod\";\n\nconst shippingSchema = z.object({\n fullName: z.string().min(2, {\n message: \"Full name must be at least 2 characters long.\",\n }),\n addressLine1: z.string().min(5, {\n message: \"Address must be at least 5 characters long.\",\n }),\n addressLine2: z.string(),\n city: z.string().min(2, {\n message: \"City must be at least 2 characters long.\",\n }),\n state: z.string().min(2, {\n message: \"State must be at least 2 characters long.\",\n }),\n postalCode: z.string().min(4, {\n message: \"Please enter a valid postal code.\",\n }),\n phone: z.string().min(8, {\n message: \"Please enter a valid phone number.\",\n }),\n});\n\ninterface ShippingFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nfunction ShippingForm({\n onSubmit,\n defaultValues,\n className,\n ...props\n}: ShippingFormProps) {\n const form = useAppForm({\n defaultValues: {\n fullName: defaultValues?.fullName ?? \"\",\n addressLine1: defaultValues?.addressLine1 ?? \"\",\n addressLine2: defaultValues?.addressLine2 ?? \"\",\n city: defaultValues?.city ?? \"\",\n state: defaultValues?.state ?? \"\",\n postalCode: defaultValues?.postalCode ?? \"\",\n phone: defaultValues?.phone ?? \"\",\n },\n validators: { onBlur: shippingSchema },\n onSubmit: ({ value }) => {\n onSubmit(value);\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n\n return (\n \n \n (\n \n Full Name\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Address Line 1\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Address Line 2 (Optional)\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n
\n (\n \n City\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n State\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n
\n\n
\n (\n \n Postal Code\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Phone Number\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n
\n\n \n \n
\n );\n}\n\nShippingForm.displayName = \"ShippingForm\";\nShippingForm.__CATEGORIES = [\"forms\", \"checkout\"];\nShippingForm.__TITLE = \"Shipping Info\";\nShippingForm.__DESCRIPTION =\n \"A shipping information form component built with TanStack Form.\";\nShippingForm.__DEPENDENCIES = [\"zod\"];\nShippingForm.__REGISTRY_DEPENDENCIES = [\n \"https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json\",\n \"button\",\n \"input\",\n];\n\nexport default ShippingForm;\n" + } + ], + "categories": [ + "forms", + "checkout" + ], + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input" + ] + }, + { + "name": "sign-up", + "type": "registry:block", + "title": "Sign Up", + "description": "A sign up form component built with TanStack Form.", + "files": [ + { + "type": "registry:block", + "path": "src/registry/new-york/blocks/sign-up.tsx", + "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useAppForm } from \"@/components/ui/tanstack-form\";\nimport { useCallback } from \"react\";\nimport type { FormHTMLAttributes } from \"react\";\nimport { z } from \"zod\";\n\nconst signUpSchema = z\n .object({\n email: z.string().email({\n message: \"Please enter a valid email address.\",\n }),\n password: z.string().min(8, {\n message: \"Password must be at least 8 characters long.\",\n }),\n confirmPassword: z.string(),\n fullName: z.string().min(2, {\n message: \"Full name must be at least 2 characters long.\",\n }),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: \"Passwords do not match\",\n path: [\"confirmPassword\"],\n });\n\ninterface SignUpFormProps\n extends Omit, \"onSubmit\"> {\n onSubmit: (data: z.infer) => void;\n defaultValues?: z.infer;\n}\n\nfunction SignUpForm({\n onSubmit,\n defaultValues,\n className,\n ...props\n}: SignUpFormProps) {\n const form = useAppForm({\n defaultValues: {\n email: defaultValues?.email ?? \"\",\n password: defaultValues?.password ?? \"\",\n confirmPassword: defaultValues?.confirmPassword ?? \"\",\n fullName: defaultValues?.fullName ?? \"\",\n },\n validators: { onBlur: signUpSchema },\n onSubmit: ({ value }) => {\n onSubmit(value);\n },\n });\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n e.stopPropagation();\n form.handleSubmit();\n },\n [form],\n );\n\n return (\n \n \n (\n \n Full Name\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Email\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n (\n \n Confirm Password\n \n field.handleChange(e.target.value)}\n onBlur={field.handleBlur}\n />\n \n \n \n )}\n />\n\n \n \n \n );\n}\n\nSignUpForm.displayName = \"SignUpForm\";\n\nexport default SignUpForm;\n" + } + ], + "categories": [ + "forms", + "auth" + ], + "dependencies": [ + "zod" + ], + "registryDependencies": [ + "https://shadcn-tanstack-form.netlify.app/r/tanstack-form.json", + "button", + "input" + ] + } + ] +} \ No newline at end of file diff --git a/src/components/block-preview.tsx b/src/components/block-preview.tsx index 94b63e4..adec353 100644 --- a/src/components/block-preview.tsx +++ b/src/components/block-preview.tsx @@ -7,11 +7,12 @@ import { import { useIframeHeight } from "@/hooks/use-iframe-height"; import { usePreload } from "@/hooks/use-preload"; import { cn } from "@/lib/utils"; +import type { RegistryItem } from "@/schemas/registry-item"; import * as RadioGroup from "@radix-ui/react-radio-group"; import { Link } from "@tanstack/react-router"; import { Check, Code2, Copy, Eye, Maximize, Terminal } from "lucide-react"; import type React from "react"; -import { lazy, useRef, useState } from "react"; +import { lazy, useEffect, useRef, useState } from "react"; import { type ImperativePanelGroupHandle, Panel, @@ -24,14 +25,7 @@ import { Button } from "./ui/button"; const Iframe = lazy(() => import("./iframe").then((module) => ({ default: module.Iframe })), ); -type Block = { - code?: string; - preview: string; - title?: string; - category?: string; - previewOnly?: boolean; - slug: string; -}; + const radioItem = "rounded-(--radius) duration-200 flex items-center justify-center h-8 px-2.5 gap-2 transition-[color] data-[state=checked]:bg-muted"; @@ -42,19 +36,20 @@ const LGSIZE = 82; const DEFAULT_HEIGHT = 224; -export const BlockPreview: React.FC = ({ - code, - preview, - title, - category, - previewOnly, - slug, -}) => { +export const BlockPreview: React.FC< + RegistryItem & { previewOnly?: boolean; codeOnly?: boolean } +> = ({ previewOnly, codeOnly, ...props }) => { + const { name, files, title } = props; + const content = + files?.find((file) => file.type === "registry:block")?.content || ""; + + const preview = `/preview/${name}`; + const [width, setWidth] = useState(DEFAULTSIZE); const [mode, setMode] = useState<"preview" | "code">("preview"); - const { iframeHeight, measureRef } = useIframeHeight(preview, DEFAULT_HEIGHT); + const { iframeHeight, measureRef } = useIframeHeight(name, DEFAULT_HEIGHT); - const terminalCode = `pnpm dlx shadcn@canary add https://shadcn-tanstack-form.netlify.app/r/${slug}.json`; + const terminalCode = `pnpm dlx shadcn@canary add https://shadcn-tanstack-form.netlify.app/r/${name}.json`; const [copied, copy] = useCopyToClipboard(); const [cliCopied, cliCopy] = useCopyToClipboard(); @@ -66,8 +61,13 @@ export const BlockPreview: React.FC = ({ root: null, rootMargin: "0px", }); - const shouldLoadIframe = entry?.isIntersecting ?? false; - console.log(shouldLoadIframe); + const shouldLoadIframe = entry?.isIntersecting; + + useEffect(() => { + if (shouldLoadIframe) { + blockRef(null); + } + }, [blockRef, shouldLoadIframe]); usePreload({ link: preview, @@ -79,7 +79,7 @@ export const BlockPreview: React.FC = ({
@@ -87,19 +87,23 @@ export const BlockPreview: React.FC = ({
- {code && ( + {content && ( <> - setMode("preview")} - aria-label="Block preview" - value="100" - checked={mode === "preview"} - className={radioItem} - > - - Preview - + {!codeOnly && ( + setMode("preview")} + aria-label="Block preview" + value="100" + checked={mode === "preview"} + className={radioItem} + > + + + Preview + + + )} setMode("code")} @@ -145,7 +149,7 @@ export const BlockPreview: React.FC = ({
- {code && ( + {content && ( <>