Commit c2111e06 authored by kristipark's avatar kristipark Committed by Commit Bot

[NTP] Improve accessibility for MV tiles and custom links

- Remove outline on click for:
  - Speech microphone icon
  - Shortcut
  - Edit link icon
  - Edit link dialog buttons
- Fix icon for restore default:
  - https://screenshot.googleplex.com/iVG7ohvNcXUp.png
- Prevent keyboard navigation to disabled "Restore default..." options.
- Keyboard navigation will start at edit dialog after it opens.
- Show add shortcut title for adding instead of edit shortcut.
- Can tab to add shortcut button.
- Keyboard navigation will remain within the edit shortcut dialog when
  open.
- Remove dark gray on click for the edit icon when dark theme/custom
  background.
- Clicking "Done" without modifying anything closes the dialog.
- Allow screen reader to move left/right through the links and edit link
  icons. (User must first use ctrl+shift+opt+down to enter the iframe).
- Prefill URL field with "https://". Text will turn dark gray on text
  input.
  - https://screenshot.googleplex.com/oBJNX4KHxeq.png
  - https://screenshot.googleplex.com/YpbDqiz0hkM.png
- Added labels to all buttons for screen readers ("name" attribute).
  - Edit = "Edit shortcut <title>"
  - Delete = "Remove <title>"
  - Cancel = "Cancel"
  - Edit icon = "Edit shortcut <title>"

Bug: 864357
Change-Id: Ie33248a0f633180e81e7a55373b06fb564f5596a
Reviewed-on: https://chromium-review.googlesource.com/1161096
Commit-Queue: Kristi Park <kristipark@chromium.org>
Reviewed-by: default avatarMathieu Perreault <mathp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580949}
parent 631ac11a
...@@ -145,7 +145,7 @@ html[dir=rtl] .bg-option-img { ...@@ -145,7 +145,7 @@ html[dir=rtl] .bg-option-img {
width: 100%; width: 100%;
} }
#custom-link-restore-default .bg-option-img { #custom-links-restore-default .bg-option-img {
background: rgb(241, 243, 244) url(icons/link_gray.svg) no-repeat center; background: rgb(241, 243, 244) url(icons/link_gray.svg) no-repeat center;
border-radius: 50%; border-radius: 50%;
} }
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file. */ * found in the LICENSE file. */
#edit-link-dialog::backdrop { body {
background-color: rgba(255, 255, 255, .75); overflow: hidden;
} }
#edit-link-dialog { #edit-link-dialog {
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
z-index: 10000; z-index: 10000;
} }
#edit-link-dialog::backdrop {
background: transparent;
}
#edit-link-dialog > div { #edit-link-dialog > div {
width: 100%; width: 100%;
} }
...@@ -64,6 +68,10 @@ input { ...@@ -64,6 +68,10 @@ input {
width: calc(100% - 16px); width: calc(100% - 16px);
} }
#url-field:not(.text-modified) {
color: rgba(32, 33, 36, 0.38);
}
.underline { .underline {
border-bottom: 2px solid rgb(26, 115, 232); border-bottom: 2px solid rgb(26, 115, 232);
bottom: 0; bottom: 0;
...@@ -132,15 +140,15 @@ button { ...@@ -132,15 +140,15 @@ button {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
} }
button:disabled {
transition: none;
}
button.primary { button.primary {
background-color: rgb(26, 115, 232); background-color: rgb(26, 115, 232);
color: white; color: white;
} }
button:focus:not(:active) {
box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.4);
}
button.primary:disabled { button.primary:disabled {
background-color: rgb(241, 243, 244); background-color: rgb(241, 243, 244);
color: rgb(128, 134, 139); color: rgb(128, 134, 139);
......
...@@ -17,15 +17,15 @@ ...@@ -17,15 +17,15 @@
<label id="title-field-name" class="field-title"></label> <label id="title-field-name" class="field-title"></label>
<div class="input-container"> <div class="input-container">
<input id="title-field" class="field-input" type="text" <input id="title-field" class="field-input" type="text"
autocomplete="no" tabindex="0" required></input> autocomplete="off" tabindex="0" required></input>
<div class="underline"></div> <div class="underline"></div>
</div> </div>
</div> </div>
<div id="url" class="field-container"> <div id="url" class="field-container">
<label id="url-field-name" class="field-title"></label> <label id="url-field-name" class="field-title"></label>
<div class="input-container"> <div class="input-container">
<input id="url-field" class="field-input" type="text" <input id="url-field" class="field-input" type="text" value="https://"
autocomplete="url" tabindex="0" required></input> autocomplete="off" tabindex="0" required></input>
<div class="underline"></div> <div class="underline"></div>
</div> </div>
<div id="invalid-url" class="error-msg"></div> <div id="invalid-url" class="error-msg"></div>
......
...@@ -35,6 +35,28 @@ const IDS = { ...@@ -35,6 +35,28 @@ const IDS = {
}; };
/**
* Enum for classes.
* @enum {string}
* @const
*/
const CLASSES = {
// Applied if the input field has been modified.
TEXT_MODIFIED: 'text-modified',
};
/**
* Enum for key codes.
* @enum {int}
* @const
*/
const KEYCODES = {
ENTER: 13,
ESC: 27,
};
/** /**
* The origin of this request, i.e. 'https://www.google.TLD' for the remote NTP, * The origin of this request, i.e. 'https://www.google.TLD' for the remote NTP,
* or 'chrome-search://local-ntp' for the local NTP. * or 'chrome-search://local-ntp' for the local NTP.
...@@ -61,6 +83,27 @@ let prepopulatedLink = { ...@@ -61,6 +83,27 @@ let prepopulatedLink = {
}; };
/**
* The title of the dialog when adding a link.
* @type {string}
*/
let addLinkTitle = '';
/**
* The title of the dialog when editing a link.
* @type {string}
*/
let editLinkTitle = '';
/**
* The accessibility title of remove link button.
* @type {string}
*/
let deleteLinkTitle = '';
/** /**
* True if the provided url is valid. * True if the provided url is valid.
* @type {string} * @type {string}
...@@ -91,6 +134,12 @@ function prepopulateFields(rid) { ...@@ -91,6 +134,12 @@ function prepopulateFields(rid) {
prepopulatedLink.rid = rid; prepopulatedLink.rid = rid;
$(IDS.TITLE_FIELD).value = prepopulatedLink.title = data.title; $(IDS.TITLE_FIELD).value = prepopulatedLink.title = data.title;
$(IDS.URL_FIELD).value = prepopulatedLink.url = data.url; $(IDS.URL_FIELD).value = prepopulatedLink.url = data.url;
$(IDS.URL_FIELD).classList.add(CLASSES.TEXT_MODIFIED);
// Set accessibility names.
$(IDS.DELETE).name = deleteLinkTitle + ' ' + data.title;
$(IDS.DONE).name = editLinkTitle + ' ' + data.title;
$(IDS.DONE).title = editLinkTitle;
} }
...@@ -123,9 +172,8 @@ function showInvalidUrlUntilTextInput() { ...@@ -123,9 +172,8 @@ function showInvalidUrlUntilTextInput() {
/** /**
* Send a message to close the edit dialog. Called when the edit flow has been * Send a message to close the edit dialog. Called when the edit flow has been
* completed. If the fields were unchanged, does not update the link data. * completed. If the fields were unchanged, does not update the link data.
* @param {!Event} event The click event.
*/ */
function finishEditLink(event) { function finishEditLink() {
// Show error message for invalid urls. // Show error message for invalid urls.
if (!isValidURL($(IDS.URL_FIELD).value)) { if (!isValidURL($(IDS.URL_FIELD).value)) {
showInvalidUrlUntilTextInput(); showInvalidUrlUntilTextInput();
...@@ -135,17 +183,17 @@ function finishEditLink(event) { ...@@ -135,17 +183,17 @@ function finishEditLink(event) {
let newUrl = ''; let newUrl = '';
let newTitle = ''; let newTitle = '';
if ($(IDS.URL_FIELD).value != prepopulatedLink.url) if ($(IDS.URL_FIELD).value != prepopulatedLink.url &&
$(IDS.URL_FIELD).value != 'https://')
newUrl = $(IDS.URL_FIELD).value; newUrl = $(IDS.URL_FIELD).value;
if ($(IDS.TITLE_FIELD).value != prepopulatedLink.title) if ($(IDS.TITLE_FIELD).value != prepopulatedLink.title)
newTitle = $(IDS.TITLE_FIELD).value; newTitle = $(IDS.TITLE_FIELD).value;
// Do not update link if fields were unchanged. // Update the link only if a field was changed.
if (!newUrl && !newTitle) if (!!newUrl || !!newTitle) {
return; chrome.embeddedSearch.newTabPage.updateCustomLink(
prepopulatedLink.rid, newUrl, newTitle);
chrome.embeddedSearch.newTabPage.updateCustomLink( }
prepopulatedLink.rid, newUrl, newTitle);
closeDialog(); closeDialog();
} }
...@@ -166,13 +214,17 @@ function deleteLink(event) { ...@@ -166,13 +214,17 @@ function deleteLink(event) {
*/ */
function closeDialog() { function closeDialog() {
window.parent.postMessage({cmd: 'closeDialog'}, DOMAIN_ORIGIN); window.parent.postMessage({cmd: 'closeDialog'}, DOMAIN_ORIGIN);
$(IDS.FORM).reset(); // Small delay to allow the dialog close before cleaning up.
$(IDS.URL_FIELD_CONTAINER).classList.remove('invalid'); window.setTimeout(function() {
$(IDS.DELETE).disabled = false; $(IDS.FORM).reset();
$(IDS.DONE).disabled = false; $(IDS.URL_FIELD_CONTAINER).classList.remove('invalid');
prepopulatedLink.rid = -1; $(IDS.URL_FIELD).classList.remove(CLASSES.TEXT_MODIFIED);
prepopulatedLink.title = ''; $(IDS.DELETE).disabled = false;
prepopulatedLink.url = ''; $(IDS.DONE).disabled = false;
prepopulatedLink.rid = -1;
prepopulatedLink.title = '';
prepopulatedLink.url = '';
}, 10);
} }
...@@ -185,15 +237,38 @@ function handlePostMessage(event) { ...@@ -185,15 +237,38 @@ function handlePostMessage(event) {
let args = event.data; let args = event.data;
if (cmd === 'linkData') { if (cmd === 'linkData') {
if (args.tid) { // We are editing a link, prepopulate the link data. if (args.tid) { // We are editing a link, prepopulate the link data.
$(IDS.DIALOG_TITLE).textContent = editLinkTitle;
prepopulateFields(args.tid); prepopulateFields(args.tid);
} else { // We are adding a link, disable the delete button. } else { // We are adding a link, disable the delete button.
$(IDS.DIALOG_TITLE).textContent = addLinkTitle;
$(IDS.DELETE).disabled = true; $(IDS.DELETE).disabled = true;
disableSubmitUntilTextInput(); disableSubmitUntilTextInput();
// Set accessibility names.
$(IDS.DONE).name = $(IDS.DONE).title = addLinkTitle;
} }
} }
} }
/**
* Disables the focus outline for |element| on mousedown.
* @param {Element} element The element to remove the focus outline from.
*/
function disableOutlineOnMouseClick(element) {
element.addEventListener('mousedown', (event) => {
element.style.outline = 'none';
let resetOutline = (event) => {
// Clear current focus to prevent the outline from reappearing when the
// user switches windows.
document.activeElement.blur();
element.style.outline = '';
element.removeEventListener('blur', resetOutline);
};
element.addEventListener('blur', resetOutline);
});
}
/** /**
* Does some initialization and shows the dialog window. * Does some initialization and shows the dialog window.
*/ */
...@@ -216,24 +291,46 @@ function init() { ...@@ -216,24 +291,46 @@ function init() {
} }
// Populate text content. // Populate text content.
$(IDS.DIALOG_TITLE).textContent = queryArgs['title']; addLinkTitle = queryArgs['addTitle'];
editLinkTitle = queryArgs['editTitle'];
deleteLinkTitle = queryArgs['linkRemove'];
$(IDS.DIALOG_TITLE).textContent = addLinkTitle;
$(IDS.TITLE_FIELD_NAME).textContent = queryArgs['nameField']; $(IDS.TITLE_FIELD_NAME).textContent = queryArgs['nameField'];
$(IDS.TITLE_FIELD_NAME).name = queryArgs['nameField']; $(IDS.TITLE_FIELD_NAME).name = queryArgs['nameField'];
$(IDS.URL_FIELD_NAME).textContent = queryArgs['urlField']; $(IDS.URL_FIELD_NAME).textContent = queryArgs['urlField'];
$(IDS.URL_FIELD_NAME).name = queryArgs['urlField']; $(IDS.URL_FIELD_NAME).name = queryArgs['urlField'];
$(IDS.DELETE).textContent = queryArgs['linkRemove']; $(IDS.DELETE).textContent = $(IDS.DELETE).title = queryArgs['linkRemove'];
$(IDS.CANCEL).textContent = queryArgs['linkCancel']; $(IDS.CANCEL).textContent = $(IDS.CANCEL).title = $(IDS.CANCEL).name =
$(IDS.DONE).textContent = queryArgs['linkDone']; queryArgs['linkCancel'];
$(IDS.DONE).textContent = $(IDS.DONE).title = queryArgs['linkDone'];
$(IDS.INVALID_URL).textContent = queryArgs['invalidUrl']; $(IDS.INVALID_URL).textContent = queryArgs['invalidUrl'];
// Set up event listeners. // Set up event listeners.
$(IDS.URL_FIELD).addEventListener('input', (event) => {
if (!$(IDS.URL_FIELD).classList.contains(CLASSES.TEXT_MODIFIED))
$(IDS.URL_FIELD).classList.add(CLASSES.TEXT_MODIFIED);
});
$(IDS.DELETE).addEventListener('click', deleteLink); $(IDS.DELETE).addEventListener('click', deleteLink);
$(IDS.CANCEL).addEventListener('click', closeDialog); $(IDS.CANCEL).addEventListener('click', closeDialog);
$(IDS.FORM).addEventListener('submit', (event) => { $(IDS.FORM).addEventListener('submit', (event) => {
// Prevent the form from submitting and modifying the URL. // Prevent the form from submitting and modifying the URL.
event.preventDefault(); event.preventDefault();
finishEditLink(event); finishEditLink();
}); });
$(IDS.FORM).onkeyup = (event) => {
if (event.keyCode === KEYCODES.ENTER) {
event.preventDefault();
if ($(IDS.DONE).disabled)
closeDialog();
else
finishEditLink();
} else if (event.keyCode === KEYCODES.ESC) {
closeDialog();
}
};
disableOutlineOnMouseClick($(IDS.DELETE));
disableOutlineOnMouseClick($(IDS.CANCEL));
disableOutlineOnMouseClick($(IDS.DONE));
// Change input field name to blue on input field focus. // Change input field name to blue on input field focus.
let changeColor = (fieldTitle) => { let changeColor = (fieldTitle) => {
......
...@@ -75,10 +75,6 @@ body.inited { ...@@ -75,10 +75,6 @@ body.inited {
display: block; display: block;
} }
body.hidden {
overflow: hidden;
}
/* Button defaults vary by platform. Reset CSS so that the NTP can use buttons /* Button defaults vary by platform. Reset CSS so that the NTP can use buttons
* as a kind of clickable div. */ * as a kind of clickable div. */
button { button {
...@@ -676,14 +672,26 @@ html[dir=rtl] #attribution, ...@@ -676,14 +672,26 @@ html[dir=rtl] #attribution,
width: 100%; width: 100%;
} }
#custom-links-edit-dialog {
background: transparent;
border: none;
height: 100%;
padding: 0;
position: absolute;
top: 0;
width: 100%;
}
#custom-links-edit-dialog::backdrop {
background-color: rgba(255, 255, 255, .75);
}
#custom-links-edit { #custom-links-edit {
border: none; border: none;
display: none;
height: 100%; height: 100%;
position: absolute; position: absolute;
top: 0; top: 0;
width: 100%; width: 100%;
z-index: 10000;
} }
#custom-links-edit.show { #custom-links-edit.show {
......
...@@ -110,6 +110,7 @@ var IDS = { ...@@ -110,6 +110,7 @@ var IDS = {
ATTRIBUTION: 'attribution', ATTRIBUTION: 'attribution',
ATTRIBUTION_TEXT: 'attribution-text', ATTRIBUTION_TEXT: 'attribution-text',
CUSTOM_LINKS_EDIT_IFRAME: 'custom-links-edit', CUSTOM_LINKS_EDIT_IFRAME: 'custom-links-edit',
CUSTOM_LINKS_EDIT_IFRAME_DIALOG: 'custom-links-edit-dialog',
FAKEBOX: 'fakebox', FAKEBOX: 'fakebox',
FAKEBOX_INPUT: 'fakebox-input', FAKEBOX_INPUT: 'fakebox-input',
FAKEBOX_TEXT: 'fakebox-text', FAKEBOX_TEXT: 'fakebox-text',
...@@ -353,6 +354,8 @@ function renderTheme() { ...@@ -353,6 +354,8 @@ function renderTheme() {
.classList.toggle( .classList.toggle(
customBackgrounds.CLASSES.OPTION_DISABLED, customBackgrounds.CLASSES.OPTION_DISABLED,
!info.customBackgroundConfigured); !info.customBackgroundConfigured);
$(customBackgrounds.IDS.RESTORE_DEFAULT).tabIndex =
(info.customBackgroundConfigured ? 0 : -1);
if (configData.isGooglePage) { if (configData.isGooglePage) {
// Hide the settings menu or individual options if the related features are // Hide the settings menu or individual options if the related features are
...@@ -714,17 +717,6 @@ function setFakeboxVisibility(show) { ...@@ -714,17 +717,6 @@ function setFakeboxVisibility(show) {
} }
/**
* @param {boolean} show True if do show the edit custom link dialog and disable
* scrolling.
*/
function setEditCustomLinkDialogVisibility(show) {
$(IDS.CUSTOM_LINKS_EDIT_IFRAME)
.classList.toggle(CLASSES.SHOW_EDIT_DIALOG, show);
document.body.classList.toggle(CLASSES.HIDE_BODY_OVERFLOW, show);
}
/** /**
* @param {!Element} element The element to register the handler for. * @param {!Element} element The element to register the handler for.
* @param {number} keycode The keycode of the key to register. * @param {number} keycode The keycode of the key to register.
...@@ -765,6 +757,8 @@ function handlePostMessage(event) { ...@@ -765,6 +757,8 @@ function handlePostMessage(event) {
.classList.toggle( .classList.toggle(
customBackgrounds.CLASSES.OPTION_DISABLED, customBackgrounds.CLASSES.OPTION_DISABLED,
!args.showRestoreDefault); !args.showRestoreDefault);
$(customBackgrounds.IDS.CUSTOM_LINKS_RESTORE_DEFAULT).tabIndex =
(args.showRestoreDefault ? 0 : -1);
} }
} else if (cmd === 'tileBlacklisted') { } else if (cmd === 'tileBlacklisted') {
if (configData.isCustomLinksEnabled) { if (configData.isCustomLinksEnabled) {
...@@ -787,9 +781,12 @@ function handlePostMessage(event) { ...@@ -787,9 +781,12 @@ function handlePostMessage(event) {
} else if (cmd === 'startEditLink') { } else if (cmd === 'startEditLink') {
$(IDS.CUSTOM_LINKS_EDIT_IFRAME) $(IDS.CUSTOM_LINKS_EDIT_IFRAME)
.contentWindow.postMessage({cmd: 'linkData', tid: args.tid}, '*'); .contentWindow.postMessage({cmd: 'linkData', tid: args.tid}, '*');
setEditCustomLinkDialogVisibility(true); // Small delay to allow the dialog to finish setting up before displaying.
window.setTimeout(function() {
$(IDS.CUSTOM_LINKS_EDIT_IFRAME_DIALOG).showModal();
}, 10);
} else if (cmd === 'closeDialog') { } else if (cmd === 'closeDialog') {
setEditCustomLinkDialogVisibility(false); $(IDS.CUSTOM_LINKS_EDIT_IFRAME_DIALOG).close();
} }
} }
...@@ -880,6 +877,26 @@ function addRippleAnimations() { ...@@ -880,6 +877,26 @@ function addRippleAnimations() {
} }
} }
/**
* Disables the focus outline for |element| on mousedown.
* @param {Element} element The element to remove the focus outline from.
*/
function disableOutlineOnMouseClick(element) {
element.addEventListener('mousedown', (event) => {
element.style.outline = 'none';
let resetOutline = (event) => {
// Clear current focus to prevent the outline from reappearing when the
// user switches windows.
document.activeElement.blur();
element.style.outline = '';
element.removeEventListener('blur', resetOutline);
};
element.addEventListener('blur', resetOutline);
});
}
/** /**
* Prepares the New Tab Page by adding listeners, the most visited pages * Prepares the New Tab Page by adding listeners, the most visited pages
* section, and Google-specific elements for a Google-provided page. * section, and Google-specific elements for a Google-provided page.
...@@ -997,6 +1014,7 @@ function init() { ...@@ -997,6 +1014,7 @@ function init() {
inputbox.ondragleave = function() { inputbox.ondragleave = function() {
setFakeboxDragFocus(false); setFakeboxDragFocus(false);
}; };
disableOutlineOnMouseClick($(IDS.FAKEBOX_MICROPHONE));
// Update the fakebox style to match the current key capturing state. // Update the fakebox style to match the current key capturing state.
setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled); setFakeboxFocus(searchboxApiHandle.isKeyCaptureEnabled);
...@@ -1109,7 +1127,10 @@ function init() { ...@@ -1109,7 +1127,10 @@ function init() {
clArgs.push('rtl=1'); clArgs.push('rtl=1');
clArgs.push( clArgs.push(
'title=' + 'addTitle=' +
encodeURIComponent(configData.translatedStrings.addLinkTitle));
clArgs.push(
'editTitle=' +
encodeURIComponent(configData.translatedStrings.editLinkTitle)); encodeURIComponent(configData.translatedStrings.editLinkTitle));
clArgs.push( clArgs.push(
'nameField=' + 'nameField=' +
...@@ -1136,7 +1157,10 @@ function init() { ...@@ -1136,7 +1157,10 @@ function init() {
clIframe.name = IDS.CUSTOM_LINKS_EDIT_IFRAME; clIframe.name = IDS.CUSTOM_LINKS_EDIT_IFRAME;
clIframe.title = configData.translatedStrings.editLinkTitle; clIframe.title = configData.translatedStrings.editLinkTitle;
clIframe.src = 'chrome-search://most-visited/edit.html?' + clArgs.join('&'); clIframe.src = 'chrome-search://most-visited/edit.html?' + clArgs.join('&');
document.body.appendChild(clIframe); let clIframeDialog = document.createElement('dialog');
clIframeDialog.id = IDS.CUSTOM_LINKS_EDIT_IFRAME_DIALOG;
clIframeDialog.appendChild(clIframe);
document.body.appendChild(clIframeDialog);
} }
window.addEventListener('message', handlePostMessage); window.addEventListener('message', handlePostMessage);
......
...@@ -375,14 +375,12 @@ html[dir=rtl] .mv-favicon { ...@@ -375,14 +375,12 @@ html[dir=rtl] .mv-favicon {
display: none; display: none;
} }
.md-tile { .md-tile-container {
border-radius: 4px; border-radius: 4px;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer;
height: var(--md-tile-height); height: var(--md-tile-height);
margin-bottom: var(--md-tile-margin); margin-bottom: var(--md-tile-margin);
opacity: 1; opacity: 1;
padding: var(--md-tile-padding-vertical) var(--md-tile-padding-horizontal);
position: relative; position: relative;
transition-property: transition-property:
background, background-color, border-color, box-shadow, opacity, background, background-color, border-color, box-shadow, opacity,
...@@ -390,29 +388,34 @@ html[dir=rtl] .mv-favicon { ...@@ -390,29 +388,34 @@ html[dir=rtl] .mv-favicon {
width: var(--md-tile-width); width: var(--md-tile-width);
} }
.md-tile {
cursor: pointer;
padding: var(--md-tile-padding-vertical) var(--md-tile-padding-horizontal);
}
.md-empty-tile { .md-empty-tile {
display: none; display: none;
} }
.md-tile:hover { .md-tile-container:hover {
background-color: rgba(33, 32, 36, 0.06); background-color: rgba(33, 32, 36, 0.06);
} }
body.dark-theme .md-tile:hover { body.dark-theme .md-tile-container:hover {
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
} }
.md-tile:hover > .md-menu { .md-tile-container:hover .md-menu {
opacity: 1; opacity: 1;
transition-delay: 500ms; transition-delay: 500ms;
} }
body.dark-theme .md-tile:active .md-menu::after { body.dark-theme .md-tile-container:active + .md-menu::after {
background-color: rgba(33, 32, 36, 0.71); background-color: rgb(189, 193, 198);
transition-delay: 500ms; transition-delay: 0ms;
} }
.md-tile.blacklisted { .md-tile-container.blacklisted {
margin: 0; margin: 0;
padding: 0; padding: 0;
transform: scale(0, 0); transform: scale(0, 0);
...@@ -426,20 +429,10 @@ body.dark-theme .md-tile:active .md-menu::after { ...@@ -426,20 +429,10 @@ body.dark-theme .md-tile:active .md-menu::after {
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;
height: 100%; height: 100%;
position: relative;
width: 100%; width: 100%;
z-index: -1; z-index: -1;
} }
.md-link {
height: 100%;
left: 0;
position: absolute;
right: 0;
top: 0;
width: 100%;
}
.md-icon { .md-icon {
margin-bottom: var(--md-icon-margin-bottom); margin-bottom: var(--md-icon-margin-bottom);
} }
...@@ -508,7 +501,6 @@ body.dark-theme .md-title { ...@@ -508,7 +501,6 @@ body.dark-theme .md-title {
body.using-theme .md-title-container { body.using-theme .md-title-container {
background-color: white; background-color: white;
border-radius: 12px; border-radius: 12px;
height: 24px;
padding: 4px; padding: 4px;
} }
......
...@@ -30,15 +30,15 @@ const CLASSES = { ...@@ -30,15 +30,15 @@ const CLASSES = {
MD_FALLBACK_BACKGROUND: 'md-fallback-background', MD_FALLBACK_BACKGROUND: 'md-fallback-background',
MD_FALLBACK_LETTER: 'md-fallback-letter', MD_FALLBACK_LETTER: 'md-fallback-letter',
MD_FAVICON: 'md-favicon', MD_FAVICON: 'md-favicon',
MD_LINK: 'md-link',
MD_ICON: 'md-icon', MD_ICON: 'md-icon',
MD_ICON_BACKGROUND: 'md-icon-background', MD_ICON_BACKGROUND: 'md-icon-background',
MD_ADD_ICON: 'md-add-icon', MD_ADD_ICON: 'md-add-icon',
MD_ADD_BACKGROUND: 'md-add-background', MD_ADD_BACKGROUND: 'md-add-background',
MD_MENU: 'md-menu', MD_MENU: 'md-menu',
MD_EDIT_MENU: 'md-edit-menu', MD_EDIT_MENU: 'md-edit-menu',
MD_TILE: 'md-tile', MD_TILE: 'md-tile-container',
MD_TILE_INNER: 'md-tile-inner', MD_TILE_INNER: 'md-tile-inner',
MD_TILE_LINK: 'md-tile',
MD_TITLE: 'md-title', MD_TITLE: 'md-title',
MD_TITLE_CONTAINER: 'md-title-container', MD_TITLE_CONTAINER: 'md-title-container',
}; };
...@@ -453,6 +453,25 @@ var isSchemeAllowed = function(url) { ...@@ -453,6 +453,25 @@ var isSchemeAllowed = function(url) {
}; };
/**
* Disables the focus outline for |element| on mousedown.
* @param {Element} element The element to remove the focus outline from.
*/
function disableOutlineOnMouseClick(element) {
element.addEventListener('mousedown', (event) => {
element.style.outline = 'none';
let resetOutline = (event) => {
// Clear current focus to prevent the outline from reappearing when the
// user switches windows.
document.activeElement.blur();
element.style.outline = '';
element.removeEventListener('blur', resetOutline);
};
element.addEventListener('blur', resetOutline);
});
}
/** /**
* Renders a MostVisited tile to the DOM. * Renders a MostVisited tile to the DOM.
* @param {object} data Object containing rid, url, title, favicon, thumbnail, * @param {object} data Object containing rid, url, title, favicon, thumbnail,
...@@ -628,12 +647,14 @@ var renderMostVisitedTile = function(data) { ...@@ -628,12 +647,14 @@ var renderMostVisitedTile = function(data) {
* @return {Element} * @return {Element}
*/ */
function renderMaterialDesignTile(data) { function renderMaterialDesignTile(data) {
let mdTile = document.createElement('a'); let mdTile = document.createElement('div');
mdTile.role = 'none';
if (data == null) { if (data == null) {
mdTile.className = CLASSES.MD_EMPTY_TILE; mdTile.className = CLASSES.MD_EMPTY_TILE;
return mdTile; return mdTile;
} }
mdTile.className = CLASSES.MD_TILE;
if (data.isCustomLink) if (data.isCustomLink)
tilesAreCustomLinks = true; tilesAreCustomLinks = true;
...@@ -643,16 +664,18 @@ function renderMaterialDesignTile(data) { ...@@ -643,16 +664,18 @@ function renderMaterialDesignTile(data) {
// This is set in the load/error event for the favicon image. // This is set in the load/error event for the favicon image.
let tileType = TileVisualType.NONE; let tileType = TileVisualType.NONE;
mdTile.className = CLASSES.MD_TILE; let mdTileLink = document.createElement('a');
mdTile.setAttribute('data-tid', data.tid); mdTileLink.className = CLASSES.MD_TILE_LINK;
mdTile.setAttribute('data-pos', position); mdTileLink.tabIndex = 0;
mdTileLink.setAttribute('data-tid', data.tid);
mdTileLink.setAttribute('data-pos', position);
if (isSchemeAllowed(data.url)) { if (isSchemeAllowed(data.url)) {
mdTile.href = data.url; mdTileLink.href = data.url;
} }
mdTile.setAttribute('aria-label', data.title); mdTileLink.setAttribute('aria-label', data.title);
mdTile.title = data.title; mdTileLink.title = data.title;
mdTile.addEventListener('click', function(ev) { mdTileLink.addEventListener('click', function(ev) {
if (data.isAddButton) { if (data.isAddButton) {
editCustomLink(); editCustomLink();
} else { } else {
...@@ -661,7 +684,7 @@ function renderMaterialDesignTile(data) { ...@@ -661,7 +684,7 @@ function renderMaterialDesignTile(data) {
data.dataGenerationTime); data.dataGenerationTime);
} }
}); });
mdTile.addEventListener('keydown', function(event) { mdTileLink.addEventListener('keydown', function(event) {
if ((event.keyCode == 46 /* DELETE */ || if ((event.keyCode == 46 /* DELETE */ ||
event.keyCode == 8 /* BACKSPACE */) && event.keyCode == 8 /* BACKSPACE */) &&
!data.isAddButton) { !data.isAddButton) {
...@@ -673,14 +696,18 @@ function renderMaterialDesignTile(data) { ...@@ -673,14 +696,18 @@ function renderMaterialDesignTile(data) {
event.preventDefault(); event.preventDefault();
this.click(); this.click();
} else if (event.keyCode == 37 /* LEFT */) { } else if (event.keyCode == 37 /* LEFT */) {
const tiles = document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE); const tiles =
tiles[Math.max(this.getAttribute('data-pos') - 1, 0)].focus(); document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE_LINK);
tiles[Math.max(Number(this.getAttribute('data-pos')) - 1, 0)].focus();
} else if (event.keyCode == 39 /* RIGHT */) { } else if (event.keyCode == 39 /* RIGHT */) {
const tiles = document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE); const tiles =
tiles[Math.min(this.getAttribute('data-pos') + 1, tiles.length - 1)] document.querySelectorAll('#mv-tiles .' + CLASSES.MD_TILE_LINK);
tiles[Math.min(
Number(this.getAttribute('data-pos')) + 1, tiles.length - 1)]
.focus(); .focus();
} }
}); });
disableOutlineOnMouseClick(mdTileLink);
let mdTileInner = document.createElement('div'); let mdTileInner = document.createElement('div');
mdTileInner.className = CLASSES.MD_TILE_INNER; mdTileInner.className = CLASSES.MD_TILE_INNER;
...@@ -754,7 +781,8 @@ function renderMaterialDesignTile(data) { ...@@ -754,7 +781,8 @@ function renderMaterialDesignTile(data) {
mdTitle.style.direction = data.direction || 'ltr'; mdTitle.style.direction = data.direction || 'ltr';
mdTitleContainer.appendChild(mdTitle); mdTitleContainer.appendChild(mdTitle);
mdTileInner.appendChild(mdTitleContainer); mdTileInner.appendChild(mdTitleContainer);
mdTile.appendChild(mdTileInner); mdTileLink.appendChild(mdTileInner);
mdTile.appendChild(mdTileLink);
if (!data.isAddButton) { if (!data.isAddButton) {
let mdMenu = document.createElement('button'); let mdMenu = document.createElement('button');
...@@ -762,6 +790,7 @@ function renderMaterialDesignTile(data) { ...@@ -762,6 +790,7 @@ function renderMaterialDesignTile(data) {
if (isCustomLinksEnabled) { if (isCustomLinksEnabled) {
mdMenu.classList.add(CLASSES.MD_EDIT_MENU); mdMenu.classList.add(CLASSES.MD_EDIT_MENU);
mdMenu.title = queryArgs['editLinkTooltip'] || ''; mdMenu.title = queryArgs['editLinkTooltip'] || '';
mdMenu.name = (queryArgs['editLinkTooltip'] || '') + ' ' + data.title;
mdMenu.addEventListener('click', function(ev) { mdMenu.addEventListener('click', function(ev) {
editCustomLink(data.tid); editCustomLink(data.tid);
ev.preventDefault(); ev.preventDefault();
...@@ -769,18 +798,20 @@ function renderMaterialDesignTile(data) { ...@@ -769,18 +798,20 @@ function renderMaterialDesignTile(data) {
}); });
} else { } else {
mdMenu.title = queryArgs['removeTooltip'] || ''; mdMenu.title = queryArgs['removeTooltip'] || '';
mdMenu.name = (queryArgs['removeTooltip'] || '') + ' ' + data.title;
mdMenu.addEventListener('click', function(ev) { mdMenu.addEventListener('click', function(ev) {
removeAllOldTiles(); removeAllOldTiles();
blacklistTile(mdTile); blacklistTile(mdTileLink);
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
}); });
} }
// Don't allow the event to bubble out to the containing tile, as that would // Don't allow the event to bubble out to the containing tile, as that would
// trigger navigation to the tile URL. // trigger navigation to the tile URL.
mdMenu.addEventListener('keydown', function(event) { mdMenu.addEventListener('keydown', function(ev) {
event.stopPropagation(); event.stopPropagation();
}); });
disableOutlineOnMouseClick(mdMenu);
mdTile.appendChild(mdMenu); mdTile.appendChild(mdMenu);
} }
......
...@@ -193,14 +193,14 @@ std::unique_ptr<base::DictionaryValue> GetTranslatedStrings(bool is_google) { ...@@ -193,14 +193,14 @@ std::unique_ptr<base::DictionaryValue> GetTranslatedStrings(bool is_google) {
IDS_NTP_CUSTOM_BG_PHOTO_SELECTED); IDS_NTP_CUSTOM_BG_PHOTO_SELECTED);
// Custom Links // Custom Links
AddString(translated_strings.get(), "addLink", AddString(translated_strings.get(), "addLinkTitle",
IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TITLE); IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TITLE);
AddString(translated_strings.get(), "addLinkTooltip", AddString(translated_strings.get(), "addLinkTooltip",
IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TOOLTIP); IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TOOLTIP);
AddString(translated_strings.get(), "editLinkTooltip",
IDS_NTP_CUSTOM_LINKS_EDIT_SHORTCUT_TOOLTIP);
AddString(translated_strings.get(), "editLinkTitle", AddString(translated_strings.get(), "editLinkTitle",
IDS_NTP_CUSTOM_LINKS_EDIT_SHORTCUT); IDS_NTP_CUSTOM_LINKS_EDIT_SHORTCUT);
AddString(translated_strings.get(), "editLinkTooltip",
IDS_NTP_CUSTOM_LINKS_EDIT_SHORTCUT_TOOLTIP);
AddString(translated_strings.get(), "nameField", IDS_NTP_CUSTOM_LINKS_NAME); AddString(translated_strings.get(), "nameField", IDS_NTP_CUSTOM_LINKS_NAME);
AddString(translated_strings.get(), "urlField", IDS_NTP_CUSTOM_LINKS_URL); AddString(translated_strings.get(), "urlField", IDS_NTP_CUSTOM_LINKS_URL);
AddString(translated_strings.get(), "linkRemove", AddString(translated_strings.get(), "linkRemove",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment