@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
package/list/list.scss CHANGED
@@ -1,12 +1,13 @@
1
+ @use '../core/option/action-container-mixins' as action-container;
1
2
  @use '../core/styles/common/list';
2
3
  @use '../core/styles/common/vendor-prefixes';
3
4
  @use './list-theme' as theme;
4
5
 
5
- @mixin _kbq-list-item-border-radius() {
6
+ /// Squares off the corners between adjacent selected rows so a run of them reads as one block.
7
+ /// Only `.kbq-list-option` is ever selectable, hence the option-only scope.
8
+ @mixin _kbq-list-option-selected-border-radius() {
6
9
  $selectors: '.kbq-selected';
7
10
 
8
- border-radius: var(--kbq-size-border-radius);
9
-
10
11
  &:is(#{$selectors}):has(+ :is(#{$selectors})) {
11
12
  border-bottom-left-radius: unset;
12
13
  border-bottom-right-radius: unset;
@@ -24,25 +25,198 @@
24
25
  outline: none;
25
26
  }
26
27
 
28
+ // `horizontal` rebinds Left/Right arrows on the key manager; without a row layout the visual order
29
+ // would disagree with the focus order.
30
+ .kbq-list-selection_horizontal {
31
+ display: flex;
32
+ flex-direction: row;
33
+
34
+ .kbq-list-option {
35
+ flex: 0 0 auto;
36
+ }
37
+ }
38
+
39
+ // Positioning context for the drop indicator, which is the list's own last child.
40
+ .kbq-list-selection {
41
+ position: relative;
42
+ }
43
+
27
44
  .kbq-list-item,
28
45
  .kbq-list-option {
29
46
  @include vendor-prefixes.user-select(none);
30
47
  @include list.kbq-list-item-base();
31
- @include _kbq-list-item-border-radius();
48
+
49
+ // Re-declared on top of the shared base so the `--kbq-list-size-*` tokens become real override
50
+ // points. `.kbq-list-option` is guaranteed to be inside `.kbq-list-selection` (it injects it),
51
+ // but `.kbq-list-item`/`a[kbq-list-item]` has no such enforcement, so every token here falls back
52
+ // to the shared base mixin's own value in case it's ever rendered outside `.kbq-list`.
53
+ // The horizontal padding subtracts the transparent focus border so the total stays on the grid.
54
+ border-width: var(--kbq-list-size-container-focus-outline-width, var(--kbq-size-3xs));
55
+ border-radius: var(--kbq-size-border-radius);
56
+ gap: var(--kbq-list-size-container-content-gap-horizontal, var(--kbq-size-s));
57
+ padding-top: var(--kbq-list-size-container-padding-vertical, var(--kbq-size-xxs));
58
+ padding-right: calc(
59
+ var(--kbq-list-size-container-padding-right, var(--kbq-size-m)) - var(
60
+ --kbq-list-size-container-focus-outline-width,
61
+ var(--kbq-size-3xs)
62
+ )
63
+ );
64
+ padding-bottom: var(--kbq-list-size-container-padding-vertical, var(--kbq-size-xxs));
65
+ padding-left: calc(
66
+ var(--kbq-list-size-container-padding-left, var(--kbq-size-m)) - var(
67
+ --kbq-list-size-container-focus-outline-width,
68
+ var(--kbq-size-3xs)
69
+ )
70
+ );
71
+
72
+ .kbq-list-option-caption {
73
+ padding-top: var(--kbq-list-size-container-content-gap-vertical, var(--kbq-size-3xs));
74
+ }
32
75
 
33
76
  &:not(.kbq-disabled) {
34
77
  cursor: pointer;
78
+ }
79
+ }
80
+
81
+ // `cursor: grabbing` goes on the body rather than on the dragged element, which CDK hides from hit
82
+ // testing anyway: https://github.com/angular/components/blob/18.2.14/src/cdk/drag-drop/resets.scss#L14-L15
83
+ // The body alone is not enough, though: the cursor is resolved from whatever sits under the pointer, and
84
+ // during a drag that is another option, whose own `grab` outranks anything the body sets.
85
+ // Keyed on the preview because it is in the DOM for as long as the drag lasts, and unlike
86
+ // `cdk-drop-list-dragging` it is not an Angular host binding waiting on change detection. The class
87
+ // comes from `CdkDrag.previewClass`, so the hook holds whichever shape `dragPreview` gives the preview.
88
+ body:has(.kbq-list-drag-preview) {
89
+ cursor: grabbing;
90
+
91
+ // Qualified with both classes so it outranks the `kbq-list-option_grab-cursor` rule below: that one
92
+ // adds a `:not(:has())`, which counts as a class, and a row that advertises the grab would otherwise
93
+ // keep advertising it mid-drag.
94
+ .kbq-list-option.kbq-list-option_draggable,
95
+ .kbq-list-option_draggable .cdk-drag-handle {
96
+ cursor: grabbing;
97
+ }
98
+
99
+ // What is being dragged into is the list, not the row under the pointer, so a row that lights up on
100
+ // hover promises a reaction it will not deliver. Taking the options out of hit testing drops `:hover`
101
+ // and `:active` in one go, and keeps a stray click off the action button mid-drag.
102
+ .kbq-list-option {
103
+ pointer-events: none;
104
+ }
105
+
106
+ // A group header is the one thing left inside a list that still resolves a cursor of its own: it is
107
+ // not an option, so nothing takes it out of hit testing, and `.kbq-optgroup-label` sets `default`.
108
+ // The pointer crossing a header is still over the list, so it must not read as leaving it — over a
109
+ // `kbq-divider`, which sets no cursor at all, it already does not. `inherit` rather than `grabbing`
110
+ // so the header also follows the body into `no-drop` below.
111
+ .kbq-optgroup-label {
112
+ cursor: inherit;
113
+ }
35
114
 
36
- &.kbq-hovered,
37
- &:hover,
38
- &.kbq-focused,
39
- &.kbq-action-button-focused {
40
- & .kbq-action-container {
41
- display: flex;
42
- }
43
- }
115
+ // Nothing under the pointer would take the option, so stop promising a drop. Nested rather than
116
+ // written as a sibling: it has to outrank `grabbing` above on specificity, not on source order.
117
+ &:has(.kbq-list-option_drop-forbidden) {
118
+ cursor: no-drop;
44
119
  }
45
120
  }
46
121
 
122
+ // Option-only: `.kbq-selected` never lands on `.kbq-list-item`, and only options host an action container.
123
+ .kbq-list-option {
124
+ @include _kbq-list-option-selected-border-radius();
125
+ @include action-container.kbq-option-action-visibility('.kbq-list-selection.cdk-keyboard-focused');
126
+
127
+ // A projected handle is the one thing that always advertises the grab: it exists to say where the
128
+ // row can be picked up.
129
+ &.kbq-list-option_draggable .cdk-drag-handle {
130
+ cursor: grab;
131
+ }
132
+
133
+ // The row itself only advertises it when the list asks, through `dragCursor="grab"` — being
134
+ // draggable is not on its own a reason to stop looking like the selectable row it also is. Matched
135
+ // on as many classes as the rule this replaces, so the drag-time `grabbing` above keeps outranking
136
+ // it by its one element, and nothing else in the cascade shifts.
137
+ &.kbq-list-option_grab-cursor:not(:has(.cdk-drag-handle)) {
138
+ cursor: grab;
139
+ }
140
+
141
+ // The row the drag started from keeps its slot. `cdk-drag-anchor` is the clone CDK leaves behind
142
+ // once the option has been dragged into a connected list.
143
+ &.cdk-drag-placeholder,
144
+ &.cdk-drag-anchor {
145
+ opacity: var(--kbq-opacity-disabled);
146
+ }
147
+
148
+ // Hidden while the placeholder sits in a list the option was not dragged from, so that list keeps
149
+ // its layout. Toggled from `KbqListSelection` on `cdkDropListEntered`.
150
+ &.kbq-list-option_foreign-placeholder {
151
+ display: none;
152
+ }
153
+
154
+ // No `.cdk-drag-animating` transition on purpose. CDK animates the preview onto the placeholder,
155
+ // which — with sorting disabled — never leaves the slot the drag started from, so the preview
156
+ // would fly back to the origin and read as a rejected drop. With no transition declared the
157
+ // duration is 0 and CDK disposes of the preview without animating it anywhere.
158
+ }
159
+
160
+ // Marks the gap the dragged option would land in. Sorting is disabled, so the list never opens a gap
161
+ // of its own and this line is the only feedback about the target position.
162
+ .kbq-list-selection__drop-indicator {
163
+ position: absolute;
164
+ background: var(--kbq-list-states-dragged-indicator-color);
165
+ pointer-events: none;
166
+ }
167
+
168
+ .kbq-list-selection:not(.kbq-list-selection_horizontal) .kbq-list-selection__drop-indicator {
169
+ left: 0;
170
+ right: 0;
171
+ // The line straddles the gap instead of sitting below it, so it reads as "between these two rows".
172
+ top: calc(var(--kbq-list-drop-indicator-offset, 0px) - var(--kbq-list-size-dragged-indicator-thickness) / 2);
173
+ block-size: var(--kbq-list-size-dragged-indicator-thickness);
174
+ }
175
+
176
+ .kbq-list-selection_horizontal .kbq-list-selection__drop-indicator {
177
+ top: 0;
178
+ bottom: 0;
179
+ left: calc(var(--kbq-list-drop-indicator-offset, 0px) - var(--kbq-list-size-dragged-indicator-thickness) / 2);
180
+ inline-size: var(--kbq-list-size-dragged-indicator-thickness);
181
+ }
182
+
47
183
  @include theme.kbq-list-theme();
48
184
  @include theme.kbq-list-typography();
185
+
186
+ // Declared after the theme so it outranks the default state, whose background is `transparent`
187
+ // because an option normally sits on the surface of its list. The dragged clone has no such surface.
188
+ // Selected states carry a higher specificity and deliberately keep their own background.
189
+ .kbq-list-option.cdk-drag-preview {
190
+ background: var(--kbq-list-states-dragged-container-background);
191
+ box-shadow: var(--kbq-list-states-dragged-container-shadow);
192
+ }
193
+
194
+ // The default preview: a plate of the option's text rather than a clone of the row. It is inserted into
195
+ // `<body>`, so it inherits nothing from the list and carries its own tokens — the same reason the clone
196
+ // is listed in `list-tokens.scss`. No width: CDK pins the preview `position: fixed`, which already
197
+ // shrinks the box to its content, so only the maximum is worth stating. No `transition` either, for the
198
+ // same reason as the clone above — a zero duration lets CDK drop the preview instead of flying it back.
199
+ .kbq-list-drag-preview_text {
200
+ // The cap is on the plate, not on the text inside it, and the page default here is `content-box`.
201
+ box-sizing: border-box;
202
+ max-inline-size: var(--kbq-list-size-drag-preview-max-width);
203
+ border-radius: var(--kbq-size-border-radius);
204
+ padding: var(--kbq-list-size-container-padding-vertical) var(--kbq-list-size-container-padding-right)
205
+ var(--kbq-list-size-container-padding-vertical) var(--kbq-list-size-container-padding-left);
206
+ background: var(--kbq-list-states-drag-preview-background);
207
+ box-shadow: var(--kbq-list-states-dragged-container-shadow);
208
+ color: var(--kbq-list-default-text-color);
209
+
210
+ // Clamped line by line, not on the plate: a long label must not push the caption out of the box.
211
+ .kbq-list-drag-preview__label,
212
+ .kbq-list-drag-preview__caption {
213
+ overflow: hidden;
214
+ text-overflow: ellipsis;
215
+ white-space: nowrap;
216
+ }
217
+
218
+ .kbq-list-drag-preview__caption {
219
+ color: var(--kbq-list-default-caption-color);
220
+ padding-top: var(--kbq-list-size-container-content-gap-vertical);
221
+ }
222
+ }
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Provider, OnDestroy } from '@angular/core';
2
+ import { InjectionToken, OnDestroy, Provider } from '@angular/core';
3
3
  import { SafeHtml } from '@angular/platform-browser';
4
4
  import { MarkedOptions } from 'marked';
5
5
 
package/modal/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import * as i0 from '@angular/core';
3
3
  import { TemplateRef, Type, EventEmitter, Injector, OnInit, OnChanges, AfterViewInit, OnDestroy, ElementRef, ViewContainerRef, SimpleChanges, ComponentRef, PipeTransform, InjectionToken } from '@angular/core';
4
- import * as i1 from '@angular/cdk/overlay';
4
+ import * as i1$1 from '@angular/cdk/overlay';
5
5
  import { OverlayRef, Overlay } from '@angular/cdk/overlay';
6
- import { KbqComponentColors } from '@koobiq/components/core';
6
+ import * as i1 from '@koobiq/components/core';
7
+ import { KbqComponentColors, KbqOverflowShadowState } from '@koobiq/components/core';
7
8
  import * as i2 from '@angular/cdk/a11y';
8
9
  import * as i3 from '@koobiq/components/button';
9
10
  import * as i4 from '@koobiq/components/icon';
@@ -141,6 +142,13 @@ declare class KbqModalComponent<T = any, R = any> extends KbqModalRef<T, R> impl
141
142
  maskAnimationClassMap: object | null;
142
143
  modalAnimationClassMap: object | null;
143
144
  transformOrigin: string;
145
+ /**
146
+ * Scroll-shadow state published by `KbqModalBody` when the modal content is composed
147
+ * manually (`kbq-modal-title`/`kbq-modal-body`/`kbq-modal-footer`), so the header/footer
148
+ * can render matching shadows without a direct template reference between them.
149
+ * @docs-private
150
+ */
151
+ readonly bodyOverflow: i0.WritableSignal<KbqOverflowShadowState>;
144
152
  get afterOpen(): Observable<void>;
145
153
  /** Observable alias for `kbqBeforeClose` */
146
154
  get beforeClose(): Observable<R | undefined>;
@@ -250,11 +258,20 @@ declare class KbqModalTitle {
250
258
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqModalTitle, never>;
251
259
  static ɵcmp: i0.ɵɵComponentDeclaration<KbqModalTitle, "[kbq-modal-title], kbq-modal-title, [kbqModalTitle]", never, {}, {}, never, ["*"], true, never>;
252
260
  }
261
+ /**
262
+ * Scrollable body of a manually composed modal (`kbqComponent`). Publishes its scroll-shadow
263
+ * state onto the modal instance so a sibling `KbqModalTitle`/`KbqModalFooter` can render the
264
+ * matching shadow, since they have no direct template reference to this element.
265
+ */
253
266
  declare class KbqModalBody {
267
+ private readonly modal;
268
+ private readonly overflowContainer;
269
+ constructor();
254
270
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqModalBody, never>;
255
- static ɵdir: i0.ɵɵDirectiveDeclaration<KbqModalBody, "[kbq-modal-body], kbq-modal-body, [kbqModalBody]", never, {}, {}, never, never, true, never>;
271
+ static ɵdir: i0.ɵɵDirectiveDeclaration<KbqModalBody, "[kbq-modal-body], kbq-modal-body, [kbqModalBody]", never, {}, {}, never, never, true, [{ directive: typeof i1.KbqOverflowShadowContainer; inputs: {}; outputs: {}; }]>;
256
272
  }
257
273
  declare class KbqModalFooter {
274
+ protected modal: KbqModalComponent<any, any>;
258
275
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqModalFooter, never>;
259
276
  static ɵdir: i0.ɵɵDirectiveDeclaration<KbqModalFooter, "[kbq-modal-footer], kbq-modal-footer, [kbqModalFooter]", never, {}, {}, never, never, true, never>;
260
277
  }
@@ -271,7 +288,7 @@ declare class CssUnitPipe implements PipeTransform {
271
288
 
272
289
  declare class KbqModalModule {
273
290
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqModalModule, never>;
274
- static ɵmod: i0.ɵɵNgModuleDeclaration<KbqModalModule, never, [typeof i1.OverlayModule, typeof i2.A11yModule, typeof i3.KbqButtonModule, typeof i4.KbqIconModule, typeof i5.KbqTitleModule, typeof i6.NgTemplateOutlet, typeof KbqModalComponent, typeof KbqModalTitle, typeof KbqModalBody, typeof KbqModalFooter, typeof CssUnitPipe, typeof KbqModalMainAction], [typeof KbqModalComponent, typeof KbqModalTitle, typeof KbqModalBody, typeof KbqModalFooter, typeof KbqModalMainAction]>;
291
+ static ɵmod: i0.ɵɵNgModuleDeclaration<KbqModalModule, never, [typeof i1$1.OverlayModule, typeof i2.A11yModule, typeof i3.KbqButtonModule, typeof i4.KbqIconModule, typeof i5.KbqTitleModule, typeof i6.NgTemplateOutlet, typeof KbqModalComponent, typeof KbqModalTitle, typeof KbqModalBody, typeof KbqModalFooter, typeof CssUnitPipe, typeof KbqModalMainAction], [typeof KbqModalComponent, typeof KbqModalTitle, typeof KbqModalBody, typeof KbqModalFooter, typeof KbqModalMainAction]>;
275
292
  static ɵinj: i0.ɵɵInjectorDeclaration<KbqModalModule>;
276
293
  }
277
294
 
@@ -2,7 +2,7 @@
2
2
  --kbq-modal-size-small-width: 400px;
3
3
  --kbq-modal-size-medium-width: 640px;
4
4
  --kbq-modal-size-large-width: 960px;
5
- --kbq-modal-size-border-radius: var(--kbq-size-s);
5
+ --kbq-modal-size-border-radius: var(--kbq-size-m);
6
6
  --kbq-modal-size-header-padding-vertical: var(--kbq-size-l);
7
7
  --kbq-modal-size-header-padding-left: var(--kbq-size-xxl);
8
8
  --kbq-modal-size-header-padding-right: var(--kbq-size-6xl);
@@ -55,17 +55,19 @@
55
55
  &.kbq-navbar-bento .kbq-navbar-item__title {
56
56
  margin-left: -8px;
57
57
  opacity: 0;
58
+
59
+ transition:
60
+ opacity 150ms ease-out,
61
+ margin-left 150ms ease-out;
58
62
  }
59
63
 
60
64
  &.kbq-navbar-bento:hover,
61
- &.kbq-navbar-bento.cdk-keyboard-focused {
65
+ &.kbq-navbar-bento.cdk-keyboard-focused,
66
+ &.kbq-navbar-bento.kbq-active,
67
+ &.kbq-navbar-bento.kbq-pressed {
62
68
  & .kbq-navbar-item__title {
63
69
  margin-left: 0;
64
70
  opacity: 1;
65
-
66
- transition:
67
- opacity 150ms ease-out,
68
- margin-left 150ms ease-out;
69
71
  }
70
72
  }
71
73
  }
package/navbar/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import * as i2 from '@koobiq/components/tooltip';
4
4
  import { KbqTooltipTrigger } from '@koobiq/components/tooltip';
5
5
  import * as i1 from '@angular/cdk/a11y';
6
6
  import { FocusOrigin, FocusMonitor } from '@angular/cdk/a11y';
7
- import { KbqButtonCssStyler, KbqButton } from '@koobiq/components/button';
7
+ import { KbqButton, KbqButtonCssStyler } from '@koobiq/components/button';
8
8
  import * as _koobiq_components_core from '@koobiq/components/core';
9
9
  import { IFocusableOption, FocusKeyManager } from '@koobiq/components/core';
10
10
  import { KbqFormField } from '@koobiq/components/form-field';
@@ -31,11 +31,25 @@ declare class KbqNavbarTitle implements AfterViewInit {
31
31
  private readonly window;
32
32
  readonly hovered: Subject<boolean>;
33
33
  outerElementWidth: number;
34
- isTextOverflown: boolean;
35
34
  get text(): string;
35
+ /** Whether the text is clipped horizontally (e.g. by `text-overflow: ellipsis`). */
36
36
  get isOverflown(): boolean;
37
+ /**
38
+ * Whether the text is clipped vertically (e.g. by `-webkit-line-clamp`).
39
+ *
40
+ * Kept separate from `isOverflown` on purpose: wrapped text never exceeds its width, so only this
41
+ * check catches a clamped title. `isOverflown` is also read by `KbqNavbarItem`, whose titles are
42
+ * always `nowrap` — folding the two together would let sub-pixel line-height rounding enable
43
+ * tooltips on every navbar item.
44
+ */
45
+ get isClamped(): boolean;
46
+ /**
47
+ * Outer width of the title: its border box plus horizontal margins.
48
+ *
49
+ * Measured with `getBoundingClientRect()` rather than `getComputedStyle().width`, which resolves
50
+ * to the used content-box width whatever `box-sizing` says and would drop the title's padding.
51
+ */
37
52
  getOuterElementWidth(): number;
38
- checkTextOverflown(): void;
39
53
  ngAfterViewInit(): void;
40
54
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqNavbarTitle, never>;
41
55
  static ɵdir: i0.ɵɵDirectiveDeclaration<KbqNavbarTitle, "kbq-navbar-title, [kbq-navbar-title]", never, {}, {}, never, never, true, never>;
@@ -93,6 +107,15 @@ declare class KbqNavbarRectangleElement {
93
107
  set collapsed(value: boolean);
94
108
  private _collapsed;
95
109
  readonly button: i0.Signal<KbqButtonCssStyler | undefined>;
110
+ /**
111
+ * Outer width of the item: its border box plus horizontal margins.
112
+ *
113
+ * Measured with `getBoundingClientRect()` rather than `getComputedStyle().width`, which resolves
114
+ * to the used content-box width whatever `box-sizing` says. `.kbq-navbar-item` is `border-box`
115
+ * with horizontal padding, so the computed value dropped that padding from every item, while
116
+ * `KbqNavbar.width` — the figure this sum is compared against — has always been a border box.
117
+ * The navbar therefore under-counted its own content and collapsed later than it should.
118
+ */
96
119
  getOuterElementWidth(): number;
97
120
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqNavbarRectangleElement, never>;
98
121
  static ɵdir: i0.ɵɵDirectiveDeclaration<KbqNavbarRectangleElement, "kbq-navbar-item, [kbq-navbar-item], kbq-navbar-divider, kbq-navbar-brand, [kbq-navbar-brand]", never, {}, {}, ["button"], never, true, never>;
@@ -143,10 +166,26 @@ declare class KbqNavbarBrand extends KbqTooltipTrigger implements AfterContentIn
143
166
  protected readonly rectangleElement: KbqNavbarRectangleElement;
144
167
  /** @docs-private */
145
168
  protected readonly navbarFocusableItem: KbqNavbarFocusableItem;
169
+ private readonly isBrowser;
170
+ private readonly resizeObserver;
171
+ private readonly contentObserver;
172
+ private readonly document;
173
+ private readonly injector;
174
+ private readonly debounceInterval;
146
175
  /** @docs-private */
147
176
  readonly title: i0.Signal<KbqNavbarTitle | undefined>;
148
- /** alternative display of the brand name in two lines */
149
- readonly longTitle: i0.InputSignal<boolean>;
177
+ /** Whether the title has been measured as not fitting into a single line. */
178
+ private readonly autoLongTitle;
179
+ /**
180
+ * Alternative display of the brand name in two lines.
181
+ *
182
+ * @deprecated The mode is now detected automatically when the title does not fit into a single line.
183
+ * Leave unset for auto-detection; `true` and `false` force the mode on and off respectively.
184
+ * Will be removed in the next major release.
185
+ */
186
+ readonly longTitle: i0.InputSignal<boolean | undefined>;
187
+ /** @docs-private */
188
+ protected readonly longTitleEnabled: i0.Signal<boolean>;
150
189
  /** text that will be displayed in the tooltip. By default, the text is taken from kbq-navbar-title. */
151
190
  get collapsedText(): string;
152
191
  set collapsedText(value: string);
@@ -168,6 +207,16 @@ declare class KbqNavbarBrand extends KbqTooltipTrigger implements AfterContentIn
168
207
  constructor();
169
208
  ngAfterContentInit(): void;
170
209
  private updateTooltip;
210
+ private observeLongTitle;
211
+ private updateAutoLongTitle;
212
+ /**
213
+ * Measures whether the title fits into a single line in the *default* presentation.
214
+ *
215
+ * Removing and restoring the class within a single task is safe: the browser does not paint mid-task, and
216
+ * Angular's host binding only writes to the DOM when its value changes, which it does not here. Both rely
217
+ * on there being no CSS transition on the title - add one and this starts to flicker.
218
+ */
219
+ private measureNeedsLongTitle;
171
220
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqNavbarBrand, never>;
172
221
  static ɵcmp: i0.ɵɵComponentDeclaration<KbqNavbarBrand, "kbq-navbar-brand, [kbq-navbar-brand]", ["kbqNavbarBrand"], { "longTitle": { "alias": "longTitle"; "required": false; "isSignal": true; }; "collapsedText": { "alias": "collapsedText"; "required": false; }; }, {}, ["title"], ["*"], true, never>;
173
222
  }
@@ -46,6 +46,14 @@ a.kbq-navbar-brand {
46
46
  }
47
47
 
48
48
  & .kbq-navbar-title {
49
+ // 154px is a raw number, and it caps the text rather than the box: the padding beside it is
50
+ // spacing towards the collapse control, not part of the budget. Under a global `border-box`
51
+ // reset in a consuming application the cap would absorb the padding and a long brand title
52
+ // would start truncating 12px earlier.
53
+ box-sizing: content-box;
54
+
55
+ max-width: 154px;
56
+
49
57
  padding-right: var(--kbq-size-m);
50
58
  }
51
59
  }
@@ -85,14 +93,19 @@ a.kbq-navbar-brand {
85
93
  display: none !important;
86
94
  }
87
95
  }
96
+ }
88
97
 
89
- &.kbq-navbar-brand_long-title .kbq-navbar-title {
90
- display: -webkit-box;
91
-
92
- -webkit-box-orient: vertical;
93
- -webkit-line-clamp: 2;
94
- white-space: normal;
95
- }
98
+ // Must stay below the per-orientation rules: `.kbq-vertical` sets `display: block` on the title at the very
99
+ // same specificity, so moving this up lets that win and silently kills the clamp - `-webkit-line-clamp` does
100
+ // nothing without `-webkit-box`, and the title then wraps to an unbounded number of lines with no ellipsis.
101
+ // `.kbq-collapsed` still wins over it via `!important`.
102
+ .kbq-navbar-brand.kbq-navbar-brand_long-title .kbq-navbar-title {
103
+ display: -webkit-box;
104
+
105
+ -webkit-box-orient: vertical;
106
+ -webkit-line-clamp: 2;
107
+ line-clamp: 2;
108
+ white-space: normal;
96
109
  }
97
110
 
98
111
  @mixin kbq-navbar-brand-theme() {
@@ -145,8 +158,11 @@ a.kbq-navbar-brand {
145
158
  @include kbq-typography-level-to-styles-css-variables(typography, navbar-title);
146
159
  }
147
160
 
161
+ // Both orientations: two lines of the 18px/28px default would grow the navbar (48px -> 72px
162
+ // horizontally), which is what the compact type exists to prevent.
148
163
  &.kbq-navbar-brand_long-title .kbq-navbar-title {
149
164
  @include kbq-typography-level-to-styles-css-variables(typography, text-normal-strong);
165
+ line-height: 16px;
150
166
  }
151
167
  }
152
168
  }
@@ -26,6 +26,12 @@
26
26
  }
27
27
 
28
28
  & .kbq-vertical-navbar__container {
29
+ // The 1px `border-right` the theme adds belongs inside these widths, not beside them: the
30
+ // collapsed rail is `--kbq-size-6xl`, the same token this component's own `min-width` above
31
+ // is written in, so rendering it a pixel wider overshot that contract. Under `content-box`
32
+ // it measured 57px collapsed and 241px expanded.
33
+ box-sizing: border-box;
34
+
29
35
  position: relative;
30
36
 
31
37
  display: flex;
@@ -195,7 +195,8 @@ declare class KbqNotificationCenterComponent extends KbqPopUp implements AfterVi
195
195
  * stall. Suppressed while a load is in flight, errored, or when there is nothing more to load.
196
196
  */
197
197
  private subscribeToScrolledToBottom;
198
- /** Whether the list is scrolled to within `scrolledToBottomOffset` pixels of the bottom. */
198
+ /** Whether the list is scrolled to within `scrolledToBottomOffset` pixels of the bottom.
199
+ * Sub-pixel measurement error is absorbed by `SCROLLED_TO_BOTTOM_TOLERANCE`. */
199
200
  private isScrolledToBottom;
200
201
  /** Emits `onNextPage` unless a load is already in flight, errored, or there is nothing more to load. */
201
202
  private requestNextPage;
@@ -28,6 +28,12 @@
28
28
  .kbq-notification-item__icon-container {
29
29
  flex-shrink: 0;
30
30
 
31
+ // The size below is the icon itself and the padding only positions it, so the two must not share
32
+ // a box. Under a global `border-box` reset in a consuming application the padding would be
33
+ // subtracted from the icon instead of added around it, and `max-width`/`max-height` would clamp
34
+ // what is left — a 16px icon squeezed into 4px wide and 2px tall.
35
+ box-sizing: content-box;
36
+
31
37
  width: var(--kbq-size-l);
32
38
  max-width: var(--kbq-size-l);
33
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koobiq/components",
3
- "version": "20.1.0",
3
+ "version": "20.3.0",
4
4
  "description": "koobiq",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,8 +25,8 @@
25
25
  "@angular/cdk": ">=20.0.0",
26
26
  "@angular/core": ">=20.0.0",
27
27
  "@angular/forms": ">=20.0.0",
28
- "@koobiq/angular-moment-adapter": "20.1.0",
29
- "@koobiq/angular-luxon-adapter": "20.1.0",
28
+ "@koobiq/angular-moment-adapter": "20.3.0",
29
+ "@koobiq/angular-luxon-adapter": "20.3.0",
30
30
  "@koobiq/date-formatter": "^3.2.3",
31
31
  "@koobiq/icons": "^11.1.3",
32
32
  "@koobiq/design-tokens": "^3.14.0"
@@ -55,9 +55,6 @@
55
55
  "./prebuilt-themes/theme.css": {
56
56
  "style": "./prebuilt-themes/theme.css"
57
57
  },
58
- "./prebuilt-visual/default-visual.css": {
59
- "style": "./prebuilt-visual/default-visual.css"
60
- },
61
58
  "./package.json": {
62
59
  "default": "./package.json"
63
60
  },
@@ -1,12 +1,12 @@
1
1
  import { AnimationTriggerMetadata } from '@angular/animations';
2
2
  import * as i0 from '@angular/core';
3
- import { AfterViewInit, TemplateRef, AfterContentInit, OnInit, DestroyRef, EventEmitter, Type, ElementRef, InjectionToken } from '@angular/core';
3
+ import { InjectionToken, AfterViewInit, TemplateRef, AfterContentInit, OnInit, DestroyRef, EventEmitter, Type, ElementRef } from '@angular/core';
4
4
  import * as rxjs from 'rxjs';
5
5
  import { Subject } from 'rxjs';
6
6
  import * as i3 from '@angular/cdk/a11y';
7
7
  import { CdkTrapFocus } from '@angular/cdk/a11y';
8
8
  import * as i1 from '@angular/cdk/overlay';
9
- import { ScrollStrategy, OverlayConfig, CdkScrollable, Overlay } from '@angular/cdk/overlay';
9
+ import { ScrollStrategy, Overlay, OverlayConfig, CdkScrollable } from '@angular/cdk/overlay';
10
10
  import { KbqPopUp, KbqPopUpTrigger, KbqPopUpPlacementValues, KbqStickToWindowPlacementValues, KbqPopUpSizeValues, KbqOverflowShadowContainer, KbqComponentColors } from '@koobiq/components/core';
11
11
  import * as i2 from '@koobiq/components/button';
12
12
  import * as i4 from '@koobiq/components/icon';