react-formule 1.3.0 â 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -11
- package/dist/admin/formComponents/PropKeyEditorObjectFieldTemplate.d.ts +4 -0
- package/dist/admin/formComponents/SortableBox.d.ts +15 -17
- package/dist/admin/utils/fieldTypes.d.ts +1273 -657
- package/dist/admin/utils/index.d.ts +1 -0
- package/dist/exposed.d.ts +8 -18
- package/dist/forms/fields/base/FileField.d.ts +12 -0
- package/dist/forms/fields/base/index.d.ts +2 -0
- package/dist/forms/templates/ArrayFieldTemplates/{NormalArrayFieldTemplate.d.ts â ArrayFieldTemplate.d.ts} +3 -3
- package/dist/forms/templates/Field/FieldCollapsible.d.ts +6 -0
- package/dist/forms/templates/StepsField.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/react-formule.js +106748 -100969
- package/dist/react-formule.umd.cjs +521 -485
- package/dist/store/configureStore.d.ts +8 -0
- package/dist/store/schemaWizard.d.ts +16 -1
- package/dist/types/index.d.ts +34 -0
- package/package.json +2 -2
- package/dist/admin/formComponents/RenderSortable.d.ts +0 -2
- package/dist/vite.svg +0 -1
package/README.md
CHANGED
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://cern-sis.github.io/react-formule/)
|
|
6
6
|
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
7
|
[](https://opensource.org/licenses/MIT)
|
|
10
8
|
[](https://www.npmjs.com/package/react-formule?activeTab=readme)
|
|
11
9
|
[](https://github.com/cern-sis/react-formule/commits/master/)
|
|
@@ -15,6 +13,8 @@
|
|
|
15
13
|
[](https://github.com/cern-sis/react-formule/actions/workflows/cypress.yml)
|
|
16
14
|
[](https://github.com/cern-sis/react-formule/actions/workflows/commit-lint.yml)
|
|
17
15
|
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
18
|
## :horse: What is Formule?
|
|
19
19
|
|
|
20
20
|
Formule is a **powerful, user-friendly, extensible and mobile-friendly form building library** based on [JSON Schema](https://json-schema.org/) and [RJSF](https://github.com/rjsf-team/react-jsonschema-form), which aims to make form creation easier for both technical and non-technical people.
|
|
@@ -70,10 +70,10 @@ Formule includes a variety of predefined field types, grouped in three categorie
|
|
|
70
70
|
- **Collections**:
|
|
71
71
|
- `Object`: Use it of you want to group fields or to add several of them inside of a `List`.
|
|
72
72
|
- `List`: It allows you to have as many instances of a field or `Object` as you want.
|
|
73
|
-
- `Accordion`:
|
|
74
|
-
- `Layer`:
|
|
73
|
+
- `Accordion`: It works as a `List` with collapsible entries.
|
|
74
|
+
- `Layer`: It works as a `List` whose entries will open in a dialog window.
|
|
75
75
|
- `Tab`: It's commonly supposed to be used as a wrapper around the rest of the elements. You will normally want to add an `Object` inside and you can use it to separate the form in different pages or sections.
|
|
76
|
-
- **Advanced fields**: More complex or situational fields such as `URI`, `Rich/Latex editor`, `Tags`, `ID Fetcher` and `
|
|
76
|
+
- **Advanced fields**: More complex or situational fields such as `URI`, `Rich/Latex editor`, `Tags`, `ID Fetcher`, `Code Editor` and `Files`.
|
|
77
77
|
|
|
78
78
|
You can freely remove some of these predefined fields and add your own custom fields and widgets following the JSON Schema specifications. More details below.
|
|
79
79
|
|
|
@@ -110,27 +110,39 @@ return (
|
|
|
110
110
|
|
|
111
111
|
### Customizing and adding new field types
|
|
112
112
|
|
|
113
|
-
Override (if existing) or create your own field types (rjsf type definitions) similarly to how it's done in `fieldTypes.jsx`, passing them as `customFieldTypes`. Implement your own custom fields and widgets (react components) by passing them as `customFields` and/or `customWidgets` (see `forms/fields/` and `forms/widgets/` for examples). If you also want to use a different published version of a field or widget, pass the component in `customPublishedFields` or `customPublishedWidgets`.
|
|
113
|
+
Override (if existing) or create your own field types (rjsf type definitions) similarly to how it's done in `fieldTypes.jsx`, passing them as `customFieldTypes`. Implement your own custom fields and widgets (react components) by passing them as `customFields` and/or `customWidgets` (see `forms/fields/` and `forms/widgets/` for examples). If you also want to use a different published version of a field or widget, pass the component in `customPublishedFields` or `customPublishedWidgets`. You can read more about the difference between fields and widgets and how to customize or wrap them in the [rjsf docs](https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-widgets-fields), but make sure you provide Formule with something like the following:
|
|
114
114
|
|
|
115
115
|
```jsx
|
|
116
|
+
const CustomWidget = ({value, required, onChange}) => {
|
|
117
|
+
return (
|
|
118
|
+
<input
|
|
119
|
+
type='text'
|
|
120
|
+
className='custom'
|
|
121
|
+
value={value}
|
|
122
|
+
required={required}
|
|
123
|
+
onChange={(event) => onChange(event.target.value)}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
116
128
|
const customFieldTypes = {
|
|
117
129
|
advanced: {
|
|
118
|
-
|
|
130
|
+
myCustomWidget: {
|
|
119
131
|
title: ...
|
|
120
132
|
...
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
|
|
125
|
-
const
|
|
126
|
-
|
|
137
|
+
const customWidgets: {
|
|
138
|
+
myCustomWidget: CustomWidget
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
<FormuleContext
|
|
130
142
|
theme={{token: {colorPrimary: "blue"}}} // antd theme
|
|
131
143
|
customFieldTypes={customFieldTypes}
|
|
132
|
-
customFields={
|
|
133
|
-
customWidgets={
|
|
144
|
+
customFields={...}
|
|
145
|
+
customWidgets={customWidgets}
|
|
134
146
|
customPublishedFields={...}
|
|
135
147
|
customPublishedWidgets={...}>
|
|
136
148
|
// ...
|
|
@@ -176,6 +188,94 @@ const handleFormuleStateChange = (newState) => {
|
|
|
176
188
|
|
|
177
189
|
Alternatively, you can pull the current state on demand by calling `getFormuleState` at any moment.
|
|
178
190
|
|
|
191
|
+
### Loading form data / prefill form
|
|
192
|
+
|
|
193
|
+
If you want to prefill the form with existing data, you can provide the form data to `FormuleForm`. This will fill in the corresponding fields with the information in `formData`:
|
|
194
|
+
|
|
195
|
+
```jsx
|
|
196
|
+
<FormuleForm
|
|
197
|
+
formData={{
|
|
198
|
+
name: "Mule",
|
|
199
|
+
age: 20,
|
|
200
|
+
weight: 370,
|
|
201
|
+
}}
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Using the Files field
|
|
206
|
+
|
|
207
|
+
In order to keep Formule's philosophy of storing forms and completion data as simple JSON objects, Formule doesn't directly store files. Instead, it stores only UIDs and leaves the specifics of how, where and when to store the corresponding files up to the user.
|
|
208
|
+
|
|
209
|
+
<details>
|
|
210
|
+
<summary>More info about <b>fetching and storing files</b> with examples</summary>
|
|
211
|
+
|
|
212
|
+
#### Fetching files
|
|
213
|
+
|
|
214
|
+
In order to fetch files from a URL (which can be your backend or a public URL), you will have to provide a `fetchFile` callback function in `customFunctions`. Formule will call this function when first loading a Files field for each of the file UIDs associated to this field, passing the file UID. This function should return a file URL.
|
|
215
|
+
|
|
216
|
+
```jsx
|
|
217
|
+
<FormuleContext
|
|
218
|
+
customFunctions={{
|
|
219
|
+
file: {
|
|
220
|
+
// You can either directly return the file URL (useful also if you want
|
|
221
|
+
// to do some kind of caching):
|
|
222
|
+
fetchFile: (uid) => {
|
|
223
|
+
return `https://example.com/files/${uid}`;
|
|
224
|
+
},
|
|
225
|
+
// Or, if you need to manage e.g. authentication, you can always fetch
|
|
226
|
+
// the image yourself, doing any processing you find necessary and finally
|
|
227
|
+
// create an object URL with URL.createObjectURL() and return it:
|
|
228
|
+
fetchFile: (uid) => {
|
|
229
|
+
return fetch(`https://example.com/files/${uid}`)
|
|
230
|
+
.then((response) => response.blob())
|
|
231
|
+
.then((blob) => URL.createObjectURL(blob));
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
}}
|
|
235
|
+
>
|
|
236
|
+
// ...
|
|
237
|
+
</FormuleContext>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### Storing files
|
|
241
|
+
|
|
242
|
+
Formule temporarily stores object URLs of files uploaded in the current session in the Formule state under `files.new`. If you want to persist files you can simply monitor the formule state (see [Syncing Formule state](#syncing-formule-state)) and whenever you want to save them (usually you will want to do it on submission, but you could do it on change) you can read new files from `files.new` and deleted files from `files.deleted`. You can then use that data to trigger the corresponding upload and delete actions in your backend.
|
|
243
|
+
|
|
244
|
+
```jsx
|
|
245
|
+
// Example of a custom function to handle form submission
|
|
246
|
+
// Only showing file-related logic
|
|
247
|
+
// Assumes you have synchronized the formule state on formuleState
|
|
248
|
+
const handleSubmit = () => {
|
|
249
|
+
// Upload new files
|
|
250
|
+
formuleState.files.new.map({uid, url} => {
|
|
251
|
+
const response = await fetch(url);
|
|
252
|
+
const blob = await response.blob();
|
|
253
|
+
|
|
254
|
+
const formData = new FormData();
|
|
255
|
+
formData.append('file', blob);
|
|
256
|
+
formData.append('uid', uid);
|
|
257
|
+
|
|
258
|
+
const uploadResponse = await fetch('https://example.com/upload', {
|
|
259
|
+
method: 'POST',
|
|
260
|
+
body: formData,
|
|
261
|
+
});
|
|
262
|
+
// handle response...
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
// Remove deleted files. You only need to handle this for form edition
|
|
266
|
+
// (forms already filled-in and saved that are being further modified)
|
|
267
|
+
// unless you are persisting files on change instead of on submission.
|
|
268
|
+
formuleState.files.deleted.map(uid => {
|
|
269
|
+
const response = await fetch(`https://example.com/files/${uid}`, {
|
|
270
|
+
method: 'DELETE',
|
|
271
|
+
});
|
|
272
|
+
// handle response...
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
</details>
|
|
278
|
+
|
|
179
279
|
> [!TIP]
|
|
180
280
|
> For more examples, feel free to browse around formule-demo and the [CERN Analysis Preservation](https://github.com/cernanalysispreservation/analysispreservation.cern.ch) repository, where we use all the features mentioned above.
|
|
181
281
|
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
id:
|
|
7
|
-
index:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
|
|
14
|
-
let id: PropTypes.Requireable<number>;
|
|
15
|
-
let index: PropTypes.Requireable<number>;
|
|
16
|
-
let moveCard: PropTypes.Requireable<(...args: any[]) => any>;
|
|
17
|
-
}
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
interface SortableBoxProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
parent: string;
|
|
6
|
+
id: number;
|
|
7
|
+
index: number;
|
|
8
|
+
onDragStart: () => void;
|
|
9
|
+
onDragEnd: () => void;
|
|
10
|
+
onDragCancel: () => void;
|
|
11
|
+
updateVisualOrder: (dragIndex: number, hoverIndex: number) => void;
|
|
12
|
+
moveItem: () => void;
|
|
18
13
|
}
|
|
14
|
+
declare function SortableBox({ children, parent, id, index, onDragStart, onDragEnd, onDragCancel, moveItem, updateVisualOrder, }: SortableBoxProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare const _default: import('react').MemoExoticComponent<typeof SortableBox>;
|
|
16
|
+
export default _default;
|