@koobiq/components 19.8.5 → 19.8.7

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 (204) hide show
  1. package/accordion/accordion-content.directive.d.ts +23 -3
  2. package/accordion/accordion-trigger.component.d.ts +1 -0
  3. package/accordion/accordion.component.scss +4 -1
  4. package/alert/alert-tokens.scss +4 -4
  5. package/app-switcher/app-switcher.d.ts +9 -2
  6. package/autocomplete/autocomplete-trigger.directive.d.ts +10 -5
  7. package/breadcrumbs/breadcrumbs.d.ts +2 -0
  8. package/breadcrumbs/breadcrumbs.scss +4 -0
  9. package/button/button-group.scss +42 -2
  10. package/button-toggle/button-toggle-tokens.scss +16 -0
  11. package/button-toggle/button-toggle.component.d.ts +3 -1
  12. package/button-toggle/button-toggle.scss +105 -1
  13. package/checkbox/checkbox.scss +5 -4
  14. package/clamped-text/clamped-text.d.ts +1 -0
  15. package/core/form-field/field-sizing-content.d.ts +0 -1
  16. package/core/form-field/form-field-ref.d.ts +1 -0
  17. package/core/form-field/index.d.ts +1 -0
  18. package/core/form-field/text-field-selection.d.ts +18 -0
  19. package/core/form-field/text-ruler.d.ts +16 -0
  20. package/core/navbar/rectangle-item.d.ts +8 -0
  21. package/core/option/_action-container-mixins.scss +28 -0
  22. package/core/option/_option-theme.scss +8 -0
  23. package/core/option/action.d.ts +9 -0
  24. package/core/option/action.scss +7 -0
  25. package/core/option/option.d.ts +10 -6
  26. package/core/pop-up/pop-up-trigger.d.ts +6 -0
  27. package/core/select/constants.d.ts +8 -1
  28. package/core/services/measure-scrollbar.service.d.ts +1 -0
  29. package/core/styles/common/_animation.scss +35 -24
  30. package/core/styles/common/_select.scss +21 -3
  31. package/core/styles/theming/_scrollbar-theme.scss +4 -0
  32. package/core/styles/typography/_typography.scss +8 -0
  33. package/core/utils/dom.d.ts +12 -0
  34. package/datepicker/datepicker-input.directive.d.ts +20 -5
  35. package/datepicker/datepicker.component.d.ts +4 -1
  36. package/dl/dl.component.d.ts +1 -0
  37. package/dl/dl.scss +10 -0
  38. package/dropdown/dropdown-item.component.d.ts +10 -2
  39. package/dropdown/dropdown-item.scss +16 -0
  40. package/dropdown/dropdown-trigger.directive.d.ts +6 -1
  41. package/dropdown/dropdown.component.d.ts +6 -1
  42. package/dropdown/dropdown.module.d.ts +1 -1
  43. package/dropdown/dropdown.scss +18 -3
  44. package/ellipsis-center/ellipsis-center.directive.d.ts +40 -2
  45. package/fesm2022/koobiq-components-accordion.mjs +104 -16
  46. package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
  47. package/fesm2022/koobiq-components-alert.mjs +2 -2
  48. package/fesm2022/koobiq-components-app-switcher.mjs +8 -8
  49. package/fesm2022/koobiq-components-app-switcher.mjs.map +1 -1
  50. package/fesm2022/koobiq-components-autocomplete.mjs +21 -9
  51. package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
  52. package/fesm2022/koobiq-components-breadcrumbs.mjs +10 -5
  53. package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
  54. package/fesm2022/koobiq-components-button-toggle.mjs +8 -5
  55. package/fesm2022/koobiq-components-button-toggle.mjs.map +1 -1
  56. package/fesm2022/koobiq-components-button.mjs +4 -4
  57. package/fesm2022/koobiq-components-button.mjs.map +1 -1
  58. package/fesm2022/koobiq-components-checkbox.mjs +2 -2
  59. package/fesm2022/koobiq-components-checkbox.mjs.map +1 -1
  60. package/fesm2022/koobiq-components-clamped-text.mjs +4 -0
  61. package/fesm2022/koobiq-components-clamped-text.mjs.map +1 -1
  62. package/fesm2022/koobiq-components-code-block.mjs +14 -5
  63. package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
  64. package/fesm2022/koobiq-components-content-panel.mjs +5 -4
  65. package/fesm2022/koobiq-components-content-panel.mjs.map +1 -1
  66. package/fesm2022/koobiq-components-core.mjs +244 -67
  67. package/fesm2022/koobiq-components-core.mjs.map +1 -1
  68. package/fesm2022/koobiq-components-datepicker.mjs +73 -49
  69. package/fesm2022/koobiq-components-datepicker.mjs.map +1 -1
  70. package/fesm2022/koobiq-components-dl.mjs +5 -4
  71. package/fesm2022/koobiq-components-dl.mjs.map +1 -1
  72. package/fesm2022/koobiq-components-dropdown.mjs +65 -22
  73. package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
  74. package/fesm2022/koobiq-components-ellipsis-center.mjs +82 -22
  75. package/fesm2022/koobiq-components-ellipsis-center.mjs.map +1 -1
  76. package/fesm2022/koobiq-components-file-upload.mjs +12 -12
  77. package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
  78. package/fesm2022/koobiq-components-filter-bar.mjs +152 -46
  79. package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
  80. package/fesm2022/koobiq-components-form-field.mjs +27 -6
  81. package/fesm2022/koobiq-components-form-field.mjs.map +1 -1
  82. package/fesm2022/koobiq-components-icon.mjs +1 -1
  83. package/fesm2022/koobiq-components-icon.mjs.map +1 -1
  84. package/fesm2022/koobiq-components-inline-edit.mjs +26 -5
  85. package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
  86. package/fesm2022/koobiq-components-list.mjs +12 -22
  87. package/fesm2022/koobiq-components-list.mjs.map +1 -1
  88. package/fesm2022/koobiq-components-markdown.mjs +4 -1
  89. package/fesm2022/koobiq-components-markdown.mjs.map +1 -1
  90. package/fesm2022/koobiq-components-modal.mjs +53 -12
  91. package/fesm2022/koobiq-components-modal.mjs.map +1 -1
  92. package/fesm2022/koobiq-components-navbar.mjs +69 -21
  93. package/fesm2022/koobiq-components-navbar.mjs.map +1 -1
  94. package/fesm2022/koobiq-components-notification-center.mjs +45 -7
  95. package/fesm2022/koobiq-components-notification-center.mjs.map +1 -1
  96. package/fesm2022/koobiq-components-overflow-items.mjs +4 -0
  97. package/fesm2022/koobiq-components-overflow-items.mjs.map +1 -1
  98. package/fesm2022/koobiq-components-popover.mjs +39 -19
  99. package/fesm2022/koobiq-components-popover.mjs.map +1 -1
  100. package/fesm2022/koobiq-components-radio.mjs +3 -7
  101. package/fesm2022/koobiq-components-radio.mjs.map +1 -1
  102. package/fesm2022/koobiq-components-resizer.mjs +23 -4
  103. package/fesm2022/koobiq-components-resizer.mjs.map +1 -1
  104. package/fesm2022/koobiq-components-search-expandable.mjs +2 -1
  105. package/fesm2022/koobiq-components-search-expandable.mjs.map +1 -1
  106. package/fesm2022/koobiq-components-select.mjs +174 -40
  107. package/fesm2022/koobiq-components-select.mjs.map +1 -1
  108. package/fesm2022/koobiq-components-sidebar.mjs +10 -0
  109. package/fesm2022/koobiq-components-sidebar.mjs.map +1 -1
  110. package/fesm2022/koobiq-components-sidepanel.mjs +34 -10
  111. package/fesm2022/koobiq-components-sidepanel.mjs.map +1 -1
  112. package/fesm2022/koobiq-components-splitter.mjs +4 -0
  113. package/fesm2022/koobiq-components-splitter.mjs.map +1 -1
  114. package/fesm2022/koobiq-components-tabs.mjs +27 -7
  115. package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
  116. package/fesm2022/koobiq-components-tags.mjs +3 -1
  117. package/fesm2022/koobiq-components-tags.mjs.map +1 -1
  118. package/fesm2022/koobiq-components-textarea.mjs +7 -14
  119. package/fesm2022/koobiq-components-textarea.mjs.map +1 -1
  120. package/fesm2022/koobiq-components-time-range.mjs +20 -2
  121. package/fesm2022/koobiq-components-time-range.mjs.map +1 -1
  122. package/fesm2022/koobiq-components-timepicker.mjs +44 -19
  123. package/fesm2022/koobiq-components-timepicker.mjs.map +1 -1
  124. package/fesm2022/koobiq-components-timezone.mjs +17 -6
  125. package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
  126. package/fesm2022/koobiq-components-title.mjs +50 -7
  127. package/fesm2022/koobiq-components-title.mjs.map +1 -1
  128. package/fesm2022/koobiq-components-toast.mjs +31 -3
  129. package/fesm2022/koobiq-components-toast.mjs.map +1 -1
  130. package/fesm2022/koobiq-components-toggle.mjs +2 -2
  131. package/fesm2022/koobiq-components-toggle.mjs.map +1 -1
  132. package/fesm2022/koobiq-components-tooltip.mjs +46 -6
  133. package/fesm2022/koobiq-components-tooltip.mjs.map +1 -1
  134. package/fesm2022/koobiq-components-tree-select.mjs +56 -14
  135. package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
  136. package/fesm2022/koobiq-components-tree.mjs +41 -37
  137. package/fesm2022/koobiq-components-tree.mjs.map +1 -1
  138. package/fesm2022/koobiq-components-username.mjs +2 -2
  139. package/fesm2022/koobiq-components-username.mjs.map +1 -1
  140. package/file-upload/dropzone.d.ts +0 -1
  141. package/file-upload/file-upload.scss +11 -0
  142. package/file-upload/multiple-file-upload.component.scss +29 -3
  143. package/file-upload/primitives/file-picker.d.ts +1 -1
  144. package/file-upload/single-file-upload.component.scss +10 -2
  145. package/filter-bar/filter-bar.d.ts +1 -1
  146. package/filter-bar/pipes/base-pipe.d.ts +31 -0
  147. package/filter-bar/pipes/pipe-date.d.ts +1 -1
  148. package/filter-bar/pipes/pipe-datetime.d.ts +1 -1
  149. package/form-field/fieldset.scss +10 -0
  150. package/form-field/form-field-control.d.ts +10 -0
  151. package/form-field/form-field.d.ts +17 -0
  152. package/inline-edit/inline-edit.d.ts +5 -0
  153. package/inline-edit/inline-edit.scss +7 -7
  154. package/link/_link-theme.scss +50 -15
  155. package/list/_list-theme.scss +12 -0
  156. package/list/list-selection.component.d.ts +1 -0
  157. package/list/list.scss +2 -9
  158. package/modal/modal-tokens.scss +1 -1
  159. package/modal/modal.component.d.ts +9 -1
  160. package/modal/modal.directive.d.ts +11 -0
  161. package/modal/modal.type.d.ts +7 -0
  162. package/navbar/_navbar-item_horizontal.scss +5 -0
  163. package/navbar/_navbar-item_vertical.scss +7 -5
  164. package/navbar/navbar-brand.component.d.ts +1 -0
  165. package/navbar/navbar-brand.scss +12 -5
  166. package/navbar/navbar-item.component.d.ts +33 -1
  167. package/navbar/navbar.component.d.ts +1 -0
  168. package/navbar/vertical-navbar.scss +6 -0
  169. package/notification-center/notification-center.d.ts +13 -1
  170. package/notification-center/notification-center.scss +12 -0
  171. package/notification-center/notification-item.scss +10 -1
  172. package/overflow-items/overflow-items.d.ts +1 -0
  173. package/package.json +8 -8
  174. package/prebuilt-themes/dark-theme.css +1 -1
  175. package/prebuilt-themes/light-theme.css +1 -1
  176. package/prebuilt-themes/theme.css +1 -1
  177. package/radio/radio.component.d.ts +1 -5
  178. package/radio/radio.scss +18 -4
  179. package/resizer/resizer.d.ts +12 -0
  180. package/schematics/ng-add/index.js +2 -2
  181. package/select/select-option.directive.d.ts +6 -0
  182. package/select/select.component.d.ts +51 -4
  183. package/sidebar/sidebar.component.d.ts +3 -0
  184. package/sidepanel/sidepanel-directives.d.ts +2 -0
  185. package/sidepanel/sidepanel.service.d.ts +8 -0
  186. package/splitter/splitter.component.d.ts +1 -0
  187. package/tabs/tab-body.component.d.ts +2 -0
  188. package/tabs/tab-header.component.d.ts +1 -0
  189. package/tabs/tab-header.scss +1 -0
  190. package/tabs/tab-nav-bar.d.ts +1 -0
  191. package/tabs/tabs-tokens.scss +1 -1
  192. package/textarea/textarea.component.d.ts +0 -1
  193. package/time-range/time-range-title-as-form-field.d.ts +11 -0
  194. package/timepicker/timepicker.directive.d.ts +3 -0
  195. package/timezone/timezone-option.directive.d.ts +6 -0
  196. package/title/title.directive.d.ts +26 -0
  197. package/toast/toast-container.component.d.ts +25 -0
  198. package/tooltip/tooltip.component.d.ts +25 -1
  199. package/tree/_tree-theme.scss +5 -11
  200. package/tree/toggle.scss +4 -0
  201. package/tree/tree-option.component.d.ts +3 -0
  202. package/tree/tree-option.scss +2 -12
  203. package/tree/tree-selection.component.d.ts +1 -0
  204. package/tree-select/tree-select.component.d.ts +24 -2
@@ -4,7 +4,7 @@ import { EventEmitter, Output, Input, ChangeDetectionStrategy, ViewEncapsulation
4
4
  import * as i2 from '@koobiq/components/button';
5
5
  import { KbqButtonModule } from '@koobiq/components/button';
6
6
  import * as i1 from '@koobiq/components/core';
7
- import { KbqOptionModule, defaultOffsetY, ruRULocaleData, validationTooltipShowDelay, validationTooltipHideDelay, KBQ_LOCALE_SERVICE, KBQ_VALIDATION, KbqErrorStateTracker, ErrorStateMatcher, KBQ_DATE_FORMATS } from '@koobiq/components/core';
7
+ import { KbqOptionModule, defaultOffsetY, ruRULocaleData, validationTooltipShowDelay, validationTooltipHideDelay, KBQ_LOCALE_SERVICE, KBQ_VALIDATION, KbqErrorStateTracker, ErrorStateMatcher, kbqRevealSelection, kbqSetSelectionRange, KBQ_DATE_FORMATS } from '@koobiq/components/core';
8
8
  import * as i3 from '@koobiq/components/icon';
9
9
  import { KbqIconModule } from '@koobiq/components/icon';
10
10
  import * as i4 from '@koobiq/components/select';
@@ -663,8 +663,14 @@ const kbqDatepickerAnimations = {
663
663
 
664
664
  /** Used to generate a unique ID for each datepicker instance. */
665
665
  let datepickerUid = 0;
666
- /** Injection token that determines the scroll handling while the calendar is open. */
667
- const KBQ_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('kbq-datepicker-scroll-strategy');
666
+ /**
667
+ * Injection token that determines the scroll handling while the calendar is open. The root default keeps the
668
+ * datepicker usable outside `KbqDatepickerModule`'s injector; providing the token anywhere still wins over it.
669
+ */
670
+ const KBQ_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('kbq-datepicker-scroll-strategy', {
671
+ providedIn: 'root',
672
+ factory: () => KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY(inject(Overlay))
673
+ });
668
674
  /** @docs-private */
669
675
  function KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay) {
670
676
  return () => overlay.scrollStrategies.reposition();
@@ -1212,20 +1218,36 @@ class KbqDatepickerInput {
1212
1218
  this.valueChange.emit(newValue);
1213
1219
  }
1214
1220
  }
1215
- /** The minimum valid date. */
1221
+ /**
1222
+ * The minimum valid date. Drives validation only — including dates typed or pasted into the
1223
+ * input — and reports `kbqDatepickerMin`. The calendar is restricted separately, by `minDate`
1224
+ * on the associated `<kbq-datepicker>`.
1225
+ *
1226
+ * Compared down to the millisecond, not by calendar day: the input keeps the time part of the
1227
+ * value it parses, and the comparison is made on wall-clock components rather than on absolute
1228
+ * time.
1229
+ */
1216
1230
  get min() {
1217
1231
  return this._min;
1218
1232
  }
1219
1233
  set min(value) {
1220
- this._min = this.adapter.deserialize(value);
1234
+ this._min = this.getValidDateOrNull(this.adapter.deserialize(value));
1221
1235
  this.validatorOnChange();
1222
1236
  }
1223
- /** The maximum valid date. */
1237
+ /**
1238
+ * The maximum valid date. Drives validation only — including dates typed or pasted into the
1239
+ * input — and reports `kbqDatepickerMax`. The calendar is restricted separately, by `maxDate`
1240
+ * on the associated `<kbq-datepicker>`.
1241
+ *
1242
+ * Compared down to the millisecond, not by calendar day: the input keeps the time part of the
1243
+ * value it parses, so an upper bound meant to include its whole day has to be the end of that
1244
+ * day.
1245
+ */
1224
1246
  get max() {
1225
1247
  return this._max;
1226
1248
  }
1227
1249
  set max(value) {
1228
- this._max = this.adapter.deserialize(value);
1250
+ this._max = this.getValidDateOrNull(this.adapter.deserialize(value));
1229
1251
  this.validatorOnChange();
1230
1252
  }
1231
1253
  /** Whether the datepicker-input is disabled. */
@@ -1295,15 +1317,9 @@ class KbqDatepickerInput {
1295
1317
  get selectionStart() {
1296
1318
  return this.elementRef.nativeElement.selectionStart;
1297
1319
  }
1298
- set selectionStart(value) {
1299
- this.elementRef.nativeElement.selectionStart = value;
1300
- }
1301
1320
  get selectionEnd() {
1302
1321
  return this.elementRef.nativeElement.selectionEnd;
1303
1322
  }
1304
- set selectionEnd(value) {
1305
- this.elementRef.nativeElement.selectionEnd = value;
1306
- }
1307
1323
  constructor(elementRef, renderer, adapter, dateFormats) {
1308
1324
  this.elementRef = elementRef;
1309
1325
  this.renderer = renderer;
@@ -1338,7 +1354,7 @@ class KbqDatepickerInput {
1338
1354
  this.thirdDigit = null;
1339
1355
  this.onTouched = () => { };
1340
1356
  this.onInput = () => {
1341
- this.correctCursorPosition();
1357
+ const cursorPosition = this.correctedCursorPosition();
1342
1358
  const formattedValue = this.replaceSymbols(this.viewValue);
1343
1359
  const newTimeObj = this.getDateFromString(formattedValue);
1344
1360
  this.lastValueValid = !!newTimeObj;
@@ -1348,12 +1364,12 @@ class KbqDatepickerInput {
1348
1364
  this.updateValue(newTimeObj);
1349
1365
  }
1350
1366
  else {
1351
- this.control.updateValueAndValidity();
1367
+ this.control?.updateValueAndValidity();
1352
1368
  }
1353
1369
  return;
1354
1370
  }
1355
1371
  this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat), true);
1356
- this.selectNextDigitByCursor(this.selectionStart);
1372
+ this.selectNextDigitByCursor(cursorPosition);
1357
1373
  this.updateValue(newTimeObj);
1358
1374
  };
1359
1375
  this.parseOnBlur = () => {
@@ -1369,7 +1385,7 @@ class KbqDatepickerInput {
1369
1385
  if (viewDigits.length !== 3) {
1370
1386
  this.lastValueValid = false;
1371
1387
  this._value = null;
1372
- return setTimeout(() => this.control.updateValueAndValidity());
1388
+ return setTimeout(() => this.control?.updateValueAndValidity());
1373
1389
  }
1374
1390
  date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1375
1391
  date[this.secondDigit.fullName] = this.secondDigit.parse(secondViewDigit);
@@ -1381,14 +1397,14 @@ class KbqDatepickerInput {
1381
1397
  else if (viewDigitWithYear.length < digitWithYear.length) {
1382
1398
  this.lastValueValid = false;
1383
1399
  this._value = null;
1384
- return setTimeout(() => this.control.updateValueAndValidity());
1400
+ return setTimeout(() => this.control?.updateValueAndValidity());
1385
1401
  }
1386
1402
  const newTimeObj = this.createDateTime(date);
1387
1403
  if (!newTimeObj) {
1388
1404
  this.lastValueValid = false;
1389
1405
  this._value = null;
1390
1406
  this.cvaOnChange(null);
1391
- return setTimeout(() => this.control.updateValueAndValidity());
1407
+ return setTimeout(() => this.control?.updateValueAndValidity());
1392
1408
  }
1393
1409
  /* Check if the number of days entered does not match the entered month */
1394
1410
  if (!this.getValidDateOrNull(newTimeObj)) {
@@ -1413,14 +1429,16 @@ class KbqDatepickerInput {
1413
1429
  };
1414
1430
  /** The form control validator for the min date. */
1415
1431
  this.minValidator = (control) => {
1416
- const controlValue = this.adapter.deserialize(control.value);
1432
+ // An unparseable value compares as NaN against any bound, which fails both `<= 0` and
1433
+ // `>= 0` and would report the value as simultaneously too early and too late.
1434
+ const controlValue = this.getValidDateOrNull(this.adapter.deserialize(control.value));
1417
1435
  return !this.min || !controlValue || this.adapter.compareDateTime(this.min, controlValue) <= 0
1418
1436
  ? null
1419
1437
  : { kbqDatepickerMin: { min: this.min, actual: controlValue } };
1420
1438
  };
1421
1439
  /** The form control validator for the max date. */
1422
1440
  this.maxValidator = (control) => {
1423
- const controlValue = this.adapter.deserialize(control.value);
1441
+ const controlValue = this.getValidDateOrNull(this.adapter.deserialize(control.value));
1424
1442
  return !this.max || !controlValue || this.adapter.compareDateTime(this.max, controlValue) >= 0
1425
1443
  ? null
1426
1444
  : { kbqDatepickerMax: { max: this.max, actual: controlValue } };
@@ -1622,9 +1640,11 @@ class KbqDatepickerInput {
1622
1640
  this.configuration = this.externalConfiguration || KBQ_DATEPICKER_DEFAULT_CONFIGURATION;
1623
1641
  }
1624
1642
  setFormat(format) {
1625
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1626
- // @ts-ignore
1627
- this.separator = format.match(/[aA-zZ]+(?<separator>\W|\D)[aA-zZ]+/).groups.separator;
1643
+ // `[a-zA-Z]`, not `[aA-zZ]`: the latter reads as `a`, the range `A-z` and `Z`, and that range
1644
+ // takes in the six characters ASCII puts between the two alphabets — opening bracket,
1645
+ // backslash, closing bracket, caret, underscore and backtick. A format escaping a literal in
1646
+ // brackets could therefore have had that bracket counted as part of a placeholder.
1647
+ this.separator = format.match(/[a-zA-Z]+(?<separator>\W|\D)[a-zA-Z]+/).groups.separator;
1628
1648
  this.separatorPositions = format
1629
1649
  .split('')
1630
1650
  .reduce((acc, item, index) => (this.separator === item ? [...acc, index + 1] : acc), []);
@@ -1656,30 +1676,36 @@ class KbqDatepickerInput {
1656
1676
  }
1657
1677
  spaceKeyHandler(event) {
1658
1678
  event.preventDefault();
1659
- if (this.selectionStart === this.selectionEnd) {
1660
- const value = this.getNewValue(event.key, this.selectionStart);
1661
- this.setViewValue(value);
1662
- setTimeout(this.onInput);
1663
- }
1664
- else if (this.selectionStart !== this.selectionEnd) {
1665
- this.selectNextDigit(this.selectionStart, true);
1679
+ const position = this.selectionStart;
1680
+ // Right after a separator the digit group is empty, so a space cannot stand in for one:
1681
+ // advance instead of inserting a character the value can never be parsed with.
1682
+ if (this.selectionStart !== this.selectionEnd || this.separatorPositions.includes(position)) {
1683
+ this.selectNextDigit(position, true);
1684
+ return;
1666
1685
  }
1686
+ this.setViewValue(this.getNewValue(event.key, position));
1687
+ setTimeout(this.onInput);
1667
1688
  }
1668
1689
  getNewValue(key, position) {
1669
1690
  return [this.viewValue.slice(0, position), key, this.viewValue.slice(position)].join('');
1670
1691
  }
1671
1692
  setViewValue(value, savePosition = false) {
1693
+ const element = this.elementRef.nativeElement;
1694
+ const selectionStart = this.selectionStart ?? 0;
1695
+ const selectionEnd = this.selectionEnd ?? 0;
1696
+ this.renderer.setProperty(element, 'value', value);
1672
1697
  if (savePosition) {
1673
- const selectionStart = this.selectionStart;
1674
- const selectionEnd = this.selectionEnd;
1675
- this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
1676
- this.selectionStart = selectionStart;
1677
- this.selectionEnd = selectionEnd;
1698
+ this.setSelection(selectionStart, selectionEnd);
1678
1699
  }
1679
1700
  else {
1680
- this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
1701
+ // A paste, a model write or a calendar pick replaces the whole value, and the offset the
1702
+ // previous one was left at has to go with it.
1703
+ kbqRevealSelection(element);
1681
1704
  }
1682
1705
  }
1706
+ setSelection(start, end) {
1707
+ kbqSetSelectionRange(this.elementRef.nativeElement, start, end);
1708
+ }
1683
1709
  replaceSymbols(value) {
1684
1710
  return value
1685
1711
  .split(this.separator)
@@ -1918,8 +1944,7 @@ class KbqDatepickerInput {
1918
1944
  changedTime = this.decrementDate(this.value, modifiedTimePart);
1919
1945
  }
1920
1946
  this.value = changedTime;
1921
- this.selectionStart = selectionStart;
1922
- this.selectionEnd = selectionEnd;
1947
+ this.setSelection(selectionStart, selectionEnd);
1923
1948
  this.cvaOnChange(changedTime);
1924
1949
  this.onChange();
1925
1950
  this.stateChanges.next();
@@ -1961,16 +1986,14 @@ class KbqDatepickerInput {
1961
1986
  selectDigitByCursor(cursorPos) {
1962
1987
  setTimeout(() => {
1963
1988
  const [, selectionStart, selectionEnd] = this.getDateEditMetrics(cursorPos);
1964
- this.selectionStart = selectionStart;
1965
- this.selectionEnd = selectionEnd;
1989
+ this.setSelection(selectionStart, selectionEnd);
1966
1990
  });
1967
1991
  }
1968
1992
  selectNextDigitByCursor(cursorPos) {
1969
1993
  setTimeout(() => {
1970
1994
  const [, , endPositionOfCurrentDigit] = this.getDateEditMetrics(cursorPos);
1971
1995
  const [, selectionStart, selectionEnd] = this.getDateEditMetrics(endPositionOfCurrentDigit + 1);
1972
- this.selectionStart = selectionStart;
1973
- this.selectionEnd = selectionEnd;
1996
+ this.setSelection(selectionStart, selectionEnd);
1974
1997
  });
1975
1998
  }
1976
1999
  selectNextDigit(cursorPos, cycle = false) {
@@ -1979,8 +2002,7 @@ class KbqDatepickerInput {
1979
2002
  const nextSeparatorPos = this.viewValue.indexOf(this.separator, cursorPos);
1980
2003
  const newCursorPos = nextSeparatorPos > 0 ? nextSeparatorPos + 1 : lastValue;
1981
2004
  const [, selectionStart, selectionEnd] = this.getDateEditMetrics(newCursorPos);
1982
- this.selectionStart = selectionStart;
1983
- this.selectionEnd = selectionEnd;
2005
+ this.setSelection(selectionStart, selectionEnd);
1984
2006
  });
1985
2007
  }
1986
2008
  /** Checks whether the input is invalid based on the native validation. */
@@ -2044,10 +2066,12 @@ class KbqDatepickerInput {
2044
2066
  }
2045
2067
  return this.adapter.createDateTime(value.year, value.month - 1, value.date, value.hours, value.minutes, value.seconds, value.milliseconds);
2046
2068
  }
2047
- correctCursorPosition() {
2048
- if (this.selectionStart && this.separatorPositions.includes(this.selectionStart)) {
2049
- this.selectionStart = this.selectionStart - 1;
2050
- }
2069
+ // The position to advance from, with a caret that landed just past a separator pulled back onto
2070
+ // the digit before it. Returned rather than applied to the field: on the paths where the value
2071
+ // cannot be parsed, a moved caret would survive into the next keystroke and change what it does.
2072
+ correctedCursorPosition() {
2073
+ const position = this.selectionStart ?? 0;
2074
+ return this.separatorPositions.includes(position) ? position - 1 : position;
2051
2075
  }
2052
2076
  /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: KbqDatepickerInput, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1.DateAdapter, optional: true }, { token: KBQ_DATE_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
2053
2077
  /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.21", type: KbqDatepickerInput, isStandalone: true, selector: "input[kbqDatepicker], input[kbqCalendar]", inputs: { errorStateMatcher: "errorStateMatcher", placeholder: "placeholder", required: "required", kbqDatepicker: "kbqDatepicker", kbqCalendar: "kbqCalendar", kbqDatepickerFilter: "kbqDatepickerFilter", value: "value", min: "min", max: "max", disabled: "disabled", id: "id", kbqValidationTooltip: "kbqValidationTooltip" }, outputs: { incorrectInput: "incorrectInput", dateChange: "dateChange", dateInput: "dateInput" }, host: { listeners: { "paste": "onPaste($event)", "change": "onChange()", "focus": "focusChanged(true)", "blur": "onBlur()", "keydown": "onKeyDown($event)" }, properties: { "attr.placeholder": "placeholder", "attr.required": "required", "attr.disabled": "disabled || null", "attr.min": "min ? toISO8601(min) : null", "attr.max": "max ? toISO8601(max) : null", "attr.autocomplete": "\"off\"" }, classAttribute: "kbq-input kbq-datepicker" }, providers: [