Skip to content

Commit

Permalink
more refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jhchen committed Jul 15, 2016
1 parent f41d9be commit 93b2459
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 223 deletions.
4 changes: 1 addition & 3 deletions quill.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import Picker from './ui/picker';
import ColorPicker from './ui/color-picker';
import IconPicker from './ui/icon-picker';
import Tooltip from './ui/tooltip';
import LinkTooltip from './ui/link-tooltip';

import BubbleTheme from './themes/bubble';
import SnowTheme from './themes/snow';
Expand Down Expand Up @@ -91,8 +90,7 @@ Quill.register({
'ui/picker': Picker,
'ui/icon-picker': IconPicker,
'ui/color-picker': ColorPicker,
'ui/tooltip': Tooltip,
'ui/link-tooltip': LinkTooltip
'ui/tooltip': Tooltip
}, true);


Expand Down
164 changes: 75 additions & 89 deletions themes/bubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,128 +6,114 @@ import { Range } from '../core/selection';
import Tooltip from '../ui/tooltip';


class BubbleTooltip extends Tooltip {
constructor(root, containers) {
super(root, containers);
this.root.innerHTML = [
'<span class="ql-tooltip-arrow"></span>',
'<div class="ql-link-editor">',
'<input type="text">',
'<a class="ql-close"></a>',
'</div>'
].join('');
this.input = this.root.querySelector('input');
this.listen();
class BubbleTheme extends BaseTheme {
constructor(quill, options) {
super(quill, options);
this.quill.container.classList.add('ql-bubble');
}

listen() {
['mousedown', 'touchstart'].forEach((name) => {
this.root.querySelector('.ql-close').addEventListener(name, (event) => {
this.root.classList.remove('ql-editing');
event.preventDefault();
});
});
extendToolbar(toolbar) {
this.tooltip = new BubbleTooltip(this.quill, this.options.bounds);
this.tooltip.root.appendChild(toolbar.container);
this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')));
this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')));
}

position(reference) {
let shift = super.position(reference);
if (shift === 0) return shift;
let arrow = this.root.querySelector('.ql-tooltip-arrow');
arrow.style.marginLeft = '';
arrow.style.marginLeft = (-1*shift - arrow.offsetWidth/2) + 'px';
}
BubbleTheme.DEFAULTS = {
modules: {
toolbar: {
container: [
['bold', 'italic', 'link'],
[{ header: 1 }, { header: 2 }, 'blockquote']
],
handlers: {
link: function(value) {
if (!value) {
this.quill.format('link', false);
} else {
this.quill.theme.tooltip.edit();
}
}
}
}
}
}


class BubbleTheme extends BaseTheme {
constructor(quill, options) {
super(quill, options);
this.quill.container.classList.add('ql-bubble');
}

buildLinkEditor(input) {
class BubbleTooltip extends Tooltip {
constructor(quill, bounds) {
super(quill, bounds);
this.quill.on(Emitter.events.SELECTION_CHANGE, (range) => {
if (range != null && range.length > 0) {
this.tooltip.root.classList.remove('ql-editing');
this.tooltip.show();
this.show();
// Lock our width so we will expand beyond our offsetParent boundaries
this.tooltip.root.style.left = '0px';
this.tooltip.root.style.width = '';
this.tooltip.root.style.width = this.tooltip.root.offsetWidth + 'px';
this.root.style.left = '0px';
this.root.style.width = '';
this.root.style.width = this.root.offsetWidth + 'px';
let lines = this.quill.scroll.lines(range.index, range.length);
if (lines.length === 1) {
this.tooltip.position(this.quill.getBounds(range));
this.position(this.quill.getBounds(range));
} else {
let lastLine = lines[lines.length - 1];
let index = lastLine.offset(this.quill.scroll);
let length = Math.min(lastLine.length() - 1, range.index + range.length - index);
let bounds = this.quill.getBounds(new Range(index, length));
this.tooltip.position(bounds);
this.position(bounds);
}
} else if (document.activeElement !== input && !!this.quill.hasFocus()) {
this.tooltip.hide();
} else if (document.activeElement !== this.textbox && this.quill.hasFocus()) {
this.hide();
}
});
}

listen() {
super.listen();
['mousedown', 'touchstart'].forEach((name) => {
this.root.querySelector('.ql-close').addEventListener(name, (event) => {
this.root.classList.remove('ql-editing');
event.preventDefault();
});
});
this.quill.on(Emitter.events.SCROLL_OPTIMIZE, () => {
// Let selection be restored by toolbar handlers before repositioning
setTimeout(() => {
if (this.tooltip.root.classList.contains('ql-hidden')) return;
if (this.root.classList.contains('ql-hidden')) return;
let range = this.quill.getSelection();
if (range != null) {
this.tooltip.position(this.quill.getBounds(range));
this.position(this.quill.getBounds(range));
}
}, 1);
});
input.addEventListener('keydown', (event) => {
if (Keyboard.match(event, 'enter')) {
let scrollTop = this.quill.root.scrollTop;
this.quill.focus();
this.quill.root.scrollTop = scrollTop;
this.quill.format('link', input.value);
this.tooltip.hide();
input.value = '';
event.preventDefault();
} else if (Keyboard.match(event, 'escape')) {
this.tooltip.root.classList.remove('ql-editing');
event.preventDefault();
}
});
}

extendToolbar(toolbar) {
let container = this.quill.addContainer('ql-tooltip', this.quill.root);
this.tooltip = new BubbleTooltip(container, {
bounds: this.options.bounds,
scroll: this.quill.root
});
this.buildLinkEditor(container.querySelector('input'));
container.appendChild(toolbar.container);
this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')));
this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')));
this.tooltip.hide();
cancel() {
this.show();
}
}
BubbleTheme.DEFAULTS = {
modules: {
toolbar: {
container: [
['bold', 'italic', 'link'],
[{ header: 1 }, { header: 2 }, 'blockquote']
],
handlers: {
link: function(value) {
if (!value) {
this.quill.format('link', false);
} else {
let tooltip = this.quill.theme.tooltip;
tooltip.root.classList.add('ql-editing');
tooltip.input.focus();
}
}
}
}

position(reference) {
let shift = super.position(reference);
if (shift === 0) return shift;
let arrow = this.root.querySelector('.ql-tooltip-arrow');
arrow.style.marginLeft = '';
arrow.style.marginLeft = (-1*shift - arrow.offsetWidth/2) + 'px';
}

save() {
let scrollTop = this.quill.root.scrollTop;
this.quill.focus();
this.quill.root.scrollTop = scrollTop;
this.quill.format('link', this.textbox.value);
this.hide();
this.textbox.value = '';
}
}
BubbleTooltip.TEMPLATE = [
'<span class="ql-tooltip-arrow"></span>',
'<div class="ql-link-editor">',
'<input type="text">',
'<a class="ql-close"></a>',
'</div>'
].join('');


export default BubbleTheme;
110 changes: 107 additions & 3 deletions themes/snow.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Emitter from '../core/emitter';
import BaseTheme from './base';
import LinkTooltip from '../ui/link-tooltip';
import LinkBlot from '../formats/link';
import Picker from '../ui/picker';
import { Range } from '../core/selection';
import Tooltip from '../ui/tooltip';


class SnowTheme extends BaseTheme {
Expand All @@ -15,7 +17,7 @@ class SnowTheme extends BaseTheme {
this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')));
this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')));
if (toolbar.container.querySelector('.ql-link')) {
this.linkTooltip = new LinkTooltip(this.quill);
this.tooltip = new SnowTooltip(this.quill);
this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function(range, context) {
toolbar.handlers['link'].call(toolbar, !context.format.link);
});
Expand All @@ -35,7 +37,7 @@ SnowTheme.DEFAULTS = {
link: function(value) {
if (value) {
let savedRange = this.quill.selection.savedRange;
this.quill.theme.linkTooltip.open(savedRange);
this.quill.theme.tooltip.open(savedRange);
} else {
this.quill.format('link', false);
}
Expand All @@ -46,4 +48,106 @@ SnowTheme.DEFAULTS = {
}


class SnowTooltip extends Tooltip {
constructor(quill, bounds) {
super(quill, bounds);
this.tooltip = new Tooltip(this.container, {
bounds: quill.theme.options.bounds,
scroll: quill.root
});
this.hide();
this.preview = this.container.querySelector('a.ql-preview');
this.textbox = this.container.querySelector('input[type=text]');
this.textbox.addEventListener('keydown', (event) => {
if (Keyboard.match(event, 'enter')) {
this.save();
event.preventDefault();
} else if (Keyboard.match(event, 'escape')) {
this.hide();
event.preventDefault();
}
});
this.container.querySelector('a.ql-action').addEventListener('click', (event) => {
if (this.container.classList.contains('ql-editing')) {
this.save();
event.preventDefault();
} else {
this.edit();
event.preventDefault();
}
});
this.container.querySelector('a.ql-remove').addEventListener('click', (event) => {
this.remove();
event.preventDefault();
});
quill.on(Emitter.events.SELECTION_CHANGE, (range) => {
if (range == null) return;
if (range.length === 0) {
let offset;
[this.link, offset] = this.quill.scroll.descendant(LinkBlot, range.index);
if (this.link != null) {
this.range = new Range(range.index - offset, this.link.length());
return this.show();
}
}
this.hide();
});
}

edit() {
this.container.classList.add('ql-editing');
this.textbox.focus();
this.textbox.setSelectionRange(0, this.textbox.value.length);
}

open() {
this.range = new Range(this.quill.selection.savedRange.index, this.quill.selection.savedRange.length);
this.show();
this.edit();
}

hide() {
this.range = this.link = null;
this.tooltip.hide();
}

remove() {
this.quill.formatText(this.range, 'link', false, Emitter.sources.USER);
this.hide();
}

save() {
let url = this.textbox.value;
let scrollTop = this.quill.root.scrollTop;
this.quill.formatText(this.range, 'link', url, Emitter.sources.USER);
this.quill.root.scrollTop = scrollTop;
this.hide();
}

show() {
this.container.classList.remove('ql-editing');
this.tooltip.show();
let preview, bounds;
let range = this.quill.selection.savedRange;
if (this.link != null) {
preview = this.link.formats()['link'];
} else {
preview = this.quill.getText(range);
if (/^\S+@\S+\.\S+$/.test(preview)) {
preview = 'mailto:' + preview;
}
}
this.preview.textContent = this.textbox.value = preview;
this.preview.setAttribute('href', preview);
this.tooltip.position(this.quill.getBounds(this.range));
}
}
SnowTooltip.TEMPLATE = [
'<a class="ql-preview" target="_blank" href="about:blank"></a>',
'<input type="text">',
'<a class="ql-action"></a>',
'<a class="ql-remove"></a>'
].join('');


export default SnowTheme;
Loading

0 comments on commit 93b2459

Please sign in to comment.