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

atinc / ngx-tethys / #55

30 Jul 2025 07:08AM UTC coverage: 9.866% (-80.4%) from 90.297%
#55

push

why520crazy
feat(empty): add setMessage for update display text #TINFR-2616

92 of 6794 branches covered (1.35%)

Branch coverage included in aggregate %.

2014 of 14552 relevant lines covered (13.84%)

6.15 hits per line

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

4.04
/src/transfer/transfer.component.ts
1
import {
2
    Component,
3
    OnInit,
4
    TemplateRef,
5
    ViewEncapsulation,
6
    computed,
7
    input,
8
    output,
9
    contentChild,
10
    numberAttribute,
11
    effect
12
} from '@angular/core';
13

14
import {
15
    Direction,
16
    InnerTransferDragEvent,
1✔
17
    ThyTransferChangeEvent,
18
    ThyTransferDragEvent,
×
19
    ThyTransferItem,
×
20
    ThyTransferSelectEvent,
×
21
    TransferDirection
×
22
} from './transfer.interface';
×
23
import { ThyFlexibleText } from 'ngx-tethys/flexible-text';
×
24
import { ThyIcon } from 'ngx-tethys/icon';
×
25
import { NgClass, NgTemplateOutlet } from '@angular/common';
×
26
import { ThyTransferList } from './transfer-list.component';
×
27
import { coerceBooleanProperty, ThyBooleanInput } from 'ngx-tethys/util';
×
28

×
29
/**
×
30
 * 穿梭框组件
×
31
 * @name thy-transfer
×
32
 * @order 10
×
33
 */
×
34
@Component({
×
35
    selector: 'thy-transfer',
×
36
    templateUrl: './transfer.component.html',
×
37
    host: {
×
38
        class: 'thy-transfer'
×
39
    },
40
    encapsulation: ViewEncapsulation.None,
41
    imports: [ThyTransferList, ThyIcon, NgClass, NgTemplateOutlet, ThyFlexibleText]
42
})
×
43
export class ThyTransfer implements OnInit {
×
44
    public leftDataSource: ThyTransferItem[] = [];
×
45

×
46
    public rightDataSource: ThyTransferItem[] = [];
×
47
    /**
×
48
     * 数据源
49
     * @type ThyTransferItem[]
×
50
     */
×
51
    readonly thyData = input<ThyTransferItem[]>([]);
52

53
    readonly thyRenderLeftTemplateRef = input<TemplateRef<any>>();
54

55
    readonly thyRenderRightTemplateRef = input<TemplateRef<any>>();
×
56

×
57
    /**
58
     * title集合,title[0]为左标题,title[1]为右标题
×
59
     * @type string[]
×
60
     */
61
    readonly thyTitles = input<string[]>([]);
×
62

×
63
    /**
×
64
     * 右侧列表是否可以锁定
×
65
     * @default false
66
     */
67
    readonly thyRightCanLock = input<boolean, ThyBooleanInput>(false, { transform: coerceBooleanProperty });
68

×
69
    /**
70
     * 右侧锁定最大数量
71
     */
×
72
    readonly thyRightLockMax = input<number, unknown>(undefined, { transform: numberAttribute });
73

×
74
    /**
×
75
     * 右侧选择最大数量
×
76
     */
×
77
    readonly thyRightMax = input<number, unknown>(undefined, { transform: numberAttribute });
×
78

79
    /**
80
     * 设置是否自动移动
×
81
     * @description.en-us Currently not implemented, in order to support the selections move
82
     * @default true
83
     */
×
84
    readonly thyAutoMove = input<boolean, ThyBooleanInput>(true, { transform: coerceBooleanProperty });
85

86
    /**
×
87
     * 左侧列表是否拖动
×
88
     * @default false
×
89
     */
×
90
    readonly thyLeftDraggable = input<boolean, ThyBooleanInput>(false, { transform: coerceBooleanProperty });
×
91

92
    /**
93
     * 右侧列表是否拖动
94
     * @default false
×
95
     */
×
96
    readonly thyRightDraggable = input<boolean, ThyBooleanInput>(false, { transform: coerceBooleanProperty });
×
97

×
98
    /**
×
99
     * @type EventEmitter<ThyTransferDragEvent>
×
100
     */
101
    thyDraggableUpdate = output<ThyTransferDragEvent>();
×
102

103
    /**
104
     * Transfer变化的回调事件
105
     * @type EventEmitter<ThyTransferChangeEvent>
106
     */
107
    thyChange = output<ThyTransferChangeEvent>();
108

×
109
    /**
×
110
     * 设置自定义Item渲染数据模板
×
111
     * @type TemplateRef
112
     */
×
113
    readonly templateRef = contentChild<TemplateRef<any>>('renderTemplate');
×
114

115
    /**
×
116
     * 设置自定义左侧内容模板
×
117
     * @type TemplateRef
118
     */
119
    readonly leftContentRef = contentChild<TemplateRef<any>>('renderLeftTemplate');
120

1✔
121
    /**
1✔
122
     * 设置自定义右侧内容模板
123
     * @type TemplateRef
124
     */
125
    readonly rightContentRef = contentChild<TemplateRef<any>>('renderRightTemplate');
126

127
    readonly leftTitle = computed(() => this.thyTitles()[0] || '');
128

129
    readonly rightTitle = computed(() => this.thyTitles()[1] || '');
130

131
    constructor() {
132
        effect(() => {
133
            this.initializeTransferData();
134
        });
135
    }
136

137
    ngOnInit() {}
138

139
    initializeTransferData(data: ThyTransferItem[] = []) {
1✔
140
        this.leftDataSource = [];
141
        this.rightDataSource = [];
142
        this.thyData().forEach(item => {
143
            if (item.direction === TransferDirection.left) {
144
                this.leftDataSource.push(item);
145
            }
146
            if (item.direction === TransferDirection.right) {
147
                this.rightDataSource.push(item);
148
            }
149
        });
150
    }
151

152
    onSelect(from: Direction, event: ThyTransferSelectEvent) {
153
        if (event.item.isFixed) {
154
            return;
155
        }
156
        if (this.thyRightMax() <= this.rightDataSource.length && from === TransferDirection.left) {
157
            return;
158
        }
159
        const to = from === TransferDirection.left ? TransferDirection.right : TransferDirection.left;
160
        event.item.checked = !event.item.checked;
161
        if (this.thyAutoMove()) {
162
            this.onMove(to);
163
        }
164
    }
165

166
    selectItem(event: ThyTransferSelectEvent) {
167
        this.onSelect(TransferDirection.left, event);
168
    }
169

170
    unselectItem(event: ThyTransferSelectEvent) {
171
        this.onSelect(TransferDirection.right, event);
172
    }
173

174
    private groupListByIsLock(list: ThyTransferItem[] = []) {
175
        const lock: ThyTransferItem[] = [],
176
            unlock: ThyTransferItem[] = [];
177
        list.forEach(item => {
178
            if (item.isLock) {
179
                lock.push(item);
180
            } else {
181
                unlock.push(item);
182
            }
183
        });
184
        return { lock: lock, unlock: unlock };
185
    }
186

187
    onMove(to: Direction) {
188
        const fromDataSource = to === TransferDirection.right ? this.leftDataSource : this.rightDataSource;
189
        const toDataSource = to === TransferDirection.right ? this.rightDataSource : this.leftDataSource;
190
        const selections = fromDataSource.filter(item => item.checked);
191
        const changeEvent: ThyTransferChangeEvent = {
192
            from: to === TransferDirection.right ? TransferDirection.left : TransferDirection.right,
193
            to: to,
194
            items: [...selections]
195
        };
196
        selections.forEach(item => {
197
            const index = fromDataSource.indexOf(item);
198
            const removed = fromDataSource.splice(index, 1)[0];
199
            removed.checked = !removed.checked;
200
            removed.direction = to;
201
            toDataSource.push(removed);
202
        });
203
        this.thyChange.emit({
204
            ...changeEvent,
205
            left: this.groupListByIsLock(this.leftDataSource),
206
            right: this.groupListByIsLock(this.rightDataSource)
207
        });
208
    }
209

210
    onDragUpdate(direction: Direction, event: InnerTransferDragEvent) {
211
        const otherDirectionData = direction === TransferDirection.left ? this.rightDataSource : this.leftDataSource;
212
        const otherListData = this.groupListByIsLock(otherDirectionData);
213
        this.thyDraggableUpdate.emit({
214
            ...event.dragEvent,
215
            left: direction === TransferDirection.left ? event.listData : otherListData,
216
            right: direction === TransferDirection.right ? event.listData : otherListData
217
        });
218

219
        this.rightDataSource =
220
            direction === TransferDirection.right
221
                ? [...event.listData.lock, ...event.listData.unlock]
222
                : [...otherListData.lock, ...otherListData.unlock];
223
    }
224
}
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

© 2026 Coveralls, Inc