@rt-tools/ui-kit 0.4.0 → 0.5.1
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 +1 -1
- package/fesm2022/rt-tools-ui-kit.mjs +3500 -3105
- package/fesm2022/rt-tools-ui-kit.mjs.map +1 -1
- package/package.json +7 -8
- package/rt-tools-ui-kit-0.5.1.tgz +0 -0
- package/src/lib/ui-kit/action-bar/components/container/rtui-action-bar-container.component.scss +47 -0
- package/src/lib/ui-kit/action-bar/stories/component/test-action-bar.component.scss +15 -0
- package/src/lib/ui-kit/aside/components/container/aside-container.component.scss +4 -1
- package/src/lib/ui-kit/aside/components/panel/aside-panel.component.scss +24 -0
- package/src/lib/ui-kit/popover/stories/component/test-popover.component.scss +32 -0
- package/src/lib/ui-kit/scrollable/scrollable-container.component.scss +57 -0
- package/src/lib/ui-kit/scrollable/stories/component/test-scrollable.component.scss +17 -0
- package/src/lib/ui-kit/side-menu/menu/rtui-side-menu.component.scss +219 -4
- package/src/lib/ui-kit/side-menu/menu-sub-item/rtui-side-menu-sub-item.component.scss +45 -4
- package/src/lib/ui-kit/snack-bar/snack-bar.component.scss +20 -0
- package/src/lib/ui-kit/toolbar/stories/component/test-toolbar.component.scss +22 -0
- package/src/styles/base/_tokens.scss +5 -0
- package/src/styles/color-scheme.spec.ts +4 -1
- package/src/styles/components/_rtui_button.scss +13 -547
- package/src/styles/components/button/_base.scss +64 -0
- package/src/styles/components/button/_map.scss +184 -0
- package/src/styles/components/button/_palettes.scss +218 -0
- package/src/styles/components/button/_sizes.scss +29 -0
- package/src/styles/components/button/_text.scss +68 -0
- package/styles/tokens.css +2 -0
- package/types/rt-tools-ui-kit.d.ts +585 -449
- package/rt-tools-ui-kit-0.4.0.tgz +0 -0
package/package.json
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rt-tools/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Yauheni Krumin",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"tslib": "^2.8.0",
|
|
9
|
-
"@rt-tools/core": "^0.
|
|
10
|
-
"@rt-tools/store": "^0.3.
|
|
11
|
-
"@rt-tools/utils": "^0.
|
|
9
|
+
"@rt-tools/core": "^0.4.0",
|
|
10
|
+
"@rt-tools/store": "^0.3.2",
|
|
11
|
+
"@rt-tools/utils": "^0.4.0"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@rt-tools/core": "^0.
|
|
15
|
-
"@rt-tools/store": "^0.3.
|
|
16
|
-
"@rt-tools/utils": "^0.
|
|
17
|
-
"@angular/animations": "^22.0.0",
|
|
14
|
+
"@rt-tools/core": "^0.4.0",
|
|
15
|
+
"@rt-tools/store": "^0.3.2",
|
|
16
|
+
"@rt-tools/utils": "^0.4.0",
|
|
18
17
|
"@angular/cdk": "^22.0.0",
|
|
19
18
|
"@angular/common": "^22.0.0",
|
|
20
19
|
"@angular/compiler": "^22.0.0",
|
|
Binary file
|
package/src/lib/ui-kit/action-bar/components/container/rtui-action-bar-container.component.scss
CHANGED
|
@@ -8,3 +8,50 @@
|
|
|
8
8
|
inset: var(--rt-action-bar-container-top) var(--rt-action-bar-container-right) var(--rt-action-bar-container-bottom)
|
|
9
9
|
var(--rt-action-bar-container-left);
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
// Выезд панели: снизу, с проскоком выше места и возвратом — тем же порядком долей, каким он шёл
|
|
13
|
+
// прежде. Проскок и делает движение живым, а не равномерным.
|
|
14
|
+
@keyframes rtui-action-bar-enter {
|
|
15
|
+
0% {
|
|
16
|
+
opacity: 0;
|
|
17
|
+
transform: translateY(100%);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
30% {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
transform: translateY(-15px);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
100% {
|
|
26
|
+
opacity: 1;
|
|
27
|
+
transform: translateY(0);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Уход панели: тот же путь в обратную сторону, и проскок стоит ближе к концу.
|
|
32
|
+
@keyframes rtui-action-bar-leave {
|
|
33
|
+
0% {
|
|
34
|
+
opacity: 1;
|
|
35
|
+
transform: translateY(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
70% {
|
|
39
|
+
opacity: 1;
|
|
40
|
+
transform: translateY(-15px);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
100% {
|
|
44
|
+
opacity: 0;
|
|
45
|
+
transform: translateY(100%);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.rtui-action-bar-container__bar {
|
|
50
|
+
animation: rtui-action-bar-enter 300ms;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.rtui-action-bar-container__bar--leaving {
|
|
54
|
+
// `forwards`: панель держится за нижней кромкой до того мига, когда её снимет разметка.
|
|
55
|
+
// Без него она возвращается на место в конце ухода и успевает мигнуть на своём месте.
|
|
56
|
+
animation: rtui-action-bar-leave 300ms forwards;
|
|
57
|
+
}
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
--rt-scrollable-content-padding: 0;
|
|
5
5
|
--rt-scrollable-footer-padding: 0;
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// Направление тела решает, куда тянется его единственный ребёнок — содержимое панели.
|
|
8
|
+
// При `row` он растягивается по высоте зоны: 305 против 290 при замере, и форма,
|
|
9
|
+
// рассчитанная на всю высоту, стоит на месте. При `column` он растянулся бы по ширине
|
|
10
|
+
// переполнения — 589 против видимых 525 — и уехал бы за правый край вместе с фоном.
|
|
8
11
|
--rt-scrollable-content-flex-direction: row;
|
|
9
12
|
|
|
10
13
|
@supports (height: 100dvh) {
|
|
@@ -74,6 +74,10 @@ $aside: (
|
|
|
74
74
|
tab-badge-incorrect: (
|
|
75
75
|
color: var(--rt-text-accent-danger),
|
|
76
76
|
),
|
|
77
|
+
slide: (
|
|
78
|
+
duration: 200ms,
|
|
79
|
+
timing: ease-in,
|
|
80
|
+
),
|
|
77
81
|
);
|
|
78
82
|
|
|
79
83
|
// NOTE: emitted at :root on purpose (ViewEncapsulation.None): consumers override
|
|
@@ -91,6 +95,26 @@ $aside: (
|
|
|
91
95
|
--rt-aside-footer-margin-left-betwen-buttons: var(--rt-aside-footer-margin-left-between-buttons);
|
|
92
96
|
}
|
|
93
97
|
|
|
98
|
+
// SLIDE
|
|
99
|
+
// Шторка выезжает из-за своей кромки и уходит туда же. Начальное положение задаёт сторона,
|
|
100
|
+
// конечное — модификатор открытия: он объявлен ниже и потому перебивает сторону.
|
|
101
|
+
.rtui-aside-panel {
|
|
102
|
+
display: block;
|
|
103
|
+
transition: transform var(--rt-aside-slide-duration) var(--rt-aside-slide-timing);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.rtui-aside-panel--left {
|
|
107
|
+
transform: translateX(-100%);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.rtui-aside-panel--right {
|
|
111
|
+
transform: translateX(100%);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.rtui-aside-panel--open {
|
|
115
|
+
transform: translateX(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
94
118
|
// BADGE
|
|
95
119
|
.icon-badge {
|
|
96
120
|
position: absolute !important;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: block;
|
|
4
|
+
min-height: 12rem;
|
|
5
|
+
|
|
6
|
+
.body {
|
|
7
|
+
max-width: 20rem;
|
|
8
|
+
padding: 0.75rem 1rem;
|
|
9
|
+
border: 1px solid var(--rt-border-neutral-default);
|
|
10
|
+
border-radius: 0.5rem;
|
|
11
|
+
background-color: var(--rt-bg-base-base);
|
|
12
|
+
|
|
13
|
+
&__title {
|
|
14
|
+
margin-bottom: 0.5rem;
|
|
15
|
+
font-weight: 500;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&__list {
|
|
19
|
+
margin: 0 0 0.5rem;
|
|
20
|
+
padding-left: 1rem;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Класс от потребителя садится на хост слоя привязкой — правило целит в сам хост, а не
|
|
26
|
+
* внутрь него: инкапсуляция дальше хоста дочернего компонента не пускает.
|
|
27
|
+
*/
|
|
28
|
+
rtui-popover-container.popover-accent {
|
|
29
|
+
outline: 2px solid var(--rt-border-accent-primary);
|
|
30
|
+
outline-offset: 0.25rem;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -24,9 +24,22 @@ $scrollable: (
|
|
|
24
24
|
padding: 1rem,
|
|
25
25
|
background-color: var(--rt-bg-base-base),
|
|
26
26
|
),
|
|
27
|
+
// Признак непоказанного снизу: полоса растушёвки у нижнего края и значок поверх неё.
|
|
28
|
+
scroll-hint: (
|
|
29
|
+
height: 2.5rem,
|
|
30
|
+
icon-size: 1.25rem,
|
|
31
|
+
// Цвет растушёвки ведёт выбранная цветовая схема: мягкая заливка акцента берётся
|
|
32
|
+
// из рядов роли, которые схема и переопределяет, а светлую и тёмную темы токен
|
|
33
|
+
// разводит сам. Взятый нейтральным фоном, цвет стоял бы одним и тем же во всех
|
|
34
|
+
// схемах. Потребитель, которому нужен свой, переназначает это свойство.
|
|
35
|
+
color: var(--rt-bg-accent-primary-subtle),
|
|
36
|
+
),
|
|
27
37
|
);
|
|
28
38
|
|
|
29
39
|
:host {
|
|
40
|
+
// Полоса растушёвки считает своё место от хоста: тело обрезает всё по своему краю, а полоса
|
|
41
|
+
// обязана дойти до содержимого подвала.
|
|
42
|
+
position: relative;
|
|
30
43
|
display: var(--rt-scrollable-host-display);
|
|
31
44
|
width: var(--rt-scrollable-host-width);
|
|
32
45
|
height: var(--rt-scrollable-host-height);
|
|
@@ -58,5 +71,49 @@ $scrollable: (
|
|
|
58
71
|
padding: var(--rt-scrollable-footer-padding);
|
|
59
72
|
background-color: var(--rt-scrollable-footer-background-color);
|
|
60
73
|
}
|
|
74
|
+
|
|
75
|
+
// Полоса стоит поверх стыка списка и подвала и не ловит указатель: под ней живой список,
|
|
76
|
+
// и перехваченное нажатие уводило бы человека мимо пункта. Значок ловит его сам — иначе
|
|
77
|
+
// подсказку не прочитать. Места в колонке полоса не занимает: занятое, оно отодвинуло бы
|
|
78
|
+
// последний пункт от края у каждого потребителя области.
|
|
79
|
+
// Подъём над низом области приходит замером — это высота подвала без верхнего отступа:
|
|
80
|
+
// ровно там начинается его содержимое, и до него полоса и доходит. Оборванная у края
|
|
81
|
+
// списка, она оставляла до разделителя полосу чистого фона.
|
|
82
|
+
&__scroll-hint {
|
|
83
|
+
position: absolute;
|
|
84
|
+
z-index: 1;
|
|
85
|
+
right: 0;
|
|
86
|
+
left: 0;
|
|
87
|
+
display: flex;
|
|
88
|
+
height: var(--rt-scrollable-scroll-hint-height);
|
|
89
|
+
align-items: flex-end;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
|
|
92
|
+
// Растушёвка красится фоном темы, а не уходит в `transparent`: прозрачный — это
|
|
93
|
+
// чёрный с нулевой прозрачностью, и на тёмной теме такой градиент идёт через грязь.
|
|
94
|
+
// Начало берётся тем же цветом с нулевой прозрачностью — переход остаётся чистым в
|
|
95
|
+
// обеих темах, а потребитель, перекрасивший область, получает свой цвет даром.
|
|
96
|
+
background: linear-gradient(
|
|
97
|
+
to bottom,
|
|
98
|
+
rgb(from var(--rt-scrollable-scroll-hint-color) r g b / 0%),
|
|
99
|
+
var(--rt-scrollable-scroll-hint-color)
|
|
100
|
+
);
|
|
101
|
+
pointer-events: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Значок сидит на своей подложке: без неё он ложится поверх растворяющегося содержимого
|
|
105
|
+
// и читается как его часть — на кадре его не отличить от полустёртого пункта.
|
|
106
|
+
&__scroll-hint-icon {
|
|
107
|
+
display: flex;
|
|
108
|
+
width: var(--rt-scrollable-scroll-hint-icon-size);
|
|
109
|
+
height: var(--rt-scrollable-scroll-hint-icon-size);
|
|
110
|
+
align-items: center;
|
|
111
|
+
justify-content: center;
|
|
112
|
+
border-radius: var(--rt-radius-full);
|
|
113
|
+
background-color: var(--rt-bg-base-base);
|
|
114
|
+
color: var(--rt-text-base-secondary);
|
|
115
|
+
font-size: var(--rt-scrollable-scroll-hint-icon-size);
|
|
116
|
+
pointer-events: auto;
|
|
117
|
+
}
|
|
61
118
|
}
|
|
62
119
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
|
|
4
|
+
.stand {
|
|
5
|
+
height: 24rem;
|
|
6
|
+
border: 1px solid var(--rt-border-neutral-default);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.title {
|
|
10
|
+
font-weight: 500;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.row {
|
|
14
|
+
padding: 0.5rem 0;
|
|
15
|
+
border-bottom: 1px solid var(--rt-border-neutral-default);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -19,13 +19,52 @@ $side-menu: (
|
|
|
19
19
|
),
|
|
20
20
|
sub-menu: (
|
|
21
21
|
width: 15rem,
|
|
22
|
+
// Скругление правых углов панели. Число здесь, а не в теме: тем же числом отступает от
|
|
23
|
+
// угла прокручиваемый список — иначе полоса прокрутки, идущая до самого низа, срезается
|
|
24
|
+
// скруглением, и её нижний конец выглядит обрубленным.
|
|
25
|
+
// Число, а не ступень скругления кита: то имя объявлено обоими китами, и стили, подключённые
|
|
26
|
+
// позже, перебивали бы его молча.
|
|
27
|
+
radius: 1rem,
|
|
28
|
+
// Ширина подменю на время набора запроса: тот же предел, до которого его растягивает тяга.
|
|
29
|
+
held-width: 30rem,
|
|
30
|
+
// Поле поиска ниже полного роста поля формы: в шапке подменю оно стоит рядом с кнопкой
|
|
31
|
+
// значка, и полная высота поднимала бы шапку выше двух строк списка.
|
|
32
|
+
search-height: 2.5rem,
|
|
22
33
|
),
|
|
23
34
|
sub-menu-item: (
|
|
24
35
|
width: 13.75rem,
|
|
25
36
|
title-font-size: 0.85rem,
|
|
26
37
|
),
|
|
38
|
+
// Полоса прокрутки списков меню. Системная занимает у узкой панели заметную долю ширины и
|
|
39
|
+
// выглядит в каждой системе по-своему; здесь она узкая, с плоской дорожкой и скруглённым
|
|
40
|
+
// ползунком. Значения взяты с образца, показанного владельцем: полоса в два пикселя, дорожка
|
|
41
|
+
// светло-серая, ползунок серый со скруглением малой ступени. Цвета берутся токенами — они же
|
|
42
|
+
// разводят светлую и тёмную тему.
|
|
43
|
+
scrollbar: (
|
|
44
|
+
width: 0.125rem,
|
|
45
|
+
thumb-radius: var(--rt-radius-xs),
|
|
46
|
+
track-color: var(--rt-bg-base-strong),
|
|
47
|
+
thumb-color: var(--rt-bg-base-emphasis-soft),
|
|
48
|
+
),
|
|
49
|
+
// Ручка тяги: видимая полоса и зона, которой она захватывается курсором, — два разных числа.
|
|
50
|
+
// Пока они были одним, попасть в четыре пикселя удавалось не с первого раза, а промах мимо
|
|
51
|
+
// ручки попадает в панель подменю — то есть вместо тяги человек нажимает пункт. Зона шире
|
|
52
|
+
// втрое и стоит по центру прежнего места: видимое от этого не двигается ни на пиксель.
|
|
53
|
+
resizer: (
|
|
54
|
+
width: 0.25rem,
|
|
55
|
+
grab-width: 0.75rem,
|
|
56
|
+
hover-color: var(--rt-border-neutral-default),
|
|
57
|
+
),
|
|
27
58
|
);
|
|
28
59
|
|
|
60
|
+
:host(.rtui-side-menu--pinned) {
|
|
61
|
+
// Полоса значков и закреплённое подменю стоят в ряд. Правило висит на модификаторе хоста, а
|
|
62
|
+
// не на самом хосте: поставленное безусловно, оно сдвинуло экраны каждого потребителя кита,
|
|
63
|
+
// где закрепления нет вовсе.
|
|
64
|
+
display: flex;
|
|
65
|
+
height: 100%;
|
|
66
|
+
}
|
|
67
|
+
|
|
29
68
|
:host {
|
|
30
69
|
@each $element, $elements in $side-menu {
|
|
31
70
|
@each $style-token, $value in $elements {
|
|
@@ -36,8 +75,37 @@ $side-menu: (
|
|
|
36
75
|
// Hide drawer mask
|
|
37
76
|
--mat-sidenav-scrim-color: transparent;
|
|
38
77
|
|
|
78
|
+
// Ширина, которой панель подменю стоит на экране: заданная оформлением потребителя либо
|
|
79
|
+
// раздвинутая тягой за край. Её знают трое — сама панель, ручка тяги и пункт внутри неё, —
|
|
80
|
+
// и выражение, повторённое у каждого, разъезжается на первой же правке одного из них.
|
|
81
|
+
--rt-side-menu-sub-menu-shown-width: max(var(--rt-side-menu-sub-menu-width), var(--rt-side-menu-sub-menu-dragged-width, 0px));
|
|
82
|
+
|
|
83
|
+
// Правый край панели подменю на экране: отступ полосы значков плюс её показанная ширина.
|
|
84
|
+
// Тем же числом стоит ручка тяги, и повторённое у неё выражение разошлось бы с этим.
|
|
85
|
+
--rt-side-menu-panel-edge: calc(var(--rt-side-menu-base-width) + var(--rt-side-menu-sub-menu-shown-width));
|
|
86
|
+
|
|
87
|
+
// Полоса прокрутки обоих списков меню — полосы значков и подменю. Оформляется здесь, а не у
|
|
88
|
+
// прокручиваемой области: вид остальных её экранов от этой правки не двигается. Правило
|
|
89
|
+
// пробивает инкапсуляцию, потому что прокручивается внутренний узел области, а полоса
|
|
90
|
+
// принадлежит ему; своего входа под вид полосы область не даёт.
|
|
91
|
+
::ng-deep .rtui-scrollable__content {
|
|
92
|
+
&::-webkit-scrollbar {
|
|
93
|
+
width: var(--rt-side-menu-scrollbar-width);
|
|
94
|
+
height: var(--rt-side-menu-scrollbar-width);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
&::-webkit-scrollbar-track {
|
|
98
|
+
background: var(--rt-side-menu-scrollbar-track-color);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&::-webkit-scrollbar-thumb {
|
|
102
|
+
border-radius: var(--rt-side-menu-scrollbar-thumb-radius);
|
|
103
|
+
background: var(--rt-side-menu-scrollbar-thumb-color);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
39
107
|
.rtui-mobile-side-menu {
|
|
40
|
-
width: var(--rt-side-menu-sub-menu-width);
|
|
108
|
+
width: var(--rt-side-menu-sub-menu-shown-width);
|
|
41
109
|
|
|
42
110
|
.rtui-mobile-side-menu-list {
|
|
43
111
|
display: flex;
|
|
@@ -45,7 +113,7 @@ $side-menu: (
|
|
|
45
113
|
flex-direction: column;
|
|
46
114
|
|
|
47
115
|
&--scrollable {
|
|
48
|
-
width:
|
|
116
|
+
min-width: min(var(--rt-side-menu-sub-menu-item-width), 100%);
|
|
49
117
|
}
|
|
50
118
|
|
|
51
119
|
&__sub {
|
|
@@ -144,6 +212,44 @@ $side-menu: (
|
|
|
144
212
|
}
|
|
145
213
|
}
|
|
146
214
|
|
|
215
|
+
// Ручка тяги лежит вне ящика подменю и вне его контейнера: панель скруглена по правому краю
|
|
216
|
+
// и режет по этой дуге всё, что внутри неё, а положенное в контейнер рядом с панелью Material
|
|
217
|
+
// уносит в свою область содержимого. Снаружи ручка идёт от края экрана до края, и стоит она
|
|
218
|
+
// там, где кончается панель: отступ полосы значков плюс ширина самой панели.
|
|
219
|
+
.rtui-side-menu-resizer {
|
|
220
|
+
// Свес — половина лишней ширины, на которую зона захвата шире видимой полосы. Его
|
|
221
|
+
// используют два места: сам узел, сдвинутый влево на эту величину, и полоса внутри него,
|
|
222
|
+
// отступающая на неё же. Выражение, повторённое у обоих, расходится на первой же правке
|
|
223
|
+
// одного.
|
|
224
|
+
--rt-side-menu-resizer-overhang: calc((var(--rt-side-menu-resizer-grab-width) - var(--rt-side-menu-resizer-width)) / 2);
|
|
225
|
+
|
|
226
|
+
// Отступ узла от правого края панели: своя ширина полосы плюс свес слева от неё.
|
|
227
|
+
--rt-side-menu-resizer-inset: calc(var(--rt-side-menu-resizer-width) + var(--rt-side-menu-resizer-overhang));
|
|
228
|
+
|
|
229
|
+
// Узел занимает зону захвата целиком и стоит так, чтобы прежняя полоса осталась на своём
|
|
230
|
+
// месте: лишняя ширина расходится поровну в обе стороны от неё.
|
|
231
|
+
position: fixed;
|
|
232
|
+
z-index: 3;
|
|
233
|
+
left: calc(var(--rt-side-menu-panel-edge) - var(--rt-side-menu-resizer-inset));
|
|
234
|
+
width: var(--rt-side-menu-resizer-grab-width);
|
|
235
|
+
cursor: col-resize;
|
|
236
|
+
inset-block: 0;
|
|
237
|
+
|
|
238
|
+
// Красится не сам узел, а полоса внутри него: закрашенная зона захвата стала бы втрое
|
|
239
|
+
// толще прежней линии и читалась бы другим оформлением, а не тем же с большей мишенью.
|
|
240
|
+
&:before {
|
|
241
|
+
position: absolute;
|
|
242
|
+
left: var(--rt-side-menu-resizer-overhang);
|
|
243
|
+
width: var(--rt-side-menu-resizer-width);
|
|
244
|
+
content: '';
|
|
245
|
+
inset-block: 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
&:hover:before {
|
|
249
|
+
background-color: var(--rt-side-menu-resizer-hover-color);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
147
253
|
.rtui-sub-side-menu {
|
|
148
254
|
position: fixed;
|
|
149
255
|
top: 0;
|
|
@@ -160,21 +266,130 @@ $side-menu: (
|
|
|
160
266
|
// Component-local elevation token for the slide-out sub-menu drawer.
|
|
161
267
|
--rt-side-menu-sub-content-shadow: 14px 0 28px rgb(0 0 0 / 25%), 10px 0 10px rgb(0 0 0 / 22%);
|
|
162
268
|
|
|
163
|
-
|
|
269
|
+
position: relative;
|
|
270
|
+
width: max(var(--rt-side-menu-sub-menu-width), var(--rt-side-menu-sub-menu-dragged-width, 0px));
|
|
271
|
+
min-width: max(var(--rt-side-menu-sub-menu-width), var(--rt-side-menu-sub-menu-dragged-width, 0px));
|
|
164
272
|
height: 100%;
|
|
273
|
+
border-radius: 0 var(--rt-side-menu-sub-menu-radius) var(--rt-side-menu-sub-menu-radius) 0;
|
|
165
274
|
border-left: 1px solid var(--rt-border-neutral-default);
|
|
166
275
|
box-shadow: var(--rt-side-menu-sub-content-shadow);
|
|
167
276
|
|
|
277
|
+
// Список отступает от скруглённого угла на его же радиус. Панель обрезает всё, что в
|
|
278
|
+
// угол заходит, и полоса прокрутки, идущая по правому краю до самого низа, теряла в
|
|
279
|
+
// нём свой конец — выглядело это обрубленной полосой, а не скруглением панели.
|
|
280
|
+
rtui-scrollable {
|
|
281
|
+
// Отступ съедает высоту, а не прибавляет её: у области задан рост во всю панель,
|
|
282
|
+
// и без этого правила она вылезала бы за её нижний край на тот же радиус.
|
|
283
|
+
box-sizing: border-box;
|
|
284
|
+
padding-bottom: var(--rt-side-menu-sub-menu-radius);
|
|
285
|
+
}
|
|
286
|
+
|
|
168
287
|
&__list {
|
|
169
288
|
width: 100%;
|
|
170
289
|
animation: fade-in-animation 0.15s ease-in-out 0.15s forwards;
|
|
171
290
|
opacity: 0;
|
|
172
291
|
|
|
292
|
+
// Список с боковой прокруткой не уже пункта — и не шире панели. Рост по
|
|
293
|
+
// содержимому раздувал бы его вместе с самой длинной подписью, и пункт уезжал
|
|
294
|
+
// бы за край панели вместо многоточия; прокрутка при этом остаётся при деле —
|
|
295
|
+
// она нужна там, где панель уже самого пункта.
|
|
173
296
|
&--scrollable {
|
|
174
|
-
min-width:
|
|
297
|
+
min-width: min(var(--rt-side-menu-sub-menu-item-width), 100%);
|
|
175
298
|
}
|
|
176
299
|
}
|
|
300
|
+
|
|
301
|
+
&__empty {
|
|
302
|
+
padding: var(--rt-spacing-16);
|
|
303
|
+
color: var(--rt-text-base-secondary);
|
|
304
|
+
font-size: var(--rt-font-size-xs);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Закреплённая шторка стоит в потоке рядом с полосой значков, а не поверх страницы:
|
|
309
|
+
// тень и перекрытие у постоянно открытой панели читаются как залипший оверлей.
|
|
310
|
+
// Пока человек работает с полем, подменю стоит во всю разрешённую ширину: отобранный
|
|
311
|
+
// список читают, а не разглядывают в узкой полосе. Раздвигает панель нижняя граница, а не
|
|
312
|
+
// ширина: ширину шторки Material держит своим кодом, и ни переход, ни кадры её не двигают.
|
|
313
|
+
&--held .rtui-sub-side-menu-content {
|
|
314
|
+
min-width: var(--rt-side-menu-sub-menu-held-width);
|
|
177
315
|
}
|
|
316
|
+
|
|
317
|
+
&--pinned {
|
|
318
|
+
position: relative;
|
|
319
|
+
left: 0;
|
|
320
|
+
width: var(--rt-side-menu-sub-menu-shown-width);
|
|
321
|
+
height: 100%;
|
|
322
|
+
flex: 0 0 auto;
|
|
323
|
+
|
|
324
|
+
.rtui-sub-side-menu-content {
|
|
325
|
+
box-shadow: none;
|
|
326
|
+
|
|
327
|
+
// Появление списка рассчитано на выдвижение шторки. Закреплённая никуда не
|
|
328
|
+
// выезжает, и её список показан сразу — иначе он живёт под гашеной анимацией
|
|
329
|
+
// и не виден вовсе там, где движение выключено.
|
|
330
|
+
&__list {
|
|
331
|
+
animation: none;
|
|
332
|
+
opacity: 1;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.rtui-sub-side-menu-head {
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: space-between;
|
|
341
|
+
padding: var(--rt-spacing-8);
|
|
342
|
+
gap: var(--rt-spacing-8);
|
|
343
|
+
|
|
344
|
+
// Поле рисует готовое кита. Своего у него остаётся место в ряду, форма пилюли и
|
|
345
|
+
// рост: в шапке подменю поле стоит рядом с кнопкой значка, и полная высота поля
|
|
346
|
+
// формы поднимала бы шапку выше строки списка. Скругление и высоту задают ручки
|
|
347
|
+
// самого поля, а не рамка поверх него.
|
|
348
|
+
&__search {
|
|
349
|
+
min-width: 0;
|
|
350
|
+
flex: 1 1 auto;
|
|
351
|
+
|
|
352
|
+
--mat-form-field-outlined-container-shape: var(--rt-radius-xl);
|
|
353
|
+
--mat-form-field-container-height: var(--rt-side-menu-sub-menu-search-height);
|
|
354
|
+
--mat-form-field-container-vertical-padding: var(--rt-spacing-8);
|
|
355
|
+
|
|
356
|
+
// Готовое поле разводит значок и текст на шестнадцать пикселей — двенадцать
|
|
357
|
+
// справа от значка и четыре у самого префикса. В узкой панели это половина
|
|
358
|
+
// строки запроса, поэтому зазор сведён к одной ступени. Ручки под него у поля
|
|
359
|
+
// формы нет: правится сам отступ значка.
|
|
360
|
+
&::ng-deep .mat-mdc-form-field-icon-prefix > .mat-icon {
|
|
361
|
+
padding: 0 var(--rt-spacing-4) 0 var(--rt-spacing-12);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Кнопку рисует готовое кита; своего у неё остаётся место в ряду.
|
|
366
|
+
&__pin {
|
|
367
|
+
flex: 0 0 auto;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Узкая раскладка: подменю занимает экран целиком, и поле поиска стоит над списком.
|
|
373
|
+
// Рисует его то же готовое кита, что и на широком экране.
|
|
374
|
+
.rtui-side-menu-search {
|
|
375
|
+
width: 100%;
|
|
376
|
+
margin: var(--rt-spacing-8) 0;
|
|
377
|
+
|
|
378
|
+
--mat-form-field-outlined-container-shape: var(--rt-radius-xl);
|
|
379
|
+
--mat-form-field-container-height: var(--rt-side-menu-sub-menu-search-height);
|
|
380
|
+
--mat-form-field-container-vertical-padding: var(--rt-spacing-8);
|
|
381
|
+
|
|
382
|
+
// Зазор между значком и запросом сведён к ступени по той же причине, что и на широком
|
|
383
|
+
// экране: готовое поле разводит их на шестнадцать пикселей.
|
|
384
|
+
&::ng-deep .mat-mdc-form-field-icon-prefix > .mat-icon {
|
|
385
|
+
padding: 0 var(--rt-spacing-4) 0 var(--rt-spacing-12);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.rtui-side-menu-empty {
|
|
390
|
+
padding: var(--rt-spacing-16);
|
|
391
|
+
color: var(--rt-text-base-secondary);
|
|
392
|
+
font-size: var(--rt-font-size-xs);
|
|
178
393
|
}
|
|
179
394
|
}
|
|
180
395
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
:host {
|
|
2
|
+
// Хост пункта — блок шириной своего места. Строкой он не сжимается по ширине вовсе, и
|
|
3
|
+
// ограничение пункта внутри него ничего не решает: список растягивается по хосту, а хост —
|
|
4
|
+
// по самой длинной подписи. Тот же класс висит и на строке списка внутри, поэтому хост
|
|
5
|
+
// адресуется именем элемента.
|
|
6
|
+
display: block;
|
|
7
|
+
max-width: 100%;
|
|
8
|
+
|
|
2
9
|
&::ng-deep {
|
|
3
10
|
// Set expansion panel header padding
|
|
4
11
|
.mat-expansion-indicator {
|
|
@@ -17,11 +24,22 @@
|
|
|
17
24
|
--mat-expansion-header-expanded-state-height: var(--mat-expansion-header-collapsed-state-height);
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
// Пункт занимает ширину панели и не больше её: панель знает, насколько широка, — её двигают
|
|
28
|
+
// тяга за край и работа с полем поиска, — а подпись, которая в эту ширину не влезла, уходит в
|
|
29
|
+
// многоточие. Своим числом верхний предел быть не может: панель, выросшая вдвое, держала бы
|
|
30
|
+
// подписи обрезанными при пустой полосе справа. Ширины места здесь тоже мало: список с
|
|
31
|
+
// боковой прокруткой сам растёт по содержимому, и пункт, отмеренный от него, раздувается
|
|
32
|
+
// вместе с подписью и уезжает за край панели. Число остаётся нижним пределом там, где
|
|
33
|
+
// прокрутка включена: сжатый пункт прокручивать нечем.
|
|
20
34
|
.rtui-side-menu-sub-item {
|
|
21
|
-
max-width:
|
|
35
|
+
max-width: 100%;
|
|
22
36
|
|
|
37
|
+
// Нижний предел ширины действует, пока для него есть место. Вложенный пункт стоит с
|
|
38
|
+
// отступом своего уровня, и предел, взятый числом, выносил бы его правый край за панель
|
|
39
|
+
// ровно на этот отступ: ширину такой пункт оправдывает, а человек видит строку, уходящую
|
|
40
|
+
// под край панели.
|
|
23
41
|
&--fixed {
|
|
24
|
-
min-width: var(--rt-side-menu-sub-menu-item-width);
|
|
42
|
+
min-width: min(var(--rt-side-menu-sub-menu-item-width), 100%);
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
.rtui-side-menu-sub-item-title {
|
|
@@ -36,6 +54,15 @@
|
|
|
36
54
|
white-space: nowrap;
|
|
37
55
|
}
|
|
38
56
|
|
|
57
|
+
// Совпавший с запросом кусок подписи. Красится набором токенов, а не умолчанием
|
|
58
|
+
// разметки выделения браузера: то жёлтое и в тёмной теме читается как чужое.
|
|
59
|
+
&__match {
|
|
60
|
+
border-radius: var(--rt-radius-xs);
|
|
61
|
+
background-color: var(--rt-bg-accent-highlight);
|
|
62
|
+
color: var(--rt-text-accent-highlight);
|
|
63
|
+
font-weight: var(--rt-font-weight-semibold);
|
|
64
|
+
}
|
|
65
|
+
|
|
39
66
|
.rtui-side-menu-sub-item-title-button {
|
|
40
67
|
display: flex;
|
|
41
68
|
align-items: center;
|
|
@@ -66,12 +93,16 @@
|
|
|
66
93
|
border-radius: var(--rt-radius-xl);
|
|
67
94
|
box-shadow: none;
|
|
68
95
|
|
|
96
|
+
// Нижний предел ширины действует, пока для него есть место. Вложенный пункт стоит с
|
|
97
|
+
// отступом своего уровня, и предел, взятый числом, выносил бы его правый край за панель
|
|
98
|
+
// ровно на этот отступ: ширину такой пункт оправдывает, а человек видит строку, уходящую
|
|
99
|
+
// под край панели.
|
|
69
100
|
&--fixed {
|
|
70
|
-
min-width: var(--rt-side-menu-sub-menu-item-width);
|
|
101
|
+
min-width: min(var(--rt-side-menu-sub-menu-item-width), 100%);
|
|
71
102
|
}
|
|
72
103
|
|
|
73
104
|
.rtui-side-menu-expand-sub-item-header {
|
|
74
|
-
max-width:
|
|
105
|
+
max-width: 100%;
|
|
75
106
|
padding: 0;
|
|
76
107
|
|
|
77
108
|
&--activated {
|
|
@@ -87,6 +118,16 @@
|
|
|
87
118
|
font-size: var(--rt-side-menu-sub-menu-item-title-font-size);
|
|
88
119
|
}
|
|
89
120
|
|
|
121
|
+
// Отметка совпавшего — та же, что у пункта со ссылкой: заголовок раздела попадает
|
|
122
|
+
// в отобранный список наравне с ним, и разное оформление одного и того же читалось
|
|
123
|
+
// бы как разный смысл.
|
|
124
|
+
&__match {
|
|
125
|
+
border-radius: var(--rt-radius-xs);
|
|
126
|
+
background-color: var(--rt-bg-accent-highlight);
|
|
127
|
+
color: var(--rt-text-accent-highlight);
|
|
128
|
+
font-weight: var(--rt-font-weight-semibold);
|
|
129
|
+
}
|
|
130
|
+
|
|
90
131
|
&:hover {
|
|
91
132
|
.rtui-side-menu-expand-sub-item-header__icon {
|
|
92
133
|
color: var(--mdc-list-list-item-hover-leading-icon-color);
|