@webority/theme 0.29.0 → 0.31.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.
- package/README.md +2 -2
- package/dist/purge-safelist.json +47 -30
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_tokens.scss +1 -0
- package/scss/_variables.scss +1 -0
- package/scss/_wui-components.scss +3 -0
- package/scss/wui/_chart.scss +5 -309
- package/scss/wui/_chat.scss +82 -0
- package/scss/wui/_choice.scss +84 -29
- package/scss/wui/_floating.scss +64 -0
- package/scss/wui/_kanban.scss +42 -10
- package/scss/wui/_media.scss +12 -0
- package/scss/wui/_shell.scss +8 -0
- package/scss/wui/_table-server.scss +4 -3
- package/scss/wui/_tour.scss +97 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"publishConfig": {
|
package/scss/_tokens.scss
CHANGED
|
@@ -331,6 +331,7 @@
|
|
|
331
331
|
--wui-z-shell-drawer: 1045; // mobile sidebar drawer
|
|
332
332
|
--wui-z-modal-backdrop: 1500;
|
|
333
333
|
--wui-z-modal: 1505;
|
|
334
|
+
--wui-z-tour: 1550; // product tour: above a modal, below menus and popovers
|
|
334
335
|
--wui-z-menu: 1600; // portalled select/date/phone menus + infotip
|
|
335
336
|
--wui-z-popover: 1600; // same rung as --wui-z-menu (above modal)
|
|
336
337
|
--wui-z-tooltip: 1650;
|
package/scss/_variables.scss
CHANGED
|
@@ -236,6 +236,7 @@ $focus-ring-width: 3px; // BS 0.25rem (4px)
|
|
|
236
236
|
//
|
|
237
237
|
// SCSS variables are compile-time and can't reference the runtime custom
|
|
238
238
|
// properties above — keep these numbers in sync with _tokens.scss by hand.
|
|
239
|
+
$zindex-tour: 1550;
|
|
239
240
|
$zindex-popover: 1600;
|
|
240
241
|
$zindex-tooltip: 1650;
|
|
241
242
|
$zindex-toast: 1700;
|
|
@@ -64,8 +64,11 @@
|
|
|
64
64
|
@import "wui/toolbar";
|
|
65
65
|
@import "wui/theme-toggle";
|
|
66
66
|
@import "wui/tree";
|
|
67
|
+
@import "wui/floating";
|
|
68
|
+
@import "wui/chat";
|
|
67
69
|
// After `overlays`: anchors the shared modal shell to the top when it holds the palette card.
|
|
68
70
|
@import "wui/command-palette";
|
|
69
71
|
@import "wui/context-menu";
|
|
72
|
+
@import "wui/tour";
|
|
70
73
|
// Last: restates the settled state of menus other partials style, so it must win.
|
|
71
74
|
@import "wui/menu-motion";
|
package/scss/wui/_chart.scss
CHANGED
|
@@ -1,315 +1,14 @@
|
|
|
1
|
-
// Charts:
|
|
1
|
+
// Charts: the <wui-chart> element behind AppChart.
|
|
2
2
|
//
|
|
3
|
-
// Part of the wui-components layer; see _wui-components.scss for the load order. The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// dark remap in _wui-aliases.scss flips with the rest of the portal.
|
|
7
|
-
|
|
8
|
-
.wui-chart {
|
|
9
|
-
margin: 0;
|
|
10
|
-
min-width: 0;
|
|
11
|
-
color: var(--color-text-muted);
|
|
12
|
-
font-size: 12px;
|
|
13
|
-
font-variant-numeric: tabular-nums;
|
|
14
|
-
}
|
|
15
|
-
.wui-chart-title {
|
|
16
|
-
margin-bottom: 12px;
|
|
17
|
-
font-size: 14px;
|
|
18
|
-
font-weight: 600;
|
|
19
|
-
color: var(--color-text);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// ============================================================================
|
|
23
|
-
// Line chart
|
|
24
|
-
// ============================================================================
|
|
25
|
-
// The top tick label sits above the top gridline, so the plot leaves room for it.
|
|
26
|
-
.wui-chart-plot {
|
|
27
|
-
position: relative;
|
|
28
|
-
margin-top: 8px;
|
|
29
|
-
}
|
|
30
|
-
// Strokes overhang the plot by half their width at the top and bottom gridlines.
|
|
31
|
-
.wui-chart-svg {
|
|
32
|
-
display: block;
|
|
33
|
-
overflow: visible;
|
|
34
|
-
}
|
|
35
|
-
.wui-chart-grid {
|
|
36
|
-
stroke: var(--color-border);
|
|
37
|
-
stroke-width: 1;
|
|
38
|
-
}
|
|
39
|
-
.wui-chart-reference {
|
|
40
|
-
stroke: var(--color-border-strong);
|
|
41
|
-
stroke-width: 1;
|
|
42
|
-
stroke-dasharray: 4 4;
|
|
43
|
-
}
|
|
44
|
-
.wui-chart-line {
|
|
45
|
-
stroke: var(--color-primary);
|
|
46
|
-
stroke-width: 2;
|
|
47
|
-
stroke-linecap: round;
|
|
48
|
-
}
|
|
49
|
-
// Bands are a quiet wash behind the series; the tone tints, it does not shout.
|
|
50
|
-
.wui-chart-band {
|
|
51
|
-
fill: var(--color-bg-muted);
|
|
52
|
-
opacity: 0.6;
|
|
53
|
-
}
|
|
54
|
-
.wui-chart-band-info {
|
|
55
|
-
fill: var(--color-info-soft);
|
|
56
|
-
}
|
|
57
|
-
.wui-chart-band-success {
|
|
58
|
-
fill: var(--color-success-soft);
|
|
59
|
-
}
|
|
60
|
-
.wui-chart-band-warning {
|
|
61
|
-
fill: var(--color-warning-soft);
|
|
62
|
-
}
|
|
63
|
-
.wui-chart-band-danger {
|
|
64
|
-
fill: var(--color-danger-soft);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Y tick values sit just above their gridline at the left edge, inside the plot, so the chart
|
|
68
|
-
// spends no width on a label gutter at 320px.
|
|
69
|
-
.wui-chart-tick,
|
|
70
|
-
.wui-chart-band-label,
|
|
71
|
-
.wui-chart-reference-label {
|
|
72
|
-
position: absolute;
|
|
73
|
-
font-size: 11px;
|
|
74
|
-
line-height: 1;
|
|
75
|
-
white-space: nowrap;
|
|
76
|
-
color: var(--color-text-muted);
|
|
77
|
-
pointer-events: none;
|
|
78
|
-
}
|
|
79
|
-
.wui-chart-tick {
|
|
80
|
-
left: 0;
|
|
81
|
-
padding-bottom: 3px;
|
|
82
|
-
transform: translateY(-100%);
|
|
83
|
-
}
|
|
84
|
-
.wui-chart-band-label {
|
|
85
|
-
right: 0;
|
|
86
|
-
padding-top: 3px;
|
|
87
|
-
}
|
|
88
|
-
.wui-chart-reference-label {
|
|
89
|
-
right: 0;
|
|
90
|
-
padding-bottom: 3px;
|
|
91
|
-
transform: translateY(-100%);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// A 24px hit area per point (WCAG 2.2 target size), drawn as an 8px ring.
|
|
95
|
-
.wui-chart-point {
|
|
96
|
-
position: absolute;
|
|
97
|
-
width: 24px;
|
|
98
|
-
height: 24px;
|
|
99
|
-
margin: -12px 0 0 -12px;
|
|
100
|
-
padding: 0;
|
|
101
|
-
display: grid;
|
|
102
|
-
place-items: center;
|
|
103
|
-
border: 0;
|
|
104
|
-
border-radius: 50%;
|
|
105
|
-
background: transparent;
|
|
106
|
-
cursor: default;
|
|
107
|
-
}
|
|
108
|
-
.wui-chart-point:focus-visible {
|
|
109
|
-
outline: 2px solid var(--color-primary);
|
|
110
|
-
outline-offset: 0;
|
|
111
|
-
}
|
|
112
|
-
.wui-chart-dot {
|
|
113
|
-
width: 8px;
|
|
114
|
-
height: 8px;
|
|
115
|
-
border-radius: 50%;
|
|
116
|
-
border: 2px solid var(--color-primary);
|
|
117
|
-
background: var(--color-surface);
|
|
118
|
-
transition: transform 120ms var(--wui-ease-out-quart);
|
|
119
|
-
}
|
|
120
|
-
.wui-chart-point:hover .wui-chart-dot,
|
|
121
|
-
.wui-chart-point:focus-visible .wui-chart-dot {
|
|
122
|
-
transform: scale(1.35);
|
|
123
|
-
background: var(--color-primary);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// CSS-only tooltip above the point; the first and last points anchor it to their own edge so
|
|
127
|
-
// it never runs off a narrow chart.
|
|
128
|
-
.wui-chart-tip {
|
|
129
|
-
position: absolute;
|
|
130
|
-
bottom: calc(100% + 4px);
|
|
131
|
-
left: 50%;
|
|
132
|
-
z-index: 1;
|
|
133
|
-
display: flex;
|
|
134
|
-
flex-direction: column;
|
|
135
|
-
align-items: flex-start;
|
|
136
|
-
gap: 2px;
|
|
137
|
-
padding: 6px 10px;
|
|
138
|
-
border: 1px solid var(--color-border-light);
|
|
139
|
-
border-radius: var(--radius);
|
|
140
|
-
background: var(--color-surface);
|
|
141
|
-
box-shadow: var(--wui-shadow-overlay);
|
|
142
|
-
white-space: nowrap;
|
|
143
|
-
text-align: left;
|
|
144
|
-
pointer-events: none;
|
|
145
|
-
visibility: hidden;
|
|
146
|
-
opacity: 0;
|
|
147
|
-
transform: translateX(-50%);
|
|
148
|
-
transition:
|
|
149
|
-
opacity 120ms var(--wui-ease-out-quart),
|
|
150
|
-
visibility 120ms;
|
|
151
|
-
}
|
|
152
|
-
.wui-chart-point-start .wui-chart-tip {
|
|
153
|
-
left: 0;
|
|
154
|
-
transform: none;
|
|
155
|
-
}
|
|
156
|
-
.wui-chart-point-end .wui-chart-tip {
|
|
157
|
-
left: auto;
|
|
158
|
-
right: 0;
|
|
159
|
-
transform: none;
|
|
160
|
-
}
|
|
161
|
-
.wui-chart-point:hover .wui-chart-tip,
|
|
162
|
-
.wui-chart-point:focus-visible .wui-chart-tip {
|
|
163
|
-
visibility: visible;
|
|
164
|
-
opacity: 1;
|
|
165
|
-
}
|
|
166
|
-
.wui-chart-tip-label {
|
|
167
|
-
font-size: 11px;
|
|
168
|
-
color: var(--color-text-muted);
|
|
169
|
-
}
|
|
170
|
-
.wui-chart-tip-value {
|
|
171
|
-
font-size: 13px;
|
|
172
|
-
font-weight: 600;
|
|
173
|
-
color: var(--color-text);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.wui-chart-xaxis {
|
|
177
|
-
position: relative;
|
|
178
|
-
height: 16px;
|
|
179
|
-
margin-top: 6px;
|
|
180
|
-
}
|
|
181
|
-
.wui-chart-xlabel {
|
|
182
|
-
position: absolute;
|
|
183
|
-
top: 0;
|
|
184
|
-
font-size: 11px;
|
|
185
|
-
line-height: 16px;
|
|
186
|
-
white-space: nowrap;
|
|
187
|
-
transform: translateX(-50%);
|
|
188
|
-
}
|
|
189
|
-
// Below md only every Nth label shows (the component marks the rest minor); a 12-month axis
|
|
190
|
-
// would otherwise overlap on a phone.
|
|
191
|
-
.wui-chart-xlabel-minor {
|
|
192
|
-
display: none;
|
|
193
|
-
}
|
|
194
|
-
@media (min-width: 768px) {
|
|
195
|
-
.wui-chart-xlabel-minor {
|
|
196
|
-
display: block;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// ============================================================================
|
|
201
|
-
// Bar chart
|
|
202
|
-
// ============================================================================
|
|
203
|
-
.wui-bar-chart-list {
|
|
204
|
-
margin: 0;
|
|
205
|
-
padding: 0;
|
|
206
|
-
list-style: none;
|
|
207
|
-
}
|
|
208
|
-
.wui-bar-chart-label {
|
|
209
|
-
min-width: 0;
|
|
210
|
-
color: var(--color-text-body);
|
|
211
|
-
overflow-wrap: anywhere;
|
|
212
|
-
}
|
|
213
|
-
.wui-bar-chart-value {
|
|
214
|
-
font-weight: 600;
|
|
215
|
-
white-space: nowrap;
|
|
216
|
-
color: var(--color-text);
|
|
217
|
-
}
|
|
218
|
-
.wui-bar-chart-track {
|
|
219
|
-
position: relative;
|
|
220
|
-
overflow: hidden;
|
|
221
|
-
border-radius: 999px;
|
|
222
|
-
background: var(--color-progress-track);
|
|
223
|
-
}
|
|
224
|
-
.wui-bar-chart-fill {
|
|
225
|
-
border-radius: inherit;
|
|
226
|
-
background: var(--color-primary);
|
|
227
|
-
}
|
|
228
|
-
// Each track is a focus stop; the ring sits outside the track's own clipped content.
|
|
229
|
-
.wui-bar-chart-track:focus-visible {
|
|
230
|
-
outline: 2px solid var(--color-primary);
|
|
231
|
-
outline-offset: 2px;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Horizontal: a label/value line over each bar, so a long label wraps instead of squeezing
|
|
235
|
-
// the bar at 320px.
|
|
236
|
-
.wui-bar-chart-horizontal .wui-bar-chart-list {
|
|
237
|
-
display: flex;
|
|
238
|
-
flex-direction: column;
|
|
239
|
-
gap: 14px;
|
|
240
|
-
}
|
|
241
|
-
.wui-bar-chart-horizontal .wui-bar-chart-item {
|
|
242
|
-
display: grid;
|
|
243
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
244
|
-
grid-template-areas:
|
|
245
|
-
"label value"
|
|
246
|
-
"track track";
|
|
247
|
-
column-gap: 12px;
|
|
248
|
-
row-gap: 6px;
|
|
249
|
-
align-items: baseline;
|
|
250
|
-
}
|
|
251
|
-
.wui-bar-chart-horizontal .wui-bar-chart-label {
|
|
252
|
-
grid-area: label;
|
|
253
|
-
}
|
|
254
|
-
.wui-bar-chart-horizontal .wui-bar-chart-value {
|
|
255
|
-
grid-area: value;
|
|
256
|
-
}
|
|
257
|
-
.wui-bar-chart-horizontal .wui-bar-chart-track {
|
|
258
|
-
grid-area: track;
|
|
259
|
-
height: 8px;
|
|
260
|
-
}
|
|
261
|
-
.wui-bar-chart-horizontal .wui-bar-chart-fill {
|
|
262
|
-
height: 100%;
|
|
263
|
-
transition: width 300ms var(--wui-ease-out-quart);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Vertical: bars side by side, value above and label below each. The plot height is one var a
|
|
267
|
-
// caller can set from its own class.
|
|
268
|
-
.wui-bar-chart-vertical .wui-bar-chart-list {
|
|
269
|
-
display: flex;
|
|
270
|
-
gap: 8px;
|
|
271
|
-
}
|
|
272
|
-
.wui-bar-chart-vertical .wui-bar-chart-item {
|
|
273
|
-
flex: 1 1 0;
|
|
274
|
-
min-width: 0;
|
|
275
|
-
display: grid;
|
|
276
|
-
grid-template-areas:
|
|
277
|
-
"value"
|
|
278
|
-
"track"
|
|
279
|
-
"label";
|
|
280
|
-
justify-items: center;
|
|
281
|
-
row-gap: 6px;
|
|
282
|
-
text-align: center;
|
|
283
|
-
}
|
|
284
|
-
// Seven bars on a 320px phone leave about 40px a column, so "18 of 25" may wrap here.
|
|
285
|
-
.wui-bar-chart-vertical .wui-bar-chart-value {
|
|
286
|
-
grid-area: value;
|
|
287
|
-
white-space: normal;
|
|
288
|
-
}
|
|
289
|
-
.wui-bar-chart-vertical .wui-bar-chart-track {
|
|
290
|
-
grid-area: track;
|
|
291
|
-
display: flex;
|
|
292
|
-
align-items: flex-end;
|
|
293
|
-
width: 100%;
|
|
294
|
-
max-width: 32px;
|
|
295
|
-
height: var(--wui-bar-chart-height, 180px);
|
|
296
|
-
border-radius: var(--radius-sm);
|
|
297
|
-
}
|
|
298
|
-
.wui-bar-chart-vertical .wui-bar-chart-fill {
|
|
299
|
-
width: 100%;
|
|
300
|
-
transition: height 300ms var(--wui-ease-out-quart);
|
|
301
|
-
}
|
|
302
|
-
.wui-bar-chart-vertical .wui-bar-chart-label {
|
|
303
|
-
grid-area: label;
|
|
304
|
-
font-size: 11px;
|
|
305
|
-
}
|
|
3
|
+
// Part of the wui-components layer; see _wui-components.scss for the load order. The element draws
|
|
4
|
+
// series in the --wui-chart-* colours. Every colour is a token the dark remap in _wui-aliases.scss
|
|
5
|
+
// flips with the rest of the portal.
|
|
306
6
|
|
|
307
7
|
// ============================================================================
|
|
308
8
|
// Chart element: <wui-chart> (AppChart, <app-chart>)
|
|
309
9
|
// ============================================================================
|
|
310
10
|
// The element builds the legend, the drawing surface, the state box and the data table; the
|
|
311
|
-
// engine draws inside .wui-chart-canvas with colours the element reads from --wui-chart-*.
|
|
312
|
-
// names here stay clear of the line and bar chart classes above.
|
|
11
|
+
// engine draws inside .wui-chart-canvas with colours the element reads from --wui-chart-*.
|
|
313
12
|
wui-chart {
|
|
314
13
|
position: relative;
|
|
315
14
|
display: block;
|
|
@@ -513,9 +212,6 @@ wui-chart[aria-busy="true"] .wui-chart-surface {
|
|
|
513
212
|
}
|
|
514
213
|
|
|
515
214
|
@media (prefers-reduced-motion: reduce) {
|
|
516
|
-
.wui-chart-dot,
|
|
517
|
-
.wui-chart-tip,
|
|
518
|
-
.wui-bar-chart-fill,
|
|
519
215
|
.wui-chart-legend-item,
|
|
520
216
|
.wui-chart-legend-swatch,
|
|
521
217
|
.wui-chart-surface {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// ── Assistant chat ──
|
|
2
|
+
// <wui-chat> / <app-chat>: a launcher pinned to a bottom corner and a side panel holding the
|
|
3
|
+
// conversation and the question box. The header, buttons, form field and alerts inside are the
|
|
4
|
+
// theme's own (.drawer-header, .btn, .form-control, .alert); only the placement and the two chat
|
|
5
|
+
// shapes live here. A compact launcher is the shared .wui-floating-btn circle (_floating.scss).
|
|
6
|
+
//
|
|
7
|
+
// Part of the wui-components layer — see _wui-components.scss for load order.
|
|
8
|
+
|
|
9
|
+
// The element is a plain host: its two parts are fixed to the viewport.
|
|
10
|
+
wui-chat {
|
|
11
|
+
display: contents;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.wui-chat-launcher {
|
|
15
|
+
position: fixed;
|
|
16
|
+
bottom: 0;
|
|
17
|
+
inset-inline-end: 0;
|
|
18
|
+
z-index: var(--wui-z-fixed);
|
|
19
|
+
margin: 1rem;
|
|
20
|
+
}
|
|
21
|
+
wui-chat[position="start"] .wui-chat-launcher {
|
|
22
|
+
inset-inline-end: auto;
|
|
23
|
+
inset-inline-start: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Above the shell chrome and below modals, like the drawer it stands in for.
|
|
27
|
+
.wui-chat-panel {
|
|
28
|
+
position: fixed;
|
|
29
|
+
top: 0;
|
|
30
|
+
bottom: 0;
|
|
31
|
+
inset-inline-end: 0;
|
|
32
|
+
z-index: var(--wui-z-shell-drawer);
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
width: min(440px, 100%);
|
|
36
|
+
background: var(--color-surface);
|
|
37
|
+
border-inline-start: 1px solid var(--color-border);
|
|
38
|
+
box-shadow: var(--wui-shadow-overlay-lg);
|
|
39
|
+
}
|
|
40
|
+
wui-chat[position="start"] .wui-chat-panel {
|
|
41
|
+
inset-inline-end: auto;
|
|
42
|
+
inset-inline-start: 0;
|
|
43
|
+
border-inline-start: 0;
|
|
44
|
+
border-inline-end: 1px solid var(--color-border);
|
|
45
|
+
}
|
|
46
|
+
// `hidden` is how the element closes the panel; display: flex above would otherwise win over it.
|
|
47
|
+
.wui-chat-panel[hidden] {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.wui-chat-body {
|
|
52
|
+
flex: 1 1 auto;
|
|
53
|
+
min-height: 0;
|
|
54
|
+
padding: 20px;
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
}
|
|
57
|
+
.wui-chat-empty {
|
|
58
|
+
padding-block: 2rem;
|
|
59
|
+
}
|
|
60
|
+
.wui-chat-foot {
|
|
61
|
+
flex: none;
|
|
62
|
+
padding: 16px 20px;
|
|
63
|
+
border-top: 1px solid var(--color-border);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The person's question, on the right; the answer is plain text on the panel.
|
|
67
|
+
.wui-chat-question {
|
|
68
|
+
max-width: 85%;
|
|
69
|
+
}
|
|
70
|
+
.wui-chat-bubble {
|
|
71
|
+
padding: 10px 14px;
|
|
72
|
+
background: var(--color-bg-muted);
|
|
73
|
+
border-radius: var(--wui-radius-lg);
|
|
74
|
+
white-space: pre-wrap;
|
|
75
|
+
overflow-wrap: anywhere;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The question box grows with its text, up to a few lines.
|
|
79
|
+
.wui-chat-foot .form-control {
|
|
80
|
+
max-height: 9rem;
|
|
81
|
+
resize: none;
|
|
82
|
+
}
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -579,8 +579,7 @@
|
|
|
579
579
|
// shut, the trigger keeps the focus look only for keyboard focus (:focus-visible); a pick or
|
|
580
580
|
// close by pointer returns it to rest. An errored field (aria-invalid on the host) keeps its red.
|
|
581
581
|
wui-datepicker:not([aria-invalid="true"]) .datepicker:not(.open) .datepicker-trigger:focus:not(:focus-visible),
|
|
582
|
-
wui-date-range:not([aria-invalid="true"]) .daterange:not(.open) .daterange-trigger:focus:not(:focus-visible)
|
|
583
|
-
wui-timepicker:not([aria-invalid="true"]) .timepicker:not(.open) .timepicker-trigger:focus:not(:focus-visible) {
|
|
582
|
+
wui-date-range:not([aria-invalid="true"]) .daterange:not(.open) .daterange-trigger:focus:not(:focus-visible) {
|
|
584
583
|
background-color: var(--color-field-bg);
|
|
585
584
|
border-color: var(--color-border);
|
|
586
585
|
}
|
|
@@ -1350,37 +1349,56 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1350
1349
|
font-size: 14px;
|
|
1351
1350
|
}
|
|
1352
1351
|
|
|
1353
|
-
// timepicker — time-of-day field with
|
|
1352
|
+
// timepicker — time-of-day text field (type "7:45 pm") with a clock button that opens hour /
|
|
1353
|
+
// minute / AM-PM columns (min/max bounds, step granularity, 12/24 display).
|
|
1354
1354
|
.timepicker {
|
|
1355
1355
|
position: relative;
|
|
1356
1356
|
width: var(--wui-timepicker-width, 12rem);
|
|
1357
1357
|
max-width: 100%;
|
|
1358
1358
|
}
|
|
1359
|
+
.wui-timepicker-field {
|
|
1360
|
+
position: relative;
|
|
1361
|
+
}
|
|
1362
|
+
// The input leaves room on the right for the clock button laid over it.
|
|
1359
1363
|
.timepicker-trigger {
|
|
1360
|
-
|
|
1361
|
-
align-items: center;
|
|
1362
|
-
justify-content: space-between;
|
|
1363
|
-
gap: 8px;
|
|
1364
|
-
text-align: left;
|
|
1365
|
-
cursor: pointer;
|
|
1364
|
+
padding-right: 44px;
|
|
1366
1365
|
}
|
|
1367
|
-
.timepicker-trigger
|
|
1368
|
-
|
|
1369
|
-
white-space: nowrap;
|
|
1370
|
-
text-overflow: ellipsis;
|
|
1366
|
+
.timepicker-trigger.input-sm {
|
|
1367
|
+
padding-right: 36px;
|
|
1371
1368
|
}
|
|
1372
|
-
.timepicker-
|
|
1373
|
-
|
|
1369
|
+
.wui-timepicker-toggle {
|
|
1370
|
+
position: absolute;
|
|
1371
|
+
top: 0;
|
|
1372
|
+
right: 0;
|
|
1373
|
+
bottom: 0;
|
|
1374
|
+
display: grid;
|
|
1375
|
+
place-items: center;
|
|
1376
|
+
width: 44px;
|
|
1377
|
+
padding: 0;
|
|
1378
|
+
border: none;
|
|
1379
|
+
border-radius: 0 var(--wui-field-radius) var(--wui-field-radius) 0;
|
|
1380
|
+
background: transparent;
|
|
1374
1381
|
color: var(--color-text-subtle);
|
|
1382
|
+
cursor: pointer;
|
|
1383
|
+
transition: color 0.12s ease;
|
|
1375
1384
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
// even when the trigger classes are toggled independently.
|
|
1379
|
-
.timepicker-placeholder {
|
|
1380
|
-
color: var(--color-text-subtle);
|
|
1385
|
+
.timepicker-trigger.input-sm + .wui-timepicker-toggle {
|
|
1386
|
+
width: 36px;
|
|
1381
1387
|
}
|
|
1382
|
-
.timepicker-
|
|
1383
|
-
|
|
1388
|
+
.wui-timepicker-toggle:hover:not(:disabled),
|
|
1389
|
+
.timepicker.open .wui-timepicker-toggle {
|
|
1390
|
+
color: var(--color-primary);
|
|
1391
|
+
}
|
|
1392
|
+
.wui-timepicker-toggle:focus-visible {
|
|
1393
|
+
outline: 2px solid var(--color-primary);
|
|
1394
|
+
outline-offset: -4px;
|
|
1395
|
+
}
|
|
1396
|
+
.wui-timepicker-toggle:disabled {
|
|
1397
|
+
cursor: not-allowed;
|
|
1398
|
+
}
|
|
1399
|
+
// The control's own message for typed text it refuses; the shell's .field-error sits below it.
|
|
1400
|
+
.wui-timepicker-error {
|
|
1401
|
+
margin-top: 4px;
|
|
1384
1402
|
}
|
|
1385
1403
|
// Width presets.
|
|
1386
1404
|
.time-w-sm {
|
|
@@ -1396,18 +1414,21 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1396
1414
|
--wui-timepicker-width: 100%;
|
|
1397
1415
|
}
|
|
1398
1416
|
.timepicker.open .timepicker-trigger {
|
|
1399
|
-
// Stay white while the
|
|
1417
|
+
// Stay white while the columns are open — see the note on .datepicker.open above.
|
|
1400
1418
|
background-color: var(--color-surface);
|
|
1401
1419
|
border-color: var(--color-focus-border);
|
|
1402
1420
|
box-shadow: none;
|
|
1403
1421
|
}
|
|
1422
|
+
// Refused typed text keeps the soft danger border while the columns are open, as the shell's
|
|
1423
|
+
// host-level aria-invalid already does.
|
|
1424
|
+
.timepicker.open .timepicker-trigger[aria-invalid="true"] {
|
|
1425
|
+
border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
|
|
1426
|
+
}
|
|
1404
1427
|
.timepicker-pop {
|
|
1405
1428
|
position: fixed;
|
|
1406
1429
|
z-index: var(--wui-z-menu);
|
|
1407
|
-
width:
|
|
1430
|
+
width: max-content;
|
|
1408
1431
|
max-width: calc(100vw - 16px);
|
|
1409
|
-
max-height: 360px;
|
|
1410
|
-
overflow-y: auto;
|
|
1411
1432
|
padding: 8px;
|
|
1412
1433
|
background: var(--color-surface);
|
|
1413
1434
|
border: 1px solid var(--color-border);
|
|
@@ -1439,14 +1460,37 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1439
1460
|
transform: translateY(0);
|
|
1440
1461
|
}
|
|
1441
1462
|
}
|
|
1463
|
+
.wui-timepicker-cols {
|
|
1464
|
+
display: flex;
|
|
1465
|
+
gap: 4px;
|
|
1466
|
+
}
|
|
1467
|
+
// Each column scrolls on its own; overscroll stays inside so a flick never scrolls the page.
|
|
1468
|
+
.wui-timepicker-col {
|
|
1469
|
+
position: relative;
|
|
1470
|
+
display: flex;
|
|
1471
|
+
flex-direction: column;
|
|
1472
|
+
gap: 2px;
|
|
1473
|
+
min-width: 56px;
|
|
1474
|
+
max-height: 264px;
|
|
1475
|
+
overflow-y: auto;
|
|
1476
|
+
overscroll-behavior: contain;
|
|
1477
|
+
scrollbar-width: thin;
|
|
1478
|
+
}
|
|
1479
|
+
.wui-timepicker-col + .wui-timepicker-col {
|
|
1480
|
+
padding-left: 4px;
|
|
1481
|
+
border-left: 1px solid var(--color-border);
|
|
1482
|
+
}
|
|
1442
1483
|
.timepicker-option {
|
|
1443
1484
|
display: block;
|
|
1485
|
+
flex-shrink: 0;
|
|
1444
1486
|
width: 100%;
|
|
1445
|
-
|
|
1487
|
+
min-height: 36px;
|
|
1488
|
+
padding: 6px 12px;
|
|
1446
1489
|
border: none;
|
|
1447
1490
|
background: transparent;
|
|
1448
|
-
text-align:
|
|
1491
|
+
text-align: center;
|
|
1449
1492
|
font-size: 14px;
|
|
1493
|
+
font-variant-numeric: tabular-nums;
|
|
1450
1494
|
color: var(--color-text);
|
|
1451
1495
|
cursor: pointer;
|
|
1452
1496
|
border-radius: var(--radius-sm);
|
|
@@ -1454,7 +1498,13 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1454
1498
|
background 0.1s,
|
|
1455
1499
|
color 0.1s;
|
|
1456
1500
|
}
|
|
1457
|
-
.
|
|
1501
|
+
// Touch screens get full 44px rows.
|
|
1502
|
+
@media (pointer: coarse) {
|
|
1503
|
+
.timepicker-option {
|
|
1504
|
+
min-height: 44px;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
.timepicker-option:hover:not(:disabled),
|
|
1458
1508
|
.timepicker-option.is-active {
|
|
1459
1509
|
background: var(--color-primary-soft, color-mix(in srgb, var(--color-primary) 8%, transparent));
|
|
1460
1510
|
color: var(--color-primary);
|
|
@@ -1464,6 +1514,11 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
|
|
|
1464
1514
|
color: var(--color-primary-text);
|
|
1465
1515
|
font-weight: 600;
|
|
1466
1516
|
}
|
|
1517
|
+
.timepicker-option:disabled {
|
|
1518
|
+
color: var(--color-text-subtle);
|
|
1519
|
+
opacity: 0.5;
|
|
1520
|
+
cursor: not-allowed;
|
|
1521
|
+
}
|
|
1467
1522
|
.timepicker-option:focus-visible {
|
|
1468
1523
|
outline: none;
|
|
1469
1524
|
box-shadow: inset 0 0 0 2px var(--color-primary);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// ── Floating stack ──
|
|
2
|
+
// The one fixed corner container the compact floating buttons (the chat launcher, the report-an-issue
|
|
3
|
+
// button) portal into, so they stack with a gap and never overlap or need a hand-set offset. React's
|
|
4
|
+
// useFloatingStack creates it; the items inside are not fixed themselves.
|
|
5
|
+
//
|
|
6
|
+
// Part of the wui-components layer — see _wui-components.scss for load order.
|
|
7
|
+
|
|
8
|
+
.wui-floating-stack {
|
|
9
|
+
position: fixed;
|
|
10
|
+
bottom: 0;
|
|
11
|
+
z-index: var(--wui-z-fixed);
|
|
12
|
+
display: flex;
|
|
13
|
+
// The primary item sits in the corner and the secondary one stacks above it.
|
|
14
|
+
flex-direction: column-reverse;
|
|
15
|
+
gap: 12px;
|
|
16
|
+
margin: 1rem;
|
|
17
|
+
// The container spans only its items, but a gap between them must not swallow clicks on the page.
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
20
|
+
.wui-floating-stack[data-position="end"] {
|
|
21
|
+
inset-inline-end: 0;
|
|
22
|
+
align-items: flex-end;
|
|
23
|
+
}
|
|
24
|
+
.wui-floating-stack[data-position="start"] {
|
|
25
|
+
inset-inline-start: 0;
|
|
26
|
+
align-items: flex-start;
|
|
27
|
+
}
|
|
28
|
+
.wui-floating-stack-item {
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
pointer-events: auto;
|
|
31
|
+
}
|
|
32
|
+
.wui-floating-stack-item--primary {
|
|
33
|
+
order: 0;
|
|
34
|
+
}
|
|
35
|
+
.wui-floating-stack-item--secondary {
|
|
36
|
+
order: 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ── Compact floating button ──
|
|
40
|
+
// An icon-only circle. The label lives in the tooltip and the aria-label, never on the button.
|
|
41
|
+
.wui-floating-btn {
|
|
42
|
+
--wui-floating-size: 48px;
|
|
43
|
+
|
|
44
|
+
display: inline-flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
width: var(--wui-floating-size);
|
|
48
|
+
height: var(--wui-floating-size);
|
|
49
|
+
padding: 0;
|
|
50
|
+
border-radius: 50%;
|
|
51
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
52
|
+
}
|
|
53
|
+
// Quieter than the primary circle: a light brand tint, the brand icon and a thin brand border.
|
|
54
|
+
.wui-floating-btn--secondary {
|
|
55
|
+
--wui-floating-size: 40px;
|
|
56
|
+
--bs-btn-border-color: var(--wui-primary-30);
|
|
57
|
+
--bs-btn-hover-border-color: var(--wui-primary-30);
|
|
58
|
+
--bs-btn-active-border-color: var(--wui-primary-30);
|
|
59
|
+
}
|
|
60
|
+
@media (prefers-reduced-motion: reduce) {
|
|
61
|
+
.wui-floating-btn {
|
|
62
|
+
transition: none;
|
|
63
|
+
}
|
|
64
|
+
}
|