Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(datepicker): add apply and cancel buttons #6638

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 31 additions & 0 deletions apps/ngx-bootstrap-docs/src/ng-api-doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,24 @@ export const ngdoc: any = {
type: 'string[]',
description: '<p>Set allowed positions of container.</p>\n'
},
{
name: 'applyButtonLabel',
defaultValue: 'Apply',
type: 'string',
description: '<p>Label for &#39;apply&#39; button</p>\n'
},
{
name: 'applyPosition',
defaultValue: 'right',
type: 'string',
description: '<p>Positioning of &#39;apply&#39; buttons</p>\n'
},
{
name: 'cancelButtonLabel',
defaultValue: 'Cancel',
type: 'string',
description: '<p>Label for &#39;cancel&#39; button</p>\n'
},
{
name: 'clearButtonLabel',
defaultValue: 'Clear',
Expand Down Expand Up @@ -690,6 +708,19 @@ export const ngdoc: any = {
description:
'<p>Allows select daterange as first and last day of week by click on week number (dateRangePicker only)</p>\n'
},
{
name: 'showApplyButton',
defaultValue: 'false',
type: 'boolean',
description:
'<p>Shows &#39;apply&#39; and &#39;cancel&#39; button, date is not sent from view to model until user presses apply. Picker remains opened until applied, cancelled or dismissed (clicked off of)</p>\n'
},
{
name: 'showCancelButton',
defaultValue: 'true',
type: 'boolean',
description: '<p>Shows &#39;cancel&#39; button (only if showApplyButton is also true)</p>\n'
},
{
name: 'showClearButton',
defaultValue: 'false',
Expand Down
15 changes: 15 additions & 0 deletions libs/doc-pages/datepicker/src/lib/datepicker-section.list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ import { DemoDateRangePickerMaxDateRangeComponent } from './demos/max-date-range
import { DemoDateRangePickerDisplayOneMonth } from './demos/daterangepicker-display-one-month/display-one-month';
import { DemoDatepickerTodayButtonComponent } from './demos/today-button/today-button';
import { DemoDatepickerClearButtonComponent } from './demos/clear-button/clear-button';
import { DemoDatepickerApplyButtonComponent } from './demos/apply-button/apply-button';
import { DemoDatepickerStartViewComponent } from "./demos/start-view/start-view";
import { DemoDatepickerPreventChangeToNextMonthComponent } from './demos/prevent-change-to-next-month/prevent-change-to-next-month.component';
import { DemoDatepickerWithTimepickerComponent } from './demos/with-timepicker/with-timepicker';
import { DatepickerCloseBehaviorComponent } from './demos/closeBehaviour/datepicker-close-behavior';
import { KeepDatesOutOfRulesComponent } from './demos/keep-dates-out-of-rules/keep-dates-out-of-rules.component';


export const demoComponentContent: ContentSection[] = [
{
name: 'Overview',
Expand Down Expand Up @@ -438,6 +440,14 @@ export const demoComponentContent: ContentSection[] = [
description: `<p>Display an optional 'Clear' button that will automatically clear date.</p>`,
outlet: DemoDatepickerClearButtonComponent
},
{
title: 'Show Apply Button',
anchor: 'datepicker-show-apply-button',
component: require('!!raw-loader!./demos/apply-button/apply-button.ts'),
html: require('!!raw-loader!./demos/apply-button/apply-button.html'),
description: `<p>Display an 'Apply' and 'Cancel' button. The datepicker will not update the model unless the 'Apply' button is pressed. If the 'Cancel' button is pressed the model value will not be updated and the datepicker will be closed. The datepicker will remain open until it is applied, cancelled, or dismissed.</p>`,
outlet: DemoDatepickerApplyButtonComponent
},
{
title: 'Start view',
anchor: 'start-view',
Expand Down Expand Up @@ -712,6 +722,11 @@ export const demoComponentContent: ContentSection[] = [
anchor: 'datepicker-show-clear-button-ex',
outlet: DemoDatepickerClearButtonComponent
},
{
title: 'Show Apply Button',
anchor: 'datepicker-show-apply-button-ex',
outlet: DemoDatepickerApplyButtonComponent
},
{
title: 'Start view',
anchor: 'start-view-ex',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="row">
<div class="col-xs-12 col-12 col-md-4 form-group mb-3">
<input type="text"
placeholder="Datepicker"
class="form-control"
bsDatepicker
[bsConfig]="{ containerClass: 'theme-dark-blue', showApplyButton: true, showCancelButton: true }">
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-datepicker-apply-button',
templateUrl: './apply-button.html'
})
export class DemoDatepickerApplyButtonComponent {}
3 changes: 3 additions & 0 deletions libs/doc-pages/datepicker/src/lib/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ import { DemoDateRangePickerMaxDateRangeComponent } from './max-date-range/max-d
import { DemoDateRangePickerDisplayOneMonth } from './daterangepicker-display-one-month/display-one-month';
import { DemoDatepickerTodayButtonComponent } from './today-button/today-button';
import { DemoDatepickerClearButtonComponent } from './clear-button/clear-button';
import { DemoDatepickerApplyButtonComponent } from './apply-button/apply-button';
import { DemoDatepickerStartViewComponent } from "./start-view/start-view";
import { DemoDatepickerPreventChangeToNextMonthComponent } from './prevent-change-to-next-month/prevent-change-to-next-month.component';
import { DemoDatepickerWithTimepickerComponent } from './with-timepicker/with-timepicker';
import { DatepickerCloseBehaviorComponent } from './closeBehaviour/datepicker-close-behavior';
import { KeepDatesOutOfRulesComponent } from './keep-dates-out-of-rules/keep-dates-out-of-rules.component';


export const DEMO_COMPONENTS = [
DemoDatePickerAdaptivePositionComponent,
DemoDatePickerAnimatedComponent,
Expand Down Expand Up @@ -84,6 +86,7 @@ export const DEMO_COMPONENTS = [
DemoDatePickerVisibilityEventsComponent,
DemoDatepickerTodayButtonComponent,
DemoDatepickerClearButtonComponent,
DemoDatepickerApplyButtonComponent,
DemoDateRangePickerShowPreviousMonth,
DemoDateRangePickerMaxDateRangeComponent,
DemoDatepickerPreventChangeToNextMonthComponent,
Expand Down
13 changes: 12 additions & 1 deletion src/datepicker/base/bs-datepicker-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ export abstract class BsDatepickerAbstractComponent {
showClearBtn?: boolean;
clearBtnLbl?: string;
clearPos?: string;

showApplyBtn?: boolean;
showCancelBtn?: boolean;
cancelBtnLbl?: string;
applyBtnLbl?: string;
applyPos?: string;

_effects?: BsDatepickerEffects;
customRanges: BsCustomDates[] = [];
customRangeBtnLbl?: string;
Expand Down Expand Up @@ -130,6 +135,12 @@ export abstract class BsDatepickerAbstractComponent {
// eslint-disable-next-line
clearDate(): void {}

// eslint-disable-next-line
apply(): void {}

// eslint-disable-next-line
cancel(): void {}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
_stopPropagation(event: any): void {
event.stopPropagation();
Expand Down
59 changes: 48 additions & 11 deletions src/datepicker/bs-datepicker.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export class BsDatepickerDirective implements OnInit, OnDestroy, OnChanges, Afte
private _datepicker: ComponentLoader<BsDatepickerContainerComponent>;
private _datepickerRef?: ComponentRef<BsDatepickerContainerComponent>;
private readonly _dateInputFormat$ = new Subject<string | undefined>();
private _externalValue?: Date;
private _unappliedValue?: Date;

constructor(public _config: BsDatepickerConfig,
private _elementRef: ElementRef,
Expand Down Expand Up @@ -159,6 +161,7 @@ export class BsDatepickerDirective implements OnInit, OnDestroy, OnChanges, Afte

this.initPreviousValue();
this._bsValue = value;

this.bsValueChange.emit(value);
}

Expand Down Expand Up @@ -240,28 +243,62 @@ export class BsDatepickerDirective implements OnInit, OnDestroy, OnChanges, Afte
this._subs.push(
this.bsValueChange.subscribe((value: Date) => {
if (this._datepickerRef) {
this._externalValue = value;
this._datepickerRef.instance.value = value;
}
})
);

// if date changes from picker (view -> model)
if (this._datepickerRef) {
this._subs.push(
this._datepickerRef.instance.valueChange.subscribe((value: Date) => {
this.initPreviousValue();
this.bsValue = value;
if (this.keepDatepickerModalOpened()) {
return;
}

this.hide();
})
);

if(!this.bsConfig?.showApplyButton){
this._subs.push(
this._datepickerRef.instance.valueChange.subscribe((value: Date) => {
this.initPreviousValue();
this.bsValue = value;
if (this.keepDatepickerModalOpened()) {
return;
}

this.hide();
})
);
}

// if apply button is shown update unappliedValue
if(this.bsConfig?.showApplyButton){
this._subs.push(
this._datepickerRef.instance.valueChange.subscribe((value: Date) => {
this._unappliedValue = value;
})
);

// if apply button is pressed update external source (view -> model)
this._subs.push(
this._datepickerRef.instance.valueApplied.subscribe(() => {
this.bsValue = this._unappliedValue;

this.hide();
})
);

// if cancel is pressed reset picker value to external value
this._subs.push(
this._datepickerRef.instance.valueCancelled.subscribe(() => {
if (this._datepickerRef) {
this._datepickerRef.instance.value = this._externalValue;
}

this.hide();
})
);
}
}
}

keepDatepickerModalOpened(): boolean {

if (!previousDate || !this.bsConfig?.keepDatepickerOpened || !this._config.withTimepicker) {
return false;
}
Expand Down
42 changes: 42 additions & 0 deletions src/datepicker/bs-datepicker.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,54 @@ export class BsDatepickerConfig implements DatepickerRenderOptions {
isAnimated = false;
value?: Date | Date[];
isDisabled?: boolean;

/**
* Default min date for all date/range pickers
*/
minDate?: Date;

/**
* Default max date for all date/range pickers
*/
maxDate?: Date;

/**
* The view that the datepicker should start in
*/
startView: BsDatepickerViewMode = 'day';

/**
* Default date custom classes for all date/range pickers
*/
dateCustomClasses?: DatepickerDateCustomClasses[];

/**
* Default tooltip text for all date/range pickers
*/
dateTooltipTexts?: DatepickerDateTooltipText[];

/**
* Disable specific days, e.g. [0,6] will disable all Saturdays and Sundays
*/
daysDisabled?: number[];

/**
* Disable specific dates
*/
datesDisabled?: Date[];

/**
* Show one months for special cases (only for dateRangePicker)
* 1. maxDate is equal to today's date
* 2. minDate's month is equal to maxDate's month
*/
displayOneMonthRange?: boolean;

/**
* Enable specific dates
*/
datesEnabled?: Date[];

/**
* Makes dates from other months active
*/
Expand Down Expand Up @@ -109,14 +119,17 @@ export class BsDatepickerConfig implements DatepickerRenderOptions {

// DatepickerRenderOptions
displayMonths = 1;

/**
* Allows to hide week numbers in datepicker
*/
showWeekNumbers = true;

dateInputFormat = 'L';

// range picker
rangeSeparator = ' - ';

/**
* Date format for date range input field
*/
Expand Down Expand Up @@ -150,6 +163,16 @@ export class BsDatepickerConfig implements DatepickerRenderOptions {
*/
showClearButton = false;

/**
* Shows 'apply' and 'cancel' button, date is not sent from view to model until user presses apply. Picker remains opened until applied, cancelled or dismissed (clicked off of)
*/
showApplyButton = false;

/**
* Shows 'cancel' button this is only if showApplyButton is also true
*/
showCancelButton = true;

/**
* Positioning of 'today' button
*/
Expand All @@ -159,6 +182,11 @@ export class BsDatepickerConfig implements DatepickerRenderOptions {
* Positioning of 'clear' button
*/
clearPosition = 'right';

/**
* Positioning of 'apply' button
*/
applyPosition = 'right';

/**
* Label for 'today' button
Expand All @@ -170,6 +198,16 @@ export class BsDatepickerConfig implements DatepickerRenderOptions {
*/
clearButtonLabel = 'Clear';

/**
* Label for 'cancel' button
*/
cancelButtonLabel = 'Cancel';

/**
* Label for 'apply' button
*/
applyButtonLabel = 'Apply';

/**
* Label for 'custom range' button
*/
Expand All @@ -179,18 +217,22 @@ export class BsDatepickerConfig implements DatepickerRenderOptions {
* Shows timepicker under datepicker
*/
withTimepicker = false;

/**
* Set current hours, minutes, seconds and milliseconds for bsValue
*/
initCurrentTime?: boolean;

/**
* Set allowed positions of container.
*/
allowedPositions = ['top', 'bottom'];

/**
* Set rule for datepicker closing. If value is true datepicker closes only if date is changed, if user changes only time datepicker doesn't close. It is available only if property withTimepicker is set true
* */
keepDatepickerOpened = false;

/**
* Allows keep invalid dates in range. Can be used with minDate, maxDate
* */
Expand Down