@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.
- package/ASSET_LICENSE.md +3 -1
- package/README.md +50 -107
- package/certification.json +20 -3
- package/dist/assets/css/moo-ui.css +8254 -7629
- package/dist/assets/css/moo-ui.min.css +3 -3
- package/dist/assets/css/moo.css +595 -71
- package/dist/assets/css/moo.min.css +3 -3
- package/dist/js/chart.js +15258 -0
- package/dist/js/chart.min.js +27 -0
- package/dist/js/combobox.js +98 -40
- package/dist/js/context-menu.js +5 -0
- package/dist/js/datatable.js +5 -0
- package/dist/js/datepicker.js +1154 -0
- package/dist/js/datepicker.min.js +6 -0
- package/dist/js/moo-ui.js +18950 -0
- package/dist/js/moo-ui.min.js +27 -0
- package/dist/js/sidebar.js +67 -25
- package/dist/js/slider.js +324 -0
- package/package.json +24 -4
- package/scss/_components.scss +73 -0
- package/scss/_config.scss +24 -0
- package/scss/_settings.scss +8 -0
- package/scss/components/_accordion.scss +59 -0
- package/scss/components/_alert.scss +94 -0
- package/scss/components/_avatar.scss +154 -0
- package/scss/components/_badge.scss +33 -0
- package/scss/components/_breadcrumb.scss +130 -0
- package/scss/components/_button.scss +228 -0
- package/scss/components/_button_group.scss +51 -0
- package/scss/components/_card.scss +142 -0
- package/scss/components/_checkbox.scss +115 -0
- package/scss/components/_close_button.scss +59 -0
- package/scss/components/_collapsible.scss +43 -0
- package/scss/components/_combobox.scss +258 -0
- package/scss/components/_context-menu.scss +28 -0
- package/scss/components/_datatable.scss +1082 -0
- package/scss/components/_datepicker.scss +236 -0
- package/scss/components/_dialog.scss +66 -0
- package/scss/components/_dropdown.scss +202 -0
- package/scss/components/_field.scss +125 -0
- package/scss/components/_input.scss +35 -0
- package/scss/components/_input_group.scss +201 -0
- package/scss/components/_kbd.scss +10 -0
- package/scss/components/_menubar.scss +16 -0
- package/scss/components/_navigation.scss +41 -0
- package/scss/components/_pagination.scss +70 -0
- package/scss/components/_popover.scss +47 -0
- package/scss/components/_progress.scss +7 -0
- package/scss/components/_radio_group.scss +9 -0
- package/scss/components/_select.scss +7 -0
- package/scss/components/_separator.scss +15 -0
- package/scss/components/_sheet.scss +32 -0
- package/scss/components/_sidebar.scss +7 -0
- package/scss/components/_skeleton.scss +17 -0
- package/scss/components/_slider.scss +214 -0
- package/scss/components/_spinner.scss +17 -0
- package/scss/components/_switch.scss +25 -0
- package/scss/components/_table.scss +7 -0
- package/scss/components/_tabs.scss +139 -0
- package/scss/components/_textarea.scss +7 -0
- package/scss/components/_toast.scss +119 -0
- package/scss/components/_toggle_group.scss +19 -0
- package/scss/components/_tooltip.scss +83 -0
- package/scss/components/sidebar/_collapsed.scss +187 -0
- package/scss/components/sidebar/_identity.scss +72 -0
- package/scss/components/sidebar/_inset.scss +59 -0
- package/scss/components/sidebar/_layout.scss +176 -0
- package/scss/components/sidebar/_menus.scss +236 -0
- package/scss/foundations/_core_global_primitives.scss +5 -0
- package/scss/foundations/_core_state_layer.scss +25 -0
- package/scss/foundations/_focus.scss +139 -0
- package/scss/foundations/_overlay_backdrop.scss +19 -0
- package/scss/moo-core.scss +47 -0
- package/scss/moo-ui.scss +50 -0
- package/scss/settings/_bootstrap_overrides.scss +11 -0
- package/scss/settings/_component_variables.scss +479 -0
- package/scss/settings/_forms.scss +65 -0
- package/scss/settings/_palette.scss +132 -0
- package/scss/themes/_scoped_core.scss +239 -0
- package/scss/themes/_standalone_root.scss +117 -0
- package/scss/utilities/_scroll_fade.scss +193 -0
- package/scss/utilities/_scroll_fade_primitives.scss +43 -0
- package/scss/_facade-settings.scss +0 -51
- package/scss/settings/_facade_public.scss +0 -23
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
.moo-datepicker {
|
|
2
|
+
--moo-datepicker-width: #{$moo-datepicker-width};
|
|
3
|
+
--moo-datepicker-popover-offset: #{$moo-datepicker-popover-offset};
|
|
4
|
+
|
|
5
|
+
position: relative;
|
|
6
|
+
width: 100%;
|
|
7
|
+
max-width: var(--moo-datepicker-width);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.moo-datepicker__trigger {
|
|
11
|
+
width: 100%;
|
|
12
|
+
justify-content: flex-start;
|
|
13
|
+
text-align: start;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.moo-datepicker__trigger [data-datepicker-label] {
|
|
17
|
+
min-width: 0;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
text-overflow: ellipsis;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.moo-datepicker__trigger.is-invalid {
|
|
23
|
+
border-color: var(--bs-form-invalid-border-color);
|
|
24
|
+
color: var(--bs-form-invalid-color);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.moo-datepicker-time-row {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
align-items: flex-end;
|
|
31
|
+
gap: $spacer * .75;
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.moo-datepicker-time-row > :first-child {
|
|
36
|
+
min-width: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.moo-datepicker-time {
|
|
40
|
+
flex: 0 0 auto;
|
|
41
|
+
width: calc(#{$moo-datepicker-width} / 2);
|
|
42
|
+
max-width: 9rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.moo-datepicker__popover {
|
|
46
|
+
position: absolute;
|
|
47
|
+
inset-block-start: calc(100% + var(--moo-datepicker-popover-offset));
|
|
48
|
+
inset-inline-start: 0;
|
|
49
|
+
z-index: $zindex-dropdown;
|
|
50
|
+
width: max-content;
|
|
51
|
+
max-width: calc(100vw - #{$spacer});
|
|
52
|
+
padding: $moo-datepicker-padding;
|
|
53
|
+
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
54
|
+
border-radius: var(--bs-border-radius-lg);
|
|
55
|
+
background: var(--bs-body-bg);
|
|
56
|
+
color: var(--bs-body-color);
|
|
57
|
+
box-shadow: var(--bs-box-shadow-lg);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.moo-datepicker__popover[hidden] {
|
|
61
|
+
display: none !important;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.moo-datepicker__popover.show {
|
|
65
|
+
display: block;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.moo-datepicker__popover[data-datepicker-placement] {
|
|
69
|
+
inset: auto;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.moo-calendar {
|
|
73
|
+
--moo-datepicker-day-size: #{$moo-datepicker-day-size};
|
|
74
|
+
--moo-datepicker-gap: #{$moo-datepicker-gap};
|
|
75
|
+
--moo-datepicker-padding: #{$moo-datepicker-padding};
|
|
76
|
+
--moo-datepicker-preset-gap: #{$moo-datepicker-preset-gap};
|
|
77
|
+
--moo-datepicker-day-border-radius: #{$moo-datepicker-day-border-radius};
|
|
78
|
+
--moo-datepicker-day-range-joined-border-radius: #{$moo-datepicker-day-range-joined-border-radius};
|
|
79
|
+
|
|
80
|
+
width: calc(var(--moo-datepicker-day-size) * 7 + var(--moo-datepicker-gap) * 6);
|
|
81
|
+
max-width: 100%;
|
|
82
|
+
color: var(--bs-body-color);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.moo-calendar__header {
|
|
86
|
+
display: grid;
|
|
87
|
+
grid-template-columns: var(--moo-datepicker-day-size) minmax(0, 1fr) var(--moo-datepicker-day-size);
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: var(--moo-datepicker-gap);
|
|
90
|
+
margin-bottom: $spacer * 0.75;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.moo-calendar__caption {
|
|
94
|
+
display: inline-flex;
|
|
95
|
+
min-width: 0;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
gap: var(--moo-datepicker-gap);
|
|
99
|
+
font-weight: $font-weight-medium;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.moo-calendar__select {
|
|
103
|
+
min-width: 0;
|
|
104
|
+
height: $input-height-sm;
|
|
105
|
+
padding: 0 $spacer * 0.375;
|
|
106
|
+
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
107
|
+
border-radius: var(--bs-border-radius-sm);
|
|
108
|
+
background: var(--bs-body-bg);
|
|
109
|
+
color: var(--bs-body-color);
|
|
110
|
+
font: inherit;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.moo-calendar__nav {
|
|
114
|
+
color: var(--bs-secondary-color);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.moo-calendar__nav:hover,
|
|
118
|
+
.moo-calendar__nav:focus-visible {
|
|
119
|
+
color: var(--bs-body-color);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.moo-calendar__nav-icon {
|
|
123
|
+
font-size: $font-size-lg;
|
|
124
|
+
line-height: 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.moo-calendar__weekdays,
|
|
128
|
+
.moo-calendar__week {
|
|
129
|
+
display: grid;
|
|
130
|
+
grid-template-columns: repeat(7, var(--moo-datepicker-day-size));
|
|
131
|
+
gap: var(--moo-datepicker-gap);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.moo-calendar__week + .moo-calendar__week {
|
|
135
|
+
margin-top: var(--moo-datepicker-gap);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.moo-calendar__weekdays {
|
|
139
|
+
margin-bottom: var(--moo-datepicker-gap);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.moo-calendar__weekday {
|
|
143
|
+
display: inline-flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
justify-content: center;
|
|
146
|
+
width: var(--moo-datepicker-day-size);
|
|
147
|
+
height: $spacer * 1.5;
|
|
148
|
+
color: var(--bs-secondary-color);
|
|
149
|
+
font-size: $font-size-sm;
|
|
150
|
+
font-weight: $font-weight-medium;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.moo-calendar__cell {
|
|
154
|
+
position: relative;
|
|
155
|
+
width: var(--moo-datepicker-day-size);
|
|
156
|
+
height: var(--moo-datepicker-day-size);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.moo-calendar__day {
|
|
160
|
+
position: relative;
|
|
161
|
+
display: inline-flex;
|
|
162
|
+
width: var(--moo-datepicker-day-size);
|
|
163
|
+
height: var(--moo-datepicker-day-size);
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: center;
|
|
166
|
+
padding: 0;
|
|
167
|
+
border: 0;
|
|
168
|
+
border-radius: var(--moo-datepicker-day-border-radius);
|
|
169
|
+
background: transparent;
|
|
170
|
+
color: var(--bs-body-color);
|
|
171
|
+
font: inherit;
|
|
172
|
+
line-height: 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.moo-calendar__day:hover:not(:disabled),
|
|
176
|
+
.moo-calendar__day:focus-visible {
|
|
177
|
+
background: var(--moo-muted-surface);
|
|
178
|
+
outline: 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.moo-calendar__day:focus-visible {
|
|
182
|
+
box-shadow: $moo-datepicker-day-focus-ring-shadow;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.moo-calendar__day[data-calendar-selected="true"] {
|
|
186
|
+
background: var(--moo-primary);
|
|
187
|
+
color: var(--moo-primary-foreground);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.moo-calendar__day[data-calendar-selected="true"]:hover:not(:disabled),
|
|
191
|
+
.moo-calendar__day[data-calendar-selected="true"]:focus-visible {
|
|
192
|
+
background: var(--moo-primary);
|
|
193
|
+
color: var(--moo-primary-foreground);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.moo-calendar__day[data-calendar-range="middle"] {
|
|
197
|
+
border-radius: var(--moo-datepicker-day-range-joined-border-radius);
|
|
198
|
+
background: color-mix(in srgb, var(--moo-primary) 14%, transparent);
|
|
199
|
+
color: var(--bs-body-color);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.moo-calendar__day[data-calendar-range="start"] {
|
|
203
|
+
border-start-end-radius: var(--moo-datepicker-day-range-joined-border-radius);
|
|
204
|
+
border-end-end-radius: var(--moo-datepicker-day-range-joined-border-radius);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.moo-calendar__day[data-calendar-range="end"] {
|
|
208
|
+
border-start-start-radius: var(--moo-datepicker-day-range-joined-border-radius);
|
|
209
|
+
border-end-start-radius: var(--moo-datepicker-day-range-joined-border-radius);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.moo-calendar__day[data-calendar-outside="true"] {
|
|
213
|
+
color: var(--bs-secondary-color);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.moo-calendar__day[data-calendar-today="true"]:not([data-calendar-selected="true"]) {
|
|
217
|
+
outline: var(--bs-border-width) solid var(--bs-border-color);
|
|
218
|
+
outline-offset: calc(-1 * var(--bs-border-width));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.moo-calendar__day:disabled {
|
|
222
|
+
color: var(--moo-disabled-foreground);
|
|
223
|
+
cursor: default;
|
|
224
|
+
opacity: var(--moo-disabled-control-opacity);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.moo-calendar__presets {
|
|
228
|
+
display: flex;
|
|
229
|
+
flex-wrap: wrap;
|
|
230
|
+
gap: var(--moo-datepicker-preset-gap);
|
|
231
|
+
margin-top: $spacer * 0.75;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.moo-calendar__preset {
|
|
235
|
+
flex: 0 0 auto;
|
|
236
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Dialog is the Moo catalog name for Bootstrap's Modal component, so this
|
|
2
|
+
// partial retunes Bootstrap's own --bs-modal-* custom properties toward the
|
|
3
|
+
// design reference: a larger radius, a quieter border tone, a borderless
|
|
4
|
+
// header, one shared surface, and a lighter-weight tightly-set title, instead
|
|
5
|
+
// of Bootstrap's own sharper-cornered, divided, heavier-titled look.
|
|
6
|
+
.modal {
|
|
7
|
+
--bs-modal-border-radius: var(--bs-border-radius-xl);
|
|
8
|
+
--bs-modal-border-color: var(--moo-border);
|
|
9
|
+
--bs-modal-header-border-width: 0;
|
|
10
|
+
--bs-modal-footer-border-color: var(--moo-border);
|
|
11
|
+
--bs-modal-footer-bg: color-mix(in srgb, var(--bs-secondary-bg) 50%, transparent);
|
|
12
|
+
--bs-modal-title-line-height: 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.modal[tabindex]:focus-visible {
|
|
16
|
+
// Bootstrap focuses the modal root when it opens. The shared focus rule
|
|
17
|
+
// intentionally covers generic tabindex controls, so reset that container
|
|
18
|
+
// ring here without weakening focus treatment for actual controls.
|
|
19
|
+
outline: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.modal-content {
|
|
23
|
+
box-shadow: var(--bs-box-shadow-lg);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.modal-title {
|
|
27
|
+
// A literal size, not $font-size-base: this project's own base is 14px,
|
|
28
|
+
// but the reference dialog title sits at a plain, un-scaled 16px regardless.
|
|
29
|
+
font-size: 1rem;
|
|
30
|
+
font-weight: $font-weight-medium;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.modal-header {
|
|
34
|
+
gap: calc(var(--bs-modal-padding) * 0.75);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.modal--alert {
|
|
38
|
+
--bs-modal-width: 24rem;
|
|
39
|
+
--bs-modal-padding: 1rem;
|
|
40
|
+
--bs-modal-footer-bg: color-mix(in srgb, var(--bs-secondary-bg) 50%, transparent);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.modal-header--alert {
|
|
44
|
+
align-items: flex-start;
|
|
45
|
+
padding: var(--bs-modal-padding);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.modal-header__content {
|
|
49
|
+
min-width: 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.modal-description {
|
|
53
|
+
margin-top: calc(var(--bs-modal-padding) * 0.375);
|
|
54
|
+
margin-bottom: 0;
|
|
55
|
+
color: var(--bs-secondary-color);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.modal--alert .modal-footer {
|
|
59
|
+
padding: var(--bs-modal-padding);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.modal-footer {
|
|
63
|
+
// Bootstrap derives this from --bs-modal-padding minus half the button
|
|
64
|
+
// gap (12px here); the reference footer keeps the full 16px card padding.
|
|
65
|
+
padding: $spacer;
|
|
66
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Dropdown uses Bootstrap's own consumption variables. These aliases give
|
|
2
|
+
// the native menu the reference's surface without a private Moo token layer.
|
|
3
|
+
.dropdown-menu {
|
|
4
|
+
--bs-dropdown-bg: var(--moo-surface);
|
|
5
|
+
--bs-dropdown-border-color: var(--moo-border);
|
|
6
|
+
--bs-dropdown-border-radius: var(--bs-border-radius-lg);
|
|
7
|
+
--bs-dropdown-box-shadow: var(--bs-box-shadow);
|
|
8
|
+
--bs-dropdown-link-color: var(--moo-foreground);
|
|
9
|
+
--bs-dropdown-link-hover-color: var(--moo-foreground);
|
|
10
|
+
--bs-dropdown-link-hover-bg: var(--moo-muted-surface);
|
|
11
|
+
--bs-dropdown-link-active-color: var(--moo-foreground);
|
|
12
|
+
--bs-dropdown-link-active-bg: var(--moo-muted-surface);
|
|
13
|
+
--bs-dropdown-link-disabled-color: var(--moo-disabled-foreground);
|
|
14
|
+
--bs-dropdown-header-color: var(--moo-muted-foreground);
|
|
15
|
+
|
|
16
|
+
// Bootstrap gates dropdown shadow behind $enable-shadows, so apply the
|
|
17
|
+
// shared elevation token directly after bridging it to --bs-dropdown-box-shadow.
|
|
18
|
+
box-shadow: var(--bs-dropdown-box-shadow);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.dropdown-item {
|
|
22
|
+
--bs-dropdown-item-border-radius: var(--bs-border-radius);
|
|
23
|
+
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: $spacer * 0.5;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Bootstrap's own .dropdown-item bundles :hover and :focus into the same
|
|
30
|
+
// highlight (vendor/bootstrap/scss/_dropdown.scss), so an item that still
|
|
31
|
+
// holds DOM focus from a prior mouse click (clicking a button leaves it
|
|
32
|
+
// focused) keeps its highlight after the mouse moves to hover a different
|
|
33
|
+
// item -- two rows lit up at once. Resetting the stale, non-keyboard focus
|
|
34
|
+
// back to resting style (only when the item isn't also genuinely hovered,
|
|
35
|
+
// so real hover still wins) leaves exactly one item highlighted at a time;
|
|
36
|
+
// real keyboard :focus-visible navigation is untouched, since it's a subset
|
|
37
|
+
// of :focus and this selector explicitly excludes it.
|
|
38
|
+
.dropdown-item:focus:not(:focus-visible):not(:hover) {
|
|
39
|
+
color: var(--bs-dropdown-link-color);
|
|
40
|
+
background-color: transparent;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.dropdown-item > [data-icon] {
|
|
44
|
+
width: 1rem;
|
|
45
|
+
height: 1rem;
|
|
46
|
+
flex: 0 0 auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Destructive dropdown items use the shared --moo-destructive token so the
|
|
50
|
+
// red stays in sync with every other destructive control. Matches the
|
|
51
|
+
// reference pattern: red text at rest, destructive-tinted bg on hover.
|
|
52
|
+
.dropdown-item[data-variant="destructive"] {
|
|
53
|
+
color: var(--moo-destructive) !important;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dropdown-item[data-variant="destructive"]:hover,
|
|
57
|
+
.dropdown-item[data-variant="destructive"]:focus {
|
|
58
|
+
color: var(--moo-destructive) !important;
|
|
59
|
+
background-color: var(--moo-destructive-surface) !important;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dropdown-item[data-variant="destructive"] > [data-icon] {
|
|
63
|
+
color: var(--moo-destructive) !important;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.dropdown-menu .dropdown-divider {
|
|
67
|
+
margin-inline: calc(-1 * var(--bs-dropdown-padding-x));
|
|
68
|
+
border-block-start: var(--bs-border-width) solid var(--bs-dropdown-divider-bg);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.dropdown-item-check.active {
|
|
72
|
+
color: var(--bs-dropdown-link-color);
|
|
73
|
+
background-color: transparent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dropdown-item-check.active:hover,
|
|
77
|
+
.dropdown-item-check.active:focus {
|
|
78
|
+
color: var(--bs-dropdown-link-hover-color);
|
|
79
|
+
background-color: var(--bs-dropdown-link-hover-bg);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// A checkbox-style menu item can't nest a real <input type="checkbox"> inside
|
|
83
|
+
// its <button> (invalid, inaccessible markup), so this indicator stands in
|
|
84
|
+
// for one. Sized off Bootstrap's own $form-check-input-* tokens and always
|
|
85
|
+
// showing its box (not opacity-toggled to nothing) so it reads as the same
|
|
86
|
+
// checkable control as Checkbox itself, wherever a menu uses it -- Menubar,
|
|
87
|
+
// Context Menu, and Data Table's column-visibility, page-size, and faceted
|
|
88
|
+
// filter menus all compose this one indicator rather than each inventing
|
|
89
|
+
// their own checkbox-adjacent look.
|
|
90
|
+
.dropdown-item-check__indicator {
|
|
91
|
+
--moo-dropdown-item-check-border-radius: #{$moo-dropdown-item-check-border-radius};
|
|
92
|
+
|
|
93
|
+
display: inline-flex;
|
|
94
|
+
flex: 0 0 auto;
|
|
95
|
+
align-items: center;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
width: $form-check-input-width;
|
|
98
|
+
height: $form-check-input-width;
|
|
99
|
+
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
100
|
+
border-radius: var(--moo-dropdown-item-check-border-radius);
|
|
101
|
+
color: transparent;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Matches Checkbox's own checked fill (scss/components/_checkbox.scss:
|
|
105
|
+
// $form-check-input-checked-bg-color, resolved to --moo-foreground) so a
|
|
106
|
+
// checked menu item and a checked Checkbox read as the same control.
|
|
107
|
+
.dropdown-item-check.active .dropdown-item-check__indicator {
|
|
108
|
+
background-color: var(--moo-foreground);
|
|
109
|
+
border-color: var(--moo-foreground);
|
|
110
|
+
color: var(--moo-surface);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.dropdown-item-check__indicator > [data-icon] {
|
|
114
|
+
width: 0.75rem;
|
|
115
|
+
height: 0.75rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Single-choice ("radio") menu items reuse the same indicator markup as the
|
|
119
|
+
// checkbox variant above but read as a plain checkmark-on-select instead: no
|
|
120
|
+
// boxed square, since a box implies each item has its own independent on/off
|
|
121
|
+
// state, which is wrong when only one item in the group can ever be active
|
|
122
|
+
// (e.g. Data Table's page-size menu). The icon stays hidden until the item
|
|
123
|
+
// is active, matching how the reference and native menus mark single-choice items.
|
|
124
|
+
.dropdown-item-check--radio .dropdown-item-check__indicator {
|
|
125
|
+
border-color: transparent;
|
|
126
|
+
background-color: transparent;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.dropdown-item-check--radio.active .dropdown-item-check__indicator {
|
|
130
|
+
background-color: transparent;
|
|
131
|
+
border-color: transparent;
|
|
132
|
+
color: var(--bs-dropdown-link-color);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.dropdown-item__icon-box {
|
|
136
|
+
--moo-dropdown-item-icon-size: #{$moo-dropdown-item-icon-size};
|
|
137
|
+
|
|
138
|
+
display: flex;
|
|
139
|
+
flex: 0 0 auto;
|
|
140
|
+
align-items: center;
|
|
141
|
+
justify-content: center;
|
|
142
|
+
width: var(--moo-dropdown-item-icon-size);
|
|
143
|
+
height: var(--moo-dropdown-item-icon-size);
|
|
144
|
+
border: var(--bs-border-width) solid var(--moo-border);
|
|
145
|
+
border-radius: var(--bs-border-radius);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.dropdown-item__icon-box > [data-icon] {
|
|
149
|
+
width: 0.875rem;
|
|
150
|
+
height: 0.875rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// The Sidebar header/footer triggers (team switcher, account menu) span
|
|
154
|
+
// the sidebar's full width, and the design reference matches its menu
|
|
155
|
+
// width to that trigger so it extends past the sidebar edge into the content
|
|
156
|
+
// column. Bootstrap's own dropdown-menu only sizes to its own content, so
|
|
157
|
+
// match the trigger via the shared positioned dropdown ancestor's width
|
|
158
|
+
// instead. Scoped by data-slot attribute, not a sidebar class selector, so
|
|
159
|
+
// this stays inside the dropdown component's own selector ownership.
|
|
160
|
+
[data-slot="sidebar-header"] .dropdown-menu,
|
|
161
|
+
[data-slot="sidebar-footer"] .dropdown-menu {
|
|
162
|
+
--moo-dropdown-sidebar-min-width: #{$moo-dropdown-sidebar-min-width};
|
|
163
|
+
|
|
164
|
+
// unquote(): libsass resolves max() as its own Sass function and rejects
|
|
165
|
+
// var() as a non-numeric argument, so escape it through to plain CSS
|
|
166
|
+
// (same technique already used for clamp() in the catalog stylesheet).
|
|
167
|
+
min-width: #{unquote("max(100%, var(--moo-dropdown-sidebar-min-width))")};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@include media-breakpoint-down(sm) {
|
|
171
|
+
[data-slot="sidebar-header"] .dropend > .dropdown-menu,
|
|
172
|
+
[data-slot="sidebar-footer"] .dropend > .dropdown-menu {
|
|
173
|
+
left: 0 !important;
|
|
174
|
+
right: 0 !important;
|
|
175
|
+
min-width: 100%;
|
|
176
|
+
margin: 0;
|
|
177
|
+
transform: none !important;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
[data-slot="sidebar-header"] .dropend > .dropdown-menu {
|
|
181
|
+
top: calc(100% + #{$spacer * 0.25}) !important;
|
|
182
|
+
bottom: auto !important;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[data-slot="sidebar-footer"] .dropend > .dropdown-menu {
|
|
186
|
+
top: auto !important;
|
|
187
|
+
bottom: calc(100% + #{$spacer * 0.25}) !important;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@include media-breakpoint-up(lg) {
|
|
192
|
+
[data-slot="sidebar-header"] [data-sidebar-dropdown-positioned] > .dropdown-menu {
|
|
193
|
+
position: fixed !important;
|
|
194
|
+
z-index: $zindex-dropdown;
|
|
195
|
+
inset-block-start: var(--moo-sidebar-dropdown-block-start) !important;
|
|
196
|
+
inset-inline-start: var(--moo-sidebar-dropdown-inline-start) !important;
|
|
197
|
+
width: var(--moo-dropdown-sidebar-min-width);
|
|
198
|
+
min-width: var(--moo-dropdown-sidebar-min-width);
|
|
199
|
+
margin: 0;
|
|
200
|
+
transform: none !important;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
.field {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: $moo-field-gap;
|
|
5
|
+
|
|
6
|
+
// Bootstrap's own .form-label carries a margin-bottom of its own
|
|
7
|
+
// (form-label-margin-bottom), which would stack on top of this flex gap
|
|
8
|
+
// and roughly double the real spacing the design reference's flat
|
|
9
|
+
// gap-only model produces.
|
|
10
|
+
> .form-label {
|
|
11
|
+
margin-bottom: 0;
|
|
12
|
+
// $form-label-font-weight is unset (inherits normal body weight); the
|
|
13
|
+
// reference's own field label sits at a medium weight instead.
|
|
14
|
+
font-weight: $font-weight-medium;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Bootstrap owns the control and .invalid-feedback state. Field mirrors
|
|
18
|
+
// the reference's wrapper-level invalid state with data-invalid, so labels
|
|
19
|
+
// inherit the destructive color only when the field itself opts into that state.
|
|
20
|
+
&[data-invalid="true"] {
|
|
21
|
+
color: var(--bs-form-invalid-color);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.field--switch {
|
|
26
|
+
flex-direction: row;
|
|
27
|
+
align-items: flex-start;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
gap: $moo-field-switch-gap;
|
|
30
|
+
|
|
31
|
+
> :first-child {
|
|
32
|
+
min-width: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
> label {
|
|
36
|
+
flex: 0 0 auto;
|
|
37
|
+
margin: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.form-label {
|
|
41
|
+
margin-bottom: $moo-field-gap * 0.5;
|
|
42
|
+
font-weight: $font-weight-medium;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.field--disabled {
|
|
47
|
+
opacity: $moo-disabled-control-opacity;
|
|
48
|
+
pointer-events: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// field_description()/field_error() keep Bootstrap's own .form-text /
|
|
52
|
+
// .invalid-feedback class alongside these (the latter is load-bearing:
|
|
53
|
+
// Bootstrap's own validity sibling-selector CSS targets that exact class
|
|
54
|
+
// name to show/hide it), so every default here is a token override on top
|
|
55
|
+
// of Bootstrap's own rule rather than a full replacement of it.
|
|
56
|
+
.field-description,
|
|
57
|
+
.field-error {
|
|
58
|
+
margin-top: 0;
|
|
59
|
+
// .form-text/.invalid-feedback both default to Bootstrap's smaller
|
|
60
|
+
// $font-size-sm scale; the reference's own description/error text sit
|
|
61
|
+
// at the same size as the label itself, which this project's own
|
|
62
|
+
// $font-size-base already matches exactly (0.875rem).
|
|
63
|
+
font-size: $font-size-base;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.field-group {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: $moo-field-group-gap;
|
|
70
|
+
|
|
71
|
+
// Inside a gap-managed group a separator adds no spacing of its own;
|
|
72
|
+
// Bootstrap's default block margins on <hr> would otherwise stack on
|
|
73
|
+
// top of the group gap and double the visual distance.
|
|
74
|
+
> hr {
|
|
75
|
+
margin-block: 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.field-form {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: $moo-field-form-gap;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Bootstrap's own <legend> reboot rule (float/width/padding/line-height)
|
|
86
|
+
// still applies here; this only retunes its type scale. A dedicated class
|
|
87
|
+
// rather than reusing .form-label, since Field owns this legend's styling
|
|
88
|
+
// independently of Radio Group's own separate <legend class="form-label">
|
|
89
|
+
// choice.
|
|
90
|
+
.field-legend {
|
|
91
|
+
font-size: $moo-field-legend-font-size;
|
|
92
|
+
font-weight: $font-weight-medium;
|
|
93
|
+
margin-bottom: $moo-field-legend-margin-bottom;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// One standard, token-based gap between a fieldset's direct building
|
|
97
|
+
// blocks (the legend/description header, field groups, lone fields).
|
|
98
|
+
// Sibling margins rather than flex gap: Bootstrap's reboot floats
|
|
99
|
+
// <legend> to give it native block flow, and turning the fieldset into a
|
|
100
|
+
// flex container would silently opt out of that contract.
|
|
101
|
+
.field-fieldset > * + * {
|
|
102
|
+
margin-top: $moo-field-fieldset-gap;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The optional description directly under the legend is part of the
|
|
106
|
+
// header unit: it sits on the legend's own tight margin instead of the
|
|
107
|
+
// standard section gap.
|
|
108
|
+
.field-fieldset > .field-legend + .field-description {
|
|
109
|
+
margin-top: 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.field-fieldset + .field-fieldset {
|
|
113
|
+
margin-top: $moo-field-fieldset-sibling-gap;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.field-form > .field-fieldset + .field-fieldset {
|
|
117
|
+
margin-top: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// A separator() placed between two fieldsets (or field-group sections) is
|
|
121
|
+
// a much quieter division in the design reference than Bootstrap's own
|
|
122
|
+
// default <hr> opacity renders.
|
|
123
|
+
.field-fieldset + hr {
|
|
124
|
+
opacity: $moo-field-separator-opacity;
|
|
125
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.form-control {
|
|
2
|
+
min-height: $input-height;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// Text inputs should visually match the outer box of an Input Group, whose
|
|
6
|
+
// border is promoted to the parent wrapper.
|
|
7
|
+
.form-control:not(textarea):not([type="file"]):not(.form-control-sm, .form-control-lg) {
|
|
8
|
+
min-height: calc(#{$input-height} + #{$input-border-width} * 2);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.form-control[type="file"]:not(.form-control-sm, .form-control-lg)::file-selector-button {
|
|
12
|
+
min-height: calc(#{$input-height} - #{$input-border-width} * 2);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.form-control[type="time"] {
|
|
16
|
+
appearance: none;
|
|
17
|
+
position: relative;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Chromium's clock icon is the primary pointer trigger for the native time
|
|
21
|
+
// picker. Keep the indicator covering the whole control but visually hidden so
|
|
22
|
+
// pointer users retain picker access without the icon showing.
|
|
23
|
+
.form-control[type="time"]::-webkit-calendar-picker-indicator {
|
|
24
|
+
position: absolute;
|
|
25
|
+
inset: 0;
|
|
26
|
+
width: 100%;
|
|
27
|
+
height: 100%;
|
|
28
|
+
opacity: 0;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.form-control:disabled,
|
|
33
|
+
.form-select:disabled {
|
|
34
|
+
opacity: var(--moo-disabled-control-opacity);
|
|
35
|
+
}
|