@corp-products/ui-components 3.2.7 → 3.2.9

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 (138) hide show
  1. package/ng-package.json +7 -0
  2. package/package.json +22 -36
  3. package/src/enums/date-formatter.ts +21 -0
  4. package/src/helper/date-handler.ts +142 -0
  5. package/src/lib/app-accordion/app-accordion.component.html +15 -0
  6. package/src/lib/app-accordion/app-accordion.component.scss +0 -0
  7. package/src/lib/app-accordion/app-accordion.component.spec.ts +21 -0
  8. package/src/lib/app-accordion/app-accordion.component.ts +21 -0
  9. package/src/lib/app-accordion/index.ts +2 -0
  10. package/src/lib/app-breadcrumb/app-breadcrumb.component.html +7 -0
  11. package/src/lib/app-breadcrumb/app-breadcrumb.component.scss +25 -0
  12. package/src/lib/app-breadcrumb/app-breadcrumb.component.ts +140 -0
  13. package/src/lib/app-breadcrumb/app-breadcrumb.interface.ts +15 -0
  14. package/src/lib/app-button/app-button.component.html +7 -0
  15. package/src/lib/app-button/app-button.component.scss +0 -0
  16. package/src/lib/app-button/app-button.component.ts +14 -0
  17. package/src/lib/app-button/app-button.ts +15 -0
  18. package/src/lib/app-button/index.ts +2 -0
  19. package/src/lib/app-dropdown-menu/app-dropdown-menu.component.html +22 -0
  20. package/src/lib/app-dropdown-menu/app-dropdown-menu.component.scss +39 -0
  21. package/src/lib/app-dropdown-menu/app-dropdown-menu.component.spec.ts +21 -0
  22. package/src/lib/app-dropdown-menu/app-dropdown-menu.component.ts +43 -0
  23. package/src/lib/app-dropdown-menu/app-dropdown-menu.ts +17 -0
  24. package/src/lib/app-dropdown-menu/index.ts +2 -0
  25. package/src/lib/app-dropdown-menu/menu-popup.pipe.ts +18 -0
  26. package/src/lib/app-header/app-header.component.html +26 -0
  27. package/src/lib/app-header/app-header.component.scss +0 -0
  28. package/src/lib/app-header/app-header.component.ts +43 -0
  29. package/src/lib/app-side-menu/app-side-menu.component.html +20 -0
  30. package/src/lib/app-side-menu/app-side-menu.component.ts +28 -0
  31. package/src/lib/app-side-menu/routes-names.ts +28 -0
  32. package/src/lib/app-side-menu/side-menu-items.ts +45 -0
  33. package/src/lib/app-side-menu/side-menu.ts +12 -0
  34. package/src/lib/app-tabs/app-tab.interface.ts +27 -0
  35. package/src/lib/app-tabs/app-tabs.component.html +37 -0
  36. package/src/lib/app-tabs/app-tabs.component.scss +103 -0
  37. package/src/lib/app-tabs/app-tabs.component.spec.ts +21 -0
  38. package/src/lib/app-tabs/app-tabs.component.ts +67 -0
  39. package/src/lib/app-tabs/index.ts +2 -0
  40. package/src/lib/bottom-sheet/bottom-sheet.component.html +18 -0
  41. package/src/lib/bottom-sheet/bottom-sheet.component.scss +31 -0
  42. package/src/lib/bottom-sheet/bottom-sheet.component.ts +26 -0
  43. package/src/lib/confirmation-dialog/confirmation-dialog.component.html +37 -0
  44. package/src/lib/confirmation-dialog/confirmation-dialog.component.scss +0 -0
  45. package/src/lib/confirmation-dialog/confirmation-dialog.component.spec.ts +22 -0
  46. package/src/lib/confirmation-dialog/confirmation-dialog.component.ts +64 -0
  47. package/src/lib/confirmation-dialog/confirmation-dialog.interface.ts +13 -0
  48. package/src/lib/confirmation-dialog/confirmation-dialog.service.ts +34 -0
  49. package/src/lib/dual-calender/date-picker-switcher/date-picker-switcher.component.html +27 -0
  50. package/src/lib/dual-calender/date-picker-switcher/date-picker-switcher.component.scss +22 -0
  51. package/src/lib/dual-calender/date-picker-switcher/date-picker-switcher.component.ts +64 -0
  52. package/src/lib/dual-calender/dual-calendar.component.html +31 -0
  53. package/src/lib/dual-calender/dual-calendar.component.scss +229 -0
  54. package/src/lib/dual-calender/dual-calendar.component.ts +107 -0
  55. package/src/lib/dual-calender/gregorian-calendar/gregorian-calendar.component.html +10 -0
  56. package/src/lib/dual-calender/gregorian-calendar/gregorian-calendar.component.scss +0 -0
  57. package/src/lib/dual-calender/gregorian-calendar/gregorian-calendar.component.spec.ts +21 -0
  58. package/src/lib/dual-calender/gregorian-calendar/gregorian-calendar.component.ts +59 -0
  59. package/src/lib/dual-calender/hijri-calendar/hijri-calendar.component.html +10 -0
  60. package/src/lib/dual-calender/hijri-calendar/hijri-calendar.component.scss +0 -0
  61. package/src/lib/dual-calender/hijri-calendar/hijri-calendar.component.spec.ts +21 -0
  62. package/src/lib/dual-calender/hijri-calendar/hijri-calendar.component.ts +59 -0
  63. package/src/lib/dual-calender/services/gregorian-i18n.service.ts +123 -0
  64. package/src/lib/dual-calender/services/islamic-i18n.service.ts +119 -0
  65. package/src/lib/dual-calender/utils/date-i18n.utils.ts +58 -0
  66. package/src/lib/dynamic-form/dynamic-form.component.html +86 -0
  67. package/src/lib/dynamic-form/dynamic-form.component.scss +0 -0
  68. package/src/lib/dynamic-form/dynamic-form.component.spec.ts +21 -0
  69. package/src/lib/dynamic-form/dynamic-form.component.ts +58 -0
  70. package/src/lib/dynamic-form/dynamic-form.interface.ts +94 -0
  71. package/src/lib/dynamic-side-bar-v2/dynamic-sidebar.component.html +32 -0
  72. package/src/lib/dynamic-side-bar-v2/dynamic-sidebar.component.scss +3 -0
  73. package/src/lib/dynamic-side-bar-v2/dynamic-sidebar.component.ts +82 -0
  74. package/src/lib/dynamic-side-bar-v2/dynamic-sidebar.config.ts +31 -0
  75. package/src/lib/dynamic-side-bar-v2/dynamic-sidebar.service.ts +41 -0
  76. package/src/lib/form-components/@utils/form-utils.ts +12 -0
  77. package/src/lib/form-components/@utils/validations/error-keys.enum.ts +24 -0
  78. package/src/lib/form-components/@utils/validations/form-validation.service.ts +68 -0
  79. package/src/lib/form-components/@utils/validations/index.ts +3 -0
  80. package/src/lib/form-components/@utils/validations/validation-message.pipe.ts +24 -0
  81. package/src/lib/form-components/components/auto-complete/auto-complete.component.html +35 -0
  82. package/src/lib/form-components/components/auto-complete/auto-complete.component.scss +7 -0
  83. package/src/lib/form-components/components/auto-complete/auto-complete.component.spec.ts +21 -0
  84. package/src/lib/form-components/components/auto-complete/auto-complete.component.ts +57 -0
  85. package/src/lib/form-components/components/base-input.component.ts +35 -0
  86. package/src/lib/form-components/components/date-picker/date-picker.component.html +41 -0
  87. package/src/lib/form-components/components/date-picker/date-picker.component.scss +16 -0
  88. package/src/lib/form-components/components/date-picker/date-picker.component.spec.ts +21 -0
  89. package/src/lib/form-components/components/date-picker/date-picker.component.ts +54 -0
  90. package/src/lib/form-components/components/input/input.component.html +63 -0
  91. package/src/lib/form-components/components/input/input.component.scss +41 -0
  92. package/src/lib/form-components/components/input/input.component.spec.ts +21 -0
  93. package/src/lib/form-components/components/input/input.component.ts +45 -0
  94. package/src/lib/form-components/components/select/select.component.html +111 -0
  95. package/src/lib/form-components/components/select/select.component.scss +43 -0
  96. package/src/lib/form-components/components/select/select.component.spec.ts +21 -0
  97. package/src/lib/form-components/components/select/select.component.ts +56 -0
  98. package/src/lib/form-components/components/select-button/select-button.component.html +21 -0
  99. package/src/lib/form-components/components/select-button/select-button.component.scss +0 -0
  100. package/src/lib/form-components/components/select-button/select-button.component.spec.ts +21 -0
  101. package/src/lib/form-components/components/select-button/select-button.component.ts +22 -0
  102. package/src/lib/form-components/components/switcher/switch.component.html +5 -0
  103. package/src/lib/form-components/components/switcher/switch.component.scss +0 -0
  104. package/src/lib/form-components/components/switcher/switch.component.spec.ts +21 -0
  105. package/src/lib/form-components/components/switcher/switch.component.ts +25 -0
  106. package/src/lib/form-components/index.ts +9 -0
  107. package/src/lib/form-components/interfaces/index.ts +1 -0
  108. package/src/lib/form-components/interfaces/label-value.ts +4 -0
  109. package/src/lib/ico-moon-icon/ico-moon-icon.component.ts +23 -0
  110. package/src/lib/read-more/read-more.component.html +17 -0
  111. package/src/lib/read-more/read-more.component.scss +0 -0
  112. package/src/lib/read-more/read-more.component.spec.ts +21 -0
  113. package/src/lib/read-more/read-more.component.ts +21 -0
  114. package/src/lib/side-bar/side-bar.component.html +24 -0
  115. package/src/lib/side-bar/side-bar.component.scss +22 -0
  116. package/src/lib/side-bar/side-bar.component.spec.ts +21 -0
  117. package/src/lib/side-bar/side-bar.component.ts +33 -0
  118. package/src/lib/side-bar-dynamic/data-injector.pipe.ts +15 -0
  119. package/src/lib/side-bar-dynamic/dynamic-sidebar.service.ts +116 -0
  120. package/src/lib/side-bar-dynamic/side-bar-dynamic.component.html +42 -0
  121. package/src/lib/side-bar-dynamic/side-bar-dynamic.component.scss +5 -0
  122. package/src/lib/side-bar-dynamic/side-bar-dynamic.component.spec.ts +21 -0
  123. package/src/lib/side-bar-dynamic/side-bar-dynamic.component.ts +37 -0
  124. package/src/lib/side-bar-dynamic/side-bar-utils.ts +30 -0
  125. package/src/lib/side-bar-dynamic/sidebar-config.ts +48 -0
  126. package/src/lib/user-autocomplete-card/user-autocomplete-card.component.html +20 -0
  127. package/src/lib/user-autocomplete-card/user-autocomplete-card.component.scss +0 -0
  128. package/src/lib/user-autocomplete-card/user-autocomplete-card.component.spec.ts +21 -0
  129. package/src/lib/user-autocomplete-card/user-autocomplete-card.component.ts +21 -0
  130. package/src/lib/user-info/user-info.component.html +10 -0
  131. package/src/lib/user-info/user-info.component.ts +11 -0
  132. package/src/public-api.ts +26 -0
  133. package/tsconfig.lib.json +18 -0
  134. package/tsconfig.lib.prod.json +11 -0
  135. package/tsconfig.spec.json +14 -0
  136. package/fesm2022/corp-products-ui-components.mjs +0 -1409
  137. package/fesm2022/corp-products-ui-components.mjs.map +0 -1
  138. package/index.d.ts +0 -623
@@ -0,0 +1,64 @@
1
+ import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
2
+ import { NavigationStart, Router } from '@angular/router';
3
+ import { TranslatePipe } from '@ngx-translate/core';
4
+ import { AvatarModule } from 'primeng/avatar';
5
+ import {
6
+ DialogService,
7
+ DynamicDialogConfig,
8
+ DynamicDialogModule,
9
+ DynamicDialogRef,
10
+ DynamicDialogStyle,
11
+ } from 'primeng/dynamicdialog';
12
+ import { filter, Subscription } from 'rxjs';
13
+ import { AppButtonComponent } from '../app-button/app-button.component';
14
+ import { DynamicFormComponent } from '../dynamic-form/dynamic-form.component';
15
+ import { DynamicFormData } from '../dynamic-form/dynamic-form.interface';
16
+
17
+ @Component({
18
+ selector: 'app-confirm-dialog',
19
+ templateUrl: './confirmation-dialog.component.html',
20
+ styleUrls: ['./confirmation-dialog.component.scss'],
21
+ encapsulation: ViewEncapsulation.None,
22
+ standalone: true,
23
+ imports: [
24
+ AppButtonComponent,
25
+ AvatarModule,
26
+ DynamicDialogModule,
27
+ DynamicFormComponent,
28
+ TranslatePipe,
29
+ ],
30
+ providers: [DialogService, DynamicDialogStyle],
31
+ })
32
+ export class ConfirmationDialogComponent extends DynamicDialogRef implements OnInit, OnDestroy {
33
+ router = inject(Router);
34
+ dialogService = inject(DialogService);
35
+ dynamicDialogConfig = inject(DynamicDialogConfig);
36
+ private readonly _ref = inject(DynamicDialogRef);
37
+ private readonly _subscription = new Subscription();
38
+ dialogFormData: DynamicFormData;
39
+
40
+ ngOnDestroy(): void {
41
+ this._subscription.unsubscribe();
42
+ }
43
+ ngOnInit() {
44
+ // closing when navigating back from the browser
45
+ this._subscription.add(
46
+ this.router.events.pipe(filter((event) => event instanceof NavigationStart)).subscribe(() => {
47
+ if (this.dynamicDialogConfig) {
48
+ this._ref.close(false);
49
+ }
50
+ })
51
+ );
52
+ this.dialogFormData = this.dynamicDialogConfig.data?.inputForm;
53
+ }
54
+ submit() {
55
+ // we should pass submitted data when using form dialog
56
+ // const submitData = { submitted: true, data: this.dialogFormData?.formGroup?.value };
57
+ // this._ref.close(this.dynamicDialogConfig.data.inputForm ? submitData : true);
58
+ this._ref.close(true);
59
+ }
60
+
61
+ override close() {
62
+ this._ref.close(false);
63
+ }
64
+ }
@@ -0,0 +1,13 @@
1
+ export interface ConfirmationDialogData {
2
+ header: string;
3
+ message: string;
4
+ confirmBtnId: string;
5
+ cancelBtnId: string;
6
+ cancelBtnLabel?: string;
7
+ confirmBtnLabel?: string;
8
+ confirmBtnIcon?: string;
9
+ confirmBtnPosition?: string;
10
+ hint?: string;
11
+ inputForm?: any;
12
+ breakpoints?: any;
13
+ }
@@ -0,0 +1,34 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
3
+ import { Observable, of } from 'rxjs';
4
+ import { filter, map } from 'rxjs/operators';
5
+ import { ConfirmationDialogComponent } from './confirmation-dialog.component';
6
+ import { ConfirmationDialogData } from './confirmation-dialog.interface';
7
+
8
+ @Injectable({
9
+ providedIn: 'root',
10
+ })
11
+ export class ConfirmationDialogService {
12
+ constructor(private dialogService: DialogService) { }
13
+
14
+ open(data: ConfirmationDialogData): Observable<boolean> {
15
+ const ref: DynamicDialogRef | null = this.dialogService.open(ConfirmationDialogComponent, {
16
+ data,
17
+ header: data.header,
18
+ showHeader: true,
19
+ width: '600px',
20
+ closable: true,
21
+ modal: true,
22
+ styleClass: 'confirmation-dialog-wrapper',
23
+ breakpoints: data.breakpoints,
24
+ });
25
+ if (!ref) {
26
+ return of(false); // or EMPTY / throwError — depending on your logic
27
+ }
28
+ // Emit true/false when dialog closes
29
+ return ref.onClose.pipe(
30
+ filter((res) => res !== undefined),
31
+ map((res) => !!res)
32
+ );
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ <div class="field flex flex-col gap-2 my-3 relative">
2
+ <p-floatlabel [variant]="variant">
3
+ <input [id]="inputId" [type]="'text-float-label'" [formControl]="control" [readonly]="true" (focus)="openCalendar(true)"
4
+ (click)="openCalendar(true)" [pSize]="'small'" style="direction: rtl; unicode-bidi: plaintext;"
5
+ [class]="
6
+ 'w-full border border-[' + defaultColor + '] rounded-none focus:outline-[' + defaultColor + '] focus:border-[' + defaultColor + '] hover:border-[' + defaultColor + '] disabled:opacity-50'
7
+ " [disabled]="disabled" [name]="name" pInputText
8
+ class="w-full"
9
+ [ngClass]="{ 'p-invalid ng-dirty ng-invalid': isInvalid, 'basic-style': basicInput, 'no-style':noStyle}" />
10
+ <label [for]="inputId">
11
+ {{ label }}
12
+ @if (required) {
13
+ <span [class.text-required]="required">*</span>
14
+ }
15
+ </label>
16
+ </p-floatlabel>
17
+ @if (hint) {
18
+ <small class="p-mt-1">{{ hint }}</small>
19
+ }
20
+ @if (isInvalid && (control.dirty || control.touched)) {
21
+ <small class="p-error">
22
+ @for (error of control.errors | validationErrors; track error) {
23
+ {{ error }}<br>
24
+ }
25
+ </small>
26
+ }
27
+ </div>
@@ -0,0 +1,22 @@
1
+ .p-datepicker-clear-icon {
2
+ @apply cursor-pointer absolute top-[50%] left-[13px] -translate-y-1/2 text-[var(--p-datepicker-input-icon-color)] ml-[calc(var(--p-icon-size))] leading-none;
3
+ }
4
+
5
+ /* Hide default calendar inside panel */
6
+
7
+ .my-custom-datepicker-panel {
8
+ display: none !important;
9
+ }
10
+
11
+ .d-ltr {
12
+ direction: ltr;
13
+ }
14
+
15
+
16
+ .text-required {
17
+ color: red;
18
+ }
19
+
20
+ .p-error {
21
+ color: #dc2626;
22
+ }
@@ -0,0 +1,64 @@
1
+ import { NgClass } from '@angular/common';
2
+ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';
3
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4
+ import { TranslatePipe } from '@ngx-translate/core';
5
+ import { DatePicker, DatePickerModule } from 'primeng/datepicker';
6
+ import { FloatLabelModule } from 'primeng/floatlabel';
7
+ import { ValidationErrorsPipe } from '../../form-components/@utils/validations/validation-message.pipe';
8
+ import { BaseInputComponent } from '../../form-components/components/base-input.component';
9
+ import { InputText } from 'primeng/inputtext';
10
+ @Component({
11
+ selector: 'stc-date-picker-switcher',
12
+ standalone: true,
13
+ imports: [
14
+ FormsModule,
15
+ ReactiveFormsModule,
16
+ NgClass,
17
+ InputText,
18
+ DatePickerModule,
19
+ ValidationErrorsPipe,
20
+ TranslatePipe,
21
+ FloatLabelModule,
22
+ ],
23
+ templateUrl: './date-picker-switcher.component.html',
24
+ styleUrl: './date-picker-switcher.component.scss',
25
+ encapsulation: ViewEncapsulation.None,
26
+ })
27
+ export class DatePickerSwitcherComponent extends BaseInputComponent implements OnChanges {
28
+ @Input() type: 'text' | 'textarea' = 'text';
29
+ @Input() contentType: 'text' | 'email' | 'number' = 'text';
30
+ @Input() size: 'small' | 'large' = "small";
31
+ @Input() prefix: string;
32
+ @Input() rows = 2;
33
+ @Input() cols = 20;
34
+ @Input() autoResize = true;
35
+ @Input() basicInput!: boolean;
36
+ @Input() noStyle!: boolean;
37
+ @Input() hideOptionalLabel: boolean;
38
+ @Input() inputDirection: 'ltr' | 'rtl' | 'inherit' = 'inherit';
39
+ @Input() variant: 'in' | 'over' | 'on' = 'over';
40
+ @Input() defaultColor = '#DFE0E6'
41
+ @Input() formattedDate = '';
42
+ @Output() openCalender = new EventEmitter<boolean>();
43
+ constructor( ) {
44
+ super();
45
+ }
46
+ ngOnChanges(changes: SimpleChanges) {
47
+ if (changes['formattedDate'] && changes['formattedDate'].currentValue) {
48
+ // Update the FormControl value if parent changes the formattedDate
49
+ this.control.setValue(changes['formattedDate'].currentValue, { emitEvent: false });
50
+ }
51
+ }
52
+ override ngOnInit() {
53
+ this.control.setValue(this.formattedDate);
54
+ }
55
+
56
+ clearButtonClick(e: any) {
57
+ this.control.setValue(null);
58
+ }
59
+
60
+ openCalendar(isOpen: boolean) {
61
+ this.openCalender.emit(isOpen)
62
+
63
+ }
64
+ }
@@ -0,0 +1,31 @@
1
+ <div class="calender-content " #calendarContainer>
2
+ <stc-date-picker-switcher [variant]="'in'" [label]="'Select Date'" id="dateId" (openCalender)="showCalender($event)"
3
+ [control]="control" [formattedDate]='selectedDate' >
4
+ </stc-date-picker-switcher>
5
+
6
+ <!-- calender.html -->
7
+ <div dir="rtl" class="p-2 calendar-wrapper " [@slideDown]="isCalendarOpen ? 'open' : 'closed'" >
8
+ <div class="header-tabs">
9
+ <div class="header-label">نوع التقويم</div>
10
+ <div class="tabs">
11
+ <button class="tab-button" [class.active]="mode === 'gregorian'" (click)=" mode='gregorian'">
12
+ الميلادي
13
+ </button>
14
+ <button class="tab-button" [class.active]="mode === 'hijri'" (click)=" mode='hijri'">
15
+ هجري
16
+ </button>
17
+ </div>
18
+ </div>
19
+ @if(mode === 'gregorian') {
20
+ <app-gregorian-calendar [model]="gregorianModel" [language]="currentLang"
21
+ (dateSelected)="onSelectGregorian($event)">
22
+ </app-gregorian-calendar>
23
+ }
24
+
25
+ @if(mode === 'hijri') {
26
+ <app-hijri-calendar [model]="hijriModel" [language]="currentLang" (dateSelected)="onSelectHijri($event)">
27
+ </app-hijri-calendar>
28
+ }
29
+ </div>
30
+
31
+ </div>
@@ -0,0 +1,229 @@
1
+ .calendar-wrapper {
2
+ font-family: "Tajawal", sans-serif;
3
+ max-width: 375px;
4
+ padding: 15px;
5
+ }
6
+
7
+ .header-tabs {
8
+ display: flex;
9
+ justify-content: space-between;
10
+ align-items: center;
11
+ gap: 10px;
12
+ margin-bottom: 10px;
13
+ }
14
+
15
+ .tabs {
16
+
17
+ background: #f0f0f5;
18
+ padding: 3px;
19
+ border-radius: 2px;
20
+ }
21
+
22
+ .tab-button {
23
+ flex: 1;
24
+ padding: 6px 10px;
25
+ font-size: 14px;
26
+ background: #f0f0f5;
27
+ border: none;
28
+ border-radius: 6px;
29
+ cursor: pointer;
30
+ transition: 0.3s;
31
+ }
32
+
33
+ .tab-button.active {
34
+ background: #dcd6f8;
35
+ color: #4a3fb4;
36
+ font-weight: 600;
37
+ }
38
+
39
+ .custom-datepicker {
40
+ width: 100%;
41
+ background: white;
42
+ border: 1px solid #e5e5e5;
43
+ border-radius: 8px;
44
+ padding: 12px;
45
+ }
46
+
47
+ /* Calendar Header (Month + Arrows) */
48
+ .custom-datepicker .ngb-dp-header {
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ margin-bottom: 12px;
53
+ max-width: 375px;
54
+ }
55
+
56
+ .custom-datepicker .ngb-dp-arrow-btn {
57
+ background: none;
58
+ border: none;
59
+ cursor: pointer;
60
+ padding: 4px;
61
+ }
62
+
63
+ /* Weekdays Row */
64
+ .custom-datepicker .ngb-dp-weekdays {
65
+ margin-bottom: 6px;
66
+ }
67
+
68
+ .custom-datepicker .ngb-dp-weekday {
69
+ color: #9b9b9b;
70
+ font-weight: 600;
71
+ text-align: center;
72
+ }
73
+
74
+
75
+
76
+ /* Today style */
77
+ .custom-day.today {
78
+ border: 1px solid #6a41d8;
79
+ border-radius: 8px;
80
+ }
81
+
82
+ /* Outside month */
83
+ .custom-day.outside {
84
+ opacity: 0.3;
85
+ }
86
+
87
+ /* Disabled */
88
+ .custom-day.disabled {
89
+ opacity: 0.2;
90
+ pointer-events: none;
91
+ }
92
+
93
+ .calender-content {
94
+ position: relative;
95
+ padding-top: 10px;
96
+ }
97
+
98
+ .calendar-wrapper {
99
+ position: absolute;
100
+ top: 100%;
101
+ left: 0;
102
+ width: 100%;
103
+ z-index: 50;
104
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
105
+ background-color: #fff;
106
+ border-radius: 4px;
107
+ overflow: hidden;
108
+ }
109
+
110
+
111
+ .ngb-datepicker {
112
+ width: 100%;
113
+
114
+ .ngb-dp-month {}
115
+ }
116
+
117
+ .custom-day {
118
+ display: inline-flex;
119
+ justify-content: center;
120
+ align-items: center;
121
+ margin: 2px;
122
+ border-radius: 50%;
123
+ cursor: pointer;
124
+ font-weight: 500;
125
+ }
126
+
127
+ .custom-day.today {
128
+ color: #4F008D;
129
+ border: 0 !important;
130
+
131
+ }
132
+
133
+ .ngb-dp-day:has(.selected) {
134
+ background-color: #EDE6F4 !important;
135
+ border: 0 !important;
136
+ color: #4F008D !important;
137
+ border-radius: 2px;
138
+ }
139
+
140
+ .custom-day.outside {
141
+ color: #ccc !important;
142
+ }
143
+
144
+ .custom-day.disabled {
145
+ color: #aaa !important;
146
+ pointer-events: none !important;
147
+ }
148
+
149
+ .ngb-dp-day,
150
+ .ngb-dp-weekday,
151
+ .ngb-dp-week-number {
152
+ width: 38px !important;
153
+ height: 44px !important;
154
+ display: flex;
155
+ margin: 5px;
156
+ justify-content: center;
157
+ align-items: center;
158
+ }
159
+
160
+ .ngb-dp-month:first-child .ngb-dp-week {
161
+ padding: 0 !important;
162
+ }
163
+
164
+ .ngb-dp-month:last-child .ngb-dp-week {
165
+ padding: 0 !important;
166
+ }
167
+
168
+ .ngb-dp-arrow-btn {
169
+ margin-inline-end: 0 !important;
170
+ outline: 0 !important;
171
+ }
172
+
173
+ .ngb-dp-header {
174
+ position: relative;
175
+
176
+ .ngb-dp-arrow {
177
+ position: absolute;
178
+
179
+ &.ngb-dp-arrow-prev {
180
+ inset-inline-end: 0;
181
+ }
182
+
183
+ &.ngb-dp-arrow-next {
184
+ inset-inline-end: 15px;
185
+ }
186
+
187
+ &:focus-visible,
188
+ .btn:focus-visible {
189
+ outline: 0 !important;
190
+ }
191
+ }
192
+
193
+ .visually-hidden {
194
+ display: none;
195
+ }
196
+ }
197
+
198
+ .ngb-dp-navigation-select {
199
+ max-width: 140px;
200
+ }
201
+
202
+ .ngb-dp-navigation-select {
203
+ select {
204
+ border: 0;
205
+
206
+ &:focus-visible {
207
+ outline: 0;
208
+ }
209
+ }
210
+ }
211
+
212
+ .ngb-datepicker-navigation-select>.form-select {
213
+ font-size: 14px;
214
+ font-weight: bold;
215
+ }
216
+
217
+ .ngb-dp-navigation-select,
218
+ .form-select:first-child {
219
+ appearance: none !important;
220
+ -webkit-appearance: none;
221
+ -moz-appearance: none;
222
+ }
223
+
224
+
225
+
226
+ .ngb-dp-navigation-chevron {
227
+ font-size: 18px;
228
+ border-width: .1em .1em 0 0 !important;
229
+ }
@@ -0,0 +1,107 @@
1
+ import { Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core';
2
+ import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
3
+ import { NgbCalendar, NgbDateStruct, NgbCalendarIslamicUmalqura, NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';
4
+ import moment from 'moment-hijri';
5
+ import { HijriCalendarComponent } from './hijri-calendar/hijri-calendar.component';
6
+ import { GregorianCalendarComponent } from './gregorian-calendar/gregorian-calendar.component';
7
+ import { FloatLabelModule } from 'primeng/floatlabel';
8
+ import { DatePickerModule } from 'primeng/datepicker';
9
+ import { trigger, state, style, transition, animate } from '@angular/animations';
10
+ import { DatePickerSwitcherComponent } from './date-picker-switcher/date-picker-switcher.component';
11
+ import '@angular/localize/init';
12
+ import { getGregorianMonthName, getHijriMonthName } from './utils/date-i18n.utils';
13
+ @Component({
14
+ selector: 'app-dual-calendar',
15
+ animations: [
16
+ trigger('slideDown', [
17
+ state('closed', style({
18
+ height: '0px',
19
+ opacity: 0,
20
+ overflow: 'hidden'
21
+ })),
22
+ state('open', style({
23
+ height: '*',
24
+ opacity: 1,
25
+ overflow: 'hidden'
26
+ })),
27
+ transition('closed <=> open', [
28
+ animate('300ms ease')
29
+ ])
30
+ ])
31
+ ],
32
+ imports: [
33
+ NgbDatepickerModule,
34
+ FormsModule,
35
+ FormsModule,
36
+ ReactiveFormsModule,
37
+ DatePickerModule,
38
+ FloatLabelModule,
39
+ HijriCalendarComponent,
40
+ DatePickerSwitcherComponent,
41
+ GregorianCalendarComponent
42
+ ],
43
+ providers: [
44
+ { provide: NgbCalendar, useClass: NgbCalendarIslamicUmalqura }
45
+ ],
46
+ templateUrl: './dual-calendar.component.html',
47
+ styleUrl: './dual-calendar.component.scss',
48
+ encapsulation: ViewEncapsulation.None
49
+ })
50
+ export class DualCalendarComponent {
51
+ selectedDate = ''
52
+ @Input() control: FormControl<any> = new FormControl({ value: null, disabled: false }, []);
53
+ mode: 'gregorian' | 'hijri' = 'gregorian';
54
+ gregorianModel!: NgbDateStruct;
55
+ hijriModel!: NgbDateStruct;
56
+ currentLang: 'ar' | 'en' = 'ar';
57
+ @Output() selectButtonChange = new EventEmitter<{ name: string; value: any }>();
58
+ isCalendarOpen = false
59
+ @ViewChild('calendarContainer') calendarContainer!: ElementRef;
60
+ constructor() { }
61
+ @HostListener('document:click', ['$event'])
62
+ onDocumentClick(event: MouseEvent) {
63
+ if (!this.calendarContainer) return;
64
+
65
+ const clickedInside = this.calendarContainer.nativeElement.contains(event.target);
66
+ if (!clickedInside) {
67
+ this.isCalendarOpen = false;
68
+ }
69
+ }
70
+ onSelectGregorian(date: NgbDateStruct) {
71
+ this.gregorianModel = date;
72
+ const m = moment(`${date.year}-${date.month}-${date.day}`, 'YYYY-M-D');
73
+ this.hijriModel = {
74
+ year: +m.format('iYYYY'),
75
+ month: +m.format('iM'),
76
+ day: +m.format('iD')
77
+ };
78
+ this.selectedDate = this.formatHijri(m);
79
+ this.isCalendarOpen = false;
80
+ }
81
+
82
+ onSelectHijri(date: NgbDateStruct) {
83
+ this.hijriModel = date;
84
+ const m = moment(`${date.year}-${date.month}-${date.day}`, 'iYYYY-iM-iD');
85
+ this.gregorianModel = {
86
+ year: +m.format('YYYY'),
87
+ month: +m.format('M'),
88
+ day: +m.format('D')
89
+ };
90
+ this.selectedDate = this.formatHijri(m);
91
+ this.isCalendarOpen = false;
92
+ }
93
+ showCalender(isOpen: boolean) {
94
+ this.isCalendarOpen = isOpen;
95
+ }
96
+ formatHijri(m: moment.Moment): string {
97
+ const hijriDay = m.format('iD');
98
+ const hijriMonth = getHijriMonthName(this.currentLang, +m.format('iM'));
99
+ const gregorianDay = m.format('D');
100
+ const gregorianMonth = getGregorianMonthName(this.currentLang, +m.format('M'));
101
+ const gregorianYear = m.format('YYYY');
102
+ const hijriYear = m.format('iYYYY');
103
+ return `${gregorianDay} ${gregorianMonth} ${gregorianYear} - ${hijriDay} ${hijriMonth} ${hijriYear}`;
104
+ }
105
+
106
+
107
+ }
@@ -0,0 +1,10 @@
1
+ <ngb-datepicker #dp [class.rtl]="language === 'ar'" [class.ltr]="language === 'en'" [ngModel]="model"
2
+ [startDate]="startDate" (ngModelChange)="onDateChange($event)" (dateSelect)="dateSelected.emit($event)"
3
+ [dayTemplate]="dayTemplate" [markDisabled]="isDisabled">
4
+ </ngb-datepicker>
5
+ <ng-template #dayTemplate let-date let-currentMonth="currentMonth" let-selected="selected" let-disabled="disabled">
6
+ <span class="custom-day" [class.today]="isToday(date)" [class.selected]="selected"
7
+ [class.outside]="date.month !== currentMonth" [class.disabled]="disabled">
8
+ {{ date.day }}
9
+ </span>
10
+ </ng-template>
@@ -0,0 +1,21 @@
1
+ import { ComponentFixture, TestBed } from "@angular/core/testing";
2
+ import { GregorianCalendarComponent } from "./gregorian-calendar.component";
3
+
4
+ describe("ReadMoreComponent", () => {
5
+ let component: GregorianCalendarComponent;
6
+ let fixture: ComponentFixture<GregorianCalendarComponent>;
7
+
8
+ beforeEach(async () => {
9
+ await TestBed.configureTestingModule({
10
+ imports: [GregorianCalendarComponent]
11
+ }).compileComponents();
12
+
13
+ fixture = TestBed.createComponent(GregorianCalendarComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it("should create", () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
@@ -0,0 +1,59 @@
1
+ import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
2
+ import {
3
+ NgbCalendar,
4
+ NgbCalendarGregorian,
5
+ NgbDatepickerI18n,
6
+ NgbDatepickerModule,
7
+ NgbDateStruct,
8
+ } from '@ng-bootstrap/ng-bootstrap';
9
+ import { FormsModule } from '@angular/forms';
10
+ import { DynamicGregorianI18n } from './../services/gregorian-i18n.service';
11
+
12
+
13
+ @Component({
14
+ selector: "app-gregorian-calendar",
15
+ standalone: true,
16
+ imports: [NgbDatepickerModule, FormsModule],
17
+ providers: [
18
+ { provide: NgbCalendar, useClass: NgbCalendarGregorian },
19
+ { provide: NgbDatepickerI18n, useClass: DynamicGregorianI18n }
20
+ ],
21
+ templateUrl: "./gregorian-calendar.component.html",
22
+ styleUrl: "./gregorian-calendar.component.scss"
23
+ })
24
+ export class GregorianCalendarComponent implements OnChanges {
25
+ @Input() model!: NgbDateStruct;
26
+ @Output() dateSelected = new EventEmitter<NgbDateStruct>();
27
+
28
+ @Input() language: 'ar' | 'en' = 'en';
29
+ startDate!: NgbDateStruct;
30
+ private calendar = new NgbCalendarGregorian();
31
+ constructor(
32
+ private i18n: NgbDatepickerI18n,
33
+ private cdr: ChangeDetectorRef
34
+ ) {
35
+ }
36
+ ngOnChanges(changes: SimpleChanges) {
37
+ if (changes['model'] && changes['model'].currentValue) {
38
+ this.startDate = { ...changes['model'].currentValue };
39
+ }
40
+ if (changes['language'] && this.i18n instanceof DynamicGregorianI18n) {
41
+ this.i18n.setLanguage(this.language);
42
+ this.cdr.detectChanges();
43
+ }
44
+ }
45
+
46
+ onDateChange(date: NgbDateStruct) {
47
+ this.model = date;
48
+ this.startDate = { ...date };
49
+ }
50
+
51
+ isToday(date: NgbDateStruct): boolean {
52
+ const today = this.calendar.getToday();
53
+ return date.year === today.year &&
54
+ date.month === today.month &&
55
+ date.day === today.day;
56
+ }
57
+
58
+ isDisabled = () => false;
59
+ }