• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atinc / ngx-tethys / 68ef226c-f83e-44c1-b8ed-e420a83c5d84

28 May 2025 10:31AM UTC coverage: 10.352% (-80.0%) from 90.316%
68ef226c-f83e-44c1-b8ed-e420a83c5d84

Pull #3460

circleci

pubuzhixing8
chore: xxx
Pull Request #3460: refactor(icon): migrate signal input #TINFR-1476

132 of 6823 branches covered (1.93%)

Branch coverage included in aggregate %.

10 of 14 new or added lines in 1 file covered. (71.43%)

11648 existing lines in 344 files now uncovered.

2078 of 14525 relevant lines covered (14.31%)

6.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

3.7
/src/upload/file-drop.directive.ts
1
import { isEmpty, isString } from 'ngx-tethys/util';
2
import { fromEvent, Subject } from 'rxjs';
3
import { filter, takeUntil, tap } from 'rxjs/operators';
4

5
import {
6
    DestroyRef,
7
    Directive,
8
    ElementRef,
9
    EventEmitter,
10
    HostBinding,
11
    Inject,
12
    Input,
13
    NgZone,
1✔
14
    OnInit,
UNCOV
15
    Output,
×
UNCOV
16
    Renderer2,
×
UNCOV
17
    inject,
×
UNCOV
18
    input,
×
UNCOV
19
    output,
×
UNCOV
20
    signal
×
UNCOV
21
} from '@angular/core';
×
UNCOV
22
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
×
UNCOV
23

×
UNCOV
24
import { FileSelectBaseDirective } from './file-select-base';
×
25
import { THY_UPLOAD_DEFAULT_OPTIONS, ThyUploadConfig } from './upload.config';
26
import { ThyFileSelectEvent } from './types';
UNCOV
27

×
UNCOV
28
/**
×
29
 * @name thyFileDrop
UNCOV
30
 */
×
UNCOV
31
@Directive({
×
32
    selector: '[thyFileDrop]',
UNCOV
33
    host: {
×
UNCOV
34
        '[class.thy-drop-over]': 'isDragOver()',
×
UNCOV
35
        '[class.drop-over]': 'isDragOver()'
×
UNCOV
36
    }
×
UNCOV
37
})
×
UNCOV
38
export class ThyFileDropDirective extends FileSelectBaseDirective implements OnInit {
×
39
    protected isDragOver = signal(false);
40

41
    readonly thyFileDropClassName = input<string>();
42

UNCOV
43
    readonly thyOnDrop = output<ThyFileSelectEvent>();
×
44

45
    /**
UNCOV
46
     * 当拖拽的文件中有不符合 thyAcceptType 中定义的类型时触发
×
47
     * @description.en-us It is triggered when there are files in the dragged files that do not conform to the types defined in thyAcceptType.
UNCOV
48
     */
×
49
    thyFilesReject = output<File[]>();
50

UNCOV
51
    private destroyRef = inject(DestroyRef);
×
UNCOV
52

×
UNCOV
53
    constructor(
×
UNCOV
54
        public elementRef: ElementRef,
×
55
        public renderer: Renderer2,
56
        public ngZone: NgZone,
57
        @Inject(THY_UPLOAD_DEFAULT_OPTIONS) public defaultConfig: ThyUploadConfig
UNCOV
58
    ) {
×
59
        super(elementRef, defaultConfig);
UNCOV
60
    }
×
61

62
    public ngOnInit(): void {
UNCOV
63
        this.ngZone.runOutsideAngular(() => {
×
UNCOV
64
            fromEvent(this.elementRef.nativeElement, 'dragenter')
×
UNCOV
65
                .pipe(
×
UNCOV
66
                    takeUntilDestroyed(this.destroyRef),
×
UNCOV
67
                    tap((event: DragEvent) => {
×
UNCOV
68
                        event.preventDefault();
×
UNCOV
69
                    }),
×
70
                    filter(event => event.dataTransfer.items && event.dataTransfer.items.length > 0)
UNCOV
71
                )
×
UNCOV
72
                .subscribe((event: DragEvent) => {
×
73
                    if (this.checkRejectFolderAndHtmlElement(event)) {
74
                        const files = this.filterFilesOrItems(Array.from(event.dataTransfer.items));
UNCOV
75
                        if (!isEmpty(files)) {
×
UNCOV
76
                            this.ngZone.run(() => {
×
77
                                this.isDragOver.set(true);
78
                                this.toggleDropOverClassName();
79
                            });
80
                        }
81
                    }
82
                });
UNCOV
83

×
UNCOV
84
            fromEvent(this.elementRef.nativeElement, 'dragover')
×
UNCOV
85
                .pipe(takeUntilDestroyed(this.destroyRef))
×
UNCOV
86
                .subscribe((event: any) => {
×
UNCOV
87
                    event.preventDefault();
×
UNCOV
88
                });
×
89

×
90
            fromEvent(this.elementRef.nativeElement, 'dragleave')
91
                .pipe(takeUntilDestroyed(this.destroyRef))
92
                .subscribe((event: any) => {
UNCOV
93
                    this.ngZone.run(() => {
×
94
                        if (!this.elementRef.nativeElement.contains(event.fromElement)) {
95
                            this.resetDragOver();
96
                            this.toggleDropOverClassName();
UNCOV
97
                        }
×
98
                    });
99
                });
×
100

UNCOV
101
            fromEvent(this.elementRef.nativeElement, 'drop')
×
UNCOV
102
                .pipe(
×
103
                    takeUntilDestroyed(this.destroyRef),
UNCOV
104
                    tap((event: DragEvent) => {
×
105
                        event.preventDefault();
106
                    })
UNCOV
107
                )
×
UNCOV
108
                .subscribe((event: DragEvent) => {
×
UNCOV
109
                    this.ngZone.run(() => {
×
UNCOV
110
                        if (this.checkRejectFolderAndHtmlElement(event)) {
×
UNCOV
111
                            const originFiles = event.dataTransfer ? Array.from(event.dataTransfer.files) : [];
×
112
                            const files = this.filterFilesOrItems(originFiles) as File[];
113

114
                            if (files.length !== originFiles.length) {
UNCOV
115
                                const differentFiles = originFiles.filter(item => !files.includes(item));
×
116
                                this.thyFilesReject.emit(differentFiles);
117
                            }
118
                            if (!isEmpty(files)) {
UNCOV
119
                                this.selectFiles(event, files, this.thyOnDrop);
×
UNCOV
120
                            }
×
UNCOV
121
                        }
×
UNCOV
122
                        this.resetDragOver();
×
123
                        this.toggleDropOverClassName();
124
                    });
UNCOV
125
                });
×
126
        });
127
    }
128

129
    private checkRejectFolderAndHtmlElement(event: DragEvent) {
UNCOV
130
        // 排除文件夹和HTML元素拖拽
×
131
        const items: DataTransferItemList | DataTransferItem[] = event.dataTransfer ? event.dataTransfer.items : [];
132
        let res = true;
1✔
133
        for (let index = 0; index < items.length; index++) {
134
            const item = items[index];
135
            const entry = this.getAsEntry(item);
136
            if (item.kind !== 'file' || (entry && !entry.isFile)) {
137
                res = false;
138
                // console.error(`file extensions not support drag upload, kind: ${item.kind}, type: ${item.type}`);
1✔
139
            }
140
        }
141
        return res;
142
    }
143

144
    private getAsEntry(item: DataTransferItem): FileSystemEntry {
1✔
145
        let entry: FileSystemEntry;
146
        if ((item as unknown as { getAsEntry: () => FileSystemEntry })['getAsEntry']) {
147
            // https://wiki.whatwg.org/wiki/DragAndDropEntries
148
            entry = (item as unknown as { getAsEntry: () => FileSystemEntry })['getAsEntry']();
149
        } else if (item.webkitGetAsEntry) {
150
            entry = item.webkitGetAsEntry();
151
        }
152
        return entry;
153
    }
154

155
    private filterFilesOrItems(items: Array<DataTransferItem | File>): Array<DataTransferItem | File> {
156
        const acceptType = this.thyAcceptType();
157
        if (acceptType && acceptType != '*/*') {
158
            return items.filter(item => {
159
                const isValidType = isString(item.type) && item.type.length > 0;
160
                return isValidType && acceptType.includes(item.type);
161
            });
162
        } else {
163
            return Array.from(items);
164
        }
165
    }
166

167
    private toggleDropOverClassName() {
168
        const dragOverCustomClass = this.thyFileDropClassName();
169
        if (dragOverCustomClass) {
170
            if (this.isDragOver()) {
171
                this.renderer.addClass(this.elementRef.nativeElement, dragOverCustomClass);
172
            } else {
173
                this.renderer.removeClass(this.elementRef.nativeElement, dragOverCustomClass);
174
            }
175
        }
176
    }
177
    private resetDragOver() {
178
        this.isDragOver.set(false);
179
    }
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc