commit
fa02cff8aa
|
@ -1,106 +1,106 @@
|
||||||
import { ComputedRef, Ref } from 'vue';
|
import { ComputedRef, Ref } from 'vue';
|
||||||
|
|
||||||
export interface UseButton {
|
export interface UseButton {
|
||||||
/**
|
/**
|
||||||
* 附加按钮的Class
|
* 附加按钮的Class
|
||||||
*/
|
*/
|
||||||
buttonClass: ComputedRef<Record<string, boolean | undefined>>;
|
buttonClass: ComputedRef<Record<string, boolean | undefined>>;
|
||||||
/**
|
/**
|
||||||
* 点击附加按钮事件响应函数
|
* 点击附加按钮事件响应函数
|
||||||
*/
|
*/
|
||||||
onClickButton: ($event: Event) => void;
|
onClickButton: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标移入附加按钮事件响应函数
|
* 鼠标移入附加按钮事件响应函数
|
||||||
*/
|
*/
|
||||||
onMouseEnterButton: ($event: MouseEvent) => void;
|
onMouseEnterButton: ($event: MouseEvent) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标移出附加按钮事件响应函数
|
* 鼠标移出附加按钮事件响应函数
|
||||||
*/
|
*/
|
||||||
onMouseLeaveButton: ($event: MouseEvent) => void;
|
onMouseLeaveButton: ($event: MouseEvent) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标滑过附加按钮事件响应函数
|
* 鼠标滑过附加按钮事件响应函数
|
||||||
*/
|
*/
|
||||||
onMouseOverButton: () => void;
|
onMouseOverButton: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UseClear {
|
export interface UseClear {
|
||||||
/**
|
/**
|
||||||
* 启用清空按钮
|
* 启用清空按钮
|
||||||
*/
|
*/
|
||||||
enableClearButton: ComputedRef<boolean>;
|
enableClearButton: ComputedRef<boolean>;
|
||||||
/**
|
/**
|
||||||
* 显示清空按钮
|
* 显示清空按钮
|
||||||
*/
|
*/
|
||||||
showClearButton: Ref<boolean>;
|
showClearButton: Ref<boolean>;
|
||||||
/**
|
/**
|
||||||
* 清空输入框值事件响应函数
|
* 清空输入框值事件响应函数
|
||||||
*/
|
*/
|
||||||
onClearValue: ($event: Event) => void;
|
onClearValue: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标进入输入框事件响应函数
|
* 鼠标进入输入框事件响应函数
|
||||||
*/
|
*/
|
||||||
onMouseEnterTextBox: ($event: MouseEvent) => void;
|
onMouseEnterTextBox: ($event: MouseEvent) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标移出输入框事件响应函数
|
* 鼠标移出输入框事件响应函数
|
||||||
*/
|
*/
|
||||||
onMouseLeaveTextBox: ($event: MouseEvent) => void;
|
onMouseLeaveTextBox: ($event: MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UseTextBox {
|
export interface UseTextBox {
|
||||||
/**
|
/**
|
||||||
* 输入框是否获得焦点
|
* 输入框是否获得焦点
|
||||||
*/
|
*/
|
||||||
hasFocusedTextBox: ComputedRef<boolean>;
|
hasFocusedTextBox: ComputedRef<boolean>;
|
||||||
/**
|
/**
|
||||||
* 输入框是否处于只读状态
|
* 输入框是否处于只读状态
|
||||||
*/
|
*/
|
||||||
isTextBoxReadonly: ComputedRef<boolean>;
|
isTextBoxReadonly: ComputedRef<boolean>;
|
||||||
/**
|
/**
|
||||||
* 输入框Class
|
* 输入框Class
|
||||||
*/
|
*/
|
||||||
textBoxClass: ComputedRef<Record<string, boolean | undefined>>;
|
textBoxClass: ComputedRef<Record<string, boolean | undefined>>;
|
||||||
/**
|
/**
|
||||||
* 输入框提示语
|
* 输入框提示语
|
||||||
*/
|
*/
|
||||||
textBoxPlaceholder: ComputedRef<string>;
|
textBoxPlaceholder: ComputedRef<string>;
|
||||||
/**
|
/**
|
||||||
* 输入框提示标签
|
* 输入框提示标签
|
||||||
*/
|
*/
|
||||||
textBoxTitle: ComputedRef<string>;
|
textBoxTitle: ComputedRef<string>;
|
||||||
/**
|
/**
|
||||||
* 更新输入框的值并触发change事件
|
* 更新输入框的值并触发change事件
|
||||||
*/
|
*/
|
||||||
changeTextBoxValue: (newValue: string, showEmitChangeEmit: boolean) => void;
|
changeTextBoxValue: (newValue: string, showEmitChangeEmit: boolean) => void;
|
||||||
/**
|
/**
|
||||||
* 输入框失去焦点事件响应函数
|
* 输入框失去焦点事件响应函数
|
||||||
*/
|
*/
|
||||||
onBlurTextBox: ($event: Event) => void;
|
onBlurTextBox: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标点击输入框事件响应函数
|
* 鼠标点击输入框事件响应函数
|
||||||
*/
|
*/
|
||||||
onClickTextBox: ($event: Event) => void;
|
onClickTextBox: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 输入框获得焦点事件响应函数
|
* 输入框获得焦点事件响应函数
|
||||||
*/
|
*/
|
||||||
onFocusTextBox: ($event: Event) => void;
|
onFocusTextBox: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 输入框值变化事件
|
* 输入框值变化事件
|
||||||
*/
|
*/
|
||||||
onInput: ($event: Event) => void;
|
onInput: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 鼠标点击输入框事件响应函数
|
* 鼠标点击输入框事件响应函数
|
||||||
*/
|
*/
|
||||||
onMouseDownTextBox: ($event: MouseEvent) => void;
|
onMouseDownTextBox: ($event: MouseEvent) => void;
|
||||||
/**
|
/**
|
||||||
* 键盘在输入框按下事件
|
* 键盘在输入框按下事件
|
||||||
*/
|
*/
|
||||||
onKeyDownTextBox: ($event: Event) => void;
|
onKeyDownTextBox: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 键盘在输入框抬起事件
|
* 键盘在输入框抬起事件
|
||||||
*/
|
*/
|
||||||
onKeyUpTextBox: ($event: Event) => void;
|
onKeyUpTextBox: ($event: Event) => void;
|
||||||
/**
|
/**
|
||||||
* 输入框值变化事件响应函数
|
* 输入框值变化事件响应函数
|
||||||
*/
|
*/
|
||||||
onTextBoxValueChange: ($event: Event) => void;
|
onTextBoxValueChange: ($event: Event) => void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { computed, defineComponent, ref, SetupContext, watch } from 'vue';
|
||||||
import { NotifyButton, NotifyData } from '../notify.props';
|
import { NotifyButton, NotifyData } from '../notify.props';
|
||||||
import { ToastProps, toastProps } from './toast.props';
|
import { ToastProps, toastProps } from './toast.props';
|
||||||
|
|
||||||
|
import './toast.css';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Toast',
|
name: 'Toast',
|
||||||
props: toastProps,
|
props: toastProps,
|
||||||
|
@ -41,6 +43,8 @@ export default defineComponent({
|
||||||
|
|
||||||
const shouldShowTitle = computed(() => toast.value.title && toast.value.msg);
|
const shouldShowTitle = computed(() => toast.value.title && toast.value.msg);
|
||||||
|
|
||||||
|
const shouldShowMessageOnly = computed(() => !toast.value.title && toast.value.msg);
|
||||||
|
|
||||||
const shouldShowCloseButton = computed(() => {
|
const shouldShowCloseButton = computed(() => {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -108,6 +112,15 @@ export default defineComponent({
|
||||||
{shouldShowButtonsInTitle.value && renderNotifyButtons()}
|
{shouldShowButtonsInTitle.value && renderNotifyButtons()}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{shouldShowMessageOnly.value &&
|
||||||
|
(toast.value.buttons ? (
|
||||||
|
<div class="toast-title-btns-wrapper d-flex">
|
||||||
|
<h5 class="toast-title modal-tips-title only-toast-msg" v-html={toast.value.msg}></h5>
|
||||||
|
<div class="after-toast-title text-right ml-auto">{renderNotifyButtons()}</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<h5 class="toast-title modal-tips-title only-toast-msg" v-html={toast.value.msg}></h5>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
.toast-title-beforeshow {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
@-webkit-keyframes farrisMoveUpIn {
|
||||||
|
0% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes farrisMoveUpIn {
|
||||||
|
0% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes farrisMoveUpOut {
|
||||||
|
0% {
|
||||||
|
transform: translateY(0);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes farrisMoveUpOut {
|
||||||
|
0% {
|
||||||
|
transform: translateY(0);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toast.fadeIn {
|
||||||
|
-webkit-animation: farrisMoveUpIn 0.2s linear;
|
||||||
|
animation: farrisMoveUpIn 0.2s linear;
|
||||||
|
}
|
||||||
|
.toast.fadeOut {
|
||||||
|
-webkit-animation: farrisMoveUpOut 0.2s linear;
|
||||||
|
animation: farrisMoveUpOut 0.2s linear;
|
||||||
|
}
|
|
@ -1,222 +0,0 @@
|
||||||
import { Component, Input, ViewChildren, QueryList, AfterContentChecked, OnInit, Output, EventEmitter, NgZone, HostListener } from '@angular/core';
|
|
||||||
import { NotifyConfig, NotifyData } from './notifiy.options';
|
|
||||||
import { isFunction } from 'lodash-es';
|
|
||||||
import { NotifyComponent } from './notify.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'farris-notify-container',
|
|
||||||
template: `
|
|
||||||
<div [id]="id" class="farris-notify" [ngClass]="[position]" [ngStyle]="style">
|
|
||||||
<farris-notify *ngFor="let toast of toasts" [toast]="toast" [animateCls]="animateCls" (close)="closeToast(toast)" (btnClick)="btnClickHander($event,toast)"></farris-notify>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
export class NotifyContainerComponent implements OnInit, AfterContentChecked {
|
|
||||||
|
|
||||||
static POSITIONS: Array<String> = ['bottom-right', 'bottom-left',
|
|
||||||
'top-right', 'top-left', 'top-center', 'bottom-center', 'center-center'];
|
|
||||||
|
|
||||||
static ANIMATES: Array<string> = ['bounceInRight', 'bounceInLeft',
|
|
||||||
'bounceInRight', 'bounceInLeft', 'bounceInDown', 'bounceInUp', 'bounceIn'];
|
|
||||||
|
|
||||||
private _position = '';
|
|
||||||
style = {
|
|
||||||
'left': '',
|
|
||||||
'right': '',
|
|
||||||
'top': '',
|
|
||||||
'bottom': ''
|
|
||||||
};
|
|
||||||
notifyDistance = {
|
|
||||||
'left': 12,
|
|
||||||
'right': 12,
|
|
||||||
'top': 136,
|
|
||||||
'bottom': 12
|
|
||||||
};
|
|
||||||
|
|
||||||
animateCls = 'fadeIn';
|
|
||||||
|
|
||||||
config: NotifyConfig;
|
|
||||||
|
|
||||||
id = '';
|
|
||||||
|
|
||||||
@ViewChildren(NotifyComponent) notifyCmpList: QueryList<NotifyComponent>;
|
|
||||||
|
|
||||||
|
|
||||||
@Output() empty = new EventEmitter();
|
|
||||||
|
|
||||||
@Input() set position(value: string) {
|
|
||||||
if (value) {
|
|
||||||
let notFound = true;
|
|
||||||
for (let i = 0; i < NotifyContainerComponent.POSITIONS.length; i++) {
|
|
||||||
if (NotifyContainerComponent.POSITIONS[i] === value) {
|
|
||||||
notFound = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notFound) {
|
|
||||||
value = this.config.position;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = this.config.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(this.config);
|
|
||||||
|
|
||||||
|
|
||||||
// if (value === 'center-center') {
|
|
||||||
// this.animateCls = 'bounceIn';
|
|
||||||
// } else {
|
|
||||||
const i = NotifyContainerComponent.POSITIONS.indexOf(value);
|
|
||||||
//this.animateCls = NotifyContainerComponent.ANIMATES[i];
|
|
||||||
this.animateCls = 'fadeIn';
|
|
||||||
//}
|
|
||||||
|
|
||||||
this._position = 'toasty-position-' + value;
|
|
||||||
|
|
||||||
if (this.config.left) {
|
|
||||||
this.notifyDistance.left = this.config.left;
|
|
||||||
}
|
|
||||||
if (this.config.right) {
|
|
||||||
this.notifyDistance.right = this.config.right;
|
|
||||||
}
|
|
||||||
if (this.config.top) {
|
|
||||||
this.notifyDistance.top = this.config.top;
|
|
||||||
}
|
|
||||||
if (this.config.bottom) {
|
|
||||||
this.notifyDistance.bottom = this.config.bottom;
|
|
||||||
}
|
|
||||||
this.initstyle();
|
|
||||||
if (value === 'top-left') {
|
|
||||||
this.style.left = `${this.notifyDistance.left}px`;
|
|
||||||
this.style.top = `${this.notifyDistance.top}px`;
|
|
||||||
}
|
|
||||||
else if (value === 'top-center') {
|
|
||||||
this.style.top = `${this.notifyDistance.top}px`;
|
|
||||||
}
|
|
||||||
else if (value === 'top-right') {
|
|
||||||
this.style.right = `${this.notifyDistance.right}px`;
|
|
||||||
this.style.top = `${this.notifyDistance.top}px`;
|
|
||||||
}
|
|
||||||
else if (value === 'bottom-left') {
|
|
||||||
this.style.left = `${this.notifyDistance.left}px`;
|
|
||||||
this.style.bottom = `${this.notifyDistance.bottom}px`;
|
|
||||||
}
|
|
||||||
else if (value === 'bottom-center') {
|
|
||||||
this.style.bottom = `${this.notifyDistance.bottom}px`;
|
|
||||||
}
|
|
||||||
else if (value === 'bottom-right') {
|
|
||||||
this.style.right = `${this.notifyDistance.right}px`;
|
|
||||||
this.style.bottom = `${this.notifyDistance.bottom}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
get position() {
|
|
||||||
if (this._position) {
|
|
||||||
return this._position;
|
|
||||||
} else {
|
|
||||||
return 'toasty-position-top-center';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initstyle() {
|
|
||||||
this.style = {
|
|
||||||
'left': '',
|
|
||||||
'right': '',
|
|
||||||
'top': '',
|
|
||||||
'bottom': ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
toasts: Array<NotifyData> = [];
|
|
||||||
|
|
||||||
constructor(private ngZone: NgZone) { }
|
|
||||||
|
|
||||||
ngOnInit() {}
|
|
||||||
|
|
||||||
@HostListener('click', ['$event'])
|
|
||||||
onContainerClick($event: MouseEvent) {
|
|
||||||
$event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
|
||||||
if (this.notifyCmpList && this.notifyCmpList.length) {
|
|
||||||
this.notifyCmpList.forEach(cmp => {
|
|
||||||
cmp.close.subscribe(() => {
|
|
||||||
this.clear(cmp.toast.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closeToast(toast: NotifyData) {
|
|
||||||
this.clear(toast.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(notify: NotifyData) {
|
|
||||||
if (this.toasts.length >= this.config.limit) {
|
|
||||||
this.toasts.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toasts.push(notify);
|
|
||||||
|
|
||||||
if (notify.timeout) {
|
|
||||||
this._setTimeout(notify);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear(id: number | string) {
|
|
||||||
if (id) {
|
|
||||||
this.toasts.forEach((value: any, key: number) => {
|
|
||||||
if (value.id === id) {
|
|
||||||
if (value.onRemove && isFunction(value.onRemove)) {
|
|
||||||
value.onRemove.call(this, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toasts.splice(key, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.toasts.length === 0) {
|
|
||||||
this.empty.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new Error('Please provide id of Toast to close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearAll() {
|
|
||||||
this.toasts.forEach((value: any, key: number) => {
|
|
||||||
if (value.onRemove && isFunction(value.onRemove)) {
|
|
||||||
value.onRemove.call(this, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.toasts = [];
|
|
||||||
this.empty.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private findNotifyComponent(id: number | string) {
|
|
||||||
return this.notifyCmpList.find(item => item.toast.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setTimeout(notify: NotifyData) {
|
|
||||||
// this.ngZone.runOutsideAngular(() => {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
// this.clear(notify.id);
|
|
||||||
const cmp = this.findNotifyComponent(notify.id);
|
|
||||||
if (cmp) {
|
|
||||||
cmp.state = true;
|
|
||||||
cmp.inCls[cmp.animateCls] = false;
|
|
||||||
}
|
|
||||||
}, notify.timeout);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
btnClickHander(ev, notify: NotifyData) {
|
|
||||||
const cmp = this.findNotifyComponent(notify.id);
|
|
||||||
ev.callback(ev['ev'], cmp);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
import {
|
|
||||||
Component,
|
|
||||||
OnInit,
|
|
||||||
OnChanges,
|
|
||||||
Input,
|
|
||||||
Output,
|
|
||||||
EventEmitter,
|
|
||||||
ViewChild,
|
|
||||||
ElementRef,
|
|
||||||
SimpleChanges,
|
|
||||||
NgZone,
|
|
||||||
TemplateRef
|
|
||||||
} from '@angular/core';
|
|
||||||
import { NotifyData,NotifyButton } from './notifiy.options';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'farris-notify',
|
|
||||||
template: `
|
|
||||||
<div #notifyDiv class="toast" [ngClass]="inCls">
|
|
||||||
<button *ngIf="toast.showClose" class="toast-close f-btn-icon f-bare" (click)="closeToast($event)">
|
|
||||||
<span class="f-icon modal_close"></span>
|
|
||||||
</button>
|
|
||||||
<section class="modal-tips" *ngIf="toast.title || toast.msg">
|
|
||||||
<div class="float-left modal-tips-iconwrap">
|
|
||||||
<span class="f-icon" [ngClass]=" getPurType()"></span>
|
|
||||||
</div>
|
|
||||||
<div class="modal-tips-content">
|
|
||||||
<ng-container *ngIf="toast.title&&toast.msg">
|
|
||||||
<h5 class="toast-title modal-tips-title" [innerHTML]="toast.title | safe: 'html'"></h5>
|
|
||||||
<p class="toast-msg" [innerHtml]="toast.msg | safe:'html'"></p>
|
|
||||||
<div class="after-toast-msg text-right" *ngIf="toast.buttons">
|
|
||||||
<ng-container [ngTemplateOutlet]="useButtonsTemplate() ? toast.buttons : defaultButtonRef"></ng-container>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngIf="!toast.title&&toast.msg">
|
|
||||||
<ng-container *ngIf="toast.buttons" >
|
|
||||||
<div class="toast-title-btns-wrapper d-flex" >
|
|
||||||
<h5 class="toast-title modal-tips-title only-toast-msg" [innerHtml]="toast.msg | safe:'html'"></h5>
|
|
||||||
<div class="after-toast-title text-right ml-auto">
|
|
||||||
<ng-container [ngTemplateOutlet]="useButtonsTemplate() ? toast.buttons : defaultButtonRef"></ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="!toast.buttons" >
|
|
||||||
<h5 class="toast-title modal-tips-title only-toast-msg" [innerHtml]="toast.msg | safe:'html'"></h5>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-template #defaultButtonRef>
|
|
||||||
<span class="'f-preten-link '+btn.cls?btn.cls:''" *ngFor="let btn of toast.buttons; last as isLast"
|
|
||||||
(click)="clickHandler($event,btn) ">
|
|
||||||
{{ btn.text }}
|
|
||||||
</span>
|
|
||||||
</ng-template>
|
|
||||||
`,
|
|
||||||
styles: [
|
|
||||||
`
|
|
||||||
.toast-title-beforeshow{
|
|
||||||
opacity:0;
|
|
||||||
}
|
|
||||||
@-webkit-keyframes farrisMoveUpIn {
|
|
||||||
0% {
|
|
||||||
transform: translateY(-100%);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes farrisMoveUpIn {
|
|
||||||
0% {
|
|
||||||
transform: translateY(-100%);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes farrisMoveUpOut {
|
|
||||||
0% {
|
|
||||||
transform: translateY(0);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: translateY(-100%);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@keyframes farrisMoveUpOut {
|
|
||||||
0% {
|
|
||||||
transform: translateY(0);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: translateY(-100%);
|
|
||||||
transform-origin: 0 0;
|
|
||||||
opacity: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.toast.fadeIn {
|
|
||||||
-webkit-animation: farrisMoveUpIn 0.2s linear;
|
|
||||||
animation: farrisMoveUpIn 0.2s linear;
|
|
||||||
}
|
|
||||||
.toast.fadeOut {
|
|
||||||
-webkit-animation: farrisMoveUpOut 0.2s linear;
|
|
||||||
animation: farrisMoveUpOut 0.2s linear;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class NotifyComponent implements OnInit, OnChanges {
|
|
||||||
@Input() toast: NotifyData;
|
|
||||||
@Output() close = new EventEmitter();
|
|
||||||
@Output() btnClick = new EventEmitter();
|
|
||||||
@ViewChild('notifyDiv') notifyDiv: ElementRef;
|
|
||||||
|
|
||||||
_state = false;
|
|
||||||
@Input() animateCls: string;
|
|
||||||
outCls = '';
|
|
||||||
inCls = {};
|
|
||||||
|
|
||||||
get state() {
|
|
||||||
return this._state;
|
|
||||||
}
|
|
||||||
|
|
||||||
set state(value) {
|
|
||||||
this._state = value;
|
|
||||||
if (value) {
|
|
||||||
this.inCls[this.animateCls] = false;
|
|
||||||
this.inCls['animated'] = value;
|
|
||||||
this.inCls[this.outCls] = value;
|
|
||||||
// this.ngZone.runOutsideAngular(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.close.next(this.toast);
|
|
||||||
}, 200);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private ngZone: NgZone) { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (this.toast.buttons) {
|
|
||||||
this.ngZone.runOutsideAngular(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
let closeEl = this.notifyDiv.nativeElement.querySelector('.toast-close');
|
|
||||||
let contentEl =this.notifyDiv.nativeElement.querySelector('.toast-title-btns-wrapper');
|
|
||||||
if(closeEl['clientHeight']+10<contentEl['clientHeight']){
|
|
||||||
contentEl.classList.remove('d-flex');
|
|
||||||
}
|
|
||||||
contentEl.classList.remove('toast-title-beforeshow');
|
|
||||||
}, 200);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
|
||||||
this.getAnimateCls(changes.animateCls.currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAnimateCls(cls?: any) {
|
|
||||||
cls = cls || 'bounceInRight';
|
|
||||||
// this.outCls = 'bounceOut' + cls.substr(8);
|
|
||||||
// switch (cls.substr(8)) {
|
|
||||||
// case 'Up':
|
|
||||||
// this.outCls = 'bounceOutDown';
|
|
||||||
// break;
|
|
||||||
// case 'Down':
|
|
||||||
// this.outCls = 'bounceOutUp';
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.outCls = 'fadeOut';
|
|
||||||
|
|
||||||
// outCls += tmp;
|
|
||||||
|
|
||||||
this.inCls = {
|
|
||||||
[this.toast.type]: true,
|
|
||||||
[this.toast.theme]: true,
|
|
||||||
'animated': false,
|
|
||||||
[cls]: true,
|
|
||||||
[this.outCls]: this.state
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
closeToast(event: any) {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
this.state = true;
|
|
||||||
}
|
|
||||||
getPurType(): string {
|
|
||||||
if (this.toast && this.toast.type) {
|
|
||||||
return 'f-icon-' + this.toast.type.replace('toasty-type-', '');
|
|
||||||
}
|
|
||||||
return 'f-icon-default';
|
|
||||||
}
|
|
||||||
useButtonsTemplate() {
|
|
||||||
return this.toast.buttons instanceof TemplateRef;
|
|
||||||
}
|
|
||||||
//点击事件抛出
|
|
||||||
clickHandler(ev, btn:NotifyButton) {
|
|
||||||
if (btn.handle) {
|
|
||||||
this.btnClick.emit({ ev: ev, callback: btn.handle });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +1,83 @@
|
||||||
|
import { isFunction } from 'lodash';
|
||||||
import { computed, defineComponent, ref, SetupContext } from 'vue';
|
import { computed, defineComponent, ref, SetupContext } from 'vue';
|
||||||
import { NotifyContainerComponent } from './notify-container.component';
|
|
||||||
import { NotifyData, NotifyProps, notifyProps } from './notify.props';
|
import { NotifyData, NotifyProps, notifyProps } from './notify.props';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Notify',
|
name: 'Notify',
|
||||||
props: notifyProps,
|
props: notifyProps,
|
||||||
emits: [],
|
emits: ['empty'],
|
||||||
setup(props: NotifyProps, context: SetupContext) {
|
setup(props: NotifyProps, context: SetupContext) {
|
||||||
const notifyClass = computed(() => ({
|
const notifyClass = computed(() => ({
|
||||||
'farris-notify': true,
|
'farris-notify': true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const defaultNotifyDistance = {
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
top: 136,
|
||||||
|
bottom: 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
const toasts = ref(props.toasts || []);
|
||||||
|
|
||||||
const notifyStyle = computed(() => ({
|
const notifyStyle = computed(() => ({
|
||||||
left: '',
|
left: props.position.indexOf('left') > -1 ? `${props.left ? props.left : defaultNotifyDistance.left}px` : '',
|
||||||
right: '',
|
right: props.position.indexOf('right') > -1 ? `${props.right ? props.right : defaultNotifyDistance.right}px` : '',
|
||||||
top: '',
|
top: props.position.indexOf('top') > -1 ? `${props.top ? props.top : defaultNotifyDistance.top}px` : '',
|
||||||
bottom: '',
|
bottom: props.position.indexOf('bottom') > -1 ? `${props.bottom ? props.bottom : defaultNotifyDistance.bottom}px` : '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const toasts = computed(() => {
|
function closeToast(toast: NotifyData) {}
|
||||||
return props.toasts ? props.toasts : [];
|
|
||||||
});
|
|
||||||
|
|
||||||
|
function addToast(toast: NotifyData) {
|
||||||
|
if (toasts.value.length >= props.limit) {
|
||||||
|
toasts.value.shift();
|
||||||
|
}
|
||||||
|
toasts.value.push(toast);
|
||||||
|
// if (props.timeout) {
|
||||||
|
// this._setTimeout(notify);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
function invokeToastOnRemoveCallback(toast: NotifyData) {
|
||||||
|
if (toast && toast.onRemove && isFunction(toast.onRemove)) {
|
||||||
|
toast.onRemove.call(this, toast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear(id: number | string) {
|
||||||
|
const targetToastIndex = toasts.value.findIndex((toast: NotifyData) => toast.id === id);
|
||||||
|
if (targetToastIndex > -1) {
|
||||||
|
const targetToast = toasts.value[targetToastIndex];
|
||||||
|
invokeToastOnRemoveCallback(targetToast);
|
||||||
|
toasts.value.splice(targetToastIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAll() {
|
||||||
|
toasts.value.forEach((toast: NotifyData) => invokeToastOnRemoveCallback(toast));
|
||||||
|
toasts.value.length = 0;
|
||||||
|
context.emit('empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
context.expose({ addToast, clear, clearAll, closeToast });
|
||||||
|
|
||||||
|
function onClose($event: Event, toast: NotifyData) {
|
||||||
|
closeToast(toast);
|
||||||
|
}
|
||||||
|
function onClick($event: Event) {}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
return (
|
return (
|
||||||
<div id={props.id} class={notifyClass.value} style={notifyStyle.value}>
|
<div id={props.id} class={notifyClass.value} style={notifyStyle.value}>
|
||||||
{toasts.value.map((toastData: NotifyData) => {
|
{toasts.value.map((toastData: NotifyData) => {
|
||||||
return <f-toast v-model={toastData} animate={props.animate} onClose={} onClick={}></f-toast>;
|
return (
|
||||||
|
<f-toast
|
||||||
|
v-model={toastData}
|
||||||
|
animate={props.animate}
|
||||||
|
onClose={($event: Event) => onClose($event, toastData)}
|
||||||
|
onClick={onClick}></f-toast>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { NotifyData } from './notify.props';
|
|
||||||
import { ExtractPropTypes, PropType } from 'vue';
|
import { ExtractPropTypes, PropType } from 'vue';
|
||||||
|
|
||||||
export type NotifyPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'top-center' | 'bottom-center' | 'center-center';
|
export type NotifyPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'top-center' | 'bottom-center' | 'center-center';
|
||||||
|
@ -13,6 +12,8 @@ export type ToastyAnimate =
|
||||||
| 'bounceIn'
|
| 'bounceIn'
|
||||||
| 'fadeIn';
|
| 'fadeIn';
|
||||||
|
|
||||||
|
export type NotifyTheme = 'default' | 'material' | 'bootstrap';
|
||||||
|
|
||||||
export interface NotifyButton {
|
export interface NotifyButton {
|
||||||
customClass?: string;
|
customClass?: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -37,9 +38,18 @@ export interface NotifyData {
|
||||||
// export interface NotifyData extends NotifyOptions {}
|
// export interface NotifyData extends NotifyOptions {}
|
||||||
|
|
||||||
export const notifyProps = {
|
export const notifyProps = {
|
||||||
|
limit: { type: Number, default: 5 },
|
||||||
|
showCloseButton: { type: Boolean, default: true },
|
||||||
|
position: { type: String as PropType<NotifyPosition>, default: 'top-center' },
|
||||||
|
timeout: { type: Number, default: 3000 },
|
||||||
|
theme: { type: String as PropType<NotifyTheme>, default: 'bootstrap' },
|
||||||
|
left: { type: Number },
|
||||||
|
right: { type: Number },
|
||||||
|
top: { type: Number },
|
||||||
|
bottom: { type: Number },
|
||||||
id: { type: String },
|
id: { type: String },
|
||||||
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
|
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
|
||||||
position: { type: String as PropType<NotifyPosition>, default: 'toasty-position-top-center' },
|
|
||||||
toasts: { type: Array<NotifyData> },
|
toasts: { type: Array<NotifyData> },
|
||||||
|
options: { type: Object as PropType<NotifyData> },
|
||||||
};
|
};
|
||||||
export type NotifyProps = ExtractPropTypes<typeof notifyProps>;
|
export type NotifyProps = ExtractPropTypes<typeof notifyProps>;
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default defineComponent({
|
||||||
<>
|
<>
|
||||||
<div class="popover-arrow arrow"></div>
|
<div class="popover-arrow arrow"></div>
|
||||||
{shouldShowTitle.value && <h3 class="popover-title popover-header">{props.title}</h3>}
|
{shouldShowTitle.value && <h3 class="popover-title popover-header">{props.title}</h3>}
|
||||||
<div class={popoverContainerClass.value}></div>
|
<div class={popoverContainerClass.value}>{context.slots.default && context.slots?.default()}</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { ExtractPropTypes } from 'vue';
|
import { ExtractPropTypes, PropType } from 'vue';
|
||||||
|
|
||||||
|
export type PopoverPosition = 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
||||||
|
|
||||||
export const popoverProps = {
|
export const popoverProps = {
|
||||||
title: { type: String },
|
title: { type: String },
|
||||||
|
position: { type: String as PropType<PopoverPosition>, default: 'top' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PopoverProps = ExtractPropTypes<typeof popoverProps>;
|
export type PopoverProps = ExtractPropTypes<typeof popoverProps>;
|
||||||
|
|
Loading…
Reference in New Issue