@ship-ui/core 0.19.4 → 0.22.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 (153) hide show
  1. package/README.md +3 -0
  2. package/assets/mcp/components.json +32 -4212
  3. package/bin/mcp/index.js +6544 -444
  4. package/bin/ship-fg-scanner +0 -0
  5. package/bin/ship-fg.mjs +18 -16
  6. package/bin/src/subset.ts +3 -1
  7. package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs +42 -0
  8. package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs.map +1 -0
  9. package/fesm2022/ship-ui-core-ship-accordion.mjs +127 -0
  10. package/fesm2022/ship-ui-core-ship-accordion.mjs.map +1 -0
  11. package/fesm2022/ship-ui-core-ship-alert.mjs +305 -0
  12. package/fesm2022/ship-ui-core-ship-alert.mjs.map +1 -0
  13. package/fesm2022/ship-ui-core-ship-blueprint.mjs +1156 -0
  14. package/fesm2022/ship-ui-core-ship-blueprint.mjs.map +1 -0
  15. package/fesm2022/ship-ui-core-ship-button-group.mjs +41 -0
  16. package/fesm2022/ship-ui-core-ship-button-group.mjs.map +1 -0
  17. package/fesm2022/ship-ui-core-ship-button.mjs +38 -0
  18. package/fesm2022/ship-ui-core-ship-button.mjs.map +1 -0
  19. package/fesm2022/ship-ui-core-ship-card.mjs +35 -0
  20. package/fesm2022/ship-ui-core-ship-card.mjs.map +1 -0
  21. package/fesm2022/ship-ui-core-ship-checkbox.mjs +113 -0
  22. package/fesm2022/ship-ui-core-ship-checkbox.mjs.map +1 -0
  23. package/fesm2022/ship-ui-core-ship-chip.mjs +44 -0
  24. package/fesm2022/ship-ui-core-ship-chip.mjs.map +1 -0
  25. package/fesm2022/ship-ui-core-ship-color-picker.mjs +947 -0
  26. package/fesm2022/ship-ui-core-ship-color-picker.mjs.map +1 -0
  27. package/fesm2022/ship-ui-core-ship-datepicker.mjs +951 -0
  28. package/fesm2022/ship-ui-core-ship-datepicker.mjs.map +1 -0
  29. package/fesm2022/ship-ui-core-ship-dialog.mjs +263 -0
  30. package/fesm2022/ship-ui-core-ship-dialog.mjs.map +1 -0
  31. package/fesm2022/ship-ui-core-ship-divider.mjs +22 -0
  32. package/fesm2022/ship-ui-core-ship-divider.mjs.map +1 -0
  33. package/fesm2022/ship-ui-core-ship-event-card.mjs +50 -0
  34. package/fesm2022/ship-ui-core-ship-event-card.mjs.map +1 -0
  35. package/fesm2022/ship-ui-core-ship-file-upload.mjs +112 -0
  36. package/fesm2022/ship-ui-core-ship-file-upload.mjs.map +1 -0
  37. package/fesm2022/ship-ui-core-ship-form-field.mjs +310 -0
  38. package/fesm2022/ship-ui-core-ship-form-field.mjs.map +1 -0
  39. package/fesm2022/ship-ui-core-ship-icon.mjs +81 -0
  40. package/fesm2022/ship-ui-core-ship-icon.mjs.map +1 -0
  41. package/fesm2022/ship-ui-core-ship-list.mjs +22 -0
  42. package/fesm2022/ship-ui-core-ship-list.mjs.map +1 -0
  43. package/fesm2022/ship-ui-core-ship-menu.mjs +545 -0
  44. package/fesm2022/ship-ui-core-ship-menu.mjs.map +1 -0
  45. package/fesm2022/ship-ui-core-ship-popover.mjs +286 -0
  46. package/fesm2022/ship-ui-core-ship-popover.mjs.map +1 -0
  47. package/fesm2022/ship-ui-core-ship-progress-bar.mjs +37 -0
  48. package/fesm2022/ship-ui-core-ship-progress-bar.mjs.map +1 -0
  49. package/fesm2022/ship-ui-core-ship-radio.mjs +102 -0
  50. package/fesm2022/ship-ui-core-ship-radio.mjs.map +1 -0
  51. package/fesm2022/ship-ui-core-ship-range-slider.mjs +277 -0
  52. package/fesm2022/ship-ui-core-ship-range-slider.mjs.map +1 -0
  53. package/fesm2022/ship-ui-core-ship-select.mjs +971 -0
  54. package/fesm2022/ship-ui-core-ship-select.mjs.map +1 -0
  55. package/fesm2022/ship-ui-core-ship-sidenav.mjs +248 -0
  56. package/fesm2022/ship-ui-core-ship-sidenav.mjs.map +1 -0
  57. package/fesm2022/ship-ui-core-ship-sortable.mjs +485 -0
  58. package/fesm2022/ship-ui-core-ship-sortable.mjs.map +1 -0
  59. package/fesm2022/ship-ui-core-ship-spinner.mjs +28 -0
  60. package/fesm2022/ship-ui-core-ship-spinner.mjs.map +1 -0
  61. package/fesm2022/ship-ui-core-ship-stepper.mjs +76 -0
  62. package/fesm2022/ship-ui-core-ship-stepper.mjs.map +1 -0
  63. package/fesm2022/ship-ui-core-ship-table-filter-bar.mjs +28 -0
  64. package/fesm2022/ship-ui-core-ship-table-filter-bar.mjs.map +1 -0
  65. package/fesm2022/ship-ui-core-ship-table.mjs +442 -0
  66. package/fesm2022/ship-ui-core-ship-table.mjs.map +1 -0
  67. package/fesm2022/ship-ui-core-ship-tabs.mjs +38 -0
  68. package/fesm2022/ship-ui-core-ship-tabs.mjs.map +1 -0
  69. package/fesm2022/ship-ui-core-ship-theme-toggle.mjs +119 -0
  70. package/fesm2022/ship-ui-core-ship-theme-toggle.mjs.map +1 -0
  71. package/fesm2022/ship-ui-core-ship-toggle-card.mjs +75 -0
  72. package/fesm2022/ship-ui-core-ship-toggle-card.mjs.map +1 -0
  73. package/fesm2022/ship-ui-core-ship-toggle.mjs +105 -0
  74. package/fesm2022/ship-ui-core-ship-toggle.mjs.map +1 -0
  75. package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs +186 -0
  76. package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs.map +1 -0
  77. package/fesm2022/ship-ui-core.mjs +880 -8782
  78. package/fesm2022/ship-ui-core.mjs.map +1 -1
  79. package/package.json +147 -3
  80. package/styles/core.scss +43 -0
  81. package/styles/helpers.scss +2 -0
  82. package/styles/index.scss +12 -123
  83. package/types/ship-ui-core-sh-form-field-experimental.d.ts +11 -0
  84. package/types/ship-ui-core-ship-accordion.d.ts +19 -0
  85. package/types/ship-ui-core-ship-alert.d.ts +68 -0
  86. package/types/ship-ui-core-ship-blueprint.d.ts +112 -0
  87. package/types/ship-ui-core-ship-button-group.d.ts +15 -0
  88. package/types/ship-ui-core-ship-button.d.ts +13 -0
  89. package/types/ship-ui-core-ship-card.d.ts +11 -0
  90. package/types/ship-ui-core-ship-checkbox.d.ts +22 -0
  91. package/types/ship-ui-core-ship-chip.d.ts +15 -0
  92. package/types/ship-ui-core-ship-color-picker.d.ts +105 -0
  93. package/types/ship-ui-core-ship-datepicker.d.ts +96 -0
  94. package/types/ship-ui-core-ship-dialog.d.ts +76 -0
  95. package/types/ship-ui-core-ship-divider.d.ts +8 -0
  96. package/types/ship-ui-core-ship-event-card.d.ts +11 -0
  97. package/types/ship-ui-core-ship-file-upload.d.ts +20 -0
  98. package/types/ship-ui-core-ship-form-field.d.ts +32 -0
  99. package/types/ship-ui-core-ship-icon.d.ts +18 -0
  100. package/types/ship-ui-core-ship-list.d.ts +8 -0
  101. package/types/ship-ui-core-ship-menu.d.ts +49 -0
  102. package/types/ship-ui-core-ship-popover.d.ts +40 -0
  103. package/types/ship-ui-core-ship-progress-bar.d.ts +14 -0
  104. package/types/ship-ui-core-ship-radio.d.ts +22 -0
  105. package/types/ship-ui-core-ship-range-slider.d.ts +31 -0
  106. package/types/ship-ui-core-ship-select.d.ts +81 -0
  107. package/types/ship-ui-core-ship-sidenav.d.ts +36 -0
  108. package/types/ship-ui-core-ship-sortable.d.ts +72 -0
  109. package/types/ship-ui-core-ship-spinner.d.ts +10 -0
  110. package/types/ship-ui-core-ship-stepper.d.ts +13 -0
  111. package/types/ship-ui-core-ship-table-filter-bar.d.ts +8 -0
  112. package/types/ship-ui-core-ship-table.d.ts +69 -0
  113. package/types/ship-ui-core-ship-tabs.d.ts +14 -0
  114. package/types/ship-ui-core-ship-theme-toggle.d.ts +28 -0
  115. package/types/ship-ui-core-ship-toggle-card.d.ts +15 -0
  116. package/types/ship-ui-core-ship-toggle.d.ts +21 -0
  117. package/types/ship-ui-core-ship-virtual-scroll.d.ts +22 -0
  118. package/types/ship-ui-core.d.ts +88 -1070
  119. package/styles/components/ship-accordion.scss +0 -113
  120. package/styles/components/ship-alert-container.scss +0 -49
  121. package/styles/components/ship-alert.scss +0 -177
  122. package/styles/components/ship-blueprint.scss +0 -242
  123. package/styles/components/ship-button-group.scss +0 -165
  124. package/styles/components/ship-button.scss +0 -141
  125. package/styles/components/ship-card.scss +0 -56
  126. package/styles/components/ship-checkbox.scss +0 -116
  127. package/styles/components/ship-chip.scss +0 -104
  128. package/styles/components/ship-color-picker.scss +0 -150
  129. package/styles/components/ship-datepicker.scss +0 -317
  130. package/styles/components/ship-dialog.scss +0 -152
  131. package/styles/components/ship-divider.scss +0 -27
  132. package/styles/components/ship-event-card.scss +0 -51
  133. package/styles/components/ship-file-upload.scss +0 -47
  134. package/styles/components/ship-form-field.scss +0 -408
  135. package/styles/components/ship-icon.scss +0 -54
  136. package/styles/components/ship-list.scss +0 -165
  137. package/styles/components/ship-menu.scss +0 -237
  138. package/styles/components/ship-popover.scss +0 -205
  139. package/styles/components/ship-progress-bar.scss +0 -173
  140. package/styles/components/ship-radio.scss +0 -113
  141. package/styles/components/ship-range-slider.scss +0 -421
  142. package/styles/components/ship-select.scss +0 -153
  143. package/styles/components/ship-sidenav.scss +0 -195
  144. package/styles/components/ship-sortable.scss +0 -45
  145. package/styles/components/ship-spinner.scss +0 -53
  146. package/styles/components/ship-stepper.scss +0 -158
  147. package/styles/components/ship-table.scss +0 -443
  148. package/styles/components/ship-tabs.scss +0 -125
  149. package/styles/components/ship-theme-toggle.scss +0 -41
  150. package/styles/components/ship-toggle-card.scss +0 -69
  151. package/styles/components/ship-toggle.scss +0 -255
  152. package/styles/components/ship-tooltip.scss +0 -151
  153. package/styles/components/ship-virtual-scroll.scss +0 -12
@@ -0,0 +1,971 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, ElementRef, input, model, output, computed, signal, contentChild, TemplateRef, viewChild, untracked, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
4
+ import { ShipCheckbox } from '@ship-ui/core/ship-checkbox';
5
+ import { ShipChip } from '@ship-ui/core/ship-chip';
6
+ import { ShipDivider } from '@ship-ui/core/ship-divider';
7
+ import { ShipFormField } from '@ship-ui/core/ship-form-field';
8
+ import { ShipIcon } from '@ship-ui/core/ship-icon';
9
+ import { ShipPopover } from '@ship-ui/core/ship-popover';
10
+ import { ShipSpinner } from '@ship-ui/core/ship-spinner';
11
+ import { shipComponentClasses, generateUniqueId } from '@ship-ui/core';
12
+
13
+ class ShipSelect {
14
+ constructor() {
15
+ this.#selfRef = inject((ElementRef));
16
+ this.value = input(/* @ts-ignore */
17
+ ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
18
+ this.label = input(/* @ts-ignore */
19
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
20
+ this.asFreeText = input(false, /* @ts-ignore */
21
+ ...(ngDevMode ? [{ debugName: "asFreeText" }] : /* istanbul ignore next */ []));
22
+ this.color = input(null, /* @ts-ignore */
23
+ ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
24
+ this.variant = input(null, /* @ts-ignore */
25
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
26
+ this.size = input(null, /* @ts-ignore */
27
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
28
+ this.optionTitle = input(null, /* @ts-ignore */
29
+ ...(ngDevMode ? [{ debugName: "optionTitle" }] : /* istanbul ignore next */ []));
30
+ this.freeTextTitle = input(null, /* @ts-ignore */
31
+ ...(ngDevMode ? [{ debugName: "freeTextTitle" }] : /* istanbul ignore next */ []));
32
+ this.freeTextPlaceholder = input('Type to create a new option', /* @ts-ignore */
33
+ ...(ngDevMode ? [{ debugName: "freeTextPlaceholder" }] : /* istanbul ignore next */ []));
34
+ this.validateFreeText = input(/* @ts-ignore */
35
+ ...(ngDevMode ? [undefined, { debugName: "validateFreeText" }] : /* istanbul ignore next */ []));
36
+ this.placeholder = input(/* @ts-ignore */
37
+ ...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
38
+ this.readonly = model(false, /* @ts-ignore */
39
+ ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
40
+ this.disabled = model(false, /* @ts-ignore */
41
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
42
+ this.hostClasses = shipComponentClasses('select', {
43
+ color: this.color,
44
+ variant: this.variant,
45
+ size: this.size,
46
+ readonly: this.readonly,
47
+ });
48
+ this.lazySearch = input(false, /* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "lazySearch" }] : /* istanbul ignore next */ []));
50
+ this.inlineSearch = input(false, /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "inlineSearch" }] : /* istanbul ignore next */ []));
52
+ this.asText = input(false, /* @ts-ignore */
53
+ ...(ngDevMode ? [{ debugName: "asText" }] : /* istanbul ignore next */ []));
54
+ this.isClearable = input(true, /* @ts-ignore */
55
+ ...(ngDevMode ? [{ debugName: "isClearable" }] : /* istanbul ignore next */ []));
56
+ this.selectMultiple = input(false, /* @ts-ignore */
57
+ ...(ngDevMode ? [{ debugName: "selectMultiple" }] : /* istanbul ignore next */ []));
58
+ this.optionTemplate = input(null, /* @ts-ignore */
59
+ ...(ngDevMode ? [{ debugName: "optionTemplate" }] : /* istanbul ignore next */ []));
60
+ this.selectedOptionTemplate = input(null, /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "selectedOptionTemplate" }] : /* istanbul ignore next */ []));
62
+ this.placeholderTemplate = input(null, /* @ts-ignore */
63
+ ...(ngDevMode ? [{ debugName: "placeholderTemplate" }] : /* istanbul ignore next */ []));
64
+ this.freeTextOptionTemplate = input(null, /* @ts-ignore */
65
+ ...(ngDevMode ? [{ debugName: "freeTextOptionTemplate" }] : /* istanbul ignore next */ []));
66
+ this.isOpen = model(false, /* @ts-ignore */
67
+ ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
68
+ this.isLoading = model(false, /* @ts-ignore */
69
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
70
+ this.options = model([], /* @ts-ignore */
71
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
72
+ this.selectedOptions = model([], /* @ts-ignore */
73
+ ...(ngDevMode ? [{ debugName: "selectedOptions" }] : /* istanbul ignore next */ []));
74
+ this.cleared = output();
75
+ this.onAddNewFreeTextOption = output();
76
+ this.spacedSelectedOptions = computed(() => {
77
+ const selectedOptions = this.selectedOptions();
78
+ const valueKey = this.value();
79
+ return selectedOptions.map((option) => (valueKey ? this.#getProperty(option, valueKey) : option)).join(', ');
80
+ }, /* @ts-ignore */
81
+ ...(ngDevMode ? [{ debugName: "spacedSelectedOptions" }] : /* istanbul ignore next */ []));
82
+ this.computedFreeTextOption = computed(() => {
83
+ const inputValue = this.inputValue();
84
+ const valueKey = this.value();
85
+ const newOption = valueKey ? {} : inputValue;
86
+ if (valueKey && typeof newOption === 'object') {
87
+ newOption[valueKey] = inputValue;
88
+ }
89
+ return newOption;
90
+ }, /* @ts-ignore */
91
+ ...(ngDevMode ? [{ debugName: "computedFreeTextOption" }] : /* istanbul ignore next */ []));
92
+ this.#previousSelectedOptions = signal(null, /* @ts-ignore */
93
+ ...(ngDevMode ? [{ debugName: "#previousSelectedOptions" }] : /* istanbul ignore next */ []));
94
+ this.inlineTemplate = contentChild(TemplateRef, /* @ts-ignore */
95
+ ...(ngDevMode ? [{ debugName: "inlineTemplate" }] : /* istanbul ignore next */ []));
96
+ this.optionsWrapRef = viewChild.required('optionsWrap');
97
+ this.inputRefInput = signal(null, /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "inputRefInput" }] : /* istanbul ignore next */ []));
99
+ this.#inputObserver = typeof MutationObserver !== 'undefined'
100
+ ? new MutationObserver((mutations) => {
101
+ for (var mutation of mutations) {
102
+ if (mutation && (mutation.target.nodeName === 'INPUT' || mutation.target.nodeName === 'TEXTAREA')) {
103
+ this.inputRefInput.set(new ElementRef(mutation.target));
104
+ this.#inputObserver.disconnect();
105
+ }
106
+ }
107
+ })
108
+ : undefined;
109
+ this.inputValue = signal('', /* @ts-ignore */
110
+ ...(ngDevMode ? [{ debugName: "inputValue" }] : /* istanbul ignore next */ []));
111
+ this.prevInputValue = signal(null, /* @ts-ignore */
112
+ ...(ngDevMode ? [{ debugName: "prevInputValue" }] : /* istanbul ignore next */ []));
113
+ this.focusedOptionIndex = signal(-1, /* @ts-ignore */
114
+ ...(ngDevMode ? [{ debugName: "focusedOptionIndex" }] : /* istanbul ignore next */ []));
115
+ this._isClearable = computed(() => this.selectMultiple() || this.isClearable(), /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "_isClearable" }] : /* istanbul ignore next */ []));
117
+ this.selectClasses = computed(() => this.#selfRef.nativeElement.classList.toString(), /* @ts-ignore */
118
+ ...(ngDevMode ? [{ debugName: "selectClasses" }] : /* istanbul ignore next */ []));
119
+ this.stretch = computed(() => this.selectClasses().includes('stretch'), /* @ts-ignore */
120
+ ...(ngDevMode ? [{ debugName: "stretch" }] : /* istanbul ignore next */ []));
121
+ this.small = computed(() => this.selectClasses().includes('small'), /* @ts-ignore */
122
+ ...(ngDevMode ? [{ debugName: "small" }] : /* istanbul ignore next */ []));
123
+ this.placeholderText = computed(() => {
124
+ const placeholder = this.placeholder();
125
+ const inputRefEl = this.inputRefEl();
126
+ return placeholder || inputRefEl?.placeholder || null;
127
+ }, /* @ts-ignore */
128
+ ...(ngDevMode ? [{ debugName: "placeholderText" }] : /* istanbul ignore next */ []));
129
+ this.selectedOptionValues = computed(() => {
130
+ const selectedOptions = this.selectedOptions();
131
+ const valueKey = this.value();
132
+ return valueKey
133
+ ? selectedOptions.map((selectedOption) => this.#getProperty(selectedOption, valueKey))
134
+ : selectedOptions;
135
+ }, /* @ts-ignore */
136
+ ...(ngDevMode ? [{ debugName: "selectedOptionValues" }] : /* istanbul ignore next */ []));
137
+ this.filteredOptions = computed(() => {
138
+ const opts = this.options() || [];
139
+ const label = this.label();
140
+ const valueKey = this.value();
141
+ const inlineSearch = this.inlineSearch();
142
+ const inputValue = this.inputValue().toLowerCase();
143
+ const inputValueRegex = this.#createWildcardRegex(inputValue);
144
+ if (opts.length <= 0)
145
+ return [];
146
+ if (!inlineSearch || inputValue === '') {
147
+ return opts;
148
+ }
149
+ const filtered = opts.filter((item) => {
150
+ const optionLabel = label
151
+ ? (this.#getProperty(item, label) ?? '').toString().toLowerCase()
152
+ : (item ?? '').toString().toLowerCase();
153
+ const optionValue = ((valueKey ? this.#getProperty(item, valueKey) : item) ?? '').toString().toLowerCase();
154
+ const testLabel = inputValueRegex.test(optionLabel);
155
+ const testValue = inputValueRegex.test(optionValue);
156
+ return testLabel || testValue;
157
+ });
158
+ const scoredOptions = filtered.map((item) => {
159
+ const optionLabel = label
160
+ ? (this.#getProperty(item, label) ?? '').toString().toLowerCase()
161
+ : (item ?? '').toString().toLowerCase();
162
+ const optionValue = ((valueKey ? this.#getProperty(item, valueKey) : item) ?? '').toString().toLowerCase();
163
+ const labelScore = this.#calculateMatchScore(optionLabel, inputValue);
164
+ const valueScore = this.#calculateMatchScore(optionValue, inputValue);
165
+ return {
166
+ item,
167
+ score: Math.max(labelScore, valueScore),
168
+ };
169
+ });
170
+ scoredOptions.sort((a, b) => b.score - a.score);
171
+ return scoredOptions.map((scoredOption) => scoredOption.item);
172
+ }, /* @ts-ignore */
173
+ ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
174
+ this.componentId = generateUniqueId();
175
+ this.inputRefEl = computed(() => {
176
+ const inputRefInput = this.inputRefInput();
177
+ if (inputRefInput === null)
178
+ return;
179
+ const input = inputRefInput ? inputRefInput.nativeElement : null;
180
+ if (!input) {
181
+ console.warn('<sh-select> input element not found are you missing to pass an <input> or <textarea> element to select component?');
182
+ return null;
183
+ }
184
+ input.autocomplete = 'off';
185
+ input.setAttribute('role', 'combobox');
186
+ input.setAttribute('id', `combobox-${this.componentId}`);
187
+ input.setAttribute('aria-haspopup', 'listbox');
188
+ input.setAttribute('aria-owns', `optionsWrapId-${this.componentId}`);
189
+ input.setAttribute('aria-controls', `optionsWrapId-${this.componentId}`);
190
+ this.#createCustomInputEventListener(input);
191
+ input.addEventListener('focus', () => {
192
+ if (this.readonly())
193
+ return;
194
+ this.open();
195
+ });
196
+ input.addEventListener('input', (e) => {
197
+ const newInputValue = e.target.value;
198
+ const inputValue = this.inputValue();
199
+ if (newInputValue === inputValue)
200
+ return;
201
+ this.focusedOptionIndex.set(this.asFreeText() ? -1 : 0);
202
+ this.inputValue.set(newInputValue);
203
+ this.updateInputElValue();
204
+ });
205
+ input.addEventListener('inputValueChanged', (event) => {
206
+ const newInputValue = event.detail.value;
207
+ const inputValue = this.inputValue();
208
+ const selectedOptionsLength = untracked(() => this.selectedOptions().length);
209
+ if (newInputValue === inputValue && (newInputValue !== '' || selectedOptionsLength > 0))
210
+ return;
211
+ this.setSelectedOptionsFromValue(newInputValue);
212
+ this.setInputValueFromOptions(this.selectedOptions());
213
+ this.#setFirstSelectedOptionAsFocused();
214
+ });
215
+ return input;
216
+ }, /* @ts-ignore */
217
+ ...(ngDevMode ? [{ debugName: "inputRefEl" }] : /* istanbul ignore next */ []));
218
+ this.focusEffect = effect(() => {
219
+ const input = this.inputRefEl();
220
+ if (!input)
221
+ return;
222
+ const index = this.focusedOptionIndex();
223
+ if (index >= 0 || (index === -1 && this.asFreeText())) {
224
+ input.setAttribute('aria-activedescendant', this.getOptionId(index));
225
+ }
226
+ else {
227
+ input.removeAttribute('aria-activedescendant');
228
+ }
229
+ }, /* @ts-ignore */
230
+ ...(ngDevMode ? [{ debugName: "focusEffect" }] : /* istanbul ignore next */ []));
231
+ this.openAbortController = null;
232
+ this.isOpenEffect = effect(() => {
233
+ const isOpen = this.isOpen();
234
+ if (isOpen) {
235
+ if (!this.openAbortController) {
236
+ this.openAbortController = new AbortController();
237
+ }
238
+ const input = this.inputRefEl();
239
+ const asFreeText = this.asFreeText();
240
+ const baseIndex = asFreeText ? -1 : 0;
241
+ if (!input)
242
+ return;
243
+ input.setAttribute('aria-expanded', this.isOpen().toString());
244
+ input.addEventListener('keydown', (e) => {
245
+ if (e.key === 'Escape' || e.key === 'Tab') {
246
+ e.preventDefault();
247
+ this.close();
248
+ }
249
+ if (e.key === 'Enter') {
250
+ e.preventDefault();
251
+ this.toggleOptionByIndex(this.focusedOptionIndex(), undefined, true);
252
+ }
253
+ if (e.key === 'ArrowDown') {
254
+ e.preventDefault();
255
+ const newIndex = this.focusedOptionIndex() + 1;
256
+ this.focusedOptionIndex.set(newIndex > this.filteredOptions().length - 1 ? baseIndex : newIndex);
257
+ }
258
+ if (e.key === 'ArrowUp') {
259
+ e.preventDefault();
260
+ const newIndex = this.focusedOptionIndex() - 1;
261
+ this.focusedOptionIndex.set(newIndex < baseIndex ? this.filteredOptions().length - 1 : newIndex);
262
+ }
263
+ }, {
264
+ signal: this.openAbortController?.signal,
265
+ });
266
+ }
267
+ else {
268
+ const input = this.inputRefEl();
269
+ if (!input)
270
+ return;
271
+ input.setAttribute('aria-expanded', this.isOpen().toString());
272
+ if (this.openAbortController) {
273
+ this.openAbortController.abort();
274
+ this.openAbortController = null;
275
+ }
276
+ }
277
+ }, /* @ts-ignore */
278
+ ...(ngDevMode ? [{ debugName: "isOpenEffect" }] : /* istanbul ignore next */ []));
279
+ this._inputValue = '';
280
+ this.inputValueEffect = effect(() => {
281
+ const inputValue = this.inputValue();
282
+ this._inputValue = inputValue;
283
+ }, /* @ts-ignore */
284
+ ...(ngDevMode ? [{ debugName: "inputValueEffect" }] : /* istanbul ignore next */ []));
285
+ this.inputRefElEffect = effect(() => {
286
+ const input = this.inputRefEl();
287
+ if (!input)
288
+ return;
289
+ const selectedOptionsLength = untracked(() => this.selectedOptions().length);
290
+ if (input.value === this._inputValue && (input.value !== '' || selectedOptionsLength > 0))
291
+ return;
292
+ this.disabled.set(input.disabled);
293
+ untracked(() => {
294
+ this.setSelectedOptionsFromValue(input.value);
295
+ this.setInputValueFromOptions(this.selectedOptions());
296
+ });
297
+ }, /* @ts-ignore */
298
+ ...(ngDevMode ? [{ debugName: "inputRefElEffect" }] : /* istanbul ignore next */ []));
299
+ this.selectedLabels = computed(() => {
300
+ const selected = this.selectedOptions();
301
+ const label = this.label();
302
+ if (!label) {
303
+ return selected.join(', ');
304
+ }
305
+ return selected.map((selected) => this.getLabel(selected)).join(', ');
306
+ }, /* @ts-ignore */
307
+ ...(ngDevMode ? [{ debugName: "selectedLabels" }] : /* istanbul ignore next */ []));
308
+ this.inputState = computed(() => {
309
+ if ((this.selectedOptions().length > 0 || (this.asFreeText() && this.inputValue().length > 0)) && !this.isOpen()) {
310
+ return 'selected';
311
+ }
312
+ if (this.isLoading()) {
313
+ return 'loading';
314
+ }
315
+ if (this.isOpen() && (this.lazySearch() || this.inlineSearch())) {
316
+ return 'open-searching';
317
+ }
318
+ if (this.isOpen()) {
319
+ return 'open';
320
+ }
321
+ if (this.inlineSearch() || this.lazySearch()) {
322
+ return 'searching';
323
+ }
324
+ return 'closed';
325
+ }, /* @ts-ignore */
326
+ ...(ngDevMode ? [{ debugName: "inputState" }] : /* istanbul ignore next */ []));
327
+ this.hasSearch = computed(() => this.inlineSearch() || this.lazySearch(), /* @ts-ignore */
328
+ ...(ngDevMode ? [{ debugName: "hasSearch" }] : /* istanbul ignore next */ []));
329
+ this.#selectedOptionsEffect = effect(() => {
330
+ if (this.selectMultiple() && this.hasSearch()) {
331
+ return;
332
+ }
333
+ const selectedOptions = this.selectedOptions();
334
+ const valueKey = this.value();
335
+ const inputValue = selectedOptions
336
+ .map((option) => {
337
+ const optionValue = valueKey ? this.#getProperty(option, valueKey) : option;
338
+ return optionValue;
339
+ })
340
+ .join(',');
341
+ this.inputValue.set(inputValue);
342
+ }, /* @ts-ignore */
343
+ ...(ngDevMode ? [{ debugName: "#selectedOptionsEffect" }] : /* istanbul ignore next */ []));
344
+ }
345
+ #selfRef;
346
+ #previousSelectedOptions;
347
+ #inputObserver;
348
+ #calculateMatchScore(option, input) {
349
+ if (!input)
350
+ return 0;
351
+ let score = 0;
352
+ let lastIndex = -1;
353
+ let matchCount = 0;
354
+ let inSequence = true;
355
+ for (let i = 0; i < input.length; i++) {
356
+ const char = input[i];
357
+ if (option.length > lastIndex + 1 && option[lastIndex + 1] === char) {
358
+ score += i === 0 ? 100 : 150;
359
+ lastIndex++;
360
+ matchCount++;
361
+ }
362
+ else {
363
+ const charIndex = option.indexOf(char, lastIndex + 1);
364
+ if (i > 0) {
365
+ inSequence = false;
366
+ }
367
+ if (charIndex === -1) {
368
+ return 0;
369
+ }
370
+ score += 100;
371
+ lastIndex = charIndex;
372
+ matchCount++;
373
+ }
374
+ }
375
+ if (inSequence && input.length === matchCount) {
376
+ score += 1000;
377
+ }
378
+ score += matchCount * 20;
379
+ return score;
380
+ }
381
+ #selectedOptionsEffect;
382
+ ngOnInit() {
383
+ this.setInitInput();
384
+ }
385
+ setInitInput() {
386
+ const input = this.#selfRef.nativeElement.querySelector('input');
387
+ if (input) {
388
+ this.inputRefInput.set(new ElementRef(input));
389
+ return;
390
+ }
391
+ const textarea = this.#selfRef.nativeElement.querySelector('textarea');
392
+ if (textarea) {
393
+ this.inputRefInput.set(new ElementRef(textarea));
394
+ return;
395
+ }
396
+ if (typeof MutationObserver !== 'undefined') {
397
+ this.#inputObserver.observe(this.#selfRef.nativeElement, {
398
+ attributes: true,
399
+ childList: true,
400
+ subtree: true,
401
+ characterData: true,
402
+ });
403
+ }
404
+ }
405
+ setSelectedOptionsFromValue(value) {
406
+ const options = this.options() || [];
407
+ const valueKey = this.value();
408
+ const selectMultiple = this.selectMultiple();
409
+ const inputValueAsString = value.toString().split(',');
410
+ if (inputValueAsString.length === 0) {
411
+ this.selectedOptions.set([]);
412
+ return;
413
+ }
414
+ const inputAsArray = selectMultiple ? inputValueAsString : [inputValueAsString[0]];
415
+ const selectedOptions = options.filter((option) => {
416
+ const optionValue = valueKey ? this.#getProperty(option, valueKey)?.toString() : option?.toString();
417
+ return optionValue !== undefined && optionValue !== null && inputAsArray.includes(optionValue);
418
+ });
419
+ const isMatched = selectedOptions.length > 0;
420
+ this.selectedOptions.set(selectMultiple ? selectedOptions : isMatched ? [selectedOptions[0]] : []);
421
+ }
422
+ setInputValueFromOptions(options) {
423
+ const valueKey = this.value();
424
+ if (options.length === 0) {
425
+ this.inputValue.set('');
426
+ this.updateInputElValue();
427
+ return;
428
+ }
429
+ const newInputValue = options
430
+ .map((option) => {
431
+ const optionValue = valueKey ? this.#getProperty(option, valueKey) : option;
432
+ return optionValue;
433
+ })
434
+ .join(',');
435
+ if (newInputValue === this.inputValue())
436
+ return;
437
+ this.inputValue.set(newInputValue);
438
+ this.updateInputElValue();
439
+ }
440
+ getValue(option) {
441
+ const valueKey = this.value();
442
+ if (!valueKey)
443
+ return option;
444
+ return this.#getProperty(option, valueKey);
445
+ }
446
+ getLabel(option) {
447
+ const label = this.label();
448
+ if (!label)
449
+ return option;
450
+ return this.#getProperty(option, label);
451
+ }
452
+ getOptionId(index) {
453
+ return `opt-${this.componentId}-${index}`;
454
+ }
455
+ toggleOptionByIndex(optionIndex, event, enterKey = false) {
456
+ let option = this.filteredOptions()[optionIndex];
457
+ if (this.asFreeText() && optionIndex === -1) {
458
+ const newOption = this.computedFreeTextOption();
459
+ const newOptionValue = this.getValue(newOption);
460
+ const validateFreeTextFunc = this.validateFreeText() ?? ((val) => true);
461
+ const isValid = validateFreeTextFunc(newOptionValue);
462
+ if (!isValid)
463
+ return;
464
+ this.options.update((options) => {
465
+ const index = options.findIndex((option) => this.getValue(option) === newOptionValue);
466
+ if (index > -1)
467
+ return options;
468
+ this.onAddNewFreeTextOption.emit(newOptionValue);
469
+ return [newOption, ...options];
470
+ });
471
+ optionIndex = 0;
472
+ option = newOption;
473
+ }
474
+ else if (!option) {
475
+ this.close();
476
+ return;
477
+ }
478
+ if (event) {
479
+ event.stopPropagation();
480
+ }
481
+ const selectMultiple = this.selectMultiple();
482
+ const isClearable = this._isClearable();
483
+ const valueKey = this.value();
484
+ const selectedOptionValues = this.selectedOptionValues();
485
+ const optionValue = valueKey ? this.#getProperty(option, valueKey) : option;
486
+ this.prevInputValue.set(null);
487
+ this.#previousSelectedOptions.set(null);
488
+ this.selectedOptions.update((selectedOptions) => {
489
+ const index = selectedOptionValues.indexOf(optionValue);
490
+ if (index > -1) {
491
+ // Remove it
492
+ const nextSelectedOptions = [...selectedOptions.slice(0, index), ...selectedOptions.slice(index + 1)];
493
+ return isClearable
494
+ ? nextSelectedOptions
495
+ : nextSelectedOptions.length > 0
496
+ ? nextSelectedOptions
497
+ : selectedOptions;
498
+ }
499
+ else {
500
+ // Add it
501
+ this.focusedOptionIndex.set(optionIndex);
502
+ return selectMultiple ? [...selectedOptions, option] : [option];
503
+ }
504
+ });
505
+ if (selectMultiple) {
506
+ this.inputRefEl()?.focus();
507
+ }
508
+ else {
509
+ this.isOpen.set(false);
510
+ }
511
+ this.setInputValueFromOptions(this.selectedOptions());
512
+ if (selectMultiple && this.hasSearch()) {
513
+ this.inputValue.set('');
514
+ this.updateInputElValue();
515
+ }
516
+ }
517
+ removeSelectedOptionByIndex($event, optionRemoveIndex) {
518
+ $event.stopPropagation();
519
+ this.selectedOptions.update((selectedOptions) => {
520
+ return [...selectedOptions.slice(0, optionRemoveIndex), ...selectedOptions.slice(optionRemoveIndex + 1)];
521
+ });
522
+ this.setInputValueFromOptions(this.selectedOptions());
523
+ }
524
+ isSelected(optionIndex) {
525
+ const valueKey = this.value();
526
+ const option = this.filteredOptions()[optionIndex];
527
+ const optionValue = valueKey ? this.#getProperty(option, valueKey) : option;
528
+ return this.selectedOptionValues().indexOf(optionValue) > -1;
529
+ }
530
+ open() {
531
+ if (this.isOpen())
532
+ return;
533
+ this.isOpen.set(true);
534
+ if (this.hasSearch()) {
535
+ this.prevInputValue.set(this.inputValue() ?? '');
536
+ this.inputValue.set('');
537
+ this.updateInputElValue();
538
+ }
539
+ else {
540
+ this.#previousSelectedOptions.set(this.selectedOptions());
541
+ }
542
+ if (!this.selectMultiple()) {
543
+ this.#setFirstSelectedOptionAsFocused();
544
+ }
545
+ }
546
+ #setFirstSelectedOptionAsFocused() {
547
+ const firstSelectedValue = this.selectedOptionValues()[0];
548
+ const valueKey = this.value();
549
+ if (firstSelectedValue) {
550
+ const findOptionIndex = this.filteredOptions().findIndex((x) => valueKey ? this.#getProperty(x, valueKey) === firstSelectedValue : x === firstSelectedValue);
551
+ this.focusedOptionIndex.set(findOptionIndex);
552
+ }
553
+ else {
554
+ this.focusedOptionIndex.set(-1);
555
+ }
556
+ }
557
+ close() {
558
+ this.isOpen.set(false);
559
+ const prevInputValue = this.prevInputValue();
560
+ const prevSelectedOptions = this.#previousSelectedOptions();
561
+ if (this.asFreeText() && !this.selectMultiple()) {
562
+ this.updateInputElValue();
563
+ return;
564
+ }
565
+ if (this.hasSearch() && prevInputValue) {
566
+ this.inputValue.set(prevInputValue);
567
+ this.setInputValueFromOptions(this.selectedOptions());
568
+ }
569
+ if (!this.hasSearch() && prevSelectedOptions !== null) {
570
+ this.setInputValueFromOptions(prevSelectedOptions);
571
+ }
572
+ if (this.selectMultiple()) {
573
+ this.setInputValueFromOptions(this.selectedOptions());
574
+ }
575
+ }
576
+ clear($event) {
577
+ $event?.stopPropagation();
578
+ this.inputValue.set('');
579
+ this.selectedOptions.set([]);
580
+ this.isOpen.set(false);
581
+ this.prevInputValue.set(null);
582
+ this.cleared.emit();
583
+ this.updateInputElValue();
584
+ }
585
+ updateInputElValue() {
586
+ const inputEl = this.inputRefEl();
587
+ const inputValue = this.inputValue();
588
+ if (!inputEl || inputEl.value === inputValue)
589
+ return;
590
+ inputEl.value = inputValue;
591
+ inputEl.dispatchEvent(new Event('input'));
592
+ }
593
+ #getProperty(obj, path) {
594
+ return path.split('.').reduce((o, i) => o?.[i], obj);
595
+ }
596
+ #createCustomInputEventListener(input) {
597
+ Object.defineProperty(input, 'value', {
598
+ configurable: true,
599
+ get() {
600
+ const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(input), 'value');
601
+ return descriptor.get.call(this);
602
+ },
603
+ set(newVal) {
604
+ const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(input), 'value');
605
+ descriptor.set.call(this, newVal);
606
+ const inputEvent = new CustomEvent('inputValueChanged', {
607
+ bubbles: true,
608
+ cancelable: true,
609
+ detail: {
610
+ value: newVal,
611
+ },
612
+ });
613
+ this.dispatchEvent(inputEvent);
614
+ return newVal;
615
+ },
616
+ });
617
+ }
618
+ #createWildcardRegex(inputValue) {
619
+ const lowerCaseInput = (inputValue ?? '').toLowerCase();
620
+ let regexPattern = '^';
621
+ for (const char of lowerCaseInput) {
622
+ regexPattern += '.*' + this.#escapeRegexChar(char);
623
+ }
624
+ regexPattern += '.*$';
625
+ return new RegExp(regexPattern, 'i');
626
+ }
627
+ #escapeRegexChar(char) {
628
+ return char.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&');
629
+ }
630
+ ngOnDestroy() {
631
+ if (this.openAbortController) {
632
+ this.openAbortController.abort();
633
+ this.openAbortController = null;
634
+ }
635
+ if (typeof MutationObserver !== 'undefined') {
636
+ this.#inputObserver.disconnect();
637
+ }
638
+ }
639
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipSelect, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
640
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: ShipSelect, isStandalone: true, selector: "sh-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, asFreeText: { classPropertyName: "asFreeText", publicName: "asFreeText", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, optionTitle: { classPropertyName: "optionTitle", publicName: "optionTitle", isSignal: true, isRequired: false, transformFunction: null }, freeTextTitle: { classPropertyName: "freeTextTitle", publicName: "freeTextTitle", isSignal: true, isRequired: false, transformFunction: null }, freeTextPlaceholder: { classPropertyName: "freeTextPlaceholder", publicName: "freeTextPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, validateFreeText: { classPropertyName: "validateFreeText", publicName: "validateFreeText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, lazySearch: { classPropertyName: "lazySearch", publicName: "lazySearch", isSignal: true, isRequired: false, transformFunction: null }, inlineSearch: { classPropertyName: "inlineSearch", publicName: "inlineSearch", isSignal: true, isRequired: false, transformFunction: null }, asText: { classPropertyName: "asText", publicName: "asText", isSignal: true, isRequired: false, transformFunction: null }, isClearable: { classPropertyName: "isClearable", publicName: "isClearable", isSignal: true, isRequired: false, transformFunction: null }, selectMultiple: { classPropertyName: "selectMultiple", publicName: "selectMultiple", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, selectedOptionTemplate: { classPropertyName: "selectedOptionTemplate", publicName: "selectedOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, placeholderTemplate: { classPropertyName: "placeholderTemplate", publicName: "placeholderTemplate", isSignal: true, isRequired: false, transformFunction: null }, freeTextOptionTemplate: { classPropertyName: "freeTextOptionTemplate", publicName: "freeTextOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, selectedOptions: { classPropertyName: "selectedOptions", publicName: "selectedOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { readonly: "readonlyChange", disabled: "disabledChange", isOpen: "isOpenChange", isLoading: "isLoadingChange", options: "optionsChange", selectedOptions: "selectedOptionsChange", cleared: "cleared", onAddNewFreeTextOption: "onAddNewFreeTextOption" }, host: { properties: { "class.multiple": "selectMultiple()", "class": "hostClasses()" } }, queries: [{ propertyName: "inlineTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "optionsWrapRef", first: true, predicate: ["optionsWrap"], descendants: true, isSignal: true }], ngImport: i0, template: `
641
+ @let _placeholderTemplate = placeholderTemplate();
642
+ @let _optionTemplate = optionTemplate();
643
+ @let _freeTextOptionTemplate = freeTextOptionTemplate();
644
+ @let _selectedOptionTemplate = selectedOptionTemplate();
645
+ @let _inlineTemplate = inlineTemplate();
646
+ @let _selectedOptions = selectedOptions();
647
+ @let _inputState = inputState();
648
+
649
+ @let _selOptionTemplate = _selectedOptionTemplate || _optionTemplate || _inlineTemplate;
650
+ @let _listOptionTemplate = _optionTemplate || _inlineTemplate;
651
+ @let _asChips = !asText() && selectMultiple();
652
+ @let _showSearchText = isOpen() && hasSearch() && (_asChips || inputValue().length > 0);
653
+
654
+ <sh-popover
655
+ #formFieldWrapper
656
+ [(isOpen)]="isOpen"
657
+ [disableOpenByClick]="true"
658
+ (closed)="close()"
659
+ [options]="{
660
+ closeOnButton: false,
661
+ closeOnEsc: false,
662
+ }">
663
+ <sh-form-field
664
+ trigger
665
+ (click)="open()"
666
+ [color]="color()"
667
+ [variant]="variant()"
668
+ [size]="size()"
669
+ [class.stretch]="stretch()"
670
+ [class.small]="small()"
671
+ [class.readonly]="readonly() || disabled()">
672
+ <ng-content select="label" ngProjectAs="label" />
673
+ <ng-content select="[prefix]" ngProjectAs="[prefix]" />
674
+ <ng-content select="[boxPrefix]" ngProjectAs="[boxPrefix]" />
675
+
676
+ <div class="input" [class.show-search-text]="_showSearchText" ngProjectAs="input">
677
+ <div class="selected-value" [class.is-selected]="_inputState === 'selected'">
678
+ @if (asFreeText() && inputValue().length > 0 && !isOpen()) {
679
+ {{ spacedSelectedOptions() }}
680
+ } @else if (_selectedOptions.length > 0) {
681
+ @for (selectedOption of _selectedOptions; track $index) {
682
+ @if (selectedOption) {
683
+ @if (_asChips) {
684
+ <sh-chip
685
+ [class]="selectClasses()"
686
+ class="small"
687
+ (click)="removeSelectedOptionByIndex($event, $index)">
688
+ @if (_selOptionTemplate) {
689
+ <ng-container
690
+ *ngTemplateOutlet="_selOptionTemplate; context: { $implicit: selectedOption, last: $last }" />
691
+ } @else {
692
+ {{ getLabel(selectedOption) }}
693
+ }
694
+
695
+ <sh-icon>x-bold</sh-icon>
696
+ </sh-chip>
697
+ } @else {
698
+ @if (!_showSearchText) {
699
+ @if (_selOptionTemplate) {
700
+ <ng-container
701
+ *ngTemplateOutlet="_selOptionTemplate; context: { $implicit: selectedOption, last: $last }" />
702
+ } @else {
703
+ {{ $last ? getLabel(selectedOption) : getLabel(selectedOption) + ',' }}
704
+ }
705
+ }
706
+ }
707
+ }
708
+ }
709
+ } @else if (!_showSearchText) {
710
+ @if (_placeholderTemplate) {
711
+ <ng-container *ngTemplateOutlet="_placeholderTemplate" />
712
+ } @else {
713
+ {{ placeholderText() ?? '' }}
714
+ }
715
+ }
716
+
717
+ <ng-content select="input" />
718
+ <ng-content select="textarea" />
719
+ </div>
720
+ </div>
721
+
722
+ @if (_inputState === 'closed') {
723
+ <sh-icon suffix>caret-down</sh-icon>
724
+ } @else if (_inputState === 'loading') {
725
+ <sh-spinner class="primary" suffix></sh-spinner>
726
+ } @else if (_inputState === 'open-searching') {
727
+ <sh-icon suffix>list-magnifying-glass</sh-icon>
728
+ } @else if (_inputState === 'searching') {
729
+ <sh-icon suffix>magnifying-glass</sh-icon>
730
+ } @else if (_inputState === 'selected' && _isClearable()) {
731
+ <sh-icon suffix (click)="clear($event)">x-bold</sh-icon>
732
+ } @else if (_inputState === 'selected' && !_isClearable()) {
733
+ <sh-icon suffix>caret-down</sh-icon>
734
+ } @else {
735
+ <sh-icon suffix>caret-up</sh-icon>
736
+ }
737
+ </sh-form-field>
738
+
739
+ <div class="ship-options" #optionsWrap [id]="'optionsWrapId-' + componentId" role="listbox">
740
+ @if (asFreeText()) {
741
+ @let freeTextOption = computedFreeTextOption();
742
+ @let freeTextOptionValue = getValue(freeTextOption);
743
+
744
+ @if ($any(freeTextOptionValue).length > 0) {
745
+ @if (freeTextTitle()) {
746
+ <p title>{{ freeTextTitle() }}</p>
747
+ }
748
+
749
+ <li
750
+ (click)="toggleOptionByIndex(-1)"
751
+ class="option"
752
+ role="option"
753
+ [id]="this.getOptionId(-1)"
754
+ [attr.aria-selected]="isSelected(-1)"
755
+ [class.selected]="isSelected(-1)"
756
+ [class.focused]="-1 === focusedOptionIndex()">
757
+ @if (_freeTextOptionTemplate) {
758
+ <ng-container *ngTemplateOutlet="_freeTextOptionTemplate; context: { $implicit: freeTextOption }" />
759
+ } @else if (_listOptionTemplate) {
760
+ <ng-container *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: freeTextOption }" />
761
+ } @else {
762
+ {{ freeTextOptionValue }}
763
+ }
764
+ </li>
765
+
766
+ @if (freeTextTitle() && filteredOptions().length > 0) {
767
+ <sh-divider />
768
+ }
769
+ }
770
+ }
771
+
772
+ @if (optionTitle() && filteredOptions().length > 0) {
773
+ <p title>{{ optionTitle() }}</p>
774
+ }
775
+
776
+ @for (option of filteredOptions(); track $index) {
777
+ <li
778
+ (click)="toggleOptionByIndex($index)"
779
+ class="option"
780
+ role="option"
781
+ [id]="this.getOptionId($index)"
782
+ [attr.aria-selected]="isSelected($index)"
783
+ [class.selected]="isSelected($index)"
784
+ [class.focused]="$index === focusedOptionIndex()">
785
+ @if (selectMultiple()) {
786
+ <sh-checkbox [class]="selectClasses()" [checked]="isSelected($index)" />
787
+ }
788
+
789
+ @if (_listOptionTemplate) {
790
+ <ng-container *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: option }" />
791
+ } @else {
792
+ {{ getLabel(option) }}
793
+ }
794
+ </li>
795
+ }
796
+ </div>
797
+ </sh-popover>
798
+ `, isInline: true, styles: ["sh-select{--miw: 13.125rem;--select-option-mih: min-content;--select-options-mh: 11.25rem;display:flex}sh-select sh-form-field{display:flex;--ff-mw: var(--miw)}sh-select sh-popover{flex:1 0}sh-select sh-popover:has(.ship-options:empty) .popover{opacity:0}sh-select .selected-value{width:100%;display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;color:var(--base-8)}sh-select .selected-value input{margin:0}sh-select .selected-value.is-selected{color:var(--base-12)}sh-select sh-form-field .input-wrap .input{position:relative;display:flex;flex-wrap:wrap;--ff-space: .4375rem .625rem}sh-select sh-form-field .input-wrap .input textarea,sh-select sh-form-field .input-wrap .input input{opacity:0;width:0;height:0;position:absolute}sh-select sh-form-field .input-wrap .input.show-search-text input{opacity:1;height:initial;min-width:min-content;field-sizing:content;font:var(--paragraph-30);position:relative}sh-select sh-form-field .input-wrap .input.show-search-text textarea{opacity:1;height:initial;margin:.25rem 0 0}sh-select sh-form-field.small .input-wrap .input{--ff-space: .3125rem .5rem}sh-select sh-form-field.small .input-wrap .input sh-chip{--chip-h: 1.25rem;font-size:80%}.ship-options{padding:.5rem 0;display:flex;flex-direction:column;max-height:var(--select-options-mh);overflow-y:auto;overflow-x:hidden}.ship-options p[title]{padding:.25rem 1rem}.ship-options sh-divider{margin:.5rem 0}.ship-options .option{display:flex;justify-content:flex-start;align-items:center;min-height:var(--select-option-mih);margin:0 .5rem;padding:.5rem;gap:.5rem;width:calc(100% - 1rem);border-radius:var(--shape-2);appearance:none;background-color:transparent;border:0;cursor:pointer;color:var(--base-12);-webkit-user-select:none;user-select:none}.ship-options .option[disabled]{opacity:.3;cursor:initial}.ship-options .option[disabled]:hover{background-color:transparent}.ship-options .option sh-icon{font-size:1rem}.ship-options .option span.placeholder{color:var(--base-7)}.ship-options .option.active,.ship-options .option:checked,.ship-options .option:hover{background-color:var(--base-2);color:var(--base-12)}.ship-options .option.focused{background-color:var(--primary-1);color:var(--base-12)}.ship-options .option.focused:hover{background-color:var(--primary-2)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "asSheetOnMobile", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipFormField, selector: "sh-form-field", inputs: ["color", "variant", "size", "readonly"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }, { kind: "component", type: ShipCheckbox, selector: "sh-checkbox", inputs: ["checked", "color", "variant", "readonly", "disabled", "noInternalInput"], outputs: ["checkedChange"] }, { kind: "component", type: ShipSpinner, selector: "sh-spinner", inputs: ["color"] }, { kind: "component", type: ShipChip, selector: "sh-chip", inputs: ["color", "variant", "size", "sharp", "dynamic", "readonly"] }, { kind: "component", type: ShipDivider, selector: "sh-divider" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
799
+ }
800
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipSelect, decorators: [{
801
+ type: Component,
802
+ args: [{ selector: 'sh-select', encapsulation: ViewEncapsulation.None, imports: [NgTemplateOutlet, ShipPopover, ShipFormField, ShipIcon, ShipCheckbox, ShipSpinner, ShipChip, ShipDivider], template: `
803
+ @let _placeholderTemplate = placeholderTemplate();
804
+ @let _optionTemplate = optionTemplate();
805
+ @let _freeTextOptionTemplate = freeTextOptionTemplate();
806
+ @let _selectedOptionTemplate = selectedOptionTemplate();
807
+ @let _inlineTemplate = inlineTemplate();
808
+ @let _selectedOptions = selectedOptions();
809
+ @let _inputState = inputState();
810
+
811
+ @let _selOptionTemplate = _selectedOptionTemplate || _optionTemplate || _inlineTemplate;
812
+ @let _listOptionTemplate = _optionTemplate || _inlineTemplate;
813
+ @let _asChips = !asText() && selectMultiple();
814
+ @let _showSearchText = isOpen() && hasSearch() && (_asChips || inputValue().length > 0);
815
+
816
+ <sh-popover
817
+ #formFieldWrapper
818
+ [(isOpen)]="isOpen"
819
+ [disableOpenByClick]="true"
820
+ (closed)="close()"
821
+ [options]="{
822
+ closeOnButton: false,
823
+ closeOnEsc: false,
824
+ }">
825
+ <sh-form-field
826
+ trigger
827
+ (click)="open()"
828
+ [color]="color()"
829
+ [variant]="variant()"
830
+ [size]="size()"
831
+ [class.stretch]="stretch()"
832
+ [class.small]="small()"
833
+ [class.readonly]="readonly() || disabled()">
834
+ <ng-content select="label" ngProjectAs="label" />
835
+ <ng-content select="[prefix]" ngProjectAs="[prefix]" />
836
+ <ng-content select="[boxPrefix]" ngProjectAs="[boxPrefix]" />
837
+
838
+ <div class="input" [class.show-search-text]="_showSearchText" ngProjectAs="input">
839
+ <div class="selected-value" [class.is-selected]="_inputState === 'selected'">
840
+ @if (asFreeText() && inputValue().length > 0 && !isOpen()) {
841
+ {{ spacedSelectedOptions() }}
842
+ } @else if (_selectedOptions.length > 0) {
843
+ @for (selectedOption of _selectedOptions; track $index) {
844
+ @if (selectedOption) {
845
+ @if (_asChips) {
846
+ <sh-chip
847
+ [class]="selectClasses()"
848
+ class="small"
849
+ (click)="removeSelectedOptionByIndex($event, $index)">
850
+ @if (_selOptionTemplate) {
851
+ <ng-container
852
+ *ngTemplateOutlet="_selOptionTemplate; context: { $implicit: selectedOption, last: $last }" />
853
+ } @else {
854
+ {{ getLabel(selectedOption) }}
855
+ }
856
+
857
+ <sh-icon>x-bold</sh-icon>
858
+ </sh-chip>
859
+ } @else {
860
+ @if (!_showSearchText) {
861
+ @if (_selOptionTemplate) {
862
+ <ng-container
863
+ *ngTemplateOutlet="_selOptionTemplate; context: { $implicit: selectedOption, last: $last }" />
864
+ } @else {
865
+ {{ $last ? getLabel(selectedOption) : getLabel(selectedOption) + ',' }}
866
+ }
867
+ }
868
+ }
869
+ }
870
+ }
871
+ } @else if (!_showSearchText) {
872
+ @if (_placeholderTemplate) {
873
+ <ng-container *ngTemplateOutlet="_placeholderTemplate" />
874
+ } @else {
875
+ {{ placeholderText() ?? '' }}
876
+ }
877
+ }
878
+
879
+ <ng-content select="input" />
880
+ <ng-content select="textarea" />
881
+ </div>
882
+ </div>
883
+
884
+ @if (_inputState === 'closed') {
885
+ <sh-icon suffix>caret-down</sh-icon>
886
+ } @else if (_inputState === 'loading') {
887
+ <sh-spinner class="primary" suffix></sh-spinner>
888
+ } @else if (_inputState === 'open-searching') {
889
+ <sh-icon suffix>list-magnifying-glass</sh-icon>
890
+ } @else if (_inputState === 'searching') {
891
+ <sh-icon suffix>magnifying-glass</sh-icon>
892
+ } @else if (_inputState === 'selected' && _isClearable()) {
893
+ <sh-icon suffix (click)="clear($event)">x-bold</sh-icon>
894
+ } @else if (_inputState === 'selected' && !_isClearable()) {
895
+ <sh-icon suffix>caret-down</sh-icon>
896
+ } @else {
897
+ <sh-icon suffix>caret-up</sh-icon>
898
+ }
899
+ </sh-form-field>
900
+
901
+ <div class="ship-options" #optionsWrap [id]="'optionsWrapId-' + componentId" role="listbox">
902
+ @if (asFreeText()) {
903
+ @let freeTextOption = computedFreeTextOption();
904
+ @let freeTextOptionValue = getValue(freeTextOption);
905
+
906
+ @if ($any(freeTextOptionValue).length > 0) {
907
+ @if (freeTextTitle()) {
908
+ <p title>{{ freeTextTitle() }}</p>
909
+ }
910
+
911
+ <li
912
+ (click)="toggleOptionByIndex(-1)"
913
+ class="option"
914
+ role="option"
915
+ [id]="this.getOptionId(-1)"
916
+ [attr.aria-selected]="isSelected(-1)"
917
+ [class.selected]="isSelected(-1)"
918
+ [class.focused]="-1 === focusedOptionIndex()">
919
+ @if (_freeTextOptionTemplate) {
920
+ <ng-container *ngTemplateOutlet="_freeTextOptionTemplate; context: { $implicit: freeTextOption }" />
921
+ } @else if (_listOptionTemplate) {
922
+ <ng-container *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: freeTextOption }" />
923
+ } @else {
924
+ {{ freeTextOptionValue }}
925
+ }
926
+ </li>
927
+
928
+ @if (freeTextTitle() && filteredOptions().length > 0) {
929
+ <sh-divider />
930
+ }
931
+ }
932
+ }
933
+
934
+ @if (optionTitle() && filteredOptions().length > 0) {
935
+ <p title>{{ optionTitle() }}</p>
936
+ }
937
+
938
+ @for (option of filteredOptions(); track $index) {
939
+ <li
940
+ (click)="toggleOptionByIndex($index)"
941
+ class="option"
942
+ role="option"
943
+ [id]="this.getOptionId($index)"
944
+ [attr.aria-selected]="isSelected($index)"
945
+ [class.selected]="isSelected($index)"
946
+ [class.focused]="$index === focusedOptionIndex()">
947
+ @if (selectMultiple()) {
948
+ <sh-checkbox [class]="selectClasses()" [checked]="isSelected($index)" />
949
+ }
950
+
951
+ @if (_listOptionTemplate) {
952
+ <ng-container *ngTemplateOutlet="_listOptionTemplate; context: { $implicit: option }" />
953
+ } @else {
954
+ {{ getLabel(option) }}
955
+ }
956
+ </li>
957
+ }
958
+ </div>
959
+ </sh-popover>
960
+ `, host: {
961
+ '[class.multiple]': 'selectMultiple()',
962
+ '[class]': 'hostClasses()',
963
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["sh-select{--miw: 13.125rem;--select-option-mih: min-content;--select-options-mh: 11.25rem;display:flex}sh-select sh-form-field{display:flex;--ff-mw: var(--miw)}sh-select sh-popover{flex:1 0}sh-select sh-popover:has(.ship-options:empty) .popover{opacity:0}sh-select .selected-value{width:100%;display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;color:var(--base-8)}sh-select .selected-value input{margin:0}sh-select .selected-value.is-selected{color:var(--base-12)}sh-select sh-form-field .input-wrap .input{position:relative;display:flex;flex-wrap:wrap;--ff-space: .4375rem .625rem}sh-select sh-form-field .input-wrap .input textarea,sh-select sh-form-field .input-wrap .input input{opacity:0;width:0;height:0;position:absolute}sh-select sh-form-field .input-wrap .input.show-search-text input{opacity:1;height:initial;min-width:min-content;field-sizing:content;font:var(--paragraph-30);position:relative}sh-select sh-form-field .input-wrap .input.show-search-text textarea{opacity:1;height:initial;margin:.25rem 0 0}sh-select sh-form-field.small .input-wrap .input{--ff-space: .3125rem .5rem}sh-select sh-form-field.small .input-wrap .input sh-chip{--chip-h: 1.25rem;font-size:80%}.ship-options{padding:.5rem 0;display:flex;flex-direction:column;max-height:var(--select-options-mh);overflow-y:auto;overflow-x:hidden}.ship-options p[title]{padding:.25rem 1rem}.ship-options sh-divider{margin:.5rem 0}.ship-options .option{display:flex;justify-content:flex-start;align-items:center;min-height:var(--select-option-mih);margin:0 .5rem;padding:.5rem;gap:.5rem;width:calc(100% - 1rem);border-radius:var(--shape-2);appearance:none;background-color:transparent;border:0;cursor:pointer;color:var(--base-12);-webkit-user-select:none;user-select:none}.ship-options .option[disabled]{opacity:.3;cursor:initial}.ship-options .option[disabled]:hover{background-color:transparent}.ship-options .option sh-icon{font-size:1rem}.ship-options .option span.placeholder{color:var(--base-7)}.ship-options .option.active,.ship-options .option:checked,.ship-options .option:hover{background-color:var(--base-2);color:var(--base-12)}.ship-options .option.focused{background-color:var(--primary-1);color:var(--base-12)}.ship-options .option.focused:hover{background-color:var(--primary-2)}\n"] }]
964
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], asFreeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "asFreeText", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], optionTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionTitle", required: false }] }], freeTextTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeTextTitle", required: false }] }], freeTextPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeTextPlaceholder", required: false }] }], validateFreeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "validateFreeText", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }, { type: i0.Output, args: ["readonlyChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], lazySearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazySearch", required: false }] }], inlineSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "inlineSearch", required: false }] }], asText: [{ type: i0.Input, args: [{ isSignal: true, alias: "asText", required: false }] }], isClearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "isClearable", required: false }] }], selectMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectMultiple", required: false }] }], optionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionTemplate", required: false }] }], selectedOptionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedOptionTemplate", required: false }] }], placeholderTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderTemplate", required: false }] }], freeTextOptionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeTextOptionTemplate", required: false }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }, { type: i0.Output, args: ["isLoadingChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }, { type: i0.Output, args: ["optionsChange"] }], selectedOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedOptions", required: false }] }, { type: i0.Output, args: ["selectedOptionsChange"] }], cleared: [{ type: i0.Output, args: ["cleared"] }], onAddNewFreeTextOption: [{ type: i0.Output, args: ["onAddNewFreeTextOption"] }], inlineTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }], optionsWrapRef: [{ type: i0.ViewChild, args: ['optionsWrap', { isSignal: true }] }] } });
965
+
966
+ /**
967
+ * Generated bundle index. Do not edit.
968
+ */
969
+
970
+ export { ShipSelect };
971
+ //# sourceMappingURL=ship-ui-core-ship-select.mjs.map