@seniorsistemas/angular-components 17.27.1-feature-sds-110-9a13a8bc → 17.27.1-feature-sds-110-57186492

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 (72) hide show
  1. package/autocomplete/index.d.ts +5 -0
  2. package/autocomplete/lib/autocomplete/autocomplete.component.d.ts +250 -0
  3. package/autocomplete/lib/autocomplete/components/autocomplete-chip/autocomplete-chip.component.d.ts +9 -0
  4. package/autocomplete/public-api.d.ts +1 -0
  5. package/button/lib/button/button.component.d.ts +1 -1
  6. package/dynamic-form/lib/dynamic-form/components/lookup/lookup.component.d.ts +1 -2
  7. package/dynamic-form/lib/dynamic-form/dynamic-form.module.d.ts +3 -2
  8. package/dynamic-form/lib/dynamic-form/form-field/fields/currency/currency-field.component.d.ts +3 -2
  9. package/esm2022/autocomplete/lib/autocomplete/autocomplete.component.mjs +554 -0
  10. package/esm2022/autocomplete/lib/autocomplete/components/autocomplete-chip/autocomplete-chip.component.mjs +17 -0
  11. package/esm2022/autocomplete/public-api.mjs +2 -0
  12. package/esm2022/autocomplete/seniorsistemas-angular-components-autocomplete.mjs +5 -0
  13. package/esm2022/button/lib/button/button.component.mjs +4 -10
  14. package/esm2022/chips/lib/chips/chips/chips.component.mjs +2 -2
  15. package/esm2022/control-errors/lib/control-errors/control-errors.component.mjs +2 -2
  16. package/esm2022/country-phone-picker/lib/country-phone-picker/country-phone-picker.component.mjs +2 -2
  17. package/esm2022/dynamic-form/lib/dynamic-form/components/lookup/lookup.component.mjs +16 -10
  18. package/esm2022/dynamic-form/lib/dynamic-form/dynamic-form.module.mjs +5 -1
  19. package/esm2022/dynamic-form/lib/dynamic-form/form-field/fields/autocomplete/autocomplete-field.component.mjs +7 -6
  20. package/esm2022/dynamic-form/lib/dynamic-form/form-field/fields/currency/currency-field.component.mjs +18 -8
  21. package/esm2022/dynamic-form/lib/dynamic-form/form-field/fields/lookup/lookup-field.component.mjs +1 -1
  22. package/esm2022/dynamic-form/lib/dynamic-form/form-field/fields/password/password-field.component.mjs +2 -2
  23. package/esm2022/dynamic-form/lib/dynamic-form/form-field/fields/select/select-field.component.mjs +1 -1
  24. package/esm2022/inline-edit/lib/inline-edit/components/fields/inline-edit-lookup/inline-edit-lookup.component.mjs +1 -1
  25. package/esm2022/lib/locale/fallback.mjs +2 -1
  26. package/esm2022/object-card/lib/object-card/object-card.component.mjs +3 -3
  27. package/esm2022/paginator/lib/paginator/paginator.component.mjs +2 -2
  28. package/esm2022/panel/lib/panel/panel.component.mjs +3 -3
  29. package/esm2022/select/lib/select/select.component.mjs +234 -336
  30. package/esm2022/text-area/lib/text-area/text-area.component.mjs +4 -4
  31. package/esm2022/text-area-ia/lib/text-area-ia/text-area-ia.component.mjs +4 -4
  32. package/esm2022/toast/lib/toast/toast.component.mjs +13 -13
  33. package/esm2022/token-list/lib/token-list/token-list.component.mjs +3 -3
  34. package/fesm2022/seniorsistemas-angular-components-autocomplete.mjs +575 -0
  35. package/fesm2022/seniorsistemas-angular-components-autocomplete.mjs.map +1 -0
  36. package/fesm2022/seniorsistemas-angular-components-button.mjs +3 -9
  37. package/fesm2022/seniorsistemas-angular-components-button.mjs.map +1 -1
  38. package/fesm2022/seniorsistemas-angular-components-chips.mjs +2 -2
  39. package/fesm2022/seniorsistemas-angular-components-chips.mjs.map +1 -1
  40. package/fesm2022/seniorsistemas-angular-components-control-errors.mjs +1 -1
  41. package/fesm2022/seniorsistemas-angular-components-control-errors.mjs.map +1 -1
  42. package/fesm2022/seniorsistemas-angular-components-country-phone-picker.mjs +2 -2
  43. package/fesm2022/seniorsistemas-angular-components-country-phone-picker.mjs.map +1 -1
  44. package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs +38 -19
  45. package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs.map +1 -1
  46. package/fesm2022/seniorsistemas-angular-components-inline-edit.mjs +1 -1
  47. package/fesm2022/seniorsistemas-angular-components-inline-edit.mjs.map +1 -1
  48. package/fesm2022/seniorsistemas-angular-components-object-card.mjs +2 -2
  49. package/fesm2022/seniorsistemas-angular-components-object-card.mjs.map +1 -1
  50. package/fesm2022/seniorsistemas-angular-components-paginator.mjs +1 -1
  51. package/fesm2022/seniorsistemas-angular-components-paginator.mjs.map +1 -1
  52. package/fesm2022/seniorsistemas-angular-components-panel.mjs +2 -2
  53. package/fesm2022/seniorsistemas-angular-components-panel.mjs.map +1 -1
  54. package/fesm2022/seniorsistemas-angular-components-select.mjs +232 -334
  55. package/fesm2022/seniorsistemas-angular-components-select.mjs.map +1 -1
  56. package/fesm2022/seniorsistemas-angular-components-text-area-ia.mjs +3 -3
  57. package/fesm2022/seniorsistemas-angular-components-text-area-ia.mjs.map +1 -1
  58. package/fesm2022/seniorsistemas-angular-components-text-area.mjs +3 -3
  59. package/fesm2022/seniorsistemas-angular-components-text-area.mjs.map +1 -1
  60. package/fesm2022/seniorsistemas-angular-components-toast.mjs +12 -12
  61. package/fesm2022/seniorsistemas-angular-components-toast.mjs.map +1 -1
  62. package/fesm2022/seniorsistemas-angular-components-token-list.mjs +2 -2
  63. package/fesm2022/seniorsistemas-angular-components-token-list.mjs.map +1 -1
  64. package/fesm2022/seniorsistemas-angular-components.mjs +1 -0
  65. package/fesm2022/seniorsistemas-angular-components.mjs.map +1 -1
  66. package/package.json +18 -12
  67. package/select/lib/select/select.component.d.ts +148 -157
  68. package/styles.css +1 -0
  69. package/text-area-ia/lib/text-area-ia/text-area-ia.component.d.ts +1 -1
  70. package/toast/lib/toast/toast.component.d.ts +4 -4
  71. package/src/lib/styles/tailwind.scss +0 -4
  72. package/tailwind.css +0 -1088
@@ -1,17 +1,18 @@
1
- import { DomPortalOutlet, TemplatePortal } from '@angular/cdk/portal';
1
+ import * as i5 from '@angular/cdk/overlay';
2
+ import { OverlayModule } from '@angular/cdk/overlay';
2
3
  import * as i3 from '@angular/cdk/scrolling';
3
4
  import { ScrollingModule } from '@angular/cdk/scrolling';
4
5
  import * as i1 from '@angular/common';
5
- import { CommonModule, isPlatformBrowser } from '@angular/common';
6
+ import { CommonModule } from '@angular/common';
6
7
  import * as i0 from '@angular/core';
7
- import { input, model, output, Component, signal, computed, inject, ElementRef, viewChild, PLATFORM_ID, ViewContainerRef, forwardRef } from '@angular/core';
8
+ import { input, model, output, Component, signal, computed, viewChild, inject, forwardRef } from '@angular/core';
9
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
10
  import * as i2 from '@angular/forms';
9
11
  import { FormGroup, FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
10
12
  import * as i4 from '@ngx-translate/core';
11
13
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
12
- import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
13
14
  import { CheckboxComponent } from '@seniorsistemas/angular-components/checkbox';
14
- import { Subject } from 'rxjs';
15
+ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
15
16
 
16
17
  class SelectOptionComponent {
17
18
  id = input.required();
@@ -37,6 +38,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
37
38
  }] });
38
39
 
39
40
  class SelectComponent {
41
+ static nextId = 0;
40
42
  placeholder = input('');
41
43
  multiple = input(false);
42
44
  options = input.required();
@@ -50,65 +52,49 @@ class SelectComponent {
50
52
  virtualScroll = input(false);
51
53
  virtualScrollItemSize = input(37);
52
54
  emptyMessage = input(null);
53
- useRawValue = input(false);
54
- dataKey = input();
55
55
  disabled = model(false);
56
- showOptions = signal(false);
56
+ showOptions = model(false);
57
57
  value = signal(null);
58
58
  values = signal([]);
59
59
  filterValue = signal('');
60
60
  focusedIndex = signal(-1);
61
- dropTop = 0;
62
- dropLeft = 0;
63
- dropWidth = 0;
64
- _portalHost = null;
61
+ componentId = `select${SelectComponent.nextId++}`;
65
62
  /**
66
- * Verifica se todas as opções visíveis (filtradas e não agrupadoras) estão selecionadas.
67
- *
68
- * - Retorna `true` se todas as opções filtradas estiverem presentes em `values()`.
69
- * - Ignora opções marcadas como `grouper`.
70
- * - Usa `_getCompareValueExtractor()` para comparar os valores de forma consistente.
71
- *
72
- * Esse estado é usado para controlar o checkbox de "Selecionar todos" quando todas as opções visíveis estão marcadas.
63
+ * Computed que indica se todas as opções filtradas estão selecionadas.
64
+ * Retorna `true` se todas as opções disponíveis (excluindo agrupadores) estiverem presentes
65
+ * na lista de valores selecionados, e `false` caso contrário.
66
+ * @returns {boolean} `true` se todas as opções filtradas estão selecionadas, `false` caso contrário.
73
67
  */
74
68
  allSelected = computed(() => {
75
- const selected = this.values();
76
- const options = this.filteredOptions().filter((o) => !o.grouper);
77
- return (options.length > 0 &&
78
- options.every((option) => selected.some((s) => this._getCompareValueExtractor()(s) === this._getCompareValueExtractor()(option))));
69
+ const options = this.filteredOptions().filter((option) => !option.grouper);
70
+ const selectedIds = this.values().map((option) => option.id);
71
+ const ret = options.length > 0 && options.every((option) => selectedIds.includes(option.id));
72
+ return ret;
79
73
  });
74
+ /**
75
+ * Retorna o item atualmente focado na lista de opções filtradas.
76
+ * @returns O dado do item focado ou `null` caso nenhum item esteja focado.
77
+ */
80
78
  focusedItem = computed(() => this.filteredOptions().at(this.focusedIndex())?.data ?? null);
81
79
  /**
82
- * Converte as opções externas (`options()`) em uma estrutura interna padronizada (`InternalDropdownOption<T>`).
83
- *
84
- * - Se os dados forem primitivos (string, number, etc.), gera opções com IDs baseados no índice.
85
- * - Se os dados forem objetos, delega a criação das opções para `_addIdToOptions()`, que adiciona IDs e marca agrupadores.
86
- * - Se o modo agrupado estiver ativo (`group()`), transforma as opções em uma lista achatada com agrupadores via `_grouperToFlat()`.
87
- *
88
- * Essa estrutura é usada internamente para renderização, filtragem e controle de seleção.
80
+ * Computa e retorna a lista interna de opções do dropdown.
81
+ * - Se as opções estiverem agrupadas (`group()` retorna verdadeiro),
82
+ * utiliza `_grouperToFlat` para transformar as opções agrupadas em uma lista plana.
83
+ * - Caso contrário, retorna as opções com IDs adicionados.
84
+ * @returns Um array de `InternalDropdownOption<T>` representando as opções disponíveis para o componente select.
89
85
  */
90
86
  internalOptions = computed(() => {
91
87
  const opts = this.options();
92
- const optionsWithId = this.TIsPrimitive()
93
- ? opts.map((value, index) => ({
94
- id: `id_primitive_${index}`,
95
- grouper: false,
96
- data: value,
97
- }))
98
- : this._addIdToOptions(opts);
88
+ const optionsWithId = this._addIdToOptions(opts);
99
89
  return this.group() ? this._grouperToFlat(optionsWithId) : optionsWithId;
100
90
  });
101
91
  /**
102
- * Retorna o texto que deve ser exibido no componente com base nos valores selecionados.
103
- *
104
- * - No modo múltiplo:
105
- * - Se houver mais de um item selecionado, exibe uma mensagem traduzida com o total de registros.
106
- * - Se houver apenas um item selecionado, exibe o label correspondente.
107
- * - No modo simples:
108
- * - Exibe o label do valor selecionado. *
109
- * - Se nenhum valor estiver selecionado, retorna uma string vazia.
110
- *
111
- * Essa lógica é usada para mostrar o conteúdo atual do campo de seleção.
92
+ * Retorna uma string representando a seleção atual do componente.
93
+ * - Se o modo múltiplo estiver ativado e mais de um item estiver selecionado,
94
+ * retorna uma mensagem traduzida indicando o número total de registros selecionados.
95
+ * - Se apenas um item estiver selecionado, retorna o rótulo desse item.
96
+ * - Se o modo múltiplo não estiver ativado, retorna o rótulo do item selecionado ou uma string vazia caso nenhum item esteja selecionado.
97
+ * @returns {string} Texto representando a seleção atual.
112
98
  */
113
99
  print = computed(() => {
114
100
  if (this.multiple()) {
@@ -119,46 +105,45 @@ class SelectComponent {
119
105
  });
120
106
  }
121
107
  else if (selected.length === 1) {
122
- return this._getLabelFromOption(selected[0]);
108
+ return this._getLabelFromOption(selected[0].data);
123
109
  }
124
110
  }
125
- else if (this.value()) {
126
- return this._getLabelFromOption(this.value());
111
+ else {
112
+ const selected = this.value();
113
+ return selected ? this._getLabelFromOption(selected.data) : '';
127
114
  }
128
115
  return '';
129
116
  });
117
+ /**
118
+ * Indica se o seletor está "limpo", ou seja, sem nenhum valor selecionado.
119
+ * - No modo múltiplo (`multiple`), retorna `true` se não houver nenhum valor selecionado (`values` está vazio).
120
+ * - No modo simples, retorna `true` se não houver valor selecionado (`value` é falsy).
121
+ * @returns `true` se não houver seleção, `false` caso contrário.
122
+ */
130
123
  isClean = computed(() => {
131
124
  return this.multiple() ? !this.values().length : !this.value();
132
125
  });
133
- TIsPrimitive = computed(() => {
134
- const opts = this.options();
135
- if (!opts.length)
136
- return false;
137
- return ['string', 'number', 'boolean'].includes(typeof opts[0]);
138
- });
139
126
  /**
140
- * Indica se o estado de seleção está indeterminado.
141
- *
142
- * - Retorna `true` quando **algumas**, mas não todas, opções visíveis estão selecionadas.
143
- * - Retorna `false` quando **todas** ou **nenhuma** das opções visíveis estão selecionadas.
144
- * - Ignora opções marcadas como `grouper`.
145
- *
146
- * Esse estado é usado, por exemplo, para exibir o checkbox de "Selecionar todos" em modo indeterminado.
127
+ * Computed que indica se o estado de seleção "Selecionar Todos" está indeterminado.
128
+ * Retorna `true` quando nem todos os itens filtrados estão selecionados e nem todos estão desmarcados,
129
+ * ou seja, quando uma seleção parcial dos itens disponíveis.
130
+ * @returns {boolean} `true` se a seleção for parcial, `false` caso contrário.
147
131
  */
148
132
  selectAllIsIndeterminate = computed(() => {
149
- const selected = this.values();
150
- const options = this.filteredOptions().filter((o) => !o.grouper);
151
- const all = options.length > 0 && options.every((o) => selected.includes(o.data));
152
- const none = options.every((o) => !selected.includes(o.data));
133
+ const selected = this.internalOptions().filter((option) => {
134
+ return this.values().some((o) => o.id === option.id);
135
+ });
136
+ const options = this.filteredOptions().filter((option) => !option.grouper);
137
+ const all = options.length > 0 && options.every((option) => selected.includes(option));
138
+ const none = options.every((option) => !selected.includes(option));
153
139
  return !all && !none;
154
140
  });
155
141
  /**
156
- * Retorna a lista de opções filtradas com base no valor digitado pelo usuário.
157
- *
158
- * - Se o filtro estiver desativado ou vazio, retorna todas as opções.
159
- * - Caso contrário, aplica o filtro usando o(s) campo(s) definidos em `filterBy`.
160
- * - Ignora opções marcadas como `grouper`.
161
- * - Usa o extrator de label para comparar os valores com o texto buscado.
142
+ * Retorna uma lista de opções filtradas com base no valor de busca (`filterValue`) e nos critérios definidos.
143
+ * - Se o filtro estiver desabilitado ou o valor de busca estiver vazio, retorna todas as opções.
144
+ * - Caso contrário, filtra as opções utilizando o(s) campo(s) especificado(s) em `filterBy`.
145
+ * - Ignora opções que possuem a propriedade `grouper` definida.
146
+ * @returns As opções filtradas conforme o valor de busca e critérios de filtro.
162
147
  */
163
148
  filteredOptions = computed(() => {
164
149
  const allOptions = this.internalOptions();
@@ -171,10 +156,10 @@ class SelectComponent {
171
156
  if (option.grouper) {
172
157
  return false;
173
158
  }
174
- const labelExtractor = this._getLabelExtractor();
175
159
  let label = '';
160
+ const optionLabel = this.optionLabel();
176
161
  if (!filterBy) {
177
- label = labelExtractor(option.data);
162
+ label = optionLabel ? String(option.data[optionLabel]) : String(option.data);
178
163
  }
179
164
  else if (Array.isArray(filterBy)) {
180
165
  label = filterBy.map((by) => String(option.data[by] ?? '')).join(' ');
@@ -188,89 +173,88 @@ class SelectComponent {
188
173
  filterForm = new FormGroup({
189
174
  filter: new FormControl('', { nonNullable: true }),
190
175
  });
191
- _elementRef = inject((ElementRef));
192
- _dropdownTemplate = viewChild('dropdownTemplate');
193
176
  _containerDiv = viewChild('containerDiv');
194
177
  _onChange = () => { };
195
178
  _onTouched = () => { };
196
- platformId = inject(PLATFORM_ID);
197
- _viewContainerRef = inject(ViewContainerRef);
198
179
  _translateService = inject(TranslateService);
199
- destroy$ = new Subject();
200
- /**
201
- * Inicializa o componente, validando os inputs e configurando o filtro com debounce.
202
- */
203
- ngOnInit() {
204
- this._validateInputs();
180
+ constructor() {
205
181
  this.filterForm
206
182
  .get('filter')
207
- .valueChanges.pipe(debounceTime(300), distinctUntilChanged(), takeUntil(this.destroy$))
183
+ .valueChanges.pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed())
208
184
  .subscribe((value) => {
209
185
  this.filterValue.set(value);
210
186
  });
211
187
  }
212
- /**
213
- * Executa limpeza de recursos ao destruir o componente.
214
- */
215
- ngOnDestroy() {
216
- this._destroyDropdownPortal();
217
- if (this._portalHost) {
218
- this._portalHost.dispose();
219
- this._portalHost = null;
220
- }
221
- this.destroy$.next();
222
- this.destroy$.complete();
188
+ ngOnInit() {
189
+ this._validateInputs();
223
190
  }
224
191
  /**
225
- * Define o valor no componente vindo do formulário externo.
226
- * @param value Valor atribuído pelo Angular forms.
192
+ * Define o valor selecionado do componente select.
193
+ * @param value O valor a ser definido. Pode ser um objeto do tipo `T`, um array de `T` (quando múltipla seleção está habilitada), ou `null`.
194
+ * @throws Se o modo múltiplo estiver ativado e o valor não for um array, lança um erro.
195
+ * @throws Se o modo múltiplo estiver desativado e o valor for um array, lança um erro.
196
+ * Este método mapeia o(s) valor(es) fornecido(s) para as opções internas do componente,
197
+ * garantindo que apenas opções válidas sejam selecionadas conforme o modo de seleção (único ou múltiplo).
227
198
  */
228
199
  writeValue(value) {
229
- if (this.multiple()) {
200
+ if (value && this.multiple()) {
230
201
  if (Array.isArray(value)) {
231
- const items = this.TIsPrimitive()
232
- ? value.map((v) => this._findOptionByPrimitiveValue(v)).filter(Boolean)
233
- : this.useRawValue()
234
- ? value
235
- : value.map((v) => this._findOptionByValue(v)).filter(Boolean);
236
- this.values.set(items);
202
+ const mapped = value
203
+ .map((val) => {
204
+ return this.internalOptions().find((option) => {
205
+ return JSON.stringify(option.data) === JSON.stringify(val);
206
+ });
207
+ })
208
+ .filter((option) => !!option);
209
+ this.values.set(mapped);
237
210
  }
238
211
  else {
239
- this.values.set([]);
212
+ throw new Error('Value must be an array when "multiple" is true.');
240
213
  }
241
214
  }
242
215
  else {
243
- const item = this.TIsPrimitive()
244
- ? this._findOptionByPrimitiveValue(value)
245
- : this.useRawValue()
246
- ? value
247
- : this._findOptionByValue(value);
248
- this.value.set(item ?? null);
216
+ const optionValue = this.optionValue();
217
+ let mapped = null;
218
+ if (Array.isArray(value)) {
219
+ throw new Error('Value must not be an array when "multiple" is false.');
220
+ }
221
+ if (optionValue && value) {
222
+ mapped =
223
+ this.internalOptions().find((option) => option.data[optionValue] === value[optionValue]) ?? null;
224
+ }
225
+ else {
226
+ mapped =
227
+ this.internalOptions().find((option) => JSON.stringify(option.data) === JSON.stringify(value)) ??
228
+ null;
229
+ }
230
+ this.value.set(mapped ?? null);
249
231
  }
250
232
  }
251
233
  /**
252
- * Registra a função que será chamada quando o valor mudar.
253
- * @param onChange Função callback.
234
+ * Registra uma função de callback que será chamada sempre que o valor do componente mudar.
235
+ * @param onChange Função de callback que recebe o novo valor selecionado ou null.
254
236
  */
255
237
  registerOnChange(onChange) {
256
238
  this._onChange = onChange;
257
239
  }
258
240
  /**
259
- * Registra a função que será chamada quando o componente for "tocado".
260
- * @param onTouched Função callback.
241
+ * Registra uma função de callback que será chamada quando o componente for tocado (perder o foco).
242
+ * @param onTouched Função de callback a ser executada quando o componente for marcado como "tocado".
261
243
  */
262
244
  registerOnTouched(onTouched) {
263
245
  this._onTouched = onTouched;
264
246
  }
265
247
  /**
266
248
  * Define o estado de desabilitado do componente.
267
- * @param disabled Indica se o componente está desabilitado.
249
+ * @param disabled Indica se o componente deve ser desabilitado (`true`) ou habilitado (`false`).
268
250
  */
269
251
  setDisabledState(disabled) {
270
252
  this.disabled.set(disabled);
271
253
  }
272
254
  /**
273
- * Alterna a exibição da lista de opções (dropdown).
255
+ * Alterna a exibição das opções do componente select.
256
+ * Se o componente estiver desabilitado, não realiza nenhuma ação.
257
+ * Caso contrário, inverte o estado de exibição das opções.
274
258
  */
275
259
  toggle() {
276
260
  if (this.disabled()) {
@@ -278,29 +262,24 @@ class SelectComponent {
278
262
  }
279
263
  const shouldOpen = !this.showOptions();
280
264
  this.showOptions.set(shouldOpen);
281
- if (shouldOpen) {
282
- setTimeout(() => {
283
- this._createDropdownPortal();
284
- });
285
- }
286
- else {
287
- this._destroyDropdownPortal();
288
- }
289
265
  }
290
266
  /**
291
- * Seleciona ou desseleciona um item na lista.
292
- * @param item Item a ser selecionado ou desmarcado.
267
+ * Seleciona um item no componente de seleção.
268
+ * @param item O item a ser selecionado ou desmarcado.
269
+ * Se o componente estiver desabilitado, a função retorna imediatamente.
270
+ * No modo múltiplo, adiciona ou remove o item da lista de valores selecionados.
271
+ * No modo simples, define o item como valor selecionado e fecha o dropdown.
272
+ * Sempre notifica as mudanças e marca o componente como tocado.
293
273
  */
294
274
  selectItem(item) {
295
275
  if (this.disabled()) {
296
276
  return;
297
277
  }
298
- const getCompareValue = this._getCompareValueExtractor();
278
+ const optionValue = this.optionValue();
299
279
  if (this.multiple()) {
300
280
  const store = [...this.values()];
301
- const itemValue = getCompareValue(item);
302
281
  const index = store.findIndex((selectedItem) => {
303
- return getCompareValue(selectedItem) === itemValue;
282
+ return selectedItem === item;
304
283
  });
305
284
  if (index !== -1) {
306
285
  store.splice(index, 1);
@@ -309,20 +288,25 @@ class SelectComponent {
309
288
  store.push(item);
310
289
  }
311
290
  this.values.set(store);
312
- const result = this.useRawValue() ? store : store.map((i) => this._getValueExtractor()(i));
291
+ const result = optionValue ? store.map((item) => item.data[optionValue]) : store;
313
292
  this._onChange(result);
314
293
  }
315
294
  else {
316
295
  this.value.set(item);
317
- const result = this.useRawValue() ? item : this._getValueExtractor()(item);
296
+ const result = optionValue ? item.data[optionValue] : item.data;
318
297
  this._onChange(result);
319
298
  this._closeDropdown();
320
299
  }
321
300
  this._onTouched();
322
301
  }
323
302
  /**
324
- * Limpa a seleção atual.
325
- * @param event Evento do clique.
303
+ * Limpa o valor selecionado no componente select.
304
+ * Se o componente estiver desabilitado, não executa nenhuma ação.
305
+ * Para seleção múltipla, remove todos os valores selecionados e notifica a alteração.
306
+ * Para seleção única, define o valor como nulo e notifica a alteração.
307
+ * Fecha o dropdown e marca o componente como "tocado".
308
+ * Impede a propagação do evento do mouse.
309
+ * @param event Evento do mouse que acionou a limpeza da seleção.
326
310
  */
327
311
  clear(event) {
328
312
  if (this.disabled()) {
@@ -330,7 +314,7 @@ class SelectComponent {
330
314
  }
331
315
  if (this.multiple()) {
332
316
  this.values.set([]);
333
- this._onChange(this._removeInternalProperties(this.values()));
317
+ this._onChange(this.values());
334
318
  }
335
319
  else {
336
320
  this.value.set(null);
@@ -341,25 +325,17 @@ class SelectComponent {
341
325
  event.stopPropagation();
342
326
  }
343
327
  /**
344
- * Limpa o filtro aplicado à lista de opções.
328
+ * Limpa o filtro do formulário, resetando o campo 'filter' do formulário de filtro.
329
+ * Este método redefine o valor do campo 'filter' para seu estado inicial,
330
+ * removendo qualquer texto ou valor previamente inserido pelo usuário.
345
331
  */
346
332
  clearFilter() {
347
333
  this.filterForm.get('filter')?.reset();
348
334
  }
349
335
  /**
350
- * Fecha o dropdown se o clique ocorrer fora do componente.
351
- * @param event Evento do clique.
352
- */
353
- onDocumentClick(event) {
354
- const clickTarget = event.target;
355
- const clickedInsideComponent = this._elementRef.nativeElement.contains(clickTarget);
356
- const dropdownInBody = document.querySelector('.dropdown-body-class')?.contains(clickTarget);
357
- if (!clickedInsideComponent && !dropdownInBody) {
358
- this._closeDropdown();
359
- }
360
- }
361
- /**
362
- * Fecha o dropdown em eventos de redimensionamento de janela.
336
+ * Manipula o evento de redimensionamento da janela.
337
+ * Fecha o dropdown de opções caso ele esteja visível ao redimensionar a janela.
338
+ * @param _ Evento de redimensionamento da janela (não utilizado).
363
339
  */
364
340
  onWindowResize(_) {
365
341
  if (this.showOptions()) {
@@ -367,15 +343,24 @@ class SelectComponent {
367
343
  }
368
344
  }
369
345
  /**
370
- * Dispara a abertura/fechamento do dropdown ao clicar no container.
346
+ * Manipula o evento de clique no elemento container da seleção.
347
+ * Este método alterna o estado de exibição do componente select e,
348
+ * em seguida, define o foco no elemento container correspondente.
371
349
  */
372
350
  onContainerDivClick() {
373
351
  this.toggle();
374
352
  this._containerDiv()?.nativeElement.focus();
375
353
  }
376
354
  /**
377
- * Trata eventos de teclado para navegação e seleção.
378
- * @param event Evento do teclado.
355
+ * Manipula eventos de teclado para navegação e seleção de opções no componente select.
356
+ * - 'ArrowDown': Move o foco para a próxima opção disponível.
357
+ * - 'ArrowUp': Move o foco para a opção anterior.
358
+ * - 'Tab': Seleciona a opção atualmente focada e fecha o dropdown.
359
+ * - 'Enter': Seleciona a opção focada se o dropdown estiver aberto, ou alterna a abertura do dropdown.
360
+ * - 'Escape': Fecha o dropdown.
361
+ * - ' ': (barra de espaço) Seleciona a opção focada se o dropdown estiver aberto, ou alterna a abertura do dropdown.
362
+ * Previne o comportamento padrão do navegador quando necessário para garantir a navegação adequada pelo teclado.
363
+ * @param event O evento de teclado disparado pelo usuário.
379
364
  */
380
365
  onKeyDown(event) {
381
366
  const options = this.filteredOptions();
@@ -385,7 +370,7 @@ class SelectComponent {
385
370
  const _selectOption = () => {
386
371
  if (this.focusedIndex() >= 0 && this.focusedIndex() < options.length) {
387
372
  event.preventDefault();
388
- this.selectItem(options[this.focusedIndex()].data);
373
+ this.selectItem(options[this.focusedIndex()]);
389
374
  }
390
375
  };
391
376
  switch (event.key) {
@@ -413,166 +398,113 @@ class SelectComponent {
413
398
  this._closeDropdown();
414
399
  break;
415
400
  case ' ':
416
- _selectOption();
401
+ event.preventDefault();
402
+ if (this.showOptions()) {
403
+ _selectOption();
404
+ }
405
+ else {
406
+ this.toggle();
407
+ }
417
408
  break;
418
409
  }
419
410
  }
420
411
  /**
421
- * Retorna o rótulo de exibição de uma opção.
422
- * @param option Item da lista de opções.
423
- * @returns Texto que será exibido no dropdown.
412
+ * Retorna o rótulo (label) associado a uma opção fornecida.
413
+ * @param option - A opção do tipo T para a qual o rótulo deve ser obtido.
414
+ * @returns O rótulo da opção como uma string.
424
415
  */
425
416
  getOptionLabel(option) {
426
417
  return this._getLabelFromOption(option);
427
418
  }
428
419
  /**
429
- * Função usada para o trackBy no for do template.
430
- * @param _ Índice do item.
431
- * @param item Item da lista.
432
- * @returns ID único da opção.
420
+ * Função de trackBy utilizada em diretivas *ngFor para otimizar a renderização de listas.
421
+ * Retorna o identificador único (`id`) de cada item do tipo `InternalDropdownOption<T>`.
422
+ * @param _ - Índice do item na lista (não utilizado).
423
+ * @param item - O item atual da lista do tipo `InternalDropdownOption<T>`.
424
+ * @returns O identificador único do item como uma string.
433
425
  */
434
426
  trackById(_, item) {
435
427
  return item.id;
436
428
  }
437
429
  /**
438
- * Verifica se uma opção está selecionada.
439
- * @param option Opção a ser verificada.
440
- * @returns Verdadeiro se a opção estiver selecionada.
430
+ * Verifica se a opção fornecida está selecionada.
431
+ * @param option - A opção interna do dropdown a ser verificada.
432
+ * @returns `true` se a opção estiver selecionada, caso contrário `false`.
441
433
  */
442
434
  isOptionSelected(option) {
443
- const getCompareValue = this._getCompareValueExtractor();
444
- if (this.multiple()) {
445
- const selectedValues = this.values().map(getCompareValue);
446
- return selectedValues.includes(getCompareValue(option));
447
- }
448
- else {
449
- const currentValue = this.value();
450
- return currentValue !== null && getCompareValue(currentValue) === getCompareValue(option);
451
- }
435
+ return (this.values().find((o) => {
436
+ return o === option;
437
+ }) !== undefined);
452
438
  }
453
439
  /**
454
- * Alterna a seleção de todos os itens filtrados.
440
+ * Alterna a seleção de todos os itens disponíveis.
441
+ * Se o modo múltiplo estiver ativado, seleciona todos os itens filtrados que não são agrupadores
442
+ * caso nem todos estejam selecionados, ou limpa a seleção caso todos já estejam selecionados.
443
+ * Após a alteração, notifica a mudança de valor.
455
444
  */
456
445
  toggleSelectAll() {
457
446
  if (!this.multiple()) {
458
447
  return;
459
448
  }
460
- const options = this.filteredOptions().filter((o) => !o.grouper);
449
+ const options = this.filteredOptions().filter((option) => !option.grouper);
461
450
  if (this.allSelected()) {
462
451
  this.values.set([]);
463
452
  }
464
453
  else {
465
- this.values.set(options.map((o) => o.data));
466
- }
467
- this._onChange(this._removeInternalProperties(this.values()));
468
- }
469
- /**
470
- * Procura e retorna uma opção com valor primitivo igual ao informado.
471
- *
472
- * @param value Valor primitivo (string, number ou boolean).
473
- * @returns A opção correspondente, ou `null` se não encontrada.
474
- */
475
- _findOptionByPrimitiveValue(value) {
476
- return this.internalOptions().find((opt) => opt.data === value)?.data ?? null;
477
- }
478
- /**
479
- * Remove propriedades internas adicionadas pelo componente, retornando apenas os dados crus.
480
- *
481
- * Usado para garantir que apenas os objetos de dados reais sejam emitidos ao formulário,
482
- * especialmente no modo múltiplo.
483
- *
484
- * @param value Valor armazenado internamente (com ou sem `InternalDropdownOption`).
485
- * @returns Os dados "puros", sem propriedades adicionais.
486
- */
487
- _removeInternalProperties(value) {
488
- if (this.TIsPrimitive()) {
489
- return value;
454
+ this.values.set(options);
490
455
  }
491
- return Array.isArray(value)
492
- ? value.map((opt) => opt.data)
493
- : value.data;
456
+ this._onChange(this.values());
494
457
  }
495
458
  /**
496
- * Retorna o texto (label) correspondente a uma determinada opção.
497
- * Usa a propriedade definida em `optionLabel`, ou tenta usar uma propriedade padrão (`label`).
498
- *
499
- * @param option A opção a ser exibida.
500
- * @returns O texto legível da opção para exibição no componente.
459
+ * Retorna o rótulo (label) de uma opção fornecida.
460
+ * Este método utiliza a propriedade definida por `optionLabel` para extrair o valor do rótulo da opção.
461
+ * Caso `optionLabel` não esteja definido, retorna a representação em string da própria opção.
462
+ * @private
463
+ * @param option A opção da qual o rótulo será extraído.
464
+ * @returns O rótulo da opção como uma string.
501
465
  */
502
466
  _getLabelFromOption(option) {
503
- if (['string', 'number', 'boolean'].includes(typeof option)) {
504
- return String(option);
505
- }
506
- const labelKey = this.optionLabel();
507
- if (!labelKey) {
508
- return String(option.label ?? '');
509
- }
510
- return String(option[labelKey]);
467
+ const optionLabel = this.optionLabel();
468
+ return optionLabel ? String(option[optionLabel]) : String(option);
511
469
  }
512
470
  /**
513
- * Valida os inputs obrigatórios do componente.
471
+ * Valida as entradas do componente de seleção.
472
+ * Este método verifica se as opções fornecidas são válidas. Caso as opções sejam objetos,
473
+ * garante que a propriedade `optionLabel` esteja definida, lançando um erro caso contrário.
474
+ * Se não houver opções, a validação é ignorada.
475
+ * @private
476
+ * @throws {Error} Se as opções forem objetos e `optionLabel` não estiver definido.
514
477
  */
515
478
  _validateInputs() {
516
- if (!this.useRawValue() && !this.TIsPrimitive() && !this.optionValue()) {
517
- throw new Error(`The 'optionValue' input must be provided for non-primitive options when useRawValue is false.`);
518
- }
519
- if (!this.TIsPrimitive() && !this.optionValue() && !this.dataKey()) {
520
- throw new Error('Either the optionValue or dataKey input must be provided for non-primitive options.');
521
- }
522
- if (!this.TIsPrimitive() && !this.optionLabel()) {
523
- throw new Error(`The 'optionLabel' input must be provided for non-primitive options.`);
524
- }
525
- }
526
- _findOptionByValue(value) {
527
- if (this.useRawValue()) {
528
- return this.options().find((option) => option === value) ?? null;
529
- }
530
- const getValue = this._getValueExtractor();
531
- return this.options().find((option) => getValue(option) === value) ?? null;
532
- }
533
- /**
534
- * Extrai o valor final de uma opção, com base em `optionValue`.
535
- * @returns Função que retorna o valor da opção.
536
- */
537
- _getValueExtractor() {
538
- if (this.TIsPrimitive()) {
539
- return (option) => option;
540
- }
541
- if (this.useRawValue()) {
542
- return (option) => option;
543
- }
544
- const valueInput = this.optionValue();
545
- if (valueInput) {
546
- return (option) => option[valueInput];
547
- }
548
- throw new Error(`optionValue is required for non-primitive options.`);
549
- }
550
- /**
551
- * Extrai o valor comparável de uma opção.
552
- * @returns Função que retorna a chave de comparação.
553
- */
554
- _getCompareValueExtractor() {
555
- if (this.TIsPrimitive()) {
556
- return (option) => option;
479
+ const options = this.options();
480
+ if (!options.length) {
481
+ return;
557
482
  }
558
- if (this.dataKey()) {
559
- const key = this.dataKey();
560
- return (option) => option[key];
483
+ const hasObjectOptions = options.some((option) => typeof option === 'object' && option !== null);
484
+ if (hasObjectOptions) {
485
+ if (!this.optionLabel()) {
486
+ throw new Error('The "optionLabel" input is required when options are objects.');
487
+ }
561
488
  }
562
- return this._getValueExtractor();
563
489
  }
564
490
  /**
565
- * Fecha o dropdown e redefine o foco.
491
+ * Fecha o dropdown de opções do componente select.
492
+ * Este método oculta as opções disponíveis e redefine o índice do item focado para -1,
493
+ * indicando que nenhum item está atualmente focado.
494
+ * @private
566
495
  */
567
496
  _closeDropdown() {
568
497
  this.showOptions.set(false);
569
- this._destroyDropdownPortal();
570
498
  this.focusedIndex.set(-1);
571
499
  }
572
500
  /**
573
- * Adiciona identificadores únicos às opções.
574
- * @param options Lista original de opções.
575
- * @returns Lista com IDs adicionados.
501
+ * Adiciona um identificador único a cada opção fornecida.
502
+ * Para cada item no array de opções, gera um objeto `InternalDropdownOption` contendo o dado original
503
+ * e um campo `id` único baseado no timestamp atual e no índice do item. Caso a opção seja um grupo
504
+ * (detectado pela presença de um array `items`), adiciona também a propriedade `grouper: true`.
505
+ * @private
506
+ * @param options Array de opções do tipo `T` a serem processadas.
507
+ * @returns Um array de objetos `InternalDropdownOption<T>` com identificadores únicos.
576
508
  */
577
509
  _addIdToOptions(options) {
578
510
  const timestamp = Date.now();
@@ -591,9 +523,14 @@ class SelectComponent {
591
523
  });
592
524
  }
593
525
  /**
594
- * Converte uma estrutura agrupada em uma lista achatada.
595
- * @param options Lista de opções com agrupadores.
596
- * @returns Lista achatada de opções e grupos.
526
+ * Converte uma lista de opções possivelmente agrupadas em uma lista plana de opções.
527
+ * Para cada opção que possui um agrupador (`grouper`) e um array de itens em `data.items`,
528
+ * adiciona a opção do agrupador e, em seguida, adiciona cada item do grupo como uma nova opção
529
+ * individual, atribuindo um `id` único para cada item do grupo.
530
+ * Caso a opção não seja um agrupador, ela é adicionada diretamente ao resultado.
531
+ * @private
532
+ * @param options Lista de opções internas do dropdown, podendo conter agrupadores.
533
+ * @returns Lista plana de opções, incluindo agrupadores e seus itens expandidos.
597
534
  */
598
535
  _grouperToFlat(options) {
599
536
  const result = [];
@@ -613,8 +550,9 @@ class SelectComponent {
613
550
  return result;
614
551
  }
615
552
  /**
616
- * Move o foco para a próxima opção.
617
- * @param options Lista de opções visíveis.
553
+ * Move o foco para a próxima opção disponível na lista, ignorando opções que sejam agrupadores.
554
+ * @private
555
+ * @param options Lista de opções internas do dropdown.
618
556
  */
619
557
  _focusNextOption(options) {
620
558
  let nextIndex = this.focusedIndex() + 1;
@@ -627,8 +565,9 @@ class SelectComponent {
627
565
  }
628
566
  }
629
567
  /**
630
- * Move o foco para a opção anterior.
631
- * @param options Lista de opções visíveis.
568
+ * Move o foco para a opção anterior na lista, ignorando opções do tipo "grouper".
569
+ * @private
570
+ * @param options Lista de opções internas do dropdown.
632
571
  */
633
572
  _focusPreviousOption(options) {
634
573
  let prevIndex = this.focusedIndex() - 1;
@@ -641,7 +580,11 @@ class SelectComponent {
641
580
  }
642
581
  }
643
582
  /**
644
- * Rola o dropdown até a opção atualmente focada.
583
+ * Rola a lista de opções para garantir que a opção atualmente focada esteja visível.
584
+ * Este método obtém o ID da opção atualmente focada a partir da lista de opções filtradas.
585
+ * Se um ID válido for encontrado, busca o elemento correspondente no DOM e utiliza
586
+ * `scrollIntoView` para rolar até a opção, alinhando-a ao bloco mais próximo.
587
+ * @private
645
588
  */
646
589
  _scrollToFocusedOption() {
647
590
  const id = this.filteredOptions()[this.focusedIndex()]?.id;
@@ -651,66 +594,14 @@ class SelectComponent {
651
594
  const el = document.getElementById(id);
652
595
  el?.scrollIntoView({ block: 'nearest' });
653
596
  }
654
- /**
655
- * Extrai o texto de uma opção, com base em `optionLabel`.
656
- * @returns Função que retorna o label da opção.
657
- */
658
- _getLabelExtractor() {
659
- if (this.TIsPrimitive()) {
660
- return (option) => String(option);
661
- }
662
- const labelInput = this.optionLabel();
663
- if (labelInput) {
664
- return (option) => String(option[labelInput] ?? '');
665
- }
666
- else {
667
- throw new Error(`optionLabel is required for non-primitive options.`);
668
- }
669
- }
670
- /**
671
- * Cria e posiciona dinamicamente o dropdown como portal.
672
- */
673
- _createDropdownPortal() {
674
- if (!isPlatformBrowser(this.platformId)) {
675
- return;
676
- }
677
- const container = this._containerDiv();
678
- const template = this._dropdownTemplate();
679
- if (!container || !template) {
680
- return;
681
- }
682
- const dropElementRect = container.nativeElement.getBoundingClientRect();
683
- this.dropTop = dropElementRect.bottom + window.scrollY;
684
- this.dropLeft = dropElementRect.left + window.scrollX;
685
- this.dropWidth = dropElementRect.width;
686
- this._destroyDropdownPortal();
687
- this._portalHost = new DomPortalOutlet(document.body);
688
- const portal = new TemplatePortal(template, this._viewContainerRef);
689
- this._portalHost.attach(portal);
690
- const dropdownEl = document.querySelector('.dropdown-body-class');
691
- if (dropdownEl) {
692
- dropdownEl.style.position = 'absolute';
693
- dropdownEl.style.top = `${this.dropTop + 4}px`;
694
- dropdownEl.style.left = `${this.dropLeft}px`;
695
- dropdownEl.style.width = `${this.dropWidth}px`;
696
- }
697
- }
698
- /**
699
- * Remove o portal do dropdown do DOM.
700
- */
701
- _destroyDropdownPortal() {
702
- if (this._portalHost?.hasAttached()) {
703
- this._portalHost.detach();
704
- }
705
- }
706
597
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
707
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SelectComponent, isStandalone: true, selector: "s-select", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, checkmark: { classPropertyName: "checkmark", publicName: "checkmark", isSignal: true, isRequired: false, transformFunction: null }, filterBy: { classPropertyName: "filterBy", publicName: "filterBy", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollItemSize: { classPropertyName: "virtualScrollItemSize", publicName: "virtualScrollItemSize", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, useRawValue: { classPropertyName: "useRawValue", publicName: "useRawValue", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "window:resize": "onWindowResize($event)" } }, providers: [
598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SelectComponent, isStandalone: true, selector: "s-select", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, checkmark: { classPropertyName: "checkmark", publicName: "checkmark", isSignal: true, isRequired: false, transformFunction: null }, filterBy: { classPropertyName: "filterBy", publicName: "filterBy", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollItemSize: { classPropertyName: "virtualScrollItemSize", publicName: "virtualScrollItemSize", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", showOptions: "showOptionsChange" }, host: { attributes: { "role": "combobox" }, listeners: { "window:resize": "onWindowResize($event)" }, properties: { "attr.aria-expanded": "showOptions()", "attr.aria-haspopup": "\"listbox\"", "attr.aria-owns": "showOptions() ? componentId + \"-listbox\" : null", "attr.aria-activedescendant": "focusedIndex() >= 0 ? filteredOptions()[focusedIndex()]?.id : null", "attr.aria-label": "placeholder() || \"Select option\"", "attr.aria-disabled": "disabled()" } }, providers: [
708
599
  {
709
600
  provide: NG_VALUE_ACCESSOR,
710
601
  useExisting: forwardRef(() => SelectComponent),
711
602
  multi: true,
712
603
  },
713
- ], viewQueries: [{ propertyName: "_dropdownTemplate", first: true, predicate: ["dropdownTemplate"], descendants: true, isSignal: true }, { propertyName: "_containerDiv", first: true, predicate: ["containerDiv"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #portalAnchor></ng-template>\n\n<div\n #containerDiv\n class=\"group flex w-full overflow-hidden rounded-[3px] border outline-1 outline-primary focus:outline\"\n [ngClass]=\"{\n 'pointer-events-none border-grayscale-20 bg-grayscale-5': disabled(),\n 'pointer-events-auto border-grayscale-30 bg-grayscale-0': !disabled(),\n }\"\n [attr.aria-disabled]=\"disabled()\"\n (click)=\"onContainerDivClick()\"\n (keydown)=\"onKeyDown($event)\"\n tabindex=\"0\"\n>\n <span\n class=\"flex flex-grow select-none px-3 py-[7px]\"\n [ngClass]=\"{\n 'text-grayscale-90': !disabled() && !isClean(),\n 'text-grayscale-60': !disabled() && isClean(),\n 'text-grayscale-30': disabled(),\n }\"\n >\n {{ print() || placeholder() }}\n </span>\n\n @if (!disabled() && showClear() && !isClean()) {\n <button\n class=\"mx-3\"\n (click)=\"clear($event)\"\n >\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n }\n\n <div\n class=\"flex items-center border-l px-3 group-hover:bg-grayscale-10 group-focus:border-primary\"\n [ngClass]=\"{ 'border-grayscale-20': disabled(), 'border-grayscale-30': !disabled() }\"\n >\n <i\n class=\"fas\"\n [ngClass]=\"{\n 'text-grayscale-30': disabled(),\n 'text-grayscale-90': !disabled(),\n 'fa-caret-down': !showOptions(),\n 'fa-caret-up': showOptions(),\n }\"\n ></i>\n </div>\n</div>\n\n<ng-template #dropdownTemplate>\n <div\n class=\"dropdown-body-class z-[1000] rounded-[3px] bg-grayscale-0 py-1 shadow-md\"\n [style.position]=\"'absolute'\"\n [style.top.px]=\"dropTop\"\n [style.left.px]=\"dropLeft\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (filter()) {\n <div class=\"flex w-full items-center gap-3 px-3 py-1.5\">\n @if (multiple()) {\n <s-checkbox\n #selectAllCheckbox\n [checked]=\"allSelected()\"\n [indeterminate]=\"selectAllIsIndeterminate()\"\n (checkedChange)=\"toggleSelectAll()\"\n ></s-checkbox>\n }\n\n <div class=\"relative flex h-[35px] grow\">\n <form\n class=\"flex grow\"\n [formGroup]=\"filterForm\"\n >\n <input\n class=\"w-full grow rounded-[3px] border border-grayscale-30 pl-2.5 pr-7 outline-1 outline-primary\"\n type=\"text\"\n formControlName=\"filter\"\n (click)=\"$event.stopPropagation()\"\n />\n </form>\n <i class=\"fas fa-search absolute right-2.5 top-2.5 text-grayscale-90\"></i>\n </div>\n <button (click)=\"clearFilter()\">\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n </div>\n }\n\n <!-- Virtual scroll -->\n @if (virtualScroll() && filteredOptions().length > 10) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"virtualScrollItemSize()\"\n class=\"h-52 overflow-auto\"\n >\n <ng-container *cdkVirtualFor=\"let option of filteredOptions(); trackBy: trackById\">\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option.data)\"\n (selected)=\"selectItem(option.data)\"\n [isGrouper]=\"option.grouper\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n\n <!-- Normal list -->\n @if (!virtualScroll() || filteredOptions().length <= 10) {\n <ul class=\"max-h-52 overflow-auto\">\n @for (option of filteredOptions(); track option.id) {\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option.data)\"\n (selected)=\"selectItem(option.data)\"\n [isGrouper]=\"option.grouper ?? false\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n }\n </ul>\n }\n\n @if (filteredOptions().length === 0) {\n <span class=\"m-3 text-grayscale-60\">{{\n emptyMessage() ?? 'platform.angular_components.no_records_found' | translate\n }}</span>\n }\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i3.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i3.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i3.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: SelectOptionComponent, selector: "s-select-option", inputs: ["id", "label", "multiple", "checkmark", "isGrouper", "isFocused", "isSelected"], outputs: ["isSelectedChange", "selected"] }, { kind: "component", type: CheckboxComponent, selector: "s-checkbox", inputs: ["disabled", "checked", "indeterminate", "label"], outputs: ["disabledChange", "checkedChange", "indeterminateChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }] });
604
+ ], viewQueries: [{ propertyName: "_containerDiv", first: true, predicate: ["containerDiv"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #containerDiv\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n class=\"select-container group flex h-[35px] w-full overflow-hidden rounded-[3px] border outline-1 outline-primary focus:outline\"\n [ngClass]=\"{\n 'pointer-events-none border-grayscale-20 bg-grayscale-5': disabled(),\n 'pointer-events-auto border-grayscale-30 bg-grayscale-0': !disabled(),\n }\"\n [attr.aria-disabled]=\"disabled()\"\n [attr.aria-expanded]=\"showOptions()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-owns]=\"'dropdown-container-' + componentId\"\n [attr.aria-labelledby]=\"componentId + '-label'\"\n role=\"combobox\"\n (click)=\"onContainerDivClick()\"\n (keydown)=\"onKeyDown($event)\"\n tabindex=\"0\"\n>\n <span\n class=\"flex flex-grow select-none px-3 py-[7px]\"\n [ngClass]=\"{\n 'text-grayscale-90': !disabled() && !isClean(),\n 'text-grayscale-60': !disabled() && isClean(),\n 'text-grayscale-30': disabled(),\n }\"\n >\n {{ print() || placeholder() }}\n </span>\n\n @if (!disabled() && showClear() && !isClean()) {\n <button\n class=\"mx-3\"\n (click)=\"clear($event)\"\n >\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n }\n\n <div\n class=\"dropdown-container flex items-center border-l px-3 group-hover:bg-grayscale-10 group-focus:border-primary\"\n [ngClass]=\"{ 'border-grayscale-20': disabled(), 'border-grayscale-30': !disabled() }\"\n >\n <i\n class=\"fas\"\n [ngClass]=\"{\n 'text-grayscale-30': disabled(),\n 'text-grayscale-90': !disabled(),\n 'fa-caret-down': !showOptions(),\n 'fa-caret-up': showOptions(),\n }\"\n ></i>\n </div>\n</div>\n\n<ng-template\n #dropdownTemplate\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"showOptions()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n (backdropClick)=\"showOptions.set(false)\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (detach)=\"showOptions.set(false)\"\n>\n <div\n [id]=\"'dropdown-container-' + componentId\"\n class=\"dropdown-body-class z-[1000] rounded-[3px] bg-grayscale-0 py-1 shadow-md\"\n (click)=\"$event.stopPropagation()\"\n [style.width.px]=\"containerDiv.offsetWidth\"\n >\n @if (filter()) {\n <div class=\"flex w-full items-center gap-3 px-3 py-1.5\">\n @if (multiple()) {\n <s-checkbox\n #selectAllCheckbox\n [checked]=\"allSelected()\"\n [indeterminate]=\"selectAllIsIndeterminate()\"\n (checkedChange)=\"toggleSelectAll()\"\n ></s-checkbox>\n }\n\n <div class=\"relative flex h-[35px] grow\">\n <form\n class=\"flex grow\"\n [formGroup]=\"filterForm\"\n >\n <input\n class=\"w-full grow rounded-[3px] border border-grayscale-30 pl-2.5 pr-7 outline-1 outline-primary\"\n type=\"text\"\n formControlName=\"filter\"\n (click)=\"$event.stopPropagation()\"\n />\n </form>\n <i class=\"fas fa-search absolute right-2.5 top-2.5 text-grayscale-90\"></i>\n </div>\n <button (click)=\"clearFilter()\">\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n </div>\n }\n\n <!-- Virtual scroll -->\n @if (virtualScroll() && filteredOptions().length > 10) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"virtualScrollItemSize()\"\n class=\"h-52 overflow-auto\"\n >\n <ng-container *cdkVirtualFor=\"let option of filteredOptions(); trackBy: trackById\">\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option)\"\n (selected)=\"selectItem(option)\"\n [isGrouper]=\"option.grouper\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n\n <!-- Normal list -->\n @if (!virtualScroll() || filteredOptions().length <= 10) {\n <ul class=\"max-h-52 overflow-auto\">\n @for (option of filteredOptions(); track option.id) {\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option)\"\n (selected)=\"selectItem(option)\"\n [isGrouper]=\"option.grouper ?? false\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n }\n </ul>\n }\n\n @if (filteredOptions().length === 0) {\n <span class=\"m-3 text-grayscale-60\">{{\n emptyMessage() ?? 'platform.angular_components.no_records_found' | translate\n }}</span>\n }\n </div>\n</ng-template>\n", styles: [":host.ng-dirty.ng-invalid{.dropdown-container,.select-container{border-color:#c13018;outline:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i3.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i3.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i3.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: SelectOptionComponent, selector: "s-select-option", inputs: ["id", "label", "multiple", "checkmark", "isGrouper", "isFocused", "isSelected"], outputs: ["isSelectedChange", "selected"] }, { kind: "component", type: CheckboxComponent, selector: "s-checkbox", inputs: ["disabled", "checked", "indeterminate", "label"], outputs: ["disabledChange", "checkedChange", "indeterminateChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i5.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i5.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }] });
714
605
  }
715
606
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, decorators: [{
716
607
  type: Component,
@@ -721,17 +612,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
721
612
  SelectOptionComponent,
722
613
  CheckboxComponent,
723
614
  TranslateModule,
615
+ OverlayModule,
724
616
  ], host: {
725
- '(document:click)': 'onDocumentClick($event)',
726
617
  '(window:resize)': 'onWindowResize($event)',
618
+ '[attr.aria-expanded]': 'showOptions()',
619
+ '[attr.aria-haspopup]': '"listbox"',
620
+ '[attr.aria-owns]': 'showOptions() ? componentId + "-listbox" : null',
621
+ '[attr.aria-activedescendant]': 'focusedIndex() >= 0 ? filteredOptions()[focusedIndex()]?.id : null',
622
+ '[attr.aria-label]': 'placeholder() || "Select option"',
623
+ '[attr.aria-disabled]': 'disabled()',
624
+ role: 'combobox',
727
625
  }, providers: [
728
626
  {
729
627
  provide: NG_VALUE_ACCESSOR,
730
628
  useExisting: forwardRef(() => SelectComponent),
731
629
  multi: true,
732
630
  },
733
- ], template: "<ng-template #portalAnchor></ng-template>\n\n<div\n #containerDiv\n class=\"group flex w-full overflow-hidden rounded-[3px] border outline-1 outline-primary focus:outline\"\n [ngClass]=\"{\n 'pointer-events-none border-grayscale-20 bg-grayscale-5': disabled(),\n 'pointer-events-auto border-grayscale-30 bg-grayscale-0': !disabled(),\n }\"\n [attr.aria-disabled]=\"disabled()\"\n (click)=\"onContainerDivClick()\"\n (keydown)=\"onKeyDown($event)\"\n tabindex=\"0\"\n>\n <span\n class=\"flex flex-grow select-none px-3 py-[7px]\"\n [ngClass]=\"{\n 'text-grayscale-90': !disabled() && !isClean(),\n 'text-grayscale-60': !disabled() && isClean(),\n 'text-grayscale-30': disabled(),\n }\"\n >\n {{ print() || placeholder() }}\n </span>\n\n @if (!disabled() && showClear() && !isClean()) {\n <button\n class=\"mx-3\"\n (click)=\"clear($event)\"\n >\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n }\n\n <div\n class=\"flex items-center border-l px-3 group-hover:bg-grayscale-10 group-focus:border-primary\"\n [ngClass]=\"{ 'border-grayscale-20': disabled(), 'border-grayscale-30': !disabled() }\"\n >\n <i\n class=\"fas\"\n [ngClass]=\"{\n 'text-grayscale-30': disabled(),\n 'text-grayscale-90': !disabled(),\n 'fa-caret-down': !showOptions(),\n 'fa-caret-up': showOptions(),\n }\"\n ></i>\n </div>\n</div>\n\n<ng-template #dropdownTemplate>\n <div\n class=\"dropdown-body-class z-[1000] rounded-[3px] bg-grayscale-0 py-1 shadow-md\"\n [style.position]=\"'absolute'\"\n [style.top.px]=\"dropTop\"\n [style.left.px]=\"dropLeft\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (filter()) {\n <div class=\"flex w-full items-center gap-3 px-3 py-1.5\">\n @if (multiple()) {\n <s-checkbox\n #selectAllCheckbox\n [checked]=\"allSelected()\"\n [indeterminate]=\"selectAllIsIndeterminate()\"\n (checkedChange)=\"toggleSelectAll()\"\n ></s-checkbox>\n }\n\n <div class=\"relative flex h-[35px] grow\">\n <form\n class=\"flex grow\"\n [formGroup]=\"filterForm\"\n >\n <input\n class=\"w-full grow rounded-[3px] border border-grayscale-30 pl-2.5 pr-7 outline-1 outline-primary\"\n type=\"text\"\n formControlName=\"filter\"\n (click)=\"$event.stopPropagation()\"\n />\n </form>\n <i class=\"fas fa-search absolute right-2.5 top-2.5 text-grayscale-90\"></i>\n </div>\n <button (click)=\"clearFilter()\">\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n </div>\n }\n\n <!-- Virtual scroll -->\n @if (virtualScroll() && filteredOptions().length > 10) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"virtualScrollItemSize()\"\n class=\"h-52 overflow-auto\"\n >\n <ng-container *cdkVirtualFor=\"let option of filteredOptions(); trackBy: trackById\">\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option.data)\"\n (selected)=\"selectItem(option.data)\"\n [isGrouper]=\"option.grouper\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n\n <!-- Normal list -->\n @if (!virtualScroll() || filteredOptions().length <= 10) {\n <ul class=\"max-h-52 overflow-auto\">\n @for (option of filteredOptions(); track option.id) {\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option.data)\"\n (selected)=\"selectItem(option.data)\"\n [isGrouper]=\"option.grouper ?? false\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n }\n </ul>\n }\n\n @if (filteredOptions().length === 0) {\n <span class=\"m-3 text-grayscale-60\">{{\n emptyMessage() ?? 'platform.angular_components.no_records_found' | translate\n }}</span>\n }\n </div>\n</ng-template>\n" }]
734
- }] });
631
+ ], template: "<div\n #containerDiv\n cdkOverlayOrigin\n #trigger=\"cdkOverlayOrigin\"\n class=\"select-container group flex h-[35px] w-full overflow-hidden rounded-[3px] border outline-1 outline-primary focus:outline\"\n [ngClass]=\"{\n 'pointer-events-none border-grayscale-20 bg-grayscale-5': disabled(),\n 'pointer-events-auto border-grayscale-30 bg-grayscale-0': !disabled(),\n }\"\n [attr.aria-disabled]=\"disabled()\"\n [attr.aria-expanded]=\"showOptions()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-owns]=\"'dropdown-container-' + componentId\"\n [attr.aria-labelledby]=\"componentId + '-label'\"\n role=\"combobox\"\n (click)=\"onContainerDivClick()\"\n (keydown)=\"onKeyDown($event)\"\n tabindex=\"0\"\n>\n <span\n class=\"flex flex-grow select-none px-3 py-[7px]\"\n [ngClass]=\"{\n 'text-grayscale-90': !disabled() && !isClean(),\n 'text-grayscale-60': !disabled() && isClean(),\n 'text-grayscale-30': disabled(),\n }\"\n >\n {{ print() || placeholder() }}\n </span>\n\n @if (!disabled() && showClear() && !isClean()) {\n <button\n class=\"mx-3\"\n (click)=\"clear($event)\"\n >\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n }\n\n <div\n class=\"dropdown-container flex items-center border-l px-3 group-hover:bg-grayscale-10 group-focus:border-primary\"\n [ngClass]=\"{ 'border-grayscale-20': disabled(), 'border-grayscale-30': !disabled() }\"\n >\n <i\n class=\"fas\"\n [ngClass]=\"{\n 'text-grayscale-30': disabled(),\n 'text-grayscale-90': !disabled(),\n 'fa-caret-down': !showOptions(),\n 'fa-caret-up': showOptions(),\n }\"\n ></i>\n </div>\n</div>\n\n<ng-template\n #dropdownTemplate\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"showOptions()\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n (backdropClick)=\"showOptions.set(false)\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (detach)=\"showOptions.set(false)\"\n>\n <div\n [id]=\"'dropdown-container-' + componentId\"\n class=\"dropdown-body-class z-[1000] rounded-[3px] bg-grayscale-0 py-1 shadow-md\"\n (click)=\"$event.stopPropagation()\"\n [style.width.px]=\"containerDiv.offsetWidth\"\n >\n @if (filter()) {\n <div class=\"flex w-full items-center gap-3 px-3 py-1.5\">\n @if (multiple()) {\n <s-checkbox\n #selectAllCheckbox\n [checked]=\"allSelected()\"\n [indeterminate]=\"selectAllIsIndeterminate()\"\n (checkedChange)=\"toggleSelectAll()\"\n ></s-checkbox>\n }\n\n <div class=\"relative flex h-[35px] grow\">\n <form\n class=\"flex grow\"\n [formGroup]=\"filterForm\"\n >\n <input\n class=\"w-full grow rounded-[3px] border border-grayscale-30 pl-2.5 pr-7 outline-1 outline-primary\"\n type=\"text\"\n formControlName=\"filter\"\n (click)=\"$event.stopPropagation()\"\n />\n </form>\n <i class=\"fas fa-search absolute right-2.5 top-2.5 text-grayscale-90\"></i>\n </div>\n <button (click)=\"clearFilter()\">\n <i class=\"fas fa-times flex items-center\"></i>\n </button>\n </div>\n }\n\n <!-- Virtual scroll -->\n @if (virtualScroll() && filteredOptions().length > 10) {\n <cdk-virtual-scroll-viewport\n [itemSize]=\"virtualScrollItemSize()\"\n class=\"h-52 overflow-auto\"\n >\n <ng-container *cdkVirtualFor=\"let option of filteredOptions(); trackBy: trackById\">\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option)\"\n (selected)=\"selectItem(option)\"\n [isGrouper]=\"option.grouper\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n\n <!-- Normal list -->\n @if (!virtualScroll() || filteredOptions().length <= 10) {\n <ul class=\"max-h-52 overflow-auto\">\n @for (option of filteredOptions(); track option.id) {\n <s-select-option\n [id]=\"option.id\"\n [label]=\"getOptionLabel(option.data)\"\n [multiple]=\"multiple()\"\n [checkmark]=\"checkmark()\"\n [isSelected]=\"isOptionSelected(option)\"\n (selected)=\"selectItem(option)\"\n [isGrouper]=\"option.grouper ?? false\"\n [isFocused]=\"option.data === focusedItem()\"\n ></s-select-option>\n }\n </ul>\n }\n\n @if (filteredOptions().length === 0) {\n <span class=\"m-3 text-grayscale-60\">{{\n emptyMessage() ?? 'platform.angular_components.no_records_found' | translate\n }}</span>\n }\n </div>\n</ng-template>\n", styles: [":host.ng-dirty.ng-invalid{.dropdown-container,.select-container{border-color:#c13018;outline:none}}\n"] }]
632
+ }], ctorParameters: () => [] });
735
633
 
736
634
  /**
737
635
  * Generated bundle index. Do not edit.