-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: replace ngx-bootstrap w/ ng-bootstrap
ng-bootstrap seems to be better maintained, uses standalone components, and better supports dark mode in Bootstrap 5.
- Loading branch information
Showing
48 changed files
with
660 additions
and
392 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
src/app/common/datepicker/datepicker-range-popup/datepicker-range-popup.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<div class="dp-hidden position-absolute"> | ||
<input | ||
class="form-control" | ||
name="datepicker" | ||
autoClose="outside" | ||
ngbDatepicker | ||
outsideDays="hidden" | ||
tabindex="-1" | ||
#datepicker="ngbDatepicker" | ||
[dayTemplate]="t" | ||
[displayMonths]="2" | ||
[startDate]="fromDate()!" | ||
(dateSelect)="onDateSelection($event)" | ||
/> | ||
<ng-template let-date let-focused="focused" #t> | ||
<span | ||
class="custom-day" | ||
[class.faded]="isHovered(date) || isInside(date)" | ||
[class.focused]="focused" | ||
[class.range]="isRange(date)" | ||
(mouseenter)="hoveredDate.set(date)" | ||
(mouseleave)="hoveredDate.set(null)" | ||
> | ||
{{ date.day }} | ||
</span> | ||
</ng-template> | ||
</div> | ||
<div class="input-group"> | ||
<input | ||
class="form-control" | ||
name="dpFromDate" | ||
[value]="fromDateString()" | ||
autocomplete="off" | ||
placeholder="yyyy-mm-dd" | ||
#dpFromDate | ||
[disabled]="disabled()" | ||
(input)="fromDate.set(validateInput(fromDate(), dpFromDate.value))" | ||
/> | ||
<span class="input-group-text">-</span> | ||
<input | ||
class="form-control" | ||
name="dpToDate" | ||
[value]="toDateString()" | ||
autocomplete="off" | ||
placeholder="yyyy-mm-dd" | ||
#dpToDate | ||
[disabled]="disabled()" | ||
(input)="toDate.set(validateInput(toDate(), dpToDate.value))" | ||
/> | ||
<button | ||
class="btn btn-outline-secondary" | ||
type="button" | ||
[disabled]="disabled()" | ||
(click)="datepicker.toggle()" | ||
> | ||
<span class="fa-solid fa-calendar-days"></span> | ||
</button> | ||
</div> |
31 changes: 31 additions & 0 deletions
31
src/app/common/datepicker/datepicker-range-popup/datepicker-range-popup.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
.dp-hidden { | ||
width: 0; | ||
margin: 0; | ||
border: none; | ||
padding: 0; | ||
} | ||
|
||
.custom-day { | ||
text-align: center; | ||
padding: 0.25rem; | ||
display: inline-block; | ||
height: 2rem; | ||
width: 2rem; | ||
} | ||
|
||
.custom-day.focused { | ||
background-color: var(--bs-primary); | ||
color: white; | ||
} | ||
|
||
.custom-day.range, | ||
.custom-day:hover { | ||
background-color: var(--bs-primary); | ||
color: white; | ||
} | ||
|
||
.custom-day.faded { | ||
background-color: var(--bs-primary-bg-subtle); | ||
color: var(--bs-body-color); | ||
} | ||
|
22 changes: 22 additions & 0 deletions
22
src/app/common/datepicker/datepicker-range-popup/datepicker-range-popup.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { DatepickerRangePopupComponent } from './datepicker-range-popup.component'; | ||
|
||
describe('DatepickerRangePopupComponent', () => { | ||
let component: DatepickerRangePopupComponent; | ||
let fixture: ComponentFixture<DatepickerRangePopupComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [DatepickerRangePopupComponent] | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(DatepickerRangePopupComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
127 changes: 127 additions & 0 deletions
127
src/app/common/datepicker/datepicker-range-popup/datepicker-range-popup.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import { JsonPipe } from '@angular/common'; | ||
import { | ||
Component, | ||
computed, | ||
effect, | ||
forwardRef, | ||
inject, | ||
input, | ||
signal, | ||
viewChild | ||
} from '@angular/core'; | ||
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; | ||
|
||
import { | ||
NgbCalendar, | ||
NgbDate, | ||
NgbDateAdapter, | ||
NgbDateParserFormatter, | ||
NgbDatepickerModule, | ||
NgbInputDatepicker | ||
} from '@ng-bootstrap/ng-bootstrap'; | ||
|
||
type DateRange = [Date | null, Date | null]; | ||
|
||
@Component({ | ||
selector: 'app-datepicker-range-popup', | ||
standalone: true, | ||
providers: [ | ||
{ | ||
provide: NG_VALUE_ACCESSOR, | ||
useExisting: forwardRef(() => DatepickerRangePopupComponent), | ||
multi: true | ||
} | ||
], | ||
imports: [NgbDatepickerModule, FormsModule, JsonPipe], | ||
templateUrl: './datepicker-range-popup.component.html', | ||
styleUrl: './datepicker-range-popup.component.scss' | ||
}) | ||
export class DatepickerRangePopupComponent implements ControlValueAccessor { | ||
readonly #calendar = inject(NgbCalendar); | ||
readonly formatter = inject(NgbDateParserFormatter); | ||
readonly #adapter = inject(NgbDateAdapter); | ||
|
||
readonly #changed = new Array<(value: DateRange) => void>(); | ||
readonly #touched = new Array<() => void>(); | ||
|
||
readonly datepicker = viewChild.required(NgbInputDatepicker); | ||
|
||
readonly disabled = input(false); | ||
|
||
readonly fromDate = signal<NgbDate | null>(null); | ||
readonly toDate = signal<NgbDate | null>(null); | ||
readonly hoveredDate = signal<NgbDate | null>(null); | ||
|
||
readonly fromDateString = computed(() => this.formatter.format(this.fromDate())); | ||
readonly toDateString = computed(() => this.formatter.format(this.toDate())); | ||
|
||
readonly asNativeDateTuple = computed<DateRange>(() => [ | ||
this.#adapter.toModel(this.fromDate()), | ||
this.#adapter.toModel(this.toDate()) | ||
]); | ||
|
||
constructor() { | ||
effect(() => { | ||
this.propagateChange(); | ||
}); | ||
} | ||
|
||
writeValue(value: DateRange) { | ||
this.fromDate.set(NgbDate.from(this.#adapter.fromModel(value?.[0]))); | ||
this.toDate.set(NgbDate.from(this.#adapter.fromModel(value?.[1]))); | ||
} | ||
|
||
registerOnChange(fn: (value: DateRange) => void) { | ||
this.#changed.push(fn); | ||
} | ||
|
||
registerOnTouched(fn: () => void) { | ||
this.#touched.push(fn); | ||
} | ||
|
||
propagateChange() { | ||
this.#changed.forEach((f) => f(this.asNativeDateTuple())); | ||
} | ||
|
||
onDateSelection(date: NgbDate) { | ||
if (!this.fromDate() && !this.toDate()) { | ||
this.fromDate.set(date); | ||
} else if (this.fromDate() && !this.toDate() && date && date.after(this.fromDate())) { | ||
this.toDate.set(date); | ||
this.datepicker().close(); | ||
} else { | ||
this.toDate.set(null); | ||
this.fromDate.set(date); | ||
} | ||
} | ||
|
||
isHovered(date: NgbDate) { | ||
return ( | ||
this.fromDate() && | ||
!this.toDate() && | ||
this.hoveredDate() && | ||
date.after(this.fromDate()) && | ||
date.before(this.hoveredDate()) | ||
); | ||
} | ||
|
||
isInside(date: NgbDate) { | ||
return this.toDate() && date.after(this.fromDate()) && date.before(this.toDate()); | ||
} | ||
|
||
isRange(date: NgbDate) { | ||
return ( | ||
date.equals(this.fromDate()) || | ||
(this.toDate() && date.equals(this.toDate())) || | ||
this.isInside(date) || | ||
this.isHovered(date) | ||
); | ||
} | ||
|
||
validateInput(currentValue: NgbDate | null, input: string): NgbDate | null { | ||
const parsed = this.formatter.parse(input); | ||
return parsed && this.#calendar.isValid(NgbDate.from(parsed)) | ||
? NgbDate.from(parsed) | ||
: currentValue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './public-api'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Injectable } from '@angular/core'; | ||
|
||
import { NgbDateAdapter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; | ||
import isInteger from 'lodash/isInteger'; | ||
|
||
/** | ||
* [`NgbDateAdapter`](#/components/datepicker/api#NgbDateAdapter) implementation that uses | ||
* native javascript dates as a user date model. | ||
*/ | ||
|
||
// eslint-disable-next-line @angular-eslint/use-injectable-provided-in | ||
@Injectable() | ||
export class NgbDateCustomAdapter extends NgbDateAdapter<Date> { | ||
/** | ||
* Converts a native `Date` to a `NgbDateStruct`. | ||
*/ | ||
fromModel(date: number | Date | string | null): NgbDateStruct | null { | ||
if (date && !(date instanceof Date)) { | ||
date = new Date(date); | ||
} | ||
return date instanceof Date && !isNaN(date.getTime()) ? this._fromNativeDate(date) : null; | ||
} | ||
|
||
/** | ||
* Converts a `NgbDateStruct` to a native `Date`. | ||
*/ | ||
toModel(date: NgbDateStruct | null): Date | null { | ||
return date && isInteger(date.year) && isInteger(date.month) && isInteger(date.day) | ||
? this._toNativeDate(date) | ||
: null; | ||
} | ||
|
||
protected _fromNativeDate(date: Date): NgbDateStruct { | ||
return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() }; | ||
} | ||
|
||
protected _toNativeDate(date: NgbDateStruct): Date { | ||
const jsDate = new Date(date.year, date.month - 1, date.day, 12); | ||
// avoid 30 -> 1930 conversion | ||
jsDate.setFullYear(date.year); | ||
return jsDate; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { makeEnvironmentProviders } from '@angular/core'; | ||
|
||
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; | ||
|
||
import { NgbDateCustomAdapter } from './ngb-date-custom-adapter'; | ||
|
||
export function provideNgbDateAdapter() { | ||
return makeEnvironmentProviders([ | ||
{ | ||
provide: NgbDateAdapter, | ||
useClass: NgbDateCustomAdapter | ||
} | ||
]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './provider'; | ||
export * from './ngb-date-custom-adapter'; | ||
export * from './datepicker-range-popup/datepicker-range-popup.component'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.