@rt-tools/ui-kit-v2 0.29.1 → 0.31.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 (64) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +23 -29
  2. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs +2 -2
  4. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +223 -3
  6. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +2 -2
  8. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +1 -2
  10. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +2 -2
  12. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +277 -904
  14. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +2 -2
  16. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +1 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -1
  19. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +35 -48
  20. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +2 -2
  22. package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs +2 -2
  23. package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs.map +1 -1
  24. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +4 -4
  25. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-select.mjs +37 -181
  27. package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
  28. package/fesm2022/rt-tools-ui-kit-v2-service-block.mjs +131 -0
  29. package/fesm2022/rt-tools-ui-kit-v2-service-block.mjs.map +1 -0
  30. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +5 -36
  31. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  32. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +2 -3
  33. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +2 -2
  35. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -1
  36. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +8 -13
  37. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -1
  38. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +12 -7
  39. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -1
  40. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +2 -2
  41. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
  42. package/fesm2022/rt-tools-ui-kit-v2.mjs +1 -0
  43. package/package.json +5 -1
  44. package/src/lib/components/dynamic-selector/stories/component/test-dynamic-selector-popup-frame.component.scss +6 -0
  45. package/src/lib/components/page-header/rt-page-header.component.scss +4 -2
  46. package/src/lib/components/service-block/CONTEXT.md +37 -0
  47. package/src/lib/components/service-block/rt-service-block.component.scss +61 -0
  48. package/src/lib/components/tree/CONTEXT.md +1 -1
  49. package/src/rich-editor/lib/components/ai-chat/CONTEXT.md +1 -1
  50. package/src/styles/_breakpoints.scss +37 -4
  51. package/src/styles/_index.scss +0 -4
  52. package/styles/tokens.css +0 -8
  53. package/types/rt-tools-ui-kit-v2-autocomplete.d.ts +4 -3
  54. package/types/rt-tools-ui-kit-v2-core.d.ts +155 -1
  55. package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +51 -269
  56. package/types/rt-tools-ui-kit-v2-multiselect.d.ts +4 -3
  57. package/types/rt-tools-ui-kit-v2-select.d.ts +12 -74
  58. package/types/rt-tools-ui-kit-v2-service-block.d.ts +34 -0
  59. package/types/rt-tools-ui-kit-v2-side-menu.d.ts +5 -20
  60. package/types/rt-tools-ui-kit-v2-tag.d.ts +2 -3
  61. package/types/rt-tools-ui-kit-v2-tree-selector.d.ts +0 -2
  62. package/types/rt-tools-ui-kit-v2-tree.d.ts +7 -9
  63. package/types/rt-tools-ui-kit-v2.d.ts +1 -0
  64. package/src/styles/_place.scss +0 -7
@@ -1,10 +1,10 @@
1
- import { DOCUMENT, NgTemplateOutlet } from "@angular/common";
1
+ import { NgTemplateOutlet } from "@angular/common";
2
2
  import * as i0 from "@angular/core";
3
- import { ChangeDetectionStrategy, Component, Directive, TemplateRef, ViewEncapsulation, afterRenderEffect, booleanAttribute, computed, contentChild, forwardRef, inject, input, output, signal, viewChild } from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, Directive, TemplateRef, ViewEncapsulation, booleanAttribute, computed, contentChild, forwardRef, inject, input, output, signal, viewChild } from "@angular/core";
4
4
  import * as i2 from "@angular/forms";
5
5
  import { FormsModule } from "@angular/forms";
6
6
  import { BlockDirective, ElemDirective, ModDirective } from "@rt-tools/core";
7
- import { RT_KIT_LABELS, rtKitLabel } from "@rt-tools/ui-kit-v2/core";
7
+ import { RT_KIT_LABELS, RtOptionList, rtKitLabel, rtTreeFind, rtTreeIsTree, rtTreeOpenFor, rtTreeRows, rtTreeSideKey, rtTreeToggle } from "@rt-tools/ui-kit-v2/core";
8
8
  import { RtFormControlBase } from "@rt-tools/ui-kit-v2/form-control";
9
9
  import { RtIconComponent } from "@rt-tools/ui-kit-v2/icon";
10
10
  import { RtIconButtonComponent } from "@rt-tools/ui-kit-v2/icon-button";
@@ -12,137 +12,6 @@ import { RtInputComponent } from "@rt-tools/ui-kit-v2/input";
12
12
  import { RtPopoverDirective } from "@rt-tools/ui-kit-v2/popover";
13
13
  import * as i1 from "@rt-tools/ui-kit-v2/radius";
14
14
  import { RtRadiusDirective } from "@rt-tools/ui-kit-v2/radius";
15
- function rtScrollActiveOptionIntoView(activeOptionId) {
16
- const document = inject(DOCUMENT);
17
- afterRenderEffect(() => {
18
- const id = activeOptionId();
19
- const option = id ? document.getElementById(id) : null;
20
- if (typeof option?.scrollIntoView === "function") option.scrollIntoView({ block: "nearest" });
21
- });
22
- }
23
- function childrenOf(option) {
24
- return option.children ?? [];
25
- }
26
- function isBranch(option) {
27
- return childrenOf(option).length > 0;
28
- }
29
- function rtTreeIsTree(options) {
30
- return options.some((option) => isBranch(option));
31
- }
32
- function rtTreeFind(options, value) {
33
- for (const option of options) {
34
- if (option.value === value) return option;
35
- const found = rtTreeFind(childrenOf(option), value);
36
- if (found) return found;
37
- }
38
- }
39
- function rtTreeLeaves(option) {
40
- if (option.disabled) return [];
41
- if (!isBranch(option)) return [option.value];
42
- return childrenOf(option).flatMap((child) => rtTreeLeaves(child));
43
- }
44
- function rtTreeBranchState(option, chosen) {
45
- const leaves = rtTreeLeaves(option);
46
- const count = leaves.filter((leaf) => chosen.includes(leaf)).length;
47
- if (count === 0) return "none";
48
- return count === leaves.length ? "all" : "some";
49
- }
50
- function holdsChosen(option, chosen) {
51
- return childrenOf(option).some((child) => chosen.includes(child.value) || holdsChosen(child, chosen));
52
- }
53
- function collectOpen(list, chosen, open) {
54
- for (const option of list) if (holdsChosen(option, chosen)) {
55
- open.add(option.value);
56
- collectOpen(childrenOf(option), chosen, open);
57
- }
58
- }
59
- function rtTreeOpenFor(options, chosen) {
60
- const open = /* @__PURE__ */ new Set();
61
- collectOpen(options, chosen, open);
62
- return open;
63
- }
64
- function matches(option, term) {
65
- return option.label.toLowerCase().includes(term);
66
- }
67
- function descendantMatches(option, term) {
68
- return childrenOf(option).some((child) => matches(child, term) || descendantMatches(child, term));
69
- }
70
- function placeOption(walk, option, place) {
71
- const self = !place.filtered || matches(option, walk.term);
72
- const below = place.filtered && descendantMatches(option, walk.term);
73
- if (!self && !below) return null;
74
- const branch = isBranch(option);
75
- const openBySet = self && walk.open.has(option.value);
76
- const open = branch && (openBySet || below);
77
- walk.rows.push({
78
- option,
79
- branch,
80
- open,
81
- level: place.level,
82
- parent: place.parent
83
- });
84
- return open ? {
85
- level: place.level + 1,
86
- parent: option.value,
87
- filtered: place.filtered && !openBySet
88
- } : null;
89
- }
90
- function walkRows(walk, list, place) {
91
- for (const option of list) {
92
- const childPlace = placeOption(walk, option, place);
93
- if (childPlace) walkRows(walk, childrenOf(option), childPlace);
94
- }
95
- }
96
- function rtTreeRows(options, open, rawTerm = "") {
97
- const term = rawTerm.toLowerCase().trim();
98
- const walk = {
99
- open,
100
- term,
101
- rows: []
102
- };
103
- walkRows(walk, options, {
104
- level: 0,
105
- parent: null,
106
- filtered: term.length > 0
107
- });
108
- return walk.rows;
109
- }
110
- function sideRight(rows, row, index) {
111
- if (row.branch && !row.open) return {
112
- toggle: row.option.value,
113
- index
114
- };
115
- return {
116
- toggle: null,
117
- index: row.branch && rows[index + 1]?.parent === row.option.value ? index + 1 : index
118
- };
119
- }
120
- function sideLeft(rows, row, index) {
121
- if (row.branch && row.open) return {
122
- toggle: row.option.value,
123
- index
124
- };
125
- const parentIndex = rows.findIndex((candidate) => candidate.option.value === row.parent);
126
- return {
127
- toggle: null,
128
- index: parentIndex >= 0 ? parentIndex : index
129
- };
130
- }
131
- function rtTreeSideKey(rows, index, key) {
132
- const row = rows[index];
133
- if (row && key === "ArrowRight") return sideRight(rows, row, index);
134
- if (row && key === "ArrowLeft") return sideLeft(rows, row, index);
135
- return {
136
- toggle: null,
137
- index
138
- };
139
- }
140
- function rtTreeToggle(open, value) {
141
- const next = new Set(open);
142
- if (next.has(value)) next.delete(value);
143
- else next.add(value);
144
- return next;
145
- }
146
15
  var RtSelectTriggerDirective = class RtSelectTriggerDirective {
147
16
  constructor() {
148
17
  this.templateRef = inject(TemplateRef);
@@ -179,21 +48,14 @@ i0.ɵɵngDeclareClassMetadata({
179
48
  }]
180
49
  });
181
50
  const BEM_BLOCK = "rt-select";
182
- const OPEN_KEYS = /* @__PURE__ */ new Set([
183
- "ArrowDown",
184
- "ArrowUp",
185
- "Enter",
186
- " "
187
- ]);
188
51
  let panelIdSeed = 0;
189
52
  function nextPanelId() {
190
53
  panelIdSeed += 1;
191
54
  return panelIdSeed;
192
55
  }
193
56
  var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
194
- #t_uiSearch;
195
57
  constructor() {
196
- super();
58
+ super(...arguments);
197
59
  this.#t_uiSearch = rtKitLabel("uiSearch");
198
60
  this.t = inject(RT_KIT_LABELS);
199
61
  this.panelId = `rt-select-panel-${nextPanelId()}`;
@@ -201,10 +63,19 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
201
63
  this.popover = viewChild.required(RtPopoverDirective, ...ngDevMode ? [{ debugName: "popover" }] : /* istanbul ignore next */ []);
202
64
  this.triggerEl = viewChild("triggerEl", ...ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []);
203
65
  this.filterTerm = signal("", ...ngDevMode ? [{ debugName: "filterTerm" }] : /* istanbul ignore next */ []);
204
- this.activeIndex = signal(-1, ...ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []);
205
66
  this.openBranches = signal(/* @__PURE__ */ new Set(), ...ngDevMode ? [{ debugName: "openBranches" }] : /* istanbul ignore next */ []);
206
67
  this.isOpen = computed(() => this.popover().isOpen(), ...ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []);
207
- this.activeOptionId = computed(() => this.isOpen() && this.activeIndex() >= 0 ? `${this.panelId}-opt-${this.activeIndex()}` : null, ...ngDevMode ? [{ debugName: "activeOptionId" }] : /* istanbul ignore next */ []);
68
+ this.optionList = new RtOptionList({
69
+ panelId: () => this.panelId,
70
+ isOpen: () => this.isOpen(),
71
+ count: () => this.rows().length,
72
+ isDisabled: (index) => Boolean(this.rows()[index]?.option.disabled),
73
+ chooseKeys: /* @__PURE__ */ new Set(["Enter", " "]),
74
+ sideKeys: true,
75
+ edgeKeys: true
76
+ });
77
+ this.activeIndex = this.optionList.activeIndex;
78
+ this.activeOptionId = this.optionList.activeOptionId;
208
79
  this.triggerTpl = contentChild(RtSelectTriggerDirective, ...ngDevMode ? [{ debugName: "triggerTpl" }] : /* istanbul ignore next */ []);
209
80
  this.hasValue = computed(() => this.value() !== null, ...ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []);
210
81
  this.selectedLabel = computed(() => {
@@ -233,8 +104,8 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
233
104
  this.panelWidth = input("trigger-min", ...ngDevMode ? [{ debugName: "panelWidth" }] : /* istanbul ignore next */ []);
234
105
  this.panelMaxHeight = input(null, ...ngDevMode ? [{ debugName: "panelMaxHeight" }] : /* istanbul ignore next */ []);
235
106
  this.selectionChange = output();
236
- rtScrollActiveOptionIntoView(this.activeOptionId);
237
107
  }
108
+ #t_uiSearch;
238
109
  setDisabledState(disabled) {
239
110
  super.setDisabledState(disabled);
240
111
  if (disabled) this.popover().close();
@@ -281,38 +152,39 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
281
152
  onTriggerKeydown(event) {
282
153
  if (this.isDisabled()) return;
283
154
  if (!this.isOpen()) {
284
- if (OPEN_KEYS.has(event.key)) {
155
+ if (this.optionList.opensBy(event.key)) {
285
156
  event.preventDefault();
286
157
  this.popover().open();
287
- if (this.activeIndex() < 0) this.#moveActive(1);
158
+ if (this.activeIndex() < 0) this.optionList.move(1);
288
159
  }
289
160
  return;
290
161
  }
291
162
  this.#handleOpenKeydown(event);
292
163
  }
293
164
  #handleOpenKeydown(event) {
294
- switch (event.key) {
295
- case "ArrowDown":
296
- event.preventDefault();
297
- this.#moveActive(1);
165
+ const action = this.optionList.actionOf(event.key);
166
+ if (action === "none") return;
167
+ event.preventDefault();
168
+ switch (action) {
169
+ case "next":
170
+ this.optionList.move(1);
298
171
  break;
299
- case "ArrowUp":
300
- event.preventDefault();
301
- this.#moveActive(-1);
172
+ case "previous":
173
+ this.optionList.move(-1);
302
174
  break;
303
- case "Enter":
304
- case " ":
305
- event.preventDefault();
175
+ case "first":
176
+ this.optionList.first();
177
+ break;
178
+ case "last":
179
+ this.optionList.last();
180
+ break;
181
+ case "choose":
306
182
  this.#selectActive();
307
183
  break;
308
- case "Escape":
309
- event.preventDefault();
184
+ case "close":
310
185
  this.popover().close();
311
186
  break;
312
- case "ArrowRight":
313
- case "ArrowLeft":
314
- event.preventDefault();
315
- this.#sideKey(event.key);
187
+ case "side": this.#sideKey(event.key);
316
188
  }
317
189
  }
318
190
  #sideKey(key) {
@@ -321,21 +193,6 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
321
193
  if (toggle !== null) this.openBranches.update((open) => rtTreeToggle(open, toggle));
322
194
  this.activeIndex.set(answer.index);
323
195
  }
324
- #moveActive(delta) {
325
- const opts = this.rows().map((row) => row.option);
326
- if (opts.length === 0) {
327
- this.activeIndex.set(-1);
328
- return;
329
- }
330
- let index = this.activeIndex();
331
- for (let step = 0; step < opts.length; step += 1) {
332
- index = (index + delta + opts.length) % opts.length;
333
- if (!opts[index].disabled) {
334
- this.activeIndex.set(index);
335
- return;
336
- }
337
- }
338
- }
339
196
  #selectActive() {
340
197
  const opts = this.rows().map((row) => row.option);
341
198
  const index = this.activeIndex();
@@ -347,7 +204,7 @@ var RtSelectComponent = class RtSelectComponent extends RtFormControlBase {
347
204
  version: "22.2.2",
348
205
  ngImport: i0,
349
206
  type: RtSelectComponent,
350
- deps: [],
207
+ deps: null,
351
208
  target: i0.ɵɵFactoryTarget.Component
352
209
  });
353
210
  }
@@ -639,7 +496,6 @@ i0.ɵɵngDeclareClassMetadata({
639
496
  styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-radius: var(--rt-input-radius);--rt-select-fill-radius: var(--rt-radius-sm);--rt-select-caret-transition-duration: var(--rt-duration-fast);--rt-select-own-outline-width: var(--rt-border-width-medium);position:relative;display:block;width:100%}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select--appearance--fill .rt-select__trigger{border-color:transparent;border-radius:var(--rt-select-fill-radius) var(--rt-select-fill-radius) 0 0;border-bottom-color:var(--rt-color-field-fill-underline);background-color:var(--rt-color-bg-surface-subtle-2)}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-select-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select__trigger--own{width:auto;min-height:0;padding:0;border-color:transparent;background-color:transparent;line-height:var(--rt-leading-none)}.rt-select__trigger--own:focus{border-color:transparent;box-shadow:none}.rt-select__trigger--own:focus-visible{outline:var(--rt-select-own-outline-width) solid var(--rt-input-color-border-focus);outline-offset:var(--rt-input-focus-outline-offset)}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--open .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--invalid .rt-select__trigger--own{border-color:transparent}.rt-select--invalid .rt-select__trigger--own:focus{box-shadow:none}.rt-select--disabled{opacity:var(--rt-input-disabled-opacity)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}:is(.rt-preset-material,[data-preset=material]) .rt-select__chevron{max-width:10px;max-height:5px;background-color:currentcolor;clip-path:polygon(0 0,100% 0,50% 100%);transform:none}:is(.rt-preset-material,[data-preset=material]) .rt-select__chevron svg{display:none}.rt-select__panel{--rt-select-panel-shadow: var(--rt-overlay-select-shadow);--rt-select-panel-radius: var(--rt-overlay-select-radius);--rt-select-option-font-size: var(--rt-overlay-select-option-font-size);--rt-select-selected-bg: var(--rt-overlay-select-selected-bg);--rt-select-selected-weight: var(--rt-overlay-select-selected-weight);--rt-select-check-display: var(--rt-overlay-select-check-display);--rt-select-option-min-height: var(--rt-overlay-select-option-min-height);--rt-select-panel-border-width: var(--rt-overlay-select-panel-border-width);--rt-select-tree-indent: var(--rt-space-lg);--rt-select-panel-max-height: var(--rt-input-panel-max-height);width:100%;max-height:var(--rt-select-panel-max-height);box-sizing:border-box;border:var(--rt-select-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-select-panel-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{--rt-select-tree-level: 0;display:flex;min-height:var(--rt-select-option-min-height);box-sizing:border-box;align-items:center;justify-content:space-between;padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-select-option-font-size);gap:var(--rt-space-sm);padding-inline-start:calc(var(--rt-space-md) + var(--rt-select-tree-indent) * var(--rt-select-tree-level))}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-select-selected-bg);font-weight:var(--rt-select-selected-weight)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__option-label{min-width:0;flex:1 1 auto}.rt-select__toggle,.rt-select__toggle-space{display:inline-flex;flex-shrink:0;block-size:var(--rt-input-icon-size);inline-size:var(--rt-input-icon-size)}.rt-select__toggle{align-items:center;justify-content:center;padding:0;border:0;background:transparent;cursor:pointer}.rt-select__check{display:var(--rt-select-check-display);flex-shrink:0}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}.rt-select[data-rt-radius=none]{--rt-select-radius: var(--rt-radius-none)}.rt-select[data-rt-radius=xs]{--rt-select-radius: var(--rt-radius-xs)}.rt-select[data-rt-radius=sm]{--rt-select-radius: var(--rt-radius-sm)}.rt-select[data-rt-radius=ms]{--rt-select-radius: var(--rt-radius-ms)}.rt-select[data-rt-radius=md]{--rt-select-radius: var(--rt-radius-md)}.rt-select[data-rt-radius=lg]{--rt-select-radius: var(--rt-radius-lg)}.rt-select[data-rt-radius=xl]{--rt-select-radius: var(--rt-radius-xl)}.rt-select[data-rt-radius=\"2xl\"]{--rt-select-radius: var(--rt-radius-2xl)}.rt-select[data-rt-radius=full]{--rt-select-radius: var(--rt-radius-full)}}\n"]
640
497
  }]
641
498
  }],
642
- ctorParameters: () => [],
643
499
  propDecorators: {
644
500
  popover: [{
645
501
  type: i0.ViewChild,
@@ -723,6 +579,6 @@ i0.ɵɵngDeclareClassMetadata({
723
579
  }]
724
580
  }
725
581
  });
726
- export { RtSelectComponent, RtSelectTriggerDirective, rtScrollActiveOptionIntoView, rtTreeBranchState, rtTreeFind, rtTreeIsTree, rtTreeLeaves, rtTreeOpenFor, rtTreeRows, rtTreeSideKey, rtTreeToggle };
582
+ export { RtSelectComponent, RtSelectTriggerDirective };
727
583
 
728
584
  //# sourceMappingURL=rt-tools-ui-kit-v2-select.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-select.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/select/rt-select-active-option.ts","../../../projects/ui-kit-v2/src/lib/components/select/rt-select-tree.ts","../../../projects/ui-kit-v2/src/lib/components/select/rt-select-trigger.directive.ts","../../../projects/ui-kit-v2/src/lib/components/select/rt-select.component.ts","../../../projects/ui-kit-v2/src/lib/components/select/rt-select.component.html"],"sourcesContent":["import { DOCUMENT } from '@angular/common';\nimport { afterRenderEffect, inject } from '@angular/core';\n\n/**\n * Держит подсвеченную клавишами опцию в видимой части панели.\n *\n * Панель списка ограничена по высоте, и длинный список прокручивается внутри неё. Клавиши двигают\n * подсветку, но фокус остаётся на поле: браузер сам панель не прокручивает, и подсветка уходила\n * за нижний край — человек выбирал Enter'ом то, чего не видел. После каждой отрисовки, в которой\n * сменилась подсвеченная опция, она прокручивается ровно настолько, чтобы встать в панель.\n *\n * Панель рисуется в наложении, вне поддерева семьи, поэтому опция ищется в документе по своему\n * идентификатору — тому же, что стоит в `aria-activedescendant`. На сервере отрисовки нет, и\n * эффект не выполняется; среда без `scrollIntoView` пропускается.\n *\n * Зовётся в контексте внедрения — полем или конструктором семьи.\n *\n * @param activeOptionId идентификатор подсвеченной опции; `null` — подсветки нет или панель закрыта\n */\nexport function rtScrollActiveOptionIntoView(activeOptionId: () => string | null): void {\n const document: Document = inject(DOCUMENT);\n\n afterRenderEffect((): void => {\n const id: string | null = activeOptionId();\n const option: HTMLElement | null = id ? document.getElementById(id) : null;\n\n if (typeof option?.scrollIntoView === 'function') {\n option.scrollIntoView({ block: 'nearest' });\n }\n });\n}\n","import { IRtSelect } from './rt-select.model';\n\n/**\n * Дерево опций выбора из списка — общее для `rt-select` и `rt-multiselect`.\n *\n * Обе семьи раскладывают, ищут и считают дерево одинаково, поэтому это чистые функции в одном\n * месте: две копии разошлись бы на первой правке. Состояние — какие ветки раскрыты — живёт в семье,\n * здесь только расчёт по нему.\n */\n\ntype TOption<TValue> = IRtSelect.Option<TValue>;\ntype TRow<TValue> = IRtSelect.Row<TValue>;\n\nfunction childrenOf<TValue>(option: TOption<TValue>): ReadonlyArray<TOption<TValue>> {\n return option.children ?? [];\n}\n\nfunction isBranch<TValue>(option: TOption<TValue>): boolean {\n return childrenOf(option).length > 0;\n}\n\n/** Список — дерево, если хоть у одной опции есть дети. Плоский список рисуется как прежде. */\nexport function rtTreeIsTree<TValue>(options: ReadonlyArray<TOption<TValue>>): boolean {\n return options.some((option: TOption<TValue>): boolean => isBranch(option));\n}\n\n/** Опция по значению на любой глубине. */\nexport function rtTreeFind<TValue>(options: ReadonlyArray<TOption<TValue>>, value: TValue): TOption<TValue> | undefined {\n for (const option of options) {\n if (option.value === value) {\n return option;\n }\n const found: TOption<TValue> | undefined = rtTreeFind(childrenOf(option), value);\n if (found) {\n return found;\n }\n }\n return undefined;\n}\n\n/**\n * Включённые листья под опцией; у самого листа — он сам. Отключённая ветка не отдаёт никого: её\n * листья выбрать нельзя, как и её саму.\n */\nexport function rtTreeLeaves<TValue>(option: TOption<TValue>): ReadonlyArray<TValue> {\n if (option.disabled) {\n return [];\n }\n if (!isBranch(option)) {\n return [option.value];\n }\n return childrenOf(option).flatMap((child: TOption<TValue>): ReadonlyArray<TValue> => rtTreeLeaves(child));\n}\n\n/** Сколько включённых листьев ветки выбрано. Флажок ветки выводится из этого, а не хранится. */\nexport function rtTreeBranchState<TValue>(option: TOption<TValue>, chosen: ReadonlyArray<TValue>): IRtSelect.TBranchState {\n const leaves: ReadonlyArray<TValue> = rtTreeLeaves(option);\n const count: number = leaves.filter((leaf: TValue): boolean => chosen.includes(leaf)).length;\n if (count === 0) {\n return 'none';\n }\n return count === leaves.length ? 'all' : 'some';\n}\n\nfunction holdsChosen<TValue>(option: TOption<TValue>, chosen: ReadonlyArray<TValue>): boolean {\n return childrenOf(option).some((child: TOption<TValue>): boolean => chosen.includes(child.value) || holdsChosen(child, chosen));\n}\n\nfunction collectOpen<TValue>(list: ReadonlyArray<TOption<TValue>>, chosen: ReadonlyArray<TValue>, open: Set<TValue>): void {\n for (const option of list) {\n if (holdsChosen(option, chosen)) {\n open.add(option.value);\n collectOpen(childrenOf(option), chosen, open);\n }\n }\n}\n\n/** Ветки, под которыми лежит выбранное значение: они раскрыты, когда список открывается. */\nexport function rtTreeOpenFor<TValue>(options: ReadonlyArray<TOption<TValue>>, chosen: ReadonlyArray<TValue>): ReadonlySet<TValue> {\n const open: Set<TValue> = new Set<TValue>();\n collectOpen(options, chosen, open);\n return open;\n}\n\nfunction matches<TValue>(option: TOption<TValue>, term: string): boolean {\n return option.label.toLowerCase().includes(term);\n}\n\nfunction descendantMatches<TValue>(option: TOption<TValue>, term: string): boolean {\n return childrenOf(option).some((child: TOption<TValue>): boolean => matches(child, term) || descendantMatches(child, term));\n}\n\ninterface ITreeWalk<TValue> {\n readonly open: ReadonlySet<TValue>;\n readonly term: string;\n readonly rows: TRow<TValue>[];\n}\n\ninterface IRowPlace<TValue> {\n readonly level: number;\n readonly parent: TValue | null;\n readonly filtered: boolean;\n}\n\n/**\n * Раскладка одной опции: кладёт её строку и отвечает, где раскладывать её детей, или `null`, если\n * их не видно. `filtered` — действует ли поисковое слово на уровень: под веткой, совпавшей самой и\n * раскрытой, дети идут все, как без поиска.\n */\nfunction placeOption<TValue>(walk: ITreeWalk<TValue>, option: TOption<TValue>, place: IRowPlace<TValue>): IRowPlace<TValue> | null {\n const self: boolean = !place.filtered || matches(option, walk.term);\n const below: boolean = place.filtered && descendantMatches(option, walk.term);\n if (!self && !below) {\n return null;\n }\n const branch: boolean = isBranch(option);\n const openBySet: boolean = self && walk.open.has(option.value);\n const open: boolean = branch && (openBySet || below);\n walk.rows.push({ option, branch, open, level: place.level, parent: place.parent });\n return open ? { level: place.level + 1, parent: option.value, filtered: place.filtered && !openBySet } : null;\n}\n\nfunction walkRows<TValue>(walk: ITreeWalk<TValue>, list: ReadonlyArray<TOption<TValue>>, place: IRowPlace<TValue>): void {\n for (const option of list) {\n const childPlace: IRowPlace<TValue> | null = placeOption(walk, option, place);\n if (childPlace) {\n walkRows(walk, childrenOf(option), childPlace);\n }\n }\n}\n\n/**\n * Видимые строки по уровням. Без поискового слова раскрыты ветки из `open`. Со словом строка\n * остаётся, если совпала она сама или кто-то под ней, и путь к совпадению показан раскрытым.\n * Ветка, совпавшая сама, держит детей такими, какими они были.\n */\nexport function rtTreeRows<TValue>(\n options: ReadonlyArray<TOption<TValue>>,\n open: ReadonlySet<TValue>,\n rawTerm: string = ''\n): ReadonlyArray<TRow<TValue>> {\n const term: string = rawTerm.toLowerCase().trim();\n const walk: ITreeWalk<TValue> = { open, term, rows: [] };\n walkRows(walk, options, { level: 0, parent: null, filtered: term.length > 0 });\n return walk.rows;\n}\n\nfunction sideRight<TValue>(rows: ReadonlyArray<TRow<TValue>>, row: TRow<TValue>, index: number): IRtSelect.SideKeyAnswer<TValue> {\n if (row.branch && !row.open) {\n return { toggle: row.option.value, index };\n }\n const firstChild: boolean = row.branch && rows[index + 1]?.parent === row.option.value;\n return { toggle: null, index: firstChild ? index + 1 : index };\n}\n\nfunction sideLeft<TValue>(rows: ReadonlyArray<TRow<TValue>>, row: TRow<TValue>, index: number): IRtSelect.SideKeyAnswer<TValue> {\n if (row.branch && row.open) {\n return { toggle: row.option.value, index };\n }\n const parentIndex: number = rows.findIndex((candidate: TRow<TValue>): boolean => candidate.option.value === row.parent);\n return { toggle: null, index: parentIndex >= 0 ? parentIndex : index };\n}\n\n/**\n * Ответ на боковую стрелку. Вправо: свёрнутая ветка раскрывается, раскрытая уводит подсветку на\n * первого ребёнка. Влево: раскрытая ветка сворачивается, иначе подсветка уходит на родителя.\n */\nexport function rtTreeSideKey<TValue>(rows: ReadonlyArray<TRow<TValue>>, index: number, key: string): IRtSelect.SideKeyAnswer<TValue> {\n const row: TRow<TValue> | undefined = rows[index];\n if (row && key === 'ArrowRight') {\n return sideRight(rows, row, index);\n }\n if (row && key === 'ArrowLeft') {\n return sideLeft(rows, row, index);\n }\n return { toggle: null, index };\n}\n\n/** Набор раскрытых веток с переключённой одной. */\nexport function rtTreeToggle<TValue>(open: ReadonlySet<TValue>, value: TValue): ReadonlySet<TValue> {\n const next: Set<TValue> = new Set<TValue>(open);\n if (next.has(value)) {\n next.delete(value);\n } else {\n next.add(value);\n }\n return next;\n}\n","import { Directive, inject, TemplateRef } from '@angular/core';\n\nimport { IRtSelect } from './rt-select.model';\n\n/**\n * Маркер своей разметки указателя — того, что человек нажимает, чтобы открыть список.\n *\n * Поведение указателя остаётся киту: открытие панели, её ширина, приметы доступности,\n * отключённость, клавиши и закрытие висят на кнопке. Потребитель даёт только то, что рисуется\n * внутри неё, и получает три значения обстановки: открыт ли список, что выбрано, отключён ли выбор.\n *\n * Один маркер на обе семьи выбора: они отличаются только разметкой внутри кнопки, и два маркера\n * разошлись бы на первой же правке.\n *\n * @example\n * ```html\n * <rt-select [options]=\"options\">\n * <ng-template rtSelectTrigger let-state>\n * <rt-tag [value]=\"state.label\" [icon]=\"state.isOpen ? 'chevron-up' : 'chevron-down'\" />\n * </ng-template>\n * </rt-select>\n * ```\n */\n@Directive({\n selector: '[rtSelectTrigger]',\n})\nexport class RtSelectTriggerDirective<TValue = unknown> {\n public readonly templateRef: TemplateRef<IRtSelect.TriggerContext<TValue>> =\n inject<TemplateRef<IRtSelect.TriggerContext<TValue>>>(TemplateRef);\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n computed,\n contentChild,\n forwardRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n InputSignalWithTransform,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport { RtPopoverDirective } from '@rt-tools/ui-kit-v2/popover';\nimport { IRtPopover } from '@rt-tools/ui-kit-v2/popover';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { rtScrollActiveOptionIntoView } from './rt-select-active-option';\nimport { rtTreeFind, rtTreeIsTree, rtTreeOpenFor, rtTreeRows, rtTreeSideKey, rtTreeToggle } from './rt-select-tree';\nimport { RtSelectTriggerDirective } from './rt-select-trigger.directive';\nimport { IRtSelect } from './rt-select.model';\n\nconst BEM_BLOCK: string = 'rt-select';\n\n/** Клавиши, открывающие закрытый dropdown с фокуса на trigger'е. */\nconst OPEN_KEYS: ReadonlySet<string> = new Set<string>(['ArrowDown', 'ArrowUp', 'Enter', ' ']);\n\ntype TBooleanInput = boolean | string;\n\nlet panelIdSeed: number = 0;\n\nfunction nextPanelId(): number {\n panelIdSeed += 1;\n return panelIdSeed;\n}\n\n/**\n * Single-select dropdown с опциональным filter input.\n *\n * Panel рендерится через CDK Overlay (директива `[rtPopover]` на trigger'е) —\n * не обрезается `overflow: hidden` предками (таблицы, aside, карточки) и\n * авто-flip'ается у края viewport. CVA через общий `RtFormControlBase`. Крестик\n * (`clearable`) слева от шеврона — виден при наличии значения, очищает выбор.\n *\n * Keyboard-nav (activedescendant): фокус на trigger'е, ArrowUp/Down двигают\n * виртуальный highlight (`aria-activedescendant`), Enter выбирает, Escape\n * закрывает. Home/End/typeahead — deferred.\n */\n@Component({\n selector: 'rt-select',\n templateUrl: './rt-select.component.html',\n styleUrl: './rt-select.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n FormsModule,\n NgTemplateOutlet,\n\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n RtInputComponent,\n RtPopoverDirective,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtSelectComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-select--open]': 'isOpen()',\n '[class.rt-select--disabled]': 'isDisabled()',\n '[class.rt-select--readonly]': 'isReadonly()',\n '[class.rt-select--invalid]': 'isInvalid()',\n '[class.rt-select--borderless]': '!bordered()',\n '[class.rt-select--appearance--fill]': \"appearance() === 'fill'\",\n '[class.rt-select--size--sm]': \"size() === 'sm'\",\n '[class.rt-select--size--lg]': \"size() === 'lg'\",\n '[class.rt-select--with-icon-left]': '!!iconLeft()',\n },\n})\nexport class RtSelectComponent<TValue> extends RtFormControlBase<TValue | null> {\n readonly #t_uiSearch: Signal<string> = rtKitLabel('uiSearch');\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly panelId: string = `rt-select-panel-${nextPanelId()}`;\n\n protected readonly filterPlaceholderText: Signal<string> = computed((): string => this.filterPlaceholder() || this.#t_uiSearch());\n\n protected readonly popover: Signal<RtPopoverDirective> = viewChild.required(RtPopoverDirective);\n\n protected readonly triggerEl: Signal<ElementRef<HTMLButtonElement> | undefined> = viewChild<ElementRef<HTMLButtonElement>>('triggerEl');\n\n protected readonly filterTerm: WritableSignal<string> = signal<string>('');\n protected readonly activeIndex: WritableSignal<number> = signal<number>(-1);\n /** Раскрытые ветки дерева. Живут, пока открыта панель: при открытии раскрыты ветки над выбранным. */\n protected readonly openBranches: WritableSignal<ReadonlySet<TValue>> = signal<ReadonlySet<TValue>>(new Set<TValue>());\n\n protected readonly isOpen: Signal<boolean> = computed((): boolean => this.popover().isOpen());\n\n /** Идентификатор подсвеченной опции — тот же, что уходит в `aria-activedescendant`. */\n protected readonly activeOptionId: Signal<string | null> = computed((): string | null =>\n this.isOpen() && this.activeIndex() >= 0 ? `${this.panelId}-opt-${this.activeIndex()}` : null\n );\n\n /**\n * Своя разметка указателя, если потребитель её объявил. Не объявил — кит рисует свою, и ни один\n * нынешний потребитель не двигается.\n */\n protected readonly triggerTpl: Signal<RtSelectTriggerDirective<TValue> | undefined> = contentChild(RtSelectTriggerDirective);\n\n protected readonly hasValue: Signal<boolean> = computed((): boolean => this.value() !== null);\n\n protected readonly selectedLabel: Signal<string> = computed((): string => {\n const v: TValue | null = this.value();\n if (v === null) {\n return '';\n }\n return rtTreeFind(this.options(), v)?.label ?? '';\n });\n\n /** Дерево ли список: тогда у строк есть место под стрелку и отступ по уровню. */\n protected readonly isTree: Signal<boolean> = computed((): boolean => rtTreeIsTree(this.options()));\n\n /** Видимые строки: плоский список — как прежде, дерево — по раскрытым веткам и поисковому слову. */\n protected readonly rows: Signal<ReadonlyArray<IRtSelect.Row<TValue>>> = computed((): ReadonlyArray<IRtSelect.Row<TValue>> =>\n rtTreeRows(this.options(), this.openBranches(), this.filterTerm())\n );\n\n /** Три значения, которые кит отдаёт своей разметке указателя, и не больше. */\n protected readonly triggerState: Signal<IRtSelect.TriggerState<TValue>> = computed((): IRtSelect.TriggerState<TValue> => ({\n isOpen: this.isOpen(),\n value: this.value(),\n label: this.selectedLabel(),\n isDisabled: this.isDisabled(),\n }));\n\n /** Вид рамки: `outline` — рамка со всех сторон, `fill` — залитое поле с чертой снизу. */\n public readonly appearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n\n public readonly displayText: Signal<string> = computed((): string => this.selectedLabel());\n\n public readonly options: InputSignal<ReadonlyArray<IRtSelect.Option<TValue>>> = input<ReadonlyArray<IRtSelect.Option<TValue>>>([]);\n\n public readonly placeholder: InputSignal<string> = input<string>('');\n\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n\n public readonly filter: InputSignalWithTransform<boolean, TBooleanInput> = input<boolean, TBooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Пусто — берётся переведённая подпись по умолчанию */\n public readonly filterPlaceholder: InputSignal<string> = input<string>('');\n\n /**\n * Чем мерится панель. По умолчанию она не уже кнопки и дальше растёт по содержимому:\n * со своим указателем кнопка бывает узкой, и панель по её ширине давила бы содержимое.\n * `trigger` возвращает прежнее — ровно по кнопке, `auto` пускает панель по содержимому\n * целиком. Предел высоты назначается входом `panelMaxHeight`.\n */\n public readonly panelWidth: InputSignal<IRtPopover.Width> = input<IRtPopover.Width>('trigger-min');\n\n /**\n * Предел высоты панели — длина как в стилях, например `20rem`. Пусто — предел кита\n * `--rt-input-panel-max-height`, и длинный список прокручивается внутри панели: без предела\n * он уходил за нижний край экрана, и пункты за краем были недостижимы. `none` снимает предел —\n * для короткого списка, который должен открываться целиком, без прокрутки. Значение ставится\n * на саму панель: она рисуется в наложении, вне поддерева блока.\n */\n public readonly panelMaxHeight: InputSignal<string | null> = input<string | null>(null);\n\n public readonly selectionChange: OutputEmitterRef<TValue | null> = output<TValue | null>();\n\n constructor() {\n super();\n // Подсветка, сдвинутая клавишами, не уходит за край панели с пределом высоты.\n rtScrollActiveOptionIntoView(this.activeOptionId);\n }\n\n public override setDisabledState(disabled: boolean): void {\n super.setDisabledState(disabled);\n if (disabled) {\n this.popover().close();\n }\n }\n\n protected getEmptyValue(): TValue | null {\n return null;\n }\n\n protected focusAfterClear(): void {\n this.triggerEl()?.nativeElement.focus();\n }\n\n protected override clear(event?: Event): void {\n if (this.isDisabled()) {\n return;\n }\n super.clear(event);\n this.selectionChange.emit(null);\n }\n\n /**\n * Открытый список встаёт подсветкой на выбранный пункт.\n *\n * Иначе стрелка вниз начинала с первого пункта, и человек с выбранным «20» из «10, 20, 40»\n * получал «10» вместо «40».\n */\n protected onOpened(): void {\n const v: TValue | null = this.value();\n this.openBranches.set(rtTreeOpenFor(this.options(), v === null ? [] : [v]));\n if (v !== null) {\n const chosen: number = this.rows().findIndex((row: IRtSelect.Row<TValue>): boolean => row.option.value === v);\n if (chosen >= 0) {\n this.activeIndex.set(chosen);\n }\n }\n }\n\n protected toggleBranch(event: Event, value: TValue): void {\n event.stopPropagation();\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, value));\n }\n\n protected onClosed(): void {\n // Закрытие (outside-click / Escape / выбор) = пользователь увёл фокус.\n this.filterTerm.set('');\n this.activeIndex.set(-1);\n this.markTouched();\n }\n\n protected select(opt: IRtSelect.Option<TValue>): void {\n if (opt.disabled) {\n return;\n }\n this.value.set(opt.value);\n this.emitChange(opt.value);\n this.selectionChange.emit(opt.value);\n this.popover().close();\n }\n\n protected setFilter(term: string): void {\n this.filterTerm.set(term);\n this.activeIndex.set(-1);\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.isDisabled()) {\n return;\n }\n if (!this.isOpen()) {\n if (OPEN_KEYS.has(event.key)) {\n event.preventDefault();\n this.popover().open();\n if (this.activeIndex() < 0) {\n this.#moveActive(1);\n }\n }\n return;\n }\n this.#handleOpenKeydown(event);\n }\n\n #handleOpenKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.#moveActive(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.#moveActive(-1);\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this.#selectActive();\n break;\n case 'Escape':\n event.preventDefault();\n this.popover().close();\n break;\n case 'ArrowRight':\n case 'ArrowLeft':\n event.preventDefault();\n this.#sideKey(event.key);\n break;\n default:\n break;\n }\n }\n\n #sideKey(key: string): void {\n const answer: IRtSelect.SideKeyAnswer<TValue> = rtTreeSideKey(this.rows(), this.activeIndex(), key);\n const toggle: TValue | null = answer.toggle;\n if (toggle !== null) {\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, toggle));\n }\n this.activeIndex.set(answer.index);\n }\n\n #moveActive(delta: number): void {\n const opts: ReadonlyArray<IRtSelect.Option<TValue>> = this.rows().map(\n (row: IRtSelect.Row<TValue>): IRtSelect.Option<TValue> => row.option\n );\n if (opts.length === 0) {\n this.activeIndex.set(-1);\n return;\n }\n let index: number = this.activeIndex();\n for (let step: number = 0; step < opts.length; step += 1) {\n index = (index + delta + opts.length) % opts.length;\n if (!opts[index].disabled) {\n this.activeIndex.set(index);\n return;\n }\n }\n }\n\n #selectActive(): void {\n const opts: ReadonlyArray<IRtSelect.Option<TValue>> = this.rows().map(\n (row: IRtSelect.Row<TValue>): IRtSelect.Option<TValue> => row.option\n );\n const index: number = this.activeIndex();\n if (index >= 0 && index < opts.length) {\n this.select(opts[index]);\n }\n }\n}\n","<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n {{ selectedLabel() || placeholder() }}\n </span>\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [style.--rt-select-panel-max-height]=\"panelMaxHeight()\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input\n iconLeft=\"ico-search\"\n [placeholder]=\"filterPlaceholderText()\"\n [ngModel]=\"filterTerm()\"\n (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n Опции listbox'а выбираются мышью через (click); keyboard-nav —\n на trigger'е (activedescendant: ArrowUp/Down/Enter), опции не\n focusable индивидуально, поэтому a11y-правила здесь не применимы.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-select-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: row.option.value === value(),\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"row.option.value === value()\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"select(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"select-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n <span rtElem=\"option-label\">{{ row.option.label }}</span>\n @if (row.option.value === value()) {\n <rt-icon rtElem=\"check\" name=\"check\" size=\"sm\" aria-hidden=\"true\" />\n }\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-select.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/select/rt-select-trigger.directive.ts","../../../projects/ui-kit-v2/src/lib/components/select/rt-select.component.ts","../../../projects/ui-kit-v2/src/lib/components/select/rt-select.component.html"],"sourcesContent":["import { Directive, inject, TemplateRef } from '@angular/core';\n\nimport { IRtSelect } from './rt-select.model';\n\n/**\n * Маркер своей разметки указателя — того, что человек нажимает, чтобы открыть список.\n *\n * Поведение указателя остаётся киту: открытие панели, её ширина, приметы доступности,\n * отключённость, клавиши и закрытие висят на кнопке. Потребитель даёт только то, что рисуется\n * внутри неё, и получает три значения обстановки: открыт ли список, что выбрано, отключён ли выбор.\n *\n * Один маркер на обе семьи выбора: они отличаются только разметкой внутри кнопки, и два маркера\n * разошлись бы на первой же правке.\n *\n * @example\n * ```html\n * <rt-select [options]=\"options\">\n * <ng-template rtSelectTrigger let-state>\n * <rt-tag [value]=\"state.label\" [icon]=\"state.isOpen ? 'chevron-up' : 'chevron-down'\" />\n * </ng-template>\n * </rt-select>\n * ```\n */\n@Directive({\n selector: '[rtSelectTrigger]',\n})\nexport class RtSelectTriggerDirective<TValue = unknown> {\n public readonly templateRef: TemplateRef<IRtSelect.TriggerContext<TValue>> =\n inject<TemplateRef<IRtSelect.TriggerContext<TValue>>>(TemplateRef);\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n computed,\n contentChild,\n forwardRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n InputSignalWithTransform,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap, rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { RtFormControlBase } from '@rt-tools/ui-kit-v2/form-control';\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport { RtPopoverDirective } from '@rt-tools/ui-kit-v2/popover';\nimport { IRtPopover } from '@rt-tools/ui-kit-v2/popover';\nimport { RtRadiusDirective } from '@rt-tools/ui-kit-v2/radius';\nimport { RtOptionList, TRtOptionListAction } from '@rt-tools/ui-kit-v2/core';\nimport { rtTreeFind, rtTreeIsTree, rtTreeOpenFor, rtTreeRows, rtTreeSideKey, rtTreeToggle } from '@rt-tools/ui-kit-v2/core';\nimport { RtSelectTriggerDirective } from './rt-select-trigger.directive';\nimport { IRtSelect } from './rt-select.model';\n\nconst BEM_BLOCK: string = 'rt-select';\n\n/** Клавиши, открывающие закрытый dropdown с фокуса на trigger'е. */\n\ntype TBooleanInput = boolean | string;\n\nlet panelIdSeed: number = 0;\n\nfunction nextPanelId(): number {\n panelIdSeed += 1;\n return panelIdSeed;\n}\n\n/**\n * Single-select dropdown с опциональным filter input.\n *\n * Panel рендерится через CDK Overlay (директива `[rtPopover]` на trigger'е) —\n * не обрезается `overflow: hidden` предками (таблицы, aside, карточки) и\n * авто-flip'ается у края viewport. CVA через общий `RtFormControlBase`. Крестик\n * (`clearable`) слева от шеврона — виден при наличии значения, очищает выбор.\n *\n * Keyboard-nav (activedescendant): фокус на trigger'е, ArrowUp/Down двигают\n * виртуальный highlight (`aria-activedescendant`), Enter выбирает, Escape\n * закрывает. Home/End/typeahead — deferred.\n */\n@Component({\n selector: 'rt-select',\n templateUrl: './rt-select.component.html',\n styleUrl: './rt-select.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n FormsModule,\n NgTemplateOutlet,\n\n // standalone components / directives\n RtIconButtonComponent,\n RtIconComponent,\n RtInputComponent,\n RtPopoverDirective,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [\n // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.\n { provide: RtFormControlBase, useExisting: forwardRef(() => RtSelectComponent) },\n ],\n hostDirectives: [{ directive: RtRadiusDirective, inputs: ['radius'] }],\n host: {\n class: BEM_BLOCK,\n '[class.rt-select--open]': 'isOpen()',\n '[class.rt-select--disabled]': 'isDisabled()',\n '[class.rt-select--readonly]': 'isReadonly()',\n '[class.rt-select--invalid]': 'isInvalid()',\n '[class.rt-select--borderless]': '!bordered()',\n '[class.rt-select--appearance--fill]': \"appearance() === 'fill'\",\n '[class.rt-select--size--sm]': \"size() === 'sm'\",\n '[class.rt-select--size--lg]': \"size() === 'lg'\",\n '[class.rt-select--with-icon-left]': '!!iconLeft()',\n },\n})\nexport class RtSelectComponent<TValue> extends RtFormControlBase<TValue | null> {\n readonly #t_uiSearch: Signal<string> = rtKitLabel('uiSearch');\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly panelId: string = `rt-select-panel-${nextPanelId()}`;\n\n protected readonly filterPlaceholderText: Signal<string> = computed((): string => this.filterPlaceholder() || this.#t_uiSearch());\n\n protected readonly popover: Signal<RtPopoverDirective> = viewChild.required(RtPopoverDirective);\n\n protected readonly triggerEl: Signal<ElementRef<HTMLButtonElement> | undefined> = viewChild<ElementRef<HTMLButtonElement>>('triggerEl');\n\n protected readonly filterTerm: WritableSignal<string> = signal<string>('');\n /** Раскрытые ветки дерева. Живут, пока открыта панель: при открытии раскрыты ветки над выбранным. */\n protected readonly openBranches: WritableSignal<ReadonlySet<TValue>> = signal<ReadonlySet<TValue>>(new Set<TValue>());\n\n protected readonly isOpen: Signal<boolean> = computed((): boolean => this.popover().isOpen());\n\n /** Клавиатура списка: подсветка, клавиши открытия, id для `aria-activedescendant`, прокрутка к опции. */\n protected readonly optionList: RtOptionList = new RtOptionList({\n panelId: (): string => this.panelId,\n isOpen: (): boolean => this.isOpen(),\n count: (): number => this.rows().length,\n isDisabled: (index: number): boolean => Boolean(this.rows()[index]?.option.disabled),\n chooseKeys: new Set<string>(['Enter', ' ']),\n sideKeys: true,\n edgeKeys: true,\n });\n\n protected readonly activeIndex: WritableSignal<number> = this.optionList.activeIndex;\n\n /** Идентификатор подсвеченной опции — тот же, что уходит в `aria-activedescendant`. */\n protected readonly activeOptionId: Signal<string | null> = this.optionList.activeOptionId;\n\n /**\n * Своя разметка указателя, если потребитель её объявил. Не объявил — кит рисует свою, и ни один\n * нынешний потребитель не двигается.\n */\n protected readonly triggerTpl: Signal<RtSelectTriggerDirective<TValue> | undefined> = contentChild(RtSelectTriggerDirective);\n\n protected readonly hasValue: Signal<boolean> = computed((): boolean => this.value() !== null);\n\n protected readonly selectedLabel: Signal<string> = computed((): string => {\n const v: TValue | null = this.value();\n if (v === null) {\n return '';\n }\n return rtTreeFind(this.options(), v)?.label ?? '';\n });\n\n /** Дерево ли список: тогда у строк есть место под стрелку и отступ по уровню. */\n protected readonly isTree: Signal<boolean> = computed((): boolean => rtTreeIsTree(this.options()));\n\n /** Видимые строки: плоский список — как прежде, дерево — по раскрытым веткам и поисковому слову. */\n protected readonly rows: Signal<ReadonlyArray<IRtSelect.Row<TValue>>> = computed((): ReadonlyArray<IRtSelect.Row<TValue>> =>\n rtTreeRows(this.options(), this.openBranches(), this.filterTerm())\n );\n\n /** Три значения, которые кит отдаёт своей разметке указателя, и не больше. */\n protected readonly triggerState: Signal<IRtSelect.TriggerState<TValue>> = computed((): IRtSelect.TriggerState<TValue> => ({\n isOpen: this.isOpen(),\n value: this.value(),\n label: this.selectedLabel(),\n isDisabled: this.isDisabled(),\n }));\n\n /** Вид рамки: `outline` — рамка со всех сторон, `fill` — залитое поле с чертой снизу. */\n public readonly appearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n\n public readonly displayText: Signal<string> = computed((): string => this.selectedLabel());\n\n public readonly options: InputSignal<ReadonlyArray<IRtSelect.Option<TValue>>> = input<ReadonlyArray<IRtSelect.Option<TValue>>>([]);\n\n public readonly placeholder: InputSignal<string> = input<string>('');\n\n public readonly iconLeft: InputSignal<IRtIcon.Name | null> = input<IRtIcon.Name | null>(null);\n\n public readonly filter: InputSignalWithTransform<boolean, TBooleanInput> = input<boolean, TBooleanInput>(false, {\n transform: booleanAttribute,\n });\n\n /** Пусто — берётся переведённая подпись по умолчанию */\n public readonly filterPlaceholder: InputSignal<string> = input<string>('');\n\n /**\n * Чем мерится панель. По умолчанию она не уже кнопки и дальше растёт по содержимому:\n * со своим указателем кнопка бывает узкой, и панель по её ширине давила бы содержимое.\n * `trigger` возвращает прежнее — ровно по кнопке, `auto` пускает панель по содержимому\n * целиком. Предел высоты назначается входом `panelMaxHeight`.\n */\n public readonly panelWidth: InputSignal<IRtPopover.Width> = input<IRtPopover.Width>('trigger-min');\n\n /**\n * Предел высоты панели — длина как в стилях, например `20rem`. Пусто — предел кита\n * `--rt-input-panel-max-height`, и длинный список прокручивается внутри панели: без предела\n * он уходил за нижний край экрана, и пункты за краем были недостижимы. `none` снимает предел —\n * для короткого списка, который должен открываться целиком, без прокрутки. Значение ставится\n * на саму панель: она рисуется в наложении, вне поддерева блока.\n */\n public readonly panelMaxHeight: InputSignal<string | null> = input<string | null>(null);\n\n public readonly selectionChange: OutputEmitterRef<TValue | null> = output<TValue | null>();\n\n public override setDisabledState(disabled: boolean): void {\n super.setDisabledState(disabled);\n if (disabled) {\n this.popover().close();\n }\n }\n\n protected getEmptyValue(): TValue | null {\n return null;\n }\n\n protected focusAfterClear(): void {\n this.triggerEl()?.nativeElement.focus();\n }\n\n protected override clear(event?: Event): void {\n if (this.isDisabled()) {\n return;\n }\n super.clear(event);\n this.selectionChange.emit(null);\n }\n\n /**\n * Открытый список встаёт подсветкой на выбранный пункт.\n *\n * Иначе стрелка вниз начинала с первого пункта, и человек с выбранным «20» из «10, 20, 40»\n * получал «10» вместо «40».\n */\n protected onOpened(): void {\n const v: TValue | null = this.value();\n this.openBranches.set(rtTreeOpenFor(this.options(), v === null ? [] : [v]));\n if (v !== null) {\n const chosen: number = this.rows().findIndex((row: IRtSelect.Row<TValue>): boolean => row.option.value === v);\n if (chosen >= 0) {\n this.activeIndex.set(chosen);\n }\n }\n }\n\n protected toggleBranch(event: Event, value: TValue): void {\n event.stopPropagation();\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, value));\n }\n\n protected onClosed(): void {\n // Закрытие (outside-click / Escape / выбор) = пользователь увёл фокус.\n this.filterTerm.set('');\n this.activeIndex.set(-1);\n this.markTouched();\n }\n\n protected select(opt: IRtSelect.Option<TValue>): void {\n if (opt.disabled) {\n return;\n }\n this.value.set(opt.value);\n this.emitChange(opt.value);\n this.selectionChange.emit(opt.value);\n this.popover().close();\n }\n\n protected setFilter(term: string): void {\n this.filterTerm.set(term);\n this.activeIndex.set(-1);\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.isDisabled()) {\n return;\n }\n if (!this.isOpen()) {\n if (this.optionList.opensBy(event.key)) {\n event.preventDefault();\n this.popover().open();\n if (this.activeIndex() < 0) {\n this.optionList.move(1);\n }\n }\n return;\n }\n this.#handleOpenKeydown(event);\n }\n\n #handleOpenKeydown(event: KeyboardEvent): void {\n const action: TRtOptionListAction = this.optionList.actionOf(event.key);\n if (action === 'none') {\n return;\n }\n event.preventDefault();\n switch (action) {\n case 'next':\n this.optionList.move(1);\n break;\n case 'previous':\n this.optionList.move(-1);\n break;\n case 'first':\n this.optionList.first();\n break;\n case 'last':\n this.optionList.last();\n break;\n case 'choose':\n this.#selectActive();\n break;\n case 'close':\n this.popover().close();\n break;\n case 'side':\n this.#sideKey(event.key);\n break;\n }\n }\n\n #sideKey(key: string): void {\n const answer: IRtSelect.SideKeyAnswer<TValue> = rtTreeSideKey(this.rows(), this.activeIndex(), key);\n const toggle: TValue | null = answer.toggle;\n if (toggle !== null) {\n this.openBranches.update((open: ReadonlySet<TValue>): ReadonlySet<TValue> => rtTreeToggle(open, toggle));\n }\n this.activeIndex.set(answer.index);\n }\n\n #selectActive(): void {\n const opts: ReadonlyArray<IRtSelect.Option<TValue>> = this.rows().map(\n (row: IRtSelect.Row<TValue>): IRtSelect.Option<TValue> => row.option\n );\n const index: number = this.activeIndex();\n if (index >= 0 && index < opts.length) {\n this.select(opts[index]);\n }\n }\n}\n","<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '—' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n [rtPopoverWidth]=\"panelWidth()\"\n [rtMod]=\"{ own: !!triggerTpl() }\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"activeOptionId()\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverOpened)=\"onOpened()\"\n (rtPopoverClosed)=\"onClosed()\">\n <!--\n Разметка указателя: своя, если потребитель её объявил, иначе зашитая китом. Она встаёт\n внутрь кнопки, а не вместо неё: поведение — открытие панели, приметы доступности,\n отключённость, клавиши и закрытие — остаётся на кнопке кита.\n -->\n @if (triggerTpl(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl.templateRef\" [ngTemplateOutletContext]=\"{ $implicit: triggerState() }\" />\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n {{ selectedLabel() || placeholder() }}\n </span>\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n }\n </button>\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [style.--rt-select-panel-max-height]=\"panelMaxHeight()\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input\n iconLeft=\"ico-search\"\n [placeholder]=\"filterPlaceholderText()\"\n [ngModel]=\"filterTerm()\"\n (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n Опции listbox'а выбираются мышью через (click); keyboard-nav —\n на trigger'е (activedescendant: ArrowUp/Down/Enter), опции не\n focusable индивидуально, поэтому a11y-правила здесь не применимы.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (row of rows(); track row.option.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"row.option.value\"\n [attr.aria-level]=\"isTree() ? row.level + 1 : null\"\n [style.--rt-select-tree-level]=\"row.level\"\n [rtMod]=\"{\n selected: row.option.value === value(),\n active: i === activeIndex(),\n disabled: !!row.option.disabled,\n }\"\n [attr.aria-selected]=\"row.option.value === value()\"\n [attr.aria-disabled]=\"!!row.option.disabled\"\n (click)=\"select(row.option)\">\n @if (isTree()) {\n @if (row.branch) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"select-option-toggle\"\n type=\"button\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.open ? t().uiCollapse : t().uiExpand\"\n [attr.aria-expanded]=\"row.open\"\n (click)=\"toggleBranch($event, row.option.value)\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"row.open ? 'chevron-down' : 'chevron-right'\" />\n </button>\n } @else {\n <span rtElem=\"toggle-space\"></span>\n }\n }\n <span rtElem=\"option-label\">{{ row.option.label }}</span>\n @if (row.option.value === value()) {\n <rt-icon rtElem=\"check\" name=\"check\" size=\"sm\" aria-hidden=\"true\" />\n }\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n"],"mappings":"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"}