@koobiq/components 20.1.0 → 20.3.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 (249) hide show
  1. package/accordion/_accordion-theme.scss +6 -2
  2. package/accordion/accordion-tokens.scss +3 -0
  3. package/accordion/{accordion-trigger.component.scss → accordion-trigger.scss} +10 -0
  4. package/accordion/accordion.scss +71 -0
  5. package/accordion/index.d.ts +264 -121
  6. package/actions-panel/index.d.ts +25 -13
  7. package/alert/_alert-theme.scss +14 -0
  8. package/alert/alert.component.scss +24 -13
  9. package/alert/index.d.ts +63 -29
  10. package/app-switcher/app-switcher-dropdown-app.scss +7 -5
  11. package/app-switcher/app-switcher-dropdown-site.scss +6 -4
  12. package/app-switcher/index.d.ts +41 -4
  13. package/app-switcher/kbq-app-switcher-list-item.scss +6 -8
  14. package/autocomplete/autocomplete.scss +2 -1
  15. package/autocomplete/index.d.ts +58 -37
  16. package/badge/index.d.ts +2 -3
  17. package/breadcrumbs/index.d.ts +1 -1
  18. package/button/index.d.ts +2 -2
  19. package/button-toggle/button-toggle-tokens.scss +2 -0
  20. package/button-toggle/button-toggle.scss +9 -0
  21. package/button-toggle/index.d.ts +5 -0
  22. package/checkbox/index.d.ts +33 -29
  23. package/clamped-text/index.d.ts +3 -2
  24. package/code-block/index.d.ts +24 -19
  25. package/content-panel/index.d.ts +1 -1
  26. package/core/index.d.ts +1217 -186
  27. package/core/option/_action-container-mixins.scss +28 -0
  28. package/core/option/_option-theme.scss +24 -0
  29. package/core/option/action.scss +7 -0
  30. package/core/styles/_core.scss +2 -0
  31. package/core/styles/_koobiq-theme.scss +2 -0
  32. package/core/styles/_tokens.scss +1 -0
  33. package/core/styles/common/_animation.scss +31 -24
  34. package/core/styles/common/_connected-overlay.scss +39 -0
  35. package/core/styles/common/_select.scss +19 -2
  36. package/core/styles/common/_tokens.scss +0 -9
  37. package/core/styles/theming/_index.scss +0 -1
  38. package/core/styles/theming/_theming.scss +34 -5
  39. package/datepicker/index.d.ts +46 -15
  40. package/dl/dl.scss +26 -0
  41. package/dl/index.d.ts +8 -1
  42. package/dropdown/_dropdown-theme.scss +28 -2
  43. package/dropdown/dropdown-item.scss +29 -0
  44. package/dropdown/dropdown-tokens.scss +2 -1
  45. package/dropdown/dropdown.scss +16 -1
  46. package/dropdown/index.d.ts +331 -17
  47. package/dynamic-translation/index.d.ts +1 -1
  48. package/fesm2022/koobiq-components-accordion.mjs +480 -317
  49. package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
  50. package/fesm2022/koobiq-components-actions-panel.mjs +57 -46
  51. package/fesm2022/koobiq-components-actions-panel.mjs.map +1 -1
  52. package/fesm2022/koobiq-components-alert.mjs +98 -58
  53. package/fesm2022/koobiq-components-alert.mjs.map +1 -1
  54. package/fesm2022/koobiq-components-app-switcher.mjs +233 -38
  55. package/fesm2022/koobiq-components-app-switcher.mjs.map +1 -1
  56. package/fesm2022/koobiq-components-autocomplete.mjs +89 -52
  57. package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
  58. package/fesm2022/koobiq-components-badge.mjs +18 -17
  59. package/fesm2022/koobiq-components-badge.mjs.map +1 -1
  60. package/fesm2022/koobiq-components-breadcrumbs.mjs +25 -25
  61. package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
  62. package/fesm2022/koobiq-components-button-toggle.mjs +17 -12
  63. package/fesm2022/koobiq-components-button-toggle.mjs.map +1 -1
  64. package/fesm2022/koobiq-components-button.mjs +25 -25
  65. package/fesm2022/koobiq-components-checkbox.mjs +69 -92
  66. package/fesm2022/koobiq-components-checkbox.mjs.map +1 -1
  67. package/fesm2022/koobiq-components-clamped-text.mjs +18 -13
  68. package/fesm2022/koobiq-components-clamped-text.mjs.map +1 -1
  69. package/fesm2022/koobiq-components-code-block.mjs +56 -69
  70. package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
  71. package/fesm2022/koobiq-components-content-panel.mjs +29 -29
  72. package/fesm2022/koobiq-components-core.mjs +2485 -735
  73. package/fesm2022/koobiq-components-core.mjs.map +1 -1
  74. package/fesm2022/koobiq-components-datepicker.mjs +168 -126
  75. package/fesm2022/koobiq-components-datepicker.mjs.map +1 -1
  76. package/fesm2022/koobiq-components-divider.mjs +7 -7
  77. package/fesm2022/koobiq-components-dl.mjs +23 -15
  78. package/fesm2022/koobiq-components-dl.mjs.map +1 -1
  79. package/fesm2022/koobiq-components-dropdown.mjs +814 -136
  80. package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
  81. package/fesm2022/koobiq-components-dynamic-translation.mjs +13 -13
  82. package/fesm2022/koobiq-components-ellipsis-center.mjs +7 -7
  83. package/fesm2022/koobiq-components-empty-state.mjs +19 -19
  84. package/fesm2022/koobiq-components-file-upload.mjs +74 -42
  85. package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
  86. package/fesm2022/koobiq-components-filter-bar.mjs +783 -199
  87. package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
  88. package/fesm2022/koobiq-components-flag.mjs +7 -7
  89. package/fesm2022/koobiq-components-form-field.mjs +350 -162
  90. package/fesm2022/koobiq-components-form-field.mjs.map +1 -1
  91. package/fesm2022/koobiq-components-icon.mjs +52 -25
  92. package/fesm2022/koobiq-components-icon.mjs.map +1 -1
  93. package/fesm2022/koobiq-components-inline-edit.mjs +175 -28
  94. package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
  95. package/fesm2022/koobiq-components-input.mjs +41 -24
  96. package/fesm2022/koobiq-components-input.mjs.map +1 -1
  97. package/fesm2022/koobiq-components-link.mjs +8 -8
  98. package/fesm2022/koobiq-components-list.mjs +657 -143
  99. package/fesm2022/koobiq-components-list.mjs.map +1 -1
  100. package/fesm2022/koobiq-components-loader-overlay.mjs +16 -16
  101. package/fesm2022/koobiq-components-markdown.mjs +14 -11
  102. package/fesm2022/koobiq-components-markdown.mjs.map +1 -1
  103. package/fesm2022/koobiq-components-modal.mjs +59 -35
  104. package/fesm2022/koobiq-components-modal.mjs.map +1 -1
  105. package/fesm2022/koobiq-components-navbar.mjs +177 -67
  106. package/fesm2022/koobiq-components-navbar.mjs.map +1 -1
  107. package/fesm2022/koobiq-components-notification-center.mjs +40 -24
  108. package/fesm2022/koobiq-components-notification-center.mjs.map +1 -1
  109. package/fesm2022/koobiq-components-overflow-items.mjs +16 -17
  110. package/fesm2022/koobiq-components-overflow-items.mjs.map +1 -1
  111. package/fesm2022/koobiq-components-popover.mjs +31 -21
  112. package/fesm2022/koobiq-components-popover.mjs.map +1 -1
  113. package/fesm2022/koobiq-components-progress-bar.mjs +13 -13
  114. package/fesm2022/koobiq-components-progress-spinner.mjs +13 -13
  115. package/fesm2022/koobiq-components-radio.mjs +10 -10
  116. package/fesm2022/koobiq-components-resizer.mjs +33 -14
  117. package/fesm2022/koobiq-components-resizer.mjs.map +1 -1
  118. package/fesm2022/koobiq-components-scrollbar.mjs +10 -10
  119. package/fesm2022/koobiq-components-search-expandable.mjs +49 -14
  120. package/fesm2022/koobiq-components-search-expandable.mjs.map +1 -1
  121. package/fesm2022/koobiq-components-select.mjs +450 -146
  122. package/fesm2022/koobiq-components-select.mjs.map +1 -1
  123. package/fesm2022/koobiq-components-sidebar.mjs +14 -14
  124. package/fesm2022/koobiq-components-sidepanel.mjs +26 -26
  125. package/fesm2022/koobiq-components-skeleton.mjs +10 -10
  126. package/fesm2022/koobiq-components-split-button.mjs +10 -17
  127. package/fesm2022/koobiq-components-split-button.mjs.map +1 -1
  128. package/fesm2022/koobiq-components-splitter.mjs +17 -17
  129. package/fesm2022/koobiq-components-table.mjs +15 -13
  130. package/fesm2022/koobiq-components-table.mjs.map +1 -1
  131. package/fesm2022/koobiq-components-tabs.mjs +80 -68
  132. package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
  133. package/fesm2022/koobiq-components-tags.mjs +283 -122
  134. package/fesm2022/koobiq-components-tags.mjs.map +1 -1
  135. package/fesm2022/koobiq-components-textarea.mjs +13 -8
  136. package/fesm2022/koobiq-components-textarea.mjs.map +1 -1
  137. package/fesm2022/koobiq-components-time-range.mjs +29 -24
  138. package/fesm2022/koobiq-components-time-range.mjs.map +1 -1
  139. package/fesm2022/koobiq-components-timepicker.mjs +61 -30
  140. package/fesm2022/koobiq-components-timepicker.mjs.map +1 -1
  141. package/fesm2022/koobiq-components-timezone.mjs +77 -42
  142. package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
  143. package/fesm2022/koobiq-components-title.mjs +7 -7
  144. package/fesm2022/koobiq-components-toast.mjs +51 -26
  145. package/fesm2022/koobiq-components-toast.mjs.map +1 -1
  146. package/fesm2022/koobiq-components-toggle.mjs +72 -64
  147. package/fesm2022/koobiq-components-toggle.mjs.map +1 -1
  148. package/fesm2022/koobiq-components-tooltip.mjs +219 -28
  149. package/fesm2022/koobiq-components-tooltip.mjs.map +1 -1
  150. package/fesm2022/koobiq-components-top-bar.mjs +13 -13
  151. package/fesm2022/koobiq-components-tree-select.mjs +222 -80
  152. package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
  153. package/fesm2022/koobiq-components-tree.mjs +302 -81
  154. package/fesm2022/koobiq-components-tree.mjs.map +1 -1
  155. package/fesm2022/koobiq-components-username.mjs +27 -27
  156. package/file-upload/index.d.ts +25 -6
  157. package/file-upload/multiple-file-upload.component.scss +14 -0
  158. package/filter-bar/_filter-bar-theme.scss +0 -10
  159. package/filter-bar/filter-bar-tokens.scss +3 -0
  160. package/filter-bar/filters.scss +3 -2
  161. package/filter-bar/index.d.ts +281 -9
  162. package/filter-bar/pipes/base-pipe.scss +0 -11
  163. package/filter-bar/pipes/pipe-input.scss +10 -0
  164. package/filter-bar/pipes/pipe-multi-tree-select.scss +0 -6
  165. package/filter-bar/pipes/pipe-multiselect.scss +0 -6
  166. package/filter-bar/pipes/pipe-tree-select.scss +0 -6
  167. package/form-field/_form-field-theme.scss +76 -20
  168. package/form-field/fieldset.scss +10 -0
  169. package/form-field/form-field-tokens.scss +15 -1
  170. package/form-field/form-field.scss +15 -19
  171. package/form-field/index.d.ts +147 -65
  172. package/icon/icon-button.scss +3 -1
  173. package/icon/index.d.ts +45 -7
  174. package/inline-edit/index.d.ts +57 -4
  175. package/inline-edit/inline-edit.scss +10 -0
  176. package/input/index.d.ts +17 -2
  177. package/list/_list-theme.scss +42 -10
  178. package/list/index.d.ts +326 -79
  179. package/list/list-tokens.scss +43 -8
  180. package/list/list.scss +186 -12
  181. package/markdown/index.d.ts +1 -1
  182. package/modal/index.d.ts +21 -4
  183. package/modal/modal-tokens.scss +1 -1
  184. package/navbar/_navbar-item_vertical.scss +7 -5
  185. package/navbar/index.d.ts +54 -5
  186. package/navbar/navbar-brand.scss +23 -7
  187. package/navbar/vertical-navbar.scss +6 -0
  188. package/notification-center/index.d.ts +2 -1
  189. package/notification-center/notification-item.scss +6 -0
  190. package/package.json +3 -6
  191. package/popover/index.d.ts +2 -2
  192. package/prebuilt-themes/dark-theme.css +1 -1
  193. package/prebuilt-themes/light-theme.css +1 -1
  194. package/prebuilt-themes/theme.css +1 -1
  195. package/resizer/index.d.ts +12 -0
  196. package/schematics/collection.json +10 -0
  197. package/schematics/migrations/alert-signals/README.md +64 -0
  198. package/schematics/migrations/alert-signals/data.js +70 -0
  199. package/schematics/migrations/alert-signals/index.js +350 -0
  200. package/schematics/migrations/alert-signals/schema.json +20 -0
  201. package/schematics/migrations/autocomplete-panel-width-auto/README.md +68 -0
  202. package/schematics/migrations/autocomplete-panel-width-auto/index.js +70 -0
  203. package/schematics/migrations/autocomplete-panel-width-auto/schema.json +15 -0
  204. package/schematics/migrations/deprecated-icons/README.md +29 -4
  205. package/schematics/migrations/deprecated-icons/data.js +7 -0
  206. package/schematics/migrations/deprecated-icons/index.js +62 -35
  207. package/schematics/migrations/filter-bar-signals/README.md +1 -1
  208. package/schematics/migrations/new-icons-pack/README.md +37 -2
  209. package/schematics/migrations/new-icons-pack/data.js +8 -0
  210. package/schematics/migrations/new-icons-pack/index.js +144 -53
  211. package/schematics/migrations.json +11 -1
  212. package/schematics/ng-add/index.js +3 -3
  213. package/schematics/utils/angular-parsing.js +41 -14
  214. package/schematics/utils/ast.js +31 -0
  215. package/schematics/utils/icon-migration.js +530 -0
  216. package/schematics/utils/typescript.js +44 -0
  217. package/scrollbar/index.d.ts +1 -1
  218. package/search-expandable/index.d.ts +3 -0
  219. package/select/index.d.ts +212 -40
  220. package/select/select-tokens.scss +0 -1
  221. package/select/select.scss +0 -2
  222. package/splitter/index.d.ts +1 -1
  223. package/table/_table-theme.scss +4 -0
  224. package/table/index.d.ts +2 -1
  225. package/table/table.scss +8 -0
  226. package/tabs/_tabs-common.scss +60 -31
  227. package/tabs/_tabs-theme.scss +12 -0
  228. package/tabs/index.d.ts +2 -2
  229. package/tabs/tab-header.scss +14 -0
  230. package/tabs/tab-nav-bar.scss +4 -0
  231. package/tabs/tabs-tokens.scss +1 -0
  232. package/tags/index.d.ts +128 -29
  233. package/textarea/index.d.ts +5 -0
  234. package/time-range/index.d.ts +1 -1
  235. package/timepicker/index.d.ts +4 -0
  236. package/timezone/index.d.ts +10 -8
  237. package/toast/index.d.ts +26 -3
  238. package/toggle/index.d.ts +34 -14
  239. package/tooltip/index.d.ts +119 -7
  240. package/tree/_tree-theme.scss +25 -5
  241. package/tree/index.d.ts +71 -3
  242. package/tree/tree-option.scss +2 -8
  243. package/tree-select/_tree-select-theme.scss +22 -0
  244. package/tree-select/index.d.ts +127 -36
  245. package/accordion/accordion.component.scss +0 -50
  246. package/core/styles/_tokens.kbq.import.scss +0 -4
  247. package/core/styles/theming/_components-theming.scss +0 -1927
  248. package/filter-bar/pipes/pipe-select.scss +0 -5
  249. package/overflow-items/overflow-items.scss +0 -20
@@ -1,25 +1,22 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, Directive, inject, ChangeDetectorRef, DestroyRef, ElementRef, input, booleanAttribute, contentChild, output, signal, contentChildren, forwardRef, Input, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, ContentChildren, NgModule } from '@angular/core';
3
1
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
+ import * as i0 from '@angular/core';
3
+ import { Directive, inject, ChangeDetectorRef, DestroyRef, ElementRef, input, booleanAttribute, contentChild, output, signal, contentChildren, forwardRef, Input, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, computed, ContentChildren, InjectionToken, NgModule } from '@angular/core';
4
4
  import { NgForm, FormGroupDirective, NgControl } from '@angular/forms';
5
5
  import { KbqAutocompleteTrigger } from '@koobiq/components/autocomplete';
6
6
  import * as i1 from '@koobiq/components/core';
7
- import { KbqFieldSizingContent, KbqColorDirective, KbqComponentColors, hasModifierKey, isNull, KBQ_TITLE_TEXT_REF, ErrorStateMatcher, FocusKeyManager, isSelectAll, SEMICOLON, COMMA, SPACE as SPACE$1, TAB as TAB$1, ENTER as ENTER$1 } from '@koobiq/components/core';
8
- import { KbqFormFieldControl, KbqTrim } from '@koobiq/components/form-field';
7
+ import { KbqFieldSizingContent, KbqColorDirective, KbqComponentColors, hasModifierKey, isNull, KBQ_TITLE_TEXT_REF, ErrorStateMatcher, FocusKeyManager, isSelectAll, ENTER as ENTER$1, TAB as TAB$1, SPACE as SPACE$1, COMMA, SEMICOLON, kbqInjectAutofilled } from '@koobiq/components/core';
8
+ import { KbqCleaner, KbqFormFieldControl, KBQ_CLEANER_CONTEXT, KbqTrim } from '@koobiq/components/form-field';
9
9
  import { FocusMonitor } from '@angular/cdk/a11y';
10
10
  import { Directionality } from '@angular/cdk/bidi';
11
11
  import * as i2 from '@angular/cdk/drag-drop';
12
12
  import { CdkDrag, CdkDropList } from '@angular/cdk/drag-drop';
13
- import { DELETE, SPACE, BACKSPACE, ENTER, ESCAPE, F2, LEFT_ARROW, TAB, HOME, END } from '@angular/cdk/keycodes';
13
+ import { DELETE, SPACE, BACKSPACE, ENTER, ESCAPE, F2, LEFT_ARROW, RIGHT_ARROW, TAB, HOME, END } from '@angular/cdk/keycodes';
14
14
  import { takeUntilDestroyed, outputToObservable } from '@angular/core/rxjs-interop';
15
15
  import { Subject, merge } from 'rxjs';
16
16
  import { startWith, takeUntil, filter } from 'rxjs/operators';
17
17
  import { KbqIcon } from '@koobiq/components/icon';
18
18
  import { PlatformModule } from '@angular/cdk/platform';
19
19
 
20
- /** Injection token to be used to override the default options for the chips module. */
21
- const KBQ_TAGS_DEFAULT_OPTIONS = new InjectionToken('kbq-tags-default-options');
22
-
23
20
  /**
24
21
  * Event object emitted by KbqTag when the tag is edited.
25
22
  */
@@ -47,10 +44,10 @@ const getTagEditInputMissingError = () => {
47
44
  * @docs-private
48
45
  */
49
46
  class KbqTagAvatar {
50
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagAvatar, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
51
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqTagAvatar, isStandalone: true, selector: "kbq-tag-avatar, [kbqTagAvatar]", host: { classAttribute: "kbq-tag-avatar" }, ngImport: i0 }); }
47
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagAvatar, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
48
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqTagAvatar, isStandalone: true, selector: "kbq-tag-avatar, [kbqTagAvatar]", host: { classAttribute: "kbq-tag-avatar" }, ngImport: i0 }); }
52
49
  }
53
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagAvatar, decorators: [{
50
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagAvatar, decorators: [{
54
51
  type: Directive,
55
52
  args: [{
56
53
  selector: 'kbq-tag-avatar, [kbqTagAvatar]',
@@ -62,10 +59,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
62
59
  * @docs-private
63
60
  */
64
61
  class KbqTagTrailingIcon {
65
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagTrailingIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
66
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqTagTrailingIcon, isStandalone: true, selector: "kbq-tag-trailing-icon, [kbqTagTrailingIcon]", host: { classAttribute: "kbq-tag-trailing-icon" }, ngImport: i0 }); }
62
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagTrailingIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
63
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqTagTrailingIcon, isStandalone: true, selector: "kbq-tag-trailing-icon, [kbqTagTrailingIcon]", host: { classAttribute: "kbq-tag-trailing-icon" }, ngImport: i0 }); }
67
64
  }
68
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagTrailingIcon, decorators: [{
65
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagTrailingIcon, decorators: [{
69
66
  type: Directive,
70
67
  args: [{
71
68
  selector: 'kbq-tag-trailing-icon, [kbqTagTrailingIcon]',
@@ -80,10 +77,10 @@ class KbqTagEditSubmit {
80
77
  /** @docs-private */
81
78
  this.tag = inject(KbqTag);
82
79
  }
83
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagEditSubmit, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
84
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqTagEditSubmit, isStandalone: true, selector: "[kbqTagEditSubmit]", host: { listeners: { "click": "tag.submitEditing(\"click\")" }, properties: { "attr.tabindex": "-1" }, classAttribute: "kbq-tag-edit-submit" }, exportAs: ["kbqTagEditSubmit"], ngImport: i0 }); }
80
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagEditSubmit, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
81
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqTagEditSubmit, isStandalone: true, selector: "[kbqTagEditSubmit]", host: { listeners: { "click": "tag.submitEditing(\"click\")" }, properties: { "attr.tabindex": "-1" }, classAttribute: "kbq-tag-edit-submit" }, exportAs: ["kbqTagEditSubmit"], ngImport: i0 }); }
85
82
  }
86
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagEditSubmit, decorators: [{
83
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagEditSubmit, decorators: [{
87
84
  type: Directive,
88
85
  args: [{
89
86
  selector: '[kbqTagEditSubmit]',
@@ -129,10 +126,10 @@ class KbqTagEditInput {
129
126
  default:
130
127
  }
131
128
  }
132
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagEditInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
133
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqTagEditInput, isStandalone: true, selector: "[kbqTagEditInput]", host: { listeners: { "keydown": "handleKeydown($event)", "click": "handleClick($event)" }, classAttribute: "kbq-tag-edit-input" }, exportAs: ["kbqTagEditInput"], hostDirectives: [{ directive: i1.KbqFieldSizingContent }], ngImport: i0 }); }
129
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagEditInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
130
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqTagEditInput, isStandalone: true, selector: "[kbqTagEditInput]", host: { listeners: { "keydown": "handleKeydown($event)", "click": "handleClick($event)" }, classAttribute: "kbq-tag-edit-input" }, exportAs: ["kbqTagEditInput"], hostDirectives: [{ directive: i1.KbqFieldSizingContent }], ngImport: i0 }); }
134
131
  }
135
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagEditInput, decorators: [{
132
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagEditInput, decorators: [{
136
133
  type: Directive,
137
134
  args: [{
138
135
  selector: '[kbqTagEditInput]',
@@ -396,16 +393,21 @@ class KbqTag extends KbqColorDirective {
396
393
  return;
397
394
  }
398
395
  if (
399
- // We should toggle selection only if tag inside of a tag list.
396
+ // We should handle modifier-based selection only if tag is inside of a tag list.
400
397
  // Single tag can only be toggled on focus or blur.
401
398
  this.tagList &&
402
399
  this.selectable &&
403
400
  hasModifierKey(event, 'metaKey', 'ctrlKey', 'shiftKey')) {
404
- this.toggleSelected(true);
401
+ if (!event.shiftKey)
402
+ this.toggleSelected(true);
403
+ this.tagList.handleSelectionInteraction(this, event.shiftKey);
405
404
  // We should stop event propagation to prevent the tag list from handling the click event.
406
405
  event.stopPropagation();
407
406
  return;
408
407
  }
408
+ if (this.tagList && this.selectable) {
409
+ this.tagList.handleSelectionInteraction(this, false);
410
+ }
409
411
  if (!this.tagList && this.selectable) {
410
412
  this.toggleSelected(true);
411
413
  }
@@ -429,7 +431,13 @@ class KbqTag extends KbqColorDirective {
429
431
  break;
430
432
  }
431
433
  case SPACE: {
432
- this.toggleSelected(true);
434
+ if (this.tagList) {
435
+ this.toggleSelected(true);
436
+ this.tagList.handleSelectionInteraction(this, false);
437
+ }
438
+ else {
439
+ this.toggleSelected(true);
440
+ }
433
441
  this.focusMonitor.focusVia(this.elementRef, 'keyboard');
434
442
  // Always prevent space from scrolling the page since the list has focus
435
443
  event.preventDefault();
@@ -532,8 +540,8 @@ class KbqTag extends KbqColorDirective {
532
540
  }
533
541
  });
534
542
  }
535
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTag, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
536
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: KbqTag, isStandalone: true, selector: "kbq-tag, [kbq-tag], kbq-basic-tag, [kbq-basic-tag]", inputs: { editable: { classPropertyName: "editable", publicName: "editable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, preventEditSubmit: { classPropertyName: "preventEditSubmit", publicName: "preventEditSubmit", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute } }, outputs: { editChange: "editChange", selectionChange: "selectionChange", destroyed: "destroyed", removed: "removed" }, host: { listeners: { "dblclick": "handleDblClick($event)", "click": "handleClick($event)", "keydown": "handleKeydown($event)" }, properties: { "attr.tabindex": "tabindex", "attr.disabled": "disabled || null", "class.kbq-selected": "selected", "class.kbq-tag-with-avatar": "avatar()", "class.kbq-tag-with-icon": "contentChildren()", "class.kbq-tag-with-trailing-icon": "trailingIcon() || removeIcon()", "class.kbq-disabled": "disabled", "class.kbq-tag_editable": "editable", "class.kbq-tag_editing": "editing()", "class.kbq-tag_removable": "removable", "class.kbq-tag_selectable": "selectable", "class.kbq-tag_draggable": "draggable" }, classAttribute: "kbq-tag" }, providers: [{ provide: KBQ_TITLE_TEXT_REF, useExisting: KbqTag }], queries: [{ propertyName: "editInputElementRef", first: true, predicate: KbqTagEditInput, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "contentChildren", predicate: KbqIcon, isSignal: true }, { propertyName: "avatar", first: true, predicate: KbqTagAvatar, descendants: true, isSignal: true }, { propertyName: "trailingIcon", first: true, predicate: KbqTagTrailingIcon, descendants: true, isSignal: true }, { propertyName: "removeIcon", first: true, predicate: i0.forwardRef(() => KbqTagRemove), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "textElement", first: true, predicate: ["kbqTitleText"], descendants: true }], exportAs: ["kbqTag"], usesInheritance: true, hostDirectives: [{ directive: i2.CdkDrag }], ngImport: i0, template: `
543
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTag, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
544
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: KbqTag, isStandalone: true, selector: "kbq-tag, [kbq-tag], kbq-basic-tag, [kbq-basic-tag]", inputs: { editable: { classPropertyName: "editable", publicName: "editable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, preventEditSubmit: { classPropertyName: "preventEditSubmit", publicName: "preventEditSubmit", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute } }, outputs: { editChange: "editChange", selectionChange: "selectionChange", destroyed: "destroyed", removed: "removed" }, host: { listeners: { "dblclick": "handleDblClick($event)", "click": "handleClick($event)", "keydown": "handleKeydown($event)" }, properties: { "attr.tabindex": "tabindex", "attr.disabled": "disabled || null", "class.kbq-selected": "selected", "class.kbq-tag-with-avatar": "avatar()", "class.kbq-tag-with-icon": "contentChildren()", "class.kbq-tag-with-trailing-icon": "trailingIcon() || removeIcon()", "class.kbq-disabled": "disabled", "class.kbq-tag_editable": "editable", "class.kbq-tag_editing": "editing()", "class.kbq-tag_removable": "removable", "class.kbq-tag_selectable": "selectable", "class.kbq-tag_draggable": "draggable" }, classAttribute: "kbq-tag" }, providers: [{ provide: KBQ_TITLE_TEXT_REF, useExisting: KbqTag }], queries: [{ propertyName: "editInputElementRef", first: true, predicate: KbqTagEditInput, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "contentChildren", predicate: KbqIcon, isSignal: true }, { propertyName: "avatar", first: true, predicate: KbqTagAvatar, descendants: true, isSignal: true }, { propertyName: "trailingIcon", first: true, predicate: KbqTagTrailingIcon, descendants: true, isSignal: true }, { propertyName: "removeIcon", first: true, predicate: i0.forwardRef(() => KbqTagRemove), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "textElement", first: true, predicate: ["kbqTitleText"], descendants: true }], exportAs: ["kbqTag"], usesInheritance: true, hostDirectives: [{ directive: i2.CdkDrag }], ngImport: i0, template: `
537
545
  <div class="kbq-tag__wrapper">
538
546
  <ng-content select="[kbq-icon]:not([kbqTagRemove]):not([kbqTagEditSubmit])" />
539
547
  <span #kbqTitleText class="kbq-tag__text">
@@ -553,7 +561,7 @@ class KbqTag extends KbqColorDirective {
553
561
  </div>
554
562
  `, isInline: true, styles: [".kbq-tag{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;display:inline-block;max-width:100%;height:var(--kbq-size-xxl);padding:var(--kbq-tag-size-padding-vertical) var(--kbq-tag-size-padding-horizontal);border-radius:4px;cursor:default;outline:none;box-sizing:border-box}.kbq-tag .kbq-icon.kbq-tag-remove{display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:var(--kbq-tag-size-close-button-margin-right)}.kbq-tag .kbq-icon.kbq-tag-remove:hover{cursor:pointer}.kbq-tag.kbq-disabled .kbq-icon.kbq-tag-remove:hover{cursor:default}.kbq-tag .kbq-tag__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;margin:0 var(--kbq-tag-size-content-gap-horizontal)}.kbq-tag .kbq-icon_left{margin-left:var(--kbq-tag-size-icon-margin-left)}.kbq-tag .kbq-tag-edit-input{background:transparent;padding:0;margin:0;border:none;outline:none}.kbq-tag .kbq-tag-edit-input::-ms-clear{display:none;width:0;height:0}.kbq-tag .kbq-tag-edit-input::-ms-reveal{display:none;width:0;height:0}.kbq-tag .kbq-tag-edit-input::-webkit-search-decoration,.kbq-tag .kbq-tag-edit-input::-webkit-search-cancel-button,.kbq-tag .kbq-tag-edit-input::-webkit-search-results-button,.kbq-tag .kbq-tag-edit-input::-webkit-search-results-decoration{display:none}.kbq-tag .kbq-tag-edit-input{min-width:var(--kbq-size-xs);max-width:100%}.kbq-tag .kbq-tag-edit-input.kbq-input{min-height:unset!important;border-radius:unset!important;padding:unset!important}.kbq-tag .kbq-tag-edit-submit{margin-right:var(--kbq-size-3xs)}.kbq-tag.cdk-drag-placeholder{opacity:var(--kbq-opacity-disabled)}.kbq-tag.cdk-drag-animating{transition:transform .3s cubic-bezier(0,0,.2,1)}.kbq-tag__wrapper{display:flex;align-items:center;height:100%;flex:1 1 100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
555
563
  }
556
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTag, decorators: [{
564
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTag, decorators: [{
557
565
  type: Component,
558
566
  args: [{ selector: 'kbq-tag, [kbq-tag], kbq-basic-tag, [kbq-basic-tag]', template: `
559
567
  <div class="kbq-tag__wrapper">
@@ -650,10 +658,10 @@ class KbqTagRemove {
650
658
  // that the tag is being removed before the event bubbles up.
651
659
  event.stopPropagation();
652
660
  }
653
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagRemove, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
654
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqTagRemove, isStandalone: true, selector: "[kbqTagRemove]", host: { listeners: { "click": "handleClick($event)", "focus": "focus($event)" }, properties: { "attr.tabindex": "-1" }, classAttribute: "kbq-tag-remove kbq-tag-trailing-icon" }, ngImport: i0 }); }
661
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagRemove, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
662
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqTagRemove, isStandalone: true, selector: "[kbqTagRemove]", host: { listeners: { "click": "handleClick($event)", "focus": "focus($event)" }, properties: { "attr.tabindex": "-1" }, classAttribute: "kbq-tag-remove kbq-tag-trailing-icon" }, ngImport: i0 }); }
655
663
  }
656
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagRemove, decorators: [{
664
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagRemove, decorators: [{
657
665
  type: Directive,
658
666
  args: [{
659
667
  selector: '[kbqTagRemove]',
@@ -869,6 +877,13 @@ class KbqTagList {
869
877
  */
870
878
  this.stateChanges = new Subject();
871
879
  this._required = false;
880
+ /**
881
+ * Implemented as part of KbqFormFieldControl. Forwarded from the registered `kbqTagInput`, which
882
+ * is the element the browser actually autofills — the tag list itself is not an input.
883
+ *
884
+ * @docs-private
885
+ */
886
+ this.autofilled = computed(() => this.registeredInput()?.autofilled?.() ?? false, ...(ngDevMode ? [{ debugName: "autofilled" }] : []));
872
887
  this._disabled = false;
873
888
  this._draggable = false;
874
889
  /**
@@ -902,15 +917,22 @@ class KbqTagList {
902
917
  /** Event emitted when the selected tag list value has been changed by the user. */
903
918
  this.change = output();
904
919
  /** @docs-private */
905
- this.cleaner = contentChild('kbqTagListCleaner', ...(ngDevMode ? [{ debugName: "cleaner" }] : []));
920
+ this.cleaner = contentChild(KbqCleaner, ...(ngDevMode ? [{ debugName: "cleaner", descendants: false }] : [{ descendants: false }]));
906
921
  /**
907
922
  * Whether the component is in an error state.
908
923
  *
909
924
  * @docs-private
910
925
  */
911
926
  this.errorState = false;
927
+ /**
928
+ * The same control as `tagInput`, kept in a signal so `autofilled` can track it reactively —
929
+ * `registerInput()` runs after the first read.
930
+ */
931
+ this.registeredInput = signal(undefined, ...(ngDevMode ? [{ debugName: "registeredInput" }] : []));
912
932
  /** True when the next `tags.changes` emission is triggered by a UI action, not programmatic update. */
913
933
  this.pendingUIChange = false;
934
+ /** Shared range selection state used by pointer and keyboard interactions. */
935
+ this.rangeSelection = null;
914
936
  /**
915
937
  * When a tag is destroyed, we store the index of the destroyed tag until the tags
916
938
  * query list notifies about the update. This is necessary because we cannot determine an
@@ -962,6 +984,12 @@ class KbqTagList {
962
984
  .pipe(startWith(null), takeUntilDestroyed(this.destroyRef))
963
985
  .subscribe((currentTags) => {
964
986
  this.resetTags();
987
+ if (this.rangeSelection) {
988
+ const tags = this.tags.toArray();
989
+ if (!tags.includes(this.rangeSelection.anchor) || !tags.includes(this.rangeSelection.activeEnd)) {
990
+ this.rangeSelection = null;
991
+ }
992
+ }
965
993
  // Check to see if we need to update our tab index
966
994
  this.updateTabIndex();
967
995
  // Check to see if we have a destroyed tag and need to refocus
@@ -972,7 +1000,13 @@ class KbqTagList {
972
1000
  this.stateChanges.next();
973
1001
  if (currentTags && this.pendingUIChange) {
974
1002
  this.pendingUIChange = false;
975
- this.propagateTagsChanges();
1003
+ // The latch can survive a UI action the consumer rejected (nothing was
1004
+ // added, so `tags.changes` never fired to clear it). Propagating then
1005
+ // would mark the control dirty for an edit the user never made, so
1006
+ // require the rendered tags to actually differ from the current value.
1007
+ if (this.hasTagsValueChanged()) {
1008
+ this.propagateTagsChanges();
1009
+ }
976
1010
  }
977
1011
  });
978
1012
  });
@@ -1008,10 +1042,7 @@ class KbqTagList {
1008
1042
  */
1009
1043
  registerInput(inputElement) {
1010
1044
  this.tagInput = inputElement;
1011
- // todo need rethink about it (#DS-3740)
1012
- if (this.ngControl && inputElement.ngControl?.statusChanges) {
1013
- inputElement.ngControl.statusChanges.subscribe(() => this.ngControl.control.setErrors(inputElement.ngControl.errors));
1014
- }
1045
+ this.registeredInput.set(inputElement);
1015
1046
  }
1016
1047
  /**
1017
1048
  * Implemented as part of ControlValueAccessor.
@@ -1073,6 +1104,22 @@ class KbqTagList {
1073
1104
  this.tagInput.focus();
1074
1105
  }
1075
1106
  }
1107
+ /**
1108
+ * Establishes a range origin or extends the current range to the specified tag.
1109
+ *
1110
+ * @docs-private
1111
+ */
1112
+ handleSelectionInteraction(tag, extendRange) {
1113
+ const tags = this.tags.toArray();
1114
+ const tagIndex = tags.indexOf(tag);
1115
+ if (this.disabled || !tag.selectable || tag.disabled || !this.isValidIndex(tagIndex))
1116
+ return;
1117
+ if (extendRange) {
1118
+ this.extendSelectionTo(tag);
1119
+ return;
1120
+ }
1121
+ this.setRangeOrigin(tag);
1122
+ }
1076
1123
  /**
1077
1124
  * Pass events to the keyboard manager. Available here for tests.
1078
1125
  *
@@ -1083,6 +1130,13 @@ class KbqTagList {
1083
1130
  if (this.disabled || isNull(target))
1084
1131
  return;
1085
1132
  const shouldSelectAll = this.selectable() && isSelectAll(event);
1133
+ const shouldUpdateKeyboardRange = this.selectable() &&
1134
+ this.isTagElement(target) &&
1135
+ event.shiftKey &&
1136
+ !event.altKey &&
1137
+ !event.ctrlKey &&
1138
+ !event.metaKey &&
1139
+ [LEFT_ARROW, RIGHT_ARROW].includes(event.keyCode);
1086
1140
  if (this.isInputEmpty(target)) {
1087
1141
  if (event.keyCode === BACKSPACE ||
1088
1142
  event.keyCode === LEFT_ARROW ||
@@ -1101,10 +1155,12 @@ class KbqTagList {
1101
1155
  else if (this.isTagElement(target)) {
1102
1156
  if (event.keyCode === HOME) {
1103
1157
  this.keyManager.setFirstItemActive();
1158
+ this.setRangeOrigin(this.keyManager.activeItem);
1104
1159
  event.preventDefault();
1105
1160
  }
1106
1161
  else if (event.keyCode === END) {
1107
1162
  this.keyManager.setLastItemActive();
1163
+ this.setRangeOrigin(this.keyManager.activeItem);
1108
1164
  event.preventDefault();
1109
1165
  }
1110
1166
  else if (shouldSelectAll) {
@@ -1113,7 +1169,18 @@ class KbqTagList {
1113
1169
  event.preventDefault();
1114
1170
  }
1115
1171
  else {
1172
+ const previousActiveIndex = this.keyManager.activeItemIndex;
1116
1173
  this.keyManager.onKeydown(event);
1174
+ if (previousActiveIndex !== this.keyManager.activeItemIndex) {
1175
+ if (shouldUpdateKeyboardRange) {
1176
+ if (!this.rangeSelection)
1177
+ this.setRangeOrigin(this.tags.get(previousActiveIndex));
1178
+ this.extendSelectionTo(this.keyManager.activeItem);
1179
+ }
1180
+ else {
1181
+ this.setRangeOrigin(this.keyManager.activeItem);
1182
+ }
1183
+ }
1117
1184
  }
1118
1185
  this.stateChanges.next();
1119
1186
  }
@@ -1196,6 +1263,11 @@ class KbqTagList {
1196
1263
  }
1197
1264
  selectAll() {
1198
1265
  this.tags.forEach((tag) => tag.selectViaInteraction());
1266
+ this.setRangeOrigin(this.keyManager.activeItem ??
1267
+ this.tags
1268
+ .toArray()
1269
+ .reverse()
1270
+ .find((tag) => tag.selectable && !tag.disabled));
1199
1271
  }
1200
1272
  /**
1201
1273
  * Unselects all tags in the list.
@@ -1204,6 +1276,7 @@ class KbqTagList {
1204
1276
  */
1205
1277
  unselectAll() {
1206
1278
  this.tags.forEach((tag) => tag.setSelectedState(false));
1279
+ this.rangeSelection = null;
1207
1280
  }
1208
1281
  /**
1209
1282
  * Removes all selected tags from the list.
@@ -1213,6 +1286,15 @@ class KbqTagList {
1213
1286
  removeSelected() {
1214
1287
  this.selected.forEach((tag) => tag.remove());
1215
1288
  }
1289
+ /** Whether the rendered tags no longer match the value this control last reported. */
1290
+ hasTagsValueChanged() {
1291
+ const currentValue = this.tags.map(({ value }) => value);
1292
+ if (!Array.isArray(this._value)) {
1293
+ return currentValue.length > 0;
1294
+ }
1295
+ return (this._value.length !== currentValue.length ||
1296
+ currentValue.some((value, index) => value !== this._value[index]));
1297
+ }
1216
1298
  propagateTagsChanges() {
1217
1299
  const valueToEmit = this.tags.map((tag) => tag.value);
1218
1300
  this._value = valueToEmit;
@@ -1291,8 +1373,47 @@ class KbqTagList {
1291
1373
  this.unselectAll();
1292
1374
  });
1293
1375
  }
1294
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagList, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1295
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: KbqTagList, isStandalone: true, selector: "kbq-tag-list", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: false, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: false, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { dropped: "dropped", valueChange: "valueChange", change: "change" }, host: { listeners: { "focus": "focus()", "blur": "blur()", "keydown": "keydown($event)" }, properties: { "class.kbq-disabled": "disabled", "class.kbq-invalid": "errorState", "class.kbq-tag-list_selectable": "selectable()", "class.kbq-tag-list_editable": "editable()", "class.kbq-tag-list_removable": "removable", "class.kbq-tag-list_draggable": "draggable", "attr.tabindex": "tabIndex", "id": "uid" }, classAttribute: "kbq-tag-list" }, providers: [{ provide: KbqFormFieldControl, useExisting: KbqTagList }], queries: [{ propertyName: "cleaner", first: true, predicate: ["kbqTagListCleaner"], descendants: true, isSignal: true }, { propertyName: "tags", predicate: i0.forwardRef(() => KbqTag), descendants: true }], exportAs: ["kbqTagList"], hostDirectives: [{ directive: i2.CdkDropList }], ngImport: i0, template: `
1376
+ extendSelectionTo(activeEnd) {
1377
+ const tags = this.tags.toArray();
1378
+ const activeEndIndex = activeEnd ? tags.indexOf(activeEnd) : -1;
1379
+ if (!activeEnd || !this.isValidIndex(activeEndIndex))
1380
+ return;
1381
+ if (!this.rangeSelection) {
1382
+ activeEnd.setSelectedState(true, { isUserInput: true, emitEvent: true });
1383
+ this.setRangeOrigin(activeEnd);
1384
+ return;
1385
+ }
1386
+ const anchorIndex = tags.indexOf(this.rangeSelection.anchor);
1387
+ const previousActiveEndIndex = tags.indexOf(this.rangeSelection.activeEnd);
1388
+ if (!this.isValidIndex(anchorIndex) || !this.isValidIndex(previousActiveEndIndex)) {
1389
+ this.rangeSelection = null;
1390
+ this.extendSelectionTo(activeEnd);
1391
+ return;
1392
+ }
1393
+ const previousFromIndex = Math.min(anchorIndex, previousActiveEndIndex);
1394
+ const previousToIndex = Math.max(anchorIndex, previousActiveEndIndex);
1395
+ const fromIndex = Math.min(anchorIndex, activeEndIndex);
1396
+ const toIndex = Math.max(anchorIndex, activeEndIndex);
1397
+ tags.forEach((tag, index) => {
1398
+ if (tag.disabled || !tag.selectable)
1399
+ return;
1400
+ const belongsToRange = index >= fromIndex && index <= toIndex;
1401
+ const belongedToPreviousRange = index >= previousFromIndex && index <= previousToIndex;
1402
+ if (belongsToRange || belongedToPreviousRange) {
1403
+ tag.setSelectedState(belongsToRange, { isUserInput: true, emitEvent: true });
1404
+ }
1405
+ });
1406
+ this.rangeSelection = { ...this.rangeSelection, activeEnd };
1407
+ }
1408
+ setRangeOrigin(anchor) {
1409
+ this.rangeSelection = anchor ? { anchor, activeEnd: anchor } : null;
1410
+ }
1411
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagList, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1412
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: KbqTagList, isStandalone: true, selector: "kbq-tag-list", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: false, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: false, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { dropped: "dropped", valueChange: "valueChange", change: "change" }, host: { listeners: { "focus": "focus()", "blur": "blur()", "keydown": "keydown($event)" }, properties: { "class.kbq-disabled": "disabled", "class.kbq-invalid": "errorState", "class.kbq-tag-list_selectable": "selectable()", "class.kbq-tag-list_editable": "editable()", "class.kbq-tag-list_removable": "removable", "class.kbq-tag-list_draggable": "draggable", "attr.tabindex": "tabIndex", "id": "uid" }, classAttribute: "kbq-tag-list" }, providers: [
1413
+ { provide: KbqFormFieldControl, useExisting: KbqTagList },
1414
+ // Tag-list cleaners use consumer-provided click handlers; do not also reset the outer form control.
1415
+ { provide: KBQ_CLEANER_CONTEXT, useValue: null }
1416
+ ], queries: [{ propertyName: "cleaner", first: true, predicate: KbqCleaner, isSignal: true }, { propertyName: "tags", predicate: i0.forwardRef(() => KbqTag), descendants: true }], exportAs: ["kbqTagList"], hostDirectives: [{ directive: i2.CdkDropList }], ngImport: i0, template: `
1296
1417
  <div class="kbq-tags-list__list-container">
1297
1418
  <ng-content />
1298
1419
  </div>
@@ -1304,7 +1425,7 @@ class KbqTagList {
1304
1425
  }
1305
1426
  `, isInline: true, styles: [".kbq-tag-list{display:flex;flex-direction:row;min-height:var(--kbq-size-xxl);box-sizing:border-box}.kbq-tag-list:focus-visible{outline:none}.kbq-tag-list.cdk-drop-list-dragging .cdk-drag{transition:transform .25s cubic-bezier(0,0,.2,1)}.kbq-tag-input{background:transparent;padding:0;margin:0;border:none;outline:none}.kbq-tag-input::-ms-clear{display:none;width:0;height:0}.kbq-tag-input::-ms-reveal{display:none;width:0;height:0}.kbq-tag-input::-webkit-search-decoration,.kbq-tag-input::-webkit-search-cancel-button,.kbq-tag-input::-webkit-search-results-button,.kbq-tag-input::-webkit-search-results-decoration{display:none}.kbq-tag-input{padding-top:var(--kbq-tag-size-padding-vertical);padding-bottom:var(--kbq-tag-size-padding-vertical);padding-left:var(--kbq-size-xxs);padding-right:unset;min-width:var(--kbq-size-3xl);max-width:100%;flex-grow:1}.kbq-tag-input.kbq-input{--kbq-input-size-padding-vertical: 0;--kbq-form-field-size-border-radius: 0;padding-left:var(--kbq-size-xxs);min-height:unset!important;width:auto}.kbq-tags-list__list-container{display:flex;flex-wrap:wrap;flex:1 1 100%;box-sizing:border-box;min-width:0;gap:var(--kbq-tag-list-size-content-gap)}.kbq-tags-list__list-container .kbq-tag-input:first-child{padding-left:var(--kbq-size-s)}.kbq-tags-list__cleaner .kbq-cleaner{max-height:24px}.kbq-form-field-type-tag-list .kbq-form-field__container{padding-top:calc(var(--kbq-tag-input-size-padding-vertical) - var(--kbq-form-field-size-border-width));padding-bottom:calc(var(--kbq-tag-input-size-padding-vertical) - var(--kbq-form-field-size-border-width))}body:has(.kbq-tag-list.cdk-drop-list-dragging){cursor:grabbing}\n", ".kbq-tag,.kbq-tag-list,.kbq-tag-input{--kbq-tag-list-size-content-gap: var(--kbq-size-xxs);--kbq-tag-size-padding-horizontal: var(--kbq-size-xxs);--kbq-tag-size-padding-vertical: var(--kbq-size-3xs);--kbq-tag-size-content-gap-horizontal: var(--kbq-size-3xs);--kbq-tag-size-icon-margin-left: var(--kbq-size-3xs);--kbq-tag-size-close-button-margin-right: var(--kbq-size-3xs);--kbq-tag-theme-fade-on-default-background: var(--kbq-background-theme-fade);--kbq-tag-theme-fade-on-default-text: var(--kbq-foreground-theme);--kbq-tag-theme-fade-on-default-icon: var(--kbq-icon-theme);--kbq-tag-theme-fade-on-default-close-button: var(--kbq-icon-theme);--kbq-tag-theme-fade-on-states-hover-background: var(--kbq-states-background-theme-fade-hover);--kbq-tag-theme-fade-on-states-hover-text: var(--kbq-foreground-theme);--kbq-tag-theme-fade-on-states-hover-icon: var(--kbq-icon-theme);--kbq-tag-theme-fade-on-states-hover-close-button: var(--kbq-icon-theme);--kbq-tag-theme-fade-on-states-focus-background: var(--kbq-background-theme-fade);--kbq-tag-theme-fade-on-states-focus-text: var(--kbq-foreground-theme);--kbq-tag-theme-fade-on-states-focus-icon: var(--kbq-icon-theme);--kbq-tag-theme-fade-on-states-focus-close-button: var(--kbq-icon-theme);--kbq-tag-theme-fade-on-states-focus-outline: var(--kbq-states-line-focus-theme);--kbq-tag-theme-fade-on-states-selected-background: var(--kbq-background-theme);--kbq-tag-theme-fade-on-states-selected-text: var(--kbq-foreground-white);--kbq-tag-theme-fade-on-states-selected-icon: var(--kbq-icon-white);--kbq-tag-theme-fade-on-states-selected-close-button: var(--kbq-icon-white);--kbq-tag-theme-fade-on-states-disabled-background: var(--kbq-states-background-disabled);--kbq-tag-theme-fade-on-states-disabled-text: var(--kbq-states-foreground-disabled);--kbq-tag-theme-fade-on-states-disabled-icon: var(--kbq-states-icon-disabled);--kbq-tag-theme-fade-on-states-disabled-close-button: var(--kbq-states-icon-disabled);--kbq-tag-contrast-fade-on-default-background: var(--kbq-background-contrast-fade);--kbq-tag-contrast-fade-on-default-text: var(--kbq-foreground-contrast);--kbq-tag-contrast-fade-on-default-icon: var(--kbq-icon-contrast-fade);--kbq-tag-contrast-fade-on-default-close-button: var(--kbq-icon-contrast-fade);--kbq-tag-contrast-fade-on-states-hover-background: var(--kbq-states-background-contrast-fade-hover);--kbq-tag-contrast-fade-on-states-hover-text: var(--kbq-foreground-contrast);--kbq-tag-contrast-fade-on-states-hover-icon: var(--kbq-icon-contrast-fade);--kbq-tag-contrast-fade-on-states-hover-close-button: var(--kbq-icon-contrast-fade);--kbq-tag-contrast-fade-on-states-focus-background: var(--kbq-background-contrast-fade);--kbq-tag-contrast-fade-on-states-focus-text: var(--kbq-foreground-contrast);--kbq-tag-contrast-fade-on-states-focus-icon: var(--kbq-icon-contrast-fade);--kbq-tag-contrast-fade-on-states-focus-close-button: var(--kbq-icon-contrast-fade);--kbq-tag-contrast-fade-on-states-focus-outline: var(--kbq-states-line-focus-theme);--kbq-tag-contrast-fade-on-states-selected-background: var(--kbq-background-theme);--kbq-tag-contrast-fade-on-states-selected-text: var(--kbq-foreground-white);--kbq-tag-contrast-fade-on-states-selected-icon: var(--kbq-icon-white);--kbq-tag-contrast-fade-on-states-selected-close-button: var(--kbq-icon-white);--kbq-tag-contrast-fade-on-states-disabled-background: var(--kbq-states-background-disabled);--kbq-tag-contrast-fade-on-states-disabled-text: var(--kbq-states-foreground-disabled);--kbq-tag-contrast-fade-on-states-disabled-icon: var(--kbq-states-icon-disabled);--kbq-tag-contrast-fade-on-states-disabled-close-button: var(--kbq-states-icon-disabled);--kbq-tag-error-fade-on-default-background: var(--kbq-background-error-fade);--kbq-tag-error-fade-on-default-text: var(--kbq-foreground-error);--kbq-tag-error-fade-on-default-icon: var(--kbq-icon-error);--kbq-tag-error-fade-on-default-close-button: var(--kbq-icon-error);--kbq-tag-error-fade-on-states-hover-background: var(--kbq-states-background-error-fade-hover);--kbq-tag-error-fade-on-states-hover-text: var(--kbq-foreground-error);--kbq-tag-error-fade-on-states-hover-icon: var(--kbq-icon-error);--kbq-tag-error-fade-on-states-hover-close-button: var(--kbq-icon-error);--kbq-tag-error-fade-on-states-focus-background: var(--kbq-background-error-fade);--kbq-tag-error-fade-on-states-focus-text: var(--kbq-foreground-error);--kbq-tag-error-fade-on-states-focus-icon: var(--kbq-icon-error);--kbq-tag-error-fade-on-states-focus-close-button: var(--kbq-icon-error);--kbq-tag-error-fade-on-states-focus-outline: var(--kbq-states-line-focus-error);--kbq-tag-error-fade-on-states-selected-background: var(--kbq-background-error);--kbq-tag-error-fade-on-states-selected-text: var(--kbq-foreground-white);--kbq-tag-error-fade-on-states-selected-icon: var(--kbq-icon-white);--kbq-tag-error-fade-on-states-selected-close-button: var(--kbq-icon-white);--kbq-tag-error-fade-on-states-disabled-background: var(--kbq-states-background-disabled);--kbq-tag-error-fade-on-states-disabled-text: var(--kbq-states-foreground-disabled);--kbq-tag-error-fade-on-states-disabled-icon: var(--kbq-states-icon-disabled);--kbq-tag-error-fade-on-states-disabled-close-button: var(--kbq-states-icon-disabled);--kbq-tag-warning-fade-default-background: var(--kbq-background-warning-fade);--kbq-tag-warning-fade-default-text: var(--kbq-foreground-warning);--kbq-tag-warning-fade-default-icon: var(--kbq-icon-warning);--kbq-tag-warning-fade-default-close-button: var(--kbq-icon-warning);--kbq-tag-warning-fade-states-hover-background: var(--kbq-states-background-warning-fade-hover);--kbq-tag-warning-fade-states-hover-text: var(--kbq-foreground-warning);--kbq-tag-warning-fade-states-hover-icon: var(--kbq-icon-warning);--kbq-tag-warning-fade-states-hover-close-button: var(--kbq-icon-warning);--kbq-tag-warning-fade-states-focus-background: var(--kbq-background-warning-fade);--kbq-tag-warning-fade-states-focus-text: var(--kbq-foreground-warning);--kbq-tag-warning-fade-states-focus-icon: var(--kbq-icon-warning);--kbq-tag-warning-fade-states-focus-close-button: var(--kbq-icon-warning);--kbq-tag-warning-fade-states-focus-outline: var(--kbq-states-line-focus-theme);--kbq-tag-warning-fade-states-selected-background: var(--kbq-background-warning);--kbq-tag-warning-fade-states-selected-text: var(--kbq-foreground-contrast);--kbq-tag-warning-fade-states-selected-icon: var(--kbq-icon-contrast);--kbq-tag-warning-fade-states-selected-close-button: var(--kbq-icon-contrast);--kbq-tag-warning-fade-states-disabled-background: var(--kbq-states-background-disabled);--kbq-tag-warning-fade-states-disabled-text: var(--kbq-states-foreground-disabled);--kbq-tag-warning-fade-states-disabled-icon: var(--kbq-states-icon-disabled);--kbq-tag-warning-fade-states-disabled-close-button: var(--kbq-states-icon-disabled)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1306
1427
  }
1307
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagList, decorators: [{
1428
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagList, decorators: [{
1308
1429
  type: Component,
1309
1430
  args: [{ selector: 'kbq-tag-list', template: `
1310
1431
  <div class="kbq-tags-list__list-container">
@@ -1316,7 +1437,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1316
1437
  <ng-content select="kbq-cleaner" />
1317
1438
  </div>
1318
1439
  }
1319
- `, providers: [{ provide: KbqFormFieldControl, useExisting: KbqTagList }], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
1440
+ `, providers: [
1441
+ { provide: KbqFormFieldControl, useExisting: KbqTagList },
1442
+ // Tag-list cleaners use consumer-provided click handlers; do not also reset the outer form control.
1443
+ { provide: KBQ_CLEANER_CONTEXT, useValue: null }
1444
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
1320
1445
  class: 'kbq-tag-list',
1321
1446
  '[class.kbq-disabled]': 'disabled',
1322
1447
  '[class.kbq-invalid]': 'errorState',
@@ -1349,7 +1474,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1349
1474
  type: Input
1350
1475
  }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], errorStateMatcher: [{
1351
1476
  type: Input
1352
- }], change: [{ type: i0.Output, args: ["change"] }], cleaner: [{ type: i0.ContentChild, args: ['kbqTagListCleaner', { isSignal: true }] }], tags: [{
1477
+ }], change: [{ type: i0.Output, args: ["change"] }], cleaner: [{ type: i0.ContentChild, args: [i0.forwardRef(() => KbqCleaner), { ...{ descendants: false }, isSignal: true }] }], tags: [{
1353
1478
  type: ContentChildren,
1354
1479
  args: [forwardRef(() => KbqTag), {
1355
1480
  // Need to use `descendants: true`,
@@ -1358,13 +1483,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1358
1483
  }]
1359
1484
  }] } });
1360
1485
 
1361
- const KBQ_TAG_INPUT_DEFAULT_SEPARATORS = {
1362
- [ENTER$1]: { symbol: /\r?\n/, key: 'Enter' },
1363
- [TAB$1]: { symbol: /\t/, key: 'Tab' },
1364
- [SPACE$1]: { symbol: / /, key: ' ' },
1365
- [COMMA]: { symbol: /,/, key: ',' },
1366
- [SEMICOLON]: { symbol: /;/, key: ';' }
1367
- };
1486
+ const KBQ_TAG_INPUT_DEFAULT_SEPARATORS = [
1487
+ { symbol: /\r?\n/, key: 'Enter', keyCode: ENTER$1 },
1488
+ { symbol: /\t/, key: 'Tab', keyCode: TAB$1 },
1489
+ { symbol: / /, key: ' ', keyCode: SPACE$1 },
1490
+ { symbol: /,/, key: ',', keyCode: COMMA },
1491
+ { symbol: /;/, key: ';', keyCode: SEMICOLON }
1492
+ ];
1493
+ const KBQ_TAGS_DEFAULT_OPTIONS_CONFIG = { separatorKeyCodes: [ENTER$1] };
1494
+ /** Injection token to be used to override the default options. */
1495
+ const KBQ_TAGS_DEFAULT_OPTIONS = new InjectionToken('kbq-tags-default-options', {
1496
+ providedIn: 'root',
1497
+ factory: () => KBQ_TAGS_DEFAULT_OPTIONS_CONFIG
1498
+ });
1499
+ /** Utility provider for `KBQ_TAGS_DEFAULT_OPTIONS`. */
1500
+ const kbqTagsDefaultOptionsProvider = (options) => ({
1501
+ provide: KBQ_TAGS_DEFAULT_OPTIONS,
1502
+ useValue: { ...KBQ_TAGS_DEFAULT_OPTIONS_CONFIG, ...options }
1503
+ });
1368
1504
  // Increasing integer for generating unique ids.
1369
1505
  let nextUniqueId = 0;
1370
1506
  /**
@@ -1382,12 +1518,14 @@ class KbqTagInput {
1382
1518
  set separatorKeyCodes(value) {
1383
1519
  this._separatorKeyCodes = value || [];
1384
1520
  }
1385
- /** @docs-private */
1521
+ /**
1522
+ * The effective set of separators: entries gated by `keyCode` are included only when that
1523
+ * code is present in `separatorKeyCodes`; entries without a `keyCode` (no single-keystroke
1524
+ * equivalent, e.g. a run of whitespace) are always included.
1525
+ * @docs-private
1526
+ */
1386
1527
  get separators() {
1387
- return this._separatorKeyCodes.reduce((acc, key) => {
1388
- const separator = this.getSeparatorByKeyCode(key);
1389
- return separator ? [...acc, separator] : acc;
1390
- }, []);
1528
+ return this._separators.filter((separator) => separator.keyCode === undefined || this._separatorKeyCodes.includes(separator.keyCode));
1391
1529
  }
1392
1530
  /** Register input for tag list */
1393
1531
  // TODO: Skipped for migration because:
@@ -1426,10 +1564,28 @@ class KbqTagInput {
1426
1564
  this.elementRef = inject(ElementRef);
1427
1565
  this.defaultOptions = inject(KBQ_TAGS_DEFAULT_OPTIONS);
1428
1566
  this.trimDirective = inject(KbqTrim, { optional: true, self: true });
1567
+ /**
1568
+ * The form control instance bound to the input, if any.
1569
+ *
1570
+ * @deprecated Read only, and unused by the library: validation lives on the `<kbq-tag-list>`
1571
+ * control. Binding `[formControl]`/`[ngModel]` to the input itself stays supported — it is what
1572
+ * `kbqAutocomplete` drives the input through — only its validators are not consulted.
1573
+ * Will be removed in a future major release.
1574
+ * @docs-private
1575
+ */
1429
1576
  this.ngControl = inject(NgControl, { optional: true, self: true });
1577
+ /**
1578
+ * The autocomplete trigger attached to the input, if any.
1579
+ * @docs-private
1580
+ */
1430
1581
  this.autocompleteTrigger = inject(KbqAutocompleteTrigger, { optional: true, self: true });
1431
- /** Whether the control is focused. */
1582
+ /**
1583
+ * Whether the control is focused.
1584
+ * @docs-private
1585
+ */
1432
1586
  this.focused = false;
1587
+ /** Whether the control's value was filled in by the browser. */
1588
+ this.autofilled = kbqInjectAutofilled();
1433
1589
  this._separatorKeyCodes = this.defaultOptions.separatorKeyCodes;
1434
1590
  this._separators = this.defaultOptions.separators || KBQ_TAG_INPUT_DEFAULT_SEPARATORS;
1435
1591
  /** Emitted when a tag is to be added. */
@@ -1464,9 +1620,9 @@ class KbqTagInput {
1464
1620
  }
1465
1621
  /** @docs-private */
1466
1622
  onKeydown(event) {
1467
- const isSeparatorKey = this.isSeparatorKey(event);
1623
+ const isSeparatorKey = this.matchesInputSeparator(event);
1468
1624
  if (!this.inputElement.value) {
1469
- if (isSeparatorKey) {
1625
+ if (isSeparatorKey && event.keyCode !== TAB$1) {
1470
1626
  event.preventDefault();
1471
1627
  }
1472
1628
  this._tagList.keydown(event);
@@ -1483,68 +1639,66 @@ class KbqTagInput {
1483
1639
  event.preventDefault();
1484
1640
  }
1485
1641
  }
1486
- /** Checks to see if the blur should emit the (tagEnd) event. */
1642
+ /**
1643
+ * Checks to see if the blur should emit the (tagEnd) event.
1644
+ * @docs-private
1645
+ */
1487
1646
  blur(event) {
1488
1647
  this.focused = false;
1489
1648
  // Blur the tag list if it is not focused
1490
1649
  if (!this._tagList.focused) {
1491
- this.triggerValidation();
1492
1650
  this._tagList.blur();
1493
1651
  }
1494
- if (this.addOnBlur && (this.autocompleteTrigger?.onInputBlur()(event) || true)) {
1652
+ // Chromium fires `blur` synchronously when the input becomes disabled, so without this
1653
+ // guard disabling a focused tag input would append whatever was typed.
1654
+ if (!this.disabled && this.addOnBlur && (this.autocompleteTrigger?.onInputBlur()(event) ?? true)) {
1495
1655
  this.emitTagEnd();
1496
1656
  }
1497
1657
  this._tagList.stateChanges.next();
1498
1658
  }
1499
- triggerValidation() {
1500
- if (!this.hasControl()) {
1501
- return;
1502
- }
1503
- this.ngControl.statusChanges.emit(this.ngControl.status);
1504
- }
1505
- /** Checks to see if the (tagEnd) event needs to be emitted. */
1659
+ /**
1660
+ * @deprecated No-op. Validation belongs to the `<kbq-tag-list>` form control, which
1661
+ * revalidates itself whenever its value changes. Will be removed in a future major release.
1662
+ * @docs-private
1663
+ */
1664
+ triggerValidation() { }
1665
+ /**
1666
+ * Checks to see if the (tagEnd) event needs to be emitted.
1667
+ * @docs-private
1668
+ */
1506
1669
  emitTagEnd() {
1507
- if (!this.hasControl() || (this.hasControl() && !this.ngControl.invalid)) {
1508
- if (this.distinct() && this.hasDuplicates)
1509
- return;
1510
- this._tagList?.notifyPendingTagChange();
1511
- this.tagEnd.emit({ input: this.inputElement, value: this.trimValue(this.inputElement.value) });
1512
- }
1670
+ this.addTag(this.trimValue(this.inputElement.value), this.existingTagValues());
1513
1671
  }
1672
+ /**
1673
+ * Whether the current input value duplicates an existing tag.
1674
+ * @docs-private
1675
+ */
1514
1676
  get hasDuplicates() {
1515
- return this._tagList.tags
1516
- .map(({ value }) => value)
1517
- .some((tagValue) => tagValue === this.trimValue(this.inputElement.value));
1677
+ return this.tagValues().has(this.trimValue(this.inputElement.value));
1518
1678
  }
1679
+ /** @docs-private */
1519
1680
  onInput() {
1520
1681
  // Let tag list know whenever the value changes.
1521
1682
  this._tagList.stateChanges.next();
1522
1683
  }
1684
+ /** @docs-private */
1523
1685
  onPaste($event) {
1524
1686
  if (!$event.clipboardData) {
1525
1687
  return;
1526
1688
  }
1527
1689
  const data = $event.clipboardData.getData('text');
1528
- if ((data && data.length === 0) || !this.addOnPaste()) {
1690
+ if (!data || !this.addOnPaste()) {
1529
1691
  return;
1530
1692
  }
1531
- const separatorsInString = this.getSeparatorsForString(data);
1693
+ const separatorsInString = this.getPasteSeparatorPatterns(data);
1532
1694
  // prettier-ignore
1533
1695
  const dividedString = separatorsInString.length > 0 ?
1534
1696
  [...data.split(new RegExp(`${separatorsInString.join('|')}`))] :
1535
1697
  [data];
1536
- let items = dividedString.map((item) => this.trimValue(item));
1537
- if (items.length === 0) {
1538
- items.push(data);
1539
- }
1540
- if (this.distinct()) {
1541
- const tagValues = this._tagList.tags.map(({ value }) => value);
1542
- items = items.filter((item) => !tagValues.includes(item));
1543
- }
1544
- if (items.length) {
1545
- this._tagList?.notifyPendingTagChange();
1546
- }
1547
- items.forEach((item) => this.tagEnd.emit({ input: this.inputElement, value: item }));
1698
+ // Built once per paste, then updated as items are accepted, so a batch also dedupes
1699
+ // against itself `tags` is a QueryList that cannot refresh between iterations.
1700
+ const existingValues = this.existingTagValues();
1701
+ dividedString.forEach((item) => this.addTag(this.trimValue(item), existingValues));
1548
1702
  $event.preventDefault();
1549
1703
  $event.stopPropagation();
1550
1704
  }
@@ -1558,32 +1712,49 @@ class KbqTagInput {
1558
1712
  focus() {
1559
1713
  this.inputElement.focus();
1560
1714
  }
1561
- getSeparatorsForString(value) {
1715
+ getPasteSeparatorPatterns(value) {
1562
1716
  return this.separators
1563
- .filter((separator) => value.search(separator.symbol) > -1)
1717
+ .filter((separator) => (!separator.appliesTo || separator.appliesTo.includes('paste')) &&
1718
+ value.search(separator.symbol) > -1)
1564
1719
  .map((separator) => separator.symbol.source);
1565
1720
  }
1566
- trimValue(value) {
1567
- return this.trimDirective ? this.trimDirective.trim(value) : value;
1568
- }
1569
- getSeparatorByKeyCode(keyCode) {
1570
- const sep = this._separators[keyCode];
1571
- if (sep) {
1572
- return sep;
1721
+ /**
1722
+ * Single path for adding a tag, shared by typed input, blur and paste.
1723
+ * `existingValues` is `null` unless `distinct` is on, and is mutated as values are accepted.
1724
+ */
1725
+ addTag(value, existingValues) {
1726
+ if (existingValues) {
1727
+ if (existingValues.has(value))
1728
+ return;
1729
+ existingValues.add(value);
1730
+ }
1731
+ // An empty value adds nothing, so latching the flag here would make the next
1732
+ // programmatic tags update look UI-initiated.
1733
+ if (value) {
1734
+ this._tagList?.notifyPendingTagChange();
1573
1735
  }
1574
- return null;
1736
+ this.tagEnd.emit({ input: this.inputElement, value });
1737
+ }
1738
+ /** Values to dedupe against, or `null` when duplicates are allowed. */
1739
+ existingTagValues() {
1740
+ return this.distinct() ? this.tagValues() : null;
1741
+ }
1742
+ tagValues() {
1743
+ return new Set(this._tagList.tags.map(({ value }) => value));
1575
1744
  }
1576
- hasControl() {
1577
- return !!this.ngControl;
1745
+ trimValue(value) {
1746
+ return (this.trimDirective ? this.trimDirective.trim(value) : value);
1578
1747
  }
1579
- /** Checks whether a keycode is one of the configured separators. */
1580
- isSeparatorKey(event) {
1581
- return this.separators.some((separator) => separator.key === event.key && !hasModifierKey(event));
1748
+ /** Checks whether a keydown event matches a separator that applies to typed input. */
1749
+ matchesInputSeparator(event) {
1750
+ return this.separators.some((separator) => separator.key === event.key &&
1751
+ !hasModifierKey(event) &&
1752
+ (!separator.appliesTo || separator.appliesTo.includes('input')));
1582
1753
  }
1583
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1584
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.25", type: KbqTagInput, isStandalone: true, selector: "input[kbqTagInputFor]", inputs: { separatorKeyCodes: { classPropertyName: "separatorKeyCodes", publicName: "kbqTagInputSeparatorKeyCodes", isSignal: false, isRequired: false, transformFunction: null }, distinct: { classPropertyName: "distinct", publicName: "distinct", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: false, isRequired: false, transformFunction: null }, tagList: { classPropertyName: "tagList", publicName: "kbqTagInputFor", isSignal: false, isRequired: false, transformFunction: null }, addOnBlur: { classPropertyName: "addOnBlur", publicName: "kbqTagInputAddOnBlur", isSignal: false, isRequired: false, transformFunction: null }, addOnPaste: { classPropertyName: "addOnPaste", publicName: "kbqTagInputAddOnPaste", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { tagEnd: "kbqTagInputTokenEnd" }, host: { listeners: { "keydown": "onKeydown($event)", "blur": "blur($event)", "focus": "onFocus()", "input": "onInput()", "paste": "onPaste($event)" }, properties: { "id": "id", "attr.disabled": "disabled || null", "attr.placeholder": "placeholder || null" }, classAttribute: "kbq-tag-input" }, exportAs: ["kbqTagInput", "kbqTagInputFor"], usesOnChanges: true, hostDirectives: [{ directive: i1.KbqFieldSizingContent }], ngImport: i0 }); }
1754
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1755
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.30", type: KbqTagInput, isStandalone: true, selector: "input[kbqTagInputFor]", inputs: { separatorKeyCodes: { classPropertyName: "separatorKeyCodes", publicName: "kbqTagInputSeparatorKeyCodes", isSignal: false, isRequired: false, transformFunction: null }, distinct: { classPropertyName: "distinct", publicName: "distinct", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: false, isRequired: false, transformFunction: null }, tagList: { classPropertyName: "tagList", publicName: "kbqTagInputFor", isSignal: false, isRequired: false, transformFunction: null }, addOnBlur: { classPropertyName: "addOnBlur", publicName: "kbqTagInputAddOnBlur", isSignal: false, isRequired: false, transformFunction: null }, addOnPaste: { classPropertyName: "addOnPaste", publicName: "kbqTagInputAddOnPaste", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { tagEnd: "kbqTagInputTokenEnd" }, host: { listeners: { "keydown": "onKeydown($event)", "blur": "blur($event)", "focus": "onFocus()", "input": "onInput()", "paste": "onPaste($event)" }, properties: { "id": "id", "attr.disabled": "disabled || null", "attr.placeholder": "placeholder || null" }, classAttribute: "kbq-tag-input" }, exportAs: ["kbqTagInput", "kbqTagInputFor"], usesOnChanges: true, hostDirectives: [{ directive: i1.KbqFieldSizingContent }], ngImport: i0 }); }
1585
1756
  }
1586
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagInput, decorators: [{
1757
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagInput, decorators: [{
1587
1758
  type: Directive,
1588
1759
  args: [{
1589
1760
  selector: 'input[kbqTagInputFor]',
@@ -1623,8 +1794,8 @@ const COMPONENTS = [
1623
1794
  KbqTagEditInput
1624
1795
  ];
1625
1796
  class KbqTagsModule {
1626
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1627
- /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: KbqTagsModule, imports: [PlatformModule, KbqTagEditSubmit,
1797
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1798
+ /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.30", ngImport: i0, type: KbqTagsModule, imports: [PlatformModule, KbqTagEditSubmit,
1628
1799
  KbqTagEditInput, KbqTagList,
1629
1800
  KbqTag,
1630
1801
  KbqTagInput,
@@ -1637,14 +1808,9 @@ class KbqTagsModule {
1637
1808
  KbqTagAvatar,
1638
1809
  KbqTagRemove, KbqTagEditSubmit,
1639
1810
  KbqTagEditInput] }); }
1640
- /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagsModule, providers: [
1641
- {
1642
- provide: KBQ_TAGS_DEFAULT_OPTIONS,
1643
- useValue: { separatorKeyCodes: [ENTER$1] }
1644
- }
1645
- ], imports: [PlatformModule] }); }
1811
+ /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagsModule, providers: [kbqTagsDefaultOptionsProvider({ separatorKeyCodes: [ENTER$1] })], imports: [PlatformModule] }); }
1646
1812
  }
1647
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqTagsModule, decorators: [{
1813
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqTagsModule, decorators: [{
1648
1814
  type: NgModule,
1649
1815
  args: [{
1650
1816
  imports: [
@@ -1657,12 +1823,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1657
1823
  KbqTagAvatar,
1658
1824
  KbqTagRemove
1659
1825
  ],
1660
- providers: [
1661
- {
1662
- provide: KBQ_TAGS_DEFAULT_OPTIONS,
1663
- useValue: { separatorKeyCodes: [ENTER$1] }
1664
- }
1665
- ],
1826
+ providers: [kbqTagsDefaultOptionsProvider({ separatorKeyCodes: [ENTER$1] })],
1666
1827
  exports: [
1667
1828
  KbqTagList,
1668
1829
  KbqTag,
@@ -1679,5 +1840,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
1679
1840
  * Generated bundle index. Do not edit.
1680
1841
  */
1681
1842
 
1682
- export { KBQ_TAGS_DEFAULT_OPTIONS, KbqTag, KbqTagAvatar, KbqTagEditChange, KbqTagEditInput, KbqTagEditSubmit, KbqTagInput, KbqTagList, KbqTagListChange, KbqTagRemove, KbqTagSelectionChange, KbqTagTrailingIcon, KbqTagsModule };
1843
+ export { KBQ_TAGS_DEFAULT_OPTIONS, KbqTag, KbqTagAvatar, KbqTagEditChange, KbqTagEditInput, KbqTagEditSubmit, KbqTagInput, KbqTagList, KbqTagListChange, KbqTagRemove, KbqTagSelectionChange, KbqTagTrailingIcon, KbqTagsModule, kbqTagsDefaultOptionsProvider };
1683
1844
  //# sourceMappingURL=koobiq-components-tags.mjs.map