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,1296 +1,1387 @@
1
- @layer nexus.components {
2
- /*
3
- * Data table: a bar (tabs, search, buttons), a frame holding the table (a header that stays while rows scroll, a
4
- * checkbox column on the left and the actions on the right that stay while columns scroll), and a footer with the
5
- * range and the pager. While rows are selected, a bar with their actions covers the column headings.
6
- */
7
- .nx-table {
8
- --nx-table-bg: var(--nx-surface);
9
- --nx-table-head-bg: color-mix(in oklab, var(--nx-text) 4%, var(--nx-surface));
10
- --nx-table-hover: color-mix(in oklab, var(--nx-text) 3%, var(--nx-surface));
11
- --nx-table-selected: color-mix(in oklab, var(--nx-primary) 11%, var(--nx-surface));
12
- --nx-table-selected-hover: color-mix(in oklab, var(--nx-primary) 16%, var(--nx-surface));
13
- --nx-table-flash: color-mix(in oklab, var(--nx-success) 18%, var(--nx-surface));
14
- --nx-table-line: var(--nx-border);
15
- --nx-table-head-h: 40px;
16
- /* The band of group headings above the columns' own, when any column names a group. */
17
- --nx-table-group-h: 30px;
18
- /* How many rows the header has: 1, or 2 while a band stands above the headings (`data-head-rows`). */
19
- --nx-table-head-rows: 1;
20
- /* The whole header, which the loading bar and the selection bar are measured from. */
21
- --nx-table-head-all: calc(var(--nx-table-head-h) + (var(--nx-table-head-rows) - 1) * var(--nx-table-group-h));
22
- --nx-table-row-h: 44px;
23
- --nx-table-pad-x: 12px;
24
- --nx-table-select-w: 44px;
25
- --nx-table-font: var(--nx-text-md);
26
- --nx-table-edge: light-dark(rgb(16 24 40 / 0.14), rgb(0 0 0 / 0.55));
27
-
28
- container: nx-table / inline-size;
29
- display: flex;
30
- flex-direction: column;
31
- gap: var(--nx-space-3);
32
- min-width: 0;
33
- color: var(--nx-text);
34
- }
35
-
36
- .nx-table[data-head-rows="2"] {
37
- --nx-table-head-rows: 2;
38
- }
39
-
40
- .nx-table--sm {
41
- --nx-table-head-h: 34px;
42
- --nx-table-group-h: 26px;
43
- --nx-table-row-h: 36px;
44
- --nx-table-pad-x: 10px;
45
- --nx-table-select-w: 38px;
46
- --nx-table-font: var(--nx-text-sm);
47
- }
48
-
49
- /* The bar: tabs and filters on the left, the search and buttons on the right. */
50
- .nx-table__bar {
51
- display: flex;
52
- flex-wrap: wrap;
53
- align-items: center;
54
- gap: var(--nx-space-2) var(--nx-space-3);
55
- }
56
-
57
- .nx-table__bar-start {
58
- display: flex;
59
- flex-wrap: wrap;
60
- align-items: center;
61
- gap: var(--nx-space-2);
62
- min-width: 0;
63
- }
64
-
65
- .nx-table__bar-end {
66
- display: flex;
67
- flex: 1 1 auto;
68
- flex-wrap: wrap;
69
- align-items: center;
70
- justify-content: flex-end;
71
- gap: var(--nx-space-2);
72
- min-width: 0;
73
- }
74
-
75
- .nx-table__search {
76
- flex: 0 1 19rem;
77
- min-width: 12rem;
78
- }
79
-
80
- /* Rows per page inside the search box, at its start, with a line between the two. */
81
- .nx-table__search .nx-field__actions--start {
82
- align-self: stretch;
83
- padding-right: 0;
84
- padding-left: 2px;
85
- border-right: 1px solid var(--nx-field-border);
86
- }
87
-
88
- .nx-table__page-size--field.nx-btn {
89
- min-width: 0;
90
- height: calc(var(--nx-field-height) - 8px);
91
- gap: 0;
92
- padding: 0 2px 0 4px;
93
- color: var(--nx-text-muted);
94
- font-variant-numeric: tabular-nums;
95
- }
96
-
97
- .nx-table__page-size--field .nx-icon {
98
- width: 14px;
99
- height: 14px;
100
- }
101
-
102
- .nx-table__page-size--field:hover {
103
- color: var(--nx-text);
104
- }
105
-
106
- @container nx-table (max-width: 560px) {
107
- .nx-table__bar-end {
108
- justify-content: flex-start;
109
- }
110
-
111
- .nx-table__search {
112
- flex: 1 1 100%;
113
- order: -1;
114
- }
115
- }
116
-
117
- /*
118
- * `fill`: the table takes the height it is given instead of growing with its rows. The bar and the footer keep their
119
- * own height, the frame takes the rest, and the rows scroll inside it. Nothing below the table moves when a page
120
- * holds fewer rows, and the page behind it never gains a scrollbar.
121
- */
122
- .nx-table--fill {
123
- height: 100%;
124
- min-height: 0;
125
- }
126
-
127
- .nx-table--fill > .nx-table__frame {
128
- display: flex;
129
- flex: 1 1 auto;
130
- flex-direction: column;
131
- min-height: 0;
132
- }
133
-
134
- .nx-table--fill .nx-table__scroll {
135
- flex: 1 1 auto;
136
- min-height: 0;
137
- /* The room for the scrollbar is kept, so the headings do not shift the moment the rows need one. */
138
- scrollbar-gutter: stable;
139
- }
140
-
141
- /* The frame. */
142
- .nx-table__frame {
143
- position: relative;
144
- min-width: 0;
145
- border: 1px solid var(--nx-table-line);
146
- border-radius: var(--nx-radius-lg);
147
- background: var(--nx-table-bg);
148
- box-shadow: var(--nx-shadow-sm);
149
- overflow: hidden;
150
- }
151
-
152
- .nx-table__scroll {
153
- overflow: auto;
154
- overscroll-behavior-x: contain;
155
- scrollbar-width: thin;
156
- scrollbar-color: color-mix(in oklab, var(--nx-text-subtle) 75%, transparent) transparent;
157
- }
158
-
159
- .nx-table__table {
160
- width: 100%;
161
- border-collapse: separate;
162
- border-spacing: 0;
163
- font-size: var(--nx-table-font);
164
- line-height: 1.45;
165
- }
166
-
167
- /* Cells */
168
- .nx-table__cell,
169
- .nx-table__heading {
170
- box-sizing: border-box;
171
- padding: 4px var(--nx-table-pad-x);
172
- text-align: start;
173
- vertical-align: middle;
174
- }
175
-
176
- .nx-table__cell {
177
- height: var(--nx-table-row-h);
178
- border-bottom: 1px solid var(--nx-table-line);
179
- background: var(--nx-row-bg);
180
- }
181
-
182
- .nx-table__row {
183
- --nx-row-bg: var(--nx-table-bg);
184
- }
185
-
186
- /* The last row's line would double the frame. */
187
- .nx-table__row:last-child > .nx-table__cell {
188
- border-bottom: 0;
189
- }
190
-
191
- .nx-table__row:not(.nx-table__row--message, .nx-table__row--skeleton):hover {
192
- --nx-row-bg: var(--nx-table-hover);
193
- }
194
-
195
- /* A row a click opens says so under the pointer. Only the pointer changes: nothing in the row moves on hover. */
196
- .nx-table__row[data-open] {
197
- cursor: pointer;
198
- }
199
-
200
- .nx-table__row[data-selected] {
201
- --nx-row-bg: var(--nx-table-selected);
202
- }
203
-
204
- .nx-table__row[data-selected]:hover {
205
- --nx-row-bg: var(--nx-table-selected-hover);
206
- }
207
-
208
- /* Muted text on a selected row's tint moves toward the text color, so it keeps 4.5:1 in every theme. */
209
- .nx-table__row[data-selected] :is(.nx-table__number, .nx-table__dash, .nx-table__no) {
210
- color: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 45%);
211
- }
212
-
213
- /* Columns scroll under the pinned ones: a soft shadow shows there is more on that side. */
214
- .nx-table__frame[data-scroll-start] {
215
- --nx-table-start-edge: 8px 0 8px -8px var(--nx-table-edge);
216
- }
217
-
218
- .nx-table__frame[data-scroll-end] {
219
- --nx-table-end-edge: -8px 0 8px -8px var(--nx-table-edge);
220
- }
221
-
222
- .nx-table__row[data-busy] > .nx-table__cell:not(.nx-table__cell--actions) {
223
- color: var(--nx-text-muted);
224
- }
225
-
226
- /* A row just added or saved: a green wash that fades (a steady one with reduced motion, until the mark goes). */
227
- .nx-table__row[data-flash] > .nx-table__cell {
228
- animation: nx-table-flash 1600ms ease-out;
229
- }
230
-
231
- @keyframes nx-table-flash {
232
- from {
233
- background-color: var(--nx-table-flash);
234
- }
235
- }
236
-
237
- .nx-table__cell--start {
238
- text-align: start;
239
- }
240
-
241
- .nx-table__cell--center {
242
- text-align: center;
243
- }
244
-
245
- .nx-table__cell--end {
246
- text-align: end;
247
- font-variant-numeric: tabular-nums;
248
- }
249
-
250
- /* A value ends in "…" at the column's width (24rem when the column sets none). */
251
- .nx-table__value {
252
- display: block;
253
- max-width: var(--nx-col-max, 24rem);
254
- overflow: hidden;
255
- text-overflow: ellipsis;
256
- white-space: nowrap;
257
- }
258
-
259
- .nx-table__cell--wrap .nx-table__value {
260
- max-width: var(--nx-col-max, 32rem);
261
- white-space: normal;
262
- overflow-wrap: anywhere;
263
- }
264
-
265
- .nx-table__cell--end .nx-table__value {
266
- margin-left: auto;
267
- }
268
-
269
- .nx-table__cell--center .nx-table__value {
270
- margin-inline: auto;
271
- }
272
-
273
- /*
274
- * Pinned columns stay while the others scroll: the numbers and checkbox column and the actions always, and
275
- * any column the user pins from its menu. Their distance from the edge is measured from the headings before them.
276
- */
277
- .nx-table__cell[data-pin],
278
- .nx-table__heading[data-pin] {
279
- position: sticky;
280
- z-index: 1;
281
- background: var(--nx-row-bg);
282
- }
283
-
284
- .nx-table__heading[data-pin] {
285
- z-index: 3;
286
- background: var(--nx-table-head-bg);
287
- }
288
-
289
- /* The last pinned column of a side carries the shadow the others scroll under. */
290
- .nx-table__cell[data-pin="left"][data-pin-edge],
291
- .nx-table__heading[data-pin="left"][data-pin-edge] {
292
- box-shadow: var(--nx-table-start-edge, 0 0 #0000);
293
- }
294
-
295
- .nx-table__cell[data-pin="right"][data-pin-edge],
296
- .nx-table__heading[data-pin="right"][data-pin-edge] {
297
- box-shadow: var(--nx-table-end-edge, 0 0 #0000);
298
- }
299
-
300
- /* The first column: the row's number, with its checkbox in the same place. */
301
- .nx-table__cell--index {
302
- width: var(--nx-table-select-w);
303
- min-width: var(--nx-table-select-w);
304
- padding: 0;
305
- text-align: center;
306
- }
307
-
308
- .nx-table__number {
309
- display: inline-flex;
310
- align-items: center;
311
- justify-content: center;
312
- width: 100%;
313
- color: var(--nx-text-muted);
314
- font-size: var(--nx-text-xs);
315
- font-variant-numeric: tabular-nums;
316
- font-weight: 500;
317
- }
318
-
319
- .nx-table__check {
320
- display: inline-flex;
321
- align-items: center;
322
- justify-content: center;
323
- width: 100%;
324
- height: 100%;
325
- min-height: 28px;
326
- }
327
-
328
- .nx-table__check .nx-choice__control {
329
- margin: 0;
330
- }
331
-
332
- /* Both in the one cell: the number, until the row is under the pointer or anything is selected. */
333
- .nx-table__cell--index[data-swap] > .nx-table__number,
334
- .nx-table__cell--index[data-swap] > .nx-table__check {
335
- position: absolute;
336
- inset: 0;
337
- width: auto;
338
- transition: opacity var(--nx-duration);
339
- }
340
-
341
- .nx-table__cell--index[data-swap] > .nx-table__check {
342
- opacity: 0;
343
- }
344
-
345
- .nx-table__row:hover > .nx-table__cell--index[data-swap] > .nx-table__check,
346
- /* The heading's own cell may stand in the band row, so the whole header counts as hovering it. */
347
- .nx-table__thead:hover .nx-table__cell--index[data-swap] > .nx-table__check,
348
- .nx-table__cell--index[data-swap]:focus-within > .nx-table__check,
349
- .nx-table[data-selecting] .nx-table__cell--index[data-swap] > .nx-table__check {
350
- opacity: 1;
351
- }
352
-
353
- .nx-table__row:hover > .nx-table__cell--index[data-swap] > .nx-table__number,
354
- .nx-table__thead:hover .nx-table__cell--index[data-swap] > .nx-table__number,
355
- .nx-table__cell--index[data-swap]:focus-within > .nx-table__number,
356
- .nx-table[data-selecting] .nx-table__cell--index[data-swap] > .nx-table__number {
357
- opacity: 0;
358
- }
359
-
360
- /* A touch screen has no pointer to hover with, so the checkbox stays. */
361
- @media (hover: none) {
362
- .nx-table__cell--index[data-swap] > .nx-table__check {
363
- opacity: 1;
364
- }
365
-
366
- .nx-table__cell--index[data-swap] > .nx-table__number {
367
- opacity: 0;
368
- }
369
- }
370
-
371
- /*
372
- * The pinned section above the page: a row across the table whose button folds the records the user pinned open and
373
- * shut, and the rows themselves, which show in the list below as well. A pinned row wears a pin where the rows of the
374
- * page show their number.
375
- */
376
- .nx-table__pins {
377
- border-bottom: 2px solid var(--nx-table-line);
378
- }
379
-
380
- .nx-table__pin-cell {
381
- padding: 0;
382
- border-bottom: 1px solid var(--nx-table-line);
383
- background: var(--nx-table-head-bg);
384
- }
385
-
386
- .nx-table__pin-toggle {
387
- display: flex;
388
- align-items: center;
389
- gap: var(--nx-space-2);
390
- width: 100%;
391
- min-height: var(--nx-table-head-h);
392
- padding: 0 var(--nx-table-pad-x);
393
- border: 0;
394
- background: none;
395
- color: var(--nx-text);
396
- font: inherit;
397
- font-size: var(--nx-table-font);
398
- font-weight: 600;
399
- text-align: start;
400
- cursor: pointer;
401
- transition: background-color var(--nx-duration);
402
- }
403
-
404
- .nx-table__pin-toggle:hover {
405
- background: color-mix(in oklab, var(--nx-text) 5%, transparent);
406
- }
407
-
408
- .nx-table__pin-toggle:focus-visible {
409
- outline: none;
410
- box-shadow: inset 0 0 0 2px var(--nx-primary);
411
- }
412
-
413
- .nx-table__pin-caret {
414
- flex-shrink: 0;
415
- width: 15px;
416
- height: 15px;
417
- color: var(--nx-text-muted);
418
- transition: transform var(--nx-duration);
419
- }
420
-
421
- .nx-table__pins[data-open] .nx-table__pin-caret {
422
- transform: rotate(90deg);
423
- }
424
-
425
- .nx-table__pin-icon {
426
- flex-shrink: 0;
427
- width: 15px;
428
- height: 15px;
429
- color: var(--nx-text-muted);
430
- }
431
-
432
- .nx-table__pin-count {
433
- color: var(--nx-text-muted);
434
- font-weight: 400;
435
- font-variant-numeric: tabular-nums;
436
- }
437
-
438
- .nx-table__pin-loading {
439
- width: 14px;
440
- height: 14px;
441
- color: var(--nx-text-muted);
442
- }
443
-
444
- .nx-table__pin-mark {
445
- width: 15px;
446
- height: 15px;
447
- color: var(--nx-text-muted);
448
- }
449
-
450
- @media (prefers-reduced-motion: reduce) {
451
- .nx-table__pin-caret,
452
- .nx-table__pin-toggle {
453
- transition: none;
454
- }
455
- }
456
-
457
- .nx-table__cell--actions {
458
- width: 1px;
459
- padding-inline: 4px 8px;
460
- white-space: nowrap;
461
- }
462
-
463
- /*
464
- * Once the user has given a column a width, the table lays its columns out from the list of widths, so dragging one
465
- * edge moves that edge only. The filler column takes whatever space the others leave.
466
- */
467
- .nx-table[data-sized] .nx-table__table {
468
- table-layout: fixed;
469
- }
470
-
471
- .nx-table__cell--filler {
472
- padding: 0;
473
- background: var(--nx-row-bg);
474
- }
475
-
476
- .nx-table__heading.nx-table__cell--filler {
477
- background: var(--nx-table-head-bg);
478
- }
479
-
480
- .nx-table__actions {
481
- display: flex;
482
- align-items: center;
483
- justify-content: flex-end;
484
- gap: 2px;
485
- }
486
-
487
- /* Header: stays at the top while rows scroll. */
488
- .nx-table__heading {
489
- position: sticky;
490
- top: 0;
491
- z-index: 2;
492
- height: var(--nx-table-head-h);
493
- border-bottom: 1px solid var(--nx-table-line);
494
- background: var(--nx-table-head-bg);
495
- color: var(--nx-text);
496
- font-size: var(--nx-text-sm);
497
- font-weight: 600;
498
- white-space: nowrap;
499
- }
500
-
501
- /* A heading that holds the sort button, the column's menu, and the edge to drag keeps no padding of its own. */
502
- .nx-table__heading:has(> .nx-table__heading-inner) {
503
- padding: 0;
504
- }
505
-
506
- /*
507
- * Columns in bands: a group's heading spans the columns next to each other that name it, and the columns with no
508
- * group keep one heading as tall as both rows. Both rows stay while the rows scroll, the band on top.
509
- */
510
- .nx-table__head--leaves > .nx-table__heading {
511
- top: var(--nx-table-group-h);
512
- }
513
-
514
- .nx-table__heading--group {
515
- height: var(--nx-table-group-h);
516
- border-bottom: 0;
517
- text-align: center;
518
- }
519
-
520
- /* The line under a band stops short of its edges, so two bands beside each other read as two, not as one rule. */
521
- .nx-table__heading--group::after {
522
- content: "";
523
- position: absolute;
524
- right: var(--nx-table-pad-x);
525
- bottom: 0;
526
- left: var(--nx-table-pad-x);
527
- height: 1px;
528
- background: var(--nx-table-line);
529
- }
530
-
531
- .nx-table__group {
532
- display: block;
533
- overflow: hidden;
534
- color: var(--nx-text);
535
- font-size: var(--nx-text-xs);
536
- font-weight: 700;
537
- letter-spacing: 0.04em;
538
- text-overflow: ellipsis;
539
- text-transform: uppercase;
540
- }
541
-
542
- .nx-table__heading-inner {
543
- display: flex;
544
- align-items: center;
545
- min-width: 0;
546
- height: 100%;
547
- }
548
-
549
- .nx-table__heading-label {
550
- min-width: 0;
551
- overflow: hidden;
552
- text-overflow: ellipsis;
553
- }
554
-
555
- .nx-table__heading-inner > .nx-table__heading-label {
556
- flex: 1 1 auto;
557
- padding: 0 var(--nx-table-pad-x);
558
- }
559
-
560
- /* A sortable heading is one button across its cell, beside the column's menu. */
561
- .nx-table__sort {
562
- display: flex;
563
- flex: 1 1 auto;
564
- align-items: center;
565
- gap: 6px;
566
- min-width: 0;
567
- height: 100%;
568
- padding: 0 4px 0 var(--nx-table-pad-x);
569
- border: 0;
570
- background: transparent;
571
- color: inherit;
572
- font: inherit;
573
- text-align: inherit;
574
- cursor: pointer;
575
- transition: background-color var(--nx-duration);
576
- }
577
-
578
- .nx-table__cell--end .nx-table__sort {
579
- flex-direction: row-reverse;
580
- padding: 0 var(--nx-table-pad-x) 0 4px;
581
- }
582
-
583
- .nx-table__cell--center .nx-table__sort {
584
- justify-content: center;
585
- }
586
-
587
- /* The column's menu (pin, move, fit, hide): at the end of the heading, and shown while it is hovered or focused. */
588
- .nx-table__col-menu.nx-btn {
589
- flex: 0 0 auto;
590
- width: 20px;
591
- min-width: 20px;
592
- height: calc(var(--nx-table-head-h) - 12px);
593
- margin-right: 2px;
594
- padding: 0;
595
- border-radius: var(--nx-radius-sm);
596
- opacity: 0;
597
- transition: opacity var(--nx-duration);
598
- }
599
-
600
- .nx-table__col-menu .nx-icon {
601
- width: 14px;
602
- height: 14px;
603
- }
604
-
605
- .nx-table__heading:hover .nx-table__col-menu,
606
- .nx-table__col-menu:is(:focus, :focus-visible),
607
- .nx-table__col-menu[aria-expanded="true"] {
608
- opacity: 1;
609
- }
610
-
611
- @media (hover: none) {
612
- .nx-table__col-menu.nx-btn {
613
- opacity: 1;
614
- }
615
- }
616
-
617
- /* The edge of a heading: drag it for the column's width, double-click it to fit the content. */
618
- .nx-table__resizer {
619
- position: absolute;
620
- top: 0;
621
- right: -2px;
622
- z-index: 4;
623
- width: 8px;
624
- height: 100%;
625
- touch-action: none;
626
- cursor: col-resize;
627
- }
628
-
629
- .nx-table__resizer::after {
630
- content: "";
631
- position: absolute;
632
- top: 7px;
633
- bottom: 7px;
634
- left: 3px;
635
- width: 2px;
636
- border-radius: 1px;
637
- background: transparent;
638
- transition: background-color var(--nx-duration);
639
- }
640
-
641
- .nx-table__heading:hover > .nx-table__resizer::after,
642
- .nx-table__resizer[data-active]::after {
643
- background: var(--nx-primary);
644
- }
645
-
646
- .nx-table__resizer[data-active]::after {
647
- top: 0;
648
- bottom: 0;
649
- }
650
-
651
- /* Dragging a heading to another place: the heading being moved, and a line where it will land. */
652
- .nx-table__heading[data-dragging] {
653
- background: color-mix(in oklab, var(--nx-primary) 14%, var(--nx-table-head-bg));
654
- }
655
-
656
- .nx-table__heading[data-drop-before]::before,
657
- .nx-table__heading[data-drop-after]::after {
658
- content: "";
659
- position: absolute;
660
- top: 0;
661
- bottom: 0;
662
- z-index: 5;
663
- width: 2px;
664
- background: var(--nx-primary);
665
- }
666
-
667
- .nx-table__heading[data-drop-before]::before {
668
- left: 0;
669
- }
670
-
671
- .nx-table__heading[data-drop-after]::after {
672
- right: 0;
673
- }
674
-
675
- .nx-table[data-col-drag] .nx-table__head {
676
- user-select: none;
677
- }
678
-
679
- .nx-table[data-col-drag] :is(.nx-table__heading, .nx-table__sort, .nx-table__col-menu, .nx-table__resizer) {
680
- cursor: grabbing;
681
- }
682
-
683
- .nx-table[data-col-resize] :is(.nx-table__heading, .nx-table__sort, .nx-table__cell) {
684
- cursor: col-resize;
685
- }
686
-
687
- .nx-table__sort:hover:not(:disabled) {
688
- background: color-mix(in oklab, var(--nx-text) 5%, transparent);
689
- }
690
-
691
- .nx-table__sort:focus-visible {
692
- outline: none;
693
- box-shadow: inset 0 0 0 2px var(--nx-primary);
694
- }
695
-
696
- .nx-table__sort:disabled {
697
- cursor: default;
698
- }
699
-
700
- .nx-table__sort-icon {
701
- flex-shrink: 0;
702
- width: 14px;
703
- height: 14px;
704
- color: var(--nx-text-muted);
705
- opacity: 0;
706
- transition: opacity var(--nx-duration);
707
- }
708
-
709
- .nx-table__sort[data-sorted] .nx-table__sort-icon {
710
- color: var(--nx-text);
711
- opacity: 1;
712
- }
713
-
714
- .nx-table__sort:is(:hover, :focus-visible):not(:disabled) .nx-table__sort-icon {
715
- opacity: 1;
716
- }
717
-
718
- @media (hover: none) {
719
- .nx-table__sort:not(:disabled) .nx-table__sort-icon {
720
- opacity: 0.7;
721
- }
722
- }
723
-
724
- /* Values */
725
- .nx-table__dash,
726
- .nx-table__no {
727
- color: var(--nx-text-muted);
728
- }
729
-
730
- .nx-table__yes {
731
- display: inline-flex;
732
- color: var(--nx-success);
733
- }
734
-
735
- .nx-table__yes .nx-icon {
736
- width: 16px;
737
- height: 16px;
738
- }
739
-
740
- /* An option with a tone: a tag with a dot of its color; the text keeps the text color, readable on every tint. */
741
- .nx-table__tag {
742
- --nx-tag-tone: var(--nx-text-muted);
743
- display: inline-flex;
744
- align-items: center;
745
- gap: 6px;
746
- max-width: 100%;
747
- padding: 1px 8px;
748
- border-radius: var(--nx-radius-full);
749
- background: color-mix(in oklab, var(--nx-tag-tone) 14%, var(--nx-surface));
750
- color: var(--nx-text);
751
- font-size: var(--nx-text-xs);
752
- font-weight: 500;
753
- line-height: 1.6;
754
- vertical-align: middle;
755
- }
756
-
757
- .nx-table__tag::before {
758
- content: "";
759
- flex-shrink: 0;
760
- width: 6px;
761
- height: 6px;
762
- border-radius: 50%;
763
- background: var(--nx-tag-tone);
764
- }
765
-
766
- .nx-table__tag[data-tone="primary"] {
767
- --nx-tag-tone: var(--nx-primary);
768
- }
769
-
770
- .nx-table__tag[data-tone="success"] {
771
- --nx-tag-tone: var(--nx-success);
772
- }
773
-
774
- .nx-table__tag[data-tone="warning"] {
775
- --nx-tag-tone: var(--nx-warning);
776
- }
777
-
778
- .nx-table__tag[data-tone="danger"] {
779
- --nx-tag-tone: var(--nx-danger);
780
- }
781
-
782
- .nx-table__tag[data-tone="info"] {
783
- --nx-tag-tone: var(--nx-info);
784
- }
785
-
786
- /* Placeholders while the first rows load. */
787
- .nx-table__bone {
788
- display: block;
789
- height: 0.75em;
790
- border-radius: var(--nx-radius-sm);
791
- background: color-mix(in oklab, var(--nx-text) 9%, transparent);
792
- animation: nx-table-pulse 1.6s ease-in-out infinite;
793
- }
794
-
795
- .nx-table__cell--end .nx-table__bone {
796
- margin-left: auto;
797
- }
798
-
799
- @keyframes nx-table-pulse {
800
- 50% {
801
- opacity: 0.45;
802
- }
803
- }
804
-
805
- /* A message across the table: no rows, no matches, a failed load. */
806
- .nx-table__message-cell {
807
- padding: 0;
808
- background: var(--nx-table-bg);
809
- }
810
-
811
- .nx-table__message {
812
- display: flex;
813
- flex-direction: column;
814
- align-items: center;
815
- gap: 4px;
816
- padding: 40px var(--nx-space-4);
817
- text-align: center;
818
- }
819
-
820
- .nx-table__message-icon {
821
- width: 28px;
822
- height: 28px;
823
- margin-bottom: 6px;
824
- color: var(--nx-text-muted);
825
- }
826
-
827
- .nx-table__message[data-tone="danger"] .nx-table__message-icon {
828
- color: var(--nx-danger);
829
- }
830
-
831
- .nx-table__message-title {
832
- margin: 0;
833
- font-size: var(--nx-text-md);
834
- font-weight: 600;
835
- }
836
-
837
- .nx-table__message-detail {
838
- max-width: 36rem;
839
- margin: 0;
840
- color: var(--nx-text-muted);
841
- font-size: var(--nx-text-sm);
842
- }
843
-
844
- .nx-table__message-actions {
845
- display: flex;
846
- flex-wrap: wrap;
847
- justify-content: center;
848
- gap: var(--nx-space-2);
849
- margin-top: var(--nx-space-3);
850
- }
851
-
852
- /* A thin bar under the header while a load runs with rows on screen. The rows dim only if it takes a moment. */
853
- .nx-table__progress {
854
- position: absolute;
855
- top: calc(var(--nx-table-head-all) - 1px);
856
- right: 0;
857
- left: 0;
858
- z-index: 4;
859
- height: 2px;
860
- overflow: hidden;
861
- opacity: 0;
862
- pointer-events: none;
863
- transition: opacity var(--nx-duration);
864
- }
865
-
866
- .nx-table__progress::before {
867
- content: "";
868
- position: absolute;
869
- inset: 0 auto 0 0;
870
- width: 35%;
871
- background: var(--nx-primary);
872
- animation: nx-table-progress 1.1s ease-in-out infinite;
873
- }
874
-
875
- @keyframes nx-table-progress {
876
- from {
877
- transform: translateX(-100%);
878
- }
879
- to {
880
- transform: translateX(290%);
881
- }
882
- }
883
-
884
- .nx-table[data-refreshing] .nx-table__progress {
885
- opacity: 1;
886
- }
887
-
888
- .nx-table__table > tbody {
889
- transition: opacity var(--nx-duration);
890
- }
891
-
892
- .nx-table[data-refreshing] .nx-table__table > tbody {
893
- opacity: 0.55;
894
- transition-delay: 180ms;
895
- }
896
-
897
- /* The selection bar covers the column headings, so the table does not move when it shows. */
898
- .nx-table__selection {
899
- position: absolute;
900
- top: 0;
901
- right: 0;
902
- left: var(--nx-table-index-w, var(--nx-table-select-w));
903
- z-index: 5;
904
- display: flex;
905
- align-items: center;
906
- gap: var(--nx-space-2);
907
- height: calc(var(--nx-table-head-all) - 1px);
908
- padding: 0 var(--nx-space-2) 0 2px;
909
- background: var(--nx-table-selected);
910
- }
911
-
912
- .nx-table__frame:has(.nx-table__selection) .nx-table__heading.nx-table__cell--index {
913
- background: var(--nx-table-selected);
914
- }
915
-
916
- .nx-table__selection-count {
917
- flex-shrink: 0;
918
- padding-right: var(--nx-space-2);
919
- font-size: var(--nx-text-sm);
920
- font-weight: 600;
921
- font-variant-numeric: tabular-nums;
922
- white-space: nowrap;
923
- }
924
-
925
- .nx-table__selection-actions {
926
- display: flex;
927
- flex: 1 1 auto;
928
- align-items: center;
929
- gap: 2px;
930
- min-width: 0;
931
- overflow: hidden;
932
- }
933
-
934
- /* Every action on the selected rows in one split button: the name of the one shown must stay readable. */
935
- .nx-table__mass {
936
- min-width: 0;
937
- }
938
-
939
- .nx-table__mass .nx-btn__label,
940
- .nx-table__mass .nx-split__main .nx-btn__label {
941
- overflow: hidden;
942
- text-overflow: ellipsis;
943
- white-space: nowrap;
944
- }
945
-
946
- .nx-table__selection-clear {
947
- flex-shrink: 0;
948
- margin-left: auto;
949
- }
950
-
951
- /* A narrow table keeps the action's name and drops the word "Clear"; it stays for screen readers and in the tooltip. */
952
- @container nx-table (max-width: 560px) {
953
- .nx-table__selection-clear.nx-btn {
954
- width: var(--nx-btn-height);
955
- padding: 0;
956
- }
957
-
958
- .nx-table__selection-clear .nx-btn__label {
959
- position: absolute;
960
- width: 1px;
961
- height: 1px;
962
- overflow: hidden;
963
- clip-path: inset(50%);
964
- white-space: nowrap;
965
- }
966
- }
967
-
968
- /* Footer: the range on the left, rows per page and the pager on the right. */
969
- .nx-table__footer {
970
- display: flex;
971
- flex-wrap: wrap;
972
- align-items: center;
973
- justify-content: space-between;
974
- gap: var(--nx-space-2) var(--nx-space-4);
975
- min-height: var(--nx-control-sm);
976
- }
977
-
978
- .nx-table__range {
979
- margin: 0;
980
- color: var(--nx-text-muted);
981
- font-size: var(--nx-text-sm);
982
- font-variant-numeric: tabular-nums;
983
- }
984
-
985
- /* The numbers themselves read as the text colour, so the line is skimmed at a glance. */
986
- .nx-table__range b {
987
- color: var(--nx-text);
988
- font-weight: 600;
989
- }
990
-
991
- .nx-table__page-info {
992
- color: var(--nx-text-muted);
993
- font-size: var(--nx-text-sm);
994
- font-variant-numeric: tabular-nums;
995
- white-space: nowrap;
996
- }
997
-
998
- .nx-table__paging {
999
- display: flex;
1000
- flex-wrap: wrap;
1001
- align-items: center;
1002
- gap: var(--nx-space-2);
1003
- margin-left: auto;
1004
- }
1005
-
1006
- .nx-table__page-size {
1007
- color: var(--nx-text-muted);
1008
- font-variant-numeric: tabular-nums;
1009
- }
1010
-
1011
- .nx-table__pager {
1012
- display: flex;
1013
- align-items: center;
1014
- gap: 2px;
1015
- }
1016
-
1017
- .nx-table__page-list {
1018
- display: flex;
1019
- align-items: center;
1020
- gap: 2px;
1021
- }
1022
-
1023
- .nx-table__page {
1024
- min-width: var(--nx-control-sm);
1025
- height: var(--nx-control-sm);
1026
- padding: 0 6px;
1027
- border: 0;
1028
- border-radius: var(--nx-radius);
1029
- background: transparent;
1030
- color: var(--nx-text);
1031
- font: inherit;
1032
- font-size: var(--nx-text-sm);
1033
- font-variant-numeric: tabular-nums;
1034
- cursor: pointer;
1035
- transition: background-color var(--nx-duration);
1036
- }
1037
-
1038
- .nx-table__page:hover:not(:disabled, [aria-current]) {
1039
- background: var(--nx-surface-hover);
1040
- }
1041
-
1042
- .nx-table__page:focus-visible {
1043
- outline: none;
1044
- box-shadow: var(--nx-focus-ring);
1045
- }
1046
-
1047
- .nx-table__page[aria-current] {
1048
- background: var(--nx-primary-solid);
1049
- color: var(--nx-primary-text);
1050
- font-weight: 600;
1051
- cursor: default;
1052
- }
1053
-
1054
- .nx-table__page:disabled {
1055
- opacity: 0.5;
1056
- cursor: default;
1057
- }
1058
-
1059
- .nx-table__page-gap {
1060
- min-width: 20px;
1061
- color: var(--nx-text-muted);
1062
- text-align: center;
1063
- }
1064
-
1065
- /* A narrow table drops the page buttons: "Page 6 of 24" and the arrows are enough. */
1066
- @container nx-table (max-width: 520px) {
1067
- .nx-table__page-list {
1068
- display: none;
1069
- }
1070
- }
1071
-
1072
- @media (prefers-reduced-motion: reduce) {
1073
- .nx-table__bone {
1074
- animation: none;
1075
- }
1076
-
1077
- .nx-table__row[data-flash] > .nx-table__cell {
1078
- animation: none;
1079
- }
1080
-
1081
- .nx-table__row[data-flash] {
1082
- --nx-row-bg: var(--nx-table-flash);
1083
- }
1084
-
1085
- .nx-table__progress::before {
1086
- width: 100%;
1087
- animation: none;
1088
- opacity: 0.6;
1089
- }
1090
- }
1091
-
1092
- /*
1093
- * The Columns panel: a checkbox per column and a handle to drag it into another place. It shares the .nx-popover
1094
- * panel with the menu, and holds a list rather than actions, so a click inside it never closes it.
1095
- */
1096
- .nx-columns {
1097
- --nx-columns-row-h: 32px;
1098
- position: fixed;
1099
- inset: auto;
1100
- min-width: 232px;
1101
- max-width: min(320px, calc(100% - 16px));
1102
- max-height: calc(100vh - 16px);
1103
- padding: var(--nx-space-2);
1104
- outline: none;
1105
- }
1106
-
1107
- .nx-columns--sm {
1108
- --nx-columns-row-h: 28px;
1109
- font-size: var(--nx-text-sm);
1110
- }
1111
-
1112
- /* A shut popover is hidden by the browser, so the panel takes its layout only while it is open. */
1113
- .nx-columns:popover-open {
1114
- display: flex;
1115
- flex-direction: column;
1116
- animation: nx-menu-in 120ms ease-out;
1117
- }
1118
-
1119
- .nx-columns__head {
1120
- display: flex;
1121
- align-items: baseline;
1122
- justify-content: space-between;
1123
- gap: var(--nx-space-3);
1124
- padding: 2px var(--nx-space-2) 6px;
1125
- }
1126
-
1127
- .nx-columns__title {
1128
- font-weight: 600;
1129
- }
1130
-
1131
- .nx-columns__count {
1132
- color: var(--nx-text-muted);
1133
- font-size: var(--nx-text-xs);
1134
- font-variant-numeric: tabular-nums;
1135
- }
1136
-
1137
- .nx-columns__list {
1138
- display: flex;
1139
- flex: 1 1 auto;
1140
- flex-direction: column;
1141
- min-height: 0;
1142
- margin: 0;
1143
- padding: 0;
1144
- overflow: hidden auto;
1145
- overscroll-behavior: contain;
1146
- list-style: none;
1147
- }
1148
-
1149
- .nx-columns__row {
1150
- display: flex;
1151
- align-items: center;
1152
- gap: 6px;
1153
- min-height: var(--nx-columns-row-h);
1154
- padding: 0 var(--nx-space-2) 0 2px;
1155
- border: 1px solid transparent;
1156
- border-radius: var(--nx-radius);
1157
- }
1158
-
1159
- .nx-columns__row:hover {
1160
- background: var(--nx-surface-hover);
1161
- }
1162
-
1163
- /* The row being dragged stays in place and goes quiet; a line shows where it would land. */
1164
- .nx-columns__row[data-held] {
1165
- background: var(--nx-surface-hover);
1166
- opacity: 0.6;
1167
- }
1168
-
1169
- .nx-columns__row[data-drop] {
1170
- border-color: var(--nx-primary);
1171
- }
1172
-
1173
- /* A band: its own line, with the columns under it indented along a line that says where the band ends. */
1174
- .nx-columns__row--band {
1175
- display: block;
1176
- padding: 0;
1177
- }
1178
-
1179
- .nx-columns__row--band:hover {
1180
- background: none;
1181
- }
1182
-
1183
- .nx-columns__band {
1184
- display: flex;
1185
- align-items: center;
1186
- gap: 6px;
1187
- min-height: var(--nx-columns-row-h);
1188
- padding: 0 var(--nx-space-2) 0 2px;
1189
- border-radius: var(--nx-radius);
1190
- }
1191
-
1192
- .nx-columns__band:hover {
1193
- background: var(--nx-surface-hover);
1194
- }
1195
-
1196
- .nx-columns__band .nx-columns__label {
1197
- font-weight: 600;
1198
- }
1199
-
1200
- .nx-columns__band-count {
1201
- flex: none;
1202
- color: var(--nx-text-subtle);
1203
- font-size: var(--nx-text-xs);
1204
- font-variant-numeric: tabular-nums;
1205
- }
1206
-
1207
- .nx-columns__children {
1208
- margin: 0 0 2px 13px;
1209
- padding: 0 0 0 9px;
1210
- border-left: 1px solid var(--nx-border);
1211
- list-style: none;
1212
- }
1213
-
1214
- /* A column whose band is hidden stays readable but plainly off. */
1215
- .nx-columns__row--child:has(input:disabled) .nx-columns__label {
1216
- color: var(--nx-text-subtle);
1217
- }
1218
-
1219
- .nx-columns__handle {
1220
- display: inline-flex;
1221
- flex: none;
1222
- align-items: center;
1223
- justify-content: center;
1224
- width: 20px;
1225
- height: var(--nx-columns-row-h);
1226
- padding: 0;
1227
- border: 0;
1228
- border-radius: var(--nx-radius-sm);
1229
- background: none;
1230
- color: var(--nx-text-subtle);
1231
- cursor: grab;
1232
- touch-action: none;
1233
- }
1234
-
1235
- .nx-columns__handle:hover {
1236
- color: var(--nx-text);
1237
- }
1238
-
1239
- .nx-columns__handle:focus-visible {
1240
- outline: none;
1241
- box-shadow: var(--nx-focus-ring);
1242
- }
1243
-
1244
- .nx-columns[data-dragging] .nx-columns__handle {
1245
- cursor: grabbing;
1246
- }
1247
-
1248
- .nx-columns__handle--fixed {
1249
- cursor: default;
1250
- }
1251
-
1252
- .nx-columns__choice {
1253
- flex: 1 1 auto;
1254
- min-width: 0;
1255
- padding: 2px 0;
1256
- }
1257
-
1258
- .nx-columns__label {
1259
- overflow: hidden;
1260
- text-overflow: ellipsis;
1261
- white-space: nowrap;
1262
- }
1263
-
1264
- .nx-columns__pin {
1265
- display: inline-flex;
1266
- flex: none;
1267
- color: var(--nx-text-subtle);
1268
- }
1269
-
1270
- .nx-columns__pin .nx-icon {
1271
- width: 14px;
1272
- height: 14px;
1273
- }
1274
-
1275
- .nx-columns__note {
1276
- margin: 6px 0 0;
1277
- padding: 0 var(--nx-space-2);
1278
- color: var(--nx-text-muted);
1279
- font-size: var(--nx-text-xs);
1280
- line-height: 1.4;
1281
- }
1282
-
1283
- .nx-columns__foot {
1284
- display: flex;
1285
- justify-content: flex-end;
1286
- margin-top: var(--nx-space-2);
1287
- padding-top: var(--nx-space-2);
1288
- border-top: 1px solid var(--nx-border);
1289
- }
1290
-
1291
- @media (prefers-reduced-motion: reduce) {
1292
- .nx-columns:popover-open {
1293
- animation: none;
1294
- }
1295
- }
1296
- }
1
+ @layer nexus.components {
2
+ /*
3
+ * Data table: a bar (tabs, search, buttons), a frame holding the table (a header that stays while rows scroll, a
4
+ * checkbox column on the left and the actions on the right that stay while columns scroll), and a footer with the
5
+ * range and the pager. While rows are selected, a bar with their actions covers the column headings.
6
+ */
7
+ .nx-table {
8
+ --nx-table-bg: var(--nx-surface);
9
+ --nx-table-head-bg: color-mix(in oklab, var(--nx-text) 4%, var(--nx-surface));
10
+ --nx-table-hover: color-mix(in oklab, var(--nx-text) 3%, var(--nx-surface));
11
+ --nx-table-selected: color-mix(in oklab, var(--nx-primary) 11%, var(--nx-surface));
12
+ --nx-table-selected-hover: color-mix(in oklab, var(--nx-primary) 16%, var(--nx-surface));
13
+ --nx-table-flash: color-mix(in oklab, var(--nx-success) 18%, var(--nx-surface));
14
+ --nx-table-line: var(--nx-border);
15
+ --nx-table-head-h: calc(var(--nx-row) + 8px);
16
+ /* The band of group headings above the columns' own, when any column names a group. */
17
+ --nx-table-group-h: 30px;
18
+ /* How many rows the header has: 1, or 2 while a band stands above the headings (`data-head-rows`). */
19
+ --nx-table-head-rows: 1;
20
+ /* The whole header, which the loading bar and the selection bar are measured from. */
21
+ --nx-table-head-all: calc(var(--nx-table-head-h) + (var(--nx-table-head-rows) - 1) * var(--nx-table-group-h));
22
+ --nx-table-row-h: calc(var(--nx-row) + 12px);
23
+ --nx-table-pad-x: 12px;
24
+ --nx-table-select-w: 44px;
25
+ --nx-table-font: var(--nx-text-md);
26
+ --nx-table-edge: light-dark(rgb(16 24 40 / 0.14), rgb(0 0 0 / 0.55));
27
+
28
+ container: nx-table / inline-size;
29
+ display: flex;
30
+ flex-direction: column;
31
+ min-width: 0;
32
+ overflow: clip;
33
+ /* One frame round the bar, the rows and the footer. */
34
+ border: 1px solid var(--nx-table-line);
35
+ border-radius: var(--nx-radius-lg);
36
+ background: var(--nx-table-bg);
37
+ box-shadow: var(--nx-shadow-sm);
38
+ color: var(--nx-text);
39
+ }
40
+
41
+ .nx-table[data-head-rows="2"] {
42
+ --nx-table-head-rows: 2;
43
+ }
44
+
45
+ .nx-table--sm {
46
+ --nx-table-head-h: calc(var(--nx-row) + 2px);
47
+ --nx-table-group-h: 26px;
48
+ --nx-table-row-h: calc(var(--nx-row) + 4px);
49
+ --nx-table-pad-x: 10px;
50
+ --nx-table-select-w: 38px;
51
+ --nx-table-font: var(--nx-text-sm);
52
+ }
53
+
54
+ /*
55
+ * The bar, and a line under it. One line on a wide table: the Add section (the table's Add and the page's own
56
+ * buttons) and the lists on the left; Reload, the options (⋮) and the search, rows per page inside it, on the right.
57
+ * A narrower table moves whole parts onto lines of their own, never a lone item: the search takes the top line
58
+ * under 670px (with nothing on the left, the buttons' line), Reload goes into the options menu under 375px, and,
59
+ * beside lists, the Add section takes the last line under 325px. On the admin shell, whose menu leaves the screen
60
+ * at 900px, those are windows of 695, 400 and 350px. The lists show their labels from 900px (TabButtons
61
+ * `labels="auto"`), where the line holds them with room to spare.
62
+ */
63
+ .nx-table__bar {
64
+ display: flex;
65
+ flex-wrap: wrap;
66
+ align-items: center;
67
+ gap: var(--nx-space-2);
68
+ margin: 6px var(--nx-space-2);
69
+ }
70
+
71
+ .nx-table__bar:not(:has(:not(.nx-table__bar-end):not(:empty))) {
72
+ display: none;
73
+ }
74
+
75
+ .nx-table__bar:has(:not(.nx-table__bar-end):not(:empty)) + .nx-table__frame {
76
+ border-top: 1px solid var(--nx-table-line);
77
+ }
78
+
79
+ .nx-table__bar-add,
80
+ .nx-table__bar-start,
81
+ .nx-table__bar-end {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: var(--nx-space-2);
85
+ min-width: 0;
86
+ }
87
+
88
+ /* A page's own tabs and filters may run long: they wrap inside their part. */
89
+ .nx-table__bar-start {
90
+ flex-wrap: wrap;
91
+ }
92
+
93
+ /* A part whose page content renders nothing (an empty fragment) is no part: no room, no gap, no step. */
94
+ .nx-table__bar-add:empty,
95
+ .nx-table__bar-start:empty {
96
+ display: none;
97
+ }
98
+
99
+ /* The right side takes the room left on its line, so it ends at the bar's end. */
100
+ .nx-table__bar-end {
101
+ margin-inline-start: auto;
102
+ }
103
+
104
+ /* 19rem where there is room. A line breaks on the 12rem basis, so the search narrows before the bar takes a line. */
105
+ .nx-table__search {
106
+ flex: 1 1 12rem;
107
+ min-width: 0;
108
+ max-width: 19rem;
109
+ }
110
+
111
+ /* Rows per page inside the search box, at its start, with a line between the two. */
112
+ .nx-table__search .nx-field__actions--start {
113
+ align-self: stretch;
114
+ padding-right: 0;
115
+ padding-left: 2px;
116
+ border-right: 1px solid var(--nx-field-border);
117
+ }
118
+
119
+ .nx-table__page-size--field.nx-btn {
120
+ min-width: 0;
121
+ height: calc(var(--nx-field-height) - 8px);
122
+ gap: 0;
123
+ padding: 0 2px 0 4px;
124
+ color: var(--nx-text-muted);
125
+ font-variant-numeric: tabular-nums;
126
+ }
127
+
128
+ .nx-table__page-size--field .nx-icon {
129
+ width: 14px;
130
+ height: 14px;
131
+ }
132
+
133
+ .nx-table__page-size--field:hover {
134
+ color: var(--nx-text);
135
+ }
136
+
137
+ /* The search comes first and takes the room: the top line beside an Add section or lists, else the buttons' line. */
138
+ @container nx-table (width < 670px) {
139
+ .nx-table__search {
140
+ order: -1;
141
+ max-width: none;
142
+ }
143
+
144
+ .nx-table__bar:has(> :is(.nx-table__bar-add, .nx-table__bar-start):not(:empty)) > .nx-table__search {
145
+ flex-basis: 100%;
146
+ }
147
+ }
148
+
149
+ /* DataTable reads this to offer Reload in the options menu. */
150
+ @container nx-table (width < 375px) {
151
+ .nx-table__reload {
152
+ display: none;
153
+ }
154
+ }
155
+
156
+ /*
157
+ * Beside lists, the Add section goes last and takes its line. Its buttons share it equally (the Add is a `block`
158
+ * button, so its label stays centred), or each takes a line of its own when they do not fit side by side.
159
+ */
160
+ @container nx-table (width < 325px) {
161
+ .nx-table__bar:has(> .nx-table__bar-start:not(:empty)) > .nx-table__bar-add {
162
+ flex-basis: 100%;
163
+ flex-wrap: wrap;
164
+ order: 1;
165
+ }
166
+
167
+ .nx-table__bar:has(> .nx-table__bar-start:not(:empty)) > .nx-table__bar-add > * {
168
+ flex: 1 1 0;
169
+ }
170
+ }
171
+
172
+ /*
173
+ * The same steps for a bar whose parts are wider than the widths above expect (a long Add label, the page's own
174
+ * buttons, cozy density): DataTable squeezes it (`data-squeeze`) one step at a time, each keeping the ones before.
175
+ */
176
+ .nx-table__bar[data-squeeze] > .nx-table__search {
177
+ order: -1;
178
+ max-width: none;
179
+ }
180
+
181
+ .nx-table__bar[data-squeeze]:has(> :is(.nx-table__bar-add, .nx-table__bar-start):not(:empty)) > .nx-table__search {
182
+ flex-basis: 100%;
183
+ }
184
+
185
+ .nx-table__bar[data-squeeze~="reload"] .nx-table__reload {
186
+ display: none;
187
+ }
188
+
189
+ .nx-table__bar[data-squeeze~="add"] > .nx-table__bar-add {
190
+ flex-basis: 100%;
191
+ flex-wrap: wrap;
192
+ order: 1;
193
+ }
194
+
195
+ .nx-table__bar[data-squeeze~="add"] > .nx-table__bar-add > * {
196
+ flex: 1 1 0;
197
+ }
198
+
199
+ /*
200
+ * `fill`: the table takes the height it is given instead of growing with its rows. The bar and the footer keep their
201
+ * own height, the frame takes the rest, and the rows scroll inside it. Nothing below the table moves when a page
202
+ * holds fewer rows, and the page behind it never gains a scrollbar.
203
+ */
204
+ .nx-table--fill {
205
+ height: 100%;
206
+ min-height: 0;
207
+ }
208
+
209
+ .nx-table--fill > .nx-table__frame {
210
+ display: flex;
211
+ flex: 1 1 auto;
212
+ flex-direction: column;
213
+ min-height: 0;
214
+ }
215
+
216
+ .nx-table--fill .nx-table__scroll {
217
+ flex: 1 1 auto;
218
+ min-height: 0;
219
+ /* The room for the scrollbar is kept, so the headings do not shift the moment the rows need one. */
220
+ scrollbar-gutter: stable;
221
+ }
222
+
223
+ /* The rows' part of the frame: the selection bar and the loading bar are placed in it. */
224
+ .nx-table__frame {
225
+ position: relative;
226
+ min-width: 0;
227
+ overflow: hidden;
228
+ }
229
+
230
+ .nx-table__scroll {
231
+ overflow: auto;
232
+ overscroll-behavior-x: contain;
233
+ scrollbar-width: thin;
234
+ scrollbar-color: color-mix(in oklab, var(--nx-text-subtle) 75%, transparent) transparent;
235
+ }
236
+
237
+ .nx-table__table {
238
+ width: 100%;
239
+ border-collapse: separate;
240
+ border-spacing: 0;
241
+ font-size: var(--nx-table-font);
242
+ line-height: 1.45;
243
+ }
244
+
245
+ /* Cells */
246
+ .nx-table__cell,
247
+ .nx-table__heading {
248
+ box-sizing: border-box;
249
+ padding: 4px var(--nx-table-pad-x);
250
+ text-align: start;
251
+ vertical-align: middle;
252
+ }
253
+
254
+ .nx-table__cell {
255
+ height: var(--nx-table-row-h);
256
+ border-bottom: 1px solid var(--nx-table-line);
257
+ background: var(--nx-row-bg);
258
+ }
259
+
260
+ .nx-table__row {
261
+ --nx-row-bg: var(--nx-table-bg);
262
+ }
263
+
264
+ /* The last row's line would double the frame. */
265
+ .nx-table__row:last-child > .nx-table__cell {
266
+ border-bottom: 0;
267
+ }
268
+
269
+ .nx-table__row:not(.nx-table__row--message, .nx-table__row--skeleton):hover {
270
+ --nx-row-bg: var(--nx-table-hover);
271
+ }
272
+
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] {
275
+ cursor: pointer;
276
+ }
277
+
278
+ .nx-table__row[data-selected] {
279
+ --nx-row-bg: var(--nx-table-selected);
280
+ }
281
+
282
+ .nx-table__row[data-selected]:hover {
283
+ --nx-row-bg: var(--nx-table-selected-hover);
284
+ }
285
+
286
+ /* 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) {
288
+ color: color-mix(in oklab, var(--nx-text-muted), var(--nx-text) 45%);
289
+ }
290
+
291
+ /* Columns scroll under the pinned ones: a soft shadow shows there is more on that side. */
292
+ .nx-table__frame[data-scroll-start] {
293
+ --nx-table-start-edge: 8px 0 8px -8px var(--nx-table-edge);
294
+ }
295
+
296
+ .nx-table__frame[data-scroll-end] {
297
+ --nx-table-end-edge: -8px 0 8px -8px var(--nx-table-edge);
298
+ }
299
+
300
+ .nx-table__row[data-busy] > .nx-table__cell:not(.nx-table__cell--actions) {
301
+ color: var(--nx-text-muted);
302
+ }
303
+
304
+ /* A row just added or saved: a green wash that fades (a steady one with reduced motion, until the mark goes). */
305
+ .nx-table__row[data-flash] > .nx-table__cell {
306
+ animation: nx-table-flash 1600ms ease-out;
307
+ }
308
+
309
+ @keyframes nx-table-flash {
310
+ from {
311
+ background-color: var(--nx-table-flash);
312
+ }
313
+ }
314
+
315
+ .nx-table__cell--start {
316
+ text-align: start;
317
+ }
318
+
319
+ .nx-table__cell--center {
320
+ text-align: center;
321
+ }
322
+
323
+ .nx-table__cell--end {
324
+ text-align: end;
325
+ font-variant-numeric: tabular-nums;
326
+ }
327
+
328
+ /* A value ends in "…" at the column's width (24rem when the column sets none). */
329
+ .nx-table__value {
330
+ display: block;
331
+ max-width: var(--nx-col-max, 24rem);
332
+ overflow: hidden;
333
+ text-overflow: ellipsis;
334
+ white-space: nowrap;
335
+ }
336
+
337
+ .nx-table__cell--wrap .nx-table__value {
338
+ max-width: var(--nx-col-max, 32rem);
339
+ white-space: normal;
340
+ overflow-wrap: anywhere;
341
+ }
342
+
343
+ .nx-table__cell--end .nx-table__value {
344
+ margin-left: auto;
345
+ }
346
+
347
+ .nx-table__cell--center .nx-table__value {
348
+ margin-inline: auto;
349
+ }
350
+
351
+ /*
352
+ * Pinned columns stay while the others scroll: the numbers and checkbox column and the actions always, and
353
+ * any column the user pins from its menu. Their distance from the edge is measured from the headings before them.
354
+ */
355
+ .nx-table__cell[data-pin],
356
+ .nx-table__heading[data-pin] {
357
+ position: sticky;
358
+ z-index: 1;
359
+ background: var(--nx-row-bg);
360
+ }
361
+
362
+ .nx-table__heading[data-pin] {
363
+ z-index: 3;
364
+ background: var(--nx-table-head-bg);
365
+ }
366
+
367
+ /* The last pinned column of a side carries the shadow the others scroll under. */
368
+ .nx-table__cell[data-pin="left"][data-pin-edge],
369
+ .nx-table__heading[data-pin="left"][data-pin-edge] {
370
+ box-shadow: var(--nx-table-start-edge, 0 0 #0000);
371
+ }
372
+
373
+ .nx-table__cell[data-pin="right"][data-pin-edge],
374
+ .nx-table__heading[data-pin="right"][data-pin-edge] {
375
+ box-shadow: var(--nx-table-end-edge, 0 0 #0000);
376
+ }
377
+
378
+ /* The first column: the row's number, with its checkbox in the same place. */
379
+ .nx-table__cell--index {
380
+ width: var(--nx-table-select-w);
381
+ min-width: var(--nx-table-select-w);
382
+ padding: 0;
383
+ text-align: center;
384
+ }
385
+
386
+ .nx-table__number {
387
+ display: inline-flex;
388
+ align-items: center;
389
+ justify-content: center;
390
+ width: 100%;
391
+ color: var(--nx-text-muted);
392
+ font-size: var(--nx-text-xs);
393
+ font-variant-numeric: tabular-nums;
394
+ font-weight: 500;
395
+ }
396
+
397
+ .nx-table__check {
398
+ display: inline-flex;
399
+ align-items: center;
400
+ justify-content: center;
401
+ width: 100%;
402
+ height: 100%;
403
+ min-height: 28px;
404
+ }
405
+
406
+ .nx-table__check .nx-choice__control {
407
+ margin: 0;
408
+ }
409
+
410
+ /* Both in the one cell: the number, until the row is under the pointer or anything is selected. */
411
+ .nx-table__cell--index[data-swap] > .nx-table__number,
412
+ .nx-table__cell--index[data-swap] > .nx-table__check {
413
+ position: absolute;
414
+ inset: 0;
415
+ width: auto;
416
+ transition: opacity var(--nx-duration);
417
+ }
418
+
419
+ .nx-table__cell--index[data-swap] > .nx-table__check {
420
+ opacity: 0;
421
+ }
422
+
423
+ .nx-table__row:hover > .nx-table__cell--index[data-swap] > .nx-table__check,
424
+ /* The heading's own cell may stand in the band row, so the whole header counts as hovering it. */
425
+ .nx-table__thead:hover .nx-table__cell--index[data-swap] > .nx-table__check,
426
+ .nx-table__cell--index[data-swap]:focus-within > .nx-table__check,
427
+ .nx-table[data-selecting] .nx-table__cell--index[data-swap] > .nx-table__check {
428
+ opacity: 1;
429
+ }
430
+
431
+ .nx-table__row:hover > .nx-table__cell--index[data-swap] > .nx-table__number,
432
+ .nx-table__thead:hover .nx-table__cell--index[data-swap] > .nx-table__number,
433
+ .nx-table__cell--index[data-swap]:focus-within > .nx-table__number,
434
+ .nx-table[data-selecting] .nx-table__cell--index[data-swap] > .nx-table__number {
435
+ opacity: 0;
436
+ }
437
+
438
+ /* A touch screen has no pointer to hover with, so the checkbox stays. */
439
+ @media (hover: none) {
440
+ .nx-table__cell--index[data-swap] > .nx-table__check {
441
+ opacity: 1;
442
+ }
443
+
444
+ .nx-table__cell--index[data-swap] > .nx-table__number {
445
+ opacity: 0;
446
+ }
447
+ }
448
+
449
+ /*
450
+ * The sections above the page: a row across the table whose button folds a set of the user's records open and shut -
451
+ * the ones they pinned, their favourites - and the rows themselves, which show in the list below as well. A section's
452
+ * row wears the section's icon where the rows of the page show their number.
453
+ */
454
+ .nx-table__section-cell {
455
+ padding: 0;
456
+ border-bottom: 1px solid var(--nx-table-line);
457
+ background: var(--nx-table-head-bg);
458
+ }
459
+
460
+ .nx-table__section-toggle {
461
+ display: flex;
462
+ align-items: center;
463
+ gap: var(--nx-space-2);
464
+ width: 100%;
465
+ min-height: var(--nx-table-head-h);
466
+ padding: 0 var(--nx-table-pad-x);
467
+ border: 0;
468
+ background: none;
469
+ color: var(--nx-text);
470
+ font: inherit;
471
+ font-size: var(--nx-table-font);
472
+ font-weight: 600;
473
+ text-align: start;
474
+ cursor: pointer;
475
+ transition: background-color var(--nx-duration);
476
+ }
477
+
478
+ .nx-table__section-toggle:hover {
479
+ background: color-mix(in oklab, var(--nx-text) 5%, transparent);
480
+ }
481
+
482
+ .nx-table__section-toggle:focus-visible {
483
+ outline: none;
484
+ box-shadow: inset 0 0 0 2px var(--nx-primary);
485
+ }
486
+
487
+ .nx-table__section-caret,
488
+ .nx-table__section-icon,
489
+ .nx-table__mark {
490
+ flex-shrink: 0;
491
+ width: 15px;
492
+ height: 15px;
493
+ color: var(--nx-text-muted);
494
+ }
495
+
496
+ /* A row's marks, where its number would be: one under the other, in the line a single mark takes. */
497
+ .nx-table__marks {
498
+ display: inline-flex;
499
+ flex-direction: column;
500
+ align-items: center;
501
+ gap: 2px;
502
+ }
503
+
504
+ .nx-table__section-caret {
505
+ transition: transform var(--nx-duration);
506
+ }
507
+
508
+ .nx-table__section[data-open] .nx-table__section-caret {
509
+ transform: rotate(90deg);
510
+ }
511
+
512
+ .nx-table__section-count {
513
+ color: var(--nx-text-muted);
514
+ font-weight: 400;
515
+ font-variant-numeric: tabular-nums;
516
+ }
517
+
518
+ .nx-table__section-loading {
519
+ width: 14px;
520
+ height: 14px;
521
+ color: var(--nx-text-muted);
522
+ }
523
+
524
+ [data-nx-motion="reduce"] .nx-table__section-caret,
525
+ [data-nx-motion="reduce"] .nx-table__section-toggle {
526
+ transition: none;
527
+ }
528
+
529
+ .nx-table__cell--actions {
530
+ width: 1px;
531
+ padding-inline: 4px 8px;
532
+ white-space: nowrap;
533
+ }
534
+
535
+ /*
536
+ * Once the user has given a column a width, the table lays its columns out from the list of widths, so dragging one
537
+ * edge moves that edge only. The filler column takes whatever space the others leave.
538
+ */
539
+ .nx-table[data-sized] .nx-table__table {
540
+ table-layout: fixed;
541
+ }
542
+
543
+ .nx-table__cell--filler {
544
+ padding: 0;
545
+ background: var(--nx-row-bg);
546
+ }
547
+
548
+ .nx-table__heading.nx-table__cell--filler {
549
+ background: var(--nx-table-head-bg);
550
+ }
551
+
552
+ .nx-table__actions {
553
+ display: flex;
554
+ align-items: center;
555
+ justify-content: flex-end;
556
+ gap: 2px;
557
+ }
558
+
559
+ /* Header: stays at the top while rows scroll. */
560
+ .nx-table__heading {
561
+ position: sticky;
562
+ top: 0;
563
+ z-index: 2;
564
+ height: var(--nx-table-head-h);
565
+ border-bottom: 1px solid var(--nx-table-line);
566
+ background: var(--nx-table-head-bg);
567
+ color: var(--nx-text);
568
+ font-size: var(--nx-text-xs);
569
+ font-weight: 650;
570
+ white-space: nowrap;
571
+ }
572
+
573
+ /* A heading that holds the sort button, the column's menu, and the edge to drag keeps no padding of its own. */
574
+ .nx-table__heading:has(> .nx-table__heading-inner) {
575
+ padding: 0;
576
+ }
577
+
578
+ /*
579
+ * Columns in bands: a group's heading spans the columns next to each other that name it, and the columns with no
580
+ * group keep one heading as tall as both rows. Both rows stay while the rows scroll, the band on top.
581
+ */
582
+ .nx-table__head--leaves > .nx-table__heading {
583
+ top: var(--nx-table-group-h);
584
+ }
585
+
586
+ .nx-table__heading--group {
587
+ height: var(--nx-table-group-h);
588
+ border-bottom: 0;
589
+ text-align: center;
590
+ }
591
+
592
+ /* The line under a band stops short of its edges, so two bands beside each other read as two, not as one rule. */
593
+ .nx-table__heading--group::after {
594
+ content: "";
595
+ position: absolute;
596
+ right: var(--nx-table-pad-x);
597
+ bottom: 0;
598
+ left: var(--nx-table-pad-x);
599
+ height: 1px;
600
+ background: var(--nx-table-line);
601
+ }
602
+
603
+ .nx-table__group {
604
+ display: block;
605
+ overflow: hidden;
606
+ color: var(--nx-text);
607
+ font-size: var(--nx-text-xs);
608
+ font-weight: 700;
609
+ letter-spacing: 0.04em;
610
+ text-overflow: ellipsis;
611
+ text-transform: uppercase;
612
+ }
613
+
614
+ .nx-table__heading-inner {
615
+ display: flex;
616
+ align-items: center;
617
+ min-width: 0;
618
+ height: 100%;
619
+ }
620
+
621
+ .nx-table__heading-label {
622
+ min-width: 0;
623
+ overflow: hidden;
624
+ text-overflow: ellipsis;
625
+ }
626
+
627
+ .nx-table__heading-inner > .nx-table__heading-label {
628
+ flex: 1 1 auto;
629
+ padding: 0 var(--nx-table-pad-x);
630
+ }
631
+
632
+ /* A sortable heading is one button across its cell, beside the column's menu. */
633
+ .nx-table__sort {
634
+ display: flex;
635
+ flex: 1 1 auto;
636
+ align-items: center;
637
+ gap: 6px;
638
+ min-width: 0;
639
+ height: 100%;
640
+ padding: 0 4px 0 var(--nx-table-pad-x);
641
+ border: 0;
642
+ background: transparent;
643
+ color: inherit;
644
+ font: inherit;
645
+ text-align: inherit;
646
+ cursor: pointer;
647
+ transition: background-color var(--nx-duration);
648
+ }
649
+
650
+ .nx-table__cell--end .nx-table__sort {
651
+ flex-direction: row-reverse;
652
+ padding: 0 var(--nx-table-pad-x) 0 4px;
653
+ }
654
+
655
+ .nx-table__cell--center .nx-table__sort {
656
+ justify-content: center;
657
+ }
658
+
659
+ /* The column's menu (pin, move, fit, hide): at the end of the heading, and shown while it is hovered or focused. */
660
+ .nx-table__col-menu.nx-btn {
661
+ flex: 0 0 auto;
662
+ width: 20px;
663
+ min-width: 20px;
664
+ height: calc(var(--nx-table-head-h) - 12px);
665
+ margin-right: 2px;
666
+ padding: 0;
667
+ border-radius: var(--nx-radius-sm);
668
+ opacity: 0;
669
+ transition: opacity var(--nx-duration);
670
+ }
671
+
672
+ .nx-table__col-menu .nx-icon {
673
+ width: 14px;
674
+ height: 14px;
675
+ }
676
+
677
+ .nx-table__heading:hover .nx-table__col-menu,
678
+ .nx-table__col-menu:is(:focus, :focus-visible),
679
+ .nx-table__col-menu[aria-expanded="true"] {
680
+ opacity: 1;
681
+ }
682
+
683
+ @media (hover: none) {
684
+ .nx-table__col-menu.nx-btn {
685
+ opacity: 1;
686
+ }
687
+ }
688
+
689
+ /* The edge of a heading: drag it for the column's width, double-click it to fit the content. */
690
+ .nx-table__resizer {
691
+ position: absolute;
692
+ top: 0;
693
+ right: -2px;
694
+ z-index: 4;
695
+ width: 8px;
696
+ height: 100%;
697
+ touch-action: none;
698
+ cursor: col-resize;
699
+ }
700
+
701
+ .nx-table__resizer::after {
702
+ content: "";
703
+ position: absolute;
704
+ top: 7px;
705
+ bottom: 7px;
706
+ left: 3px;
707
+ width: 2px;
708
+ border-radius: 1px;
709
+ background: transparent;
710
+ transition: background-color var(--nx-duration);
711
+ }
712
+
713
+ .nx-table__heading:hover > .nx-table__resizer::after,
714
+ .nx-table__resizer[data-active]::after {
715
+ background: var(--nx-primary);
716
+ }
717
+
718
+ .nx-table__resizer[data-active]::after {
719
+ top: 0;
720
+ bottom: 0;
721
+ }
722
+
723
+ /* Dragging a heading to another place: the heading being moved, and a line where it will land. */
724
+ .nx-table__heading[data-dragging] {
725
+ background: color-mix(in oklab, var(--nx-primary) 14%, var(--nx-table-head-bg));
726
+ }
727
+
728
+ .nx-table__heading[data-drop-before]::before,
729
+ .nx-table__heading[data-drop-after]::after {
730
+ content: "";
731
+ position: absolute;
732
+ top: 0;
733
+ bottom: 0;
734
+ z-index: 5;
735
+ width: 2px;
736
+ background: var(--nx-primary);
737
+ }
738
+
739
+ .nx-table__heading[data-drop-before]::before {
740
+ left: 0;
741
+ }
742
+
743
+ .nx-table__heading[data-drop-after]::after {
744
+ right: 0;
745
+ }
746
+
747
+ .nx-table[data-col-drag] .nx-table__head {
748
+ user-select: none;
749
+ }
750
+
751
+ .nx-table[data-col-drag] :is(.nx-table__heading, .nx-table__sort, .nx-table__col-menu, .nx-table__resizer) {
752
+ cursor: grabbing;
753
+ }
754
+
755
+ .nx-table[data-col-resize] :is(.nx-table__heading, .nx-table__sort, .nx-table__cell) {
756
+ cursor: col-resize;
757
+ }
758
+
759
+ .nx-table__sort:hover:not(:disabled) {
760
+ background: color-mix(in oklab, var(--nx-text) 5%, transparent);
761
+ }
762
+
763
+ .nx-table__sort:focus-visible {
764
+ outline: none;
765
+ box-shadow: inset 0 0 0 2px var(--nx-primary);
766
+ }
767
+
768
+ .nx-table__sort:disabled {
769
+ cursor: default;
770
+ }
771
+
772
+ .nx-table__sort-icon {
773
+ flex-shrink: 0;
774
+ width: 14px;
775
+ height: 14px;
776
+ color: var(--nx-text-muted);
777
+ opacity: 0;
778
+ transition: opacity var(--nx-duration);
779
+ }
780
+
781
+ .nx-table__sort[data-sorted] .nx-table__sort-icon {
782
+ color: var(--nx-text);
783
+ opacity: 1;
784
+ }
785
+
786
+ .nx-table__sort:is(:hover, :focus-visible):not(:disabled) .nx-table__sort-icon {
787
+ opacity: 1;
788
+ }
789
+
790
+ @media (hover: none) {
791
+ .nx-table__sort:not(:disabled) .nx-table__sort-icon {
792
+ opacity: 0.7;
793
+ }
794
+ }
795
+
796
+ /* Values */
797
+ .nx-table__dash,
798
+ .nx-table__no {
799
+ color: var(--nx-text-muted);
800
+ }
801
+
802
+ .nx-table__yes {
803
+ display: inline-flex;
804
+ color: var(--nx-success);
805
+ }
806
+
807
+ .nx-table__yes .nx-icon {
808
+ width: 16px;
809
+ height: 16px;
810
+ }
811
+
812
+ /* An option with a tone: a tag with a dot of its color; the text keeps the text color, readable on every tint. */
813
+ .nx-table__tag {
814
+ --nx-tag-tone: var(--nx-text-muted);
815
+ display: inline-flex;
816
+ align-items: center;
817
+ gap: 6px;
818
+ max-width: 100%;
819
+ padding: 1px 8px;
820
+ border-radius: var(--nx-radius-full);
821
+ background: color-mix(in oklab, var(--nx-tag-tone) 14%, var(--nx-surface));
822
+ color: var(--nx-text);
823
+ font-size: var(--nx-text-xs);
824
+ font-weight: 500;
825
+ line-height: 1.6;
826
+ vertical-align: middle;
827
+ }
828
+
829
+ .nx-table__tag::before {
830
+ content: "";
831
+ flex-shrink: 0;
832
+ width: 6px;
833
+ height: 6px;
834
+ border-radius: 50%;
835
+ background: var(--nx-tag-tone);
836
+ }
837
+
838
+ .nx-table__tag[data-tone="primary"] {
839
+ --nx-tag-tone: var(--nx-primary);
840
+ }
841
+
842
+ .nx-table__tag[data-tone="success"] {
843
+ --nx-tag-tone: var(--nx-success);
844
+ }
845
+
846
+ .nx-table__tag[data-tone="warning"] {
847
+ --nx-tag-tone: var(--nx-warning);
848
+ }
849
+
850
+ .nx-table__tag[data-tone="danger"] {
851
+ --nx-tag-tone: var(--nx-danger);
852
+ }
853
+
854
+ .nx-table__tag[data-tone="info"] {
855
+ --nx-tag-tone: var(--nx-info);
856
+ }
857
+
858
+ /* A tag after a value (a column's `tag`): "Asia" then "Trashed", a word's space apart. */
859
+ .nx-table__tag--after {
860
+ margin-inline-start: var(--nx-space-2);
861
+ }
862
+
863
+ /* Placeholders while the first rows load. */
864
+ .nx-table__bone {
865
+ display: block;
866
+ height: 0.75em;
867
+ border-radius: var(--nx-radius-sm);
868
+ background: color-mix(in oklab, var(--nx-text) 9%, transparent);
869
+ animation: nx-table-pulse 1.6s ease-in-out infinite;
870
+ }
871
+
872
+ .nx-table__cell--end .nx-table__bone {
873
+ margin-left: auto;
874
+ }
875
+
876
+ @keyframes nx-table-pulse {
877
+ 50% {
878
+ opacity: 0.45;
879
+ }
880
+ }
881
+
882
+ /* A message across the table: no rows, no matches, a failed load. */
883
+ .nx-table__message-cell {
884
+ padding: 0;
885
+ background: var(--nx-table-bg);
886
+ }
887
+
888
+ .nx-table__message {
889
+ display: flex;
890
+ flex-direction: column;
891
+ align-items: center;
892
+ gap: 4px;
893
+ padding: 40px var(--nx-space-4);
894
+ text-align: center;
895
+ }
896
+
897
+ .nx-table__message-icon {
898
+ width: 28px;
899
+ height: 28px;
900
+ margin-bottom: 6px;
901
+ color: var(--nx-text-muted);
902
+ }
903
+
904
+ .nx-table__message[data-tone="danger"] .nx-table__message-icon {
905
+ color: var(--nx-danger);
906
+ }
907
+
908
+ .nx-table__message-title {
909
+ margin: 0;
910
+ font-size: var(--nx-text-md);
911
+ font-weight: 600;
912
+ }
913
+
914
+ .nx-table__message-detail {
915
+ max-width: 36rem;
916
+ margin: 0;
917
+ color: var(--nx-text-muted);
918
+ font-size: var(--nx-text-sm);
919
+ }
920
+
921
+ .nx-table__message-actions {
922
+ display: flex;
923
+ flex-wrap: wrap;
924
+ justify-content: center;
925
+ gap: var(--nx-space-2);
926
+ margin-top: var(--nx-space-3);
927
+ }
928
+
929
+ /* A thin bar under the header while a load runs with rows on screen. The rows dim only if it takes a moment. */
930
+ .nx-table__progress {
931
+ position: absolute;
932
+ top: calc(var(--nx-table-head-all) - 1px);
933
+ right: 0;
934
+ left: 0;
935
+ z-index: 4;
936
+ height: 2px;
937
+ overflow: hidden;
938
+ opacity: 0;
939
+ pointer-events: none;
940
+ transition: opacity var(--nx-duration);
941
+ }
942
+
943
+ .nx-table__progress::before {
944
+ content: "";
945
+ position: absolute;
946
+ inset: 0 auto 0 0;
947
+ width: 35%;
948
+ background: var(--nx-primary);
949
+ animation: nx-table-progress 1.1s ease-in-out infinite;
950
+ }
951
+
952
+ @keyframes nx-table-progress {
953
+ from {
954
+ transform: translateX(-100%);
955
+ }
956
+ to {
957
+ transform: translateX(290%);
958
+ }
959
+ }
960
+
961
+ .nx-table[data-refreshing] .nx-table__progress {
962
+ opacity: 1;
963
+ }
964
+
965
+ .nx-table__table > tbody {
966
+ transition: opacity var(--nx-duration);
967
+ }
968
+
969
+ .nx-table[data-refreshing] .nx-table__table > tbody {
970
+ opacity: 0.55;
971
+ transition-delay: 180ms;
972
+ }
973
+
974
+ /* The selection bar covers the column headings, so the table does not move when it shows. */
975
+ .nx-table__selection {
976
+ position: absolute;
977
+ top: 0;
978
+ right: 0;
979
+ left: var(--nx-table-index-w, var(--nx-table-select-w));
980
+ z-index: 5;
981
+ display: flex;
982
+ align-items: center;
983
+ gap: var(--nx-space-2);
984
+ height: calc(var(--nx-table-head-all) - 1px);
985
+ padding: 0 var(--nx-space-2) 0 2px;
986
+ background: var(--nx-table-selected);
987
+ }
988
+
989
+ .nx-table__frame:has(.nx-table__selection) .nx-table__heading.nx-table__cell--index {
990
+ background: var(--nx-table-selected);
991
+ }
992
+
993
+ .nx-table__selection-count {
994
+ flex-shrink: 0;
995
+ padding-right: var(--nx-space-2);
996
+ font-size: var(--nx-text-sm);
997
+ font-weight: 600;
998
+ font-variant-numeric: tabular-nums;
999
+ white-space: nowrap;
1000
+ }
1001
+
1002
+ /* With ways to select by what the rows are, the count is the button that opens them. */
1003
+ .nx-table__selection-pick .nx-table__selection-count {
1004
+ padding-right: 0;
1005
+ }
1006
+
1007
+ .nx-table__selection-actions {
1008
+ display: flex;
1009
+ flex: 1 1 auto;
1010
+ align-items: center;
1011
+ gap: 2px;
1012
+ min-width: 0;
1013
+ overflow: hidden;
1014
+ }
1015
+
1016
+ /* Every action on the selected rows in one split button: the name of the one shown must stay readable. */
1017
+ .nx-table__mass {
1018
+ min-width: 0;
1019
+ }
1020
+
1021
+ .nx-table__mass .nx-btn__label,
1022
+ .nx-table__mass .nx-split__main .nx-btn__label {
1023
+ overflow: hidden;
1024
+ text-overflow: ellipsis;
1025
+ white-space: nowrap;
1026
+ }
1027
+
1028
+ .nx-table__selection-clear {
1029
+ flex-shrink: 0;
1030
+ margin-left: auto;
1031
+ }
1032
+
1033
+ /* A narrow table keeps the action's name and drops the word "Clear"; it stays for screen readers and in the tooltip. */
1034
+ @container nx-table (max-width: 560px) {
1035
+ .nx-table__selection-clear.nx-btn {
1036
+ width: var(--nx-btn-height);
1037
+ padding: 0;
1038
+ }
1039
+
1040
+ .nx-table__selection-clear .nx-btn__label {
1041
+ position: absolute;
1042
+ width: 1px;
1043
+ height: 1px;
1044
+ overflow: hidden;
1045
+ clip-path: inset(50%);
1046
+ white-space: nowrap;
1047
+ }
1048
+ }
1049
+
1050
+ /* Footer: the frame's bottom band, the range on the left and the pager on the right. */
1051
+ .nx-table__footer {
1052
+ display: flex;
1053
+ flex-wrap: wrap;
1054
+ align-items: center;
1055
+ justify-content: space-between;
1056
+ gap: var(--nx-space-2) var(--nx-space-4);
1057
+ min-height: var(--nx-footbar);
1058
+ padding: 6px var(--nx-space-2) 6px var(--nx-table-pad-x);
1059
+ border-top: 1px solid var(--nx-table-line);
1060
+ }
1061
+
1062
+ .nx-table__range {
1063
+ margin: 0;
1064
+ color: var(--nx-text-muted);
1065
+ font-size: var(--nx-text-sm);
1066
+ font-variant-numeric: tabular-nums;
1067
+ }
1068
+
1069
+ /* The numbers themselves read as the text colour, so the line is skimmed at a glance. */
1070
+ .nx-table__range b {
1071
+ color: var(--nx-text);
1072
+ font-weight: 600;
1073
+ }
1074
+
1075
+ .nx-table__page-info {
1076
+ color: var(--nx-text-muted);
1077
+ font-size: var(--nx-text-sm);
1078
+ font-variant-numeric: tabular-nums;
1079
+ white-space: nowrap;
1080
+ }
1081
+
1082
+ .nx-table__paging {
1083
+ display: flex;
1084
+ flex-wrap: wrap;
1085
+ align-items: center;
1086
+ gap: var(--nx-space-2);
1087
+ margin-left: auto;
1088
+ }
1089
+
1090
+ .nx-table__page-size {
1091
+ color: var(--nx-text-muted);
1092
+ font-variant-numeric: tabular-nums;
1093
+ }
1094
+
1095
+ .nx-table__pager {
1096
+ display: flex;
1097
+ align-items: center;
1098
+ gap: 2px;
1099
+ }
1100
+
1101
+ .nx-table__page-list {
1102
+ display: flex;
1103
+ align-items: center;
1104
+ gap: 2px;
1105
+ }
1106
+
1107
+ .nx-table__page {
1108
+ min-width: var(--nx-control-sm);
1109
+ height: var(--nx-control-sm);
1110
+ padding: 0 6px;
1111
+ border: 0;
1112
+ border-radius: var(--nx-radius);
1113
+ background: transparent;
1114
+ color: var(--nx-text);
1115
+ font: inherit;
1116
+ font-size: var(--nx-text-sm);
1117
+ font-variant-numeric: tabular-nums;
1118
+ cursor: pointer;
1119
+ transition: background-color var(--nx-duration);
1120
+ }
1121
+
1122
+ .nx-table__page:hover:not(:disabled, [aria-current]) {
1123
+ background: var(--nx-surface-hover);
1124
+ }
1125
+
1126
+ .nx-table__page:focus-visible {
1127
+ outline: none;
1128
+ box-shadow: var(--nx-focus-ring);
1129
+ }
1130
+
1131
+ .nx-table__page[aria-current] {
1132
+ background: var(--nx-primary-solid);
1133
+ color: var(--nx-primary-text);
1134
+ font-weight: 600;
1135
+ cursor: default;
1136
+ }
1137
+
1138
+ .nx-table__page:disabled {
1139
+ opacity: 0.5;
1140
+ cursor: default;
1141
+ }
1142
+
1143
+ .nx-table__page-gap {
1144
+ min-width: 20px;
1145
+ color: var(--nx-text-muted);
1146
+ text-align: center;
1147
+ }
1148
+
1149
+ /* A narrow table drops the page buttons: "Page 6 of 24" and the arrows are enough. */
1150
+ @container nx-table (max-width: 520px) {
1151
+ .nx-table__page-list {
1152
+ display: none;
1153
+ }
1154
+ }
1155
+
1156
+ [data-nx-motion="reduce"] .nx-table__bone {
1157
+ animation: none;
1158
+ }
1159
+
1160
+ [data-nx-motion="reduce"] .nx-table__row[data-flash] > .nx-table__cell {
1161
+ animation: none;
1162
+ }
1163
+
1164
+ [data-nx-motion="reduce"] .nx-table__row[data-flash] {
1165
+ --nx-row-bg: var(--nx-table-flash);
1166
+ }
1167
+
1168
+ [data-nx-motion="reduce"] .nx-table__progress::before {
1169
+ width: 100%;
1170
+ animation: none;
1171
+ opacity: 0.6;
1172
+ }
1173
+
1174
+ /*
1175
+ * The panels under the table's options button (⋮), Columns and Filters: a head, a body that scrolls when the window
1176
+ * is short, and a foot. They share the .nx-popover panel with the menu, and hold controls rather than actions, so a
1177
+ * click inside never closes them.
1178
+ */
1179
+ .nx-table-panel {
1180
+ --nx-popover-pad: var(--nx-space-2);
1181
+
1182
+ position: fixed;
1183
+ inset: auto;
1184
+ outline: none;
1185
+ }
1186
+
1187
+ .nx-table-panel__head {
1188
+ display: flex;
1189
+ align-items: baseline;
1190
+ justify-content: space-between;
1191
+ gap: var(--nx-space-3);
1192
+ padding: 2px var(--nx-space-2) 6px;
1193
+ }
1194
+
1195
+ .nx-table-panel__title {
1196
+ font-weight: 600;
1197
+ }
1198
+
1199
+ .nx-table-panel__count {
1200
+ color: var(--nx-text-muted);
1201
+ font-size: var(--nx-text-xs);
1202
+ font-variant-numeric: tabular-nums;
1203
+ }
1204
+
1205
+ .nx-table-panel__foot {
1206
+ display: flex;
1207
+ align-items: center;
1208
+ justify-content: flex-end;
1209
+ margin-top: var(--nx-space-2);
1210
+ padding-top: var(--nx-space-2);
1211
+ border-top: 1px solid var(--nx-border);
1212
+ }
1213
+
1214
+ /* The Columns panel: a checkbox per column and a handle to drag it into another place. */
1215
+ .nx-columns {
1216
+ --nx-columns-row-h: var(--nx-row);
1217
+ min-width: 232px;
1218
+ max-width: min(320px, calc(100% - 16px));
1219
+ }
1220
+
1221
+ .nx-columns--sm {
1222
+ --nx-columns-row-h: calc(var(--nx-row) - 4px);
1223
+ font-size: var(--nx-text-sm);
1224
+ }
1225
+
1226
+ .nx-columns__list {
1227
+ display: flex;
1228
+ flex-direction: column;
1229
+ margin: 0;
1230
+ padding: 0;
1231
+ list-style: none;
1232
+ }
1233
+
1234
+ .nx-columns__row {
1235
+ display: flex;
1236
+ align-items: center;
1237
+ gap: 6px;
1238
+ min-height: var(--nx-columns-row-h);
1239
+ padding: 0 var(--nx-space-2) 0 2px;
1240
+ border: 1px solid transparent;
1241
+ border-radius: var(--nx-radius);
1242
+ }
1243
+
1244
+ .nx-columns__row:hover {
1245
+ background: var(--nx-surface-hover);
1246
+ }
1247
+
1248
+ /* The row being dragged stays in place and goes quiet; a line shows where it would land. */
1249
+ .nx-columns__row[data-held] {
1250
+ background: var(--nx-surface-hover);
1251
+ opacity: 0.6;
1252
+ }
1253
+
1254
+ .nx-columns__row[data-drop] {
1255
+ border-color: var(--nx-primary);
1256
+ }
1257
+
1258
+ /* A band: its own line, with the columns under it indented along a line that says where the band ends. */
1259
+ .nx-columns__row--band {
1260
+ display: block;
1261
+ padding: 0;
1262
+ }
1263
+
1264
+ .nx-columns__row--band:hover {
1265
+ background: none;
1266
+ }
1267
+
1268
+ .nx-columns__band {
1269
+ display: flex;
1270
+ align-items: center;
1271
+ gap: 6px;
1272
+ min-height: var(--nx-columns-row-h);
1273
+ padding: 0 var(--nx-space-2) 0 2px;
1274
+ border-radius: var(--nx-radius);
1275
+ }
1276
+
1277
+ .nx-columns__band:hover {
1278
+ background: var(--nx-surface-hover);
1279
+ }
1280
+
1281
+ .nx-columns__band .nx-columns__label {
1282
+ font-weight: 600;
1283
+ }
1284
+
1285
+ .nx-columns__band-count {
1286
+ flex: none;
1287
+ color: var(--nx-text-subtle);
1288
+ font-size: var(--nx-text-xs);
1289
+ font-variant-numeric: tabular-nums;
1290
+ }
1291
+
1292
+ .nx-columns__children {
1293
+ margin: 0 0 2px 13px;
1294
+ padding: 0 0 0 9px;
1295
+ border-left: 1px solid var(--nx-border);
1296
+ list-style: none;
1297
+ }
1298
+
1299
+ /* A column whose band is hidden stays readable but plainly off. */
1300
+ .nx-columns__row--child:has(input:disabled) .nx-columns__label {
1301
+ color: var(--nx-text-subtle);
1302
+ }
1303
+
1304
+ .nx-columns__handle {
1305
+ display: inline-flex;
1306
+ flex: none;
1307
+ align-items: center;
1308
+ justify-content: center;
1309
+ width: 20px;
1310
+ height: var(--nx-columns-row-h);
1311
+ padding: 0;
1312
+ border: 0;
1313
+ border-radius: var(--nx-radius-sm);
1314
+ background: none;
1315
+ color: var(--nx-text-subtle);
1316
+ cursor: grab;
1317
+ touch-action: none;
1318
+ }
1319
+
1320
+ .nx-columns__handle:hover {
1321
+ color: var(--nx-text);
1322
+ }
1323
+
1324
+ .nx-columns__handle:focus-visible {
1325
+ outline: none;
1326
+ box-shadow: var(--nx-focus-ring);
1327
+ }
1328
+
1329
+ .nx-columns[data-dragging] .nx-columns__handle {
1330
+ cursor: grabbing;
1331
+ }
1332
+
1333
+ .nx-columns__handle--fixed {
1334
+ cursor: default;
1335
+ }
1336
+
1337
+ .nx-columns__choice {
1338
+ flex: 1 1 auto;
1339
+ min-width: 0;
1340
+ padding: 2px 0;
1341
+ }
1342
+
1343
+ .nx-columns__label {
1344
+ overflow: hidden;
1345
+ text-overflow: ellipsis;
1346
+ white-space: nowrap;
1347
+ }
1348
+
1349
+ .nx-columns__pin {
1350
+ display: inline-flex;
1351
+ flex: none;
1352
+ color: var(--nx-text-subtle);
1353
+ }
1354
+
1355
+ .nx-columns__pin .nx-icon {
1356
+ width: 14px;
1357
+ height: 14px;
1358
+ }
1359
+
1360
+ .nx-columns__note {
1361
+ margin: 6px 0 0;
1362
+ padding: 0 var(--nx-space-2);
1363
+ color: var(--nx-text-muted);
1364
+ font-size: var(--nx-text-xs);
1365
+ line-height: 1.4;
1366
+ }
1367
+
1368
+ /* The Filters panel: one column of small inputs (the form's grid spans every cell this narrow), and the rows left. */
1369
+ .nx-table-filters {
1370
+ --nx-form-row-gap: var(--nx-space-3);
1371
+ width: min(22rem, calc(100vw - 16px));
1372
+ font-size: var(--nx-text-sm);
1373
+ }
1374
+
1375
+ /* In line with the head, with room for a focus ring inside the scroller. */
1376
+ .nx-table-filters__form {
1377
+ padding: 2px var(--nx-space-2);
1378
+ }
1379
+
1380
+ .nx-table-filters__rows {
1381
+ margin-right: auto;
1382
+ padding-left: var(--nx-space-2);
1383
+ color: var(--nx-text-muted);
1384
+ font-size: var(--nx-text-xs);
1385
+ font-variant-numeric: tabular-nums;
1386
+ }
1387
+ }