diff --git a/package-lock.json b/package-lock.json
index a3e2d0a951..43dff4d10d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"packages/material-ui",
"packages/mui",
"packages/playground",
+ "packages/primereact",
"packages/semantic-ui",
"packages/utils",
"packages/validator-ajv6",
@@ -9612,6 +9613,10 @@
"resolved": "packages/playground",
"link": true
},
+ "node_modules/@rjsf/primereact": {
+ "resolved": "packages/primereact",
+ "link": true
+ },
"node_modules/@rjsf/semantic-ui": {
"resolved": "packages/semantic-ui",
"link": true
@@ -26848,6 +26853,44 @@
"node": ">=4"
}
},
+ "node_modules/primeflex": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz",
+ "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/primeicons": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz",
+ "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/primereact": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/primereact/-/primereact-10.9.2.tgz",
+ "integrity": "sha512-uJTghCPlnPWJc0mvkqYJDj6bl4udROPGrMEfV4CPh7UurMS+E8b+82ABZ+OPWibQOWxnEQl5NXcfG4F/7YRXwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react-transition-group": "^4.4.1",
+ "react-transition-group": "^4.4.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/prism-react-renderer": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz",
@@ -35158,6 +35201,59 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
+ "packages/primereact": {
+ "name": "@rjsf/primereact",
+ "version": "5.24.2",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "classnames": "^2.5.1",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21"
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.23.9",
+ "@babel/core": "^7.23.9",
+ "@babel/eslint-parser": "^7.23.10",
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@babel/plugin-proposal-optional-chaining": "^7.21.0",
+ "@babel/preset-env": "^7.23.9",
+ "@babel/preset-react": "^7.23.3",
+ "@babel/preset-typescript": "^7.23.3",
+ "@rjsf/core": "^5.24.2",
+ "@rjsf/snapshot-tests": "^5.24.2",
+ "@rjsf/utils": "^5.24.2",
+ "@rjsf/validator-ajv8": "^5.24.2",
+ "@rollup/plugin-replace": "^5.0.5",
+ "@types/jest": "^29.5.12",
+ "@types/lodash": "^4.14.202",
+ "@types/react": "^18.2.58",
+ "@types/react-dom": "^18.2.19",
+ "@types/react-test-renderer": "^18.0.7",
+ "babel-jest": "^29.7.0",
+ "eslint": "^8.56.0",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
+ "primeflex": "^3.3.1",
+ "primeicons": "^7.0.0",
+ "primereact": "^10.9.2",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-test-renderer": "^18.2.0",
+ "rimraf": "^5.0.5",
+ "rollup": "^3.29.4",
+ "typescript": "^4.9.5"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "@rjsf/core": "^5.24.x",
+ "@rjsf/utils": "^5.24.x",
+ "primeicons": ">=6.0.0",
+ "primereact": ">=8.0.0",
+ "react": "^16.14.0 || >=17"
+ }
+ },
"packages/semantic-ui": {
"name": "@rjsf/semantic-ui",
"version": "5.24.3",
diff --git a/package.json b/package.json
index 5852ea996c..fcd4e61d99 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
"packages/material-ui",
"packages/mui",
"packages/playground",
+ "packages/primereact",
"packages/semantic-ui",
"packages/utils",
"packages/validator-ajv6",
diff --git a/packages/playground/package.json b/packages/playground/package.json
index 8667d4541b..fd53127a2b 100644
--- a/packages/playground/package.json
+++ b/packages/playground/package.json
@@ -50,6 +50,7 @@
"@rjsf/fluentui-rc": "^5.24.3",
"@rjsf/material-ui": "^5.24.3",
"@rjsf/mui": "^5.24.3",
+ "@rjsf/primereact": "^5.24.3",
"@rjsf/semantic-ui": "^5.24.3",
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv6": "^5.24.3",
diff --git a/packages/playground/src/app.tsx b/packages/playground/src/app.tsx
index 0997ac50d5..91185ef127 100644
--- a/packages/playground/src/app.tsx
+++ b/packages/playground/src/app.tsx
@@ -6,6 +6,7 @@ import { Theme as SuiTheme } from '@rjsf/semantic-ui';
import { Theme as AntdTheme } from '@rjsf/antd';
import { Theme as Bootstrap4Theme } from '@rjsf/bootstrap-4';
import { Theme as ChakraUITheme } from '@rjsf/chakra-ui';
+import { Theme as PrimeReactTheme } from '@rjsf/primereact';
import v8Validator, { customizeValidator } from '@rjsf/validator-ajv8';
import v6Validator from '@rjsf/validator-ajv6';
import localize_es from 'ajv-i18n/localize/es';
@@ -121,6 +122,168 @@ const themes: PlaygroundProps['themes'] = {
stylesheet: '',
theme: MuiV5Theme,
},
+ primereact: {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-cyan/theme.css',
+ theme: PrimeReactTheme,
+ subthemes: {
+ 'arya-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/arya-blue/theme.css',
+ },
+ 'arya-green': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/arya-green/theme.css',
+ },
+ 'arya-orange': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/arya-orange/theme.css',
+ },
+ 'arya-purple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/arya-purple/theme.css',
+ },
+ 'bootstrap4-dark-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/bootstrap4-dark-blue/theme.css',
+ },
+ 'bootstrap4-dark-purple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/bootstrap4-dark-purple/theme.css',
+ },
+ 'bootstrap4-light-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/bootstrap4-light-blue/theme.css',
+ },
+ 'bootstrap4-light-purple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/bootstrap4-light-purple/theme.css',
+ },
+ 'fluent-light': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/fluent-light/theme.css',
+ },
+ 'lara-dark-amber': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-amber/theme.css',
+ },
+ 'lara-dark-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-blue/theme.css',
+ },
+ 'lara-dark-cyan': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-cyan/theme.css',
+ },
+ 'lara-dark-green': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-green/theme.css',
+ },
+ 'lara-dark-indigo': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-indigo/theme.css',
+ },
+ 'lara-dark-pink': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-pink/theme.css',
+ },
+ 'lara-dark-purple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-purple/theme.css',
+ },
+ 'lara-dark-teal': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-dark-teal/theme.css',
+ },
+ 'lara-light-amber': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-amber/theme.css',
+ },
+ 'lara-light-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-blue/theme.css',
+ },
+ 'lara-light-cyan': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-cyan/theme.css',
+ },
+ 'lara-light-green': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-green/theme.css',
+ },
+ 'lara-light-indigo': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-indigo/theme.css',
+ },
+ 'lara-light-pink': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-pink/theme.css',
+ },
+ 'lara-light-purple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-purple/theme.css',
+ },
+ 'lara-light-teal': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/lara-light-teal/theme.css',
+ },
+ 'luna-amber': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/luna-amber/theme.css',
+ },
+ 'luna-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/luna-blue/theme.css',
+ },
+ 'luna-green': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/luna-green/theme.css',
+ },
+ 'luna-pink': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/luna-pink/theme.css',
+ },
+ 'md-dark-deeppurple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/md-dark-deeppurple/theme.css',
+ },
+ 'md-dark-indigo': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/md-dark-indigo/theme.css',
+ },
+ 'md-light-deeppurple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/md-light-deeppurple/theme.css',
+ },
+ 'md-light-indigo': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/md-light-indigo/theme.css',
+ },
+ 'mdc-dark-deeppurple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/mdc-dark-deeppurple/theme.css',
+ },
+ 'mdc-dark-indigo': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/mdc-dark-indigo/theme.css',
+ },
+ 'mdc-light-deeppurple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/mdc-light-deeppurple/theme.css',
+ },
+ 'mdc-light-indigo': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/mdc-light-indigo/theme.css',
+ },
+ mira: {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/mira/theme.css',
+ },
+ nano: {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/nano/theme.css',
+ },
+ nova: {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/nova/theme.css',
+ },
+ 'nova-accent': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/nova-accent/theme.css',
+ },
+ 'nova-alt': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/nova-alt/theme.css',
+ },
+ rhea: {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/rhea/theme.css',
+ },
+ 'saga-blue': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/saga-blue/theme.css',
+ },
+ 'saga-green': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/saga-green/theme.css',
+ },
+ 'saga-orange': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/saga-orange/theme.css',
+ },
+ 'saga-purple': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/saga-purple/theme.css',
+ },
+ 'soho-dark': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/soho-dark/theme.css',
+ },
+ 'soho-light': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/soho-light/theme.css',
+ },
+ 'tailwind-light': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/tailwind-light/theme.css',
+ },
+ 'viva-dark': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/viva-dark/theme.css',
+ },
+ 'viva-light': {
+ stylesheet: '//cdn.jsdelivr.net/npm/primereact@10.9.2/resources/themes/viva-light/theme.css',
+ },
+ },
+ },
'semantic-ui': {
stylesheet: '//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css',
theme: SuiTheme,
diff --git a/packages/playground/src/components/DemoFrame.tsx b/packages/playground/src/components/DemoFrame.tsx
index 983d08cde3..4ca830fd3b 100644
--- a/packages/playground/src/components/DemoFrame.tsx
+++ b/packages/playground/src/components/DemoFrame.tsx
@@ -8,6 +8,7 @@ import Frame, { FrameComponentProps, FrameContextConsumer } from 'react-frame-co
import { __createChakraFrameProvider } from '@rjsf/chakra-ui';
import { StyleProvider as AntdStyleProvider } from '@ant-design/cssinjs';
import { __createFluentUIRCFrameProvider } from '@rjsf/fluentui-rc';
+import { PrimeReactProvider } from 'primereact/api';
/*
Adapted from https://github.com/mui-org/material-ui/blob/master/docs/src/modules/components/DemoSandboxed.js
@@ -123,6 +124,14 @@ export default function DemoFrame(props: DemoFrameProps) {
body = ready ? (
{children}
) : null;
+ } else if (theme === 'primereact') {
+ body = ready ? (
+ <>
+
+
+ {children}
+ >
+ ) : null;
}
return (
diff --git a/packages/playground/tsconfig.json b/packages/playground/tsconfig.json
index fe14477307..261f8deb49 100644
--- a/packages/playground/tsconfig.json
+++ b/packages/playground/tsconfig.json
@@ -30,6 +30,7 @@
{ "path": "../fluentui-rc" },
{ "path": "../material-ui" },
{ "path": "../mui" },
+ { "path": "../primereact" },
{ "path": "../semantic-ui" },
{ "path": "../utils" },
{ "path": "../validator-ajv6" },
diff --git a/packages/playground/vite.config.ts b/packages/playground/vite.config.ts
index cd3e413c15..33c18c920e 100644
--- a/packages/playground/vite.config.ts
+++ b/packages/playground/vite.config.ts
@@ -23,6 +23,7 @@ export default defineConfig({
'@rjsf/fluentui-rc': path.resolve(__dirname, '../fluentui-rc/src'),
'@rjsf/material-ui': path.resolve(__dirname, '../material-ui/src'),
'@rjsf/mui': path.resolve(__dirname, '../mui/src'),
+ '@rjsf/primereact': path.resolve(__dirname, '../primereact/src'),
'@rjsf/semantic-ui': path.resolve(__dirname, '../semantic-ui/src'),
'@rjsf/utils': path.resolve(__dirname, '../utils/src'),
'@rjsf/validator-ajv8': path.resolve(__dirname, '../validator-ajv8/src'),
diff --git a/packages/primereact/.eslintrc b/packages/primereact/.eslintrc
new file mode 100644
index 0000000000..8f31b04e27
--- /dev/null
+++ b/packages/primereact/.eslintrc
@@ -0,0 +1,9 @@
+{
+ "extends": "../../.eslintrc",
+ "plugins": [
+ "@typescript-eslint",
+ "jsx-a11y",
+ "react",
+ "import"
+ ]
+}
diff --git a/packages/primereact/README.md b/packages/primereact/README.md
new file mode 100644
index 0000000000..24ba4762c7
--- /dev/null
+++ b/packages/primereact/README.md
@@ -0,0 +1,118 @@
+[![Build Status][build-shield]][build-url]
+[![npm][npm-shield]][npm-url]
+[![npm downloads][npm-dl-shield]][npm-dl-url]
+[![Contributors][contributors-shield]][contributors-url]
+[![Apache 2.0 License][license-shield]][license-url]
+
+
+
+
+
+
+
+
@rjsf/primereact
+
+
+ PrimeReact theme, fields, and widgets for react-jsonschema-form
.
+
+ Explore the docs »
+
+
+ View Playground
+ ·
+ Report Bug
+ ·
+ Request Feature
+
+
+
+## Table of Contents
+
+- [About The Project](#about-the-project)
+ - [Built With](#built-with)
+- [Getting Started](#getting-started)
+ - [Prerequisites](#prerequisites)
+ - [Installation](#installation)
+- [Usage](#usage)
+- [Roadmap](#roadmap)
+- [Contributing](#contributing)
+- [Contact](#contact)
+
+## About The Project
+
+PrimeReact theme, fields, and widgets for `react-jsonschema-form`.
+
+### Built With
+
+- [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form/)
+- [PrimeReact](https://github.com/primefaces/primereact)
+
+## Getting Started
+
+### Prerequisites
+
+- `primereact >= 8.0.0`
+- `primeicons >= 6.0.0`
+- `@rjsf/core >= 2.0.0`
+
+```sh
+npm install primereact primeicons @rjsf/core
+```
+
+### Installation
+
+```sh
+npm install @rjsf/primereact
+```
+
+## Usage
+
+```javascript
+import Form from '@rjsf/primereact';
+```
+
+or
+
+```javascript
+import {withTheme} from '@rjsf/core';
+import {Theme as PrimeReactTheme} from '@rjsf/primereact';
+
+// Customize the theme with your own fields and widgets
+
+const Form = withTheme(PrimeReactTheme);
+```
+
+## Roadmap
+
+See the general [open issues](https://github.com/rjsf-team/react-jsonschema-form/issues).
+
+## Contributing
+
+Read our [contributors' guide](https://rjsf-team.github.io/react-jsonschema-form/docs/contributing/) to get started.
+
+## Contact
+
+rjsf team: [https://github.com/orgs/rjsf-team/people](https://github.com/orgs/rjsf-team/people)
+
+GitHub
+repository: [https://github.com/rjsf-team/react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form)
+
+[build-shield]: https://github.com/rjsf-team/react-jsonschema-form/workflows/CI/badge.svg
+
+[build-url]: https://github.com/rjsf-team/react-jsonschema-form/actions
+
+[contributors-shield]: https://img.shields.io/github/contributors/rjsf-team/react-jsonschema-form.svg
+
+[contributors-url]: https://github.com/rjsf-team/react-jsonschema-form/graphs/contributors
+
+[license-shield]: https://img.shields.io/badge/license-Apache%202.0-blue.svg?style=flat-square
+
+[license-url]: https://choosealicense.com/licenses/apache-2.0/
+
+[npm-shield]: https://img.shields.io/npm/v/@rjsf/primereact/latest.svg?style=flat-square
+
+[npm-url]: https://www.npmjs.com/package/@rjsf/primereact
+
+[npm-dl-shield]: https://img.shields.io/npm/dm/@rjsf/primereact.svg?style=flat-square
+
+[npm-dl-url]: https://www.npmjs.com/package/@rjsf/primereact
diff --git a/packages/primereact/babel.config.js b/packages/primereact/babel.config.js
new file mode 100644
index 0000000000..5f772a56c4
--- /dev/null
+++ b/packages/primereact/babel.config.js
@@ -0,0 +1,3 @@
+const defaultConfig = require('../../babel.config');
+
+module.exports = defaultConfig;
diff --git a/packages/primereact/jest.config.js b/packages/primereact/jest.config.js
new file mode 100644
index 0000000000..2a31de0575
--- /dev/null
+++ b/packages/primereact/jest.config.js
@@ -0,0 +1,11 @@
+module.exports = {
+ verbose: true,
+ testEnvironment: 'jsdom',
+ testEnvironmentOptions: {
+ browsers: ['chrome', 'firefox', 'safari'],
+ },
+ moduleNameMapper: {
+ '\\.(css|less)$': '/__mocks__/styleMock.js',
+ },
+ transformIgnorePatterns: [`/node_modules/(?!nanoid)`],
+};
diff --git a/packages/primereact/logo.png b/packages/primereact/logo.png
new file mode 100644
index 0000000000..b4a65d42e6
Binary files /dev/null and b/packages/primereact/logo.png differ
diff --git a/packages/primereact/package.json b/packages/primereact/package.json
new file mode 100644
index 0000000000..5dc01c966f
--- /dev/null
+++ b/packages/primereact/package.json
@@ -0,0 +1,103 @@
+{
+ "name": "@rjsf/primereact",
+ "version": "5.24.2",
+ "description": "PrimeReact theme, fields and widgets for react-jsonschema-form",
+ "main": "dist/index.js",
+ "module": "lib/index.js",
+ "typings": "lib/index.d.ts",
+ "scripts": {
+ "build:ts": "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/primereact.esm.js --sourcemap --packages=external --format=esm",
+ "build:umd": "rollup dist/primereact.esm.js --format=umd --file=dist/primereact.umd.js --name=@rjsf/primereact",
+ "build": "npm run build:ts && npm run build:cjs && npm run build:esm && npm run build:umd",
+ "cs-check": "prettier -l \"{src,test}/**/*.ts?(x)\"",
+ "cs-format": "prettier \"{src,test}/**/*.ts?(x)\" --write",
+ "lint": "eslint src test",
+ "precommit": "lint-staged",
+ "test": "jest",
+ "test:update": "jest --u"
+ },
+ "lint-staged": {
+ "{src,test}/**/*.ts?(x)": [
+ "eslint --fix"
+ ]
+ },
+ "files": [
+ "dist",
+ "lib",
+ "src"
+ ],
+ "engineStrict": false,
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "@rjsf/core": "^5.24.x",
+ "@rjsf/utils": "^5.24.x",
+ "primeicons": ">=6.0.0",
+ "primereact": ">=8.0.0",
+ "react": "^16.14.0 || >=17"
+ },
+ "dependencies": {
+ "classnames": "^2.5.1",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21"
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.23.9",
+ "@babel/core": "^7.23.9",
+ "@babel/eslint-parser": "^7.23.10",
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@babel/plugin-proposal-optional-chaining": "^7.21.0",
+ "@babel/preset-env": "^7.23.9",
+ "@babel/preset-react": "^7.23.3",
+ "@babel/preset-typescript": "^7.23.3",
+ "@rjsf/core": "^5.24.2",
+ "@rjsf/snapshot-tests": "^5.24.2",
+ "@rjsf/utils": "^5.24.2",
+ "@rjsf/validator-ajv8": "^5.24.2",
+ "@rollup/plugin-replace": "^5.0.5",
+ "@types/jest": "^29.5.12",
+ "@types/lodash": "^4.14.202",
+ "@types/react": "^18.2.58",
+ "@types/react-dom": "^18.2.19",
+ "@types/react-test-renderer": "^18.0.7",
+ "babel-jest": "^29.7.0",
+ "eslint": "^8.56.0",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
+ "primeicons": "^7.0.0",
+ "primeflex": "^3.3.1",
+ "primereact": "^10.9.2",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-test-renderer": "^18.2.0",
+ "rimraf": "^5.0.5",
+ "rollup": "^3.29.4",
+ "typescript": "^4.9.5"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rjsf-team/react-jsonschema-form.git"
+ },
+ "keywords": [
+ "primereact",
+ "primeicons",
+ "react",
+ "react-jsonschema-form",
+ "jsonschema",
+ "json-schema",
+ "json",
+ "rjsf-primereact"
+ ],
+ "author": "Arnold Daniels ",
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/rjsf-team/react-jsonschema-form/issues"
+ },
+ "homepage": "https://github.com/rjsf-team/react-jsonschema-form",
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/primereact/src/AddButton/AddButton.tsx b/packages/primereact/src/AddButton/AddButton.tsx
new file mode 100644
index 0000000000..86876e7b07
--- /dev/null
+++ b/packages/primereact/src/AddButton/AddButton.tsx
@@ -0,0 +1,23 @@
+import { Button } from 'primereact/button';
+import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
+
+/** The `AddButton` renders a button that represents the `Add` action on a form
+ */
+export default function AddButton({
+ uiSchema,
+ registry,
+ color,
+ ...props
+}: IconButtonProps) {
+ const { translateString } = registry;
+ return (
+
+ );
+}
diff --git a/packages/primereact/src/AddButton/index.ts b/packages/primereact/src/AddButton/index.ts
new file mode 100644
index 0000000000..752d720d32
--- /dev/null
+++ b/packages/primereact/src/AddButton/index.ts
@@ -0,0 +1,2 @@
+export { default } from './AddButton';
+export * from './AddButton';
diff --git a/packages/primereact/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx b/packages/primereact/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx
new file mode 100644
index 0000000000..561096867d
--- /dev/null
+++ b/packages/primereact/src/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx
@@ -0,0 +1,74 @@
+import { ArrayFieldTemplateItemType, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+
+/** The `ArrayFieldItemTemplate` component is the template used to render an items of an array.
+ *
+ * @param props - The `ArrayFieldTemplateItemType` props for the component
+ */
+export default function ArrayFieldItemTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: ArrayFieldTemplateItemType) {
+ const {
+ children,
+ disabled,
+ hasToolbar,
+ hasCopy,
+ hasMoveDown,
+ hasMoveUp,
+ hasRemove,
+ index,
+ onCopyIndexClick,
+ onDropIndexClick,
+ onReorderClick,
+ readonly,
+ uiSchema,
+ registry,
+ } = props;
+ const { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates;
+ return (
+
+ {hasToolbar && (
+
+ {(hasMoveUp || hasMoveDown || hasRemove) && (
+
+ {(hasMoveUp || hasMoveDown) && (
+
+ )}
+ {(hasMoveUp || hasMoveDown) && (
+
+ )}
+ {hasCopy && (
+
+ )}
+ {hasRemove && (
+
+ )}
+
+ )}
+
+ )}
+ {children}
+
+ );
+}
diff --git a/packages/primereact/src/ArrayFieldItemTemplate/index.ts b/packages/primereact/src/ArrayFieldItemTemplate/index.ts
new file mode 100644
index 0000000000..f104431399
--- /dev/null
+++ b/packages/primereact/src/ArrayFieldItemTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ArrayFieldItemTemplate';
+export * from './ArrayFieldItemTemplate';
diff --git a/packages/primereact/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/packages/primereact/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx
new file mode 100644
index 0000000000..956b63b37c
--- /dev/null
+++ b/packages/primereact/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx
@@ -0,0 +1,109 @@
+import {
+ getTemplate,
+ getUiOptions,
+ isFixedItems,
+ ArrayFieldTemplateProps,
+ ArrayFieldTemplateItemType,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ UI_OPTIONS_KEY,
+} from '@rjsf/utils';
+import { Fieldset } from 'primereact/fieldset';
+import AddButton from '../AddButton';
+
+/** The `ArrayFieldTemplate` component is the template used to render all items in an array.
+ *
+ * @param props - The `ArrayFieldTemplateItemType` props for the component
+ */
+export default function ArrayFieldTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: ArrayFieldTemplateProps) {
+ const {
+ uiSchema,
+ idSchema,
+ canAdd,
+ className,
+ disabled,
+ items,
+ onAddClick,
+ readonly,
+ schema,
+ title,
+ registry,
+ required,
+ ...rest
+ } = props;
+
+ const uiOptions = getUiOptions(uiSchema);
+ const ArrayFieldDescriptionTemplate = getTemplate<'ArrayFieldDescriptionTemplate', T, S, F>(
+ 'ArrayFieldDescriptionTemplate',
+ registry,
+ uiOptions
+ );
+ const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate', T, S, F>(
+ 'ArrayFieldItemTemplate',
+ registry,
+ uiOptions
+ );
+ const ArrayFieldTitleTemplate = getTemplate<'ArrayFieldTitleTemplate', T, S, F>(
+ 'ArrayFieldTitleTemplate',
+ registry,
+ uiOptions
+ );
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/packages/primereact/src/ArrayFieldTemplate/index.ts b/packages/primereact/src/ArrayFieldTemplate/index.ts
new file mode 100644
index 0000000000..ab908dec2c
--- /dev/null
+++ b/packages/primereact/src/ArrayFieldTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ArrayFieldTemplate';
+export * from './ArrayFieldTemplate';
diff --git a/packages/primereact/src/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx b/packages/primereact/src/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx
new file mode 100644
index 0000000000..017c94d6aa
--- /dev/null
+++ b/packages/primereact/src/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx
@@ -0,0 +1,14 @@
+import { ArrayFieldTitleProps, FormContextType, getUiOptions, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+
+export default function ArrayFieldTitleTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>({ title, uiSchema, required }: ArrayFieldTitleProps) {
+ const uiOptions = getUiOptions(uiSchema);
+ return (
+
+ {uiOptions.title || title} {required ? '*' : ''}
+
+ );
+}
diff --git a/packages/primereact/src/ArrayFieldTitleTemplate/index.ts b/packages/primereact/src/ArrayFieldTitleTemplate/index.ts
new file mode 100644
index 0000000000..9893e24393
--- /dev/null
+++ b/packages/primereact/src/ArrayFieldTitleTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ArrayFieldTitleTemplate';
+export * from './ArrayFieldTitleTemplate';
diff --git a/packages/primereact/src/AutoCompleteWidget/AutoCompleteWidget.tsx b/packages/primereact/src/AutoCompleteWidget/AutoCompleteWidget.tsx
new file mode 100644
index 0000000000..7b2e7f8f2f
--- /dev/null
+++ b/packages/primereact/src/AutoCompleteWidget/AutoCompleteWidget.tsx
@@ -0,0 +1,89 @@
+import {
+ ariaDescribedByIds,
+ FormContextType,
+ getInputProps,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { ChangeEvent, useState } from 'react';
+import { AutoComplete, AutoCompleteCompleteEvent } from 'primereact/autocomplete';
+
+export default function AutoCompleteWidget<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: WidgetProps) {
+ const {
+ id,
+ placeholder,
+ value,
+ required,
+ readonly,
+ disabled,
+ onChange,
+ onChangeOverride,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ type,
+ rawErrors = [],
+ } = props;
+ const inputProps = getInputProps(schema, type, options);
+ const _onChange = ({ target: { value } }: ChangeEvent) =>
+ onChange(value === '' ? options.emptyValue : value);
+ const _onBlur = () => onBlur && onBlur(id, value);
+ const _onFocus = () => onFocus && onFocus(id, value);
+
+ const examples = (schema.examples as string[]).concat(
+ schema.default && !(schema.examples as string[]).includes(schema.default.toString())
+ ? [schema.default.toString()]
+ : []
+ );
+
+ const [items, setItems] = useState([]);
+
+ const search = (event: AutoCompleteCompleteEvent) => {
+ setItems(examples.filter((example) => example.toString().toLowerCase().includes(event.query.toLowerCase())));
+ };
+
+ return (
+ >}
+ required={required}
+ autoFocus={autofocus}
+ disabled={disabled || readonly}
+ suggestions={items}
+ completeMethod={search}
+ value={value || value === 0 ? value : ''}
+ dropdown
+ invalid={rawErrors.length > 0}
+ onChange={(onChangeOverride as any) || _onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ aria-describedby={ariaDescribedByIds(id, !!schema.examples)}
+ /* Make autocomplete look like a dropdown, which looks much nicer */
+ pt={{
+ root: {
+ className: 'p-dropdown',
+ },
+ input: {
+ root: {
+ style: { border: 'none' },
+ },
+ },
+ dropdownButton: {
+ root: {
+ className: 'p-button-text p-button-secondary',
+ },
+ },
+ }}
+ />
+ );
+}
diff --git a/packages/primereact/src/AutoCompleteWidget/index.ts b/packages/primereact/src/AutoCompleteWidget/index.ts
new file mode 100644
index 0000000000..a6c6ea9599
--- /dev/null
+++ b/packages/primereact/src/AutoCompleteWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './AutoCompleteWidget';
+export * from './AutoCompleteWidget';
diff --git a/packages/primereact/src/BaseInputTemplate/BaseInputTemplate.tsx b/packages/primereact/src/BaseInputTemplate/BaseInputTemplate.tsx
new file mode 100644
index 0000000000..fbbc749c23
--- /dev/null
+++ b/packages/primereact/src/BaseInputTemplate/BaseInputTemplate.tsx
@@ -0,0 +1,69 @@
+import {
+ ariaDescribedByIds,
+ BaseInputTemplateProps,
+ examplesId,
+ FormContextType,
+ getInputProps,
+ RJSFSchema,
+ StrictRJSFSchema,
+} from '@rjsf/utils';
+import { ChangeEvent } from 'react';
+import { InputText } from 'primereact/inputtext';
+
+/** The `BaseInputTemplate` is the template the fallback if no widget is specified.
+ */
+export default function BaseInputTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: BaseInputTemplateProps) {
+ const {
+ id,
+ placeholder,
+ value,
+ required,
+ readonly,
+ disabled,
+ onChange,
+ onChangeOverride,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ type,
+ registry,
+ rawErrors = [],
+ } = props;
+
+ const { AutoCompleteWidget } = registry.widgets;
+
+ if (Array.isArray(schema.examples)) {
+ return ;
+ }
+
+ const inputProps = getInputProps(schema, type, options);
+ const _onChange = ({ target: { value } }: ChangeEvent) =>
+ onChange(value === '' ? options.emptyValue : value);
+ const _onBlur = () => onBlur && onBlur(id, value);
+ const _onFocus = () => onFocus && onFocus(id, value);
+
+ return (
+ (id) : undefined}
+ value={value || value === 0 ? value : ''}
+ invalid={rawErrors.length > 0}
+ onChange={onChangeOverride || _onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ aria-describedby={ariaDescribedByIds(id, !!schema.examples)}
+ />
+ );
+}
diff --git a/packages/primereact/src/BaseInputTemplate/index.ts b/packages/primereact/src/BaseInputTemplate/index.ts
new file mode 100644
index 0000000000..f7ef8d5939
--- /dev/null
+++ b/packages/primereact/src/BaseInputTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './BaseInputTemplate';
+export * from './BaseInputTemplate';
diff --git a/packages/primereact/src/CheckboxWidget/CheckboxWidget.tsx b/packages/primereact/src/CheckboxWidget/CheckboxWidget.tsx
new file mode 100644
index 0000000000..f6722e9227
--- /dev/null
+++ b/packages/primereact/src/CheckboxWidget/CheckboxWidget.tsx
@@ -0,0 +1,86 @@
+import {
+ ariaDescribedByIds,
+ descriptionId,
+ getTemplate,
+ labelValue,
+ schemaRequiresTrueValue,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { Checkbox, CheckboxChangeEvent } from 'primereact/checkbox';
+import { Label } from '../util';
+
+/** The `CheckBoxWidget` is a widget for rendering boolean properties.
+ * It is typically used to represent a boolean.
+ *
+ * @param props - The `WidgetProps` for this component
+ */
+export default function CheckboxWidget<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: WidgetProps) {
+ const {
+ id,
+ value,
+ disabled,
+ readonly,
+ label = '',
+ hideLabel,
+ autofocus,
+ onChange,
+ onBlur,
+ options,
+ onFocus,
+ schema,
+ uiSchema,
+ registry,
+ } = props;
+
+ const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>(
+ 'DescriptionFieldTemplate',
+ registry,
+ options
+ );
+
+ const required = schemaRequiresTrueValue(schema);
+ const _onChange = (e: CheckboxChangeEvent) => onChange && onChange(e.checked);
+ const _onBlur = () => onBlur && onBlur(id, value);
+ const _onFocus = () => onFocus && onFocus(id, value);
+ const checked = value === 'true' || value === true;
+ const description = options.description ?? schema.description;
+
+ return (
+ <>
+ {!hideLabel && !!description && (
+ (id)}
+ description={description}
+ schema={schema}
+ uiSchema={uiSchema}
+ registry={registry}
+ />
+ )}
+
+ (id)}
+ />
+ {labelValue(, hideLabel, false)}
+
+ >
+ );
+}
diff --git a/packages/primereact/src/CheckboxWidget/index.ts b/packages/primereact/src/CheckboxWidget/index.ts
new file mode 100644
index 0000000000..b9e3c318ec
--- /dev/null
+++ b/packages/primereact/src/CheckboxWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './CheckboxWidget';
+export * from './CheckboxWidget';
diff --git a/packages/primereact/src/CheckboxesWidget/CheckboxesWidget.tsx b/packages/primereact/src/CheckboxesWidget/CheckboxesWidget.tsx
new file mode 100644
index 0000000000..17e4d6ee23
--- /dev/null
+++ b/packages/primereact/src/CheckboxesWidget/CheckboxesWidget.tsx
@@ -0,0 +1,106 @@
+import { Checkbox, CheckboxChangeEvent } from 'primereact/checkbox';
+import {
+ ariaDescribedByIds,
+ enumOptionsDeselectValue,
+ enumOptionsIsSelected,
+ enumOptionsSelectValue,
+ optionId,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+ descriptionId,
+ getTemplate,
+} from '@rjsf/utils';
+import { Label } from '../util';
+
+/** The `CheckboxesWidget` is a widget for rendering checkbox groups.
+ * It is typically used to represent an array of enums.
+ *
+ * @param props - The `WidgetProps` for this component
+ */
+export default function CheckboxesWidget<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: WidgetProps) {
+ const {
+ id,
+ disabled,
+ options,
+ value,
+ autofocus,
+ readonly,
+ onChange,
+ onBlur,
+ onFocus,
+ schema,
+ uiSchema,
+ registry,
+ hideLabel,
+ } = props;
+ const { enumOptions, enumDisabled } = options;
+ const checkboxesValues = Array.isArray(value) ? value : [value];
+
+ const _onChange = (index: number) => (e: CheckboxChangeEvent) => {
+ if (e.checked) {
+ onChange(enumOptionsSelectValue(index, checkboxesValues, enumOptions));
+ } else {
+ onChange(enumOptionsDeselectValue(index, checkboxesValues, enumOptions));
+ }
+ };
+
+ const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>(
+ 'DescriptionFieldTemplate',
+ registry,
+ options
+ );
+
+ const _onBlur = () => onBlur(id, value);
+ const _onFocus = () => onFocus(id, value);
+
+ const description = options.description ?? schema.description;
+
+ return (
+
+ {!hideLabel && !!description && (
+
(id)}
+ description={description}
+ schema={schema}
+ uiSchema={uiSchema}
+ registry={registry}
+ />
+ )}
+ {Array.isArray(enumOptions) &&
+ enumOptions.map((option, index) => {
+ const checked = enumOptionsIsSelected(option.value, checkboxesValues);
+ const itemDisabled = Array.isArray(enumDisabled) && enumDisabled.indexOf(option.value) !== -1;
+ return (
+
+ (id)}
+ />
+
+
+ );
+ })}
+
+ );
+}
diff --git a/packages/primereact/src/CheckboxesWidget/index.ts b/packages/primereact/src/CheckboxesWidget/index.ts
new file mode 100644
index 0000000000..97152004fa
--- /dev/null
+++ b/packages/primereact/src/CheckboxesWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './CheckboxesWidget';
+export * from './CheckboxesWidget';
diff --git a/packages/primereact/src/ColorWidget/ColorWidget.tsx b/packages/primereact/src/ColorWidget/ColorWidget.tsx
new file mode 100644
index 0000000000..2f3d149afb
--- /dev/null
+++ b/packages/primereact/src/ColorWidget/ColorWidget.tsx
@@ -0,0 +1,56 @@
+import {
+ ariaDescribedByIds,
+ FormContextType,
+ getInputProps,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { ChangeEvent } from 'react';
+import { ColorPicker } from 'primereact/colorpicker';
+
+export default function ColorWidget(
+ props: WidgetProps
+) {
+ const {
+ id,
+ placeholder,
+ value,
+ required,
+ readonly,
+ disabled,
+ onChange,
+ onChangeOverride,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ type,
+ } = props;
+ const inputProps = getInputProps(schema, type, options);
+ const { inline } = options;
+
+ const _onChange = ({ target: { value } }: ChangeEvent) =>
+ onChange(value === '' ? options.emptyValue : value);
+ const _onBlur = () => onBlur && onBlur(id, value);
+ const _onFocus = () => onFocus && onFocus(id, value);
+
+ return (
+ (id, !!schema.examples)}
+ />
+ );
+}
diff --git a/packages/primereact/src/ColorWidget/index.ts b/packages/primereact/src/ColorWidget/index.ts
new file mode 100644
index 0000000000..9d202495a3
--- /dev/null
+++ b/packages/primereact/src/ColorWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ColorWidget';
+export * from './ColorWidget';
diff --git a/packages/primereact/src/DescriptionField/DescriptionField.tsx b/packages/primereact/src/DescriptionField/DescriptionField.tsx
new file mode 100644
index 0000000000..ba29039282
--- /dev/null
+++ b/packages/primereact/src/DescriptionField/DescriptionField.tsx
@@ -0,0 +1,17 @@
+import { DescriptionFieldProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+
+/** The `DescriptionField` is the template to use to render the description of a field
+ *
+ * @param props - The `DescriptionFieldProps` for this component
+ */
+export default function DescriptionField<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: DescriptionFieldProps) {
+ const { id, description } = props;
+ if (!description) {
+ return null;
+ }
+ return {description};
+}
diff --git a/packages/primereact/src/DescriptionField/index.ts b/packages/primereact/src/DescriptionField/index.ts
new file mode 100644
index 0000000000..401540d99b
--- /dev/null
+++ b/packages/primereact/src/DescriptionField/index.ts
@@ -0,0 +1,2 @@
+export { default } from './DescriptionField';
+export * from './DescriptionField';
diff --git a/packages/primereact/src/ErrorList/ErrorList.tsx b/packages/primereact/src/ErrorList/ErrorList.tsx
new file mode 100644
index 0000000000..8bfede575c
--- /dev/null
+++ b/packages/primereact/src/ErrorList/ErrorList.tsx
@@ -0,0 +1,44 @@
+import { Message } from 'primereact/message';
+import { TimesCircleIcon } from 'primereact/icons/timescircle';
+import { ErrorListProps, FormContextType, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
+
+/** The `ErrorList` component is the template that renders all the errors associated with the fields in the `Form`
+ *
+ * @param props - The `ErrorListProps` for this component
+ */
+export default function ErrorList({
+ errors,
+ registry,
+}: ErrorListProps) {
+ const { translateString } = registry;
+
+ const content = (
+
+
+
+
{translateString(TranslatableString.ErrorsLabel)}
+
+
+ {errors.map((error, index) => (
+ - {error.stack}
+ ))}
+
+
+ );
+
+ return (
+
+ );
+}
diff --git a/packages/primereact/src/ErrorList/index.ts b/packages/primereact/src/ErrorList/index.ts
new file mode 100644
index 0000000000..79376ace11
--- /dev/null
+++ b/packages/primereact/src/ErrorList/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ErrorList';
+export * from './ErrorList';
diff --git a/packages/primereact/src/FieldErrorTemplate/FieldErrorTemplate.tsx b/packages/primereact/src/FieldErrorTemplate/FieldErrorTemplate.tsx
new file mode 100644
index 0000000000..f7d67fb38e
--- /dev/null
+++ b/packages/primereact/src/FieldErrorTemplate/FieldErrorTemplate.tsx
@@ -0,0 +1,30 @@
+import { errorId, FieldErrorProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+import { Message } from 'primereact/message';
+
+/** The `FieldErrorTemplate` component renders the errors local to the particular field
+ *
+ * @param props - The `FieldErrorProps` for the errors being rendered
+ */
+export default function FieldErrorTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>({ errors, idSchema }: FieldErrorProps) {
+ if (errors && errors.length > 0) {
+ const id = errorId(idSchema);
+ const content = errors.map((error, i: number) => {
+ return {error}
;
+ });
+
+ return (
+
+ );
+ }
+ return null;
+}
diff --git a/packages/primereact/src/FieldErrorTemplate/index.ts b/packages/primereact/src/FieldErrorTemplate/index.ts
new file mode 100644
index 0000000000..2fbf1c353d
--- /dev/null
+++ b/packages/primereact/src/FieldErrorTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './FieldErrorTemplate';
+export * from './FieldErrorTemplate';
diff --git a/packages/primereact/src/FieldHelpTemplate/FieldHelpTemplate.tsx b/packages/primereact/src/FieldHelpTemplate/FieldHelpTemplate.tsx
new file mode 100644
index 0000000000..ba6bab868d
--- /dev/null
+++ b/packages/primereact/src/FieldHelpTemplate/FieldHelpTemplate.tsx
@@ -0,0 +1,18 @@
+import { FieldHelpProps, FormContextType, RJSFSchema, StrictRJSFSchema, helpId } from '@rjsf/utils';
+
+/** The `FieldHelpTemplate` component renders any help desired for a field
+ *
+ * @param props - The `FieldHelpProps` to be rendered
+ */
+export default function FieldHelpTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: FieldHelpProps) {
+ const { idSchema, help } = props;
+ if (help) {
+ const id = helpId(idSchema);
+ return {help};
+ }
+ return null;
+}
diff --git a/packages/primereact/src/FieldHelpTemplate/index.ts b/packages/primereact/src/FieldHelpTemplate/index.ts
new file mode 100644
index 0000000000..b439bce3f1
--- /dev/null
+++ b/packages/primereact/src/FieldHelpTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './FieldHelpTemplate';
+export * from './FieldHelpTemplate';
diff --git a/packages/primereact/src/FieldTemplate/FieldTemplate.tsx b/packages/primereact/src/FieldTemplate/FieldTemplate.tsx
new file mode 100644
index 0000000000..2568b1e1a6
--- /dev/null
+++ b/packages/primereact/src/FieldTemplate/FieldTemplate.tsx
@@ -0,0 +1,39 @@
+import {
+ FieldTemplateProps,
+ FormContextType,
+ getTemplate,
+ getUiOptions,
+ RJSFSchema,
+ StrictRJSFSchema,
+} from '@rjsf/utils';
+import { Label } from '../util';
+
+export default function FieldTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: FieldTemplateProps) {
+ const { children, errors, help, displayLabel, description, rawDescription, hidden, uiSchema, registry } = props;
+ const uiOptions = getUiOptions(uiSchema);
+ const WrapIfAdditionalTemplate = getTemplate<'WrapIfAdditionalTemplate', T, S, F>(
+ 'WrapIfAdditionalTemplate',
+ registry,
+ uiOptions
+ );
+
+ if (hidden) {
+ return {children}
;
+ }
+
+ return (
+
+
+ {displayLabel && }
+ {children}
+ {displayLabel && rawDescription && {description}}
+ {errors}
+ {help}
+
+
+ );
+}
diff --git a/packages/primereact/src/FieldTemplate/index.ts b/packages/primereact/src/FieldTemplate/index.ts
new file mode 100644
index 0000000000..6f7dc3861c
--- /dev/null
+++ b/packages/primereact/src/FieldTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './FieldTemplate';
+export * from './FieldTemplate';
diff --git a/packages/primereact/src/IconButton/IconButton.tsx b/packages/primereact/src/IconButton/IconButton.tsx
new file mode 100644
index 0000000000..e757624e54
--- /dev/null
+++ b/packages/primereact/src/IconButton/IconButton.tsx
@@ -0,0 +1,47 @@
+import { Button } from 'primereact/button';
+import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
+
+function IconButton(
+ props: IconButtonProps
+) {
+ const { icon, iconType, uiSchema, registry, ...otherProps } = props;
+ return ;
+}
+
+export default IconButton;
+
+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/primereact/src/IconButton/index.ts b/packages/primereact/src/IconButton/index.ts
new file mode 100644
index 0000000000..655ec4c488
--- /dev/null
+++ b/packages/primereact/src/IconButton/index.ts
@@ -0,0 +1,2 @@
+export { default } from './IconButton';
+export * from './IconButton';
diff --git a/packages/primereact/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx b/packages/primereact/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx
new file mode 100644
index 0000000000..e643c65cbd
--- /dev/null
+++ b/packages/primereact/src/ObjectFieldTemplate/ObjectFieldTemplate.tsx
@@ -0,0 +1,85 @@
+import {
+ FormContextType,
+ ObjectFieldTemplateProps,
+ RJSFSchema,
+ StrictRJSFSchema,
+ canExpand,
+ descriptionId,
+ getTemplate,
+ getUiOptions,
+ titleId,
+} from '@rjsf/utils';
+
+/** The `ObjectFieldTemplate` is the template to use to render all the inner properties of an object along with the
+ * title and description if available. If the object is expandable, then an `AddButton` is also rendered after all
+ * the properties.
+ *
+ * @param props - The `ObjectFieldTemplateProps` for this component
+ */
+export default function ObjectFieldTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: ObjectFieldTemplateProps) {
+ const {
+ description,
+ onAddClick,
+ title,
+ properties,
+ disabled,
+ readonly,
+ required,
+ uiSchema,
+ schema,
+ formData,
+ idSchema,
+ 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((prop) => prop.content)}
+ {canExpand(schema, uiSchema, formData) && (
+
+ )}
+ >
+ );
+}
diff --git a/packages/primereact/src/ObjectFieldTemplate/index.ts b/packages/primereact/src/ObjectFieldTemplate/index.ts
new file mode 100644
index 0000000000..77c68a9a40
--- /dev/null
+++ b/packages/primereact/src/ObjectFieldTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './ObjectFieldTemplate';
+export * from './ObjectFieldTemplate';
diff --git a/packages/primereact/src/PasswordWidget/PasswordWidget.tsx b/packages/primereact/src/PasswordWidget/PasswordWidget.tsx
new file mode 100644
index 0000000000..8f08ad078e
--- /dev/null
+++ b/packages/primereact/src/PasswordWidget/PasswordWidget.tsx
@@ -0,0 +1,66 @@
+import {
+ ariaDescribedByIds,
+ FormContextType,
+ getInputProps,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { ChangeEvent } from 'react';
+import { Password } from 'primereact/password';
+
+export default function PasswordWidget<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: WidgetProps) {
+ const {
+ id,
+ placeholder,
+ value,
+ required,
+ readonly,
+ disabled,
+ onChange,
+ onChangeOverride,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ type,
+ rawErrors = [],
+ } = props;
+ const inputProps = getInputProps(schema, type, options);
+ const { feedback, promptLabel, weakLabel, mediumLabel, strongLabel, toggleMask } = options;
+
+ const _onChange = ({ target: { value } }: ChangeEvent) =>
+ onChange(value === '' ? options.emptyValue : value);
+ const _onBlur = () => onBlur && onBlur(id, value);
+ const _onFocus = () => onFocus && onFocus(id, value);
+
+ return (
+ 0}
+ feedback={!!feedback}
+ promptLabel={promptLabel as string}
+ weakLabel={weakLabel as string}
+ mediumLabel={mediumLabel as string}
+ strongLabel={strongLabel as string}
+ toggleMask={!!toggleMask}
+ onChange={onChangeOverride || _onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ aria-describedby={ariaDescribedByIds(id, !!schema.examples)}
+ pt={{ root: { style: { display: 'flex', flexDirection: 'column' } } }}
+ />
+ );
+}
diff --git a/packages/primereact/src/PasswordWidget/index.ts b/packages/primereact/src/PasswordWidget/index.ts
new file mode 100644
index 0000000000..84fd5e9026
--- /dev/null
+++ b/packages/primereact/src/PasswordWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './PasswordWidget';
+export * from './PasswordWidget';
diff --git a/packages/primereact/src/PrimeForm/PrimeForm.ts b/packages/primereact/src/PrimeForm/PrimeForm.ts
new file mode 100644
index 0000000000..ccc1d3588b
--- /dev/null
+++ b/packages/primereact/src/PrimeForm/PrimeForm.ts
@@ -0,0 +1,15 @@
+import { ComponentType } from 'react';
+import { withTheme, FormProps } from '@rjsf/core';
+import { FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+
+import { generateTheme } from '../Theme';
+
+export function generateForm<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(): ComponentType> {
+ return withTheme(generateTheme());
+}
+
+export default generateForm();
diff --git a/packages/primereact/src/PrimeForm/index.ts b/packages/primereact/src/PrimeForm/index.ts
new file mode 100644
index 0000000000..c33c2151a8
--- /dev/null
+++ b/packages/primereact/src/PrimeForm/index.ts
@@ -0,0 +1,2 @@
+export { default } from './PrimeForm';
+export * from './PrimeForm';
diff --git a/packages/primereact/src/RadioWidget/RadioWidget.tsx b/packages/primereact/src/RadioWidget/RadioWidget.tsx
new file mode 100644
index 0000000000..2939e263ef
--- /dev/null
+++ b/packages/primereact/src/RadioWidget/RadioWidget.tsx
@@ -0,0 +1,58 @@
+import {
+ ariaDescribedByIds,
+ enumOptionsIsSelected,
+ enumOptionsValueForIndex,
+ optionId,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { RadioButton, RadioButtonChangeEvent } from 'primereact/radiobutton';
+
+/** The `RadioWidget` is a widget for rendering a radio group.
+ * It is typically used with a string property constrained with enum options.
+ *
+ * @param props - The `WidgetProps` for this component
+ */
+export default function RadioWidget(
+ props: WidgetProps
+) {
+ const { id, value, disabled, readonly, onChange, onBlur, onFocus, options } = props;
+ const { enumOptions, enumDisabled, emptyValue } = options;
+
+ const _onChange = (e: RadioButtonChangeEvent) => {
+ onChange(enumOptionsValueForIndex(e.value, enumOptions, emptyValue));
+ };
+
+ const _onBlur = () => onBlur(id, value);
+ const _onFocus = () => onFocus(id, value);
+
+ return (
+
+ {Array.isArray(enumOptions) &&
+ enumOptions.map((option, index) => {
+ const checked = enumOptionsIsSelected
(option.value, value);
+ const itemDisabled = Array.isArray(enumDisabled) && enumDisabled.indexOf(option.value) !== -1;
+ return (
+
+ (id)}
+ />
+
+
+ );
+ })}
+
+ );
+}
diff --git a/packages/primereact/src/RadioWidget/index.ts b/packages/primereact/src/RadioWidget/index.ts
new file mode 100644
index 0000000000..10292dc565
--- /dev/null
+++ b/packages/primereact/src/RadioWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './RadioWidget';
+export * from './RadioWidget';
diff --git a/packages/primereact/src/RangeWidget/RangeWidget.tsx b/packages/primereact/src/RangeWidget/RangeWidget.tsx
new file mode 100644
index 0000000000..e337e9e45b
--- /dev/null
+++ b/packages/primereact/src/RangeWidget/RangeWidget.tsx
@@ -0,0 +1,35 @@
+import { FocusEvent } from 'react';
+import { Slider, SliderChangeEvent } from 'primereact/slider';
+import { ariaDescribedByIds, FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps, rangeSpec } from '@rjsf/utils';
+
+/** The `RangeWidget` component uses the `Slider` from PrimeReact, wrapping the result
+ * in a div, with the value alongside it.
+ *
+ * @param props - The `WidgetProps` for this component
+ */
+export default function RangeWidget(
+ props: WidgetProps
+) {
+ const { value, readonly, disabled, onBlur, onFocus, options, schema, onChange, id } = props;
+ const sliderProps = { value, id, ...rangeSpec(schema) };
+
+ const _onChange = (e: SliderChangeEvent) => {
+ onChange(e.value ?? options.emptyValue);
+ };
+ const _onBlur = ({ target }: FocusEvent) => onBlur(id, target && target.value);
+ const _onFocus = ({ target }: FocusEvent) => onFocus(id, target && target.value);
+
+ return (
+ <>
+ e.stopPropagation()}
+ onChange={_onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ {...sliderProps}
+ aria-describedby={ariaDescribedByIds(id)}
+ />
+ >
+ );
+}
diff --git a/packages/primereact/src/RangeWidget/index.ts b/packages/primereact/src/RangeWidget/index.ts
new file mode 100644
index 0000000000..d8c49226c6
--- /dev/null
+++ b/packages/primereact/src/RangeWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './RangeWidget';
+export * from './RangeWidget';
diff --git a/packages/primereact/src/SelectWidget/SelectWidget.tsx b/packages/primereact/src/SelectWidget/SelectWidget.tsx
new file mode 100644
index 0000000000..c7e8eaee67
--- /dev/null
+++ b/packages/primereact/src/SelectWidget/SelectWidget.tsx
@@ -0,0 +1,137 @@
+import { FocusEvent } from 'react';
+import { Dropdown } from 'primereact/dropdown';
+import {
+ ariaDescribedByIds,
+ enumOptionsIndexForValue,
+ enumOptionsValueForIndex,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { MultiSelect } from 'primereact/multiselect';
+
+/** The `SelectWidget` is a widget for rendering dropdowns.
+ * It is typically used with string properties constrained with enum options.
+ *
+ * @param props - The `WidgetProps` for this component
+ */
+export default function SelectWidget(
+ props: WidgetProps
+) {
+ const multiple = typeof props.multiple === 'undefined' ? false : props.multiple;
+
+ return multiple ? : ;
+}
+
+function SingleSelectWidget({
+ schema,
+ id,
+ name, // remove this from dropdownProps
+ options,
+ label,
+ hideLabel,
+ required,
+ disabled,
+ placeholder,
+ readonly,
+ value,
+ multiple,
+ autofocus,
+ onChange,
+ onBlur,
+ onFocus,
+ errorSchema,
+ rawErrors = [],
+ registry,
+ uiSchema,
+ hideError,
+ formContext,
+ ...dropdownProps
+}: 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 _onChange = (e: { value: any }) => onChange(enumOptionsValueForIndex(e.value, enumOptions, optEmptyVal));
+ 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 { ...dropdownRemainingProps } = dropdownProps;
+
+ return (
+ ({
+ label,
+ value: String(i),
+ disabled: Array.isArray(enumDisabled) && enumDisabled.indexOf(value) !== -1,
+ }))}
+ onChange={_onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ placeholder={placeholder}
+ disabled={disabled || readonly}
+ autoFocus={autofocus}
+ aria-describedby={ariaDescribedByIds(id)}
+ {...dropdownRemainingProps}
+ />
+ );
+}
+
+function MultiSelectWidget({
+ id,
+ options,
+ disabled,
+ placeholder,
+ readonly,
+ value,
+ multiple,
+ autofocus,
+ onChange,
+ onBlur,
+ onFocus,
+}: 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 _onChange = (e: { value: any }) => onChange(enumOptionsValueForIndex(e.value, enumOptions, optEmptyVal));
+ 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);
+
+ return (
+ ({
+ label,
+ value: String(i),
+ disabled: Array.isArray(enumDisabled) && enumDisabled.indexOf(value) !== -1,
+ }))}
+ onChange={_onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ placeholder={placeholder}
+ disabled={disabled || readonly}
+ autoFocus={autofocus}
+ display={options.display === 'chip' ? 'chip' : 'comma'}
+ aria-describedby={ariaDescribedByIds(id)}
+ pt={{ root: { style: { position: 'relative' } } }}
+ />
+ );
+}
diff --git a/packages/primereact/src/SelectWidget/index.ts b/packages/primereact/src/SelectWidget/index.ts
new file mode 100644
index 0000000000..e37ea725b8
--- /dev/null
+++ b/packages/primereact/src/SelectWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './SelectWidget';
+export * from './SelectWidget';
diff --git a/packages/primereact/src/SubmitButton/SubmitButton.tsx b/packages/primereact/src/SubmitButton/SubmitButton.tsx
new file mode 100644
index 0000000000..081e1d01cf
--- /dev/null
+++ b/packages/primereact/src/SubmitButton/SubmitButton.tsx
@@ -0,0 +1,16 @@
+import { Button } from 'primereact/button';
+import { getSubmitButtonOptions, FormContextType, RJSFSchema, StrictRJSFSchema, SubmitButtonProps } from '@rjsf/utils';
+
+/** The `SubmitButton` renders a button that represents 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/primereact/src/SubmitButton/index.ts b/packages/primereact/src/SubmitButton/index.ts
new file mode 100644
index 0000000000..f676497ba2
--- /dev/null
+++ b/packages/primereact/src/SubmitButton/index.ts
@@ -0,0 +1,2 @@
+export { default } from './SubmitButton';
+export * from './SubmitButton';
diff --git a/packages/primereact/src/Templates/Templates.ts b/packages/primereact/src/Templates/Templates.ts
new file mode 100644
index 0000000000..f4578fcd20
--- /dev/null
+++ b/packages/primereact/src/Templates/Templates.ts
@@ -0,0 +1,48 @@
+import { FormContextType, RJSFSchema, StrictRJSFSchema, TemplatesType } from '@rjsf/utils';
+
+import AddButton from '../AddButton';
+import ArrayFieldItemTemplate from '../ArrayFieldItemTemplate';
+import ArrayFieldTemplate from '../ArrayFieldTemplate';
+import BaseInputTemplate from '../BaseInputTemplate';
+import DescriptionField from '../DescriptionField';
+import ErrorList from '../ErrorList';
+import { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from '../IconButton';
+import FieldErrorTemplate from '../FieldErrorTemplate';
+import FieldHelpTemplate from '../FieldHelpTemplate';
+import FieldTemplate from '../FieldTemplate';
+import ObjectFieldTemplate from '../ObjectFieldTemplate';
+import SubmitButton from '../SubmitButton';
+import TitleField from '../TitleField';
+import WrapIfAdditionalTemplate from '../WrapIfAdditionalTemplate';
+import ArrayFieldTitleTemplate from '../ArrayFieldTitleTemplate';
+
+export function generateTemplates<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(): Partial> {
+ return {
+ ArrayFieldItemTemplate,
+ ArrayFieldTemplate,
+ ArrayFieldTitleTemplate,
+ BaseInputTemplate,
+ ButtonTemplates: {
+ AddButton,
+ CopyButton,
+ MoveDownButton,
+ MoveUpButton,
+ RemoveButton,
+ SubmitButton,
+ },
+ DescriptionFieldTemplate: DescriptionField,
+ ErrorListTemplate: ErrorList,
+ FieldErrorTemplate,
+ FieldHelpTemplate,
+ FieldTemplate,
+ ObjectFieldTemplate,
+ TitleFieldTemplate: TitleField,
+ WrapIfAdditionalTemplate,
+ };
+}
+
+export default generateTemplates();
diff --git a/packages/primereact/src/Templates/index.ts b/packages/primereact/src/Templates/index.ts
new file mode 100644
index 0000000000..612ccf692a
--- /dev/null
+++ b/packages/primereact/src/Templates/index.ts
@@ -0,0 +1,2 @@
+export { default } from './Templates';
+export * from './Templates';
diff --git a/packages/primereact/src/TextareaWidget/TextareaWidget.tsx b/packages/primereact/src/TextareaWidget/TextareaWidget.tsx
new file mode 100644
index 0000000000..8654458155
--- /dev/null
+++ b/packages/primereact/src/TextareaWidget/TextareaWidget.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { InputTextarea } from 'primereact/inputtextarea';
+import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils';
+
+/** The `TextareaWidget` is a widget for rendering input fields as textarea using PrimeReact.
+ *
+ * @param props - The `WidgetProps` for this component
+ */
+export default function TextareaWidget<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: WidgetProps) {
+ const { id, value, required, disabled, readonly, autofocus, onChange, onBlur, onFocus, options } = props;
+
+ let rows = 5;
+ // noinspection SuspiciousTypeOfGuard
+ if (typeof options.rows === 'string' || typeof options.rows === 'number') {
+ rows = Number(options.rows);
+ }
+
+ const handleChange = (event: React.ChangeEvent) => {
+ onChange(event.target.value === '' ? options.emptyValue : event.target.value);
+ };
+
+ return (
+ onBlur(id, event.target.value))}
+ onFocus={onFocus && ((event) => onFocus(id, event.target.value))}
+ />
+ );
+}
diff --git a/packages/primereact/src/TextareaWidget/index.ts b/packages/primereact/src/TextareaWidget/index.ts
new file mode 100644
index 0000000000..20e6d8e26b
--- /dev/null
+++ b/packages/primereact/src/TextareaWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './TextareaWidget';
+export * from './TextareaWidget';
diff --git a/packages/primereact/src/Theme/Theme.ts b/packages/primereact/src/Theme/Theme.ts
new file mode 100644
index 0000000000..14f4669804
--- /dev/null
+++ b/packages/primereact/src/Theme/Theme.ts
@@ -0,0 +1,20 @@
+import { FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+import { ThemeProps } from '@rjsf/core';
+import { Form as SuiForm } from 'semantic-ui-react';
+
+import { generateTemplates } from '../Templates';
+import { generateWidgets } from '../Widgets';
+
+export function generateTheme<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(): ThemeProps {
+ return {
+ templates: generateTemplates(),
+ widgets: generateWidgets(),
+ _internalFormWrapper: SuiForm,
+ };
+}
+
+export default generateTheme();
diff --git a/packages/primereact/src/Theme/index.ts b/packages/primereact/src/Theme/index.ts
new file mode 100644
index 0000000000..6dfd7fa6e1
--- /dev/null
+++ b/packages/primereact/src/Theme/index.ts
@@ -0,0 +1,2 @@
+export { default } from './Theme';
+export * from './Theme';
diff --git a/packages/primereact/src/TitleField/TitleField.tsx b/packages/primereact/src/TitleField/TitleField.tsx
new file mode 100644
index 0000000000..a799bc2c76
--- /dev/null
+++ b/packages/primereact/src/TitleField/TitleField.tsx
@@ -0,0 +1,19 @@
+import { Divider } from 'primereact/divider';
+import { FormContextType, getUiOptions, RJSFSchema, StrictRJSFSchema, TitleFieldProps } from '@rjsf/utils';
+
+export default function TitleField({
+ id,
+ title,
+ uiSchema,
+ required,
+}: TitleFieldProps) {
+ const uiOptions = getUiOptions(uiSchema);
+ return (
+
+
+ {uiOptions.title || title} {required ? '*' : ''}
+
+
+
+ );
+}
diff --git a/packages/primereact/src/TitleField/index.ts b/packages/primereact/src/TitleField/index.ts
new file mode 100644
index 0000000000..cfa479d034
--- /dev/null
+++ b/packages/primereact/src/TitleField/index.ts
@@ -0,0 +1,2 @@
+export { default } from './TitleField';
+export * from './TitleField';
diff --git a/packages/primereact/src/UpDownWidget/UpDownWidget.tsx b/packages/primereact/src/UpDownWidget/UpDownWidget.tsx
new file mode 100644
index 0000000000..6789472990
--- /dev/null
+++ b/packages/primereact/src/UpDownWidget/UpDownWidget.tsx
@@ -0,0 +1,63 @@
+import {
+ ariaDescribedByIds,
+ FormContextType,
+ getInputProps,
+ RJSFSchema,
+ StrictRJSFSchema,
+ WidgetProps,
+} from '@rjsf/utils';
+import { InputNumber, InputNumberChangeEvent } from 'primereact/inputnumber';
+
+export default function UpDownWidget(
+ props: WidgetProps
+) {
+ const {
+ id,
+ placeholder,
+ value,
+ required,
+ readonly,
+ disabled,
+ onChange,
+ onChangeOverride,
+ onBlur,
+ onFocus,
+ autofocus,
+ options,
+ schema,
+ type,
+ rawErrors = [],
+ } = props;
+ const inputProps = getInputProps(schema, type, options);
+ const { showButtons, buttonLayout, useGrouping, minFractionDigits, maxFractionDigits, locale, currency } = options;
+ const _onChange = (event: InputNumberChangeEvent) => onChange(event.value === null ? options.emptyValue : value);
+ const _onBlur = () => onBlur && onBlur(id, value);
+ const _onFocus = () => onFocus && onFocus(id, value);
+
+ return (
+ 0}
+ onChange={(onChangeOverride as any) || _onChange}
+ onBlur={_onBlur}
+ onFocus={_onFocus}
+ aria-describedby={ariaDescribedByIds(id, !!schema.examples)}
+ />
+ );
+}
diff --git a/packages/primereact/src/UpDownWidget/index.ts b/packages/primereact/src/UpDownWidget/index.ts
new file mode 100644
index 0000000000..2e2ae55279
--- /dev/null
+++ b/packages/primereact/src/UpDownWidget/index.ts
@@ -0,0 +1,2 @@
+export { default } from './UpDownWidget';
+export * from './UpDownWidget';
diff --git a/packages/primereact/src/Widgets/Widgets.tsx b/packages/primereact/src/Widgets/Widgets.tsx
new file mode 100644
index 0000000000..5ccd782f77
--- /dev/null
+++ b/packages/primereact/src/Widgets/Widgets.tsx
@@ -0,0 +1,33 @@
+import { FormContextType, RegistryWidgetsType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+
+import AutoCompleteWidget from '../AutoCompleteWidget/AutoCompleteWidget';
+import CheckboxWidget from '../CheckboxWidget/CheckboxWidget';
+import CheckboxesWidget from '../CheckboxesWidget/CheckboxesWidget';
+import ColorWidget from '../ColorWidget/ColorWidget';
+import PasswordWidget from '../PasswordWidget/PasswordWidget';
+import RadioWidget from '../RadioWidget/RadioWidget';
+import RangeWidget from '../RangeWidget/RangeWidget';
+import SelectWidget from '../SelectWidget/SelectWidget';
+import TextareaWidget from '../TextareaWidget/TextareaWidget';
+import UpDownWidget from '../UpDownWidget/UpDownWidget';
+
+export function generateWidgets<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(): RegistryWidgetsType {
+ return {
+ AutoCompleteWidget,
+ CheckboxWidget,
+ CheckboxesWidget,
+ ColorWidget,
+ PasswordWidget,
+ RadioWidget,
+ RangeWidget,
+ SelectWidget,
+ TextareaWidget,
+ UpDownWidget,
+ };
+}
+
+export default generateWidgets();
diff --git a/packages/primereact/src/Widgets/index.ts b/packages/primereact/src/Widgets/index.ts
new file mode 100644
index 0000000000..de857bf557
--- /dev/null
+++ b/packages/primereact/src/Widgets/index.ts
@@ -0,0 +1,2 @@
+export { default } from './Widgets';
+export * from './Widgets';
diff --git a/packages/primereact/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx b/packages/primereact/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx
new file mode 100644
index 0000000000..02b8cd7220
--- /dev/null
+++ b/packages/primereact/src/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx
@@ -0,0 +1,71 @@
+import { FocusEvent } from 'react';
+import {
+ ADDITIONAL_PROPERTY_FLAG,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ TranslatableString,
+ WrapIfAdditionalTemplateProps,
+} from '@rjsf/utils';
+import { InputText } from 'primereact/inputtext';
+
+export default function WrapIfAdditionalTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>({
+ classNames,
+ style,
+ children,
+ disabled,
+ id,
+ label,
+ onDropPropertyClick,
+ onKeyChange,
+ readonly,
+ required,
+ schema,
+ registry,
+}: WrapIfAdditionalTemplateProps) {
+ const { templates, translateString } = registry;
+ const { RemoveButton } = templates.ButtonTemplates;
+ const keyLabel = translateString(TranslatableString.KeyLabel, [label]);
+ const additional = ADDITIONAL_PROPERTY_FLAG in schema;
+
+ if (!additional) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ const handleBlur = ({ target }: FocusEvent) => onKeyChange(target.value);
+
+ return (
+
+
+
+
+
+
{children}
+
+
+
+
+ );
+}
diff --git a/packages/primereact/src/WrapIfAdditionalTemplate/index.ts b/packages/primereact/src/WrapIfAdditionalTemplate/index.ts
new file mode 100644
index 0000000000..7d7af6629d
--- /dev/null
+++ b/packages/primereact/src/WrapIfAdditionalTemplate/index.ts
@@ -0,0 +1,2 @@
+export { default } from './WrapIfAdditionalTemplate';
+export * from './WrapIfAdditionalTemplate';
diff --git a/packages/primereact/src/index.ts b/packages/primereact/src/index.ts
new file mode 100644
index 0000000000..fc99ce1c10
--- /dev/null
+++ b/packages/primereact/src/index.ts
@@ -0,0 +1,8 @@
+import SemanticUIForm from './PrimeForm/PrimeForm';
+
+export { default as Templates, generateTemplates } from './Templates';
+export { default as Form, generateForm } from './PrimeForm';
+export { default as Theme, generateTheme } from './Theme';
+export { default as Widgets, generateWidgets } from './Widgets';
+
+export default SemanticUIForm;
diff --git a/packages/primereact/src/tsconfig.json b/packages/primereact/src/tsconfig.json
new file mode 100644
index 0000000000..ea4f988573
--- /dev/null
+++ b/packages/primereact/src/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "include": [
+ "./"
+ ],
+ "compilerOptions": {
+ "rootDir": "./",
+ "outDir": "../lib",
+ "baseUrl": "../",
+ "jsx": "react-jsx",
+ // There are type errors in semantic-ui-react type definitions because of which skipLibCheck had to be added
+ // semantic-ui-react/dist/commonjs/generic.d.ts(73,73): error TS2344: Type 'TProps' does not satisfy the constraint 'Record'.
+ "skipLibCheck": true
+ },
+ "references": [
+ {
+ "path": "../../core"
+ },
+ {
+ "path": "../../utils"
+ },
+ {
+ "path": "../../validator-ajv8"
+ }
+ ]
+}
diff --git a/packages/primereact/src/util.tsx b/packages/primereact/src/util.tsx
new file mode 100644
index 0000000000..740ed0e44e
--- /dev/null
+++ b/packages/primereact/src/util.tsx
@@ -0,0 +1,141 @@
+import { ElementType } from 'react';
+import {
+ UiSchema,
+ GenericObjectType,
+ getUiOptions,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ UIOptionsType,
+} from '@rjsf/utils';
+
+export type SemanticPropsTypes = {
+ formContext?: F;
+ uiSchema?: UiSchema;
+ options?: UIOptionsType;
+ defaultSchemaProps?: GenericObjectType;
+ defaultContextProps?: GenericObjectType;
+};
+
+export type SemanticErrorPropsType<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+> = {
+ formContext?: F;
+ uiSchema?: UiSchema;
+ options?: UIOptionsType;
+ defaultProps?: GenericObjectType;
+};
+
+export type WrapProps = GenericObjectType & {
+ wrap: boolean;
+ component?: ElementType;
+};
+
+/**
+ * Extract props meant for semantic UI components from props that are
+ * passed to Widgets, Templates and Fields.
+ * @param {Object} params
+ * @param {Object?} params.formContext
+ * @param {Object?} params.uiSchema
+ * @param {Object?} params.options
+ * @param {Object?} params.defaultSchemaProps
+ * @param {Object?} params.defaultContextProps
+ * @returns {any}
+ */
+export function getSemanticProps({
+ formContext = {} as F,
+ uiSchema = {},
+ options = {},
+ defaultSchemaProps = {
+ fluid: true,
+ inverted: false,
+ },
+ defaultContextProps = {},
+}: SemanticPropsTypes) {
+ const formContextProps = formContext.semantic;
+ const schemaProps = getUiOptions(uiSchema).semantic;
+ const optionProps = options.semantic;
+ // formContext props should overide other props
+ return Object.assign(
+ {},
+ { ...defaultSchemaProps },
+ { ...defaultContextProps },
+ schemaProps,
+ optionProps,
+ formContextProps
+ );
+}
+
+/**
+ * Extract error props meant for semantic UI components from props that are
+ * passed to Widgets, Templates and Fields.
+ * @param {Object} params
+ * @param {Object?} params.formContext
+ * @param {Object?} params.uiSchema
+ * @param {Object?} params.defaultProps
+ * @returns {any}
+ */
+export function getSemanticErrorProps<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>({
+ formContext = {} as F,
+ uiSchema = {},
+ options = {},
+ defaultProps = { size: 'small', pointing: 'above' },
+}: SemanticErrorPropsType) {
+ const formContextProps = formContext.semantic && formContext.semantic.errorOptions;
+ const semanticOptions: GenericObjectType = getUiOptions(uiSchema).semantic as GenericObjectType;
+ const schemaProps = semanticOptions && semanticOptions.errorOptions;
+ const optionProps = options.semantic && (options.semantic as GenericObjectType).errorOptions;
+
+ return Object.assign({}, { ...defaultProps }, schemaProps, optionProps, formContextProps);
+}
+
+/**
+ * Combine multiple strings containing class names into a single string,
+ * removing duplicates. E.g.
+ * cleanClassNames('bar', 'baz bar', 'x y ', undefined)
+ * // 'bar baz x y'
+ * @param {Array} classNameArr
+ * @param {Array} omit
+ * @returns {string}
+ */
+export function cleanClassNames(classNameArr: Array, omit: string[] = []) {
+ // Split each arg on whitespace, and add it to an array. Skip false-y args
+ // like "" and undefined.
+ const classList = classNameArr
+ .filter(Boolean)
+ .reduce((previous, current) => previous.concat(current!.trim().split(/\s+/)), []);
+
+ // Remove any class names from omit, and make the rest unique before
+ // returning them as a string
+ return [...new Set(classList.filter((cn) => !omit.includes(cn)))].join(' ');
+}
+
+/**
+ *
+ * @param {boolean} wrap
+ * @param Component
+ * @param {Object} props
+ * @returns {*}
+ * @constructor
+ */
+export function MaybeWrap({ wrap, component: Component = 'div', ...props }: WrapProps) {
+ return wrap ? : props.children;
+}
+
+export function Label({ id, text, required }: { id: string; text?: string; required?: boolean }) {
+ if (!text) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/packages/primereact/test/Array.test.tsx b/packages/primereact/test/Array.test.tsx
new file mode 100644
index 0000000000..a17060201a
--- /dev/null
+++ b/packages/primereact/test/Array.test.tsx
@@ -0,0 +1,5 @@
+import { arrayTests } from '@rjsf/snapshot-tests';
+
+import Form from '../src';
+
+arrayTests(Form);
diff --git a/packages/primereact/test/Form.test.tsx b/packages/primereact/test/Form.test.tsx
new file mode 100644
index 0000000000..ba487994fc
--- /dev/null
+++ b/packages/primereact/test/Form.test.tsx
@@ -0,0 +1,60 @@
+import { formTests, SLIDER_CUSTOMIZE, TEXTAREA_CUSTOMIZE } from '@rjsf/snapshot-tests';
+
+import Form from '../src';
+
+// The `TextareaAutosize` code reads the following data from the `getComputedStyle()` function in a useEffect hook
+jest.spyOn(window, 'getComputedStyle').mockImplementation(() => {
+ return {
+ width: 100,
+ 'box-sizing': 10,
+ 'padding-bottom': 1,
+ 'padding-top': 1,
+ 'border-bottom-width': 1,
+ 'border-top-width': 1,
+ } as unknown as CSSStyleDeclaration;
+});
+
+formTests(Form, {
+ [TEXTAREA_CUSTOMIZE]: {
+ createNodeMock: (element) => {
+ if (element.type === 'textarea') {
+ // the `TextareaAutosize` code expects a ref for two textareas to exist, so use the feature of
+ // react-test-renderer to create one
+ // See: https://reactjs.org/docs/test-renderer.html#ideas
+ if (element.props['aria-hidden']) {
+ // The hidden one reads the following values
+ return {
+ style: { width: 10 },
+ scrollHeight: 100,
+ };
+ }
+ // The other one needs to look like an input node with focus and style elements
+ return {
+ nodeName: 'INPUT',
+ focus: jest.fn(),
+ style: {},
+ };
+ }
+ return null;
+ },
+ },
+ [SLIDER_CUSTOMIZE]: {
+ createNodeMock: (element) => {
+ // the `Slider` code expects a ref for a span.root to exist, so use the feature of
+ // react-test-renderer to create one
+ // See: https://reactjs.org/docs/test-renderer.html#ideas
+ if (element.type === 'span' && element.props.id === 'root') {
+ // Pretend to be an event listening component inside of an event listening document
+ return {
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ ownerDocument: {
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ },
+ };
+ }
+ return null;
+ },
+ },
+});
diff --git a/packages/primereact/test/Object.test.tsx b/packages/primereact/test/Object.test.tsx
new file mode 100644
index 0000000000..dabe023e32
--- /dev/null
+++ b/packages/primereact/test/Object.test.tsx
@@ -0,0 +1,5 @@
+import { objectTests } from '@rjsf/snapshot-tests';
+
+import Form from '../src';
+
+objectTests(Form);
diff --git a/packages/primereact/test/tsconfig.json b/packages/primereact/test/tsconfig.json
new file mode 100644
index 0000000000..d97653648c
--- /dev/null
+++ b/packages/primereact/test/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "include": [
+ "./"
+ ],
+ "compilerOptions": {
+ "rootDir": "./",
+ "baseUrl": "../",
+ "noEmit": true,
+ "jsx": "react-jsx"
+ },
+ "references": [
+ {
+ "path": "../src"
+ },
+ {
+ "path": "../../snapshot-tests"
+ }
+ ]
+}
diff --git a/packages/primereact/tsconfig.json b/packages/primereact/tsconfig.json
new file mode 100644
index 0000000000..82462dfbeb
--- /dev/null
+++ b/packages/primereact/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "files": [],
+ "references": [
+ {
+ "path": "./src"
+ },
+ {
+ "path": "./test"
+ }
+ ]
+}