@internetstiftelsen/styleguide 4.0.12-beta.0.5 → 4.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/components.js
CHANGED
|
@@ -1,140 +1,269 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var className = 'm-multi-select';
|
|
5
|
-
var multiSelectElements = document.querySelectorAll('.js-' + className);
|
|
6
|
-
var namespace = void 0;
|
|
3
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
5
|
+
var _className = require('../../assets/js/className');
|
|
6
|
+
|
|
7
|
+
var _className2 = _interopRequireDefault(_className);
|
|
8
|
+
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
|
|
11
|
+
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
12
|
+
|
|
13
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
14
|
+
|
|
15
|
+
var MultiSelect = function () {
|
|
16
|
+
function MultiSelect(el) {
|
|
17
|
+
var _this = this;
|
|
18
|
+
|
|
19
|
+
_classCallCheck(this, MultiSelect);
|
|
20
|
+
|
|
21
|
+
this.onInput = function () {
|
|
22
|
+
var value = _this.input.value;
|
|
23
|
+
|
|
24
|
+
// Clear suggestions if less than 2 characters are typed
|
|
25
|
+
|
|
26
|
+
if (value.length < 2) {
|
|
27
|
+
_this.clearSuggestions();
|
|
28
|
+
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var suggestions = _this.filterData(value);
|
|
33
|
+
|
|
34
|
+
_this.populateSuggestions(suggestions);
|
|
35
|
+
_this.resetFocus();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this.onKeyDown = function (e) {
|
|
39
|
+
if (e.keyCode === 40) {
|
|
40
|
+
_this.highlight('down');
|
|
41
|
+
} else if (e.keyCode === 38) {
|
|
42
|
+
_this.highlight('up');
|
|
43
|
+
} else if (e.keyCode === 13) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
|
|
46
|
+
_this.selectHighlighted();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
this.onClick = function (e) {
|
|
51
|
+
if (e.target.classList.contains((0, _className2.default)(_this.baseClassName + '__suggestion-btn'))) {
|
|
52
|
+
_this.addItem(_this.data.find(function (d) {
|
|
53
|
+
return d.value === e.target.value;
|
|
54
|
+
}));
|
|
55
|
+
_this.clearSuggestions();
|
|
56
|
+
_this.input.value = '';
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
this.element = el;
|
|
61
|
+
this.baseClassName = 'm-multi-select';
|
|
62
|
+
this.currentFocus = -1;
|
|
63
|
+
this.name = this.element.getAttribute('data-multi-select-name');
|
|
64
|
+
this.input = this.element.querySelector('.js-' + this.baseClassName + '__input');
|
|
65
|
+
this.suggestionsBox = this.element.querySelector('.js-' + this.baseClassName + '-suggestions-box');
|
|
66
|
+
this.selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
|
|
67
|
+
this.selectedItems = [];
|
|
68
|
+
this.data = [];
|
|
69
|
+
|
|
70
|
+
this.getData();
|
|
71
|
+
this.attach();
|
|
29
72
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
var buttonTextContainer = document.createElement('span');
|
|
44
|
-
buttonTextContainer.classList.add('u-visuallyhidden');
|
|
45
|
-
removeBtn.appendChild(buttonTextContainer);
|
|
46
|
-
buttonTextContainer.textContent = 'Ta bort ' + item; // Accessibility label for screen readers
|
|
47
|
-
|
|
48
|
-
// Event listener for removing the selected item
|
|
49
|
-
removeBtn.addEventListener('click', function () {
|
|
50
|
-
removeItem(newItem, Array.from(selectedItemsList.children).indexOf(newItem));
|
|
51
|
-
});
|
|
52
|
-
newItem.appendChild(removeBtn);
|
|
53
|
-
selectedItemsList.appendChild(newItem);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Remove an item and manage focus appropriately
|
|
57
|
-
function removeItem(item, index) {
|
|
58
|
-
var selectedItemsList = document.querySelector('.js-m-multi-select-selected-items');
|
|
59
|
-
selectedItemsList.removeChild(item);
|
|
60
|
-
|
|
61
|
-
var remainingItems = selectedItemsList.getElementsByTagName('div');
|
|
62
|
-
// Focus management: set focus to the next item, or the search input if no items left
|
|
63
|
-
if (remainingItems.length > 0) {
|
|
64
|
-
if (index < remainingItems.length) {
|
|
65
|
-
remainingItems[index].getElementsByTagName('button')[0].focus();
|
|
66
|
-
} else {
|
|
67
|
-
remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
|
|
73
|
+
|
|
74
|
+
_createClass(MultiSelect, [{
|
|
75
|
+
key: 'getData',
|
|
76
|
+
value: function getData() {
|
|
77
|
+
var id = this.input.getAttribute('data-multi-select-suggestions');
|
|
78
|
+
var el = document.getElementById(id);
|
|
79
|
+
|
|
80
|
+
if (!el) {
|
|
81
|
+
this.data = [];
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.data = JSON.parse(el.textContent);
|
|
68
86
|
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
var multiSelectInput = multiSelectElement.querySelector('.js-' + className + '__input');
|
|
76
|
-
var suggestionsBox = multiSelectElement.querySelector('.js-' + className + '-suggestions-box');
|
|
77
|
-
var suggestionsData = multiSelectInput.getAttribute('data-multi-select-suggestions');
|
|
78
|
-
|
|
79
|
-
// Event listener for input changes in the search field
|
|
80
|
-
multiSelectInput.addEventListener('input', function () {
|
|
81
|
-
var value = this.value;
|
|
82
|
-
// Clear suggestions if less than 2 characters are typed
|
|
83
|
-
if (value.length < 2) {
|
|
84
|
-
suggestionsBox.innerHTML = '';
|
|
85
|
-
return;
|
|
87
|
+
}, {
|
|
88
|
+
key: 'attach',
|
|
89
|
+
value: function attach() {
|
|
90
|
+
this.input.addEventListener('input', this.onInput);
|
|
91
|
+
this.input.addEventListener('keydown', this.onKeyDown);
|
|
92
|
+
this.suggestionsBox.addEventListener('click', this.onClick);
|
|
86
93
|
}
|
|
94
|
+
}, {
|
|
95
|
+
key: 'setFocus',
|
|
96
|
+
value: function setFocus(index) {
|
|
97
|
+
this.currentFocus = index;
|
|
98
|
+
}
|
|
99
|
+
}, {
|
|
100
|
+
key: 'resetFocus',
|
|
101
|
+
value: function resetFocus() {
|
|
102
|
+
this.setFocus(-1);
|
|
103
|
+
}
|
|
104
|
+
}, {
|
|
105
|
+
key: 'clearSuggestions',
|
|
106
|
+
value: function clearSuggestions() {
|
|
107
|
+
this.suggestionsBox.innerHTML = '';
|
|
108
|
+
}
|
|
109
|
+
}, {
|
|
110
|
+
key: 'addSuggestions',
|
|
111
|
+
value: function addSuggestions(suggestions) {
|
|
112
|
+
var _this2 = this;
|
|
87
113
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
this.data = [].concat(_toConsumableArray(this.data), _toConsumableArray(suggestions.filter(function (s) {
|
|
115
|
+
return !_this2.data.find(function (d) {
|
|
116
|
+
return d.value === s.value;
|
|
117
|
+
});
|
|
118
|
+
})));
|
|
119
|
+
}
|
|
120
|
+
}, {
|
|
121
|
+
key: 'filterData',
|
|
122
|
+
value: function filterData(query) {
|
|
123
|
+
var _this3 = this;
|
|
124
|
+
|
|
125
|
+
return this.data.filter(function (item) {
|
|
126
|
+
return item.name.toLowerCase().startsWith(query.toLowerCase());
|
|
127
|
+
}).filter(function (item) {
|
|
128
|
+
return !_this3.selectedItems.includes(item.name);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}, {
|
|
132
|
+
key: 'populateSuggestions',
|
|
133
|
+
value: function populateSuggestions(suggestions) {
|
|
134
|
+
var cls = (0, _className2.default)(this.baseClassName + '__suggestion-btn');
|
|
135
|
+
this.suggestionsBox.innerHTML = suggestions.map(function (item) {
|
|
136
|
+
return '<button class=\'' + cls + '\' tabindex=\'0\' value="' + item.value + '">' + item.name + '</button>';
|
|
137
|
+
}).join('');
|
|
138
|
+
}
|
|
139
|
+
}, {
|
|
140
|
+
key: 'removeItem',
|
|
141
|
+
value: function removeItem(item) {
|
|
142
|
+
var node = this.element.querySelector('.js-m-multi-select-selected-items li[data-value="' + item.value + '"]');
|
|
103
143
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
var items = suggestionsBox.getElementsByClassName(namespace + 'm-multi-select__suggestion-btn');
|
|
107
|
-
// Navigate down in the suggestions list
|
|
108
|
-
if (e.keyCode == 40) {
|
|
109
|
-
currentFocus = (currentFocus + 1) % items.length;
|
|
110
|
-
setActive(items);
|
|
111
|
-
// Navigate up in the suggestions list
|
|
112
|
-
} else if (e.keyCode == 38) {
|
|
113
|
-
currentFocus = (currentFocus - 1 + items.length) % items.length;
|
|
114
|
-
setActive(items);
|
|
115
|
-
// Handle Enter key to select a focused item
|
|
116
|
-
} else if (e.keyCode == 13) {
|
|
117
|
-
e.preventDefault();
|
|
118
|
-
if (currentFocus > -1 && items[currentFocus]) {
|
|
119
|
-
addSelectedItem(items[currentFocus].textContent);
|
|
120
|
-
suggestionsBox.innerHTML = '';
|
|
121
|
-
multiSelectInput.value = '';
|
|
122
|
-
currentFocus = -1;
|
|
144
|
+
if (!node) {
|
|
145
|
+
return;
|
|
123
146
|
}
|
|
147
|
+
|
|
148
|
+
var parent = node.closest('.js-m-multi-select-selected-items');
|
|
149
|
+
var index = Array.prototype.indexOf.call(parent.children, node);
|
|
150
|
+
|
|
151
|
+
node.remove();
|
|
152
|
+
|
|
153
|
+
var remainingItems = parent.getElementsByTagName('li');
|
|
154
|
+
|
|
155
|
+
// Focus management: set focus to the next item, or the search input if no items left
|
|
156
|
+
if (remainingItems.length > 0) {
|
|
157
|
+
if (index < remainingItems.length) {
|
|
158
|
+
remainingItems[index].getElementsByTagName('button')[0].focus();
|
|
159
|
+
} else {
|
|
160
|
+
remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
this.input.focus();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.selectedItems = this.selectedItems.filter(function (i) {
|
|
167
|
+
return i.value !== item.value;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
var itemInput = this.element.querySelector('input[value="' + item.value + '"]');
|
|
171
|
+
itemInput.remove();
|
|
124
172
|
}
|
|
125
|
-
}
|
|
173
|
+
}, {
|
|
174
|
+
key: 'addItem',
|
|
175
|
+
value: function addItem(item) {
|
|
176
|
+
var _this4 = this;
|
|
177
|
+
|
|
178
|
+
if (!item) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
var newItem = document.createElement('li');
|
|
183
|
+
newItem.textContent = item.name + ' ';
|
|
184
|
+
newItem.setAttribute('data-value', item.value);
|
|
185
|
+
newItem.classList.add((0, _className2.default)('a-tag'));
|
|
186
|
+
newItem.classList.add((0, _className2.default)(this.baseClassName + '__tag'));
|
|
187
|
+
|
|
188
|
+
var removeBtn = document.createElement('button');
|
|
189
|
+
removeBtn.classList.add((0, _className2.default)(this.baseClassName + '-selected-items__remove-btn'));
|
|
190
|
+
|
|
191
|
+
var buttonTextContainer = document.createElement('span');
|
|
192
|
+
buttonTextContainer.classList.add('u-visuallyhidden');
|
|
193
|
+
removeBtn.appendChild(buttonTextContainer);
|
|
194
|
+
buttonTextContainer.textContent = 'Ta bort ' + item.name; // Accessibility label for screen readers
|
|
195
|
+
|
|
196
|
+
// Event listener for removing the selected item
|
|
197
|
+
removeBtn.addEventListener('click', function () {
|
|
198
|
+
_this4.removeItem(item);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
newItem.appendChild(removeBtn);
|
|
202
|
+
|
|
203
|
+
this.selectedItemsList.appendChild(newItem);
|
|
204
|
+
this.selectedItems.push(item);
|
|
205
|
+
|
|
206
|
+
var itemInput = document.createElement('input');
|
|
126
207
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
suggestionsBox.innerHTML = '';
|
|
133
|
-
multiSelectInput.value = '';
|
|
208
|
+
itemInput.type = 'hidden';
|
|
209
|
+
itemInput.name = this.name + '[]';
|
|
210
|
+
itemInput.value = item.value;
|
|
211
|
+
|
|
212
|
+
this.element.appendChild(itemInput);
|
|
134
213
|
}
|
|
135
|
-
}
|
|
136
|
-
|
|
214
|
+
}, {
|
|
215
|
+
key: 'removeHighlight',
|
|
216
|
+
value: function removeHighlight() {
|
|
217
|
+
var items = this.suggestionsBox.getElementsByClassName(this.baseClassName + '__suggestion-btn');
|
|
218
|
+
|
|
219
|
+
[].forEach.call(items, function (item) {
|
|
220
|
+
item.classList.remove('autocomplete-active');
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}, {
|
|
224
|
+
key: 'highlight',
|
|
225
|
+
value: function highlight(direction) {
|
|
226
|
+
var items = this.suggestionsBox.getElementsByClassName(this.baseClassName + '__suggestion-btn');
|
|
227
|
+
var focus = this.currentFocus;
|
|
228
|
+
|
|
229
|
+
if (direction === 'down') {
|
|
230
|
+
focus = focus >= items.length - 1 ? 0 : focus + 1;
|
|
231
|
+
} else {
|
|
232
|
+
focus = focus <= 0 ? items.length - 1 : focus - 1;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.setFocus(focus);
|
|
236
|
+
this.removeHighlight();
|
|
237
|
+
|
|
238
|
+
items[this.currentFocus].classList.add('autocomplete-active');
|
|
239
|
+
items[this.currentFocus].scrollIntoView({ block: 'nearest' });
|
|
240
|
+
}
|
|
241
|
+
}, {
|
|
242
|
+
key: 'selectHighlighted',
|
|
243
|
+
value: function selectHighlighted() {
|
|
244
|
+
var items = this.suggestionsBox.getElementsByClassName(this.baseClassName + '__suggestion-btn');
|
|
245
|
+
|
|
246
|
+
if (this.currentFocus > -1 && items[this.currentFocus]) {
|
|
247
|
+
var item = items[this.currentFocus];
|
|
248
|
+
|
|
249
|
+
this.addItem(this.data.find(function (d) {
|
|
250
|
+
return d.value === item.value;
|
|
251
|
+
}));
|
|
252
|
+
|
|
253
|
+
this.clearSuggestions();
|
|
254
|
+
this.input.value = '';
|
|
255
|
+
this.resetFocus();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}]);
|
|
259
|
+
|
|
260
|
+
return MultiSelect;
|
|
261
|
+
}();
|
|
262
|
+
|
|
263
|
+
var multiSelectElements = document.querySelectorAll('.js-m-multi-select');
|
|
137
264
|
|
|
138
265
|
if (multiSelectElements) {
|
|
139
|
-
[].forEach.call(multiSelectElements,
|
|
266
|
+
[].forEach.call(multiSelectElements, function (el) {
|
|
267
|
+
el.multiSelect = new MultiSelect(el);
|
|
268
|
+
});
|
|
140
269
|
}
|
package/package.json
CHANGED
package/src/components.js
CHANGED
|
@@ -1,134 +1,219 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import className from '../../assets/js/className';
|
|
2
|
+
|
|
3
|
+
class MultiSelect {
|
|
4
|
+
constructor(el) {
|
|
5
|
+
this.element = el;
|
|
6
|
+
this.baseClassName = 'm-multi-select';
|
|
7
|
+
this.currentFocus = -1;
|
|
8
|
+
this.name = this.element.getAttribute('data-multi-select-name');
|
|
9
|
+
this.input = this.element.querySelector(`.js-${this.baseClassName}__input`);
|
|
10
|
+
this.suggestionsBox = this.element.querySelector(`.js-${this.baseClassName}-suggestions-box`);
|
|
11
|
+
this.selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
|
|
12
|
+
this.selectedItems = [];
|
|
13
|
+
this.data = [];
|
|
14
|
+
|
|
15
|
+
this.getData();
|
|
16
|
+
this.attach();
|
|
17
|
+
}
|
|
5
18
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
19
|
+
getData() {
|
|
20
|
+
const id = this.input.getAttribute('data-multi-select-suggestions');
|
|
21
|
+
const el = document.getElementById(id);
|
|
9
22
|
|
|
10
|
-
|
|
23
|
+
if (!el) {
|
|
24
|
+
this.data = [];
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
11
27
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!items.length) return false;
|
|
15
|
-
removeActive(items);
|
|
16
|
-
if (currentFocus >= items.length) currentFocus = 0;
|
|
17
|
-
if (currentFocus < 0) currentFocus = items.length - 1;
|
|
18
|
-
items[currentFocus].classList.add('autocomplete-active');
|
|
28
|
+
this.data = JSON.parse(el.textContent);
|
|
29
|
+
}
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
attach() {
|
|
32
|
+
this.input.addEventListener('input', this.onInput);
|
|
33
|
+
this.input.addEventListener('keydown', this.onKeyDown);
|
|
34
|
+
this.suggestionsBox.addEventListener('click', this.onClick);
|
|
35
|
+
}
|
|
22
36
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
for (let i = 0; i < items.length; i+=1) {
|
|
26
|
-
items[i].classList.remove('autocomplete-active');
|
|
37
|
+
setFocus(index) {
|
|
38
|
+
this.currentFocus = index;
|
|
27
39
|
}
|
|
28
|
-
}
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const newItem = document.createElement('li');
|
|
34
|
-
newItem.textContent = item + ' ';
|
|
35
|
-
newItem.classList.add(`${namespace}a-tag`);
|
|
36
|
-
newItem.classList.add(`${namespace}m-multi-select__tag`);
|
|
37
|
-
|
|
38
|
-
const removeBtn = document.createElement('button');
|
|
39
|
-
removeBtn.classList.add(`${namespace}m-multi-select-selected-items__remove-btn`);
|
|
40
|
-
|
|
41
|
-
const buttonTextContainer = document.createElement('span');
|
|
42
|
-
buttonTextContainer.classList.add('u-visuallyhidden');
|
|
43
|
-
removeBtn.appendChild(buttonTextContainer);
|
|
44
|
-
buttonTextContainer.textContent ='Ta bort ' + item; // Accessibility label for screen readers
|
|
45
|
-
|
|
46
|
-
// Event listener for removing the selected item
|
|
47
|
-
removeBtn.addEventListener('click', function () {
|
|
48
|
-
removeItem(newItem, Array.from(selectedItemsList.children).indexOf(newItem));
|
|
49
|
-
});
|
|
50
|
-
newItem.appendChild(removeBtn);
|
|
51
|
-
selectedItemsList.appendChild(newItem);
|
|
52
|
-
}
|
|
41
|
+
resetFocus() {
|
|
42
|
+
this.setFocus(-1);
|
|
43
|
+
}
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
selectedItemsList.removeChild(item);
|
|
45
|
+
clearSuggestions() {
|
|
46
|
+
this.suggestionsBox.innerHTML = '';
|
|
47
|
+
}
|
|
58
48
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
addSuggestions(suggestions) {
|
|
50
|
+
this.data = [
|
|
51
|
+
...this.data,
|
|
52
|
+
...suggestions.filter((s) => !this.data.find((d) => d.value === s.value)),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
filterData(query) {
|
|
57
|
+
return this.data
|
|
58
|
+
.filter((item) => item.name.toLowerCase().startsWith(query.toLowerCase()))
|
|
59
|
+
.filter((item) => !this.selectedItems.includes(item.name));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
populateSuggestions(suggestions) {
|
|
63
|
+
const cls = className(`${this.baseClassName}__suggestion-btn`);
|
|
64
|
+
this.suggestionsBox.innerHTML = suggestions.map((item) => `<button class='${cls}' tabindex='0' value="${item.value}">${item.name}</button>`).join('');
|
|
69
65
|
}
|
|
70
|
-
}
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const suggestionsBox = multiSelectElement.querySelector(`.js-${className}-suggestions-box`);
|
|
75
|
-
const suggestionsData = multiSelectInput.getAttribute('data-multi-select-suggestions');
|
|
67
|
+
onInput = () => {
|
|
68
|
+
const { value } = this.input;
|
|
76
69
|
|
|
77
|
-
// Event listener for input changes in the search field
|
|
78
|
-
multiSelectInput.addEventListener('input', function () {
|
|
79
|
-
const value = this.value;
|
|
80
70
|
// Clear suggestions if less than 2 characters are typed
|
|
81
71
|
if (value.length < 2) {
|
|
82
|
-
|
|
72
|
+
this.clearSuggestions();
|
|
73
|
+
|
|
83
74
|
return;
|
|
84
75
|
}
|
|
85
76
|
|
|
86
|
-
|
|
87
|
-
let suggestions = document.getElementById(suggestionsData).textContent;
|
|
88
|
-
suggestions = JSON.parse(suggestions);
|
|
77
|
+
const suggestions = this.filterData(value);
|
|
89
78
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
suggestionsBox.innerHTML = filtered.map(item => `<button class='${namespace}m-multi-select__suggestion-btn' tabindex='0'>${item.name}</button>`).join('');
|
|
94
|
-
// Reset the current focus
|
|
95
|
-
currentFocus = -1;
|
|
96
|
-
});
|
|
79
|
+
this.populateSuggestions(suggestions);
|
|
80
|
+
this.resetFocus();
|
|
81
|
+
}
|
|
97
82
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
83
|
+
removeItem(item) {
|
|
84
|
+
const node = this.element.querySelector(`.js-m-multi-select-selected-items li[data-value="${item.value}"]`);
|
|
85
|
+
|
|
86
|
+
if (!node) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const parent = node.closest('.js-m-multi-select-selected-items');
|
|
91
|
+
const index = Array.prototype.indexOf.call(parent.children, node);
|
|
92
|
+
|
|
93
|
+
node.remove();
|
|
94
|
+
|
|
95
|
+
const remainingItems = parent.getElementsByTagName('li');
|
|
96
|
+
|
|
97
|
+
// Focus management: set focus to the next item, or the search input if no items left
|
|
98
|
+
if (remainingItems.length > 0) {
|
|
99
|
+
if (index < remainingItems.length) {
|
|
100
|
+
remainingItems[index].getElementsByTagName('button')[0].focus();
|
|
101
|
+
} else {
|
|
102
|
+
remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
|
|
117
103
|
}
|
|
104
|
+
} else {
|
|
105
|
+
this.input.focus();
|
|
118
106
|
}
|
|
119
|
-
});
|
|
120
107
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
108
|
+
this.selectedItems = this.selectedItems
|
|
109
|
+
.filter((i) => i.value !== item.value);
|
|
110
|
+
|
|
111
|
+
const itemInput = this.element.querySelector(`input[value="${item.value}"]`);
|
|
112
|
+
itemInput.remove();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
addItem(item) {
|
|
116
|
+
if (!item) {
|
|
117
|
+
return;
|
|
128
118
|
}
|
|
129
|
-
|
|
119
|
+
|
|
120
|
+
const newItem = document.createElement('li');
|
|
121
|
+
newItem.textContent = `${item.name} `;
|
|
122
|
+
newItem.setAttribute('data-value', item.value);
|
|
123
|
+
newItem.classList.add(className('a-tag'));
|
|
124
|
+
newItem.classList.add(className(`${this.baseClassName}__tag`));
|
|
125
|
+
|
|
126
|
+
const removeBtn = document.createElement('button');
|
|
127
|
+
removeBtn.classList.add(className(`${this.baseClassName}-selected-items__remove-btn`));
|
|
128
|
+
|
|
129
|
+
const buttonTextContainer = document.createElement('span');
|
|
130
|
+
buttonTextContainer.classList.add('u-visuallyhidden');
|
|
131
|
+
removeBtn.appendChild(buttonTextContainer);
|
|
132
|
+
buttonTextContainer.textContent = `Ta bort ${item.name}`; // Accessibility label for screen readers
|
|
133
|
+
|
|
134
|
+
// Event listener for removing the selected item
|
|
135
|
+
removeBtn.addEventListener('click', () => {
|
|
136
|
+
this.removeItem(item);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
newItem.appendChild(removeBtn);
|
|
140
|
+
|
|
141
|
+
this.selectedItemsList.appendChild(newItem);
|
|
142
|
+
this.selectedItems.push(item);
|
|
143
|
+
|
|
144
|
+
const itemInput = document.createElement('input');
|
|
145
|
+
|
|
146
|
+
itemInput.type = 'hidden';
|
|
147
|
+
itemInput.name = `${this.name}[]`;
|
|
148
|
+
itemInput.value = item.value;
|
|
149
|
+
|
|
150
|
+
this.element.appendChild(itemInput);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
removeHighlight() {
|
|
154
|
+
const items = this.suggestionsBox.getElementsByClassName(`${this.baseClassName}__suggestion-btn`);
|
|
155
|
+
|
|
156
|
+
[].forEach.call(items, (item) => {
|
|
157
|
+
item.classList.remove('autocomplete-active');
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
highlight(direction) {
|
|
162
|
+
const items = this.suggestionsBox.getElementsByClassName(`${this.baseClassName}__suggestion-btn`);
|
|
163
|
+
let focus = this.currentFocus;
|
|
164
|
+
|
|
165
|
+
if (direction === 'down') {
|
|
166
|
+
focus = (focus >= items.length - 1) ? 0 : focus + 1;
|
|
167
|
+
} else {
|
|
168
|
+
focus = (focus <= 0) ? items.length - 1 : focus - 1;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.setFocus(focus);
|
|
172
|
+
this.removeHighlight();
|
|
173
|
+
|
|
174
|
+
items[this.currentFocus].classList.add('autocomplete-active');
|
|
175
|
+
items[this.currentFocus].scrollIntoView({ block: 'nearest' });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
selectHighlighted() {
|
|
179
|
+
const items = this.suggestionsBox.getElementsByClassName(`${this.baseClassName}__suggestion-btn`);
|
|
180
|
+
|
|
181
|
+
if (this.currentFocus > -1 && items[this.currentFocus]) {
|
|
182
|
+
const item = items[this.currentFocus];
|
|
183
|
+
|
|
184
|
+
this.addItem(this.data.find((d) => d.value === item.value));
|
|
185
|
+
|
|
186
|
+
this.clearSuggestions();
|
|
187
|
+
this.input.value = '';
|
|
188
|
+
this.resetFocus();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
onKeyDown = (e) => {
|
|
193
|
+
if (e.keyCode === 40) {
|
|
194
|
+
this.highlight('down');
|
|
195
|
+
} else if (e.keyCode === 38) {
|
|
196
|
+
this.highlight('up');
|
|
197
|
+
} else if (e.keyCode === 13) {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
|
|
200
|
+
this.selectHighlighted();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
onClick = (e) => {
|
|
205
|
+
if (e.target.classList.contains(className(`${this.baseClassName}__suggestion-btn`))) {
|
|
206
|
+
this.addItem(this.data.find((d) => d.value === e.target.value));
|
|
207
|
+
this.clearSuggestions();
|
|
208
|
+
this.input.value = '';
|
|
209
|
+
}
|
|
210
|
+
}
|
|
130
211
|
}
|
|
131
212
|
|
|
213
|
+
const multiSelectElements = document.querySelectorAll('.js-m-multi-select');
|
|
214
|
+
|
|
132
215
|
if (multiSelectElements) {
|
|
133
|
-
[].forEach.call(multiSelectElements,
|
|
216
|
+
[].forEach.call(multiSelectElements, (el) => {
|
|
217
|
+
el.multiSelect = new MultiSelect(el);
|
|
218
|
+
});
|
|
134
219
|
}
|