nexus-shared 3.0.0 → 3.2.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 (162) hide show
  1. package/CHANGELOG.md +442 -0
  2. package/README.md +1123 -1000
  3. package/dist/Client.Index.d.ts +6 -0
  4. package/dist/Client.Index.js +6 -0
  5. package/dist/Components/Chats/ChatParts.d.ts +2 -2
  6. package/dist/Components/Chats/ChatParts.js +3 -7
  7. package/dist/Components/Chats/ChatThread.js +3 -3
  8. package/dist/Components/Documents/Button.d.ts +6 -1
  9. package/dist/Components/Documents/Button.js +2 -2
  10. package/dist/Components/Documents/Menu.js +21 -9
  11. package/dist/Components/Documents/SplitButton.d.ts +3 -1
  12. package/dist/Components/Documents/SplitButton.js +7 -5
  13. package/dist/Components/Documents/TabButtons.d.ts +5 -2
  14. package/dist/Components/Documents/TabButtons.js +2 -2
  15. package/dist/Components/Forms/ApiForm.d.ts +1 -1
  16. package/dist/Components/Forms/ApiForm.js +1 -1
  17. package/dist/Components/Forms/Crud.js +486 -206
  18. package/dist/Components/Forms/CrudHistory.d.ts +66 -0
  19. package/dist/Components/Forms/CrudHistory.js +140 -0
  20. package/dist/Components/Forms/CrudParts.d.ts +5 -1
  21. package/dist/Components/Forms/CrudParts.js +38 -5
  22. package/dist/Components/Forms/ExcelImport.d.ts +23 -7
  23. package/dist/Components/Forms/ExcelImport.js +189 -60
  24. package/dist/Components/Forms/Form.js +9 -14
  25. package/dist/Components/Forms/SubmitForm.d.ts +11 -2
  26. package/dist/Components/Forms/SubmitForm.js +23 -12
  27. package/dist/Components/Inputs/CodeBox.d.ts +69 -0
  28. package/dist/Components/Inputs/CodeBox.js +316 -0
  29. package/dist/Components/Inputs/DatePicker.js +4 -1
  30. package/dist/Components/Inputs/DateRangePicker.js +4 -1
  31. package/dist/Components/Inputs/DateTimePicker.js +4 -1
  32. package/dist/Components/Inputs/Dropdown.js +4 -1
  33. package/dist/Components/Inputs/DropdownParts.d.ts +1 -1
  34. package/dist/Components/Inputs/DropdownParts.js +4 -6
  35. package/dist/Components/Inputs/InputRenderer.js +3 -0
  36. package/dist/Components/Inputs/NumberBox.js +26 -2
  37. package/dist/Components/Inputs/RowsInput.js +2 -2
  38. package/dist/Components/Inputs/Slider.js +4 -1
  39. package/dist/Components/Inputs/TabularForm.js +42 -10
  40. package/dist/Components/Inputs/TextArea.js +4 -1
  41. package/dist/Components/Inputs/TextBox.js +4 -1
  42. package/dist/Components/Inputs/TimePanel.js +4 -3
  43. package/dist/Components/Inputs/TimePicker.js +4 -1
  44. package/dist/Components/Inputs/TreeView.d.ts +55 -5
  45. package/dist/Components/Inputs/TreeView.js +372 -23
  46. package/dist/Components/Layouts/Popup.js +1 -4
  47. package/dist/Components/Layouts/ThemePicker.d.ts +21 -3
  48. package/dist/Components/Layouts/ThemePicker.js +89 -6
  49. package/dist/Components/Layouts/ThemeScript.d.ts +4 -2
  50. package/dist/Components/Layouts/ThemeScript.js +4 -2
  51. package/dist/Components/Layouts/ThemeSwitcher.d.ts +6 -3
  52. package/dist/Components/Layouts/ThemeSwitcher.js +9 -8
  53. package/dist/Components/Viewers/Avatar.d.ts +21 -0
  54. package/dist/Components/Viewers/Avatar.js +21 -0
  55. package/dist/Components/Viewers/DataTable.js +252 -62
  56. package/dist/Components/Viewers/DataTableColumns.d.ts +7 -3
  57. package/dist/Components/Viewers/DataTableColumns.js +25 -68
  58. package/dist/Components/Viewers/DataTableFilters.d.ts +21 -0
  59. package/dist/Components/Viewers/DataTableFilters.js +68 -0
  60. package/dist/Components/Viewers/DataTableParts.d.ts +28 -10
  61. package/dist/Components/Viewers/DataTableParts.js +42 -13
  62. package/dist/Helpers/AnimationHelpers.d.ts +11 -0
  63. package/dist/Helpers/AnimationHelpers.js +20 -2
  64. package/dist/Helpers/ApiModules.d.ts +3 -34
  65. package/dist/Helpers/ApiModules.js +3 -3
  66. package/dist/Helpers/ApiRoutes.d.ts +68 -26
  67. package/dist/Helpers/ApiRoutes.js +31 -27
  68. package/dist/Helpers/ArrayHelpers.d.ts +0 -0
  69. package/dist/Helpers/ArrayHelpers.js +2 -0
  70. package/dist/Helpers/CodeHelpers.d.ts +97 -0
  71. package/dist/Helpers/CodeHelpers.js +332 -0
  72. package/dist/Helpers/CrudBackend.d.ts +101 -8
  73. package/dist/Helpers/CrudBackend.js +172 -11
  74. package/dist/Helpers/CrudColumns.d.ts +51 -0
  75. package/dist/Helpers/CrudColumns.js +69 -0
  76. package/dist/Helpers/CrudHelpers.d.ts +50 -5
  77. package/dist/Helpers/CrudHelpers.js +109 -12
  78. package/dist/Helpers/ExcelBackend.d.ts +3 -3
  79. package/dist/Helpers/ExcelBackend.js +8 -2
  80. package/dist/Helpers/ExcelHelpers.d.ts +36 -8
  81. package/dist/Helpers/ExcelHelpers.js +55 -13
  82. package/dist/Helpers/FormStore.d.ts +9 -5
  83. package/dist/Helpers/FormStore.js +25 -5
  84. package/dist/Helpers/InputParamsHelpers.js +8 -0
  85. package/dist/Helpers/MessageBuilder.js +3 -0
  86. package/dist/Helpers/NexusHelpers.d.ts +0 -0
  87. package/dist/Helpers/NexusHelpers.js +1 -0
  88. package/dist/Helpers/NumberHelpers.d.ts +7 -1
  89. package/dist/Helpers/NumberHelpers.js +41 -3
  90. package/dist/Helpers/PopoverHelpers.d.ts +17 -5
  91. package/dist/Helpers/PopoverHelpers.js +52 -17
  92. package/dist/Helpers/PopoverHooks.d.ts +24 -0
  93. package/dist/Helpers/PopoverHooks.js +62 -0
  94. package/dist/Helpers/RequestCache.d.ts +35 -0
  95. package/dist/Helpers/RequestCache.js +113 -0
  96. package/dist/Helpers/RowsStore.d.ts +7 -2
  97. package/dist/Helpers/RowsStore.js +15 -3
  98. package/dist/Helpers/TableFilters.d.ts +25 -0
  99. package/dist/Helpers/TableFilters.js +162 -0
  100. package/dist/Helpers/TableHelpers.d.ts +7 -4
  101. package/dist/Helpers/TableHelpers.js +15 -6
  102. package/dist/Helpers/TableJoins.d.ts +70 -0
  103. package/dist/Helpers/TableJoins.js +250 -0
  104. package/dist/Helpers/TableStore.d.ts +16 -3
  105. package/dist/Helpers/TableStore.js +25 -5
  106. package/dist/Helpers/ThemeHelpers.d.ts +21 -4
  107. package/dist/Helpers/ThemeHelpers.js +46 -9
  108. package/dist/Helpers/TreeStore.d.ts +225 -11
  109. package/dist/Helpers/TreeStore.js +683 -90
  110. package/dist/Helpers/UserPreferences.d.ts +22 -0
  111. package/dist/Helpers/UserPreferences.js +116 -0
  112. package/dist/Interfaces/CrudInterfaces.d.ts +67 -10
  113. package/dist/Interfaces/ExcelInterfaces.d.ts +12 -0
  114. package/dist/Interfaces/FormInterfaces.d.ts +37 -6
  115. package/dist/Interfaces/InputInterfaces.d.ts +24 -0
  116. package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
  117. package/dist/Interfaces/PreferenceInterfaces.d.ts +51 -0
  118. package/dist/Interfaces/PreferenceInterfaces.js +25 -0
  119. package/dist/Interfaces/TableInterfaces.d.ts +145 -16
  120. package/dist/Interfaces/ThemeInterfaces.d.ts +25 -0
  121. package/dist/Interfaces/ThemeInterfaces.js +15 -0
  122. package/dist/Services/ApiProxy.d.ts +36 -1
  123. package/dist/Services/ApiProxy.js +182 -52
  124. package/dist/Services/ApiProxyRules.d.ts +203 -0
  125. package/dist/Services/ApiProxyRules.js +426 -0
  126. package/dist/Services/ExcelApi.d.ts +3 -1
  127. package/dist/Services/ExcelApi.js +1 -1
  128. package/dist/Services/JoinSources.d.ts +48 -0
  129. package/dist/Services/JoinSources.js +74 -0
  130. package/dist/Services/ServerApi.d.ts +13 -2
  131. package/dist/Services/ServerApi.js +8 -3
  132. package/dist/Services/ThemeService.d.ts +18 -2
  133. package/dist/Services/ThemeService.js +80 -16
  134. package/dist/Shared.Index.d.ts +6 -0
  135. package/dist/Shared.Index.js +6 -0
  136. package/package.json +4 -2
  137. package/src/Styles/Nexus.Avatar.css +79 -0
  138. package/src/Styles/Nexus.Base.css +51 -4
  139. package/src/Styles/Nexus.Button.css +27 -0
  140. package/src/Styles/Nexus.Calendar.css +4 -6
  141. package/src/Styles/Nexus.Chat.css +5 -51
  142. package/src/Styles/Nexus.Choice.css +4 -6
  143. package/src/Styles/Nexus.Crud.css +16 -9
  144. package/src/Styles/Nexus.Dropdown.css +12 -61
  145. package/src/Styles/Nexus.Editor.css +4 -6
  146. package/src/Styles/Nexus.Excel.css +34 -15
  147. package/src/Styles/Nexus.Form.css +25 -67
  148. package/src/Styles/Nexus.Index.css +1 -0
  149. package/src/Styles/Nexus.Input.css +794 -637
  150. package/src/Styles/Nexus.Markdown.css +0 -28
  151. package/src/Styles/Nexus.Menu.css +8 -30
  152. package/src/Styles/Nexus.Popup.css +22 -29
  153. package/src/Styles/Nexus.RichText.css +2 -32
  154. package/src/Styles/Nexus.Rows.css +51 -10
  155. package/src/Styles/Nexus.Slider.css +3 -5
  156. package/src/Styles/Nexus.Tab.Buttons.css +20 -7
  157. package/src/Styles/Nexus.Table.css +1387 -1296
  158. package/src/Styles/Nexus.Theme.Picker.css +342 -232
  159. package/src/Styles/Nexus.Time.css +9 -12
  160. package/src/Styles/Nexus.Toast.css +6 -8
  161. package/src/Styles/Nexus.Tokens.css +17 -0
  162. package/src/Styles/Nexus.Tree.css +76 -11
@@ -1,6 +1,9 @@
1
+ import type { IconNode } from "nexus-icons";
1
2
  import type { CSSProperties, ReactNode, Ref } from "react";
2
3
  import type { TableStore } from "../Helpers/TableStore.ts";
4
+ import type { ApiEndpoint } from "./ApiInterfaces.ts";
3
5
  import type { ExcelEndpoints } from "./ExcelInterfaces.ts";
6
+ import type { InputParams } from "./FormInterfaces.ts";
4
7
  import type { Size, Tone } from "./TypeInterfaces.ts";
5
8
  /** A row's id: its `rowKey` field, such as `id`. */
6
9
  export type TableKey = string | number;
@@ -15,6 +18,11 @@ export interface TableColumnOption {
15
18
  /** Draws the value as a tag with a dot of this color. */
16
19
  tone?: Tone;
17
20
  }
21
+ /** A short tag after a cell's value (`TableColumn.tag`): its words and the color of its dot. */
22
+ export interface TableTag {
23
+ label: string;
24
+ tone?: Tone;
25
+ }
18
26
  /** One column: which field it shows, how, and what the user can do with it. */
19
27
  export interface TableColumn<TRow extends TableRow = TableRow> {
20
28
  /** The field: `"name"`, or a path into the row, `"guest.name"`. It is also the column's id and its sort name. */
@@ -64,6 +72,8 @@ export interface TableColumn<TRow extends TableRow = TableRow> {
64
72
  hideable?: boolean;
65
73
  /** Long text wraps onto more lines instead of ending in "…". */
66
74
  wrap?: boolean;
75
+ /** Its text is Markdown: the table shows the text, and a record's details show it formatted. */
76
+ markdown?: boolean;
67
77
  /** The user can drag the heading to another place, or move it from its menu. Default true. */
68
78
  movable?: boolean;
69
79
  /** The user can drag the edge of the heading to change the width. Default true; `false` for a column that must keep its width. */
@@ -72,6 +82,44 @@ export interface TableColumn<TRow extends TableRow = TableRow> {
72
82
  pinnable?: boolean;
73
83
  /** Pinned before the user pins anything: `left` keeps it at the start, `right` at the end. */
74
84
  pinned?: TablePinSide;
85
+ /**
86
+ * The value is another record's, looked up by the key this row holds: a country's sub-region named from the
87
+ * sub-regions' `FindListing`. The table collects every key its rows hold, asks once for the ones it has not got, keeps
88
+ * the answers for every table on the page, and shows the field once they are in - searched and sorted like any other
89
+ * column. A key the source does not answer shows as "Not found". (A join the database already made needs none of
90
+ * this: the row carries the name, and `tag` reads its status.)
91
+ */
92
+ join?: TableJoin;
93
+ /**
94
+ * A short tag after the value: "Trashed" beside a parent that is in the trash. Given the row and, on a `join` column,
95
+ * the record it looked up; nothing when it answers nothing. `crudColumns.dbJoin` and `crudColumns.clientJoin` set it.
96
+ */
97
+ tag?: (row: TRow, joined?: TableJoinedRecord) => TableTag | null | undefined;
98
+ }
99
+ /** A record a `join` looked up: whatever its source answers for one key. */
100
+ export type TableJoinedRecord = Readonly<Record<string, unknown>>;
101
+ /** Where a `join` looks records up: the records behind a set of keys, in any status. */
102
+ export interface TableJoinSource {
103
+ /** One name per source. Every column and table that names it shares its answers and its calls. */
104
+ readonly name: string;
105
+ /** The records behind these keys; a key with no record is left out. One call for every key the page needs at once. */
106
+ load(keys: readonly string[], signal: AbortSignal): Promise<readonly TableJoinedRecord[]>;
107
+ /** The field of a loaded record that holds its key. Default `id`. */
108
+ readonly key?: string;
109
+ /** How long an answer is kept, in milliseconds, before it is asked again. Default 5 minutes. */
110
+ readonly lifetime?: number;
111
+ }
112
+ /** A column whose value is another record's (`TableColumn.join`). */
113
+ export interface TableJoin {
114
+ /** The field of this row that holds the other record's key: `"subRegionId"`. */
115
+ from: string;
116
+ /** The field of the other record the column shows. Default: the column's own `key`. */
117
+ field?: string;
118
+ /**
119
+ * Where the records come from: an endpoint the keys are POSTed to as an array (a Nexus controller's `findListing`),
120
+ * or a source of the page's own.
121
+ */
122
+ source: ApiEndpoint | TableJoinSource;
75
123
  }
76
124
  /** Which side a pinned column stays on while the others scroll. */
77
125
  export type TablePinSide = "left" | "right";
@@ -81,7 +129,13 @@ export interface TableSort {
81
129
  key: string;
82
130
  direction: TableSortDirection;
83
131
  }
84
- /** What the table shows: the search, the sort, and the page. */
132
+ /**
133
+ * The advanced filter's values, by input name: only the inputs that hold a value, in the order of the inputs, so the
134
+ * same filters always serialize the same way (a cache key, a request). A date range is its pair ["2026-10-01", null],
135
+ * a range slider [2, 7], a multiple choice its list, a ticked checkbox `true`.
136
+ */
137
+ export type TableFilters = Readonly<Record<string, unknown>>;
138
+ /** What the table shows: the search, the filters, the sort, and the page. */
85
139
  export interface TableQuery {
86
140
  /** The search text, trimmed. */
87
141
  search: string;
@@ -89,13 +143,18 @@ export interface TableQuery {
89
143
  /** The page, from 1. */
90
144
  page: number;
91
145
  pageSize: number;
146
+ /**
147
+ * The advanced filter while any input holds a value; absent otherwise. A client table matches its rows; a server
148
+ * table's loader receives the raw values (`filterRequest` gives them in a server's names).
149
+ */
150
+ filters?: TableFilters;
92
151
  }
93
152
  /** One page of rows, as a server sends it. */
94
153
  export interface TablePage<TRow extends TableRow = TableRow> {
95
154
  rows: readonly TRow[];
96
155
  /** Every row there is. Default: the rows given. */
97
156
  total?: number;
98
- /** The rows that match the search. Default: `total`. */
157
+ /** The rows that match the search and the filters. Default: `total`. */
99
158
  matches?: number;
100
159
  }
101
160
  /**
@@ -120,7 +179,7 @@ export interface TableState<TRow extends TableRow = TableRow> {
120
179
  offset: number;
121
180
  /** Every row there is. */
122
181
  total: number;
123
- /** The rows that match the search. */
182
+ /** The rows that match the search and the filters. */
124
183
  matches: number;
125
184
  query: TableQuery;
126
185
  /** The selected rows by key (keys as text), in the order they were picked. */
@@ -175,10 +234,15 @@ export interface TableExcel {
175
234
  subject?: string;
176
235
  /** Values the endpoints' own params need, such as a parent's id. */
177
236
  params?: Record<string, string | number>;
178
- /** The filters the page holds, beside the table's search and sort, read when an export is asked for. */
237
+ /** Filters the page holds outside the table, sent beside the table's own (`filterInputs`); the page's win a clash. */
179
238
  filters?: () => Record<string, unknown>;
180
- /** After an import wrote something. The table reloads itself either way. */
239
+ /** After an import wrote something, before the table reads its rows again: drop here whatever the page keeps of them. */
181
240
  onImported?: () => void;
241
+ /**
242
+ * No **Bulk insert** entry. A sheet with a required list cannot be typed in - its key is picked in the spreadsheet -
243
+ * and its panel says so; this leaves the entry out instead.
244
+ */
245
+ bulk?: false;
182
246
  }
183
247
  /** An item of the table's options menu (⋮): Import, Print, a report of the page's own. */
184
248
  export interface TableMenuAction {
@@ -195,6 +259,36 @@ export interface TableMenuAction {
195
259
  separator?: boolean;
196
260
  onSelect: () => void;
197
261
  }
262
+ /**
263
+ * Rows kept in a section above the page, which the user folds open and shut: the records they pinned, their
264
+ * favourites. They are the rows as they came from their own endpoint, the same records also show in the list below,
265
+ * and each section's row wears the section's icon where the rows of the page show their number.
266
+ */
267
+ export interface TableRowSection<TRow extends TableRow = TableRow> {
268
+ /** Tells the sections apart; the table remembers each one's fold under it. */
269
+ key: string;
270
+ /** What the section is called: "Pinned rooms". */
271
+ label: string;
272
+ /** The section's icon, in its head and on each of its rows. */
273
+ icon: IconNode;
274
+ rows: readonly TRow[];
275
+ /** The rows are on their way: the section shows that it is loading. */
276
+ loading?: boolean;
277
+ /** Most rows the section can hold, shown beside the count so the user knows the limit. */
278
+ max?: number;
279
+ }
280
+ /** A mark a row carries for this user - pinned, a favourite - drawn in its first column where its number would be. */
281
+ export interface TableRowMark {
282
+ icon: IconNode;
283
+ /** What the mark says: "Pinned". */
284
+ label: string;
285
+ }
286
+ /** A way to select rows by what they are, such as "Pinned" or "Not flagged". */
287
+ export interface TableSelectBy<TRow extends TableRow = TableRow> {
288
+ label: string;
289
+ icon?: ReactNode;
290
+ test: (row: TRow) => boolean;
291
+ }
198
292
  /** An action on one row, in its row: an icon button (`inline`) or an item of the row's menu. */
199
293
  export interface TableRowAction<TRow extends TableRow = TableRow> {
200
294
  /** Tells actions apart; a test or a style can find it by `data-action`. */
@@ -247,22 +341,33 @@ export interface DataTableProps<TRow extends TableRow = TableRow> {
247
341
  searchable?: boolean;
248
342
  /** Default "Search…". */
249
343
  searchPlaceholder?: string;
250
- /** Before the search box, on the left of the bar: tabs, filters. */
344
+ /** On the left of the bar, after the Add section: tabs, filters. On a narrow table the line under the search. */
251
345
  toolbar?: ReactNode;
252
- /** At the end of the bar: an Add button. */
346
+ /** The page's own buttons, in the Add section after the table's Add: first in the bar, last on a phone. */
253
347
  actions?: ReactNode;
348
+ /**
349
+ * The table's Add, first in the bar. With `excel`, its arrow holds the template, the import and the export, which
350
+ * then leave the options menu.
351
+ */
352
+ add?: {
353
+ label: ReactNode;
354
+ onClick: () => void;
355
+ };
254
356
  /** The Columns panel, where columns are shown, hidden, and dragged into another order. Default true. */
255
357
  columnsMenu?: boolean;
256
358
  /**
257
359
  * The spreadsheet side of this table, against the backend's Excel controller. The options menu (⋮) then offers
258
360
  * **Download template**, **Import from a spreadsheet** (the file is verified, the person goes over the rows, and the
259
- * table reloads once they are written) and, where the endpoints have one, **Export to Excel** - which asks the
260
- * server with the search and the sort on screen instead of writing a file of the rows this browser happens to hold.
361
+ * table reloads once they are written), **Bulk insert** (the same import with no file: the rows are typed into the
362
+ * same editor; offered only with `add`, as it is an Add of many rows) and, where the endpoints have one, **Export to
363
+ * Excel** - which asks the server with the search, the filters and the sort on screen instead of writing a file of
364
+ * the rows this browser happens to hold.
261
365
  *
262
- * It replaces `onImport`, which is the hook for a page that imports its own way.
366
+ * It replaces `onImport`, which is the hook for a page that imports its own way. With `add`, they are under the
367
+ * Add's arrow instead of the options menu.
263
368
  */
264
369
  excel?: TableExcel;
265
- /** A Reload button. Default true when rows are loaded. */
370
+ /** A Reload button (in the options menu on a table under 375px wide). Default true when rows are loaded. */
266
371
  reloadable?: boolean;
267
372
  /** When the user presses Reload, before the rows load again: drop a cache of your own. */
268
373
  onReload?: () => void;
@@ -288,6 +393,11 @@ export interface DataTableProps<TRow extends TableRow = TableRow> {
288
393
  maxSelection?: number;
289
394
  /** After the selection changes. */
290
395
  onSelectionChange?: (rows: TRow[]) => void;
396
+ /**
397
+ * While rows are selected, their count opens a menu that selects again by what the rows are: every row on the page,
398
+ * then these, each picking exactly the rows on the page that pass its test.
399
+ */
400
+ selectBy?: readonly TableSelectBy<TRow>[];
291
401
  /** Actions on the selected rows. A function gets the selected rows. */
292
402
  massActions?: readonly TableMassAction<TRow>[] | ((rows: TRow[]) => readonly TableMassAction<TRow>[]);
293
403
  /** Actions of each row. */
@@ -301,8 +411,19 @@ export interface DataTableProps<TRow extends TableRow = TableRow> {
301
411
  */
302
412
  openOnClick?: boolean;
303
413
  /**
304
- * Rows kept in a section above the page, which the user folds open: the records they pinned. They are the rows as
305
- * they came from their own endpoint, and the same records also show in the list below. Leave it out for no section.
414
+ * Sections above the page, in this order, each folding open and shut on its own: the records the user pinned, then
415
+ * their favourites. A section with no rows and none on their way does not show.
416
+ */
417
+ sections?: readonly TableRowSection<TRow>[];
418
+ /**
419
+ * The marks each row carries (pinned, a favourite), in its first column in place of its number, one under the
420
+ * other - in the list and in the sections alike, so no column of their own is needed. A row with none shows its
421
+ * number; a section's row with none shows the section's icon.
422
+ */
423
+ rowMarks?: (row: TRow) => readonly TableRowMark[];
424
+ /**
425
+ * One section of pinned rows, before any `sections`: the same as a section with the key `pins` and the pin icon.
426
+ * Leave it out for no such section.
306
427
  */
307
428
  pinnedRows?: readonly TRow[];
308
429
  /** The pinned rows are on their way: the section shows that it is loading. */
@@ -322,14 +443,22 @@ export interface DataTableProps<TRow extends TableRow = TableRow> {
322
443
  * as the pointer is over the row or anything is selected.
323
444
  */
324
445
  numbered?: boolean;
325
- /** Remembers the page size, the sort, hidden columns, the order, widths, and pins of the columns, and whether the
326
- * pinned section is open, on this device. */
446
+ /** Remembers the page size, the sort, hidden columns, the order, widths, and pins of the columns, and which sections
447
+ * are open, on this device. */
327
448
  stateKey?: string;
328
449
  /** Milliseconds after the last key before a search goes to a server. Default 300; a table with every row searches at once. */
329
450
  searchDelay?: number;
451
+ /**
452
+ * The advanced filter: the inputs the rows can be filtered by, as a form takes them. Keep the array stable.
453
+ * With any, the options menu (⋮) offers **Filters**: a panel of these inputs under its button, applied as they change.
454
+ * Name an input after the column `key` it filters (else a path into the row); that name is also what a server gets.
455
+ * A `number` input named `{key}From` / `{key}To` is a lower / upper bound of column `{key}`.
456
+ * `required`, `defaultValue` and `defaultChecked` are ignored, so the Add form's params can be reused.
457
+ */
458
+ filterInputs?: readonly InputParams[];
330
459
  /** Shown when there are no rows at all. Default "No records yet." */
331
460
  emptyMessage?: ReactNode;
332
- /** Beside the empty message, such as an Add button. */
461
+ /** Beside the empty message. Default: the table's Add (`add`); `null` for none. */
333
462
  emptyAction?: ReactNode;
334
463
  /** The tallest the table grows before its rows scroll under a header that stays: pixels or a CSS length. */
335
464
  maxHeight?: number | string;
@@ -9,9 +9,34 @@ export interface ThemeOption {
9
9
  /** The themes in `nexus-shared/styles.css`. Each has a light and a dark palette. */
10
10
  export declare const NEXUS_THEMES: readonly ThemeOption[];
11
11
  export declare const DEFAULT_THEME = "nexus";
12
+ /** `compact` (default): today's sizes. `cozy`: every control and row a step taller, for touch screens and large monitors. */
13
+ export type Density = "compact" | "cozy";
14
+ /**
15
+ * Whether things move: `on` even when the operating system asks for less motion, `off` never, `system` as the
16
+ * operating system says (`prefers-reduced-motion`). Some people turn animations off for the whole desktop and still
17
+ * want them here, which is why this is a choice of its own.
18
+ */
19
+ export type AnimationMode = "on" | "off" | "system";
20
+ /** The scale grid, a development tool: off, a 4px sheet with a solid line every 48px, or millimetres and centimetres. */
21
+ export type ScaleGrid = "off" | "px" | "cm";
12
22
  /** Browser storage keys for the saved choice. */
13
23
  export declare const THEME_STORAGE_KEY = "nx-theme";
14
24
  export declare const THEME_MODE_STORAGE_KEY = "nx-mode";
25
+ export declare const DENSITY_STORAGE_KEY = "nx-density";
26
+ export declare const ANIMATIONS_STORAGE_KEY = "nx-animations";
27
+ export declare const GRID_STORAGE_KEY = "nx-grid";
15
28
  /** Attributes on `<html>` that select the theme and the mode. Without `data-nx-mode`, the system setting applies. */
16
29
  export declare const THEME_ATTRIBUTE = "data-nx-theme";
17
30
  export declare const THEME_MODE_ATTRIBUTE = "data-nx-mode";
31
+ /** `compact` or `cozy`; the size tokens follow it. */
32
+ export declare const DENSITY_ATTRIBUTE = "data-nx-density";
33
+ /** The visitor's animations choice, `on` or `off`. Without it, the system setting applies. */
34
+ export declare const ANIMATIONS_ATTRIBUTE = "data-nx-animations";
35
+ /**
36
+ * What the stylesheets read: `reduce` while nothing may move, `full` otherwise. `ThemeScript` works it out from the
37
+ * choice and the system setting before the first paint, and again when the system setting changes, so no component
38
+ * asks `prefers-reduced-motion` itself.
39
+ */
40
+ export declare const MOTION_ATTRIBUTE = "data-nx-motion";
41
+ /** `px` or `cm` while the scale grid shows; development builds only. */
42
+ export declare const GRID_ATTRIBUTE = "data-nx-grid";
@@ -12,6 +12,21 @@ export const DEFAULT_THEME = "nexus";
12
12
  /** Browser storage keys for the saved choice. */
13
13
  export const THEME_STORAGE_KEY = "nx-theme";
14
14
  export const THEME_MODE_STORAGE_KEY = "nx-mode";
15
+ export const DENSITY_STORAGE_KEY = "nx-density";
16
+ export const ANIMATIONS_STORAGE_KEY = "nx-animations";
17
+ export const GRID_STORAGE_KEY = "nx-grid";
15
18
  /** Attributes on `<html>` that select the theme and the mode. Without `data-nx-mode`, the system setting applies. */
16
19
  export const THEME_ATTRIBUTE = "data-nx-theme";
17
20
  export const THEME_MODE_ATTRIBUTE = "data-nx-mode";
21
+ /** `compact` or `cozy`; the size tokens follow it. */
22
+ export const DENSITY_ATTRIBUTE = "data-nx-density";
23
+ /** The visitor's animations choice, `on` or `off`. Without it, the system setting applies. */
24
+ export const ANIMATIONS_ATTRIBUTE = "data-nx-animations";
25
+ /**
26
+ * What the stylesheets read: `reduce` while nothing may move, `full` otherwise. `ThemeScript` works it out from the
27
+ * choice and the system setting before the first paint, and again when the system setting changes, so no component
28
+ * asks `prefers-reduced-motion` itself.
29
+ */
30
+ export const MOTION_ATTRIBUTE = "data-nx-motion";
31
+ /** `px` or `cm` while the scale grid shows; development builds only. */
32
+ export const GRID_ATTRIBUTE = "data-nx-grid";
@@ -1,4 +1,6 @@
1
1
  import type { ApiModuleValue } from "../Interfaces/ApiInterfaces.ts";
2
+ import { type ApiProxyCode, type ApiProxyFailure } from "./ApiProxyRules.ts";
3
+ export { DEFAULT_PROXY_BODY_LIMIT, DEFAULT_PROXY_REDIRECTS, DEFAULT_PROXY_RESPONSE_HEADERS, lastForwardedAddress, nexusFailure, PROXY_REFUSALS, readIpAddress, type ApiProxyCode, type ApiProxyFailure } from "./ApiProxyRules.ts";
2
4
  export interface ApiProxyOptions {
3
5
  /** The user's token for the backend, such as from the session cookie. Without one, the request's own Authorization header goes on. */
4
6
  token?: (request: Request) => string | null | undefined | Promise<string | null | undefined>;
@@ -6,10 +8,34 @@ export interface ApiProxyOptions {
6
8
  headers?: (request: Request) => Record<string, string> | undefined | Promise<Record<string, string> | undefined>;
7
9
  /** Serves only these modules, of those the app's `ApiModules` sends through the proxy. Default: all of those. */
8
10
  allow?: readonly ApiModuleValue[];
9
- /** Milliseconds to wait for the backend to start answering. Default 60,000. */
11
+ /** Milliseconds to wait for the backend to start answering, its redirects included. Default 60,000. */
10
12
  timeout?: number;
11
13
  /** The `fetch` to use. Default: the global one. */
12
14
  fetch?: typeof fetch;
15
+ /**
16
+ * More of the backend's response headers to pass on, besides `DEFAULT_PROXY_RESPONSE_HEADERS` and the Nexus backend's
17
+ * own `X-Nexus-*`: `["X-Total-Count"]`. A header that names the backend or carries its own policies (Server,
18
+ * X-Powered-By, X-AspNet-*, Via, Alt-Svc, Strict-Transport-Security, CORS, Location, Link) is dropped even when named.
19
+ */
20
+ responseHeaders?: readonly string[];
21
+ /** Passes the backend's Set-Cookie headers to the browser. Default false: the app keeps its own session. */
22
+ passCookies?: boolean;
23
+ /** The most bytes a request body may have; a bigger one is refused with 413 before it reaches the backend. Default 100 MB. */
24
+ maxBodySize?: number;
25
+ /** How many redirects inside a module to follow for one call; 0 refuses every redirect. Default 3. */
26
+ maxRedirects?: number;
27
+ /**
28
+ * The client's IP address, sent to the backend as X-Forwarded-For for its sign-in lockout and rate limits. Default:
29
+ * the last address of the X-Forwarded-For the app received, the one Next.js or the app's own reverse proxy wrote. Pass
30
+ * it behind more than one proxy of the app's own, or behind a CDN that names the client in a header of its own.
31
+ */
32
+ clientAddress?: (request: Request) => string | null | undefined | Promise<string | null | undefined>;
33
+ /** Lets pages of other sites call through the proxy. Default false: the app's own pages, links followed, and non-browser callers. */
34
+ allowCrossSite?: boolean;
35
+ /** Builds the answer to a refusal of the proxy's own, for a backend whose envelope is not Nexus's. Default `nexusFailure`. */
36
+ failure?: (failure: ApiProxyFailure) => Response;
37
+ /** Hears what the proxy hid or refused, on the server. Default: one line in `console.error`. */
38
+ onProblem?: (problem: ApiProxyProblem) => void;
13
39
  }
14
40
  /** What a Next.js route handler for `[module]/[...path]` receives. */
15
41
  export interface ApiProxyContext {
@@ -18,6 +44,15 @@ export interface ApiProxyContext {
18
44
  path?: string[];
19
45
  }>;
20
46
  }
47
+ /** What the proxy hid or refused, for the server's log. It never holds the user's token or a query string. */
48
+ export interface ApiProxyProblem {
49
+ /** The refusal's code, the one the browser got: "BACKEND_REDIRECT_REFUSED". */
50
+ messageCode: ApiProxyCode;
51
+ /** The call, as the browser made it: "POST configuration/Region/MassRecover". */
52
+ call: string;
53
+ /** What happened, for a developer: the backend's address and status, a piece of its error page, why it failed. */
54
+ detail: string;
55
+ }
21
56
  /**
22
57
  * Makes the route handlers of the proxy. Only the modules' configured roots can be reached: a path cannot climb out of
23
58
  * its module (`..` is refused), and the query goes on unchanged.