@rt-tools/ui-kit-v2 0.8.1 → 0.11.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.
Files changed (69) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2.mjs +1001 -471
  2. package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
  3. package/package.json +3 -3
  4. package/rt-tools-ui-kit-v2-0.11.0.tgz +0 -0
  5. package/src/assets/icons/social-facebook.svg +7 -0
  6. package/src/assets/icons/social-instagram.svg +14 -0
  7. package/src/assets/icons/social-kakaotalk.svg +7 -0
  8. package/src/assets/icons/social-line.svg +7 -0
  9. package/src/assets/icons/social-naver.svg +7 -0
  10. package/src/assets/icons/social-pinterest.svg +7 -0
  11. package/src/assets/icons/social-tiktok.svg +7 -0
  12. package/src/assets/icons/social-vk.svg +7 -0
  13. package/src/assets/icons/social-wechat.svg +7 -0
  14. package/src/assets/icons/social-x.svg +7 -0
  15. package/src/assets/icons/social-xiaohongshu.svg +7 -0
  16. package/src/assets/icons/social-youtube.svg +7 -0
  17. package/src/assets/icons-material/NOTICE.md +20 -0
  18. package/src/assets/icons-material/arrow-left.svg +3 -0
  19. package/src/assets/icons-material/ban.svg +5 -0
  20. package/src/assets/icons-material/bars.svg +3 -0
  21. package/src/assets/icons-material/check.svg +3 -0
  22. package/src/assets/icons-material/chevron-down.svg +3 -0
  23. package/src/assets/icons-material/chevron-left.svg +3 -0
  24. package/src/assets/icons-material/chevron-right.svg +3 -0
  25. package/src/assets/icons-material/close.svg +3 -0
  26. package/src/assets/icons-material/cloud-upload.svg +5 -0
  27. package/src/assets/icons-material/cog.svg +5 -0
  28. package/src/assets/icons-material/copy.svg +5 -0
  29. package/src/assets/icons-material/ellipsis-h.svg +5 -0
  30. package/src/assets/icons-material/ellipsis-v.svg +5 -0
  31. package/src/assets/icons-material/external-link.svg +5 -0
  32. package/src/assets/icons-material/eye-slash.svg +5 -0
  33. package/src/assets/icons-material/eye.svg +5 -0
  34. package/src/assets/icons-material/ico-download.svg +5 -0
  35. package/src/assets/icons-material/ico-plus.svg +3 -0
  36. package/src/assets/icons-material/info-circle.svg +5 -0
  37. package/src/assets/icons-material/pencil.svg +5 -0
  38. package/src/assets/icons-material/refresh.svg +5 -0
  39. package/src/assets/icons-material/search.svg +5 -0
  40. package/src/assets/icons-material/star.svg +5 -0
  41. package/src/assets/icons-material/sync.svg +5 -0
  42. package/src/assets/icons-material/table.svg +5 -0
  43. package/src/assets/icons-material/tag.svg +5 -0
  44. package/src/assets/icons-material/trash.svg +5 -0
  45. package/src/assets/icons-material/user.svg +5 -0
  46. package/src/lib/components/aside/CONTEXT.md +15 -0
  47. package/src/lib/components/aside/_rt-aside-overlay.scss +35 -22
  48. package/src/lib/components/aside-section/CONTEXT.md +8 -0
  49. package/src/lib/components/aside-section/rt-aside-section.component.scss +4 -16
  50. package/src/lib/components/button/rt-button.directive.scss +10 -1
  51. package/src/lib/components/icon/CONTEXT.md +23 -0
  52. package/src/lib/components/icon/rt-icon.component.scss +3 -0
  53. package/src/lib/components/icon-button/rt-icon-button.component.scss +31 -1
  54. package/src/lib/components/page-header/CONTEXT.md +19 -7
  55. package/src/lib/components/page-header/rt-page-header-compact.scss +149 -0
  56. package/src/lib/components/ripple/rt-ripple.directive.scss +46 -0
  57. package/src/lib/components/skeleton/rt-skeleton.component.scss +3 -3
  58. package/src/lib/components/tabs/CONTEXT.md +4 -0
  59. package/src/lib/components/thread-list/CONTEXT.md +6 -1
  60. package/src/styles/_form.scss +240 -0
  61. package/src/styles/_index.scss +16 -0
  62. package/src/styles/_preset-material.scss +102 -0
  63. package/src/styles/_primitives.scss +74 -4
  64. package/src/styles/_scrollbar.scss +39 -0
  65. package/src/styles/_semantic.scss +35 -24
  66. package/src/styles/_theme-dark.scss +16 -12
  67. package/styles/tokens.css +520 -38
  68. package/types/rt-tools-ui-kit-v2.d.ts +199 -33
  69. package/rt-tools-ui-kit-v2-0.8.1.tgz +0 -0
@@ -13,5 +13,8 @@
13
13
  height: 100%;
14
14
  fill: currentcolor;
15
15
  }
16
+
17
+ /* Набор рисунков стилями не выбирается: ссылку на символ спрайта CSS подменить нечем.
18
+ Признак набора компонент читает у разметки над собой — см. rt-icon.component.ts. */
16
19
  }
17
20
  }
@@ -21,8 +21,27 @@
21
21
  .rt-icon-button {
22
22
  --rt-icon-button-circle-radius: var(--rt-radius-full);
23
23
  --rt-icon-button-square-radius: var(--rt-radius-md);
24
+
25
+ /* Промежуточные формы делят расстояние от квадрата до круга на три равные
26
+ части, а не берут свою долю стороны. Доля, взятая сама по себе, промахивается
27
+ мимо квадрата: у него радиус в 8 пикселей, а это от четверти стороны на
28
+ кнопке в 32 пикселя до восьмой на кнопке в 64 — ближняя ступень сливается с
29
+ ним на одних размерах и отрывается на других. Отсчёт от самого квадрата
30
+ держит шаг ровным на всём ряду: 8 → 12 → 16 → 20 на кнопке в 40 пикселей. */
31
+ --rt-icon-button-rounded-sm-radius: calc(var(--rt-icon-button-square-radius) + (50% - var(--rt-icon-button-square-radius)) / 3);
32
+ --rt-icon-button-rounded-lg-radius: calc(
33
+ var(--rt-icon-button-square-radius) + (50% - var(--rt-icon-button-square-radius)) * 2 / 3
34
+ );
24
35
  --rt-icon-button-indicator-radius: var(--rt-radius-full);
25
36
  --rt-icon-button-focus-shadow: var(--rt-shadow-focus-ring);
37
+
38
+ /* Тень покоя — ручка для потребителя, как `--rt-btn-shadow` у обычной кнопки; у
39
+ кнопки-значка её не было вовсе, и приподнять её было нечем. По умолчанию тени
40
+ нет: у первого кита её нет ни у одной кнопки — подъём там несёт круглая кнопка
41
+ действия, а не кнопка. Дающему ей значение: кольцо фокуса ниже объявляет
42
+ `box-shadow` целиком и тень покоя на это время снимает — списком они встанут
43
+ только тогда, когда покой перестанет быть `none`. */
44
+ --rt-icon-button-shadow: var(--rt-shadow-none);
26
45
  --rt-icon-button-transition-duration: var(--rt-duration-fast);
27
46
 
28
47
  position: relative;
@@ -34,9 +53,12 @@
34
53
  padding: 0;
35
54
  border: 0;
36
55
  background: transparent;
56
+ box-shadow: var(--rt-icon-button-shadow);
37
57
  color: var(--rt-icon-button-color);
38
58
  cursor: pointer;
39
- transition: background-color var(--rt-icon-button-transition-duration) ease;
59
+ transition:
60
+ background-color var(--rt-icon-button-transition-duration) ease,
61
+ box-shadow var(--rt-icon-button-transition-duration) ease;
40
62
 
41
63
  &--sm {
42
64
  --rt-icon-button-size: var(--rt-control-height-sm);
@@ -62,6 +84,14 @@
62
84
  border-radius: var(--rt-icon-button-circle-radius);
63
85
  }
64
86
 
87
+ &--rounded-sm {
88
+ border-radius: var(--rt-icon-button-rounded-sm-radius);
89
+ }
90
+
91
+ &--rounded-lg {
92
+ border-radius: var(--rt-icon-button-rounded-lg-radius);
93
+ }
94
+
65
95
  &--square {
66
96
  border-radius: var(--rt-icon-button-square-radius);
67
97
  }
@@ -6,16 +6,20 @@
6
6
  <rt-page-header ariaLabel="Разделы" [items]="sections()" [user]="user()" (itemClick)="go($event)" (userClick)="openProfile()" />
7
7
  ```
8
8
 
9
- | вход | тип | умолчание |
10
- | ----------- | --------------------------- | ------------------ |
11
- | `items` | `ReadonlyArray<Item>` | `[]` |
12
- | `user` | `{ name, avatar? } \| null` | `null` |
13
- | `userTitle` | `string` | `''` |
14
- | `userMenu` | `TemplateRef \| null` | `null` |
15
- | `ariaLabel` | `string` | `''` → `uiMainNav` |
9
+ | вход | тип | умолчание |
10
+ | --------------------- | --------------------------- | ------------------ |
11
+ | `items` | `ReadonlyArray<Item>` | `[]` |
12
+ | `user` | `{ name, avatar? } \| null` | `null` |
13
+ | `userTitle` | `string` | `''` |
14
+ | `userMenu` | `TemplateRef \| null` | `null` |
15
+ | `ariaLabel` | `string` | `''` → `uiMainNav` |
16
+ | `stickyCompact` | `boolean` | `false` |
17
+ | `compactVisibleCount` | `number` | `2` |
16
18
 
17
19
  Выходы: `itemClick`, `userClick`.
18
20
 
21
+ Слоты сжатой полосы: `[rtCompactLeft]`, `[rtCompactCenter]`, `[rtCompactRight]`.
22
+
19
23
  ## Главное, что нужно знать
20
24
 
21
25
  **Пункт с `route` — это ссылка, пункт с `columns` — кнопка.** Второму некуда вести: он
@@ -25,6 +29,14 @@
25
29
  **Узкая разметка отдельная**, а не свёрнутая стилями: те же разделы доступны через кнопку-бургер
26
30
  (`header-nav-burger`), и оба варианта живут в DOM одновременно.
27
31
 
32
+ **С `stickyCompact` шапка липнет к верху окна и, прилипнув, сжимается в полосу** высотой 46:
33
+ слева кнопка «ещё» (`header-compact-more`) и круги первых разделов с иконкой
34
+ (`header-compact-section`), дальше три слота потребителя. Число кругов — `compactVisibleCount`;
35
+ раздел без иконки в круг не попадает. «Ещё» открывает ту же панель, что и бургер. Прилипание
36
+ ловит наблюдатель пересечения: верх шапки стоит на пиксель выше края, и, прилипнув, она теряет
37
+ его из видимой области — так работает и внутри прокручиваемого контейнера. Без входа разметка и
38
+ поведение прежние.
39
+
28
40
  ## Края
29
41
 
30
42
  - `unread: true` у пункта рисует отдельную точку.
@@ -0,0 +1,149 @@
1
+ @layer rt-kit.components {
2
+ /* Сжатая полоса шапки: та же плашка, прилипшая к верху и сжатая по макету до 46px.
3
+ Слева стопка кругов с шагом в половину круга — «ещё» поверх остальных, — дальше три
4
+ слота потребителя. Своей ширины у полосы нет: она заполняет хост. */
5
+ .rt-page-header {
6
+ --rt-nav-compact-height: 46px;
7
+ --rt-nav-compact-circle-size: var(--rt-size-6);
8
+ --rt-nav-compact-circle-overlap: calc(var(--rt-nav-compact-circle-size) / 2);
9
+ --rt-nav-compact-circle-ring-width: var(--rt-border-width-thick);
10
+ --rt-nav-compact-circle-radius: var(--rt-radius-full);
11
+ --rt-nav-compact-circle-color-bg: var(--rt-neutral-100);
12
+ --rt-nav-compact-circle-color-bg-active: var(--rt-nav-item-color-bg-active);
13
+ --rt-nav-compact-color-bg: var(--rt-color-bg-surface);
14
+ --rt-nav-compact-stack-depth: 16; /* круг с индексом 0 лежит выше всех, следующие — ниже */
15
+
16
+ /* Верх на пиксель выше края: прилипнув, шапка теряет его из видимой области, и по
17
+ этому наблюдатель пересечения узнаёт о прилипании. Иначе сравнивать нечего — у края
18
+ прокручиваемого контейнера липкий узел стоит там же, где стоял в потоке. */
19
+ &--sticky {
20
+ position: sticky;
21
+ z-index: var(--rt-z-sticky);
22
+ top: -1px;
23
+ }
24
+
25
+ &__compact {
26
+ display: none;
27
+ min-width: 0;
28
+ min-height: var(--rt-nav-compact-height);
29
+ flex: 1 1 auto;
30
+ align-items: center;
31
+ gap: var(--rt-space-4);
32
+ }
33
+
34
+ /* Сжатое состояние: широкий ряд, бургер и блок пользователя уходят, полоса встаёт на
35
+ их место. Скругление снимается — полоса лежит по краю окна, а не в теле страницы. */
36
+ &--is-compact {
37
+ min-height: var(--rt-nav-compact-height);
38
+ padding: 0 var(--rt-space-5);
39
+ border-radius: 0;
40
+ background-color: var(--rt-nav-compact-color-bg);
41
+
42
+ > :not(.rt-page-header__compact) {
43
+ display: none;
44
+ }
45
+
46
+ .rt-page-header__compact {
47
+ display: flex;
48
+ }
49
+ }
50
+
51
+ /* Стопка — свой контекст наложения: номера кругов сравниваются только между собой. */
52
+ &__compact-stack {
53
+ display: flex;
54
+ flex-shrink: 0;
55
+ align-items: center;
56
+ isolation: isolate;
57
+ }
58
+
59
+ /* Порядок в стопке: чем правее круг, тем ниже он лежит. Индекс приходит из разметки,
60
+ «ещё» стоит первым и без индекса — поверх всех. */
61
+ &__compact-circle {
62
+ --rt-nav-compact-index: 0;
63
+
64
+ position: relative;
65
+ z-index: calc(var(--rt-nav-compact-stack-depth) - var(--rt-nav-compact-index));
66
+ display: inline-flex;
67
+ width: var(--rt-nav-compact-circle-size);
68
+ height: var(--rt-nav-compact-circle-size);
69
+ box-sizing: border-box;
70
+ flex-shrink: 0;
71
+ align-items: center;
72
+ justify-content: center;
73
+ padding: 0;
74
+ border: 0;
75
+ border-radius: var(--rt-nav-compact-circle-radius);
76
+ background-color: var(--rt-nav-compact-circle-color-bg);
77
+ box-shadow: 0 0 0 var(--rt-nav-compact-circle-ring-width) var(--rt-nav-compact-color-bg);
78
+ color: var(--rt-nav-item-color);
79
+ cursor: pointer;
80
+ text-decoration: none;
81
+ transition: color var(--rt-nav-header-transition-duration) linear;
82
+
83
+ & + & {
84
+ margin-left: calc(-1 * var(--rt-nav-compact-circle-overlap));
85
+ }
86
+
87
+ &:hover {
88
+ color: var(--rt-nav-item-color-hover);
89
+ }
90
+
91
+ &:active {
92
+ color: var(--rt-nav-item-color-pressed);
93
+ }
94
+
95
+ &:focus {
96
+ outline: none;
97
+ }
98
+
99
+ /* Круг с фокусом поднимается над соседями: иначе кольцо фокуса режется следующим. */
100
+ &:focus-visible {
101
+ z-index: calc(var(--rt-nav-compact-stack-depth) + 1);
102
+ outline: 2px dashed var(--rt-nav-color-focus);
103
+ outline-offset: 2px;
104
+ }
105
+
106
+ &--is-active,
107
+ &--is-open {
108
+ background-color: var(--rt-nav-compact-circle-color-bg-active);
109
+ color: var(--rt-nav-item-color-hover);
110
+ }
111
+
112
+ &--disabled,
113
+ &:disabled {
114
+ color: var(--rt-nav-color-disabled);
115
+ cursor: not-allowed;
116
+ }
117
+ }
118
+
119
+ &__compact-marker {
120
+ position: absolute;
121
+ top: 0;
122
+ right: 0;
123
+ width: var(--rt-nav-marker-size);
124
+ height: var(--rt-nav-marker-size);
125
+ border-radius: var(--rt-nav-marker-radius);
126
+ background-color: var(--rt-nav-marker-color);
127
+ }
128
+
129
+ &__compact-slot {
130
+ display: flex;
131
+ min-width: 0;
132
+ align-items: center;
133
+
134
+ &--left {
135
+ flex: 0 1 auto;
136
+ }
137
+
138
+ &--center {
139
+ flex: 1 1 auto;
140
+ justify-content: center;
141
+ }
142
+
143
+ &--right {
144
+ flex: 0 0 auto;
145
+ margin-left: auto;
146
+ }
147
+ }
148
+ }
149
+ }
@@ -0,0 +1,46 @@
1
+ @layer rt-kit.components {
2
+ /* Позиции здесь нет намеренно. Блок висит на каждой кнопке кита, и `position: relative`,
3
+ поставленный всем подряд, меняет растеризацию у восемнадцати кадров витрины, ничего не
4
+ двигая в раскладке. Позицию ставит сама директива — тому элементу, который нажали, и
5
+ только если он до сих пор `static`. */
6
+ .rt-ripple {
7
+ --rt-ripple-color: currentcolor;
8
+ --rt-ripple-opacity: 0.24;
9
+ --rt-ripple-duration: 550ms;
10
+ --rt-ripple-radius: var(--rt-radius-full);
11
+ }
12
+
13
+ /* Обрезает волну по форме элемента. Отдельным слоем, а не overflow на самом
14
+ элементе: у кнопки-значка метка вынесена за край, и она была бы срезана. */
15
+ .rt-ripple__area {
16
+ position: absolute;
17
+ overflow: hidden;
18
+ border-radius: inherit;
19
+ inset: 0;
20
+ pointer-events: none;
21
+ }
22
+
23
+ .rt-ripple__wave {
24
+ position: absolute;
25
+ border-radius: var(--rt-ripple-radius);
26
+ animation: rt-ripple-spread var(--rt-ripple-duration) ease-out forwards;
27
+ background-color: var(--rt-ripple-color);
28
+ opacity: var(--rt-ripple-opacity);
29
+ transform: scale(0);
30
+ }
31
+
32
+ /* Волна гаснет вместе с остальным движением: тот, кто попросил систему не
33
+ двигать интерфейс, получает подсветку без расхождения круга. */
34
+ @media (prefers-reduced-motion: reduce) {
35
+ .rt-ripple__wave {
36
+ animation-duration: 1ms;
37
+ }
38
+ }
39
+
40
+ @keyframes rt-ripple-spread {
41
+ to {
42
+ opacity: 0;
43
+ transform: scale(1);
44
+ }
45
+ }
46
+ }
@@ -9,9 +9,9 @@
9
9
  overflow: hidden;
10
10
  background: linear-gradient(
11
11
  90deg,
12
- var(--rt-color-bg-subtle) 0%,
13
- var(--rt-color-border-default) 50%,
14
- var(--rt-color-bg-subtle) 100%
12
+ var(--rt-color-skeleton-base) 0%,
13
+ var(--rt-color-skeleton-wave) 50%,
14
+ var(--rt-color-skeleton-base) 100%
15
15
  );
16
16
  background-size: 200% 100%;
17
17
 
@@ -30,6 +30,8 @@
30
30
  | `titleTemplate` | `TemplateRef \| null` | `null` |
31
31
  | `icon` / `iconColor` | `IRtIcon.Name \| null` / `IRtTabs.TitleColor` | `null` / `'current'` |
32
32
  | `badge` | `string \| number \| null` | `null` |
33
+ | `badgeTitle` | `string` — подсказка при наведении на метку | `''` |
34
+ | `badgeAriaLabel` | `string` — подпись метки для чтения с экрана | `''` |
33
35
  | `disabled` / `hidden` / `invalid` | `boolean` | `false` |
34
36
  | `invalidMessage` | `string` | `''` → `uiTabInvalid` |
35
37
 
@@ -57,5 +59,7 @@
57
59
 
58
60
  - `hidden` убирает вкладку из шапки целиком; `disabled` оставляет её видимой, но некликабельной.
59
61
  - У невалидной вкладки появляется подсказка с текстом `invalidMessage`.
62
+ - Метка без `badgeTitle` и `badgeAriaLabel` рисуется как прежде: голое значение, без подсказки и
63
+ без подписи. Подсказка метки — своя, на самой метке: подсказка кнопки занята неверной вкладкой.
60
64
  - Стрелки прокрутки шапки есть в разметке всегда — показывает их модификатор `--visible`,
61
65
  который выставляется по замеру ширины после отрисовки.
@@ -13,7 +13,7 @@
13
13
  (openInNewTab)="openInTab($event)"
14
14
  (searchChange)="search($event)"
15
15
  (loadMore)="next()">
16
- <ng-template rtThreadListRow let-row>…</ng-template>
16
+ <ng-template let-row [rtThreadListRow]="rows()">…</ng-template>
17
17
  <ng-template rtThreadListFilters>…</ng-template>
18
18
  </rt-thread-list>
19
19
  ```
@@ -22,6 +22,11 @@
22
22
  `filtersActive`. Выходы: `selectRow`, `openInNewTab`, `searchChange`, `loadMore`.
23
23
 
24
24
  Строка обязана иметь `id`, `hasUnread` и (необязательно) `overdue` — это всё, что список о ней знает.
25
+ Номер строки — число или строка: у записей домена ключ не всегда числовой. Тем же типом приходит
26
+ `activeId` и уходят `selectRow` и `openInNewTab`.
27
+
28
+ Тот же массив строк передаётся входом `[rtThreadListRow]` в шаблон строки: из него выводится тип
29
+ `row`. Без этого входа строгая сборка приложения отказывает — атрибут без значения приходит строкой.
25
30
 
26
31
  ## Главное, что нужно знать
27
32
 
@@ -0,0 +1,240 @@
1
+ @use './breakpoints' as bp;
2
+
3
+ /* Словарь формы панели: раздел, колонка контролов, строка контрола, элемент в ряду. Объявлен
4
+ классами, а не компонентом, поэтому доехать до потребителя может только через агрегатор
5
+ стилей. Иерархия одна на кит: до неё каждое приложение заводило свою, и одно имя значило в
6
+ двух деревьях разное — `controls` было и колонкой контролов раздела, и полосой кнопок внизу
7
+ панели.
8
+
9
+ Блок формы объявляется ровно один раз на панель и стоит НАД полосой вкладок: вкладки лежат
10
+ внутри формы, а не наоборот. Носителем бывает и `<form>`, и `<div>` — форма Angular и блок
11
+ формы разные вещи, и `[formGroup]` законно живёт ниже, обёрткой над одной строкой контрола.
12
+
13
+ Промежуток задаёт контейнер своим зазором, а своего отступа нет ни у одного элемента. Так у
14
+ каждого промежутка один источник: зазор не достаётся первому ребёнку, не складывается с
15
+ зазором зоны содержимого панели и действует на проецируемое содержимое наравне со своей
16
+ разметкой — а правило соседства знает только те имена, которые перечислены в нём самом. */
17
+
18
+ @layer rt-kit.components {
19
+ .rt-form {
20
+ display: flex;
21
+ width: 100%;
22
+ flex: 1 1 100%;
23
+ flex-direction: column;
24
+ gap: var(--rt-space-xl);
25
+
26
+ /* Раздел: смысловая группа полей. Вкладывается в себя — цвет внутри сезона остаётся
27
+ разделом, а не заводит третье имя. */
28
+ &__item {
29
+ display: flex;
30
+ width: 100%;
31
+ flex-direction: column;
32
+ gap: var(--rt-space-xl);
33
+ }
34
+
35
+ /* Колонка контролов раздела: всё, что в разделе выстроено сверху вниз. Её зазор — ритм
36
+ строк, и он же отбивает заголовок раздела от первой строки. */
37
+ &__controls {
38
+ position: relative;
39
+ display: flex;
40
+ width: 100%;
41
+ flex-direction: column;
42
+ gap: var(--rt-space-md);
43
+ }
44
+
45
+ /* Заголовок раздела и описание под ним. Оба живут внутри колонки контролов: подпись
46
+ принадлежит столбцу, а не разделу целиком. */
47
+ &__title {
48
+ margin: 0;
49
+ color: var(--rt-color-text-primary);
50
+ font-size: var(--rt-text-md);
51
+ font-weight: var(--rt-font-weight-semibold);
52
+ line-height: var(--rt-leading-snug);
53
+ }
54
+
55
+ &__title-descr {
56
+ margin: 0;
57
+ color: var(--rt-color-text-muted);
58
+ font-size: var(--rt-text-sm);
59
+ line-height: var(--rt-leading-normal);
60
+ }
61
+
62
+ /* Строка контрола: один ряд колонки. Поле занимает строку целиком — иначе каждый
63
+ потребитель дописывает ширину сам. Зазор строки разводит элементы в ряду. */
64
+ &__control-item {
65
+ display: flex;
66
+ width: 100%;
67
+ align-items: center;
68
+ justify-content: flex-start;
69
+ gap: var(--rt-space-xl);
70
+
71
+ /* Поле, стоящее в строке одно, занимает её целиком. Без этого оно получает ширину
72
+ по содержимому и обрывается посреди строки, оставляя справа пустое место. */
73
+ > rt-field {
74
+ min-width: 0;
75
+ flex: 1 1 auto;
76
+ }
77
+ }
78
+
79
+ /* Строка, разведённая по краям: подпись у начала, значение у конца. Так стоит
80
+ карточка просмотра — записи в ней показывают, а не правят. */
81
+ &__control-item--split {
82
+ justify-content: space-between;
83
+ }
84
+
85
+ /* Строка колонкой: подпись сверху, контрол под ней. Берётся там, где подпись рядом с
86
+ контролом не встаёт — в узкой панели три состояния права рядом с его именем
87
+ переносились бы по слогам. */
88
+ &__control-item--stack {
89
+ flex-direction: column;
90
+ align-items: stretch;
91
+ gap: var(--rt-space-xs);
92
+ }
93
+
94
+ /* Элемент в ряду: два и больше контролов, стоящих рядом в одной строке. Нулевая нижняя
95
+ граница ширины обязательна: без неё длинное значение не даёт строке перенестись, и
96
+ соседи сжимаются до столбика по одной букве. */
97
+ &__control-sub-item {
98
+ display: flex;
99
+ min-width: 0;
100
+ align-items: center;
101
+ justify-content: flex-start;
102
+
103
+ /* Поле внутри элемента ряда занимает его целиком. Без этого элемент растянут своим
104
+ видом, а поле в нём стоит по ширине содержимого: в панели внешней брони шесть
105
+ полей занимали меньше половины своей колонки. */
106
+ > rt-field {
107
+ min-width: 0;
108
+ flex: 1 1 auto;
109
+ }
110
+ }
111
+
112
+ /* Подпись в паре «подпись — значение»: приглушённый цвет отличает её от значения. */
113
+ &__control-sub-item--label {
114
+ color: var(--rt-color-text-muted);
115
+ font-size: var(--rt-text-sm);
116
+ }
117
+
118
+ /* Значение рядом с подписью: длинное переносится в любом месте — адрес почты и имя
119
+ процедуры по словам не переносятся. */
120
+ &__control-sub-item--value {
121
+ color: var(--rt-color-text-primary);
122
+ font-size: var(--rt-text-sm);
123
+ overflow-wrap: anywhere;
124
+ }
125
+
126
+ /* Элемент, делящий ширину строки поровну с соседями: пара дат встаёт одной ширины, а не
127
+ по ширине своего содержимого. */
128
+ &__control-sub-item--grow {
129
+ flex: 1;
130
+ }
131
+
132
+ /* Значение снятой записи: адреса, которого больше нет. */
133
+ &__control-sub-item--gone {
134
+ color: var(--rt-color-text-muted);
135
+ }
136
+
137
+ /* Приписка о неудаче: приёмник запрос не принял. */
138
+ &__control-sub-item--warning {
139
+ color: var(--rt-color-state-warning);
140
+ }
141
+
142
+ /* Подпись над контролом — там, где своей подписи у контрола нет. */
143
+ &__label {
144
+ display: block;
145
+ color: var(--rt-color-text-primary);
146
+ font-size: var(--rt-text-sm);
147
+ font-weight: var(--rt-font-weight-medium);
148
+ }
149
+
150
+ /* Ссылка на связанную запись: обычная ссылка с иконкой перехода. */
151
+ &__related {
152
+ display: inline-flex;
153
+ align-items: center;
154
+ align-self: flex-start;
155
+ color: var(--rt-color-text-link);
156
+ font-size: var(--rt-text-sm);
157
+ gap: var(--rt-space-xs);
158
+ }
159
+
160
+ /* Полоса меток записи: их бывает много, и в одну строку они не влезают. */
161
+ &__tags {
162
+ display: flex;
163
+ flex-flow: row wrap;
164
+ align-items: center;
165
+ gap: var(--rt-space-xs);
166
+ }
167
+
168
+ /* Строки одной службы в общей рамке: ключей у службы бывает два, и рамка на каждом
169
+ читалась бы как две разные карточки. */
170
+ &__framed-list {
171
+ display: flex;
172
+ width: 100%;
173
+ flex-direction: column;
174
+ border: var(--rt-border-width-thin) solid var(--rt-color-border-subtle);
175
+ border-radius: var(--rt-radius-md);
176
+
177
+ > * + * {
178
+ border-top: var(--rt-border-width-thin) solid var(--rt-color-border-subtle);
179
+ }
180
+ }
181
+
182
+ /* Длинное тело запроса или ответа: по словам оно не переносится и прокручивается внутри
183
+ своего блока, чтобы не растягивать панель. */
184
+ &__code {
185
+ overflow: auto;
186
+ padding: var(--rt-space-xs);
187
+ border-radius: var(--rt-radius-sm);
188
+ margin: 0;
189
+ background: var(--rt-color-bg-surface-subtle);
190
+ color: var(--rt-color-text-primary);
191
+ font-size: var(--rt-text-xs);
192
+ max-block-size: 12rem;
193
+ overflow-wrap: anywhere;
194
+ white-space: pre-wrap;
195
+ }
196
+
197
+ /* Просмотр готового документа: лист виден целиком, рамка отделяет его от полей. */
198
+ &__preview {
199
+ width: 100%;
200
+ height: 460px;
201
+ flex-shrink: 0;
202
+ border: var(--rt-border-width-thin) solid var(--rt-color-border-subtle);
203
+ border-radius: var(--rt-radius-sm);
204
+ }
205
+
206
+ /* Запись внутри общей рамки: свой внутренний отступ и ритм строк. Без него строки
207
+ записей лежат вплотную к рамке и к соседней записи, и перечень читается сплошной
208
+ лентой слов. */
209
+ &__framed-item {
210
+ display: flex;
211
+ flex-direction: column;
212
+ padding: var(--rt-space-sm) var(--rt-space-md);
213
+ gap: var(--rt-space-xs);
214
+ }
215
+
216
+ /* Маячок догрузки в конце списка: он невидим и только отмечает низ, до которого
217
+ дочитали. */
218
+ &__sentinel {
219
+ block-size: 1px;
220
+ }
221
+
222
+ /* Зона содержимого вкладки внутри формы: разделы лежат в ней, и ритм у них тот же, что
223
+ у разделов вне вкладок. Своим зазором зона вкладок отбивает произвольное содержимое,
224
+ и разделы формы получали от неё чужой шаг — панель с вкладками и панель без них
225
+ показывали одни и те же разделы по-разному. */
226
+ .rt-tabs__content {
227
+ gap: var(--rt-space-xl);
228
+ }
229
+ }
230
+
231
+ /* На узком экране панель занимает весь экран, и ритм разделов сжимается вдвое. Порог тот же,
232
+ что у самой панели. Ритм строк не трогается: строки и на узком идут тем же шагом. */
233
+ @media (width <= #{bp.$rt-bp-md}) {
234
+ .rt-form,
235
+ .rt-form__item,
236
+ .rt-form .rt-tabs__content {
237
+ gap: var(--rt-space-md);
238
+ }
239
+ }
240
+ }
@@ -8,6 +8,12 @@
8
8
 
9
9
  @forward './primitives';
10
10
  @forward './semantic';
11
+
12
+ // Материальный набор стоит между назначениями и тёмной темой: у признака набора и признака
13
+ // тёмной темы на корне одинаковая специфичность, и порядок здесь — единственное, чем тёмная
14
+ // тема выигрывает у набора.
15
+ @forward './preset-material';
16
+
11
17
  @forward './theme-dark';
12
18
  @forward './theme-dark-rules';
13
19
  @forward './scrollbar';
@@ -18,7 +24,17 @@
18
24
  // этого не видят — истории у экрана входа нет.
19
25
  @forward './login';
20
26
 
27
+ // Словарь формы панели: раздел, колонка контролов, строка контрола, элемент в ряду. Он тоже
28
+ // объявлен классами, а не компонентом, и другого пути к потребителю у него нет. Без этой строки
29
+ // приложение размечает панель именами, которым ни одно правило не отвечает, — то есть заводит
30
+ // раскладку у себя, ради чего словарь и переехал в кит.
31
+ @forward './form';
32
+
21
33
  // Стили директивы кнопки: своего представления у директивы нет, поэтому её `.scss` не
22
34
  // подключён ни одним `styleUrl`. Без этой строки потребитель, взявший `tokens.css` по
23
35
  // инструкции README, получает `rtButton`, нарисованный браузерной кнопкой.
24
36
  @forward '../lib/components/button/rt-button.directive';
37
+
38
+ // Стили волны нажатия — по той же причине: директива своего представления не имеет.
39
+ // Без этой строки узел волны ставится в разметку и остаётся невидимым.
40
+ @forward '../lib/components/ripple/rt-ripple.directive';