diff --git a/README.md b/README.md index f8e423038b..f7efd9d098 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ - [Bootstrap 3](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/core) - [React-Bootstrap (Bootstrap 5)](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/react-bootstrap) - [Chakra UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/chakra-ui) +- [Daisy UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/daisyui) - [Fluent UI 9](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/fluentui-rc) - [Material UI 5](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/mui) - [Semantic UI](https://github.com/rjsf-team/react-jsonschema-form/tree/main/packages/semantic-ui) diff --git a/package-lock.json b/package-lock.json index c73bac8a94..efde17135d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "packages/antd", "packages/chakra-ui", "packages/core", + "packages/daisyui", "packages/docs", "packages/fluentui-rc", "packages/mui", @@ -21,6 +22,9 @@ "packages/validator-ajv8", "packages/snapshot-tests" ], + "dependencies": { + "tailwindcss": "^4.0.1" + }, "devDependencies": { "@babel/eslint-parser": "^7.23.10", "@nrwl/nx-cloud": "^15.3.5", @@ -3247,6 +3251,12 @@ "node": ">=10" } }, + "node_modules/@date-fns/tz": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz", + "integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==", + "license": "MIT" + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -5889,6 +5899,52 @@ "@swc/helpers": "^0.5.1" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", + "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", + "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", + "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -9200,6 +9256,10 @@ "resolved": "packages/core", "link": true }, + "node_modules/@rjsf/daisyui": { + "resolved": "packages/daisyui", + "link": true + }, "node_modules/@rjsf/docs": { "resolved": "packages/docs", "link": true @@ -9803,6 +9863,239 @@ "node": ">=6" } }, + "node_modules/@tailwindcss/node": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.6.tgz", + "integrity": "sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q==", + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.0", + "jiti": "^2.4.2", + "tailwindcss": "4.0.6" + } + }, + "node_modules/@tailwindcss/node/node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.6.tgz", + "integrity": "sha512-lVyKV2y58UE9CeKVcYykULe9QaE1dtKdxDEdrTPIdbzRgBk6bdxHNAoDqvcqXbIGXubn3VOl1O/CFF77v/EqSA==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.0.6", + "@tailwindcss/oxide-darwin-arm64": "4.0.6", + "@tailwindcss/oxide-darwin-x64": "4.0.6", + "@tailwindcss/oxide-freebsd-x64": "4.0.6", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.6", + "@tailwindcss/oxide-linux-arm64-gnu": "4.0.6", + "@tailwindcss/oxide-linux-arm64-musl": "4.0.6", + "@tailwindcss/oxide-linux-x64-gnu": "4.0.6", + "@tailwindcss/oxide-linux-x64-musl": "4.0.6", + "@tailwindcss/oxide-win32-arm64-msvc": "4.0.6", + "@tailwindcss/oxide-win32-x64-msvc": "4.0.6" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.6.tgz", + "integrity": "sha512-xDbym6bDPW3D2XqQqX3PjqW3CKGe1KXH7Fdkc60sX5ZLVUbzPkFeunQaoP+BuYlLc2cC1FoClrIRYnRzof9Sow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.6.tgz", + "integrity": "sha512-1f71/ju/tvyGl5c2bDkchZHy8p8EK/tDHCxlpYJ1hGNvsYihZNurxVpZ0DefpN7cNc9RTT8DjrRoV8xXZKKRjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.6.tgz", + "integrity": "sha512-s/hg/ZPgxFIrGMb0kqyeaqZt505P891buUkSezmrDY6lxv2ixIELAlOcUVTkVh245SeaeEiUVUPiUN37cwoL2g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.6.tgz", + "integrity": "sha512-Z3Wo8FWZnmio8+xlcbb7JUo/hqRMSmhQw8IGIRoRJ7GmLR0C+25Wq+bEX/135xe/yEle2lFkhu9JBHd4wZYiig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.6.tgz", + "integrity": "sha512-SNSwkkim1myAgmnbHs4EjXsPL7rQbVGtjcok5EaIzkHkCAVK9QBQsWeP2Jm2/JJhq4wdx8tZB9Y7psMzHYWCkA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.6.tgz", + "integrity": "sha512-tJ+mevtSDMQhKlwCCuhsFEFg058kBiSy4TkoeBG921EfrHKmexOaCyFKYhVXy4JtkaeeOcjJnCLasEeqml4i+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.6.tgz", + "integrity": "sha512-IoArz1vfuTR4rALXMUXI/GWWfx2EaO4gFNtBNkDNOYhlTD4NVEwE45nbBoojYiTulajI4c2XH8UmVEVJTOJKxA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.6.tgz", + "integrity": "sha512-QtsUfLkEAeWAC3Owx9Kg+7JdzE+k9drPhwTAXbXugYB9RZUnEWWx5x3q/au6TvUYcL+n0RBqDEO2gucZRvRFgQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.6.tgz", + "integrity": "sha512-QthvJqIji2KlGNwLcK/PPYo7w1Wsi/8NK0wAtRGbv4eOPdZHkQ9KUk+oCoP20oPO7i2a6X1aBAFQEL7i08nNMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.6.tgz", + "integrity": "sha512-+oka+dYX8jy9iP00DJ9Y100XsqvbqR5s0yfMZJuPR1H/lDVtDfsZiSix1UFBQ3X1HWxoEEl6iXNJHWd56TocVw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.6.tgz", + "integrity": "sha512-+o+juAkik4p8Ue/0LiflQXPmVatl6Av3LEZXpBTfg4qkMIbZdhCGWFzHdt2NjoMiLOJCFDddoV6GYaimvK1Olw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.6.tgz", + "integrity": "sha512-O25vZ/URWbZ2JHdk2o8wH7jOKqEGCsYmX3GwGmYS5DjE4X3mpf93a72Rn7VRnefldNauBzr5z2hfZptmBNtTUQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "^4.0.6", + "@tailwindcss/oxide": "^4.0.6", + "lightningcss": "^1.29.1", + "tailwindcss": "4.0.6" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, "node_modules/@testing-library/dom": { "version": "9.3.4", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", @@ -14515,6 +14808,16 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/daisyui": { + "version": "5.0.0-beta.7", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.0-beta.7.tgz", + "integrity": "sha512-AW158KxukhyWwU9HTlfNkjAa9nPcfUZilITK7Taa3pqwVH/Suq31+1FHVcw7zLVRRw5jBUKrJ76xBQJOcRiKgQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/saadeghi/daisyui?sponsor=1" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -14559,6 +14862,12 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -14882,6 +15191,18 @@ "node": ">=4" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -15246,9 +15567,10 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -15498,6 +15820,346 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -21548,6 +22210,234 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/lightningcss": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", + "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.1", + "lightningcss-darwin-x64": "1.29.1", + "lightningcss-freebsd-x64": "1.29.1", + "lightningcss-linux-arm-gnueabihf": "1.29.1", + "lightningcss-linux-arm64-gnu": "1.29.1", + "lightningcss-linux-arm64-musl": "1.29.1", + "lightningcss-linux-x64-gnu": "1.29.1", + "lightningcss-linux-x64-musl": "1.29.1", + "lightningcss-win32-arm64-msvc": "1.29.1", + "lightningcss-win32-x64-msvc": "1.29.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", + "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", + "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", + "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", + "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", + "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", + "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", + "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", + "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", + "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", + "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -27362,6 +28252,37 @@ "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-day-picker": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.5.1.tgz", + "integrity": "sha512-PxuK8inYLlYgM2zZUVBPsaBM5jI40suPeG+naKyx7kpyF032RRlEAUEjkpW9/poTASh/vyWAOVqjGuGw+47isw==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.2.0", + "date-fns": "^4.1.0", + "date-fns-jalali": "^4.1.0-0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/react-day-picker/node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/react-deep-force-update": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.2.tgz", @@ -30486,6 +31407,12 @@ "tslib": "^2.3.1" } }, + "node_modules/tailwindcss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.6.tgz", + "integrity": "sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw==", + "license": "MIT" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -34294,6 +35221,103 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "packages/daisyui": { + "name": "@rjsf/daisyui", + "version": "6.0.0-alpha.1", + "license": "Apache-2.0", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.7.2", + "@fortawesome/free-solid-svg-icons": "^6.7.2", + "@fortawesome/react-fontawesome": "^0.2.2", + "@tailwindcss/vite": "^4.0.6", + "react": "^18.2.0", + "react-day-picker": "^9.5.1", + "tailwindcss": "^4.0.6" + }, + "devDependencies": { + "daisyui": "^5.0.0-beta.7", + "esbuild": "^0.14.0", + "rollup": "^2.70.0", + "typescript": "^4.9.5" + }, + "peerDependencies": { + "@rjsf/core": "^6.x", + "@rjsf/utils": "^6.x", + "primeicons": ">=6.0.0", + "primereact": ">=8.0.0", + "react": "^18.2.0" + } + }, + "packages/daisyui/node_modules/@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "packages/daisyui/node_modules/esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "packages/daisyui/node_modules/rollup": { + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "packages/docs": { "name": "@rjsf/docs", "version": "6.0.0-alpha.0", diff --git a/package.json b/package.json index df57fb59f3..8a5c95919c 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "packages/antd", "packages/chakra-ui", "packages/core", + "packages/daisyui", "packages/docs", "packages/fluentui-rc", "packages/mui", @@ -77,5 +78,9 @@ "packages/validator-ajv6", "packages/validator-ajv8", "packages/snapshot-tests" - ] + ], + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e", + "dependencies": { + "tailwindcss": "^4.0.1" + } } diff --git a/packages/daisyui/.eslintrc b/packages/daisyui/.eslintrc new file mode 100644 index 0000000000..1a6d515210 --- /dev/null +++ b/packages/daisyui/.eslintrc @@ -0,0 +1,18 @@ +{ + "extends": ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "react"], + "env": { + "browser": true, + "es2021": true + }, + "settings": { + "react": { + "version": "detect" + } + }, + "rules": { + "react/prop-types": "off", + "@typescript-eslint/explicit-module-boundary-types": "off" + } +} diff --git a/packages/daisyui/README.md b/packages/daisyui/README.md new file mode 100644 index 0000000000..5177600dea --- /dev/null +++ b/packages/daisyui/README.md @@ -0,0 +1,70 @@ +# @rjsf/daisyui + +A [DaisyUI](https://daisyui.com/) theme for [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form/). + +**Warning:** This integrates DaisyUI v5.0.0-beta.7. This is not yet released and is subject to change. It also integrates tailwindcss v4.0.6. + +## Features + +- Complete DaisyUI styling for all form elements +- Support for custom themes via DaisyUI theme system +- Responsive form layouts +- Support for all RJSF field types including: + - Text inputs + - Select dropdowns (with examples support) + - Checkboxes and radio buttons + - Arrays and objects + - Enumerated objects + - Custom array handling + +## Installation + +```bash +npm install @rjsf/daisyui @rjsf/core @rjsf/utils +``` + +## Usage + +```jsx +import { Form } from '@rjsf/daisyui'; +import validator from '@rjsf/validator-ajv8'; + +function App() { + return ( +
+ ); +} +``` + +## Theme Customization + +The form components use DaisyUI's theme system. You can customize the theme by adding DaisyUI theme classes to your HTML: + +```html + + + +``` + +## Development + +```bash +# Install dependencies +npm install + +# Build the package +npm run build + +# Run tests +npm test +``` + +## License + +Apache-2.0 diff --git a/packages/daisyui/babel.config.js b/packages/daisyui/babel.config.js new file mode 100644 index 0000000000..b1d9a0258a --- /dev/null +++ b/packages/daisyui/babel.config.js @@ -0,0 +1,11 @@ +module.exports = { + presets: [ + '@babel/preset-env', + '@babel/preset-react', + '@babel/preset-typescript' + ], + plugins: [ + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-object-rest-spread' + ] +}; diff --git a/packages/daisyui/package.json b/packages/daisyui/package.json new file mode 100644 index 0000000000..6e4b4d7cfa --- /dev/null +++ b/packages/daisyui/package.json @@ -0,0 +1,42 @@ +{ + "name": "@rjsf/daisyui", + "version": "6.0.0-alpha.1", + "description": "Daisy UI components for react-jsonschema-form", + "main": "src/index.ts", + "types": "src/index.ts", + "scripts": { + "build": "tsc -b", + "build:cjs": "esbuild ./src/index.ts --bundle --outfile=dist/index.js --sourcemap --packages=external --format=cjs", + "build:esm": "esbuild ./src/index.ts --bundle --outfile=dist/daisy-ui.esm.js --sourcemap --packages=external --format=esm", + "build:umd": "rollup dist/daisy-ui.esm.js --format=umd --file=dist/daisy-ui.umd.js --name=@rjsf/daisy-ui", + "dev": "vite" + }, + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.7.2", + "@fortawesome/free-solid-svg-icons": "^6.7.2", + "@fortawesome/react-fontawesome": "^0.2.2", + "@tailwindcss/vite": "^4.0.6", + "react": "^18.2.0", + "react-day-picker": "^9.5.1", + "tailwindcss": "^4.0.6" + }, + "peerDependencies": { + "react": "^18.2.0", + "@rjsf/core": "^6.x", + "@rjsf/utils": "^6.x", + "primeicons": ">=6.0.0", + "primereact": ">=8.0.0" + }, + "devDependencies": { + "daisyui": "^5.0.0-beta.7", + "esbuild": "^0.14.0", + "rollup": "^2.70.0", + "typescript": "^4.9.5" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/rjsf-team/react-jsonschema-form.git" + }, + "author": "Your Name ", + "license": "Apache-2.0" +} diff --git a/packages/daisyui/src/DaisyUIFrameProvider.tsx b/packages/daisyui/src/DaisyUIFrameProvider.tsx new file mode 100644 index 0000000000..68db55ed18 --- /dev/null +++ b/packages/daisyui/src/DaisyUIFrameProvider.tsx @@ -0,0 +1,71 @@ +/** + * __createDaisyUIFrameProvider is used to ensure that DaisyUI styles + * can be rendered within an iframe without affecting the parent page. + * Required for using DaisyUI in the playground. + * + * NOTE: This is an internal component only used by @rjsf/playground + */ + +import React from 'react'; + +interface DaisyUIFrameProviderProps { + children: React.ReactNode; + subtheme?: { dataTheme?: string } | null; +} + +export const __createDaisyUIFrameProvider = (props: DaisyUIFrameProviderProps) => { + return function DaisyUIFrame({ document }: { document?: Document }) { + // Get theme from localStorage or use default + const theme = (() => { + try { + if (props.subtheme?.dataTheme) { + localStorage.setItem('daisyui-theme', props.subtheme.dataTheme); + return props.subtheme.dataTheme; + } + return localStorage.getItem('daisyui-theme') || 'cupcake'; + } catch { + return 'cupcake'; + } + })(); + + if (document) { + // Add Tailwind first + const tailwindScript = document.createElement('script'); + tailwindScript.src = 'https://unpkg.com/@tailwindcss/browser@4.0.6'; + document.head.appendChild(tailwindScript); + + // Add DaisyUI CSS + const daisyLink = document.createElement('link'); + daisyLink.rel = 'stylesheet'; + daisyLink.href = 'https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.7/daisyui.css'; + document.head.appendChild(daisyLink); + + // Add all themes + const daisyLinkAllThemes = document.createElement('link'); + daisyLinkAllThemes.rel = 'stylesheet'; + daisyLinkAllThemes.href = 'https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.7/themes.css'; + document.head.appendChild(daisyLinkAllThemes); + + // Configure Tailwind + const configScript = document.createElement('script'); + configScript.textContent = ` + window.tailwind = window.tailwind || {}; + window.tailwind.config = { + daisyui: { + themes: true, + }, + future: { + disableProductionWarning: true + } + } + `; + document.head.appendChild(configScript); + } + + return ( +
+ {props.children} +
+ ); + }; +}; diff --git a/packages/daisyui/src/daisyForm/DaisyForm.tsx b/packages/daisyui/src/daisyForm/DaisyForm.tsx new file mode 100644 index 0000000000..c5d1f5cae0 --- /dev/null +++ b/packages/daisyui/src/daisyForm/DaisyForm.tsx @@ -0,0 +1,26 @@ +import { ComponentType } from 'react'; +import { withTheme, FormProps } from '@rjsf/core'; +import { generateTheme } from '../theme'; +import { FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils'; + +export function generateForm< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): ComponentType> { + const theme = generateTheme(); + return withTheme(theme); +} + +const Form = generateForm(); + +export { Form, generateTheme }; + +export default Form; + +function selectWidget(schema: RJSFSchema, widget?: string) { + if (schema.type === 'array' && schema.uniqueItems && schema.items && (schema.items as RJSFSchema).enum) { + return 'checkboxes'; + } + return widget; +} diff --git a/packages/daisyui/src/daisyForm/index.ts b/packages/daisyui/src/daisyForm/index.ts new file mode 100644 index 0000000000..7bea340e73 --- /dev/null +++ b/packages/daisyui/src/daisyForm/index.ts @@ -0,0 +1,2 @@ +export { default } from './DaisyForm'; +export * from './DaisyForm'; \ No newline at end of file diff --git a/packages/daisyui/src/index.ts b/packages/daisyui/src/index.ts new file mode 100644 index 0000000000..fd454139a0 --- /dev/null +++ b/packages/daisyui/src/index.ts @@ -0,0 +1,30 @@ +import Form from '@rjsf/core'; + +export { default as Form, generateForm } from './daisyForm/DaisyForm'; +export { __createDaisyUIFrameProvider } from './DaisyUIFrameProvider'; +export { default as ArrayFieldItemButtonsTemplate } from './templates/ArrayFieldItemButtonsTemplate'; +export { default as ArrayFieldItemTemplate } from './templates/ArrayFieldItemTemplate'; +export { default as BaseInputTemplate } from './templates/BaseInputTemplate/BaseInputTemplate'; +export * from './templates/ButtonTemplates'; +export { default as buttonTemplates } from './templates/ButtonTemplates'; +export { default as DescriptionField } from './templates/DescriptionField'; +export { default as ErrorList } from './templates/ErrorList'; +export { default as FieldErrorTemplate } from './templates/FieldErrorTemplate'; +export { default as FieldHelpTemplate } from './templates/FieldHelpTemplate'; +export { default as FieldTemplate } from './templates/FieldTemplate'; +export { default as ObjectFieldTemplate } from './templates/ObjectFieldTemplate'; +export { generateTemplates } from './templates/Templates'; +export { default as TitleField } from './templates/TitleField'; +export { default as WrapIfAdditionalTemplate } from './templates/WrapIfAdditionalTemplate'; +export { generateTheme, default as Theme } from './theme/Theme'; +export { default as CheckboxesWidget } from './widgets/CheckboxesWidget/CheckboxesWidget'; +export { default as CheckboxWidget } from './widgets/CheckboxWidget/CheckboxWidget'; +export { default as RadioWidget } from './widgets/RadioWidget/RadioWidget'; +export { default as RangeWidget } from './widgets/RangeWidget/RangeWidget'; +export { default as SelectWidget } from './widgets/SelectWidget/SelectWidget'; +export { default as TextAreaWidget } from './widgets/TextareaWidget/TextareaWidget'; +export { generateWidgets, default as Widgets } from './widgets/Widgets'; + +export type { DaisyUiSchema as UiSchema } from './utils'; + +export default Form; diff --git a/packages/daisyui/src/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.tsx b/packages/daisyui/src/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.tsx new file mode 100644 index 0000000000..1f2daaca32 --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.tsx @@ -0,0 +1,18 @@ +import { FormContextType, StrictRJSFSchema, RJSFSchema, ArrayFieldDescriptionProps } from '@rjsf/utils'; + +const ArrayFieldDescriptionTemplate = < + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>( + props: ArrayFieldDescriptionProps +) => { + const { description } = props; + return ( +
+

{description}

+
+ ); +}; + +export default ArrayFieldDescriptionTemplate; diff --git a/packages/daisyui/src/templates/ArrayFieldDescriptionTemplate/index.ts b/packages/daisyui/src/templates/ArrayFieldDescriptionTemplate/index.ts new file mode 100644 index 0000000000..1c87aacefb --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldDescriptionTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldDescriptionTemplate'; +export * from './ArrayFieldDescriptionTemplate'; diff --git a/packages/daisyui/src/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.tsx b/packages/daisyui/src/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.tsx new file mode 100644 index 0000000000..48353c07d2 --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.tsx @@ -0,0 +1,80 @@ +import { useMemo } from 'react'; +import { + FormContextType, + RJSFSchema, + StrictRJSFSchema, + ArrayFieldItemButtonsTemplateType, + buttonId, +} from '@rjsf/utils'; + +export default function ArrayFieldItemButtonsTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldItemButtonsTemplateType) { + const { + disabled, + hasCopy, + hasMoveDown, + hasMoveUp, + hasRemove, + idSchema, + index, + onCopyIndexClick, + onDropIndexClick, + onReorderClick, + readonly, + registry, + uiSchema, + } = props; + const { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates; + const onCopyClick = useMemo(() => onCopyIndexClick(index), [index, onCopyIndexClick]); + const onRemoveClick = useMemo(() => onDropIndexClick(index), [index, onDropIndexClick]); + const onArrowUpClick = useMemo(() => onReorderClick(index, index - 1), [index, onReorderClick]); + const onArrowDownClick = useMemo(() => onReorderClick(index, index + 1), [index, onReorderClick]); + + return ( + <> + {(hasMoveUp || hasMoveDown) && ( + (idSchema, 'moveUp')} + className='btn btn-sm btn-ghost' + disabled={disabled || readonly || !hasMoveUp} + onClick={onArrowUpClick} + uiSchema={uiSchema} + registry={registry} + /> + )} + {(hasMoveUp || hasMoveDown) && ( + (idSchema, 'moveDown')} + className='btn btn-sm btn-ghost' + disabled={disabled || readonly || !hasMoveDown} + onClick={onArrowDownClick} + uiSchema={uiSchema} + registry={registry} + /> + )} + {hasCopy && ( + (idSchema, 'copy')} + className='btn btn-sm btn-ghost' + disabled={disabled || readonly} + onClick={onCopyClick} + uiSchema={uiSchema} + registry={registry} + /> + )} + {hasRemove && ( + (idSchema, 'remove')} + className='btn btn-sm btn-ghost btn-error' + disabled={disabled || readonly} + onClick={onRemoveClick} + uiSchema={uiSchema} + registry={registry} + /> + )} + + ); +} diff --git a/packages/daisyui/src/templates/ArrayFieldItemButtonsTemplate/index.ts b/packages/daisyui/src/templates/ArrayFieldItemButtonsTemplate/index.ts new file mode 100644 index 0000000000..477fb0853b --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldItemButtonsTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldItemButtonsTemplate'; +export * from './ArrayFieldItemButtonsTemplate'; \ No newline at end of file diff --git a/packages/daisyui/src/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx b/packages/daisyui/src/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx new file mode 100644 index 0000000000..20147519e5 --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx @@ -0,0 +1,40 @@ +import { CSSProperties } from 'react'; +import { + ArrayFieldItemTemplateType, + FormContextType, + getTemplate, + getUiOptions, + RJSFSchema, + StrictRJSFSchema, +} from '@rjsf/utils'; + +/** The `ArrayFieldItemTemplate` component is the template used to render an items of an array. + * + * @param props - The `ArrayFieldItemTemplateType` props for the component + */ +export default function ArrayFieldItemTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldItemTemplateType) { + const { children, className, buttonsProps, hasToolbar, registry, uiSchema } = props; + const uiOptions = getUiOptions(uiSchema); + const ArrayFieldItemButtonsTemplate = getTemplate<'ArrayFieldItemButtonsTemplate', T, S, F>( + 'ArrayFieldItemButtonsTemplate', + registry, + uiOptions + ); + + return ( +
+
{children}
+ {hasToolbar && ( +
+
+ +
+
+ )} +
+ ); +} diff --git a/packages/daisyui/src/templates/ArrayFieldItemTemplate/index.ts b/packages/daisyui/src/templates/ArrayFieldItemTemplate/index.ts new file mode 100644 index 0000000000..f104431399 --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldItemTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldItemTemplate'; +export * from './ArrayFieldItemTemplate'; diff --git a/packages/daisyui/src/templates/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/packages/daisyui/src/templates/ArrayFieldTemplate/ArrayFieldTemplate.tsx new file mode 100644 index 0000000000..4383039334 --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldTemplate/ArrayFieldTemplate.tsx @@ -0,0 +1,72 @@ +import { ArrayFieldTemplateProps, FormContextType, RJSFSchema, StrictRJSFSchema, getUiOptions } from '@rjsf/utils'; +import ArrayFieldTitleTemplate from '../ArrayFieldTitleTemplate/ArrayFieldTitleTemplate'; +import ArrayFieldDescriptionTemplate from '../ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate'; +import ArrayFieldItemTemplate from '../ArrayFieldItemTemplate/ArrayFieldItemTemplate'; + +/** The `ArrayFieldTemplate` component is the template used to render all items in an array. + * + * @param props - The `ArrayFieldItemTemplateType` props for the component + */ +export default function ArrayFieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldTemplateProps) { + const { + canAdd, + className, + disabled, + idSchema, + uiSchema, + items, + onAddClick, + readonly, + registry, + required, + schema, + title, + } = props; + + const uiOptions = getUiOptions(uiSchema); + + // Get templates directly from registry + const { + ButtonTemplates: { AddButton }, + } = registry.templates; + + return ( +
+ + +
+ {items.map((item) => ( + + ))} +
+ {canAdd && ( +
+ +
+ )} +
+ ); +} diff --git a/packages/daisyui/src/templates/ArrayFieldTemplate/index.ts b/packages/daisyui/src/templates/ArrayFieldTemplate/index.ts new file mode 100644 index 0000000000..ab908dec2c --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldTemplate'; +export * from './ArrayFieldTemplate'; diff --git a/packages/daisyui/src/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx b/packages/daisyui/src/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx new file mode 100644 index 0000000000..bad2a8beff --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx @@ -0,0 +1,10 @@ +import { ArrayFieldTitleProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function ArrayFieldTitleTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldTitleProps) { + const { title } = props; + return

{title}

; +} diff --git a/packages/daisyui/src/templates/ArrayFieldTitleTemplate/index.ts b/packages/daisyui/src/templates/ArrayFieldTitleTemplate/index.ts new file mode 100644 index 0000000000..9893e24393 --- /dev/null +++ b/packages/daisyui/src/templates/ArrayFieldTitleTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ArrayFieldTitleTemplate'; +export * from './ArrayFieldTitleTemplate'; diff --git a/packages/daisyui/src/templates/BaseInputTemplate/BaseInputTemplate.tsx b/packages/daisyui/src/templates/BaseInputTemplate/BaseInputTemplate.tsx new file mode 100644 index 0000000000..49680a5b64 --- /dev/null +++ b/packages/daisyui/src/templates/BaseInputTemplate/BaseInputTemplate.tsx @@ -0,0 +1,25 @@ +import { WidgetProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function BaseInputTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { + const { id, value, required, disabled, readonly, onChange, label } = props; + return ( +
+ + onChange(event.target.value)} + className='input input-bordered' + /> +
+ ); +} diff --git a/packages/daisyui/src/templates/BaseInputTemplate/index.ts b/packages/daisyui/src/templates/BaseInputTemplate/index.ts new file mode 100644 index 0000000000..f7ef8d5939 --- /dev/null +++ b/packages/daisyui/src/templates/BaseInputTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './BaseInputTemplate'; +export * from './BaseInputTemplate'; diff --git a/packages/daisyui/src/templates/ButtonTemplates/AddButton.tsx b/packages/daisyui/src/templates/ButtonTemplates/AddButton.tsx new file mode 100644 index 0000000000..e2facaceba --- /dev/null +++ b/packages/daisyui/src/templates/ButtonTemplates/AddButton.tsx @@ -0,0 +1,32 @@ +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faArrowDown, faArrowUp, faCopy, faTrash, faAdd } from '@fortawesome/free-solid-svg-icons'; +import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils'; +import DaisyUIButton from './DaisyUIButton'; + +library.add(faAdd, faCopy, faArrowDown, faArrowUp, faTrash); + +/** The `AddButton` renders a button that represent the `Add` action on a form + */ +export default function AddButton({ + className, + onClick, + disabled, + registry, +}: IconButtonProps) { + const { translateString } = registry; + return ( +
+

+ +

+
+ ); +} diff --git a/packages/daisyui/src/templates/ButtonTemplates/DaisyUIButton.tsx b/packages/daisyui/src/templates/ButtonTemplates/DaisyUIButton.tsx new file mode 100644 index 0000000000..d6cd73426e --- /dev/null +++ b/packages/daisyui/src/templates/ButtonTemplates/DaisyUIButton.tsx @@ -0,0 +1,24 @@ +import { memo } from 'react'; +import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { IconDefinition } from '@fortawesome/fontawesome-svg-core'; + +interface DaisyUIButtonProps + extends Omit, 'icon'> { + icon: IconDefinition; +} + +function DaisyUIButton( + props: DaisyUIButtonProps +) { + const { icon, iconType, uiSchema, registry, ...otherProps } = props; + return ( + + ); +} + +DaisyUIButton.displayName = 'DaisyUIButton'; + +export default memo(DaisyUIButton) as typeof DaisyUIButton; diff --git a/packages/daisyui/src/templates/ButtonTemplates/IconButton.tsx b/packages/daisyui/src/templates/ButtonTemplates/IconButton.tsx new file mode 100644 index 0000000000..5163e76774 --- /dev/null +++ b/packages/daisyui/src/templates/ButtonTemplates/IconButton.tsx @@ -0,0 +1,46 @@ +import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils'; +import DaisyUIButton from './DaisyUIButton'; +import { faCopy, faArrowDown, faArrowUp, faTrash, faPlus } from '@fortawesome/free-solid-svg-icons'; + +export function CopyButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props; + return ; +} + +export function MoveDownButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props; + return ; +} + +export function MoveUpButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props; + return ; +} + +export function RemoveButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props; + return ( + + ); +} diff --git a/packages/daisyui/src/templates/ButtonTemplates/SubmitButton.tsx b/packages/daisyui/src/templates/ButtonTemplates/SubmitButton.tsx new file mode 100644 index 0000000000..00e6b3a10b --- /dev/null +++ b/packages/daisyui/src/templates/ButtonTemplates/SubmitButton.tsx @@ -0,0 +1,25 @@ +import { getSubmitButtonOptions, FormContextType, RJSFSchema, StrictRJSFSchema, SubmitButtonProps } from '@rjsf/utils'; + +/** The `SubmitButton` renders a button that represent the `Submit` action on a form + */ +export default function SubmitButton< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ uiSchema }: SubmitButtonProps) { + const { submitText, norender, props: submitButtonProps = {} } = getSubmitButtonOptions(uiSchema); + if (norender) { + return null; + } + return ( +
+ +
+ ); +} diff --git a/packages/daisyui/src/templates/ButtonTemplates/index.ts b/packages/daisyui/src/templates/ButtonTemplates/index.ts new file mode 100644 index 0000000000..7d9a9dacc0 --- /dev/null +++ b/packages/daisyui/src/templates/ButtonTemplates/index.ts @@ -0,0 +1,26 @@ +import { FormContextType, RJSFSchema, StrictRJSFSchema, TemplatesType } from '@rjsf/utils'; + +import SubmitButton from './SubmitButton'; +import AddButton from './AddButton'; +import { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from './IconButton'; + +function buttonTemplates< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): TemplatesType['ButtonTemplates'] { + return { + SubmitButton, + AddButton, + CopyButton, + MoveDownButton, + MoveUpButton, + RemoveButton, + }; +} + +export { default as AddButton } from './AddButton'; +export { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from './IconButton'; +export { default as SubmitButton } from './SubmitButton'; + +export default buttonTemplates; diff --git a/packages/daisyui/src/templates/DescriptionField/DescriptionField.tsx b/packages/daisyui/src/templates/DescriptionField/DescriptionField.tsx new file mode 100644 index 0000000000..fa9fb754d9 --- /dev/null +++ b/packages/daisyui/src/templates/DescriptionField/DescriptionField.tsx @@ -0,0 +1,14 @@ +import { DescriptionFieldProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function DescriptionField< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: DescriptionFieldProps) { + const { id, description } = props; + return ( +
+

{description}

+
+ ); +} diff --git a/packages/daisyui/src/templates/DescriptionField/index.ts b/packages/daisyui/src/templates/DescriptionField/index.ts new file mode 100644 index 0000000000..401540d99b --- /dev/null +++ b/packages/daisyui/src/templates/DescriptionField/index.ts @@ -0,0 +1,2 @@ +export { default } from './DescriptionField'; +export * from './DescriptionField'; diff --git a/packages/daisyui/src/templates/ErrorList/ErrorList.tsx b/packages/daisyui/src/templates/ErrorList/ErrorList.tsx new file mode 100644 index 0000000000..09610ea647 --- /dev/null +++ b/packages/daisyui/src/templates/ErrorList/ErrorList.tsx @@ -0,0 +1,16 @@ +import { ErrorListProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function ErrorList( + props: ErrorListProps +) { + const { errors } = props; + return ( +
+
    + {errors.map((error, index) => ( +
  • {error.stack}
  • + ))} +
+
+ ); +} diff --git a/packages/daisyui/src/templates/ErrorList/index.ts b/packages/daisyui/src/templates/ErrorList/index.ts new file mode 100644 index 0000000000..79376ace11 --- /dev/null +++ b/packages/daisyui/src/templates/ErrorList/index.ts @@ -0,0 +1,2 @@ +export { default } from './ErrorList'; +export * from './ErrorList'; diff --git a/packages/daisyui/src/templates/FieldErrorTemplate/FieldErrorTemplate.tsx b/packages/daisyui/src/templates/FieldErrorTemplate/FieldErrorTemplate.tsx new file mode 100644 index 0000000000..15313abfa8 --- /dev/null +++ b/packages/daisyui/src/templates/FieldErrorTemplate/FieldErrorTemplate.tsx @@ -0,0 +1,14 @@ +import { FieldErrorProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function FieldErrorTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: FieldErrorProps) { + const { errors } = props; + return ( +
+
    {errors?.map((error, index) =>
  • {error}
  • ) ?? []}
+
+ ); +} diff --git a/packages/daisyui/src/templates/FieldErrorTemplate/index.ts b/packages/daisyui/src/templates/FieldErrorTemplate/index.ts new file mode 100644 index 0000000000..2fbf1c353d --- /dev/null +++ b/packages/daisyui/src/templates/FieldErrorTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './FieldErrorTemplate'; +export * from './FieldErrorTemplate'; diff --git a/packages/daisyui/src/templates/FieldHelpTemplate/FieldHelpTemplate.tsx b/packages/daisyui/src/templates/FieldHelpTemplate/FieldHelpTemplate.tsx new file mode 100644 index 0000000000..5663374fef --- /dev/null +++ b/packages/daisyui/src/templates/FieldHelpTemplate/FieldHelpTemplate.tsx @@ -0,0 +1,14 @@ +import { FieldHelpProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function FieldHelpTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: FieldHelpProps) { + const { help } = props; + return ( +
+

{help}

+
+ ); +} diff --git a/packages/daisyui/src/templates/FieldHelpTemplate/index.ts b/packages/daisyui/src/templates/FieldHelpTemplate/index.ts new file mode 100644 index 0000000000..b439bce3f1 --- /dev/null +++ b/packages/daisyui/src/templates/FieldHelpTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './FieldHelpTemplate'; +export * from './FieldHelpTemplate'; diff --git a/packages/daisyui/src/templates/FieldTemplate/FieldTemplate.tsx b/packages/daisyui/src/templates/FieldTemplate/FieldTemplate.tsx new file mode 100644 index 0000000000..8ad26680f1 --- /dev/null +++ b/packages/daisyui/src/templates/FieldTemplate/FieldTemplate.tsx @@ -0,0 +1,46 @@ +import { FieldTemplateProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +const FieldTemplate = ( + props: FieldTemplateProps +) => { + const { + id, + label, + children, + errors, + formContext, + formData, + help, + hideError, + displayLabel, + classNames, + // Destructure props we don't want to pass to div + onKeyChange, + onDropPropertyClick, + uiSchema, + schema, + readonly, + required, + rawErrors, + rawHelp, + rawDescription, + hidden, + onChange, + ...divProps + } = props; + + return ( +
+ {displayLabel && ( + + )} + {children} + {errors} + {help} +
+ ); +}; + +export default FieldTemplate; diff --git a/packages/daisyui/src/templates/FieldTemplate/index.ts b/packages/daisyui/src/templates/FieldTemplate/index.ts new file mode 100644 index 0000000000..6f7dc3861c --- /dev/null +++ b/packages/daisyui/src/templates/FieldTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './FieldTemplate'; +export * from './FieldTemplate'; diff --git a/packages/daisyui/src/templates/ObjectFieldTemplate/ObjectFieldTemplate.tsx b/packages/daisyui/src/templates/ObjectFieldTemplate/ObjectFieldTemplate.tsx new file mode 100644 index 0000000000..a611244b50 --- /dev/null +++ b/packages/daisyui/src/templates/ObjectFieldTemplate/ObjectFieldTemplate.tsx @@ -0,0 +1,87 @@ +import { + canExpand, + descriptionId, + FormContextType, + getTemplate, + getUiOptions, + ObjectFieldTemplateProps, + RJSFSchema, + StrictRJSFSchema, + titleId, +} from '@rjsf/utils'; + +export default function ObjectFieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ObjectFieldTemplateProps) { + const { + description, + title, + properties, + required, + disabled, + readonly, + uiSchema, + idSchema, + schema, + formData, + onAddClick, + registry, + } = props; + const uiOptions = getUiOptions(uiSchema); + const TitleFieldTemplate = getTemplate<'TitleFieldTemplate', T, S, F>('TitleFieldTemplate', registry, uiOptions); + const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>( + 'DescriptionFieldTemplate', + registry, + uiOptions + ); + // Button templates are not overridden in the uiSchema + const { + ButtonTemplates: { AddButton }, + } = registry.templates; + + return ( +
+ {title && ( + (idSchema)} + title={title} + required={required} + schema={schema} + uiSchema={uiSchema} + registry={registry} + /> + )} + {description && ( + (idSchema)} + description={description} + schema={schema} + uiSchema={uiSchema} + registry={registry} + /> + )} +
+ {properties.map((element, index) => + element.hidden ? ( + element.content + ) : ( +
{element.content}
+ ) + )} + {canExpand(schema, uiSchema, formData) && ( +
+ +
+ )} +
+
+ ); +} diff --git a/packages/daisyui/src/templates/ObjectFieldTemplate/index.ts b/packages/daisyui/src/templates/ObjectFieldTemplate/index.ts new file mode 100644 index 0000000000..77c68a9a40 --- /dev/null +++ b/packages/daisyui/src/templates/ObjectFieldTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './ObjectFieldTemplate'; +export * from './ObjectFieldTemplate'; diff --git a/packages/daisyui/src/templates/Templates.tsx b/packages/daisyui/src/templates/Templates.tsx new file mode 100644 index 0000000000..ac70e95ffa --- /dev/null +++ b/packages/daisyui/src/templates/Templates.tsx @@ -0,0 +1,49 @@ +import ArrayFieldItemTemplate from './ArrayFieldItemTemplate'; +import ArrayFieldTemplate from './ArrayFieldTemplate/ArrayFieldTemplate'; +import ArrayFieldItemButtonsTemplate from './ArrayFieldItemButtonsTemplate'; +import BaseInputTemplate from './BaseInputTemplate/BaseInputTemplate'; +import DescriptionField from './DescriptionField'; +import ErrorList from './ErrorList'; +import FieldErrorTemplate from './FieldErrorTemplate'; +import FieldHelpTemplate from './FieldHelpTemplate'; +import FieldTemplate from './FieldTemplate'; +import ObjectFieldTemplate from './ObjectFieldTemplate'; +import TitleFieldTemplate from './TitleField/TitleField'; +import WrapIfAdditionalTemplate from './WrapIfAdditionalTemplate'; + +import { FormContextType, RJSFSchema, StrictRJSFSchema, TemplatesType } from '@rjsf/utils'; + +import AddButton from './ButtonTemplates/AddButton'; +import SubmitButton from './ButtonTemplates/SubmitButton'; +import { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from './ButtonTemplates/IconButton'; + +export function generateTemplates< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): Partial> { + return { + ArrayFieldItemTemplate, + ArrayFieldTemplate, + ArrayFieldItemButtonsTemplate, + BaseInputTemplate, + ButtonTemplates: { + AddButton, + SubmitButton, + CopyButton, + MoveDownButton, + MoveUpButton, + RemoveButton, + }, + DescriptionFieldTemplate: DescriptionField, + ErrorListTemplate: ErrorList, + FieldErrorTemplate, + FieldHelpTemplate, + FieldTemplate, + ObjectFieldTemplate, + TitleFieldTemplate, + WrapIfAdditionalTemplate, + }; +} + +export default generateTemplates(); diff --git a/packages/daisyui/src/templates/TitleField/TitleField.tsx b/packages/daisyui/src/templates/TitleField/TitleField.tsx new file mode 100644 index 0000000000..1dd76b6d66 --- /dev/null +++ b/packages/daisyui/src/templates/TitleField/TitleField.tsx @@ -0,0 +1,14 @@ +import { TitleFieldProps, StrictRJSFSchema, RJSFSchema, FormContextType, getUiOptions } from '@rjsf/utils'; + +export default function TitleField( + props: TitleFieldProps +) { + const { id, title, uiSchema } = props; + const uiOptions = getUiOptions(uiSchema); + + return ( +
+

{uiOptions.title || title}

+
+ ); +} diff --git a/packages/daisyui/src/templates/TitleField/index.ts b/packages/daisyui/src/templates/TitleField/index.ts new file mode 100644 index 0000000000..35fcaa5f37 --- /dev/null +++ b/packages/daisyui/src/templates/TitleField/index.ts @@ -0,0 +1,2 @@ +export { default } from './TitleField'; +export * from './TitleField'; \ No newline at end of file diff --git a/packages/daisyui/src/templates/UnsupportedField/UnsupportedField.tsx b/packages/daisyui/src/templates/UnsupportedField/UnsupportedField.tsx new file mode 100644 index 0000000000..9414c82515 --- /dev/null +++ b/packages/daisyui/src/templates/UnsupportedField/UnsupportedField.tsx @@ -0,0 +1,10 @@ +import { UnsupportedFieldProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function UnsupportedField< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: UnsupportedFieldProps) { + props; + return
UnsupportedField
; +} diff --git a/packages/daisyui/src/templates/UnsupportedField/index.ts b/packages/daisyui/src/templates/UnsupportedField/index.ts new file mode 100644 index 0000000000..cb553b6067 --- /dev/null +++ b/packages/daisyui/src/templates/UnsupportedField/index.ts @@ -0,0 +1,2 @@ +export { default } from './UnsupportedField'; +export * from './UnsupportedField'; diff --git a/packages/daisyui/src/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx b/packages/daisyui/src/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx new file mode 100644 index 0000000000..3e9471461e --- /dev/null +++ b/packages/daisyui/src/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx @@ -0,0 +1,41 @@ +import { WrapIfAdditionalTemplateProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +const WrapIfAdditionalTemplate = ( + props: WrapIfAdditionalTemplateProps +) => { + const { + children, + classNames, + disabled, + id, + label, + readonly, + required, + schema, + onKeyChange, + onDropPropertyClick, + ...rest + } = props; + return ( +
+
+ onKeyChange(event.target.value)} + defaultValue={label} + disabled={disabled || readonly} + /> + {schema.additionalProperties && ( + + )} +
+ {children} +
+ ); +}; + +export default WrapIfAdditionalTemplate; diff --git a/packages/daisyui/src/templates/WrapIfAdditionalTemplate/index.ts b/packages/daisyui/src/templates/WrapIfAdditionalTemplate/index.ts new file mode 100644 index 0000000000..7d7af6629d --- /dev/null +++ b/packages/daisyui/src/templates/WrapIfAdditionalTemplate/index.ts @@ -0,0 +1,2 @@ +export { default } from './WrapIfAdditionalTemplate'; +export * from './WrapIfAdditionalTemplate'; diff --git a/packages/daisyui/src/templates/index.ts b/packages/daisyui/src/templates/index.ts new file mode 100644 index 0000000000..ab7e64e3da --- /dev/null +++ b/packages/daisyui/src/templates/index.ts @@ -0,0 +1,19 @@ +export * from './ArrayFieldDescriptionTemplate'; +export * from './ArrayFieldItemTemplate'; +export * from './ArrayFieldTemplate'; +export * from './ArrayFieldTitleTemplate'; +export * from './BaseInputTemplate'; +export * from './ButtonTemplates'; +export * from './DescriptionField'; +export * from './ErrorList'; +export * from './FieldErrorTemplate'; +export * from './FieldHelpTemplate'; +export * from './FieldTemplate'; +export * from './ObjectFieldTemplate'; +export * from './TitleField'; +export * from './UnsupportedField'; +export * from './WrapIfAdditionalTemplate'; + +export * from './Templates'; + +export { generateTemplates } from './Templates'; diff --git a/packages/daisyui/src/theme/Theme.tsx b/packages/daisyui/src/theme/Theme.tsx new file mode 100644 index 0000000000..dff4c8f701 --- /dev/null +++ b/packages/daisyui/src/theme/Theme.tsx @@ -0,0 +1,60 @@ +import { ThemeProps } from '@rjsf/core'; +import { FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils'; +import { getDefaultRegistry } from '@rjsf/core'; +import { generateTemplates } from '../templates/Templates'; +import { generateWidgets } from '../widgets/Widgets'; +import React from 'react'; + +export function generateTheme< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(): ThemeProps { + const { fields, widgets } = getDefaultRegistry(); + const generatedWidgets = generateWidgets(); + return { + templates: generateTemplates(), + widgets: { + ...generatedWidgets, + boolean: generatedWidgets.toggle, + }, + fields, + }; +} + +const Theme = generateTheme(); + +export default Theme; + +interface ThemeContextType { + theme: string; + setTheme: (theme: string) => void; +} + +export const ThemeContext = React.createContext({ + theme: 'cupcake', + setTheme: () => {}, +}); + +export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [theme, setTheme] = React.useState(() => { + try { + return localStorage.getItem('daisyui-theme') || 'cupcake'; + } catch { + return 'cupcake'; + } + }); + + const handleSetTheme = React.useCallback((newTheme: string) => { + try { + localStorage.setItem('daisyui-theme', newTheme); + setTheme(newTheme); + } catch { + // Ignore localStorage errors + } + }, []); + + return {children}; +}; + +export const useTheme = () => React.useContext(ThemeContext); diff --git a/packages/daisyui/src/theme/index.ts b/packages/daisyui/src/theme/index.ts new file mode 100644 index 0000000000..6dfd7fa6e1 --- /dev/null +++ b/packages/daisyui/src/theme/index.ts @@ -0,0 +1,2 @@ +export { default } from './Theme'; +export * from './Theme'; diff --git a/packages/daisyui/src/tsconfig.json b/packages/daisyui/src/tsconfig.json new file mode 100644 index 0000000000..01834bf1ab --- /dev/null +++ b/packages/daisyui/src/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": [ + "./" + ], + "compilerOptions": { + "rootDir": "./", + "outDir": "../lib", + "baseUrl": "../", + "jsx": "react-jsx" + }, + "references": [ + { + "path": "../../core" + }, + { + "path": "../../utils" + }, + { + "path": "../../validator-ajv8" + } + ] +} diff --git a/packages/daisyui/src/types.d.ts b/packages/daisyui/src/types.d.ts new file mode 100644 index 0000000000..f5dc5deb93 --- /dev/null +++ b/packages/daisyui/src/types.d.ts @@ -0,0 +1,4 @@ +declare module '*.css?inline' { + const content: string; + export default content; +} diff --git a/packages/daisyui/src/utils.ts b/packages/daisyui/src/utils.ts new file mode 100644 index 0000000000..46c69593a7 --- /dev/null +++ b/packages/daisyui/src/utils.ts @@ -0,0 +1,17 @@ +import { DaisyProps } from './types/DaisyProps'; +import { UiSchema } from '@rjsf/utils'; + +export interface DaisyUiSchema extends Omit { + 'ui:options'?: DaisyUiOptions; +} + +type DaisyUiOptions = UiSchema['ui:options'] & { daisy?: DaisyProps }; + +interface GetDaisyProps { + uiSchema?: DaisyUiSchema; +} + +export function getDaisy({ uiSchema = {} }: GetDaisyProps): DaisyProps { + const daisyProps = (uiSchema['ui:options'] && uiSchema['ui:options'].daisy) || {}; + return daisyProps; +} diff --git a/packages/daisyui/src/widgets/CheckboxWidget/CheckboxWidget.tsx b/packages/daisyui/src/widgets/CheckboxWidget/CheckboxWidget.tsx new file mode 100644 index 0000000000..f85e46070e --- /dev/null +++ b/packages/daisyui/src/widgets/CheckboxWidget/CheckboxWidget.tsx @@ -0,0 +1,25 @@ +import { WidgetProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function CheckboxWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { + const { id, value, required, disabled, readonly, onChange, label } = props; + return ( +
+ +
+ ); +} diff --git a/packages/daisyui/src/widgets/CheckboxWidget/index.ts b/packages/daisyui/src/widgets/CheckboxWidget/index.ts new file mode 100644 index 0000000000..b9e3c318ec --- /dev/null +++ b/packages/daisyui/src/widgets/CheckboxWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './CheckboxWidget'; +export * from './CheckboxWidget'; diff --git a/packages/daisyui/src/widgets/CheckboxesWidget/CheckboxesWidget.tsx b/packages/daisyui/src/widgets/CheckboxesWidget/CheckboxesWidget.tsx new file mode 100644 index 0000000000..9243ee7513 --- /dev/null +++ b/packages/daisyui/src/widgets/CheckboxesWidget/CheckboxesWidget.tsx @@ -0,0 +1,57 @@ +import { WidgetProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +const CheckboxesWidget = ({ + id, + disabled, + options, + value, + readonly, + required, + onChange, +}: WidgetProps) => { + const { enumOptions } = options; + const isEnumeratedObject = enumOptions && enumOptions[0]?.value && typeof enumOptions[0].value === 'object'; + + const isChecked = (option: any) => { + if (!Array.isArray(value)) { + return false; + } + if (isEnumeratedObject) { + return value.some((v) => v.name === option.value.name); + } + return value.includes(option.value); + }; + + const _onChange = (option: any) => { + const newValue = Array.isArray(value) ? [...value] : []; + const optionValue = isEnumeratedObject ? option.value : option.value; + + if (isChecked(option)) { + onChange(newValue.filter((v) => (isEnumeratedObject ? v.name !== optionValue.name : v !== optionValue))); + } else { + onChange([...newValue, optionValue]); + } + }; + + return ( +
+ {enumOptions?.map((option) => ( + + ))} +
+ ); +}; + +export default CheckboxesWidget; diff --git a/packages/daisyui/src/widgets/CheckboxesWidget/index.ts b/packages/daisyui/src/widgets/CheckboxesWidget/index.ts new file mode 100644 index 0000000000..97152004fa --- /dev/null +++ b/packages/daisyui/src/widgets/CheckboxesWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './CheckboxesWidget'; +export * from './CheckboxesWidget'; diff --git a/packages/daisyui/src/widgets/DateTimeWidget/DateTimeWidget.tsx b/packages/daisyui/src/widgets/DateTimeWidget/DateTimeWidget.tsx new file mode 100644 index 0000000000..0fc9a017eb --- /dev/null +++ b/packages/daisyui/src/widgets/DateTimeWidget/DateTimeWidget.tsx @@ -0,0 +1,250 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { faCalendar } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { format, isValid, isToday, isSameDay } from 'date-fns'; +import { DayPicker, ClassNames, ModifiersClassNames, UI } from 'react-day-picker'; +import 'react-day-picker/dist/style.css'; + +import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils'; + +// +// Types +// +interface DateTimePickerProps { + selectedDate?: Date; + month: Date; + onMonthChange: (date: Date) => void; + onSelect: (date: Date | undefined) => void; + onTimeChange: (e: React.ChangeEvent) => void; +} + +// +// Hook to manage popup state and the displayed month +// +const useDatePickerState = (initialDate?: Date) => { + const [isOpen, setIsOpen] = useState(false); + const [month, setMonth] = useState(initialDate ?? new Date()); + return { isOpen, setIsOpen, month, setMonth }; +}; + +// +// Hook for detecting clicks outside of a container +// +const useClickOutside = (ref: React.RefObject, callback: () => void) => { + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + callback(); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [ref, callback]); +}; + +// +// Predefined DayPicker styles using DaisyUI classes +// +const dayPickerStyles: { classNames: Partial; modifiers: Partial } = { + classNames: { + [UI.Root]: 'relative', + [UI.Nav]: 'hidden', + [UI.Chevron]: 'hidden', + [UI.CaptionLabel]: 'hidden', + [UI.Dropdowns]: 'flex justify-between gap-4 px-4 pb-4', + [UI.Dropdown]: 'select select-bordered select-sm w-32', + [UI.MonthsDropdown]: 'select select-bordered select-sm', + [UI.YearsDropdown]: 'select select-bordered select-sm', + [UI.Months]: 'flex justify-center', + [UI.Month]: 'w-full', + [UI.MonthCaption]: 'flex justify-center', + [UI.MonthGrid]: 'w-full', + [UI.Weekdays]: 'grid grid-cols-7 text-center border-b mb-2 pb-1 text-base-content/60 uppercase', + [UI.Weekday]: 'p-1 font-medium text-base-content/60 text-sm', + [UI.Week]: 'grid grid-cols-7', + [UI.Day]: 'w-10 h-8 p-0 relative rounded-md', + [UI.DayButton]: + 'btn btn-ghost absolute inset-0 flex items-center justify-center w-full h-full cursor-pointer rounded-md hover:btn-primary', + }, + modifiers: { + selected: 'btn btn-accent min-h-0 h-full', + // today: 'btn btn-outline btn-info min-h-0 h-full', + outside: 'text-base-content/30 hover:btn-ghost', + disabled: 'opacity-50 cursor-not-allowed hover:btn-disabled', + }, +}; + +// +// Popup component for the calendar and time input. +// Wrapped with React.memo to avoid unnecessary re‑renders. +// +const DateTimePickerPopup: React.FC = React.memo( + ({ selectedDate, month, onMonthChange, onSelect, onTimeChange }) => { + const customDayModifiers = { + selected: selectedDate, // Keep the 'selected' modifier as is - DayPicker uses this to track selection + + // NEW 'custom-today' modifier - we will style "today" manually + 'custom-today': (date: Date) => isToday(date) && !(selectedDate && isSameDay(date, selectedDate)), + }; + + const customModifiersClassNames: ModifiersClassNames = { + selected: dayPickerStyles.modifiers.selected as string, // Keep selected style + 'custom-today': 'btn btn-outline btn-info min-h-0 h-full', // Manually apply "today" style + }; + + return ( +
+ +
+ e.stopPropagation()} + /> +
+
+ ); + } +); + +// +// Main widget component +// +const DateTimeWidget = ( + props: WidgetProps +) => { + const { id, value, onChange, schema } = props; + // Initialize the local date from the parent's value. + const initialDate = useMemo(() => (value ? new Date(value) : undefined), [value]); + const [localDate, setLocalDate] = useState(initialDate); + + // When the parent's value changes externally, update local state. + useEffect(() => { + setLocalDate(initialDate); + }, [initialDate]); + + const { isOpen, setIsOpen, month, setMonth } = useDatePickerState(initialDate); + const containerRef = useRef(null); + + // Close the popup when clicking outside and commit changes. + useClickOutside(containerRef, () => { + setIsOpen(false); + onChange(localDate ? localDate.toISOString() : ''); + }); + + // When the local date changes, update the displayed month. + useEffect(() => { + if (localDate) { + setMonth(localDate); + } + }, [localDate, setMonth]); + + // Update the month when the user navigates the calendar. + const handleMonthChange = useCallback((date: Date) => setMonth(date), [setMonth]); + + // Update local state on day selection (but do not commit immediately). + const handleSelect = useCallback( + (date: Date | undefined) => { + if (date) { + if (localDate) { + date.setHours(localDate.getHours(), localDate.getMinutes()); + } + setLocalDate(date); + } + }, + [localDate] + ); + + // Update local state on time change. + const handleTimeChange = useCallback( + (e: React.ChangeEvent) => { + if (localDate) { + const [hours, minutes] = e.target.value.split(':'); + const newDate = new Date(localDate); + newDate.setHours(parseInt(hours, 10), parseInt(minutes, 10)); + setLocalDate(newDate); + } + }, + [localDate] + ); + + // Toggle popup visibility. + const togglePicker = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + setIsOpen((prev) => !prev); + }, + [setIsOpen] + ); + + return ( +
+
{ + if (e.key === 'Enter' || e.key === ' ') { + togglePicker(e as unknown as React.MouseEvent); + } + }} + > +
+ + {localDate && isValid(localDate) ? format(localDate, 'PP p') : schema.title} + + +
+ {isOpen && ( +
+ +
+ +
+
+ )} +
+
+ ); +}; + +export default DateTimeWidget; diff --git a/packages/daisyui/src/widgets/DateTimeWidget/index.ts b/packages/daisyui/src/widgets/DateTimeWidget/index.ts new file mode 100644 index 0000000000..0db366167f --- /dev/null +++ b/packages/daisyui/src/widgets/DateTimeWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './DateTimeWidget'; +export * from './DateTimeWidget'; diff --git a/packages/daisyui/src/widgets/DateWidget/DateWidget.tsx b/packages/daisyui/src/widgets/DateWidget/DateWidget.tsx new file mode 100644 index 0000000000..ce784fec89 --- /dev/null +++ b/packages/daisyui/src/widgets/DateWidget/DateWidget.tsx @@ -0,0 +1,220 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { faCalendar } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { format, isValid, isToday, isSameDay } from 'date-fns'; +import { DayPicker, ClassNames, ModifiersClassNames, UI } from 'react-day-picker'; +import 'react-day-picker/dist/style.css'; + +import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils'; + +// +// Types +// +interface DateTimePickerProps { + selectedDate?: Date; + month: Date; + onMonthChange: (date: Date) => void; + onSelect: (date: Date | undefined) => void; +} + +// +// Hook to manage popup state and the displayed month +// +const useDatePickerState = (initialDate?: Date) => { + const [isOpen, setIsOpen] = useState(false); + const [month, setMonth] = useState(initialDate ?? new Date()); + return { isOpen, setIsOpen, month, setMonth }; +}; + +// +// Hook for detecting clicks outside of a container +// +const useClickOutside = (ref: React.RefObject, callback: () => void) => { + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + callback(); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [ref, callback]); +}; + +// +// Predefined DayPicker styles using DaisyUI classes +// +const dayPickerStyles: { classNames: Partial; modifiers: Partial } = { + classNames: { + [UI.Root]: 'relative', + [UI.Nav]: 'hidden', + [UI.Chevron]: 'hidden', + [UI.CaptionLabel]: 'hidden', + [UI.Dropdowns]: 'flex justify-between gap-4 px-4 pb-4', + [UI.Dropdown]: 'select select-bordered select-sm w-32', + [UI.MonthsDropdown]: 'select select-bordered select-sm', + [UI.YearsDropdown]: 'select select-bordered select-sm', + [UI.Months]: 'flex justify-center', + [UI.Month]: 'w-full', + [UI.MonthCaption]: 'flex justify-center', + [UI.MonthGrid]: 'w-full', + [UI.Weekdays]: 'grid grid-cols-7 text-center border-b mb-2 pb-1 text-base-content/60 uppercase', + [UI.Weekday]: 'p-1 font-medium text-base-content/60 text-sm', + [UI.Week]: 'grid grid-cols-7', + [UI.Day]: 'w-10 h-8 p-0 relative rounded-md', + [UI.DayButton]: + 'btn btn-ghost absolute inset-0 flex items-center justify-center w-full h-full cursor-pointer rounded-md hover:btn-primary', + }, + modifiers: { + selected: 'btn btn-accent min-h-0 h-full', + // today: 'btn btn-outline btn-info min-h-0 h-full', + outside: 'text-base-content/30 hover:btn-ghost', + disabled: 'opacity-50 cursor-not-allowed hover:btn-disabled', + }, +}; + +// +// Popup component for the calendar. +// Wrapped with React.memo to avoid unnecessary re‑renders. +// +const DateTimePickerPopup: React.FC = React.memo( + ({ selectedDate, month, onMonthChange, onSelect }) => { + const customDayModifiers = { + selected: selectedDate, // DayPicker uses this for tracking selection. + 'custom-today': (date: Date) => isToday(date) && !(selectedDate && isSameDay(date, selectedDate)), + }; + + const customModifiersClassNames: ModifiersClassNames = { + selected: dayPickerStyles.modifiers.selected as string, + 'custom-today': 'btn btn-outline btn-info min-h-0 h-full', + }; + + return ( +
+ +
+ ); + } +); + +// +// Main widget component +// +const DateTimeWidget = ( + props: WidgetProps +) => { + const { id, value, onChange, schema } = props; + // Initialize the local date from the parent's value. + const initialDate = useMemo(() => (value ? new Date(value) : undefined), [value]); + const [localDate, setLocalDate] = useState(initialDate); + + // When the parent's value changes externally, update local state. + useEffect(() => { + setLocalDate(initialDate); + }, [initialDate]); + + const { isOpen, setIsOpen, month, setMonth } = useDatePickerState(initialDate); + const containerRef = useRef(null); + + // Close the popup when clicking outside and commit changes. + useClickOutside(containerRef, () => { + setIsOpen(false); + onChange(localDate ? localDate.toISOString() : ''); + }); + + // When the local date changes, update the displayed month. + useEffect(() => { + if (localDate) { + setMonth(localDate); + } + }, [localDate, setMonth]); + + // Update the month when the user navigates the calendar. + const handleMonthChange = useCallback((date: Date) => setMonth(date), [setMonth]); + + // Update local state on day selection (but do not commit immediately). + const handleSelect = useCallback((date: Date | undefined) => { + if (date) { + // Remove any time component by setting hours, minutes, seconds, and milliseconds to zero. + date.setHours(0, 0, 0, 0); + setLocalDate(date); + } + }, []); + + // Toggle popup visibility. + const togglePicker = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + setIsOpen((prev) => !prev); + }, + [setIsOpen] + ); + + return ( +
+
{ + if (e.key === 'Enter' || e.key === ' ') { + togglePicker(e as unknown as React.MouseEvent); + } + }} + > +
+ + {localDate && isValid(localDate) ? format(localDate, 'PP') : schema.title} + + +
+ {isOpen && ( +
+ +
+ +
+
+ )} +
+
+ ); +}; + +export default DateTimeWidget; diff --git a/packages/daisyui/src/widgets/DateWidget/index.ts b/packages/daisyui/src/widgets/DateWidget/index.ts new file mode 100644 index 0000000000..923b0077fb --- /dev/null +++ b/packages/daisyui/src/widgets/DateWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './DateWidget'; +export * from './DateWidget'; diff --git a/packages/daisyui/src/widgets/FileWidget/FileWidget.tsx b/packages/daisyui/src/widgets/FileWidget/FileWidget.tsx new file mode 100644 index 0000000000..1d008c4930 --- /dev/null +++ b/packages/daisyui/src/widgets/FileWidget/FileWidget.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils'; + +const FileWidget = ( + props: WidgetProps +) => { + const { id, required, disabled, readonly, schema, onChange, options = {} } = props; + + // Ensure isMulti is explicitly a boolean. + const isMulti: boolean = schema.type === 'array' || Boolean(options.multiple); + + // Accept attribute for restricting file types (e.g., "image/*"), if defined in options. + const accept: string | undefined = typeof options.accept === 'string' ? options.accept : undefined; + + const handleChange = (event: React.ChangeEvent) => { + if (!event.target.files) { + return; + } + // Convert FileList to array for multiple upload handling. + const fileList = Array.from(event.target.files); + if (isMulti) { + onChange(fileList); + } else { + // For single file, send the first file (or null if none chosen) + onChange(fileList[0] || null); + } + }; + + return ( + + ); +}; + +export default FileWidget; diff --git a/packages/daisyui/src/widgets/FileWidget/index.ts b/packages/daisyui/src/widgets/FileWidget/index.ts new file mode 100644 index 0000000000..9cf5e26368 --- /dev/null +++ b/packages/daisyui/src/widgets/FileWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './FileWidget'; +export * from './FileWidget'; \ No newline at end of file diff --git a/packages/daisyui/src/widgets/RadioWidget/RadioWidget.tsx b/packages/daisyui/src/widgets/RadioWidget/RadioWidget.tsx new file mode 100644 index 0000000000..a0d2235988 --- /dev/null +++ b/packages/daisyui/src/widgets/RadioWidget/RadioWidget.tsx @@ -0,0 +1,55 @@ +import { WidgetProps, StrictRJSFSchema, FormContextType } from '@rjsf/utils'; + +const RadioWidget = ({ + id, + options, + value, + required, + disabled, + readonly, + label, + onChange, +}: WidgetProps) => { + const { enumOptions } = options; + const isEnumeratedObject = enumOptions && enumOptions[0]?.value && typeof enumOptions[0].value === 'object'; + + const getValue = (option: any) => { + if (isEnumeratedObject) { + return option.value; + } + return option.value; + }; + + const isChecked = (option: any) => { + if (isEnumeratedObject) { + return value && value.name === option.value.name; + } + return value === option.value; + }; + + return ( +
+ {enumOptions?.map((option) => ( + + ))} +
+ ); +}; + +export default RadioWidget; diff --git a/packages/daisyui/src/widgets/RadioWidget/index.ts b/packages/daisyui/src/widgets/RadioWidget/index.ts new file mode 100644 index 0000000000..10292dc565 --- /dev/null +++ b/packages/daisyui/src/widgets/RadioWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './RadioWidget'; +export * from './RadioWidget'; diff --git a/packages/daisyui/src/widgets/RangeWidget/RangeWidget.tsx b/packages/daisyui/src/widgets/RangeWidget/RangeWidget.tsx new file mode 100644 index 0000000000..95bfe39bf5 --- /dev/null +++ b/packages/daisyui/src/widgets/RangeWidget/RangeWidget.tsx @@ -0,0 +1,31 @@ +import { WidgetProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +const RangeWidget = ({ + id, + value, + required, + disabled, + readonly, + onChange, + schema, +}: WidgetProps) => { + return ( +
+ onChange(event.target.value)} + /> + {value} +
+ ); +}; + +export default RangeWidget; diff --git a/packages/daisyui/src/widgets/RangeWidget/index.ts b/packages/daisyui/src/widgets/RangeWidget/index.ts new file mode 100644 index 0000000000..d8c49226c6 --- /dev/null +++ b/packages/daisyui/src/widgets/RangeWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './RangeWidget'; +export * from './RangeWidget'; diff --git a/packages/daisyui/src/widgets/RatingWidget/RatingWidget.tsx b/packages/daisyui/src/widgets/RatingWidget/RatingWidget.tsx new file mode 100644 index 0000000000..87a4146969 --- /dev/null +++ b/packages/daisyui/src/widgets/RatingWidget/RatingWidget.tsx @@ -0,0 +1,42 @@ +import { ChangeEvent } from 'react'; +import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils'; + +export default function RatingWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ id, value, required, disabled, readonly, autofocus, onChange, schema, options }: WidgetProps) { + const { stars = 5 } = options; + const numStars = Math.min(Math.max(stars as number, 1), 5); // Limit between 1-5 stars + const min = schema.minimum || 0; + const max = schema.maximum || numStars; + + const _onChange = ({ target: { value } }: ChangeEvent) => { + onChange(parseInt(value)); + }; + + return ( +
+
+ {[...Array(numStars)].map((_, index) => { + const starValue = min + index; + return ( + + ); + })} +
+
+ ); +} diff --git a/packages/daisyui/src/widgets/SelectWidget/SelectWidget.tsx b/packages/daisyui/src/widgets/SelectWidget/SelectWidget.tsx new file mode 100644 index 0000000000..34967b0463 --- /dev/null +++ b/packages/daisyui/src/widgets/SelectWidget/SelectWidget.tsx @@ -0,0 +1,121 @@ +import { + ariaDescribedByIds, + enumOptionsIndexForValue, + enumOptionsValueForIndex, + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from '@rjsf/utils'; +import { ChangeEvent, FocusEvent } from 'react'; + +export default function SelectWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>({ + schema, + id, + options, + label, + required, + disabled, + placeholder, + readonly, + value, + multiple, + autofocus, + onChange, + onBlur, + onFocus, + rawErrors = [], +}: WidgetProps) { + const { enumOptions, enumDisabled, emptyValue: optEmptyVal } = options; + multiple = typeof multiple === 'undefined' ? false : !!multiple; + const emptyValue = multiple ? [] : ''; + const isEmpty = typeof value === 'undefined' || (multiple && value.length < 1) || (!multiple && value === emptyValue); + const dataListId = `${id}-datalist`; + + const getDisplayValue = (val: any) => { + if (!val) return ''; + if (typeof val === 'object') { + if (val.name) return val.name; + return val.label || JSON.stringify(val); + } + return String(val); + }; + + const isEnumeratedObject = enumOptions && enumOptions[0]?.value && typeof enumOptions[0].value === 'object'; + const shouldUseSelect = isEnumeratedObject || schema.examples; + + const _onChange = ({ target: { value } }: ChangeEvent<{ value: string }>) => { + if (shouldUseSelect) { + const idx = parseInt(value); + onChange( + isEnumeratedObject ? enumOptions[idx].value : enumOptionsValueForIndex(value, enumOptions, optEmptyVal) + ); + } else { + const option = optionsList.find((opt) => getDisplayValue(opt.label) === value); + onChange(option ? option.value : value); + } + }; + const _onBlur = ({ target }: FocusEvent) => + onBlur(id, enumOptionsValueForIndex(target && target.value, enumOptions, optEmptyVal)); + const _onFocus = ({ target }: FocusEvent) => + onFocus(id, enumOptionsValueForIndex(target && target.value, enumOptions, optEmptyVal)); + const selectedIndexes = enumOptionsIndexForValue(value, enumOptions, multiple); + const showPlaceholderOption = !multiple && schema.default === undefined; + + const optionsList = + enumOptions || + (Array.isArray(schema.examples) ? schema.examples.map((example) => ({ value: example, label: example })) : []); + + return ( +
+ {shouldUseSelect ? ( + + ) : ( + <> + + + {optionsList.map(({ value, label }, i) => ( + + ))} + + + )} +
+ ); +} diff --git a/packages/daisyui/src/widgets/SelectWidget/index.ts b/packages/daisyui/src/widgets/SelectWidget/index.ts new file mode 100644 index 0000000000..e37ea725b8 --- /dev/null +++ b/packages/daisyui/src/widgets/SelectWidget/index.ts @@ -0,0 +1,2 @@ +export { default } from './SelectWidget'; +export * from './SelectWidget'; diff --git a/packages/daisyui/src/widgets/TextAreaWidget/TextAreaWidget.tsx b/packages/daisyui/src/widgets/TextAreaWidget/TextAreaWidget.tsx new file mode 100644 index 0000000000..065ed5f363 --- /dev/null +++ b/packages/daisyui/src/widgets/TextAreaWidget/TextAreaWidget.tsx @@ -0,0 +1,21 @@ +import { WidgetProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils'; + +export default function TextareaWidget< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: WidgetProps) { + const { id, value, required, disabled, readonly, onChange } = props; + return ( +
+