Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Accesible toolbar #1

Closed
wants to merge 11 commits into from

2 participants

Ambrin Chaudhary Iliyan Peychev
Ambrin Chaudhary

No description provided.

Iliyan Peychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

ambrinchaudhary added some commits
Ambrin Chaudhary ambrinchaudhary Toolbars are now accesible by keyboards thanks to FocusManager
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
0c97d8d
Ambrin Chaudhary ambrinchaudhary Remove autohide for triggerButton
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
f764381
Ambrin Chaudhary ambrinchaudhary Remove autohide for toolbar images
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
0b55b94
Ambrin Chaudhary ambrinchaudhary handle ESC key when editor is focused
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
038679f
Ambrin Chaudhary ambrinchaudhary always hide before showing in order to reset styles
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
a3b888d
Ambrin Chaudhary ambrinchaudhary Y.soon is not needed if we put focus properly in editor and toolbar
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
1f39705
Ambrin Chaudhary ambrinchaudhary SF
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
80ca24e
Ambrin Chaudhary ambrinchaudhary SF c61b33f
Iliyan Peychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

Ambrin Chaudhary ambrinchaudhary changed the title from SF to Accesible toolbar
Iliyan Peychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

Iliyan Peychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

Iliyan Peychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

Iliyan Peychev ipeychev closed this
Ambrin Chaudhary ambrinchaudhary deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 30, 2014
  1. Ambrin Chaudhary

    Toolbars are now accesible by keyboards thanks to FocusManager

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  2. Ambrin Chaudhary

    Remove autohide for triggerButton

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  3. Ambrin Chaudhary

    Remove autohide for toolbar images

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  4. Ambrin Chaudhary

    handle ESC key when editor is focused

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  5. Ambrin Chaudhary

    always hide before showing in order to reset styles

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  6. Ambrin Chaudhary

    Y.soon is not needed if we put focus properly in editor and toolbar

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  7. Ambrin Chaudhary

    SF

    ambrinchaudhary authored
    Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
  8. Ambrin Chaudhary

    SF

    ambrinchaudhary authored
Commits on Oct 1, 2014
  1. Ambrin Chaudhary

    refactor

    ambrinchaudhary authored
  2. Ambrin Chaudhary
Commits on Oct 2, 2014
  1. Ambrin Chaudhary
This page is out of date. Refresh to see the latest.
138 src/ui/yui/src/adapter/yui.js
View
@@ -4,7 +4,10 @@ YUI.add('alloy-editor', function(Y) {
'use strict';
var Lang = Y.Lang,
- AlloyEditor;
+ AlloyEditor,
+ KEY_ESC = 27,
+ KEY_F10 = 121,
+ KEY_TAB = 9;
/**
* YUI3 Adapter for CKEditor. This class provides YUI3 like way of creating instances of
@@ -44,8 +47,12 @@ YUI.add('alloy-editor', function(Y) {
this._editor = editor;
- this._docInteractHandler = Y.one(Y.config.doc).on(['click', 'keyup'],
- CKEDITOR.tools.debounce(this._onDocInteract, this.get('toolbarsHideDelay'), this));
+ this._eventHandles = [
+ Y.one(Y.config.doc).on(['click', 'keyup'],
+ CKEDITOR.tools.debounce(this._onDocInteract, this.get('toolbarsHideDelay'), this)),
+ Y.one(editor.element.$).on('keydown', this._onEditorKeyDown, this),
+ editor.on('toolbarKeyDown', this._onToolbarKeyDown, this)
+ ];
},
/**
@@ -68,7 +75,75 @@ YUI.add('alloy-editor', function(Y) {
editorInstance.destroy();
}
- this._docInteractHandler.detach();
+ A.Array.invoke(instance._eventHandles, 'detach');
+ },
+
+ /**
+ * It makes a circular search between toolbars to find the next toolbar
+ * that has to be focused.
+ *
+ * @method _focusNextToolbar
+ * @protected
+ */
+ _focusNextToolbar: function() {
+ var currentFocusedToolbar,
+ found,
+ i,
+ indexOfCurrentToolbar,
+ splice,
+ toolbar,
+ toolbars;
+
+ currentFocusedToolbar = this._focusedToolbar;
+
+ toolbars = this._editor.config.toolbars;
+
+ //We need to convert toolbars to an array so we can reorder it
+ toolbars = Object.keys(toolbars).map(function(item) { return toolbars[item] });
+
+ indexOfCurrentToolbar = toolbars.indexOf(currentFocusedToolbar);
+
+ splice = toolbars.splice(indexOfCurrentToolbar, toolbars.length - indexOfCurrentToolbar);
+
+ toolbars = splice.concat(toolbars);
+
+ for (i = 0; i < toolbars.length; i++) {
+ toolbar = toolbars[i];
+
+ if (!found && (toolbar != currentFocusedToolbar) && toolbar._focus()) {
+ this._focusedToolbar = toolbar;
+
+ found = true;
+ }
+ }
+ },
+
+ /**
+ * Searches for the first visible toolbar in editor. If there is none, but
+ * there is a toolbar with a trigger button, this one will be selected.
+ *
+ * @method _focusVisibleToolbar
+ * @protected
+ */
+ _focusVisibleToolbar: function() {
+ var currentFocusedToolbar,
+ found,
+ i,
+ toolbars;
+
+ currentFocusedToolbar = this._focusedToolbar;
+
+ toolbars = this._editor.config.toolbars;
+
+ for (i in toolbars) {
+ if (hasOwnProperty.call(toolbars, i)) {
+ if (!found && (toolbars[i] != currentFocusedToolbar) && toolbars[i]._focus()) {
+ this._focusedToolbar = toolbars[i];
+
+ found = toolbars[i].get('visible');
+ }
+ }
+ }
},
/**
@@ -83,6 +158,23 @@ YUI.add('alloy-editor', function(Y) {
},
/**
+ * Hide all visible toolbars in editor
+ *
+ * @method _hideToolbars
+ * @protected
+ */
+ _hideToolbars: function() {
+ var i,
+ toolbars;
+
+ toolbars = this._editor.config.toolbars;
+
+ for (i in toolbars) {
+ toolbars[i].hide();
+ }
+ },
+
+ /**
* Fires <code>toolbarsHide</code> event if none of the toolbars or their child nodes is the element user is
* currently interacting.
*
@@ -111,6 +203,44 @@ YUI.add('alloy-editor', function(Y) {
},
/**
+ * Handles key events in the editor:
+ * - ALT + F10: access to toolbar
+ * - ESC: hide visible toolbars
+ *
+ * @method _onEditorKeyDown
+ * @param {Event} event keyboard event
+ * @protected
+ */
+ _onEditorKeyDown: function(event) {
+ if (event.altKey && event.keyCode === KEY_F10) {
+ this._focusVisibleToolbar();
+
+ } else if (event.keyCode === KEY_ESC) {
+ this._hideToolbars();
+ }
+ },
+
+ /**
+ * Handles key events in the toolbar:
+ * - TAB: focus next toolbar
+ * - ESC: return focus to editor
+ *
+ * @method _onToolbarKeyDown
+ * @param {Event} eventkeyboard event
+ * @protected
+ */
+ _onToolbarKeyDown: function(event) {
+ if (event.data.keyCode === KEY_TAB) {
+ event.data.preventDefault();
+ this._focusNextToolbar();
+
+ } else if (event.data.keyCode === KEY_ESC) {
+ this._focusedToolbar._removeFocus();
+ this._focusedToolbar = null;
+ }
+ },
+
+ /**
* Validates the allowed content attribute. Look
* [here](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent) for more information about the
* supported values.
27 src/ui/yui/src/assets/css/alloy-editor-core.css
View
@@ -15,51 +15,60 @@
outline: none;
height: 36px;
}
+/* line 16, ../sass/alloy-editor-core.scss */
+.alloy-editor-toolbar .alloy-editor-button:focus {
+ color: #44B6E0;
+}
-/* line 20, ../sass/alloy-editor-core.scss */
+/* line 24, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container {
display: inline-block;
padding: 2px;
}
-/* line 24, ../sass/alloy-editor-core.scss */
+/* line 28, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container input {
outline: none;
width: 162px;
}
-/* line 29, ../sass/alloy-editor-core.scss */
+/* line 33, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container input::-ms-clear {
display: none;
}
-/* line 33, ../sass/alloy-editor-core.scss */
+/* line 37, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container .input-clear {
display: inline-block;
height: 14px;
vertical-align: text-top;
width: 14px;
}
-/* line 39, ../sass/alloy-editor-core.scss */
+/* line 43, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container .input-clear i {
cursor: pointer;
}
-/* line 48, ../sass/alloy-editor-core.scss */
+/* line 52, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-add .alloy-editor-button {
font-size: 20px;
}
-/* line 55, ../sass/alloy-editor-core.scss */
+/* line 59, ../sass/alloy-editor-core.scss */
.alloy-editor-tooltip-link .link-container .icon-link-container {
margin: 0 5px 0 5px;
vertical-align: text-top;
}
-/* line 60, ../sass/alloy-editor-core.scss */
+/* line 64, ../sass/alloy-editor-core.scss */
.alloy-editor-tooltip-link .link-container .link-preview {
display: inline-block;
padding: 4px;
outline: none;
}
-/* line 68, ../sass/alloy-editor-core.scss */
+/* line 72, ../sass/alloy-editor-core.scss */
.alloy-editor-placeholder:empty:not(:focus):before {
content: attr(data-placeholder);
}
+
+/* line 76, ../sass/alloy-editor-core.scss */
+alloy-editor-toolbar .alloy-editor-button.focus {
+ color: #44B6E0;
+}
8 src/ui/yui/src/assets/sass/alloy-editor-core.scss
View
@@ -12,6 +12,10 @@
padding: 10px 10px 8px 10px;
outline: none;
height: 36px;
+
+ &:focus {
+ color: #44B6E0;
+ }
}
}
@@ -67,4 +71,8 @@
.alloy-editor-placeholder:empty:not(:focus):before {
content:attr(data-placeholder);
+}
+
+alloy-editor-toolbar .alloy-editor-button.focus {
+ color: #44B6E0;
}
2  src/ui/yui/src/buttons/button-base.js
View
@@ -89,7 +89,7 @@ YUI.add('button-base', function(Y) {
this.afterHostMethod('renderUI', this.renderUI, this);
this.afterHostMethod('bindUI', this.bindUI, this);
- this.afterHostEvent(['visibleChange', 'actionPerformed'], this.updateUI, this);
+ this.afterHostEvent(['positionChange', 'actionPerformed'], this.updateUI, this);
},
/**
4 src/ui/yui/src/plugins/loader.js
View
@@ -3,6 +3,10 @@
var hasOwnProperty = Object.prototype.hasOwnProperty;
+ var KEY_ESC = 27;
+
+ var KEY_F10 = 121;
+
if (CKEDITOR.plugins.get('uiloader')) {
return;
}
51 src/ui/yui/src/toolbars/toolbar-add.js
View
@@ -18,7 +18,7 @@ YUI.add('toolbar-add', function(Y) {
*
* @class ToolbarAdd
*/
- ToolbarAdd = Y.Base.create('toolbaradd', Y.Widget, [Y.ToolbarBase, Y.ToolbarPosition, Y.WidgetPosition, Y.WidgetPositionConstrain, Y.WidgetAutohide], {
+ ToolbarAdd = Y.Base.create('toolbaradd', Y.Widget, [Y.ToolbarBase, Y.ToolbarPosition, Y.WidgetPosition, Y.WidgetPositionConstrain], {
/**
* Initializer lifecycle implementation for the ToolbarAdd class.
*
@@ -78,6 +78,7 @@ YUI.add('toolbar-add', function(Y) {
this._renderTrigger();
},
+
/**
* Calculates and sets the position of the toolbar.
*
@@ -118,6 +119,26 @@ YUI.add('toolbar-add', function(Y) {
},
/**
+ * If toolbar is visible, puts focus on it with FocusManager.
+ * If not, trigger button is focused
+ *
+ * @method _focus
+ * @protected
+ * @return {Boolean} if toolbar has been focused
+ */
+ _focus: function() {
+ var buttonsContainer = this.get('buttonsContainer');
+
+ if (this.get('visible')) {
+ buttonsContainer.focusManager.focus(0);
+ } else {
+ this._triggerButton.focus();
+ }
+
+ return true;
+ },
+
+ /**
* Hides all present toolbars other than this one.
*
* @method _hideEditorToolbars
@@ -164,7 +185,12 @@ YUI.add('toolbar-add', function(Y) {
y: nativeEvent.pageY
});
- this._showTriggerAtPoint(this._editorNode.getX(), selectionData.region.top + startRect.height / 2);
+ this._triggerButtonPosition = {
+ left: this._editorNode.getX(),
+ top: selectionData.region.top + startRect.height / 2
+ };
+
+ this._showTriggerAtPoint(this._triggerButtonPosition.left, this._triggerButtonPosition.top);
}
},
@@ -214,6 +240,18 @@ YUI.add('toolbar-add', function(Y) {
},
/**
+ * Returns the focus to the editor and shows again the _triggerButton
+ *
+ * @method _removeFocus
+ * @protected
+ */
+ _removeFocus: function() {
+ this.get('editor').focus();
+
+ this._showTriggerAtPoint(this._triggerButtonPosition.left, this._triggerButtonPosition.top);
+ },
+
+ /**
* Creates the container where buttons, attached to the instance of Toolbar should render.
*
* @method _renderButtons
@@ -263,6 +301,8 @@ YUI.add('toolbar-add', function(Y) {
this._triggerButton = triggerButton;
this._triggerButtonContainer = triggerButtonContainer;
+
+ this._triggerButtonContainer.on('keydown', this._onKeyDown, this);
},
/**
@@ -279,6 +319,8 @@ YUI.add('toolbar-add', function(Y) {
this._trigger.hide();
this._editorNode.focus();
+
+ this._focus();
},
/**
@@ -382,14 +424,13 @@ YUI.add('toolbar-add', function(Y) {
Y.ToolbarAdd = ToolbarAdd;
-
/**
* The ToolbarAddTrigger class hosts controls for showing the toolbar with the add controls. This class is intended to be
* used internally by {{#crossLink "ToolbarAdd"}}{{/crossLink}} class.
*
* @class ToolbarAddTrigger
*/
- ToolbarAddTrigger = Y.Base.create('toolbaraddtrigger', Y.Widget, [Y.WidgetPosition, Y.WidgetPositionAlign, Y.WidgetAutohide], {
+ ToolbarAddTrigger = Y.Base.create('toolbaraddtrigger', Y.Widget, [Y.WidgetPosition, Y.WidgetPositionAlign], {
BOUNDING_TEMPLATE: '<div class="alloy-editor-toolbar alloy-editor-toolbar-add-trigger"></div>',
CONTENT_TEMPLATE: '<div class="alloy-editor-toolbar-content btn-toolbar"></div>'
@@ -397,5 +438,5 @@ YUI.add('toolbar-add', function(Y) {
});
}, '0.1', {
- requires: ['widget-base', 'widget-position', 'widget-position-constrain', 'widget-position-align', 'widget-autohide', 'toolbar-base', 'toolbar-position']
+ requires: ['widget-base', 'widget-position', 'widget-position-constrain', 'widget-position-align', 'toolbar-base', 'toolbar-position']
});
69 src/ui/yui/src/toolbars/toolbar-base.js
View
@@ -50,10 +50,33 @@ YUI.add('toolbar-base', function(Y) {
}
);
+ instance.after('render', instance._afterRender, instance);
+
editor.on('editorInteraction', instance._onEditorInteraction, instance);
},
/**
+ * When toolbar has been rendered, initialize the focus manager and attach
+ * listener for keyboard events
+ *
+ * @method _afterRender
+ * @protected
+ */
+ _afterRender: function() {
+ var buttonsContainer = this.get('buttonsContainer');
+
+ buttonsContainer.plug(Y.Plugin.NodeFocusManager, {
+ activeDescendant: 0,
+ circular: true,
+ descendants: 'button',
+ focusClass: 'focus',
+ keys: {next: 'down:39', previous: 'down:37'}
+ });
+
+ Y.one(buttonsContainer.getDOMNode()).on('keydown', this._onKeyDown, this);
+ },
+
+ /**
* Applies transition specified via {{#crossLink "ToolbarBase/transition:attribute"}}{{/crossLink}} attribute.
*
* @method _applyTransition
@@ -128,11 +151,30 @@ YUI.add('toolbar-base', function(Y) {
},
/**
+ * If toolbar is visible, puts focus on it with FocusManager
+ *
+ * @method focus
+ * @protected
+ * @return {Boolean} if toolbar has been focused or not
+ */
+ _focus: function() {
+ var buttonsContainer;
+
+ buttonsContainer = this.get('buttonsContainer');
+
+ if (this.get('visible')) {
+ buttonsContainer.focusManager.focus(0);
+ }
+
+ return this.get('visible');
+ },
+
+ /**
* Returns the container in which all buttons are being rendered.
*
* @method _getButtonsContainer
* @protected
- * @return {Node} The container of all buttons attached to the current isntance of Toolbar.
+ * @return {Node} The container of all buttons attached to the current instance of Toolbar.
*/
_getButtonsContainer: function() {
return this._buttonsContainer;
@@ -198,6 +240,27 @@ YUI.add('toolbar-base', function(Y) {
},
/**
+ * Fires <code>toolbarKeyDown</code> event. Editor should listen this event
+ * and perform the associated action
+ *
+ * @param {Event} event key event
+ * @protected
+ */
+ _onKeyDown: function(event) {
+ this.get('editor').fire('toolbarKeyDown', event);
+ },
+
+ /**
+ * Return focus to editor
+ *
+ * @method removeFocus
+ * @protected
+ */
+ _removeFocus: function() {
+ this.get('editor').focus();
+ },
+
+ /**
* Returns true if the passed node is a child node of the toolbar, false otherwise.
*
* @method ownsNode
@@ -254,5 +317,5 @@ YUI.add('toolbar-base', function(Y) {
Y.ToolbarBase = ToolbarBase;
}, '', {
- requires: ['plugin', 'node-base']
-});
+ requires: ['node-focusmanager', 'node-base', 'plugin']
+});
9 src/ui/yui/src/toolbars/toolbar-image.js
View
@@ -9,9 +9,7 @@ YUI.add('toolbar-image', function(Y) {
*
* @class ToolbarImage
*/
- ToolbarImage = Y.Base.create('toolbarimage', Y.Widget, [Y.WidgetPosition, Y.WidgetPositionConstrain,
- Y.WidgetAutohide, Y.ToolbarBase, Y.ToolbarPosition], {
-
+ ToolbarImage = Y.Base.create('toolbarimage', Y.Widget, [Y.WidgetPosition, Y.WidgetPositionConstrain, Y.WidgetAutohide, Y.ToolbarBase, Y.ToolbarPosition], {
/**
* Creates the container where buttons, attached to the instance of Toolbar should render.
*
@@ -198,6 +196,5 @@ YUI.add('toolbar-image', function(Y) {
Y.ToolbarImage = ToolbarImage;
}, '', {
- requires: ['dom-screen', 'widget-base', 'widget-position', 'widget-position-constrain', 'widget-autohide',
- 'toolbar-base', 'toolbar-position']
-});
+ requires: ['dom-screen', 'widget-base', 'widget-position', 'widget-position-constrain', 'widget-autohide', 'toolbar-base', 'toolbar-position']
+});
10 src/ui/yui/src/toolbars/toolbar-styles.js
View
@@ -9,9 +9,7 @@ YUI.add('toolbar-styles', function(Y) {
*
* @class ToolbarStyles
*/
- ToolbarStyles = Y.Base.create('toolbarstyles', Y.Widget, [Y.WidgetPosition, Y.WidgetAutohide,
- Y.WidgetPositionConstrain, Y.ToolbarBase, Y.ToolbarPosition], {
-
+ ToolbarStyles = Y.Base.create('toolbarstyles', Y.Widget, [Y.WidgetPosition, Y.WidgetAutohide, Y.WidgetPositionConstrain, Y.ToolbarBase, Y.ToolbarPosition], {
/**
* Creates the container where buttons, attached to the instance of Toolbar should render.
*
@@ -109,6 +107,7 @@ YUI.add('toolbar-styles', function(Y) {
this.showAtPoint(position.x, position.y, position.direction);
+ this.fire('positionChange', this);
} else {
this.hide();
}
@@ -176,6 +175,5 @@ YUI.add('toolbar-styles', function(Y) {
Y.ToolbarStyles = ToolbarStyles;
}, '', {
- requires: ['toolbar-base', 'toolbar-position', 'widget-base', 'widget-position', 'widget-position-constrain',
- 'widget-autohide']
-});
+ requires: ['toolbar-base', 'toolbar-position', 'widget-base', 'widget-position', 'widget-position-constrain', 'widget-autohide']
+});
Something went wrong with that request. Please try again.