-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
import {createApp, nextTick} from 'vue';
import $ from 'jquery';
import {vueDelimiters} from './VueComponentLoader.js';
export function initRepoBranchTagDropdown(selector) {
$(selector).each(function () {
const $dropdown = $(this);
const $data = $dropdown.find('.data');
const data = {
items: [],
mode: $data.data('mode'),
searchTerm: '',
refName: '',
noResults: '',
canCreateBranch: false,
menuVisible: false,
createTag: false,
isViewTag: false,
isViewBranch: false,
isViewTree: false,
active: 0,
branchForm: '',
branchURLPrefix: '',
branchURLSuffix: '',
tagURLPrefix: '',
tagURLSuffix: '',
setAction: false,
submitForm: false,
};
$data.find('.item').each(function () {
data.items.push({
name: $(this).text(),
url: $(this).data('url'),
branch: $(this).hasClass('branch'),
tag: $(this).hasClass('tag'),
selected: $(this).hasClass('selected')
});
});
$data.remove();
// eslint-disable-next-line unicorn/no-this-assignment
const elRoot = this;
const view = createApp({
delimiters: vueDelimiters,
data() {
return data;
},
computed: {
filteredItems() {
const items = this.items.filter((item) => {
return ((this.mode === 'branches' && item.branch) || (this.mode === 'tags' && item.tag)) &&
(!this.searchTerm || item.name.toLowerCase().includes(this.searchTerm.toLowerCase()));
});
// no idea how to fix this so linting rule is disabled instead
this.active = (items.length === 0 && this.showCreateNewBranch ? 0 : -1); // eslint-disable-line vue/no-side-effects-in-computed-properties
return items;
},
showNoResults() {
return this.filteredItems.length === 0 && !this.showCreateNewBranch;
},
showCreateNewBranch() {
if (!this.canCreateBranch || !this.searchTerm) {
return false;
}
return this.items.filter((item) => item.name.toLowerCase() === this.searchTerm.toLowerCase()).length === 0;
}
},
watch: {
menuVisible(visible) {
if (visible) {
this.focusSearchField();
}
}
},
beforeMount() {
this.noResults = elRoot.getAttribute('data-no-results');
this.canCreateBranch = elRoot.getAttribute('data-can-create-branch') === 'true';
this.branchForm = elRoot.getAttribute('data-branch-form');
switch (elRoot.getAttribute('data-view-type')) {
case 'tree':
this.isViewTree = true;
break;
case 'tag':
this.isViewTag = true;
break;
default:
this.isViewBranch = true;
break;
}
this.refName = elRoot.getAttribute('data-ref-name');
this.branchURLPrefix = elRoot.getAttribute('data-branch-url-prefix');
this.branchURLSuffix = elRoot.getAttribute('data-branch-url-suffix');
this.tagURLPrefix = elRoot.getAttribute('data-tag-url-prefix');
this.tagURLSuffix = elRoot.getAttribute('data-tag-url-suffix');
this.setAction = elRoot.getAttribute('data-set-action') === 'true';
this.submitForm = elRoot.getAttribute('data-submit-form') === 'true';
document.body.addEventListener('click', (event) => {
if (elRoot.contains(event.target)) return;
if (this.menuVisible) {
this.menuVisible = false;
}
});
},
methods: {
selectItem(item) {
const prev = this.getSelected();
if (prev !== null) {
prev.selected = false;
}
item.selected = true;
const url = (item.tag) ? this.tagURLPrefix + item.url + this.tagURLSuffix : this.branchURLPrefix + item.url + this.branchURLSuffix;
if (this.branchForm === '') {
window.location.href = url;
} else {
this.isViewTree = false;
this.isViewTag = false;
this.isViewBranch = false;
this.$refs.dropdownRefName.textContent = item.name;
if (this.setAction) {
$(`#${this.branchForm}`).attr('action', url);
} else {
$(`#${this.branchForm} input[name="refURL"]`).val(url);
}
$(`#${this.branchForm} input[name="ref"]`).val(item.name);
if (item.tag) {
this.isViewTag = true;
$(`#${this.branchForm} input[name="refType"]`).val('tag');
} else {
this.isViewBranch = true;
$(`#${this.branchForm} input[name="refType"]`).val('branch');
}
if (this.submitForm) {
$(`#${this.branchForm}`).trigger('submit');
}
this.menuVisible = false;
}
},
createNewBranch() {
if (!this.showCreateNewBranch) return;
$(this.$refs.newBranchForm).trigger('submit');
},
focusSearchField() {
nextTick(() => {
this.$refs.searchField.focus();
});
},
getSelected() {
for (let i = 0, j = this.items.length; i < j; ++i) {
if (this.items[i].selected) return this.items[i];
}
return null;
},
getSelectedIndexInFiltered() {
for (let i = 0, j = this.filteredItems.length; i < j; ++i) {
if (this.filteredItems[i].selected) return i;
}
return -1;
},
scrollToActive() {
let el = this.$refs[`listItem${this.active}`];
if (!el || !el.length) return;
if (Array.isArray(el)) {
el = el[0];
}
const cont = this.$refs.scrollContainer;
if (el.offsetTop < cont.scrollTop) {
cont.scrollTop = el.offsetTop;
} else if (el.offsetTop + el.clientHeight > cont.scrollTop + cont.clientHeight) {
cont.scrollTop = el.offsetTop + el.clientHeight - cont.clientHeight;
}
},
keydown(event) {
if (event.keyCode === 40) { // arrow down
event.preventDefault();
if (this.active === -1) {
this.active = this.getSelectedIndexInFiltered();
}
if (this.active + (this.showCreateNewBranch ? 0 : 1) >= this.filteredItems.length) {
return;
}
this.active++;
this.scrollToActive();
} else if (event.keyCode === 38) { // arrow up
event.preventDefault();
if (this.active === -1) {
this.active = this.getSelectedIndexInFiltered();
}
if (this.active <= 0) {
return;
}
this.active--;
this.scrollToActive();
} else if (event.keyCode === 13) { // enter
event.preventDefault();
if (this.active >= this.filteredItems.length) {
this.createNewBranch();
} else if (this.active >= 0) {
this.selectItem(this.filteredItems[this.active]);
}
} else if (event.keyCode === 27) { // escape
event.preventDefault();
this.menuVisible = false;
}
}
}
});
view.mount(this);
});
}