diff --git a/packages/ui-vue/components/avatar/src/composition/types.ts b/packages/ui-vue/components/avatar/src/composition/types.ts index 8331d5b..0e54c3e 100644 --- a/packages/ui-vue/components/avatar/src/composition/types.ts +++ b/packages/ui-vue/components/avatar/src/composition/types.ts @@ -7,3 +7,13 @@ export interface UseImage { imageTitle: ComputedRef; } + +export interface ImageFile { + size: number; + name: string; + type: string; + lastModified?: string; + lastModifiedDate?: Date; + state?: string; + base64?: string; +} diff --git a/packages/ui-vue/components/avatar/src/composition/use-file.ts b/packages/ui-vue/components/avatar/src/composition/use-file.ts index de1dfdf..be512af 100644 --- a/packages/ui-vue/components/avatar/src/composition/use-file.ts +++ b/packages/ui-vue/components/avatar/src/composition/use-file.ts @@ -1,5 +1,8 @@ +import { stringLiteral } from '@babel/types'; import { SetupContext } from 'vue'; +import { resourceLimits } from 'worker_threads'; import { AvatarProps } from '../avatar.props'; +import { ImageFile } from './types'; export function useFile(props: AvatarProps, context: SetupContext, allowTypes: string[]) { function getFileData($event: Event) { @@ -39,36 +42,51 @@ export function useFile(props: AvatarProps, context: SetupContext, allowTypes: s fileInput.value = ''; } - transformFile(getfile: any) { - // const subject = new Subject(); - this.imgFileObj = { - size: getfile.size, - name: getfile.name, - type: getfile.type, - lastModified: getfile.lastModified, - lastModifiedDate: getfile.lastModifiedDate - } - this.do(getfile).subscribe(res => { - this.loadingImg = false; - if (res['state'] === 'done') { - this.imgSrc = res['result']; + function readSourceFile(sourceFile: File): Promise<{ state: string; content: string | ArrayBuffer | null }> { + const result = new Promise<{ state: string; content: string | ArrayBuffer | null }>((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(sourceFile); + reader.onload = () => { + resolve({ state: 'done', content: reader.result }); + }; + reader.onerror = function () { + reject({ state: 'error', content: reader.result }); + }; + }); + return result; + } - //this.onChangeCallback(this.imgSrc); - this.onChangeCallback(this.removeBase64(this.imgSrc)); - this.onTouchedCallback(); - } - else if (res['state'] === 'error') { - let uploaderrorText = this.localeService.getValue('avatar.uploadError'); + function updateImageSrc(imageSrc: string) {} + + function removeBase64(imageSrc: string) {} + + function transformFile(sourceFile: File) { + // const subject = new Subject(); + const imageFile: ImageFile = { + size: sourceFile.size, + name: sourceFile.name, + type: sourceFile.type, + lastModified: String(sourceFile.lastModified), + }; + readSourceFile(sourceFile) + .then((result) => { + if (result.state === 'done') { + updateImageSrc(result.content as string); + const imageFileContent = removeBase64(imageSrc); + context.emit('change', imageFileContent); + context.emit('touched'); + imageFile.state = result.state; + imageFile.base64 = result.content as string; + context.emit('imageChange', imageFile); + } + }) + .catch((reason) => { + const uploadErrorText = this.localeService.getValue('avatar.uploadError'); this.notifyService.error({ type: 'error', title: '', - msg: uploaderrorText - }) - // this.notifyService.error('图片上传失败,请重试!'); - } - this.imgFileObj.state = res['state']; - this.imgFileObj.base64 = res['result']; - this.imgChange.emit(this.imgFileObj); - }); + msg: uploadErrorText, + }); + }); } } diff --git a/packages/ui-vue/components/avatar/src/composition/use-image.ts b/packages/ui-vue/components/avatar/src/composition/use-image.ts index 774d537..cef6a30 100644 --- a/packages/ui-vue/components/avatar/src/composition/use-image.ts +++ b/packages/ui-vue/components/avatar/src/composition/use-image.ts @@ -2,7 +2,7 @@ import { computed, SetupContext } from 'vue'; import { AvatarProps } from '../avatar.props'; import { UseImage } from './types'; -export function useImage(props: AvatarProps, context: SetupContext): UseImage { +export function useImage(props: AvatarProps, context: SetupContext, fileInput: HTMLInputElement): UseImage { const defaultImage = ''; const errorImage = ''; @@ -56,5 +56,16 @@ export function useImage(props: AvatarProps, context: SetupContext): UseImage { return props.readonly ? '' : props.tile; }); + function onClickImage() { + if (this.readonly) { + return; + } + fileInput.click(); + } + + function getImageFile() { + return this.imgFileObj; + } + return { acceptTypes, imageSrc, imageTitle }; } diff --git a/packages/ui-vue/components/notify/index.ts b/packages/ui-vue/components/notify/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/ui-vue/components/notify/src/components/toast.component.tsx b/packages/ui-vue/components/notify/src/components/toast.component.tsx new file mode 100644 index 0000000..e69de29 diff --git a/packages/ui-vue/components/notify/src/components/toast.props.ts b/packages/ui-vue/components/notify/src/components/toast.props.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/ui-vue/components/notify/src/composition/types.ts b/packages/ui-vue/components/notify/src/composition/types.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/ui-vue/components/notify/src/notify-container.component.tsx b/packages/ui-vue/components/notify/src/notify-container.component.tsx new file mode 100644 index 0000000..3094c98 --- /dev/null +++ b/packages/ui-vue/components/notify/src/notify-container.component.tsx @@ -0,0 +1,222 @@ +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: ` +
+ +
+ ` +}) +export class NotifyContainerComponent implements OnInit, AfterContentChecked { + + static POSITIONS: Array = ['bottom-right', 'bottom-left', + 'top-right', 'top-left', 'top-center', 'bottom-center', 'center-center']; + + static ANIMATES: Array = ['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; + + + @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 = []; + + 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); + } +} diff --git a/packages/ui-vue/components/notify/src/notify.component.ts b/packages/ui-vue/components/notify/src/notify.component.ts new file mode 100644 index 0000000..c3818e2 --- /dev/null +++ b/packages/ui-vue/components/notify/src/notify.component.ts @@ -0,0 +1,223 @@ +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: ` +
+ + +
+ + + + {{ btn.text }} + + + `, + 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 { + return
; + }; + }, +}); diff --git a/packages/ui-vue/components/notify/src/notify.props.ts b/packages/ui-vue/components/notify/src/notify.props.ts new file mode 100644 index 0000000..b0c8685 --- /dev/null +++ b/packages/ui-vue/components/notify/src/notify.props.ts @@ -0,0 +1,4 @@ +import { ExtractPropTypes } from 'vue'; + +export const notifyProps = {}; +export type NotifyProps = ExtractPropTypes;