@duoduo-oba/ng-devui
Version:
DevUI components based on Angular
583 lines (574 loc) • 18.2 kB
JavaScript
import { Directive, ElementRef, Input, HostListener, ContentChildren, Inject, forwardRef, HostBinding, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReplaySubject, Subject } from 'rxjs';
/**
* @fileoverview added by tsickle
* Generated from: anchor.directive.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class AnchorDirective {
/**
* @param {?} el
*/
constructor(el) {
this.el = el;
this.anchorActive = 'active';
this.activeChangeSubject = new ReplaySubject(1);
this.REACH_TOP_VISION_OFFSET = 50;
this.THROTTLE_DELAY = 100;
this.THROTTLE_TRIGGER = 600;
this.throttle = (/**
* @return {?}
*/
() => {
/** @type {?} */
const fn = this.checkActiveStatus;
/** @type {?} */
const time = Date.now();
if (this.scrollTimer) {
clearTimeout(this.scrollTimer);
}
if (!this.scrollPreStart) {
this.scrollPreStart = time;
}
if (time - this.scrollPreStart > this.THROTTLE_TRIGGER) {
fn();
this.scrollPreStart = null;
this.scrollTimer = null;
}
else {
this.scrollTimer = setTimeout((/**
* @return {?}
*/
() => {
fn();
this.scrollPreStart = null;
this.scrollTimer = null;
}), this.THROTTLE_DELAY);
}
});
this.checkActiveStatus = (/**
* @param {?=} activeChangeBy
* @return {?}
*/
(activeChangeBy) => {
/** @type {?} */
const top = this.element.getBoundingClientRect().top - (this.boxElement.view && this.boxElement.view.top || 0);
/** @type {?} */
const bottom = this.element.getBoundingClientRect().bottom - (this.boxElement.view && this.boxElement.view.top || 0);
// 首个个特殊处理
if (this.anchor === this.boxElement.defaultAnchor) {
this.activeChangeBy = activeChangeBy || 'scroll';
this.isActive = bottom > this.REACH_TOP_VISION_OFFSET ? true : false;
return;
}
// 默认处理
this.activeChangeBy = activeChangeBy || 'scroll';
this.isActive = bottom > this.REACH_TOP_VISION_OFFSET && top < this.REACH_TOP_VISION_OFFSET ? true : false;
});
this.element = this.el.nativeElement;
}
/**
* @param {?} active
* @return {?}
*/
set isActive(active) {
this._isActive = active;
this.activeChangeSubject.next(active);
}
/**
* @return {?}
*/
get isActive() {
return this._isActive;
}
/**
* @param {?} box
* @return {?}
*/
set boxElement(box) {
this._boxElement = box;
this.updateScrollListenTarget();
}
/**
* @return {?}
*/
get boxElement() { return this._boxElement; }
/**
* @return {?}
*/
ngAfterViewInit() {
this.activeChangeSubscription = this.activeChangeSubject.asObservable().subscribe((/**
* @param {?} active
* @return {?}
*/
active => {
if (this.lastActiveBy) {
this.element.classList.remove(this.lastActiveBy);
}
if (active) {
this.element.classList.add(this.anchorActive);
this.lastActiveBy = 'anchor-active-by-' + this.activeChangeBy;
// setTimeout是为了this.lastActiveBy每次都能被再次触发
setTimeout((/**
* @return {?}
*/
() => { this.element.classList.add(this.lastActiveBy); }), 0);
}
else {
this.element.classList.remove(this.anchorActive);
}
}));
setTimeout((/**
* @return {?}
*/
() => { this.checkActiveStatus('initial'); }));
}
/**
* @return {?}
*/
ngOnDestroy() {
this.scrollListenTarget.removeEventListener('scroll', this.throttle);
if (this.activeChangeSubscription) {
this.activeChangeSubscription.unsubscribe();
}
}
/**
* @return {?}
*/
beFocused() {
this.boxElement.forceActiveAnchor(this.anchor, 'click-inside');
}
/**
* @return {?}
*/
updateScrollListenTarget() {
if (this.scrollListenTarget) {
return;
}
if (this.boxElement) {
this.scrollListenTarget = this.boxElement.scrollTarget || window; // window有scroll事件,document.documentElement没有scroll事件
}
this.scrollListenTarget.addEventListener('scroll', this.throttle, { passive: true });
}
}
AnchorDirective.decorators = [
{ type: Directive, args: [{
selector: '[dAnchor]'
},] }
];
/** @nocollapse */
AnchorDirective.ctorParameters = () => [
{ type: ElementRef }
];
AnchorDirective.propDecorators = {
anchor: [{ type: Input, args: ['dAnchor',] }],
anchorActive: [{ type: Input }],
beFocused: [{ type: HostListener, args: ['click',] }]
};
if (false) {
/** @type {?} */
AnchorDirective.prototype.anchor;
/** @type {?} */
AnchorDirective.prototype.anchorActive;
/** @type {?} */
AnchorDirective.prototype._isActive;
/** @type {?} */
AnchorDirective.prototype.activeChangeBy;
/** @type {?} */
AnchorDirective.prototype.activeChangeSubscription;
/** @type {?} */
AnchorDirective.prototype.activeChangeSubject;
/** @type {?} */
AnchorDirective.prototype.lastActiveBy;
/** @type {?} */
AnchorDirective.prototype.element;
/** @type {?} */
AnchorDirective.prototype._boxElement;
/** @type {?} */
AnchorDirective.prototype.scrollListenTarget;
/** @type {?} */
AnchorDirective.prototype.REACH_TOP_VISION_OFFSET;
/**
* @type {?}
* @private
*/
AnchorDirective.prototype.THROTTLE_DELAY;
/**
* @type {?}
* @private
*/
AnchorDirective.prototype.THROTTLE_TRIGGER;
/**
* @type {?}
* @private
*/
AnchorDirective.prototype.scrollPreStart;
/**
* @type {?}
* @private
*/
AnchorDirective.prototype.scrollTimer;
/** @type {?} */
AnchorDirective.prototype.throttle;
/** @type {?} */
AnchorDirective.prototype.checkActiveStatus;
/**
* @type {?}
* @private
*/
AnchorDirective.prototype.el;
}
/**
* @fileoverview added by tsickle
* Generated from: anchor-box.directive.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class AnchorBoxDirective {
constructor() {
this.refreshAnchorMap = new Subject();
}
/**
* @param {?} list
* @return {?}
*/
set anchorList(list) {
this.anchorMap = {};
this._anchorList = list;
this._anchorList.toArray().forEach((/**
* @param {?} targetAnchor
* @return {?}
*/
targetAnchor => {
this.anchorMap[targetAnchor.anchor] = targetAnchor;
targetAnchor.boxElement = this;
}));
this.refreshAnchorMap.next();
}
/**
* @return {?}
*/
get anchorList() {
return this._anchorList;
}
/**
* @param {?} anchorName
* @param {?=} forceActiveSource
* @param {?=} deactivateOtherAnchor
* @return {?}
*/
forceActiveAnchor(anchorName, forceActiveSource = 'scroll', deactivateOtherAnchor = true) {
this.anchorMap[anchorName].activeChangeBy = forceActiveSource;
this.anchorMap[anchorName].isActive = true;
if (deactivateOtherAnchor) {
Object.keys(this.anchorMap)
.filter((/**
* @param {?} name
* @return {?}
*/
name => name !== anchorName))
.map((/**
* @param {?} name
* @return {?}
*/
name => this.anchorMap[name]))
.forEach((/**
* @param {?} anchor
* @return {?}
*/
anchor => {
anchor.activeChangeBy = forceActiveSource;
anchor.isActive = false;
}));
}
}
}
AnchorBoxDirective.decorators = [
{ type: Directive, args: [{
selector: '[dAnchorBox]'
},] }
];
/** @nocollapse */
AnchorBoxDirective.ctorParameters = () => [];
AnchorBoxDirective.propDecorators = {
view: [{ type: Input }],
defaultAnchor: [{ type: Input }],
scrollTarget: [{ type: Input }],
anchorList: [{ type: ContentChildren, args: [AnchorDirective, { descendants: true },] }]
};
if (false) {
/** @type {?} */
AnchorBoxDirective.prototype.view;
/** @type {?} */
AnchorBoxDirective.prototype.defaultAnchor;
/** @type {?} */
AnchorBoxDirective.prototype.scrollTarget;
/** @type {?} */
AnchorBoxDirective.prototype.refreshAnchorMap;
/** @type {?} */
AnchorBoxDirective.prototype.anchorMap;
/** @type {?} */
AnchorBoxDirective.prototype._anchorList;
}
/**
* @fileoverview added by tsickle
* Generated from: anchor-link.directive.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class AnchorLinkDirective {
/**
* @param {?} box
*/
constructor(box) {
this.bindAnchorAfterBoxReady = (/**
* @return {?}
*/
() => {
if (this.boxElement.anchorMap) {
setTimeout((/**
* @return {?}
*/
() => { this.anchorBlock = this.boxElement.anchorMap[this.anchorName]; }), 0);
}
else {
this.bindingAnchorTimer = setTimeout(this.bindAnchorAfterBoxReady, 500);
}
});
this.boxElement = box;
}
/**
* @return {?}
*/
get anchorActiveClass() {
return (this.anchorBlock && this.anchorBlock.isActive) ? (this.anchorActive || '') : '';
}
/**
* @param {?} anchor
* @return {?}
*/
set anchorName(anchor) {
this._anchorName = anchor;
this.bindAnchorAfterBoxReady();
}
/**
* @return {?}
*/
get anchorName() { return this._anchorName; }
/**
* @return {?}
*/
ngOnInit() {
this.subscribeAnchorMapChange();
}
/**
* @return {?}
*/
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
/**
* @return {?}
*/
subscribeAnchorMapChange() {
if (this.boxElement) {
this.subscription = this.boxElement.refreshAnchorMap.subscribe((/**
* @return {?}
*/
() => {
if (this.bindingAnchorTimer) {
clearTimeout(this.bindingAnchorTimer);
this.bindingAnchorTimer = undefined;
}
this.bindAnchorAfterBoxReady();
}));
}
}
/**
* @return {?}
*/
scrollToAnchor() {
if (!this.anchorBlock) {
return;
}
/** @type {?} */
const callback = (/**
* @return {?}
*/
() => {
setTimeout((/**
* @return {?}
*/
() => { this.boxElement.forceActiveAnchor(this.anchorName, 'anchor-link'); }), 120);
});
((/**
* @param {?} container
* @param {?} anchor
* @return {?}
*/
(container, anchor) => {
/** @type {?} */
let containerScrollTop = container.scrollTop;
if (container === document.documentElement) {
containerScrollTop += document.body.scrollTop;
}
this.scrollAnimate(container, containerScrollTop, (containerScrollTop + anchor.getBoundingClientRect().top - (this.boxElement.view && this.boxElement.view.top || 0)), undefined, undefined, callback);
}))(this.boxElement.scrollTarget || document.documentElement, this.anchorBlock.element);
}
/**
* @param {?} target
* @param {?} currentTopValue
* @param {?} targetTopValue
* @param {?=} timeGap
* @param {?=} scrollTime
* @param {?=} callback
* @return {?}
*/
scrollAnimate(target, currentTopValue, targetTopValue, timeGap = 40, scrollTime = 450, callback) {
/** @type {?} */
const startTimeStamp = Date.now();
/** @type {?} */
const drawAnimateFrame = (/**
* @return {?}
*/
() => {
/** @type {?} */
const currentTime = Date.now() - startTimeStamp;
if (currentTime - timeGap > scrollTime) {
target.scrollTop = targetTopValue;
if (target === document.documentElement) {
// 兼容写法,老浏览器/老API模式需要document.body滚动,新的需要documentElement滚动
document.body.scrollTop = targetTopValue;
}
if (callback) {
callback();
}
}
else {
/** @type {?} */
const tempTopValue = this.easeInOutCubic(currentTime, currentTopValue, targetTopValue, scrollTime);
target.scrollTop = tempTopValue;
if (target === document.documentElement) {
document.body.scrollTop = tempTopValue;
}
setTimeout((/**
* @return {?}
*/
() => { requestAnimationFrame(drawAnimateFrame); }), timeGap);
}
});
requestAnimationFrame(drawAnimateFrame);
}
/**
* @param {?} t
* @param {?} b
* @param {?} c
* @param {?} d
* @return {?}
*/
easeInOutCubic(t, b, c, d) {
/** @type {?} */
const cc = c - b;
/** @type {?} */
let tt = t / (d / 2);
if (tt < 1) {
return cc / 2 * tt * tt * tt + b;
}
else {
return cc / 2 * ((tt -= 2) * tt * tt + 2) + b;
}
}
}
AnchorLinkDirective.decorators = [
{ type: Directive, args: [{
selector: '[dAnchorLink]',
},] }
];
/** @nocollapse */
AnchorLinkDirective.ctorParameters = () => [
{ type: AnchorBoxDirective, decorators: [{ type: Inject, args: [forwardRef((/**
* @return {?}
*/
() => AnchorBoxDirective)),] }] }
];
AnchorLinkDirective.propDecorators = {
anchorActiveClass: [{ type: HostBinding, args: ['class',] }],
anchorName: [{ type: Input, args: ['dAnchorLink',] }],
anchorActive: [{ type: Input }],
scrollToAnchor: [{ type: HostListener, args: ['click',] }]
};
if (false) {
/**
* @type {?}
* @private
*/
AnchorLinkDirective.prototype._anchorName;
/** @type {?} */
AnchorLinkDirective.prototype.anchorActive;
/** @type {?} */
AnchorLinkDirective.prototype.boxElement;
/** @type {?} */
AnchorLinkDirective.prototype.anchorBlock;
/** @type {?} */
AnchorLinkDirective.prototype.bindingAnchorTimer;
/** @type {?} */
AnchorLinkDirective.prototype.subscription;
/** @type {?} */
AnchorLinkDirective.prototype.bindAnchorAfterBoxReady;
}
/**
* @fileoverview added by tsickle
* Generated from: anchor.module.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class AnchorModule {
}
AnchorModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule
],
declarations: [
AnchorLinkDirective,
AnchorBoxDirective,
AnchorDirective
],
exports: [
AnchorLinkDirective,
AnchorBoxDirective,
AnchorDirective
],
},] }
];
/**
* @fileoverview added by tsickle
* Generated from: anchor.type.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @record
*/
function IAnchorBox() { }
if (false) {
/** @type {?} */
IAnchorBox.prototype.scrollTarget;
/** @type {?} */
IAnchorBox.prototype.defaultAnchor;
/** @type {?} */
IAnchorBox.prototype.forceActiveAnchor;
/** @type {?} */
IAnchorBox.prototype.view;
}
/**
* @fileoverview added by tsickle
* Generated from: public-api.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* Generated from: ng-devui-anchor.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { AnchorBoxDirective, AnchorDirective, AnchorLinkDirective, AnchorModule };
//# sourceMappingURL=ng-devui-anchor.js.map