Skip to content

Commit

Permalink
feat(module:slider): support nzTooltipVisible
Browse files Browse the repository at this point in the history
close #2373
  • Loading branch information
Wendell committed Jan 21, 2019
1 parent a2000fa commit 8155fcd
Show file tree
Hide file tree
Showing 26 changed files with 600 additions and 613 deletions.
11 changes: 0 additions & 11 deletions components/core/dom/reverse.ts

This file was deleted.

3 changes: 0 additions & 3 deletions components/core/style/map.ts

This file was deleted.

4 changes: 4 additions & 0 deletions components/core/util/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ export function arrayEquals<T>(array1: T[], array2: T[]): boolean {
}
return true;
}

export function shallowCopyArray<T>(source: T[]): T[] {
return source.slice();
}
4 changes: 3 additions & 1 deletion components/core/util/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ export function isNotNil(value: any): boolean {
return (typeof(value) !== 'undefined') && value !== null;
}

/** 校验对象是否相等 */
/**
* Examine if two objects are shallowly equaled.
*/
export function shallowEqual(objA: {}, objB: {}): boolean {
if (objA === objB) {
return true;
Expand Down
50 changes: 49 additions & 1 deletion components/core/util/dom.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
import { Observable } from 'rxjs';

import { filterNotEmptyNode } from './check';

/**
* Silent an event by stopping and preventing it.
*/
export function silentEvent(e: Event): void {
e.stopPropagation();
e.preventDefault();
}

export function getElementOffset(elem: HTMLElement): { top: number, left: number } {
if (!elem.getClientRects().length) {
return { top: 0, left: 0 };
}

const rect = elem.getBoundingClientRect();
const win = elem.ownerDocument.defaultView;
return {
top : rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
};
}

export function findFirstNotEmptyNode(element: HTMLElement): Node {
const children = element.childNodes;
for (let i = 0; i < children.length; i++) {
Expand All @@ -20,4 +43,29 @@ export function findLastNotEmptyNode(element: HTMLElement): Node {
}
}
return null;
}
}

export function reverseChildNodes(parent: HTMLElement): void {
const children = parent.childNodes;
let length = children.length;
if (length) {
const nodes: Node[] = [];
children.forEach((node, i) => nodes[ i ] = node);
while (length--) {
parent.appendChild(nodes[ length ]);
}
}
}

export interface MouseTouchObserverConfig {
end: string;
move: string;
pluckKey: string[];
start: string;

end$?: Observable<Event>;
moveResolved$?: Observable<number>;
startPlucked$?: Observable<number>;

filter?(e: Event): boolean;
}
1 change: 0 additions & 1 deletion components/core/util/getMentions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

export function getRegExp(prefix: string | string[]): RegExp {
const prefixArray = Array.isArray(prefix) ? prefix : [prefix];
let prefixToken = prefixArray.join('').replace(/(\$|\^)/g, '\\$1');
Expand Down
19 changes: 19 additions & 0 deletions components/core/util/number.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function getPercent(min: number, max: number, value: number): number {
return (value - min) / (max - min) * 100;
}

export function getPrecision(num: number): number {
const numStr = num.toString();
const dotIndex = numStr.indexOf('.');
return dotIndex >= 0 ? numStr.length - dotIndex - 1 : 0;
}

export function ensureNumberInRange(num: number, min: number, max: number): number {
if (isNaN(num) || num < min) {
return min;
} else if (num > max) {
return max;
} else {
return num;
}
}
16 changes: 16 additions & 0 deletions components/slider/demo/tooltip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
order: 7
title:
zh-CN: 控制 Tooltip 的显示
en-US: Control visibility of Tooltip
---

## zh-CN

`nzTooltipVisible``always` 时,将始终显示 ToolTip,为 `never` 时反之则始终不显示,即使在拖动、移入时也是如此。

## en-US

When `nzTooltipVisible` is `always`, Tooltip will show always. And set to `never`, tooltip would never show, even when user is dragging or hovering.


11 changes: 11 additions & 0 deletions components/slider/demo/tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-slider-tooltip',
template: `
<nz-slider [nzTooltipVisible]="'always'"></nz-slider>
<nz-slider [nzTooltipVisible]="'never'"></nz-slider>
`
})
export class NzDemoSliderTooltipComponent {
}
60 changes: 60 additions & 0 deletions components/slider/nz-slider-definitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export type Mark = string | MarkObj;

export type MarkObj = {
style?: object;
label: string;
}

export class Marks {
[ key: number ]: Mark;
}

/**
* Marks that have been processed.
*/
export interface ExtendedMark {
value: number;
offset: number;
config: Mark;
}

export interface DisplayedMark extends ExtendedMark {
active: boolean;
label: string;
style?: object;
}

export class MarksArray extends Array<{ value: number, offset: number, config: Mark }> {
[ index: number ]: {
value: number;
offset: number;
config: Mark;
}
}

export interface DisplayedStep extends ExtendedMark {
active: boolean;
style?: object;
}

export type NzSliderShowTooltip = 'always' | 'never' | 'default';

export type SliderValue = number[] | number;

export class SliderHandler {
offset: number;
value: number;
active: boolean;
}

export function isValueARange(value: SliderValue): value is number[] {
if (value instanceof Array) {
return value.length === 2;
} else {
return false;
}
}

export function isConfigAObject(config: Mark): config is MarkObj {
return config instanceof Object;
}
7 changes: 7 additions & 0 deletions components/slider/nz-slider-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function getValueTypeNotMatchError(): Error {
return new Error(`The "nzRange" can't match the "nzValue"'s type, please check these properties: "nzRange", "nzValue", "nzDefaultValue".`);
}

export function getValueTypeNotLegalError(): Error {
return new Error(``);
}
10 changes: 7 additions & 3 deletions components/slider/nz-slider-handle.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<nz-tooltip *ngIf="nzTipFormatter !== null" #tooltip [nzTitle]="tooltipTitle" [nzTrigger]="null">
<div nz-tooltip [class]="nzClassName" [ngStyle]="style"></div>
<nz-tooltip
*ngIf="nzTipFormatter !== null && nzTooltipVisible !== 'never'"
#tooltip
[nzTitle]="tooltipTitle"
[nzTrigger]="null">
<div nz-tooltip class="ant-slider-handle" [ngStyle]="style"></div>
</nz-tooltip>
<div *ngIf="nzTipFormatter === null" [class]="nzClassName" [ngStyle]="style"></div>
<div *ngIf="nzTipFormatter === null || nzTooltipVisible === 'never'" class="ant-slider-handle" [ngStyle]="style"></div>
126 changes: 87 additions & 39 deletions components/slider/nz-slider-handle.component.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,126 @@
import { Component, HostListener, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
import {
AfterViewInit,
ChangeDetectionStrategy, ChangeDetectorRef,
Component,
ElementRef,
Input,
NgZone,
OnChanges,
OnDestroy,
SimpleChanges,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';

import { toBoolean } from '../core/util/convert';
import { InputBoolean } from '../core/util/convert';
import { NzToolTipComponent } from '../tooltip/nz-tooltip.component';

import { NzSliderShowTooltip } from './nz-slider-definitions';
import { NzSliderComponent } from './nz-slider.component';

@Component({
changeDetection : ChangeDetectionStrategy.OnPush,
encapsulation : ViewEncapsulation.None,
selector : 'nz-slider-handle',
preserveWhitespaces: false,
templateUrl : './nz-slider-handle.component.html'
})
export class NzSliderHandleComponent implements OnChanges {
export class NzSliderHandleComponent implements OnChanges, AfterViewInit, OnDestroy {
@ViewChild('tooltip') tooltip: NzToolTipComponent;

// Static properties
@Input() nzClassName: string;
@Input() nzVertical: string;
@Input() nzOffset: number;
@Input() nzValue: number; // [For tooltip]
@Input() nzTipFormatter: (value: number) => string; // [For tooltip]
@Input() set nzActive(value: boolean) { // [For tooltip]
const show = toBoolean(value);
if (this.tooltip) {
if (show) {
this.tooltip.show();
} else {
this.tooltip.hide();
}
}
}
@Input() nzValue: number;
@Input() nzTooltipVisible: NzSliderShowTooltip = 'default';
@Input() nzTipFormatter: (value: number) => string;
@Input() @InputBoolean() nzActive = false;

// Locals
@ViewChild('tooltip') tooltip: NzToolTipComponent; // [For tooltip]
tooltipTitle: string; // [For tooltip]
tooltipTitle: string;
style: object = {};

constructor(private _slider: NzSliderComponent) {
private hovers_ = new Subscription();

constructor(
private sliderComponent: NzSliderComponent,
private ngZone: NgZone,
private el: ElementRef,
private cdr: ChangeDetectorRef
) {
}

ngOnChanges(changes: SimpleChanges): void {
if (changes.nzOffset) {
this._updateStyle();
const { nzOffset, nzValue, nzActive, nzTooltipVisible } = changes;

if (nzOffset) {
this.updateStyle();
}
if (nzValue) {
this.updateTooltipTitle();
this.updateTooltipPosition();
}
if (changes.nzValue) {
this._updateTooltipTitle(); // [For tooltip]
this._updateTooltipPosition(); // [For tooltip]
if (nzActive) {
if (nzActive.currentValue) {
this.toggleTooltip(true);
} else {
this.toggleTooltip(false);
}
}
if (nzTooltipVisible && !nzTooltipVisible.isFirstChange()) {
this.tooltip.show();
}
}

// Hover to toggle tooltip when not dragging
@HostListener('mouseenter', [ '$event' ])
onMouseEnter($event: MouseEvent): void {
if (!this._slider.isDragging) {
this.nzActive = true;
ngAfterViewInit(): void {
if (this.nzTooltipVisible === 'always' && this.tooltip) {
Promise.resolve().then(() => this.tooltip.show());
}

// NOTE: run outside of Angular for performance consideration.
this.ngZone.runOutsideAngular(() => {
this.hovers_.add(fromEvent(this.el.nativeElement, 'mouseenter').subscribe(() => {
if (!this.sliderComponent.isDragging) {
this.toggleTooltip(true);
this.updateTooltipPosition();
this.cdr.detectChanges();
}
}));
this.hovers_.add(fromEvent(this.el.nativeElement, 'mouseleave').subscribe(() => {
if (!this.sliderComponent.isDragging) {
this.toggleTooltip(false);
this.cdr.detectChanges();
}
}));
});
}

@HostListener('mouseleave', [ '$event' ])
onMouseLeave($event: MouseEvent): void {
if (!this._slider.isDragging) {
this.nzActive = false;
ngOnDestroy(): void {
this.hovers_.unsubscribe();
}

private toggleTooltip(show: boolean): void {
if (this.nzTooltipVisible !== 'default' || !this.tooltip) {
return;
}

if (show) {
this.tooltip.show();
} else {
this.tooltip.hide();
}
}

private _updateTooltipTitle(): void { // [For tooltip]
private updateTooltipTitle(): void {
this.tooltipTitle = this.nzTipFormatter ? this.nzTipFormatter(this.nzValue) : `${this.nzValue}`;
}

private _updateTooltipPosition(): void { // [For tooltip]
private updateTooltipPosition(): void {
if (this.tooltip) {
window.setTimeout(() => this.tooltip.updatePosition(), 0); // MAY use ngAfterViewChecked? but this will be called so many times.
Promise.resolve().then(() => this.tooltip.updatePosition());
}
}

private _updateStyle(): void {
private updateStyle(): void {
this.style[ this.nzVertical ? 'bottom' : 'left' ] = `${this.nzOffset}%`;
}
}
Loading

0 comments on commit 8155fcd

Please sign in to comment.