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(module:date-picker): support date-picker placement #7527

Merged
merged 1 commit into from Jul 11, 2022
Merged
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
34 changes: 34 additions & 0 deletions components/core/overlay/overlay-position.ts
Expand Up @@ -72,3 +72,37 @@ export function getPlacementName(position: ConnectedOverlayPositionChange): stri
}
return undefined;
}

export const DATE_PICKER_POSITION_MAP = {
bottomLeft: new ConnectionPositionPair(
{ originX: 'start', originY: 'bottom' },
{ overlayX: 'start', overlayY: 'top' },
undefined,
2
),
topLeft: new ConnectionPositionPair(
{ originX: 'start', originY: 'top' },
{ overlayX: 'start', overlayY: 'bottom' },
undefined,
-2
),
bottomRight: new ConnectionPositionPair(
{ originX: 'end', originY: 'bottom' },
{ overlayX: 'end', overlayY: 'top' },
undefined,
2
),
topRight: new ConnectionPositionPair(
{ originX: 'end', originY: 'top' },
{ overlayX: 'end', overlayY: 'bottom' },
undefined,
-2
)
};

export const DEFAULT_DATE_PICKER_POSITIONS = [
DATE_PICKER_POSITION_MAP.bottomLeft,
DATE_PICKER_POSITION_MAP.topLeft,
DATE_PICKER_POSITION_MAP.bottomRight,
DATE_PICKER_POSITION_MAP.topRight
];
48 changes: 48 additions & 0 deletions components/date-picker/date-picker.component.spec.ts
Expand Up @@ -514,6 +514,52 @@ describe('NzDatePickerComponent', () => {
openPickerByClickTrigger();
expect(overlayContainerElement.children[0].classList).toContain('cdk-overlay-backdrop');
}));
it('should support nzPlacement', fakeAsync(() => {
fixtureInstance.nzPlacement = 'bottomLeft';
fixture.detectChanges();
openPickerByClickTrigger();
let element = queryFromOverlay('.ant-picker-dropdown');
expect(element.classList.contains('ant-picker-dropdown-placement-bottomLeft')).toBe(true);
expect(element.classList.contains('ant-picker-dropdown-placement-topLeft')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-bottomRight')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-topRight')).toBe(false);
triggerInputBlur();
fixture.detectChanges();
tick(500);
fixture.detectChanges();
fixtureInstance.nzPlacement = 'topLeft';
fixture.detectChanges();
openPickerByClickTrigger();
element = queryFromOverlay('.ant-picker-dropdown');
expect(element.classList.contains('ant-picker-dropdown-placement-bottomLeft')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-topLeft')).toBe(true);
expect(element.classList.contains('ant-picker-dropdown-placement-bottomRight')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-topRight')).toBe(false);
triggerInputBlur();
fixture.detectChanges();
tick(500);
fixture.detectChanges();
fixtureInstance.nzPlacement = 'bottomRight';
fixture.detectChanges();
openPickerByClickTrigger();
element = queryFromOverlay('.ant-picker-dropdown');
expect(element.classList.contains('ant-picker-dropdown-placement-bottomLeft')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-topLeft')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-bottomRight')).toBe(true);
expect(element.classList.contains('ant-picker-dropdown-placement-topRight')).toBe(false);
triggerInputBlur();
fixture.detectChanges();
tick(500);
fixture.detectChanges();
fixtureInstance.nzPlacement = 'topRight';
fixture.detectChanges();
openPickerByClickTrigger();
element = queryFromOverlay('.ant-picker-dropdown');
expect(element.classList.contains('ant-picker-dropdown-placement-bottomLeft')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-topLeft')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-bottomRight')).toBe(false);
expect(element.classList.contains('ant-picker-dropdown-placement-topRight')).toBe(true);
}));
});

describe('panel switch and move forward/afterward', () => {
Expand Down Expand Up @@ -1289,6 +1335,7 @@ describe('in form', () => {
[nzBorderless]="nzBorderless"
[nzInline]="nzInline"
[nzBackdrop]="nzBackdrop"
[nzPlacement]="nzPlacement"
></nz-date-picker>
<ng-template #tplDateRender let-current>
<div [class.test-first-day]="current.getDate() === 1">{{ current.getDate() }}</div>
Expand Down Expand Up @@ -1348,6 +1395,7 @@ class NzTestDatePickerComponent {
nzBorderless = false;
nzInline = false;
nzBackdrop = false;
nzPlacement = 'bottomLeft';

// nzRanges;
nzOnPanelChange(_: string): void {}
Expand Down
47 changes: 16 additions & 31 deletions components/date-picker/date-picker.component.ts
Expand Up @@ -48,6 +48,7 @@ import { slideMotion } from 'ng-zorro-antd/core/animation';
import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';
import { NzFormNoStatusService, NzFormStatusService } from 'ng-zorro-antd/core/form';
import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation';
import { DEFAULT_DATE_PICKER_POSITIONS, DATE_PICKER_POSITION_MAP } from 'ng-zorro-antd/core/overlay';
import { CandyDate, cloneDate, CompatibleValue, wrongSortOrder } from 'ng-zorro-antd/core/time';
import {
BooleanInput,
Expand Down Expand Up @@ -83,6 +84,7 @@ const POPUP_STYLE_PATCH = { position: 'relative' }; // Aim to override antd's st
const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'datePicker';

export type NzDatePickerSizeType = 'large' | 'default' | 'small';
export type NzPlacement = 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight';

/**
* The base picker for all common APIs
Expand Down Expand Up @@ -303,6 +305,7 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Afte
@Input() @WithConfig() nzSuffixIcon: string | TemplateRef<NzSafeAny> = 'calendar';
@Input() @WithConfig() nzBackdrop = false;
@Input() nzId: string | null = null;
@Input() nzPlacement: NzPlacement = 'bottomLeft';

// TODO(@wenqi73) The PanelMode need named for each pickers and export
@Output() readonly nzOnPanelChange = new EventEmitter<NzDateMode | NzDateMode[] | string | string[]>();
Expand Down Expand Up @@ -335,36 +338,7 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Afte
inputValue!: NzSafeAny;
activeBarStyle: object = {};
overlayOpen: boolean = false; // Available when "nzOpen" = undefined
overlayPositions: ConnectionPositionPair[] = [
{
offsetY: 2,
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top'
},
{
offsetY: -2,
originX: 'start',
originY: 'top',
overlayX: 'start',
overlayY: 'bottom'
},
{
offsetY: 2,
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top'
},
{
offsetY: -2,
originX: 'end',
originY: 'top',
overlayX: 'end',
overlayY: 'bottom'
}
] as ConnectionPositionPair[];
overlayPositions: ConnectionPositionPair[] = [...DEFAULT_DATE_PICKER_POSITIONS];
currentPositionX: HorizontalConnectionPos = 'start';
currentPositionY: VerticalConnectionPos = 'bottom';

Expand Down Expand Up @@ -680,7 +654,7 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Afte
}

ngOnChanges(changes: SimpleChanges): void {
const { nzStatus } = changes;
const { nzStatus, nzPlacement } = changes;
if (changes.nzPopupStyle) {
// Always assign the popup style patch
this.nzPopupStyle = this.nzPopupStyle ? { ...this.nzPopupStyle, ...POPUP_STYLE_PATCH } : POPUP_STYLE_PATCH;
Expand Down Expand Up @@ -712,6 +686,10 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Afte
if (nzStatus) {
this.setStatusStyles(this.nzStatus, this.hasFeedback);
}

if (nzPlacement) {
this.setPlacement(this.nzPlacement);
}
}

ngOnDestroy(): void {
Expand Down Expand Up @@ -877,4 +855,11 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Afte
}
});
}

private setPlacement(placement: NzPlacement): void {
const position: ConnectionPositionPair = DATE_PICKER_POSITION_MAP[placement];
this.overlayPositions = [position, ...DEFAULT_DATE_PICKER_POSITIONS];
this.currentPositionX = position.originX;
this.currentPositionY = position.originY;
}
}
14 changes: 14 additions & 0 deletions components/date-picker/demo/placement.md
@@ -0,0 +1,14 @@
---
order: 6
title:
zh-CN: 自定义位置
en-US: Placement
---

## zh-CN

可以通过 `nzPlacement` 手动指定弹出的位置。

## en-US

You can manually specify the position of the popup via `nzPlacement`.
29 changes: 29 additions & 0 deletions components/date-picker/demo/placement.ts
@@ -0,0 +1,29 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-date-picker-placement',
template: `
<nz-radio-group [(ngModel)]="placement">
<label nz-radio-button nzValue="bottomLeft">bottomLeft</label>
<label nz-radio-button nzValue="bottomRight">bottomRight</label>
<label nz-radio-button nzValue="topLeft">topLeft</label>
<label nz-radio-button nzValue="topRight">topRight</label>
</nz-radio-group>
<br />
<br />
<nz-date-picker [nzPlacement]="placement"></nz-date-picker>
<br />
<nz-range-picker [nzPlacement]="placement"></nz-range-picker>
`,
styles: [
`
nz-date-picker,
nz-range-picker {
margin: 0 8px 12px 0;
}
`
]
})
export class NzDemoDatePickerPlacementComponent {
placement: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight' = 'bottomLeft';
}
1 change: 1 addition & 0 deletions components/date-picker/doc/index.en-US.md
Expand Up @@ -51,6 +51,7 @@ The following APIs are shared by nz-date-picker, nz-range-picker.
| `[nzRenderExtraFooter]` | render extra footer in panel | `TemplateRef \| string \| (() => TemplateRef \| string)` | - |
| `[nzSize]` | determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `'large' \| 'small'` | - | - |
| `[nzStatus]` | Set validation status | `'error' \| 'warning'` | - |
| `[nzPlacement]` | The position where the selection box pops up | `'bottomLeft' \| 'bottomRight' \| 'topLeft' \| 'topRight'` | `'bottomLeft'` | |
| `[nzSuffixIcon]` | the custom suffix icon | `string` \| `TemplateRef` | - | ✅ |
| `[nzBorderless]` | remove the border | `boolean` | `false` | - |
| `[nzInline]` | inline mode | `boolean` | `false` | - |
Expand Down
1 change: 1 addition & 0 deletions components/date-picker/doc/index.zh-CN.md
Expand Up @@ -51,6 +51,7 @@ registerLocaleData(zh);
| `[nzRenderExtraFooter]` | 在面板中添加额外的页脚 | `TemplateRef \| string \| (() => TemplateRef \| string)` | - |
| `[nzSize]` | 输入框大小,`large` 高度为 40px,`small` 为 24px,默认是 32px | `'large' \| 'small'` | - | - |
| `[nzStatus]` | 设置校验状态 | `'error' \| 'warning'` | - |
| `[nzPlacement]` | 选择框弹出的位置 | `'bottomLeft' \| 'bottomRight' \| 'topLeft' \| 'topRight'` | `'bottomLeft'` | |
| `[nzSuffixIcon]` | 自定义的后缀图标 | `string` \| `TemplateRef` | - | ✅ |
| `[nzBorderless]` | 移除边框 | `boolean` | `false` | - |
| `[nzInline]` | 内联模式 | `boolean` | `false` | - |
Expand Down