forked from FINAKON/HelpProject
1. Initial Commit - a boiler plate code and POC to realize the concept of context sensitive help 2. Frontend code written in ReactJS 3. Backend code written in Java, Spring Boot Framework 4. Frontend Start: pre-requisites : node, npm npm run dev ==> to start the frontend vite server 5. Backend Start: pre-requisites : java, mvn mvn spring-boot:run ==> to start the backend server 6. Visit http://localhost:5173/ for basic demo of help, press F1 in textboxes 7. Visit http://localhost:5173/editor and enter "admin123" to add/modify texts. Happy Coding !!! Thank you, Bhargava.
187 lines
5.6 KiB
JavaScript
187 lines
5.6 KiB
JavaScript
import Keyboard from '../modules/keyboard';
|
|
import DropdownIcon from '../assets/icons/dropdown.svg';
|
|
|
|
let optionsCounter = 0;
|
|
|
|
function toggleAriaAttribute(element, attribute) {
|
|
element.setAttribute(attribute, !(element.getAttribute(attribute) === 'true'));
|
|
}
|
|
|
|
class Picker {
|
|
constructor(select) {
|
|
this.select = select;
|
|
this.container = document.createElement('span');
|
|
this.buildPicker();
|
|
this.select.style.display = 'none';
|
|
this.select.parentNode.insertBefore(this.container, this.select);
|
|
|
|
this.label.addEventListener('mousedown', () => {
|
|
this.togglePicker();
|
|
});
|
|
this.label.addEventListener('keydown', (event) => {
|
|
switch(event.keyCode) {
|
|
// Allows the "Enter" key to open the picker
|
|
case Keyboard.keys.ENTER:
|
|
this.togglePicker();
|
|
break;
|
|
|
|
// Allows the "Escape" key to close the picker
|
|
case Keyboard.keys.ESCAPE:
|
|
this.escape();
|
|
event.preventDefault();
|
|
break;
|
|
default:
|
|
}
|
|
});
|
|
this.select.addEventListener('change', this.update.bind(this));
|
|
}
|
|
|
|
togglePicker() {
|
|
this.container.classList.toggle('ql-expanded');
|
|
// Toggle aria-expanded and aria-hidden to make the picker accessible
|
|
toggleAriaAttribute(this.label, 'aria-expanded');
|
|
toggleAriaAttribute(this.options, 'aria-hidden');
|
|
}
|
|
|
|
buildItem(option) {
|
|
let item = document.createElement('span');
|
|
item.tabIndex = '0';
|
|
item.setAttribute('role', 'button');
|
|
|
|
item.classList.add('ql-picker-item');
|
|
if (option.hasAttribute('value')) {
|
|
item.setAttribute('data-value', option.getAttribute('value'));
|
|
}
|
|
if (option.textContent) {
|
|
item.setAttribute('data-label', option.textContent);
|
|
}
|
|
item.addEventListener('click', () => {
|
|
this.selectItem(item, true);
|
|
});
|
|
item.addEventListener('keydown', (event) => {
|
|
switch(event.keyCode) {
|
|
// Allows the "Enter" key to select an item
|
|
case Keyboard.keys.ENTER:
|
|
this.selectItem(item, true);
|
|
event.preventDefault();
|
|
break;
|
|
|
|
// Allows the "Escape" key to close the picker
|
|
case Keyboard.keys.ESCAPE:
|
|
this.escape();
|
|
event.preventDefault();
|
|
break;
|
|
default:
|
|
}
|
|
});
|
|
|
|
return item;
|
|
}
|
|
|
|
buildLabel() {
|
|
let label = document.createElement('span');
|
|
label.classList.add('ql-picker-label');
|
|
label.innerHTML = DropdownIcon;
|
|
label.tabIndex = '0';
|
|
label.setAttribute('role', 'button');
|
|
label.setAttribute('aria-expanded', 'false');
|
|
this.container.appendChild(label);
|
|
return label;
|
|
}
|
|
|
|
buildOptions() {
|
|
let options = document.createElement('span');
|
|
options.classList.add('ql-picker-options');
|
|
|
|
// Don't want screen readers to read this until options are visible
|
|
options.setAttribute('aria-hidden', 'true');
|
|
options.tabIndex = '-1';
|
|
|
|
// Need a unique id for aria-controls
|
|
options.id = `ql-picker-options-${optionsCounter}`;
|
|
optionsCounter += 1;
|
|
this.label.setAttribute('aria-controls', options.id);
|
|
|
|
this.options = options;
|
|
|
|
[].slice.call(this.select.options).forEach((option) => {
|
|
let item = this.buildItem(option);
|
|
options.appendChild(item);
|
|
if (option.selected === true) {
|
|
this.selectItem(item);
|
|
}
|
|
});
|
|
this.container.appendChild(options);
|
|
}
|
|
|
|
buildPicker() {
|
|
[].slice.call(this.select.attributes).forEach((item) => {
|
|
this.container.setAttribute(item.name, item.value);
|
|
});
|
|
this.container.classList.add('ql-picker');
|
|
this.label = this.buildLabel();
|
|
this.buildOptions();
|
|
}
|
|
|
|
escape() {
|
|
// Close menu and return focus to trigger label
|
|
this.close();
|
|
// Need setTimeout for accessibility to ensure that the browser executes
|
|
// focus on the next process thread and after any DOM content changes
|
|
setTimeout(() => this.label.focus(), 1);
|
|
}
|
|
|
|
close() {
|
|
this.container.classList.remove('ql-expanded');
|
|
this.label.setAttribute('aria-expanded', 'false');
|
|
this.options.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
selectItem(item, trigger = false) {
|
|
let selected = this.container.querySelector('.ql-selected');
|
|
if (item === selected) return;
|
|
if (selected != null) {
|
|
selected.classList.remove('ql-selected');
|
|
}
|
|
if (item == null) return;
|
|
item.classList.add('ql-selected');
|
|
this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item);
|
|
if (item.hasAttribute('data-value')) {
|
|
this.label.setAttribute('data-value', item.getAttribute('data-value'));
|
|
} else {
|
|
this.label.removeAttribute('data-value');
|
|
}
|
|
if (item.hasAttribute('data-label')) {
|
|
this.label.setAttribute('data-label', item.getAttribute('data-label'));
|
|
} else {
|
|
this.label.removeAttribute('data-label');
|
|
}
|
|
if (trigger) {
|
|
if (typeof Event === 'function') {
|
|
this.select.dispatchEvent(new Event('change'));
|
|
} else if (typeof Event === 'object') { // IE11
|
|
let event = document.createEvent('Event');
|
|
event.initEvent('change', true, true);
|
|
this.select.dispatchEvent(event);
|
|
}
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
update() {
|
|
let option;
|
|
if (this.select.selectedIndex > -1) {
|
|
let item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex];
|
|
option = this.select.options[this.select.selectedIndex];
|
|
this.selectItem(item);
|
|
} else {
|
|
this.selectItem(null);
|
|
}
|
|
let isActive = option != null && option !== this.select.querySelector('option[selected]');
|
|
this.label.classList.toggle('ql-active', isActive);
|
|
}
|
|
}
|
|
|
|
|
|
export default Picker;
|