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

@ambrinchaudhary

No description provided.

@ipeychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

ambrinchaudhary added some commits
@ambrinchaudhary ambrinchaudhary Toolbars are now accesible by keyboards thanks to FocusManager
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
0c97d8d
@ambrinchaudhary ambrinchaudhary Remove autohide for triggerButton
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
f764381
@ambrinchaudhary ambrinchaudhary Remove autohide for toolbar images
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
0b55b94
@ambrinchaudhary ambrinchaudhary handle ESC key when editor is focused
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
038679f
@ambrinchaudhary ambrinchaudhary always hide before showing in order to reset styles
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
a3b888d
@ambrinchaudhary 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
@ambrinchaudhary ambrinchaudhary SF
Signed-off-by: Ambrin Chaudhary <ambrin.chaudhary@liferay.com>
80ca24e
@ambrinchaudhary ambrinchaudhary SF c61b33f
@ipeychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

@ambrinchaudhary ambrinchaudhary changed the title from SF to Accesible toolbar
@ipeychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

@ipeychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

@ipeychev
Owner

Just started reviewing :)

:octocat: Sent from GH.

@ipeychev ipeychev closed this
@ambrinchaudhary ambrinchaudhary deleted the ambrinchaudhary:accessible_toolbars 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. @ambrinchaudhary

    Toolbars are now accesible by keyboards thanks to FocusManager

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

    Remove autohide for triggerButton

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

    Remove autohide for toolbar images

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

    handle ESC key when editor is focused

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

    always hide before showing in order to reset styles

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

    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. @ambrinchaudhary

    SF

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

    SF

    ambrinchaudhary authored
Commits on Oct 1, 2014
  1. @ambrinchaudhary

    refactor

    ambrinchaudhary authored
  2. @ambrinchaudhary
Commits on Oct 2, 2014
  1. @ambrinchaudhary
This page is out of date. Refresh to see the latest.
View
138 src/ui/yui/src/adapter/yui.js
@@ -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.
View
27 src/ui/yui/src/assets/css/alloy-editor-core.css
@@ -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;
+}
View
8 src/ui/yui/src/assets/sass/alloy-editor-core.scss
@@ -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;
}
View
2  src/ui/yui/src/buttons/button-base.js
@@ -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);
},
/**
View
4 src/ui/yui/src/plugins/loader.js
@@ -3,6 +3,10 @@
var hasOwnProperty = Object.prototype.hasOwnProperty;
+ var KEY_ESC = 27;
+
+ var KEY_F10 = 121;
+
if (CKEDITOR.plugins.get('uiloader')) {
return;
}
View
51 src/ui/yui/src/toolbars/toolbar-add.js
@@ -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']
});
View
69 src/ui/yui/src/toolbars/toolbar-base.js
@@ -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']
+});
View
9 src/ui/yui/src/toolbars/toolbar-image.js
@@ -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']
+});
View
10 src/ui/yui/src/toolbars/toolbar-styles.js
@@ -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.