declarative-forms 2.0.0 โ 2.1.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 +149 -145
- package/dist/index.cjs +83 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -1
- package/dist/index.d.ts +66 -1
- package/dist/index.js +83 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/styles/classic.css +743 -0
- package/styles/declarative-forms.css +1023 -468
- package/CHANGELOG.md +0 -115
- package/styles/theme.css +0 -1293
package/README.md
CHANGED
|
@@ -1,102 +1,110 @@
|
|
|
1
1
|
# declarative-forms
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Ask the user for an object, the way `prompt()` asks for a string.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every other form library gives you a _component to mount_. This one gives you a
|
|
6
|
+
_function to call_. There is no component, no form state, no mount point, and no
|
|
7
|
+
place in your tree where the form has to live. You ask a question from wherever
|
|
8
|
+
you happen to be standing in your code, and you get the answer back.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
```ts
|
|
11
|
+
import { ask } from 'declarative-forms';
|
|
12
|
+
|
|
13
|
+
const release = await ask([
|
|
14
|
+
{ name: 'title', displayName: 'Release title' },
|
|
15
|
+
{ name: 'notes', kind: 'textarea', displayName: 'What changed' },
|
|
16
|
+
{
|
|
17
|
+
name: 'reviewers',
|
|
18
|
+
kind: 'select',
|
|
19
|
+
multiple: true,
|
|
20
|
+
displayName: 'Sign-off from',
|
|
21
|
+
options: () => fetchReviewers(),
|
|
22
|
+
},
|
|
23
|
+
]);
|
|
8
24
|
|
|
9
|
-
|
|
25
|
+
// { title: 'Sunrise 2.0', notes: 'โฆ', reviewers: ['Ada', 'Grace'] }
|
|
26
|
+
```
|
|
10
27
|
|
|
11
|
-
|
|
28
|
+
That is the whole integration. No `<Form>`, no `useForm`, no `onChange`, no
|
|
29
|
+
`useState`, no layout, no `<div>`. The dialog draws itself, loads its own
|
|
30
|
+
options, keeps every field in sync, and resolves.
|
|
12
31
|
|
|
13
|
-
|
|
32
|
+
`ask` ships with the library โ
|
|
33
|
+
[what it takes and what it resolves with](https://wolfoo2931.github.io/declarative-forms/guide/asking-for-data#the-ask-function).
|
|
14
34
|
|
|
15
|
-
|
|
35
|
+
๐ **[Read the documentation](https://wolfoo2931.github.io/declarative-forms/)** ยท
|
|
36
|
+
๐ **[Try the live demo](https://wolfoo2931.github.io/declarative-forms/#demo)**
|
|
16
37
|
|
|
17
|
-
|
|
38
|
+
## Why a call and not a component
|
|
18
39
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
40
|
+
`window.prompt()` is the one form API the browser gives you for free. You ask,
|
|
41
|
+
the browser draws the dialog, you get the answer. No markup, no state, no
|
|
42
|
+
layout, no lifecycle. Its only flaw is that it can ask for exactly one string.
|
|
22
43
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{ name: 'title', displayName: 'Title' },
|
|
26
|
-
{
|
|
27
|
-
name: 'language',
|
|
28
|
-
kind: 'select',
|
|
29
|
-
displayName: 'Language',
|
|
30
|
-
options: [
|
|
31
|
-
{ value: 'en', label: 'English' },
|
|
32
|
-
{ value: 'de', label: 'German' },
|
|
33
|
-
],
|
|
34
|
-
defaultValue: 'en',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: 'repo',
|
|
38
|
-
kind: 'select',
|
|
39
|
-
displayName: 'Repository',
|
|
40
|
-
reloadOnChangeOf: ['language'],
|
|
41
|
-
options: async ({ data }) => {
|
|
42
|
-
const lang = String(data['language'] ?? 'en');
|
|
43
|
-
return [{ value: `repo-${lang}`, label: `Repo (${lang})` }];
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
onConfirm: (values) => console.log(values),
|
|
48
|
-
onCancel: () => {},
|
|
49
|
-
});
|
|
44
|
+
Everything the industry built to replace it went the other way โ into the
|
|
45
|
+
component tree:
|
|
50
46
|
|
|
51
|
-
|
|
47
|
+
```jsx
|
|
48
|
+
// The component model: the form is a thing that lives somewhere.
|
|
49
|
+
const { register, handleSubmit } = useForm();
|
|
50
|
+
return (
|
|
51
|
+
<Modal open={open} onClose={() => setOpen(false)}>
|
|
52
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
53
|
+
<input {...register('title')} />โฆ
|
|
54
|
+
</form>
|
|
55
|
+
</Modal>
|
|
56
|
+
);
|
|
52
57
|
```
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
Now the form has a _location_. It needs a parent, a piece of `open` state, a
|
|
60
|
+
close handler, a submit handler, and a route from the answer back to the code
|
|
61
|
+
that wanted it. For a product's signup page, that is fine โ you were going to
|
|
62
|
+
lay that page out anyway. For the two hundredth settings dialog in an internal
|
|
63
|
+
tool, it is all overhead: you did not want a form, you wanted an answer.
|
|
59
64
|
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- Built-in modal stack support for nested dialogs and wizards
|
|
64
|
-
- Tabbed forms and array-of-record editing
|
|
65
|
-
- Automatic handling of many front-end concerns: layout, state sync, stacking, dialog behavior, and reusable field logic
|
|
66
|
-
- Zero runtime dependencies
|
|
67
|
-
- Works in plain JavaScript or TypeScript, not just React/Vue/Svelte
|
|
65
|
+
`declarative-forms` keeps `prompt()`'s shape and removes its one-string limit.
|
|
66
|
+
The form is not a thing that lives somewhere. It is a question, asked and
|
|
67
|
+
answered:
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
- computed fields that recalculate before confirmation
|
|
72
|
-
- file fields that require a `persistFile` callback to convert a local file into a stored URL
|
|
73
|
-
- custom render fields for embedding arbitrary DOM or widget logic inside a form
|
|
74
|
-
- message, cards, and custom field types for dynamic UI without a separate component library
|
|
75
|
-
|
|
76
|
-
## When to use this library
|
|
69
|
+
```ts
|
|
70
|
+
if (await confirmDetails()) { โฆ }
|
|
77
71
|
|
|
78
|
-
|
|
72
|
+
const filters = await ask(filterFields);
|
|
73
|
+
const entry = await ask(entryFields, { confirmLabel: 'Add' });
|
|
74
|
+
```
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
## What that model gives you that a component cannot
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- internal tools that need compact, data-driven UI without a UI framework dependency
|
|
78
|
+
**Dialogs stack, and the inner one can read the outer ones.** Because a form is
|
|
79
|
+
a call and not a node, opening one from inside another is just... calling it.
|
|
80
|
+
The library keeps the stack, and any field in any dialog can read the values of
|
|
81
|
+
every dialog beneath it through `stackData` โ with no lifted state, no context
|
|
82
|
+
provider, and no prop drilling, because there is no tree to lift through.
|
|
88
83
|
|
|
89
|
-
|
|
84
|
+
```ts
|
|
85
|
+
{
|
|
86
|
+
name: 'recap',
|
|
87
|
+
kind: 'message',
|
|
88
|
+
// stackData[0] is the outermost dialog, still open behind this one.
|
|
89
|
+
message: ({ data, stackData }) =>
|
|
90
|
+
`Publishing ${stackData[0]['title']} ${data['when'] === 'now' ? 'now' : 'later'}.`,
|
|
91
|
+
}
|
|
92
|
+
```
|
|
90
93
|
|
|
91
|
-
|
|
94
|
+
**Lists of records are just the same call again.** An `array` field opens one
|
|
95
|
+
dialog per entry, which is the natural shape for a record, and awkward to do
|
|
96
|
+
inline.
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
- `ButtonDescriptor`: supports async enable/disable logic and โdo not closeโ actions for wizards
|
|
98
|
+
**Works anywhere, because it needs nothing.** Plain DOM plus one web component,
|
|
99
|
+
no runtime dependencies. Call it from React, Vue, Svelte, Angular, an Electron
|
|
100
|
+
main-window script, or a `<script type="module">` tag in a static page. It never
|
|
101
|
+
touches your render tree, so there is nothing to integrate.
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
**The descriptor is live, not static.** `options`, `isActive`, `defaultValue`,
|
|
104
|
+
`placeholder`, `message`, `tab` and `compute` may each be a _function of the
|
|
105
|
+
current values_, and `reloadOnChangeOf` declares which field depends on which.
|
|
106
|
+
Options loaded from your API that change when another field changes are the
|
|
107
|
+
core feature, not an extension point.
|
|
100
108
|
|
|
101
109
|
## Install
|
|
102
110
|
|
|
@@ -104,96 +112,92 @@ This is why the project feels more like a small โform engineโ than a typical
|
|
|
104
112
|
npm install declarative-forms
|
|
105
113
|
```
|
|
106
114
|
|
|
107
|
-
And include the default CSS:
|
|
108
|
-
|
|
109
115
|
```ts
|
|
110
116
|
import 'declarative-forms/styles.css';
|
|
111
117
|
```
|
|
112
118
|
|
|
113
|
-
|
|
119
|
+
That is the default look, and it follows the operating system's light/dark
|
|
120
|
+
setting on its own. The v1 look ships as `declarative-forms/classic.css`.
|
|
121
|
+
|
|
122
|
+
No build step is required โ the ESM build loads directly in a `<script type="module">`.
|
|
123
|
+
|
|
124
|
+
## The full API, if you want the object instead of the promise
|
|
125
|
+
|
|
126
|
+
`ask` is a thin wrapper over the object underneath โ a normal object you can
|
|
127
|
+
hold on to, embed in a page instead of a dialog, subscribe to, and drive from
|
|
128
|
+
code:
|
|
114
129
|
|
|
115
130
|
```ts
|
|
116
131
|
import { DeclarativeForm } from 'declarative-forms';
|
|
117
132
|
|
|
118
133
|
const form = new DeclarativeForm({
|
|
119
|
-
fields: [
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
name: 'role',
|
|
123
|
-
kind: 'select',
|
|
124
|
-
displayName: 'Role',
|
|
125
|
-
options: ['Admin', 'Editor', 'Viewer'],
|
|
126
|
-
defaultValue: 'Editor',
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
onConfirm: (values) => {
|
|
130
|
-
console.log('submitted', values);
|
|
131
|
-
},
|
|
132
|
-
onCancel: () => {
|
|
133
|
-
console.log('cancelled');
|
|
134
|
-
},
|
|
134
|
+
fields: [...],
|
|
135
|
+
onConfirm: (values) => save(values),
|
|
136
|
+
onCancel: () => {},
|
|
135
137
|
});
|
|
136
138
|
|
|
137
|
-
form.openInModal();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
## Example: reactive field logic
|
|
139
|
+
form.openInModal(); // โฆor:
|
|
140
|
+
form.appendInElement(document.querySelector('#panel'));
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
fields: [
|
|
145
|
-
{
|
|
146
|
-
name: 'source',
|
|
147
|
-
kind: 'select',
|
|
148
|
-
displayName: 'Source',
|
|
149
|
-
options: ['GitHub', 'GitLab'],
|
|
150
|
-
defaultValue: 'GitHub',
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
name: 'token',
|
|
154
|
-
displayName: 'Token',
|
|
155
|
-
type: 'password',
|
|
156
|
-
isActive: ({ data }) => data['source'] === 'GitLab',
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
name: 'repo',
|
|
160
|
-
kind: 'select',
|
|
161
|
-
displayName: 'Repository',
|
|
162
|
-
reloadOnChangeOf: ['source'],
|
|
163
|
-
options: async ({ data }) => {
|
|
164
|
-
const source = String(data['source'] ?? 'GitHub');
|
|
165
|
-
return [{ value: `${source}-repo`, label: `${source} repo` }];
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
onConfirm: (values) => console.log(values),
|
|
170
|
-
});
|
|
142
|
+
form.subscribeOnInput((values) => renderPreview(values));
|
|
143
|
+
form.field('role')?.setValue('Admin');
|
|
171
144
|
```
|
|
172
145
|
|
|
173
|
-
|
|
146
|
+
See [the API reference](https://wolfoo2931.github.io/declarative-forms/reference/api).
|
|
174
147
|
|
|
175
|
-
##
|
|
148
|
+
## The ten field kinds
|
|
176
149
|
|
|
177
|
-
|
|
150
|
+
`text` ยท `textarea` ยท `select` (single, multiple, async, searchable) ยท
|
|
151
|
+
`checkbox` ยท `message` ยท `file` ยท `computed` ยท `cards` ยท `custom` ยท `array`
|
|
178
152
|
|
|
179
|
-
|
|
180
|
-
- [Field kinds](https://wolfoo2931.github.io/declarative-forms/guide/fields/) โ the ten built-in kinds
|
|
181
|
-
- [Reactivity](https://wolfoo2931.github.io/declarative-forms/guide/reactivity) โ `isActive`, `reloadOnChangeOf`, computed fields
|
|
182
|
-
- [API reference](https://wolfoo2931.github.io/declarative-forms/reference/api)
|
|
183
|
-
- [Migrating from v1](https://wolfoo2931.github.io/declarative-forms/migration-v1)
|
|
153
|
+
Full descriptions in **[Field kinds](https://wolfoo2931.github.io/declarative-forms/guide/fields/)**.
|
|
184
154
|
|
|
185
|
-
##
|
|
155
|
+
## Where it fits
|
|
186
156
|
|
|
187
|
-
|
|
157
|
+
Best in **settings and metadata dialogs for tool-like apps**: many optional
|
|
158
|
+
fields, grouped into tabs, where the available choices depend on what the user
|
|
159
|
+
already picked.
|
|
188
160
|
|
|
189
|
-
-
|
|
190
|
-
-
|
|
191
|
-
-
|
|
161
|
+
- admin panels and settings dialogs
|
|
162
|
+
- configuration flows with conditions and dependencies
|
|
163
|
+
- modal wizards with several steps
|
|
164
|
+
- forms whose options come from a server and depend on other fields
|
|
165
|
+
- editing lists whose entries are records of their own
|
|
166
|
+
- internal tools that need a data-driven UI without a framework dependency
|
|
167
|
+
|
|
168
|
+
## Where it does not fit
|
|
169
|
+
|
|
170
|
+
Plainly, so you can rule it out fast:
|
|
171
|
+
|
|
172
|
+
- **It is not a general-purpose form library.** It renders one fixed layout. If
|
|
173
|
+
you need control over the markup โ a product signup page, a marketing form โ
|
|
174
|
+
use a state library and write the markup yourself.
|
|
175
|
+
- **There is no validation framework.** No `required`, no rule objects, no
|
|
176
|
+
schema. There _are_ per-field error, warning and loading messages
|
|
177
|
+
(`setTooltipError` and friends), and `isValidRecord` and a button's `isActive`
|
|
178
|
+
gate submission โ but you write each check yourself, and the message and the
|
|
179
|
+
gate are separate things you wire up separately. See
|
|
180
|
+
[Validation](https://wolfoo2931.github.io/declarative-forms/guide/validation).
|
|
181
|
+
- **Accessibility is unfinished.** Labels, ids, focusable buttons and
|
|
182
|
+
keyboard-reachable checkboxes are correct. Dialog semantics, a focus trap, and
|
|
183
|
+
combobox ARIA are not. Read
|
|
184
|
+
[Accessibility](https://wolfoo2931.github.io/declarative-forms/accessibility)
|
|
185
|
+
in full before using it where conformance is required.
|
|
186
|
+
- **The rendered DOM is a frozen contract.** Class names, ids and structure are
|
|
187
|
+
stable across the v1 โ v2 rewrite, so existing stylesheets keep working. See
|
|
188
|
+
the [DOM contract](https://wolfoo2931.github.io/declarative-forms/dom-contract).
|
|
192
189
|
|
|
193
|
-
##
|
|
190
|
+
## Documentation
|
|
194
191
|
|
|
195
|
-
|
|
192
|
+
- [Asking for data](https://wolfoo2931.github.io/declarative-forms/guide/asking-for-data) โ the model, and `ask`
|
|
193
|
+
- [Getting started](https://wolfoo2931.github.io/declarative-forms/guide/getting-started)
|
|
194
|
+
- [Field kinds](https://wolfoo2931.github.io/declarative-forms/guide/fields/)
|
|
195
|
+
- [Reactivity](https://wolfoo2931.github.io/declarative-forms/guide/reactivity) โ `isActive`, `reloadOnChangeOf`, computed fields
|
|
196
|
+
- [Validation](https://wolfoo2931.github.io/declarative-forms/guide/validation) โ checks, tooltip messages, gating submit
|
|
197
|
+
- [Modals & stacking](https://wolfoo2931.github.io/declarative-forms/guide/modals)
|
|
198
|
+
- [API reference](https://wolfoo2931.github.io/declarative-forms/reference/api)
|
|
199
|
+
- [Migrating from v1](https://wolfoo2931.github.io/declarative-forms/migration-v1)
|
|
196
200
|
|
|
197
|
-
|
|
201
|
+
## License
|
|
198
202
|
|
|
199
|
-
|
|
203
|
+
MIT ยฉ Oliver Wolf
|
package/dist/index.cjs
CHANGED
|
@@ -195,6 +195,21 @@ var Field = class {
|
|
|
195
195
|
);
|
|
196
196
|
if (value !== void 0) this.setValue(value);
|
|
197
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Apply a plainly declared default value straight away.
|
|
200
|
+
*
|
|
201
|
+
* {@link applyDefaultValue} is awaited during initialisation, so a value it
|
|
202
|
+
* applies cannot be seen by the first layout pass. A default that is neither a
|
|
203
|
+
* function nor a promise needs no awaiting, so it is applied at build time and
|
|
204
|
+
* `isActive` predicates reading it are right from the first paint. The async
|
|
205
|
+
* pass runs afterwards regardless and simply sets the same value again.
|
|
206
|
+
*/
|
|
207
|
+
applyStaticDefaultValue() {
|
|
208
|
+
const value = this.descriptor.defaultValue;
|
|
209
|
+
if (value === void 0 || typeof value === "function") return;
|
|
210
|
+
if (value instanceof Promise) return;
|
|
211
|
+
this.setValue(value);
|
|
212
|
+
}
|
|
198
213
|
/**
|
|
199
214
|
* Hook for setup that cannot happen in {@link build} โ loading select
|
|
200
215
|
* options, or a first render that needs the form context. May be synchronous.
|
|
@@ -2106,6 +2121,7 @@ var DeclarativeForm = class _DeclarativeForm {
|
|
|
2106
2121
|
closeWith: (action) => this.close(action)
|
|
2107
2122
|
});
|
|
2108
2123
|
this.buildFields();
|
|
2124
|
+
this.applyInitialLayout();
|
|
2109
2125
|
this.ready = this.initialize();
|
|
2110
2126
|
}
|
|
2111
2127
|
// ------------------------------------------------------------------ setup
|
|
@@ -2127,12 +2143,40 @@ var DeclarativeForm = class _DeclarativeForm {
|
|
|
2127
2143
|
);
|
|
2128
2144
|
}
|
|
2129
2145
|
const field = this.registry.create(descriptor, context);
|
|
2146
|
+
field.applyStaticDefaultValue();
|
|
2130
2147
|
this.fieldList.push(field);
|
|
2131
2148
|
this.fieldsByName.set(descriptor.name, field);
|
|
2132
2149
|
this.tooltips.register(descriptor.name, field.tooltipMarker);
|
|
2133
2150
|
this.formElement.appendChild(field.wrapper);
|
|
2134
2151
|
}
|
|
2135
2152
|
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Decide field visibility and build the tab strip synchronously, before the
|
|
2155
|
+
* caller gets the form back.
|
|
2156
|
+
*
|
|
2157
|
+
* {@link initialize} is async โ it awaits option loads and default values โ
|
|
2158
|
+
* but `openInModal()` normally runs in the same tick as the constructor.
|
|
2159
|
+
* Without this pass the dialog paints every field of every tab, then collapses
|
|
2160
|
+
* to the active tab once that first async update lands, which is a visible
|
|
2161
|
+
* layout shift as soon as any field loads its options over the network.
|
|
2162
|
+
*
|
|
2163
|
+
* Only visibility is decided here: no `onFormChange`, no `onFormUpdate`, and
|
|
2164
|
+
* the value model is left untouched, so the regular update cycle still sees
|
|
2165
|
+
* the first real change and refreshes the buttons.
|
|
2166
|
+
*/
|
|
2167
|
+
applyInitialLayout() {
|
|
2168
|
+
const values = this.getValues();
|
|
2169
|
+
const activeTabs = /* @__PURE__ */ new Set();
|
|
2170
|
+
for (const field of this.fieldList) {
|
|
2171
|
+
const active = field.descriptor.isActive?.({
|
|
2172
|
+
...this.contextFor(field.descriptor),
|
|
2173
|
+
data: values
|
|
2174
|
+
}) ?? true;
|
|
2175
|
+
field.setActive(active);
|
|
2176
|
+
if (active) for (const tab of field.tabs()) activeTabs.add(tab);
|
|
2177
|
+
}
|
|
2178
|
+
this.renderTabs(activeTabs);
|
|
2179
|
+
}
|
|
2136
2180
|
async initialize() {
|
|
2137
2181
|
await Promise.allSettled(
|
|
2138
2182
|
this.fieldList.map(
|
|
@@ -2470,6 +2514,44 @@ var DeclarativeForm = class _DeclarativeForm {
|
|
|
2470
2514
|
}
|
|
2471
2515
|
};
|
|
2472
2516
|
|
|
2517
|
+
// src/core/ask.ts
|
|
2518
|
+
function ask(fields, options = {}) {
|
|
2519
|
+
return askWith(DeclarativeForm, fields, options);
|
|
2520
|
+
}
|
|
2521
|
+
function askWith(FormClass, fields, options = {}) {
|
|
2522
|
+
const { modal, dismissable = true, buttons, ...formOptions } = options;
|
|
2523
|
+
return new Promise((resolve) => {
|
|
2524
|
+
let settled = false;
|
|
2525
|
+
const settle = (values) => {
|
|
2526
|
+
if (settled) return;
|
|
2527
|
+
settled = true;
|
|
2528
|
+
setTimeout(() => resolve(values), 0);
|
|
2529
|
+
};
|
|
2530
|
+
new FormClass({
|
|
2531
|
+
...formOptions,
|
|
2532
|
+
fields,
|
|
2533
|
+
buttons: buttons && settleOnClose(buttons, settle),
|
|
2534
|
+
onConfirm: (values) => settle(values),
|
|
2535
|
+
onCancel: dismissable ? () => settle(void 0) : void 0
|
|
2536
|
+
}).openInModal(modal);
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
function settleOnClose(buttons, settle) {
|
|
2540
|
+
return Object.fromEntries(
|
|
2541
|
+
Object.entries(buttons).map(([label, descriptor]) => [
|
|
2542
|
+
label,
|
|
2543
|
+
descriptor.doNotCloseModal ? descriptor : {
|
|
2544
|
+
...descriptor,
|
|
2545
|
+
action: async (values) => {
|
|
2546
|
+
const result = await descriptor.action?.(values);
|
|
2547
|
+
settle(values);
|
|
2548
|
+
return result;
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
])
|
|
2552
|
+
);
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2473
2555
|
exports.ArrayField = ArrayField;
|
|
2474
2556
|
exports.ButtonBar = ButtonBar;
|
|
2475
2557
|
exports.CardsField = CardsField;
|
|
@@ -2496,6 +2578,7 @@ exports.TextAreaField = TextAreaField;
|
|
|
2496
2578
|
exports.TextField = TextField;
|
|
2497
2579
|
exports.TooltipController = TooltipController;
|
|
2498
2580
|
exports.UpdateScheduler = UpdateScheduler;
|
|
2581
|
+
exports.ask = ask;
|
|
2499
2582
|
exports.deepEqual = deepEqual;
|
|
2500
2583
|
exports.defaultFieldRegistry = defaultFieldRegistry;
|
|
2501
2584
|
exports.defineDlSelect = defineDlSelect;
|