nexus-shared 3.2.0 → 4.0.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/CHANGELOG.md +1564 -0
- package/README.md +1705 -1123
- package/dist/Client.Index.d.ts +14 -0
- package/dist/Client.Index.js +14 -0
- package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
- package/dist/Components/Documents/DocumentFrame.js +83 -0
- package/dist/Components/Documents/Menu.d.ts +4 -0
- package/dist/Components/Documents/Menu.js +40 -24
- package/dist/Components/Documents/PrintDocument.d.ts +95 -0
- package/dist/Components/Documents/PrintDocument.js +62 -0
- package/dist/Components/Documents/Tabs.d.ts +59 -0
- package/dist/Components/Documents/Tabs.js +70 -0
- package/dist/Components/Forms/ApiForm.js +19 -2
- package/dist/Components/Forms/Crud.d.ts +2 -2
- package/dist/Components/Forms/Crud.js +576 -243
- package/dist/Components/Forms/CrudActions.d.ts +136 -0
- package/dist/Components/Forms/CrudActions.js +184 -0
- package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
- package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
- package/dist/Components/Forms/CrudParts.d.ts +30 -2
- package/dist/Components/Forms/CrudParts.js +55 -5
- package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
- package/dist/Components/Forms/CrudRowForms.js +114 -0
- package/dist/Components/Forms/CrudScopes.d.ts +28 -0
- package/dist/Components/Forms/CrudScopes.js +76 -0
- package/dist/Components/Forms/CrudTree.d.ts +90 -0
- package/dist/Components/Forms/CrudTree.js +363 -0
- package/dist/Components/Forms/CrudWorkspace.d.ts +63 -0
- package/dist/Components/Forms/CrudWorkspace.js +169 -0
- package/dist/Components/Forms/ExcelImport.js +3 -3
- package/dist/Components/Forms/Form.d.ts +4 -4
- package/dist/Components/Forms/Form.js +123 -17
- package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
- package/dist/Components/Forms/FormLeaveGuard.js +65 -0
- package/dist/Components/Forms/RecordFlows.d.ts +50 -0
- package/dist/Components/Forms/RecordFlows.js +282 -0
- package/dist/Components/Forms/SubmitForm.d.ts +37 -2
- package/dist/Components/Forms/SubmitForm.js +195 -10
- package/dist/Components/Inputs/CheckBox.js +1 -1
- package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
- package/dist/Components/Inputs/ChoiceField.js +28 -2
- package/dist/Components/Inputs/Dropdown.d.ts +13 -0
- package/dist/Components/Inputs/Dropdown.js +5 -5
- package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
- package/dist/Components/Inputs/DropdownParts.js +16 -4
- package/dist/Components/Inputs/Field.d.ts +7 -1
- package/dist/Components/Inputs/Field.js +2 -2
- package/dist/Components/Inputs/GroupForm.d.ts +6 -6
- package/dist/Components/Inputs/GroupForm.js +10 -11
- package/dist/Components/Inputs/InputRenderer.d.ts +9 -6
- package/dist/Components/Inputs/InputRenderer.js +37 -32
- package/dist/Components/Inputs/PictureInput.d.ts +41 -0
- package/dist/Components/Inputs/PictureInput.js +70 -0
- package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
- package/dist/Components/Inputs/RadioGroup.js +8 -3
- package/dist/Components/Inputs/RowsInput.d.ts +1 -1
- package/dist/Components/Inputs/RowsInput.js +18 -3
- package/dist/Components/Inputs/TabularForm.d.ts +5 -5
- package/dist/Components/Inputs/TabularForm.js +29 -29
- package/dist/Components/Inputs/TextArea.d.ts +6 -0
- package/dist/Components/Inputs/TextArea.js +11 -2
- package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
- package/dist/Components/Inputs/TextBoxTypes.js +17 -0
- package/dist/Components/Inputs/TreeView.d.ts +53 -2
- package/dist/Components/Inputs/TreeView.js +147 -13
- package/dist/Components/Layouts/MessageBox.d.ts +15 -2
- package/dist/Components/Layouts/MessageBox.js +56 -7
- package/dist/Components/Layouts/Popup.d.ts +13 -6
- package/dist/Components/Layouts/Popup.js +37 -9
- package/dist/Components/Layouts/PopupDock.d.ts +3 -1
- package/dist/Components/Layouts/PopupDock.js +2 -1
- package/dist/Components/Layouts/PopupHost.js +5 -4
- package/dist/Components/Viewers/DataTable.js +150 -48
- package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
- package/dist/Components/Viewers/DataTableFilters.js +17 -2
- package/dist/Components/Viewers/DataTableParts.d.ts +21 -5
- package/dist/Components/Viewers/DataTableParts.js +26 -14
- package/dist/Components/Viewers/OwnerFiles.d.ts +175 -0
- package/dist/Components/Viewers/OwnerFiles.js +589 -0
- package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
- package/dist/Components/Viewers/QrCodeImage.js +15 -0
- package/dist/Components/Viewers/RecordCards.d.ts +16 -0
- package/dist/Components/Viewers/RecordCards.js +509 -0
- package/dist/Helpers/BusyHooks.d.ts +6 -0
- package/dist/Helpers/BusyHooks.js +39 -0
- package/dist/Helpers/CrudBackend.d.ts +20 -2
- package/dist/Helpers/CrudBackend.js +46 -2
- package/dist/Helpers/CrudColumns.d.ts +53 -6
- package/dist/Helpers/CrudColumns.js +98 -7
- package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
- package/dist/Helpers/CrudDetailsTabs.js +126 -0
- package/dist/Helpers/CrudHelpers.d.ts +81 -2
- package/dist/Helpers/CrudHelpers.js +195 -0
- package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
- package/dist/Helpers/CrudNarrowPick.js +157 -0
- package/dist/Helpers/CrudRowForms.d.ts +15 -0
- package/dist/Helpers/CrudRowForms.js +43 -0
- package/dist/Helpers/CrudScopes.d.ts +32 -0
- package/dist/Helpers/CrudScopes.js +79 -0
- package/dist/Helpers/DropdownHelpers.d.ts +3 -1
- package/dist/Helpers/DropdownHelpers.js +6 -2
- package/dist/Helpers/ExcelHelpers.d.ts +1 -1
- package/dist/Helpers/ExcelHelpers.js +12 -11
- package/dist/Helpers/FileAdapter.d.ts +78 -0
- package/dist/Helpers/FileAdapter.js +102 -0
- package/dist/Helpers/FilePrimaries.d.ts +48 -0
- package/dist/Helpers/FilePrimaries.js +138 -0
- package/dist/Helpers/FormConfirm.d.ts +32 -0
- package/dist/Helpers/FormConfirm.js +40 -0
- package/dist/Helpers/FormStore.d.ts +90 -5
- package/dist/Helpers/FormStore.js +318 -22
- package/dist/Helpers/InputConditions.d.ts +50 -0
- package/dist/Helpers/InputConditions.js +168 -0
- package/dist/Helpers/InputMemory.d.ts +106 -0
- package/dist/Helpers/InputMemory.js +385 -0
- package/dist/Helpers/InputNarrowing.d.ts +18 -0
- package/dist/Helpers/InputNarrowing.js +67 -0
- package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
- package/dist/Helpers/InputParamsHelpers.js +157 -74
- package/dist/Helpers/NexusDriveAdapter.d.ts +55 -0
- package/dist/Helpers/NexusDriveAdapter.js +207 -0
- package/dist/Helpers/NumberHelpers.d.ts +95 -0
- package/dist/Helpers/NumberHelpers.js +126 -0
- package/dist/Helpers/Permissions.js +2 -1
- package/dist/Helpers/QrCode.d.ts +26 -0
- package/dist/Helpers/QrCode.js +463 -0
- package/dist/Helpers/RecordCards.d.ts +205 -0
- package/dist/Helpers/RecordCards.js +378 -0
- package/dist/Helpers/RequestCache.d.ts +14 -2
- package/dist/Helpers/RequestCache.js +22 -3
- package/dist/Helpers/RowsStore.d.ts +37 -2
- package/dist/Helpers/RowsStore.js +128 -10
- package/dist/Helpers/ServerPreview.d.ts +69 -0
- package/dist/Helpers/ServerPreview.js +99 -0
- package/dist/Helpers/TableColumns.js +4 -3
- package/dist/Helpers/TableExport.d.ts +7 -1
- package/dist/Helpers/TableExport.js +9 -3
- package/dist/Helpers/TableFilters.d.ts +1 -1
- package/dist/Helpers/TableFilters.js +24 -23
- package/dist/Helpers/TableHelpers.d.ts +23 -2
- package/dist/Helpers/TableHelpers.js +51 -10
- package/dist/Helpers/TableJoins.d.ts +13 -0
- package/dist/Helpers/TableJoins.js +34 -0
- package/dist/Helpers/TableStore.d.ts +21 -1
- package/dist/Helpers/TableStore.js +64 -7
- package/dist/Helpers/TreeStore.d.ts +81 -0
- package/dist/Helpers/TreeStore.js +365 -0
- package/dist/Interfaces/CrudInterfaces.d.ts +465 -5
- package/dist/Interfaces/FileInterfaces.d.ts +162 -0
- package/dist/Interfaces/FileInterfaces.js +4 -0
- package/dist/Interfaces/FormInterfaces.d.ts +391 -30
- package/dist/Interfaces/FormInterfaces.js +79 -1
- package/dist/Interfaces/InputInterfaces.d.ts +15 -3
- package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
- package/dist/Interfaces/RecordInterfaces.js +1 -0
- package/dist/Interfaces/TableInterfaces.d.ts +74 -1
- package/dist/Services/ExcelApi.js +5 -0
- package/dist/Services/FileApi.d.ts +13 -0
- package/dist/Services/FileApi.js +29 -0
- package/dist/Services/ImageEditorLoader.d.ts +14 -0
- package/dist/Services/ImageEditorLoader.js +51 -0
- package/dist/Services/JoinSources.d.ts +37 -5
- package/dist/Services/JoinSources.js +90 -14
- package/dist/Services/ModuleImport.d.ts +7 -0
- package/dist/Services/ModuleImport.js +22 -0
- package/dist/Shared.Index.d.ts +16 -0
- package/dist/Shared.Index.js +16 -0
- package/package.json +7 -2
- package/src/Styles/Nexus.Avatar.css +6 -5
- package/src/Styles/Nexus.Button.css +10 -4
- package/src/Styles/Nexus.Choice.css +16 -0
- package/src/Styles/Nexus.Crud.css +383 -9
- package/src/Styles/Nexus.Dropdown.css +8 -0
- package/src/Styles/Nexus.Files.css +410 -0
- package/src/Styles/Nexus.Form.css +5 -2
- package/src/Styles/Nexus.Index.css +4 -0
- package/src/Styles/Nexus.Input.css +42 -0
- package/src/Styles/Nexus.Menu.css +10 -1
- package/src/Styles/Nexus.Popup.css +77 -1
- package/src/Styles/Nexus.Print.css +288 -0
- package/src/Styles/Nexus.QrCode.css +12 -0
- package/src/Styles/Nexus.Record.css +430 -0
- package/src/Styles/Nexus.Tab.Buttons.css +87 -22
- package/src/Styles/Nexus.Table.css +67 -5
- package/src/Styles/Nexus.Themes.css +12 -6
- package/src/Styles/Nexus.Tokens.css +8 -3
- package/src/Styles/Nexus.Tree.css +31 -1
|
@@ -8,18 +8,98 @@ import type { DateTimePickerProps } from "../Components/Inputs/DateTimePicker.ts
|
|
|
8
8
|
import type { DropdownProps } from "../Components/Inputs/Dropdown.tsx";
|
|
9
9
|
import type { MarkdownEditorProps } from "../Components/Inputs/MarkdownEditor.tsx";
|
|
10
10
|
import type { NumberBoxProps } from "../Components/Inputs/NumberBox.tsx";
|
|
11
|
+
import type { PictureInputProps } from "../Components/Inputs/PictureInput.tsx";
|
|
11
12
|
import type { RadioGroupProps } from "../Components/Inputs/RadioGroup.tsx";
|
|
12
13
|
import type { RichTextEditorProps } from "../Components/Inputs/RichTextEditor.tsx";
|
|
13
14
|
import type { RangeSliderProps, SliderProps } from "../Components/Inputs/Slider.tsx";
|
|
14
15
|
import type { TextAreaProps } from "../Components/Inputs/TextArea.tsx";
|
|
15
|
-
import type { TextBoxProps
|
|
16
|
+
import type { TextBoxProps } from "../Components/Inputs/TextBox.tsx";
|
|
16
17
|
import type { TimePickerProps } from "../Components/Inputs/TimePicker.tsx";
|
|
17
18
|
import type { TreeViewProps } from "../Components/Inputs/TreeView.tsx";
|
|
19
|
+
import type { PopupSize } from "../Components/Layouts/Popup.tsx";
|
|
18
20
|
import type { FormStore } from "../Helpers/FormStore.ts";
|
|
19
21
|
import type { ApiEndpoint, ApiResult } from "./ApiInterfaces.ts";
|
|
20
22
|
import type { DatePreferences } from "./DateInterfaces.ts";
|
|
21
23
|
import type { InputDesign, OptionValue } from "./InputInterfaces.ts";
|
|
24
|
+
import type { TableColumn } from "./TableInterfaces.ts";
|
|
22
25
|
import type { Size } from "./TypeInterfaces.ts";
|
|
26
|
+
/**
|
|
27
|
+
* Which input a params object describes: `{ type: EInputTypes.Dropdown, name: "country", ... }`. A number, so a
|
|
28
|
+
* database, a generated page and a C# enum hold the same value.
|
|
29
|
+
*
|
|
30
|
+
* The numbers come in blocks by kind, each with room to grow. 0 is never a kind, so a missing value is never a valid
|
|
31
|
+
* one; a released number is never reused or given a new meaning.
|
|
32
|
+
*
|
|
33
|
+
* | Block | Kind | Members |
|
|
34
|
+
* |---|---|---|
|
|
35
|
+
* | 1–19 | Text boxes | Text 1, Email 2, Password 3, Search 4, Tel 5, Url 6, TextArea 7, Code 8 |
|
|
36
|
+
* | 20–39 | Choices | CheckBox 20, Switch 21, Radio 22, CheckBoxGroup 23, RadioGroup 24, Dropdown 25 |
|
|
37
|
+
* | 40–59 | Dates and times | Date 40, DateRange 41, Time 42, DateTime 43 |
|
|
38
|
+
* | 60–79 | Numbers and sliders | Number 60, Slider 61, RangeSlider 62 |
|
|
39
|
+
* | 80–99 | Editors | Markdown 80, RichText 81 |
|
|
40
|
+
* | 100–119 | Structures | Tree 100, Tabular 101, Group 102, Picture 103 |
|
|
41
|
+
*
|
|
42
|
+
* A dropdown is one member: `multiple` decides whether it picks one item or many. Every number fits a byte.
|
|
43
|
+
*/
|
|
44
|
+
export declare const EInputTypes: {
|
|
45
|
+
/** `TextBox`, a single line. */
|
|
46
|
+
readonly Text: 1;
|
|
47
|
+
/** `TextBox` of HTML type email. */
|
|
48
|
+
readonly Email: 2;
|
|
49
|
+
/** `TextBox` of HTML type password, with a button that shows what was typed. */
|
|
50
|
+
readonly Password: 3;
|
|
51
|
+
/** `TextBox` of HTML type search. */
|
|
52
|
+
readonly Search: 4;
|
|
53
|
+
/** `TextBox` of HTML type tel: a phone number. */
|
|
54
|
+
readonly Tel: 5;
|
|
55
|
+
/** `TextBox` of HTML type url. */
|
|
56
|
+
readonly Url: 6;
|
|
57
|
+
/** `TextArea`: several lines of plain text. */
|
|
58
|
+
readonly TextArea: 7;
|
|
59
|
+
/** `CodeBox`: a box per character, such as a one-time code. */
|
|
60
|
+
readonly Code: 8;
|
|
61
|
+
/** `CheckBox`: `true` or `false`. */
|
|
62
|
+
readonly CheckBox: 20;
|
|
63
|
+
/** `Switch`: `true` or `false`, for a setting that takes effect. */
|
|
64
|
+
readonly Switch: 21;
|
|
65
|
+
/** `RadioButton`: one radio, on or off; in rows, only one row has it on. */
|
|
66
|
+
readonly Radio: 22;
|
|
67
|
+
/** `CheckBoxGroup`: any number of options. */
|
|
68
|
+
readonly CheckBoxGroup: 23;
|
|
69
|
+
/** `RadioGroup`: one option of a few, all in view. */
|
|
70
|
+
readonly RadioGroup: 24;
|
|
71
|
+
/** `Dropdown`: one item, or many with `multiple`. */
|
|
72
|
+
readonly Dropdown: 25;
|
|
73
|
+
/** `DatePicker`: an ISO date. */
|
|
74
|
+
readonly Date: 40;
|
|
75
|
+
/** `DateRangePicker`: two ISO dates. */
|
|
76
|
+
readonly DateRange: 41;
|
|
77
|
+
/** `TimePicker`: an ISO time. */
|
|
78
|
+
readonly Time: 42;
|
|
79
|
+
/** `DateTimePicker`: a UTC moment shown in the user's zone. */
|
|
80
|
+
readonly DateTime: 43;
|
|
81
|
+
/** `NumberBox`. */
|
|
82
|
+
readonly Number: 60;
|
|
83
|
+
/** `Slider`: one number on a track. */
|
|
84
|
+
readonly Slider: 61;
|
|
85
|
+
/** `RangeSlider`: two numbers on a track. */
|
|
86
|
+
readonly RangeSlider: 62;
|
|
87
|
+
/** `MarkdownEditor`: Markdown text. */
|
|
88
|
+
readonly Markdown: 80;
|
|
89
|
+
/** `RichTextEditor`: sanitized HTML. */
|
|
90
|
+
readonly RichText: 81;
|
|
91
|
+
/** `TreeView`: nodes picked in a tree. */
|
|
92
|
+
readonly Tree: 100;
|
|
93
|
+
/** `TabularForm`: rows of inputs in a table. */
|
|
94
|
+
readonly Tabular: 101;
|
|
95
|
+
/** `GroupForm`: the same rows, each as a section of inputs. */
|
|
96
|
+
readonly Group: 102;
|
|
97
|
+
/** `PictureInput`: a record's picture, kept by the file backend as one of the owner's files, not a value of the form. */
|
|
98
|
+
readonly Picture: 103;
|
|
99
|
+
};
|
|
100
|
+
export type EInputTypes = (typeof EInputTypes)[keyof typeof EInputTypes];
|
|
101
|
+
/** The text box kinds: the members a `TextBox` renders, each with its HTML type. */
|
|
102
|
+
export type TextInputType = typeof EInputTypes.Text | typeof EInputTypes.Email | typeof EInputTypes.Password | typeof EInputTypes.Search | typeof EInputTypes.Tel | typeof EInputTypes.Url;
|
|
23
103
|
/** Where a form places an input. The input itself ignores these keys. */
|
|
24
104
|
export interface InputLayoutParams {
|
|
25
105
|
/** Optional heading before this field when the section changes (Form only). */
|
|
@@ -35,18 +115,214 @@ export interface InputLayoutParams {
|
|
|
35
115
|
/**
|
|
36
116
|
* Other inputs of the same form this one follows (Form only). When any of their values changes, this input draws
|
|
37
117
|
* again — without mounting again, so nothing typed in it is lost — and its params are read afresh: a getter for
|
|
38
|
-
* `hidden`, `readOnly`, `readOnlyMessage`, `label`, `hint` or `
|
|
39
|
-
* own rule is checked again too, so a `required` that a getter just switched on or off counts at once. Name the
|
|
118
|
+
* `hidden`, `readOnly`, `readOnlyMessage`, `label`, `hint`, `options` or `span` answers for the values as they are now.
|
|
119
|
+
* Its own rule is checked again too, so a `required` that a getter just switched on or off counts at once. Name the
|
|
40
120
|
* input itself to follow its own typing (a hint that says what the typed text will become).
|
|
41
121
|
*
|
|
42
122
|
* An input that names any is asked `hidden` every time it draws, so it can come and go; one that names none is
|
|
43
123
|
* asked once, when the form draws.
|
|
44
124
|
*/
|
|
45
125
|
dependsOn?: readonly string[];
|
|
126
|
+
/**
|
|
127
|
+
* Another input of the same form whose value narrows this one's options (Form only): a Package list of the Group picked.
|
|
128
|
+
* The options are the records whose `key` field holds that value (static `options` filtered; `loadOptions` asked with
|
|
129
|
+
* `request.narrow`, which `crudPicker` answers from the server when its endpoint takes the parent, else from its kept list);
|
|
130
|
+
* the list loads afresh when the value changes, offers nothing while it is empty, and says what to pick first. A value
|
|
131
|
+
* picked here is cleared when a person changes the other one. It follows the other input as `dependsOn` would.
|
|
132
|
+
*
|
|
133
|
+
* @example { type: EInputTypes.Dropdown, name: "subscriptionPackageId", label: "Package", ...crudPicker(PACKAGES, { labelKey: "packageName" }), narrowedBy: { name: "subscriptionGroupId" } }
|
|
134
|
+
*/
|
|
135
|
+
narrowedBy?: InputNarrowing;
|
|
136
|
+
/**
|
|
137
|
+
* When the input shows (Form only): while every condition holds, each on another input's value as it is now. The form
|
|
138
|
+
* follows the inputs named as `dependsOn` would, so the input comes and goes as they change, keeps what was typed in it
|
|
139
|
+
* (its value stays in the data, as a `hidden` one's does) and is not checked while it is hidden. A `hidden: true`, or a
|
|
140
|
+
* `hidden` getter, still hides it whatever the conditions say.
|
|
141
|
+
*
|
|
142
|
+
* @example showWhen: { name: "counterType", in: [0, 1] }
|
|
143
|
+
* @example showWhen: [{ name: "counterType", in: [2] }, { name: "subscriptionMiscellaneousFeeId", filled: false }]
|
|
144
|
+
*/
|
|
145
|
+
showWhen?: InputCondition | readonly InputCondition[];
|
|
146
|
+
/**
|
|
147
|
+
* When the input is read-only (Form only): while every condition holds, each on another input's value as it is now -
|
|
148
|
+
* the same conditions as `showWhen`. It shows its value with the lock and says why on any try to change it (the
|
|
149
|
+
* input's own `readOnlyMessage`), its value stays in the data sent, and it is not checked while read-only. The form
|
|
150
|
+
* follows the inputs named as `dependsOn` would. A `readOnly: true`, or a `readOnly` getter, still locks it whatever
|
|
151
|
+
* the conditions say.
|
|
152
|
+
*
|
|
153
|
+
* A CRUD's Edit form holds the record it edits, its key (`rowKey`, default `id`) among the values, and its Add form
|
|
154
|
+
* does not: so an input picked on Add and fixed once saved is one line.
|
|
155
|
+
*
|
|
156
|
+
* @example readOnlyWhen: { name: "id", filled: true }, readOnlyMessage: "A saved price keeps its group."
|
|
157
|
+
*/
|
|
158
|
+
readOnlyWhen?: InputCondition | readonly InputCondition[];
|
|
159
|
+
/**
|
|
160
|
+
* Opens the input on a value the form's own values leave it without (a Form's input, or an input in rows): the
|
|
161
|
+
* company's `preferred` value, else the person's last saved choice for `key`, kept in this browser per `viewer` when a
|
|
162
|
+
* form holding it saves - each only while the input still offers it, else dropped without a word and a kept choice
|
|
163
|
+
* forgotten. "Offers": a choice group's or a dropdown's own `options` list it (narrowed as `narrowedBy` says), neither
|
|
164
|
+
* disabled nor inactive; a picker (`loadOptions`, as `crudPicker` gives) is asked by the value's key first
|
|
165
|
+
* (`peekOption`, else `resolveOptions`, and its pages for a key that answers nothing) and must answer it, not inactive
|
|
166
|
+
* (`isOptionInactive`) and inside its narrowing - nothing opens until it answers, its box shows its spinner meanwhile,
|
|
167
|
+
* an answer for a group (`narrowedBy`, `per`, `when`) the form no longer holds opens nothing, and a picker that cannot
|
|
168
|
+
* be read opens nothing and forgets nothing. "Leave it without": the values the form opens with (its `defaultValue`, a
|
|
169
|
+
* CRUD's new values, a record) do not name the input or hold it empty; the input's own `defaultValue` gives way. The
|
|
170
|
+
* value counts as the one the form opened with, not as a change; a pick (or a value from code) before a picker answers
|
|
171
|
+
* is never replaced; a form reset after a save opens on the new choice.
|
|
172
|
+
*
|
|
173
|
+
* In rows (a tabular or group form): a new row's input opens on the kept value unless `newRow` or the values the row
|
|
174
|
+
* is added with name it - so give a remembered row input its first choice as its own `defaultValue`, not in `newRow`
|
|
175
|
+
* - and a save keeps the value of the row added last (none added: nothing kept).
|
|
176
|
+
*
|
|
177
|
+
* @example remember: { key: "counter-paid-by", viewer: `${userId}|${companyId}`, preferred: settings.defaultPaymentMode }
|
|
178
|
+
* @example remember: { key: "subscribers-package", viewer, per: "subscriptionGroupId", when: { name: "id", filled: false } }
|
|
179
|
+
*/
|
|
180
|
+
remember?: InputRemember;
|
|
181
|
+
/**
|
|
182
|
+
* A label, and a placeholder, that follow another input's value (Form only): `labels` by that value (compared as
|
|
183
|
+
* text, so `1` matches `"1"`), `otherwise` for any other value or none (default: the input's own `label`). The
|
|
184
|
+
* input's messages name it by the label it shows ("VAT number is required"), unless it has a `fieldName`. The form
|
|
185
|
+
* follows the other input as `dependsOn` would: the field draws again, keeping what was typed.
|
|
186
|
+
*
|
|
187
|
+
* @example labelWhen: { name: "registrationType", labels: { 1: "PAN number", 2: "VAT number" }, otherwise: "PAN/VAT number" }
|
|
188
|
+
*/
|
|
189
|
+
labelWhen?: InputLabelWhen;
|
|
190
|
+
/**
|
|
191
|
+
* Another width while conditions hold (Form only): `span` while every condition of `when` holds, each on another
|
|
192
|
+
* input's value as it is now (the conditions of `showWhen`; the input's own name follows its own value), else the
|
|
193
|
+
* input's own `span`. The form follows the inputs named as `dependsOn` would: the cell takes its new width without
|
|
194
|
+
* mounting again, so what is typed, the caret and the focus stay. A narrow form still gives every input the whole row.
|
|
195
|
+
*
|
|
196
|
+
* @example spanWhen: { when: { name: "billDate", filled: false }, span: 12 } // the whole row until a date is put, then its own 6
|
|
197
|
+
* @example spanWhen: { when: [{ name: "counterType", in: [0] }, { name: "billDate", filled: false }], span: 12 }
|
|
198
|
+
*/
|
|
199
|
+
spanWhen?: InputSpanWhen;
|
|
200
|
+
/**
|
|
201
|
+
* A value worked out from other inputs (Form only), shown and never typed: written when the form opens (new values, a
|
|
202
|
+
* loaded record) and again whenever an input in `from` changes - by a person, by code, a load, a reset - or the form's
|
|
203
|
+
* `computeWith` changes. It is no form value: never sent, never checked, never a change, never remembered or kept in a
|
|
204
|
+
* draft. Meant for a Number (its `decimals`, `prefix`, `suffix`, in the person's number format); any other type shows
|
|
205
|
+
* its text. Null, undefined, "" or NaN leave the box blank. The box is an `<output>` on the read-only tint, with no
|
|
206
|
+
* lock, no steppers and no Tab stop. In a tabular or group form's rows it is ignored, as `showWhen` is.
|
|
207
|
+
*
|
|
208
|
+
* @example compute: { from: ["price", "discountPercentage", "vatPercentage"], value: values => priceAmounts(values).netAmount }
|
|
209
|
+
* @example compute: { from: [], value: (_values, preview) => preview?.netAmount } // with computeWith={preview}
|
|
210
|
+
*/
|
|
211
|
+
compute?: InputCompute;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* How an input's value is worked out (`InputLayoutParams.compute`): from the form's values as they are now, and from what
|
|
215
|
+
* the page passes as the form's `computeWith` (a server's preview answer). `withValue` is whatever the page passed, so it
|
|
216
|
+
* is `any` here: name its type in the function (`(_values, line: CounterLine | null) => line?.netAmount`).
|
|
217
|
+
*/
|
|
218
|
+
export interface InputCompute<TWith = any> {
|
|
219
|
+
/** The inputs it follows: it is worked out again when any of them changes. `[]` follows only `computeWith`. */
|
|
220
|
+
from: readonly string[];
|
|
221
|
+
/** The value to show. `withValue` is the form's `computeWith`, as the page passed it (`undefined` when it passed none). */
|
|
222
|
+
value(values: FormValues, withValue: TWith): unknown;
|
|
223
|
+
}
|
|
224
|
+
/** An input's label that follows another input's value (`InputLayoutParams.labelWhen`). */
|
|
225
|
+
export interface InputLabelWhen {
|
|
226
|
+
/** The other input's name: `"registrationType"`. */
|
|
227
|
+
name: string;
|
|
228
|
+
/** The label for each of its values: `{ 1: "PAN number", 2: "VAT number" }`. */
|
|
229
|
+
labels: Readonly<Record<string, string>>;
|
|
230
|
+
/** The label for any other value, or none. Default: the input's own `label`. */
|
|
231
|
+
otherwise?: string;
|
|
232
|
+
/** The placeholder for each of its values; one not listed keeps the input's own `placeholder`. */
|
|
233
|
+
placeholders?: Readonly<Record<string, string>>;
|
|
234
|
+
}
|
|
235
|
+
/** An input's width while conditions hold (`InputLayoutParams.spanWhen`). */
|
|
236
|
+
export interface InputSpanWhen {
|
|
237
|
+
/** The conditions, as `showWhen`'s: every one must hold. An empty list holds always. */
|
|
238
|
+
when: InputCondition | readonly InputCondition[];
|
|
239
|
+
/** The columns it spans while they hold, 1 to 12 (as `span`). */
|
|
240
|
+
span: number;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* An input's remembered value (`InputLayoutParams.remember`), kept under `nx-input:{key}:{viewer}` in local storage.
|
|
244
|
+
*/
|
|
245
|
+
export interface InputRemember {
|
|
246
|
+
/** What the value is, the same in every form that asks it: `"counter-paid-by"`. */
|
|
247
|
+
key: string;
|
|
248
|
+
/**
|
|
249
|
+
* Whose choice it is: the person, with the company they work in when one person serves several (`"8|3"`). Without a
|
|
250
|
+
* viewer nothing is read or kept; `preferred` still applies.
|
|
251
|
+
*/
|
|
252
|
+
viewer?: string | null;
|
|
253
|
+
/** The company's default, from a setting the page reads: it wins over the remembered choice while it is offered. */
|
|
254
|
+
preferred?: unknown;
|
|
255
|
+
/**
|
|
256
|
+
* Keeps one value per value of another input: a package per group, since a package is one of its group's. Kept under
|
|
257
|
+
* `nx-input:{key}-{that value}:{viewer}`; while the other input holds no single value nothing is read or kept. Read
|
|
258
|
+
* among the values the form opens with and saves (an input in rows: its row's). A narrowed input (`narrowedBy`) is
|
|
259
|
+
* kept per the input that narrows it, so another group never forgets this one's.
|
|
260
|
+
*
|
|
261
|
+
* @example per: "subscriptionGroupId"
|
|
262
|
+
*/
|
|
263
|
+
per?: string;
|
|
264
|
+
/**
|
|
265
|
+
* Remembers only while these hold on the values the form opens with and the values it saves (an input in rows: its
|
|
266
|
+
* row's): no opening value, no `preferred`, nothing kept otherwise. A CRUD's Edit form holds the record's key (`id`)
|
|
267
|
+
* and its Add form does not, so `{ name: "id", filled: false }` remembers on Add only: Edit opens on the record's own
|
|
268
|
+
* values and a save there keeps nothing.
|
|
269
|
+
*
|
|
270
|
+
* @example when: { name: "id", filled: false }
|
|
271
|
+
*/
|
|
272
|
+
when?: InputCondition | readonly InputCondition[];
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* One condition of an input's `showWhen`, on another input's value. Values are compared as text, so `1` matches a
|
|
276
|
+
* value of `"1"`; a list value (a multiple choice) matches `in` when any of its items is listed. Every key given must hold.
|
|
277
|
+
*/
|
|
278
|
+
export interface InputCondition {
|
|
279
|
+
/** The other input's name: `"counterType"`. */
|
|
280
|
+
name: string;
|
|
281
|
+
/** Holds while the value is one of these: `in: [0, 1]`. */
|
|
282
|
+
in?: readonly unknown[];
|
|
283
|
+
/** Holds while the value is none of these. */
|
|
284
|
+
notIn?: readonly unknown[];
|
|
285
|
+
/** `true`: holds while it has a value; `false`: while it is empty (null, a blank text, an empty list). `false` and `0` are values. */
|
|
286
|
+
filled?: boolean;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* What narrows an input's options (`narrowedBy`): another input's value (`name`), or a value the page knows when the
|
|
290
|
+
* form opens (`value`, with the options' `key`): the Packages of the subscriber's own group.
|
|
291
|
+
*/
|
|
292
|
+
export type InputNarrowing = InputNarrowingByInput | InputNarrowingByValue;
|
|
293
|
+
/** Options narrowed by another input of the same form: its value, as it changes. */
|
|
294
|
+
export interface InputNarrowingByInput {
|
|
295
|
+
/** The other input's name: `"subscriptionGroupId"`. */
|
|
296
|
+
name: string;
|
|
297
|
+
/** The options' field that must hold the other input's value. Default `name`. */
|
|
298
|
+
key?: string;
|
|
299
|
+
/** What the box says while the other input is empty. Default "Pick a {other input's label} first". */
|
|
300
|
+
placeholder?: string;
|
|
301
|
+
/** Also offers the options whose `key` field is empty: a fee of every group beside the group's own. */
|
|
302
|
+
orUnset?: boolean;
|
|
303
|
+
value?: never;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Options narrowed by a fixed value: the records whose `key` field holds `value`. Nothing is followed and nothing is
|
|
307
|
+
* cleared; a `null` value offers nothing (or, with `orUnset`, only the options whose field is empty). A getter on
|
|
308
|
+
* `narrowedBy` may answer for the form as it is now.
|
|
309
|
+
*
|
|
310
|
+
* @example narrowedBy: { key: "subscriptionGroupId", value: subscriber.subscriptionGroupId }
|
|
311
|
+
*/
|
|
312
|
+
export interface InputNarrowingByValue {
|
|
313
|
+
/** The value the options' `key` field must hold. */
|
|
314
|
+
value: OptionValue | null;
|
|
315
|
+
/** The options' field that must hold it. */
|
|
316
|
+
key: string;
|
|
317
|
+
/** What the box says while `value` is null. Default: the input's own placeholder. */
|
|
318
|
+
placeholder?: string;
|
|
319
|
+
/** Also offers the options whose `key` field is empty: a fee of every group beside the group's own. */
|
|
320
|
+
orUnset?: boolean;
|
|
321
|
+
name?: never;
|
|
46
322
|
}
|
|
47
323
|
/** What every input's params have. */
|
|
48
|
-
export interface InputParamsBase<TType extends
|
|
49
|
-
/** Which input:
|
|
324
|
+
export interface InputParamsBase<TType extends EInputTypes = EInputTypes> extends InputLayoutParams {
|
|
325
|
+
/** Which input: `EInputTypes.Text`, `EInputTypes.Dropdown`, `EInputTypes.Tabular`, ... */
|
|
50
326
|
type: TType;
|
|
51
327
|
/** The key of the value in the form's data, and in each row of a tabular or group form. */
|
|
52
328
|
name: string;
|
|
@@ -59,41 +335,47 @@ type FormOwnedProps = "type" | "name" | "ref" | "value" | "checked";
|
|
|
59
335
|
/** Omit that keeps each member of a union apart, so a dropdown's `multiple: true` still decides its value's type. */
|
|
60
336
|
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
61
337
|
/** An input's params: its component's props without the ones the form owns, plus the base keys. */
|
|
62
|
-
export type InputParamsOf<TType extends
|
|
63
|
-
/** `TextBox`:
|
|
64
|
-
export type TextInputParams = InputParamsOf<
|
|
65
|
-
export type TextAreaParams = InputParamsOf<
|
|
66
|
-
export type NumberInputParams = InputParamsOf<
|
|
338
|
+
export type InputParamsOf<TType extends EInputTypes, TProps> = DistributiveOmit<TProps, FormOwnedProps> & InputParamsBase<TType>;
|
|
339
|
+
/** `TextBox`: `Text`, `Email`, `Password`, `Search`, `Tel` or `Url`, which also decides its HTML type. */
|
|
340
|
+
export type TextInputParams = InputParamsOf<TextInputType, TextBoxProps>;
|
|
341
|
+
export type TextAreaParams = InputParamsOf<typeof EInputTypes.TextArea, TextAreaProps>;
|
|
342
|
+
export type NumberInputParams = InputParamsOf<typeof EInputTypes.Number, NumberBoxProps>;
|
|
67
343
|
/** `CodeBox`: a box per character. The value is the code's characters, without separators. */
|
|
68
|
-
export type CodeInputParams = InputParamsOf<
|
|
344
|
+
export type CodeInputParams = InputParamsOf<typeof EInputTypes.Code, CodeBoxProps>;
|
|
69
345
|
/** A checkbox: `true` or `false`. `defaultChecked` is the value of a new row. */
|
|
70
|
-
export type CheckBoxParams = InputParamsOf<
|
|
71
|
-
export type SwitchParams = InputParamsOf<
|
|
346
|
+
export type CheckBoxParams = InputParamsOf<typeof EInputTypes.CheckBox, CheckBoxProps & CheckBoxOnlyProps>;
|
|
347
|
+
export type SwitchParams = InputParamsOf<typeof EInputTypes.Switch, CheckBoxProps>;
|
|
72
348
|
/**
|
|
73
349
|
* One radio button, on or off. In a tabular or group form only one row can have it on, like a primary address: turning
|
|
74
350
|
* it on in a row turns it off in the others. `required` there means one row must have it.
|
|
75
351
|
*/
|
|
76
|
-
export type RadioParams = InputParamsOf<
|
|
77
|
-
export type CheckBoxGroupParams = InputParamsOf<
|
|
78
|
-
export type RadioGroupParams = InputParamsOf<
|
|
352
|
+
export type RadioParams = InputParamsOf<typeof EInputTypes.Radio, CheckBoxProps>;
|
|
353
|
+
export type CheckBoxGroupParams = InputParamsOf<typeof EInputTypes.CheckBoxGroup, CheckBoxGroupProps<OptionValue>>;
|
|
354
|
+
export type RadioGroupParams = InputParamsOf<typeof EInputTypes.RadioGroup, RadioGroupProps<OptionValue>>;
|
|
79
355
|
/** A list of params holds dropdowns of many item types (countries, staff), so items are `any` here: typed columns and callbacks fit. */
|
|
80
|
-
export type DropdownParams = InputParamsOf<
|
|
81
|
-
export type DateParams = InputParamsOf<
|
|
82
|
-
export type DateRangeParams = InputParamsOf<
|
|
83
|
-
export type TimeParams = InputParamsOf<
|
|
84
|
-
export type DateTimeParams = InputParamsOf<
|
|
85
|
-
export type SliderParams = InputParamsOf<
|
|
86
|
-
export type RangeSliderParams = InputParamsOf<
|
|
87
|
-
export type MarkdownParams = InputParamsOf<
|
|
356
|
+
export type DropdownParams = InputParamsOf<typeof EInputTypes.Dropdown, DropdownProps<any, OptionValue>>;
|
|
357
|
+
export type DateParams = InputParamsOf<typeof EInputTypes.Date, DatePickerProps>;
|
|
358
|
+
export type DateRangeParams = InputParamsOf<typeof EInputTypes.DateRange, DateRangePickerProps>;
|
|
359
|
+
export type TimeParams = InputParamsOf<typeof EInputTypes.Time, TimePickerProps>;
|
|
360
|
+
export type DateTimeParams = InputParamsOf<typeof EInputTypes.DateTime, DateTimePickerProps>;
|
|
361
|
+
export type SliderParams = InputParamsOf<typeof EInputTypes.Slider, SliderProps>;
|
|
362
|
+
export type RangeSliderParams = InputParamsOf<typeof EInputTypes.RangeSlider, RangeSliderProps>;
|
|
363
|
+
export type MarkdownParams = InputParamsOf<typeof EInputTypes.Markdown, MarkdownEditorProps>;
|
|
88
364
|
/** `RichTextEditor`: the value is sanitized HTML. */
|
|
89
|
-
export type RichTextParams = InputParamsOf<
|
|
90
|
-
export type TreeParams = InputParamsOf<
|
|
365
|
+
export type RichTextParams = InputParamsOf<typeof EInputTypes.RichText, RichTextEditorProps>;
|
|
366
|
+
export type TreeParams = InputParamsOf<typeof EInputTypes.Tree, TreeViewProps<any>>;
|
|
91
367
|
/** `TabularForm`: rows of inputs in a table. The value is an array of row objects. */
|
|
92
|
-
export type TabularParams = InputParamsOf<
|
|
368
|
+
export type TabularParams = InputParamsOf<typeof EInputTypes.Tabular, TabularFormProps>;
|
|
93
369
|
/** `GroupForm`: the same rows, each as a section of inputs. */
|
|
94
|
-
export type GroupParams = InputParamsOf<
|
|
370
|
+
export type GroupParams = InputParamsOf<typeof EInputTypes.Group, GroupFormProps>;
|
|
371
|
+
/**
|
|
372
|
+
* `PictureInput`: the owner's main picture, changed at once through the file backend. It holds no value, so the form
|
|
373
|
+
* sends nothing for it. `ownerId` is the record's id (null for a record not saved yet).
|
|
374
|
+
*/
|
|
375
|
+
export type PictureParams = InputParamsOf<typeof EInputTypes.Picture, PictureInputProps>;
|
|
95
376
|
/** The params of any input: the root type every form takes as `inputs`. */
|
|
96
|
-
export type InputParams = TextInputParams | TextAreaParams | NumberInputParams | CodeInputParams | CheckBoxParams | SwitchParams | RadioParams | CheckBoxGroupParams | RadioGroupParams | DropdownParams | DateParams | DateRangeParams | TimeParams | DateTimeParams | SliderParams | RangeSliderParams | MarkdownParams | RichTextParams | TreeParams | TabularParams | GroupParams;
|
|
377
|
+
export type InputParams = TextInputParams | TextAreaParams | NumberInputParams | CodeInputParams | CheckBoxParams | SwitchParams | RadioParams | CheckBoxGroupParams | RadioGroupParams | DropdownParams | DateParams | DateRangeParams | TimeParams | DateTimeParams | SliderParams | RangeSliderParams | MarkdownParams | RichTextParams | TreeParams | TabularParams | PictureParams | GroupParams;
|
|
378
|
+
/** The `type` of any input: a member of `EInputTypes`. */
|
|
97
379
|
export type InputType = InputParams["type"];
|
|
98
380
|
/** Settings a check needs to write its message the way the field shows values: the user's date and time settings. */
|
|
99
381
|
export interface InputValidationContext {
|
|
@@ -316,6 +598,14 @@ export interface FormProps<TValues extends FormValues = FormValues> {
|
|
|
316
598
|
* nothing typed is taken (even with `disabled` taken off by hand) until it ends. A submit waits too.
|
|
317
599
|
*/
|
|
318
600
|
loading?: boolean;
|
|
601
|
+
/**
|
|
602
|
+
* What the page knows beside the values, for the inputs worked out from it (`compute`): a server's preview answer, a
|
|
603
|
+
* record the form was opened for. Every computed input is worked out again when it changes, compared by identity, so
|
|
604
|
+
* pass state or a `useMemo` value - never an object made afresh on every draw, which would work them out on every draw.
|
|
605
|
+
*
|
|
606
|
+
* @example <ApiForm computeWith={line} inputs={[..., { type: EInputTypes.Number, name: "unitPrice", label: "Rate", decimals: 2, compute: { from: [], value: (_values, line) => line?.unitPrice } }]} />
|
|
607
|
+
*/
|
|
608
|
+
computeWith?: unknown;
|
|
319
609
|
id?: string;
|
|
320
610
|
className?: string;
|
|
321
611
|
style?: CSSProperties;
|
|
@@ -354,6 +644,60 @@ export type FormButtonsPlacement = "end" | "start" | false;
|
|
|
354
644
|
* spinner already says it is working, and every field is in view with its own message under it.
|
|
355
645
|
*/
|
|
356
646
|
export type FormStatusLineMode = "full" | "errors";
|
|
647
|
+
/**
|
|
648
|
+
* The fill of a form's submit button: `primary`, or `danger` for a save that removes something ("Remove", "Turn two-step
|
|
649
|
+
* off", "Make new codes"), filled with the danger colour and its light text as every solid danger fill is.
|
|
650
|
+
*/
|
|
651
|
+
export type FormSubmitTone = "primary" | "danger";
|
|
652
|
+
/**
|
|
653
|
+
* What a form's question before saving shows in full (`FormConfirmParams.details`): a record and its fields, drawn as a
|
|
654
|
+
* record's details are (`CrudDetails`), in a section under a heading for each run of neighbouring columns with one
|
|
655
|
+
* `group`, each value in its column's format.
|
|
656
|
+
*/
|
|
657
|
+
export interface FormConfirmDetails<TRow extends object = any> {
|
|
658
|
+
/** What will be saved, as a record: the body that will be sent, the values, or the server's preview of them. */
|
|
659
|
+
row: TRow;
|
|
660
|
+
/** Its fields, as a table's columns describe them. `group` names the section a field is in. */
|
|
661
|
+
columns: readonly TableColumn<TRow>[];
|
|
662
|
+
/** The currency of a `money` field without its own ("Rs."). */
|
|
663
|
+
currency?: string | null;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* A question before a form saves: what will be saved, in full, then Confirm or Back (`SubmitFormProps.confirm`, and so
|
|
667
|
+
* `ApiForm`'s and a CRUD's Add and Edit forms'). It is asked after every value has passed its checks and before the save
|
|
668
|
+
* starts. While it is open nothing else submits the form (Save, Enter, Ctrl+S, `form.submit()`) and the form does not say
|
|
669
|
+
* "Saving…". Back, Esc and the close button send nothing and leave the form as it was, the focus back where it was;
|
|
670
|
+
* Confirm saves. A click outside the question leaves it open. A rule here that throws shows its message under the form
|
|
671
|
+
* and asks nothing.
|
|
672
|
+
*
|
|
673
|
+
* `body` is what will be sent: an `ApiForm`'s `transform` answer (worked out once, before the question, and sent as it
|
|
674
|
+
* is, so the question never shows figures of another body), or the values in a `SubmitForm`.
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* confirm={{
|
|
678
|
+
* title: "Place this order?",
|
|
679
|
+
* message: (_values, body) => `Rs. ${(body as Order).total} for ${(body as Order).item}.`,
|
|
680
|
+
* details: (_values, body) => ({ row: body as Order, columns: ORDER_COLUMNS, currency: "Rs." }),
|
|
681
|
+
* confirmLabel: "Place order",
|
|
682
|
+
* size: "xl",
|
|
683
|
+
* }}
|
|
684
|
+
*/
|
|
685
|
+
export interface FormConfirmParams<TValues extends FormValues = FormValues> {
|
|
686
|
+
/** Ask only when this holds, e.g. for the kinds that take money. Default: before every save. */
|
|
687
|
+
when?: (values: TValues) => boolean;
|
|
688
|
+
/** The question: "Save this payment?". Text, or from the values and the body. */
|
|
689
|
+
title: ReactNode | ((values: TValues, body: unknown) => ReactNode);
|
|
690
|
+
/** One line under it, in the box's muted text. */
|
|
691
|
+
message?: ReactNode | ((values: TValues, body: unknown) => ReactNode);
|
|
692
|
+
/** What will be saved, in full, drawn as a record's details: a section per run of columns with one `group`. `null` draws none. */
|
|
693
|
+
details?: (values: TValues, body: unknown) => FormConfirmDetails | null | undefined;
|
|
694
|
+
/** The button that saves. Default "Confirm and save". It has the focus, so Enter saves. */
|
|
695
|
+
confirmLabel?: ReactNode | ((values: TValues, body: unknown) => ReactNode);
|
|
696
|
+
/** The button that goes back to the form. Default "Back to the form". */
|
|
697
|
+
cancelLabel?: ReactNode;
|
|
698
|
+
/** The question's width. Default `lg` with details (two fields to a line), else `sm`; `xl` gives three to a line. */
|
|
699
|
+
size?: PopupSize;
|
|
700
|
+
}
|
|
357
701
|
/** Props of `SubmitForm`: a `Form` in a `<form>`, checked on submit, with Save, Cancel, and Reset. */
|
|
358
702
|
export interface SubmitFormProps<TValues extends FormValues = FormValues> extends FormProps<TValues> {
|
|
359
703
|
/**
|
|
@@ -372,6 +716,8 @@ export interface SubmitFormProps<TValues extends FormValues = FormValues> extend
|
|
|
372
716
|
submitLabel?: ReactNode;
|
|
373
717
|
/** Default: a floppy disk. `null` shows none. */
|
|
374
718
|
submitIcon?: ReactNode | null;
|
|
719
|
+
/** Default `primary`; `danger` for a save that removes something, as in a step that confirms it with a password. */
|
|
720
|
+
submitTone?: FormSubmitTone;
|
|
375
721
|
/** Default "Cancel". */
|
|
376
722
|
cancelLabel?: ReactNode;
|
|
377
723
|
/** Shows a Reset button, which brings back the values the form opened with (or saved last). */
|
|
@@ -386,9 +732,20 @@ export interface SubmitFormProps<TValues extends FormValues = FormValues> extend
|
|
|
386
732
|
statusLine?: FormStatusLineMode;
|
|
387
733
|
/** Asks before the page closes or reloads with unsaved changes. Default: true without a `draftKey` (a draft keeps them). */
|
|
388
734
|
confirmLeave?: boolean;
|
|
735
|
+
/**
|
|
736
|
+
* Asks before it saves, showing what will be saved in full, then Confirm or Back (`FormConfirmParams`): after every
|
|
737
|
+
* value has passed its checks, before "Saving…". Default: saves at once.
|
|
738
|
+
*/
|
|
739
|
+
confirm?: FormConfirmParams<TValues>;
|
|
389
740
|
/** Name for screen readers. */
|
|
390
741
|
"aria-label"?: string;
|
|
391
742
|
"aria-labelledby"?: string;
|
|
743
|
+
/**
|
|
744
|
+
* What the form does, for screen readers: the id of the line that says it ("Google Authenticator will stop giving
|
|
745
|
+
* codes"), heard after the form's name, so a step that puts the focus straight into its password box still says what
|
|
746
|
+
* goes.
|
|
747
|
+
*/
|
|
748
|
+
"aria-describedby"?: string;
|
|
392
749
|
}
|
|
393
750
|
/** A request an API form sends to a URL outside the app's modules, through the app's `api`. A module's API is an endpoint. */
|
|
394
751
|
export interface ApiRequest {
|
|
@@ -410,7 +767,11 @@ export interface ApiFormProps<TValues extends FormValues = FormValues> extends O
|
|
|
410
767
|
* with a client of the app's own. Endpoints and requests go through `api`, with the app's headers and sign-in handling.
|
|
411
768
|
*/
|
|
412
769
|
api: ApiRequest | ApiEndpoint | ApiSend<TValues>;
|
|
413
|
-
/**
|
|
770
|
+
/**
|
|
771
|
+
* Shapes what is sent, e.g. `{ items: [values] }` for a batch endpoint. May be async. Default: the values. With
|
|
772
|
+
* `confirm`, it runs once, before the question, which is handed what it made (`body`); that is then sent as it is. A
|
|
773
|
+
* throw shows its message under the form.
|
|
774
|
+
*/
|
|
414
775
|
transform?: (values: TValues, context: FormSubmitContext<TValues>) => unknown;
|
|
415
776
|
/**
|
|
416
777
|
* Reads the server's answer. Default: the app's reader (`configureApi({ read })`), else `readApiResponse`: Nexus API
|
|
@@ -1 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Input params: one plain object per input, `{ type, name, ...props }`. The same object renders the input
|
|
3
|
+
* (`renderInput`), validates its value without a browser (`validateInput`), and describes a row of a tabular or group
|
|
4
|
+
* form. Forms (Form, SubmitForm, ApiForm, and Crud) take a list of them: `inputs: InputParams[]`.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Which input a params object describes: `{ type: EInputTypes.Dropdown, name: "country", ... }`. A number, so a
|
|
8
|
+
* database, a generated page and a C# enum hold the same value.
|
|
9
|
+
*
|
|
10
|
+
* The numbers come in blocks by kind, each with room to grow. 0 is never a kind, so a missing value is never a valid
|
|
11
|
+
* one; a released number is never reused or given a new meaning.
|
|
12
|
+
*
|
|
13
|
+
* | Block | Kind | Members |
|
|
14
|
+
* |---|---|---|
|
|
15
|
+
* | 1–19 | Text boxes | Text 1, Email 2, Password 3, Search 4, Tel 5, Url 6, TextArea 7, Code 8 |
|
|
16
|
+
* | 20–39 | Choices | CheckBox 20, Switch 21, Radio 22, CheckBoxGroup 23, RadioGroup 24, Dropdown 25 |
|
|
17
|
+
* | 40–59 | Dates and times | Date 40, DateRange 41, Time 42, DateTime 43 |
|
|
18
|
+
* | 60–79 | Numbers and sliders | Number 60, Slider 61, RangeSlider 62 |
|
|
19
|
+
* | 80–99 | Editors | Markdown 80, RichText 81 |
|
|
20
|
+
* | 100–119 | Structures | Tree 100, Tabular 101, Group 102, Picture 103 |
|
|
21
|
+
*
|
|
22
|
+
* A dropdown is one member: `multiple` decides whether it picks one item or many. Every number fits a byte.
|
|
23
|
+
*/
|
|
24
|
+
export const EInputTypes = {
|
|
25
|
+
/** `TextBox`, a single line. */
|
|
26
|
+
Text: 1,
|
|
27
|
+
/** `TextBox` of HTML type email. */
|
|
28
|
+
Email: 2,
|
|
29
|
+
/** `TextBox` of HTML type password, with a button that shows what was typed. */
|
|
30
|
+
Password: 3,
|
|
31
|
+
/** `TextBox` of HTML type search. */
|
|
32
|
+
Search: 4,
|
|
33
|
+
/** `TextBox` of HTML type tel: a phone number. */
|
|
34
|
+
Tel: 5,
|
|
35
|
+
/** `TextBox` of HTML type url. */
|
|
36
|
+
Url: 6,
|
|
37
|
+
/** `TextArea`: several lines of plain text. */
|
|
38
|
+
TextArea: 7,
|
|
39
|
+
/** `CodeBox`: a box per character, such as a one-time code. */
|
|
40
|
+
Code: 8,
|
|
41
|
+
/** `CheckBox`: `true` or `false`. */
|
|
42
|
+
CheckBox: 20,
|
|
43
|
+
/** `Switch`: `true` or `false`, for a setting that takes effect. */
|
|
44
|
+
Switch: 21,
|
|
45
|
+
/** `RadioButton`: one radio, on or off; in rows, only one row has it on. */
|
|
46
|
+
Radio: 22,
|
|
47
|
+
/** `CheckBoxGroup`: any number of options. */
|
|
48
|
+
CheckBoxGroup: 23,
|
|
49
|
+
/** `RadioGroup`: one option of a few, all in view. */
|
|
50
|
+
RadioGroup: 24,
|
|
51
|
+
/** `Dropdown`: one item, or many with `multiple`. */
|
|
52
|
+
Dropdown: 25,
|
|
53
|
+
/** `DatePicker`: an ISO date. */
|
|
54
|
+
Date: 40,
|
|
55
|
+
/** `DateRangePicker`: two ISO dates. */
|
|
56
|
+
DateRange: 41,
|
|
57
|
+
/** `TimePicker`: an ISO time. */
|
|
58
|
+
Time: 42,
|
|
59
|
+
/** `DateTimePicker`: a UTC moment shown in the user's zone. */
|
|
60
|
+
DateTime: 43,
|
|
61
|
+
/** `NumberBox`. */
|
|
62
|
+
Number: 60,
|
|
63
|
+
/** `Slider`: one number on a track. */
|
|
64
|
+
Slider: 61,
|
|
65
|
+
/** `RangeSlider`: two numbers on a track. */
|
|
66
|
+
RangeSlider: 62,
|
|
67
|
+
/** `MarkdownEditor`: Markdown text. */
|
|
68
|
+
Markdown: 80,
|
|
69
|
+
/** `RichTextEditor`: sanitized HTML. */
|
|
70
|
+
RichText: 81,
|
|
71
|
+
/** `TreeView`: nodes picked in a tree. */
|
|
72
|
+
Tree: 100,
|
|
73
|
+
/** `TabularForm`: rows of inputs in a table. */
|
|
74
|
+
Tabular: 101,
|
|
75
|
+
/** `GroupForm`: the same rows, each as a section of inputs. */
|
|
76
|
+
Group: 102,
|
|
77
|
+
/** `PictureInput`: a record's picture, kept by the file backend as one of the owner's files, not a value of the form. */
|
|
78
|
+
Picture: 103,
|
|
79
|
+
};
|
|
@@ -127,8 +127,10 @@ export type DropdownItem = object;
|
|
|
127
127
|
* - `avatar`: a round photo from a URL; the initials of the item's label when there is no URL or it fails to load.
|
|
128
128
|
* - `image`: a small picture from a URL, 4:3, such as a flag or a logo.
|
|
129
129
|
* - `icon`: a Tabler icon by name, such as `"wifi"`.
|
|
130
|
+
* - `money`: a figure with 2 places in the person's number format (1,00,000.00 under lakh and crore), at the end of the
|
|
131
|
+
* column in even digits, such as a fee's price beside its name; empty for a missing one.
|
|
130
132
|
*/
|
|
131
|
-
export type DropdownColumnType = "text" | "muted" | "code" | "avatar" | "image" | "icon";
|
|
133
|
+
export type DropdownColumnType = "text" | "muted" | "code" | "avatar" | "image" | "icon" | "money";
|
|
132
134
|
/** One column of a dropdown's rows. Plain values, so a list of fields can describe it; `render` is the only function. */
|
|
133
135
|
export interface DropdownColumn<TItem extends DropdownItem = DropdownItem> {
|
|
134
136
|
/** The item's field, e.g. `"username"`. */
|
|
@@ -137,9 +139,9 @@ export interface DropdownColumn<TItem extends DropdownItem = DropdownItem> {
|
|
|
137
139
|
header?: string;
|
|
138
140
|
/** Default `text`. */
|
|
139
141
|
type?: DropdownColumnType;
|
|
140
|
-
/** A CSS grid track: `"6rem"`, `"auto"`, `"1fr"`. Default: `auto` for avatar, image, icon, and
|
|
142
|
+
/** A CSS grid track: `"6rem"`, `"auto"`, `"1fr"`. Default: `auto` for avatar, image, icon, code, and money; `minmax(0, 1fr)` for text. */
|
|
141
143
|
width?: string;
|
|
142
|
-
/** Default `start`. */
|
|
144
|
+
/** Default `start`; `end` for `money`. */
|
|
143
145
|
align?: "start" | "center" | "end";
|
|
144
146
|
/** Draws the cell instead of `type`. */
|
|
145
147
|
render?: (item: TItem) => ReactNode;
|
|
@@ -154,6 +156,16 @@ export interface DropdownPageRequest {
|
|
|
154
156
|
skip: number;
|
|
155
157
|
/** Aborted when a newer search replaces this request, or the dropdown unmounts. */
|
|
156
158
|
signal: AbortSignal;
|
|
159
|
+
/**
|
|
160
|
+
* Only the options whose `key` field holds `value`: set by a form for an input another narrows (`narrowedBy`). A source
|
|
161
|
+
* of its own should honour it (`crudPicker` does); a static list is narrowed by the form itself. `orUnset`: the options
|
|
162
|
+
* whose `key` field is empty are offered too (`narrowedBy.orUnset`), and a `null` value then offers only those.
|
|
163
|
+
*/
|
|
164
|
+
narrow?: {
|
|
165
|
+
key: string;
|
|
166
|
+
value: OptionValue | null;
|
|
167
|
+
orUnset?: boolean;
|
|
168
|
+
};
|
|
157
169
|
}
|
|
158
170
|
/** What `loadOptions` returns for one page. */
|
|
159
171
|
export interface DropdownPage<TItem extends DropdownItem = DropdownItem> {
|