@brickclay-org/ui 0.1.25 → 0.1.27

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.
@@ -657,6 +657,32 @@ class BkCustomCalendar {
657
657
  return false;
658
658
  return !this.startDate || !this.endDate;
659
659
  }
660
+ /**
661
+ * Popup single-date with manual Apply: do not push to ngModel / (selected) until Apply.
662
+ * Inline calendars have no Apply footer, so they always commit immediately.
663
+ */
664
+ shouldDeferSingleDateCommit() {
665
+ return this.singleDatePicker && !this.autoApply && !this.inline;
666
+ }
667
+ emitSelectionUnlessSingleDateDeferred() {
668
+ if (this.shouldDeferSingleDateCommit())
669
+ return;
670
+ this.emitSelection();
671
+ }
672
+ /** Discard in-popup draft and restore last committed value from {@link selectedValue}. */
673
+ revertSingleDateDraftIfNeeded() {
674
+ if (this.shouldDeferSingleDateCommit()) {
675
+ this.applyValueToState(this.selectedValue ?? null);
676
+ }
677
+ }
678
+ finishPopupDismissal() {
679
+ if (this.inline)
680
+ return;
681
+ this.revertSingleDateDraftIfNeeded();
682
+ this.show = false;
683
+ this.onTouched();
684
+ this.closed.emit();
685
+ }
660
686
  /** User preference before viewport adjustment (legacy `drop` still applies when `popupPosition` is `bottom`). */
661
687
  preferPopupAbove() {
662
688
  return this.popupPosition === 'top' || (this.popupPosition === 'bottom' && this.drop === 'up');
@@ -730,6 +756,29 @@ class BkCustomCalendar {
730
756
  markAsTouched() {
731
757
  this.onTouched();
732
758
  }
759
+ /** Clears embedded time picker ngModels, spinner state, raw minute typing, and forces dropdowns closed. */
760
+ resetEmbeddedTimePickerUiState() {
761
+ this.singleTimeModel = null;
762
+ this.startTimeModel = null;
763
+ this.endTimeModel = null;
764
+ this.selectedHour = 1;
765
+ this.selectedMinute = 0;
766
+ this.selectedSecond = 0;
767
+ this.selectedAMPM = 'AM';
768
+ this.startHour = 1;
769
+ this.startMinute = 0;
770
+ this.startSecond = 0;
771
+ this.startAMPM = 'AM';
772
+ this.endHour = 2;
773
+ this.endMinute = 0;
774
+ this.endSecond = 0;
775
+ this.endAMPM = 'AM';
776
+ this.minuteInputValues = {};
777
+ this.openTimePickerId = null;
778
+ for (const id of ['single-time', 'dual-start', 'dual-end']) {
779
+ this.closePickerCounter[id] = (this.closePickerCounter[id] || 0) + 1;
780
+ }
781
+ }
733
782
  /** Apply CalendarSelection to internal state (startDate, endDate, startTime, endTime, selectedDates, calendar view). */
734
783
  applyValueToState(value) {
735
784
  if (!value) {
@@ -738,9 +787,8 @@ class BkCustomCalendar {
738
787
  this.startTime = null;
739
788
  this.endTime = null;
740
789
  this.selectedDates = [];
741
- this.singleTimeModel = null;
742
- this.startTimeModel = null;
743
- this.endTimeModel = null;
790
+ this.activeRange = null;
791
+ this.resetEmbeddedTimePickerUiState();
744
792
  this.month = this.today.getMonth();
745
793
  this.year = this.today.getFullYear();
746
794
  this.generateCalendar();
@@ -817,8 +865,13 @@ class BkCustomCalendar {
817
865
  this.selectedAMPM = this.startAMPM;
818
866
  }
819
867
  else if (this.startDate) {
868
+ // Date-only committed value: keep startTime null; sync spinners for UX only (not emitted until user sets time).
820
869
  this.initializeTimeFromDate(this.startDate, true);
821
- this.startTime = this.formatTimeToAmPm(this.startHour, this.startMinute, this.startAMPM);
870
+ if (!this.dualCalendar) {
871
+ this.selectedHour = this.startHour;
872
+ this.selectedMinute = this.startMinute;
873
+ this.selectedAMPM = this.startAMPM;
874
+ }
822
875
  }
823
876
  if (this.endTime) {
824
877
  const endParsed = this.parsePickerTimeString(this.endTime);
@@ -836,7 +889,6 @@ class BkCustomCalendar {
836
889
  }
837
890
  else if (this.endDate) {
838
891
  this.initializeTimeFromDate(this.endDate, false);
839
- this.endTime = this.formatTimeToAmPm(this.endHour, this.endMinute, this.endAMPM);
840
892
  }
841
893
  if (this.startDate && this.endDate) {
842
894
  this.checkAndSetActiveRange();
@@ -880,9 +932,14 @@ class BkCustomCalendar {
880
932
  this.initializeDual();
881
933
  else
882
934
  this.generateCalendar();
883
- // Initialize time from existing dates if available
935
+ // Initialize spinner state from existing dates (does not set startTime/endTime — those stay null until user picks time).
884
936
  if (this.startDate) {
885
937
  this.initializeTimeFromDate(this.startDate, true);
938
+ if (!this.dualCalendar) {
939
+ this.selectedHour = this.startHour;
940
+ this.selectedMinute = this.startMinute;
941
+ this.selectedAMPM = this.startAMPM;
942
+ }
886
943
  }
887
944
  if (this.endDate) {
888
945
  this.initializeTimeFromDate(this.endDate, false);
@@ -1025,7 +1082,7 @@ class BkCustomCalendar {
1025
1082
  this.opened.emit();
1026
1083
  }
1027
1084
  else {
1028
- this.closed.emit();
1085
+ this.finishPopupDismissal();
1029
1086
  }
1030
1087
  }
1031
1088
  /** Update popup position when appendToBody is true (fixed positioning relative to viewport). */
@@ -1247,13 +1304,10 @@ class BkCustomCalendar {
1247
1304
  }
1248
1305
  }
1249
1306
  close() {
1250
- // Don't close if inline mode is enabled
1251
1307
  if (this.inline) {
1252
1308
  return;
1253
1309
  }
1254
- this.show = false;
1255
- this.onTouched();
1256
- this.closed.emit();
1310
+ this.finishPopupDismissal();
1257
1311
  }
1258
1312
  onDateHover(day, fromRight = false) {
1259
1313
  if (!day || this.singleDatePicker || this.multiDateSelection) {
@@ -1339,8 +1393,7 @@ class BkCustomCalendar {
1339
1393
  this.close();
1340
1394
  }
1341
1395
  else {
1342
- // Always emit selection event even if autoApply is false (especially for inline calendars)
1343
- this.emitSelection();
1396
+ this.emitSelectionUnlessSingleDateDeferred();
1344
1397
  }
1345
1398
  return;
1346
1399
  }
@@ -1477,23 +1530,21 @@ class BkCustomCalendar {
1477
1530
  return;
1478
1531
  // Format minute inputs to 2 digits before applying
1479
1532
  this.formatAllMinuteInputs();
1480
- // Apply time to dates
1533
+ // Apply time only when the user has an explicit time string (avoids inventing "12:00 AM" from picker defaults).
1481
1534
  if (this.enableTimepicker) {
1482
1535
  if (this.dualCalendar) {
1483
- // Dual calendar with separate start/end times (always 12-hour format)
1484
- if (this.startDate) {
1536
+ if (this.startDate && this.startTime != null) {
1485
1537
  this.applyTimeToDate(this.startDate, true);
1486
1538
  }
1487
- if (this.endDate) {
1539
+ if (this.endDate && this.endTime != null) {
1488
1540
  this.applyTimeToDate(this.endDate, false);
1489
1541
  }
1490
1542
  }
1491
1543
  else {
1492
- // Single calendar with time (always 12-hour format)
1493
- if (this.startDate) {
1544
+ if (this.startDate && this.startTime != null) {
1494
1545
  this.applyTimeToDate(this.startDate, true);
1495
1546
  }
1496
- if (this.endDate && !this.singleDatePicker) {
1547
+ if (this.endDate && !this.singleDatePicker && this.startTime != null) {
1497
1548
  this.applyTimeToDate(this.endDate, true);
1498
1549
  }
1499
1550
  }
@@ -1508,10 +1559,14 @@ class BkCustomCalendar {
1508
1559
  cancel() {
1509
1560
  if (this.disabled)
1510
1561
  return;
1511
- this.startDate = null;
1512
- this.endDate = null;
1513
- this.selectedDates = [];
1514
- this.resetCalendarFocusAfterClear();
1562
+ // if (this.shouldDeferSingleDateCommit()) {
1563
+ // this.finishPopupDismissal();
1564
+ // return;
1565
+ // }
1566
+ // this.startDate = null;
1567
+ // this.endDate = null;
1568
+ // this.selectedDates = [];
1569
+ // this.resetCalendarFocusAfterClear();
1515
1570
  this.close();
1516
1571
  }
1517
1572
  clear() {
@@ -1521,22 +1576,9 @@ class BkCustomCalendar {
1521
1576
  this.endDate = null;
1522
1577
  this.startTime = null;
1523
1578
  this.endTime = null;
1524
- // Time picker for single calendar (12-hour format: 1-12)
1525
- this.selectedHour = 1;
1526
- this.selectedMinute = 0;
1527
- this.selectedSecond = 0;
1528
- this.selectedAMPM = 'AM';
1529
- // NEW: Separate time pickers for dual calendar (12-hour format: 1-12)
1530
- this.startHour = 1;
1531
- this.startMinute = 0;
1532
- this.startSecond = 0;
1533
- this.startAMPM = 'AM';
1534
- this.endHour = 2;
1535
- this.endMinute = 0;
1536
- this.endSecond = 0;
1537
- this.endAMPM = 'AM';
1538
1579
  this.selectedDates = [];
1539
- this.activeRange = null; // Clear active range
1580
+ this.activeRange = null;
1581
+ this.resetEmbeddedTimePickerUiState();
1540
1582
  this.resetCalendarFocusAfterClear();
1541
1583
  this.emitSelection();
1542
1584
  }
@@ -1616,8 +1658,13 @@ class BkCustomCalendar {
1616
1658
  else if (this.startDate) {
1617
1659
  this.keyboardFocusDate = new Date(this.startDate.getFullYear(), this.startDate.getMonth(), this.startDate.getDate());
1618
1660
  }
1619
- this.emitSelection();
1620
- if (this.autoApply || this.closeOnAutoApply) {
1661
+ this.emitSelectionUnlessSingleDateDeferred();
1662
+ if (this.shouldDeferSingleDateCommit()) {
1663
+ if (this.autoApply) {
1664
+ this.apply();
1665
+ }
1666
+ }
1667
+ else if (this.autoApply || this.closeOnAutoApply) {
1621
1668
  this.close();
1622
1669
  }
1623
1670
  }
@@ -1981,7 +2028,7 @@ class BkCustomCalendar {
1981
2028
  if (this.startDate) {
1982
2029
  this.startDate.setHours(0, 0, this.selectedSecond);
1983
2030
  }
1984
- this.emitSelection();
2031
+ this.emitSelectionUnlessSingleDateDeferred();
1985
2032
  return;
1986
2033
  }
1987
2034
  const { hour12, minute, ampm } = this.parsePickerTimeString(time);
@@ -1998,7 +2045,7 @@ class BkCustomCalendar {
1998
2045
  h24 = 0;
1999
2046
  this.startDate.setHours(h24, minute, this.selectedSecond);
2000
2047
  }
2001
- this.emitSelection();
2048
+ this.emitSelectionUnlessSingleDateDeferred();
2002
2049
  }
2003
2050
  // NEW: Handle BkTimePicker change for dual calendar
2004
2051
  onDualTimePickerChange(time, isStart = true) {
@@ -2079,7 +2126,7 @@ class BkCustomCalendar {
2079
2126
  this.selectedMinute = m;
2080
2127
  if (this.startDate) {
2081
2128
  this.startDate.setHours(h, m, this.selectedSecond);
2082
- this.emitSelection();
2129
+ this.emitSelectionUnlessSingleDateDeferred();
2083
2130
  }
2084
2131
  }
2085
2132
  // Custom time picker controls
@@ -2240,7 +2287,7 @@ class BkCustomCalendar {
2240
2287
  if (this.selectedAMPM === 'AM' && h === 12)
2241
2288
  h = 0;
2242
2289
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2243
- this.emitSelection();
2290
+ this.emitSelectionUnlessSingleDateDeferred();
2244
2291
  }
2245
2292
  }
2246
2293
  decrementSingleHour() {
@@ -2256,7 +2303,7 @@ class BkCustomCalendar {
2256
2303
  if (this.selectedAMPM === 'AM' && h === 12)
2257
2304
  h = 0;
2258
2305
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2259
- this.emitSelection();
2306
+ this.emitSelectionUnlessSingleDateDeferred();
2260
2307
  }
2261
2308
  }
2262
2309
  incrementSingleMinute() {
@@ -2268,7 +2315,7 @@ class BkCustomCalendar {
2268
2315
  if (this.selectedAMPM === 'AM' && h === 12)
2269
2316
  h = 0;
2270
2317
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2271
- this.emitSelection();
2318
+ this.emitSelectionUnlessSingleDateDeferred();
2272
2319
  }
2273
2320
  }
2274
2321
  decrementSingleMinute() {
@@ -2280,7 +2327,7 @@ class BkCustomCalendar {
2280
2327
  if (this.selectedAMPM === 'AM' && h === 12)
2281
2328
  h = 0;
2282
2329
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2283
- this.emitSelection();
2330
+ this.emitSelectionUnlessSingleDateDeferred();
2284
2331
  }
2285
2332
  }
2286
2333
  toggleSingleAMPM() {
@@ -2292,7 +2339,7 @@ class BkCustomCalendar {
2292
2339
  if (this.selectedAMPM === 'AM' && h === 12)
2293
2340
  h = 0;
2294
2341
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2295
- this.emitSelection();
2342
+ this.emitSelectionUnlessSingleDateDeferred();
2296
2343
  }
2297
2344
  }
2298
2345
  getMonthName(month) {
@@ -2321,7 +2368,7 @@ class BkCustomCalendar {
2321
2368
  if (this.selectedAMPM === 'AM' && h === 12)
2322
2369
  h = 0;
2323
2370
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2324
- this.emitSelection();
2371
+ this.emitSelectionUnlessSingleDateDeferred();
2325
2372
  }
2326
2373
  }
2327
2374
  else if (isStart) {
@@ -2376,7 +2423,7 @@ class BkCustomCalendar {
2376
2423
  if (this.selectedAMPM === 'AM' && h === 12)
2377
2424
  h = 0;
2378
2425
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2379
- this.emitSelection();
2426
+ this.emitSelectionUnlessSingleDateDeferred();
2380
2427
  }
2381
2428
  }
2382
2429
  else if (isStart) {
@@ -2470,7 +2517,7 @@ class BkCustomCalendar {
2470
2517
  if (this.selectedAMPM === 'AM' && h === 12)
2471
2518
  h = 0;
2472
2519
  this.startDate.setHours(h, this.selectedMinute, this.selectedSecond);
2473
- this.emitSelection();
2520
+ this.emitSelectionUnlessSingleDateDeferred();
2474
2521
  }
2475
2522
  }
2476
2523
  else if (isStart) {