Improved Squire by visualizing some active buttons states

This commit is contained in:
the-djmaze 2023-04-04 10:15:18 +02:00
parent baacddd324
commit 2e0fc1585d
3 changed files with 62 additions and 39 deletions

View file

@ -19,6 +19,8 @@ const
htmlToPlain = html => rl.Utils.htmlToPlain(html).trim(), htmlToPlain = html => rl.Utils.htmlToPlain(html).trim(),
plainToHtml = text => rl.Utils.plainToHtml(text), plainToHtml = text => rl.Utils.plainToHtml(text),
forEachObjectValue = (obj, fn) => Object.values(obj).forEach(fn),
getFragmentOfChildren = parent => { getFragmentOfChildren = parent => {
let frag = doc.createDocumentFragment(); let frag = doc.createDocumentFragment();
frag.append(...parent.childNodes); frag.append(...parent.childNodes);
@ -127,37 +129,43 @@ class SquireUI
html: 'B', html: 'B',
cmd: () => this.doAction('bold'), cmd: () => this.doAction('bold'),
key: 'B', key: 'B',
hint: 'Bold' hint: 'Bold',
matches: 'B,STRONT'
}, },
italic: { italic: {
html: 'I', html: 'I',
cmd: () => this.doAction('italic'), cmd: () => this.doAction('italic'),
key: 'I', key: 'I',
hint: 'Italic' hint: 'Italic',
matches: 'I'
}, },
underline: { underline: {
html: '<u>U</u>', html: '<u>U</u>',
cmd: () => this.doAction('underline'), cmd: () => this.doAction('underline'),
key: 'U', key: 'U',
hint: 'Underline' hint: 'Underline',
matches: 'U'
}, },
strike: { strike: {
html: '<s>S</s>', html: '<s>S</s>',
cmd: () => this.doAction('strikethrough'), cmd: () => this.doAction('strikethrough'),
key: 'Shift + 7', key: 'Shift + 7',
hint: 'Strikethrough' hint: 'Strikethrough',
matches: 'S'
}, },
sub: { sub: {
html: 'Xₙ', html: 'Xₙ',
cmd: () => this.doAction('subscript'), cmd: () => this.doAction('subscript'),
key: 'Shift + 5', key: 'Shift + 5',
hint: 'Subscript' hint: 'Subscript',
matches: 'SUB'
}, },
sup: { sup: {
html: 'Xⁿ', html: 'Xⁿ',
cmd: () => this.doAction('superscript'), cmd: () => this.doAction('superscript'),
key: 'Shift + 6', key: 'Shift + 6',
hint: 'Superscript' hint: 'Superscript',
matches: 'SUP'
} }
}, },
block: { block: {
@ -165,13 +173,15 @@ class SquireUI
html: '#', html: '#',
cmd: () => this.doList('OL'), cmd: () => this.doList('OL'),
key: 'Shift + 8', key: 'Shift + 8',
hint: 'Ordered list' hint: 'Ordered list',
matches: 'OL'
}, },
ul: { ul: {
html: '⋮', html: '⋮',
cmd: () => this.doList('UL'), cmd: () => this.doList('UL'),
key: 'Shift + 9', key: 'Shift + 9',
hint: 'Unordered list' hint: 'Unordered list',
matches: 'UL'
}, },
quote: { quote: {
html: '"', html: '"',
@ -179,7 +189,8 @@ class SquireUI
let parent = squire.getSelectionClosest('UL,OL,BLOCKQUOTE')?.nodeName; let parent = squire.getSelectionClosest('UL,OL,BLOCKQUOTE')?.nodeName;
('BLOCKQUOTE' == parent) ? squire.decreaseQuoteLevel() : squire.increaseQuoteLevel(); ('BLOCKQUOTE' == parent) ? squire.decreaseQuoteLevel() : squire.increaseQuoteLevel();
}, },
hint: 'Blockquote' hint: 'Blockquote',
matches: 'BLOCKQUOTE'
}, },
indentDecrease: { indentDecrease: {
html: '⇤', html: '⇤',
@ -204,7 +215,8 @@ class SquireUI
url.length ? squire.makeLink(url) : (node && squire.removeLink()); url.length ? squire.makeLink(url) : (node && squire.removeLink());
} }
}, },
hint: 'Link' hint: 'Link',
matches: 'A'
}, },
imageUrl: { imageUrl: {
html: '🖼️', html: '🖼️',
@ -213,12 +225,14 @@ class SquireUI
src = prompt("Image", node?.src || "https://"); src = prompt("Image", node?.src || "https://");
src?.length ? squire.insertImage(src) : (node && squire.detach(node)); src?.length ? squire.insertImage(src) : (node && squire.detach(node));
}, },
hint: 'Image URL' hint: 'Image URL',
matches: 'IMG'
}, },
imageUpload: { imageUpload: {
html: '📂️', html: '📂️',
cmd: () => browseImage.click(), cmd: () => browseImage.click(),
hint: 'Image select', hint: 'Image select',
matches: 'IMG'
} }
}, },
/* /*
@ -241,7 +255,10 @@ class SquireUI
}, },
source: { source: {
html: '👁', html: '👁',
cmd: () => this.setMode('source' == this.mode ? 'wysiwyg' : 'source'), cmd: btn => {
this.setMode('source' == this.mode ? 'wysiwyg' : 'source');
btn.classList.toggle('active', 'source' == this.mode);
},
hint: i18n('EDITOR/TEXT_SWITCHER_SOURCE', 'Source') hint: i18n('EDITOR/TEXT_SWITCHER_SOURCE', 'Source')
} }
} }
@ -362,15 +379,21 @@ class SquireUI
container.append(toolbar, wysiwyg, plain); container.append(toolbar, wysiwyg, plain);
squire.addEventListener('pathChange', e => {
forEachObjectValue(actions, entries => {
forEachObjectValue(entries, cfg => {
// cfg.matches && cfg.input.classList.toggle('active', e.element && e.element.matches(cfg.matches));
cfg.matches && cfg.input.classList.toggle('active', e.element && e.element.closestWithin(cfg.matches, squire.getRoot()));
});
});
});
/* /*
squire.addEventListener('dragover', ); squire.addEventListener('cursor', e => {
squire.addEventListener('drop', ); console.dir({cursor:e.range});
squire.addEventListener('pathChange', ); });
squire.addEventListener('cursor', ); squire.addEventListener('select', e => {
squire.addEventListener('select', ); console.dir({select:e.range});
squire.addEventListener('input', ); });
squire.addEventListener( 'keydown keyup', monitorShiftKey )
squire.addEventListener( 'keydown', onKey )
*/ */
// CKEditor gimmicks used by HtmlEditor // CKEditor gimmicks used by HtmlEditor
@ -393,11 +416,11 @@ class SquireUI
fn = {UL:'makeUnorderedList',OL:'makeOrderedList'}; fn = {UL:'makeUnorderedList',OL:'makeOrderedList'};
(parent == type) ? this.squire.removeList() : this.squire[fn[type]](); (parent == type) ? this.squire.removeList() : this.squire[fn[type]]();
} }
/*
testPresenceinSelection(format, validation) { testPresenceinSelection(format, validation) {
return validation.test(this.squire.getPath()) || this.squire.hasFormat(format); return validation.test(this.squire.getPath()) || this.squire.hasFormat(format);
} }
*/
setMode(mode) { setMode(mode) {
if (this.mode != mode) { if (this.mode != mode) {
let cl = this.container.classList, source = 'source' == this.mode; let cl = this.container.classList, source = 'source' == this.mode;

View file

@ -266,8 +266,7 @@ trait ResponseParser
$oImapResponse, true, $sPreviousAtomUpperCase, '[' $oImapResponse, true, $sPreviousAtomUpperCase, '['
); );
if (null !== $sListBlock) if (null !== $sListBlock) {
{
$sAtomBuilder .= $sListBlock.']'; $sAtomBuilder .= $sListBlock.']';
} }

View file

@ -76,7 +76,8 @@ const
BLOCK = 2, BLOCK = 2,
CONTAINER = 3, CONTAINER = 3,
isLeaf = node => node.nodeType === ELEMENT_NODE && !!leafNodeNames[ node.nodeName ], isElement = node => node.nodeType === ELEMENT_NODE,
isLeaf = node => isElement(node) && !!leafNodeNames[ node.nodeName ],
getNodeCategory = node => { getNodeCategory = node => {
switch (node.nodeType) { switch (node.nodeType) {
@ -157,7 +158,7 @@ const
let path = '', style; let path = '', style;
if (node && node !== root) { if (node && node !== root) {
path = getPath(node.parentNode, root); path = getPath(node.parentNode, root);
if (node.nodeType === ELEMENT_NODE) { if (isElement(node)) {
path += (path ? '>' : '') + node.nodeName; path += (path ? '>' : '') + node.nodeName;
if (node.id) { if (node.id) {
path += '#' + node.id; path += '#' + node.id;
@ -395,7 +396,7 @@ const
frags.push(empty(child)); frags.push(empty(child));
} }
} }
else if (child.nodeType === ELEMENT_NODE) { else if (isElement(child)) {
child.append(...frags.reverse()); child.append(...frags.reverse());
frags = []; frags = [];
_mergeInlines(child, fakeRange); _mergeInlines(child, fakeRange);
@ -407,7 +408,7 @@ const
if (node.nodeType === TEXT_NODE) { if (node.nodeType === TEXT_NODE) {
node = node.parentNode; node = node.parentNode;
} }
if (node.nodeType === ELEMENT_NODE) { if (isElement(node)) {
let fakeRange = { let fakeRange = {
startContainer: range.startContainer, startContainer: range.startContainer,
startOffset: range.startOffset, startOffset: range.startOffset,
@ -425,7 +426,7 @@ const
let parent, last, offset; let parent, last, offset;
while ((parent = container.parentNode) && while ((parent = container.parentNode) &&
parent !== root && parent !== root &&
parent.nodeType === ELEMENT_NODE && isElement(parent) &&
parent.childNodes.length === 1) { parent.childNodes.length === 1) {
container = parent; container = parent;
} }
@ -485,7 +486,7 @@ const
getNodeBefore = (node, offset) => { getNodeBefore = (node, offset) => {
let children = node.childNodes; let children = node.childNodes;
while (offset && node.nodeType === ELEMENT_NODE) { while (offset && isElement(node)) {
node = children[ offset - 1 ]; node = children[ offset - 1 ];
children = node.childNodes; children = node.childNodes;
offset = children.length; offset = children.length;
@ -494,7 +495,7 @@ const
}, },
getNodeAfter = (node, offset) => { getNodeAfter = (node, offset) => {
if (node.nodeType === ELEMENT_NODE) { if (isElement(node)) {
let children = node.childNodes; let children = node.childNodes;
if (offset < children.length) { if (offset < children.length) {
node = children[ offset ]; node = children[ offset ];
@ -1221,7 +1222,7 @@ const
// Focus cursor // Focus cursor
// If there's a <b>/<i> etc. at the beginning of the split // If there's a <b>/<i> etc. at the beginning of the split
// make sure we focus inside it. // make sure we focus inside it.
while (nodeAfterSplit.nodeType === ELEMENT_NODE) { while (isElement(nodeAfterSplit)) {
let child = nodeAfterSplit.firstChild, let child = nodeAfterSplit.firstChild,
next; next;
@ -1525,7 +1526,7 @@ const
child; child;
while (l--) { while (l--) {
child = children[l]; child = children[l];
if (child.nodeType === ELEMENT_NODE && !isLeaf(child)) { if (isElement(child) && !isLeaf(child)) {
removeEmptyInlines(child); removeEmptyInlines(child);
if (!child.firstChild && isInline(child)) { if (!child.firstChild && isInline(child)) {
child.remove(); child.remove();
@ -1538,7 +1539,7 @@ const
// --- // ---
notWSTextNode = node => node.nodeType === ELEMENT_NODE ? node.nodeName === 'BR' : notWS.test(node.data), notWSTextNode = node => isElement(node) ? node.nodeName === 'BR' : notWS.test(node.data),
isLineBreak = (br, isLBIfEmptyBlock) => { isLineBreak = (br, isLBIfEmptyBlock) => {
let walker, block = br.parentNode; let walker, block = br.parentNode;
while (isInline(block)) { while (isInline(block)) {
@ -2139,7 +2140,7 @@ let keyHandlers = {
moveRangeBoundariesUpTree(range, root, root, root); moveRangeBoundariesUpTree(range, root, root, root);
cursorContainer = range.endContainer; cursorContainer = range.endContainer;
cursorOffset = range.endOffset; cursorOffset = range.endOffset;
if (cursorContainer.nodeType === ELEMENT_NODE) { if (isElement(cursorContainer)) {
nodeAfterCursor = cursorContainer.childNodes[ cursorOffset ]; nodeAfterCursor = cursorContainer.childNodes[ cursorOffset ];
if (nodeAfterCursor?.nodeName === 'IMG') { if (nodeAfterCursor?.nodeName === 'IMG') {
event.preventDefault(); event.preventDefault();
@ -2660,15 +2661,15 @@ class Squire
if (range) { if (range) {
let anchor = range.startContainer, let anchor = range.startContainer,
focus = range.endContainer, focus = range.endContainer,
newPath; newPath, node;
if (force || anchor !== this._lastAnchorNode || focus !== this._lastFocusNode) { if (force || anchor !== this._lastAnchorNode || focus !== this._lastFocusNode) {
this._lastAnchorNode = anchor; this._lastAnchorNode = anchor;
this._lastFocusNode = focus; this._lastFocusNode = focus;
newPath = (anchor && focus) ? (anchor === focus ? node = anchor === focus ? focus : null;
getPath(focus, this._root) : '(selection)') : ''; newPath = (anchor && focus) ? (node ? getPath(focus, this._root) : '(selection)') : '';
if (this._path !== newPath) { if (this._path !== newPath) {
this._path = newPath; this._path = newPath;
this.fireEvent('pathChange', { path: newPath }); this.fireEvent('pathChange', { path: newPath, element: isElement(node) ? node : node.parentElement });
} }
} }
this.fireEvent(range.collapsed ? 'cursor' : 'select', { this.fireEvent(range.collapsed ? 'cursor' : 'select', {