@rt-tools/ui-kit 0.7.1 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/ui-kit",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Yauheni Krumin",
6
6
  "type": "module",
Binary file
@@ -0,0 +1,123 @@
1
+ :host {
2
+ display: block;
3
+
4
+ // Блок отделён от списка раздела чертой: без неё строка избранного и строка того же пункта
5
+ // в списке стоят подряд и читаются как повтор. Отступ под чертой даёт верхний отступ самого
6
+ // списка, 8 px: свой отступ здесь удвоил бы его. Черта и отступы — те же, что у разделителей
7
+ // пунктов подменю у потребителей: 8 px, приглушённая черта, 8 px.
8
+ &:has(.rtui-side-menu-favorites__list) {
9
+ padding-bottom: var(--rt-side-menu-favorites-divider-gap, var(--rt-spacing-8));
10
+ border-bottom: 1px solid var(--rt-side-menu-favorites-divider-color, var(--rt-border-neutral-subtle));
11
+ }
12
+
13
+ .rtui-side-menu-favorites {
14
+ // Заголовок: подпись и звезда. Звезда красится тем же цветом, что и закрашенные звёзды
15
+ // списка; цвет и размер подписи и звезды приложение задаёт своими свойствами.
16
+ &__title {
17
+ display: flex;
18
+ align-items: center;
19
+ padding: var(--rt-spacing-8) var(--rt-spacing-16) var(--rt-spacing-4);
20
+ color: var(--rt-side-menu-favorites-title-color, var(--rt-text-base-secondary));
21
+ font-size: var(--rt-side-menu-favorites-title-font-size, var(--rt-font-size-sm));
22
+ font-weight: var(--rt-font-weight-semibold);
23
+ gap: var(--rt-spacing-6);
24
+ }
25
+
26
+ &__title-icon {
27
+ width: var(--rt-side-menu-favorites-title-icon-size, var(--rt-icon-size-sm));
28
+ height: var(--rt-side-menu-favorites-title-icon-size, var(--rt-icon-size-sm));
29
+ color: var(
30
+ --rt-side-menu-favorite-color,
31
+ var(--mat-sys-primary, var(--mat-button-filled-container-color, var(--rt-icon-accent-primary)))
32
+ );
33
+ font-size: var(--rt-side-menu-favorites-title-icon-size, var(--rt-icon-size-sm));
34
+ }
35
+
36
+ &__title-text {
37
+ overflow: hidden;
38
+ text-overflow: ellipsis;
39
+ white-space: nowrap;
40
+ }
41
+
42
+ &__list {
43
+ padding: 0;
44
+ }
45
+ }
46
+ }
47
+
48
+ // Строка, ручка и её значок объявлены вне корня блока: строку в руке CDK выносит в оверлей, и
49
+ // правило из корня до неё не доходит — ручка там становилась кнопкой Material по умолчанию,
50
+ // крупнее и другого цвета.
51
+ .rtui-side-menu-favorites {
52
+ &__row {
53
+ display: flex;
54
+ align-items: center;
55
+ }
56
+
57
+ &__handle {
58
+ --mat-icon-button-state-layer-size: var(--rt-side-menu-favorite-action-size, calc(var(--rt-spacing-16) * 2));
59
+ --mat-icon-button-icon-size: var(--rt-side-menu-favorite-action-icon-size, var(--rt-font-size-lg));
60
+ --mat-icon-button-icon-color: var(--rt-side-menu-favorite-action-color, var(--rt-text-base-secondary));
61
+
62
+ flex: 0 0 auto;
63
+ cursor: grab;
64
+ opacity: 0;
65
+
66
+ &:active {
67
+ cursor: grabbing;
68
+ }
69
+
70
+ // Ручка видна так же, как кнопка «убрать»: под наведением и фокусом своей строки, на узком
71
+ // экране и там, где указатель не умеет наводиться, — всегда. Прячется прозрачностью, а не
72
+ // снятием с раскладки: кнопка остаётся на месте и достижима клавишей Tab.
73
+ &--always {
74
+ opacity: 1;
75
+ }
76
+
77
+ @media (hover: none) {
78
+ opacity: 1;
79
+ }
80
+ }
81
+
82
+ // Фокус показывает кнопки только с клавиатуры: после нажатия мышью кнопка держит фокус, и
83
+ // правило по любому фокусу оставляло бы её видимой, когда указатель уже ушёл.
84
+ &__row:hover &__handle,
85
+ &__row:has(:focus-visible) &__handle,
86
+ &__row.cdk-drag-preview &__handle {
87
+ opacity: 1;
88
+ }
89
+
90
+ // Значок берёт размер кнопки: иначе он остаётся в 24 px и съезжает от центра. Поворот глифа
91
+ // приходит числом из настроек меню, своего поворота у стилей нет.
92
+ &__handle-icon {
93
+ width: var(--mat-icon-button-icon-size);
94
+ height: var(--mat-icon-button-icon-size);
95
+ font-size: var(--mat-icon-button-icon-size);
96
+ line-height: 1;
97
+ }
98
+
99
+ &__item {
100
+ min-width: 0;
101
+ flex: 1 1 auto;
102
+ }
103
+ }
104
+
105
+ // На узком экране под блоком не список со своим отступом, а сами пункты раздела вплотную:
106
+ // отступ под чертой блок ставит сам.
107
+ :host-context(.rtui-mobile-side-menu-list__sub):has(.rtui-side-menu-favorites__list) {
108
+ margin-bottom: var(--rt-spacing-8);
109
+ }
110
+
111
+ // Строка в руке живёт в оверлее CDK, вне блока, и правило из корня до неё не доходит. Тень — та же,
112
+ // что у строки в руке динамического селектора: имена шкалы теней кита совпадают со вторым китом.
113
+ .rtui-side-menu-favorites__row.cdk-drag-preview {
114
+ display: flex;
115
+ align-items: center;
116
+ border-radius: var(--rt-radius-xl);
117
+ background-color: var(--rt-side-menu-favorites-drag-background, var(--rt-bg-base-base));
118
+ box-shadow: var(--rt-side-menu-favorites-drag-shadow, 0 0.5rem 1rem 0 var(--rt-shadow-color));
119
+ }
120
+
121
+ .rtui-side-menu-favorites__row.cdk-drag-placeholder {
122
+ opacity: 0;
123
+ }
@@ -320,6 +320,11 @@ $side-menu: (
320
320
  }
321
321
 
322
322
  &--pinned {
323
+ // Закреплённая панель стоит вплотную к странице, и скругления у неё нет: угол срезал
324
+ // нижнюю подсказку прокрутки вместе с её кнопкой. Потребитель по-прежнему задаёт
325
+ // радиус закреплённой панели своим свойством на любом предке меню.
326
+ --rt-side-menu-sub-menu-radius: var(--rt-side-menu-sub-menu-pinned-radius, 0);
327
+
323
328
  position: relative;
324
329
  left: 0;
325
330
  width: 0;
@@ -58,8 +58,13 @@
58
58
  align-items: center;
59
59
  justify-content: space-between;
60
60
 
61
+ // Подпись забирает свободное место строки, и все кнопки встают вплотную к правому краю:
62
+ // кнопка пункта, звезда или «убрать», ручка. Разнос кнопок по ширине разводил их
63
+ // врозь, как только их становилось больше одной.
61
64
  &__text {
62
65
  overflow: hidden;
66
+ min-width: 0;
67
+ flex: 1 1 auto;
63
68
  font-size: var(--rt-side-menu-sub-menu-item-title-font-size);
64
69
  text-overflow: ellipsis;
65
70
  white-space: nowrap;
@@ -94,6 +99,90 @@
94
99
  border-color: var(--rt-border-neutral-emphasis);
95
100
  }
96
101
  }
102
+
103
+ // В разделе с избранным кнопка потребителя стоит последней, в столбце звёзд и ручек:
104
+ // её центр ставится на центр их кнопки — половина их размера минус половина её
105
+ // ширины в 26 px: значок, внутренний отступ и рамка.
106
+ &--favorites .rtui-side-menu-sub-item-title-button {
107
+ --rt-side-menu-sub-item-favorite-column: var(--rt-side-menu-favorite-action-size, calc(var(--rt-spacing-16) * 2));
108
+
109
+ margin-right: calc((var(--rt-side-menu-sub-item-favorite-column) - var(--rt-spacing-24) - var(--rt-spacing-2)) / 2);
110
+ }
111
+
112
+ // Кнопка избранного: звезда у пункта раздела и «убрать» у строки блока. Закрашенная
113
+ // звезда видна всегда: выбранное читается с одного взгляда. Остальные — под
114
+ // наведением и под фокусом своей строки: кнопки на каждой строке длинного списка
115
+ // читались бы шумом. Прячется прозрачностью, а не снятием с раскладки: кнопка остаётся
116
+ // на своём месте и достижима клавишей Tab.
117
+
118
+ // Цвета и размеры кнопок приложение задаёт своими свойствами `--rt-side-menu-favorite-*`
119
+ // на любом предке меню. Свойства не объявлены в ките нарочно: значение по умолчанию стоит
120
+ // запасным в `var()`, а объявленное на кнопке перебило бы значение от предка.
121
+ &__favorite {
122
+ --mat-icon-button-state-layer-size: var(--rt-side-menu-favorite-action-size, calc(var(--rt-spacing-16) * 2));
123
+ --mat-icon-button-icon-size: var(--rt-side-menu-favorite-action-icon-size, var(--rt-font-size-lg));
124
+ --mat-icon-button-icon-color: var(--rt-side-menu-favorite-action-color, var(--rt-text-base-secondary));
125
+
126
+ flex: 0 0 auto;
127
+ margin-left: var(--rt-spacing-4);
128
+ opacity: 0;
129
+
130
+ // Узкий экран отмечает порог сервиса кита, а не медиазапрос: порог настраивается
131
+ // приложением, и число в стилях с ним разошлось бы. На узком экране рисуется своя
132
+ // ветка разметки, и наводиться там нечем.
133
+ &--on,
134
+ &--always {
135
+ opacity: 1;
136
+ }
137
+
138
+ &--on {
139
+ --mat-icon-button-icon-color: var(
140
+ --rt-side-menu-favorite-color,
141
+ var(--mat-sys-primary, var(--mat-button-filled-container-color, var(--rt-icon-accent-primary)))
142
+ );
143
+ }
144
+
145
+ // Корзина под указателем и под фокусом клавиатуры краснеет: нажатие убирает пункт.
146
+ // Приложение ставит свой цвет свойством на предке меню, свой обычный — чтобы не
147
+ // краснела вовсе. Наведение — только где указатель умеет наводиться: на сенсорном
148
+ // экране оно залипает в точке касания, и после «убрать» под пальцем краснела бы
149
+ // корзина соседней строки.
150
+ @media (hover: hover) {
151
+ &--remove:hover {
152
+ --mat-icon-button-icon-color: var(--rt-side-menu-favorite-remove-hover-color, var(--rt-icon-accent-danger));
153
+ }
154
+ }
155
+
156
+ &--remove:focus-visible {
157
+ --mat-icon-button-icon-color: var(--rt-side-menu-favorite-remove-hover-color, var(--rt-icon-accent-danger));
158
+ }
159
+
160
+ // Где указатель не умеет наводиться, кнопке ждать нечего.
161
+ @media (hover: none) {
162
+ opacity: 1;
163
+ }
164
+
165
+ // Зона касания Material шире кнопки и заходит на соседние строки; мыши она не нужна.
166
+ @media (pointer: fine) {
167
+ --mat-icon-button-touch-target-display: none;
168
+ }
169
+ }
170
+
171
+ // Значок берёт размер кнопки. Сам по себе он остаётся в 24 px, а отступ кнопки
172
+ // считается от её размера значка — и глиф съезжает влево вверх на разницу.
173
+ &__favorite-icon {
174
+ width: var(--mat-icon-button-icon-size);
175
+ height: var(--mat-icon-button-icon-size);
176
+ font-size: var(--mat-icon-button-icon-size);
177
+ line-height: 1;
178
+ }
179
+ }
180
+
181
+ // Фокус показывает кнопки только с клавиатуры: снятая мышью звезда держит фокус, и правило
182
+ // по любому фокусу оставляло бы полую звезду видимой, когда указатель уже ушёл.
183
+ &:hover .rtui-side-menu-sub-item-title__favorite,
184
+ &:has(:focus-visible) .rtui-side-menu-sub-item-title__favorite {
185
+ opacity: 1;
97
186
  }
98
187
  }
99
188
 
@@ -116,6 +205,14 @@
116
205
  max-width: 100%;
117
206
  padding: 0;
118
207
 
208
+ // В разделе с избранным шеврон папки стоит в одном столбце со звёздами: центр значка
209
+ // кнопки звезды левее центра шеврона на половину её размера без половины шеврона в 24 px.
210
+ // Сдвиг — внешним отступом: раскрытая папка поворачивает значок вместе с его внутренними
211
+ // отступами, и неравный внутренний отступ уходил бы на другую сторону.
212
+ &--favorites ::ng-deep .mat-expansion-indicator {
213
+ margin-right: calc(var(--rt-side-menu-favorite-action-size, calc(var(--rt-spacing-16) * 2)) / 2 - var(--rt-spacing-12));
214
+ }
215
+
119
216
  &--activated {
120
217
  background-color: var(--mat-list-active-indicator-color);
121
218
  }
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Type, OnInit, Signal, ElementRef, WritableSignal, TemplateRef, InputSignal, InjectionToken, InputSignalWithTransform, OutputEmitterRef, ModelSignal, AfterViewChecked, AfterViewInit, Provider, AfterContentChecked, OnDestroy } from '@angular/core';
2
+ import { Type, OnInit, Signal, ElementRef, WritableSignal, TemplateRef, InputSignal, InjectionToken, InputSignalWithTransform, OutputEmitterRef, EnvironmentProviders, ModelSignal, AfterViewChecked, AfterViewInit, Provider, AfterContentChecked, OnDestroy } from '@angular/core';
3
3
  import { FormControl, ControlValueAccessor, Validator, FormGroup, ValidationErrors } from '@angular/forms';
4
4
  import { ThemePalette } from '@angular/material/core';
5
5
  import { Observable, Subject } from 'rxjs';
@@ -106,7 +106,7 @@ declare class RtModalService {
106
106
  confirm<T>(data: IModal.Data<T>, config?: MatDialogConfig): Observable<TNullable<IModal.DataAnswer<T>>>;
107
107
  with<T>(data: IModal.Data<T>, config?: MatDialogConfig): IModal.ConfirmResponse<T>;
108
108
  static ɵfac: i0.ɵɵFactoryDeclaration<RtModalService, never>;
109
- static ɵprov: i0.ɵɵInjectableDeclaration<RtModalService>;
109
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
110
110
  }
111
111
 
112
112
  declare class RtuiScrollableContainerHeaderDirective {
@@ -173,10 +173,21 @@ declare class RtuiScrollableContainerComponent {
173
173
  * ровно то, что человек искал.
174
174
  */
175
175
  interface IRtuiSideMenuHost {
176
+ /** Пункты полосы: блок избранного ищет в их подменю пункты, стоящие за номерами списка. */
177
+ readonly menuItems: Signal<ISideMenu.Item[]>;
176
178
  readonly activeMenuIds: Signal<Array<string | number>>;
179
+ /**
180
+ * Набор, который подменю показывает сейчас, до отбора поиском: пустой, пока подменю нечего
181
+ * показать. По нему блок избранного и звёзды узнают свой пункт полосы.
182
+ */
183
+ readonly shownSubMenu: Signal<ISideMenu.Item[]>;
177
184
  readonly expandedMenuIds: Signal<Array<string | number>>;
178
185
  readonly highlightedMenuId: Signal<string | number | null>;
179
186
  readonly subMenuQuery: Signal<string>;
187
+ /** Номер меню, под которым лежат его настройки: избранное читается и пишется по нему. */
188
+ readonly menuId: Signal<string>;
189
+ /** Указатель ушёл с панели; без пункта — то же, что уход мышью. */
190
+ toggleSubMenu(item?: ISideMenu.Item): void;
180
191
  }
181
192
  declare const RTUI_SIDE_MENU: InjectionToken<IRtuiSideMenuHost>;
182
193
  declare namespace ISideMenu {
@@ -186,12 +197,26 @@ declare namespace ISideMenu {
186
197
  * стоит открытым, пока человек сам его не свернёт.
187
198
  */
188
199
  type SubMenuMode = 'hover' | 'pinned';
200
+ /** Номер пункта в списке избранного — тот же, что `id` пункта меню. */
201
+ type FavoriteId = Item['id'];
202
+ /**
203
+ * Настройки одного меню под его номером в хранилище. Незнакомые поля кит не удаляет: их могло
204
+ * положить приложение или следующая версия кита.
205
+ */
206
+ interface Settings {
207
+ favorites?: FavoriteId[];
208
+ subMenuMode?: SubMenuMode;
209
+ subMenuWidth?: number;
210
+ [field: string]: unknown;
211
+ }
189
212
  interface Item {
190
213
  id: string | number;
191
214
  icon?: string;
192
215
  name?: string;
193
216
  link?: string;
194
217
  submenu?: Item[];
218
+ /** Избранное в подменю этого пункта полосы. По умолчанию выключено. */
219
+ favorites?: boolean;
195
220
  iconButton?: {
196
221
  icon: string;
197
222
  data?: ItemData;
@@ -270,7 +295,7 @@ declare class RtuiSideMenuFooterDirective {
270
295
  static ɵfac: i0.ɵɵFactoryDeclaration<RtuiSideMenuFooterDirective, never>;
271
296
  static ɵdir: i0.ɵɵDirectiveDeclaration<RtuiSideMenuFooterDirective, "[rtuiSideMenuFooter]", never, {}, {}, never, never, true, never>;
272
297
  }
273
- declare class RtuiSideMenuComponent {
298
+ declare class RtuiSideMenuComponent implements IRtuiSideMenuHost {
274
299
  #private;
275
300
  /**
276
301
  * Натянутая ширина панели. Своего выбора нет — переменная не ставится вовсе, и ширину берёт
@@ -323,27 +348,25 @@ declare class RtuiSideMenuComponent {
323
348
  readonly backToMainMenuButton: Signal<ISideMenu.Item>;
324
349
  readonly selectedItem: WritableSignal<TNullable<ISideMenu.Item>>;
325
350
  readonly selectedSubMenu: WritableSignal<TNullable<ISideMenu.Item[]>>;
351
+ /** Набор, который подменю наполняет сейчас, до отбора поиском; по нему избранное узнаёт свой раздел. */
352
+ readonly shownSubMenu: Signal<ISideMenu.Item[]>;
326
353
  activeMenuIds: InputSignal<Array<string | number>>;
327
354
  menuItems: InputSignalWithTransform<ISideMenu.Item[], ISideMenu.Item[]>;
328
- /** Мода подменю. Умолчаниесегодняшнее поведение: открывается наведением. */
329
- subMenuMode: InputSignal<ISideMenu.SubMenuMode>;
330
- /**
331
- * Ширина закреплённого подменю в пикселях. Пустая — ширину ставит оформление; хранит выбор
332
- * потребитель, как и моду.
333
- */
334
- subMenuWidth: InputSignal<number | null>;
355
+ /** Мода подменю от приложения. Задана важнее сохранённой; нет ни той, ни другой — наведение. */
356
+ subMenuMode: InputSignal<ISideMenu.SubMenuMode | undefined>;
357
+ /** Ширина закреплённого подменю в пикселях от приложения. Пусто — берётся сохранённая, её нет — оформление. */
358
+ subMenuWidth: InputSignal<number | null | undefined>;
359
+ /** Под каким номером меню хранит свои настройки: у двух меню приложения — два номера. */
360
+ menuId: InputSignalWithTransform<string, string | null | undefined>;
335
361
  isSubMenuXScrollEnabled: InputSignalWithTransform<boolean, boolean>;
336
362
  isMainMenuIconsOutlined: InputSignalWithTransform<boolean, boolean>;
337
363
  isSubMenuIconsOutlined: InputSignalWithTransform<boolean, boolean>;
338
364
  isSubMenuButtonIconsOutlined: InputSignalWithTransform<boolean, boolean>;
339
365
  isSubMenuTooltipsShown: InputSignalWithTransform<boolean, boolean>;
340
366
  activeMenuId: Signal<number | string>;
341
- /**
342
- * Просьба о моде, а не смена моды: предпочтение хранит потребитель кита и возвращает его
343
- * входом. Своего состояния о нём меню не заводит.
344
- */
367
+ /** Выбранная мода: меню пишет её в настройки, если они включены, и сообщает приложению. */
345
368
  readonly subMenuModeChange: OutputEmitterRef<ISideMenu.SubMenuMode>;
346
- /** Просьба о ширине по той же причине, что и просьба о моде: своего состояния меню не держит. */
369
+ /** Натянутая ширинатак же, как мода. */
347
370
  readonly subMenuWidthChange: OutputEmitterRef<number>;
348
371
  readonly closeMobileMenuAction: OutputEmitterRef<void>;
349
372
  readonly clickSubMenuAction: OutputEmitterRef<{
@@ -367,12 +390,9 @@ declare class RtuiSideMenuComponent {
367
390
  * указателя нельзя — закрывает нажатие снаружи, то есть подложка под незакреплённой панелью.
368
391
  */
369
392
  onSearchHold(): void;
370
- /** Нажат переключатель моды: наружу уходит просьба, вход остаётся прежним. */
393
+ /** Нажат переключатель моды: выбор уходит в настройки меню и наружу. */
371
394
  onSubMenuModeToggle(): void;
372
- /**
373
- * Взята ручка правого края. Слушатели вешаются на документ: рука уходит с узкой полоски
374
- * ручки в первое же движение, и слушатель на ней самой терял бы тягу сразу.
375
- */
395
+ /** Взята ручка правого края закреплённого подменю. */
376
396
  onResizeStart(event: MouseEvent): void;
377
397
  onSubMenuSearch(query: string): void;
378
398
  /**
@@ -387,9 +407,115 @@ declare class RtuiSideMenuComponent {
387
407
  event: MouseEvent;
388
408
  }): void;
389
409
  static ɵfac: i0.ɵɵFactoryDeclaration<RtuiSideMenuComponent, never>;
390
- static ɵcmp: i0.ɵɵComponentDeclaration<RtuiSideMenuComponent, "rtui-side-menu", never, { "activeMenuIds": { "alias": "activeMenuIds"; "required": true; "isSignal": true; }; "menuItems": { "alias": "menuItems"; "required": false; "isSignal": true; }; "subMenuMode": { "alias": "subMenuMode"; "required": false; "isSignal": true; }; "subMenuWidth": { "alias": "subMenuWidth"; "required": false; "isSignal": true; }; "isSubMenuXScrollEnabled": { "alias": "isSubMenuXScrollEnabled"; "required": false; "isSignal": true; }; "isMainMenuIconsOutlined": { "alias": "isMainMenuIconsOutlined"; "required": false; "isSignal": true; }; "isSubMenuIconsOutlined": { "alias": "isSubMenuIconsOutlined"; "required": false; "isSignal": true; }; "isSubMenuButtonIconsOutlined": { "alias": "isSubMenuButtonIconsOutlined"; "required": false; "isSignal": true; }; "isSubMenuTooltipsShown": { "alias": "isSubMenuTooltipsShown"; "required": false; "isSignal": true; }; }, { "subMenuModeChange": "subMenuModeChange"; "subMenuWidthChange": "subMenuWidthChange"; "closeMobileMenuAction": "closeMobileMenuAction"; "clickSubMenuAction": "clickSubMenuAction"; "clickSubMenuAdditionalAction": "clickSubMenuAdditionalAction"; }, ["headerTpl", "footerTpl"], never, true, never>;
410
+ static ɵcmp: i0.ɵɵComponentDeclaration<RtuiSideMenuComponent, "rtui-side-menu", never, { "activeMenuIds": { "alias": "activeMenuIds"; "required": true; "isSignal": true; }; "menuItems": { "alias": "menuItems"; "required": false; "isSignal": true; }; "subMenuMode": { "alias": "subMenuMode"; "required": false; "isSignal": true; }; "subMenuWidth": { "alias": "subMenuWidth"; "required": false; "isSignal": true; }; "menuId": { "alias": "menuId"; "required": false; "isSignal": true; }; "isSubMenuXScrollEnabled": { "alias": "isSubMenuXScrollEnabled"; "required": false; "isSignal": true; }; "isMainMenuIconsOutlined": { "alias": "isMainMenuIconsOutlined"; "required": false; "isSignal": true; }; "isSubMenuIconsOutlined": { "alias": "isSubMenuIconsOutlined"; "required": false; "isSignal": true; }; "isSubMenuButtonIconsOutlined": { "alias": "isSubMenuButtonIconsOutlined"; "required": false; "isSignal": true; }; "isSubMenuTooltipsShown": { "alias": "isSubMenuTooltipsShown"; "required": false; "isSignal": true; }; }, { "subMenuModeChange": "subMenuModeChange"; "subMenuWidthChange": "subMenuWidthChange"; "closeMobileMenuAction": "closeMobileMenuAction"; "clickSubMenuAction": "clickSubMenuAction"; "clickSubMenuAdditionalAction": "clickSubMenuAdditionalAction"; }, ["headerTpl", "footerTpl"], never, true, never>;
391
411
  }
392
412
 
413
+ /**
414
+ * Ключ настроек бокового меню в хранилище.
415
+ *
416
+ * Назван приставкой кита: хранилище браузера общее на весь адрес, и короткое имя столкнулось бы с
417
+ * ключом потребителя молча. Под ключом — один объект, в нём настройки каждого меню под его номером.
418
+ */
419
+ declare const SIDE_MENU_SETTINGS_KEY: string;
420
+ /** Номер меню, которому приложение своего не задало. */
421
+ declare const DEFAULT_MENU_ID: string;
422
+ /** Запись хранилища как есть: номер меню — и что под ним лежит, в том числе сломанное. */
423
+ type TSideMenuSettingsRecord = Record<string, unknown>;
424
+
425
+ /** Подписи избранного. У первого кита нет словаря, и приложение на другом языке называет их само. */
426
+ interface IRtuiSideMenuFavoritesLabels {
427
+ readonly title: string;
428
+ readonly add: string;
429
+ readonly remove: string;
430
+ readonly drag: string;
431
+ }
432
+ /** Значок кнопки избранного: глиф набора Material Symbols и поворот. */
433
+ interface IRtuiSideMenuFavoritesIcon {
434
+ readonly glyph: string;
435
+ /** Градусы по часовой стрелке; не задан — ноль. */
436
+ readonly rotate?: number;
437
+ }
438
+ interface IRtuiSideMenuFavoritesIcons {
439
+ readonly remove: IRtuiSideMenuFavoritesIcon;
440
+ readonly drag: IRtuiSideMenuFavoritesIcon;
441
+ }
442
+ interface IRtuiSideMenuSettingsConfig {
443
+ /** Ключ записи в хранилище; два приложения на одном адресе держат свои настройки двумя ключами. */
444
+ readonly storageKey?: string;
445
+ readonly labels?: Partial<IRtuiSideMenuFavoritesLabels>;
446
+ readonly icons?: Partial<IRtuiSideMenuFavoritesIcons>;
447
+ }
448
+ declare const RTUI_SIDE_MENU_SETTINGS_CONFIG: InjectionToken<IRtuiSideMenuSettingsConfig>;
449
+ /**
450
+ * Настройки боковых меню приложения: избранное, мода подменю и его ширина.
451
+ *
452
+ * В хранилище под одним ключом лежит объект: у каждого меню — свои настройки под его номером.
453
+ * Номер задаёт приложение входом `menuId` меню — например, номером пользователя, — меню без номера
454
+ * или с пустым номером хранится под `default`. Приложение читает и пишет настройки любого меню по
455
+ * его номеру.
456
+ *
457
+ * Запись начинается с чтения хранилища, а не копии в памяти: другая вкладка или другой код могли
458
+ * записать своё после того, как сервис прочитал ключ, и запись из копии затёрла бы их. Правится одно
459
+ * поле одного меню, остальное уходит как лежало. Изменения из других вкладок приходят событием
460
+ * `storage` и обновляют сигналы. Кит сам ничего не удаляет: настройки меню уходят только по вызову
461
+ * приложения.
462
+ *
463
+ * Держатель один: меню и приложение читают и пишут через этот же сервис. Поэтому сервис ставится
464
+ * провайдером окружения — `provideRtuiSideMenuSettings()`, — а меню берёт его необязательно: не
465
+ * поставлен — меню ничего не хранит, звёзд нет, мода и ширина приходят входами.
466
+ *
467
+ * Хранилище берётся токеном `@rt-tools/core`. Без него, вне браузера, закрытое или полное —
468
+ * настройки живут в памяти, и ни чтение, ни запись не бросают.
469
+ */
470
+ declare class RtuiSideMenuSettingsService {
471
+ #private;
472
+ /** Номера меню, чьи настройки лежат в хранилище. */
473
+ readonly menuIds: Signal<string[]>;
474
+ readonly labels: IRtuiSideMenuFavoritesLabels;
475
+ /** Заданный приложением значок заменяет свой целиком, незаданный остаётся по умолчанию. */
476
+ readonly icons: IRtuiSideMenuFavoritesIcons;
477
+ constructor();
478
+ /** Настройки одного меню целиком, с незнакомыми полями; не сохранены — пустой объект. */
479
+ settings(menuId: string): Signal<ISideMenu.Settings>;
480
+ /** Список избранного одного меню. Сигнал на номер один: повторный вызов отдаёт тот же. */
481
+ ids(menuId: string): Signal<ReadonlyArray<ISideMenu.FavoriteId>>;
482
+ /** Мода подменю, которую выбрал человек; ничего не выбрано — открытие наведением. */
483
+ subMenuMode(menuId: string): Signal<ISideMenu.SubMenuMode>;
484
+ /** Ширина закреплённого подменю; ничего не выбрано — пусто, и ширину ставит оформление. */
485
+ subMenuWidth(menuId: string): Signal<number | null>;
486
+ setSubMenuMode(menuId: string, mode: ISideMenu.SubMenuMode): void;
487
+ /** Пишется приведённая к пределам: за них уводит и рука, и чужая правка записи. */
488
+ setSubMenuWidth(menuId: string, width: number): void;
489
+ has(menuId: string, id: ISideMenu.FavoriteId): boolean;
490
+ /** Номер, уже стоящий в списке, остаётся на своём месте. */
491
+ add(menuId: string, id: ISideMenu.FavoriteId): void;
492
+ remove(menuId: string, id: ISideMenu.FavoriteId): void;
493
+ /** Решает по записи хранилища, а не по копии: другая вкладка могла уже поменять список. */
494
+ toggle(menuId: string, id: ISideMenu.FavoriteId): void;
495
+ /** Перенос по местам списка, не блока: место вне списка у источника ничего не меняет. */
496
+ move(menuId: string, from: number, to: number): void;
497
+ /**
498
+ * Перенос по местам видимых номеров, как в блоке меню: видимые меняются местами между собой,
499
+ * скрытые — пункт которых меню сейчас не показывает — остаются на своих.
500
+ */
501
+ moveVisible(menuId: string, visibleIds: ReadonlyArray<ISideMenu.FavoriteId>, from: number, to: number): void;
502
+ /** Замена списка целиком — чужие значения и повторы отбрасываются так же, как при чтении. */
503
+ set(menuId: string, ids: ReadonlyArray<ISideMenu.FavoriteId>): void;
504
+ clear(menuId: string): void;
505
+ /** Удаление всех настроек одного меню. Сам кит его не вызывает: удаляет только приложение. */
506
+ deleteSettings(menuId: string): void;
507
+ static ɵfac: i0.ɵɵFactoryDeclaration<RtuiSideMenuSettingsService, never>;
508
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
509
+ }
510
+ /**
511
+ * Включение настроек меню: сервис и его настройки в инжектор окружения приложения.
512
+ *
513
+ * ```ts
514
+ * bootstrapApplication(App, { providers: [provideRtStorage(), provideRtuiSideMenuSettings({ storageKey: 'my-app-side-menu' })] });
515
+ * ```
516
+ */
517
+ declare function provideRtuiSideMenuSettings(config?: IRtuiSideMenuSettingsConfig): EnvironmentProviders;
518
+
393
519
  declare class RtuiSpinnerComponent implements OnInit {
394
520
  #private;
395
521
  diameter: InputSignalWithTransform<number, number>;
@@ -477,7 +603,7 @@ declare class RtAsideService {
477
603
  */
478
604
  open<COMPONENT = null, DATA = null, ANSWER = null>(component: ComponentType$1<COMPONENT>, position: TAsidePositions, data: DATA, config?: IAsideConfig): Observable<ANSWER | null>;
479
605
  static ɵfac: i0.ɵɵFactoryDeclaration<RtAsideService, never>;
480
- static ɵprov: i0.ɵɵInjectableDeclaration<RtAsideService>;
606
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
481
607
  }
482
608
 
483
609
  declare class RtuiAsideContainerHeaderDirective {
@@ -1288,7 +1414,7 @@ declare class RtTableConfigService<ENTITY_TYPE> {
1288
1414
  updateConfig(storageKey: string, config: ITable.Config.Data<ENTITY_TYPE>): void;
1289
1415
  deleteConfig(storageKey: string): void;
1290
1416
  static ɵfac: i0.ɵɵFactoryDeclaration<RtTableConfigService<any>, never>;
1291
- static ɵprov: i0.ɵɵInjectableDeclaration<RtTableConfigService<any>>;
1417
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1292
1418
  }
1293
1419
 
1294
1420
  declare class RtCommonSelectorsDirective<ENTITY_TYPE extends Record<string, unknown>, KEY extends Extract<keyof ENTITY_TYPE, string>> {
@@ -1450,7 +1576,7 @@ declare class RtSnackBarService {
1450
1576
  success(message: string, config?: IRtSnackBar.Config): MatSnackBarRef<RtuiSnackBarComponent>;
1451
1577
  dismiss(): void;
1452
1578
  static ɵfac: i0.ɵɵFactoryDeclaration<RtSnackBarService, never>;
1453
- static ɵprov: i0.ɵɵInjectableDeclaration<RtSnackBarService>;
1579
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1454
1580
  }
1455
1581
 
1456
1582
  declare enum EInfoBadgeSize {
@@ -2040,7 +2166,7 @@ declare class RtActionBarService {
2040
2166
  setCounts(selected: number, total: number): void;
2041
2167
  closeActionBar(): void;
2042
2168
  static ɵfac: i0.ɵɵFactoryDeclaration<RtActionBarService, never>;
2043
- static ɵprov: i0.ɵɵInjectableDeclaration<RtActionBarService>;
2169
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
2044
2170
  }
2045
2171
 
2046
2172
  /**
@@ -2078,7 +2204,7 @@ declare class RtThemeService {
2078
2204
  */
2079
2205
  registerColorScheme(name: string, ramp: TRtColorSchemeRamp): void;
2080
2206
  static ɵfac: i0.ɵɵFactoryDeclaration<RtThemeService, never>;
2081
- static ɵprov: i0.ɵɵInjectableDeclaration<RtThemeService>;
2207
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
2082
2208
  }
2083
2209
 
2084
2210
  /**
@@ -2114,5 +2240,5 @@ declare class RtHideTooltipDirective implements AfterViewInit {
2114
2240
  static ɵdir: i0.ɵɵDirectiveDeclaration<RtHideTooltipDirective, "[rtHideTooltipDirective]", never, { "element": { "alias": "rtHideTooltipDirective"; "required": true; "isSignal": true; }; "isTooltipShown": { "alias": "isTooltipShown"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
2115
2241
  }
2116
2242
 
2117
- export { ASIDE_REF, AsideRef, DEFAULT_PAGE_MODEL, DEFAULT_PAGE_SIZE, EAsideButtons, EInfoBadgeSize, EInfoBadgeType, EModalWindowSize, ERtAccentRole, ERtTheme, ETableColumnFilterTypes, ETableColumnTypes, ETextCellColor, EToggleSizeType, IAside, IModal, IRtActionBar, IRtSnackBar, IRtUiConfig, IRtuiButton, ISideMenu, ITable, RTUI_SIDE_MENU, RTUI_TABLE_COMPONENT_TOKEN, RTUI_TABLE_STOP_ROW_CLICK_ATTRIBUTE, RT_COLOR_SCHEME_STORAGE_KEY, RT_DARK_CLASS, RT_DEFAULT_SCHEME, RT_SCHEME_ATTRIBUTE, RT_THEME_ATTRIBUTE, RT_THEME_AUTO_CLASS, RT_THEME_STORAGE_KEY, RT_UI_CONFIG, RtActionBarService, RtAsideService, RtDynamicListSelectorsDirective, RtHideTooltipDirective, RtModalService, RtPopoverDirective, RtSnackBarService, RtTableConfigService, RtTableSelectorsDirective, RtThemeDirective, RtThemeService, RtuiActionBarComponent, RtuiActionBarContainerComponent, RtuiAsideContainerComponent, RtuiAsideContainerHeaderDirective, RtuiButtonComponent, RtuiClearButtonComponent, RtuiCustomTableCellsDirective, RtuiDynamicInputAdditionalControlDirective, RtuiDynamicInputComponent, RtuiDynamicListComponent, RtuiDynamicListCustomTableCellsDirective, RtuiDynamicListRowActionsDirective, RtuiDynamicListRowAdditionalActionsDirective, RtuiDynamicListToolbarActionsDirective, RtuiDynamicListToolbarSelectorsDirective, RtuiDynamicSelectorAdditionalControlDirective, RtuiDynamicSelectorComponent, RtuiDynamicSelectorItemAdditionalControlDirective, RtuiDynamicSelectorItemTitleDirective, RtuiDynamicSelectorItemTitleProjectionDirective, RtuiDynamicSelectorListActionsComponent, RtuiDynamicSelectorPlaceholderComponent, RtuiDynamicSelectorSelectedListComponent, RtuiFileUploadComponent, RtuiHeaderCenterDirective, RtuiHeaderComponent, RtuiHeaderLeftDirective, RtuiHeaderRightDirective, RtuiIconComponent, RtuiImageUploadComponent, RtuiInfoBadgeComponent, RtuiModalComponent, RtuiMultiButtonComponent, RtuiMultiSelectorPopupComponent, RtuiPaginationComponent, RtuiScrollableContainerComponent, RtuiScrollableContainerContentDirective, RtuiScrollableContainerFooterDirective, RtuiScrollableContainerHeaderDirective, RtuiSideMenuComponent, RtuiSideMenuFooterDirective, RtuiSideMenuHeaderDirective, RtuiSnackBarComponent, RtuiSpinnerComponent, RtuiStopTableRowClickDirective, RtuiTableAdditionalRowActionsDirective, RtuiTableComponent, RtuiTableRowActionsDirective, RtuiTableRowClickDirective, RtuiToggleComponent, RtuiToolbarCenterDirective, RtuiToolbarComponent, RtuiToolbarLeftDirective, RtuiToolbarRightDirective, SUB_MENU_MODE_KEY, SUB_MENU_WIDTH_KEY, SUB_MENU_WIDTH_MAX, SUB_MENU_WIDTH_MIN, clampSubMenuWidth, comparePropNames, darkenHexColor, getColorBasedOnBackground, provideRtUi, readSubMenuMode, readSubMenuWidth, splitSubMenuTitle, writeSubMenuMode, writeSubMenuWidth };
2118
- export type { IAsideConfig, IModalIcon, INameValueType, IRtuiSideMenuHost, IRtuiTable, ISelect, ISubMenuTitlePart, TAsideButtonsType, TAsidePositions, TIconSideType, TImageUploadFormat, TInfoBadgeSizeType, TInfoBadgeType, TMenuItemTrigger, TModalButtonAppearance, TModalWindowSizeType, TRtAccentRole, TRtColorSchemeRamp, TRtThemeType, TRtUiDesign, TRtuiIconSizeType, TRtuiIconThemeType, TToggleSizeType };
2243
+ export { ASIDE_REF, AsideRef, DEFAULT_MENU_ID, DEFAULT_PAGE_MODEL, DEFAULT_PAGE_SIZE, EAsideButtons, EInfoBadgeSize, EInfoBadgeType, EModalWindowSize, ERtAccentRole, ERtTheme, ETableColumnFilterTypes, ETableColumnTypes, ETextCellColor, EToggleSizeType, IAside, IModal, IRtActionBar, IRtSnackBar, IRtUiConfig, IRtuiButton, ISideMenu, ITable, RTUI_SIDE_MENU, RTUI_SIDE_MENU_SETTINGS_CONFIG, RTUI_TABLE_COMPONENT_TOKEN, RTUI_TABLE_STOP_ROW_CLICK_ATTRIBUTE, RT_COLOR_SCHEME_STORAGE_KEY, RT_DARK_CLASS, RT_DEFAULT_SCHEME, RT_SCHEME_ATTRIBUTE, RT_THEME_ATTRIBUTE, RT_THEME_AUTO_CLASS, RT_THEME_STORAGE_KEY, RT_UI_CONFIG, RtActionBarService, RtAsideService, RtDynamicListSelectorsDirective, RtHideTooltipDirective, RtModalService, RtPopoverDirective, RtSnackBarService, RtTableConfigService, RtTableSelectorsDirective, RtThemeDirective, RtThemeService, RtuiActionBarComponent, RtuiActionBarContainerComponent, RtuiAsideContainerComponent, RtuiAsideContainerHeaderDirective, RtuiButtonComponent, RtuiClearButtonComponent, RtuiCustomTableCellsDirective, RtuiDynamicInputAdditionalControlDirective, RtuiDynamicInputComponent, RtuiDynamicListComponent, RtuiDynamicListCustomTableCellsDirective, RtuiDynamicListRowActionsDirective, RtuiDynamicListRowAdditionalActionsDirective, RtuiDynamicListToolbarActionsDirective, RtuiDynamicListToolbarSelectorsDirective, RtuiDynamicSelectorAdditionalControlDirective, RtuiDynamicSelectorComponent, RtuiDynamicSelectorItemAdditionalControlDirective, RtuiDynamicSelectorItemTitleDirective, RtuiDynamicSelectorItemTitleProjectionDirective, RtuiDynamicSelectorListActionsComponent, RtuiDynamicSelectorPlaceholderComponent, RtuiDynamicSelectorSelectedListComponent, RtuiFileUploadComponent, RtuiHeaderCenterDirective, RtuiHeaderComponent, RtuiHeaderLeftDirective, RtuiHeaderRightDirective, RtuiIconComponent, RtuiImageUploadComponent, RtuiInfoBadgeComponent, RtuiModalComponent, RtuiMultiButtonComponent, RtuiMultiSelectorPopupComponent, RtuiPaginationComponent, RtuiScrollableContainerComponent, RtuiScrollableContainerContentDirective, RtuiScrollableContainerFooterDirective, RtuiScrollableContainerHeaderDirective, RtuiSideMenuComponent, RtuiSideMenuFooterDirective, RtuiSideMenuHeaderDirective, RtuiSideMenuSettingsService, RtuiSnackBarComponent, RtuiSpinnerComponent, RtuiStopTableRowClickDirective, RtuiTableAdditionalRowActionsDirective, RtuiTableComponent, RtuiTableRowActionsDirective, RtuiTableRowClickDirective, RtuiToggleComponent, RtuiToolbarCenterDirective, RtuiToolbarComponent, RtuiToolbarLeftDirective, RtuiToolbarRightDirective, SIDE_MENU_SETTINGS_KEY, SUB_MENU_MODE_KEY, SUB_MENU_WIDTH_KEY, SUB_MENU_WIDTH_MAX, SUB_MENU_WIDTH_MIN, clampSubMenuWidth, comparePropNames, darkenHexColor, getColorBasedOnBackground, provideRtUi, provideRtuiSideMenuSettings, readSubMenuMode, readSubMenuWidth, splitSubMenuTitle, writeSubMenuMode, writeSubMenuWidth };
2244
+ export type { IAsideConfig, IModalIcon, INameValueType, IRtuiSideMenuFavoritesIcon, IRtuiSideMenuFavoritesIcons, IRtuiSideMenuFavoritesLabels, IRtuiSideMenuHost, IRtuiSideMenuSettingsConfig, IRtuiTable, ISelect, ISubMenuTitlePart, TAsideButtonsType, TAsidePositions, TIconSideType, TImageUploadFormat, TInfoBadgeSizeType, TInfoBadgeType, TMenuItemTrigger, TModalButtonAppearance, TModalWindowSizeType, TRtAccentRole, TRtColorSchemeRamp, TRtThemeType, TRtUiDesign, TRtuiIconSizeType, TRtuiIconThemeType, TSideMenuSettingsRecord, TToggleSizeType };
Binary file