Bug 983712 - Fix the GCLI menu to remove scrollbars; r=mratcliffe

This commit is contained in:
Joe Walker 2014-04-13 07:47:30 +01:00
parent c7aa2eafde
commit 940d0377a1
7 changed files with 187 additions and 98 deletions

View File

@ -17,15 +17,35 @@
content: '\bb';
}
.gcli-menu-option {
overflow: hidden;
.gcli-menu-template {
white-space: nowrap;
cursor: pointer;
width: 290px;
display: flex;
}
.gcli-menu-template {
border-collapse: collapse;
width: 100%;
.gcli-menu-names {
white-space: nowrap;
flex-grow: 0;
flex-shrink: 0;
}
.gcli-menu-descs {
flex-grow: 1;
flex-shrink: 1;
}
.gcli-menu-name,
.gcli-menu-desc {
white-space: nowrap;
}
.gcli-menu-name {
-moz-padding-end: 10px;
}
.gcli-menu-desc {
text-overflow: ellipsis;
overflow: hidden;
}
.gcli-menu-name,

View File

@ -120,12 +120,13 @@
color: hsl(210,30%,75%);
}
.gcli-menu-option:hover {
.gcli-menu-name:hover,
.gcli-menu-desc:hover {
background-color: hsla(0,0%,0%,.3);
}
.gcli-menu-highlight,
.gcli-menu-highlight.gcli-menu-option:hover {
.gcli-menu-highlight:hover {
background-color: hsla(0,100%,100%,.1);
}

View File

@ -122,12 +122,13 @@
color: hsl(210,30%,75%);
}
.gcli-menu-option:hover {
.gcli-menu-name:hover,
.gcli-menu-desc:hover {
background-color: hsla(0,0%,0%,.3);
}
.gcli-menu-highlight,
.gcli-menu-highlight.gcli-menu-option:hover {
.gcli-menu-highlight:hover {
background-color: hsla(0,100%,100%,.1);
}

View File

@ -120,12 +120,13 @@
color: hsl(210,30%,75%);
}
.gcli-menu-option:hover {
.gcli-menu-name:hover,
.gcli-menu-desc:hover {
background-color: hsla(0,0%,0%,.3);
}
.gcli-menu-highlight,
.gcli-menu-highlight.gcli-menu-option:hover {
.gcli-menu-highlight:hover {
background-color: hsla(0,100%,100%,.1);
}

View File

@ -4,62 +4,57 @@
font-size: 90%;
}
.gcli-menu-template {
column-width: 20em;
column-rule-style: solid;
column-rule-width: 1px;
-moz-column-width: 20em;
-moz-column-rule-style: solid;
-moz-column-rule-width: 1px;
-webkit-column-width: 20em;
-webkit-column-rule-style: solid;
-webkit-column-rule-width: 1px;
position: relative;
height: 5em;
.gcli-menu:not(:first-of-type) {
padding-top: 5px;
}
.dark .gcli-menu-template {
column-rule-color: #000;
-moz-column-rule-color: #000;
-webkit-column--rule-color: #000;
}
.light .gcli-menu-template {
column-rule-color: #FFF;
-moz-column-rule-color: #FFF;
-webkit-column-rule-color: #FFF;
}
.gcli-menu-option {
overflow: hidden;
.gcli-menu-vert {
white-space: nowrap;
cursor: pointer;
max-width: 22em;
display: inline-flex;
-moz-padding-end: 20px;
-webkit-padding-end: 20px;
}
.gcli-menu-names {
white-space: nowrap;
flex-grow: 0;
flex-shrink: 0;
}
.gcli-menu-descs {
flex-grow: 1;
flex-shrink: 1;
}
.gcli-menu-name,
.gcli-menu-desc {
display: inline-block;
padding: 1px 3px;
-moz-padding-start: 8px;
-webkit-padding-start: 8px;
white-space: nowrap;
}
.gcli-menu-name {
width: 5em;
-moz-padding-start: 2px;
-webkit-padding-start: 2px;
-moz-padding-end: 8px;
-webkit-padding-end: 8px;
}
.gcli-menu-desc {
width: 15em;
-moz-padding-end: 2px;
-webkit-padding-end: 2px;
color: #777;
text-overflow: ellipsis;
overflow: hidden;
}
.gcli-menu-option:hover {
background-color: rgba(0, 0, 0, 0.2);
.gcli-menu-name:hover,
.gcli-menu-desc:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.gcli-menu-highlight,
.gcli-menu-highlight.gcli-menu-option:hover {
background-color: rgba(0, 0, 0, 0.3);
background-color: rgba(0, 0, 0, 0.1);
}
.gcli-menu-typed {
@ -68,9 +63,7 @@
.gcli-menu-more {
font-size: 80%;
position: absolute;
bottom: 0;
right: 0;
-moz-padding-end: 8px;
-webkit-padding-end: 8px;
width: 8em;
display: inline-flex;
vertical-align: bottom;
}

View File

@ -1,11 +1,20 @@
<div>
<table class="gcli-menu-template" aria-live="polite">
<tr class="gcli-menu-option" foreach="item in ${items}"
onclick="${onItemClickInternal}" title="${item.manual}">
<td class="gcli-menu-name">${item.name}</td>
<td class="gcli-menu-desc">${item.description}</td>
</tr>
</table>
<div class="gcli-menu-more" if="${items.hasMore}">${l10n.fieldMenuMore}</div>
<div class="gcli-menu-template" aria-live="polite">
<div class="gcli-menu-names">
<div class="gcli-menu-name"
foreach="item in ${items}"
data-name="${item.name}"
onclick="${onItemClickInternal}"
title="${item.manual}">${item.highlight}</div>
</div>
<div class="gcli-menu-descs">
<div class="gcli-menu-desc"
foreach="item in ${items}"
data-name="${item.name}"
onclick="${onItemClickInternal}"
title="${item.manual}">${item.description}</div>
</div>
</div>
<div class="gcli-menu-more" if="${hasMore}">${l10n.fieldMenuMore}</div>
</div>

View File

@ -97,16 +97,37 @@ Menu.prototype.destroy = function() {
* @param ev The click event from the browser
*/
Menu.prototype.onItemClickInternal = function(ev) {
var name = ev.currentTarget.querySelector('.gcli-menu-name').textContent;
var name = ev.currentTarget.getAttribute('data-name');
if (!name) {
var named = ev.currentTarget.querySelector('[data-name]');
name = named.getAttribute('data-name');
}
this.onItemClick({ name: name });
};
/**
* Act as though someone clicked on the selected item
*/
Menu.prototype.clickSelected = function() {
this.onItemClick({ name: this.selected });
};
/**
* What is the currently selected item?
*/
Object.defineProperty(Menu.prototype, 'isSelected', {
get: function() {
return this.selected != null;
},
enumerable: true
});
/**
* What is the currently selected item?
*/
Object.defineProperty(Menu.prototype, 'selected', {
get: function() {
var item = this.element.querySelector('.gcli-menu-highlight .gcli-menu-name');
var item = this.element.querySelector('.gcli-menu-name.gcli-menu-highlight');
if (!item) {
return null;
}
@ -131,11 +152,9 @@ Menu.prototype.show = function(items, match) {
return item.hidden === undefined || item.hidden !== true;
}.bind(this));
if (match) {
this.items = this.items.map(function(item) {
return getHighlightingProxy(item, match, this.template.ownerDocument);
}.bind(this));
}
this.items = this.items.map(function(item) {
return getHighlightingProxy(item, match, this.template.ownerDocument);
}.bind(this));
if (this.items.length === 0) {
this.element.style.display = 'none';
@ -159,37 +178,77 @@ Menu.prototype.show = function(items, match) {
this.element.style.display = 'block';
};
var MAX_ITEMS = 3;
/**
* Takes an array of items and cuts it into an array of arrays to help us
* to place the items into columns.
* The inner arrays will have at most MAX_ITEMS in them, with the number of
* outer arrays expanding to accommodate.
*/
Object.defineProperty(Menu.prototype, 'itemsSubdivided', {
get: function() {
var reply = [];
var taken = 0;
while (taken < this.items.length) {
reply.push(this.items.slice(taken, taken + MAX_ITEMS));
taken += MAX_ITEMS;
}
return reply;
},
enumerable: true
});
/**
* Create a proxy around an item that highlights matching text
*/
function getHighlightingProxy(item, match, document) {
if (typeof Proxy === 'undefined') {
return item;
}
return Proxy.create({
get: function(rcvr, name) {
var value = item[name];
if (name !== 'name') {
return value;
}
var proxy = {};
Object.defineProperties(proxy, {
highlight: {
get: function() {
if (!match) {
return item.name;
}
var startMatch = value.indexOf(match);
if (startMatch === -1) {
return value;
}
var value = item.name;
var startMatch = value.indexOf(match);
if (startMatch === -1) {
return value;
}
var before = value.substr(0, startMatch);
var after = value.substr(startMatch + match.length);
var parent = util.createElement(document, 'span');
parent.appendChild(document.createTextNode(before));
var highlight = util.createElement(document, 'span');
highlight.classList.add('gcli-menu-typed');
highlight.appendChild(document.createTextNode(match));
parent.appendChild(highlight);
parent.appendChild(document.createTextNode(after));
return parent;
var before = value.substr(0, startMatch);
var after = value.substr(startMatch + match.length);
var parent = util.createElement(document, 'span');
parent.appendChild(document.createTextNode(before));
var highlight = util.createElement(document, 'span');
highlight.classList.add('gcli-menu-typed');
highlight.appendChild(document.createTextNode(match));
parent.appendChild(highlight);
parent.appendChild(document.createTextNode(after));
return parent;
},
enumerable: true
},
name: {
value: item.name,
enumerable: true
},
manual: {
value: item.manual,
enumerable: true
},
description: {
value: item.description,
enumerable: true
}
});
return proxy;
}
/**
@ -239,21 +298,26 @@ Menu.prototype.unsetChoice = function() {
* Internal option to update the currently highlighted option
*/
Menu.prototype._updateHighlight = function() {
var nodes = this.element.querySelectorAll('.gcli-menu-option');
for (var i = 0; i < nodes.length; i++) {
nodes[i].classList.remove('gcli-menu-highlight');
var names = this.element.querySelectorAll('.gcli-menu-name');
var descs = this.element.querySelectorAll('.gcli-menu-desc');
for (var i = 0; i < names.length; i++) {
names[i].classList.remove('gcli-menu-highlight');
}
for (i = 0; i < descs.length; i++) {
descs[i].classList.remove('gcli-menu-highlight');
}
if (this._choice == null || nodes.length === 0) {
if (this._choice == null || names.length === 0) {
return;
}
var index = this._choice % nodes.length;
var index = this._choice % names.length;
if (index < 0) {
index = nodes.length + index;
index = names.length + index;
}
nodes.item(index).classList.add('gcli-menu-highlight');
names.item(index).classList.add('gcli-menu-highlight');
descs.item(index).classList.add('gcli-menu-highlight');
};
/**