@rt-tools/ui-kit-v2 0.1.0 → 0.4.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 (117) hide show
  1. package/README.md +25 -16
  2. package/fesm2022/rt-tools-ui-kit-v2.mjs +786 -1504
  3. package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
  4. package/package.json +1 -2
  5. package/rt-tools-ui-kit-v2-0.4.0.tgz +0 -0
  6. package/src/lib/components/CONTEXT.md +49 -0
  7. package/src/lib/components/aside/CONTEXT.md +51 -0
  8. package/src/lib/components/aside/_rt-aside-overlay.scss +88 -0
  9. package/src/lib/components/aside/rt-aside.component.scss +19 -94
  10. package/src/lib/components/aside-section/CONTEXT.md +26 -0
  11. package/src/lib/components/autocomplete/CONTEXT.md +58 -0
  12. package/src/lib/components/bar-list/CONTEXT.md +27 -0
  13. package/src/lib/components/bar-list/rt-bar-list.component.scss +5 -2
  14. package/src/lib/components/bottom-sheet/CONTEXT.md +54 -0
  15. package/src/lib/components/bottom-sheet/rt-bottom-sheet.component.scss +53 -53
  16. package/src/lib/components/button/CONTEXT.md +53 -0
  17. package/src/lib/components/button/rt-button.directive.scss +20 -0
  18. package/src/lib/components/calendar/CONTEXT.md +41 -0
  19. package/src/lib/components/calendar/rt-calendar.component.scss +96 -99
  20. package/src/lib/components/card/CONTEXT.md +34 -0
  21. package/src/lib/components/card/rt-card.component.scss +5 -2
  22. package/src/lib/components/chat/CONTEXT.md +41 -0
  23. package/src/lib/components/checkbox/CONTEXT.md +37 -0
  24. package/src/lib/components/checkbox/rt-checkbox.component.scss +13 -4
  25. package/src/lib/components/collapsible-text/CONTEXT.md +29 -0
  26. package/src/lib/components/confirm-popover/CONTEXT.md +47 -0
  27. package/src/lib/components/container/CONTEXT.md +42 -0
  28. package/src/lib/components/container/rt-container.component.scss +5 -69
  29. package/src/lib/components/counter/CONTEXT.md +40 -0
  30. package/src/lib/components/counter/rt-counter.component.scss +10 -9
  31. package/src/lib/components/counter-row/CONTEXT.md +24 -0
  32. package/src/lib/components/counter-row/rt-counter-row.component.scss +23 -22
  33. package/src/lib/components/date-picker/CONTEXT.md +41 -0
  34. package/src/lib/components/delta-view/CONTEXT.md +31 -0
  35. package/src/lib/components/detail-list/CONTEXT.md +29 -0
  36. package/src/lib/components/dialog/CONTEXT.md +50 -0
  37. package/src/lib/components/dialog/footer/rt-dialog-footer.component.scss +20 -9
  38. package/src/lib/components/dialog/header/rt-dialog-header.component.scss +27 -15
  39. package/src/lib/components/dialog/rt-dialog.component.scss +6 -2
  40. package/src/lib/components/download-link/CONTEXT.md +26 -0
  41. package/src/lib/components/download-link/rt-download-link.component.scss +13 -0
  42. package/src/lib/components/empty-state/CONTEXT.md +35 -0
  43. package/src/lib/components/field/CONTEXT.md +72 -0
  44. package/src/lib/components/file-card/CONTEXT.md +28 -0
  45. package/src/lib/components/file-drop/CONTEXT.md +39 -0
  46. package/src/lib/components/file-input/CONTEXT.md +36 -0
  47. package/src/lib/components/file-list/CONTEXT.md +15 -0
  48. package/src/lib/components/filter-control/CONTEXT.md +33 -0
  49. package/src/lib/components/form-control/CONTEXT.md +50 -0
  50. package/src/lib/components/header/CONTEXT.md +36 -0
  51. package/src/lib/components/icon/CONTEXT.md +46 -0
  52. package/src/lib/components/icon/rt-icon.component.scss +3 -5
  53. package/src/lib/components/icon-button/CONTEXT.md +49 -0
  54. package/src/lib/components/icon-button/rt-icon-button.component.scss +14 -1
  55. package/src/lib/components/info-item/CONTEXT.md +30 -0
  56. package/src/lib/components/input/CONTEXT.md +47 -0
  57. package/src/lib/components/input-number/CONTEXT.md +52 -0
  58. package/src/lib/components/live-badge/CONTEXT.md +31 -0
  59. package/src/lib/components/live-badge/rt-live-badge.component.scss +5 -2
  60. package/src/lib/components/logo/CONTEXT.md +42 -0
  61. package/src/lib/components/logo/rt-logo.component.scss +4 -6
  62. package/src/lib/components/menu/CONTEXT.md +65 -0
  63. package/src/lib/components/message/CONTEXT.md +35 -0
  64. package/src/lib/components/message/rt-message.component.scss +5 -2
  65. package/src/lib/components/message-composer/CONTEXT.md +46 -0
  66. package/src/lib/components/money-list/CONTEXT.md +32 -0
  67. package/src/lib/components/multiselect/CONTEXT.md +43 -0
  68. package/src/lib/components/night-grid/CONTEXT.md +31 -0
  69. package/src/lib/components/night-grid/rt-night-grid.component.scss +5 -2
  70. package/src/lib/components/note/CONTEXT.md +27 -0
  71. package/src/lib/components/notifications-bell/CONTEXT.md +29 -0
  72. package/src/lib/components/page-header/CONTEXT.md +33 -0
  73. package/src/lib/components/pagination/CONTEXT.md +43 -0
  74. package/src/lib/components/photo-viewer/CONTEXT.md +30 -0
  75. package/src/lib/components/photo-viewer/rt-photo-viewer.component.scss +95 -96
  76. package/src/lib/components/popover/CONTEXT.md +54 -0
  77. package/src/lib/components/rich-editor/CONTEXT.md +36 -0
  78. package/src/lib/components/section-nav/CONTEXT.md +27 -0
  79. package/src/lib/components/section-nav/rt-section-nav.component.scss +7 -1
  80. package/src/lib/components/select/CONTEXT.md +54 -0
  81. package/src/lib/components/skeleton/CONTEXT.md +41 -0
  82. package/src/lib/components/skeleton-wrapper/CONTEXT.md +24 -0
  83. package/src/lib/components/skeleton-wrapper/rt-skeleton-wrapper.component.scss +5 -1
  84. package/src/lib/components/spinner/CONTEXT.md +39 -0
  85. package/src/lib/components/split-button/CONTEXT.md +39 -0
  86. package/src/lib/components/split-button/rt-split-button.component.scss +13 -0
  87. package/src/lib/components/stat-tile/CONTEXT.md +35 -0
  88. package/src/lib/components/stat-tile/rt-stat-tile.component.scss +5 -2
  89. package/src/lib/components/stepper/CONTEXT.md +36 -0
  90. package/src/lib/components/stepper/rt-stepper.component.scss +5 -2
  91. package/src/lib/components/table/CONTEXT.md +53 -0
  92. package/src/lib/components/table/settings-aside/rt-table-settings-aside.component.scss +4 -2
  93. package/src/lib/components/table/settings-panel/rt-table-settings-panel.component.scss +1 -1
  94. package/src/lib/components/tabs/CONTEXT.md +56 -0
  95. package/src/lib/components/tag/CONTEXT.md +47 -0
  96. package/src/lib/components/tag/rt-tag.component.scss +5 -2
  97. package/src/lib/components/textarea/CONTEXT.md +38 -0
  98. package/src/lib/components/theme-toggle/CONTEXT.md +28 -0
  99. package/src/lib/components/theme-toggle/rt-theme-toggle.component.scss +5 -1
  100. package/src/lib/components/thread-list/CONTEXT.md +42 -0
  101. package/src/lib/components/timeline/CONTEXT.md +33 -0
  102. package/src/lib/components/timeline/rt-timeline.component.scss +5 -2
  103. package/src/lib/components/toast/CONTEXT.md +53 -0
  104. package/src/lib/components/toggle-button-group/CONTEXT.md +38 -0
  105. package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +12 -7
  106. package/src/lib/components/toggle-switch/CONTEXT.md +35 -0
  107. package/src/lib/components/toggle-switch/rt-toggle-switch.component.scss +13 -4
  108. package/src/lib/components/toolbar/CONTEXT.md +22 -0
  109. package/src/lib/components/tooltip/CONTEXT.md +34 -0
  110. package/src/lib/components/welcome-dialog/CONTEXT.md +20 -0
  111. package/src/lib/components/welcome-dialog/rt-welcome-dialog.component.scss +6 -2
  112. package/src/lib/components/workspace/CONTEXT.md +46 -0
  113. package/src/lib/components/workspace-details/CONTEXT.md +42 -0
  114. package/src/styles/_semantic.scss +11 -2
  115. package/styles/tokens.css +26 -1
  116. package/types/rt-tools-ui-kit-v2.d.ts +365 -87
  117. package/rt-tools-ui-kit-v2-0.1.0.tgz +0 -0
@@ -0,0 +1,72 @@
1
+ # `rt-field`
2
+
3
+ Обёртка единой анатомии поля: подпись сверху, контрол посередине, подсказка или ошибка снизу.
4
+
5
+ ```html
6
+ <rt-field label="Имя" hint="Как в паспорте" help="Совпадает с документом" [errors]="{ required: 'Без имени не пустим' }">
7
+ <rt-input [formControl]="name" />
8
+ </rt-field>
9
+
10
+ <rt-field label="Комментарий">
11
+ <rt-textarea [formControl]="comment" />
12
+ <span rtFieldHint>
13
+ Не короче
14
+ <strong>трёх</strong>
15
+ символов
16
+ </span>
17
+ </rt-field>
18
+ ```
19
+
20
+ | вход | тип | умолчание |
21
+ | ------------------ | ------------------------------ | --------- |
22
+ | `label` | `string` | `''` |
23
+ | `hint` | `string` | `''` |
24
+ | `help` | `string` | `''` |
25
+ | `errors` | `Record<validatorKey, string>` | `{}` |
26
+ | `readonly` | `boolean` | `false` |
27
+ | `loading` | `boolean` | `false` |
28
+ | `hideRequiredMark` | `boolean` | `false` |
29
+ | `reserveHintSpace` | `boolean` | `false` |
30
+
31
+ ## Главное, что нужно знать
32
+
33
+ **Поле само находит контрол** через `contentChild(RtFormControlBase)` — достаточно положить любой
34
+ контрол кита внутрь. Дальше оно:
35
+
36
+ - выдаёт контролу авто-идентификатор (`rt-field-N`) и связывает с ним `<label for>`, если у
37
+ контрола нет своего `controlId`;
38
+ - рисует «\*», если у контрола есть валидатор обязательности (обнаруживается сам, входа для этого
39
+ нет);
40
+ - показывает текст ошибки и включает режим плоского чтения.
41
+
42
+ Всё это работает только для контролов кита. Обычный `<input>` внутрь класть можно, но подписи,
43
+ звёздочки и ошибки он не получит.
44
+
45
+ ## Ошибки
46
+
47
+ Показывается **одно** сообщение — первого сработавшего валидатора. Текст берётся так:
48
+
49
+ 1. `errors[имяВалидатора]`, переданный формой;
50
+ 2. переведённое умолчание кита (`required`, `email`, `minlength`, `maxlength`, `min`, `max`,
51
+ `pattern`);
52
+ 3. пусто, если валидатор незнаком и своего текста нет.
53
+
54
+ Ошибка появляется только после касания или правки контрола — это решает сам контрол, а не поле.
55
+
56
+ ## Подсказка
57
+
58
+ - Строковый `[hint]` — обычный случай.
59
+ - Проекция `[rtFieldHint]` — когда подсказке нужна разметка; она **важнее** строкового входа.
60
+ - Без резерва подсказка уступает место ошибке. С `reserveHintSpace` она остаётся в потоке, но
61
+ прячется от скринридера (`aria-hidden`), а высота строки перестаёт скакать при появлении ошибки.
62
+
63
+ ## Края
64
+
65
+ - `hideRequiredMark` нужен формам, где обязательны все поля и звёздочка становится шумом.
66
+ - `loading` оборачивает **только зону контрола** в заглушку; подпись и подсказка остаются
67
+ видимыми — по ним читается, чего ждут.
68
+ - Текст пояснения (`help`) живёт в поповере и до наведения не отрисован.
69
+
70
+ ## Рядом
71
+
72
+ - [`RtFormControlBase`](../form-control/CONTEXT.md) — что именно поле читает у контрола.
@@ -0,0 +1,28 @@
1
+ # `rt-file-card`
2
+
3
+ Карточка файла: иконка по типу, имя, размер и необязательные действия.
4
+
5
+ ```html
6
+ <rt-file-card name="Договор.pdf" size="md" showDownload showRemove [sizeBytes]="204800" (download)="save()" (removed)="detach()" />
7
+ ```
8
+
9
+ | вход | тип | умолчание |
10
+ | -------------------------------------------- | ---------------------- | -------------- |
11
+ | `name` | `string` | **обязателен** |
12
+ | `sizeBytes` | `number \| null` | `null` |
13
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
14
+ | `showDownload` / `showRename` / `showRemove` | `boolean` | `false` |
15
+ | `disabled` | `boolean` | `false` |
16
+
17
+ Выходы: `download`, `rename`, `removed`.
18
+
19
+ ## Главное, что нужно знать
20
+
21
+ **Иконка и заголовок выводятся из имени файла.** Расширение выбирает иконку (незнакомое даёт
22
+ общую), а заголовок — это имя без расширения с заменёнными `_` и `-` на пробелы. Отдельных
23
+ входов для них нет.
24
+
25
+ **Длинное имя обрезается в середине**, чтобы расширение осталось видно: `очень-длинн...pdf`.
26
+
27
+ **Без входов действий карточка немая** — ни одной кнопки. Она бывает просто вложением, которое
28
+ показывают.
@@ -0,0 +1,39 @@
1
+ # `rt-file-drop`
2
+
3
+ Область приёма файлов перетаскиванием. Оборачивает любое содержимое и показывает подсказку
4
+ поверх него.
5
+
6
+ ```html
7
+ <rt-file-drop accept=".pdf" [zones]="zones" (filesDropped)="attach($event)" (zoneFilesDropped)="attachTo($event)">
8
+ <app-form />
9
+ </rt-file-drop>
10
+ ```
11
+
12
+ | вход | тип | умолчание |
13
+ | -------------- | ----------------- | -------------------------- |
14
+ | `disabled` | `boolean` | `false` |
15
+ | `overlayLabel` | `string` | `''` → `uiFileDropOverlay` |
16
+ | `zones` | `readonly Zone[]` | `[]` |
17
+ | `accept` | `string` | `''` |
18
+
19
+ Выходы: `filesDropped` (`File[]`), `zoneFilesDropped` (`{ zoneId, files }`), `draggingChange`.
20
+
21
+ ## Главное, что нужно знать
22
+
23
+ **С зонами общее событие сброса не поднимается** — зона обязана быть названа. Без зон, наоборот,
24
+ приходит только `filesDropped`.
25
+
26
+ **Уход курсора гасит подсказку не сразу** (80 мс). Без задержки рамка мерцала бы при переходе
27
+ между дочерними элементами области.
28
+
29
+ **Файлы не того типа отсекаются по `accept`**, и пустой после фильтра сброс события не поднимает.
30
+
31
+ ## Края
32
+
33
+ - Зона выбирается долей от высоты области. Пока высоты нет (первый кадр, скрытая вкладка),
34
+ выбор детерминирован — первая зона, а не отказ: файл терять нельзя.
35
+ - Отключённая область не показывает подсказку и не принимает файлы.
36
+
37
+ ## Рядом
38
+
39
+ - [`rt-file-input`](../file-input/CONTEXT.md) — выбор файлов кнопкой.
@@ -0,0 +1,36 @@
1
+ # `rt-file-input`
2
+
3
+ Поле выбора файлов: кнопка, скрытое нативное поле и список выбранного карточками.
4
+
5
+ ```html
6
+ <rt-field label="Документы">
7
+ <rt-file-input accept=".pdf,.docx" buttonLabel="Прикрепить" [formControl]="docs" [multiple]="true" />
8
+ </rt-field>
9
+ ```
10
+
11
+ Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md):
12
+
13
+ | вход | тип | умолчание |
14
+ | ------------- | ---------------- | --------------------- |
15
+ | `multiple` | `boolean` | `false` |
16
+ | `accept` | `string \| null` | `null` |
17
+ | `directory` | `boolean` | `false` |
18
+ | `buttonLabel` | `string` | `''` → `uiChooseFile` |
19
+
20
+ Значение — `File[]`.
21
+
22
+ ## Главное, что нужно знать
23
+
24
+ **Нативное поле спрятано.** Его нельзя оформить, поэтому выбор открывает своя кнопка, кликая по
25
+ скрытому полю программно.
26
+
27
+ **Без `multiple` берётся только первый файл**, даже если браузер отдал набор: так бывает при
28
+ перетаскивании в одиночное поле.
29
+
30
+ **Удаление файла сбрасывает значение нативного поля.** Иначе повторный выбор того же файла не
31
+ дал бы события `change`.
32
+
33
+ ## Рядом
34
+
35
+ - [`rt-file-drop`](../file-drop/CONTEXT.md) — приём файлов перетаскиванием.
36
+ - [`rt-file-card`](../file-card/CONTEXT.md) — чем рисуется каждый выбранный файл.
@@ -0,0 +1,15 @@
1
+ # `rt-file-list`
2
+
3
+ Раскладка перечня файлов. Входов, выходов и своей разметки нет — только отступы и перенос.
4
+
5
+ ```html
6
+ <rt-file-list>
7
+ <rt-file-card *ngFor="…" … />
8
+ </rt-file-list>
9
+ ```
10
+
11
+ ## Главное, что нужно знать
12
+
13
+ **Это чистая раскладка.** Что показывать — карточки [`rt-file-card`](../file-card/CONTEXT.md),
14
+ ссылки [`rt-download-link`](../download-link/CONTEXT.md) или свои строки — решает потребитель.
15
+ Пустоту и загрузку тоже рисует он: у списка нет ни заглушки, ни состояния.
@@ -0,0 +1,33 @@
1
+ # `rt-filter-control<T>`
2
+
3
+ Фильтр, который сам выбирает представление по ширине экрана.
4
+
5
+ ```html
6
+ <rt-filter-control ariaLabel="Статус" [options]="options" [value]="status()" (valueChange)="status.set($event)" />
7
+ ```
8
+
9
+ | вход | тип | умолчание |
10
+ | ------------------------ | ------------------------------------------------ | -------------- |
11
+ | `options` | `ReadonlyArray<{ value, label, icon?, title? }>` | **обязателен** |
12
+ | `value` | `T \| undefined` | `undefined` |
13
+ | `ariaLabel` | `string \| null` | `null` |
14
+ | `placeholder` | `string` | `''` |
15
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` |
16
+ | `disabled` / `fullWidth` | `boolean` | `false` |
17
+
18
+ Выход: `valueChange`.
19
+
20
+ ## Главное, что нужно знать
21
+
22
+ **Одно и то же наполнение рисуется двумя разными контролами.** На широком экране это
23
+ [`rt-toggle-button-group`](../toggle-button-group/CONTEXT.md), на узком (≤1080px, по
24
+ `BreakpointsService.narrow`) — [`rt-select`](../select/CONTEXT.md): сегменты в строку не
25
+ помещаются. Потребитель об этом не думает и передаёт один набор.
26
+
27
+ **Очистка списка выключена**: фильтр всегда в каком-то состоянии, «ничего не выбрано» здесь не
28
+ предусмотрено.
29
+
30
+ ## Края
31
+
32
+ - Иконки вариантов доезжают только до сегментов; в списке остаются одни подписи.
33
+ - Отключение доходит до обоих представлений.
@@ -0,0 +1,50 @@
1
+ # `RtFormControlBase<TValue>`
2
+
3
+ Абстрактная основа всех полей ввода кита. Собственной разметки не имеет и в разметку не ставится —
4
+ от неё наследуются [`rt-input`](../input/CONTEXT.md), [`rt-textarea`](../textarea/CONTEXT.md),
5
+ [`rt-input-number`](../input-number/CONTEXT.md), [`rt-select`](../select/CONTEXT.md),
6
+ [`rt-multiselect`](../multiselect/CONTEXT.md), [`rt-autocomplete`](../autocomplete/CONTEXT.md),
7
+ [`rt-date-picker`](../date-picker/CONTEXT.md).
8
+
9
+ Общие входы, которые получает каждый наследник:
10
+
11
+ | вход | тип | умолчание |
12
+ | ----------- | ---------------------- | --------- |
13
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` |
14
+ | `disabled` | `boolean` | `false` |
15
+ | `controlId` | `string \| null` | `null` |
16
+ | `ariaLabel` | `string \| null` | `null` |
17
+ | `clearable` | `boolean` | `true` |
18
+ | `bordered` | `boolean` | `true` |
19
+
20
+ ## Главное, что нужно знать
21
+
22
+ **`NgControl` инжектится самим полем (`{ self: true, optional: true }`), а `valueAccessor`
23
+ привязывается вручную — провайдера `NG_VALUE_ACCESSOR` здесь нет.** Так поле получает доступ к
24
+ `control.invalid` / `touched` / `errors` (и умеет подсвечивать ошибку и отдавать её текст в
25
+ [`rt-field`](../field/CONTEXT.md)), не создавая цикла самоссылки, на котором Angular падает
26
+ с NG0200.
27
+
28
+ **Подсветка ошибки включается только после касания или правки**: `invalid && (touched || dirty)`.
29
+ Пустое обязательное поле невалидно с самого начала, но краснеть до первого касания не должно.
30
+
31
+ ## Что задаёт вмещающее поле, а не потребитель
32
+
33
+ `rt-field` вызывает у контрола три метода — руками их звать не нужно:
34
+
35
+ - `assignControlId(id)` — авто-идентификатор для связки `label[for]`, если у контрола нет своего;
36
+ - `setReadonly(value)` — плоский режим чтения (`isReadonly`), в котором контрол подменяется
37
+ текстом `displayText()`;
38
+ - читает `required()`, `invalid()`, `errors()` — для звёздочки и текста ошибки.
39
+
40
+ ## Что обязан реализовать наследник
41
+
42
+ | член | зачем |
43
+ | ------------------- | ----------------------------------------------- |
44
+ | `getEmptyValue()` | пустое значение своего типа: `''`, `null`, `[]` |
45
+ | `hasValue` | есть ли что очищать — гейт видимости крестика |
46
+ | `focusAfterClear()` | куда вернуть фокус после очистки |
47
+ | `displayText` | текст плоского режима чтения |
48
+
49
+ Поля с отдельным состоянием отображения (`rt-autocomplete`, `rt-input-number`) вдобавок
50
+ переопределяют `writeValue` и `clearValue`, синхронизируя свой сигнал отображения.
@@ -0,0 +1,36 @@
1
+ # `rt-header`
2
+
3
+ Верхняя полоса приложения: кнопка «назад», логотип, приглашение, колокольчик и профиль.
4
+
5
+ ```html
6
+ <rt-header showInvite [canGoBack]="canGoBack()" (backClick)="back()" (inviteClick)="invite()" (profileClick)="openProfile()">
7
+ <rt-notifications-bell rtHeaderBell [unread]="hasUnread()" (clicked)="openNotifications()" />
8
+ </rt-header>
9
+ ```
10
+
11
+ | вход | тип | умолчание |
12
+ | ------------ | --------- | --------- |
13
+ | `canGoBack` | `boolean` | `false` |
14
+ | `showInvite` | `boolean` | `false` |
15
+
16
+ Выходы: `backClick`, `inviteClick`, `profileClick`. Слот `[rtHeaderBell]` — колокольчик.
17
+
18
+ ## Главное, что нужно знать
19
+
20
+ **Кнопка «назад» не появляется и не исчезает — она проявляется.** Узел есть в разметке всегда,
21
+ видимость даёт модификатор `rt-header__back--visible`: иначе соседние элементы шапки прыгали бы
22
+ при каждом переходе.
23
+
24
+ **Колокольчик приходит проекцией.** Шапка ничего не знает про уведомления — их состоянием
25
+ владеет приложение.
26
+
27
+ ## Края
28
+
29
+ - Кнопки действий подогнаны под высоту шапки инлайновым `--rt-icon-button-size: 35px` — это
30
+ осознанный выход за шкалу размеров.
31
+ - Логотип рисуется вариантом `wordmark` высотой 15px; сами файлы даёт приложение
32
+ (см. [`rt-logo`](../logo/CONTEXT.md)).
33
+
34
+ ## Рядом
35
+
36
+ - [`rt-page-header`](../page-header/CONTEXT.md) — вторая полоса, с разделами.
@@ -0,0 +1,46 @@
1
+ # `rt-icon`
2
+
3
+ ```html
4
+ <rt-icon name="check" size="md" color="current" [rotate]="null" />
5
+ ```
6
+
7
+ | вход | тип | умолчание |
8
+ | -------- | ----------------------------------------------------------------------------------- | -------------- |
9
+ | `name` | `IRtIcon.Name` — литеральный union из `rt-icon-names.ts` | **обязателен** |
10
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | `'md'` |
11
+ | `color` | `'current' \| 'muted' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'inverse'` | `'current'` |
12
+ | `rotate` | `number \| string \| null` | `null` |
13
+
14
+ Выходов нет. Размер и цвет едут инлайновым стилем host-а (`width`/`height`/`color`), не классом:
15
+ `xs` 12px, `sm` 16px, `md` 20px, `lg` 24px, `xl` 32px, `2xl` 40px.
16
+
17
+ ## Главное, что нужно знать
18
+
19
+ **Компонент ничего не грузит.** Он рисует `<use href="#rt-icon-<name>">` — ссылку на symbol
20
+ внутри спрайта, который собирает `RtIconRegistry` и вставляет в `<body>`
21
+ провайдер `provideRtIcons()`. Без этого провайдера разметка верна, а на экране пусто.
22
+
23
+ Приложение обязано опубликовать набор из `assets/icons` пакета по адресу, который знает реестр
24
+ (по умолчанию `/icons`, иначе `provideRtIcons('/my/icons')`).
25
+
26
+ ## Как этим пользоваться
27
+
28
+ - Иконка спрятана от скринридера (`aria-hidden="true"`) — подпись даёт вмещающий её контрол.
29
+ Отдельная `rt-icon` без подписи для AT невидима, и это правильно.
30
+ - Цвет по умолчанию наследуется от текста. Явное значение нужно, только когда иконка должна
31
+ отличаться от текста своего контрола.
32
+ - `rotate` принимает строку (`rotate="180"`) — так значение приходит из атрибута разметки.
33
+ Ноль и нечисловая строка гасят поворот: лишний `transform` создавал бы отдельный слой отрисовки.
34
+
35
+ ## Края
36
+
37
+ - **Имя без файла роняет весь набор.** Реестр собирает спрайт одним `forkJoin`; один отказ
38
+ гасит иконки во всём приложении. Добавляя имя в `rt-icon-names.ts`, положи SVG рядом.
39
+ - Реестр пропускает загрузку, если спрайт уже в DOM (`#rt-icon-sprite`) — так витрина не гоняет
40
+ сотни запросов между историями.
41
+ - На сервере (SSR) спрайт не грузится: относительных HTTP-запросов к статике там нет, иконки
42
+ дорисуются после гидрации.
43
+
44
+ ## Рядом
45
+
46
+ - [`rt-icon-button`](../icon-button/CONTEXT.md) — кликабельная иконка с подписью для AT.
@@ -1,8 +1,6 @@
1
- /* rt-icon — глобальные правила компонента (ViewEncapsulation.None).
2
- Намеренно без :host-обёртки: правила должны таргетировать `.rt-icon` глобально
3
- (host-класс компонента + потенциальные программные потребители sprite'а).
4
- Это исключение из общего правила "обёртывать стили в :host", применимого
5
- только для Emulated-инкапсуляции. */
1
+ /* rt-icon — правила компонента (ViewEncapsulation.None).
2
+ Селектор bare `.rt-icon` (класс на host) — им же адресуются и программные
3
+ потребители sprite'а, рисующие иконку без компонента. */
6
4
  .rt-icon {
7
5
  display: inline-flex;
8
6
  flex-shrink: 0;
@@ -0,0 +1,49 @@
1
+ # `rt-icon-button`
2
+
3
+ ```html
4
+ <rt-icon-button icon="ico-trash" ariaLabel="Удалить" variant="danger" size="md" (clicked)="remove()" />
5
+ ```
6
+
7
+ | вход | тип | умолчание |
8
+ | ----------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------- |
9
+ | `icon` | `IRtIcon.Name` | **обязателен** |
10
+ | `ariaLabel` | `string` | **обязателен** |
11
+ | `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger' \| 'success' \| 'warning'` | `'ghost'` |
12
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | `'md'` |
13
+ | `iconSize` | `IRtIcon.Size \| null` | `null` → от размера кнопки |
14
+ | `iconColor` | `IRtIcon.Color` | `'current'` |
15
+ | `shape` | `'circle' \| 'square'` | `'square'` |
16
+ | `type` | `'button' \| 'submit'` | `'button'` |
17
+ | `tooltip` | `string` | `''` (пусто → подсказки нет) |
18
+ | `tabIndex` | `number` | `0` |
19
+ | `loading` / `disabled` / `active` / `indicator` | `boolean` | `false` |
20
+
21
+ | выход | тип |
22
+ | --------- | ------------ |
23
+ | `clicked` | `MouseEvent` |
24
+
25
+ ## Главное, что нужно знать
26
+
27
+ **`ariaLabel` обязателен.** У кнопки нет текста, и без подписи она для скринридера безымянна.
28
+ Это единственная причина, по которой вход помечен `required`.
29
+
30
+ **Кнопку компонент рисует внутри себя**, host остаётся обычным элементом. Диаметр держит
31
+ свойство `--rt-icon-button-size`, и объявляет его модификатор размера на самой кнопке —
32
+ поэтому ни класс на host-е, ни инлайн-стиль на нём диаметра не меняют: собственное
33
+ объявление элемента бьёт унаследованное. Размер вне шкалы получается не обходом, а новой
34
+ ступенью здесь же: `sm` 28px, `md` 36px, `lg` 44px, `xl` 52px, `2xl` 60px.
35
+
36
+ ## Как этим пользоваться
37
+
38
+ - `loading` = `disabled` плюс кольцо вместо иконки. Клик в этом состоянии наружу не уходит.
39
+ - `active` превращается в `aria-pressed="true"` — для кнопок-переключателей.
40
+ - `tabIndex="-1"` оставляет клик мышью, но убирает кнопку из таб-порядка: так сделаны
41
+ вспомогательные крестики внутри полей.
42
+ - `iconSize` нужен, когда крупная кнопка должна остаться крупной (в неё целятся пальцем), а
43
+ иконка во весь диаметр выглядит тяжеловесно.
44
+ - Модификаторы в камелкейсе конвертируются в дефис: `indicator` → `rt-icon-button--has-indicator`.
45
+
46
+ ## Рядом
47
+
48
+ - [`[rtButton]`](../button/CONTEXT.md) — кнопка с подписью.
49
+ - [`[rtTooltip]`](../tooltip/CONTEXT.md) — то, чем рисуется вход `tooltip`.
@@ -1,4 +1,9 @@
1
- :host {
1
+ /* rt-icon-button — иконочная кнопка в common/ui. ViewEncapsulation.None.
2
+ Хост адресуется по имени элемента, а не по классу блока: класс `.rt-icon-button` висит
3
+ и на `<rt-icon-button>`, и на корне шаблона, поэтому правила блока вложены в хост —
4
+ так они достаются только корню шаблона. */
5
+
6
+ rt-icon-button {
2
7
  display: inline-flex;
3
8
 
4
9
  /* Дефолт = md; перезаписывается модификаторами sm/lg ниже либо inline стилем потребителя */
@@ -30,6 +35,14 @@
30
35
  --rt-icon-button-size: 44px;
31
36
  }
32
37
 
38
+ &--xl {
39
+ --rt-icon-button-size: 52px;
40
+ }
41
+
42
+ &--2xl {
43
+ --rt-icon-button-size: 60px;
44
+ }
45
+
33
46
  &--circle {
34
47
  border-radius: var(--rt-radius-full);
35
48
  }
@@ -0,0 +1,30 @@
1
+ # `rt-info-item`
2
+
3
+ ```html
4
+ <rt-info-item label="Тариф" [loading]="!plan()" [grow]="true">{{ plan()?.title }}</rt-info-item>
5
+ ```
6
+
7
+ | вход | тип | умолчание |
8
+ | --------- | --------- | -------------- |
9
+ | `label` | `string` | **обязателен** |
10
+ | `loading` | `boolean` | `false` |
11
+ | `grow` | `boolean` | `false` |
12
+
13
+ Значение приходит **проекцией**, а не входом — поэтому внутрь можно положить что угодно:
14
+ текст, `rt-tag`, ссылку, `@if`.
15
+
16
+ ## Главное, что нужно знать
17
+
18
+ **Во время загрузки подменяется только значение.** Подпись остаётся на месте: по ней читается,
19
+ чего именно ждут. Заглушка узкая (80px) — под значение, а не под всю строку.
20
+
21
+ ## Как этим пользоваться
22
+
23
+ - `grow` растягивает элемент по свободному месту строки. Без него он занимает свою ширину —
24
+ так пары «подпись: значение» выстраиваются в ряд.
25
+ - Пара подпись/значение — самостоятельный блок; для длинного вертикального перечня
26
+ «ключ → значение» есть [`rt-detail-list`](../detail-list/CONTEXT.md).
27
+
28
+ ## Рядом
29
+
30
+ - [`rt-skeleton-wrapper`](../skeleton-wrapper/CONTEXT.md) — то, чем нарисована заглушка.
@@ -0,0 +1,47 @@
1
+ # `rt-input`
2
+
3
+ ```html
4
+ <rt-field label="Имя">
5
+ <rt-input placeholder="Как в паспорте" iconLeft="search" [formControl]="name" />
6
+ </rt-field>
7
+
8
+ <rt-input type="password" [formControl]="password" [passwordToggle]="true" />
9
+ ```
10
+
11
+ Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md)
12
+ (`size`, `disabled`, `controlId`, `ariaLabel`, `clearable`, `bordered`):
13
+
14
+ | вход | тип | умолчание |
15
+ | ------------------------ | ------------------------------------------- | --------- |
16
+ | `type` | `'text' \| 'password' \| 'email' \| 'time'` | `'text'` |
17
+ | `placeholder` | `string` | `''` |
18
+ | `iconLeft` / `iconRight` | `IRtIcon.Name \| null` | `null` |
19
+ | `passwordToggle` | `boolean` | `false` |
20
+ | `autocomplete` | `string \| null` | `null` |
21
+
22
+ Значение — `string`.
23
+
24
+ ## Главное, что нужно знать
25
+
26
+ **Крестик очистки и переключатель пароля делят один постфикс.** На поле пароля с включённым
27
+ переключателем крестика не будет: показать оба некуда.
28
+
29
+ Крестик показывается при выполнении трёх условий сразу: `clearable` (по умолчанию включён),
30
+ в поле есть значение, поле не отключено. Из таб-порядка он исключён (`tabindex="-1"`) — до
31
+ вспомогательного аффорданса не должны доходить табом.
32
+
33
+ ## Как этим пользоваться
34
+
35
+ - `type="time"` заведён намеренно: браузер сам отдаёт `HH:MM` и раскладку под локаль, а разбор
36
+ строки руками означал бы своё поле ввода времени рядом с китом.
37
+ - Клик по рамке (падинги принадлежат host-у, а не `<input>`) переводит фокус в поле — кроме
38
+ кликов по самому полю и по интерактивному постфиксу.
39
+ - Режим плоского чтения включает **вмещающий [`rt-field`](../field/CONTEXT.md)**, а не вход
40
+ компонента: `[readonly]` стоит на поле-обёртке. Пустое значение в этом режиме рисуется прочерком.
41
+ - Подсветка ошибки (`rt-input--invalid`) включается сама после касания — см. основу полей.
42
+
43
+ ## Рядом
44
+
45
+ - [`rt-field`](../field/CONTEXT.md) — подпись, звёздочка, текст ошибки, режим чтения.
46
+ - [`rt-input-number`](../input-number/CONTEXT.md) — для чисел с группировкой разрядов.
47
+ - [`rt-autocomplete`](../autocomplete/CONTEXT.md) — поле с подсказками.
@@ -0,0 +1,52 @@
1
+ # `rt-input-number`
2
+
3
+ ```html
4
+ <rt-field label="Сумма">
5
+ <rt-input-number prefix="₽" [formControl]="amount" [min]="0" [max]="1000000" [maxFractionDigits]="2" />
6
+ </rt-field>
7
+ ```
8
+
9
+ Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md):
10
+
11
+ | вход | тип | умолчание |
12
+ | ------------------- | ---------------------- | ------------------------------ |
13
+ | `prefix` | `string \| null` | `null` |
14
+ | `iconLeft` | `IRtIcon.Name \| null` | `null` |
15
+ | `placeholder` | `string` | `''` |
16
+ | `min` / `max` | `number \| null` | `null` (сторона не ограничена) |
17
+ | `minFractionDigits` | `number` | `0` |
18
+ | `maxFractionDigits` | `number` | `2` |
19
+
20
+ Значение — `number | null`.
21
+
22
+ ## Главное, что нужно знать
23
+
24
+ **Точность фиксирована, а не задана диапазоном.** Итоговое число знаков —
25
+ `max(minFractionDigits, min(maxFractionDigits, 20))`, и одно и то же значение служит и нижней, и
26
+ верхней границей. С умолчаниями это значит: `12` после ухода фокуса становится `12,00`. Целое
27
+ поле — это `maxFractionDigits: 0`.
28
+
29
+ **Разбор и запись зависят от локали (`LOCALE_ID`) и обязаны сходиться.** Разделитель разрядов
30
+ берётся у локали, и разбор снимает именно его: `1 234,56` в `ru`, `1,234.56` в `en-US`.
31
+ Разделителем дробной части считается только тот знак из «`.`», «`,`», которым в этой локали не
32
+ разделяются разряды, — поэтому в `ru` годятся оба, а в `en-US` запятая всегда читается как
33
+ разряды. Локаль-слепой разбор здесь и ломался: безусловная замена первой запятой на точку
34
+ превращала показанное `1,000` в `1`.
35
+
36
+ ## Как этим пользоваться
37
+
38
+ - Это `<input type="text" inputmode="decimal">`, а не `type="number"`: у нативного числового поля
39
+ колесо мыши меняет значение, а разделители зависят от браузера.
40
+ - Во время набора разряды группируются на лету, а каретка удерживается на месте (позиция
41
+ пересчитывается по числу значимых символов слева от неё).
42
+ - Нечисловой ввод стрипается из отображения, но **прежнее значение держится до ухода фокуса** —
43
+ наружу оно не уходит. Пустое поле пишет `null`.
44
+ - На уходе фокуса значение зажимается в границы, округляется до точности и переписывается
45
+ отформатированным.
46
+ - **Крестик обнуляет поле, а не опустошает его** (`0,00`): у числового поля ноль осмыслен.
47
+ При нуле крестик не показывается — обнулять больше нечего.
48
+ - Префикс спрятан от скринридера: валюту он прочитает из подписи поля.
49
+
50
+ ## Рядом
51
+
52
+ - [`rt-counter`](../counter/CONTEXT.md) — когда число меняют шагами, а не набирают.
@@ -0,0 +1,31 @@
1
+ # `rt-live-badge`
2
+
3
+ ```html
4
+ <rt-live-badge label="Смотрят сейчас" [count]="visitors()" [active]="streamAlive()" />
5
+ ```
6
+
7
+ | вход | тип | умолчание |
8
+ | -------- | ---------------- | ---------------------------------- |
9
+ | `label` | `string` | `''` → переведённое `uiLiveOnSite` |
10
+ | `count` | `number \| null` | `null` → прочерк `—` |
11
+ | `active` | `boolean` | `false` |
12
+
13
+ Выходов нет.
14
+
15
+ ## Главное, что нужно знать
16
+
17
+ **`active` означает «поток жив», а не «счётчик больше нуля».** Ноль посетителей при живом
18
+ потоке — это данные; оборванный поток — их отсутствие, и выглядеть они должны по-разному.
19
+ Поэтому `[active]="true"` с `[count]="0"` — законное состояние, а `count === null` рисует
20
+ прочерк, а не ноль.
21
+
22
+ ## Как этим пользоваться
23
+
24
+ - Пустая подпись равна отсутствию входа: подставляется переведённая формулировка кита.
25
+ Значит `[label]="maybeEmpty()"` безопасно.
26
+ - Живость дублируется атрибутом `data-active` — по нему бейдж находят проверки, не завязываясь
27
+ на BEM-класс.
28
+
29
+ ## Рядом
30
+
31
+ - [`rt-tag`](../tag/CONTEXT.md) — статусная пилюля без счётчика.
@@ -1,6 +1,9 @@
1
- /* rt-live-badge — пилюля живого счётчика в common/ui (Emulated encapsulation; :host обязательно). */
1
+ /* rt-live-badge — пилюля живого счётчика в common/ui. ViewEncapsulation.None.
2
+ Хост адресуется по имени элемента, а не по классу блока: класс `.rt-live-badge` висит
3
+ и на `<rt-live-badge>`, и на корне шаблона, поэтому правила блока вложены в хост —
4
+ так они достаются только корню шаблона. */
2
5
 
3
- :host {
6
+ rt-live-badge {
4
7
  display: inline-flex;
5
8
 
6
9
  .rt-live-badge {