nexus-shared 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/CHANGELOG.md +1564 -0
  2. package/README.md +1705 -1123
  3. package/dist/Client.Index.d.ts +14 -0
  4. package/dist/Client.Index.js +14 -0
  5. package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
  6. package/dist/Components/Documents/DocumentFrame.js +83 -0
  7. package/dist/Components/Documents/Menu.d.ts +4 -0
  8. package/dist/Components/Documents/Menu.js +40 -24
  9. package/dist/Components/Documents/PrintDocument.d.ts +95 -0
  10. package/dist/Components/Documents/PrintDocument.js +62 -0
  11. package/dist/Components/Documents/Tabs.d.ts +59 -0
  12. package/dist/Components/Documents/Tabs.js +70 -0
  13. package/dist/Components/Forms/ApiForm.js +19 -2
  14. package/dist/Components/Forms/Crud.d.ts +2 -2
  15. package/dist/Components/Forms/Crud.js +576 -243
  16. package/dist/Components/Forms/CrudActions.d.ts +136 -0
  17. package/dist/Components/Forms/CrudActions.js +184 -0
  18. package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
  19. package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
  20. package/dist/Components/Forms/CrudParts.d.ts +30 -2
  21. package/dist/Components/Forms/CrudParts.js +55 -5
  22. package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
  23. package/dist/Components/Forms/CrudRowForms.js +114 -0
  24. package/dist/Components/Forms/CrudScopes.d.ts +28 -0
  25. package/dist/Components/Forms/CrudScopes.js +76 -0
  26. package/dist/Components/Forms/CrudTree.d.ts +90 -0
  27. package/dist/Components/Forms/CrudTree.js +363 -0
  28. package/dist/Components/Forms/CrudWorkspace.d.ts +63 -0
  29. package/dist/Components/Forms/CrudWorkspace.js +169 -0
  30. package/dist/Components/Forms/ExcelImport.js +3 -3
  31. package/dist/Components/Forms/Form.d.ts +4 -4
  32. package/dist/Components/Forms/Form.js +123 -17
  33. package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
  34. package/dist/Components/Forms/FormLeaveGuard.js +65 -0
  35. package/dist/Components/Forms/RecordFlows.d.ts +50 -0
  36. package/dist/Components/Forms/RecordFlows.js +282 -0
  37. package/dist/Components/Forms/SubmitForm.d.ts +37 -2
  38. package/dist/Components/Forms/SubmitForm.js +195 -10
  39. package/dist/Components/Inputs/CheckBox.js +1 -1
  40. package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
  41. package/dist/Components/Inputs/ChoiceField.js +28 -2
  42. package/dist/Components/Inputs/Dropdown.d.ts +13 -0
  43. package/dist/Components/Inputs/Dropdown.js +5 -5
  44. package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
  45. package/dist/Components/Inputs/DropdownParts.js +16 -4
  46. package/dist/Components/Inputs/Field.d.ts +7 -1
  47. package/dist/Components/Inputs/Field.js +2 -2
  48. package/dist/Components/Inputs/GroupForm.d.ts +6 -6
  49. package/dist/Components/Inputs/GroupForm.js +10 -11
  50. package/dist/Components/Inputs/InputRenderer.d.ts +9 -6
  51. package/dist/Components/Inputs/InputRenderer.js +37 -32
  52. package/dist/Components/Inputs/PictureInput.d.ts +41 -0
  53. package/dist/Components/Inputs/PictureInput.js +70 -0
  54. package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
  55. package/dist/Components/Inputs/RadioGroup.js +8 -3
  56. package/dist/Components/Inputs/RowsInput.d.ts +1 -1
  57. package/dist/Components/Inputs/RowsInput.js +18 -3
  58. package/dist/Components/Inputs/TabularForm.d.ts +5 -5
  59. package/dist/Components/Inputs/TabularForm.js +29 -29
  60. package/dist/Components/Inputs/TextArea.d.ts +6 -0
  61. package/dist/Components/Inputs/TextArea.js +11 -2
  62. package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
  63. package/dist/Components/Inputs/TextBoxTypes.js +17 -0
  64. package/dist/Components/Inputs/TreeView.d.ts +53 -2
  65. package/dist/Components/Inputs/TreeView.js +147 -13
  66. package/dist/Components/Layouts/MessageBox.d.ts +15 -2
  67. package/dist/Components/Layouts/MessageBox.js +56 -7
  68. package/dist/Components/Layouts/Popup.d.ts +13 -6
  69. package/dist/Components/Layouts/Popup.js +37 -9
  70. package/dist/Components/Layouts/PopupDock.d.ts +3 -1
  71. package/dist/Components/Layouts/PopupDock.js +2 -1
  72. package/dist/Components/Layouts/PopupHost.js +5 -4
  73. package/dist/Components/Viewers/DataTable.js +150 -48
  74. package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
  75. package/dist/Components/Viewers/DataTableFilters.js +17 -2
  76. package/dist/Components/Viewers/DataTableParts.d.ts +21 -5
  77. package/dist/Components/Viewers/DataTableParts.js +26 -14
  78. package/dist/Components/Viewers/OwnerFiles.d.ts +175 -0
  79. package/dist/Components/Viewers/OwnerFiles.js +589 -0
  80. package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
  81. package/dist/Components/Viewers/QrCodeImage.js +15 -0
  82. package/dist/Components/Viewers/RecordCards.d.ts +16 -0
  83. package/dist/Components/Viewers/RecordCards.js +509 -0
  84. package/dist/Helpers/BusyHooks.d.ts +6 -0
  85. package/dist/Helpers/BusyHooks.js +39 -0
  86. package/dist/Helpers/CrudBackend.d.ts +20 -2
  87. package/dist/Helpers/CrudBackend.js +46 -2
  88. package/dist/Helpers/CrudColumns.d.ts +53 -6
  89. package/dist/Helpers/CrudColumns.js +98 -7
  90. package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
  91. package/dist/Helpers/CrudDetailsTabs.js +126 -0
  92. package/dist/Helpers/CrudHelpers.d.ts +81 -2
  93. package/dist/Helpers/CrudHelpers.js +195 -0
  94. package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
  95. package/dist/Helpers/CrudNarrowPick.js +157 -0
  96. package/dist/Helpers/CrudRowForms.d.ts +15 -0
  97. package/dist/Helpers/CrudRowForms.js +43 -0
  98. package/dist/Helpers/CrudScopes.d.ts +32 -0
  99. package/dist/Helpers/CrudScopes.js +79 -0
  100. package/dist/Helpers/DropdownHelpers.d.ts +3 -1
  101. package/dist/Helpers/DropdownHelpers.js +6 -2
  102. package/dist/Helpers/ExcelHelpers.d.ts +1 -1
  103. package/dist/Helpers/ExcelHelpers.js +12 -11
  104. package/dist/Helpers/FileAdapter.d.ts +78 -0
  105. package/dist/Helpers/FileAdapter.js +102 -0
  106. package/dist/Helpers/FilePrimaries.d.ts +48 -0
  107. package/dist/Helpers/FilePrimaries.js +138 -0
  108. package/dist/Helpers/FormConfirm.d.ts +32 -0
  109. package/dist/Helpers/FormConfirm.js +40 -0
  110. package/dist/Helpers/FormStore.d.ts +90 -5
  111. package/dist/Helpers/FormStore.js +318 -22
  112. package/dist/Helpers/InputConditions.d.ts +50 -0
  113. package/dist/Helpers/InputConditions.js +168 -0
  114. package/dist/Helpers/InputMemory.d.ts +106 -0
  115. package/dist/Helpers/InputMemory.js +385 -0
  116. package/dist/Helpers/InputNarrowing.d.ts +18 -0
  117. package/dist/Helpers/InputNarrowing.js +67 -0
  118. package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
  119. package/dist/Helpers/InputParamsHelpers.js +157 -74
  120. package/dist/Helpers/NexusDriveAdapter.d.ts +55 -0
  121. package/dist/Helpers/NexusDriveAdapter.js +207 -0
  122. package/dist/Helpers/NumberHelpers.d.ts +95 -0
  123. package/dist/Helpers/NumberHelpers.js +126 -0
  124. package/dist/Helpers/Permissions.js +2 -1
  125. package/dist/Helpers/QrCode.d.ts +26 -0
  126. package/dist/Helpers/QrCode.js +463 -0
  127. package/dist/Helpers/RecordCards.d.ts +205 -0
  128. package/dist/Helpers/RecordCards.js +378 -0
  129. package/dist/Helpers/RequestCache.d.ts +14 -2
  130. package/dist/Helpers/RequestCache.js +22 -3
  131. package/dist/Helpers/RowsStore.d.ts +37 -2
  132. package/dist/Helpers/RowsStore.js +128 -10
  133. package/dist/Helpers/ServerPreview.d.ts +69 -0
  134. package/dist/Helpers/ServerPreview.js +99 -0
  135. package/dist/Helpers/TableColumns.js +4 -3
  136. package/dist/Helpers/TableExport.d.ts +7 -1
  137. package/dist/Helpers/TableExport.js +9 -3
  138. package/dist/Helpers/TableFilters.d.ts +1 -1
  139. package/dist/Helpers/TableFilters.js +24 -23
  140. package/dist/Helpers/TableHelpers.d.ts +23 -2
  141. package/dist/Helpers/TableHelpers.js +51 -10
  142. package/dist/Helpers/TableJoins.d.ts +13 -0
  143. package/dist/Helpers/TableJoins.js +34 -0
  144. package/dist/Helpers/TableStore.d.ts +21 -1
  145. package/dist/Helpers/TableStore.js +64 -7
  146. package/dist/Helpers/TreeStore.d.ts +81 -0
  147. package/dist/Helpers/TreeStore.js +365 -0
  148. package/dist/Interfaces/CrudInterfaces.d.ts +465 -5
  149. package/dist/Interfaces/FileInterfaces.d.ts +162 -0
  150. package/dist/Interfaces/FileInterfaces.js +4 -0
  151. package/dist/Interfaces/FormInterfaces.d.ts +391 -30
  152. package/dist/Interfaces/FormInterfaces.js +79 -1
  153. package/dist/Interfaces/InputInterfaces.d.ts +15 -3
  154. package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
  155. package/dist/Interfaces/RecordInterfaces.js +1 -0
  156. package/dist/Interfaces/TableInterfaces.d.ts +74 -1
  157. package/dist/Services/ExcelApi.js +5 -0
  158. package/dist/Services/FileApi.d.ts +13 -0
  159. package/dist/Services/FileApi.js +29 -0
  160. package/dist/Services/ImageEditorLoader.d.ts +14 -0
  161. package/dist/Services/ImageEditorLoader.js +51 -0
  162. package/dist/Services/JoinSources.d.ts +37 -5
  163. package/dist/Services/JoinSources.js +90 -14
  164. package/dist/Services/ModuleImport.d.ts +7 -0
  165. package/dist/Services/ModuleImport.js +22 -0
  166. package/dist/Shared.Index.d.ts +16 -0
  167. package/dist/Shared.Index.js +16 -0
  168. package/package.json +7 -2
  169. package/src/Styles/Nexus.Avatar.css +6 -5
  170. package/src/Styles/Nexus.Button.css +10 -4
  171. package/src/Styles/Nexus.Choice.css +16 -0
  172. package/src/Styles/Nexus.Crud.css +383 -9
  173. package/src/Styles/Nexus.Dropdown.css +8 -0
  174. package/src/Styles/Nexus.Files.css +410 -0
  175. package/src/Styles/Nexus.Form.css +5 -2
  176. package/src/Styles/Nexus.Index.css +4 -0
  177. package/src/Styles/Nexus.Input.css +42 -0
  178. package/src/Styles/Nexus.Menu.css +10 -1
  179. package/src/Styles/Nexus.Popup.css +77 -1
  180. package/src/Styles/Nexus.Print.css +288 -0
  181. package/src/Styles/Nexus.QrCode.css +12 -0
  182. package/src/Styles/Nexus.Record.css +430 -0
  183. package/src/Styles/Nexus.Tab.Buttons.css +87 -22
  184. package/src/Styles/Nexus.Table.css +67 -5
  185. package/src/Styles/Nexus.Themes.css +12 -6
  186. package/src/Styles/Nexus.Tokens.css +8 -3
  187. package/src/Styles/Nexus.Tree.css +31 -1
@@ -105,25 +105,25 @@
105
105
  transition: color var(--nx-duration);
106
106
  }
107
107
 
108
- .nx-tabs__option:hover:not(:has(:checked, :disabled)) {
108
+ .nx-tabs__option:hover:not(:has(:checked, :disabled), [aria-selected="true"], :disabled) {
109
109
  background: var(--nx-tabs-hover);
110
110
  color: var(--nx-text);
111
111
  }
112
112
 
113
- .nx-tabs__option:has(:checked) {
113
+ .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) {
114
114
  color: var(--nx-tabs-active-color);
115
115
  cursor: default;
116
116
  }
117
117
 
118
- .nx-tabs__option:has(:checked) .nx-icon {
118
+ .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) .nx-icon {
119
119
  color: var(--nx-tabs-active-icon);
120
120
  }
121
121
 
122
- .nx-tabs__option:has(:focus-visible) {
122
+ .nx-tabs__option:is(:has(:focus-visible), :focus-visible) {
123
123
  box-shadow: var(--nx-focus-ring);
124
124
  }
125
125
 
126
- .nx-tabs__option:has(:disabled) {
126
+ .nx-tabs__option:is(:has(:disabled), :disabled) {
127
127
  cursor: not-allowed;
128
128
  opacity: 0.45;
129
129
  }
@@ -178,7 +178,7 @@
178
178
  padding 240ms cubic-bezier(0.2, 0, 0, 1);
179
179
  }
180
180
 
181
- .nx-tabs--labels-selected .nx-tabs__option:has(:checked) {
181
+ .nx-tabs--labels-selected .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) {
182
182
  padding: 0 var(--nx-space-3);
183
183
  background: var(--nx-tabs-indicator);
184
184
  box-shadow:
@@ -187,7 +187,7 @@
187
187
  }
188
188
 
189
189
  /* In a full-width group, the selected option takes the free space. */
190
- .nx-tabs--labels-selected.nx-tabs--block .nx-tabs__option:has(:checked) {
190
+ .nx-tabs--labels-selected.nx-tabs--block .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) {
191
191
  flex: 1 1 auto;
192
192
  }
193
193
 
@@ -202,7 +202,7 @@
202
202
  margin 240ms cubic-bezier(0.2, 0, 0, 1);
203
203
  }
204
204
 
205
- .nx-tabs--labels-selected .nx-tabs__option:has(:checked) .nx-tabs__label {
205
+ .nx-tabs--labels-selected .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) .nx-tabs__label {
206
206
  grid-template-columns: 1fr;
207
207
  margin-inline-start: 6px;
208
208
  }
@@ -239,9 +239,10 @@
239
239
  * its own, exactly as the one choice's indicator is, and an option that is not chosen sits flat on the track. The
240
240
  * options wrap onto more rows when they do not fit, each row as tall as a one-row track's options, so a picker of
241
241
  * 26 letters is one track of several rows rather than a line that runs off a phone. A one-character option is
242
- * square, so a row of letters lines up.
242
+ * square, so a row of letters lines up. A choice group drawn as tabs (`RadioGroup design="tabs"`, `nx-tabs--wrap`)
243
+ * is drawn the same way: its choice lives in the inputs, not in a value the track could slide an indicator to.
243
244
  */
244
- .nx-tabs--multiple {
245
+ :is(.nx-tabs--multiple, .nx-tabs--wrap) {
245
246
  --nx-tabs-option-height: calc(var(--nx-tabs-height) - 2 * var(--nx-tabs-pad) - 2px);
246
247
 
247
248
  display: inline-flex;
@@ -251,26 +252,30 @@
251
252
  height: auto;
252
253
  }
253
254
 
254
- .nx-tabs--multiple.nx-tabs--block {
255
+ :is(.nx-tabs--multiple, .nx-tabs--wrap).nx-tabs--block {
255
256
  display: flex;
256
257
  }
257
258
 
258
- .nx-tabs--multiple .nx-tabs__option {
259
+ :is(.nx-tabs--multiple, .nx-tabs--wrap) .nx-tabs__option {
259
260
  flex: 0 0 auto;
260
261
  min-width: var(--nx-tabs-option-height);
261
262
  height: var(--nx-tabs-option-height);
262
263
  }
263
264
 
264
- /* A chosen option can be unchosen, so it keeps the pointer, and it keeps its raised look under the pointer too. */
265
- .nx-tabs--multiple .nx-tabs__option:has(:checked) {
265
+ /* A chosen option keeps its raised look under the pointer too. */
266
+ :is(.nx-tabs--multiple, .nx-tabs--wrap) .nx-tabs__option:has(:checked) {
266
267
  background: var(--nx-tabs-indicator);
267
268
  box-shadow:
268
269
  inset 0 0 0 1px var(--nx-tabs-indicator-border),
269
270
  var(--nx-tabs-indicator-shadow);
271
+ }
272
+
273
+ /* One of several can be unchosen, so it keeps the pointer. */
274
+ .nx-tabs--multiple .nx-tabs__option:has(:checked) {
270
275
  cursor: pointer;
271
276
  }
272
277
 
273
- .nx-tabs--multiple .nx-tabs__option:has(:checked):has(:focus-visible) {
278
+ :is(.nx-tabs--multiple, .nx-tabs--wrap) .nx-tabs__option:has(:checked):has(:focus-visible) {
274
279
  box-shadow:
275
280
  inset 0 0 0 1px var(--nx-tabs-indicator-border),
276
281
  var(--nx-focus-ring);
@@ -278,7 +283,7 @@
278
283
 
279
284
  /* A finger needs more than a small button's 22px: on a touch screen every option is a medium control's height, square at the least. */
280
285
  @media (pointer: coarse) {
281
- .nx-tabs--multiple {
286
+ :is(.nx-tabs--multiple, .nx-tabs--wrap) {
282
287
  --nx-tabs-option-height: var(--nx-control-md);
283
288
  }
284
289
  }
@@ -340,21 +345,21 @@
340
345
  transform 200ms cubic-bezier(0.2, 0, 0, 1);
341
346
  }
342
347
 
343
- .nx-tabs--list .nx-tabs__option:has(:checked) {
348
+ .nx-tabs--list .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) {
344
349
  background: var(--nx-tabs-list-selected);
345
350
  }
346
351
 
347
352
  /* A list of several choices: a chosen row can be unchosen, so it keeps the pointer. */
348
- .nx-tabs--list:not([role="radiogroup"]) .nx-tabs__option:has(:checked) {
353
+ .nx-tabs--list:not([role="radiogroup"], [role="tablist"]) .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) {
349
354
  cursor: pointer;
350
355
  }
351
356
 
352
- .nx-tabs--list .nx-tabs__option:has(:checked)::before {
357
+ .nx-tabs--list .nx-tabs__option:is(:has(:checked), [aria-selected="true"])::before {
353
358
  opacity: 1;
354
359
  transform: none;
355
360
  }
356
361
 
357
- .nx-tabs--list .nx-tabs__option:has(:checked) .nx-tabs__text {
362
+ .nx-tabs--list .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) .nx-tabs__text {
358
363
  font-weight: 600;
359
364
  }
360
365
 
@@ -404,7 +409,7 @@
404
409
  --nx-tabs-active-icon: var(--nx-primary);
405
410
  }
406
411
 
407
- .nx-tabs--list.nx-tabs--primary .nx-tabs__option:has(:checked) .nx-tabs__badge {
412
+ .nx-tabs--list.nx-tabs--primary .nx-tabs__option:is(:has(:checked), [aria-selected="true"]) .nx-tabs__badge {
408
413
  background: var(--nx-primary-solid);
409
414
  color: var(--nx-primary-text);
410
415
  }
@@ -423,8 +428,68 @@
423
428
  --nx-tabs-list-gap: 12px;
424
429
  }
425
430
 
431
+ /*
432
+ * Tabs (`Tabs`): the same options as buttons with role="tab", marked by aria-selected rather than a checked radio.
433
+ * Only what a button brings of its own is taken away; the ring is the options' own box-shadow.
434
+ */
435
+ button.nx-tabs__option {
436
+ margin: 0;
437
+ border: 0;
438
+ background: none;
439
+ font-family: inherit;
440
+ outline: none;
441
+ }
442
+
443
+ [role="tablist"] .nx-tabs__option[aria-selected="true"] {
444
+ cursor: default;
445
+ }
446
+
447
+ /*
448
+ * design="auto": the list, until its container (a popup's workspace) is narrower than 720px; there the same rows lie
449
+ * in one strip that scrolls sideways, the bar under the chosen one. The strip keeps room for the focus ring, which a
450
+ * scrolling box would cut off.
451
+ */
452
+ @container (max-width: 719.98px) {
453
+ .nx-tabs--auto {
454
+ grid-auto-columns: max-content;
455
+ grid-auto-flow: column;
456
+ padding: 3px;
457
+ overflow-x: auto;
458
+ overscroll-behavior-x: contain;
459
+ scrollbar-width: none;
460
+ }
461
+
462
+ .nx-tabs--auto .nx-tabs__option {
463
+ white-space: nowrap;
464
+ }
465
+
466
+ .nx-tabs--auto .nx-tabs__option::before {
467
+ top: auto;
468
+ right: 10px;
469
+ bottom: 0;
470
+ left: 10px;
471
+ width: auto;
472
+ height: 3px;
473
+ transform: scaleX(0.3);
474
+ }
475
+
476
+ .nx-tabs--auto .nx-tabs__description {
477
+ display: none;
478
+ }
479
+ }
480
+
481
+ /* A tab's panel: it takes the focus when it holds nothing that does, so its ring is drawn inside it. */
482
+ .nx-tab-panel {
483
+ min-width: 0;
484
+ }
485
+
486
+ .nx-tab-panel:focus-visible {
487
+ outline: 2px solid var(--nx-primary);
488
+ outline-offset: -2px;
489
+ }
490
+
426
491
  [data-nx-motion="reduce"] .nx-tabs__indicator,
427
- [data-nx-motion="reduce"] .nx-tabs--multiple .nx-tabs__option,
492
+ [data-nx-motion="reduce"] :is(.nx-tabs--multiple, .nx-tabs--wrap) .nx-tabs__option,
428
493
  [data-nx-motion="reduce"] .nx-tabs--labels-selected :is(.nx-tabs__option, .nx-tabs__label),
429
494
  [data-nx-motion="reduce"] .nx-tabs--list .nx-tabs__option::before {
430
495
  transition: none;
@@ -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);
@@ -860,6 +909,11 @@
860
909
  margin-inline-start: var(--nx-space-2);
861
910
  }
862
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
+
863
917
  /* Placeholders while the first rows load. */
864
918
  .nx-table__bone {
865
919
  display: block;
@@ -885,7 +939,15 @@
885
939
  background: var(--nx-table-bg);
886
940
  }
887
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
+ */
888
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%);
889
951
  display: flex;
890
952
  flex-direction: column;
891
953
  align-items: center;
@@ -6,7 +6,9 @@
6
6
  * Colors follow each theme's official palette, darkened or lightened where needed to meet WCAG AA contrast.
7
7
  * --nx-primary and --nx-danger are the accent as text and lines (light in dark mode, for contrast on dark panels);
8
8
  * --nx-primary-solid and --nx-danger-solid are the same hue as a fill behind white text (buttons, selected days,
9
- * checks), a darker shade in dark mode. --nx-primary-text and --nx-danger-text sit on the solid fills.
9
+ * checks), a darker shade in dark mode. --nx-primary-text and --nx-danger-text sit on the solid fills: a light
10
+ * label in both modes (white, or the theme's own near-white), since a fill under the pointer or pressed darkens
11
+ * (Nexus.Tokens.css). A fill keeps 2:1 on --nx-surface at rest and under the pointer.
10
12
  * After changing a color, run: npm run check:themes
11
13
  *
12
14
  * A new theme is one more block with its own data-nx-theme value, added to NEXUS_THEMES or passed to the theme components.
@@ -57,7 +59,11 @@
57
59
  --nx-info: light-dark(#0964d0, #4493f8);
58
60
  }
59
61
 
60
- /* Nord: arctic, north-bluish calm. */
62
+ /*
63
+ * Nord: arctic, north-bluish calm. In dark mode its fills carry pure white: its panel (#2e3440) is lighter than
64
+ * other themes', and with snow (#eceff4) as the label a fill had no room to darken under the pointer and still
65
+ * stand 2:1 off the panel.
66
+ */
61
67
  [data-nx-theme="nord"] {
62
68
  --nx-bg: light-dark(#e5e9f0, #242933);
63
69
  --nx-surface: light-dark(#eceff4, #2e3440);
@@ -69,11 +75,11 @@
69
75
  --nx-text-muted: light-dark(#4c566a, #b9c2d0);
70
76
  --nx-text-subtle: light-dark(#7b88a1, #7b88a1);
71
77
  --nx-primary: light-dark(#446184, #88c0d0);
72
- --nx-primary-solid: light-dark(#446184, #4b717c);
73
- --nx-primary-text: light-dark(#eceff4, #eceff4);
78
+ --nx-primary-solid: light-dark(#446184, #557b86);
79
+ --nx-primary-text: light-dark(#eceff4, #ffffff);
74
80
  --nx-danger: light-dark(#9e4049, #dba6ab);
75
- --nx-danger-solid: light-dark(#9e4049, #8e5e63);
76
- --nx-danger-text: light-dark(#eceff4, #eceff4);
81
+ --nx-danger-solid: light-dark(#9e4049, #99686d);
82
+ --nx-danger-text: light-dark(#eceff4, #ffffff);
77
83
  --nx-success: light-dark(#4e6639, #a3be8c);
78
84
  --nx-warning: light-dark(#954931, #ebcb8b);
79
85
  --nx-info: light-dark(#406283, #9cb5ce);
@@ -68,13 +68,18 @@
68
68
  /*
69
69
  * Derived colors, declared wherever a theme applies, so an element with its own data-nx-theme
70
70
  * (a theme preview, for example) gets hover and soft colors from that theme.
71
- * Mixing toward the text color darkens in light mode and lightens in dark mode.
71
+ *
72
+ * A solid fill under the pointer (--nx-primary-hover, --nx-danger-hover; a button pressed too) is the fill with 12%
73
+ * black mixed in, in every theme and in light and dark mode alike. The fill sits behind the white --nx-*-text label,
74
+ * so the darker step only raises the label's contrast: 6:1 or more, from 4.6:1 at rest. It is about the same step in
75
+ * every theme (an OKLab difference of 5.8 to 7.4, x100). Mixing toward the text color lightened the fill in dark
76
+ * mode, to 3.5:1. npm run check:themes reads these two lines and checks both.
72
77
  */
73
78
  :root,
74
79
  [data-nx-theme] {
75
- --nx-primary-hover: color-mix(in oklab, var(--nx-primary-solid), var(--nx-text) 16%);
80
+ --nx-primary-hover: color-mix(in oklab, var(--nx-primary-solid), black 12%);
76
81
  --nx-primary-soft: color-mix(in oklab, var(--nx-primary) 14%, var(--nx-surface));
77
- --nx-danger-hover: color-mix(in oklab, var(--nx-danger-solid), var(--nx-text) 16%);
82
+ --nx-danger-hover: color-mix(in oklab, var(--nx-danger-solid), black 12%);
78
83
  --nx-danger-soft: color-mix(in oklab, var(--nx-danger) 14%, var(--nx-surface));
79
84
  --nx-success-soft: color-mix(in oklab, var(--nx-success) 14%, var(--nx-surface));
80
85
  --nx-warning-soft: color-mix(in oklab, var(--nx-warning) 14%, var(--nx-surface));
@@ -103,6 +103,18 @@
103
103
  margin-left: var(--nx-space-1);
104
104
  }
105
105
 
106
+ /* A tree that fills (`fill`): the height it is given, the toolbar on top and the rows scrolling under it. */
107
+ .nx-tree-field--fill {
108
+ height: 100%;
109
+ min-height: 0;
110
+ }
111
+
112
+ .nx-tree-field--fill > .nx-tree,
113
+ .nx-tree-field--fill .nx-tree__viewport {
114
+ flex: 1 1 auto;
115
+ min-height: 0;
116
+ }
117
+
106
118
  /* Rows */
107
119
  .nx-tree__viewport {
108
120
  position: relative;
@@ -158,11 +170,29 @@
158
170
  box-shadow: inset 0 0 0 2px var(--nx-primary);
159
171
  }
160
172
 
161
- .nx-tree__row[role="treeitem"]:is(:hover, :focus-visible) {
173
+ .nx-tree__row[role="treeitem"]:is(:hover, :focus-visible),
174
+ .nx-tree__row--selected {
162
175
  --nx-tree-muted: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 35%);
163
176
  --nx-tree-count: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 50%);
164
177
  }
165
178
 
179
+ /* The selected row (`selectedValue`): a single choice's tint, as a dropdown's chosen option has, and a heavier label.
180
+ It stays when focus moves on; under the pointer or with the keyboard's focus the tint deepens, and its muted texts
181
+ move further toward the text color (35% read 4.49:1 there in Catppuccin light). */
182
+ .nx-tree__row--selected {
183
+ background-color: color-mix(in oklab, var(--nx-primary) 10%, transparent);
184
+ }
185
+
186
+ .nx-tree__row--selected[role="treeitem"]:is(:hover, :focus-visible) {
187
+ --nx-tree-muted: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 50%);
188
+ --nx-tree-count: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 65%);
189
+ background-color: color-mix(in oklab, var(--nx-primary) 17%, transparent);
190
+ }
191
+
192
+ .nx-tree__row--selected .nx-tree__label {
193
+ font-weight: 500;
194
+ }
195
+
166
196
  .nx-tree__toggle {
167
197
  display: grid;
168
198
  flex: none;