PluginProbe
seQura / 3.0.5
seQura v3.0.5
4.3.4 4.3.3 4.3.2 4.3.1 trunk 2.0.0 2.0.10 2.0.11 2.0.12 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0.0 3.0.2 3.0.5 3.0.6 3.0.7 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 4.0.0 All 30 releases
sequra / assets / js / src / core / DropdownComponent.js

DropdownComponent.js in seQura 3.0.5, at assets/js/src/core/DropdownComponent.js

153 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 if (!window.SequraFE) {
2 window.SequraFE = {};
3 }
4
5 if (!window.SequraFE.components) {
6 window.SequraFE.components = {};
7 }
8
9 (function () {
10 /**
11 * @typedef DropdownComponentModel
12 *
13 * @property {Option[]} options
14 * @property {string?} name
15 * @property {string?} value
16 * @property {string?} placeholder
17 * @property {(value: string) => void?} onChange
18 * @property {(open: boolean) => void?} onOpenStateChange
19 * @property {boolean?} updateTextOnChange
20 * @property {boolean?} searchable
21 */
22
23 /**
24 * Single-select dropdown component.
25 *
26 * @param {DropdownComponentModel} props
27 *
28 * @constructor
29 */
30 const DropdownComponent = ({
31 options,
32 name,
33 value = '',
34 placeholder,
35 onChange,
36 onOpenStateChange,
37 updateTextOnChange = true,
38 searchable = false
39 }) => {
40 const { elementGenerator: generator, translationService } = SequraFE;
41 const filterItems = (text) => {
42 const filteredItems = text
43 ? options.filter((option) => option.label.toLowerCase().includes(text.toLowerCase()))
44 : options;
45
46 renderOptions(filteredItems);
47 };
48
49 const renderOptions = (options) => {
50 list.innerHTML = '';
51 if (options.length) {
52 options.forEach((option) => {
53 const listItem = generator.createElement(
54 'li',
55 'sqp-dropdown-list-item' + (option === selectedItem ? ' sqs--selected' : ''),
56 option.label
57 );
58 list.append(listItem);
59
60 listItem.addEventListener('click', (e) => {
61 e.stopPropagation();
62 hiddenInput.value = option.value;
63 updateTextOnChange && (buttonSpan.innerHTML = translationService.translate(option.label));
64 list.classList.remove('sqs--show');
65 list.childNodes.forEach((node) => node.classList.remove('sqs--selected'));
66 listItem.classList.add('sqs--selected');
67 wrapper.classList.remove('sqs--active');
68 buttonSpan.classList.add('sqs--selected');
69 selectButton.classList.remove('sqs--search-active');
70 onChange && onChange(option.value);
71 });
72 });
73 } else {
74 list.append(
75 generator.createElement('li', 'sqp-dropdown-list-item sqv--no-items', 'general.noItemsAvailable', {
76 onClick: (e) => {
77 e.stopPropagation();
78 searchInput?.focus();
79 }
80 })
81 );
82 }
83 };
84
85 const hiddenInput = generator.createElement('input', 'sqp-hidden-input', '', { type: 'hidden', name, value });
86 const wrapper = generator.createElement('div', 'sq-single-select-dropdown');
87
88 const selectButton = generator.createElement('button', 'sqp-dropdown-button sqp-field-component', '', {
89 type: 'button'
90 });
91 const selectedItem = options.find((option) => option.value === value);
92 const buttonSpan = generator.createElement(
93 'span',
94 selectedItem ? 'sqs--selected' : '',
95 selectedItem ? selectedItem.label : placeholder
96 );
97 selectButton.append(buttonSpan);
98
99 const searchInput = generator.createElement('input', 'sq-text-input', '', {
100 type: 'text',
101 placeholder: translationService.translate('general.search')
102 });
103 searchInput.addEventListener('input', (event) => filterItems(event.currentTarget?.value || ''));
104 if (searchable) {
105 selectButton.append(searchInput);
106 }
107
108 const list = generator.createElement('ul', 'sqp-dropdown-list');
109 renderOptions(options);
110
111 selectButton.addEventListener('click', (e) => {
112 e.stopPropagation();
113
114 if (searchable && selectButton.classList.contains('sqs--search-active')) {
115 return;
116 }
117
118 list.classList.toggle('sqs--show');
119 wrapper.classList.toggle('sqs--active');
120 onOpenStateChange?.(list.classList.contains('sqs--show'));
121 if (searchable) {
122 selectButton.classList.toggle('sqs--search-active');
123 if (selectButton.classList.contains('sqs--search-active')) {
124 searchInput.focus();
125 searchInput.value = '';
126 filterItems('');
127 }
128 }
129 });
130
131 document.documentElement.addEventListener('click', (event) => {
132 if (!wrapper.contains(event.target) && event.target !== wrapper) {
133 list.classList.remove('sqs--show');
134 wrapper.classList.remove('sqs--active');
135 selectButton.classList.remove('sqs--search-active');
136 onOpenStateChange?.(list.classList.contains('sqs--show'));
137 }
138 });
139
140 wrapper.append(hiddenInput, selectButton, list);
141
142 return wrapper;
143 };
144
145 SequraFE.components.Dropdown = {
146 /**
147 * @param {DropdownComponentModel} config
148 * @returns {HTMLElement}
149 */
150 create: (config) => DropdownComponent(config)
151 };
152 })();
153