@koobiq/components 20.1.0 → 20.2.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 (120) hide show
  1. package/accordion/_accordion-theme.scss +1 -1
  2. package/accordion/accordion-tokens.scss +1 -0
  3. package/accordion/{accordion-trigger.component.scss → accordion-trigger.scss} +4 -0
  4. package/accordion/{accordion.component.scss → accordion.scss} +3 -3
  5. package/accordion/index.d.ts +215 -119
  6. package/alert/_alert-theme.scss +14 -0
  7. package/alert/alert.component.scss +24 -13
  8. package/alert/index.d.ts +63 -29
  9. package/autocomplete/autocomplete.scss +2 -1
  10. package/autocomplete/index.d.ts +35 -29
  11. package/badge/index.d.ts +2 -3
  12. package/clamped-text/index.d.ts +3 -2
  13. package/core/index.d.ts +116 -11
  14. package/core/styles/_core.scss +2 -0
  15. package/core/styles/common/_animation.scss +31 -24
  16. package/core/styles/common/_connected-overlay.scss +36 -0
  17. package/core/styles/common/_select.scss +7 -2
  18. package/dropdown/dropdown-item.scss +16 -0
  19. package/dropdown/dropdown-tokens.scss +2 -1
  20. package/dropdown/dropdown.scss +9 -0
  21. package/dropdown/index.d.ts +73 -6
  22. package/fesm2022/koobiq-components-accordion.mjs +406 -303
  23. package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
  24. package/fesm2022/koobiq-components-actions-panel.mjs +1 -1
  25. package/fesm2022/koobiq-components-actions-panel.mjs.map +1 -1
  26. package/fesm2022/koobiq-components-alert.mjs +88 -48
  27. package/fesm2022/koobiq-components-alert.mjs.map +1 -1
  28. package/fesm2022/koobiq-components-app-switcher.mjs +1 -1
  29. package/fesm2022/koobiq-components-app-switcher.mjs.map +1 -1
  30. package/fesm2022/koobiq-components-autocomplete.mjs +37 -23
  31. package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
  32. package/fesm2022/koobiq-components-badge.mjs +5 -4
  33. package/fesm2022/koobiq-components-badge.mjs.map +1 -1
  34. package/fesm2022/koobiq-components-breadcrumbs.mjs +1 -1
  35. package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
  36. package/fesm2022/koobiq-components-clamped-text.mjs +5 -0
  37. package/fesm2022/koobiq-components-clamped-text.mjs.map +1 -1
  38. package/fesm2022/koobiq-components-code-block.mjs +1 -1
  39. package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
  40. package/fesm2022/koobiq-components-core.mjs +98 -4
  41. package/fesm2022/koobiq-components-core.mjs.map +1 -1
  42. package/fesm2022/koobiq-components-datepicker.mjs +10 -7
  43. package/fesm2022/koobiq-components-datepicker.mjs.map +1 -1
  44. package/fesm2022/koobiq-components-dropdown.mjs +99 -20
  45. package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
  46. package/fesm2022/koobiq-components-file-upload.mjs +2 -2
  47. package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
  48. package/fesm2022/koobiq-components-filter-bar.mjs +203 -32
  49. package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
  50. package/fesm2022/koobiq-components-form-field.mjs +4 -4
  51. package/fesm2022/koobiq-components-form-field.mjs.map +1 -1
  52. package/fesm2022/koobiq-components-icon.mjs +14 -5
  53. package/fesm2022/koobiq-components-icon.mjs.map +1 -1
  54. package/fesm2022/koobiq-components-inline-edit.mjs +4 -2
  55. package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
  56. package/fesm2022/koobiq-components-navbar.mjs +117 -22
  57. package/fesm2022/koobiq-components-navbar.mjs.map +1 -1
  58. package/fesm2022/koobiq-components-notification-center.mjs +19 -4
  59. package/fesm2022/koobiq-components-notification-center.mjs.map +1 -1
  60. package/fesm2022/koobiq-components-popover.mjs +5 -0
  61. package/fesm2022/koobiq-components-popover.mjs.map +1 -1
  62. package/fesm2022/koobiq-components-search-expandable.mjs +43 -8
  63. package/fesm2022/koobiq-components-search-expandable.mjs.map +1 -1
  64. package/fesm2022/koobiq-components-select.mjs +36 -45
  65. package/fesm2022/koobiq-components-select.mjs.map +1 -1
  66. package/fesm2022/koobiq-components-split-button.mjs +3 -10
  67. package/fesm2022/koobiq-components-split-button.mjs.map +1 -1
  68. package/fesm2022/koobiq-components-tabs.mjs +29 -17
  69. package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
  70. package/fesm2022/koobiq-components-tags.mjs +1 -1
  71. package/fesm2022/koobiq-components-tags.mjs.map +1 -1
  72. package/fesm2022/koobiq-components-timepicker.mjs +3 -4
  73. package/fesm2022/koobiq-components-timepicker.mjs.map +1 -1
  74. package/fesm2022/koobiq-components-timezone.mjs +3 -5
  75. package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
  76. package/fesm2022/koobiq-components-toast.mjs +1 -1
  77. package/fesm2022/koobiq-components-toast.mjs.map +1 -1
  78. package/fesm2022/koobiq-components-tooltip.mjs +118 -6
  79. package/fesm2022/koobiq-components-tooltip.mjs.map +1 -1
  80. package/fesm2022/koobiq-components-tree-select.mjs +39 -31
  81. package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
  82. package/filter-bar/_filter-bar-theme.scss +0 -4
  83. package/filter-bar/filter-bar-tokens.scss +3 -0
  84. package/filter-bar/filters.scss +3 -2
  85. package/filter-bar/index.d.ts +62 -3
  86. package/filter-bar/pipes/base-pipe.scss +0 -11
  87. package/filter-bar/pipes/pipe-input.scss +10 -0
  88. package/icon/icon-button.scss +3 -1
  89. package/icon/index.d.ts +15 -2
  90. package/navbar/_navbar-item_vertical.scss +7 -5
  91. package/navbar/index.d.ts +38 -4
  92. package/navbar/navbar-brand.scss +17 -7
  93. package/notification-center/index.d.ts +2 -1
  94. package/package.json +3 -3
  95. package/prebuilt-themes/dark-theme.css +1 -1
  96. package/prebuilt-themes/light-theme.css +1 -1
  97. package/prebuilt-themes/theme.css +1 -1
  98. package/schematics/collection.json +10 -0
  99. package/schematics/migrations/alert-signals/README.md +64 -0
  100. package/schematics/migrations/alert-signals/data.js +70 -0
  101. package/schematics/migrations/alert-signals/index.js +350 -0
  102. package/schematics/migrations/alert-signals/schema.json +20 -0
  103. package/schematics/migrations/autocomplete-panel-width-auto/README.md +68 -0
  104. package/schematics/migrations/autocomplete-panel-width-auto/index.js +70 -0
  105. package/schematics/migrations/autocomplete-panel-width-auto/schema.json +15 -0
  106. package/schematics/migrations/filter-bar-signals/README.md +1 -1
  107. package/schematics/migrations.json +11 -1
  108. package/schematics/ng-add/index.js +1 -1
  109. package/schematics/utils/angular-parsing.js +11 -2
  110. package/search-expandable/index.d.ts +3 -0
  111. package/select/index.d.ts +51 -24
  112. package/tabs/_tabs-common.scss +60 -31
  113. package/tabs/_tabs-theme.scss +12 -0
  114. package/tabs/index.d.ts +2 -2
  115. package/tabs/tab-header.scss +14 -0
  116. package/tabs/tab-nav-bar.scss +4 -0
  117. package/tabs/tabs-tokens.scss +1 -0
  118. package/timezone/index.d.ts +1 -3
  119. package/tooltip/index.d.ts +73 -4
  120. package/tree-select/index.d.ts +46 -22
@@ -7,9 +7,19 @@
7
7
  "factory": "./migrations/v20-upgrade/index"
8
8
  },
9
9
  "filter-bar-signals": {
10
- "version": "21.0.0-0",
10
+ "version": "20.2.0-0",
11
11
  "description": "Migrates KbqFilterBar consumers to the signal-based API. Rewrites programmatic reads of the now-signal members (filter, pipeTemplates, isChanged, isDisabled, isReadOnly, isSaved, isSavedAndChanged) to calls and `filter = v` writes to `filter.set(v)` on receivers typed KbqFilterBar, including reads via template reference variables on <kbq-filter-bar>. Renames KbqFilterBarRefresher → KbqFilterRefresher and warns on changes/preparePopover/view-query cases that need manual migration.",
12
12
  "factory": "./migrations/filter-bar-signals/index"
13
+ },
14
+ "alert-signals": {
15
+ "version": "20.2.0-0",
16
+ "description": "Migrates KbqAlert consumers to its finished signal-based API. Rewrites value-safe programmatic reads of the now-signal members (compact, alertStyle) to calls on receivers typed KbqAlert, including reads via template reference variables on <kbq-alert> (external and inline templates). Warns — without auto-fixing — on alertColor (now a read-only InputSignal whose getter returns the raw color instead of the `kbq-alert_<color>` class), the content-query members that became protected (icon/iconItem/button/title/control/closeButton/isColored), narrowed alertColor/alertStyle inputs, the compact booleanAttribute behavior change, descendants:false content projection, view/content queries returning the instance, and KbqAlertModule no longer re-exporting A11yModule/PlatformModule.",
17
+ "factory": "./migrations/alert-signals/index"
18
+ },
19
+ "autocomplete-panel-width-auto": {
20
+ "version": "20.2.0-0",
21
+ "description": "Replaces `panelWidth=\"auto\"` with `panelWidth=\"fit-content\"` on `<kbq-autocomplete>`. `auto` used to be passed to CSS verbatim, so the panel shrank to fit its content; it now means \"match the host width\", matching `kbq-select` and `kbq-tree-select`. The change is silent — `auto` still type-checks and still renders — so unmigrated templates only lay out differently.",
22
+ "factory": "./migrations/autocomplete-panel-width-auto/index"
13
23
  }
14
24
  }
15
25
  }
@@ -27,7 +27,7 @@ function ngAdd(options) {
27
27
  }
28
28
  // Installing dependencies
29
29
  utils_packageConfig.addPackageToPackageJson(tree, '@angular/cdk', "^20.2.14");
30
- utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/angular-luxon-adapter', "^20.1.0");
30
+ utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/angular-luxon-adapter', "^20.2.0");
31
31
  utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/date-formatter', "^3.5.1");
32
32
  utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/date-adapter', "^3.5.1");
33
33
  utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/icons', "^11.7.1");
@@ -122,6 +122,7 @@ async function transformTemplateAttributes(template, fileName, migrationData) {
122
122
  if (parsed.tree !== undefined) {
123
123
  const visitor = new utils_typescript.ElementCollector(migrationData, fileName);
124
124
  utils_ast.visitAll(visitor, parsed.tree.rootNodes);
125
+ let didReplace = false;
125
126
  for (const el of visitor.elementsToMigrate) {
126
127
  const migrationAttr = el.attrs.find((attr) => utils_ast.getSimpleAttributeName(attr.name) === migrationData.attrs.key.from);
127
128
  let updatedAttrValue;
@@ -132,7 +133,14 @@ async function transformTemplateAttributes(template, fileName, migrationData) {
132
133
  updatedAttrValue = replacement.to;
133
134
  }
134
135
  else {
135
- console.warn(`Element is using dynamic value. Check the code and change value on your own.${(fileName && ' File: ' + fileName) || ''}`);
136
+ // A bound attribute (`[panelWidth]="..."`) whose value isn't a quoted string
137
+ // literal is a genuine runtime expression and can't be resolved statically. A
138
+ // plain attribute, or a bound attribute holding a quoted literal, has a known
139
+ // value at migration time — it's just one the replacement table doesn't cover.
140
+ const isDynamicExpression = migrationAttr.name.startsWith('[') && !/^\s*['"].*['"]\s*$/.test(migrationAttr.value);
141
+ console.warn(isDynamicExpression
142
+ ? `Element is using a dynamic value. Check the code and change value on your own.${(fileName && ' File: ' + fileName) || ''}`
143
+ : `Element has value "${cleanAttrValue}" with no matching replacement. Check the code and change value on your own.${(fileName && ' File: ' + fileName) || ''}`);
136
144
  continue;
137
145
  }
138
146
  updatedTemplate =
@@ -141,9 +149,10 @@ async function transformTemplateAttributes(template, fileName, migrationData) {
141
149
  updatedTemplate.slice(migrationAttr.valueSpan.end.offset + 1 - offset, updatedTemplate.length);
142
150
  offset += migrationAttr.name.length - migrationData.attrs.key.to.length;
143
151
  offset += migrationAttr.value.length - updatedAttrValue.length;
152
+ didReplace = true;
144
153
  }
145
154
  }
146
- return { fileContent: updatedTemplate, changed: visitor.elementsToMigrate.length > 0, errors: parsed.errors };
155
+ return { fileContent: updatedTemplate, changed: didReplace, errors: parsed.errors };
147
156
  }
148
157
  return { fileContent: updatedTemplate, changed: undefined, errors: parsed.errors };
149
158
  }
@@ -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;
package/select/index.d.ts CHANGED
@@ -9,7 +9,7 @@ import { ConnectedPosition, CdkConnectedOverlay, CdkOverlayOrigin } from '@angul
9
9
  import { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
10
10
  import { ControlValueAccessor, NgForm, FormGroupDirective, NgControl } from '@angular/forms';
11
11
  import * as i2 from '@koobiq/components/core';
12
- import { KbqAbstractSelect, CanUpdateErrorState, ErrorStateMatcher, KbqLocaleService, KbqOptionBase, ActiveDescendantKeyManager, KbqOption, KbqSelectTrigger, KbqSelectMatcher, KbqOptgroup, KbqSelectSearch, KbqSelectSearchEmptyResult, KbqOptionSelectionChange, KbqSelectAllEvent, KbqVirtualOption, KbqComponentColors } from '@koobiq/components/core';
12
+ import { KbqAbstractSelect, CanUpdateErrorState, ErrorStateMatcher, KbqLocaleService, KbqPanelWidth, KbqPanelMinWidth, KbqPanelMaxWidth, KbqOptionBase, ActiveDescendantKeyManager, KbqOption, KbqSelectTrigger, KbqSelectMatcher, KbqOptgroup, KbqSelectSearch, KbqSelectSearchEmptyResult, KbqOptionSelectionChange, KbqSelectAllEvent, KbqVirtualOption, KbqComponentColors } from '@koobiq/components/core';
13
13
  import * as i11 from '@koobiq/components/form-field';
14
14
  import { KbqFormFieldControl, KbqCleaner } from '@koobiq/components/form-field';
15
15
  import * as i4 from '@koobiq/components/tags';
@@ -75,19 +75,27 @@ declare class KbqSelectChange {
75
75
  value: any;
76
76
  constructor(source: KbqSelect, value: any);
77
77
  }
78
- /** Select panel width type. */
79
- type KbqSelectPanelWidth = 'auto' | number | null;
78
+ /**
79
+ * Select panel width type.
80
+ * @deprecated Use `KbqPanelWidth` from `@koobiq/components/core` instead.
81
+ */
82
+ type KbqSelectPanelWidth = KbqPanelWidth;
80
83
  /** Options for the `kbq-select` that can be configured using the `KBQ_SELECT_OPTIONS` injection token. */
81
84
  type KbqSelectOptions = Partial<{
82
85
  /**
83
86
  * Width of the panel. If set to `auto`, the panel will match the trigger width.
84
87
  * If set to null or an empty string, the panel will grow to match the longest option's text.
85
88
  */
86
- panelWidth: KbqSelectPanelWidth;
89
+ panelWidth: KbqPanelWidth;
87
90
  /**
88
91
  * Minimum width of the panel. If minWidth is larger than window width or property set to null, it will be ignored.
89
92
  */
90
- panelMinWidth: Exclude<KbqSelectPanelWidth, 'auto'>;
93
+ panelMinWidth: KbqPanelMinWidth;
94
+ /**
95
+ * Maximum width of the panel. Caps growth by content only — it never overrides the trigger width or an
96
+ * explicit `panelWidth`. If null, the `--kbq-panel-size-width-max` token applies.
97
+ */
98
+ panelMaxWidth: KbqPanelMaxWidth;
91
99
  /**
92
100
  * Whether to enable hiding search by default if options is less than minimum.
93
101
  *
@@ -103,11 +111,16 @@ declare const KBQ_SELECT_OPTIONS: InjectionToken<Partial<{
103
111
  * Width of the panel. If set to `auto`, the panel will match the trigger width.
104
112
  * If set to null or an empty string, the panel will grow to match the longest option's text.
105
113
  */
106
- panelWidth: KbqSelectPanelWidth;
114
+ panelWidth: KbqPanelWidth;
107
115
  /**
108
116
  * Minimum width of the panel. If minWidth is larger than window width or property set to null, it will be ignored.
109
117
  */
110
- panelMinWidth: Exclude<KbqSelectPanelWidth, "auto">;
118
+ panelMinWidth: KbqPanelMinWidth;
119
+ /**
120
+ * Maximum width of the panel. Caps growth by content only — it never overrides the trigger width or an
121
+ * explicit `panelWidth`. If null, the `--kbq-panel-size-width-max` token applies.
122
+ */
123
+ panelMaxWidth: KbqPanelMaxWidth;
111
124
  /**
112
125
  * Whether to enable hiding search by default if options is less than minimum.
113
126
  *
@@ -145,11 +158,16 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
145
158
  * Width of the panel. If set to `auto`, the panel will match the trigger width.
146
159
  * If set to null or an empty string, the panel will grow to match the longest option's text.
147
160
  */
148
- panelWidth: KbqSelectPanelWidth;
161
+ panelWidth: KbqPanelWidth;
149
162
  /**
150
163
  * Minimum width of the panel. If minWidth is larger than window width or property set to null, it will be ignored.
151
164
  */
152
- panelMinWidth: Exclude<KbqSelectPanelWidth, "auto">;
165
+ panelMinWidth: KbqPanelMinWidth;
166
+ /**
167
+ * Maximum width of the panel. Caps growth by content only — it never overrides the trigger width or an
168
+ * explicit `panelWidth`. If null, the `--kbq-panel-size-width-max` token applies.
169
+ */
170
+ panelMaxWidth: KbqPanelMaxWidth;
153
171
  /**
154
172
  * Whether to enable hiding search by default if options is less than minimum.
155
173
  *
@@ -188,11 +206,20 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
188
206
  /** Strategy that will be used to handle scrolling while the select panel is open. */
189
207
  scrollStrategy: _angular_cdk_overlay_module_d.ScrollStrategy;
190
208
  /**
191
- * The y-offset of the overlay panel in relation to the trigger's top start corner.
192
- * This must be adjusted to align the selected option text over the trigger text
193
- * when the panel opens. Will change based on the y-position of the selected option.
209
+ * The y-offset of the overlay panel in relation to the trigger.
210
+ *
211
+ * @deprecated noop. The trigger↔panel gap is now controlled by the `--kbq-connected-overlay-gap` CSS
212
+ * variable (transparent padding inside the pane via the `kbq-connected-overlay_below/_above` panel
213
+ * classes), not by a physical overlay offset — so setting this has no effect. Will be removed in 21.0.0.
194
214
  */
195
215
  offsetY: number;
216
+ /**
217
+ * Minimum space to keep between the overlay and the viewport edge.
218
+ * At least `defaultOffsetY` so CDK's fit check — which runs before the `kbq-connected-overlay_below/_above`
219
+ * gap padding is applied to the pane — stays conservative enough to absorb that padding instead of
220
+ * letting the panel overflow the viewport by the size of the gap.
221
+ */
222
+ protected readonly viewportMargin: number;
196
223
  /**
197
224
  * This position config ensures that the top "start" corner of the overlay
198
225
  * is aligned with the top "start" of the origin by default (overlapping
@@ -363,15 +390,22 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
363
390
  get selectAllHandler(): (event: KeyboardEvent, select: KbqSelect) => void;
364
391
  set selectAllHandler(fn: (event: KeyboardEvent, select: KbqSelect) => void);
365
392
  /**
366
- * Width of the panel. If set to `auto`, the panel will match the trigger width.
367
- * If set to null or an empty string, the panel will grow to match the longest option's text.
393
+ * Width of the panel. If set to `auto`, the panel will match the trigger width, but will never be
394
+ * narrower than `panelMinWidth`. If set to null or an empty string, the panel will grow to match the
395
+ * longest option's text. Any other value is used as an exact width, and `panelMinWidth` is not applied.
368
396
  */
369
- panelWidth: KbqSelectPanelWidth;
397
+ readonly panelWidth: _angular_core.InputSignal<KbqPanelWidth>;
370
398
  /**
371
399
  * Minimum width of the panel in pixels.
372
400
  * If minWidth is larger than window width, it will be ignored.
373
401
  */
374
- panelMinWidth: Exclude<KbqSelectPanelWidth, 'auto'>;
402
+ readonly panelMinWidth: _angular_core.InputSignalWithTransform<KbqPanelMinWidth, unknown>;
403
+ /**
404
+ * Maximum width of the panel in pixels. Caps how far the panel grows with its content — it never makes
405
+ * the panel narrower than the trigger, and never clamps an explicit `panelWidth`.
406
+ * When null, the `--kbq-panel-size-width-max` token applies.
407
+ */
408
+ readonly panelMaxWidth: _angular_core.InputSignalWithTransform<KbqPanelMaxWidth, unknown>;
375
409
  /** Value of the select control. Can be a single value or array of values for multiple selection. */
376
410
  get value(): any;
377
411
  set value(newValue: any);
@@ -437,10 +471,6 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
437
471
  private readonly uid;
438
472
  /** Subject that emits when the component visibility changes. */
439
473
  private visibleChanges;
440
- /** Width of the overlay panel in pixels or as a string. */
441
- protected overlayWidth: string | number;
442
- /** Minimum width of the overlay panel in pixels. */
443
- protected overlayMinWidth: string | number;
444
474
  /** Origin element for the overlay panel positioning. */
445
475
  protected overlayOrigin?: CdkOverlayOrigin | ElementRef;
446
476
  /** Flag indicating if the dropdown class has been added to the overlay container. */
@@ -663,8 +693,6 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
663
693
  private highlightCorrectOption;
664
694
  /** Scrolls the active option into view. */
665
695
  private scrollActiveOptionIntoView;
666
- /** Gets how wide the overlay panel should be. */
667
- private getOverlayWidth;
668
696
  /** Comparison function to specify which option is displayed. Defaults to object equality. */
669
697
  private _compareWith;
670
698
  /** Function for handling the combination Ctrl + A (select all). By default, the internal handler is used. */
@@ -684,9 +712,8 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
684
712
  /** Removes the dropdown class from the overlay container when select closes. */
685
713
  private removeClassFromOverlayContainer;
686
714
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<KbqSelect, never>;
687
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<KbqSelect, "kbq-select", ["kbqSelect"], { "hiddenItemsText": { "alias": "hiddenItemsText"; "required": false; }; "showPreselectedValues": { "alias": "showPreselectedValues"; "required": false; "isSignal": true; }; "triggerValuesLimit": { "alias": "triggerValuesLimit"; "required": false; "isSignal": true; }; "panelClass": { "alias": "panelClass"; "required": false; "isSignal": true; }; "backdropClass": { "alias": "backdropClass"; "required": false; "isSignal": true; }; "errorStateMatcher": { "alias": "errorStateMatcher"; "required": false; }; "sortComparator": { "alias": "sortComparator"; "required": false; "isSignal": true; }; "multiline": { "alias": "multiline"; "required": false; "isSignal": true; }; "searchMinOptionsThreshold": { "alias": "searchMinOptionsThreshold"; "required": false; }; "scrolledToBottomOffset": { "alias": "scrolledToBottomOffset"; "required": false; "isSignal": true; }; "hasBackdrop": { "alias": "hasBackdrop"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "required": { "alias": "required"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "compareWith": { "alias": "compareWith"; "required": false; }; "virtualOptionFactory": { "alias": "virtualOptionFactory"; "required": false; "isSignal": true; }; "selectAllToggle": { "alias": "selectAllToggle"; "required": false; "isSignal": true; }; "selectAllHandler": { "alias": "selectAllHandler"; "required": false; }; "panelWidth": { "alias": "panelWidth"; "required": false; }; "panelMinWidth": { "alias": "panelMinWidth"; "required": false; }; "value": { "alias": "value"; "required": false; }; "id": { "alias": "id"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "hiddenItemsTextFormatter": { "alias": "hiddenItemsTextFormatter"; "required": false; }; }, { "openedChange": "openedChange"; "beforeOpened": "beforeOpened"; "openedStream": "opened"; "closedStream": "closed"; "selectionChange": "selectionChange"; "onSelectAll": "onSelectAll"; "valueChange": "valueChange"; "scrolledToBottom": "scrolledToBottom"; }, ["footer", "cdkVirtualForOf", "virtualScrollViewport", "customTrigger", "customMatcher", "customTagTemplateRef", "cleaner", "optionGroups", "search", "searchEmpty", "options"], ["kbq-select-matcher, [kbq-select-matcher]", "kbq-select-trigger, [kbq-select-trigger]", "kbq-cleaner", "[kbqSelectSearch]", "[kbq-select-search-empty-result]", "*", "kbq-select-footer,[kbq-select-footer]"], true, never>;
715
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<KbqSelect, "kbq-select", ["kbqSelect"], { "hiddenItemsText": { "alias": "hiddenItemsText"; "required": false; }; "showPreselectedValues": { "alias": "showPreselectedValues"; "required": false; "isSignal": true; }; "triggerValuesLimit": { "alias": "triggerValuesLimit"; "required": false; "isSignal": true; }; "panelClass": { "alias": "panelClass"; "required": false; "isSignal": true; }; "backdropClass": { "alias": "backdropClass"; "required": false; "isSignal": true; }; "errorStateMatcher": { "alias": "errorStateMatcher"; "required": false; }; "sortComparator": { "alias": "sortComparator"; "required": false; "isSignal": true; }; "multiline": { "alias": "multiline"; "required": false; "isSignal": true; }; "searchMinOptionsThreshold": { "alias": "searchMinOptionsThreshold"; "required": false; }; "scrolledToBottomOffset": { "alias": "scrolledToBottomOffset"; "required": false; "isSignal": true; }; "hasBackdrop": { "alias": "hasBackdrop"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "required": { "alias": "required"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "compareWith": { "alias": "compareWith"; "required": false; }; "virtualOptionFactory": { "alias": "virtualOptionFactory"; "required": false; "isSignal": true; }; "selectAllToggle": { "alias": "selectAllToggle"; "required": false; "isSignal": true; }; "selectAllHandler": { "alias": "selectAllHandler"; "required": false; }; "panelWidth": { "alias": "panelWidth"; "required": false; "isSignal": true; }; "panelMinWidth": { "alias": "panelMinWidth"; "required": false; "isSignal": true; }; "panelMaxWidth": { "alias": "panelMaxWidth"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; }; "id": { "alias": "id"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "hiddenItemsTextFormatter": { "alias": "hiddenItemsTextFormatter"; "required": false; }; }, { "openedChange": "openedChange"; "beforeOpened": "beforeOpened"; "openedStream": "opened"; "closedStream": "closed"; "selectionChange": "selectionChange"; "onSelectAll": "onSelectAll"; "valueChange": "valueChange"; "scrolledToBottom": "scrolledToBottom"; }, ["footer", "cdkVirtualForOf", "virtualScrollViewport", "customTrigger", "customMatcher", "customTagTemplateRef", "cleaner", "optionGroups", "search", "searchEmpty", "options"], ["kbq-select-matcher, [kbq-select-matcher]", "kbq-select-trigger, [kbq-select-trigger]", "kbq-cleaner", "[kbqSelectSearch]", "[kbq-select-search-empty-result]", "*", "kbq-select-footer,[kbq-select-footer]"], true, never>;
688
716
  static ngAcceptInputType_multiple: unknown;
689
- static ngAcceptInputType_panelMinWidth: unknown;
690
717
  static ngAcceptInputType_tabIndex: unknown;
691
718
  static ngAcceptInputType_disabled: unknown;
692
719
  }
@@ -33,25 +33,6 @@
33
33
 
34
34
  outline: none;
35
35
 
36
- &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
37
- padding-left: 14px;
38
- padding-right: 14px;
39
- }
40
-
41
- &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical).kbq-tab-label_with-icon-left {
42
- padding-left: tokens.kbq-difference-series-css-variables(
43
- [tabs-size-tab-item-padding-horizontal,
44
- tabs-size-tab-item-focus-outline-width]
45
- );
46
- }
47
-
48
- &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical).kbq-tab-label_with-icon-right {
49
- padding-right: tokens.kbq-difference-series-css-variables(
50
- [tabs-size-tab-item-padding-horizontal,
51
- tabs-size-tab-item-focus-outline-width]
52
- );
53
- }
54
-
55
36
  &.kbq-selected {
56
37
  cursor: default;
57
38
  }
@@ -75,30 +56,63 @@
75
56
  &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical):before {
76
57
  content: '';
77
58
  position: absolute;
78
- left: 0;
79
- right: 0;
80
- top: -10px;
81
- bottom: -10px;
59
+ inset: calc(-1 * (var(--kbq-size-s) + var(--kbq-tabs-size-tab-item-focus-outline-width)))
60
+ calc(-1 * var(--kbq-tabs-size-tab-item-focus-outline-width));
61
+ }
62
+
63
+ &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical):not(.kbq-tab-label_icon-only):after {
64
+ content: '';
65
+ position: absolute;
66
+ left: calc(
67
+ var(--kbq-tabs-size-tab-item-padding-horizontal) - var(--kbq-tabs-size-tab-item-focus-outline-width)
68
+ );
69
+ right: calc(
70
+ var(--kbq-tabs-size-tab-item-padding-horizontal) - var(--kbq-tabs-size-tab-item-focus-outline-width)
71
+ );
72
+ bottom: calc(-1 * (var(--kbq-size-s) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
73
+ height: 3px;
74
+ border-radius: var(--kbq-size-3xs) var(--kbq-size-3xs) 0 0;
75
+ background: transparent;
76
+ transition: background 0.2s ease-in-out;
77
+ pointer-events: none;
82
78
  }
83
79
 
84
80
  &.kbq-tab-label_icon-only {
85
81
  width: var(--kbq-size-3xl);
86
82
  height: var(--kbq-size-3xl);
87
83
  padding: 0 !important;
88
- margin-inline: var(--kbq-size-xs);
84
+ margin-inline: var(--kbq-size-xxs);
89
85
  box-sizing: content-box;
90
86
 
91
- // Keep the space between icon-only tabs clickable after adding margins.
87
+ &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
88
+ width: var(--kbq-tabs-size-tab-item-underlined-icon-only-size);
89
+ height: var(--kbq-tabs-size-tab-item-underlined-icon-only-size);
90
+
91
+ &:before {
92
+ left: calc(-1 * (var(--kbq-size-xxs) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
93
+ right: calc(-1 * (var(--kbq-size-xxs) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
94
+ }
95
+
96
+ &:after {
97
+ top: auto;
98
+ bottom: calc(-1 * (var(--kbq-size-s) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
99
+ height: 3px;
100
+ left: calc(-1 * var(--kbq-tabs-size-tab-item-focus-outline-width));
101
+ right: calc(-1 * var(--kbq-tabs-size-tab-item-focus-outline-width));
102
+ border-radius: var(--kbq-size-3xs) var(--kbq-size-3xs) 0 0;
103
+ transition: background 0.2s ease-in-out;
104
+ pointer-events: none;
105
+ }
106
+ }
107
+
92
108
  &:not(.kbq-tab-label_vertical):after {
93
109
  content: '';
94
110
  display: block;
95
111
  position: absolute;
96
112
  top: 0;
97
113
  bottom: 0;
98
- // Absolute offsets are calculated from the padding box, so we add
99
- // focus outline width to fully cover the visual margin gap.
100
- left: calc(-1 * (var(--kbq-size-xs) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
101
- right: calc(-1 * (var(--kbq-size-xs) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
114
+ left: calc(-1 * (var(--kbq-size-xxs) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
115
+ right: calc(-1 * (var(--kbq-size-xxs) + var(--kbq-tabs-size-tab-item-focus-outline-width)));
102
116
  background: transparent;
103
117
  }
104
118
  }
@@ -144,7 +158,18 @@
144
158
  }
145
159
 
146
160
  .kbq-tab-header_underlined:not(.kbq-tab-header_vertical) .kbq-tab-list__content {
147
- padding: 8px 0;
161
+ padding: var(--kbq-size-s) 0;
162
+
163
+ // Negative margin shifts the first tab left so the focus frame can extend beyond the
164
+ // container edge (instead of padding-left:0 which would clip it under overflow:hidden).
165
+ > .kbq-tab-label:first-child:not(.kbq-tab-label_icon-only),
166
+ > .kbq-tab-link:first-child {
167
+ margin-inline-start: calc(-1 * var(--kbq-tabs-size-tab-item-padding-horizontal));
168
+ }
169
+
170
+ > .kbq-tab-label_icon-only:first-child {
171
+ margin-inline-start: 0;
172
+ }
148
173
  }
149
174
 
150
175
  .kbq-tab-header_underlined:not(.kbq-tab-header_vertical):after {
@@ -166,8 +191,12 @@
166
191
  position: absolute;
167
192
  bottom: 0;
168
193
  height: 3px;
169
- border-radius: 2px 2px 0 0;
194
+ border-radius: var(--kbq-size-3xs) var(--kbq-size-3xs) 0 0;
170
195
  background: var(--kbq-line-contrast);
171
196
  transition: all 0.2s ease-in-out;
197
+
198
+ &.kbq-disabled {
199
+ background: var(--kbq-states-icon-disabled);
200
+ }
172
201
  }
173
202
  }
@@ -16,6 +16,14 @@
16
16
 
17
17
  &:is(:hover, .kbq-hover):not(.kbq-disabled) {
18
18
  @include kbq-tab-item-state($type, $sub-type, states-hover);
19
+
20
+ &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
21
+ background: transparent;
22
+
23
+ &:not(.kbq-selected)::after {
24
+ background: var(--kbq-line-contrast-fade);
25
+ }
26
+ }
19
27
  }
20
28
 
21
29
  &.kbq-tab-label_empty {
@@ -27,6 +35,10 @@
27
35
 
28
36
  &.kbq-tab-label_underlined:not(.kbq-tab-label_vertical) {
29
37
  background: none;
38
+
39
+ &::after {
40
+ background: transparent;
41
+ }
30
42
  }
31
43
 
32
44
  &:is(:hover, .kbq-hover) {
package/tabs/index.d.ts CHANGED
@@ -409,10 +409,9 @@ declare class KbqTabHeader extends KbqPaginatedTabHeader {
409
409
  readonly nextPaginator: ElementRef<HTMLElement>;
410
410
  readonly previousPaginator: ElementRef<HTMLElement>;
411
411
  private readonly isBrowser;
412
- /** Width of the active tab, adjusted for icon-only tab margins. */
413
412
  protected get activeTabOffsetWidth(): number | undefined;
414
- /** Left offset of the active tab, adjusted for icon-only tab margins. */
415
413
  protected get activeTabOffsetLeft(): number | undefined;
414
+ protected get activeTabDisabled(): boolean;
416
415
  protected itemSelected(event: KeyboardEvent): void;
417
416
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqTabHeader, never>;
418
417
  static ɵcmp: i0.ɵɵComponentDeclaration<KbqTabHeader, "kbq-tab-header", never, { "underlined": { "alias": "underlined"; "required": false; "isSignal": true; }; }, { "selectFocusedIndex": "selectFocusedIndex"; "indexFocused": "indexFocused"; }, ["items"], ["*"], true, never>;
@@ -568,6 +567,7 @@ declare class KbqTabNavBar extends KbqPaginatedTabHeader implements AfterContent
568
567
  get role(): string | null;
569
568
  protected get activeTabOffsetWidth(): number | undefined;
570
569
  protected get activeTabOffsetLeft(): number | undefined;
570
+ protected get activeTabDisabled(): boolean;
571
571
  ngAfterContentInit(): void;
572
572
  protected itemSelected(): void;
573
573
  /** Notifies the component that the active link has been changed. */
@@ -9,6 +9,16 @@
9
9
  position: relative;
10
10
  flex-shrink: 0;
11
11
  border-radius: var(--kbq-tabs-size-tab-stack-horizontal-border-radius);
12
+
13
+ &.kbq-tab-header_underlined {
14
+ .kbq-tab-group:not(.kbq-tab-group_on-surface) & {
15
+ border-radius: 0;
16
+ }
17
+
18
+ &:not(.kbq-tab-header__pagination-controls_enabled) {
19
+ overflow: visible;
20
+ }
21
+ }
12
22
  }
13
23
 
14
24
  .kbq-tab-list__content {
@@ -32,6 +42,10 @@
32
42
  flex-grow: 1;
33
43
  z-index: 1;
34
44
  overflow: hidden;
45
+
46
+ .kbq-tab-header_underlined:not(.kbq-tab-header__pagination-controls_enabled) & {
47
+ overflow: visible;
48
+ }
35
49
  }
36
50
 
37
51
  .kbq-tab-list {
@@ -13,6 +13,10 @@
13
13
  overflow: hidden;
14
14
  }
15
15
 
16
+ &.kbq-tab-header_underlined:not(.kbq-tab-header__pagination-controls_enabled) .kbq-tab-link__container {
17
+ overflow: visible;
18
+ }
19
+
16
20
  .kbq-tab-list {
17
21
  position: relative;
18
22
  width: 100%;
@@ -7,6 +7,7 @@
7
7
  --kbq-tabs-size-tab-item-focus-outline-width: var(--kbq-size-3xs);
8
8
  --kbq-tabs-size-tab-stack-horizontal-border-radius: var(--kbq-size-border-radius);
9
9
  --kbq-tabs-size-tab-stack-vertical-content-gap-vertical: var(--kbq-size-s);
10
+ --kbq-tabs-size-tab-item-underlined-icon-only-size: calc(var(--kbq-size-3xl) - var(--kbq-size-xxs));
10
11
  /* THEME TOKENS */
11
12
  --kbq-tabs-tab-item-filled-on-background-default-background: transparent;
12
13
  --kbq-tabs-tab-item-filled-on-background-default-text-color: var(--kbq-foreground-contrast);
@@ -5,7 +5,7 @@ import { KbqOption } from '@koobiq/components/core';
5
5
  import * as i6 from '@koobiq/components/tooltip';
6
6
  import { KbqTooltipTrigger } from '@koobiq/components/tooltip';
7
7
  import * as i3 from '@koobiq/components/select';
8
- import { KbqSelect, KbqSelectPanelWidth } from '@koobiq/components/select';
8
+ import { KbqSelect } from '@koobiq/components/select';
9
9
  import * as i1 from '@angular/cdk/overlay';
10
10
  import * as i4 from '@koobiq/components/icon';
11
11
  import * as i5 from '@koobiq/components/tags';
@@ -69,8 +69,6 @@ declare class KbqTimezoneSelectTrigger {
69
69
  }
70
70
  declare class KbqTimezoneSelect extends KbqSelect implements AfterContentInit {
71
71
  readonly customTrigger: i0.Signal<KbqTimezoneSelectTrigger | undefined>;
72
- panelWidth: KbqSelectPanelWidth;
73
- panelMinWidth: number;
74
72
  ngAfterContentInit(): void;
75
73
  private updateLocaleParamsForSearch;
76
74
  static ɵfac: i0.ɵɵFactoryDeclaration<KbqTimezoneSelect, never>;