nexus-shared 3.1.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.
Files changed (234) hide show
  1. package/CHANGELOG.md +2268 -446
  2. package/LICENSE +21 -21
  3. package/README.md +1705 -1084
  4. package/dist/Client.Index.d.ts +17 -0
  5. package/dist/Client.Index.js +17 -0
  6. package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
  7. package/dist/Components/Documents/DocumentFrame.js +83 -0
  8. package/dist/Components/Documents/Menu.d.ts +4 -0
  9. package/dist/Components/Documents/Menu.js +40 -24
  10. package/dist/Components/Documents/PrintDocument.d.ts +95 -0
  11. package/dist/Components/Documents/PrintDocument.js +62 -0
  12. package/dist/Components/Documents/Tabs.d.ts +59 -0
  13. package/dist/Components/Documents/Tabs.js +70 -0
  14. package/dist/Components/Forms/ApiForm.js +19 -2
  15. package/dist/Components/Forms/Crud.d.ts +2 -2
  16. package/dist/Components/Forms/Crud.js +871 -377
  17. package/dist/Components/Forms/CrudActions.d.ts +136 -0
  18. package/dist/Components/Forms/CrudActions.js +184 -0
  19. package/dist/Components/Forms/CrudHistory.d.ts +66 -0
  20. package/dist/Components/Forms/CrudHistory.js +140 -0
  21. package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
  22. package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
  23. package/dist/Components/Forms/CrudParts.d.ts +30 -2
  24. package/dist/Components/Forms/CrudParts.js +56 -5
  25. package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
  26. package/dist/Components/Forms/CrudRowForms.js +114 -0
  27. package/dist/Components/Forms/CrudScopes.d.ts +28 -0
  28. package/dist/Components/Forms/CrudScopes.js +76 -0
  29. package/dist/Components/Forms/CrudTree.d.ts +90 -0
  30. package/dist/Components/Forms/CrudTree.js +363 -0
  31. package/dist/Components/Forms/CrudWorkspace.d.ts +63 -0
  32. package/dist/Components/Forms/CrudWorkspace.js +169 -0
  33. package/dist/Components/Forms/ExcelImport.js +3 -3
  34. package/dist/Components/Forms/Form.d.ts +4 -4
  35. package/dist/Components/Forms/Form.js +125 -16
  36. package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
  37. package/dist/Components/Forms/FormLeaveGuard.js +65 -0
  38. package/dist/Components/Forms/RecordFlows.d.ts +50 -0
  39. package/dist/Components/Forms/RecordFlows.js +282 -0
  40. package/dist/Components/Forms/SubmitForm.d.ts +37 -2
  41. package/dist/Components/Forms/SubmitForm.js +195 -10
  42. package/dist/Components/Inputs/CheckBox.js +1 -1
  43. package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
  44. package/dist/Components/Inputs/ChoiceField.js +28 -2
  45. package/dist/Components/Inputs/CodeBox.d.ts +69 -0
  46. package/dist/Components/Inputs/CodeBox.js +316 -0
  47. package/dist/Components/Inputs/DatePicker.js +4 -1
  48. package/dist/Components/Inputs/DateRangePicker.js +4 -1
  49. package/dist/Components/Inputs/DateTimePicker.js +4 -1
  50. package/dist/Components/Inputs/Dropdown.d.ts +13 -0
  51. package/dist/Components/Inputs/Dropdown.js +8 -5
  52. package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
  53. package/dist/Components/Inputs/DropdownParts.js +16 -4
  54. package/dist/Components/Inputs/Field.d.ts +7 -1
  55. package/dist/Components/Inputs/Field.js +2 -2
  56. package/dist/Components/Inputs/GroupForm.d.ts +6 -6
  57. package/dist/Components/Inputs/GroupForm.js +10 -11
  58. package/dist/Components/Inputs/InputRenderer.d.ts +9 -6
  59. package/dist/Components/Inputs/InputRenderer.js +39 -31
  60. package/dist/Components/Inputs/NumberBox.js +4 -2
  61. package/dist/Components/Inputs/PictureInput.d.ts +41 -0
  62. package/dist/Components/Inputs/PictureInput.js +70 -0
  63. package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
  64. package/dist/Components/Inputs/RadioGroup.js +8 -3
  65. package/dist/Components/Inputs/RowsInput.d.ts +1 -1
  66. package/dist/Components/Inputs/RowsInput.js +18 -3
  67. package/dist/Components/Inputs/TabularForm.d.ts +5 -5
  68. package/dist/Components/Inputs/TabularForm.js +29 -29
  69. package/dist/Components/Inputs/TextArea.d.ts +6 -0
  70. package/dist/Components/Inputs/TextArea.js +14 -2
  71. package/dist/Components/Inputs/TextBox.js +4 -1
  72. package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
  73. package/dist/Components/Inputs/TextBoxTypes.js +17 -0
  74. package/dist/Components/Inputs/TimePicker.js +4 -1
  75. package/dist/Components/Inputs/TreeView.d.ts +107 -6
  76. package/dist/Components/Inputs/TreeView.js +513 -30
  77. package/dist/Components/Layouts/MessageBox.d.ts +15 -2
  78. package/dist/Components/Layouts/MessageBox.js +56 -7
  79. package/dist/Components/Layouts/Popup.d.ts +13 -6
  80. package/dist/Components/Layouts/Popup.js +37 -9
  81. package/dist/Components/Layouts/PopupDock.d.ts +3 -1
  82. package/dist/Components/Layouts/PopupDock.js +2 -1
  83. package/dist/Components/Layouts/PopupHost.js +5 -4
  84. package/dist/Components/Layouts/ThemePicker.d.ts +3 -2
  85. package/dist/Components/Layouts/ThemePicker.js +16 -4
  86. package/dist/Components/Viewers/DataTable.js +205 -61
  87. package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
  88. package/dist/Components/Viewers/DataTableFilters.js +17 -2
  89. package/dist/Components/Viewers/DataTableParts.d.ts +46 -12
  90. package/dist/Components/Viewers/DataTableParts.js +62 -21
  91. package/dist/Components/Viewers/OwnerFiles.d.ts +175 -0
  92. package/dist/Components/Viewers/OwnerFiles.js +589 -0
  93. package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
  94. package/dist/Components/Viewers/QrCodeImage.js +15 -0
  95. package/dist/Components/Viewers/RecordCards.d.ts +16 -0
  96. package/dist/Components/Viewers/RecordCards.js +509 -0
  97. package/dist/Helpers/ApiModules.d.ts +3 -34
  98. package/dist/Helpers/ApiModules.js +3 -3
  99. package/dist/Helpers/ApiRoutes.d.ts +68 -26
  100. package/dist/Helpers/ApiRoutes.js +31 -27
  101. package/dist/Helpers/ArrayHelpers.d.ts +0 -0
  102. package/dist/Helpers/ArrayHelpers.js +2 -0
  103. package/dist/Helpers/BusyHooks.d.ts +6 -0
  104. package/dist/Helpers/BusyHooks.js +39 -0
  105. package/dist/Helpers/CodeHelpers.d.ts +97 -0
  106. package/dist/Helpers/CodeHelpers.js +332 -0
  107. package/dist/Helpers/CrudBackend.d.ts +114 -6
  108. package/dist/Helpers/CrudBackend.js +214 -11
  109. package/dist/Helpers/CrudColumns.d.ts +98 -0
  110. package/dist/Helpers/CrudColumns.js +160 -0
  111. package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
  112. package/dist/Helpers/CrudDetailsTabs.js +126 -0
  113. package/dist/Helpers/CrudHelpers.d.ts +130 -6
  114. package/dist/Helpers/CrudHelpers.js +304 -12
  115. package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
  116. package/dist/Helpers/CrudNarrowPick.js +157 -0
  117. package/dist/Helpers/CrudRowForms.d.ts +15 -0
  118. package/dist/Helpers/CrudRowForms.js +43 -0
  119. package/dist/Helpers/CrudScopes.d.ts +32 -0
  120. package/dist/Helpers/CrudScopes.js +79 -0
  121. package/dist/Helpers/DropdownHelpers.d.ts +3 -1
  122. package/dist/Helpers/DropdownHelpers.js +6 -2
  123. package/dist/Helpers/ExcelHelpers.d.ts +1 -1
  124. package/dist/Helpers/ExcelHelpers.js +12 -11
  125. package/dist/Helpers/FileAdapter.d.ts +78 -0
  126. package/dist/Helpers/FileAdapter.js +102 -0
  127. package/dist/Helpers/FilePrimaries.d.ts +48 -0
  128. package/dist/Helpers/FilePrimaries.js +138 -0
  129. package/dist/Helpers/FormConfirm.d.ts +32 -0
  130. package/dist/Helpers/FormConfirm.js +40 -0
  131. package/dist/Helpers/FormStore.d.ts +90 -5
  132. package/dist/Helpers/FormStore.js +318 -22
  133. package/dist/Helpers/InputConditions.d.ts +50 -0
  134. package/dist/Helpers/InputConditions.js +168 -0
  135. package/dist/Helpers/InputMemory.d.ts +106 -0
  136. package/dist/Helpers/InputMemory.js +385 -0
  137. package/dist/Helpers/InputNarrowing.d.ts +18 -0
  138. package/dist/Helpers/InputNarrowing.js +67 -0
  139. package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
  140. package/dist/Helpers/InputParamsHelpers.js +162 -71
  141. package/dist/Helpers/MessageBuilder.js +2 -0
  142. package/dist/Helpers/NexusDriveAdapter.d.ts +55 -0
  143. package/dist/Helpers/NexusDriveAdapter.js +207 -0
  144. package/dist/Helpers/NexusHelpers.d.ts +0 -0
  145. package/dist/Helpers/NexusHelpers.js +1 -0
  146. package/dist/Helpers/NumberHelpers.d.ts +95 -0
  147. package/dist/Helpers/NumberHelpers.js +126 -0
  148. package/dist/Helpers/Permissions.js +2 -1
  149. package/dist/Helpers/QrCode.d.ts +26 -0
  150. package/dist/Helpers/QrCode.js +463 -0
  151. package/dist/Helpers/RecordCards.d.ts +205 -0
  152. package/dist/Helpers/RecordCards.js +378 -0
  153. package/dist/Helpers/RequestCache.d.ts +47 -0
  154. package/dist/Helpers/RequestCache.js +132 -0
  155. package/dist/Helpers/RowsStore.d.ts +37 -2
  156. package/dist/Helpers/RowsStore.js +128 -10
  157. package/dist/Helpers/ServerPreview.d.ts +69 -0
  158. package/dist/Helpers/ServerPreview.js +99 -0
  159. package/dist/Helpers/TableColumns.js +4 -3
  160. package/dist/Helpers/TableExport.d.ts +7 -1
  161. package/dist/Helpers/TableExport.js +21 -15
  162. package/dist/Helpers/TableFilters.d.ts +1 -1
  163. package/dist/Helpers/TableFilters.js +24 -23
  164. package/dist/Helpers/TableHelpers.d.ts +24 -3
  165. package/dist/Helpers/TableHelpers.js +58 -12
  166. package/dist/Helpers/TableJoins.d.ts +83 -0
  167. package/dist/Helpers/TableJoins.js +284 -0
  168. package/dist/Helpers/TableStore.d.ts +26 -1
  169. package/dist/Helpers/TableStore.js +73 -7
  170. package/dist/Helpers/TreeStore.d.ts +306 -11
  171. package/dist/Helpers/TreeStore.js +1048 -90
  172. package/dist/Helpers/UserPreferences.d.ts +8 -0
  173. package/dist/Helpers/UserPreferences.js +17 -0
  174. package/dist/Interfaces/CrudInterfaces.d.ts +512 -14
  175. package/dist/Interfaces/FileInterfaces.d.ts +162 -0
  176. package/dist/Interfaces/FileInterfaces.js +4 -0
  177. package/dist/Interfaces/FormInterfaces.d.ts +393 -29
  178. package/dist/Interfaces/FormInterfaces.js +79 -1
  179. package/dist/Interfaces/InputInterfaces.d.ts +39 -3
  180. package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
  181. package/dist/Interfaces/PreferenceInterfaces.d.ts +8 -0
  182. package/dist/Interfaces/PreferenceInterfaces.js +2 -5
  183. package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
  184. package/dist/Interfaces/RecordInterfaces.js +1 -0
  185. package/dist/Interfaces/TableInterfaces.d.ts +160 -5
  186. package/dist/Services/ApiProxy.d.ts +36 -1
  187. package/dist/Services/ApiProxy.js +182 -52
  188. package/dist/Services/ApiProxyRules.d.ts +203 -0
  189. package/dist/Services/ApiProxyRules.js +426 -0
  190. package/dist/Services/ExcelApi.js +5 -0
  191. package/dist/Services/FileApi.d.ts +13 -0
  192. package/dist/Services/FileApi.js +29 -0
  193. package/dist/Services/ImageEditorLoader.d.ts +14 -0
  194. package/dist/Services/ImageEditorLoader.js +51 -0
  195. package/dist/Services/JoinSources.d.ts +80 -0
  196. package/dist/Services/JoinSources.js +150 -0
  197. package/dist/Services/ModuleImport.d.ts +7 -0
  198. package/dist/Services/ModuleImport.js +22 -0
  199. package/dist/Services/ServerApi.d.ts +13 -2
  200. package/dist/Services/ServerApi.js +8 -3
  201. package/dist/Shared.Index.d.ts +20 -0
  202. package/dist/Shared.Index.js +20 -0
  203. package/package.json +92 -85
  204. package/src/Styles/Nexus.Avatar.css +80 -79
  205. package/src/Styles/Nexus.Base.css +117 -117
  206. package/src/Styles/Nexus.Button.css +186 -180
  207. package/src/Styles/Nexus.Calendar.css +273 -273
  208. package/src/Styles/Nexus.Chat.css +1443 -1443
  209. package/src/Styles/Nexus.Choice.css +359 -343
  210. package/src/Styles/Nexus.Crud.css +542 -168
  211. package/src/Styles/Nexus.DateTime.css +81 -81
  212. package/src/Styles/Nexus.Dropdown.css +733 -725
  213. package/src/Styles/Nexus.Editor.css +295 -295
  214. package/src/Styles/Nexus.Excel.css +389 -389
  215. package/src/Styles/Nexus.Files.css +410 -0
  216. package/src/Styles/Nexus.Form.css +282 -279
  217. package/src/Styles/Nexus.Index.css +41 -37
  218. package/src/Styles/Nexus.Input.css +238 -4
  219. package/src/Styles/Nexus.Markdown.css +237 -237
  220. package/src/Styles/Nexus.Menu.css +246 -237
  221. package/src/Styles/Nexus.Popup.css +800 -724
  222. package/src/Styles/Nexus.Print.css +288 -0
  223. package/src/Styles/Nexus.QrCode.css +12 -0
  224. package/src/Styles/Nexus.Record.css +430 -0
  225. package/src/Styles/Nexus.Rows.css +637 -637
  226. package/src/Styles/Nexus.Slider.css +276 -276
  227. package/src/Styles/Nexus.Tab.Buttons.css +497 -432
  228. package/src/Styles/Nexus.Table.css +98 -35
  229. package/src/Styles/Nexus.Theme.Picker.css +59 -3
  230. package/src/Styles/Nexus.Themes.css +200 -194
  231. package/src/Styles/Nexus.Time.css +144 -144
  232. package/src/Styles/Nexus.Toast.css +263 -263
  233. package/src/Styles/Nexus.Tokens.css +94 -89
  234. package/src/Styles/Nexus.Tree.css +530 -431
@@ -72,6 +72,15 @@
72
72
  display: none;
73
73
  }
74
74
 
75
+ /* What the rows are (`caption`): a line over the bar, inside the frame, so a table beside another starts level with it. */
76
+ .nx-table__caption {
77
+ margin: var(--nx-space-2) var(--nx-space-3) 0;
78
+ color: var(--nx-text-muted);
79
+ font-size: var(--nx-text-md);
80
+ line-height: 1.5;
81
+ overflow-wrap: anywhere;
82
+ }
83
+
75
84
  .nx-table__bar:has(:not(.nx-table__bar-end):not(:empty)) + .nx-table__frame {
76
85
  border-top: 1px solid var(--nx-table-line);
77
86
  }
@@ -90,6 +99,21 @@
90
99
  flex-wrap: wrap;
91
100
  }
92
101
 
102
+ /*
103
+ * The Add section's items keep their own widths on a shared line: none shrinks to its longest word (a switch's
104
+ * label) or runs into the lists, so a bar that does not fit takes its next step instead. On the line the section
105
+ * takes of its own (below) they share it, and one that would not fit its share takes a line of its own, whole. The
106
+ * Add is `block` for that line; beside other parts it is as wide as its label.
107
+ */
108
+ .nx-table__bar-add > * {
109
+ flex: none;
110
+ min-width: fit-content;
111
+ }
112
+
113
+ .nx-table__bar-add > :is(.nx-btn--block, .nx-split--block) {
114
+ width: auto;
115
+ }
116
+
93
117
  /* A part whose page content renders nothing (an empty fragment) is no part: no room, no gap, no step. */
94
118
  .nx-table__bar-add:empty,
95
119
  .nx-table__bar-start:empty {
@@ -270,24 +294,49 @@
270
294
  --nx-row-bg: var(--nx-table-hover);
271
295
  }
272
296
 
273
- /* A row a click opens says so under the pointer. Only the pointer changes: nothing in the row moves on hover. */
274
- .nx-table__row[data-open] {
297
+ /* A row a click opens or picks says so under the pointer. Only the pointer changes: nothing in the row moves on hover. */
298
+ .nx-table__row:is([data-open], [data-pick]) {
275
299
  cursor: pointer;
276
300
  }
277
301
 
278
- .nx-table__row[data-selected] {
302
+ /* A selected row, and the picked one (`onPick`): the same tint, which the checkbox or the list beside tells apart. */
303
+ .nx-table__row:is([data-selected], [data-picked]) {
279
304
  --nx-row-bg: var(--nx-table-selected);
280
305
  }
281
306
 
282
- .nx-table__row[data-selected]:hover {
307
+ .nx-table__row:is([data-selected], [data-picked]):hover {
283
308
  --nx-row-bg: var(--nx-table-selected-hover);
284
309
  }
285
310
 
286
311
  /* Muted text on a selected row's tint moves toward the text color, so it keeps 4.5:1 in every theme. */
287
- .nx-table__row[data-selected] :is(.nx-table__number, .nx-table__dash, .nx-table__no) {
312
+ .nx-table__row:is([data-selected], [data-picked]) :is(.nx-table__number, .nx-table__dash, .nx-table__no) {
288
313
  color: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 45%);
289
314
  }
290
315
 
316
+ /*
317
+ * The keyboard's focus on a row's radio button (`onPick`), which is out of sight: a ring round the row, drawn on its
318
+ * cells as the tree's rows draw theirs, so a pinned cell carries its part of it.
319
+ */
320
+ .nx-table__row:has(.nx-table__pick:focus-visible) > .nx-table__cell {
321
+ box-shadow:
322
+ inset 0 2px 0 var(--nx-primary),
323
+ inset 0 -2px 0 var(--nx-primary);
324
+ }
325
+
326
+ .nx-table__row:has(.nx-table__pick:focus-visible) > .nx-table__cell:first-child {
327
+ box-shadow:
328
+ inset 2px 0 0 var(--nx-primary),
329
+ inset 0 2px 0 var(--nx-primary),
330
+ inset 0 -2px 0 var(--nx-primary);
331
+ }
332
+
333
+ .nx-table__row:has(.nx-table__pick:focus-visible) > .nx-table__cell:last-child {
334
+ box-shadow:
335
+ inset -2px 0 0 var(--nx-primary),
336
+ inset 0 2px 0 var(--nx-primary),
337
+ inset 0 -2px 0 var(--nx-primary);
338
+ }
339
+
291
340
  /* Columns scroll under the pinned ones: a soft shadow shows there is more on that side. */
292
341
  .nx-table__frame[data-scroll-start] {
293
342
  --nx-table-start-edge: 8px 0 8px -8px var(--nx-table-edge);
@@ -447,21 +496,17 @@
447
496
  }
448
497
 
449
498
  /*
450
- * The pinned section above the page: a row across the table whose button folds the records the user pinned open and
451
- * shut, and the rows themselves, which show in the list below as well. A pinned row wears a pin where the rows of the
452
- * page show their number.
499
+ * The sections above the page: a row across the table whose button folds a set of the user's records open and shut -
500
+ * the ones they pinned, their favourites - and the rows themselves, which show in the list below as well. A section's
501
+ * row wears the section's icon where the rows of the page show their number.
453
502
  */
454
- .nx-table__pins {
455
- border-bottom: 2px solid var(--nx-table-line);
456
- }
457
-
458
- .nx-table__pin-cell {
503
+ .nx-table__section-cell {
459
504
  padding: 0;
460
505
  border-bottom: 1px solid var(--nx-table-line);
461
506
  background: var(--nx-table-head-bg);
462
507
  }
463
508
 
464
- .nx-table__pin-toggle {
509
+ .nx-table__section-toggle {
465
510
  display: flex;
466
511
  align-items: center;
467
512
  gap: var(--nx-space-2);
@@ -479,54 +524,54 @@
479
524
  transition: background-color var(--nx-duration);
480
525
  }
481
526
 
482
- .nx-table__pin-toggle:hover {
527
+ .nx-table__section-toggle:hover {
483
528
  background: color-mix(in oklab, var(--nx-text) 5%, transparent);
484
529
  }
485
530
 
486
- .nx-table__pin-toggle:focus-visible {
531
+ .nx-table__section-toggle:focus-visible {
487
532
  outline: none;
488
533
  box-shadow: inset 0 0 0 2px var(--nx-primary);
489
534
  }
490
535
 
491
- .nx-table__pin-caret {
536
+ .nx-table__section-caret,
537
+ .nx-table__section-icon,
538
+ .nx-table__mark {
492
539
  flex-shrink: 0;
493
540
  width: 15px;
494
541
  height: 15px;
495
542
  color: var(--nx-text-muted);
496
- transition: transform var(--nx-duration);
497
543
  }
498
544
 
499
- .nx-table__pins[data-open] .nx-table__pin-caret {
500
- transform: rotate(90deg);
545
+ /* A row's marks, where its number would be: one under the other, in the line a single mark takes. */
546
+ .nx-table__marks {
547
+ display: inline-flex;
548
+ flex-direction: column;
549
+ align-items: center;
550
+ gap: 2px;
501
551
  }
502
552
 
503
- .nx-table__pin-icon {
504
- flex-shrink: 0;
505
- width: 15px;
506
- height: 15px;
507
- color: var(--nx-text-muted);
553
+ .nx-table__section-caret {
554
+ transition: transform var(--nx-duration);
555
+ }
556
+
557
+ .nx-table__section[data-open] .nx-table__section-caret {
558
+ transform: rotate(90deg);
508
559
  }
509
560
 
510
- .nx-table__pin-count {
561
+ .nx-table__section-count {
511
562
  color: var(--nx-text-muted);
512
563
  font-weight: 400;
513
564
  font-variant-numeric: tabular-nums;
514
565
  }
515
566
 
516
- .nx-table__pin-loading {
567
+ .nx-table__section-loading {
517
568
  width: 14px;
518
569
  height: 14px;
519
570
  color: var(--nx-text-muted);
520
571
  }
521
572
 
522
- .nx-table__pin-mark {
523
- width: 15px;
524
- height: 15px;
525
- color: var(--nx-text-muted);
526
- }
527
-
528
- [data-nx-motion="reduce"] .nx-table__pin-caret,
529
- [data-nx-motion="reduce"] .nx-table__pin-toggle {
573
+ [data-nx-motion="reduce"] .nx-table__section-caret,
574
+ [data-nx-motion="reduce"] .nx-table__section-toggle {
530
575
  transition: none;
531
576
  }
532
577
 
@@ -859,6 +904,16 @@
859
904
  --nx-tag-tone: var(--nx-info);
860
905
  }
861
906
 
907
+ /* A tag after a value (a column's `tag`): "Asia" then "Trashed", a word's space apart. */
908
+ .nx-table__tag--after {
909
+ margin-inline-start: var(--nx-space-2);
910
+ }
911
+
912
+ /* A word with no state to tell (a keyword in a record's card): the tag without its dot. */
913
+ .nx-table__tag--plain::before {
914
+ content: none;
915
+ }
916
+
862
917
  /* Placeholders while the first rows load. */
863
918
  .nx-table__bone {
864
919
  display: block;
@@ -884,7 +939,15 @@
884
939
  background: var(--nx-table-bg);
885
940
  }
886
941
 
942
+ /*
943
+ * The cell spans every column, so on a table wider than its frame the message is held to the part on screen:
944
+ * sticky at the start edge and as wide as the scroller shows (DataTable measures it into --nx-table-view-w).
945
+ */
887
946
  .nx-table__message {
947
+ position: sticky;
948
+ inset-inline-start: 0;
949
+ box-sizing: border-box;
950
+ width: var(--nx-table-view-w, 100%);
888
951
  display: flex;
889
952
  flex-direction: column;
890
953
  align-items: center;
@@ -55,6 +55,11 @@
55
55
  * The scroller runs to the panel's right wall, so its scroll bar is the wall's and not a padding's width inside it,
56
56
  * and it carries the panel's right padding inside itself; the panel's other children keep the same inset with a
57
57
  * margin. Its right corners take the panel's rounding, so the bar is cut by the curve instead of drawn over it.
58
+ * A scroller cuts off what reaches past its edge, and a focus ring is drawn outside its box (3px; a checkbox's
59
+ * outline 4px). So on its other three sides it reaches 4px into the panel's padding and pads itself by as much:
60
+ * what it holds stays where it was, and a field at its edge shows its whole ring (every panel's padding is 4px or
61
+ * more). Focus scrolls a field as far clear of the edges (`scroll-padding`), so one left flush with an edge by a
62
+ * scroll shows its ring too.
58
63
  */
59
64
  .nx-popover:has(> .nx-popover__scroll) {
60
65
  max-height: var(--nx-popover-room, calc(100vh - 16px));
@@ -74,12 +79,31 @@
74
79
  .nx-popover__scroll {
75
80
  flex: 1 1 auto;
76
81
  min-height: 0;
82
+ margin: calc(var(--nx-space-1) * -1);
83
+ margin-right: 0;
84
+ padding: var(--nx-space-1);
77
85
  padding-right: var(--nx-popover-pad, var(--nx-space-4));
86
+ scroll-padding: var(--nx-space-1);
78
87
  overflow: hidden auto;
79
88
  overscroll-behavior: contain;
80
89
  border-radius: 0 calc(var(--nx-radius-lg) - 1px) calc(var(--nx-radius-lg) - 1px) 0;
81
90
  }
82
91
 
92
+ /*
93
+ * Where the scroller reaches one of the panel's left corners (it is the panel's first or last child, as a menu's
94
+ * list is), its own corner follows the panel's curve: the panel's inner radius less the room between their edges,
95
+ * which is the panel's padding less the 4px taken back. A menu's 4px puts the two edges together, so its corner takes
96
+ * the whole 9px; an 8px padding leaves 5px, the theme panel's 16px none. Square, a scrolled item's highlight ran
97
+ * over the rounded border.
98
+ */
99
+ .nx-popover > .nx-popover__scroll:first-child {
100
+ border-top-left-radius: max(0px, var(--nx-radius-lg) - 1px - (var(--nx-popover-pad, var(--nx-space-4)) - var(--nx-space-1)));
101
+ }
102
+
103
+ .nx-popover > .nx-popover__scroll:last-child {
104
+ border-bottom-left-radius: max(0px, var(--nx-radius-lg) - 1px - (var(--nx-popover-pad, var(--nx-space-4)) - var(--nx-space-1)));
105
+ }
106
+
83
107
  /*
84
108
  * The arrow that ties a panel to the button or the place it opened from: an isosceles tail, 26px across and 11px
85
109
  * deep, with a slightly rounded tip. It is drawn in two layers, because a shape with a line along two of its sides
@@ -130,19 +154,45 @@
130
154
  transform: scaleY(-1);
131
155
  }
132
156
 
157
+ /* Wide enough for the sections to sit two to a row. */
133
158
  .nx-theme-switcher__panel {
134
159
  position: fixed;
135
160
  inset: auto;
136
- width: min(340px, calc(100vw - 16px));
161
+ width: min(680px, calc(100vw - 16px));
137
162
  }
138
163
 
139
164
  .nx-theme-picker,
140
- .nx-theme-switcher__body {
165
+ .nx-theme-switcher__body,
166
+ .nx-theme-picker__block {
141
167
  display: grid;
142
168
  gap: var(--nx-space-4);
143
169
  }
144
170
 
145
- /* A section of the preferences: a line above it and its name, a step stronger than the small headings under it. */
171
+ /*
172
+ * In the panel every section is a cell of one grid: two to a row, the theme across both, and one under the other
173
+ * where the window is narrow. The two pickers' own wrappers step aside so their sections share that grid.
174
+ */
175
+ .nx-theme-switcher__body {
176
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
177
+ align-items: start;
178
+ column-gap: var(--nx-space-6);
179
+ }
180
+
181
+ .nx-theme-switcher__body > .nx-theme-picker {
182
+ display: contents;
183
+ }
184
+
185
+ .nx-theme-picker__block {
186
+ align-content: start;
187
+ min-width: 0;
188
+ gap: var(--nx-space-3);
189
+ }
190
+
191
+ .nx-theme-picker__block--wide {
192
+ grid-column: 1 / -1;
193
+ }
194
+
195
+ /* A section's name, a line above it, a step stronger than the small headings under it. */
146
196
  .nx-theme-picker__section {
147
197
  margin: 0;
148
198
  padding-top: var(--nx-space-4);
@@ -152,6 +202,12 @@
152
202
  font-weight: 600;
153
203
  }
154
204
 
205
+ /* The first section starts at the top of its picker, with nothing above it to set it off from. */
206
+ .nx-theme-picker:first-child > .nx-theme-picker__block:first-child > .nx-theme-picker__section {
207
+ padding-top: 0;
208
+ border-top: 0;
209
+ }
210
+
155
211
  .nx-theme-picker__group {
156
212
  min-width: 0;
157
213
  margin: 0;