nexus-shared 2.0.0 → 3.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.
Files changed (176) hide show
  1. package/CHANGELOG.md +446 -0
  2. package/LICENSE +21 -21
  3. package/README.md +1084 -903
  4. package/dist/Client.Index.d.ts +14 -0
  5. package/dist/Client.Index.js +14 -0
  6. package/dist/Components/Chats/Chat.d.ts +28 -0
  7. package/dist/Components/Chats/Chat.js +19 -0
  8. package/dist/Components/Chats/ChatButton.d.ts +26 -0
  9. package/dist/Components/Chats/ChatButton.js +41 -0
  10. package/dist/Components/Chats/ChatComposer.d.ts +41 -0
  11. package/dist/Components/Chats/ChatComposer.js +177 -0
  12. package/dist/Components/Chats/ChatConversations.d.ts +35 -0
  13. package/dist/Components/Chats/ChatConversations.js +38 -0
  14. package/dist/Components/Chats/ChatPanel.d.ts +66 -0
  15. package/dist/Components/Chats/ChatPanel.js +94 -0
  16. package/dist/Components/Chats/ChatParts.d.ts +87 -0
  17. package/dist/Components/Chats/ChatParts.js +96 -0
  18. package/dist/Components/Chats/ChatThread.d.ts +22 -0
  19. package/dist/Components/Chats/ChatThread.js +96 -0
  20. package/dist/Components/Documents/Button.d.ts +6 -1
  21. package/dist/Components/Documents/Button.js +2 -2
  22. package/dist/Components/Documents/Menu.js +44 -28
  23. package/dist/Components/Documents/SplitButton.d.ts +3 -1
  24. package/dist/Components/Documents/SplitButton.js +12 -8
  25. package/dist/Components/Documents/TabButtons.d.ts +29 -8
  26. package/dist/Components/Documents/TabButtons.js +24 -5
  27. package/dist/Components/Forms/ApiForm.d.ts +6 -4
  28. package/dist/Components/Forms/ApiForm.js +15 -14
  29. package/dist/Components/Forms/Crud.js +342 -71
  30. package/dist/Components/Forms/CrudParts.d.ts +5 -1
  31. package/dist/Components/Forms/CrudParts.js +37 -5
  32. package/dist/Components/Forms/ExcelImport.d.ts +58 -0
  33. package/dist/Components/Forms/ExcelImport.js +319 -0
  34. package/dist/Components/Forms/Form.js +9 -13
  35. package/dist/Components/Forms/SubmitForm.d.ts +11 -2
  36. package/dist/Components/Forms/SubmitForm.js +23 -12
  37. package/dist/Components/Inputs/DateTimePicker.js +2 -1
  38. package/dist/Components/Inputs/DropdownParts.d.ts +1 -1
  39. package/dist/Components/Inputs/DropdownParts.js +4 -6
  40. package/dist/Components/Inputs/GroupForm.js +4 -2
  41. package/dist/Components/Inputs/InputRenderer.d.ts +2 -0
  42. package/dist/Components/Inputs/InputRenderer.js +1 -1
  43. package/dist/Components/Inputs/NumberBox.js +24 -2
  44. package/dist/Components/Inputs/ReadOnlyNotice.js +2 -1
  45. package/dist/Components/Inputs/RowsInput.d.ts +12 -1
  46. package/dist/Components/Inputs/RowsInput.js +55 -6
  47. package/dist/Components/Inputs/Slider.js +4 -1
  48. package/dist/Components/Inputs/TabularForm.d.ts +7 -3
  49. package/dist/Components/Inputs/TabularForm.js +117 -20
  50. package/dist/Components/Inputs/TimePanel.js +4 -3
  51. package/dist/Components/Inputs/TimePicker.js +2 -1
  52. package/dist/Components/Layouts/Popup.js +1 -4
  53. package/dist/Components/Layouts/ThemePicker.d.ts +20 -3
  54. package/dist/Components/Layouts/ThemePicker.js +77 -6
  55. package/dist/Components/Layouts/ThemeScript.d.ts +4 -2
  56. package/dist/Components/Layouts/ThemeScript.js +4 -2
  57. package/dist/Components/Layouts/ThemeSwitcher.d.ts +6 -3
  58. package/dist/Components/Layouts/ThemeSwitcher.js +39 -18
  59. package/dist/Components/Viewers/Avatar.d.ts +21 -0
  60. package/dist/Components/Viewers/Avatar.js +21 -0
  61. package/dist/Components/Viewers/DataTable.js +262 -60
  62. package/dist/Components/Viewers/DataTableColumns.d.ts +32 -0
  63. package/dist/Components/Viewers/DataTableColumns.js +200 -0
  64. package/dist/Components/Viewers/DataTableFilters.d.ts +21 -0
  65. package/dist/Components/Viewers/DataTableFilters.js +68 -0
  66. package/dist/Components/Viewers/DataTableParts.d.ts +8 -4
  67. package/dist/Components/Viewers/DataTableParts.js +24 -12
  68. package/dist/Helpers/AnimationHelpers.d.ts +11 -0
  69. package/dist/Helpers/AnimationHelpers.js +20 -2
  70. package/dist/Helpers/ApiClient.d.ts +22 -0
  71. package/dist/Helpers/ApiClient.js +4 -1
  72. package/dist/Helpers/ApiFormHelpers.d.ts +27 -4
  73. package/dist/Helpers/ApiFormHelpers.js +73 -9
  74. package/dist/Helpers/ApiModules.d.ts +0 -4
  75. package/dist/Helpers/ApiModules.js +1 -0
  76. package/dist/Helpers/ApiResponses.d.ts +14 -4
  77. package/dist/Helpers/ApiResponses.js +116 -25
  78. package/dist/Helpers/ApiRoutes.d.ts +0 -10
  79. package/dist/Helpers/ApiRoutes.js +2 -10
  80. package/dist/Helpers/ChatBackend.d.ts +134 -0
  81. package/dist/Helpers/ChatBackend.js +332 -0
  82. package/dist/Helpers/ChatHelpers.d.ts +189 -0
  83. package/dist/Helpers/ChatHelpers.js +486 -0
  84. package/dist/Helpers/ChatHooks.d.ts +81 -0
  85. package/dist/Helpers/ChatHooks.js +175 -0
  86. package/dist/Helpers/ChatStore.d.ts +132 -0
  87. package/dist/Helpers/ChatStore.js +394 -0
  88. package/dist/Helpers/ChatSummaries.d.ts +39 -0
  89. package/dist/Helpers/ChatSummaries.js +118 -0
  90. package/dist/Helpers/CrudBackend.d.ts +54 -9
  91. package/dist/Helpers/CrudBackend.js +103 -6
  92. package/dist/Helpers/CrudHelpers.d.ts +39 -4
  93. package/dist/Helpers/CrudHelpers.js +76 -11
  94. package/dist/Helpers/ExcelBackend.d.ts +106 -0
  95. package/dist/Helpers/ExcelBackend.js +296 -0
  96. package/dist/Helpers/ExcelHelpers.d.ts +126 -0
  97. package/dist/Helpers/ExcelHelpers.js +344 -0
  98. package/dist/Helpers/FormStore.d.ts +11 -5
  99. package/dist/Helpers/FormStore.js +44 -6
  100. package/dist/Helpers/MessageBuilder.js +1 -2
  101. package/dist/Helpers/NumberHelpers.d.ts +7 -1
  102. package/dist/Helpers/NumberHelpers.js +41 -3
  103. package/dist/Helpers/PopoverHelpers.d.ts +46 -9
  104. package/dist/Helpers/PopoverHelpers.js +163 -24
  105. package/dist/Helpers/PopoverHooks.d.ts +24 -0
  106. package/dist/Helpers/PopoverHooks.js +62 -0
  107. package/dist/Helpers/RowsStore.d.ts +14 -3
  108. package/dist/Helpers/RowsStore.js +37 -3
  109. package/dist/Helpers/TableColumns.d.ts +7 -0
  110. package/dist/Helpers/TableColumns.js +41 -13
  111. package/dist/Helpers/TableExport.js +12 -12
  112. package/dist/Helpers/TableFilters.d.ts +25 -0
  113. package/dist/Helpers/TableFilters.js +162 -0
  114. package/dist/Helpers/TableHelpers.d.ts +15 -4
  115. package/dist/Helpers/TableHelpers.js +28 -4
  116. package/dist/Helpers/TableStore.d.ts +11 -3
  117. package/dist/Helpers/TableStore.js +16 -5
  118. package/dist/Helpers/ThemeHelpers.d.ts +21 -4
  119. package/dist/Helpers/ThemeHelpers.js +46 -9
  120. package/dist/Helpers/UserPreferences.d.ts +14 -0
  121. package/dist/Helpers/UserPreferences.js +99 -0
  122. package/dist/Interfaces/ApiInterfaces.d.ts +29 -4
  123. package/dist/Interfaces/ApiInterfaces.js +16 -10
  124. package/dist/Interfaces/ChatInterfaces.d.ts +240 -0
  125. package/dist/Interfaces/ChatInterfaces.js +1 -0
  126. package/dist/Interfaces/CrudInterfaces.d.ts +92 -12
  127. package/dist/Interfaces/ExcelInterfaces.d.ts +227 -0
  128. package/dist/Interfaces/ExcelInterfaces.js +45 -0
  129. package/dist/Interfaces/FormInterfaces.d.ts +85 -7
  130. package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
  131. package/dist/Interfaces/PreferenceInterfaces.d.ts +43 -0
  132. package/dist/Interfaces/PreferenceInterfaces.js +28 -0
  133. package/dist/Interfaces/TableInterfaces.d.ts +116 -9
  134. package/dist/Interfaces/ThemeInterfaces.d.ts +25 -0
  135. package/dist/Interfaces/ThemeInterfaces.js +15 -0
  136. package/dist/Services/BrowserApi.d.ts +18 -1
  137. package/dist/Services/BrowserApi.js +18 -0
  138. package/dist/Services/ChatApi.d.ts +37 -0
  139. package/dist/Services/ChatApi.js +98 -0
  140. package/dist/Services/ExcelApi.d.ts +48 -0
  141. package/dist/Services/ExcelApi.js +196 -0
  142. package/dist/Services/ServerApi.d.ts +6 -1
  143. package/dist/Services/ServerApi.js +3 -0
  144. package/dist/Services/ThemeService.d.ts +18 -2
  145. package/dist/Services/ThemeService.js +80 -16
  146. package/dist/Shared.Index.d.ts +10 -0
  147. package/dist/Shared.Index.js +10 -0
  148. package/package.json +85 -82
  149. package/src/Styles/Nexus.Avatar.css +79 -0
  150. package/src/Styles/Nexus.Base.css +117 -70
  151. package/src/Styles/Nexus.Button.css +180 -151
  152. package/src/Styles/Nexus.Calendar.css +273 -275
  153. package/src/Styles/Nexus.Chat.css +1443 -0
  154. package/src/Styles/Nexus.Choice.css +343 -345
  155. package/src/Styles/Nexus.Crud.css +168 -84
  156. package/src/Styles/Nexus.DateTime.css +81 -81
  157. package/src/Styles/Nexus.Dropdown.css +725 -774
  158. package/src/Styles/Nexus.Editor.css +295 -297
  159. package/src/Styles/Nexus.Excel.css +389 -0
  160. package/src/Styles/Nexus.Form.css +279 -298
  161. package/src/Styles/Nexus.Index.css +37 -34
  162. package/src/Styles/Nexus.Input.css +602 -637
  163. package/src/Styles/Nexus.Markdown.css +237 -265
  164. package/src/Styles/Nexus.Menu.css +237 -249
  165. package/src/Styles/Nexus.Popup.css +724 -731
  166. package/src/Styles/Nexus.RichText.css +2 -32
  167. package/src/Styles/Nexus.Rows.css +637 -566
  168. package/src/Styles/Nexus.Slider.css +276 -278
  169. package/src/Styles/Nexus.Tab.Buttons.css +432 -364
  170. package/src/Styles/Nexus.Table.css +1386 -1014
  171. package/src/Styles/Nexus.Theme.Picker.css +286 -159
  172. package/src/Styles/Nexus.Themes.css +194 -194
  173. package/src/Styles/Nexus.Time.css +144 -147
  174. package/src/Styles/Nexus.Toast.css +263 -265
  175. package/src/Styles/Nexus.Tokens.css +89 -72
  176. package/src/Styles/Nexus.Tree.css +431 -435
@@ -0,0 +1,227 @@
1
+ import type { ApiEndpoint, ApiModuleValue, AppModule } from "./ApiInterfaces.ts";
2
+ /**
3
+ * What one column of a sheet holds. It decides the cell's format in the file, the check the spreadsheet makes while
4
+ * the person types, and the input the review editors draw. The numbers are the backend's (`EExcelDataTypes`); an
5
+ * object, not an enum, so a test that strips types can import it.
6
+ */
7
+ export declare const ExcelDataType: {
8
+ /** One line of text: a name, a code. */
9
+ readonly Text: 0;
10
+ /** Many lines of text: a description, a note. */
11
+ readonly Note: 1;
12
+ /** A whole number, no decimals. */
13
+ readonly WholeNumber: 2;
14
+ /** A number with decimals. */
15
+ readonly Decimal: 3;
16
+ /** Money: decimals fixed at the backend's places. */
17
+ readonly Amount: 4;
18
+ /** A date with no time, as an ISO date: `2026-09-29`. */
19
+ readonly Date: 5;
20
+ /** A date and a time, zone-less, as `2026-09-29 14:30`. */
21
+ readonly DateTime: 6;
22
+ /** A time of day with no date: `14:30`. */
23
+ readonly Time: 7;
24
+ /** Yes or No. */
25
+ readonly YesNo: 8;
26
+ /** A choice from a list the backend owns, picked in the sheet and travelling as its key. */
27
+ readonly List: 9;
28
+ };
29
+ export type ExcelDataType = (typeof ExcelDataType)[keyof typeof ExcelDataType];
30
+ /** What one row of an upload turned out to be. The backend's `EExcelRowKinds`. */
31
+ export declare const ExcelRowKind: {
32
+ /** Nothing is wrong with it and it is not a record yet: this is what an import writes. */
33
+ readonly New: 0;
34
+ /** It is already a record, or repeats a row earlier in the file. Nothing updates it. */
35
+ readonly Duplicate: 1;
36
+ /** Something in it has to be fixed before it can be written. */
37
+ readonly Invalid: 2;
38
+ };
39
+ export type ExcelRowKind = (typeof ExcelRowKind)[keyof typeof ExcelRowKind];
40
+ /** One column of a sheet: enough to draw a heading, know what the cell holds, and check it before anything is sent. */
41
+ export interface ExcelColumnInfo {
42
+ /** The property behind it, and the key every row's values are held under: `CityName`. */
43
+ field: string;
44
+ /** The heading the person reads: "City name". */
45
+ header: string;
46
+ dataType: ExcelDataType;
47
+ /** A row with this cell empty is refused. */
48
+ isRequired?: boolean;
49
+ /** The file fills it, not the person. */
50
+ isReadOnly?: boolean;
51
+ /** It is a choice: it travels as a key with its words beside it, and is picked in the sheet. */
52
+ isReference?: boolean;
53
+ /** The list it picks from. */
54
+ list?: string | null;
55
+ minLength?: number;
56
+ maxLength?: number;
57
+ /** Least value of a number. Only a number has one. */
58
+ minimum?: number | null;
59
+ /** Most value of a number. */
60
+ maximum?: number | null;
61
+ /** What the person reads about this column, in their words. */
62
+ note?: string | null;
63
+ }
64
+ /**
65
+ * The limits and the shape of a sheet, asked for before either side is offered: so an export that will be cut short is
66
+ * said before it is written, and the editors know what they are drawing. It is the same for everybody and changes once
67
+ * a release, so it is kept.
68
+ */
69
+ export interface ExcelSettings {
70
+ /** What one row is: "City". */
71
+ subject: string;
72
+ /** What the downloaded template is called. */
73
+ templateFileName?: string;
74
+ /** Rows the template offers to fill. */
75
+ sheetRows?: number;
76
+ /** The most rows one export writes. Over this, the person is warned first. */
77
+ maxExportRows: number;
78
+ /** The most rows one import takes. */
79
+ maxImportRows: number;
80
+ /** The columns, in the order they read. */
81
+ columns: ExcelColumnInfo[];
82
+ /**
83
+ * How each column should be drawn as an input. **Empty** until the backend can describe an input, and `columns` is
84
+ * what the editors draw from until it is not.
85
+ */
86
+ inputs?: readonly unknown[];
87
+ /**
88
+ * The statuses a new record may start in when it is imported, each with its words: the import offers them as a
89
+ * choice. Empty, or absent, when the model offers none, and then no choice is shown.
90
+ */
91
+ recordStatuses?: ExcelRecordStatus[];
92
+ /** The status picked unless the person picks another: the backend's "Imported". */
93
+ defaultRecordStatus?: number;
94
+ }
95
+ /** One status an imported record may start in: the backend's number, and the words for it. */
96
+ export interface ExcelRecordStatus {
97
+ value: number;
98
+ label: string;
99
+ }
100
+ /** One thing wrong with one cell, told so the person can find it. */
101
+ export interface ExcelCellProblem {
102
+ /** The row of the sheet, the number down the side of the file. */
103
+ row: number;
104
+ /** The column, counting from 1. */
105
+ column: number;
106
+ /** The cell as a spreadsheet names it: `B7`. */
107
+ cell: string;
108
+ /** The column's heading, in the person's words. */
109
+ header: string;
110
+ /** The property behind it, which is what an editor binds to. */
111
+ field: string;
112
+ /** What to tell the person, in plain words. */
113
+ message: string;
114
+ /** The unique code of the line that found it, for a developer. */
115
+ messageCode?: string;
116
+ /** What kind of failure it is, as a number. */
117
+ errorCode?: number;
118
+ }
119
+ /** One row of the review: what it holds, what it turned out to be, and what is wrong with it. */
120
+ export interface ExcelReviewRow {
121
+ /** The row of the sheet, so the person can find it in their own file. */
122
+ number: number;
123
+ kind: ExcelRowKind;
124
+ /** What each column held, by the property's name. A choice holds its key, never a plain id. */
125
+ values: Record<string, string | null>;
126
+ /** What the person picked in each choice, for the review to show. */
127
+ labels?: Record<string, string | null>;
128
+ /** Everything wrong with the row, one per cell. */
129
+ problems?: ExcelCellProblem[];
130
+ /** Why it is a duplicate, in the person's words. */
131
+ duplicate?: string | null;
132
+ }
133
+ /** What a verify answers: the whole upload, counted and told row by row. Nothing was written. */
134
+ export interface ExcelVerifyAnswer {
135
+ /** What one row is. */
136
+ subject: string;
137
+ /** Rows that had something in them. Empty rows of the template are not counted. */
138
+ read: number;
139
+ /** Rows an import would write. */
140
+ new: number;
141
+ /** Rows that are already records, or repeat one earlier in the file. */
142
+ duplicates: number;
143
+ /** Rows with something to fix. */
144
+ invalid: number;
145
+ /** An import would write something and nothing is waiting on a fix. */
146
+ isReady: boolean;
147
+ /** The upload in one sentence, for above the review. */
148
+ note: string;
149
+ /** The columns, in the order they read. */
150
+ columns: ExcelColumnInfo[];
151
+ /** Every row that had something in it. */
152
+ rows: ExcelReviewRow[];
153
+ /** Empty until the backend can describe an input. */
154
+ inputs?: readonly unknown[];
155
+ }
156
+ /**
157
+ * One row as it travels, in text and nothing else. The same shape in both directions, which is the point: a verify
158
+ * reads it out of the file and answers with it, the person fixes what they need to, and the import sends the same rows
159
+ * back to be read again by the same code. A reference column holds its key.
160
+ */
161
+ export interface ExcelSheetRow {
162
+ /** The row of the sheet this came from, so every problem can name where it is. */
163
+ number: number;
164
+ /** What each column held, by the property's name. A reference holds its key. */
165
+ values: Record<string, string | null>;
166
+ /** What the person picked in each reference column: "Nepal (NP)". */
167
+ labels?: Record<string, string | null>;
168
+ }
169
+ /** What an import did: what it wrote, and what it did not, in the person's words. */
170
+ export interface ExcelImportAnswer {
171
+ /** Rows the request carried. */
172
+ sent: number;
173
+ /** Records written. */
174
+ created: number;
175
+ /** Rows that were already records. Nothing was updated. */
176
+ duplicates: number;
177
+ /** Rows that still had something to fix. */
178
+ invalid: number;
179
+ /** The rows nothing was written for, so the person sees which and why without asking again. */
180
+ refused?: ExcelReviewRow[];
181
+ /** What happened, in one sentence. */
182
+ note: string;
183
+ }
184
+ /**
185
+ * The endpoints of one model's spreadsheets. `excelEndpoints` builds the Nexus ones; an app on another backend writes
186
+ * its own object of the same four or five.
187
+ *
188
+ * `export` is the model's own action, because only the model knows what the person has on screen - the search, the
189
+ * filters, which list they are looking at, and whose rows they may see. Without one, no export is offered.
190
+ */
191
+ export interface ExcelEndpoints<TModule extends ApiModuleValue = AppModule> {
192
+ /** The limits and the shape of the sheet. The one answer a browser may keep. */
193
+ settings: ApiEndpoint<TModule>;
194
+ /** The template to fill, as a file. */
195
+ template: ApiEndpoint<TModule>;
196
+ /** A filled file read and judged, with nothing written. */
197
+ verify: ApiEndpoint<TModule>;
198
+ /** Writes the rows a verify answered with, as the person left them. */
199
+ import: ApiEndpoint<TModule>;
200
+ /** The model's own export, with the search on screen. */
201
+ export?: ApiEndpoint<TModule>;
202
+ }
203
+ /** What the person has on screen, as an export asks for it. The model's own filters go in `filters`. */
204
+ export interface ExcelExportQuery {
205
+ /** The search box's text. */
206
+ search?: string | null;
207
+ /** The sorted column, as the server names it. */
208
+ sort?: string | null;
209
+ /** Which way it is sorted. */
210
+ direction?: "asc" | "desc";
211
+ /** The filters the page holds, sent as they are: a list, a date range, a status. */
212
+ filters?: Record<string, unknown>;
213
+ }
214
+ /** A file an endpoint answered with, and what it says about itself. */
215
+ export interface ExcelDownload {
216
+ blob: Blob;
217
+ /** The name the answer gave it (`Content-Disposition`), else the one that was asked for. */
218
+ fileName: string;
219
+ /** Rows written into the file, when the answer says. */
220
+ rows?: number;
221
+ /** Rows asked for that did not fit under the limit. */
222
+ skipped?: number;
223
+ /** The most rows one export writes, as the backend has it. */
224
+ limit?: number;
225
+ /** What to tell the person when rows were left out. */
226
+ note?: string;
227
+ }
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Types of the spreadsheet side: what a model's sheet is made of, what a verify answers, what an import sends back, and
3
+ * the endpoints that serve all three. One shape travels both ways (`ExcelSheetRow`), so a verify and an import can
4
+ * never disagree about a row.
5
+ *
6
+ * A reference column travels as its **key** - an encrypted id made for the person who downloaded the file - with the
7
+ * words they picked beside it. The plain id in the sheet is theirs to read and is never read back, so a value copied
8
+ * out of a spreadsheet, or out of a verify answer, cannot be posted to an endpoint as an id.
9
+ */
10
+ /**
11
+ * What one column of a sheet holds. It decides the cell's format in the file, the check the spreadsheet makes while
12
+ * the person types, and the input the review editors draw. The numbers are the backend's (`EExcelDataTypes`); an
13
+ * object, not an enum, so a test that strips types can import it.
14
+ */
15
+ export const ExcelDataType = {
16
+ /** One line of text: a name, a code. */
17
+ Text: 0,
18
+ /** Many lines of text: a description, a note. */
19
+ Note: 1,
20
+ /** A whole number, no decimals. */
21
+ WholeNumber: 2,
22
+ /** A number with decimals. */
23
+ Decimal: 3,
24
+ /** Money: decimals fixed at the backend's places. */
25
+ Amount: 4,
26
+ /** A date with no time, as an ISO date: `2026-09-29`. */
27
+ Date: 5,
28
+ /** A date and a time, zone-less, as `2026-09-29 14:30`. */
29
+ DateTime: 6,
30
+ /** A time of day with no date: `14:30`. */
31
+ Time: 7,
32
+ /** Yes or No. */
33
+ YesNo: 8,
34
+ /** A choice from a list the backend owns, picked in the sheet and travelling as its key. */
35
+ List: 9,
36
+ };
37
+ /** What one row of an upload turned out to be. The backend's `EExcelRowKinds`. */
38
+ export const ExcelRowKind = {
39
+ /** Nothing is wrong with it and it is not a record yet: this is what an import writes. */
40
+ New: 0,
41
+ /** It is already a record, or repeats a row earlier in the file. Nothing updates it. */
42
+ Duplicate: 1,
43
+ /** Something in it has to be fixed before it can be written. */
44
+ Invalid: 2,
45
+ };
@@ -21,6 +21,8 @@ import type { InputDesign, OptionValue } from "./InputInterfaces.ts";
21
21
  import type { Size } from "./TypeInterfaces.ts";
22
22
  /** Where a form places an input. The input itself ignores these keys. */
23
23
  export interface InputLayoutParams {
24
+ /** Optional heading before this field when the section changes (Form only). */
25
+ section?: string;
24
26
  /** Columns the input spans in a form's 12-column grid, and in a group form's section. Default 12, the full width. */
25
27
  span?: number;
26
28
  /** The column's width in a tabular form: a CSS length ("9rem") or pixels. Default by type, e.g. 10rem for text. */
@@ -29,6 +31,17 @@ export interface InputLayoutParams {
29
31
  grow?: number;
30
32
  /** Leaves the input out, e.g. a field only some roles may see. Its value stays in the data as it was. */
31
33
  hidden?: boolean;
34
+ /**
35
+ * Other inputs of the same form this one follows (Form only). When any of their values changes, this input draws
36
+ * again — without mounting again, so nothing typed in it is lost — and its params are read afresh: a getter for
37
+ * `hidden`, `readOnly`, `readOnlyMessage`, `label`, `hint` or `options` answers for the values as they are now. Its
38
+ * own rule is checked again too, so a `required` that a getter just switched on or off counts at once. Name the
39
+ * input itself to follow its own typing (a hint that says what the typed text will become).
40
+ *
41
+ * An input that names any is asked `hidden` every time it draws, so it can come and go; one that names none is
42
+ * asked once, when the form draws.
43
+ */
44
+ dependsOn?: readonly string[];
32
45
  }
33
46
  /** What every input's params have. */
34
47
  export interface InputParamsBase<TType extends string = string> extends InputLayoutParams {
@@ -140,6 +153,42 @@ export interface RowsInputProps<TRow extends RowValue = RowValue> {
140
153
  removable?: boolean;
141
154
  /** Rows can be moved up and down. Default false. */
142
155
  sortable?: boolean;
156
+ /**
157
+ * Values a row's **position** decides rather than the user: a bitwise enum's value, a line number, a running total.
158
+ * It is asked after every change — a value typed, a row added, removed, duplicated or **moved** — with every row,
159
+ * and returns one patch per row (or nothing at all, or nothing for a row).
160
+ *
161
+ * The patches are written as if code had set them, so only the cells whose value really changed are drawn again:
162
+ * moving a row renumbers the column in front of the user and leaves focus where they put it. It runs after every
163
+ * change, so keep it quick, and pair it with `cellReadOnly` when the user must not type what it writes.
164
+ */
165
+ deriveRows?: (rows: TRow[]) => (Partial<TRow> | null | undefined)[] | null | undefined;
166
+ /**
167
+ * Which cells hold a **generated** value rather than a typed one: an enum's bitwise value, a line's total. It is
168
+ * asked for every cell as it renders, so a column can lock and unlock while the form around it changes — params are
169
+ * fixed once a form opens, and this is the one thing about a cell that is not.
170
+ *
171
+ * Return the reason to lock the cell and say why (the input wears its read-only lock and shows the note), `true` to
172
+ * lock it without a reason, or nothing to leave the cell as its params have it. Locking a cell never stops code from
173
+ * writing it: that is the point.
174
+ */
175
+ cellReadOnly?: (row: TRow, name: string, index: number) => string | boolean | null | undefined;
176
+ /**
177
+ * Rows the user did not write and may not change, such as the fields a class inherits from its parent: every cell of
178
+ * such a row is read-only with the reason returned, and in a tabular form its menu offers nothing and no row can be
179
+ * moved above it. Return the reason, `true` for no reason, or nothing. Keep such rows first: nothing reorders them.
180
+ */
181
+ rowReadOnly?: (row: TRow, index: number) => string | boolean | null | undefined;
182
+ /**
183
+ * Params of one cell that differ from its column's: a picker whose choices depend on the row, such as a type argument
184
+ * that takes a key type in one row and a model in the next. Asked as the row draws, which is when one of its values
185
+ * changes; return the keys to change (`{ options, selectedOptions }`), or nothing for the column's own params.
186
+ *
187
+ * Return the **same object** for the same answer (keep them in a map by what decides them): a new object draws the
188
+ * cell again, and a picker indexes its options once per array. Rules still come from the column's params — a row's
189
+ * value is checked the same way in every row — so change what is offered here, not what is required.
190
+ */
191
+ cellParams?: (row: TRow, name: string, index: number) => Partial<InputParams> | null | undefined;
143
192
  /** Every input is read-only, and rows cannot be added, removed, or moved. */
144
193
  readOnly?: boolean;
145
194
  /** What the rows say when someone tries to change them. Default "Read-only: these rows cannot be changed." */
@@ -164,8 +213,13 @@ export interface RowsInputHandle<TRow extends RowValue = RowValue> {
164
213
  setValue: (rows: readonly TRow[]) => void;
165
214
  /** Adds a row with these values (over `newRow`), at `index` or at the end, and focuses its first input. */
166
215
  add: (values?: Partial<TRow>, index?: number) => void;
167
- /** Removes the row at `index`. */
168
- remove: (index: number) => void;
216
+ /**
217
+ * Removes the row at `index`, with an Undo under the rows - or none with `undo: false`, for a row the code drops
218
+ * rather than the person, such as one nothing was typed in.
219
+ */
220
+ remove: (index: number, options?: {
221
+ undo?: boolean;
222
+ }) => void;
169
223
  /** Checks every row, shows every message, and focuses the first problem. Returns whether all is valid. */
170
224
  validate: () => boolean;
171
225
  /** Brings the row at `index` into view and focuses its input named `name`, or its first input. */
@@ -179,6 +233,17 @@ export interface TabularFormProps<TRow extends RowValue = RowValue> extends Rows
179
233
  maxHeight?: number | string;
180
234
  /** The row number column. Default true; it is also the button of each row's menu. */
181
235
  numbered?: boolean;
236
+ /**
237
+ * Where each row's actions sit. `"number"` (the default): a menu beside the row's number. `"end"`: a column fixed at
238
+ * the right edge, as a data table has them - Remove and Lock as buttons of their own, any other action in a menu
239
+ * after them.
240
+ */
241
+ actionsAt?: "number" | "end";
242
+ /**
243
+ * The person can lock a row, and unlock it: a locked row's inputs are read-only and it cannot be removed. The locks
244
+ * are the screen's, never part of the value - a locked row is still sent. Its button is with the row's actions.
245
+ */
246
+ lockable?: boolean;
182
247
  }
183
248
  /** `GroupForm`: each row as a section of inputs, laid out on a 12-column grid by each input's `span`. */
184
249
  export interface GroupFormProps<TRow extends RowValue = RowValue> extends RowsInputProps<TRow> {
@@ -243,7 +308,10 @@ export interface FormProps<TValues extends FormValues = FormValues> {
243
308
  readOnly?: boolean;
244
309
  /** Every input is disabled, and nothing is checked. */
245
310
  disabled?: boolean;
246
- /** Shows placeholders in place of the inputs, e.g. while the values load. */
311
+ /**
312
+ * The values are loading: the inputs show at once, disabled, so the form has its final size from the first frame, and
313
+ * nothing typed is taken (even with `disabled` taken off by hand) until it ends. A submit waits too.
314
+ */
247
315
  loading?: boolean;
248
316
  id?: string;
249
317
  className?: string;
@@ -276,6 +344,13 @@ export type FormSubmitResult<TValues extends FormValues = FormValues> = void | u
276
344
  };
277
345
  /** Where `SubmitForm` puts its buttons: at the end with the status line before them, at the start, or nowhere (`FormButtons` elsewhere). */
278
346
  export type FormButtonsPlacement = "end" | "start" | false;
347
+ /**
348
+ * What a form's status line reports. `full`: loading, saving, what needs attention, unsaved changes, and when it saved.
349
+ * `errors`: only an error that belongs to no field, such as the server's "The sign-in took too long", for a short form
350
+ * that keeps nothing (a sign-in, a search, a code to verify): "Unsaved changes" would be untrue there, the button's
351
+ * spinner already says it is working, and every field is in view with its own message under it.
352
+ */
353
+ export type FormStatusLineMode = "full" | "errors";
279
354
  /** Props of `SubmitForm`: a `Form` in a `<form>`, checked on submit, with Save, Cancel, and Reset. */
280
355
  export interface SubmitFormProps<TValues extends FormValues = FormValues> extends FormProps<TValues> {
281
356
  /**
@@ -304,6 +379,8 @@ export interface SubmitFormProps<TValues extends FormValues = FormValues> extend
304
379
  resetOnSuccess?: boolean;
305
380
  /** Where the buttons go. Default `end`. With `false`, put a `FormButtons` with the same `form` elsewhere, such as a popup's footer. */
306
381
  buttons?: FormButtonsPlacement;
382
+ /** What the status line reports. Default `full`; `errors` for a short form that keeps nothing, such as a sign-in or a search. */
383
+ statusLine?: FormStatusLineMode;
307
384
  /** Asks before the page closes or reloads with unsaved changes. Default: true without a `draftKey` (a draft keeps them). */
308
385
  confirmLeave?: boolean;
309
386
  /** Name for screen readers. */
@@ -333,8 +410,9 @@ export interface ApiFormProps<TValues extends FormValues = FormValues> extends O
333
410
  /** Shapes what is sent, e.g. `{ items: [values] }` for a batch endpoint. May be async. Default: the values. */
334
411
  transform?: (values: TValues, context: FormSubmitContext<TValues>) => unknown;
335
412
  /**
336
- * Reads the server's answer. Default `readApiResponse`: Nexus API responses (`{ isSuccess, message, inputName, result }`),
337
- * ASP.NET Core problem details (`{ title, errors }`), and any JSON by its HTTP status.
413
+ * Reads the server's answer. Default: the app's reader (`configureApi({ read })`), else `readApiResponse`: Nexus API
414
+ * responses (`{ isSuccess, message, inputName, result }`), ASP.NET Core problem details (`{ title, errors }`), and
415
+ * any JSON by its HTTP status.
338
416
  */
339
417
  readResponse?: (body: unknown, status: number | undefined) => ApiResult;
340
418
  /** After a save, once the saved values are the form's own, with what the server sent back: close a popup, update a table row. */
@@ -344,8 +422,8 @@ export interface ApiFormProps<TValues extends FormValues = FormValues> extends O
344
422
  /** The toast after a save: text, or `false` for none. Default: the server's message, else the endpoint's ("Room saved."), else "Saved." */
345
423
  successMessage?: ReactNode | false;
346
424
  /**
347
- * Loads the values to edit, e.g. a record by id. Placeholders show while it runs, and a failure offers Retry. It runs
348
- * once; give the form a new `key` to load another record.
425
+ * Loads the values to edit, e.g. a record by id. The inputs show disabled while it runs, and a failure offers Retry.
426
+ * It runs once; give the form a new `key` to load another record.
349
427
  */
350
428
  loadValue?: (signal: AbortSignal) => Promise<Partial<TValues> | null | undefined>;
351
429
  }
@@ -1,5 +1,5 @@
1
1
  /** The actions the builder knows by name (`MESSAGE_ACTION_NAMES`); `actionWords(name)` gives their words. */
2
- export type MessageActionName = "load" | "save" | "create" | "update" | "delete" | "trash" | "archive" | "restore" | "recover" | "flag" | "unflag" | "pin" | "unpin" | "send" | "submit" | "upload" | "download" | "import" | "export" | "copy" | "approve" | "reject" | "cancel";
2
+ export type MessageActionName = "load" | "save" | "create" | "update" | "delete" | "trash" | "restore" | "recover" | "flag" | "unflag" | "pin" | "unpin" | "send" | "submit" | "upload" | "download" | "import" | "export" | "copy" | "approve" | "reject" | "cancel";
3
3
  /** The words of an action. */
4
4
  export interface ActionWords {
5
5
  /** "save", or with the object inside: "move {object} to the trash". */
@@ -0,0 +1,43 @@
1
+ /** The words of the app itself. Data keeps the language it was entered in. */
2
+ export type InterfaceLanguage = "en" | "ne";
3
+ /**
4
+ * The core's `ENumberScales`, by member value: where the groups of a number fall. `International` writes 1,234,567
5
+ * (thousand, million, billion); `SouthAsian` writes 12,34,567 (thousand, lakh, crore).
6
+ */
7
+ export declare const ENumberScales: {
8
+ readonly International: 0;
9
+ readonly SouthAsian: 1;
10
+ };
11
+ export type ENumberScales = (typeof ENumberScales)[keyof typeof ENumberScales];
12
+ /**
13
+ * The mark between groups of digits: 1,200, 1.200, 1 200 (a no-break space) or 1200. A period between the groups
14
+ * takes a comma for the decimal mark, as it does in much of Europe; every other choice keeps the period.
15
+ */
16
+ export type ThousandsSeparator = "comma" | "period" | "space" | "none";
17
+ export interface UserPreferences {
18
+ /** Default `en`. */
19
+ language: InterfaceLanguage;
20
+ /** Default `International`. */
21
+ numberScale: ENumberScales;
22
+ /** Default `comma`. */
23
+ thousands: ThousandsSeparator;
24
+ /**
25
+ * The rows every table opens with, over the size the table names itself, when the table offers it; a size picked in a
26
+ * table with a `stateKey` still wins there. `null` (the default): not chosen, each table's own.
27
+ */
28
+ pageSize: number | null;
29
+ /**
30
+ * A single click on a row opens its record (`true`) or only a double-click does (`false`), in every table, over what
31
+ * the table says itself. `null` (the default): not chosen, each table's own `openOnClick`.
32
+ */
33
+ rowClick: boolean | null;
34
+ }
35
+ export declare const DEFAULT_USER_PREFERENCES: UserPreferences;
36
+ /** Local storage keys of the settings. A number scale is kept as its member value ("0", "1"), a row click as "1" or "0". */
37
+ export declare const PREFERENCE_STORAGE_KEYS: {
38
+ readonly language: "nx-language";
39
+ readonly numberScale: "nx-number-scale";
40
+ readonly thousands: "nx-thousands";
41
+ readonly pageSize: "nx-page-size";
42
+ readonly rowClick: "nx-row-click";
43
+ };
@@ -0,0 +1,28 @@
1
+ /*
2
+ * The person's own settings beyond the theme and the dates: the interface language, how numbers are written, and what a
3
+ * table does before anyone touches its own controls. Saved on this device and read by the components that show them,
4
+ * so a change applies to everything on the page at once. The date settings are `DatePreferences` (DateInterfaces.ts).
5
+ */
6
+ /**
7
+ * The core's `ENumberScales`, by member value: where the groups of a number fall. `International` writes 1,234,567
8
+ * (thousand, million, billion); `SouthAsian` writes 12,34,567 (thousand, lakh, crore).
9
+ */
10
+ export const ENumberScales = {
11
+ International: 0,
12
+ SouthAsian: 1,
13
+ };
14
+ export const DEFAULT_USER_PREFERENCES = {
15
+ language: "en",
16
+ numberScale: ENumberScales.International,
17
+ thousands: "comma",
18
+ pageSize: null,
19
+ rowClick: null,
20
+ };
21
+ /** Local storage keys of the settings. A number scale is kept as its member value ("0", "1"), a row click as "1" or "0". */
22
+ export const PREFERENCE_STORAGE_KEYS = {
23
+ language: "nx-language",
24
+ numberScale: "nx-number-scale",
25
+ thousands: "nx-thousands",
26
+ pageSize: "nx-page-size",
27
+ rowClick: "nx-row-click",
28
+ };