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

atinc / ngx-tethys / d9ae709b-3c27-4b69-b125-b8b80b54f90b

pending completion
d9ae709b-3c27-4b69-b125-b8b80b54f90b

Pull #2757

circleci

mengshuicmq
fix: fix code review
Pull Request #2757: feat(color-picker): color-picker support disabled (#INFR-8645)

98 of 6315 branches covered (1.55%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

2392 of 13661 relevant lines covered (17.51%)

83.12 hits per line

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

8.33
/src/property/property-item.component.ts
1
import { InputBoolean, InputNumber, ThyClickDispatcher } from 'ngx-tethys/core';
2
import { combineLatest, fromEvent, Subject, Subscription } from 'rxjs';
3
import { delay, filter, take, takeUntil } from 'rxjs/operators';
4
import { OverlayOutsideClickDispatcher, OverlayRef } from '@angular/cdk/overlay';
5
import {
6
    ChangeDetectionStrategy,
7
    ChangeDetectorRef,
8
    Component,
9
    ContentChild,
10
    ElementRef,
11
    HostBinding,
12
    Input,
13
    NgZone,
14
    OnChanges,
15
    OnDestroy,
1✔
16
    OnInit,
17
    SimpleChanges,
×
18
    TemplateRef,
19
    ViewChild
20
} from '@angular/core';
×
21
import { ThyPropertiesComponent } from './properties.component';
×
22
import { ThyFlexibleTextComponent } from 'ngx-tethys/flexible-text';
×
23
import { NgIf, NgTemplateOutlet } from '@angular/common';
×
24

×
25
export type ThyPropertyItemOperationTrigger = 'hover' | 'always';
×
26

×
27
/**
×
28
 * 属性组件
×
29
 * @name thy-property-item
×
30
 */
×
31
@Component({
×
32
    selector: 'thy-property-item',
×
33
    templateUrl: './property-item.component.html',
34
    host: {
35
        class: 'thy-property-item',
×
36
        '[class.thy-property-item-operational]': '!!operation',
×
37
        '[class.thy-property-item-operational-hover]': "thyOperationTrigger === 'hover'"
×
38
    },
×
39
    changeDetection: ChangeDetectionStrategy.OnPush,
40
    standalone: true,
41
    imports: [NgIf, ThyFlexibleTextComponent, NgTemplateOutlet]
42
})
×
43
export class ThyPropertyItemComponent implements OnInit, OnChanges, OnDestroy {
×
44
    /**
45
     * 属性名称
46
     * @type sting
×
47
     * @default thyLabelText
×
48
     */
×
49
    @Input() thyLabelText: string;
×
50

×
51
    /**
52
     * 设置属性是否是可编辑的
53
     * @type sting
54
     * @default false
55
     */
×
56
    @Input() @InputBoolean() thyEditable: boolean;
×
57

×
58
    /**
59
     * 设置跨列的数量
60
     * @type number
61
     */
62
    @Input() @InputNumber() thySpan: number = 1;
63

64
    /**
×
65
     * 设置属性操作现实触发方式,默认 always 一直显示
66
     * @type 'hover' | 'always'
67
     */
×
68
    @Input() thyOperationTrigger: ThyPropertyItemOperationTrigger = 'always';
69

70
    /**
×
71
     * 属性名称自定义模板
×
72
     * @type TemplateRef
×
73
     */
×
74
    @ContentChild('label', { static: true }) label!: TemplateRef<void>;
75

×
76
    /**
77
     * 属性内容编辑模板,只有在 thyEditable 为 true 时生效
78
     * @type TemplateRef
×
79
     */
×
80
    @ContentChild('editor', { static: true }) editor!: TemplateRef<void>;
81

82
    /**
83
     * 操作区模板
84
     * @type TemplateRef
85
     */
×
86
    @ContentChild('operation', { static: true }) operation!: TemplateRef<void>;
×
87

×
88
    /**
×
89
     * @private
90
     */
91
    @ViewChild('contentTemplate', { static: true }) content!: TemplateRef<void>;
×
92

93
    /**
94
     * @private
95
     */
96
    @ViewChild('item', { static: true }) itemContent: ElementRef<HTMLElement>;
×
97

98
    editing: boolean;
99

×
100
    changes$ = new Subject<SimpleChanges>();
101

102
    private destroy$ = new Subject();
×
103

104
    private eventDestroy$ = new Subject();
105

106
    private originOverlays: OverlayRef[] = [];
×
107

×
108
    private clickEventSubscription: Subscription;
109

110
    @HostBinding('style.grid-column')
×
111
    get gridColumn() {
112
        return `span ${Math.min(this.thySpan, this.parent.thyColumn)}`;
113
    }
114

×
115
    isVertical = false;
×
116

×
117
    constructor(
×
118
        private cdr: ChangeDetectorRef,
119
        private clickDispatcher: ThyClickDispatcher,
1✔
120
        private ngZone: NgZone,
121
        private overlayOutsideClickDispatcher: OverlayOutsideClickDispatcher,
122
        private parent: ThyPropertiesComponent
123
    ) {
124
        this.originOverlays = [...this.overlayOutsideClickDispatcher._attachedOverlays] as OverlayRef[];
125
    }
126

1✔
127
    ngOnInit() {
128
        this.subscribeClick();
129
        this.parent.layout$.pipe(takeUntil(this.destroy$)).subscribe(layout => {
130
            this.isVertical = layout === 'vertical';
131
            this.cdr.markForCheck();
132
        });
133
    }
134

135
    ngOnChanges(changes: SimpleChanges): void {
136
        if (changes.thyEditable && changes.thyEditable.currentValue) {
137
            this.subscribeClick();
138
        } else {
139
            this.eventDestroy$.next();
1✔
140
            this.eventDestroy$.complete();
141

142
            if (this.clickEventSubscription) {
143
                this.clickEventSubscription.unsubscribe();
1✔
144
                this.clickEventSubscription = null;
145
            }
146
        }
147
    }
1✔
148

149
    setEditing(editing: boolean) {
150
        this.ngZone.run(() => {
151
            this.editing = editing;
152
            this.cdr.markForCheck();
153
        });
154
    }
155

156
    /**
157
     * @deprecated please use setEditing(editing: boolean)
158
     */
159
    setKeepEditing(keep: boolean) {
160
        this.setEditing(keep);
161
    }
162

163
    private hasOverlay() {
164
        return this.overlayOutsideClickDispatcher._attachedOverlays.length > this.originOverlays.length;
165
    }
166

167
    private subscribeClick() {
168
        if (this.thyEditable === true) {
169
            this.ngZone.runOutsideAngular(() => {
170
                if (this.clickEventSubscription) {
171
                    return;
172
                }
173
                this.clickEventSubscription = fromEvent(this.itemContent.nativeElement, 'click')
174
                    .pipe(takeUntil(this.eventDestroy$))
175
                    .subscribe(() => {
176
                        this.setEditing(true);
177
                        this.bindEditorBlurEvent(this.itemContent.nativeElement);
178
                    });
179
            });
180
        }
181
    }
182

183
    private subscribeOverlayDetach() {
184
        const openedOverlays = this.overlayOutsideClickDispatcher._attachedOverlays.slice(this.originOverlays.length);
185
        const overlaysDetachments$ = openedOverlays.map(overlay => overlay.detachments());
186
        if (overlaysDetachments$.length) {
187
            combineLatest(overlaysDetachments$)
188
                .pipe(delay(50), take(1), takeUntil(this.destroy$))
189
                .subscribe(() => {
190
                    this.setEditing(false);
191
                });
192
        }
193
    }
194

195
    private subscribeDocumentClick(editorElement: HTMLElement) {
196
        this.clickDispatcher
197
            .clicked(0)
198
            .pipe(
199
                filter(event => {
200
                    return !editorElement.contains(event.target as HTMLElement);
201
                }),
202
                take(1),
203
                takeUntil(this.destroy$)
204
            )
205
            .subscribe(() => {
206
                this.setEditing(false);
207
            });
208
    }
209

210
    private bindEditorBlurEvent(editorElement: HTMLElement) {
211
        if (this.hasOverlay()) {
212
            this.subscribeOverlayDetach();
213
        } else {
214
            this.subscribeDocumentClick(editorElement);
215
        }
216
    }
217

218
    ngOnDestroy(): void {
219
        this.destroy$.next();
220
        this.destroy$.complete();
221

222
        this.eventDestroy$.next();
223
        this.eventDestroy$.complete();
224
    }
225
}
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