@koobiq/components 20.3.1 → 20.4.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 (104) hide show
  1. package/accordion/accordion.scss +4 -1
  2. package/accordion/index.d.ts +36 -12
  3. package/alert/index.d.ts +8 -0
  4. package/breadcrumbs/index.d.ts +2 -0
  5. package/button-toggle/button-toggle-tokens.scss +14 -0
  6. package/button-toggle/button-toggle.scss +75 -3
  7. package/core/index.d.ts +146 -5
  8. package/core/option/_option-theme.scss +3 -0
  9. package/core/styles/common/_select.scss +98 -3
  10. package/core/styles/typography/_typography.scss +8 -0
  11. package/ellipsis-center/ellipsis-center.scss +31 -0
  12. package/ellipsis-center/index.d.ts +35 -2
  13. package/fesm2022/koobiq-components-accordion.mjs +143 -44
  14. package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
  15. package/fesm2022/koobiq-components-alert.mjs +17 -1
  16. package/fesm2022/koobiq-components-alert.mjs.map +1 -1
  17. package/fesm2022/koobiq-components-autocomplete.mjs +2 -2
  18. package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
  19. package/fesm2022/koobiq-components-breadcrumbs.mjs +8 -3
  20. package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
  21. package/fesm2022/koobiq-components-button-toggle.mjs +2 -2
  22. package/fesm2022/koobiq-components-button-toggle.mjs.map +1 -1
  23. package/fesm2022/koobiq-components-code-block.mjs +10 -5
  24. package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
  25. package/fesm2022/koobiq-components-core.mjs +214 -11
  26. package/fesm2022/koobiq-components-core.mjs.map +1 -1
  27. package/fesm2022/koobiq-components-dropdown.mjs +2 -2
  28. package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
  29. package/fesm2022/koobiq-components-ellipsis-center.mjs +101 -34
  30. package/fesm2022/koobiq-components-ellipsis-center.mjs.map +1 -1
  31. package/fesm2022/koobiq-components-file-upload.mjs +7 -7
  32. package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
  33. package/fesm2022/koobiq-components-filter-bar.mjs +94 -55
  34. package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
  35. package/fesm2022/koobiq-components-inline-edit.mjs +177 -14
  36. package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
  37. package/fesm2022/koobiq-components-list.mjs +359 -44
  38. package/fesm2022/koobiq-components-list.mjs.map +1 -1
  39. package/fesm2022/koobiq-components-markdown.mjs +6 -2
  40. package/fesm2022/koobiq-components-markdown.mjs.map +1 -1
  41. package/fesm2022/koobiq-components-modal.mjs +48 -7
  42. package/fesm2022/koobiq-components-modal.mjs.map +1 -1
  43. package/fesm2022/koobiq-components-select.mjs +117 -12
  44. package/fesm2022/koobiq-components-select.mjs.map +1 -1
  45. package/fesm2022/koobiq-components-sidebar.mjs +5 -1
  46. package/fesm2022/koobiq-components-sidebar.mjs.map +1 -1
  47. package/fesm2022/koobiq-components-sidepanel.mjs +25 -6
  48. package/fesm2022/koobiq-components-sidepanel.mjs.map +1 -1
  49. package/fesm2022/koobiq-components-tabs.mjs +460 -197
  50. package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
  51. package/fesm2022/koobiq-components-time-range.mjs +388 -74
  52. package/fesm2022/koobiq-components-time-range.mjs.map +1 -1
  53. package/fesm2022/koobiq-components-timezone.mjs +4 -4
  54. package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
  55. package/fesm2022/koobiq-components-title.mjs +15 -3
  56. package/fesm2022/koobiq-components-title.mjs.map +1 -1
  57. package/fesm2022/koobiq-components-tree-select.mjs +2 -2
  58. package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
  59. package/fesm2022/koobiq-components-tree.mjs +14 -4
  60. package/fesm2022/koobiq-components-tree.mjs.map +1 -1
  61. package/file-upload/file-upload.scss +0 -18
  62. package/file-upload/index.d.ts +1 -1
  63. package/file-upload/multiple-file-upload.component.scss +15 -3
  64. package/file-upload/single-file-upload.component.scss +10 -2
  65. package/filter-bar/filter-bar-tokens.scss +2 -1
  66. package/filter-bar/index.d.ts +41 -4
  67. package/filter-bar/pipes/base-pipe.scss +24 -6
  68. package/filter-bar/pipes/pipe-input.scss +3 -1
  69. package/inline-edit/index.d.ts +87 -8
  70. package/inline-edit/inline-edit-tokens.scss +1 -0
  71. package/inline-edit/inline-edit.scss +26 -0
  72. package/link/_link-theme.scss +50 -15
  73. package/list/index.d.ts +126 -5
  74. package/markdown/index.d.ts +2 -0
  75. package/modal/index.d.ts +16 -7
  76. package/navbar/_navbar-item_horizontal.scss +5 -0
  77. package/package.json +3 -3
  78. package/prebuilt-themes/dark-theme.css +1 -1
  79. package/prebuilt-themes/light-theme.css +1 -1
  80. package/prebuilt-themes/theme.css +1 -1
  81. package/schematics/migrations/alert-signals/index.js +4 -1
  82. package/schematics/migrations/autocomplete-panel-width-auto/index.js +3 -34
  83. package/schematics/migrations/empty-state-size-attr/index.js +3 -34
  84. package/schematics/migrations/filter-bar-signals/index.js +4 -1
  85. package/schematics/migrations/loader-overlay-size-attr/index.js +3 -34
  86. package/schematics/migrations/v20-upgrade/index.js +4 -1
  87. package/schematics/ng-add/index.js +1 -1
  88. package/schematics/utils/angular-parsing.js +11 -12
  89. package/schematics/utils/package-config.js +5 -4
  90. package/schematics/utils/typescript.js +24 -0
  91. package/select/_select-theme.scss +9 -1
  92. package/select/index.d.ts +18 -2
  93. package/select/select.scss +13 -0
  94. package/sidebar/index.d.ts +1 -0
  95. package/sidepanel/index.d.ts +9 -0
  96. package/tabs/_tabs-common.scss +111 -2
  97. package/tabs/index.d.ts +79 -48
  98. package/tabs/tab-group.scss +0 -4
  99. package/tabs/tab-header.scss +0 -2
  100. package/tabs/tab-nav-bar.scss +3 -11
  101. package/time-range/index.d.ts +140 -5
  102. package/time-range/time-range.scss +16 -1
  103. package/title/index.d.ts +10 -2
  104. package/tree/index.d.ts +1 -0
@@ -2,37 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var node_path = require('node:path');
6
- var process = require('node:process');
7
- var ts = require('typescript');
8
5
  var utils_angularParsing = require('../../utils/angular-parsing.js');
9
6
  var utils_packageConfig = require('../../utils/package-config.js');
10
7
  var utils_typescript = require('../../utils/typescript.js');
11
- require('path');
8
+ require('typescript');
12
9
  require('../../utils/ast.js');
13
10
  require('@angular-devkit/schematics');
14
11
  require('@schematics/angular/utility');
15
12
  require('../../utils/messages.js');
16
13
 
17
- function _interopNamespaceDefault(e) {
18
- var n = Object.create(null);
19
- if (e) {
20
- Object.keys(e).forEach(function (k) {
21
- if (k !== 'default') {
22
- var d = Object.getOwnPropertyDescriptor(e, k);
23
- Object.defineProperty(n, k, d.get ? d : {
24
- enumerable: true,
25
- get: function () { return e[k]; }
26
- });
27
- }
28
- });
29
- }
30
- n.default = e;
31
- return Object.freeze(n);
32
- }
33
-
34
- var process__namespace = /*#__PURE__*/_interopNamespaceDefault(process);
35
-
36
14
  const migrationData = {
37
15
  elementName: 'kbq-autocomplete',
38
16
  attrs: {
@@ -48,20 +26,11 @@ const migrationData = {
48
26
  function migrate(options) {
49
27
  return async (tree, context) => {
50
28
  const { project } = options;
51
- const { tsPaths, templatePaths, projectDefinition } = await utils_packageConfig.getParsingInfo(project, tree);
52
- const program = ts.createProgram(Array.from(tsPaths, (item) => node_path.resolve(projectDefinition.root, `.${item}`)), {
53
- baseUrl: projectDefinition.root,
54
- rootDir: projectDefinition.root,
55
- _enableTemplateTypeChecker: true, // Required for the template type checker to work.
56
- compileNonExportedClasses: true, // We want to migrate non-exported classes too.
57
- // Avoid checking libraries to speed up the migration.
58
- skipLibCheck: true,
59
- skipDefaultLibCheck: true
60
- });
29
+ const { tsPaths, templatePaths } = await utils_packageConfig.getParsingInfo(project, tree);
61
30
  // Update external html
62
31
  await utils_angularParsing.migrateTemplate(tree, Array.from(templatePaths), context, migrationData);
63
32
  // Update inline html
64
- await utils_angularParsing.migrateTs(tree, program.getSourceFiles().filter((sourceFile) => utils_typescript.canMigrateFile(sourceFile, program)), process__namespace.cwd(), context, migrationData);
33
+ await utils_angularParsing.migrateTs(tree, utils_typescript.readSourceFiles(tree, tsPaths), context, migrationData);
65
34
  context.logger.warn('Warning! Run linter in updated files since line breaks or indents may be broken.');
66
35
  };
67
36
  }
@@ -2,37 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var node_path = require('node:path');
6
- var process = require('node:process');
7
- var ts = require('typescript');
8
5
  var utils_angularParsing = require('../../utils/angular-parsing.js');
9
6
  var utils_packageConfig = require('../../utils/package-config.js');
10
7
  var utils_typescript = require('../../utils/typescript.js');
11
- require('path');
8
+ require('typescript');
12
9
  require('../../utils/ast.js');
13
10
  require('@angular-devkit/schematics');
14
11
  require('@schematics/angular/utility');
15
12
  require('../../utils/messages.js');
16
13
 
17
- function _interopNamespaceDefault(e) {
18
- var n = Object.create(null);
19
- if (e) {
20
- Object.keys(e).forEach(function (k) {
21
- if (k !== 'default') {
22
- var d = Object.getOwnPropertyDescriptor(e, k);
23
- Object.defineProperty(n, k, d.get ? d : {
24
- enumerable: true,
25
- get: function () { return e[k]; }
26
- });
27
- }
28
- });
29
- }
30
- n.default = e;
31
- return Object.freeze(n);
32
- }
33
-
34
- var process__namespace = /*#__PURE__*/_interopNamespaceDefault(process);
35
-
36
14
  const migrationData = {
37
15
  elementName: 'kbq-empty-state',
38
16
  attrs: {
@@ -52,20 +30,11 @@ const migrationData = {
52
30
  function migrate(options) {
53
31
  return async (tree, context) => {
54
32
  const { project } = options;
55
- const { tsPaths, templatePaths, projectDefinition } = await utils_packageConfig.getParsingInfo(project, tree);
56
- const program = ts.createProgram(Array.from(tsPaths, (item) => node_path.resolve(projectDefinition.root, `.${item}`)), {
57
- baseUrl: projectDefinition.root,
58
- rootDir: projectDefinition.root,
59
- _enableTemplateTypeChecker: true, // Required for the template type checker to work.
60
- compileNonExportedClasses: true, // We want to migrate non-exported classes too.
61
- // Avoid checking libraries to speed up the migration.
62
- skipLibCheck: true,
63
- skipDefaultLibCheck: true
64
- });
33
+ const { tsPaths, templatePaths } = await utils_packageConfig.getParsingInfo(project, tree);
65
34
  // Update external html
66
35
  await utils_angularParsing.migrateTemplate(tree, Array.from(templatePaths), context, migrationData);
67
36
  // Update inline html
68
- await utils_angularParsing.migrateTs(tree, program.getSourceFiles().filter((sourceFile) => utils_typescript.canMigrateFile(sourceFile, program)), process__namespace.cwd(), context, migrationData);
37
+ await utils_angularParsing.migrateTs(tree, utils_typescript.readSourceFiles(tree, tsPaths), context, migrationData);
69
38
  context.logger.warn('Warning! Run linter in updated files since line breaks or indents maybe be broken.');
70
39
  };
71
40
  }
@@ -262,7 +262,10 @@ function referencesFilterBar(content) {
262
262
  }
263
263
  function filterBarSignals(options) {
264
264
  return async (tree, context) => {
265
- const { project, fix } = options;
265
+ const { project } = options;
266
+ // `ng update` invokes migrations with no options at all, and migrations.json declares no schema, so the
267
+ // schema default never reaches us — applying the fix is the intended behaviour there.
268
+ const fix = options.fix ?? true;
266
269
  const projectDefinition = await utils_packageConfig.setupOptions(project, tree);
267
270
  const root = projectDefinition?.root ?? '';
268
271
  const rootDir = root ? tree.getDir(root) : tree.root;
@@ -2,37 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var node_path = require('node:path');
6
- var process = require('node:process');
7
- var ts = require('typescript');
8
5
  var utils_angularParsing = require('../../utils/angular-parsing.js');
9
6
  var utils_packageConfig = require('../../utils/package-config.js');
10
7
  var utils_typescript = require('../../utils/typescript.js');
11
- require('path');
8
+ require('typescript');
12
9
  require('../../utils/ast.js');
13
10
  require('@angular-devkit/schematics');
14
11
  require('@schematics/angular/utility');
15
12
  require('../../utils/messages.js');
16
13
 
17
- function _interopNamespaceDefault(e) {
18
- var n = Object.create(null);
19
- if (e) {
20
- Object.keys(e).forEach(function (k) {
21
- if (k !== 'default') {
22
- var d = Object.getOwnPropertyDescriptor(e, k);
23
- Object.defineProperty(n, k, d.get ? d : {
24
- enumerable: true,
25
- get: function () { return e[k]; }
26
- });
27
- }
28
- });
29
- }
30
- n.default = e;
31
- return Object.freeze(n);
32
- }
33
-
34
- var process__namespace = /*#__PURE__*/_interopNamespaceDefault(process);
35
-
36
14
  const migrationData = {
37
15
  elementName: 'kbq-loader-overlay',
38
16
  attrs: {
@@ -52,20 +30,11 @@ const migrationData = {
52
30
  function migrate(options) {
53
31
  return async (tree, context) => {
54
32
  const { project } = options;
55
- const { tsPaths, templatePaths, projectDefinition } = await utils_packageConfig.getParsingInfo(project, tree);
56
- const program = ts.createProgram(Array.from(tsPaths, (item) => node_path.resolve(projectDefinition.root, `.${item}`)), {
57
- baseUrl: projectDefinition.root,
58
- rootDir: projectDefinition.root,
59
- _enableTemplateTypeChecker: true, // Required for the template type checker to work.
60
- compileNonExportedClasses: true, // We want to migrate non-exported classes too.
61
- // Avoid checking libraries to speed up the migration.
62
- skipLibCheck: true,
63
- skipDefaultLibCheck: true
64
- });
33
+ const { tsPaths, templatePaths } = await utils_packageConfig.getParsingInfo(project, tree);
65
34
  // Update external html
66
35
  await utils_angularParsing.migrateTemplate(tree, Array.from(templatePaths), context, migrationData);
67
36
  // Update inline html
68
- await utils_angularParsing.migrateTs(tree, program.getSourceFiles().filter((sourceFile) => utils_typescript.canMigrateFile(sourceFile, program)), process__namespace.cwd(), context, migrationData);
37
+ await utils_angularParsing.migrateTs(tree, utils_typescript.readSourceFiles(tree, tsPaths), context, migrationData);
69
38
  context.logger.warn('Warning! Run linter in updated files since line breaks or indents maybe be broken.');
70
39
  };
71
40
  }
@@ -158,7 +158,10 @@ function pickReplacements(filePath) {
158
158
  }
159
159
  function v20Upgrade(options) {
160
160
  return async (tree, context) => {
161
- const { project, fix } = options;
161
+ const { project } = options;
162
+ // `ng update` invokes migrations with no options at all, and migrations.json declares no schema, so the
163
+ // schema default never reaches us — applying the fix is the intended behaviour there.
164
+ const fix = options.fix ?? true;
162
165
  const projectDefinition = await utils_packageConfig.setupOptions(project, tree);
163
166
  const root = projectDefinition?.root ?? '';
164
167
  const rootDir = root ? tree.getDir(root) : tree.root;
@@ -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.3.1");
30
+ utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/angular-luxon-adapter', "^20.4.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', "^12.2.0");
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var path = require('path');
4
3
  var ts = require('typescript');
5
4
  var utils_ast = require('./ast.js');
6
5
  var utils_typescript = require('./typescript.js');
@@ -47,7 +46,7 @@ async function migrateTemplate(tree, filePaths, context, migrationData) {
47
46
  * @param context
48
47
  * @param transform
49
48
  */
50
- async function migrateTsWithTransform(tree, sourceFiles, basePath, context, transform) {
49
+ async function migrateTsWithTransform(tree, sourceFiles, context, transform) {
51
50
  // run and track changed components
52
51
  const analysis = new Map();
53
52
  for (const sourceFile of sourceFiles) {
@@ -56,16 +55,15 @@ async function migrateTsWithTransform(tree, sourceFiles, basePath, context, tran
56
55
  });
57
56
  }
58
57
  // update source file
59
- for (const path$1 of analysis.keys()) {
60
- const file = analysis.get(path$1);
58
+ for (const path of analysis.keys()) {
59
+ const file = analysis.get(path);
61
60
  const ranges = file.getSortedRanges();
62
- const relativePath = path.relative(basePath, path$1);
63
- const content = tree.readText(relativePath);
64
- const update = tree.beginUpdate(relativePath);
61
+ const content = tree.readText(path);
62
+ const update = tree.beginUpdate(path);
65
63
  for (const { start, end } of ranges) {
66
64
  const template = content.slice(start, end);
67
65
  const length = (end ?? content.length) - start;
68
- const { fileContent, changed, errors } = await transform(template, relativePath);
66
+ const { fileContent, changed, errors } = await transform(template, path);
69
67
  if (changed) {
70
68
  update.remove(start, length);
71
69
  update.insertLeft(start, fileContent);
@@ -81,12 +79,11 @@ async function migrateTsWithTransform(tree, sourceFiles, basePath, context, tran
81
79
  * Update typescript file if classes with @Component decorator and `template` property exists
82
80
  * @param tree
83
81
  * @param sourceFiles
84
- * @param basePath
85
82
  * @param context
86
83
  * @param migrationData
87
84
  */
88
- async function migrateTs(tree, sourceFiles, basePath, context, migrationData) {
89
- return migrateTsWithTransform(tree, sourceFiles, basePath, context, (template, fileName) => transformTemplateAttributes(template, fileName, migrationData));
85
+ async function migrateTs(tree, sourceFiles, context, migrationData) {
86
+ return migrateTsWithTransform(tree, sourceFiles, context, (template, fileName) => transformTemplateAttributes(template, fileName, migrationData));
90
87
  }
91
88
  /**
92
89
  * Analyzes the decorators of a given class declaration to find and save
@@ -158,9 +155,11 @@ async function transformTemplateAttributes(template, fileName, migrationData) {
158
155
  : `Element has value "${cleanAttrValue}" with no matching replacement. Check the code and change value on your own.${(fileName && ' File: ' + fileName) || ''}`);
159
156
  continue;
160
157
  }
158
+ // The author's quote is kept: inside a "…" TS literal only single quotes are valid.
159
+ const quote = template[migrationAttr.valueSpan.start.offset - 1] === "'" ? "'" : '"';
161
160
  updatedTemplate =
162
161
  updatedTemplate.slice(0, migrationAttr.keySpan.start.offset - offset) +
163
- `${migrationData.attrs.key.to}="${updatedAttrValue}"` +
162
+ `${migrationData.attrs.key.to}=${quote}${updatedAttrValue}${quote}` +
164
163
  updatedTemplate.slice(migrationAttr.valueSpan.end.offset + 1 - offset, updatedTemplate.length);
165
164
  offset += migrationAttr.name.length - migrationData.attrs.key.to.length;
166
165
  offset += migrationAttr.value.length - updatedAttrValue.length;
@@ -60,10 +60,11 @@ async function getParsingInfo(project, tree) {
60
60
  const tsPaths = new Set();
61
61
  const templatePaths = new Set();
62
62
  const projectDefinition = await setupOptions(project, tree);
63
- if (!projectDefinition) {
64
- throw new schematics.SchematicsException(utils_messages.noProject('no project'));
65
- }
66
- tree.getDir(projectDefinition.root).visit((filePath) => {
63
+ // `ng update` passes no options at all, so without a project the whole workspace is migrated.
64
+ const root = projectDefinition?.root;
65
+ (root ? tree.getDir(root) : tree.root).visit((filePath) => {
66
+ if (filePath.includes('node_modules') || filePath.includes('/dist/'))
67
+ return;
67
68
  if (filePath.endsWith('.ts')) {
68
69
  tsPaths.add(filePath);
69
70
  }
@@ -54,6 +54,29 @@ function collectInlineTemplateRanges(sourceFile) {
54
54
  });
55
55
  return ranges;
56
56
  }
57
+ /**
58
+ * Parses the given tree files into source files.
59
+ *
60
+ * The callers only walk decorators and inline templates, so no program is built: one rooted at paths
61
+ * that live in the tree rather than on disk resolves none of them and returns the default libraries
62
+ * instead — around 490 declaration files per run, and not one project source.
63
+ */
64
+ function readSourceFiles(tree, paths) {
65
+ const sourceFiles = [];
66
+ for (const path of paths) {
67
+ if (path.endsWith('.d.ts'))
68
+ continue;
69
+ // Without the BOM, as readText and the update recorder that later edit the file see it.
70
+ const content = tree
71
+ .read(path)
72
+ ?.toString()
73
+ .replace(/^\uFEFF/, '');
74
+ if (content !== undefined) {
75
+ sourceFiles.push(ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS));
76
+ }
77
+ }
78
+ return sourceFiles;
79
+ }
57
80
  function canMigrateFile(sourceFile, program) {
58
81
  // We shouldn't migrate .d.ts files, files from an external library or type checking files.
59
82
  return !(sourceFile.fileName.endsWith('.ngtypecheck.ts') ||
@@ -212,4 +235,5 @@ exports.forEachClass = forEachClass;
212
235
  exports.forEachStringLiteral = forEachStringLiteral;
213
236
  exports.getClassWithUpdatedDecorator = getClassWithUpdatedDecorator;
214
237
  exports.parseTemplate = parseTemplate;
238
+ exports.readSourceFiles = readSourceFiles;
215
239
  exports.updateDecoratorProperty = updateDecoratorProperty;
@@ -1,4 +1,5 @@
1
1
  @use '../core/styles/common/popup' as *;
2
+ @use '../core/styles/common/select' as *;
2
3
  @use '../core/styles/theming/theming' as *;
3
4
  @use '../core/styles/common/tokens' as *;
4
5
 
@@ -39,6 +40,8 @@
39
40
  & .kbq-select__footer {
40
41
  border-color: var(--kbq-divider-color, var(--kbq-line-contrast-less));
41
42
  }
43
+
44
+ @include kbq-select-footer-item-theme();
42
45
  }
43
46
 
44
47
  .kbq-select__search-container {
@@ -55,7 +58,12 @@
55
58
  @include kbq-typography-level-to-styles-css-variables(typography, text-normal);
56
59
  }
57
60
 
58
- .kbq-select__panel {
61
+ // The footer's action row is projected as a native `button`, whose user-agent `font` shorthand resets
62
+ // the stylistic sets and the letter spacing the panel puts on every option. Inheriting the individual
63
+ // longhands cannot undo that, so the row takes the level in full, as `.kbq-button` and
64
+ // `.kbq-dropdown-item` do.
65
+ .kbq-select__panel,
66
+ .kbq-select__footer-item {
59
67
  @include kbq-typography-level-to-styles-css-variables(typography, text-normal);
60
68
  }
61
69
  }
package/select/index.d.ts CHANGED
@@ -54,12 +54,18 @@ declare class KbqOptionTooltip extends KbqTooltipTrigger implements AfterViewIni
54
54
  private readonly resizeObserver;
55
55
  private readonly contentObserver;
56
56
  private readonly isBrowser;
57
+ private readonly window;
57
58
  private readonly debounceInterval;
58
59
  private resizeSubscription;
59
60
  private contentObserverSubscription;
60
61
  private focusMonitorSubscription;
61
62
  get textElement(): HTMLElement;
62
63
  get isOverflown(): boolean;
64
+ /**
65
+ * Whether one of the option's own line boxes is truncating its text. The ellipsis is part of the
66
+ * condition, so a child clipping for another reason is not read as truncated text.
67
+ */
68
+ private hasClippedLine;
63
69
  constructor();
64
70
  ngAfterViewInit(): void;
65
71
  ngOnDestroy(): void;
@@ -150,6 +156,7 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
150
156
  private readonly _changeDetectorRef;
151
157
  private readonly _ngZone;
152
158
  private readonly _renderer;
159
+ private readonly interactivityChecker;
153
160
  defaultErrorStateMatcher: ErrorStateMatcher;
154
161
  elementRef: ElementRef<HTMLElement>;
155
162
  private overlayContainer;
@@ -629,7 +636,7 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
629
636
  * Sets up the overlay dimensions based on trigger size and configured options.
630
637
  */
631
638
  openPanel(): void;
632
- /** Closes the overlay panel and focuses the host element. */
639
+ /** Closes the overlay panel. */
633
640
  close(): void;
634
641
  /**
635
642
  * Sets the select's value. Part of the ControlValueAccessor interface
@@ -725,6 +732,15 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
725
732
  * @param $event The mouse event to handle.
726
733
  */
727
734
  handleClick($event: MouseEvent): void;
735
+ /** Whether TAB should stop on a footer control. */
736
+ private isFooterTabStop;
737
+ /**
738
+ * Moves focus to the footer control next to `current` — the first one when focus is still outside the
739
+ * footer — and reports whether it landed. Every element is offered to `InteractivityChecker` rather
740
+ * than matched against a hand-written selector, which would drift from what the browser considers a
741
+ * tab stop.
742
+ */
743
+ private focusNextInFooter;
728
744
  /** @docs-private */
729
745
  setSelectedOptionsByClick(option: KbqOption): void;
730
746
  /** @docs-private */
@@ -865,7 +881,7 @@ declare class KbqSelect extends KbqAbstractSelect implements AfterContentInit, O
865
881
 
866
882
  declare class KbqSelectModule {
867
883
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<KbqSelectModule, never>;
868
- static ɵmod: _angular_core.ɵɵNgModuleDeclaration<KbqSelectModule, never, [typeof i1.OverlayModule, typeof i2.KbqOptionModule, typeof i3.KbqIconModule, typeof i4.KbqTagsModule, typeof i5.KbqToolTipModule, typeof i2.KbqSelectSearch, typeof i2.KbqSelectFooter, typeof i2.KbqSelectMatcher, typeof i2.KbqSelectTrigger, typeof i2.KbqSelectSearchEmptyResult, typeof i6.NgTemplateOutlet, typeof i7.A11yModule, typeof KbqSelect, typeof KbqOptionTooltip, typeof KbqSelectLoading, typeof KbqSelectError, typeof KbqSelectNoOptions, typeof KbqSelectErrorText], [typeof KbqSelect, typeof KbqOptionTooltip, typeof i2.KbqOptionModule, typeof i2.KbqSelectSearch, typeof i2.KbqSelectFooter, typeof i2.KbqSelectMatcher, typeof i2.KbqSelectTrigger, typeof i2.KbqSelectSearchEmptyResult, typeof KbqSelectLoading, typeof i11.KbqFormFieldModule, typeof KbqSelectError, typeof KbqSelectNoOptions, typeof KbqSelectErrorText]>;
884
+ static ɵmod: _angular_core.ɵɵNgModuleDeclaration<KbqSelectModule, never, [typeof i1.OverlayModule, typeof i2.KbqOptionModule, typeof i3.KbqIconModule, typeof i4.KbqTagsModule, typeof i5.KbqToolTipModule, typeof i2.KbqSelectSearch, typeof i2.KbqSelectFooter, typeof i2.KbqSelectFooterItem, typeof i2.KbqSelectMatcher, typeof i2.KbqSelectTrigger, typeof i2.KbqSelectSearchEmptyResult, typeof i6.NgTemplateOutlet, typeof i7.A11yModule, typeof KbqSelect, typeof KbqOptionTooltip, typeof KbqSelectLoading, typeof KbqSelectError, typeof KbqSelectNoOptions, typeof KbqSelectErrorText], [typeof KbqSelect, typeof KbqOptionTooltip, typeof i2.KbqOptionModule, typeof i2.KbqSelectSearch, typeof i2.KbqSelectFooter, typeof i2.KbqSelectFooterItem, typeof i2.KbqSelectMatcher, typeof i2.KbqSelectTrigger, typeof i2.KbqSelectSearchEmptyResult, typeof KbqSelectLoading, typeof i11.KbqFormFieldModule, typeof KbqSelectError, typeof KbqSelectNoOptions, typeof KbqSelectErrorText]>;
869
885
  static ɵinj: _angular_core.ɵɵInjectorDeclaration<KbqSelectModule>;
870
886
  }
871
887
 
@@ -14,6 +14,19 @@
14
14
  & .kbq-select__footer {
15
15
  @extend %kbq-select-footer;
16
16
  }
17
+
18
+ & .kbq-select__footer-item {
19
+ @extend %kbq-select-footer-item;
20
+ }
21
+
22
+ // A menu-style action lines up with the options above it, so the footer drops to the list's own
23
+ // inset and lets the row's padding cover the rest. Kept here rather than in `%kbq-select-footer`,
24
+ // which the dropdown and the autocomplete also extend without ever styling the row itself. No child
25
+ // combinator: the content query behind the row is `descendants: true`, so wrapping it must not
26
+ // silently leave the inset behind.
27
+ & .kbq-select__footer:has(.kbq-select__footer-item) {
28
+ --kbq-select-footer-size-padding-horizontal: var(--kbq-size-xxs);
29
+ }
17
30
  }
18
31
 
19
32
  .kbq-select__content {
@@ -37,6 +37,7 @@ declare class KbqSidebar implements OnDestroy, AfterContentInit {
37
37
  protected readonly document: Document;
38
38
  private readonly renderer;
39
39
  private readonly changeDetectorRef;
40
+ private readonly destroyRef;
40
41
  private readonly isBrowser;
41
42
  get opened(): boolean;
42
43
  set opened(value: boolean);
@@ -155,6 +155,7 @@ declare class KbqSidepanelClose implements OnInit, OnChanges {
155
155
  sidepanelRef: KbqSidepanelRef<any, any>;
156
156
  private elementRef;
157
157
  private sidepanelService;
158
+ private readonly renderer;
158
159
  sidepanelResult: any;
159
160
  readonly kbqSidepanelClose: i0.InputSignal<any>;
160
161
  ngOnInit(): void;
@@ -250,6 +251,14 @@ declare class KbqSidepanelService implements OnDestroy {
250
251
  private closeSidepanels;
251
252
  private getOpenedSidepanelsWithSamePosition;
252
253
  private updateAnimationState;
254
+ /**
255
+ * Moves a sidepanel into `state`, unless it is closing already.
256
+ *
257
+ * The exit animation is what disposes the overlay, so interrupting it with another state leaves the
258
+ * sidepanel in the DOM and in `openedSidepanels` for good — which is what closing a stack of them as
259
+ * a group used to do to the panels underneath.
260
+ */
261
+ private setAnimationState;
253
262
  /**
254
263
  * Removes a sidepanel from the array of open sidepanels.
255
264
  * @param sidepanelRef Sidepanel to be removed.
@@ -139,14 +139,23 @@
139
139
  .kbq-tab-header__pagination {
140
140
  @include vendor-prefixes.user-select(none);
141
141
 
142
- position: relative;
142
+ // Overlaid on top of the scroll container (rather than a flex sibling reserving its own
143
+ // space) so the tab list always spans the full width, matching Koobiq React: the button
144
+ // itself fades away at each scroll bound instead of the tab list resizing around it.
145
+ position: absolute;
146
+ inset-block: 0;
143
147
  display: none;
144
148
  justify-content: center;
145
149
  align-items: center;
146
150
  z-index: 2;
147
151
  -webkit-tap-highlight-color: transparent;
148
- touch-action: none;
152
+ // Not `none`: this element overlays live scroll-container edge, so `none` also blocks a
153
+ // touch swipe that starts in that band from panning the strip at all. `manipulation` still
154
+ // suppresses double-tap-to-zoom, which is all this was here for.
155
+ touch-action: manipulation;
149
156
  padding: 0 var(--kbq-size-m);
157
+ opacity: 1;
158
+ visibility: visible;
150
159
 
151
160
  .kbq-tab-header__pagination-controls_enabled & {
152
161
  display: flex;
@@ -155,6 +164,106 @@
155
164
  &:not(.kbq-disabled) {
156
165
  cursor: pointer;
157
166
  }
167
+
168
+ &.kbq-disabled {
169
+ opacity: 0;
170
+ visibility: hidden;
171
+ pointer-events: none;
172
+ }
173
+ }
174
+
175
+ .kbq-tab-header__pagination_before {
176
+ inset-inline-start: 0;
177
+ }
178
+
179
+ .kbq-tab-header__pagination_after {
180
+ inset-inline-end: 0;
181
+ }
182
+
183
+ .kbq-tab-header__scroll-container {
184
+ @include vendor-prefixes.user-select(none);
185
+
186
+ // Matches `.kbq-tab-header__pagination`'s real rendered width (its horizontal padding
187
+ // twice, plus the 16px arrow icon) rather than an unrelated constant — otherwise the label
188
+ // underneath fades in for a few pixels *before* it clears the paginator it's still hidden
189
+ // behind, which is a band that's visible but can't be clicked (the paginator, not the
190
+ // label, is what's on top there).
191
+ --mask-size: calc(var(--kbq-size-m) * 2 + 16px);
192
+
193
+ overflow-x: auto;
194
+ overflow-y: hidden;
195
+ scrollbar-width: none;
196
+ -ms-overflow-style: none;
197
+
198
+ &::-webkit-scrollbar {
199
+ display: none;
200
+ }
201
+
202
+ // `kbq-tab-group_vertical` sits on an ancestor either way: the `<kbq-tab-group>` wrapping a
203
+ // `kbq-tab-header`, or the `[kbqTabNavBar]` host itself — so one descendant selector covers
204
+ // both `KbqPaginatedTabHeader` subclasses instead of the two disagreeing on the scrollbar.
205
+ .kbq-tab-group_vertical & {
206
+ overflow-x: hidden;
207
+ overflow-y: auto;
208
+ scrollbar-width: auto;
209
+
210
+ &::-webkit-scrollbar {
211
+ display: block;
212
+ }
213
+ }
214
+
215
+ .kbq-tab-header__pagination-controls_enabled & {
216
+ cursor: grab;
217
+ }
218
+
219
+ &.kbq-tab-header__scroll-container_dragging {
220
+ cursor: grabbing;
221
+ }
222
+
223
+ &.kbq-tab-header__scroll-container_overflow-before {
224
+ mask-image: linear-gradient(to right, transparent var(--mask-size), #000 calc(var(--mask-size) * 1.5));
225
+ }
226
+
227
+ &.kbq-tab-header__scroll-container_overflow-after {
228
+ mask-image: linear-gradient(
229
+ to right,
230
+ #000 calc(100% - var(--mask-size) * 1.5),
231
+ transparent calc(100% - var(--mask-size))
232
+ );
233
+ }
234
+
235
+ &.kbq-tab-header__scroll-container_overflow-before.kbq-tab-header__scroll-container_overflow-after {
236
+ mask-image: linear-gradient(
237
+ to right,
238
+ transparent var(--mask-size),
239
+ #000 calc(var(--mask-size) * 1.5),
240
+ #000 calc(100% - var(--mask-size) * 1.5),
241
+ transparent calc(100% - var(--mask-size))
242
+ );
243
+ }
244
+
245
+ .kbq-tab-header_rtl &.kbq-tab-header__scroll-container_overflow-before {
246
+ mask-image: linear-gradient(to left, transparent var(--mask-size), #000 calc(var(--mask-size) * 1.5));
247
+ }
248
+
249
+ .kbq-tab-header_rtl &.kbq-tab-header__scroll-container_overflow-after {
250
+ mask-image: linear-gradient(
251
+ to left,
252
+ #000 calc(100% - var(--mask-size) * 1.5),
253
+ transparent calc(100% - var(--mask-size))
254
+ );
255
+ }
256
+
257
+ .kbq-tab-header_rtl
258
+ &.kbq-tab-header__scroll-container_overflow-before.kbq-tab-header__scroll-container_overflow-after {
259
+ mask-image: linear-gradient(
260
+ to left,
261
+ transparent var(--mask-size),
262
+ #000 calc(var(--mask-size) * 1.5),
263
+ #000 calc(100% - var(--mask-size) * 1.5),
264
+ transparent calc(100% - var(--mask-size))
265
+ );
266
+ }
158
267
  }
159
268
 
160
269
  .kbq-tab-header_underlined:not(.kbq-tab-header_vertical) .kbq-tab-list__content {