-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
issue-705: Leaf BlockSettings and InlineTools via keyboard #723
Changes from 32 commits
84689b5
d2cb37f
7d1ce59
a750894
4073724
a146c2e
f0d9548
d5bc6fa
57b5a09
f554945
53df2d5
ee9321a
5f71c6f
00622cd
b2d0acc
72c0987
5f0d242
800657e
2de4318
27cbaa5
ca58f74
3befe95
f518a67
1cd6149
0ab6a29
4816fb4
f25c497
b730ed8
e99b21c
1ca9b64
c5f1ba3
ffcf176
9ebc7a4
9d2a009
e37b3bf
0f68cfd
2bac1a6
3d3e7f6
14a3b30
7ac2e42
1b176a5
5a734b3
eb32095
460bf4b
202f5eb
369ce68
f4dd0c9
092281e
a7d5123
e64e948
031d687
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -520,6 +520,80 @@ export default class Dom { | |
} | ||
|
||
/** | ||
* Leafs nodes inside the target list from active element | ||
* | ||
* @param {HTMLElement[]} nodeList - target list of nodes | ||
* @param {number} activeIndex — index of active node. By default it must be -1 | ||
* @param {string} direction - leaf direction. Can be 'left' or 'right' | ||
* @param {string} activeCSSClass - css class that will be added | ||
* | ||
* @return {Number} index of active node | ||
*/ | ||
public static leafNodesAndReturnIndex(nodeList, activeIndex, direction, activeCSSClass = 'cdx-settings-button--focused'): number { | ||
/** | ||
* If activeButtonIndex === -1 then we have no chosen Tool in Toolbox | ||
*/ | ||
if (activeIndex === -1) { | ||
/** | ||
* Normalize "previous" Tool index depending on direction. | ||
* We need to do this to highlight "first" Tool correctly | ||
* | ||
* Order of Tools: [0] [1] ... [n - 1] | ||
* [0 = n] because of: n % n = 0 % n | ||
* | ||
* Direction 'right': for [0] the [n - 1] is a previous index | ||
* [n - 1] -> [0] | ||
* | ||
* Direction 'left': for [n - 1] the [0] is a previous index | ||
* [n - 1] <- [0] | ||
* | ||
* @type {number} | ||
*/ | ||
activeIndex = direction === 'right' ? -1 : 0; | ||
} else { | ||
/** | ||
* If we have chosen Tool then remove highlighting | ||
*/ | ||
(nodeList[activeIndex] as HTMLElement).classList.remove(activeCSSClass); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Нужно задать типы аргументов и не придется так писать |
||
} | ||
|
||
/** | ||
* Count index for next Tool | ||
*/ | ||
if (direction === 'right') { | ||
/** | ||
* If we go right then choose next (+1) Tool | ||
* @type {number} | ||
*/ | ||
activeIndex = (activeIndex + 1) % nodeList.length; | ||
} else { | ||
/** | ||
* If we go left then choose previous (-1) Tool | ||
* Before counting module we need to add length before because of "The JavaScript Modulo Bug" | ||
* @type {number} | ||
*/ | ||
activeIndex = (nodeList.length + activeIndex - 1) % nodeList.length; | ||
} | ||
|
||
if (Dom.isNativeInput(nodeList[activeIndex])) { | ||
/** | ||
* Focus input | ||
*/ | ||
nodeList[activeIndex].focus(); | ||
} | ||
|
||
/** | ||
* Highlight new chosen Tool | ||
*/ | ||
(nodeList[activeIndex] as HTMLElement).classList.add(activeCSSClass); | ||
|
||
/** | ||
* Return Active index | ||
*/ | ||
return activeIndex; | ||
} | ||
|
||
/* | ||
* Helper for get holder from {string} or return HTMLElement | ||
* @param element | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,16 @@ | |
*/ | ||
import Module from '../__module'; | ||
import _ from '../utils'; | ||
import SelectionUtils from '../selection'; | ||
|
||
export default class BlockEvents extends Module { | ||
|
||
/** | ||
* SelectionUtils instance | ||
* @type {SelectionUtils} | ||
*/ | ||
private selection: SelectionUtils = new SelectionUtils(); | ||
|
||
/** | ||
* All keydowns on Block | ||
* @param {KeyboardEvent} event - keydown | ||
|
@@ -62,7 +70,12 @@ export default class BlockEvents extends Module { | |
return; | ||
} | ||
|
||
this.Editor.Toolbar.close(); | ||
/** | ||
* Leaf Toolbar component's Nodes in case of Tab press | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. коммент не соответствует коду |
||
*/ | ||
if (event.keyCode !== _.keyCodes.TAB) { | ||
neSpecc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.Editor.Toolbar.close(); | ||
} | ||
|
||
const cmdKey = event.ctrlKey || event.metaKey; | ||
const altKey = event.altKey; | ||
|
@@ -110,6 +123,10 @@ export default class BlockEvents extends Module { | |
|
||
const {currentBlock} = this.Editor.BlockManager; | ||
|
||
if (!currentBlock) { | ||
return; | ||
} | ||
|
||
/** Prevent Default behaviour */ | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
|
@@ -118,32 +135,73 @@ export default class BlockEvents extends Module { | |
const shiftKey = event.shiftKey, | ||
direction = shiftKey ? 'left' : 'right'; | ||
|
||
/** | ||
* Don't show Plus and Toolbox near not-inital Tools | ||
*/ | ||
if (!this.Editor.Tools.isInitial(currentBlock.tool)) { | ||
return; | ||
} | ||
|
||
if (currentBlock.isEmpty) { | ||
neSpecc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/** | ||
* For empty Blocks we show Plus button via Toobox only for initial Blocks | ||
*/ | ||
if (!this.Editor.Tools.isInitial(currentBlock.tool)) { | ||
return; | ||
} | ||
|
||
/** | ||
* Work with Toolbox | ||
* ------------------ | ||
* | ||
* If Toolbox is not open, then just open it and show plus button | ||
* Next Tab press will leaf Toolbox Tools | ||
*/ | ||
if (!this.Editor.Toolbar.opened) { | ||
this.Editor.Toolbar.open(false , false); | ||
this.Editor.Toolbar.plusButton.show(); | ||
} else { | ||
this.Editor.Toolbox.leaf(direction); | ||
} | ||
|
||
this.Editor.Toolbox.open(); | ||
} | ||
|
||
if (this.Editor.Toolbox.opened) { | ||
this.Editor.Toolbox.leaf(direction); | ||
} else if (!currentBlock.isEmpty && SelectionUtils.isTextSelected) { | ||
/** | ||
* Work with Inline Tools | ||
* ----------------------- | ||
* | ||
* If InlineToolbar is not open, just open it and focus first button | ||
* Next Tab press will leaf InlineToolbar Tools | ||
*/ | ||
if (this.Editor.InlineToolbar.opened) { | ||
this.Editor.InlineToolbar.leaf(direction); | ||
} | ||
} else { | ||
/** | ||
* Work with Block Tunes | ||
* ---------------------- | ||
* | ||
* If BlockSettings is not open, then open BlockSettings | ||
* Next Tab press will leaf Settings Buttons | ||
*/ | ||
if (!this.Editor.BlockSettings.opened) { | ||
this.Editor.BlockSettings.open(); | ||
} else { | ||
this.Editor.BlockSettings.leaf(direction); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Escape pressed | ||
* @param event | ||
* If some of Toolbar components are opened, then close it otherwise close Toolbar | ||
* | ||
* @param {Event} event | ||
*/ | ||
public escapePressed(event): void { } | ||
public escapePressed(event): void { | ||
if (this.Editor.Toolbox.opened) { | ||
this.Editor.Toolbox.close(); | ||
} else if (this.Editor.BlockSettings.opened) { | ||
this.Editor.BlockSettings.close(); | ||
} else if (this.Editor.InlineToolbar.opened) { | ||
this.Editor.InlineToolbar.close(); | ||
} else { | ||
this.Editor.Toolbar.close(); | ||
} | ||
} | ||
|
||
/** | ||
* Add drop target styles | ||
|
@@ -231,18 +289,32 @@ export default class BlockEvents extends Module { | |
* Don't handle Enter keydowns when Tool sets enableLineBreaks to true. | ||
* Uses for Tools like <code> where line breaks should be handled by default behaviour. | ||
*/ | ||
if (tool && tool[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) { | ||
if (tool | ||
&& tool[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS] | ||
&& !this.Editor.BlockSettings.opened | ||
&& !this.Editor.InlineToolbar.opened) { | ||
return; | ||
} | ||
|
||
if (this.Editor.Toolbox.opened && this.Editor.Toolbox.getActiveTool) { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
event.stopImmediatePropagation(); | ||
|
||
this.Editor.Toolbox.toolButtonActivate(event, this.Editor.Toolbox.getActiveTool); | ||
return; | ||
} | ||
|
||
if (this.Editor.InlineToolbar.opened && this.Editor.InlineToolbar.getActiveButton) { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
event.stopImmediatePropagation(); | ||
|
||
(this.Editor.InlineToolbar.getActiveButton as HTMLElement).click(); | ||
this.Editor.InlineToolbar.dropActiveButtonIndex(); | ||
return; | ||
} | ||
|
||
/** | ||
* Allow to create linebreaks by Shift+Enter | ||
*/ | ||
|
@@ -441,8 +513,9 @@ export default class BlockEvents extends Module { | |
*/ | ||
private needToolbarClosing(event) { | ||
const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbox.opened), | ||
flippingToolboxItems = event.keyCode === _.keyCodes.TAB; | ||
blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened), | ||
flippingToolBarItems = event.keyCode === _.keyCodes.TAB; | ||
|
||
return !(event.shiftKey || flippingToolboxItems || toolboxItemSelected); | ||
return !(event.shiftKey || flippingToolBarItems || toolboxItemSelected || blockSettingsItemSelected); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,8 @@ export default class BlockSettings extends Module { | |
defaultSettings: 'ce-settings__default-zone', | ||
|
||
button: 'ce-settings__button', | ||
|
||
activeButton : 'cdx-settings-button--active', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. а где это используется? |
||
}; | ||
} | ||
|
||
|
@@ -58,6 +60,16 @@ export default class BlockSettings extends Module { | |
defaultSettings: null, | ||
}; | ||
|
||
/** | ||
* List of buttons | ||
*/ | ||
private buttons: Node[] = []; | ||
|
||
/** | ||
* Index of active button | ||
*/ | ||
private activeButtonIndex: number = -1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. focusedButtonIndex |
||
|
||
/** | ||
* Panel with block settings with 2 sections: | ||
* - Tool's Settings | ||
|
@@ -106,8 +118,62 @@ export default class BlockSettings extends Module { | |
|
||
/** Tell to subscribers that block settings is closed */ | ||
this.Editor.Events.emit(this.events.closed); | ||
|
||
/** Clear cached buttons */ | ||
this.buttons = []; | ||
|
||
/** Clear focus on active button */ | ||
this.activeButtonIndex = -1; | ||
|
||
} | ||
|
||
/** | ||
* @todo optimize | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. как? может сразу сделать? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. надо оптимизировать код и удалить todo |
||
* @return {HTMLElement[]} | ||
*/ | ||
public get blockTunesButtons(): Node[] { | ||
/** | ||
* Return from cache | ||
*/ | ||
if (this.buttons.length !== 0) { | ||
return this.buttons; | ||
} | ||
|
||
const toolSettings = this.nodes.toolSettings.querySelectorAll(`.${this.Editor.StylesAPI.classes.settingsButton}`); | ||
const defaultSettings = this.nodes.defaultSettings.querySelectorAll(`.${BlockSettings.CSS.button}`); | ||
|
||
toolSettings.forEach((item, index) => { | ||
this.buttons.push(item); | ||
if (item.classList.contains(BlockSettings.CSS.activeButton)) { | ||
this.activeButtonIndex = index; | ||
} | ||
}); | ||
|
||
defaultSettings.forEach((item) => { | ||
this.buttons.push(item); | ||
}); | ||
|
||
return this.buttons; | ||
} | ||
|
||
/** | ||
* Leaf Block Tunes | ||
* @param {string} direction | ||
*/ | ||
public leaf(direction: string = 'right'): void { | ||
this.activeButtonIndex = $.leafNodesAndReturnIndex(this.blockTunesButtons, this.activeButtonIndex, direction); | ||
} | ||
|
||
/** | ||
* @return {HTMLElement} | ||
neSpecc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
public get getActiveButton(): HTMLElement { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getFocusedButton |
||
if (this.activeButtonIndex === -1) { | ||
return null; | ||
} | ||
|
||
return (this.buttons[this.activeButtonIndex] as HTMLElement); | ||
} | ||
/** | ||
* Add Tool's settings | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
лучше не хардкодить класс, а сделать его обязательным