@wpmoo/ui 1.0.0-rc.2 → 1.0.0-rc.4

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 (84) hide show
  1. package/ASSET_LICENSE.md +3 -1
  2. package/README.md +50 -107
  3. package/certification.json +20 -3
  4. package/dist/assets/css/moo-ui.css +8254 -7629
  5. package/dist/assets/css/moo-ui.min.css +3 -3
  6. package/dist/assets/css/moo.css +595 -71
  7. package/dist/assets/css/moo.min.css +3 -3
  8. package/dist/js/chart.js +15258 -0
  9. package/dist/js/chart.min.js +27 -0
  10. package/dist/js/combobox.js +98 -40
  11. package/dist/js/context-menu.js +5 -0
  12. package/dist/js/datatable.js +5 -0
  13. package/dist/js/datepicker.js +1154 -0
  14. package/dist/js/datepicker.min.js +6 -0
  15. package/dist/js/moo-ui.js +18950 -0
  16. package/dist/js/moo-ui.min.js +27 -0
  17. package/dist/js/sidebar.js +67 -25
  18. package/dist/js/slider.js +324 -0
  19. package/package.json +24 -4
  20. package/scss/_components.scss +73 -0
  21. package/scss/_config.scss +24 -0
  22. package/scss/_settings.scss +8 -0
  23. package/scss/components/_accordion.scss +59 -0
  24. package/scss/components/_alert.scss +94 -0
  25. package/scss/components/_avatar.scss +154 -0
  26. package/scss/components/_badge.scss +33 -0
  27. package/scss/components/_breadcrumb.scss +130 -0
  28. package/scss/components/_button.scss +228 -0
  29. package/scss/components/_button_group.scss +51 -0
  30. package/scss/components/_card.scss +142 -0
  31. package/scss/components/_checkbox.scss +115 -0
  32. package/scss/components/_close_button.scss +59 -0
  33. package/scss/components/_collapsible.scss +43 -0
  34. package/scss/components/_combobox.scss +258 -0
  35. package/scss/components/_context-menu.scss +28 -0
  36. package/scss/components/_datatable.scss +1082 -0
  37. package/scss/components/_datepicker.scss +236 -0
  38. package/scss/components/_dialog.scss +66 -0
  39. package/scss/components/_dropdown.scss +202 -0
  40. package/scss/components/_field.scss +125 -0
  41. package/scss/components/_input.scss +35 -0
  42. package/scss/components/_input_group.scss +201 -0
  43. package/scss/components/_kbd.scss +10 -0
  44. package/scss/components/_menubar.scss +16 -0
  45. package/scss/components/_navigation.scss +41 -0
  46. package/scss/components/_pagination.scss +70 -0
  47. package/scss/components/_popover.scss +47 -0
  48. package/scss/components/_progress.scss +7 -0
  49. package/scss/components/_radio_group.scss +9 -0
  50. package/scss/components/_select.scss +7 -0
  51. package/scss/components/_separator.scss +15 -0
  52. package/scss/components/_sheet.scss +32 -0
  53. package/scss/components/_sidebar.scss +7 -0
  54. package/scss/components/_skeleton.scss +17 -0
  55. package/scss/components/_slider.scss +214 -0
  56. package/scss/components/_spinner.scss +17 -0
  57. package/scss/components/_switch.scss +25 -0
  58. package/scss/components/_table.scss +7 -0
  59. package/scss/components/_tabs.scss +139 -0
  60. package/scss/components/_textarea.scss +7 -0
  61. package/scss/components/_toast.scss +119 -0
  62. package/scss/components/_toggle_group.scss +19 -0
  63. package/scss/components/_tooltip.scss +83 -0
  64. package/scss/components/sidebar/_collapsed.scss +187 -0
  65. package/scss/components/sidebar/_identity.scss +72 -0
  66. package/scss/components/sidebar/_inset.scss +59 -0
  67. package/scss/components/sidebar/_layout.scss +176 -0
  68. package/scss/components/sidebar/_menus.scss +236 -0
  69. package/scss/foundations/_core_global_primitives.scss +5 -0
  70. package/scss/foundations/_core_state_layer.scss +25 -0
  71. package/scss/foundations/_focus.scss +139 -0
  72. package/scss/foundations/_overlay_backdrop.scss +19 -0
  73. package/scss/moo-core.scss +47 -0
  74. package/scss/moo-ui.scss +50 -0
  75. package/scss/settings/_bootstrap_overrides.scss +11 -0
  76. package/scss/settings/_component_variables.scss +479 -0
  77. package/scss/settings/_forms.scss +65 -0
  78. package/scss/settings/_palette.scss +132 -0
  79. package/scss/themes/_scoped_core.scss +239 -0
  80. package/scss/themes/_standalone_root.scss +117 -0
  81. package/scss/utilities/_scroll_fade.scss +193 -0
  82. package/scss/utilities/_scroll_fade_primitives.scss +43 -0
  83. package/scss/_facade-settings.scss +0 -51
  84. package/scss/settings/_facade_public.scss +0 -23
@@ -0,0 +1,1082 @@
1
+ // Bootstrap ships .table markup only, with no sort/filter/pagination
2
+ // behavior and no faceted-filter or column-visibility concept, so this
3
+ // partial covers only the pieces that have no Bootstrap token to consume:
4
+ // the facet dropdown's own checkbox indicator (mirroring the shared
5
+ // dropdown_toggle_item() indicator in scss/components/_dropdown.scss), the
6
+ // sort-direction icon swap, and the hidden-column state the runtime drives.
7
+
8
+ // A fixed layout keeps the table's overall width stable as sorting,
9
+ // filtering, pagination, and column visibility change which rows and
10
+ // columns are on screen; Bootstrap's own .table stays auto-layout (sized
11
+ // to the current content), which is what let the table jump width as
12
+ // visible content changed.
13
+ .datatable {
14
+ --moo-datatable-min-width: #{$moo-datatable-min-width};
15
+ --moo-datatable-actions-cell-fade-width: #{$moo-datatable-actions-cell-fade-width};
16
+ --moo-datatable-card-label-width: #{$moo-datatable-card-label-width};
17
+ --moo-datatable-frame-header-min-height: #{$moo-datatable-frame-header-min-height};
18
+ --moo-datatable-select-cell-inset: #{$moo-datatable-select-cell-inset};
19
+ --moo-datatable-table-select-all-offset-y: #{$moo-datatable-table-select-all-offset-y};
20
+ --moo-datatable-toolbar-control-height: #{$moo-datatable-toolbar-control-height};
21
+ --moo-datatable-search-filter-menu-width: #{$moo-datatable-search-filter-menu-width};
22
+ --moo-datatable-search-filter-options-menu-width: #{$moo-datatable-search-filter-options-menu-width};
23
+ --moo-datatable-scrollbar-size: #{$moo-datatable-scrollbar-size};
24
+ --moo-datatable-bulk-actions-bottom: #{$moo-datatable-bulk-actions-bottom};
25
+ }
26
+
27
+ .datatable-table {
28
+ table-layout: fixed;
29
+ }
30
+
31
+ .datatable--responsive-scroll .datatable-table,
32
+ .datatable--responsive-toggle .datatable-table {
33
+ min-width: var(--moo-datatable-min-width);
34
+ }
35
+
36
+ // Toggle mode renders both the table and the card list at once (unlike
37
+ // cards mode, which swaps them by breakpoint) so a person, not a viewport,
38
+ // can choose the layout the way Odoo's List/Kanban switcher works. Visibility
39
+ // follows the root's own data-datatable-view state instead of a d-*-none
40
+ // utility.
41
+ .datatable--responsive-toggle[data-datatable-view="table"] .datatable-card-frame {
42
+ display: none;
43
+ }
44
+
45
+ .datatable--responsive-toggle[data-datatable-view="cards"] .datatable-frame {
46
+ display: none;
47
+ }
48
+
49
+ // Bootstrap's .table has no bordered/rounded container variant of its own
50
+ // (.table-bordered adds vertical column rules too, which the reference
51
+ // doesn't have -- only an outer frame plus its own default row dividers).
52
+ // The radius has to live on this wrapper, not the <table> itself: Bootstrap
53
+ // resets every table to border-collapse: collapse, and a border-radius on a
54
+ // collapsed-border table does not clip its own cell backgrounds.
55
+ .datatable-frame {
56
+ border: var(--bs-border-width) solid var(--bs-border-color);
57
+ border-radius: var(--bs-border-radius);
58
+ overflow: hidden;
59
+ }
60
+
61
+ .datatable-card-frame {
62
+ border: var(--bs-border-width) solid var(--bs-border-color);
63
+ border-radius: var(--bs-border-radius);
64
+ overflow: hidden;
65
+ }
66
+
67
+ // Pin the actions column to the inline-end edge so the menu trigger stays
68
+ // in view at every scroll position; the opaque background hides the cells
69
+ // sliding underneath. Row menus are temporarily moved under body while open
70
+ // (see datatable.js), so the wrapper keeps its overflow and scroll offset.
71
+ .datatable-frame [data-datatable-column="actions"] {
72
+ position: sticky;
73
+ inset-inline-end: 0;
74
+ z-index: 2;
75
+ background: linear-gradient(
76
+ 90deg,
77
+ transparent 0,
78
+ var(--bs-body-bg) var(--moo-datatable-actions-cell-fade-width)
79
+ );
80
+ }
81
+
82
+ [dir="rtl"] .datatable-frame [data-datatable-column="actions"],
83
+ .datatable-frame [dir="rtl"] [data-datatable-column="actions"] {
84
+ background: linear-gradient(
85
+ 270deg,
86
+ transparent 0,
87
+ var(--bs-body-bg) var(--moo-datatable-actions-cell-fade-width)
88
+ );
89
+ }
90
+
91
+ // Card menus still live inside the card frame, so lift that frame while a
92
+ // card menu is open.
93
+ .datatable-card-frame:has(.datatable-card .dropdown-menu.show) {
94
+ position: relative;
95
+ z-index: $zindex-dropdown;
96
+ overflow: visible;
97
+ }
98
+
99
+ .datatable-frame-header {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: space-between;
103
+ gap: 0.625rem;
104
+ min-block-size: var(--moo-datatable-frame-header-min-height);
105
+ padding-block: 0.375rem;
106
+ padding-inline-start: var(--moo-datatable-select-cell-inset);
107
+ padding-inline-end: 0.5rem;
108
+ border-bottom: var(--bs-border-width) solid var(--bs-border-color);
109
+ background-color: var(--bs-body-bg);
110
+ }
111
+
112
+ .datatable-frame .table-responsive {
113
+ position: relative;
114
+ margin-bottom: 0;
115
+ }
116
+
117
+ .datatable-frame .datatable-table {
118
+ margin-bottom: 0;
119
+ }
120
+
121
+ .datatable-table > :not(caption) > * > * {
122
+ vertical-align: middle;
123
+ }
124
+
125
+ // The toolbar's flex layout belongs to the component itself, not to
126
+ // Bootstrap layout utilities on the macro markup: the narrow-screen grid
127
+ // override below can replace it without an !important fight against
128
+ // .d-flex, and the trailing actions group owns its own
129
+ // margin-inline-start: auto alignment instead of .ms-auto.
130
+ .datatable-toolbar {
131
+ display: flex;
132
+ flex-wrap: wrap;
133
+ align-items: center;
134
+ gap: 0.5rem;
135
+ margin-bottom: 0.5rem;
136
+ }
137
+
138
+ .datatable-toolbar-actions {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 0.5rem;
142
+ margin-inline-start: auto;
143
+ }
144
+
145
+ // Bootstrap's .form-control defaults to width: 100%, so inside the
146
+ // toolbar's flex row the search input claims the entire line on its own
147
+ // and pushes the Status/Priority facet triggers onto a wrapped second
148
+ // line even when there is room beside it. A max-width (not a fixed width)
149
+ // caps it on wide toolbars while still letting it shrink on narrow ones.
150
+ .datatable-toolbar-query {
151
+ display: flex;
152
+ flex: 0 1 22rem;
153
+ gap: 0.5rem;
154
+ align-items: center;
155
+ min-width: 20rem;
156
+ max-width: 100%;
157
+ }
158
+
159
+ .datatable-search-filter {
160
+ position: relative;
161
+ flex: 1 1 auto;
162
+ min-width: 0;
163
+ }
164
+
165
+ .datatable-searchbar {
166
+ position: relative;
167
+ display: flex;
168
+ flex: 1 1 auto;
169
+ flex-wrap: nowrap;
170
+ align-items: center;
171
+ min-block-size: var(--moo-datatable-toolbar-control-height);
172
+ width: 100%;
173
+ }
174
+
175
+ .datatable-searchbar .datatable-search {
176
+ flex: 1 1 auto;
177
+ max-width: none;
178
+ min-width: 0;
179
+ width: 1%;
180
+ padding-inline-start: 2rem;
181
+ }
182
+
183
+ .datatable-search-icon {
184
+ position: absolute;
185
+ inset-block-start: 50%;
186
+ inset-inline-start: 0.625rem;
187
+ z-index: 2;
188
+ display: inline-flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ color: var(--bs-secondary-color);
192
+ pointer-events: none;
193
+ transform: translateY(-50%);
194
+ }
195
+
196
+ .datatable-search-icon > [data-icon] {
197
+ width: 0.875rem;
198
+ height: 0.875rem;
199
+ }
200
+
201
+ .datatable-filter-picker-trigger {
202
+ display: inline-flex;
203
+ flex: 0 0 auto;
204
+ align-items: center;
205
+ justify-content: center;
206
+ inline-size: 2rem;
207
+ block-size: 2rem;
208
+ padding: 0;
209
+ margin-inline-end: 0;
210
+ border-radius: var(--bs-border-radius-sm);
211
+ gap: 0;
212
+ }
213
+
214
+ .btn.datatable-filter-picker-trigger:focus:not(:focus-visible) {
215
+ outline: 0;
216
+ box-shadow: none;
217
+ }
218
+
219
+ .btn.datatable-filter-picker-trigger:focus-visible {
220
+ z-index: 3;
221
+ outline: var(--bs-focus-ring-width) solid var(--moo-ring);
222
+ outline-offset: 0;
223
+ box-shadow: none;
224
+ }
225
+
226
+ .input-group > .btn-ghost.datatable-filter-picker-trigger:focus-visible::before {
227
+ opacity: 1;
228
+ }
229
+
230
+ .datatable-filter-picker-trigger > [data-icon] {
231
+ width: 0.875rem;
232
+ height: 0.875rem;
233
+ margin: 0;
234
+ }
235
+
236
+ .datatable-search-filter-menu {
237
+ --bs-dropdown-border-radius: var(--bs-border-radius);
238
+
239
+ width: var(--moo-datatable-search-filter-menu-width);
240
+ max-width: calc(100vw - 1.5rem);
241
+ max-height: calc(100vh - 8rem);
242
+ overflow-y: auto;
243
+ overscroll-behavior: contain;
244
+ }
245
+
246
+ .datatable-search-filter-menu[data-datatable-filter-menu-panel="options"] {
247
+ width: var(--moo-datatable-search-filter-options-menu-width);
248
+ }
249
+
250
+ .datatable-facet-menu {
251
+ --bs-dropdown-border-radius: var(--bs-border-radius);
252
+
253
+ min-width: $moo-datatable-facet-menu-min-width;
254
+ }
255
+
256
+ .datatable-filter-panel {
257
+ min-width: 0;
258
+ }
259
+
260
+ // Only the option rows scroll; the facet/back-button header and the search
261
+ // box above them stay in normal flow outside this nested list, so they
262
+ // never move. list-style/margin/padding reset because this <ul> nests
263
+ // inside a .dropdown-menu <li>, which does not get Bootstrap's own
264
+ // dropdown-menu list reset applied to it a second time.
265
+ .datatable-facet-options,
266
+ .datatable-filter-options {
267
+ max-height: $moo-datatable-facet-options-max-height;
268
+ margin: 0;
269
+ padding: 0;
270
+ overflow-y: auto;
271
+ overscroll-behavior: contain;
272
+ list-style: none;
273
+ }
274
+
275
+ .datatable-search-filter-menu .dropdown-header {
276
+ padding-block: 0.125rem 0.375rem;
277
+ padding-inline: 0.5rem;
278
+ color: var(--bs-secondary-color);
279
+ font-size: $font-size-sm;
280
+ font-weight: 600;
281
+ line-height: 1.25rem;
282
+ }
283
+
284
+ .datatable-search-filter-menu .dropdown-item,
285
+ .datatable-facet-menu .dropdown-item {
286
+ min-height: $moo-datatable-facet-item-height;
287
+ padding-block: 0.375rem;
288
+ padding-inline: 0.5rem;
289
+ border-radius: var(--bs-border-radius-sm);
290
+ gap: 0.5rem;
291
+ cursor: default;
292
+ line-height: 1.25rem;
293
+ }
294
+
295
+ .datatable-search-filter-menu .dropdown-item.active,
296
+ .datatable-facet-menu .dropdown-item.active {
297
+ color: var(--bs-dropdown-link-color);
298
+ background-color: transparent;
299
+ }
300
+
301
+ .datatable-search-filter-menu .dropdown-item:hover,
302
+ .datatable-search-filter-menu .dropdown-item:focus-visible,
303
+ .datatable-facet-menu .dropdown-item:hover,
304
+ .datatable-facet-menu .dropdown-item:focus-visible {
305
+ color: var(--bs-dropdown-link-hover-color);
306
+ background-color: var(--bs-dropdown-link-hover-bg);
307
+ outline: 0;
308
+ box-shadow: none;
309
+ }
310
+
311
+ .datatable-search-filter-menu .dropdown-item:focus:not(:focus-visible, :hover),
312
+ .datatable-facet-menu .dropdown-item:focus:not(:focus-visible, :hover) {
313
+ color: var(--bs-dropdown-link-color);
314
+ background-color: transparent;
315
+ outline: 0;
316
+ box-shadow: none;
317
+ }
318
+
319
+ .datatable-filter-group,
320
+ .datatable-filter-panel-back {
321
+ gap: 0.5rem;
322
+ }
323
+
324
+ .datatable-filter-group {
325
+ justify-content: flex-start;
326
+ }
327
+
328
+ .datatable-filter-group-summary {
329
+ color: var(--bs-secondary-color);
330
+ font-size: $font-size-sm;
331
+ font-weight: var(--bs-body-font-weight);
332
+ font-variant-numeric: tabular-nums;
333
+ }
334
+
335
+ .datatable-filter-group > [data-icon],
336
+ .datatable-filter-panel-back > [data-icon] {
337
+ width: 0.875rem;
338
+ height: 0.875rem;
339
+ color: var(--bs-secondary-color);
340
+ }
341
+
342
+ .datatable-filter-panel-back {
343
+ color: var(--bs-secondary-color);
344
+ font-weight: var(--bs-body-font-weight);
345
+ }
346
+
347
+ .datatable-filter-panel-back span {
348
+ color: var(--bs-body-color);
349
+ }
350
+
351
+ .datatable-filter-picker-item .text-body-secondary,
352
+ .datatable-facet-item .text-body-secondary {
353
+ flex: 0 0 auto;
354
+ font-variant-numeric: tabular-nums;
355
+ }
356
+
357
+ .datatable-filter-picker-item > [data-datatable-filter-option-label],
358
+ .datatable-facet-item > [data-datatable-facet-option-label] {
359
+ min-width: 0;
360
+ overflow: hidden;
361
+ text-overflow: ellipsis;
362
+ white-space: nowrap;
363
+ }
364
+
365
+ .datatable-filter-option-search-wrap,
366
+ .datatable-facet-search-wrap {
367
+ position: relative;
368
+ display: flex;
369
+ min-height: 2.25rem;
370
+ align-items: center;
371
+ gap: 0.5rem;
372
+ padding: 0 0.75rem;
373
+ }
374
+
375
+ .datatable-filter-option-search-wrap::after,
376
+ .datatable-facet-search-wrap::after {
377
+ position: absolute;
378
+ inset-inline: calc(-1 * var(--bs-dropdown-padding-x, 0));
379
+ inset-block-end: 0;
380
+ border-bottom: var(--bs-border-width) solid var(--bs-border-color);
381
+ content: "";
382
+ }
383
+
384
+ .datatable-dropdown-search-icon {
385
+ display: inline-flex;
386
+ flex: 0 0 auto;
387
+ align-items: center;
388
+ justify-content: center;
389
+ color: var(--bs-secondary-color);
390
+ }
391
+
392
+ .datatable-dropdown-search-icon > [data-icon] {
393
+ width: 0.875rem;
394
+ height: 0.875rem;
395
+ }
396
+
397
+ .datatable-filter-option-search,
398
+ .datatable-facet-search {
399
+ flex: 1 1 auto;
400
+ min-width: 0;
401
+ height: 2.25rem;
402
+ min-height: 2.25rem;
403
+ padding: 0;
404
+ border: 0;
405
+ border-radius: 0;
406
+ background-color: transparent;
407
+ box-shadow: none;
408
+ color: var(--bs-body-color);
409
+ font-size: 0.875rem;
410
+ line-height: 1.25rem;
411
+ }
412
+
413
+ .datatable-filter-option-search::placeholder,
414
+ .datatable-facet-search::placeholder {
415
+ color: var(--bs-secondary-color);
416
+ }
417
+
418
+ .datatable-filter-option-search:focus,
419
+ .datatable-facet-search:focus {
420
+ border-color: transparent;
421
+ background-color: transparent;
422
+ box-shadow: none;
423
+ outline: 0;
424
+ }
425
+
426
+ .datatable-filter-option-search-hidden,
427
+ .datatable-facet-search-hidden {
428
+ display: none;
429
+ }
430
+
431
+ .datatable-toolbar-filters {
432
+ display: flex;
433
+ flex: 0 1 auto;
434
+ flex-wrap: wrap;
435
+ gap: 0.5rem;
436
+ align-items: center;
437
+ }
438
+
439
+ .datatable-layout-switch {
440
+ display: flex;
441
+ flex: 0 0 auto;
442
+ align-items: center;
443
+ justify-content: flex-end;
444
+ }
445
+
446
+ .datatable-view-option {
447
+ display: inline-flex;
448
+ align-items: center;
449
+ justify-content: center;
450
+ gap: 0.375rem;
451
+ min-block-size: var(--moo-datatable-toolbar-control-height);
452
+ min-width: 2rem;
453
+ padding-inline: 0.5rem;
454
+ }
455
+
456
+ .datatable .datatable-layout-switch .btn-group-sm > .btn.datatable-view-option:has(> [data-icon="inline-start"]) {
457
+ padding-inline: 0.5rem;
458
+ }
459
+
460
+ .datatable-view-option > [data-icon] {
461
+ width: 1rem;
462
+ height: 1rem;
463
+ }
464
+
465
+ .datatable-view-toggle .btn-check:checked + .datatable-view-option {
466
+ border-color: var(--moo-border);
467
+ color: var(--bs-body-color);
468
+ background-color: var(--moo-muted-surface);
469
+ }
470
+
471
+ .datatable-view-toggle .btn-check:focus-visible + .datatable-view-option {
472
+ z-index: 3;
473
+ border-color: var(--moo-ring);
474
+ box-shadow: 0 0 0 #{$moo-form-focus-ring-width} color-mix(in srgb, #{$moo-form-focus-ring-color} 50%, transparent);
475
+ }
476
+
477
+ .datatable-view-trigger {
478
+ min-block-size: var(--moo-datatable-toolbar-control-height);
479
+ }
480
+
481
+ .datatable .datatable-toolbar-actions .btn.btn-sm.datatable-view-trigger:has(> [data-icon="inline-start"]) {
482
+ padding-inline: $btn-padding-x-sm;
483
+ }
484
+
485
+ .datatable-select-all {
486
+ display: inline-flex;
487
+ align-items: center;
488
+ gap: 0.625rem;
489
+ margin: 0;
490
+ padding-inline-start: 0;
491
+ color: var(--bs-secondary-color);
492
+ white-space: nowrap;
493
+ }
494
+
495
+ .datatable-select-all .form-check-input {
496
+ float: none;
497
+ margin: 0;
498
+ }
499
+
500
+ .datatable-select-all-table {
501
+ transform: translateY(var(--moo-datatable-table-select-all-offset-y));
502
+ }
503
+
504
+ .datatable-checkbox-target {
505
+ position: relative;
506
+ display: grid;
507
+ width: 1rem;
508
+ height: 1rem;
509
+ min-height: 1rem;
510
+ padding-inline-start: 0;
511
+ margin: 0;
512
+ place-items: center;
513
+ cursor: pointer;
514
+ }
515
+
516
+ .datatable-checkbox-target::before {
517
+ position: absolute;
518
+ inset: -0.5625rem;
519
+ border-radius: var(--bs-border-radius);
520
+ content: "";
521
+ }
522
+
523
+ .datatable-checkbox-target .form-check-input {
524
+ position: relative;
525
+ z-index: 1;
526
+ float: none;
527
+ margin: 0;
528
+ }
529
+
530
+ @media (pointer: coarse) {
531
+ .datatable-checkbox-target::before {
532
+ inset: -0.8125rem;
533
+ }
534
+ }
535
+
536
+ .datatable-table > :not(caption) > * > .datatable-col-select {
537
+ width: 2.75rem;
538
+ padding-inline-start: var(--moo-datatable-select-cell-inset);
539
+ padding-inline-end: $table-cell-padding-x;
540
+ white-space: nowrap;
541
+ }
542
+
543
+ .datatable-col-hidden {
544
+ display: none;
545
+ }
546
+
547
+ .datatable-item-cell {
548
+ display: flex;
549
+ min-width: 0;
550
+ align-items: center;
551
+ gap: 0.5rem;
552
+ }
553
+
554
+ .datatable-item-cell > .text-truncate {
555
+ flex: 1 1 auto;
556
+ min-width: 0;
557
+ }
558
+
559
+ .datatable-item-cell > .badge {
560
+ flex: 0 0 auto;
561
+ }
562
+
563
+ .datatable-icon-label {
564
+ display: inline-flex;
565
+ align-items: center;
566
+ gap: $moo-datatable-icon-label-gap;
567
+ }
568
+
569
+ .datatable-sort-trigger {
570
+ margin-inline-start: calc(-1 * var(--bs-btn-padding-x, 0.5rem));
571
+ gap: 0.5rem;
572
+ }
573
+
574
+ .datatable-sort-icon {
575
+ display: inline-flex;
576
+ align-items: center;
577
+ justify-content: center;
578
+ }
579
+
580
+ .datatable-sort-icon-asc,
581
+ .datatable-sort-icon-desc {
582
+ display: none;
583
+ }
584
+
585
+ .datatable-sort-icon > [data-icon],
586
+ .datatable-icon-label > [data-icon] {
587
+ width: 1rem;
588
+ height: 1rem;
589
+ flex: 0 0 auto;
590
+ margin: 0;
591
+ }
592
+
593
+ [data-datatable-sort-direction="asc"] .datatable-sort-icon-none,
594
+ [data-datatable-sort-direction="desc"] .datatable-sort-icon-none {
595
+ display: none;
596
+ }
597
+
598
+ [data-datatable-sort-direction="asc"] .datatable-sort-icon-asc {
599
+ display: inline-flex;
600
+ }
601
+
602
+ [data-datatable-sort-direction="desc"] .datatable-sort-icon-desc {
603
+ display: inline-flex;
604
+ }
605
+
606
+ // A card view is a grid, not a stack: on md/lg/tablet-and-wider screens a
607
+ // single edge-to-edge card reads as an oversized table row, not a card.
608
+ // auto-fill + a minimum card width wraps into as many columns as the
609
+ // viewport allows and falls back to one column once it can't.
610
+ .datatable-cards {
611
+ --moo-datatable-card-min-width: #{$moo-datatable-card-min-width};
612
+
613
+ display: grid;
614
+ gap: 0.625rem;
615
+ grid-template-columns: repeat(auto-fill, minmax(var(--moo-datatable-card-min-width), 1fr));
616
+ padding: 0.625rem;
617
+ }
618
+
619
+ .datatable-card {
620
+ position: relative;
621
+ overflow: hidden;
622
+ border: var(--bs-border-width) solid var(--bs-border-color);
623
+ border-radius: var(--bs-border-radius);
624
+ background-color: var(--bs-body-bg);
625
+ }
626
+
627
+ .datatable-card-header {
628
+ display: grid;
629
+ grid-template-columns: auto minmax(0, 1fr) auto;
630
+ gap: 0.75rem;
631
+ align-items: center;
632
+ padding: 0.5rem 0.75rem;
633
+ border-bottom: var(--bs-border-width) solid var(--bs-border-color);
634
+ background-color: var(--bs-tertiary-bg);
635
+ }
636
+
637
+ .datatable-card-select {
638
+ display: inline-flex;
639
+ align-items: center;
640
+ justify-content: center;
641
+ }
642
+
643
+ .datatable-card-select .form-check-input {
644
+ margin: 0;
645
+ }
646
+
647
+ .datatable-card-heading {
648
+ min-width: 0;
649
+ }
650
+
651
+ .datatable-card-title {
652
+ overflow: hidden;
653
+ font-weight: 600;
654
+ text-overflow: ellipsis;
655
+ white-space: nowrap;
656
+ }
657
+
658
+ .datatable-card-code {
659
+ overflow: hidden;
660
+ color: var(--bs-secondary-color);
661
+ font-family: var(--bs-font-monospace);
662
+ font-size: var(--bs-body-font-size);
663
+ text-overflow: ellipsis;
664
+ white-space: nowrap;
665
+ }
666
+
667
+ .datatable-card-actions {
668
+ display: inline-flex;
669
+ align-items: center;
670
+ justify-content: flex-end;
671
+ }
672
+
673
+ .datatable-card-actions .btn {
674
+ min-width: 2rem;
675
+ min-height: 2rem;
676
+ padding: 0.25rem;
677
+ }
678
+
679
+ .datatable-card-list {
680
+ margin: 0;
681
+ }
682
+
683
+ .datatable-card-row {
684
+ display: grid;
685
+ grid-template-columns: minmax(var(--moo-datatable-card-label-width), 30%) minmax(0, 1fr);
686
+ gap: 0.75rem;
687
+ align-items: center;
688
+ padding: 0.375rem 0.875rem;
689
+ }
690
+
691
+ // .datatable-col-hidden is a single class (display: none) declared above
692
+ // .datatable-card-row's own display: grid; a tied, later-wins cascade let
693
+ // the row's grid display beat the hide, so the View dropdown's column
694
+ // toggles never reached the card list. Two classes outrank one regardless
695
+ // of source order.
696
+ .datatable-card-row.datatable-col-hidden {
697
+ display: none;
698
+ }
699
+
700
+ .datatable-card-row:first-child {
701
+ padding-top: 0.875rem;
702
+ }
703
+
704
+ .datatable-card-row:last-child {
705
+ padding-bottom: 0.875rem;
706
+ }
707
+
708
+ .datatable-card-row dt,
709
+ .datatable-card-row dd {
710
+ min-width: 0;
711
+ margin: 0;
712
+ }
713
+
714
+ .datatable-card-row dt {
715
+ font-weight: var(--bs-body-font-weight);
716
+ color: var(--bs-secondary-color);
717
+ }
718
+
719
+ .datatable-card-value {
720
+ display: flex;
721
+ min-width: 0;
722
+ justify-content: flex-start;
723
+ text-align: start;
724
+ }
725
+
726
+ .datatable-card-value .datatable-item-cell {
727
+ justify-content: flex-start;
728
+ }
729
+
730
+ .datatable-card-value .datatable-item-cell > .text-truncate {
731
+ flex: 0 1 auto;
732
+ }
733
+
734
+ .datatable-row-selected > td {
735
+ background-color: var(--moo-muted-surface);
736
+ }
737
+
738
+ .datatable-card.datatable-row-selected {
739
+ border-color: var(--moo-border);
740
+ background-color: var(--moo-muted-surface);
741
+ }
742
+
743
+ .datatable-empty {
744
+ padding: 2rem 1rem;
745
+ border-width: var(--bs-border-width);
746
+ border-style: dashed;
747
+ border-color: var(--bs-border-color);
748
+ border-radius: var(--bs-border-radius);
749
+ text-align: center;
750
+ }
751
+
752
+ .datatable-empty-title {
753
+ font-weight: var(--bs-font-weight-semibold, 600);
754
+ }
755
+
756
+ .datatable-pagination {
757
+ display: grid;
758
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
759
+ align-items: center;
760
+ gap: 0.75rem 1rem;
761
+ }
762
+
763
+ .datatable-page-size {
764
+ justify-self: start;
765
+ }
766
+
767
+ .datatable-page-size-trigger {
768
+ min-width: $moo-datatable-page-size-trigger-min-width;
769
+ justify-content: space-between;
770
+ }
771
+
772
+ .datatable-page-size-menu {
773
+ min-width: $moo-datatable-page-size-menu-min-width;
774
+ }
775
+
776
+ .datatable-results-summary {
777
+ justify-self: center;
778
+ text-align: center;
779
+ white-space: nowrap;
780
+ }
781
+
782
+ .datatable-page-nav {
783
+ max-width: 100%;
784
+ justify-self: end;
785
+ overflow-x: auto;
786
+ overflow-y: hidden;
787
+ -webkit-overflow-scrolling: touch;
788
+ }
789
+
790
+ .datatable-page-nav .pagination {
791
+ flex-wrap: nowrap;
792
+ justify-content: flex-end;
793
+ min-width: max-content;
794
+ }
795
+
796
+ .datatable-card-row--selection .form-check-input {
797
+ margin: 0;
798
+ }
799
+
800
+ // .datatable-facet-item__indicator is Data Table's own facet-dropdown item
801
+ // (datatable_facet_item(), not the shared dropdown_toggle_item() macro), so
802
+ // it can't inherit .dropdown-item-check__indicator's shared rule (scss/
803
+ // components/_dropdown.scss) directly -- it restates the same
804
+ // $form-check-input-* tokens and checked treatment so the facet dropdown and
805
+ // the filter-picker (which does use the shared indicator) read identically.
806
+ .datatable-facet-item__indicator {
807
+ --moo-datatable-facet-item-border-radius: #{$moo-datatable-facet-item-border-radius};
808
+
809
+ display: inline-flex;
810
+ flex: 0 0 auto;
811
+ align-items: center;
812
+ justify-content: center;
813
+ width: $form-check-input-width;
814
+ height: $form-check-input-width;
815
+ border: var(--bs-border-width) solid var(--bs-border-color);
816
+ border-radius: var(--moo-datatable-facet-item-border-radius);
817
+ color: transparent;
818
+ }
819
+
820
+ .datatable-facet-item.active .datatable-facet-item__indicator {
821
+ background-color: var(--moo-foreground);
822
+ border-color: var(--moo-foreground);
823
+ color: var(--moo-surface);
824
+ }
825
+
826
+ .datatable-facet-item__indicator > [data-icon] {
827
+ width: 0.75rem;
828
+ height: 0.75rem;
829
+ }
830
+
831
+ // Bootstrap has no "inactive filter" button variant, so the faceted-filter
832
+ // trigger is unstyled `.btn.btn-sm` by default and gets its own dashed,
833
+ // muted-token treatment here. Selected filters keep the same calm outline
834
+ // while the inline summary carries the active value.
835
+ .datatable-facet-trigger {
836
+ --bs-btn-active-bg: var(--bs-body-bg);
837
+ --bs-btn-active-border-color: var(--bs-border-color);
838
+ --bs-btn-active-color: var(--bs-body-color);
839
+ --bs-btn-border-color: var(--bs-border-color);
840
+ --bs-btn-border-radius: var(--bs-border-radius);
841
+ --bs-btn-border-width: var(--bs-border-width);
842
+ --bs-btn-font-size: #{$moo-datatable-facet-trigger-font-size};
843
+ --bs-btn-hover-bg: var(--moo-muted-surface);
844
+ --bs-btn-hover-border-color: var(--bs-border-color);
845
+ --bs-btn-hover-color: var(--bs-body-color);
846
+ --bs-btn-line-height: 1.25rem;
847
+ --bs-btn-padding-x: #{$spacer * 0.625};
848
+ --bs-btn-padding-y: 0;
849
+
850
+ display: inline-flex;
851
+ align-items: center;
852
+ gap: 0.375rem;
853
+ min-height: $moo-datatable-facet-trigger-min-height;
854
+ border-style: dashed;
855
+ color: var(--bs-secondary-color);
856
+ }
857
+
858
+ .datatable-facet-trigger:hover,
859
+ .datatable-facet-trigger:focus,
860
+ .datatable-facet-trigger:focus-visible,
861
+ .datatable-facet-trigger:active,
862
+ .datatable-facet-trigger.show {
863
+ border-color: var(--bs-border-color);
864
+ color: var(--bs-body-color);
865
+ }
866
+
867
+ .btn.datatable-facet-trigger:focus:not(:focus-visible) {
868
+ outline: 0;
869
+ box-shadow: none;
870
+ }
871
+
872
+ .btn.datatable-facet-trigger:focus-visible,
873
+ .btn.datatable-facet-trigger.show:focus-visible,
874
+ .btn.datatable-facet-trigger-active:focus-visible {
875
+ box-shadow: none;
876
+ }
877
+
878
+ .datatable-facet-trigger-active {
879
+ border-style: dashed;
880
+ border-color: var(--bs-border-color);
881
+ color: var(--bs-body-color);
882
+ }
883
+
884
+ .datatable-facet-summary {
885
+ display: inline-flex;
886
+ align-items: center;
887
+ gap: 0.25rem;
888
+ padding-inline-start: 0.5rem;
889
+ margin-inline-start: 0.5rem;
890
+ border-inline-start: var(--bs-border-width) solid var(--bs-border-color);
891
+ }
892
+
893
+ .datatable-facet-badge {
894
+ display: inline-flex;
895
+ align-items: center;
896
+ justify-content: center;
897
+ min-height: $spacer * 1.5;
898
+ padding-block: 0;
899
+ padding-inline: 0.5rem;
900
+ border: 0;
901
+ border-radius: var(--bs-border-radius-sm);
902
+ background-color: var(--moo-muted-surface);
903
+ color: var(--bs-body-color);
904
+ font-size: $font-size-base * 0.857;
905
+ font-weight: var(--bs-body-font-weight);
906
+ line-height: $spacer;
907
+ white-space: nowrap;
908
+ }
909
+
910
+ // Bootstrap has no floating/overlay toolbar primitive, so the selection
911
+ // bulk-actions bar (shown only once at least one row is selected) is a
912
+ // Moo-owned fixed-position surface. It reuses Bootstrap's own shadow,
913
+ // border, and radius tokens; only the fixed positioning, translucency, and
914
+ // backdrop blur have no existing Bootstrap utility to consume.
915
+ .datatable-bulk-actions {
916
+ --moo-datatable-bulk-actions-hover-scale: #{$moo-datatable-bulk-actions-hover-scale};
917
+ --moo-datatable-bulk-actions-backdrop-blur: #{$moo-datatable-bulk-actions-backdrop-blur};
918
+
919
+ position: fixed;
920
+ inset-inline-start: 50%;
921
+ inset-block-end: var(--moo-datatable-bulk-actions-bottom);
922
+ transform: translateX(-50%) scale(1);
923
+ @include transition(transform 0.15s ease-out);
924
+ z-index: var(--bs-zindex-fixed, 1030);
925
+ display: flex;
926
+ align-items: center;
927
+ gap: 0.5rem;
928
+ padding: 0.5rem;
929
+ border: var(--bs-border-width) solid var(--bs-border-color);
930
+ border-radius: var(--bs-border-radius-xl);
931
+ background-color: var(--bs-body-bg);
932
+ background-color: color-mix(in srgb, var(--bs-body-bg) 95%, transparent);
933
+ backdrop-filter: blur(var(--moo-datatable-bulk-actions-backdrop-blur));
934
+ box-shadow: var(--bs-box-shadow-lg);
935
+ }
936
+
937
+ .datatable-bulk-actions:hover,
938
+ .datatable-bulk-actions:focus-within {
939
+ transform: translateX(-50%) scale(var(--moo-datatable-bulk-actions-hover-scale));
940
+ }
941
+
942
+ .datatable-bulk-summary {
943
+ display: inline-flex;
944
+ align-items: center;
945
+ gap: 0.5rem;
946
+ white-space: nowrap;
947
+ }
948
+
949
+ // Catalog preview chrome: caps the full-bleed release-review preview pages.
950
+ // A Sass knob keeps the geometry on the ordered settings aggregate instead
951
+ // of a component-local literal.
952
+ .datatable-preview-shell {
953
+ max-width: $moo-datatable-preview-shell-max-width;
954
+ }
955
+
956
+ @media (max-width: 991.98px) {
957
+ .datatable-pagination {
958
+ grid-template-columns: minmax(0, 1fr) auto;
959
+ }
960
+
961
+ .datatable-results-summary {
962
+ justify-self: end;
963
+ text-align: end;
964
+ }
965
+
966
+ .datatable-page-nav {
967
+ grid-column: 1 / -1;
968
+ justify-self: start;
969
+ margin-block-start: 0.25rem;
970
+ }
971
+
972
+ .datatable-page-nav .pagination {
973
+ justify-content: flex-start;
974
+ }
975
+ }
976
+
977
+ @media (max-width: 575.98px) {
978
+ .datatable-toolbar {
979
+ display: grid;
980
+ grid-template-columns: minmax(0, 1fr) auto;
981
+ align-items: start;
982
+ }
983
+
984
+ .datatable-search-filter {
985
+ grid-column: 1 / -1;
986
+ min-width: 0;
987
+ }
988
+
989
+ .datatable-toolbar-query {
990
+ grid-column: 1 / -1;
991
+ min-width: 0;
992
+ }
993
+
994
+ .datatable-toolbar-query .datatable-search-filter {
995
+ flex: 1 1 auto;
996
+ }
997
+
998
+ .datatable-searchbar .datatable-search {
999
+ max-width: none;
1000
+ }
1001
+
1002
+ .datatable-search-filter-menu {
1003
+ max-width: calc(100vw - 2rem);
1004
+ }
1005
+
1006
+ .datatable-search-filter-menu .dropdown-header {
1007
+ padding-block-end: 0.25rem;
1008
+ font-size: $font-size-sm;
1009
+ }
1010
+
1011
+ .datatable-search-filter-menu .dropdown-item {
1012
+ min-height: 1.875rem;
1013
+ padding-block: 0.3125rem;
1014
+ font-size: $font-size-sm;
1015
+ }
1016
+
1017
+ .datatable-toolbar-filters {
1018
+ display: flex;
1019
+ grid-column: 1 / -1;
1020
+ flex-direction: column;
1021
+ align-items: flex-start;
1022
+ min-width: 0;
1023
+ }
1024
+
1025
+ .datatable-layout-switch {
1026
+ grid-column: 2;
1027
+ justify-self: end;
1028
+ }
1029
+
1030
+ .datatable-select-all {
1031
+ grid-column: 1;
1032
+ }
1033
+
1034
+ .datatable-toolbar-actions {
1035
+ grid-column: 1 / -1;
1036
+ margin-inline-start: 0;
1037
+ justify-self: end;
1038
+ }
1039
+
1040
+ .datatable-toolbar-actions .datatable-view-trigger {
1041
+ display: inline-flex;
1042
+ align-items: center;
1043
+ justify-content: center;
1044
+ gap: 0.375rem;
1045
+ flex: 0 0 auto;
1046
+ min-width: 0;
1047
+ min-height: var(--moo-datatable-toolbar-control-height);
1048
+ inline-size: auto;
1049
+ block-size: var(--moo-datatable-toolbar-control-height);
1050
+ padding-inline: $btn-padding-x-sm;
1051
+ }
1052
+
1053
+ .datatable-toolbar-actions .btn.btn-sm.datatable-view-trigger:has(> [data-icon="inline-start"]) {
1054
+ padding-inline: $btn-padding-x-sm;
1055
+ }
1056
+
1057
+ .datatable-toolbar-actions .datatable-view-trigger > [data-icon] {
1058
+ width: 0.875rem;
1059
+ height: 0.875rem;
1060
+ }
1061
+
1062
+ .datatable-view-trigger-label {
1063
+ display: inline;
1064
+ }
1065
+
1066
+ .datatable-pagination {
1067
+ grid-template-columns: minmax(0, 1fr) auto;
1068
+ }
1069
+
1070
+ .datatable-page-nav {
1071
+ grid-column: 1 / -1;
1072
+ justify-self: start;
1073
+ }
1074
+
1075
+ .datatable-page-nav .pagination {
1076
+ justify-content: flex-start;
1077
+ }
1078
+ }
1079
+
1080
+ .datatable--release-review {
1081
+ --moo-datatable-min-width: #{$moo-datatable-release-review-min-width};
1082
+ }