nexus-shared 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/CHANGELOG.md +2268 -446
  2. package/LICENSE +21 -21
  3. package/README.md +1705 -1084
  4. package/dist/Client.Index.d.ts +17 -0
  5. package/dist/Client.Index.js +17 -0
  6. package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
  7. package/dist/Components/Documents/DocumentFrame.js +83 -0
  8. package/dist/Components/Documents/Menu.d.ts +4 -0
  9. package/dist/Components/Documents/Menu.js +40 -24
  10. package/dist/Components/Documents/PrintDocument.d.ts +95 -0
  11. package/dist/Components/Documents/PrintDocument.js +62 -0
  12. package/dist/Components/Documents/Tabs.d.ts +59 -0
  13. package/dist/Components/Documents/Tabs.js +70 -0
  14. package/dist/Components/Forms/ApiForm.js +19 -2
  15. package/dist/Components/Forms/Crud.d.ts +2 -2
  16. package/dist/Components/Forms/Crud.js +871 -377
  17. package/dist/Components/Forms/CrudActions.d.ts +136 -0
  18. package/dist/Components/Forms/CrudActions.js +184 -0
  19. package/dist/Components/Forms/CrudHistory.d.ts +66 -0
  20. package/dist/Components/Forms/CrudHistory.js +140 -0
  21. package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
  22. package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
  23. package/dist/Components/Forms/CrudParts.d.ts +30 -2
  24. package/dist/Components/Forms/CrudParts.js +56 -5
  25. package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
  26. package/dist/Components/Forms/CrudRowForms.js +114 -0
  27. package/dist/Components/Forms/CrudScopes.d.ts +28 -0
  28. package/dist/Components/Forms/CrudScopes.js +76 -0
  29. package/dist/Components/Forms/CrudTree.d.ts +90 -0
  30. package/dist/Components/Forms/CrudTree.js +363 -0
  31. package/dist/Components/Forms/CrudWorkspace.d.ts +63 -0
  32. package/dist/Components/Forms/CrudWorkspace.js +169 -0
  33. package/dist/Components/Forms/ExcelImport.js +3 -3
  34. package/dist/Components/Forms/Form.d.ts +4 -4
  35. package/dist/Components/Forms/Form.js +125 -16
  36. package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
  37. package/dist/Components/Forms/FormLeaveGuard.js +65 -0
  38. package/dist/Components/Forms/RecordFlows.d.ts +50 -0
  39. package/dist/Components/Forms/RecordFlows.js +282 -0
  40. package/dist/Components/Forms/SubmitForm.d.ts +37 -2
  41. package/dist/Components/Forms/SubmitForm.js +195 -10
  42. package/dist/Components/Inputs/CheckBox.js +1 -1
  43. package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
  44. package/dist/Components/Inputs/ChoiceField.js +28 -2
  45. package/dist/Components/Inputs/CodeBox.d.ts +69 -0
  46. package/dist/Components/Inputs/CodeBox.js +316 -0
  47. package/dist/Components/Inputs/DatePicker.js +4 -1
  48. package/dist/Components/Inputs/DateRangePicker.js +4 -1
  49. package/dist/Components/Inputs/DateTimePicker.js +4 -1
  50. package/dist/Components/Inputs/Dropdown.d.ts +13 -0
  51. package/dist/Components/Inputs/Dropdown.js +8 -5
  52. package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
  53. package/dist/Components/Inputs/DropdownParts.js +16 -4
  54. package/dist/Components/Inputs/Field.d.ts +7 -1
  55. package/dist/Components/Inputs/Field.js +2 -2
  56. package/dist/Components/Inputs/GroupForm.d.ts +6 -6
  57. package/dist/Components/Inputs/GroupForm.js +10 -11
  58. package/dist/Components/Inputs/InputRenderer.d.ts +9 -6
  59. package/dist/Components/Inputs/InputRenderer.js +39 -31
  60. package/dist/Components/Inputs/NumberBox.js +4 -2
  61. package/dist/Components/Inputs/PictureInput.d.ts +41 -0
  62. package/dist/Components/Inputs/PictureInput.js +70 -0
  63. package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
  64. package/dist/Components/Inputs/RadioGroup.js +8 -3
  65. package/dist/Components/Inputs/RowsInput.d.ts +1 -1
  66. package/dist/Components/Inputs/RowsInput.js +18 -3
  67. package/dist/Components/Inputs/TabularForm.d.ts +5 -5
  68. package/dist/Components/Inputs/TabularForm.js +29 -29
  69. package/dist/Components/Inputs/TextArea.d.ts +6 -0
  70. package/dist/Components/Inputs/TextArea.js +14 -2
  71. package/dist/Components/Inputs/TextBox.js +4 -1
  72. package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
  73. package/dist/Components/Inputs/TextBoxTypes.js +17 -0
  74. package/dist/Components/Inputs/TimePicker.js +4 -1
  75. package/dist/Components/Inputs/TreeView.d.ts +107 -6
  76. package/dist/Components/Inputs/TreeView.js +513 -30
  77. package/dist/Components/Layouts/MessageBox.d.ts +15 -2
  78. package/dist/Components/Layouts/MessageBox.js +56 -7
  79. package/dist/Components/Layouts/Popup.d.ts +13 -6
  80. package/dist/Components/Layouts/Popup.js +37 -9
  81. package/dist/Components/Layouts/PopupDock.d.ts +3 -1
  82. package/dist/Components/Layouts/PopupDock.js +2 -1
  83. package/dist/Components/Layouts/PopupHost.js +5 -4
  84. package/dist/Components/Layouts/ThemePicker.d.ts +3 -2
  85. package/dist/Components/Layouts/ThemePicker.js +16 -4
  86. package/dist/Components/Viewers/DataTable.js +205 -61
  87. package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
  88. package/dist/Components/Viewers/DataTableFilters.js +17 -2
  89. package/dist/Components/Viewers/DataTableParts.d.ts +46 -12
  90. package/dist/Components/Viewers/DataTableParts.js +62 -21
  91. package/dist/Components/Viewers/OwnerFiles.d.ts +175 -0
  92. package/dist/Components/Viewers/OwnerFiles.js +589 -0
  93. package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
  94. package/dist/Components/Viewers/QrCodeImage.js +15 -0
  95. package/dist/Components/Viewers/RecordCards.d.ts +16 -0
  96. package/dist/Components/Viewers/RecordCards.js +509 -0
  97. package/dist/Helpers/ApiModules.d.ts +3 -34
  98. package/dist/Helpers/ApiModules.js +3 -3
  99. package/dist/Helpers/ApiRoutes.d.ts +68 -26
  100. package/dist/Helpers/ApiRoutes.js +31 -27
  101. package/dist/Helpers/ArrayHelpers.d.ts +0 -0
  102. package/dist/Helpers/ArrayHelpers.js +2 -0
  103. package/dist/Helpers/BusyHooks.d.ts +6 -0
  104. package/dist/Helpers/BusyHooks.js +39 -0
  105. package/dist/Helpers/CodeHelpers.d.ts +97 -0
  106. package/dist/Helpers/CodeHelpers.js +332 -0
  107. package/dist/Helpers/CrudBackend.d.ts +114 -6
  108. package/dist/Helpers/CrudBackend.js +214 -11
  109. package/dist/Helpers/CrudColumns.d.ts +98 -0
  110. package/dist/Helpers/CrudColumns.js +160 -0
  111. package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
  112. package/dist/Helpers/CrudDetailsTabs.js +126 -0
  113. package/dist/Helpers/CrudHelpers.d.ts +130 -6
  114. package/dist/Helpers/CrudHelpers.js +304 -12
  115. package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
  116. package/dist/Helpers/CrudNarrowPick.js +157 -0
  117. package/dist/Helpers/CrudRowForms.d.ts +15 -0
  118. package/dist/Helpers/CrudRowForms.js +43 -0
  119. package/dist/Helpers/CrudScopes.d.ts +32 -0
  120. package/dist/Helpers/CrudScopes.js +79 -0
  121. package/dist/Helpers/DropdownHelpers.d.ts +3 -1
  122. package/dist/Helpers/DropdownHelpers.js +6 -2
  123. package/dist/Helpers/ExcelHelpers.d.ts +1 -1
  124. package/dist/Helpers/ExcelHelpers.js +12 -11
  125. package/dist/Helpers/FileAdapter.d.ts +78 -0
  126. package/dist/Helpers/FileAdapter.js +102 -0
  127. package/dist/Helpers/FilePrimaries.d.ts +48 -0
  128. package/dist/Helpers/FilePrimaries.js +138 -0
  129. package/dist/Helpers/FormConfirm.d.ts +32 -0
  130. package/dist/Helpers/FormConfirm.js +40 -0
  131. package/dist/Helpers/FormStore.d.ts +90 -5
  132. package/dist/Helpers/FormStore.js +318 -22
  133. package/dist/Helpers/InputConditions.d.ts +50 -0
  134. package/dist/Helpers/InputConditions.js +168 -0
  135. package/dist/Helpers/InputMemory.d.ts +106 -0
  136. package/dist/Helpers/InputMemory.js +385 -0
  137. package/dist/Helpers/InputNarrowing.d.ts +18 -0
  138. package/dist/Helpers/InputNarrowing.js +67 -0
  139. package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
  140. package/dist/Helpers/InputParamsHelpers.js +162 -71
  141. package/dist/Helpers/MessageBuilder.js +2 -0
  142. package/dist/Helpers/NexusDriveAdapter.d.ts +55 -0
  143. package/dist/Helpers/NexusDriveAdapter.js +207 -0
  144. package/dist/Helpers/NexusHelpers.d.ts +0 -0
  145. package/dist/Helpers/NexusHelpers.js +1 -0
  146. package/dist/Helpers/NumberHelpers.d.ts +95 -0
  147. package/dist/Helpers/NumberHelpers.js +126 -0
  148. package/dist/Helpers/Permissions.js +2 -1
  149. package/dist/Helpers/QrCode.d.ts +26 -0
  150. package/dist/Helpers/QrCode.js +463 -0
  151. package/dist/Helpers/RecordCards.d.ts +205 -0
  152. package/dist/Helpers/RecordCards.js +378 -0
  153. package/dist/Helpers/RequestCache.d.ts +47 -0
  154. package/dist/Helpers/RequestCache.js +132 -0
  155. package/dist/Helpers/RowsStore.d.ts +37 -2
  156. package/dist/Helpers/RowsStore.js +128 -10
  157. package/dist/Helpers/ServerPreview.d.ts +69 -0
  158. package/dist/Helpers/ServerPreview.js +99 -0
  159. package/dist/Helpers/TableColumns.js +4 -3
  160. package/dist/Helpers/TableExport.d.ts +7 -1
  161. package/dist/Helpers/TableExport.js +21 -15
  162. package/dist/Helpers/TableFilters.d.ts +1 -1
  163. package/dist/Helpers/TableFilters.js +24 -23
  164. package/dist/Helpers/TableHelpers.d.ts +24 -3
  165. package/dist/Helpers/TableHelpers.js +58 -12
  166. package/dist/Helpers/TableJoins.d.ts +83 -0
  167. package/dist/Helpers/TableJoins.js +284 -0
  168. package/dist/Helpers/TableStore.d.ts +26 -1
  169. package/dist/Helpers/TableStore.js +73 -7
  170. package/dist/Helpers/TreeStore.d.ts +306 -11
  171. package/dist/Helpers/TreeStore.js +1048 -90
  172. package/dist/Helpers/UserPreferences.d.ts +8 -0
  173. package/dist/Helpers/UserPreferences.js +17 -0
  174. package/dist/Interfaces/CrudInterfaces.d.ts +512 -14
  175. package/dist/Interfaces/FileInterfaces.d.ts +162 -0
  176. package/dist/Interfaces/FileInterfaces.js +4 -0
  177. package/dist/Interfaces/FormInterfaces.d.ts +393 -29
  178. package/dist/Interfaces/FormInterfaces.js +79 -1
  179. package/dist/Interfaces/InputInterfaces.d.ts +39 -3
  180. package/dist/Interfaces/MessageInterfaces.d.ts +1 -1
  181. package/dist/Interfaces/PreferenceInterfaces.d.ts +8 -0
  182. package/dist/Interfaces/PreferenceInterfaces.js +2 -5
  183. package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
  184. package/dist/Interfaces/RecordInterfaces.js +1 -0
  185. package/dist/Interfaces/TableInterfaces.d.ts +160 -5
  186. package/dist/Services/ApiProxy.d.ts +36 -1
  187. package/dist/Services/ApiProxy.js +182 -52
  188. package/dist/Services/ApiProxyRules.d.ts +203 -0
  189. package/dist/Services/ApiProxyRules.js +426 -0
  190. package/dist/Services/ExcelApi.js +5 -0
  191. package/dist/Services/FileApi.d.ts +13 -0
  192. package/dist/Services/FileApi.js +29 -0
  193. package/dist/Services/ImageEditorLoader.d.ts +14 -0
  194. package/dist/Services/ImageEditorLoader.js +51 -0
  195. package/dist/Services/JoinSources.d.ts +80 -0
  196. package/dist/Services/JoinSources.js +150 -0
  197. package/dist/Services/ModuleImport.d.ts +7 -0
  198. package/dist/Services/ModuleImport.js +22 -0
  199. package/dist/Services/ServerApi.d.ts +13 -2
  200. package/dist/Services/ServerApi.js +8 -3
  201. package/dist/Shared.Index.d.ts +20 -0
  202. package/dist/Shared.Index.js +20 -0
  203. package/package.json +92 -85
  204. package/src/Styles/Nexus.Avatar.css +80 -79
  205. package/src/Styles/Nexus.Base.css +117 -117
  206. package/src/Styles/Nexus.Button.css +186 -180
  207. package/src/Styles/Nexus.Calendar.css +273 -273
  208. package/src/Styles/Nexus.Chat.css +1443 -1443
  209. package/src/Styles/Nexus.Choice.css +359 -343
  210. package/src/Styles/Nexus.Crud.css +542 -168
  211. package/src/Styles/Nexus.DateTime.css +81 -81
  212. package/src/Styles/Nexus.Dropdown.css +733 -725
  213. package/src/Styles/Nexus.Editor.css +295 -295
  214. package/src/Styles/Nexus.Excel.css +389 -389
  215. package/src/Styles/Nexus.Files.css +410 -0
  216. package/src/Styles/Nexus.Form.css +282 -279
  217. package/src/Styles/Nexus.Index.css +41 -37
  218. package/src/Styles/Nexus.Input.css +238 -4
  219. package/src/Styles/Nexus.Markdown.css +237 -237
  220. package/src/Styles/Nexus.Menu.css +246 -237
  221. package/src/Styles/Nexus.Popup.css +800 -724
  222. package/src/Styles/Nexus.Print.css +288 -0
  223. package/src/Styles/Nexus.QrCode.css +12 -0
  224. package/src/Styles/Nexus.Record.css +430 -0
  225. package/src/Styles/Nexus.Rows.css +637 -637
  226. package/src/Styles/Nexus.Slider.css +276 -276
  227. package/src/Styles/Nexus.Tab.Buttons.css +497 -432
  228. package/src/Styles/Nexus.Table.css +98 -35
  229. package/src/Styles/Nexus.Theme.Picker.css +59 -3
  230. package/src/Styles/Nexus.Themes.css +200 -194
  231. package/src/Styles/Nexus.Time.css +144 -144
  232. package/src/Styles/Nexus.Toast.css +263 -263
  233. package/src/Styles/Nexus.Tokens.css +94 -89
  234. package/src/Styles/Nexus.Tree.css +530 -431
@@ -1,725 +1,733 @@
1
- @layer nexus.components {
2
- /*
3
- * Dropdown: the field shell holds the chosen value (or chips for several) over a search input, and the list opens in
4
- * a popover placed by the component. Rows are flex rows, or, with columns, subgrid rows of one grid, so every column
5
- * lines up down the list like a table. Current values that cannot be picked again carry a tag and a dashed chip.
6
- */
7
- .nx-field--dropdown {
8
- --nx-dropdown-chip: 22px;
9
- --nx-dropdown-value-media: 20px;
10
- }
11
-
12
- .nx-field--dropdown.nx-field--sm {
13
- --nx-dropdown-chip: 18px;
14
- --nx-dropdown-value-media: 16px;
15
- }
16
-
17
- .nx-field--dropdown.nx-field--lg {
18
- --nx-dropdown-chip: 26px;
19
- --nx-dropdown-value-media: 22px;
20
- }
21
-
22
- /* The box */
23
- .nx-dropdown__control {
24
- position: relative;
25
- flex: 1 1 auto;
26
- align-self: stretch;
27
- min-width: 0;
28
- cursor: pointer;
29
- }
30
-
31
- .nx-field[data-disabled] .nx-dropdown__control {
32
- cursor: not-allowed;
33
- }
34
-
35
- .nx-field[data-readonly] .nx-dropdown__control {
36
- cursor: default;
37
- }
38
-
39
- .nx-dropdown__control:not([data-searchable]) .nx-dropdown__input {
40
- caret-color: transparent;
41
- cursor: inherit;
42
- }
43
-
44
- /* One value: the value and the input share one cell; typing hides the value and shows the search. */
45
- .nx-dropdown__control--single {
46
- display: grid;
47
- }
48
-
49
- .nx-dropdown__control--single > :is(.nx-dropdown__value, .nx-dropdown__input) {
50
- grid-area: 1 / 1;
51
- }
52
-
53
- .nx-dropdown__value {
54
- display: flex;
55
- align-items: center;
56
- gap: var(--nx-space-2);
57
- min-width: 0;
58
- padding: 0 var(--nx-field-pad-x);
59
- color: var(--nx-text);
60
- font-size: var(--nx-field-font);
61
- line-height: 1.4;
62
- pointer-events: none;
63
- }
64
-
65
- .nx-dropdown__value-text {
66
- min-width: 0;
67
- overflow: hidden;
68
- text-overflow: ellipsis;
69
- white-space: nowrap;
70
- }
71
-
72
- .nx-dropdown__control--single:has(.nx-dropdown__input:not(:placeholder-shown)) .nx-dropdown__value {
73
- visibility: hidden;
74
- }
75
-
76
- .nx-field--inbox .nx-dropdown__value {
77
- padding-top: var(--nx-field-inbox-pad);
78
- }
79
-
80
- .nx-field__icon:not(.nx-field__icon--end) + .nx-dropdown__control :is(.nx-dropdown__value, .nx-dropdown__input) {
81
- padding-left: var(--nx-space-2);
82
- }
83
-
84
- .nx-dropdown__control:has(~ .nx-field__actions) :is(.nx-dropdown__value, .nx-dropdown__input) {
85
- padding-right: var(--nx-space-1);
86
- }
87
-
88
- .nx-dropdown__value .nx-dropdown-avatar {
89
- --nx-dropdown-media: var(--nx-dropdown-value-media);
90
- }
91
-
92
- .nx-dropdown__value .nx-dropdown-image {
93
- height: calc(var(--nx-dropdown-value-media) * 0.72);
94
- }
95
-
96
- /* Several values: chips wrap, and the search input takes the rest of the last line. */
97
- .nx-dropdown__control--multiple {
98
- display: flex;
99
- flex-wrap: wrap;
100
- align-content: center;
101
- align-items: center;
102
- gap: 4px;
103
- padding: 4px var(--nx-field-pad-x);
104
- }
105
-
106
- .nx-field--inbox .nx-dropdown__control--multiple {
107
- padding-top: calc(var(--nx-field-inbox-pad) + 1px);
108
- }
109
-
110
- .nx-field__icon:not(.nx-field__icon--end) + .nx-dropdown__control--multiple {
111
- padding-left: var(--nx-space-2);
112
- }
113
-
114
- .nx-dropdown__control--multiple:has(~ .nx-field__actions) {
115
- padding-right: var(--nx-space-1);
116
- }
117
-
118
- .nx-field .nx-dropdown__control--multiple .nx-dropdown__input {
119
- flex: 1 1 2.5rem;
120
- align-self: center;
121
- width: 2.5rem;
122
- min-width: 2.5rem;
123
- height: var(--nx-dropdown-chip);
124
- padding: 0;
125
- }
126
-
127
- /*
128
- * With chips, the search input needs room only while someone can type: at rest (or without search) it shrinks to the
129
- * end of the last line, so chips that fill a line leave no empty line under them.
130
- */
131
- .nx-field .nx-dropdown__control--multiple[data-has-value]:is(:not(:focus-within), :not([data-searchable])) .nx-dropdown__input {
132
- flex-basis: 1px;
133
- width: 1px;
134
- min-width: 1px;
135
- }
136
-
137
- .nx-dropdown-chip {
138
- --nx-dropdown-media: calc(var(--nx-dropdown-chip) - 4px);
139
-
140
- display: inline-flex;
141
- align-items: center;
142
- gap: 5px;
143
- min-width: 0;
144
- max-width: 100%;
145
- height: var(--nx-dropdown-chip);
146
- padding: 0 7px;
147
- border: 1px solid transparent;
148
- border-radius: var(--nx-radius-sm);
149
- background: color-mix(in oklab, var(--nx-text) 8%, var(--nx-field-bg));
150
- color: var(--nx-text);
151
- font-size: var(--nx-text-sm);
152
- line-height: 1;
153
- white-space: nowrap;
154
- cursor: default;
155
- }
156
-
157
- .nx-field--sm .nx-dropdown-chip {
158
- gap: 4px;
159
- padding: 0 5px;
160
- font-size: var(--nx-text-xs);
161
- }
162
-
163
- .nx-dropdown-chip:has(.nx-dropdown-chip__remove) {
164
- padding-right: 1px;
165
- }
166
-
167
- .nx-dropdown-chip:has(> .nx-dropdown-avatar) {
168
- padding-left: 2px;
169
- }
170
-
171
- .nx-dropdown-chip__label {
172
- min-width: 0;
173
- overflow: hidden;
174
- text-overflow: ellipsis;
175
- }
176
-
177
- .nx-dropdown-chip .nx-dropdown-image {
178
- height: calc(var(--nx-dropdown-chip) - 10px);
179
- }
180
-
181
- .nx-dropdown-chip .nx-dropdown-icon {
182
- color: var(--nx-text-muted);
183
- }
184
-
185
- /* A current value that cannot be picked again: an outline instead of a fill. The tooltip says why. */
186
- .nx-dropdown-chip[data-state] {
187
- border-style: dashed;
188
- border-color: var(--nx-text-subtle);
189
- background: transparent;
190
- color: var(--nx-text-muted);
191
- }
192
-
193
- /* "+12 more": the values past maxChips. Its tooltip names them. */
194
- .nx-dropdown-chip--more {
195
- font-variant-numeric: tabular-nums;
196
- font-weight: 500;
197
- }
198
-
199
- .nx-dropdown-chip__remove {
200
- display: grid;
201
- place-items: center;
202
- flex-shrink: 0;
203
- width: calc(var(--nx-dropdown-chip) - 4px);
204
- height: calc(var(--nx-dropdown-chip) - 4px);
205
- padding: 0;
206
- border: 0;
207
- border-radius: 3px;
208
- background: none;
209
- color: var(--nx-text-muted);
210
- cursor: pointer;
211
- transition:
212
- background-color var(--nx-duration),
213
- color var(--nx-duration);
214
- }
215
-
216
- .nx-dropdown-chip__remove:hover {
217
- background: color-mix(in oklab, var(--nx-text) 12%, transparent);
218
- color: var(--nx-text);
219
- }
220
-
221
- .nx-dropdown-chip__remove .nx-icon {
222
- width: 12px;
223
- height: 12px;
224
- vertical-align: 0;
225
- }
226
-
227
- .nx-dropdown__spinner {
228
- display: grid;
229
- place-items: center;
230
- width: calc(var(--nx-field-base) - 10px);
231
- height: calc(var(--nx-field-base) - 10px);
232
- color: var(--nx-text-muted);
233
- }
234
-
235
- .nx-dropdown__spinner .nx-icon {
236
- width: var(--nx-field-icon);
237
- height: var(--nx-field-icon);
238
- vertical-align: 0;
239
- }
240
-
241
- .nx-dropdown__toggle .nx-icon {
242
- transition: transform 160ms ease;
243
- }
244
-
245
- .nx-dropdown__toggle[aria-expanded="true"] {
246
- color: var(--nx-field-accent);
247
- }
248
-
249
- .nx-dropdown__toggle[aria-expanded="true"] .nx-icon {
250
- transform: rotate(180deg);
251
- }
252
-
253
- /* The panel */
254
- .nx-dropdown-panel {
255
- --nx-dropdown-row: calc(var(--nx-row) + 2px);
256
- --nx-dropdown-font: var(--nx-text-md);
257
- --nx-dropdown-media: 24px;
258
- --nx-dropdown-check: 16px;
259
- --nx-dropdown-pad-x: var(--nx-space-2);
260
- --nx-dropdown-bg: light-dark(var(--nx-surface), var(--nx-surface-2));
261
-
262
- position: fixed;
263
- inset: auto;
264
- width: max-content;
265
- max-width: min(640px, calc(100vw - 16px));
266
- overflow: hidden;
267
- }
268
-
269
- /* Two classes: the shared panel (.nx-popover) loads after this file, and its padding would wrap the list in 16px. */
270
- .nx-popover.nx-dropdown-panel {
271
- padding: 0;
272
- }
273
-
274
- .nx-dropdown-panel--sm {
275
- --nx-dropdown-row: calc(var(--nx-row) - 2px);
276
- --nx-dropdown-font: var(--nx-text-sm);
277
- --nx-dropdown-media: 20px;
278
- --nx-dropdown-check: 14px;
279
- }
280
-
281
- .nx-dropdown-panel--lg {
282
- --nx-dropdown-row: calc(var(--nx-row) + 8px);
283
- --nx-dropdown-font: var(--nx-text-lg);
284
- --nx-dropdown-media: 28px;
285
- --nx-dropdown-check: 18px;
286
- --nx-dropdown-pad-x: 10px;
287
- }
288
-
289
- /* Only while open: a display on the popover itself would show it closed. */
290
- .nx-dropdown-panel:popover-open {
291
- display: flex;
292
- flex-direction: column;
293
- }
294
-
295
- /*
296
- * Rows sit 10px from the top and bottom and 8px from the sides. Where scroll bars take room (Windows), the gutter
297
- * kept for one is already space on the right, so the right padding gives it back (the component measures it).
298
- */
299
- .nx-dropdown-list {
300
- position: relative;
301
- flex: 1 1 auto;
302
- min-height: 0;
303
- padding: 10px 8px;
304
- padding-right: max(0px, 8px - var(--nx-dropdown-gutter, 0px));
305
- overflow: hidden auto;
306
- overscroll-behavior: contain;
307
- outline: none;
308
- color: var(--nx-text);
309
- font-size: var(--nx-dropdown-font);
310
- line-height: 1.35;
311
- scrollbar-gutter: stable;
312
- }
313
-
314
- /*
315
- * Columns: one grid for the whole list; rows and the header are subgrids, so columns line up across rows. Subgrid rows
316
- * take no side padding (Chromium does not widen the edge tracks for it): the check cell carries the start inset, and
317
- * an empty last track the end.
318
- */
319
- .nx-dropdown-list--columns {
320
- display: grid;
321
- align-content: start;
322
- column-gap: 10px;
323
- padding-top: 0;
324
- }
325
-
326
- .nx-dropdown-list--columns > * {
327
- grid-column: 1 / -1;
328
- }
329
-
330
- .nx-dropdown-list--columns > :is(.nx-dropdown-option, .nx-dropdown-list__header) {
331
- display: grid;
332
- grid-template-columns: subgrid;
333
- align-items: center;
334
- }
335
-
336
- .nx-dropdown-list--columns > .nx-dropdown-list__header + *,
337
- .nx-dropdown-list--columns > :first-child:not(.nx-dropdown-list__header) {
338
- margin-top: 4px;
339
- }
340
-
341
- .nx-dropdown-list__header {
342
- position: sticky;
343
- z-index: 1;
344
- top: 0;
345
- padding: 7px 0 6px;
346
- border-bottom: 1px solid var(--nx-border);
347
- background: var(--nx-dropdown-bg);
348
- color: var(--nx-text-muted);
349
- font-size: var(--nx-text-xs);
350
- font-weight: 600;
351
- line-height: 1.3;
352
- white-space: nowrap;
353
- }
354
-
355
- .nx-dropdown-list__heading {
356
- min-width: 0;
357
- overflow: hidden;
358
- text-overflow: ellipsis;
359
- }
360
-
361
- .nx-dropdown-list__group {
362
- padding: 6px var(--nx-dropdown-pad-x) 3px;
363
- color: var(--nx-text-muted);
364
- font-size: var(--nx-text-xs);
365
- font-weight: 600;
366
- letter-spacing: 0.04em;
367
- text-transform: uppercase;
368
- }
369
-
370
- .nx-dropdown-list__separator {
371
- height: 1px;
372
- margin: 4px 0;
373
- background: var(--nx-border);
374
- }
375
-
376
- /* Rows */
377
- .nx-dropdown-option {
378
- display: flex;
379
- align-items: center;
380
- gap: var(--nx-space-2);
381
- min-height: var(--nx-dropdown-row);
382
- padding: 5px var(--nx-dropdown-pad-x);
383
- border-radius: var(--nx-radius);
384
- cursor: pointer;
385
- user-select: none;
386
- }
387
-
388
- .nx-dropdown-list--columns > .nx-dropdown-option {
389
- padding-inline: 0;
390
- }
391
-
392
- .nx-dropdown-list--columns > * > .nx-dropdown-option__check,
393
- .nx-dropdown-list--columns > .nx-dropdown-list__header > :first-child {
394
- box-sizing: content-box;
395
- padding-left: var(--nx-dropdown-pad-x);
396
- }
397
-
398
- .nx-dropdown-option[data-active] {
399
- background: var(--nx-surface-hover);
400
- }
401
-
402
- /* The chosen row of a single choice: a faint primary tint, a check, and a heavier label. */
403
- .nx-dropdown-list:not([aria-multiselectable]) .nx-dropdown-option[aria-selected="true"] {
404
- background: color-mix(in oklab, var(--nx-primary) 10%, transparent);
405
- }
406
-
407
- .nx-dropdown-list:not([aria-multiselectable]) .nx-dropdown-option[aria-selected="true"][data-active] {
408
- background: color-mix(in oklab, var(--nx-primary) 17%, transparent);
409
- }
410
-
411
- .nx-dropdown-list:not([aria-multiselectable]) .nx-dropdown-option[aria-selected="true"] :is(.nx-dropdown-option__text, .nx-dropdown-cell__text) {
412
- font-weight: 500;
413
- }
414
-
415
- .nx-dropdown-option[aria-disabled="true"] {
416
- background: none;
417
- cursor: default;
418
- }
419
-
420
- .nx-dropdown-option[aria-disabled="true"] > * {
421
- opacity: 0.5;
422
- }
423
-
424
- /* Rows of an earlier search, while the next one loads. */
425
- .nx-dropdown-list[data-stale] .nx-dropdown-option {
426
- opacity: 0.55;
427
- pointer-events: none;
428
- }
429
-
430
- .nx-dropdown-option__check {
431
- display: grid;
432
- flex-shrink: 0;
433
- place-items: center;
434
- width: var(--nx-dropdown-check);
435
- color: var(--nx-primary);
436
- }
437
-
438
- .nx-dropdown-option__check .nx-icon {
439
- width: var(--nx-dropdown-check);
440
- height: var(--nx-dropdown-check);
441
- vertical-align: 0;
442
- }
443
-
444
- /*
445
- * The checkbox of a multiple list, drawn like the checkbox input. The list panel is lighter than a page panel in dark
446
- * mode, so the edge takes the muted color there to keep 3:1, and the box takes the row color instead of a darker fill.
447
- */
448
- .nx-dropdown-checkbox {
449
- display: grid;
450
- place-content: center;
451
- width: var(--nx-dropdown-check);
452
- height: var(--nx-dropdown-check);
453
- border: 1px solid light-dark(var(--nx-text-subtle), var(--nx-text-muted));
454
- border-radius: 4px;
455
- background: transparent;
456
- color: var(--nx-primary-text);
457
- transition:
458
- background-color var(--nx-duration),
459
- border-color var(--nx-duration);
460
- }
461
-
462
- .nx-dropdown-checkbox::before {
463
- content: "";
464
- width: calc(var(--nx-dropdown-check) - 2px);
465
- height: calc(var(--nx-dropdown-check) - 2px);
466
- background: currentColor;
467
- mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5l9.5 -10'/%3E%3C/svg%3E") center / 100% no-repeat;
468
- transform: scale(0);
469
- transition: transform var(--nx-duration) ease-out;
470
- }
471
-
472
- .nx-dropdown-option:hover .nx-dropdown-checkbox {
473
- border-color: light-dark(var(--nx-text-muted), var(--nx-text));
474
- }
475
-
476
- .nx-dropdown-option[aria-selected="true"] .nx-dropdown-checkbox {
477
- border-color: var(--nx-primary-solid);
478
- background: var(--nx-primary-solid);
479
- }
480
-
481
- .nx-dropdown-option[aria-selected="true"] .nx-dropdown-checkbox::before {
482
- transform: scale(1);
483
- }
484
-
485
- .nx-dropdown-option__icon {
486
- display: grid;
487
- flex-shrink: 0;
488
- place-items: center;
489
- width: 1.15em;
490
- color: var(--nx-text-muted);
491
- }
492
-
493
- .nx-dropdown-option__icon .nx-icon,
494
- .nx-dropdown-icon .nx-icon {
495
- width: 1.15em;
496
- height: 1.15em;
497
- vertical-align: 0;
498
- }
499
-
500
- .nx-dropdown-option__body {
501
- display: grid;
502
- flex: 1 1 auto;
503
- min-width: 0;
504
- }
505
-
506
- .nx-dropdown-option__label {
507
- display: flex;
508
- align-items: center;
509
- gap: 6px;
510
- min-width: 0;
511
- }
512
-
513
- .nx-dropdown-option__text {
514
- min-width: 0;
515
- overflow: hidden;
516
- text-overflow: ellipsis;
517
- white-space: nowrap;
518
- }
519
-
520
- .nx-dropdown-option__description {
521
- color: var(--nx-text-muted);
522
- font-size: var(--nx-text-xs);
523
- line-height: 1.4;
524
- }
525
-
526
- /* Cells */
527
- .nx-dropdown-cell {
528
- display: flex;
529
- align-items: center;
530
- gap: 6px;
531
- min-width: 0;
532
- white-space: nowrap;
533
- }
534
-
535
- .nx-dropdown-cell__text {
536
- min-width: 0;
537
- overflow: hidden;
538
- text-overflow: ellipsis;
539
- }
540
-
541
- .nx-dropdown-cell--muted {
542
- color: var(--nx-text-muted);
543
- }
544
-
545
- .nx-dropdown-cell--code {
546
- color: var(--nx-text-muted);
547
- font-size: 0.93em;
548
- font-variant-numeric: tabular-nums;
549
- font-weight: 500;
550
- letter-spacing: 0.03em;
551
- }
552
-
553
- .nx-dropdown-cell--media {
554
- display: grid;
555
- place-items: center;
556
- }
557
-
558
- /* A bare value, with no fields for the columns. */
559
- .nx-dropdown-cell--span {
560
- grid-column: 2 / -1;
561
- }
562
-
563
- .nx-dropdown-icon {
564
- display: inline-grid;
565
- flex-shrink: 0;
566
- place-items: center;
567
- color: var(--nx-text-muted);
568
- }
569
-
570
- /* The shared avatar, at the size of the dropdown's pictures. */
571
- .nx-dropdown-avatar {
572
- --nx-avatar-size: var(--nx-dropdown-media);
573
- }
574
-
575
- /* A flag or a logo: 4:3, with a faint edge so white flags do not melt into the panel. */
576
- .nx-dropdown-image {
577
- display: block;
578
- flex-shrink: 0;
579
- width: auto;
580
- height: calc(var(--nx-dropdown-media) * 0.66);
581
- aspect-ratio: 4 / 3;
582
- border-radius: 2px;
583
- box-shadow: 0 0 0 1px color-mix(in oklab, var(--nx-text) 14%, transparent);
584
- object-fit: cover;
585
- }
586
-
587
- .nx-dropdown-image--empty {
588
- background: color-mix(in oklab, var(--nx-text) 8%, transparent);
589
- }
590
-
591
- /* Why a current value cannot be picked again. The text color, not muted: muted text on a tint misses 4.5:1 in most themes. */
592
- .nx-dropdown-tag {
593
- flex-shrink: 0;
594
- padding: 1px 6px;
595
- border-radius: var(--nx-radius-full);
596
- background: color-mix(in oklab, var(--nx-text) 9%, transparent);
597
- color: var(--nx-text);
598
- font-size: var(--nx-text-xs);
599
- font-weight: 500;
600
- line-height: 1.35;
601
- white-space: nowrap;
602
- }
603
-
604
- .nx-dropdown-option[aria-disabled="true"] .nx-dropdown-tag {
605
- opacity: 1;
606
- }
607
-
608
- /* Placeholders while options load */
609
- .nx-dropdown-option--skeleton {
610
- cursor: default;
611
- }
612
-
613
- .nx-dropdown-skeleton {
614
- display: inline-block;
615
- width: 6em;
616
- max-width: 100%;
617
- height: 0.7em;
618
- border-radius: var(--nx-radius-full);
619
- background: color-mix(in oklab, var(--nx-text) 11%, transparent);
620
- vertical-align: middle;
621
- animation: nx-dropdown-pulse 1.2s ease-in-out infinite;
622
- }
623
-
624
- .nx-dropdown-skeleton--avatar {
625
- width: var(--nx-dropdown-media);
626
- height: var(--nx-dropdown-media);
627
- border-radius: 50%;
628
- }
629
-
630
- .nx-dropdown-skeleton--image {
631
- width: calc(var(--nx-dropdown-media) * 0.88);
632
- height: calc(var(--nx-dropdown-media) * 0.66);
633
- border-radius: 2px;
634
- }
635
-
636
- .nx-dropdown-skeleton--icon {
637
- width: 1.15em;
638
- height: 1.15em;
639
- border-radius: var(--nx-radius-sm);
640
- }
641
-
642
- @keyframes nx-dropdown-pulse {
643
- 50% {
644
- opacity: 0.45;
645
- }
646
- }
647
-
648
- /* Loading more, a failed load, no matches */
649
- .nx-dropdown-list__status {
650
- display: flex;
651
- align-items: center;
652
- justify-content: center;
653
- flex-wrap: wrap;
654
- gap: var(--nx-space-2);
655
- padding: 10px var(--nx-space-3);
656
- color: var(--nx-text-muted);
657
- font-size: var(--nx-text-sm);
658
- text-align: center;
659
- }
660
-
661
- .nx-dropdown-list__status > .nx-icon {
662
- flex-shrink: 0;
663
- width: 16px;
664
- height: 16px;
665
- vertical-align: 0;
666
- }
667
-
668
- .nx-dropdown-list__status--error > .nx-icon {
669
- color: var(--nx-danger);
670
- }
671
-
672
- /* The footer never gives up height: a panel short of room shrinks its list instead. */
673
- .nx-dropdown-panel__foot {
674
- display: flex;
675
- flex: none;
676
- flex-wrap: wrap;
677
- align-items: center;
678
- justify-content: space-between;
679
- gap: 4px var(--nx-space-3);
680
- min-height: 40px;
681
- padding: 4px var(--nx-space-2) 4px var(--nx-space-3);
682
- border-top: 1px solid var(--nx-border);
683
- color: var(--nx-text-muted);
684
- font-size: var(--nx-text-xs);
685
- }
686
-
687
- /*
688
- * A multiple choice's one footer: Select all and Clear on the left, where their hover tint lines up with the rows'
689
- * and their text with the checkboxes; the count and Done on the right. A narrow panel moves the right pair to a
690
- * second line.
691
- */
692
- .nx-dropdown-panel__foot--actions {
693
- padding-left: var(--nx-space-2);
694
- }
695
-
696
- .nx-dropdown-panel__actions,
697
- .nx-dropdown-panel__end {
698
- display: flex;
699
- align-items: center;
700
- }
701
-
702
- .nx-dropdown-panel__actions {
703
- gap: 2px;
704
- }
705
-
706
- .nx-dropdown-panel__end {
707
- gap: var(--nx-space-3);
708
- margin-left: auto;
709
- }
710
-
711
- .nx-dropdown-panel__count {
712
- font-variant-numeric: tabular-nums;
713
- white-space: nowrap;
714
- }
715
-
716
- [data-nx-motion="reduce"] .nx-dropdown-skeleton {
717
- animation: none;
718
- }
719
-
720
- [data-nx-motion="reduce"] .nx-dropdown__toggle .nx-icon,
721
- [data-nx-motion="reduce"] .nx-dropdown-checkbox,
722
- [data-nx-motion="reduce"] .nx-dropdown-checkbox::before {
723
- transition: none;
724
- }
725
- }
1
+ @layer nexus.components {
2
+ /*
3
+ * Dropdown: the field shell holds the chosen value (or chips for several) over a search input, and the list opens in
4
+ * a popover placed by the component. Rows are flex rows, or, with columns, subgrid rows of one grid, so every column
5
+ * lines up down the list like a table. Current values that cannot be picked again carry a tag and a dashed chip.
6
+ */
7
+ .nx-field--dropdown {
8
+ --nx-dropdown-chip: 22px;
9
+ --nx-dropdown-value-media: 20px;
10
+ }
11
+
12
+ .nx-field--dropdown.nx-field--sm {
13
+ --nx-dropdown-chip: 18px;
14
+ --nx-dropdown-value-media: 16px;
15
+ }
16
+
17
+ .nx-field--dropdown.nx-field--lg {
18
+ --nx-dropdown-chip: 26px;
19
+ --nx-dropdown-value-media: 22px;
20
+ }
21
+
22
+ /* The box */
23
+ .nx-dropdown__control {
24
+ position: relative;
25
+ flex: 1 1 auto;
26
+ align-self: stretch;
27
+ min-width: 0;
28
+ cursor: pointer;
29
+ }
30
+
31
+ .nx-field[data-disabled] .nx-dropdown__control {
32
+ cursor: not-allowed;
33
+ }
34
+
35
+ .nx-field[data-readonly] .nx-dropdown__control {
36
+ cursor: default;
37
+ }
38
+
39
+ .nx-dropdown__control:not([data-searchable]) .nx-dropdown__input {
40
+ caret-color: transparent;
41
+ cursor: inherit;
42
+ }
43
+
44
+ /* One value: the value and the input share one cell; typing hides the value and shows the search. */
45
+ .nx-dropdown__control--single {
46
+ display: grid;
47
+ }
48
+
49
+ .nx-dropdown__control--single > :is(.nx-dropdown__value, .nx-dropdown__input) {
50
+ grid-area: 1 / 1;
51
+ }
52
+
53
+ .nx-dropdown__value {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: var(--nx-space-2);
57
+ min-width: 0;
58
+ padding: 0 var(--nx-field-pad-x);
59
+ color: var(--nx-text);
60
+ font-size: var(--nx-field-font);
61
+ line-height: 1.4;
62
+ pointer-events: none;
63
+ }
64
+
65
+ .nx-dropdown__value-text {
66
+ min-width: 0;
67
+ overflow: hidden;
68
+ text-overflow: ellipsis;
69
+ white-space: nowrap;
70
+ }
71
+
72
+ .nx-dropdown__control--single:has(.nx-dropdown__input:not(:placeholder-shown)) .nx-dropdown__value {
73
+ visibility: hidden;
74
+ }
75
+
76
+ .nx-field--inbox .nx-dropdown__value {
77
+ padding-top: var(--nx-field-inbox-pad);
78
+ }
79
+
80
+ .nx-field__icon:not(.nx-field__icon--end) + .nx-dropdown__control :is(.nx-dropdown__value, .nx-dropdown__input) {
81
+ padding-left: var(--nx-space-2);
82
+ }
83
+
84
+ .nx-dropdown__control:has(~ .nx-field__actions) :is(.nx-dropdown__value, .nx-dropdown__input) {
85
+ padding-right: var(--nx-space-1);
86
+ }
87
+
88
+ .nx-dropdown__value .nx-dropdown-avatar {
89
+ --nx-dropdown-media: var(--nx-dropdown-value-media);
90
+ }
91
+
92
+ .nx-dropdown__value .nx-dropdown-image {
93
+ height: calc(var(--nx-dropdown-value-media) * 0.72);
94
+ }
95
+
96
+ /* Several values: chips wrap, and the search input takes the rest of the last line. */
97
+ .nx-dropdown__control--multiple {
98
+ display: flex;
99
+ flex-wrap: wrap;
100
+ align-content: center;
101
+ align-items: center;
102
+ gap: 4px;
103
+ padding: 4px var(--nx-field-pad-x);
104
+ }
105
+
106
+ .nx-field--inbox .nx-dropdown__control--multiple {
107
+ padding-top: calc(var(--nx-field-inbox-pad) + 1px);
108
+ }
109
+
110
+ .nx-field__icon:not(.nx-field__icon--end) + .nx-dropdown__control--multiple {
111
+ padding-left: var(--nx-space-2);
112
+ }
113
+
114
+ .nx-dropdown__control--multiple:has(~ .nx-field__actions) {
115
+ padding-right: var(--nx-space-1);
116
+ }
117
+
118
+ .nx-field .nx-dropdown__control--multiple .nx-dropdown__input {
119
+ flex: 1 1 2.5rem;
120
+ align-self: center;
121
+ width: 2.5rem;
122
+ min-width: 2.5rem;
123
+ height: var(--nx-dropdown-chip);
124
+ padding: 0;
125
+ }
126
+
127
+ /*
128
+ * With chips, the search input needs room only while someone can type: at rest (or without search) it shrinks to the
129
+ * end of the last line, so chips that fill a line leave no empty line under them.
130
+ */
131
+ .nx-field .nx-dropdown__control--multiple[data-has-value]:is(:not(:focus-within), :not([data-searchable])) .nx-dropdown__input {
132
+ flex-basis: 1px;
133
+ width: 1px;
134
+ min-width: 1px;
135
+ }
136
+
137
+ .nx-dropdown-chip {
138
+ --nx-dropdown-media: calc(var(--nx-dropdown-chip) - 4px);
139
+
140
+ display: inline-flex;
141
+ align-items: center;
142
+ gap: 5px;
143
+ min-width: 0;
144
+ max-width: 100%;
145
+ height: var(--nx-dropdown-chip);
146
+ padding: 0 7px;
147
+ border: 1px solid transparent;
148
+ border-radius: var(--nx-radius-sm);
149
+ background: color-mix(in oklab, var(--nx-text) 8%, var(--nx-field-bg));
150
+ color: var(--nx-text);
151
+ font-size: var(--nx-text-sm);
152
+ line-height: 1;
153
+ white-space: nowrap;
154
+ cursor: default;
155
+ }
156
+
157
+ .nx-field--sm .nx-dropdown-chip {
158
+ gap: 4px;
159
+ padding: 0 5px;
160
+ font-size: var(--nx-text-xs);
161
+ }
162
+
163
+ .nx-dropdown-chip:has(.nx-dropdown-chip__remove) {
164
+ padding-right: 1px;
165
+ }
166
+
167
+ .nx-dropdown-chip:has(> .nx-dropdown-avatar) {
168
+ padding-left: 2px;
169
+ }
170
+
171
+ .nx-dropdown-chip__label {
172
+ min-width: 0;
173
+ overflow: hidden;
174
+ text-overflow: ellipsis;
175
+ }
176
+
177
+ .nx-dropdown-chip .nx-dropdown-image {
178
+ height: calc(var(--nx-dropdown-chip) - 10px);
179
+ }
180
+
181
+ .nx-dropdown-chip .nx-dropdown-icon {
182
+ color: var(--nx-text-muted);
183
+ }
184
+
185
+ /* A current value that cannot be picked again: an outline instead of a fill. The tooltip says why. */
186
+ .nx-dropdown-chip[data-state] {
187
+ border-style: dashed;
188
+ border-color: var(--nx-text-subtle);
189
+ background: transparent;
190
+ color: var(--nx-text-muted);
191
+ }
192
+
193
+ /* "+12 more": the values past maxChips. Its tooltip names them. */
194
+ .nx-dropdown-chip--more {
195
+ font-variant-numeric: tabular-nums;
196
+ font-weight: 500;
197
+ }
198
+
199
+ .nx-dropdown-chip__remove {
200
+ display: grid;
201
+ place-items: center;
202
+ flex-shrink: 0;
203
+ width: calc(var(--nx-dropdown-chip) - 4px);
204
+ height: calc(var(--nx-dropdown-chip) - 4px);
205
+ padding: 0;
206
+ border: 0;
207
+ border-radius: 3px;
208
+ background: none;
209
+ color: var(--nx-text-muted);
210
+ cursor: pointer;
211
+ transition:
212
+ background-color var(--nx-duration),
213
+ color var(--nx-duration);
214
+ }
215
+
216
+ .nx-dropdown-chip__remove:hover {
217
+ background: color-mix(in oklab, var(--nx-text) 12%, transparent);
218
+ color: var(--nx-text);
219
+ }
220
+
221
+ .nx-dropdown-chip__remove .nx-icon {
222
+ width: 12px;
223
+ height: 12px;
224
+ vertical-align: 0;
225
+ }
226
+
227
+ .nx-dropdown__spinner {
228
+ display: grid;
229
+ place-items: center;
230
+ width: calc(var(--nx-field-base) - 10px);
231
+ height: calc(var(--nx-field-base) - 10px);
232
+ color: var(--nx-text-muted);
233
+ }
234
+
235
+ .nx-dropdown__spinner .nx-icon {
236
+ width: var(--nx-field-icon);
237
+ height: var(--nx-field-icon);
238
+ vertical-align: 0;
239
+ }
240
+
241
+ .nx-dropdown__toggle .nx-icon {
242
+ transition: transform 160ms ease;
243
+ }
244
+
245
+ .nx-dropdown__toggle[aria-expanded="true"] {
246
+ color: var(--nx-field-accent);
247
+ }
248
+
249
+ .nx-dropdown__toggle[aria-expanded="true"] .nx-icon {
250
+ transform: rotate(180deg);
251
+ }
252
+
253
+ /* The panel */
254
+ .nx-dropdown-panel {
255
+ --nx-dropdown-row: calc(var(--nx-row) + 2px);
256
+ --nx-dropdown-font: var(--nx-text-md);
257
+ --nx-dropdown-media: 24px;
258
+ --nx-dropdown-check: 16px;
259
+ --nx-dropdown-pad-x: var(--nx-space-2);
260
+ --nx-dropdown-bg: light-dark(var(--nx-surface), var(--nx-surface-2));
261
+
262
+ position: fixed;
263
+ inset: auto;
264
+ width: max-content;
265
+ max-width: min(640px, calc(100vw - 16px));
266
+ overflow: hidden;
267
+ }
268
+
269
+ /* Two classes: the shared panel (.nx-popover) loads after this file, and its padding would wrap the list in 16px. */
270
+ .nx-popover.nx-dropdown-panel {
271
+ padding: 0;
272
+ }
273
+
274
+ .nx-dropdown-panel--sm {
275
+ --nx-dropdown-row: calc(var(--nx-row) - 2px);
276
+ --nx-dropdown-font: var(--nx-text-sm);
277
+ --nx-dropdown-media: 20px;
278
+ --nx-dropdown-check: 14px;
279
+ }
280
+
281
+ .nx-dropdown-panel--lg {
282
+ --nx-dropdown-row: calc(var(--nx-row) + 8px);
283
+ --nx-dropdown-font: var(--nx-text-lg);
284
+ --nx-dropdown-media: 28px;
285
+ --nx-dropdown-check: 18px;
286
+ --nx-dropdown-pad-x: 10px;
287
+ }
288
+
289
+ /* Only while open: a display on the popover itself would show it closed. */
290
+ .nx-dropdown-panel:popover-open {
291
+ display: flex;
292
+ flex-direction: column;
293
+ }
294
+
295
+ /*
296
+ * Rows sit 10px from the top and bottom and 8px from the sides. Where scroll bars take room (Windows), the gutter
297
+ * kept for one is already space on the right, so the right padding gives it back (the component measures it).
298
+ */
299
+ .nx-dropdown-list {
300
+ position: relative;
301
+ flex: 1 1 auto;
302
+ min-height: 0;
303
+ padding: 10px 8px;
304
+ padding-right: max(0px, 8px - var(--nx-dropdown-gutter, 0px));
305
+ overflow: hidden auto;
306
+ overscroll-behavior: contain;
307
+ outline: none;
308
+ color: var(--nx-text);
309
+ font-size: var(--nx-dropdown-font);
310
+ line-height: 1.35;
311
+ scrollbar-gutter: stable;
312
+ }
313
+
314
+ /*
315
+ * Columns: one grid for the whole list; rows and the header are subgrids, so columns line up across rows. Subgrid rows
316
+ * take no side padding (Chromium does not widen the edge tracks for it): the check cell carries the start inset, and
317
+ * an empty last track the end.
318
+ */
319
+ .nx-dropdown-list--columns {
320
+ display: grid;
321
+ align-content: start;
322
+ column-gap: 10px;
323
+ padding-top: 0;
324
+ }
325
+
326
+ .nx-dropdown-list--columns > * {
327
+ grid-column: 1 / -1;
328
+ }
329
+
330
+ .nx-dropdown-list--columns > :is(.nx-dropdown-option, .nx-dropdown-list__header) {
331
+ display: grid;
332
+ grid-template-columns: subgrid;
333
+ align-items: center;
334
+ }
335
+
336
+ .nx-dropdown-list--columns > .nx-dropdown-list__header + *,
337
+ .nx-dropdown-list--columns > :first-child:not(.nx-dropdown-list__header) {
338
+ margin-top: 4px;
339
+ }
340
+
341
+ .nx-dropdown-list__header {
342
+ position: sticky;
343
+ z-index: 1;
344
+ top: 0;
345
+ padding: 7px 0 6px;
346
+ border-bottom: 1px solid var(--nx-border);
347
+ background: var(--nx-dropdown-bg);
348
+ color: var(--nx-text-muted);
349
+ font-size: var(--nx-text-xs);
350
+ font-weight: 600;
351
+ line-height: 1.3;
352
+ white-space: nowrap;
353
+ }
354
+
355
+ .nx-dropdown-list__heading {
356
+ min-width: 0;
357
+ overflow: hidden;
358
+ text-overflow: ellipsis;
359
+ }
360
+
361
+ .nx-dropdown-list__group {
362
+ padding: 6px var(--nx-dropdown-pad-x) 3px;
363
+ color: var(--nx-text-muted);
364
+ font-size: var(--nx-text-xs);
365
+ font-weight: 600;
366
+ letter-spacing: 0.04em;
367
+ text-transform: uppercase;
368
+ }
369
+
370
+ .nx-dropdown-list__separator {
371
+ height: 1px;
372
+ margin: 4px 0;
373
+ background: var(--nx-border);
374
+ }
375
+
376
+ /* Rows */
377
+ .nx-dropdown-option {
378
+ display: flex;
379
+ align-items: center;
380
+ gap: var(--nx-space-2);
381
+ min-height: var(--nx-dropdown-row);
382
+ padding: 5px var(--nx-dropdown-pad-x);
383
+ border-radius: var(--nx-radius);
384
+ cursor: pointer;
385
+ user-select: none;
386
+ }
387
+
388
+ /* The list's gutters, not the row's own 8px gap: a subgrid with a gap of its own sets its cells a pixel past each side
389
+ of their headings, so a figure at the end of its column ended a pixel past its heading. */
390
+ .nx-dropdown-list--columns > .nx-dropdown-option {
391
+ column-gap: normal;
392
+ padding-inline: 0;
393
+ }
394
+
395
+ .nx-dropdown-list--columns > * > .nx-dropdown-option__check,
396
+ .nx-dropdown-list--columns > .nx-dropdown-list__header > :first-child {
397
+ box-sizing: content-box;
398
+ padding-left: var(--nx-dropdown-pad-x);
399
+ }
400
+
401
+ .nx-dropdown-option[data-active] {
402
+ background: var(--nx-surface-hover);
403
+ }
404
+
405
+ /* The chosen row of a single choice: a faint primary tint, a check, and a heavier label. */
406
+ .nx-dropdown-list:not([aria-multiselectable]) .nx-dropdown-option[aria-selected="true"] {
407
+ background: color-mix(in oklab, var(--nx-primary) 10%, transparent);
408
+ }
409
+
410
+ .nx-dropdown-list:not([aria-multiselectable]) .nx-dropdown-option[aria-selected="true"][data-active] {
411
+ background: color-mix(in oklab, var(--nx-primary) 17%, transparent);
412
+ }
413
+
414
+ .nx-dropdown-list:not([aria-multiselectable]) .nx-dropdown-option[aria-selected="true"] :is(.nx-dropdown-option__text, .nx-dropdown-cell__text) {
415
+ font-weight: 500;
416
+ }
417
+
418
+ .nx-dropdown-option[aria-disabled="true"] {
419
+ background: none;
420
+ cursor: default;
421
+ }
422
+
423
+ .nx-dropdown-option[aria-disabled="true"] > * {
424
+ opacity: 0.5;
425
+ }
426
+
427
+ /* Rows of an earlier search, while the next one loads. */
428
+ .nx-dropdown-list[data-stale] .nx-dropdown-option {
429
+ opacity: 0.55;
430
+ pointer-events: none;
431
+ }
432
+
433
+ .nx-dropdown-option__check {
434
+ display: grid;
435
+ flex-shrink: 0;
436
+ place-items: center;
437
+ width: var(--nx-dropdown-check);
438
+ color: var(--nx-primary);
439
+ }
440
+
441
+ .nx-dropdown-option__check .nx-icon {
442
+ width: var(--nx-dropdown-check);
443
+ height: var(--nx-dropdown-check);
444
+ vertical-align: 0;
445
+ }
446
+
447
+ /*
448
+ * The checkbox of a multiple list, drawn like the checkbox input. The list panel is lighter than a page panel in dark
449
+ * mode, so the edge takes the muted color there to keep 3:1, and the box takes the row color instead of a darker fill.
450
+ */
451
+ .nx-dropdown-checkbox {
452
+ display: grid;
453
+ place-content: center;
454
+ width: var(--nx-dropdown-check);
455
+ height: var(--nx-dropdown-check);
456
+ border: 1px solid light-dark(var(--nx-text-subtle), var(--nx-text-muted));
457
+ border-radius: 4px;
458
+ background: transparent;
459
+ color: var(--nx-primary-text);
460
+ transition:
461
+ background-color var(--nx-duration),
462
+ border-color var(--nx-duration);
463
+ }
464
+
465
+ .nx-dropdown-checkbox::before {
466
+ content: "";
467
+ width: calc(var(--nx-dropdown-check) - 2px);
468
+ height: calc(var(--nx-dropdown-check) - 2px);
469
+ background: currentColor;
470
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5l9.5 -10'/%3E%3C/svg%3E") center / 100% no-repeat;
471
+ transform: scale(0);
472
+ transition: transform var(--nx-duration) ease-out;
473
+ }
474
+
475
+ .nx-dropdown-option:hover .nx-dropdown-checkbox {
476
+ border-color: light-dark(var(--nx-text-muted), var(--nx-text));
477
+ }
478
+
479
+ .nx-dropdown-option[aria-selected="true"] .nx-dropdown-checkbox {
480
+ border-color: var(--nx-primary-solid);
481
+ background: var(--nx-primary-solid);
482
+ }
483
+
484
+ .nx-dropdown-option[aria-selected="true"] .nx-dropdown-checkbox::before {
485
+ transform: scale(1);
486
+ }
487
+
488
+ .nx-dropdown-option__icon {
489
+ display: grid;
490
+ flex-shrink: 0;
491
+ place-items: center;
492
+ width: 1.15em;
493
+ color: var(--nx-text-muted);
494
+ }
495
+
496
+ .nx-dropdown-option__icon .nx-icon,
497
+ .nx-dropdown-icon .nx-icon {
498
+ width: 1.15em;
499
+ height: 1.15em;
500
+ vertical-align: 0;
501
+ }
502
+
503
+ .nx-dropdown-option__body {
504
+ display: grid;
505
+ flex: 1 1 auto;
506
+ min-width: 0;
507
+ }
508
+
509
+ .nx-dropdown-option__label {
510
+ display: flex;
511
+ align-items: center;
512
+ gap: 6px;
513
+ min-width: 0;
514
+ }
515
+
516
+ .nx-dropdown-option__text {
517
+ min-width: 0;
518
+ overflow: hidden;
519
+ text-overflow: ellipsis;
520
+ white-space: nowrap;
521
+ }
522
+
523
+ .nx-dropdown-option__description {
524
+ color: var(--nx-text-muted);
525
+ font-size: var(--nx-text-xs);
526
+ line-height: 1.4;
527
+ }
528
+
529
+ /* Cells */
530
+ .nx-dropdown-cell {
531
+ display: flex;
532
+ align-items: center;
533
+ gap: 6px;
534
+ min-width: 0;
535
+ white-space: nowrap;
536
+ }
537
+
538
+ .nx-dropdown-cell__text {
539
+ min-width: 0;
540
+ overflow: hidden;
541
+ text-overflow: ellipsis;
542
+ }
543
+
544
+ .nx-dropdown-cell--muted {
545
+ color: var(--nx-text-muted);
546
+ }
547
+
548
+ .nx-dropdown-cell--code {
549
+ color: var(--nx-text-muted);
550
+ font-size: 0.93em;
551
+ font-variant-numeric: tabular-nums;
552
+ font-weight: 500;
553
+ letter-spacing: 0.03em;
554
+ }
555
+
556
+ /* A price beside a name: figures in even digits, so a column of them lines up at the end. */
557
+ .nx-dropdown-cell--money {
558
+ font-variant-numeric: tabular-nums;
559
+ }
560
+
561
+ .nx-dropdown-cell--media {
562
+ display: grid;
563
+ place-items: center;
564
+ }
565
+
566
+ /* A bare value, with no fields for the columns. */
567
+ .nx-dropdown-cell--span {
568
+ grid-column: 2 / -1;
569
+ }
570
+
571
+ .nx-dropdown-icon {
572
+ display: inline-grid;
573
+ flex-shrink: 0;
574
+ place-items: center;
575
+ color: var(--nx-text-muted);
576
+ }
577
+
578
+ /* The shared avatar, at the size of the dropdown's pictures. */
579
+ .nx-dropdown-avatar {
580
+ --nx-avatar-size: var(--nx-dropdown-media);
581
+ }
582
+
583
+ /* A flag or a logo: 4:3, with a faint edge so white flags do not melt into the panel. */
584
+ .nx-dropdown-image {
585
+ display: block;
586
+ flex-shrink: 0;
587
+ width: auto;
588
+ height: calc(var(--nx-dropdown-media) * 0.66);
589
+ aspect-ratio: 4 / 3;
590
+ border-radius: 2px;
591
+ box-shadow: 0 0 0 1px color-mix(in oklab, var(--nx-text) 14%, transparent);
592
+ object-fit: cover;
593
+ }
594
+
595
+ .nx-dropdown-image--empty {
596
+ background: color-mix(in oklab, var(--nx-text) 8%, transparent);
597
+ }
598
+
599
+ /* Why a current value cannot be picked again. The text color, not muted: muted text on a tint misses 4.5:1 in most themes. */
600
+ .nx-dropdown-tag {
601
+ flex-shrink: 0;
602
+ padding: 1px 6px;
603
+ border-radius: var(--nx-radius-full);
604
+ background: color-mix(in oklab, var(--nx-text) 9%, transparent);
605
+ color: var(--nx-text);
606
+ font-size: var(--nx-text-xs);
607
+ font-weight: 500;
608
+ line-height: 1.35;
609
+ white-space: nowrap;
610
+ }
611
+
612
+ .nx-dropdown-option[aria-disabled="true"] .nx-dropdown-tag {
613
+ opacity: 1;
614
+ }
615
+
616
+ /* Placeholders while options load */
617
+ .nx-dropdown-option--skeleton {
618
+ cursor: default;
619
+ }
620
+
621
+ .nx-dropdown-skeleton {
622
+ display: inline-block;
623
+ width: 6em;
624
+ max-width: 100%;
625
+ height: 0.7em;
626
+ border-radius: var(--nx-radius-full);
627
+ background: color-mix(in oklab, var(--nx-text) 11%, transparent);
628
+ vertical-align: middle;
629
+ animation: nx-dropdown-pulse 1.2s ease-in-out infinite;
630
+ }
631
+
632
+ .nx-dropdown-skeleton--avatar {
633
+ width: var(--nx-dropdown-media);
634
+ height: var(--nx-dropdown-media);
635
+ border-radius: 50%;
636
+ }
637
+
638
+ .nx-dropdown-skeleton--image {
639
+ width: calc(var(--nx-dropdown-media) * 0.88);
640
+ height: calc(var(--nx-dropdown-media) * 0.66);
641
+ border-radius: 2px;
642
+ }
643
+
644
+ .nx-dropdown-skeleton--icon {
645
+ width: 1.15em;
646
+ height: 1.15em;
647
+ border-radius: var(--nx-radius-sm);
648
+ }
649
+
650
+ @keyframes nx-dropdown-pulse {
651
+ 50% {
652
+ opacity: 0.45;
653
+ }
654
+ }
655
+
656
+ /* Loading more, a failed load, no matches */
657
+ .nx-dropdown-list__status {
658
+ display: flex;
659
+ align-items: center;
660
+ justify-content: center;
661
+ flex-wrap: wrap;
662
+ gap: var(--nx-space-2);
663
+ padding: 10px var(--nx-space-3);
664
+ color: var(--nx-text-muted);
665
+ font-size: var(--nx-text-sm);
666
+ text-align: center;
667
+ }
668
+
669
+ .nx-dropdown-list__status > .nx-icon {
670
+ flex-shrink: 0;
671
+ width: 16px;
672
+ height: 16px;
673
+ vertical-align: 0;
674
+ }
675
+
676
+ .nx-dropdown-list__status--error > .nx-icon {
677
+ color: var(--nx-danger);
678
+ }
679
+
680
+ /* The footer never gives up height: a panel short of room shrinks its list instead. */
681
+ .nx-dropdown-panel__foot {
682
+ display: flex;
683
+ flex: none;
684
+ flex-wrap: wrap;
685
+ align-items: center;
686
+ justify-content: space-between;
687
+ gap: 4px var(--nx-space-3);
688
+ min-height: 40px;
689
+ padding: 4px var(--nx-space-2) 4px var(--nx-space-3);
690
+ border-top: 1px solid var(--nx-border);
691
+ color: var(--nx-text-muted);
692
+ font-size: var(--nx-text-xs);
693
+ }
694
+
695
+ /*
696
+ * A multiple choice's one footer: Select all and Clear on the left, where their hover tint lines up with the rows'
697
+ * and their text with the checkboxes; the count and Done on the right. A narrow panel moves the right pair to a
698
+ * second line.
699
+ */
700
+ .nx-dropdown-panel__foot--actions {
701
+ padding-left: var(--nx-space-2);
702
+ }
703
+
704
+ .nx-dropdown-panel__actions,
705
+ .nx-dropdown-panel__end {
706
+ display: flex;
707
+ align-items: center;
708
+ }
709
+
710
+ .nx-dropdown-panel__actions {
711
+ gap: 2px;
712
+ }
713
+
714
+ .nx-dropdown-panel__end {
715
+ gap: var(--nx-space-3);
716
+ margin-left: auto;
717
+ }
718
+
719
+ .nx-dropdown-panel__count {
720
+ font-variant-numeric: tabular-nums;
721
+ white-space: nowrap;
722
+ }
723
+
724
+ [data-nx-motion="reduce"] .nx-dropdown-skeleton {
725
+ animation: none;
726
+ }
727
+
728
+ [data-nx-motion="reduce"] .nx-dropdown__toggle .nx-icon,
729
+ [data-nx-motion="reduce"] .nx-dropdown-checkbox,
730
+ [data-nx-motion="reduce"] .nx-dropdown-checkbox::before {
731
+ transition: none;
732
+ }
733
+ }