+
+
+
+
+## Table of Contents
+
+- [Table of Contents](#table-of-contents)
+- [About The Project](#about-the-project)
+ - [Built With](#built-with)
+- [Getting Started](#getting-started)
+ - [Prerequisites](#prerequisites)
+ - [Installation](#installation)
+- [Usage](#usage)
+- [Optional Mantine Theme properties](#optional-mantine-theme-properties)
+ - [Mantine Widget Optional Properties](#mantine-widget-optional-properties)
+- [Roadmap](#roadmap)
+- [Contributing](#contributing)
+- [Contact](#contact)
+
+
+
+## About The Project
+
+`Mantine` theme, fields and widgets for `react-jsonschema-form`.
+
+### Built With
+
+- [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form/)
+- [Mantine](https://mantine.dev/)
+
+
+
+## Getting Started
+
+- See the [getting started guide](https://mantine.dev/getting-started/) on Mantine documentation.
+
+### Prerequisites
+
+- `@mantine/core >= 7`
+- `@mantine/hooks >= 7`
+- `@mantine/dates >= 7`
+- `dayjs >= 1.8.0`
+- `@rjsf/core >= 2.0.0`
+
+```sh
+npm install @mantine/core @mantine/hooks @mantine/dates dayjs @rjsf/core
+```
+
+### Installation
+
+```sh
+npm install @rjsf/mantine
+```
+
+
+
+## Usage
+
+```javascript
+import Form from '@rjsf/mantine';
+```
+
+or
+
+```javascript
+import { withTheme } from '@rjsf/core';
+import { Theme as MantineTheme } from '@rjsf/mantine';
+
+// Make modifications to the theme with your own fields and widgets
+
+const Form = withTheme(MantineTheme);
+```
+
+## Optional Mantine Theme properties
+
+- To pass additional properties to widgets, see this [guide](https://rjsf-team.github.io/react-jsonschema-form/docs/usage/objects#additional-properties).
+
+#### Mantine Widget Optional Properties
+
+- [Mantine props for CheckboxWidget](https://mantine.dev/core/checkbox/?t=props)
+- [Mantine props for ColorWidget](https://mantine.dev/core/color-input/?t=props)
+- [Mantine props for DateWidget](https://mantine.dev/dates/date-input/?t=props)
+- [Mantine props for DateTimeWidget](https://mantine.dev/dates/date-input/?t=props)
+- [Mantine props for PasswordWidget](https://mantine.dev/core/password-input/?t=props)
+- [Mantine props for RadioWidget](https://mantine.dev/core/radio/?t=props)
+- [Mantine props for RangeWidget](https://mantine.dev/core/slider/?t=props)
+- [Mantine props for SelectWidget](https://mantine.dev/core/select/?t=props)
+- [Mantine props for UpDownWidget](https://mantine.dev/core/number-input/?t=props)
+- [Mantine props for TextWidget](https://mantine.dev/core/text-input/?t=props)
+- [Mantine props for TextAreaWidget](https://mantine.dev/core/textarea/?t=props)
+- [Mantine props for TimeWidget](https://mantine.dev/dates/time-input/?t=props)
+
+
+
+## Roadmap
+
+See the [open issues](https://github.com/rjsf-team/react-jsonschema-form/issues) for a list of proposed features (and known 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/mantine/latest.svg?style=flat-square
+[npm-url]: https://www.npmjs.com/package/@rjsf/mantine
+[npm-dl-shield]: https://img.shields.io/npm/dm/@rjsf/mantine.svg?style=flat-square
+[npm-dl-url]: https://www.npmjs.com/package/@rjsf/mantine
+[product-screenshot]: https://raw.githubusercontent.com/rjsf-team/react-jsonschema-form/59a8206e148474bea854bbb004f624143fbcbac8/packages/mantine/screenshot.png
diff --git a/packages/mantine/babel.config.js b/packages/mantine/babel.config.js
new file mode 100644
index 0000000000..5f772a56c4
--- /dev/null
+++ b/packages/mantine/babel.config.js
@@ -0,0 +1,3 @@
+const defaultConfig = require('../../babel.config');
+
+module.exports = defaultConfig;
diff --git a/packages/mantine/jest.config.js b/packages/mantine/jest.config.js
new file mode 100644
index 0000000000..6e7a8ee681
--- /dev/null
+++ b/packages/mantine/jest.config.js
@@ -0,0 +1,10 @@
+module.exports = {
+ rootDir: './',
+ snapshotSerializers: ['/test/cleanSnapshotSerializer.ts'],
+ verbose: true,
+ testEnvironment: 'jsdom',
+ testEnvironmentOptions: {
+ browsers: ['chrome', 'firefox', 'safari'],
+ },
+ transformIgnorePatterns: [`/node_modules/(?!nanoid)`],
+};
diff --git a/packages/mantine/logo.png b/packages/mantine/logo.png
new file mode 100644
index 0000000000..1da1cc6141
Binary files /dev/null and b/packages/mantine/logo.png differ
diff --git a/packages/mantine/package.json b/packages/mantine/package.json
new file mode 100644
index 0000000000..7aef8d615a
--- /dev/null
+++ b/packages/mantine/package.json
@@ -0,0 +1,97 @@
+{
+ "name": "@rjsf/mantine",
+ "version": "5.24.3",
+ "main": "dist/index.js",
+ "module": "lib/index.js",
+ "typings": "lib/index.d.ts",
+ "description": "Mantine theme, fields and widgets for react-jsonschema-form",
+ "files": [
+ "dist",
+ "lib",
+ "src"
+ ],
+ "engineStrict": false,
+ "engines": {
+ "node": ">=14"
+ },
+ "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/mantine.esm.js --sourcemap --packages=external --format=esm",
+ "build:umd": "rollup dist/mantine.esm.js --format=umd --file=dist/mantine.umd.js --name=@rjsf/mantine",
+ "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"
+ ]
+ },
+ "peerDependencies": {
+ "@rjsf/core": "^5.24.x",
+ "@rjsf/utils": "^5.24.x",
+ "react": "^16.14.0 || >=17",
+ "@mantine/core": ">=7",
+ "@mantine/hooks": ">=7",
+ "@mantine/dates": ">=7"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.23.9",
+ "@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.3",
+ "@rjsf/snapshot-tests": "^5.24.3",
+ "@rjsf/utils": "^5.24.3",
+ "@rjsf/validator-ajv8": "^5.24.3",
+ "@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",
+ "atob": "^2.1.2",
+ "babel-jest": "^29.7.0",
+ "eslint": "^8.56.0",
+ "jest": "^29.7.0",
+ "jest-environment-jsdom": "^29.7.0",
+ "nanoid": "^3.3.7",
+ "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"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "author": "Farhad Zare ",
+ "contributors": [
+ "Heath Chiavettone (): ComponentType> {
+ return withTheme(generateTheme());
+}
+
+export default generateForm();
diff --git a/packages/mantine/src/Theme/index.ts b/packages/mantine/src/Theme/index.ts
new file mode 100644
index 0000000000..e23141183f
--- /dev/null
+++ b/packages/mantine/src/Theme/index.ts
@@ -0,0 +1,18 @@
+import { ThemeProps } from '@rjsf/core';
+import { FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+
+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(),
+ };
+}
+
+export default generateTheme();
diff --git a/packages/mantine/src/index.ts b/packages/mantine/src/index.ts
new file mode 100644
index 0000000000..7cdb4fbd94
--- /dev/null
+++ b/packages/mantine/src/index.ts
@@ -0,0 +1,8 @@
+import Form, { generateForm } from './Form';
+import Templates, { generateTemplates } from './templates';
+import Theme, { generateTheme } from './Theme';
+import Widgets, { generateWidgets } from './widgets';
+
+export { Form, Templates, Theme, Widgets, generateForm, generateTemplates, generateTheme, generateWidgets };
+
+export default Form;
diff --git a/packages/mantine/src/templates/ArrayFieldItemTemplate.tsx b/packages/mantine/src/templates/ArrayFieldItemTemplate.tsx
new file mode 100644
index 0000000000..87314da5ff
--- /dev/null
+++ b/packages/mantine/src/templates/ArrayFieldItemTemplate.tsx
@@ -0,0 +1,83 @@
+import { ArrayFieldTemplateItemType, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+import { Box, Flex, Group } from '@mantine/core';
+
+/** 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 {
+ disabled,
+ className,
+ hasCopy,
+ hasMoveDown,
+ hasMoveUp,
+ hasRemove,
+ hasToolbar,
+ index,
+ onCopyIndexClick,
+ onDropIndexClick,
+ onReorderClick,
+ readonly,
+ uiSchema,
+ registry,
+ children,
+ } = props;
+ const { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates;
+
+ return (
+
+
+ {children}
+ {hasToolbar && (
+
+ {(hasMoveUp || hasMoveDown) && (
+
+ )}
+ {(hasMoveUp || hasMoveDown) && (
+
+ )}
+ {hasCopy && (
+
+ )}
+ {hasRemove && (
+
+ )}
+
+ )}
+
+
+ );
+}
diff --git a/packages/mantine/src/templates/ArrayFieldTemplate.tsx b/packages/mantine/src/templates/ArrayFieldTemplate.tsx
new file mode 100644
index 0000000000..8bc87e49df
--- /dev/null
+++ b/packages/mantine/src/templates/ArrayFieldTemplate.tsx
@@ -0,0 +1,100 @@
+import {
+ getTemplate,
+ getUiOptions,
+ ArrayFieldTemplateProps,
+ ArrayFieldTemplateItemType,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+} from '@rjsf/utils';
+import { Fieldset, Box, Group } from '@mantine/core';
+
+/** 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 {
+ canAdd,
+ className,
+ disabled,
+ idSchema,
+ items,
+ onAddClick,
+ readonly,
+ required,
+ schema,
+ uiSchema,
+ title,
+ registry,
+ } = 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
+ );
+ // Button templates are not overridden in the uiSchema
+ const {
+ ButtonTemplates: { AddButton },
+ } = registry.templates;
+
+ const legend = (uiOptions.title || title) && (
+
+ );
+
+ return (
+
+ );
+}
diff --git a/packages/mantine/src/templates/ArrayFieldTitleTemplate.tsx b/packages/mantine/src/templates/ArrayFieldTitleTemplate.tsx
new file mode 100644
index 0000000000..67b09cd4e9
--- /dev/null
+++ b/packages/mantine/src/templates/ArrayFieldTitleTemplate.tsx
@@ -0,0 +1,33 @@
+import {
+ getUiOptions,
+ titleId,
+ ArrayFieldTitleProps,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+} from '@rjsf/utils';
+import { Title } from '@mantine/core';
+
+/** The `ArrayFieldTitleTemplate` component renders a `TitleFieldTemplate` with an `id` derived from
+ * the `idSchema`.
+ *
+ * @param props - The `ArrayFieldTitleProps` for the component
+ */
+export default function ArrayFieldTitleTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: ArrayFieldTitleProps) {
+ const { idSchema, title, uiSchema, registry } = props;
+
+ const options = getUiOptions(uiSchema, registry.globalUiOptions);
+ const { label: displayLabel = true } = options;
+ if (!title || !displayLabel) {
+ return null;
+ }
+ return (
+ (idSchema)} order={4} fw='normal'>
+ {title}
+
+ );
+}
diff --git a/packages/mantine/src/templates/BaseInputTemplate.tsx b/packages/mantine/src/templates/BaseInputTemplate.tsx
new file mode 100644
index 0000000000..a45f5d34dc
--- /dev/null
+++ b/packages/mantine/src/templates/BaseInputTemplate.tsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import {
+ ariaDescribedByIds,
+ BaseInputTemplateProps,
+ examplesId,
+ getInputProps,
+ labelValue,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+} from '@rjsf/utils';
+import { TextInput, NumberInput } from '@mantine/core';
+
+export default function BaseInputTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: BaseInputTemplateProps) {
+ const {
+ id,
+ type,
+ schema,
+ value,
+ placeholder,
+ required,
+ disabled,
+ readonly,
+ autofocus,
+ label,
+ hideLabel,
+ onChange,
+ onChangeOverride,
+ onBlur,
+ onFocus,
+ options,
+ rawErrors,
+ } = props;
+
+ const inputProps = getInputProps(schema, type, options, false);
+
+ const handleNumberChange = (value: number | string) => onChange(value);
+
+ const handleChange = onChangeOverride
+ ? onChangeOverride
+ : (e: React.ChangeEvent) =>
+ onChange(e.target.value === '' ? options.emptyValue ?? '' : e.target.value);
+
+ const handleBlur = (e: React.FocusEvent) => onBlur(id, e.target && e.target.value);
+
+ const handleFocus = (e: React.FocusEvent) => onFocus(id, e.target && e.target.value);
+
+ const input =
+ inputProps.type === 'number' || inputProps.type === 'integer' ? (
+ 0 ? rawErrors.join('\n') : undefined}
+ list={schema.examples ? examplesId(id) : undefined}
+ {...inputProps}
+ step={typeof inputProps.step === 'number' ? inputProps.step : 1}
+ type='text'
+ value={value}
+ aria-describedby={ariaDescribedByIds(id, !!schema.examples)}
+ />
+ ) : (
+ 0 ? rawErrors.join('\n') : undefined}
+ list={schema.examples ? examplesId(id) : undefined}
+ {...inputProps}
+ value={value}
+ aria-describedby={ariaDescribedByIds(id, !!schema.examples)}
+ />
+ );
+
+ return (
+ <>
+ {input}
+ {Array.isArray(schema.examples) && (
+
+ )}
+ >
+ );
+}
diff --git a/packages/mantine/src/templates/ButtonTemplates/AddButton.tsx b/packages/mantine/src/templates/ButtonTemplates/AddButton.tsx
new file mode 100644
index 0000000000..2ac8796ef1
--- /dev/null
+++ b/packages/mantine/src/templates/ButtonTemplates/AddButton.tsx
@@ -0,0 +1,14 @@
+import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
+import IconButton from './IconButton';
+import { Plus } from '../icons';
+
+export default function AddButton(
+ props: IconButtonProps
+) {
+ const {
+ registry: { translateString },
+ } = props;
+ return (
+ } />
+ );
+}
diff --git a/packages/mantine/src/templates/ButtonTemplates/IconButton.tsx b/packages/mantine/src/templates/ButtonTemplates/IconButton.tsx
new file mode 100644
index 0000000000..4b44326808
--- /dev/null
+++ b/packages/mantine/src/templates/ButtonTemplates/IconButton.tsx
@@ -0,0 +1,86 @@
+import React from 'react';
+import { ActionIcon, ActionIconProps } from '@mantine/core';
+import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
+import { Copy, ChevronDown, ChevronUp, X } from '../icons';
+
+export type MantineIconButtonProps<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+> = IconButtonProps & Omit;
+
+export default function IconButton(
+ props: MantineIconButtonProps
+) {
+ const { icon, iconType, color, onClick, uiSchema, registry, ...otherProps } = props;
+ return (
+ & React.MouseEventHandler}
+ {...otherProps}
+ >
+ {icon}
+
+ );
+}
+
+export function CopyButton(
+ props: MantineIconButtonProps
+) {
+ const {
+ registry: { translateString },
+ } = props;
+ return (
+ } />
+ );
+}
+
+export function MoveDownButton(
+ props: MantineIconButtonProps
+) {
+ const {
+ registry: { translateString },
+ } = props;
+ return (
+ }
+ />
+ );
+}
+
+export function MoveUpButton(
+ props: MantineIconButtonProps
+) {
+ const {
+ registry: { translateString },
+ } = props;
+ return (
+ }
+ />
+ );
+}
+
+export function RemoveButton(
+ props: MantineIconButtonProps
+) {
+ const {
+ registry: { translateString },
+ } = props;
+ return (
+ }
+ />
+ );
+}
diff --git a/packages/mantine/src/templates/ButtonTemplates/SubmitButton.tsx b/packages/mantine/src/templates/ButtonTemplates/SubmitButton.tsx
new file mode 100644
index 0000000000..1e22e8fec0
--- /dev/null
+++ b/packages/mantine/src/templates/ButtonTemplates/SubmitButton.tsx
@@ -0,0 +1,20 @@
+import { Button } from '@mantine/core';
+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/mantine/src/templates/ButtonTemplates/index.ts b/packages/mantine/src/templates/ButtonTemplates/index.ts
new file mode 100644
index 0000000000..aaf7c6fa23
--- /dev/null
+++ b/packages/mantine/src/templates/ButtonTemplates/index.ts
@@ -0,0 +1,21 @@
+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 buttonTemplates;
diff --git a/packages/mantine/src/templates/ErrorList.tsx b/packages/mantine/src/templates/ErrorList.tsx
new file mode 100644
index 0000000000..4b4020d832
--- /dev/null
+++ b/packages/mantine/src/templates/ErrorList.tsx
@@ -0,0 +1,35 @@
+import { ErrorListProps, FormContextType, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
+import { Alert, Title, List } from '@mantine/core';
+import { ExclamationCircle } from './icons';
+
+/** The `ErrorList` component is the template that renders the 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;
+
+ return (
+
+ {translateString(TranslatableString.ErrorsLabel)}
+
+ }
+ icon={}
+ >
+
+ {errors.map((error, index) => (
+
+ {error.stack}
+
+ ))}
+
+
+ );
+}
diff --git a/packages/mantine/src/templates/FieldErrorTemplate.tsx b/packages/mantine/src/templates/FieldErrorTemplate.tsx
new file mode 100644
index 0000000000..5fb11b8be0
--- /dev/null
+++ b/packages/mantine/src/templates/FieldErrorTemplate.tsx
@@ -0,0 +1,27 @@
+import { errorId, FieldErrorProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+import { Box, List } from '@mantine/core';
+
+/** 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) {
+ return null;
+ }
+ // In mantine, errors are handled directly in each component, so there is no need to render a separate error template.
+ const id = errorId(idSchema);
+ return (
+
+
+ {errors.map((error, index) => (
+ {error}
+ ))}
+
+
+ );
+}
diff --git a/packages/mantine/src/templates/FieldHelpTemplate.tsx b/packages/mantine/src/templates/FieldHelpTemplate.tsx
new file mode 100644
index 0000000000..043e926616
--- /dev/null
+++ b/packages/mantine/src/templates/FieldHelpTemplate.tsx
@@ -0,0 +1,18 @@
+import { helpId, FieldHelpProps, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+import { Text } from '@mantine/core';
+
+export default function FieldHelpTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: FieldHelpProps) {
+ const { idSchema, help } = props;
+
+ const id = helpId(idSchema);
+
+ return !help ? null : (
+
+ {help}
+
+ );
+}
diff --git a/packages/mantine/src/templates/FieldTemplate.tsx b/packages/mantine/src/templates/FieldTemplate.tsx
new file mode 100644
index 0000000000..adb312776d
--- /dev/null
+++ b/packages/mantine/src/templates/FieldTemplate.tsx
@@ -0,0 +1,46 @@
+import {
+ FieldTemplateProps,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ getTemplate,
+ getUiOptions,
+} from '@rjsf/utils';
+import { Box } from '@mantine/core';
+
+export default function FieldTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: FieldTemplateProps) {
+ const { id, classNames, style, label, errors, help, hidden, schema, uiSchema, registry, children, ...otherProps } =
+ props;
+
+ const uiOptions = getUiOptions(uiSchema);
+ const WrapIfAdditionalTemplate = getTemplate<'WrapIfAdditionalTemplate', T, S, F>(
+ 'WrapIfAdditionalTemplate',
+ registry,
+ uiOptions
+ );
+
+ if (hidden) {
+ return {children};
+ }
+
+ return (
+
+ {children}
+ {help}
+ {errors}
+
+ );
+}
diff --git a/packages/mantine/src/templates/ObjectFieldTemplate.tsx b/packages/mantine/src/templates/ObjectFieldTemplate.tsx
new file mode 100644
index 0000000000..be1a40b4ad
--- /dev/null
+++ b/packages/mantine/src/templates/ObjectFieldTemplate.tsx
@@ -0,0 +1,94 @@
+import {
+ FormContextType,
+ ObjectFieldTemplateProps,
+ ObjectFieldTemplatePropertyType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ canExpand,
+ descriptionId,
+ getTemplate,
+ getUiOptions,
+ titleId,
+} from '@rjsf/utils';
+import { Container, Box, SimpleGrid, MantineSpacing } from '@mantine/core';
+
+export default function ObjectFieldTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: ObjectFieldTemplateProps) {
+ const {
+ title,
+ description,
+ disabled,
+ properties,
+ onAddClick,
+ readonly,
+ required,
+ schema,
+ uiSchema,
+ idSchema,
+ formData,
+ 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;
+ const gridCols = (typeof uiOptions?.gridCols === 'number' && uiOptions?.gridCols) || undefined;
+ const gridSpacing = uiOptions?.gridSpacing;
+ const gridVerticalSpacing = uiOptions?.gridVerticalSpacing;
+
+ return (
+
+ {title && (
+ (idSchema)}
+ title={title}
+ required={required}
+ schema={schema}
+ uiSchema={uiSchema}
+ registry={registry}
+ />
+ )}
+ {description && (
+ (idSchema)}
+ description={description}
+ schema={schema}
+ uiSchema={uiSchema}
+ registry={registry}
+ />
+ )}
+
+ {properties
+ .filter((e) => !e.hidden)
+ .map((element: ObjectFieldTemplatePropertyType) => (
+ {element.content}
+ ))}
+
+
+ {canExpand(schema, uiSchema, formData) && (
+
+
+
+ )}
+
+ );
+}
diff --git a/packages/mantine/src/templates/TitleField.tsx b/packages/mantine/src/templates/TitleField.tsx
new file mode 100644
index 0000000000..1a85acbdc5
--- /dev/null
+++ b/packages/mantine/src/templates/TitleField.tsx
@@ -0,0 +1,13 @@
+import { FormContextType, TitleFieldProps, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
+import { Title } from '@mantine/core';
+
+export default function TitleField(
+ props: TitleFieldProps
+) {
+ const { id, title } = props;
+ return title ? (
+
+ {title}
+
+ ) : null;
+}
diff --git a/packages/mantine/src/templates/WrapIfAdditionalTemplate.tsx b/packages/mantine/src/templates/WrapIfAdditionalTemplate.tsx
new file mode 100644
index 0000000000..3614699777
--- /dev/null
+++ b/packages/mantine/src/templates/WrapIfAdditionalTemplate.tsx
@@ -0,0 +1,94 @@
+import React from 'react';
+import {
+ ADDITIONAL_PROPERTY_FLAG,
+ UI_OPTIONS_KEY,
+ FormContextType,
+ RJSFSchema,
+ StrictRJSFSchema,
+ TranslatableString,
+ WrapIfAdditionalTemplateProps,
+} from '@rjsf/utils';
+import { Flex, Grid, TextInput } from '@mantine/core';
+
+/** The `WrapIfAdditional` component is used by the `FieldTemplate` to rename, or remove properties that are
+ * part of an `additionalProperties` part of a schema.
+ *
+ * @param props - The `WrapIfAdditionalProps` for this component
+ */
+export default function WrapIfAdditionalTemplate<
+ T = any,
+ S extends StrictRJSFSchema = RJSFSchema,
+ F extends FormContextType = any
+>(props: WrapIfAdditionalTemplateProps) {
+ const {
+ id,
+ classNames,
+ style,
+ label,
+ required,
+ readonly,
+ disabled,
+ schema,
+ uiSchema,
+ onKeyChange,
+ onDropPropertyClick,
+ registry,
+ children,
+ } = props;
+ const { templates, translateString } = registry;
+ // Button templates are not overridden in the uiSchema
+ const { RemoveButton } = templates.ButtonTemplates;
+ const keyLabel = translateString(TranslatableString.KeyLabel, [label]);
+ const additional = ADDITIONAL_PROPERTY_FLAG in schema;
+
+ if (!additional) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ const handleBlur = ({ target }: React.FocusEvent) => onKeyChange(target && target.value);
+
+ // The `block` prop is not part of the `IconButtonProps` defined in the template, so put it into the uiSchema instead
+ const uiOptions = uiSchema ? uiSchema[UI_OPTIONS_KEY] : {};
+ const buttonUiOptions = {
+ ...uiSchema,
+ [UI_OPTIONS_KEY]: { ...uiOptions, block: true },
+ };
+
+ return (
+