1 | const DropdownIcon = "<svg viewbox=\"0 0 18 18\"><polygon class=\"ql-stroke\" points=\"7 11 9 13 11 11 7 11\"/><polygon class=\"ql-stroke\" points=\"7 7 9 5 11 7 7 7\"/></svg>";
|
2 | let optionsCounter = 0;
|
3 | function toggleAriaAttribute(element, attribute) {
|
4 | element.setAttribute(attribute, `${!(element.getAttribute(attribute) === 'true')}`);
|
5 | }
|
6 | class Picker {
|
7 | constructor(select) {
|
8 | this.select = select;
|
9 | this.container = document.createElement('span');
|
10 | this.buildPicker();
|
11 | this.select.style.display = 'none';
|
12 |
|
13 | this.select.parentNode.insertBefore(this.container, this.select);
|
14 | this.label.addEventListener('mousedown', () => {
|
15 | this.togglePicker();
|
16 | });
|
17 | this.label.addEventListener('keydown', event => {
|
18 | switch (event.key) {
|
19 | case 'Enter':
|
20 | this.togglePicker();
|
21 | break;
|
22 | case 'Escape':
|
23 | this.escape();
|
24 | event.preventDefault();
|
25 | break;
|
26 | default:
|
27 | }
|
28 | });
|
29 | this.select.addEventListener('change', this.update.bind(this));
|
30 | }
|
31 | togglePicker() {
|
32 | this.container.classList.toggle('ql-expanded');
|
33 |
|
34 | toggleAriaAttribute(this.label, 'aria-expanded');
|
35 |
|
36 | toggleAriaAttribute(this.options, 'aria-hidden');
|
37 | }
|
38 | buildItem(option) {
|
39 | const item = document.createElement('span');
|
40 |
|
41 | item.tabIndex = '0';
|
42 | item.setAttribute('role', 'button');
|
43 | item.classList.add('ql-picker-item');
|
44 | const value = option.getAttribute('value');
|
45 | if (value) {
|
46 | item.setAttribute('data-value', value);
|
47 | }
|
48 | if (option.textContent) {
|
49 | item.setAttribute('data-label', option.textContent);
|
50 | }
|
51 | item.addEventListener('click', () => {
|
52 | this.selectItem(item, true);
|
53 | });
|
54 | item.addEventListener('keydown', event => {
|
55 | switch (event.key) {
|
56 | case 'Enter':
|
57 | this.selectItem(item, true);
|
58 | event.preventDefault();
|
59 | break;
|
60 | case 'Escape':
|
61 | this.escape();
|
62 | event.preventDefault();
|
63 | break;
|
64 | default:
|
65 | }
|
66 | });
|
67 | return item;
|
68 | }
|
69 | buildLabel() {
|
70 | const label = document.createElement('span');
|
71 | label.classList.add('ql-picker-label');
|
72 | label.innerHTML = DropdownIcon;
|
73 |
|
74 | label.tabIndex = '0';
|
75 | label.setAttribute('role', 'button');
|
76 | label.setAttribute('aria-expanded', 'false');
|
77 | this.container.appendChild(label);
|
78 | return label;
|
79 | }
|
80 | buildOptions() {
|
81 | const options = document.createElement('span');
|
82 | options.classList.add('ql-picker-options');
|
83 |
|
84 |
|
85 | options.setAttribute('aria-hidden', 'true');
|
86 |
|
87 | options.tabIndex = '-1';
|
88 |
|
89 |
|
90 | options.id = `ql-picker-options-${optionsCounter}`;
|
91 | optionsCounter += 1;
|
92 | this.label.setAttribute('aria-controls', options.id);
|
93 |
|
94 |
|
95 | this.options = options;
|
96 | Array.from(this.select.options).forEach(option => {
|
97 | const item = this.buildItem(option);
|
98 | options.appendChild(item);
|
99 | if (option.selected === true) {
|
100 | this.selectItem(item);
|
101 | }
|
102 | });
|
103 | this.container.appendChild(options);
|
104 | }
|
105 | buildPicker() {
|
106 | Array.from(this.select.attributes).forEach(item => {
|
107 | this.container.setAttribute(item.name, item.value);
|
108 | });
|
109 | this.container.classList.add('ql-picker');
|
110 | this.label = this.buildLabel();
|
111 | this.buildOptions();
|
112 | }
|
113 | escape() {
|
114 |
|
115 | this.close();
|
116 |
|
117 |
|
118 | setTimeout(() => this.label.focus(), 1);
|
119 | }
|
120 | close() {
|
121 | this.container.classList.remove('ql-expanded');
|
122 | this.label.setAttribute('aria-expanded', 'false');
|
123 |
|
124 | this.options.setAttribute('aria-hidden', 'true');
|
125 | }
|
126 | selectItem(item) {
|
127 | let trigger = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
128 | const selected = this.container.querySelector('.ql-selected');
|
129 | if (item === selected) return;
|
130 | if (selected != null) {
|
131 | selected.classList.remove('ql-selected');
|
132 | }
|
133 | if (item == null) return;
|
134 | item.classList.add('ql-selected');
|
135 |
|
136 | this.select.selectedIndex = Array.from(item.parentNode.children).indexOf(item);
|
137 | if (item.hasAttribute('data-value')) {
|
138 |
|
139 | this.label.setAttribute('data-value', item.getAttribute('data-value'));
|
140 | } else {
|
141 | this.label.removeAttribute('data-value');
|
142 | }
|
143 | if (item.hasAttribute('data-label')) {
|
144 |
|
145 | this.label.setAttribute('data-label', item.getAttribute('data-label'));
|
146 | } else {
|
147 | this.label.removeAttribute('data-label');
|
148 | }
|
149 | if (trigger) {
|
150 | this.select.dispatchEvent(new Event('change'));
|
151 | this.close();
|
152 | }
|
153 | }
|
154 | update() {
|
155 | let option;
|
156 | if (this.select.selectedIndex > -1) {
|
157 | const item =
|
158 |
|
159 | this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex];
|
160 | option = this.select.options[this.select.selectedIndex];
|
161 |
|
162 | this.selectItem(item);
|
163 | } else {
|
164 | this.selectItem(null);
|
165 | }
|
166 | const isActive = option != null && option !== this.select.querySelector('option[selected]');
|
167 | this.label.classList.toggle('ql-active', isActive);
|
168 | }
|
169 | }
|
170 | export default Picker;
|
171 |
|
\ | No newline at end of file |