@internetstiftelsen/styleguide 4.0.12-beta.0.4 → 4.0.12
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 +3 -1
- package/dist/molecules/multi-select/multi-select.js +210 -126
- package/package.json +1 -1
- package/src/atoms/icon/_all-icons.zip +0 -0
- package/src/atoms/icon/copy.svg +4 -1
- package/src/atoms/icon/sprite.svg +2 -2
- package/src/components.js +1 -1
- package/src/molecules/multi-select/multi-select.js +158 -110
package/dist/components.js
CHANGED
|
@@ -1,140 +1,224 @@
|
|
|
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
12
|
+
|
|
13
|
+
var MultiSelect = function () {
|
|
14
|
+
function MultiSelect(el) {
|
|
15
|
+
var _this = this;
|
|
16
|
+
|
|
17
|
+
_classCallCheck(this, MultiSelect);
|
|
18
|
+
|
|
19
|
+
this.onInput = function () {
|
|
20
|
+
var value = _this.input.value;
|
|
21
|
+
|
|
22
|
+
// Clear suggestions if less than 2 characters are typed
|
|
23
|
+
|
|
24
|
+
if (value.length < 2) {
|
|
25
|
+
_this.clearSuggestions();
|
|
26
|
+
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var suggestions = _this.filterData(value);
|
|
31
|
+
|
|
32
|
+
_this.populateSuggestions(suggestions);
|
|
33
|
+
_this.resetFocus();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this.onKeyDown = function (e) {
|
|
37
|
+
if (e.keyCode === 40) {
|
|
38
|
+
_this.highlight('down');
|
|
39
|
+
} else if (e.keyCode === 38) {
|
|
40
|
+
_this.highlight('up');
|
|
41
|
+
} else if (e.keyCode === 13) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
|
|
44
|
+
_this.selectHighlighted();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
this.onClick = function (e) {
|
|
49
|
+
if (e.target.classList.contains((0, _className2.default)(_this.baseClassName + '__suggestion-btn'))) {
|
|
50
|
+
_this.addItem(e.target.textContent);
|
|
51
|
+
_this.clearSuggestions();
|
|
52
|
+
_this.input.value = '';
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
this.element = el;
|
|
57
|
+
this.baseClassName = 'm-multi-select';
|
|
58
|
+
this.currentFocus = -1;
|
|
59
|
+
this.input = this.element.querySelector('.js-' + this.baseClassName + '__input');
|
|
60
|
+
this.suggestionsBox = this.element.querySelector('.js-' + this.baseClassName + '-suggestions-box');
|
|
61
|
+
this.selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
|
|
62
|
+
this.selectedItems = [];
|
|
63
|
+
this.data = [];
|
|
64
|
+
|
|
65
|
+
this.getData();
|
|
66
|
+
this.attach();
|
|
29
67
|
}
|
|
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();
|
|
68
|
+
|
|
69
|
+
_createClass(MultiSelect, [{
|
|
70
|
+
key: 'getData',
|
|
71
|
+
value: function getData() {
|
|
72
|
+
var id = this.input.getAttribute('data-multi-select-suggestions');
|
|
73
|
+
var el = document.getElementById(id);
|
|
74
|
+
|
|
75
|
+
if (!el) {
|
|
76
|
+
this.data = [];
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.data = JSON.parse(el.textContent);
|
|
68
81
|
}
|
|
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;
|
|
82
|
+
}, {
|
|
83
|
+
key: 'attach',
|
|
84
|
+
value: function attach() {
|
|
85
|
+
this.input.addEventListener('input', this.onInput);
|
|
86
|
+
this.input.addEventListener('keydown', this.onKeyDown);
|
|
87
|
+
this.suggestionsBox.addEventListener('click', this.onClick);
|
|
86
88
|
}
|
|
89
|
+
}, {
|
|
90
|
+
key: 'setFocus',
|
|
91
|
+
value: function setFocus(index) {
|
|
92
|
+
this.currentFocus = index;
|
|
93
|
+
}
|
|
94
|
+
}, {
|
|
95
|
+
key: 'resetFocus',
|
|
96
|
+
value: function resetFocus() {
|
|
97
|
+
this.setFocus(-1);
|
|
98
|
+
}
|
|
99
|
+
}, {
|
|
100
|
+
key: 'clearSuggestions',
|
|
101
|
+
value: function clearSuggestions() {
|
|
102
|
+
this.suggestionsBox.innerHTML = '';
|
|
103
|
+
}
|
|
104
|
+
}, {
|
|
105
|
+
key: 'filterData',
|
|
106
|
+
value: function filterData(query) {
|
|
107
|
+
var _this2 = this;
|
|
108
|
+
|
|
109
|
+
return this.data.filter(function (item) {
|
|
110
|
+
return item.name.toLowerCase().startsWith(query.toLowerCase());
|
|
111
|
+
}).filter(function (item) {
|
|
112
|
+
return !_this2.selectedItems.includes(item.name);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}, {
|
|
116
|
+
key: 'populateSuggestions',
|
|
117
|
+
value: function populateSuggestions(suggestions) {
|
|
118
|
+
var cls = (0, _className2.default)(this.baseClassName + '__suggestion-btn');
|
|
119
|
+
this.suggestionsBox.innerHTML = suggestions.map(function (item) {
|
|
120
|
+
return '<button class=\'' + cls + '\' tabindex=\'0\'>' + item.name + '</button>';
|
|
121
|
+
}).join('');
|
|
122
|
+
}
|
|
123
|
+
}, {
|
|
124
|
+
key: 'removeItem',
|
|
125
|
+
value: function removeItem(item, index) {
|
|
126
|
+
var selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
|
|
127
|
+
selectedItemsList.removeChild(item);
|
|
128
|
+
|
|
129
|
+
var remainingItems = selectedItemsList.getElementsByTagName('div');
|
|
130
|
+
|
|
131
|
+
// Focus management: set focus to the next item, or the search input if no items left
|
|
132
|
+
if (remainingItems.length > 0) {
|
|
133
|
+
if (index < remainingItems.length) {
|
|
134
|
+
remainingItems[index].getElementsByTagName('button')[0].focus();
|
|
135
|
+
} else {
|
|
136
|
+
remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
this.input.focus();
|
|
140
|
+
}
|
|
87
141
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Populate the suggestions box with the filtered results
|
|
97
|
-
suggestionsBox.innerHTML = filtered.map(function (item) {
|
|
98
|
-
return '<button class=\'' + namespace + 'm-multi-select__suggestion-btn\' tabindex=\'0\'>' + item.name + '</button>';
|
|
99
|
-
}).join('');
|
|
100
|
-
// Reset the current focus
|
|
101
|
-
currentFocus = -1;
|
|
102
|
-
});
|
|
142
|
+
this.selectedItems = this.selectedItems.filter(function (name) {
|
|
143
|
+
return name !== item.firstChild.textContent.trim();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}, {
|
|
147
|
+
key: 'addItem',
|
|
148
|
+
value: function addItem(item) {
|
|
149
|
+
var _this3 = this;
|
|
103
150
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
151
|
+
var newItem = document.createElement('li');
|
|
152
|
+
newItem.textContent = item + ' ';
|
|
153
|
+
newItem.classList.add((0, _className2.default)('a-tag'));
|
|
154
|
+
newItem.classList.add((0, _className2.default)(this.baseClassName + '__tag'));
|
|
155
|
+
|
|
156
|
+
var removeBtn = document.createElement('button');
|
|
157
|
+
removeBtn.classList.add((0, _className2.default)(this.baseClassName + '-selected-items__remove-btn'));
|
|
158
|
+
|
|
159
|
+
var buttonTextContainer = document.createElement('span');
|
|
160
|
+
buttonTextContainer.classList.add('u-visuallyhidden');
|
|
161
|
+
removeBtn.appendChild(buttonTextContainer);
|
|
162
|
+
buttonTextContainer.textContent = 'Ta bort ' + item; // Accessibility label for screen readers
|
|
163
|
+
|
|
164
|
+
// Event listener for removing the selected item
|
|
165
|
+
removeBtn.addEventListener('click', function () {
|
|
166
|
+
_this3.removeItem(newItem, Array.from(_this3.selectedItemsList.children).indexOf(newItem));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
newItem.appendChild(removeBtn);
|
|
170
|
+
|
|
171
|
+
this.selectedItemsList.appendChild(newItem);
|
|
172
|
+
this.selectedItems.push(item);
|
|
124
173
|
}
|
|
125
|
-
}
|
|
174
|
+
}, {
|
|
175
|
+
key: 'removeHighlight',
|
|
176
|
+
value: function removeHighlight() {
|
|
177
|
+
var items = this.suggestionsBox.getElementsByClassName(this.baseClassName + '__suggestion-btn');
|
|
178
|
+
|
|
179
|
+
[].forEach.call(items, function (item) {
|
|
180
|
+
item.classList.remove('autocomplete-active');
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}, {
|
|
184
|
+
key: 'highlight',
|
|
185
|
+
value: function highlight(direction) {
|
|
186
|
+
var items = this.suggestionsBox.getElementsByClassName(this.baseClassName + '__suggestion-btn');
|
|
187
|
+
var focus = this.currentFocus;
|
|
188
|
+
|
|
189
|
+
if (direction === 'down') {
|
|
190
|
+
focus = focus >= items.length - 1 ? 0 : focus + 1;
|
|
191
|
+
} else {
|
|
192
|
+
focus = focus <= 0 ? items.length - 1 : focus - 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.setFocus(focus);
|
|
196
|
+
this.removeHighlight();
|
|
126
197
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Add the clicked suggestion to the selected items list
|
|
130
|
-
if (e.target && e.target.classList.contains('suggestion-btn')) {
|
|
131
|
-
addSelectedItem(e.target.textContent);
|
|
132
|
-
suggestionsBox.innerHTML = '';
|
|
133
|
-
multiSelectInput.value = '';
|
|
198
|
+
items[this.currentFocus].classList.add('autocomplete-active');
|
|
199
|
+
items[this.currentFocus].scrollIntoView({ block: 'nearest' });
|
|
134
200
|
}
|
|
135
|
-
}
|
|
136
|
-
|
|
201
|
+
}, {
|
|
202
|
+
key: 'selectHighlighted',
|
|
203
|
+
value: function selectHighlighted() {
|
|
204
|
+
var items = this.suggestionsBox.getElementsByClassName(this.baseClassName + '__suggestion-btn');
|
|
205
|
+
|
|
206
|
+
if (this.currentFocus > -1 && items[this.currentFocus]) {
|
|
207
|
+
this.addItem(items[this.currentFocus].textContent);
|
|
208
|
+
this.clearSuggestions();
|
|
209
|
+
this.input.value = '';
|
|
210
|
+
this.resetFocus();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}]);
|
|
214
|
+
|
|
215
|
+
return MultiSelect;
|
|
216
|
+
}();
|
|
217
|
+
|
|
218
|
+
var multiSelectElements = document.querySelectorAll('.js-m-multi-select');
|
|
137
219
|
|
|
138
220
|
if (multiSelectElements) {
|
|
139
|
-
[].forEach.call(multiSelectElements,
|
|
221
|
+
[].forEach.call(multiSelectElements, function (el) {
|
|
222
|
+
return new MultiSelect(el);
|
|
223
|
+
});
|
|
140
224
|
}
|
package/package.json
CHANGED
|
Binary file
|
package/src/atoms/icon/copy.svg
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" version="1.1" viewBox="0 0 32 32"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" version="1.1" viewBox="0 0 32 32" width="32" height="32"><defs><style>.cls-1{fill:#000;stroke-width:0}</style></defs>
|
|
2
|
+
<path d="M7 16h9v3H7v-3Zm0 6h9v3H7v-3Z" class="cls-1"/>
|
|
3
|
+
<path d="M10 1v8.9H1V31h21.1v-9H31V1H10Zm3 3h15v15h-5.9V9.9H13V4ZM4 28V13h15v15H4Z" class="cls-1"/>
|
|
4
|
+
</svg>
|
|
@@ -186,8 +186,8 @@
|
|
|
186
186
|
<path d="M17.07 6.69l1.414-1.414 6.293 6.293-1.414 1.415z"/>
|
|
187
187
|
</symbol>
|
|
188
188
|
<symbol id="icon-copy" viewbox="0 0 32 32">
|
|
189
|
-
<path d="M7
|
|
190
|
-
<path d="M10
|
|
189
|
+
<path d="M7 16h9v3H7v-3Zm0 6h9v3H7v-3Z" class="cls-1"/>
|
|
190
|
+
<path d="M10 1v8.9H1V31h21.1v-9H31V1H10Zm3 3h15v15h-5.9V9.9H13V4ZM4 28V13h15v15H4Z" class="cls-1"/>
|
|
191
191
|
</symbol>
|
|
192
192
|
<symbol id="icon-speaker" viewbox="0 0 32 32">
|
|
193
193
|
<path d="M18 29a1 1 0 0 1-.57-.18l-10-7A1 1 0 0 1 7 21V11a1 1 0 0 1 .43-.82l10-7a1 1 0 0 1 1-.07A1 1 0 0 1 19 4v24a1 1 0 0 1-.54.89A1 1 0 0 1 18 29zm-9-8.52l8 5.6V5.92l-8 5.6z"/><path class="cls-1" d="M8 22H4a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3h4a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1zM4 12a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h3v-8zM18 21v-2a3 3 0 0 0 2.12-5.12l1.42-1.42A5 5 0 0 1 18 21z"/><path class="cls-1" d="M23.65 22.65a1 1 0 0 1-.7-.29A1 1 0 0 1 23 21a7 7 0 0 0 0-9.9 1 1 0 0 1 1.41-1.41 9 9 0 0 1 0 12.72 1 1 0 0 1-.76.24z"/>
|
package/src/components.js
CHANGED
|
@@ -1,134 +1,182 @@
|
|
|
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.input = this.element.querySelector(`.js-${this.baseClassName}__input`);
|
|
9
|
+
this.suggestionsBox = this.element.querySelector(`.js-${this.baseClassName}-suggestions-box`);
|
|
10
|
+
this.selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
|
|
11
|
+
this.selectedItems = [];
|
|
12
|
+
this.data = [];
|
|
13
|
+
|
|
14
|
+
this.getData();
|
|
15
|
+
this.attach();
|
|
16
|
+
}
|
|
5
17
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
getData() {
|
|
19
|
+
const id = this.input.getAttribute('data-multi-select-suggestions');
|
|
20
|
+
const el = document.getElementById(id);
|
|
9
21
|
|
|
10
|
-
|
|
22
|
+
if (!el) {
|
|
23
|
+
this.data = [];
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
11
26
|
|
|
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');
|
|
27
|
+
this.data = JSON.parse(el.textContent);
|
|
28
|
+
}
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
attach() {
|
|
31
|
+
this.input.addEventListener('input', this.onInput);
|
|
32
|
+
this.input.addEventListener('keydown', this.onKeyDown);
|
|
33
|
+
this.suggestionsBox.addEventListener('click', this.onClick);
|
|
34
|
+
}
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
for (let i = 0; i < items.length; i+=1) {
|
|
26
|
-
items[i].classList.remove('autocomplete-active');
|
|
36
|
+
setFocus(index) {
|
|
37
|
+
this.currentFocus = index;
|
|
27
38
|
}
|
|
28
|
-
}
|
|
29
39
|
|
|
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
|
-
}
|
|
40
|
+
resetFocus() {
|
|
41
|
+
this.setFocus(-1);
|
|
42
|
+
}
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
selectedItemsList.removeChild(item);
|
|
44
|
+
clearSuggestions() {
|
|
45
|
+
this.suggestionsBox.innerHTML = '';
|
|
46
|
+
}
|
|
58
47
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
remainingItems[index].getElementsByTagName('button')[0].focus();
|
|
64
|
-
} else {
|
|
65
|
-
remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
multiSelectInput.focus();
|
|
48
|
+
filterData(query) {
|
|
49
|
+
return this.data
|
|
50
|
+
.filter((item) => item.name.toLowerCase().startsWith(query.toLowerCase()))
|
|
51
|
+
.filter((item) => !this.selectedItems.includes(item.name));
|
|
69
52
|
}
|
|
70
|
-
}
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
54
|
+
populateSuggestions(suggestions) {
|
|
55
|
+
const cls = className(`${this.baseClassName}__suggestion-btn`);
|
|
56
|
+
this.suggestionsBox.innerHTML = suggestions.map((item) => `<button class='${cls}' tabindex='0'>${item.name}</button>`).join('');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
onInput = () => {
|
|
60
|
+
const { value } = this.input;
|
|
76
61
|
|
|
77
|
-
// Event listener for input changes in the search field
|
|
78
|
-
multiSelectInput.addEventListener('input', function () {
|
|
79
|
-
const value = this.value;
|
|
80
62
|
// Clear suggestions if less than 2 characters are typed
|
|
81
63
|
if (value.length < 2) {
|
|
82
|
-
|
|
64
|
+
this.clearSuggestions();
|
|
65
|
+
|
|
83
66
|
return;
|
|
84
67
|
}
|
|
85
68
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
setActive(items);
|
|
105
|
-
// Navigate up in the suggestions list
|
|
106
|
-
} else if (e.keyCode == 38) {
|
|
107
|
-
currentFocus = (currentFocus - 1 + items.length) % items.length;
|
|
108
|
-
setActive(items);
|
|
109
|
-
// Handle Enter key to select a focused item
|
|
110
|
-
} else if (e.keyCode == 13) {
|
|
111
|
-
e.preventDefault();
|
|
112
|
-
if (currentFocus > -1 && items[currentFocus]) {
|
|
113
|
-
addSelectedItem(items[currentFocus].textContent);
|
|
114
|
-
suggestionsBox.innerHTML = '';
|
|
115
|
-
multiSelectInput.value = '';
|
|
116
|
-
currentFocus = -1;
|
|
69
|
+
const suggestions = this.filterData(value);
|
|
70
|
+
|
|
71
|
+
this.populateSuggestions(suggestions);
|
|
72
|
+
this.resetFocus();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
removeItem(item, index) {
|
|
76
|
+
const selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
|
|
77
|
+
selectedItemsList.removeChild(item);
|
|
78
|
+
|
|
79
|
+
const remainingItems = selectedItemsList.getElementsByTagName('div');
|
|
80
|
+
|
|
81
|
+
// Focus management: set focus to the next item, or the search input if no items left
|
|
82
|
+
if (remainingItems.length > 0) {
|
|
83
|
+
if (index < remainingItems.length) {
|
|
84
|
+
remainingItems[index].getElementsByTagName('button')[0].focus();
|
|
85
|
+
} else {
|
|
86
|
+
remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
|
|
117
87
|
}
|
|
88
|
+
} else {
|
|
89
|
+
this.input.focus();
|
|
118
90
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
91
|
+
|
|
92
|
+
this.selectedItems = this.selectedItems
|
|
93
|
+
.filter((name) => name !== item.firstChild.textContent.trim());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
addItem(item) {
|
|
97
|
+
const newItem = document.createElement('li');
|
|
98
|
+
newItem.textContent = `${item} `;
|
|
99
|
+
newItem.classList.add(className('a-tag'));
|
|
100
|
+
newItem.classList.add(className(`${this.baseClassName}__tag`));
|
|
101
|
+
|
|
102
|
+
const removeBtn = document.createElement('button');
|
|
103
|
+
removeBtn.classList.add(className(`${this.baseClassName}-selected-items__remove-btn`));
|
|
104
|
+
|
|
105
|
+
const buttonTextContainer = document.createElement('span');
|
|
106
|
+
buttonTextContainer.classList.add('u-visuallyhidden');
|
|
107
|
+
removeBtn.appendChild(buttonTextContainer);
|
|
108
|
+
buttonTextContainer.textContent = `Ta bort ${item}`; // Accessibility label for screen readers
|
|
109
|
+
|
|
110
|
+
// Event listener for removing the selected item
|
|
111
|
+
removeBtn.addEventListener('click', () => {
|
|
112
|
+
this.removeItem(newItem, Array.from(this.selectedItemsList.children).indexOf(newItem));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
newItem.appendChild(removeBtn);
|
|
116
|
+
|
|
117
|
+
this.selectedItemsList.appendChild(newItem);
|
|
118
|
+
this.selectedItems.push(item);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
removeHighlight() {
|
|
122
|
+
const items = this.suggestionsBox.getElementsByClassName(`${this.baseClassName}__suggestion-btn`);
|
|
123
|
+
|
|
124
|
+
[].forEach.call(items, (item) => {
|
|
125
|
+
item.classList.remove('autocomplete-active');
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
highlight(direction) {
|
|
130
|
+
const items = this.suggestionsBox.getElementsByClassName(`${this.baseClassName}__suggestion-btn`);
|
|
131
|
+
let focus = this.currentFocus;
|
|
132
|
+
|
|
133
|
+
if (direction === 'down') {
|
|
134
|
+
focus = (focus >= items.length - 1) ? 0 : focus + 1;
|
|
135
|
+
} else {
|
|
136
|
+
focus = (focus <= 0) ? items.length - 1 : focus - 1;
|
|
128
137
|
}
|
|
129
|
-
|
|
138
|
+
|
|
139
|
+
this.setFocus(focus);
|
|
140
|
+
this.removeHighlight();
|
|
141
|
+
|
|
142
|
+
items[this.currentFocus].classList.add('autocomplete-active');
|
|
143
|
+
items[this.currentFocus].scrollIntoView({ block: 'nearest' });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
selectHighlighted() {
|
|
147
|
+
const items = this.suggestionsBox.getElementsByClassName(`${this.baseClassName}__suggestion-btn`);
|
|
148
|
+
|
|
149
|
+
if (this.currentFocus > -1 && items[this.currentFocus]) {
|
|
150
|
+
this.addItem(items[this.currentFocus].textContent);
|
|
151
|
+
this.clearSuggestions();
|
|
152
|
+
this.input.value = '';
|
|
153
|
+
this.resetFocus();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
onKeyDown = (e) => {
|
|
158
|
+
if (e.keyCode === 40) {
|
|
159
|
+
this.highlight('down');
|
|
160
|
+
} else if (e.keyCode === 38) {
|
|
161
|
+
this.highlight('up');
|
|
162
|
+
} else if (e.keyCode === 13) {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
|
|
165
|
+
this.selectHighlighted();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
onClick = (e) => {
|
|
170
|
+
if (e.target.classList.contains(className(`${this.baseClassName}__suggestion-btn`))) {
|
|
171
|
+
this.addItem(e.target.textContent);
|
|
172
|
+
this.clearSuggestions();
|
|
173
|
+
this.input.value = '';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
130
176
|
}
|
|
131
177
|
|
|
178
|
+
const multiSelectElements = document.querySelectorAll('.js-m-multi-select');
|
|
179
|
+
|
|
132
180
|
if (multiSelectElements) {
|
|
133
|
-
[].forEach.call(multiSelectElements,
|
|
181
|
+
[].forEach.call(multiSelectElements, (el) => new MultiSelect(el));
|
|
134
182
|
}
|