@dcsl/flex-ui 0.0.14 → 0.0.17

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.
@@ -1,10 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Renderer2, HostListener, ViewChild, Component, input, EventEmitter, effect, Output, ContentChild, Input, ContentChildren, output, model, signal } from '@angular/core';
2
+ import { inject, Renderer2, HostListener, ViewChild, Component, input, EventEmitter, effect, Output, ContentChild, Input, ContentChildren, output, model, signal, Injectable } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i2 from '@angular/forms';
6
6
  import { FormsModule } from '@angular/forms';
7
7
  import dayjs from 'dayjs';
8
+ import calendarSystems from '@calidy/dayjs-calendarsystems';
9
+ import PersianCalendarSystem from '@calidy/dayjs-calendarsystems/calendarSystems/PersianCalendarSystem';
10
+ import HijriCalendarSystem from '@calidy/dayjs-calendarsystems/calendarSystems/HijriCalendarSystem';
11
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
8
12
 
9
13
  class FlexPanelComponent {
10
14
  resizeObserver;
@@ -437,25 +441,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImpor
437
441
  //
438
442
  // ✅ Umm al-Qura Hijri calendar with Latin digits
439
443
  //
440
- const hijriFmt = new Intl.DateTimeFormat('en-u-ca-islamic-umalqura-nu-latn', {
444
+ const hijriFmt$1 = new Intl.DateTimeFormat('en-u-ca-islamic-umalqura-nu-latn', {
441
445
  day: 'numeric',
442
446
  month: 'numeric',
443
447
  year: 'numeric'
444
448
  });
445
- const hijriMonthNamesEn = [
449
+ const hijriMonthNamesEn$1 = [
446
450
  'Muharram', 'Safar', 'Rabi I', 'Rabi II',
447
451
  'Jumada I', 'Jumada II', 'Rajab', 'Sha’ban',
448
452
  'Ramadan', 'Shawwal', 'Dhu al-Qi’dah', 'Dhu al-Hijjah'
449
453
  ];
450
- const hijriMonthNamesAr = [
454
+ const hijriMonthNamesAr$1 = [
451
455
  'محرم', 'صفر', 'ربيع الأول', 'ربيع الآخر',
452
456
  'جمادى الأولى', 'جمادى الآخرة', 'رجب', 'شعبان',
453
457
  'رمضان', 'شوال', 'ذو القعدة', 'ذو الحجة'
454
458
  ];
455
- const weekDaysEn = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
456
- const weekDaysAr = ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'];
457
- function hijriFromGregorian(g) {
458
- const parts = hijriFmt.formatToParts(g);
459
+ const weekDaysEn$1 = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
460
+ const weekDaysAr$1 = ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'];
461
+ function hijriFromGregorian$1(g) {
462
+ const parts = hijriFmt$1.formatToParts(g);
459
463
  //console.log(parts);
460
464
  const map = {};
461
465
  for (const p of parts)
@@ -463,26 +467,26 @@ function hijriFromGregorian(g) {
463
467
  const n = (k) => parseInt(map[k] ?? '0', 10);
464
468
  return { hy: n('year'), hm: n('month'), hd: n('day') };
465
469
  }
466
- function compareHijri(a, b) {
470
+ function compareHijri$1(a, b) {
467
471
  if (a.hy !== b.hy)
468
472
  return a.hy - b.hy;
469
473
  if (a.hm !== b.hm)
470
474
  return a.hm - b.hm;
471
475
  return a.hd - b.hd;
472
476
  }
473
- function findGregorianForHijri(target) {
477
+ function findGregorianForHijri$1(target) {
474
478
  let lo = new Date(1937, 0, 1).getTime();
475
479
  let hi = new Date(2076, 11, 31).getTime();
476
480
  const dayMs = 86400000;
477
481
  while (lo <= hi) {
478
482
  const mid = Math.floor((lo + hi) / 2);
479
- const h = hijriFromGregorian(new Date(mid));
480
- compareHijri(h, target) < 0 ? (lo = mid + dayMs) : (hi = mid - dayMs);
483
+ const h = hijriFromGregorian$1(new Date(mid));
484
+ compareHijri$1(h, target) < 0 ? (lo = mid + dayMs) : (hi = mid - dayMs);
481
485
  }
482
486
  let d = new Date(lo);
483
487
  while (true) {
484
- const h = hijriFromGregorian(d);
485
- const cmp = compareHijri(h, target);
488
+ const h = hijriFromGregorian$1(d);
489
+ const cmp = compareHijri$1(h, target);
486
490
  if (cmp === 0)
487
491
  return d;
488
492
  if (cmp > 0)
@@ -498,35 +502,35 @@ class HijriCalendarComponent {
498
502
  minDate = null;
499
503
  maxDate = null;
500
504
  /** Month + weekday language: 'en' or 'ar' */
501
- monthLang = 'en';
505
+ monthLang = 'ar';
502
506
  hy = signal(0, ...(ngDevMode ? [{ debugName: "hy" }] : []));
503
507
  hm = signal(0, ...(ngDevMode ? [{ debugName: "hm" }] : []));
504
508
  selectedMonth = 1;
505
509
  selectedYear = 1447;
506
510
  yearRange = [];
507
511
  get hijriMonthsEn() {
508
- return hijriMonthNamesEn;
512
+ return hijriMonthNamesEn$1;
509
513
  }
510
514
  get hijriMonthsAr() {
511
- return hijriMonthNamesAr;
515
+ return hijriMonthNamesAr$1;
512
516
  }
513
517
  get hijriMonths() {
514
- return this.monthLang === 'ar' ? hijriMonthNamesAr : hijriMonthNamesEn;
518
+ return this.monthLang === 'ar' ? hijriMonthNamesAr$1 : hijriMonthNamesEn$1;
515
519
  }
516
520
  get weekDays() {
517
- return this.monthLang === 'ar' ? weekDaysAr : weekDaysEn;
521
+ return this.monthLang === 'ar' ? weekDaysAr$1 : weekDaysEn$1;
518
522
  }
519
523
  get weekDaysEn() {
520
- return weekDaysEn;
524
+ return weekDaysEn$1;
521
525
  }
522
526
  get weekDaysAr() {
523
- return weekDaysAr;
527
+ return weekDaysAr$1;
524
528
  }
525
529
  get currentMonthName() {
526
530
  return this.hijriMonths[this.selectedMonth - 1];
527
531
  }
528
532
  constructor() {
529
- const h = hijriFromGregorian(new Date());
533
+ const h = hijriFromGregorian$1(new Date());
530
534
  this.hy.set(h.hy);
531
535
  this.hm.set(h.hm);
532
536
  this.selectedYear = h.hy;
@@ -537,7 +541,7 @@ class HijriCalendarComponent {
537
541
  }
538
542
  ngOnChanges() {
539
543
  if (this.value) {
540
- const h = hijriFromGregorian(this.value);
544
+ const h = hijriFromGregorian$1(this.value);
541
545
  this.hy.set(h.hy);
542
546
  this.hm.set(h.hm);
543
547
  this.selectedYear = h.hy;
@@ -546,8 +550,8 @@ class HijriCalendarComponent {
546
550
  this.buildYearRange();
547
551
  }
548
552
  buildYearRange() {
549
- const minH = this.minDate ? hijriFromGregorian(this.minDate) : { hy: 1356, hm: 1, hd: 1 };
550
- const maxH = this.maxDate ? hijriFromGregorian(this.maxDate) : { hy: 1499, hm: 12, hd: 30 };
553
+ const minH = this.minDate ? hijriFromGregorian$1(this.minDate) : { hy: 1356, hm: 1, hd: 1 };
554
+ const maxH = this.maxDate ? hijriFromGregorian$1(this.maxDate) : { hy: 1499, hm: 12, hd: 30 };
551
555
  this.yearRange = [];
552
556
  for (let y = minH.hy; y <= maxH.hy; y++) {
553
557
  this.yearRange.push(y);
@@ -558,18 +562,18 @@ class HijriCalendarComponent {
558
562
  this.hm.set(this.selectedMonth);
559
563
  }
560
564
  firstOfMonth() {
561
- return findGregorianForHijri({ hy: this.hy(), hm: this.hm(), hd: 1 });
565
+ return findGregorianForHijri$1({ hy: this.hy(), hm: this.hm(), hd: 1 });
562
566
  }
563
567
  cells() {
564
568
  const firstG = this.firstOfMonth();
565
- const firstH = hijriFromGregorian(firstG);
569
+ const firstH = hijriFromGregorian$1(firstG);
566
570
  const start = new Date(firstG);
567
571
  start.setDate(firstG.getDate() - firstG.getDay());
568
572
  const result = [];
569
573
  for (let i = 0; i < 42; i++) {
570
574
  const g = new Date(start);
571
575
  g.setDate(start.getDate() + i);
572
- const h = hijriFromGregorian(g);
576
+ const h = hijriFromGregorian$1(g);
573
577
  let disabled = false;
574
578
  if (this.minDate && g < this.stripTime(this.minDate))
575
579
  disabled = true;
@@ -613,13 +617,13 @@ class HijriCalendarComponent {
613
617
  canPrev() {
614
618
  if (!this.minDate)
615
619
  return true;
616
- const minH = hijriFromGregorian(this.stripTime(this.minDate));
620
+ const minH = hijriFromGregorian$1(this.stripTime(this.minDate));
617
621
  return this.hy() > minH.hy || (this.hy() === minH.hy && this.hm() > minH.hm);
618
622
  }
619
623
  canNext() {
620
624
  if (!this.maxDate)
621
625
  return true;
622
- const maxH = hijriFromGregorian(this.stripTime(this.maxDate));
626
+ const maxH = hijriFromGregorian$1(this.stripTime(this.maxDate));
623
627
  return this.hy() < maxH.hy || (this.hy() === maxH.hy && this.hm() < maxH.hm);
624
628
  }
625
629
  isToday(g) {
@@ -637,7 +641,7 @@ class HijriCalendarComponent {
637
641
  select(c) {
638
642
  if (c.disabled)
639
643
  return;
640
- console.log(c);
644
+ //console.log(c);
641
645
  this.value = c.g;
642
646
  this.valueChange.emit(this.value);
643
647
  }
@@ -645,11 +649,11 @@ class HijriCalendarComponent {
645
649
  return new Date(d.getFullYear(), d.getMonth(), d.getDate());
646
650
  }
647
651
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: HijriCalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
648
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: HijriCalendarComponent, isStandalone: true, selector: "app-hijri-calendar", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate", monthLang: "monthLang" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"card border-0 p-0\">\r\n <div class=\"card-header p-1\">\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n <button class=\"btn btn-sm\" (click)=\"prevMonth()\" [disabled]=\"!canPrev()\">&laquo;</button>\r\n <div class=\"d-flex justify-content-center gap-1\">\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedMonth\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (m of hijriMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ hijriMonthsAr[$index] }} ({{ hijriMonthsEn[$index] }})</option>\r\n }\r\n </select>\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedYear\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (y of yearRange; track y) {\r\n <option [value]=\"y\">{{ y }}</option>\r\n }\r\n </select>\r\n </div>\r\n <button class=\"btn btn-sm\" (click)=\"nextMonth()\" [disabled]=\"!canNext()\">&raquo;</button>\r\n </div>\r\n </div>\r\n <div class=\"card-body p-1\">\r\n\r\n\r\n <!-- month/year pickers -->\r\n\r\n\r\n <!-- weekday labels -->\r\n <div class=\"grid\">\r\n @for (w of weekDays; track $index) {\r\n <div class=\"col fw-bold\">{{ w }}</div>\r\n }\r\n\r\n </div>\r\n\r\n <!-- calendar cells -->\r\n <div class=\"grid\">\r\n <button *ngFor=\"let c of cells()\" class=\"cell btn rounded-0\" [class.muted]=\"!c.inCurrentMonth\"\r\n [class.today]=\"isToday(c.g)\" [class.selected]=\"isSelected(c.g)\" [disabled]=\"c.disabled\"\r\n (click)=\"select(c)\">\r\n {{ c.h.hd }}\r\n </button>\r\n </div>\r\n </div>\r\n</div>", styles: [".grid{display:grid;grid-template-columns:repeat(7,1fr)}.cell{border:1px solid #ddd}.cell.muted{opacity:.5}.cell.selected{background-color:#0d6efd;color:#fff}.cell:disabled{background-color:#f8f9fa;color:#aaa;cursor:not-allowed}.col{text-align:center}.btn{font-size:90%;padding:.25rem}.btn:hover{background-color:azure}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
652
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: HijriCalendarComponent, isStandalone: true, selector: "app-hijri-calendar", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate", monthLang: "monthLang" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"card border-0 p-0\">\r\n <div class=\"card-header p-1\">\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n <button class=\"btn btn-sm\" (click)=\"prevMonth()\" [disabled]=\"!canPrev()\">&laquo;</button>\r\n <div class=\"d-flex justify-content-center gap-1\">\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedMonth\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (m of hijriMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ hijriMonthsAr[$index] }} ({{ hijriMonthsEn[$index] }})</option>\r\n }\r\n </select>\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedYear\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (y of yearRange; track y) {\r\n <option [value]=\"y\">{{ y }}</option>\r\n }\r\n </select>\r\n </div>\r\n <button class=\"btn btn-sm\" (click)=\"nextMonth()\" [disabled]=\"!canNext()\">&raquo;</button>\r\n </div>\r\n </div>\r\n <div class=\"card-body p-1\">\r\n\r\n\r\n <!-- month/year pickers -->\r\n\r\n\r\n <!-- weekday labels -->\r\n <div class=\"grid\">\r\n @for (w of weekDays; track $index) {\r\n <div class=\"col fw-bold border pb-1\">{{ w }}</div>\r\n }\r\n\r\n </div>\r\n\r\n <!-- calendar cells -->\r\n <div class=\"grid\">\r\n <button *ngFor=\"let c of cells()\" class=\"cell btn rounded-0\" [class.muted]=\"!c.inCurrentMonth\"\r\n [class.today]=\"isToday(c.g)\" [class.selected]=\"isSelected(c.g)\" [disabled]=\"c.disabled\"\r\n (click)=\"select(c)\">\r\n {{ c.h.hd }}\r\n </button>\r\n </div>\r\n </div>\r\n</div>", styles: [".grid{display:grid;grid-template-columns:repeat(7,1fr)}.cell{border:1px solid #ddd}.cell.muted{opacity:.5}.cell.selected{background-color:#0d6efd;color:#fff}.cell:disabled{background-color:#f8f9fa;color:#aaa;cursor:not-allowed}.col{text-align:center}.btn{font-size:90%;padding:.25rem .1rem}.btn:hover{background-color:azure}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
649
653
  }
650
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: HijriCalendarComponent, decorators: [{
651
655
  type: Component,
652
- args: [{ selector: 'app-hijri-calendar', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"card border-0 p-0\">\r\n <div class=\"card-header p-1\">\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n <button class=\"btn btn-sm\" (click)=\"prevMonth()\" [disabled]=\"!canPrev()\">&laquo;</button>\r\n <div class=\"d-flex justify-content-center gap-1\">\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedMonth\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (m of hijriMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ hijriMonthsAr[$index] }} ({{ hijriMonthsEn[$index] }})</option>\r\n }\r\n </select>\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedYear\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (y of yearRange; track y) {\r\n <option [value]=\"y\">{{ y }}</option>\r\n }\r\n </select>\r\n </div>\r\n <button class=\"btn btn-sm\" (click)=\"nextMonth()\" [disabled]=\"!canNext()\">&raquo;</button>\r\n </div>\r\n </div>\r\n <div class=\"card-body p-1\">\r\n\r\n\r\n <!-- month/year pickers -->\r\n\r\n\r\n <!-- weekday labels -->\r\n <div class=\"grid\">\r\n @for (w of weekDays; track $index) {\r\n <div class=\"col fw-bold\">{{ w }}</div>\r\n }\r\n\r\n </div>\r\n\r\n <!-- calendar cells -->\r\n <div class=\"grid\">\r\n <button *ngFor=\"let c of cells()\" class=\"cell btn rounded-0\" [class.muted]=\"!c.inCurrentMonth\"\r\n [class.today]=\"isToday(c.g)\" [class.selected]=\"isSelected(c.g)\" [disabled]=\"c.disabled\"\r\n (click)=\"select(c)\">\r\n {{ c.h.hd }}\r\n </button>\r\n </div>\r\n </div>\r\n</div>", styles: [".grid{display:grid;grid-template-columns:repeat(7,1fr)}.cell{border:1px solid #ddd}.cell.muted{opacity:.5}.cell.selected{background-color:#0d6efd;color:#fff}.cell:disabled{background-color:#f8f9fa;color:#aaa;cursor:not-allowed}.col{text-align:center}.btn{font-size:90%;padding:.25rem}.btn:hover{background-color:azure}\n"] }]
656
+ args: [{ selector: 'app-hijri-calendar', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"card border-0 p-0\">\r\n <div class=\"card-header p-1\">\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n <button class=\"btn btn-sm\" (click)=\"prevMonth()\" [disabled]=\"!canPrev()\">&laquo;</button>\r\n <div class=\"d-flex justify-content-center gap-1\">\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedMonth\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (m of hijriMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ hijriMonthsAr[$index] }} ({{ hijriMonthsEn[$index] }})</option>\r\n }\r\n </select>\r\n <select class=\"form-select form-select-sm w-auto\" [(ngModel)]=\"selectedYear\"\r\n (change)=\"onMonthYearChange()\">\r\n @for (y of yearRange; track y) {\r\n <option [value]=\"y\">{{ y }}</option>\r\n }\r\n </select>\r\n </div>\r\n <button class=\"btn btn-sm\" (click)=\"nextMonth()\" [disabled]=\"!canNext()\">&raquo;</button>\r\n </div>\r\n </div>\r\n <div class=\"card-body p-1\">\r\n\r\n\r\n <!-- month/year pickers -->\r\n\r\n\r\n <!-- weekday labels -->\r\n <div class=\"grid\">\r\n @for (w of weekDays; track $index) {\r\n <div class=\"col fw-bold border pb-1\">{{ w }}</div>\r\n }\r\n\r\n </div>\r\n\r\n <!-- calendar cells -->\r\n <div class=\"grid\">\r\n <button *ngFor=\"let c of cells()\" class=\"cell btn rounded-0\" [class.muted]=\"!c.inCurrentMonth\"\r\n [class.today]=\"isToday(c.g)\" [class.selected]=\"isSelected(c.g)\" [disabled]=\"c.disabled\"\r\n (click)=\"select(c)\">\r\n {{ c.h.hd }}\r\n </button>\r\n </div>\r\n </div>\r\n</div>", styles: [".grid{display:grid;grid-template-columns:repeat(7,1fr)}.cell{border:1px solid #ddd}.cell.muted{opacity:.5}.cell.selected{background-color:#0d6efd;color:#fff}.cell:disabled{background-color:#f8f9fa;color:#aaa;cursor:not-allowed}.col{text-align:center}.btn{font-size:90%;padding:.25rem .1rem}.btn:hover{background-color:azure}\n"] }]
653
657
  }], ctorParameters: () => [], propDecorators: { value: [{
654
658
  type: Input
655
659
  }], valueChange: [{
@@ -662,24 +666,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImpor
662
666
  type: Input
663
667
  }] } });
664
668
 
669
+ // Extend Day.js with calendar systems support
670
+ dayjs.extend(calendarSystems);
671
+ dayjs.extend(customParseFormat);
672
+ // Register the Persian calendar
673
+ dayjs.registerCalendarSystem('persian', new PersianCalendarSystem());
674
+ dayjs.registerCalendarSystem('islamic', new HijriCalendarSystem());
675
+ class DateServiceService {
676
+ log(date) {
677
+ //var dj = dayjs(date);
678
+ // // Convert current date to Persian calendar
679
+ // const persianDate = dj.toCalendarSystem('persian');
680
+ // console.log(persianDate.format('YYYY-MM-DD')); // Example: "1403-08-15"
681
+ // // Create a date from Persian calendar
682
+ // const nowruz = dayjs.fromCalendarSystem('persian', 1403, 1, 1);
683
+ // console.log(nowruz.format('YYYY-MM-DD')); // "2024-03-20"
684
+ // const hijriDate = dj.toCalendarSystem('islamic');
685
+ // console.log(hijriDate.format('YYYY-MM-DD')); // e.g., "1446-03-15"
686
+ //Step 1: User will input gregorian or islamic(hijri) date. for this we will use customParseFormat
687
+ //in the control we will specify the format using binding.
688
+ const d = dayjs("18-05-1447", "DD-MM-YYYY");
689
+ //we are writing this to parse the date , month, year only.
690
+ //then based on the calender type we will decide.
691
+ //but by default system will take gregorian
692
+ const customDate = dayjs.fromCalendarSystem('islamic', d.year(), d.month() + 1, d.date());
693
+ //here we are assuming the control want to handle islamic calender date, we will take it dynmically.
694
+ console.log("Prased Date in Gregorian String as per format ['DD-MM-YYYY']:", customDate.format('DD-MM-YYYY'));
695
+ //here it will show gregorian calender, because even if we parse using islamic calender, it will convert the date
696
+ //and keep it to gregorian
697
+ //to show islamic date, we have to tell the dayjs to use islamic calender
698
+ console.log("Hijri Date String: ", customDate.toCalendarSystem('islamic').format('YYYY-MM-DD'));
699
+ // console.log(d.format("DD/MM/YYYY"));
700
+ // console.log(d.date());
701
+ // console.log(d.month() + 1);
702
+ // console.log(d.year());
703
+ // console.log(d.toDate());
704
+ // const d2 = dayjs("09-11-2025", "DD-MM-YYYY");
705
+ // console.log(d2.format("DD/MM/YYYY"));
706
+ // console.log(d2.date());
707
+ // console.log(d2.month() + 1);
708
+ // console.log(d2.year());
709
+ // console.log(d2.toDate());
710
+ // // Create a Day.js instance from a specific calendar system
711
+ // const hijriCustomDate = dayjs.fromCalendarSystem('islamic', 1447, 5, 18);
712
+ // console.log(hijriCustomDate.toString());
713
+ // // The result is a standard Day.js instance in that calendar system
714
+ // console.log(hijriCustomDate.toCalendarSystem('islamic').format('YYYY-MM-DD')); // In Persian: "1403-01-01"
715
+ // // Convert to Gregorian to see the equivalent date
716
+ // const gregorian = hijriCustomDate.toCalendarSystem('gregory');
717
+ // console.log(gregorian.format('YYYY-MM-DD')); // "2024-03-20"
718
+ }
719
+ format(date, calendar, format) {
720
+ const d = dayjs(date);
721
+ return d.toCalendarSystem(calendar).format(format);
722
+ }
723
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: DateServiceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
724
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: DateServiceService, providedIn: 'root' });
725
+ }
726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: DateServiceService, decorators: [{
727
+ type: Injectable,
728
+ args: [{
729
+ providedIn: 'root'
730
+ }]
731
+ }] });
732
+
665
733
  class HijriDatepickerComponent {
734
+ dateService;
666
735
  value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : []));
667
- valueChange = output();
668
736
  selectedDate;
669
737
  showCalendar = false;
738
+ calender = input("gregory", ...(ngDevMode ? [{ debugName: "calender" }] : []));
670
739
  enabled = input(true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
671
740
  cssClass = input(...(ngDevMode ? [undefined, { debugName: "cssClass" }] : []));
672
- format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : []));
741
+ format = input("DD/MM/YYYY", ...(ngDevMode ? [{ debugName: "format" }] : []));
673
742
  inputDate = "";
674
743
  isMobile = false;
675
- constructor() {
744
+ constructor(dateService) {
745
+ this.dateService = dateService;
676
746
  effect(() => {
677
747
  const iso = this.value();
678
748
  if (iso) {
679
749
  const date = dayjs(iso);
680
750
  if (date.isValid()) {
681
751
  this.selectedDate = date.toDate();
682
- this.inputDate = this.formatDateForInput(this.selectedDate);
752
+ this.inputDate = this.formatDate(this.selectedDate, this.calender(), this.format());
683
753
  }
684
754
  return;
685
755
  }
@@ -691,7 +761,8 @@ class HijriDatepickerComponent {
691
761
  this.showCalendar = !this.showCalendar;
692
762
  }
693
763
  onDateSelectedFromCalender(e) {
694
- console.log("onDateSelectedFromCalender: ", e);
764
+ // console.log("onDateSelectedFromCalender: ", e);
765
+ this.dateService.log(e);
695
766
  //var time = this.selectedDate ?? new Date();
696
767
  // e.setHours(time.getHours());
697
768
  // e.setMinutes(time.getMinutes());
@@ -714,23 +785,18 @@ class HijriDatepickerComponent {
714
785
  /** Reusable update method */
715
786
  updateDate(d) {
716
787
  this.selectedDate = d;
717
- this.inputDate = this.formatDateForInput(d);
788
+ //this.inputDate = this.formatDateForInput(d);
718
789
  this.value.set(d);
719
- this.valueChange.emit(d);
790
+ //this.valueChange.emit(d);
720
791
  }
721
- formatHijri(date, format, locale = 'en') {
722
- // Tell dayjs to use the "islamic" calendar system
723
- // return dayjs(date)
724
- // .toCalendarSystem('islamic') // or 'islamic-umalqura'
725
- // .locale(locale)
726
- // .format(format);
727
- return "";
728
- }
729
- formatDateForInput(date) {
730
- const fmt = this.format()?.toUpperCase() ?? "DD/MM/YYYY";
731
- //return dayjs(date).format(fmt);
732
- return this.formatHijri(date, fmt);
792
+ formatDate(date, calender, format) {
793
+ return this.dateService.format(date, calender, format);
733
794
  }
795
+ // private formatDateForInput(date: Date): string {
796
+ // const fmt = this.format()?.toUpperCase() ?? "DD/MM/YYYY";
797
+ // //return dayjs(date).format(fmt);
798
+ // return this.formatDate(date, fmt);
799
+ // }
734
800
  parseInputDate(str) {
735
801
  const fmt = this.format()?.toUpperCase() ?? "DD/MM/YYYY";
736
802
  console.log(str, "-", fmt);
@@ -757,13 +823,13 @@ class HijriDatepickerComponent {
757
823
  }
758
824
  this.showCalendar = false;
759
825
  }
760
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: HijriDatepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
761
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: HijriDatepickerComponent, isStandalone: true, selector: "app-hijri-datepicker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, cssClass: { classPropertyName: "cssClass", publicName: "cssClass", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "inputDiv", first: true, predicate: ["inputDiv"], descendants: true }, { propertyName: "modalDiv", first: true, predicate: ["modalDiv"], descendants: true }, { propertyName: "popupDiv", first: true, predicate: ["popupDiv"], descendants: true }], ngImport: i0, template: "<div class=\"input-group flex-nowrap\" #inputDiv>\r\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"inputDate\" (blur)=\"onManualDateEntered()\"\r\n [disabled]=\"!enabled()\" [placeholder]=\"format()?.toUpperCase()\" (keyup.enter)=\"onManualDateEntered()\">\r\n\r\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"toggleCalendar()\" [disabled]=\"!enabled()\">\r\n <i class=\"bi bi-calendar3\"></i>\r\n </button>\r\n</div>\r\n\r\n@if(showCalendar){\r\n@if(isMobile){\r\n<div class=\"modal modal-backdrop\" tabindex=\"-1\" role=\"dialog\" style=\"display: block;\" #modalDiv>\r\n <div class=\"modal-dialog modal-dialog-scrollable\" role=\"document\">\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <button type=\"button\" class=\"btn btn-danger close ms-auto btn-sm\" (click)=\"showCalendar = false\">\r\n <i class=\"bi bi-x\"></i>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n@else{\r\n<div class=\"dropdown-menu show\" #popupDiv>\r\n <app-hijri-calendar (valueChange)=\"onDateSelectedFromCalender($event)\"></app-hijri-calendar>\r\n</div>\r\n}\r\n}", styles: [".dropdown-menu{--bs-dropdown-padding-y: 0}.card-body{padding:0!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HijriCalendarComponent, selector: "app-hijri-calendar", inputs: ["value", "minDate", "maxDate", "monthLang"], outputs: ["valueChange"] }] });
826
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: HijriDatepickerComponent, deps: [{ token: DateServiceService }], target: i0.ɵɵFactoryTarget.Component });
827
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: HijriDatepickerComponent, isStandalone: true, selector: "app-hijri-datepicker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, calender: { classPropertyName: "calender", publicName: "calender", isSignal: true, isRequired: false, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, cssClass: { classPropertyName: "cssClass", publicName: "cssClass", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "inputDiv", first: true, predicate: ["inputDiv"], descendants: true }, { propertyName: "modalDiv", first: true, predicate: ["modalDiv"], descendants: true }, { propertyName: "popupDiv", first: true, predicate: ["popupDiv"], descendants: true }], ngImport: i0, template: "<div class=\"input-group flex-nowrap\" #inputDiv>\r\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"inputDate\" (blur)=\"onManualDateEntered()\"\r\n [disabled]=\"!enabled()\" [placeholder]=\"format()?.toUpperCase()\" (keyup.enter)=\"onManualDateEntered()\">\r\n\r\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"toggleCalendar()\" [disabled]=\"!enabled()\">\r\n <i class=\"bi bi-calendar3\"></i>\r\n </button>\r\n</div>\r\n\r\n@if(showCalendar){\r\n@if(isMobile){\r\n<div class=\"modal modal-backdrop\" tabindex=\"-1\" role=\"dialog\" style=\"display: block;\" #modalDiv>\r\n <div class=\"modal-dialog modal-dialog-scrollable\" role=\"document\">\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <button type=\"button\" class=\"btn btn-danger close ms-auto btn-sm\" (click)=\"showCalendar = false\">\r\n <i class=\"bi bi-x\"></i>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n@else{\r\n<div class=\"dropdown-menu show\" #popupDiv>\r\n <app-hijri-calendar (valueChange)=\"onDateSelectedFromCalender($event)\"></app-hijri-calendar>\r\n</div>\r\n}\r\n}", styles: [".dropdown-menu{--bs-dropdown-padding-y: 0}.card-body{padding:0!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: HijriCalendarComponent, selector: "app-hijri-calendar", inputs: ["value", "minDate", "maxDate", "monthLang"], outputs: ["valueChange"] }] });
762
828
  }
763
829
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: HijriDatepickerComponent, decorators: [{
764
830
  type: Component,
765
831
  args: [{ selector: 'app-hijri-datepicker', imports: [CommonModule, FormsModule, HijriCalendarComponent], template: "<div class=\"input-group flex-nowrap\" #inputDiv>\r\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"inputDate\" (blur)=\"onManualDateEntered()\"\r\n [disabled]=\"!enabled()\" [placeholder]=\"format()?.toUpperCase()\" (keyup.enter)=\"onManualDateEntered()\">\r\n\r\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"toggleCalendar()\" [disabled]=\"!enabled()\">\r\n <i class=\"bi bi-calendar3\"></i>\r\n </button>\r\n</div>\r\n\r\n@if(showCalendar){\r\n@if(isMobile){\r\n<div class=\"modal modal-backdrop\" tabindex=\"-1\" role=\"dialog\" style=\"display: block;\" #modalDiv>\r\n <div class=\"modal-dialog modal-dialog-scrollable\" role=\"document\">\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <button type=\"button\" class=\"btn btn-danger close ms-auto btn-sm\" (click)=\"showCalendar = false\">\r\n <i class=\"bi bi-x\"></i>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n@else{\r\n<div class=\"dropdown-menu show\" #popupDiv>\r\n <app-hijri-calendar (valueChange)=\"onDateSelectedFromCalender($event)\"></app-hijri-calendar>\r\n</div>\r\n}\r\n}", styles: [".dropdown-menu{--bs-dropdown-padding-y: 0}.card-body{padding:0!important}\n"] }]
766
- }], ctorParameters: () => [], propDecorators: { inputDiv: [{
832
+ }], ctorParameters: () => [{ type: DateServiceService }], propDecorators: { inputDiv: [{
767
833
  type: ViewChild,
768
834
  args: ["inputDiv"]
769
835
  }], modalDiv: [{
@@ -780,7 +846,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImpor
780
846
  class FlexContainerComponent {
781
847
  renderer;
782
848
  contentHost;
783
- resizeHandler;
849
+ //private resizeHandler!: () => void;
784
850
  // Allow consumer to provide templates
785
851
  headerTpl;
786
852
  footerTpl;
@@ -790,13 +856,16 @@ class FlexContainerComponent {
790
856
  }
791
857
  ngAfterViewInit() {
792
858
  this.resizeContent();
793
- this.resizeHandler = this.resizeContent.bind(this);
794
- window.addEventListener('resize', this.resizeHandler);
859
+ //this.resizeHandler = this.resizeContent.bind(this);
860
+ //window.addEventListener('resize', this.resizeHandler);
795
861
  }
796
862
  ngOnDestroy() {
797
- if (this.resizeHandler) {
798
- window.removeEventListener('resize', this.resizeHandler);
799
- }
863
+ // if (this.resizeHandler) {
864
+ // window.removeEventListener('resize', this.resizeHandler);
865
+ // }
866
+ }
867
+ onWindowResize() {
868
+ this.resizeContent();
800
869
  }
801
870
  resizeContent() {
802
871
  const parent = this.contentHost.nativeElement.parentElement;
@@ -807,9 +876,10 @@ class FlexContainerComponent {
807
876
  const footerHeight = this.contentHost.nativeElement.nextElementSibling?.clientHeight || 0;
808
877
  const available = parentHeight - headerHeight - footerHeight;
809
878
  this.renderer.setStyle(this.contentHost.nativeElement, 'height', `${available}px`);
879
+ this.renderer.setStyle(this.contentHost.nativeElement, 'width', `${parent.clientWidth}px`);
810
880
  }
811
881
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: FlexContainerComponent, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
812
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: FlexContainerComponent, isStandalone: true, selector: "flex-container", queries: [{ propertyName: "headerTpl", first: true, predicate: ["header"], descendants: true }, { propertyName: "footerTpl", first: true, predicate: ["footer"], descendants: true }, { propertyName: "contentTpl", first: true, predicate: ["content"], descendants: true }], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true }], ngImport: i0, template: "<div class=\"flex-container\">\r\n @if(headerTpl){\r\n <div class=\"header\">\r\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\r\n </div>\r\n }\r\n\r\n\r\n <div #contentHost class=\"content\">\r\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\r\n </div>\r\n @if(footerTpl){\r\n <div class=\"footer\">\r\n <ng-container *ngTemplateOutlet=\"footerTpl\"></ng-container>\r\n </div>\r\n }\r\n\r\n</div>", styles: [".flex-container{display:flex;flex-direction:column;height:100%}.header,.footer{flex:0 0 auto}.content{flex:1 1 auto;min-height:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
882
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: FlexContainerComponent, isStandalone: true, selector: "flex-container", host: { listeners: { "window:resize": "onWindowResize()" } }, queries: [{ propertyName: "headerTpl", first: true, predicate: ["header"], descendants: true }, { propertyName: "footerTpl", first: true, predicate: ["footer"], descendants: true }, { propertyName: "contentTpl", first: true, predicate: ["content"], descendants: true }], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true }], ngImport: i0, template: "<div class=\"flex-container\">\r\n @if(headerTpl){\r\n <div class=\"header\">\r\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\r\n </div>\r\n }\r\n\r\n\r\n <div #contentHost class=\"content\">\r\n <ng-container *ngTemplateOutlet=\"contentTpl\"></ng-container>\r\n </div>\r\n @if(footerTpl){\r\n <div class=\"footer\">\r\n <ng-container *ngTemplateOutlet=\"footerTpl\"></ng-container>\r\n </div>\r\n }\r\n\r\n</div>", styles: [".flex-container{display:flex;flex-direction:column;height:100%}.header,.footer{flex:0 0 auto}.content{flex:1 1 auto;min-height:0;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
813
883
  }
814
884
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: FlexContainerComponent, decorators: [{
815
885
  type: Component,
@@ -826,6 +896,485 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImpor
826
896
  }], contentTpl: [{
827
897
  type: ContentChild,
828
898
  args: ['content']
899
+ }], onWindowResize: [{
900
+ type: HostListener,
901
+ args: ['window:resize']
902
+ }] } });
903
+
904
+ // ✅ Intl formatters
905
+ const hijriFmt = new Intl.DateTimeFormat('en-u-ca-islamic-umalqura-nu-latn', {
906
+ day: 'numeric',
907
+ month: 'numeric',
908
+ year: 'numeric'
909
+ });
910
+ const hijriMonthNamesEn = [
911
+ 'Muharram', 'Safar', 'Rabi I', 'Rabi II',
912
+ 'Jumada I', 'Jumada II', 'Rajab', 'Sha’ban',
913
+ 'Ramadan', 'Shawwal', 'Dhu al-Qi’dah', 'Dhu al-Hijjah'
914
+ ];
915
+ const hijriMonthNamesAr = [
916
+ 'محرم', 'صفر', 'ربيع الأول', 'ربيع الآخر',
917
+ 'جمادى الأولى', 'جمادى الآخرة', 'رجب', 'شعبان',
918
+ 'رمضان', 'شوال', 'ذو القعدة', 'ذو الحجة'
919
+ ];
920
+ const gregMonthNamesEn = [
921
+ 'January', 'February', 'March', 'April', 'May', 'June',
922
+ 'July', 'August', 'September', 'October', 'November', 'December'
923
+ ];
924
+ const gregMonthNamesAr = [
925
+ 'يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو',
926
+ 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'
927
+ ];
928
+ const weekDaysEn = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
929
+ const weekDaysAr = ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'];
930
+ function hijriFromGregorian(g) {
931
+ const parts = hijriFmt.formatToParts(g);
932
+ const map = {};
933
+ for (const p of parts)
934
+ map[p.type] = p.value;
935
+ const n = (k) => parseInt(map[k] ?? '0', 10);
936
+ return { hy: n('year'), hm: n('month'), hd: n('day') };
937
+ }
938
+ function compareHijri(a, b) {
939
+ if (a.hy !== b.hy)
940
+ return a.hy - b.hy;
941
+ if (a.hm !== b.hm)
942
+ return a.hm - b.hm;
943
+ return a.hd - b.hd;
944
+ }
945
+ function findGregorianForHijri(target) {
946
+ let lo = new Date(1937, 0, 1).getTime();
947
+ let hi = new Date(2076, 11, 31).getTime();
948
+ const dayMs = 86400000;
949
+ while (lo <= hi) {
950
+ const mid = Math.floor((lo + hi) / 2);
951
+ const h = hijriFromGregorian(new Date(mid));
952
+ compareHijri(h, target) < 0 ? (lo = mid + dayMs) : (hi = mid - dayMs);
953
+ }
954
+ let d = new Date(lo);
955
+ while (true) {
956
+ const h = hijriFromGregorian(d);
957
+ const cmp = compareHijri(h, target);
958
+ if (cmp === 0)
959
+ return d;
960
+ if (cmp > 0)
961
+ throw new Error('Hijri date out of supported range');
962
+ d = new Date(d.getTime() + dayMs);
963
+ }
964
+ }
965
+ function stripTime(d) {
966
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
967
+ }
968
+ class FlexDualCalendarComponent {
969
+ /** Two-way bound selected date (Gregorian) */
970
+ value;
971
+ valueChange = new EventEmitter();
972
+ /** Optional min/max (Gregorian) */
973
+ minDate;
974
+ maxDate;
975
+ /** Month/weekday language */
976
+ monthLang = 'en';
977
+ /** Start mode, default Hijri */
978
+ startMode = 'gregory';
979
+ /** Current mode (hijri | gregory) */
980
+ mode = signal('gregory', ...(ngDevMode ? [{ debugName: "mode" }] : []));
981
+ /** Hijri view state */
982
+ hy = signal(0, ...(ngDevMode ? [{ debugName: "hy" }] : []));
983
+ hm = signal(0, ...(ngDevMode ? [{ debugName: "hm" }] : []));
984
+ /** Gregorian view state */
985
+ gy = signal(0, ...(ngDevMode ? [{ debugName: "gy" }] : []));
986
+ gm = signal(0, ...(ngDevMode ? [{ debugName: "gm" }] : [])); // 1..12
987
+ /** selectors */
988
+ selectedMonth = 1;
989
+ selectedYear = 1447;
990
+ yearRange = [];
991
+ // ---- getters for UI ----
992
+ get hijriMonths() {
993
+ return this.monthLang === 'ar' ? hijriMonthNamesAr : hijriMonthNamesEn;
994
+ }
995
+ get gregMonths() {
996
+ return this.monthLang === 'ar' ? gregMonthNamesAr : gregMonthNamesEn;
997
+ }
998
+ get weekDays() {
999
+ return this.monthLang === 'ar' ? weekDaysAr : weekDaysEn;
1000
+ }
1001
+ get currentMonthName() {
1002
+ return this.mode() === 'islamic'
1003
+ ? this.hijriMonths[this.selectedMonth - 1]
1004
+ : this.gregMonths[this.selectedMonth - 1];
1005
+ }
1006
+ constructor() {
1007
+ const today = new Date();
1008
+ const h = hijriFromGregorian(today);
1009
+ this.hy.set(h.hy);
1010
+ this.hm.set(h.hm);
1011
+ this.gy.set(today.getFullYear());
1012
+ this.gm.set(today.getMonth() + 1);
1013
+ // initialize selectors with start mode
1014
+ this.mode.set(this.startMode);
1015
+ if (this.mode() === 'islamic') {
1016
+ this.selectedYear = this.hy();
1017
+ this.selectedMonth = this.hm();
1018
+ }
1019
+ else {
1020
+ this.selectedYear = this.gy();
1021
+ this.selectedMonth = this.gm();
1022
+ }
1023
+ }
1024
+ ngOnInit() {
1025
+ this.buildYearRange();
1026
+ }
1027
+ ngOnChanges(changes) {
1028
+ // If value changed from outside, update both calendars
1029
+ if (changes['value'] && this.value instanceof Date) {
1030
+ const g = stripTime(this.value);
1031
+ const h = hijriFromGregorian(g);
1032
+ this.gy.set(g.getFullYear());
1033
+ this.gm.set(g.getMonth() + 1);
1034
+ this.hy.set(h.hy);
1035
+ this.hm.set(h.hm);
1036
+ // Update dropdowns based on current mode
1037
+ if (this.mode() === 'islamic') {
1038
+ this.selectedYear = this.hy();
1039
+ this.selectedMonth = this.hm();
1040
+ }
1041
+ else {
1042
+ this.selectedYear = this.gy();
1043
+ this.selectedMonth = this.gm();
1044
+ }
1045
+ }
1046
+ // Rebuild year range if min/max changed
1047
+ if (changes['minDate'] || changes['maxDate'] || changes['value']) {
1048
+ this.buildYearRange();
1049
+ }
1050
+ }
1051
+ // Build years based on min/max expressed in Hijri for Hijri mode, Gregorian for Gregorian mode
1052
+ buildYearRange() {
1053
+ if (this.mode() === 'islamic') {
1054
+ const minH = this.minDate ? hijriFromGregorian(stripTime(this.minDate)) : { hy: 1356, hm: 1, hd: 1 };
1055
+ const maxH = this.maxDate ? hijriFromGregorian(stripTime(this.maxDate)) : { hy: 1499, hm: 12, hd: 30 };
1056
+ this.yearRange = [];
1057
+ for (let y = minH.hy; y <= maxH.hy; y++)
1058
+ this.yearRange.push(y);
1059
+ }
1060
+ else {
1061
+ const minG = this.minDate ? stripTime(this.minDate) : new Date(1937, 0, 1);
1062
+ const maxG = this.maxDate ? stripTime(this.maxDate) : new Date(2076, 11, 31);
1063
+ this.yearRange = [];
1064
+ for (let y = minG.getFullYear(); y <= maxG.getFullYear(); y++)
1065
+ this.yearRange.push(y);
1066
+ }
1067
+ }
1068
+ // 🔁 Mode switch (SYNCED view — keep same instant)
1069
+ switchMode(value) {
1070
+ const pivot = this.value ?? this.firstOfMonth(); // use selected date or current view start
1071
+ if (value === 'gregory') {
1072
+ // To Gregorian mode
1073
+ const g = stripTime(pivot);
1074
+ this.gy.set(g.getFullYear());
1075
+ this.gm.set(g.getMonth() + 1);
1076
+ this.mode.set('gregory');
1077
+ this.selectedYear = this.gy();
1078
+ this.selectedMonth = this.gm();
1079
+ }
1080
+ else {
1081
+ // To Hijri mode
1082
+ const h = hijriFromGregorian(stripTime(pivot));
1083
+ this.hy.set(h.hy);
1084
+ this.hm.set(h.hm);
1085
+ this.mode.set('islamic');
1086
+ this.selectedYear = this.hy();
1087
+ this.selectedMonth = this.hm();
1088
+ }
1089
+ this.buildYearRange();
1090
+ }
1091
+ // called when dropdowns change
1092
+ onMonthYearChange() {
1093
+ if (this.mode() === 'islamic') {
1094
+ this.hy.set(this.selectedYear);
1095
+ this.hm.set(this.selectedMonth);
1096
+ }
1097
+ else {
1098
+ this.gy.set(this.selectedYear);
1099
+ this.gm.set(this.selectedMonth);
1100
+ }
1101
+ }
1102
+ // Start of visible month (Gregorian date)
1103
+ firstOfMonth() {
1104
+ if (this.mode() === 'islamic') {
1105
+ return findGregorianForHijri({ hy: this.hy(), hm: this.hm(), hd: 1 });
1106
+ }
1107
+ return new Date(this.gy(), this.gm() - 1, 1);
1108
+ }
1109
+ // Calendar cells for current view
1110
+ cells() {
1111
+ const firstG = this.firstOfMonth();
1112
+ const firstH = hijriFromGregorian(firstG);
1113
+ // grid start (Sunday-first; adjust if you want Saturday-first)
1114
+ const start = new Date(firstG);
1115
+ start.setDate(firstG.getDate() - firstG.getDay());
1116
+ const result = [];
1117
+ for (let i = 0; i < 42; i++) {
1118
+ const g = new Date(start);
1119
+ g.setDate(start.getDate() + i);
1120
+ const h = hijriFromGregorian(g);
1121
+ let disabled = false;
1122
+ if (this.minDate && g < stripTime(this.minDate))
1123
+ disabled = true;
1124
+ if (this.maxDate && g > stripTime(this.maxDate))
1125
+ disabled = true;
1126
+ const inCurrentMonth = this.mode() === 'islamic'
1127
+ ? (h.hy === firstH.hy && h.hm === firstH.hm)
1128
+ : (g.getFullYear() === firstG.getFullYear() && g.getMonth() === firstG.getMonth());
1129
+ result.push({ g, h, inCurrentMonth, disabled });
1130
+ }
1131
+ return result;
1132
+ }
1133
+ prevMonth() {
1134
+ if (!this.canPrev())
1135
+ return;
1136
+ if (this.mode() === 'islamic') {
1137
+ let m = this.hm(), y = this.hy();
1138
+ if (--m < 1) {
1139
+ m = 12;
1140
+ y--;
1141
+ }
1142
+ this.hm.set(m);
1143
+ this.hy.set(y);
1144
+ this.selectedYear = y;
1145
+ this.selectedMonth = m;
1146
+ }
1147
+ else {
1148
+ let m = this.gm(), y = this.gy();
1149
+ if (--m < 1) {
1150
+ m = 12;
1151
+ y--;
1152
+ }
1153
+ this.gm.set(m);
1154
+ this.gy.set(y);
1155
+ this.selectedYear = y;
1156
+ this.selectedMonth = m;
1157
+ }
1158
+ }
1159
+ nextMonth() {
1160
+ if (!this.canNext())
1161
+ return;
1162
+ if (this.mode() === 'islamic') {
1163
+ let m = this.hm(), y = this.hy();
1164
+ if (++m > 12) {
1165
+ m = 1;
1166
+ y++;
1167
+ }
1168
+ this.hm.set(m);
1169
+ this.hy.set(y);
1170
+ this.selectedYear = y;
1171
+ this.selectedMonth = m;
1172
+ }
1173
+ else {
1174
+ let m = this.gm(), y = this.gy();
1175
+ if (++m > 12) {
1176
+ m = 1;
1177
+ y++;
1178
+ }
1179
+ this.gm.set(m);
1180
+ this.gy.set(y);
1181
+ this.selectedYear = y;
1182
+ this.selectedMonth = m;
1183
+ }
1184
+ }
1185
+ canPrev() {
1186
+ if (!this.minDate)
1187
+ return true;
1188
+ const minG = stripTime(this.minDate);
1189
+ const viewFirst = this.firstOfMonth();
1190
+ // Can go prev if the first day of previous month is >= minG
1191
+ const prev = new Date(viewFirst.getFullYear(), viewFirst.getMonth() - 1, 1);
1192
+ return prev >= new Date(minG.getFullYear(), minG.getMonth(), 1);
1193
+ }
1194
+ canNext() {
1195
+ if (!this.maxDate)
1196
+ return true;
1197
+ const maxG = stripTime(this.maxDate);
1198
+ const viewFirst = this.firstOfMonth();
1199
+ const next = new Date(viewFirst.getFullYear(), viewFirst.getMonth() + 1, 1);
1200
+ // move to the first of the month after max
1201
+ const maxMonthAfter = new Date(maxG.getFullYear(), maxG.getMonth() + 1, 1);
1202
+ return next < maxMonthAfter;
1203
+ }
1204
+ isToday(g) {
1205
+ const t = new Date();
1206
+ return g.getFullYear() === t.getFullYear()
1207
+ && g.getMonth() === t.getMonth()
1208
+ && g.getDate() === t.getDate();
1209
+ }
1210
+ isSelected(g) {
1211
+ return !!this.value &&
1212
+ g.getFullYear() === this.value.getFullYear() &&
1213
+ g.getMonth() === this.value.getMonth() &&
1214
+ g.getDate() === this.value.getDate();
1215
+ }
1216
+ select(c) {
1217
+ if (c.disabled)
1218
+ return;
1219
+ this.value = stripTime(c.g);
1220
+ this.valueChange.emit(this.value);
1221
+ // keep both views in sync after selection
1222
+ const h = hijriFromGregorian(this.value);
1223
+ this.hy.set(h.hy);
1224
+ this.hm.set(h.hm);
1225
+ this.gy.set(this.value.getFullYear());
1226
+ this.gm.set(this.value.getMonth() + 1);
1227
+ if (this.mode() === 'islamic') {
1228
+ this.selectedYear = this.hy();
1229
+ this.selectedMonth = this.hm();
1230
+ }
1231
+ else {
1232
+ this.selectedYear = this.gy();
1233
+ this.selectedMonth = this.gm();
1234
+ }
1235
+ }
1236
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: FlexDualCalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1237
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: FlexDualCalendarComponent, isStandalone: true, selector: "flex-dual-calendar", inputs: { value: "value", minDate: "minDate", maxDate: "maxDate", monthLang: "monthLang", startMode: "startMode" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"card border-0\">\r\n <div class=\"card-header p-1\">\r\n <div class=\"row g-1\">\r\n <div class=\"col-1\">\r\n <button class=\"btn btn-sm w-100 btn-info\" (click)=\"prevMonth()\" [disabled]=\"!canPrev()\">&laquo;</button>\r\n </div>\r\n <div class=\"col-6\">\r\n <select class=\"form-select form-select-sm\" [(ngModel)]=\"selectedMonth\" (change)=\"onMonthYearChange()\">\r\n @if (mode() === 'islamic') {\r\n @for (m of hijriMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ hijriMonths[$index] }} ({{ hijriMonths[$index] }})</option>\r\n }\r\n } @else {\r\n @for (m of gregMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ gregMonths[$index] }}</option>\r\n }\r\n }\r\n </select>\r\n </div>\r\n <div class=\"col-4\">\r\n <select class=\"form-select form-select-sm\" [(ngModel)]=\"selectedYear\" (change)=\"onMonthYearChange()\">\r\n @for (y of yearRange; track y) {\r\n <option [value]=\"y\">{{ y }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"col-1 \">\r\n <button class=\"btn btn-sm w-100 btn-info\" (click)=\"nextMonth()\" [disabled]=\"!canNext()\">&raquo;</button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"card-body p-0\">\r\n <!-- Weekday labels -->\r\n <div class=\"grid\">\r\n @for (w of weekDays; track $index) {\r\n <div class=\"col fw-bold border pb-1\">{{ w }}</div>\r\n }\r\n </div>\r\n\r\n <!-- Calendar cells -->\r\n <div class=\"grid\">\r\n @for (c of cells(); track $index) {\r\n <div class=\"cell\">\r\n <button class=\"btn w-100\" [class.muted]=\"!c.inCurrentMonth\" [class.today]=\"isToday(c.g)\"\r\n [class.selected]=\"isSelected(c.g)\" [disabled]=\"c.disabled\" (click)=\"select(c)\">\r\n <!-- show Hijri day in Hijri mode, otherwise Gregorian -->\r\n @if(mode() === 'islamic'){\r\n {{ c.h.hd }}\r\n }\r\n @else{\r\n {{ c.g.getDate() }}\r\n }\r\n </button>\r\n </div>\r\n }\r\n\r\n </div>\r\n </div>\r\n <div class=\"card-footer px-0 py-1\">\r\n <div class=\"d-flex justify-content-center\">\r\n <div class=\"btn-group\" role=\"group\" aria-label=\"Basic example\">\r\n <button class=\"btn btn-sm btn-outline-secondary\" type=\"button\" (click)=\"switchMode('gregory')\">\r\n Gregorian\r\n </button>\r\n <button class=\"btn btn-sm btn-outline-secondary\" type=\"button\" (click)=\"switchMode('islamic')\">\r\n \u0647\u062C\u0631\u064A\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: [".grid{display:grid;grid-template-columns:repeat(7,1fr)}.cell{border:1px solid #ddd}.cell.muted{opacity:.5}.cell.today{outline:2px solid #0d6efd}.cell.selected{background-color:#0d6efd;color:#fff}.cell:disabled{background-color:#f8f9fa;color:#aaa;cursor:not-allowed}.col{text-align:center}.btn{font-size:90%;--bs-btn-padding-x: .3rem;--bs-btn-padding-y: .25rem}.btn:hover{background-color:azure}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
1238
+ }
1239
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: FlexDualCalendarComponent, decorators: [{
1240
+ type: Component,
1241
+ args: [{ selector: 'flex-dual-calendar', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"card border-0\">\r\n <div class=\"card-header p-1\">\r\n <div class=\"row g-1\">\r\n <div class=\"col-1\">\r\n <button class=\"btn btn-sm w-100 btn-info\" (click)=\"prevMonth()\" [disabled]=\"!canPrev()\">&laquo;</button>\r\n </div>\r\n <div class=\"col-6\">\r\n <select class=\"form-select form-select-sm\" [(ngModel)]=\"selectedMonth\" (change)=\"onMonthYearChange()\">\r\n @if (mode() === 'islamic') {\r\n @for (m of hijriMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ hijriMonths[$index] }} ({{ hijriMonths[$index] }})</option>\r\n }\r\n } @else {\r\n @for (m of gregMonths; track $index) {\r\n <option [value]=\"$index + 1\">{{ gregMonths[$index] }}</option>\r\n }\r\n }\r\n </select>\r\n </div>\r\n <div class=\"col-4\">\r\n <select class=\"form-select form-select-sm\" [(ngModel)]=\"selectedYear\" (change)=\"onMonthYearChange()\">\r\n @for (y of yearRange; track y) {\r\n <option [value]=\"y\">{{ y }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"col-1 \">\r\n <button class=\"btn btn-sm w-100 btn-info\" (click)=\"nextMonth()\" [disabled]=\"!canNext()\">&raquo;</button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"card-body p-0\">\r\n <!-- Weekday labels -->\r\n <div class=\"grid\">\r\n @for (w of weekDays; track $index) {\r\n <div class=\"col fw-bold border pb-1\">{{ w }}</div>\r\n }\r\n </div>\r\n\r\n <!-- Calendar cells -->\r\n <div class=\"grid\">\r\n @for (c of cells(); track $index) {\r\n <div class=\"cell\">\r\n <button class=\"btn w-100\" [class.muted]=\"!c.inCurrentMonth\" [class.today]=\"isToday(c.g)\"\r\n [class.selected]=\"isSelected(c.g)\" [disabled]=\"c.disabled\" (click)=\"select(c)\">\r\n <!-- show Hijri day in Hijri mode, otherwise Gregorian -->\r\n @if(mode() === 'islamic'){\r\n {{ c.h.hd }}\r\n }\r\n @else{\r\n {{ c.g.getDate() }}\r\n }\r\n </button>\r\n </div>\r\n }\r\n\r\n </div>\r\n </div>\r\n <div class=\"card-footer px-0 py-1\">\r\n <div class=\"d-flex justify-content-center\">\r\n <div class=\"btn-group\" role=\"group\" aria-label=\"Basic example\">\r\n <button class=\"btn btn-sm btn-outline-secondary\" type=\"button\" (click)=\"switchMode('gregory')\">\r\n Gregorian\r\n </button>\r\n <button class=\"btn btn-sm btn-outline-secondary\" type=\"button\" (click)=\"switchMode('islamic')\">\r\n \u0647\u062C\u0631\u064A\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: [".grid{display:grid;grid-template-columns:repeat(7,1fr)}.cell{border:1px solid #ddd}.cell.muted{opacity:.5}.cell.today{outline:2px solid #0d6efd}.cell.selected{background-color:#0d6efd;color:#fff}.cell:disabled{background-color:#f8f9fa;color:#aaa;cursor:not-allowed}.col{text-align:center}.btn{font-size:90%;--bs-btn-padding-x: .3rem;--bs-btn-padding-y: .25rem}.btn:hover{background-color:azure}\n"] }]
1242
+ }], ctorParameters: () => [], propDecorators: { value: [{
1243
+ type: Input
1244
+ }], valueChange: [{
1245
+ type: Output
1246
+ }], minDate: [{
1247
+ type: Input
1248
+ }], maxDate: [{
1249
+ type: Input
1250
+ }], monthLang: [{
1251
+ type: Input
1252
+ }], startMode: [{
1253
+ type: Input
1254
+ }] } });
1255
+
1256
+ class FlexDualDatepickerComponent {
1257
+ dateService;
1258
+ value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : []));
1259
+ enabled = input(true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
1260
+ cssClass = input(...(ngDevMode ? [undefined, { debugName: "cssClass" }] : []));
1261
+ format = input("DD/MM/YYYY", ...(ngDevMode ? [{ debugName: "format" }] : []));
1262
+ //selectedDate is needed to keep the current data for process
1263
+ selectedDate;
1264
+ showCalendar = false;
1265
+ calendar = "gregory";
1266
+ inputDate = "";
1267
+ isMobile = false;
1268
+ constructor(dateService) {
1269
+ this.dateService = dateService;
1270
+ effect(() => {
1271
+ const iso = this.value();
1272
+ if (iso) {
1273
+ const date = dayjs(iso);
1274
+ if (date.isValid()) {
1275
+ this.selectedDate = date.toDate();
1276
+ this.inputDate = this.formatDate(this.selectedDate);
1277
+ }
1278
+ return;
1279
+ }
1280
+ this.selectedDate = undefined;
1281
+ this.inputDate = "";
1282
+ });
1283
+ }
1284
+ switchCalendar() {
1285
+ if (this.calendar == "gregory") {
1286
+ this.calendar = "islamic";
1287
+ }
1288
+ else {
1289
+ this.calendar = "gregory";
1290
+ }
1291
+ this.inputDate = this.formatDate(this.selectedDate);
1292
+ }
1293
+ toggleCalendar() {
1294
+ this.showCalendar = !this.showCalendar;
1295
+ }
1296
+ onDateSelectedFromCalender(e) {
1297
+ // console.log("onDateSelectedFromCalender: ", e);
1298
+ this.dateService.log(e);
1299
+ this.inputDate = this.formatDate(e);
1300
+ //var time = this.selectedDate ?? new Date();
1301
+ // e.setHours(time.getHours());
1302
+ // e.setMinutes(time.getMinutes());
1303
+ this.updateDate(e);
1304
+ this.showCalendar = false;
1305
+ // console.log(e);
1306
+ }
1307
+ /** Triggered on blur OR Enter key */
1308
+ onManualDateEntered() {
1309
+ if (!this.inputDate)
1310
+ return;
1311
+ const parsed = this.parseInputDate(this.inputDate);
1312
+ if (parsed) {
1313
+ this.updateDate(parsed);
1314
+ }
1315
+ else {
1316
+ console.warn("Invalid date entered:", this.inputDate);
1317
+ }
1318
+ }
1319
+ /** Reusable update method */
1320
+ updateDate(d) {
1321
+ this.selectedDate = d;
1322
+ this.inputDate = this.formatDate(d);
1323
+ this.value.set(d);
1324
+ //this.valueChange.emit(d);
1325
+ }
1326
+ formatDate(date) {
1327
+ return this.dateService.format(date, this.calendar, this.format());
1328
+ }
1329
+ // private formatDateForInput(date: Date): string {
1330
+ // const fmt = this.format()?.toUpperCase() ?? "DD/MM/YYYY";
1331
+ // //return dayjs(date).format(fmt);
1332
+ // return this.formatDate(date, fmt);
1333
+ // }
1334
+ parseInputDate(str) {
1335
+ const fmt = this.format()?.toUpperCase() ?? "DD/MM/YYYY";
1336
+ console.log(str, "-", fmt);
1337
+ const d = dayjs(str, fmt); // strict parse
1338
+ console.log(d.toDate());
1339
+ if (!d.isValid())
1340
+ return null;
1341
+ if (!this.selectedDate) {
1342
+ this.selectedDate = new Date();
1343
+ }
1344
+ // gd.set("hour", this.selectedDate.getHours());
1345
+ // gd.set("minute", this.selectedDate.getMinutes());
1346
+ // gd.set("second", this.selectedDate.getSeconds());
1347
+ return null;
1348
+ }
1349
+ inputDiv;
1350
+ modalDiv;
1351
+ popupDiv;
1352
+ onDocumentClick(event) {
1353
+ if (this.inputDiv?.nativeElement.contains(event.target)
1354
+ || this.modalDiv?.nativeElement.contains(event.target)
1355
+ || this.popupDiv?.nativeElement.contains(event.target)) {
1356
+ return;
1357
+ }
1358
+ this.showCalendar = false;
1359
+ }
1360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: FlexDualDatepickerComponent, deps: [{ token: DateServiceService }], target: i0.ɵɵFactoryTarget.Component });
1361
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.1", type: FlexDualDatepickerComponent, isStandalone: true, selector: "flex-dual-datepicker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, cssClass: { classPropertyName: "cssClass", publicName: "cssClass", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "inputDiv", first: true, predicate: ["inputDiv"], descendants: true }, { propertyName: "modalDiv", first: true, predicate: ["modalDiv"], descendants: true }, { propertyName: "popupDiv", first: true, predicate: ["popupDiv"], descendants: true }], ngImport: i0, template: "<div class=\"input-group flex-nowrap\" #inputDiv>\r\n <span class=\"input-group-text\" style=\"cursor: pointer;\" (click)=\"switchCalendar()\"> @if(calendar == 'gregory') {G}\r\n @else{H}</span>\r\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"inputDate\" (blur)=\"onManualDateEntered()\"\r\n [disabled]=\"!enabled()\" [placeholder]=\"format()\" (keyup.enter)=\"onManualDateEntered()\">\r\n\r\n\r\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"toggleCalendar()\" [disabled]=\"!enabled()\">\r\n <i class=\"bi bi-calendar3\"></i>\r\n </button>\r\n</div>\r\n\r\n@if(showCalendar){\r\n@if(isMobile){\r\n<div class=\"modal modal-backdrop\" tabindex=\"-1\" role=\"dialog\" style=\"display: block;\" #modalDiv>\r\n <div class=\"modal-dialog modal-dialog-scrollable\" role=\"document\">\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <button type=\"button\" class=\"btn btn-danger close ms-auto btn-sm\" (click)=\"showCalendar = false\">\r\n <i class=\"bi bi-x\"></i>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n@else{\r\n<div class=\"dropdown-menu show\" #popupDiv>\r\n <flex-dual-calendar (valueChange)=\"onDateSelectedFromCalender($event)\"\r\n [(value)]=\"selectedDate\"></flex-dual-calendar>\r\n</div>\r\n}\r\n}", styles: [".dropdown-menu{--bs-dropdown-padding-y: 0}.card-body{padding:0!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FlexDualCalendarComponent, selector: "flex-dual-calendar", inputs: ["value", "minDate", "maxDate", "monthLang", "startMode"], outputs: ["valueChange"] }] });
1362
+ }
1363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImport: i0, type: FlexDualDatepickerComponent, decorators: [{
1364
+ type: Component,
1365
+ args: [{ selector: 'flex-dual-datepicker', imports: [CommonModule, FormsModule, FlexDualCalendarComponent], template: "<div class=\"input-group flex-nowrap\" #inputDiv>\r\n <span class=\"input-group-text\" style=\"cursor: pointer;\" (click)=\"switchCalendar()\"> @if(calendar == 'gregory') {G}\r\n @else{H}</span>\r\n <input type=\"text\" class=\"form-control\" [(ngModel)]=\"inputDate\" (blur)=\"onManualDateEntered()\"\r\n [disabled]=\"!enabled()\" [placeholder]=\"format()\" (keyup.enter)=\"onManualDateEntered()\">\r\n\r\n\r\n <button class=\"btn btn-outline-secondary\" type=\"button\" (click)=\"toggleCalendar()\" [disabled]=\"!enabled()\">\r\n <i class=\"bi bi-calendar3\"></i>\r\n </button>\r\n</div>\r\n\r\n@if(showCalendar){\r\n@if(isMobile){\r\n<div class=\"modal modal-backdrop\" tabindex=\"-1\" role=\"dialog\" style=\"display: block;\" #modalDiv>\r\n <div class=\"modal-dialog modal-dialog-scrollable\" role=\"document\">\r\n <div class=\"modal-content\">\r\n <div class=\"modal-header\">\r\n <button type=\"button\" class=\"btn btn-danger close ms-auto btn-sm\" (click)=\"showCalendar = false\">\r\n <i class=\"bi bi-x\"></i>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n@else{\r\n<div class=\"dropdown-menu show\" #popupDiv>\r\n <flex-dual-calendar (valueChange)=\"onDateSelectedFromCalender($event)\"\r\n [(value)]=\"selectedDate\"></flex-dual-calendar>\r\n</div>\r\n}\r\n}", styles: [".dropdown-menu{--bs-dropdown-padding-y: 0}.card-body{padding:0!important}\n"] }]
1366
+ }], ctorParameters: () => [{ type: DateServiceService }], propDecorators: { inputDiv: [{
1367
+ type: ViewChild,
1368
+ args: ["inputDiv"]
1369
+ }], modalDiv: [{
1370
+ type: ViewChild,
1371
+ args: ["modalDiv"]
1372
+ }], popupDiv: [{
1373
+ type: ViewChild,
1374
+ args: ["popupDiv"]
1375
+ }], onDocumentClick: [{
1376
+ type: HostListener,
1377
+ args: ['document:click', ['$event']]
829
1378
  }] } });
830
1379
 
831
1380
  /*
@@ -836,5 +1385,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.1", ngImpor
836
1385
  * Generated bundle index. Do not edit.
837
1386
  */
838
1387
 
839
- export { ComboDatePickerComponent, FlexColumnComponent, FlexColumnsComponent, FlexContainerComponent, FlexGridComponent, FlexPaginationComponent, FlexPanelComponent, HijriDatepickerComponent };
1388
+ export { ComboDatePickerComponent, FlexColumnComponent, FlexColumnsComponent, FlexContainerComponent, FlexDualDatepickerComponent, FlexGridComponent, FlexPaginationComponent, FlexPanelComponent, HijriDatepickerComponent };
840
1389
  //# sourceMappingURL=dcsl-flex-ui.mjs.map