@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
@@ -0,0 +1,530 @@
1
+ 'use strict';
2
+
3
+ var ts = require('typescript');
4
+ var utils_ast = require('./ast.js');
5
+ var utils_typescript = require('./typescript.js');
6
+
7
+ function escapeRegExp(value) {
8
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
+ }
10
+ /**
11
+ * Builds the exact-match token table shared by every exact-token-only path (class lists,
12
+ * `[class.X]` bindings, bare TS string literals, style-file regex): the bare scope word (mapped
13
+ * per `scopeClassInList`) plus, for every icon, `<prefix>-<icon.from> -> scope.to-<icon.to>` under
14
+ * *both* the old scope prefix and the new one — a project may already have run the prefix half of
15
+ * a migration (`mc-<name>` -> `kbq-<name>`) without yet getting the name rename, so both starting
16
+ * points have to resolve to the same, final `scope.to`-prefixed value. A pair that resolves to
17
+ * itself (already fully migrated) is never added, so it can't be reported/rewritten as a spurious
18
+ * no-op change.
19
+ */
20
+ function buildIconTokenMap(data) {
21
+ const map = new Map();
22
+ const prefixes = Array.from(new Set([data.scope.from, data.scope.to]));
23
+ const setIfChanged = (key, value) => {
24
+ if (key !== value) {
25
+ map.set(key, value);
26
+ }
27
+ };
28
+ setIfChanged(data.scope.from, data.scopeClassInList === 'remove' ? null : data.scope.to);
29
+ for (const icon of data.icons) {
30
+ for (const prefix of prefixes) {
31
+ setIfChanged(`${prefix}-${icon.from}`, `${data.scope.to}-${icon.to}`);
32
+ }
33
+ }
34
+ return map;
35
+ }
36
+ function exactTokenLookup(tokenMap, found) {
37
+ return (token) => {
38
+ if (!tokenMap.has(token)) {
39
+ return undefined;
40
+ }
41
+ const replacement = tokenMap.get(token);
42
+ found.push({ from: token, to: replacement ?? '' });
43
+ return replacement;
44
+ };
45
+ }
46
+ /**
47
+ * HTML-AST visitor, element-name-agnostic (unlike `ElementCollector`'s single `elementName`),
48
+ * since icon directives attach via attribute selector to any host element. Collects every
49
+ * element carrying at least one relevant attribute — a `valueAttrs` match, a bare `class`
50
+ * attribute, or a `class.X` binding.
51
+ */
52
+ class IconAttributeCollector {
53
+ constructor(data) {
54
+ this.data = data;
55
+ this.elementsToMigrate = [];
56
+ }
57
+ visitElement(el) {
58
+ if (el.attrs?.length) {
59
+ const isRelevant = el.attrs.some((attr) => {
60
+ const name = utils_ast.getSimpleAttributeName(attr.name);
61
+ return (this.data.valueAttrs.includes(name) || name === 'class' || utils_ast.getClassBindingToken(name) !== undefined);
62
+ });
63
+ if (isRelevant) {
64
+ this.elementsToMigrate.push(el);
65
+ }
66
+ }
67
+ this.visitChildren(el);
68
+ }
69
+ visitChildren(el) {
70
+ if (el.children?.length) {
71
+ for (const child of el.children) {
72
+ child.visit(this);
73
+ }
74
+ }
75
+ }
76
+ visitBlock(block, _1) {
77
+ this.visitChildren(block);
78
+ }
79
+ // ICU expansions (`{count, plural, =1 {...} other {...}}`) hold their child nodes on
80
+ // `cases[].expression`, not `children`, so `visitChildren` never reaches them on its own.
81
+ visitExpansion(expansion, _1) {
82
+ utils_ast.visitAll(this, expansion.cases);
83
+ }
84
+ visitExpansionCase(expansionCase, _1) {
85
+ utils_ast.visitAll(this, expansionCase.expression);
86
+ }
87
+ // part of interface
88
+ visitAttribute(_, _1) { }
89
+ visitText(_, _1) { }
90
+ visitComment(_, _1) { }
91
+ visitBlockParameter(_, _1) { }
92
+ visitLetDeclaration(_, _1) { }
93
+ }
94
+ /**
95
+ * If `value` (already trimmed) is a single quoted string literal — starts and ends with the same
96
+ * quote character, with no further occurrence of that character in between — returns the quote
97
+ * used and the unquoted content. Returns `undefined` for anything else, including a concatenation
98
+ * like `'mc-' + name` (which merely starts and ends with a quote).
99
+ */
100
+ function extractQuotedLiteral(value) {
101
+ const trimmed = value.trim();
102
+ if (trimmed.length < 2) {
103
+ return undefined;
104
+ }
105
+ const quote = trimmed[0];
106
+ if ((quote !== "'" && quote !== '"') || trimmed[trimmed.length - 1] !== quote) {
107
+ return undefined;
108
+ }
109
+ const inner = trimmed.slice(1, -1);
110
+ if (inner.includes(quote)) {
111
+ return undefined;
112
+ }
113
+ return { quote, content: inner };
114
+ }
115
+ /**
116
+ * Resolves a bare (non-template) string literal like `'pt-icons word-wrap_16'` or
117
+ * `'pt-icons-word-wrap_16'`, operating on its *raw* source text (escape sequences intact, never
118
+ * decoded) so the replacement can be spliced back in without corrupting them. Only proceeds when
119
+ * the literal contains an unambiguous icon reference — a hyphenated `scope-suffix` token that's a
120
+ * known replacement, or a bare icon suffix *alongside* the scope word elsewhere in the same
121
+ * literal — so a string that merely contains the bare scope word (e.g. `'pt-icons my-custom-thing'`,
122
+ * where `my-custom-thing` isn't a known icon) is left untouched, and a completely unscoped bare
123
+ * suffix (e.g. `'add-to-list_16'` with no scope word anywhere) is never rewritten on its own.
124
+ */
125
+ function resolveBareStringLiteral(rawText, tokenMap, data) {
126
+ const tokens = rawText.split(/\s+/).filter(Boolean);
127
+ const hasScopeWord = tokens.includes(data.scope.from) || tokens.includes(data.scope.to);
128
+ const hasPrefixedIconMatch = tokens.some((token) => tokenMap.has(token) && token !== data.scope.from && token !== data.scope.to);
129
+ const hasBareIconMatch = hasScopeWord && tokens.some((token) => data.icons.some(({ from }) => from === token));
130
+ if (!hasPrefixedIconMatch && !hasBareIconMatch) {
131
+ return undefined;
132
+ }
133
+ const found = [];
134
+ let changed = false;
135
+ const resolved = tokens
136
+ .map((token) => {
137
+ if (tokenMap.has(token)) {
138
+ const replacement = tokenMap.get(token);
139
+ changed = true;
140
+ found.push({ from: token, to: replacement ?? '' });
141
+ return replacement;
142
+ }
143
+ const icon = data.icons.find(({ from }) => from === token);
144
+ if (icon && icon.to !== token) {
145
+ changed = true;
146
+ found.push({ from: token, to: icon.to });
147
+ return icon.to;
148
+ }
149
+ return token;
150
+ })
151
+ .filter((token) => !!token);
152
+ return changed ? { result: resolved.join(' '), found } : undefined;
153
+ }
154
+ /**
155
+ * Template-level transform pluggable into `migrateTemplateWithTransform`. Walks the HTML AST via
156
+ * `IconAttributeCollector` and, for each matched element:
157
+ * - `valueAttrs` match: a bound attribute (`[kbq-icon]="expr"`) whose value isn't a single quoted
158
+ * literal (e.g. a bare identifier, or a concatenation like `'mc-' + name`) is a genuine runtime
159
+ * expression and is warned about, never mutated — same for any value containing `{{ }}`
160
+ * interpolation, bound or not, since it can't be resolved statically either. A quoted literal's
161
+ * replacement is re-wrapped in the same quote character so the result stays a valid Angular
162
+ * expression; a plain attribute's value is spliced in as-is.
163
+ * - `class` attribute: only when it's a plain (unbound) attribute — only exact-matching tokens in
164
+ * the space-separated list are replaced or dropped; unrelated classes, and any `[class]`
165
+ * binding expression, are left untouched.
166
+ * - `class.X` binding: `X` is matched exactly against `tokenMap`. A rename splices just the
167
+ * attribute's name (the `X` part); a token that maps to removal drops the whole attribute
168
+ * (name, value and `=`) since there's no value left to bind.
169
+ * When `fix` is false, performs the same walk without mutating and reports matches via `onFound`.
170
+ */
171
+ function createIconTemplateTransform(data, tokenMap, opts) {
172
+ return async (template, fileName) => {
173
+ const parsed = await utils_typescript.parseTemplate(template);
174
+ if (parsed.tree === undefined) {
175
+ return { fileContent: template, changed: undefined, errors: parsed.errors };
176
+ }
177
+ const visitor = new IconAttributeCollector(data);
178
+ utils_ast.visitAll(visitor, parsed.tree.rootNodes);
179
+ const edits = [];
180
+ const allFound = [];
181
+ const resolveValueToken = (token) => {
182
+ if (tokenMap.has(token)) {
183
+ return tokenMap.get(token);
184
+ }
185
+ if (data.rescopeUnknownValues && token.startsWith(`${data.scope.from}-`)) {
186
+ return `${data.scope.to}-${token.slice(data.scope.from.length + 1)}`;
187
+ }
188
+ return undefined;
189
+ };
190
+ const warn = (message) => {
191
+ opts.warn(`${message}${(fileName && ' File: ' + fileName) || ''}`);
192
+ };
193
+ for (const el of visitor.elementsToMigrate) {
194
+ for (const attr of el.attrs) {
195
+ if (!attr.valueSpan) {
196
+ continue;
197
+ }
198
+ const simpleName = utils_ast.getSimpleAttributeName(attr.name);
199
+ if (data.valueAttrs.includes(simpleName)) {
200
+ if (attr.value.includes('{{')) {
201
+ warn('Element is using an interpolated value. Check the code and change value on your own.');
202
+ continue;
203
+ }
204
+ const isBound = attr.name.startsWith('[');
205
+ const literal = extractQuotedLiteral(attr.value);
206
+ const isDynamicExpression = isBound && literal === undefined;
207
+ if (isDynamicExpression) {
208
+ warn('Element is using a dynamic value. Check the code and change value on your own.');
209
+ continue;
210
+ }
211
+ const cleanValue = literal ? literal.content : attr.value;
212
+ const resolved = resolveValueToken(cleanValue);
213
+ if (resolved) {
214
+ const replacementText = literal ? `${literal.quote}${resolved}${literal.quote}` : resolved;
215
+ edits.push({
216
+ start: attr.valueSpan.start.offset,
217
+ end: attr.valueSpan.end.offset,
218
+ replacement: replacementText
219
+ });
220
+ allFound.push({ from: cleanValue, to: resolved });
221
+ }
222
+ continue;
223
+ }
224
+ if (simpleName === 'class' && !attr.name.startsWith('[')) {
225
+ const found = [];
226
+ const newValue = utils_ast.replaceTokenList(attr.value, exactTokenLookup(tokenMap, found));
227
+ if (newValue !== undefined) {
228
+ edits.push({
229
+ start: attr.valueSpan.start.offset,
230
+ end: attr.valueSpan.end.offset,
231
+ replacement: newValue
232
+ });
233
+ allFound.push(...found);
234
+ }
235
+ continue;
236
+ }
237
+ const bindingToken = utils_ast.getClassBindingToken(simpleName);
238
+ if (bindingToken !== undefined && tokenMap.has(bindingToken)) {
239
+ const replacement = tokenMap.get(bindingToken);
240
+ if (replacement) {
241
+ const newSimpleName = simpleName.replace(new RegExp(`\\.${escapeRegExp(bindingToken)}$`), `.${replacement}`);
242
+ const newName = attr.name.startsWith('[') ? `[${newSimpleName}]` : newSimpleName;
243
+ edits.push({
244
+ start: attr.keySpan.start.offset,
245
+ end: attr.keySpan.end.offset,
246
+ replacement: newName
247
+ });
248
+ }
249
+ else {
250
+ // No replacement — the binding can't be renamed in place, and there's no
251
+ // value left worth keeping either, so drop the whole attribute.
252
+ edits.push({
253
+ start: attr.sourceSpan.start.offset,
254
+ end: attr.sourceSpan.end.offset,
255
+ replacement: ''
256
+ });
257
+ }
258
+ allFound.push({ from: bindingToken, to: replacement ?? '' });
259
+ }
260
+ }
261
+ }
262
+ if (!opts.fix) {
263
+ if (allFound.length > 0) {
264
+ opts.onFound(fileName, allFound);
265
+ }
266
+ return { fileContent: template, changed: false, errors: parsed.errors };
267
+ }
268
+ if (edits.length === 0) {
269
+ return { fileContent: template, changed: false, errors: parsed.errors };
270
+ }
271
+ edits.sort((a, b) => a.start - b.start);
272
+ let updatedTemplate = template;
273
+ let offset = 0;
274
+ for (const edit of edits) {
275
+ updatedTemplate =
276
+ updatedTemplate.slice(0, edit.start - offset) +
277
+ edit.replacement +
278
+ updatedTemplate.slice(edit.end - offset);
279
+ offset += edit.end - edit.start - edit.replacement.length;
280
+ }
281
+ return { fileContent: updatedTemplate, changed: true, errors: parsed.errors };
282
+ };
283
+ }
284
+ function getComponentMetadataObjects(sourceFile) {
285
+ const results = [];
286
+ utils_typescript.forEachClass(sourceFile, (node) => {
287
+ const decorator = ts
288
+ .getDecorators(node)
289
+ ?.find((dec) => ts.isCallExpression(dec.expression) &&
290
+ ts.isIdentifier(dec.expression.expression) &&
291
+ dec.expression.expression.text === 'Component');
292
+ if (!decorator || !ts.isCallExpression(decorator.expression)) {
293
+ return;
294
+ }
295
+ const [arg] = decorator.expression.arguments;
296
+ if (arg && ts.isObjectLiteralExpression(arg)) {
297
+ results.push(arg);
298
+ }
299
+ });
300
+ return results;
301
+ }
302
+ /**
303
+ * Migrates the `host` and `styles` properties of an inline `@Component` decorator — the parts of
304
+ * a component that the HTML-AST template path and `collectInlineTemplateRanges` never see. `host`
305
+ * is matched the same way as a template's `class`/`class.X` attributes; `styles` array entries are
306
+ * plain CSS text, so they're matched with the same word/selector-boundary-safe regex used for
307
+ * `.scss` files.
308
+ *
309
+ * Returns every string-literal range it inspected (`claimedRanges`) — changed or not — so the
310
+ * caller can exclude them from the generic bare-string-literal walk, which would otherwise
311
+ * re-interpret the same text under different (whitespace-token) matching rules.
312
+ */
313
+ function collectHostAndStylesEdits(sourceFile, tokenMap, stylePatterns) {
314
+ const edits = [];
315
+ const found = [];
316
+ const claimedRanges = [];
317
+ for (const metadata of getComponentMetadataObjects(sourceFile)) {
318
+ for (const prop of metadata.properties) {
319
+ if (!ts.isPropertyAssignment(prop) || (!ts.isIdentifier(prop.name) && !ts.isStringLiteralLike(prop.name))) {
320
+ continue;
321
+ }
322
+ const propName = prop.name.text;
323
+ if (propName === 'host' && ts.isObjectLiteralExpression(prop.initializer)) {
324
+ for (const hostProp of prop.initializer.properties) {
325
+ if (!ts.isPropertyAssignment(hostProp) ||
326
+ (!ts.isIdentifier(hostProp.name) && !ts.isStringLiteralLike(hostProp.name))) {
327
+ continue;
328
+ }
329
+ if (ts.isStringLiteralLike(hostProp.name)) {
330
+ claimedRanges.push({
331
+ start: hostProp.name.getStart(sourceFile) + 1,
332
+ end: hostProp.name.getEnd() - 1
333
+ });
334
+ }
335
+ const hostKey = hostProp.name.text;
336
+ const simpleName = utils_ast.getSimpleAttributeName(hostKey);
337
+ if (simpleName === 'class' && ts.isStringLiteralLike(hostProp.initializer)) {
338
+ const start = hostProp.initializer.getStart(sourceFile) + 1;
339
+ const end = hostProp.initializer.getEnd() - 1;
340
+ claimedRanges.push({ start, end });
341
+ const valueFound = [];
342
+ const newValue = utils_ast.replaceTokenList(sourceFile.text.slice(start, end), exactTokenLookup(tokenMap, valueFound));
343
+ if (newValue !== undefined) {
344
+ edits.push({ start, end, replacement: newValue });
345
+ found.push(...valueFound);
346
+ }
347
+ continue;
348
+ }
349
+ const bindingToken = utils_ast.getClassBindingToken(simpleName);
350
+ if (bindingToken !== undefined && tokenMap.has(bindingToken)) {
351
+ const replacement = tokenMap.get(bindingToken);
352
+ if (replacement) {
353
+ const rawName = hostProp.name.getText(sourceFile);
354
+ const quote = rawName[0] === "'" || rawName[0] === '"' ? rawName[0] : '';
355
+ const newSimpleName = simpleName.replace(new RegExp(`\\.${escapeRegExp(bindingToken)}$`), `.${replacement}`);
356
+ const newInner = hostKey.startsWith('[') ? `[${newSimpleName}]` : newSimpleName;
357
+ edits.push({
358
+ start: hostProp.name.getStart(sourceFile),
359
+ end: hostProp.name.getEnd(),
360
+ replacement: quote ? `${quote}${newInner}${quote}` : newInner
361
+ });
362
+ }
363
+ else {
364
+ edits.push({
365
+ start: hostProp.getStart(sourceFile),
366
+ end: hostProp.getEnd(),
367
+ replacement: ''
368
+ });
369
+ }
370
+ found.push({ from: bindingToken, to: replacement ?? '' });
371
+ }
372
+ }
373
+ continue;
374
+ }
375
+ if (propName === 'styles' && ts.isArrayLiteralExpression(prop.initializer)) {
376
+ for (const el of prop.initializer.elements) {
377
+ if (!ts.isStringLiteralLike(el)) {
378
+ continue;
379
+ }
380
+ const start = el.getStart(sourceFile) + 1;
381
+ const end = el.getEnd() - 1;
382
+ claimedRanges.push({ start, end });
383
+ const { content, changed, found: styleFound } = replaceKnownIconTokens(sourceFile.text.slice(start, end), stylePatterns, true);
384
+ if (changed) {
385
+ edits.push({ start, end, replacement: content });
386
+ }
387
+ found.push(...styleFound);
388
+ }
389
+ }
390
+ }
391
+ }
392
+ return { edits, found, claimedRanges };
393
+ }
394
+ /**
395
+ * Migrates icon usage in `.ts` files: inline `@Component({ template: '…' })` strings (via
396
+ * `createIconTemplateTransform`, same as external HTML), the `host`/`styles` decorator properties
397
+ * (via `collectHostAndStylesEdits`), and bare string literals outside of any of those (e.g.
398
+ * `return 'pt-icons ' + iconName;`), matched exact-token-only against `tokenMap`.
399
+ *
400
+ * All edits are computed from the same unmodified `sourceFile`/`content` and applied in a single
401
+ * `beginUpdate`/`commitUpdate` pass per file — `Tree`'s `UpdateRecorder` records changes against
402
+ * the file's original offsets regardless of call order, so this only needs to avoid *overlapping*
403
+ * edits, not sequence them.
404
+ *
405
+ * `filePaths` entries must be paths `tree.readText`/`tree.beginUpdate` can resolve directly (i.e.
406
+ * the file's own tree path, not a real filesystem path) — these migrations run against the
407
+ * schematic `Tree`, not the on-disk project. Each file's `ts.SourceFile` is created here, one at a
408
+ * time, rather than by the caller up front, so the whole tree's ASTs are never held in memory at
409
+ * once.
410
+ *
411
+ * `stylePatterns` (built via `buildIconTokenPatterns`) is used for the CSS text inside an inline
412
+ * `styles` array — pass patterns built from a separate map when a bare scope word should be
413
+ * *renamed* there even though it's *dropped* from HTML class lists (see `deprecated-icons/index.ts`).
414
+ */
415
+ async function migrateIconsInTsFiles(tree, filePaths, context, data, tokenMap, opts, stylePatterns) {
416
+ for (const relativePath of filePaths) {
417
+ const content = tree.readText(relativePath);
418
+ const sourceFile = ts.createSourceFile(relativePath, content, ts.ScriptTarget.Latest, true);
419
+ const templateRanges = utils_typescript.collectInlineTemplateRanges(sourceFile);
420
+ const edits = [];
421
+ const allFound = [];
422
+ const templateTransform = createIconTemplateTransform(data, tokenMap, {
423
+ fix: opts.fix,
424
+ onFound: (_fileName, found) => allFound.push(...found),
425
+ warn: opts.warn
426
+ });
427
+ for (const range of templateRanges) {
428
+ const template = content.slice(range.start, range.end);
429
+ const { fileContent, changed, errors } = await templateTransform(template, relativePath);
430
+ if (errors.length > 0) {
431
+ context.logger.error(errors.map(({ error }) => error.toString()).join('\n'));
432
+ }
433
+ if (changed) {
434
+ edits.push({ start: range.start, end: range.end, replacement: fileContent });
435
+ }
436
+ }
437
+ const hostAndStyles = collectHostAndStylesEdits(sourceFile, tokenMap, stylePatterns);
438
+ if (opts.fix) {
439
+ edits.push(...hostAndStyles.edits);
440
+ }
441
+ allFound.push(...hostAndStyles.found);
442
+ const claimedRanges = [...templateRanges, ...hostAndStyles.claimedRanges];
443
+ utils_typescript.forEachStringLiteral(sourceFile, (node) => {
444
+ const start = node.getStart(sourceFile) + 1;
445
+ const end = node.getEnd() - 1;
446
+ const isClaimed = claimedRanges.some((range) => start >= range.start && end <= range.end);
447
+ if (isClaimed) {
448
+ return;
449
+ }
450
+ const resolved = resolveBareStringLiteral(content.slice(start, end), tokenMap, data);
451
+ if (resolved !== undefined) {
452
+ if (opts.fix) {
453
+ edits.push({ start, end, replacement: resolved.result });
454
+ }
455
+ allFound.push(...resolved.found);
456
+ }
457
+ });
458
+ if (!opts.fix) {
459
+ if (allFound.length > 0) {
460
+ opts.onFound(relativePath, allFound);
461
+ }
462
+ continue;
463
+ }
464
+ if (edits.length === 0) {
465
+ continue;
466
+ }
467
+ const update = tree.beginUpdate(relativePath);
468
+ for (const edit of edits) {
469
+ update.remove(edit.start, edit.end - edit.start);
470
+ update.insertLeft(edit.start, edit.replacement);
471
+ }
472
+ tree.commitUpdate(update);
473
+ }
474
+ }
475
+ /**
476
+ * Precompiles the word/selector-boundary-safe regex for every `tokenMap` entry once, so a run over
477
+ * many style/markdown files (and inline `styles` arrays) can reuse the same `IconTokenPattern[]`
478
+ * instead of rebuilding every regex — there can be 1000+ tokens — on every single file.
479
+ */
480
+ function buildIconTokenPatterns(tokenMap,
481
+ /** The bare, unprefixed scope word (`data.scope.from`), if present in `tokenMap`. Unlike every
482
+ * other token — which is a specific, hyphenated `scope-suffix` string — this one is short and
483
+ * generic enough (e.g. `mc`) to collide with unrelated identifiers (`$mc: red;`,
484
+ * `@import 'mc'`, `font-family: mc;`). Matched only when immediately preceded by `.`, so it
485
+ * still catches a real class selector (`.mc {}`) without touching those. */
486
+ bareScopeWord) {
487
+ return Array.from(tokenMap, ([token, replacement]) => ({
488
+ token,
489
+ replacement,
490
+ pattern: token === bareScopeWord
491
+ ? new RegExp(`(?<=\\.)${escapeRegExp(token)}(?![\\w-])`, 'g')
492
+ : new RegExp(`(?<![\\w-])${escapeRegExp(token)}(?![\\w-])`, 'g')
493
+ }));
494
+ }
495
+ /**
496
+ * Word/selector-boundary-safe regex replace over raw style-file text, using patterns precompiled
497
+ * by `buildIconTokenPatterns` — e.g. `.pt-icons {}` and `@extend .pt-icons;` match (preceded by
498
+ * `.`, a non-word/non-hyphen char), but `.my-widget-pt-icons-preview` and `mc-word-wrap_16-suffix`
499
+ * do not. When `fix` is false, only collects `found` without mutating.
500
+ *
501
+ * Each pattern's `lastIndex` is reset before use: a global regex's `String.replace` resets it
502
+ * internally, but `RegExp.test` does not — reusing the same pattern object (the whole point of
503
+ * precompiling once) across multiple, unrelated `content` strings would otherwise carry a match
504
+ * position from one file into the next and silently miss real matches in the next file.
505
+ */
506
+ function replaceKnownIconTokens(content, patterns, fix) {
507
+ const found = [];
508
+ let result = content;
509
+ for (const { token, replacement, pattern } of patterns) {
510
+ pattern.lastIndex = 0;
511
+ if (fix) {
512
+ const next = result.replace(pattern, replacement ?? '');
513
+ if (next !== result) {
514
+ found.push({ from: token, to: replacement ?? '' });
515
+ result = next;
516
+ }
517
+ }
518
+ else if (pattern.test(content)) {
519
+ found.push({ from: token, to: replacement ?? '' });
520
+ }
521
+ }
522
+ return { content: fix ? result : content, changed: fix && found.length > 0, found };
523
+ }
524
+
525
+ exports.IconAttributeCollector = IconAttributeCollector;
526
+ exports.buildIconTokenMap = buildIconTokenMap;
527
+ exports.buildIconTokenPatterns = buildIconTokenPatterns;
528
+ exports.createIconTemplateTransform = createIconTemplateTransform;
529
+ exports.migrateIconsInTsFiles = migrateIconsInTsFiles;
530
+ exports.replaceKnownIconTokens = replaceKnownIconTokens;
@@ -12,6 +12,48 @@ function forEachClass(sourceFile, callback) {
12
12
  node.forEachChild(walk);
13
13
  });
14
14
  }
15
+ /** Walks every string literal (`'...'`/`"..."` or a template literal with no substitutions) in a source file. */
16
+ function forEachStringLiteral(sourceFile, callback) {
17
+ sourceFile.forEachChild(function walk(node) {
18
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
19
+ callback(node);
20
+ }
21
+ node.forEachChild(walk);
22
+ });
23
+ }
24
+ /**
25
+ * Interior `[start, end)` ranges of inline `@Component({ template: '…' })` string literals.
26
+ *
27
+ * Discovered through the AST rather than by scanning for `template:`, so a `template` property of
28
+ * some other object, or one mentioned in a comment or a string, is never mistaken for a template.
29
+ * A template literal with a `${…}` substitution is skipped — its text is not a template on its own.
30
+ */
31
+ function collectInlineTemplateRanges(sourceFile) {
32
+ const ranges = [];
33
+ forEachClass(sourceFile, (node) => {
34
+ const decorator = ts
35
+ .getDecorators(node)
36
+ ?.find((dec) => ts.isCallExpression(dec.expression) &&
37
+ ts.isIdentifier(dec.expression.expression) &&
38
+ dec.expression.expression.text === 'Component');
39
+ if (!decorator || !ts.isCallExpression(decorator.expression))
40
+ return;
41
+ const [arg] = decorator.expression.arguments;
42
+ if (!arg || !ts.isObjectLiteralExpression(arg))
43
+ return;
44
+ for (const prop of arg.properties) {
45
+ if (ts.isPropertyAssignment(prop) &&
46
+ (ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name)) &&
47
+ prop.name.text === 'template' &&
48
+ ts.isStringLiteralLike(prop.initializer) &&
49
+ prop.initializer.text) {
50
+ // +1 / -1 to exclude the opening/closing quote characters.
51
+ ranges.push({ start: prop.initializer.getStart(sourceFile) + 1, end: prop.initializer.getEnd() - 1 });
52
+ }
53
+ }
54
+ });
55
+ return ranges;
56
+ }
15
57
  function canMigrateFile(sourceFile, program) {
16
58
  // We shouldn't migrate .d.ts files, files from an external library or type checking files.
17
59
  return !(sourceFile.fileName.endsWith('.ngtypecheck.ts') ||
@@ -165,7 +207,9 @@ exports.AnalyzedFile = AnalyzedFile;
165
207
  exports.ElementCollector = ElementCollector;
166
208
  exports.LEADING_TRIVIA_CHARS = LEADING_TRIVIA_CHARS;
167
209
  exports.canMigrateFile = canMigrateFile;
210
+ exports.collectInlineTemplateRanges = collectInlineTemplateRanges;
168
211
  exports.forEachClass = forEachClass;
212
+ exports.forEachStringLiteral = forEachStringLiteral;
169
213
  exports.getClassWithUpdatedDecorator = getClassWithUpdatedDecorator;
170
214
  exports.parseTemplate = parseTemplate;
171
215
  exports.updateDecoratorProperty = updateDecoratorProperty;
@@ -1,5 +1,5 @@
1
1
  import * as overlayscrollbars from 'overlayscrollbars';
2
- import { EventListeners, PartialOptions, InitializationTarget, EventListenerArgs, OverlayScrollbars } from 'overlayscrollbars';
2
+ import { PartialOptions, EventListeners, InitializationTarget, EventListenerArgs, OverlayScrollbars } from 'overlayscrollbars';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, Provider, AfterViewInit, OnDestroy, ElementRef, EventEmitter } from '@angular/core';
5
5
  import { KbqOverflowShadowSource } from '@koobiq/components/core';
@@ -35,6 +35,8 @@ declare class KbqSearchExpandable implements ControlValueAccessor, AfterViewInit
35
35
  /** Current value in input. */
36
36
  value: BehaviorSubject<string>;
37
37
  protected lastFocusOrigin: 'touch' | 'mouse' | 'keyboard' | 'program' | null;
38
+ /** Suppress the automatic input focus once — set when the component auto-opens from a model value. */
39
+ private suppressInputFocus;
38
40
  /** state of component. */
39
41
  isOpened: boolean;
40
42
  /** Emit event by enter or not. Default is false */
@@ -77,6 +79,7 @@ declare class KbqSearchExpandable implements ControlValueAccessor, AfterViewInit
77
79
  /** Implemented as part of ControlValueAccessor. */
78
80
  setDisabledState(isDisabled: boolean): void;
79
81
  toggle(): void;
82
+ private setOpened;
80
83
  private updateLocaleParams;
81
84
  private initDefaultParams;
82
85
  private emitValue;