@tensor_1/tokens 2.34.0 → 2.36.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/build/css/components.css +1 -1
- package/build/css/styles.css +1 -1
- package/package.json +1 -1
- package/styles/components/avatar.css +20 -0
- package/styles/components/date-picker.css +72 -0
- package/styles/components/inline-edit.css +28 -0
- package/styles/components/list.css +3 -0
- package/styles/components/radio.css +33 -0
- package/styles/components/scheduling-calendar.css +18 -0
- package/styles/components/scheduling-timeline.css +219 -38
- package/styles/components/search-suggest.css +2 -0
- package/styles/components/tree.css +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tensor_1/tokens",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.36.0",
|
|
4
4
|
"description": "UIx v2 design tokens — the --uix-* contract for house products (Tensor, POSx, SHOPx). One DTCG source -> CSS variables, Tailwind theme, and typed TS constants.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
.uix-avatar--xl .uix-avatar__status { width: 14px; height: 14px; right: 1px; bottom: 1px; }
|
|
14
14
|
.uix-avatar--2xl .uix-avatar__status { width: 18px; height: 18px; border-width: 3px; right: 4px; bottom: 4px; }
|
|
15
15
|
.uix-avatar__status { position: absolute; right: -1px; bottom: -1px; width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--uix-surface); background: var(--uix-success); }
|
|
16
|
+
/* The dot sits on the circle's edge, so the circle must not clip it: it used to, and a quarter
|
|
17
|
+
of the dot was all that showed (HAR-1374). Only an avatar with a dot stops clipping; its
|
|
18
|
+
photo and its edit overlay keep the round shape themselves. */
|
|
19
|
+
.uix-avatar:has(> .uix-avatar__status) { overflow: visible; }
|
|
20
|
+
.uix-avatar:has(> .uix-avatar__status) > img, .uix-avatar__edit { border-radius: 50%; }
|
|
21
|
+
.uix-avatar--lg .uix-avatar__status { right: 1px; bottom: 1px; }
|
|
16
22
|
.uix-avatar-group { display: inline-flex; }
|
|
17
23
|
.uix-avatar-group .uix-avatar { box-shadow: 0 0 0 2px var(--uix-surface); margin-left: calc(-1 * var(--uix-space-2)); }
|
|
18
24
|
.uix-avatar-group .uix-avatar:first-child { margin-left: 0; }
|
|
@@ -27,3 +33,17 @@
|
|
|
27
33
|
.uix-avatar--editable:hover .uix-avatar__edit, .uix-avatar--editable:focus-visible .uix-avatar__edit { opacity: 1; }
|
|
28
34
|
.uix-avatar--empty { background: var(--uix-bg-subtle); border: 1.5px dashed var(--uix-border-strong); color: var(--uix-text-muted); }
|
|
29
35
|
}
|
|
36
|
+
|
|
37
|
+
/* Presence (HAR-1374): online, busy, away, offline. Each state has its own SHAPE as well as its
|
|
38
|
+
own colour (a filled dot, a dot with a bar, two rings), and the React components add the state
|
|
39
|
+
in text. The same four looks serve the dot on an avatar and the standalone .uix-presence. The
|
|
40
|
+
ring colours are text colours on purpose: the status hues are too light for a 2 px ring
|
|
41
|
+
(packages/tokens/tests/presence-contrast.test.mjs holds all four to 3:1 on the surface). */
|
|
42
|
+
@layer uix.components {
|
|
43
|
+
.uix-presence { position: relative; display: inline-block; flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--uix-success); }
|
|
44
|
+
.uix-avatar__status::after, .uix-presence::after { content: ""; position: absolute; left: 20%; right: 20%; top: calc(50% - 1px); height: 2px; border-radius: var(--uix-radius-pill); background: var(--uix-surface); display: none; }
|
|
45
|
+
.uix-avatar__status[data-presence="busy"], .uix-presence--busy { background: var(--uix-danger); }
|
|
46
|
+
.uix-avatar__status[data-presence="busy"]::after, .uix-presence--busy::after { display: block; }
|
|
47
|
+
.uix-avatar__status[data-presence="away"], .uix-presence--away { background: var(--uix-surface); box-shadow: inset 0 0 0 2px var(--uix-warning-text); }
|
|
48
|
+
.uix-avatar__status[data-presence="offline"], .uix-presence--offline { background: var(--uix-surface); box-shadow: inset 0 0 0 2px var(--uix-text-muted); }
|
|
49
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* uix date fields — DatePicker, DateTimePicker and the field mode of DateRangePicker (HAR-1383).
|
|
2
|
+
The day cells are the range picker's (date-range-picker.css); this file adds the field, the
|
|
3
|
+
popover's one-month calendar and the single selected day. Depends only on --uix-* tokens. */
|
|
4
|
+
@layer uix.components {
|
|
5
|
+
/* --- DatePicker: a text input with a calendar button inside its trailing edge ------------- */
|
|
6
|
+
.uix-date-picker { display: grid; gap: var(--uix-space-1); width: 100%; min-width: 0; max-width: 11rem; }
|
|
7
|
+
.uix-date-picker__control { position: relative; display: flex; min-width: 0; }
|
|
8
|
+
.uix-date-picker__input { padding-inline-end: calc(var(--uix-control-h) + var(--uix-space-1)); font-variant-numeric: tabular-nums; }
|
|
9
|
+
.uix-date-picker--sm .uix-date-picker__input { padding-inline-end: var(--uix-space-7); }
|
|
10
|
+
.uix-date-picker__toggle {
|
|
11
|
+
/* Half a space step (2 px) inside the field's border, on every side it touches. */
|
|
12
|
+
--date-picker-inset: calc(var(--uix-space-1) / 2);
|
|
13
|
+
position: absolute; inset-block: var(--date-picker-inset); inset-inline-end: var(--date-picker-inset); aspect-ratio: 1;
|
|
14
|
+
display: inline-flex; align-items: center; justify-content: center; padding: 0;
|
|
15
|
+
border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer;
|
|
16
|
+
transition: background-color var(--uix-dur-fast) var(--uix-ease-out), color var(--uix-dur-fast) var(--uix-ease-out);
|
|
17
|
+
}
|
|
18
|
+
.uix-date-picker__toggle:hover:not(:disabled),
|
|
19
|
+
.uix-date-picker__toggle[aria-expanded="true"] { background: var(--uix-bg-hover); color: var(--uix-text); }
|
|
20
|
+
.uix-date-picker__toggle:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 0; }
|
|
21
|
+
.uix-date-picker__toggle:disabled { cursor: not-allowed; opacity: .55; }
|
|
22
|
+
.uix-date-picker__error { min-width: 0; overflow-wrap: anywhere; }
|
|
23
|
+
|
|
24
|
+
/* --- The calendar in the popover -------------------------------------------------------- */
|
|
25
|
+
.uix-date-picker__popover { padding: var(--uix-space-3); }
|
|
26
|
+
.uix-date-picker__calendar { display: grid; gap: var(--uix-space-2); width: 17rem; max-width: calc(100vw - 2 * var(--uix-space-6)); color: var(--uix-text); }
|
|
27
|
+
.uix-date-picker__nav { display: flex; align-items: center; justify-content: space-between; gap: var(--uix-space-2); }
|
|
28
|
+
.uix-date-picker__month { font-weight: 600; }
|
|
29
|
+
.uix-date-picker__nav-btn {
|
|
30
|
+
display: inline-flex; align-items: center; justify-content: center; flex: none; width: 28px; height: 28px; padding: 0;
|
|
31
|
+
border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: var(--uix-text-muted); cursor: pointer;
|
|
32
|
+
}
|
|
33
|
+
.uix-date-picker__nav-btn:hover:not([aria-disabled="true"]) { background: var(--uix-bg-hover); color: var(--uix-text); }
|
|
34
|
+
.uix-date-picker__nav-btn:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
35
|
+
.uix-date-picker__nav-btn[aria-disabled="true"] { opacity: .4; cursor: not-allowed; }
|
|
36
|
+
.uix-date-picker__nav-btn:dir(rtl) svg { transform: scaleX(-1); }
|
|
37
|
+
|
|
38
|
+
/* One selected day: the accent circle the range picker gives its two ends. A day that
|
|
39
|
+
cannot be chosen stays focusable (so it can be read out), hence aria-disabled, not :disabled. */
|
|
40
|
+
.uix-date-picker__grid button { cursor: pointer; }
|
|
41
|
+
.uix-date-picker__grid button[data-selected] { color: var(--uix-accent-fg); font-weight: 600; }
|
|
42
|
+
.uix-date-picker__grid button[data-selected]::after,
|
|
43
|
+
.uix-date-picker__grid button[data-selected]:hover:not(:disabled)::after { background: var(--uix-accent); }
|
|
44
|
+
.uix-date-picker__grid button[aria-disabled="true"] { color: var(--uix-text-hushed); text-decoration: line-through; cursor: not-allowed; }
|
|
45
|
+
.uix-date-picker__grid button[aria-disabled="true"]:not([data-selected]):hover:not(:disabled)::after { background: transparent; }
|
|
46
|
+
|
|
47
|
+
/* --- DateTimePicker: the date field, a time input and the zone they are in ---------------- */
|
|
48
|
+
.uix-date-time-picker { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--uix-space-2); min-width: 0; }
|
|
49
|
+
.uix-date-time-picker > .uix-date-picker { flex: 1 1 8.5rem; }
|
|
50
|
+
.uix-date-time-picker__time { flex: 0 1 auto; width: auto; min-width: 6.5rem; font-variant-numeric: tabular-nums; }
|
|
51
|
+
.uix-date-time-picker__zone { display: inline-flex; align-items: center; min-height: var(--uix-control-h); color: var(--uix-text-muted); font-size: var(--uix-text-meta); white-space: nowrap; }
|
|
52
|
+
.uix-date-time-picker--sm .uix-date-time-picker__zone { min-height: 28px; }
|
|
53
|
+
|
|
54
|
+
/* --- DateRangePicker mode="field": a trigger that shows the range ------------------------- */
|
|
55
|
+
.uix-date-range-field { display: grid; width: 100%; min-width: 0; max-width: 18rem; }
|
|
56
|
+
.uix-date-range-field__trigger { display: flex; align-items: center; gap: var(--uix-space-2); min-width: 0; text-align: start; cursor: pointer; }
|
|
57
|
+
.uix-date-range-field__trigger:disabled { cursor: not-allowed; }
|
|
58
|
+
.uix-date-range-field__icon { display: inline-flex; flex: none; color: var(--uix-text-muted); }
|
|
59
|
+
.uix-date-range-field__value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
60
|
+
.uix-date-range-field__value--empty { color: var(--uix-text-muted); }
|
|
61
|
+
.uix-date-range-field__popover { padding: 0; }
|
|
62
|
+
.uix-date-range-picker--in-popover { max-width: min(46rem, calc(100vw - 2 * var(--uix-space-4))); border: 0; background: transparent; }
|
|
63
|
+
.uix-date-range-picker--in-popover .uix-date-range-picker__months:has(> :only-child) { grid-template-columns: minmax(15rem, 1fr); }
|
|
64
|
+
|
|
65
|
+
@media (forced-colors: active) {
|
|
66
|
+
.uix-date-picker__grid button[data-selected] { forced-color-adjust: none; color: HighlightText; }
|
|
67
|
+
.uix-date-picker__grid button[data-selected]::after,
|
|
68
|
+
.uix-date-picker__grid button[data-selected]:hover:not(:disabled)::after { forced-color-adjust: none; background: Highlight; }
|
|
69
|
+
.uix-date-picker__grid button[aria-disabled="true"] { color: GrayText; }
|
|
70
|
+
.uix-date-picker__toggle[aria-expanded="true"] { outline: 1px solid Highlight; }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* uix inline edit — a value that is edited in place (React `InlineEdit`, HAR-1381).
|
|
2
|
+
Depends only on --uix-* tokens. */
|
|
3
|
+
@layer uix.components {
|
|
4
|
+
.uix-inline-edit { display: inline-flex; max-width: 100%; min-width: 0; vertical-align: top; }
|
|
5
|
+
.uix-inline-edit--editing { display: flex; }
|
|
6
|
+
/* A value that cannot be edited is plain text on the same line box as its editable neighbours. */
|
|
7
|
+
.uix-inline-edit--readonly { align-items: center; min-height: 28px; }
|
|
8
|
+
/* The value IS the button: it looks like text until it is hovered or focused. The negative
|
|
9
|
+
inline margin keeps the text on the column it had as plain text. */
|
|
10
|
+
.uix-inline-edit__view {
|
|
11
|
+
display: inline-flex; align-items: center; gap: var(--uix-space-2); max-width: 100%; min-width: 0; min-height: 28px;
|
|
12
|
+
margin: 0 calc(-1 * var(--uix-space-2)); padding: var(--uix-space-1) var(--uix-space-2);
|
|
13
|
+
border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: inherit; font: inherit; text-align: start; cursor: pointer;
|
|
14
|
+
transition: background-color var(--uix-dur-fast) var(--uix-ease-out);
|
|
15
|
+
}
|
|
16
|
+
.uix-inline-edit__view:hover { background: var(--uix-bg-hover); }
|
|
17
|
+
.uix-inline-edit__view:focus-visible { outline: 2px solid var(--uix-ring); outline-offset: 1px; }
|
|
18
|
+
.uix-inline-edit__value { min-width: 0; overflow-wrap: anywhere; }
|
|
19
|
+
.uix-inline-edit__placeholder { color: var(--uix-text-muted); }
|
|
20
|
+
/* 75% at rest keeps the pencil above 3:1 (the same floor as the view menu's quiet controls). */
|
|
21
|
+
.uix-inline-edit__icon { display: inline-flex; flex: none; color: var(--uix-text-muted); opacity: .75; transition: opacity var(--uix-dur-fast) var(--uix-ease-out); }
|
|
22
|
+
.uix-inline-edit__view:is(:hover, :focus-visible) .uix-inline-edit__icon { opacity: 1; }
|
|
23
|
+
.uix-inline-edit__editor { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--uix-space-2); flex: 1; min-width: 0; }
|
|
24
|
+
.uix-inline-edit__control { flex: 1 1 12rem; min-width: 0; }
|
|
25
|
+
.uix-inline-edit__control > * { width: 100%; }
|
|
26
|
+
.uix-inline-edit__actions { display: inline-flex; flex: none; gap: var(--uix-space-2); }
|
|
27
|
+
.uix-inline-edit__error { flex-basis: 100%; }
|
|
28
|
+
}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
.uix-list__item { display: flex; align-items: center; gap: var(--uix-space-3); padding: var(--uix-space-4) var(--uix-space-5); border-bottom: 1px solid var(--uix-border); }
|
|
5
5
|
.uix-list__item:last-child { border-bottom: 0; }
|
|
6
6
|
.uix-list__item:hover { background: var(--uix-bg-hover); }
|
|
7
|
+
/* roving list: the items take focus one at a time; the ring is inset because the list clips */
|
|
8
|
+
.uix-list--roving > .uix-list__item:focus-visible { outline-offset: -2px; }
|
|
9
|
+
.uix-list--roving > .uix-list__item[tabindex] { cursor: default; }
|
|
7
10
|
.uix-list__title { display: block; font-weight: 500; }
|
|
8
11
|
.uix-list__meta { display: block; font-size: var(--uix-text-meta); color: var(--uix-text-muted); }
|
|
9
12
|
.uix-list__trail { margin-left: auto; display: flex; align-items: center; gap: var(--uix-space-2); }
|
|
@@ -18,3 +18,36 @@
|
|
|
18
18
|
.uix-radio > input:focus-visible + .uix-radio__dot { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
|
|
19
19
|
.uix-radio > input:disabled ~ * { opacity: .5; }
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
/* RadioCard (HAR-1373): a native radio inside a label card, for a choice whose options need a
|
|
23
|
+
description or a preview. Selected = a stronger border AND a check mark, never colour alone.
|
|
24
|
+
The group is a grid that drops to one column when a card would be narrower than 12rem. */
|
|
25
|
+
@layer uix.components {
|
|
26
|
+
.uix-radio-group--cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr)); gap: var(--uix-space-3); min-width: 0; margin: 0; padding: 0; border: 0; }
|
|
27
|
+
.uix-radio-group--cards > .uix-radio-group__legend { grid-column: 1 / -1; padding: 0; margin-bottom: var(--uix-space-1); }
|
|
28
|
+
.uix-radio-card {
|
|
29
|
+
position: relative; display: flex; flex-direction: column; gap: var(--uix-space-3); min-width: 0; padding: var(--uix-space-4);
|
|
30
|
+
border: 1px solid var(--uix-border-strong); border-radius: var(--uix-radius-lg); background: var(--uix-surface); color: var(--uix-text); cursor: pointer;
|
|
31
|
+
transition: border-color var(--uix-dur-fast) var(--uix-ease-out), box-shadow var(--uix-dur-fast) var(--uix-ease-out);
|
|
32
|
+
}
|
|
33
|
+
.uix-radio-card > input { position: absolute; opacity: 0; width: 1px; height: 1px; }
|
|
34
|
+
.uix-radio-card:hover { border-color: var(--uix-text-muted); }
|
|
35
|
+
.uix-radio-card:has(> input:checked) { border-color: var(--uix-accent); box-shadow: inset 0 0 0 1px var(--uix-accent); }
|
|
36
|
+
.uix-radio-card:has(> input:focus-visible) { outline: 2px solid var(--uix-ring); outline-offset: 2px; }
|
|
37
|
+
.uix-radio-card:has(> input:disabled) { opacity: .5; cursor: not-allowed; }
|
|
38
|
+
.uix-radio-card__media { display: block; min-width: 0; border-radius: var(--uix-radius-md); overflow: hidden; }
|
|
39
|
+
.uix-radio-card__media > :is(img, svg) { display: block; max-width: 100%; }
|
|
40
|
+
.uix-radio-card__body { display: grid; gap: var(--uix-space-1); min-width: 0; padding-inline-end: var(--uix-space-6); }
|
|
41
|
+
.uix-radio-card__title { font-size: var(--uix-text-body); font-weight: 600; overflow-wrap: anywhere; }
|
|
42
|
+
.uix-radio-card__desc { font-size: var(--uix-text-meta); color: var(--uix-text-hushed); overflow-wrap: anywhere; }
|
|
43
|
+
.uix-radio-card__check {
|
|
44
|
+
position: absolute; inset-block-start: var(--uix-space-3); inset-inline-end: var(--uix-space-3); display: grid; place-items: center;
|
|
45
|
+
width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--uix-border-strong); background: var(--uix-surface); color: transparent;
|
|
46
|
+
}
|
|
47
|
+
.uix-radio-card__check svg { width: 14px; height: 14px; }
|
|
48
|
+
.uix-radio-card > input:checked ~ .uix-radio-card__check { border-color: var(--uix-accent); background: var(--uix-accent); color: var(--uix-accent-fg); }
|
|
49
|
+
@media (forced-colors: active) {
|
|
50
|
+
.uix-radio-card:has(> input:checked) { outline: 2px solid Highlight; outline-offset: -2px; }
|
|
51
|
+
.uix-radio-card > input:checked ~ .uix-radio-card__check { background: Highlight; color: HighlightText; }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -109,6 +109,24 @@
|
|
|
109
109
|
:is(.uix-scheduling-calendar__entry,.uix-scheduling-calendar__swatch)[data-band="high"]:is([data-status="tentative"],[data-status="dead"],[data-status="done"]) { border-color:var(--calendar-band-high-text); }
|
|
110
110
|
.uix-scheduling-calendar__entry[data-band="high"]:focus-visible { outline-color:var(--calendar-band-high-text); }
|
|
111
111
|
|
|
112
|
+
/* emphasis (HAR-1527): an item the consumer brings forward or sets back. Weight and
|
|
113
|
+
quietness only, and it leaves the other channels alone: a highlight is a heavier edge all
|
|
114
|
+
round in the text colour, also round the fill of the high band (a medium band keeps a
|
|
115
|
+
thicker leading edge still), a dimmed item is quieter text. The line style of the state
|
|
116
|
+
stays as it is: on the fill a dashed or dotted edge keeps the colour that shows on the fill.
|
|
117
|
+
A dimmed item of the filled band gives the fill up and keeps the band on its edge. */
|
|
118
|
+
.uix-scheduling-calendar__entry[data-highlight] { border-width:2px; border-color:var(--calendar-chrome-text); font-weight:600; }
|
|
119
|
+
.uix-scheduling-calendar__entry[data-highlight] .uix-scheduling-calendar__title { font-weight:600; }
|
|
120
|
+
.uix-scheduling-calendar__entry[data-band="medium"][data-highlight] { border-left-width:5px; }
|
|
121
|
+
.uix-scheduling-calendar__entry[data-dim] { color:var(--calendar-chrome-text-quiet); }
|
|
122
|
+
.uix-scheduling-calendar__entry[data-dim] .uix-scheduling-calendar__title { font-weight:400; }
|
|
123
|
+
.uix-scheduling-calendar__entry[data-band="high"][data-dim] { border-color:var(--calendar-band-high-fill); border-left-width:4px; background:var(--calendar-chrome-surface); color:var(--calendar-chrome-text-quiet); }
|
|
124
|
+
/* in the agendas the heavier edge is the leading one, drawn inside the row so nothing in it moves */
|
|
125
|
+
:is(.uix-scheduling-calendar__agenda-row,.uix-scheduling-calendar__agenda > ol > li > button)[data-highlight] { box-shadow:inset 3px 0 0 var(--calendar-chrome-text); font-weight:600; }
|
|
126
|
+
.uix-scheduling-calendar__agenda-row[data-highlight] .uix-scheduling-calendar__title { font-weight:600; }
|
|
127
|
+
:is(.uix-scheduling-calendar__agenda-row,.uix-scheduling-calendar__agenda > ol > li > button)[data-dim] { color:var(--calendar-chrome-text-quiet); }
|
|
128
|
+
.uix-scheduling-calendar__agenda-row[data-dim] .uix-scheduling-calendar__title,.uix-scheduling-calendar__agenda > ol > li > button[data-dim] strong { font-weight:400; }
|
|
129
|
+
|
|
112
130
|
/* spans and windows: placed in the week row by the layout, one lane each */
|
|
113
131
|
.uix-scheduling-calendar__span,.uix-scheduling-calendar__window,.uix-scheduling-calendar__rowmore { grid-row:1; align-self:start; height:calc(var(--calendar-lane) - var(--calendar-gap)); margin-top:calc(var(--calendar-pad) + var(--calendar-head) + var(--uix-scheduling-calendar-lane,0) * var(--calendar-lane)); margin-inline:var(--calendar-pad-inline); }
|
|
114
132
|
:is(.uix-scheduling-calendar__span,.uix-scheduling-calendar__window)[data-continues-before] { margin-left:0; border-left-style:dashed; border-top-left-radius:0; border-bottom-left-radius:0; }
|
|
@@ -1,51 +1,232 @@
|
|
|
1
|
-
/* uix scheduling timeline (React `SchedulingTimeline`, HAR-1364) — lanes of bars on a
|
|
2
|
-
axis. Depends only on --uix-* tokens. The lane label column is sticky; the axis scrolls.
|
|
1
|
+
/* uix scheduling timeline (React `SchedulingTimeline`, HAR-1364, HAR-1521) — lanes of bars on a
|
|
2
|
+
time axis. Depends only on --uix-* tokens. The lane label column is sticky; the axis scrolls.
|
|
3
|
+
|
|
4
|
+
COLOUR MAP. Every colour the timeline paints is named once in the first rule and read from
|
|
5
|
+
there; no other rule names a --uix-* colour. One name has one meaning:
|
|
6
|
+
--timeline-band-* the band, the only dimension that takes a hue (only `high` is filled)
|
|
7
|
+
--timeline-status-* the state channel of a bar: line style, weight and dimming, never a hue
|
|
8
|
+
--timeline-window-* windows: neutral, told apart by pattern and visible label
|
|
9
|
+
--timeline-marker-* markers: a shape or icon with text on a bar, a dashed line on the axis
|
|
10
|
+
--timeline-chrome-* rows, lines, the axis, the now-line, the move outline and controls
|
|
11
|
+
To retune a colour, change its line in the map (HAR-1521; packages/tokens/tests/scheduling-timeline-colours.test.mjs). */
|
|
3
12
|
@layer uix.components {
|
|
4
|
-
.uix-scheduling-timeline {
|
|
5
|
-
|
|
13
|
+
.uix-scheduling-timeline {
|
|
14
|
+
--timeline-chrome-text: var(--uix-text);
|
|
15
|
+
--timeline-chrome-text-quiet: var(--uix-text-muted);
|
|
16
|
+
--timeline-chrome-surface: var(--uix-surface);
|
|
17
|
+
--timeline-chrome-surface-quiet: var(--uix-bg-subtle);
|
|
18
|
+
--timeline-chrome-line: var(--uix-border);
|
|
19
|
+
--timeline-chrome-line-strong: var(--uix-border-strong);
|
|
20
|
+
--timeline-chrome-now: var(--uix-text);
|
|
21
|
+
--timeline-chrome-now-text: var(--uix-surface);
|
|
22
|
+
--timeline-chrome-ghost: var(--uix-text);
|
|
23
|
+
--timeline-chrome-ghost-halo: var(--uix-surface);
|
|
24
|
+
--timeline-status-surface: var(--uix-surface);
|
|
25
|
+
--timeline-status-surface-quiet: var(--uix-bg-subtle);
|
|
26
|
+
--timeline-status-edge: var(--uix-neutral-border);
|
|
27
|
+
--timeline-status-edge-strong: var(--uix-neutral-solid);
|
|
28
|
+
--timeline-status-text: var(--uix-text);
|
|
29
|
+
--timeline-status-text-dim: var(--uix-text-muted);
|
|
30
|
+
--timeline-band-medium-edge: var(--uix-neutral-solid);
|
|
31
|
+
--timeline-band-high-fill: var(--uix-danger);
|
|
32
|
+
--timeline-band-high-text: var(--uix-danger-fg);
|
|
33
|
+
--timeline-window-surface: var(--uix-neutral-bg);
|
|
34
|
+
--timeline-window-ink: var(--uix-neutral-solid);
|
|
35
|
+
--timeline-window-edge: var(--uix-neutral-border);
|
|
36
|
+
--timeline-window-text: var(--uix-text);
|
|
37
|
+
--timeline-marker-ink: var(--uix-text);
|
|
38
|
+
--timeline-marker-line: var(--uix-neutral-solid);
|
|
39
|
+
|
|
40
|
+
/* geometry: a lane is its sub-rows plus its padding (the rule under it is inside that); a group
|
|
41
|
+
head is one line. The component counts row positions in these three lengths (scheduling-timeline-rows.ts). */
|
|
42
|
+
--timeline-bar-row: 2.25rem;
|
|
43
|
+
--timeline-bar: 2rem;
|
|
44
|
+
--timeline-bar-min: 1.5rem; /* the component measures this (the element .…__bar-min) and packs bars as at least this wide */
|
|
45
|
+
--timeline-lane-pad: calc(2 * var(--uix-space-2));
|
|
46
|
+
--timeline-head: 2.5rem;
|
|
47
|
+
--timeline-axis: 2.25rem;
|
|
48
|
+
--timeline-axis-height: var(--timeline-axis);
|
|
49
|
+
--timeline-gap: 0.125rem;
|
|
50
|
+
|
|
51
|
+
display: grid; gap: var(--uix-space-3); color: var(--timeline-chrome-text); min-width: 0;
|
|
52
|
+
}
|
|
53
|
+
.uix-scheduling-timeline__notice { padding: var(--uix-space-2) var(--uix-space-3); border: 1px solid var(--timeline-chrome-line); border-radius: var(--uix-radius-sm); background: var(--timeline-chrome-surface-quiet); font-size: var(--uix-text-meta); }
|
|
54
|
+
.uix-scheduling-timeline__notice > :first-child { margin-top: 0; }
|
|
55
|
+
.uix-scheduling-timeline__notice > :last-child { margin-bottom: 0; }
|
|
56
|
+
.uix-scheduling-timeline__scroller { position: relative; overflow-x: auto; border: 1px solid var(--timeline-chrome-line); border-radius: var(--uix-radius-md); background: var(--timeline-chrome-surface); }
|
|
57
|
+
/* with `maxHeight` the lanes scroll inside the timeline, under the axis */
|
|
58
|
+
.uix-scheduling-timeline__scroller[data-scroll-y] { overflow-y: auto; }
|
|
6
59
|
.uix-scheduling-timeline__grid { display: grid; width: max-content; min-width: 100%; }
|
|
7
|
-
.uix-scheduling-
|
|
60
|
+
.uix-scheduling-timeline__grid[data-sub-ticks] { --timeline-axis-height: calc(var(--timeline-axis) + 1rem); }
|
|
61
|
+
.uix-scheduling-timeline__row { display: flex; border-bottom: 1px solid var(--timeline-chrome-line); }
|
|
8
62
|
.uix-scheduling-timeline__row:last-child { border-bottom: 0; }
|
|
9
63
|
.uix-scheduling-timeline__lane-label {
|
|
10
64
|
position: sticky; left: 0; z-index: var(--uix-z-sticky); flex: 0 0 clamp(7.5rem, 30vw, 12rem); display: grid; align-content: center; gap: var(--uix-space-1);
|
|
11
|
-
padding: var(--uix-space-2) var(--uix-space-3); border-right: 1px solid var(--
|
|
65
|
+
padding: var(--uix-space-2) var(--uix-space-3); border-right: 1px solid var(--timeline-chrome-line); background: var(--timeline-chrome-surface); min-width: 0;
|
|
12
66
|
}
|
|
13
|
-
.uix-scheduling-timeline__row--axis .uix-scheduling-timeline__lane-label { background: var(--uix-bg-subtle); color: var(--uix-text-muted); font-size: var(--uix-text-meta); font-weight: 600; }
|
|
14
67
|
.uix-scheduling-timeline__lane-name { font-weight: 500; overflow-wrap: break-word; hyphens: auto; }
|
|
15
|
-
.uix-scheduling-timeline__lane-meta { font-size: var(--uix-text-meta); color: var(--
|
|
16
|
-
.uix-scheduling-timeline__track { position: relative; flex: 1 0 auto; min-height: calc(var(--uix-timeline-rows, 1) *
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.uix-scheduling-
|
|
20
|
-
.uix-scheduling-
|
|
21
|
-
.uix-scheduling-
|
|
22
|
-
.uix-scheduling-
|
|
23
|
-
.uix-scheduling-
|
|
24
|
-
.uix-scheduling-
|
|
25
|
-
.uix-scheduling-
|
|
26
|
-
|
|
27
|
-
.uix-scheduling-
|
|
28
|
-
|
|
29
|
-
.uix-scheduling-
|
|
30
|
-
.uix-scheduling-
|
|
68
|
+
.uix-scheduling-timeline__lane-meta { font-size: var(--uix-text-meta); color: var(--timeline-chrome-text-quiet); }
|
|
69
|
+
.uix-scheduling-timeline__track { position: relative; flex: 1 0 auto; min-height: calc(var(--uix-timeline-rows, 1) * var(--timeline-bar-row) + 2 * var(--uix-space-2)); }
|
|
70
|
+
|
|
71
|
+
/* the axis: it stays at the top of a timeline that scrolls inside itself */
|
|
72
|
+
.uix-scheduling-timeline__row--axis { position: sticky; top: 0; z-index: var(--uix-z-sticky); }
|
|
73
|
+
.uix-scheduling-timeline__row--axis .uix-scheduling-timeline__lane-label { background: var(--timeline-chrome-surface-quiet); color: var(--timeline-chrome-text-quiet); font-size: var(--uix-text-meta); font-weight: 600; }
|
|
74
|
+
.uix-scheduling-timeline__axis { min-height: var(--timeline-axis-height); background: var(--timeline-chrome-surface-quiet); }
|
|
75
|
+
.uix-scheduling-timeline__tick { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--timeline-chrome-line); }
|
|
76
|
+
.uix-scheduling-timeline__tick[data-major] { border-left-color: var(--timeline-chrome-line-strong); }
|
|
77
|
+
.uix-scheduling-timeline__tick-label { position: absolute; top: 50%; left: var(--uix-space-1); transform: translateY(-50%); white-space: nowrap; font-size: var(--uix-text-meta); font-variant-numeric: tabular-nums; color: var(--timeline-chrome-text-quiet); }
|
|
78
|
+
.uix-scheduling-timeline__tick[data-major] .uix-scheduling-timeline__tick-label { color: var(--timeline-chrome-text); font-weight: 600; }
|
|
79
|
+
/* a repeated hour says which one it is: its UTC offset, under the hour */
|
|
80
|
+
.uix-scheduling-timeline__tick-offset { display: block; font-size: var(--uix-text-eyebrow); font-weight: 400; line-height: 1; }
|
|
81
|
+
/* sub-ticks: the day labels take the upper line of a taller axis, the hours the lower one */
|
|
82
|
+
.uix-scheduling-timeline__grid[data-sub-ticks] .uix-scheduling-timeline__tick:not([data-minor]) > .uix-scheduling-timeline__tick-label { top: var(--uix-space-1); transform: none; }
|
|
83
|
+
.uix-scheduling-timeline__tick[data-minor] { top: auto; height: 1.25rem; }
|
|
84
|
+
.uix-scheduling-timeline__tick[data-minor] > .uix-scheduling-timeline__tick-label { top: auto; bottom: var(--timeline-gap); transform: none; font-size: var(--uix-text-eyebrow); }
|
|
85
|
+
|
|
86
|
+
/* notes under the axis, one for the column of a day */
|
|
87
|
+
.uix-scheduling-timeline__row--notes { background: var(--timeline-chrome-surface-quiet); }
|
|
88
|
+
.uix-scheduling-timeline__row--notes > .uix-scheduling-timeline__lane-label { background: var(--timeline-chrome-surface-quiet); padding-block: 0; }
|
|
89
|
+
.uix-scheduling-timeline__row--notes > .uix-scheduling-timeline__track { min-height: 1.75rem; }
|
|
90
|
+
.uix-scheduling-timeline__column-note { position: absolute; top: 0; bottom: 0; display: flex; align-items: center; min-width: 0; padding: 0 var(--uix-space-1); border-left: 1px solid var(--timeline-chrome-line); overflow: hidden; white-space: nowrap; font-size: var(--uix-text-meta); color: var(--timeline-chrome-text); }
|
|
91
|
+
|
|
92
|
+
/* the body: the rows, and under them one layer that holds each grid line, window, marker and
|
|
93
|
+
the now-line once. `isolation` keeps the sticky lane labels under the sticky axis. */
|
|
94
|
+
.uix-scheduling-timeline__body { position: relative; isolation: isolate; }
|
|
95
|
+
.uix-scheduling-timeline__layer { position: absolute; inset: 0; display: flex; pointer-events: none; }
|
|
96
|
+
/* the layer repeats the lane column, unseen, so its track is exactly as wide as the tracks of the rows */
|
|
97
|
+
.uix-scheduling-timeline__layer > .uix-scheduling-timeline__lane-label { position: static; visibility: hidden; }
|
|
98
|
+
.uix-scheduling-timeline__layer > .uix-scheduling-timeline__track { min-height: 0; }
|
|
99
|
+
/* a click on the empty part of a lane reaches the window drawn under it */
|
|
100
|
+
.uix-scheduling-timeline__body > .uix-scheduling-timeline__row > .uix-scheduling-timeline__track { pointer-events: none; }
|
|
101
|
+
.uix-scheduling-timeline__spacer { flex: none; }
|
|
102
|
+
.uix-scheduling-timeline__gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--timeline-chrome-line); }
|
|
103
|
+
.uix-scheduling-timeline__gridline[data-major] { background: var(--timeline-chrome-line-strong); }
|
|
104
|
+
.uix-scheduling-timeline__gridline[data-minor] { background: repeating-linear-gradient(to bottom, var(--timeline-chrome-line) 0 2px, transparent 2px 6px); }
|
|
105
|
+
|
|
106
|
+
/* Fixed rows. With groups, a window or marker limited to lanes, or a virtual window, every row
|
|
107
|
+
has the height the component counts with: a lane is its sub-rows, a head is one line, and a
|
|
108
|
+
label that does not fit is cut. A plain timeline keeps rows that grow with their label. */
|
|
109
|
+
.uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body > .uix-scheduling-timeline__row { height: calc(var(--uix-timeline-rows, 1) * var(--timeline-bar-row) + var(--timeline-lane-pad)); }
|
|
110
|
+
.uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body .uix-scheduling-timeline__lane-label { padding-block: 0; overflow: hidden; }
|
|
111
|
+
.uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body .uix-scheduling-timeline__track { min-height: 0; }
|
|
112
|
+
.uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body :is(.uix-scheduling-timeline__lane-name, .uix-scheduling-timeline__lane-meta) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
113
|
+
|
|
114
|
+
/* a group: one row. Its head band is lifted over the layer and opaque, so nothing shows through
|
|
115
|
+
the name. Collapsed, that row is all there is of the group; when a window covers a lane it
|
|
116
|
+
hides, the row has a strip under the head, one sub-row tall and not lifted, where the window
|
|
117
|
+
(and the grid lines) are drawn with nothing over them. */
|
|
118
|
+
.uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body > .uix-scheduling-timeline__row--group { flex-direction: column; height: var(--timeline-head); background: var(--timeline-chrome-surface-quiet); }
|
|
119
|
+
.uix-scheduling-timeline[data-fixed] .uix-scheduling-timeline__body > .uix-scheduling-timeline__row--group[data-strip] { height: calc(var(--timeline-head) + var(--timeline-bar-row)); }
|
|
120
|
+
.uix-scheduling-timeline__group-head { position: relative; flex: 1 1 var(--timeline-head); display: flex; min-height: 0; background: var(--timeline-chrome-surface-quiet); }
|
|
121
|
+
.uix-scheduling-timeline__group-strip { flex: 0 0 calc(var(--timeline-bar-row) - 1px); }
|
|
122
|
+
.uix-scheduling-timeline__group-label { position: sticky; left: 0; z-index: var(--uix-z-sticky); display: flex; align-items: center; gap: var(--uix-space-3); min-width: 0; padding: 0 var(--uix-space-2); background: var(--timeline-chrome-surface-quiet); white-space: nowrap; }
|
|
123
|
+
.uix-scheduling-timeline__group-toggle { display: inline-flex; align-items: center; gap: var(--uix-space-2); min-height: 1.75rem; padding: 0 var(--uix-space-2); border: 0; border-radius: var(--uix-radius-sm); background: transparent; color: inherit; font: inherit; font-weight: 600; cursor: pointer; }
|
|
124
|
+
.uix-scheduling-timeline__group-toggle:hover { background: var(--timeline-chrome-surface); }
|
|
125
|
+
/* the chevron is two borders, so it survives forced colours; it points down while the group is open */
|
|
126
|
+
.uix-scheduling-timeline__group-chevron { flex: none; width: 0.5rem; height: 0.5rem; margin-top: -0.1875rem; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); }
|
|
127
|
+
.uix-scheduling-timeline__group-toggle[aria-expanded="false"] > .uix-scheduling-timeline__group-chevron { margin-top: 0; transform: rotate(-45deg); }
|
|
128
|
+
.uix-scheduling-timeline__group-name { overflow: hidden; text-overflow: ellipsis; }
|
|
129
|
+
.uix-scheduling-timeline__group-meta { color: var(--timeline-chrome-text-quiet); font-size: var(--uix-text-meta); }
|
|
130
|
+
.uix-scheduling-timeline__group-summary { display: inline-flex; align-items: center; gap: var(--uix-space-2); font-size: var(--uix-text-meta); }
|
|
131
|
+
.uix-scheduling-timeline__group-count { font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
132
|
+
|
|
133
|
+
/* windows: one element each, neutral, told apart by pattern and words */
|
|
134
|
+
.uix-scheduling-timeline__overlay { position: absolute; top: 0; bottom: 0; display: block; min-width: 0; margin: 0; padding: 0; border: 0; border-inline: 1px solid var(--timeline-window-edge); border-radius: 0; background-color: var(--timeline-window-surface); color: var(--timeline-window-text); font: inherit; text-align: start; }
|
|
135
|
+
button.uix-scheduling-timeline__overlay { pointer-events: auto; cursor: pointer; }
|
|
136
|
+
/* the ring is drawn inside: a clip would cut an outline that sits outside the box */
|
|
137
|
+
button.uix-scheduling-timeline__overlay:focus-visible { outline-offset: -2px; }
|
|
138
|
+
/* limited to its lanes: the clip is the rows of those lanes, written by the component in row units */
|
|
139
|
+
:is(.uix-scheduling-timeline__overlay, .uix-scheduling-timeline__marker)[data-scope="lanes"] { clip-path: var(--uix-timeline-clip, none); }
|
|
140
|
+
.uix-scheduling-timeline__overlay[data-pattern="diagonal"] { background-image: repeating-linear-gradient(45deg, var(--timeline-window-ink) 0 1px, transparent 1px 6px); }
|
|
141
|
+
.uix-scheduling-timeline__overlay[data-pattern="cross"] { background-image: repeating-linear-gradient(45deg, var(--timeline-window-ink) 0 1px, transparent 1px 6px), repeating-linear-gradient(-45deg, var(--timeline-window-ink) 0 1px, transparent 1px 6px); }
|
|
142
|
+
.uix-scheduling-timeline__overlay[data-pattern="dotted"] { background-image: radial-gradient(circle, var(--timeline-window-ink) 1px, transparent 1.5px); background-size: 5px 5px; }
|
|
143
|
+
/* the words sit on their own patch of the window colour, so the hatch never runs under them.
|
|
144
|
+
The component puts them at the top of a lane (--uix-timeline-label-top, in row units): the
|
|
145
|
+
first lane of each run of rows the window covers. */
|
|
146
|
+
.uix-scheduling-timeline__overlay-label { position: absolute; top: calc(var(--uix-timeline-label-top, 0%) + var(--timeline-gap)); left: var(--timeline-gap); display: flex; gap: var(--uix-space-1); max-width: calc(100% - 2 * var(--timeline-gap)); padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); background: var(--timeline-window-surface); overflow: hidden; white-space: nowrap; font-size: var(--uix-text-meta); line-height: 1.25rem; }
|
|
147
|
+
.uix-scheduling-timeline__overlay-kind { flex: none; font-weight: 600; }
|
|
148
|
+
.uix-scheduling-timeline__overlay-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
149
|
+
.uix-scheduling-timeline__overlay-scope { flex: 0 2 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-style: italic; }
|
|
150
|
+
|
|
151
|
+
/* now: one neutral line with its word; a point marker: one dashed line */
|
|
152
|
+
.uix-scheduling-timeline__now { position: absolute; top: 0; bottom: 0; z-index: var(--uix-z-raised); width: 2px; transform: translateX(-50%); background: var(--timeline-chrome-now); }
|
|
153
|
+
.uix-scheduling-timeline__now-label { position: sticky; top: var(--timeline-axis-height); display: block; width: max-content; margin-left: var(--uix-space-1); padding: 0 var(--uix-space-1); border-radius: var(--uix-radius-xs); background: var(--timeline-chrome-now); color: var(--timeline-chrome-now-text); font-size: var(--uix-text-eyebrow); line-height: 1.5; white-space: nowrap; }
|
|
154
|
+
.uix-scheduling-timeline__marker { position: absolute; top: 0; bottom: 0; z-index: var(--uix-z-raised); border-left: 2px dashed var(--timeline-marker-line); }
|
|
155
|
+
|
|
156
|
+
/* bars */
|
|
31
157
|
.uix-scheduling-timeline__items { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
|
|
32
|
-
|
|
158
|
+
/* a bar is never a smaller target than --timeline-bar-min (24px) on either side. The component
|
|
159
|
+
measures that length and packs bars as at least that wide, so two drawn at it never cover each other. */
|
|
160
|
+
.uix-scheduling-timeline__slot { position: absolute; top: calc(var(--uix-space-2) + var(--uix-timeline-row, 0) * var(--timeline-bar-row)); height: var(--timeline-bar); min-width: var(--timeline-bar-min); pointer-events: auto; }
|
|
161
|
+
/* the default look is the `committed` status with no band: a plain outlined bar */
|
|
33
162
|
.uix-scheduling-timeline__item {
|
|
34
|
-
display: flex; align-items: center; gap: var(--uix-space-
|
|
35
|
-
border:
|
|
36
|
-
font: inherit; font-size: var(--uix-text-meta);
|
|
163
|
+
position: relative; display: flex; align-items: center; gap: var(--uix-space-1); width: 100%; height: 100%; padding: 0 var(--uix-space-2); overflow: hidden;
|
|
164
|
+
border: 1px solid var(--timeline-status-edge); border-radius: var(--uix-radius-sm); background: var(--timeline-status-surface); color: var(--timeline-status-text);
|
|
165
|
+
font: inherit; font-size: var(--uix-text-meta); line-height: 1.25rem; text-align: start; cursor: pointer;
|
|
37
166
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.uix-scheduling-timeline__item
|
|
42
|
-
|
|
43
|
-
.uix-scheduling-timeline__item
|
|
167
|
+
/* the text wraps and only its first line shows: a long label is cut at a word, never in one.
|
|
168
|
+
The empty box in front is a place to break before the first word, so a first word that does
|
|
169
|
+
not fit goes to the hidden second line too: no part of a word (or of a time) is ever shown. */
|
|
170
|
+
.uix-scheduling-timeline__item-title { flex: 1 1 auto; min-width: 0; max-height: 1.25rem; overflow: hidden; font-weight: 600; }
|
|
171
|
+
.uix-scheduling-timeline__item-title::before { content: ""; display: inline-block; }
|
|
172
|
+
.uix-scheduling-timeline__item-meta { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
173
|
+
.uix-scheduling-timeline__item-markers { flex: none; display: inline-flex; align-items: center; gap: var(--timeline-gap); }
|
|
174
|
+
|
|
175
|
+
/* status: line style, weight and dimming only */
|
|
176
|
+
.uix-scheduling-timeline__item[data-status="tentative"] { border-style: dashed; border-color: var(--timeline-status-edge-strong); }
|
|
177
|
+
.uix-scheduling-timeline__item[data-status="live"] { border-color: var(--timeline-status-edge-strong); }
|
|
178
|
+
/* the live dot is a border, so it survives forced colours */
|
|
179
|
+
.uix-scheduling-timeline__item[data-status="live"]::before { content: ""; flex: none; border: 0.1875rem solid currentColor; border-radius: 50%; }
|
|
180
|
+
.uix-scheduling-timeline__item[data-status="done"] { border-style: none none solid; background: var(--timeline-status-surface-quiet); color: var(--timeline-status-text-dim); }
|
|
181
|
+
.uix-scheduling-timeline__item[data-status="dead"] { border-style: dotted; border-color: var(--timeline-status-edge-strong); color: var(--timeline-status-text-dim); }
|
|
182
|
+
.uix-scheduling-timeline__item[data-status="dead"] > .uix-scheduling-timeline__item-title { text-decoration: line-through; }
|
|
183
|
+
|
|
184
|
+
/* band: `medium` is a heavier leading edge, `high` is the one fill */
|
|
185
|
+
.uix-scheduling-timeline__item[data-band="medium"] { border-left: 3px solid var(--timeline-band-medium-edge); }
|
|
186
|
+
.uix-scheduling-timeline__item[data-band="high"] { border-color: var(--timeline-band-high-fill); background: var(--timeline-band-high-fill); color: var(--timeline-band-high-text); }
|
|
187
|
+
.uix-scheduling-timeline__item[data-band="high"]:is([data-status="tentative"], [data-status="dead"]) { border-color: var(--timeline-band-high-text); }
|
|
188
|
+
/* done keeps the whole fill on the high band (dimming it would cost the text its contrast). Its
|
|
189
|
+
cue is a line in the text colour inside the fill, near the bottom: on the edge it would be the
|
|
190
|
+
colour of the row and the bar would only look shorter. */
|
|
191
|
+
.uix-scheduling-timeline__item[data-band="high"][data-status="done"] { border-style: solid; background-image: linear-gradient(to top, transparent 0 3px, var(--timeline-band-high-text) 3px 5px, transparent 5px); }
|
|
192
|
+
|
|
193
|
+
/* shares time with another bar of its lane (only while `flagOverlaps` is on): hatched in its own text colour */
|
|
194
|
+
.uix-scheduling-timeline__item[data-conflict]::after { content: ""; position: absolute; inset: 0; background-image: repeating-linear-gradient(135deg, transparent 0 6px, currentColor 6px 9px); opacity: 0.2; pointer-events: none; }
|
|
44
195
|
.uix-scheduling-timeline__item[data-clipped-start] { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-style: dashed; }
|
|
45
|
-
.uix-scheduling-timeline__item[data-clipped-end] { border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
|
196
|
+
.uix-scheduling-timeline__item[data-clipped-end] { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-style: dashed; }
|
|
46
197
|
.uix-scheduling-timeline__item[data-movable] { cursor: grab; touch-action: none; }
|
|
47
|
-
.uix-scheduling-timeline__item[data-dragging] { cursor: grabbing; box-shadow: var(--uix-shadow-popover);
|
|
48
|
-
.uix-scheduling-timeline__item:focus-visible {
|
|
49
|
-
|
|
50
|
-
|
|
198
|
+
.uix-scheduling-timeline__item[data-dragging] { cursor: grabbing; box-shadow: var(--uix-shadow-popover); z-index: var(--uix-z-raised); }
|
|
199
|
+
.uix-scheduling-timeline__item:focus-visible { z-index: var(--uix-z-raised); }
|
|
200
|
+
|
|
201
|
+
/* markers on a bar: a shape (or the icon of the consumer) with text; the text is in the accessible name */
|
|
202
|
+
.uix-scheduling-timeline__item-marker { flex: none; display: inline-flex; align-items: center; color: var(--timeline-marker-ink); }
|
|
203
|
+
.uix-scheduling-timeline__item .uix-scheduling-timeline__item-marker { color: inherit; }
|
|
204
|
+
.uix-scheduling-timeline__item-marker-icon { display: inline-flex; width: 0.6875rem; height: 0.6875rem; }
|
|
205
|
+
.uix-scheduling-timeline__item-marker-icon > svg { width: 100%; height: 100%; }
|
|
206
|
+
.uix-scheduling-timeline__item-marker-label { margin-left: var(--uix-space-1); }
|
|
207
|
+
.uix-scheduling-timeline__item-marker[data-glyph]::before { content: ""; width: 0.625rem; height: 0.625rem; background: currentColor; }
|
|
208
|
+
.uix-scheduling-timeline__item-marker[data-glyph="neutral"]::before { border-radius: 50%; }
|
|
209
|
+
.uix-scheduling-timeline__item-marker[data-glyph="warning"]::before { clip-path: polygon(50% 0, 100% 100%, 0 100%); }
|
|
210
|
+
.uix-scheduling-timeline__item-marker[data-glyph="refused"]::before { clip-path: polygon(20% 0, 50% 30%, 80% 0, 100% 20%, 70% 50%, 100% 80%, 80% 100%, 50% 70%, 20% 100%, 0 80%, 30% 50%, 0 20%); }
|
|
211
|
+
|
|
212
|
+
/* a pending move: an outline where the bar would go; the bar itself stays put. Only an edge, so
|
|
213
|
+
what is under it stays readable, and in two tones: the dashes in the text colour, with a line
|
|
214
|
+
of the surface colour on each side. On the row the dashes carry it (3:1 and more); over a bar,
|
|
215
|
+
the high band included, whichever tone the fill is not close to does. It is lifted as far as
|
|
216
|
+
a focused bar is, and comes after the bars, so the bar being moved does not hide it. */
|
|
217
|
+
.uix-scheduling-timeline__ghost { position: absolute; top: calc(var(--uix-space-2) + var(--uix-timeline-row, 0) * var(--timeline-bar-row)); height: var(--timeline-bar); min-width: var(--timeline-bar-min); border: 2px dashed var(--timeline-chrome-ghost); border-radius: var(--uix-radius-sm); pointer-events: none; z-index: var(--uix-z-raised); }
|
|
218
|
+
.uix-scheduling-timeline__ghost::before, .uix-scheduling-timeline__ghost::after { content: ""; position: absolute; inset: 0; border: 2px solid var(--timeline-chrome-ghost-halo); border-radius: inherit; }
|
|
219
|
+
.uix-scheduling-timeline__ghost::before { inset: -4px; }
|
|
220
|
+
.uix-scheduling-timeline__ghost[data-sent] { border-style: dotted; }
|
|
221
|
+
|
|
222
|
+
/* the narrowest bar, measured by the component (never drawn) */
|
|
223
|
+
.uix-scheduling-timeline__bar-min { position: absolute; top: 0; left: 0; width: var(--timeline-bar-min); height: 0; visibility: hidden; pointer-events: none; }
|
|
224
|
+
/* the three row units, measured by a virtual timeline to turn a scroll position into rows */
|
|
225
|
+
.uix-scheduling-timeline__probe { position: absolute; top: 0; left: 0; width: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; }
|
|
226
|
+
.uix-scheduling-timeline__probe > span { display: block; height: var(--timeline-bar-row); }
|
|
227
|
+
.uix-scheduling-timeline__probe > span:nth-child(2) { height: var(--timeline-lane-pad); }
|
|
228
|
+
.uix-scheduling-timeline__probe > span:nth-child(3) { height: var(--timeline-head); }
|
|
229
|
+
|
|
230
|
+
.uix-scheduling-timeline__empty { margin: 0; color: var(--timeline-chrome-text-quiet); font-size: var(--uix-text-meta); }
|
|
231
|
+
.uix-scheduling-timeline__state { padding: var(--uix-space-8); border: 1px dashed var(--timeline-chrome-line-strong); border-radius: var(--uix-radius-lg); text-align: center; }
|
|
51
232
|
}
|
|
@@ -92,6 +92,8 @@
|
|
|
92
92
|
}
|
|
93
93
|
.uix-search-suggest__footer:first-child { margin-top: 0; border-top: 0; border-radius: var(--uix-radius-sm); }
|
|
94
94
|
.uix-search-suggest__footer[data-active] { background: var(--uix-bg-hover); }
|
|
95
|
+
/* a line under the options that is not an option (HAR-1648): "More matches. Keep typing…" */
|
|
96
|
+
.uix-search-suggest__note { padding: var(--uix-space-2) var(--uix-space-3); border-top: 1px solid var(--uix-border); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
|
|
95
97
|
|
|
96
98
|
.uix-search-suggest__state {
|
|
97
99
|
display: flex; align-items: center; justify-content: center; gap: var(--uix-space-2); flex-wrap: wrap;
|
|
@@ -27,3 +27,11 @@
|
|
|
27
27
|
.uix-tree__item[aria-expanded="false"] > .uix-tree__row .uix-tree__toggle { transform: rotate(-90deg); }
|
|
28
28
|
.uix-tree__item[aria-expanded="false"] > .uix-tree__group { display: none; }
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
/* Children loaded on expand (HAR-1382): the loading row and the failed row under a node. Both
|
|
32
|
+
are ordinary tree rows, so the arrow keys reach them; the failed row is the Retry control. */
|
|
33
|
+
@layer uix.components {
|
|
34
|
+
.uix-tree__status { display: inline-flex; align-items: center; gap: var(--uix-space-2); color: var(--uix-text-muted); font-size: var(--uix-text-meta); }
|
|
35
|
+
.uix-tree__status--error { color: var(--uix-danger-text); }
|
|
36
|
+
.uix-tree__retry { color: var(--uix-link); text-decoration: underline; text-underline-offset: .15em; }
|
|
37
|
+
}
|