Initial commit as of 2018-10-16
This commit is contained in:
35
assets/js/ui/color-picker.js
Normal file
35
assets/js/ui/color-picker.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import Picker from './picker';
|
||||
|
||||
|
||||
class ColorPicker extends Picker {
|
||||
constructor(select, label) {
|
||||
super(select);
|
||||
this.label.innerHTML = label;
|
||||
this.container.classList.add('ql-color-picker');
|
||||
[].slice.call(this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function(item) {
|
||||
item.classList.add('ql-primary');
|
||||
});
|
||||
}
|
||||
|
||||
buildItem(option) {
|
||||
let item = super.buildItem(option);
|
||||
item.style.backgroundColor = option.getAttribute('value') || '';
|
||||
return item;
|
||||
}
|
||||
|
||||
selectItem(item, trigger) {
|
||||
super.selectItem(item, trigger);
|
||||
let colorLabel = this.label.querySelector('.ql-color-label');
|
||||
let value = item ? item.getAttribute('data-value') || '' : '';
|
||||
if (colorLabel) {
|
||||
if (colorLabel.tagName === 'line') {
|
||||
colorLabel.style.stroke = value;
|
||||
} else {
|
||||
colorLabel.style.fill = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default ColorPicker;
|
23
assets/js/ui/icon-picker.js
Normal file
23
assets/js/ui/icon-picker.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Picker from './picker';
|
||||
|
||||
|
||||
class IconPicker extends Picker {
|
||||
constructor(select, icons) {
|
||||
super(select);
|
||||
this.container.classList.add('ql-icon-picker');
|
||||
[].forEach.call(this.container.querySelectorAll('.ql-picker-item'), (item) => {
|
||||
item.innerHTML = icons[item.getAttribute('data-value') || ''];
|
||||
});
|
||||
this.defaultItem = this.container.querySelector('.ql-selected');
|
||||
this.selectItem(this.defaultItem);
|
||||
}
|
||||
|
||||
selectItem(item, trigger) {
|
||||
super.selectItem(item, trigger);
|
||||
item = item || this.defaultItem;
|
||||
this.label.innerHTML = item.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default IconPicker;
|
49
assets/js/ui/icons.js
Normal file
49
assets/js/ui/icons.js
Normal file
@@ -0,0 +1,49 @@
|
||||
module.exports = {
|
||||
'align': {
|
||||
'' : require('../assets/icons/align-left.svg'),
|
||||
'center' : require('../assets/icons/align-center.svg'),
|
||||
'right' : require('../assets/icons/align-right.svg'),
|
||||
'justify' : require('../assets/icons/align-justify.svg')
|
||||
},
|
||||
'background': require('../assets/icons/background.svg'),
|
||||
'blockquote': require('../assets/icons/blockquote.svg'),
|
||||
'bold' : require('../assets/icons/bold.svg'),
|
||||
'clean' : require('../assets/icons/clean.svg'),
|
||||
'code' : require('../assets/icons/code.svg'),
|
||||
'code-block': require('../assets/icons/code.svg'),
|
||||
'color' : require('../assets/icons/color.svg'),
|
||||
'direction' : {
|
||||
'' : require('../assets/icons/direction-ltr.svg'),
|
||||
'rtl' : require('../assets/icons/direction-rtl.svg')
|
||||
},
|
||||
'float': {
|
||||
'center' : require('../assets/icons/float-center.svg'),
|
||||
'full' : require('../assets/icons/float-full.svg'),
|
||||
'left' : require('../assets/icons/float-left.svg'),
|
||||
'right' : require('../assets/icons/float-right.svg')
|
||||
},
|
||||
'formula' : require('../assets/icons/formula.svg'),
|
||||
'header': {
|
||||
'1' : require('../assets/icons/header.svg'),
|
||||
'2' : require('../assets/icons/header-2.svg')
|
||||
},
|
||||
'italic' : require('../assets/icons/italic.svg'),
|
||||
'image' : require('../assets/icons/image.svg'),
|
||||
'indent': {
|
||||
'+1' : require('../assets/icons/indent.svg'),
|
||||
'-1' : require('../assets/icons/outdent.svg')
|
||||
},
|
||||
'link' : require('../assets/icons/link.svg'),
|
||||
'list': {
|
||||
'ordered' : require('../assets/icons/list-ordered.svg'),
|
||||
'bullet' : require('../assets/icons/list-bullet.svg'),
|
||||
'check' : require('../assets/icons/list-check.svg')
|
||||
},
|
||||
'script': {
|
||||
'sub' : require('../assets/icons/subscript.svg'),
|
||||
'super' : require('../assets/icons/superscript.svg'),
|
||||
},
|
||||
'strike' : require('../assets/icons/strike.svg'),
|
||||
'underline' : require('../assets/icons/underline.svg'),
|
||||
'video' : require('../assets/icons/video.svg')
|
||||
};
|
186
assets/js/ui/picker.js
Normal file
186
assets/js/ui/picker.js
Normal file
@@ -0,0 +1,186 @@
|
||||
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;
|
53
assets/js/ui/tooltip.js
Normal file
53
assets/js/ui/tooltip.js
Normal file
@@ -0,0 +1,53 @@
|
||||
class Tooltip {
|
||||
constructor(quill, boundsContainer) {
|
||||
this.quill = quill;
|
||||
this.boundsContainer = boundsContainer || document.body;
|
||||
this.root = quill.addContainer('ql-tooltip');
|
||||
this.root.innerHTML = this.constructor.TEMPLATE;
|
||||
if (this.quill.root === this.quill.scrollingContainer) {
|
||||
this.quill.root.addEventListener('scroll', () => {
|
||||
this.root.style.marginTop = (-1*this.quill.root.scrollTop) + 'px';
|
||||
});
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.root.classList.add('ql-hidden');
|
||||
}
|
||||
|
||||
position(reference) {
|
||||
let left = reference.left + reference.width/2 - this.root.offsetWidth/2;
|
||||
// root.scrollTop should be 0 if scrollContainer !== root
|
||||
let top = reference.bottom + this.quill.root.scrollTop;
|
||||
this.root.style.left = left + 'px';
|
||||
this.root.style.top = top + 'px';
|
||||
this.root.classList.remove('ql-flip');
|
||||
let containerBounds = this.boundsContainer.getBoundingClientRect();
|
||||
let rootBounds = this.root.getBoundingClientRect();
|
||||
let shift = 0;
|
||||
if (rootBounds.right > containerBounds.right) {
|
||||
shift = containerBounds.right - rootBounds.right;
|
||||
this.root.style.left = (left + shift) + 'px';
|
||||
}
|
||||
if (rootBounds.left < containerBounds.left) {
|
||||
shift = containerBounds.left - rootBounds.left;
|
||||
this.root.style.left = (left + shift) + 'px';
|
||||
}
|
||||
if (rootBounds.bottom > containerBounds.bottom) {
|
||||
let height = rootBounds.bottom - rootBounds.top;
|
||||
let verticalShift = reference.bottom - reference.top + height;
|
||||
this.root.style.top = (top - verticalShift) + 'px';
|
||||
this.root.classList.add('ql-flip');
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
show() {
|
||||
this.root.classList.remove('ql-editing');
|
||||
this.root.classList.remove('ql-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default Tooltip;
|
Reference in New Issue
Block a user