1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 23:02:42 +01:00

Begin rebuilding dropdown menus on ActionList

Summary:
Dropdown menus are entirely dynamic right now and use custom CSS. Begin rebuilding them to use ActionList CSS.

This introduces PHUIX components which are basically JS copy/pastes of the PHP PHUI components, just implemented in JS.

We have two other dropdowns: policy controls and one in Conpherence. I'll convert those, then implement D8966.

Test Plan: {F150418}

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Differential Revision: https://secure.phabricator.com/D8973
This commit is contained in:
epriestley 2014-05-05 10:56:14 -07:00
parent 707c5aec81
commit 56aa508f43
7 changed files with 479 additions and 108 deletions

View file

@ -133,7 +133,8 @@ div.jx-typeahead-results {
z-index: 20;
}
.dropdown-menu-frame {
.dropdown-menu-frame,
.phuix-dropdown-menu {
z-index: 32;
}

View file

@ -166,7 +166,8 @@ button.link:hover {
text-decoration: underline;
}
.dropdown-menu-frame {
.dropdown-menu-frame,
.phuix-dropdown-menu {
position: absolute;
width: 240px;
background: #fff;

View file

@ -944,6 +944,7 @@ JX.install('DOM', {
try { node.focus(); } catch (lol_ie) {}
},
/**
* Scroll to the position of an element in the document.
* @task view

View file

@ -4,13 +4,13 @@
* javelin-dom
* javelin-util
* javelin-stratcom
* phabricator-dropdown-menu
* phabricator-menu-item
* phuix-dropdown-menu
* phuix-action-list-view
* phuix-action-view
* phabricator-phtize
*/
JX.behavior('differential-dropdown-menus', function(config) {
var pht = JX.phtize(config.pht);
function show_more(container) {
@ -30,109 +30,6 @@ JX.behavior('differential-dropdown-menus', function(config) {
}
}
function build_menu(button, data) {
function link_to(name, uri) {
var item = new JX.PhabricatorMenuItem(
name,
JX.bind(null, window.open, uri),
uri);
item.setDisabled(!uri);
return item;
}
var reveal_item = new JX.PhabricatorMenuItem('', function () {
show_more(JX.$(data.containerID));
});
var diffusion_item;
if (data.diffusionURI) {
// Show this only if we have a link, since when this appears in Diffusion
// it is otherwise potentially confusing.
diffusion_item = link_to(pht('Browse in Diffusion'), data.diffusionURI);
}
var menu = new JX.PhabricatorDropdownMenu(buttons[ii])
.addItem(reveal_item);
var visible_item = new JX.PhabricatorMenuItem('', function () {
JX.Stratcom.invoke('differential-toggle-file', null, {
diff: JX.DOM.scry(JX.$(data.containerID), 'table', 'differential-diff')
});
});
menu.addItem(visible_item);
if (diffusion_item) {
menu.addItem(diffusion_item);
}
menu.addItem(link_to(pht('View Standalone'), data.standaloneURI));
if (data.leftURI) {
menu.addItem(link_to(pht('Show Raw File (Left)'), data.leftURI));
}
if (data.rightURI) {
menu.addItem(link_to(pht('Show Raw File (Right)'), data.rightURI));
}
if (data.editor) {
menu.addItem(new JX.PhabricatorMenuItem(
pht('Open in Editor'),
// Open in the same window.
JX.bind(location, location.assign, data.editor),
data.editor));
}
if (data.editorConfigure) {
menu.addItem(link_to(pht('Configure Editor'), data.editorConfigure));
}
menu.listen(
'open',
function() {
// When the user opens the menu, check if there are any "Show More"
// links in the changeset body. If there aren't, disable the "Show
// Entire File" menu item since it won't change anything.
var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more');
if (nodes.length) {
reveal_item.setDisabled(false);
reveal_item.setName(pht('Show Entire File'));
} else {
reveal_item.setDisabled(true);
reveal_item.setName(pht('Entire File Shown'));
}
visible_item.setDisabled(true);
visible_item.setName(pht("Can't Toggle Unloaded File"));
var diffs = JX.DOM.scry(JX.$(data.containerID),
'table', 'differential-diff');
if (diffs.length > 1) {
JX.$E(
'More than one node with sigil "differential-diff" was found in "'+
data.containerID+'."');
} else if (diffs.length == 1) {
diff = diffs[0];
visible_item.setDisabled(false);
if (JX.Stratcom.getData(diff).hidden) {
visible_item.setName(pht('Expand File'));
} else {
visible_item.setName(pht('Collapse File'));
}
} else {
// Do nothing when there is no diff shown in the table. For example,
// the file is binary.
}
});
}
var buttons = JX.DOM.scry(window.document, 'a', 'differential-view-options');
for (var ii = 0; ii < buttons.length; ii++) {
build_menu(buttons[ii], JX.Stratcom.getData(buttons[ii]));
}
JX.Stratcom.listen(
'click',
'differential-reveal-all',
@ -147,4 +44,124 @@ JX.behavior('differential-dropdown-menus', function(config) {
e.kill();
});
var buildmenu = function(e) {
var button = e.getNode('differential-view-options');
var data = JX.Stratcom.getData(button);
if (data.menu) {
return;
}
e.prevent();
var menu = new JX.PHUIXDropdownMenu(button);
var list = new JX.PHUIXActionListView();
var add_link = function(icon, name, href, local) {
if (!href) {
return;
}
var link = new JX.PHUIXActionView()
.setIcon(icon)
.setName(name)
.setHref(href)
.setHandler(function(e) {
if (local) {
window.location.assign(href);
} else {
window.open(href);
}
menu.close();
e.prevent();
});
list.addItem(link);
return link;
};
var reveal_item = new JX.PHUIXActionView()
.setIcon('preview');
list.addItem(reveal_item);
var visible_item = new JX.PHUIXActionView()
.setHandler(function(e) {
var diff = JX.DOM.scry(
JX.$(data.containerID),
'table',
'differential-diff');
JX.Stratcom.invoke('differential-toggle-file', null, {diff: diff});
e.prevent();
menu.close();
});
list.addItem(visible_item);
add_link('file', pht('Browse in Diffusion'), data.diffusionURI);
add_link('transcript', pht('View Standalone'), data.standaloneURI);
add_link('arrow_left', pht('Show Raw File (Left)'), data.leftURI);
add_link('arrow_right', pht('Show Raw File (Right)'), data.rightURI);
add_link('edit', pht('Open in Editor'), data.editor, true);
add_link('wrench', pht('Configure Editor'), data.editorConfigure);
menu.setContent(list.getNode());
menu.listen('open', function() {
// When the user opens the menu, check if there are any "Show More"
// links in the changeset body. If there aren't, disable the "Show
// Entire File" menu item since it won't change anything.
var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more');
if (nodes.length) {
reveal_item
.setDisabled(false)
.setName(pht('Show Entire File'))
.setHandler(function(e) {
show_more(JX.$(data.containerID));
e.prevent();
menu.close();
});
} else {
reveal_item
.setDisabled(true)
.setName(pht('Entire File Shown'))
.setHandler(function(e) { e.prevent(); });
}
visible_item.setDisabled(true);
visible_item.setName(pht("Can't Toggle Unloaded File"));
var diffs = JX.DOM.scry(
JX.$(data.containerID),
'table',
'differential-diff');
if (diffs.length > 1) {
JX.$E(
'More than one node with sigil "differential-diff" was found in "'+
data.containerID+'."');
} else if (diffs.length == 1) {
diff = diffs[0];
visible_item.setDisabled(false);
if (JX.Stratcom.getData(diff).hidden) {
visible_item
.setName(pht('Expand File'))
.setIcon('unmerge');
} else {
visible_item
.setName(pht('Collapse File'))
.setIcon('merge');
}
} else {
// Do nothing when there is no diff shown in the table. For example,
// the file is binary.
}
});
data.menu = menu;
menu.open();
};
JX.Stratcom.listen('click', 'differential-view-options', buildmenu);
});

View file

@ -0,0 +1,36 @@
/**
* @provides phuix-action-list-view
* @requires javelin-install
* javelin-dom
*/
JX.install('PHUIXActionListView', {
construct: function() {
this._items = [];
},
members: {
_items: null,
_node: null,
addItem: function(item) {
this._items.push(item);
this.getNode().appendChild(item.getNode());
return this;
},
getNode: function() {
if (!this._node) {
var attrs = {
className: 'phabricator-action-list-view'
};
this._node = JX.$N('ul', attrs);
}
return this._node;
}
}
});

View file

@ -0,0 +1,138 @@
/**
* @provides phuix-action-view
* @requires javelin-install
* javelin-dom
* javelin-util
* @javelin
*/
JX.install('PHUIXActionView', {
members: {
_node: null,
_name: null,
_icon: 'none',
_disabled: false,
_handler: null,
_iconNode: null,
_nameNode: null,
setDisabled: function(disabled) {
this._disabled = disabled;
JX.DOM.alterClass(
this.getNode(),
'phabricator-action-view-disabled',
disabled);
this._buildIconNode(true);
return this;
},
getDisabled: function() {
return this._disabled;
},
setName: function(name) {
this._name = name;
this._buildNameNode(true);
return this;
},
setHandler: function(handler) {
this._handler = handler;
this._buildNameNode(true);
return this;
},
setIcon: function(icon) {
this._icon = icon;
this._buildIconNode(true);
return this;
},
setHref: function(href) {
this._href = href;
this._buildNameNode(true);
return this;
},
getNode: function() {
if (!this._node) {
var attr = {
className: 'phabricator-action-view'
};
var content = [
this._buildIconNode(),
this._buildNameNode()
];
this._node = JX.$N('li', attr, content);
}
return this._node;
},
_buildIconNode: function(dirty) {
if (!this._iconNode || dirty) {
var attr = {
className: 'phui-icon-view sprite-icons phabricator-action-view-icon'
};
var node = JX.$N('span', attr);
var icon_class = 'icons-' + this._icon;
if (this._disabled) {
icon_class = icon_class + '-grey';
}
JX.DOM.alterClass(node, icon_class, true);
if (this._iconNode && this._iconNode.parentNode) {
JX.DOM.replace(this._iconNode, node);
}
this._iconNode = node;
}
return this._iconNode;
},
_buildNameNode: function(dirty) {
if (!this._nameNode || dirty) {
var attr = {
className: 'phabricator-action-view-item'
};
var href = this._href;
if (!href && this._handler) {
href = '#';
}
if (href) {
attr.href = href;
}
var tag = href ? 'a' : 'span';
var node = JX.$N(tag, attr, this._name);
JX.DOM.listen(node, 'click', null, JX.bind(this, this._onclick));
if (this._nameNode && this._nameNode.parentNode) {
JX.DOM.replace(this._nameNode, node);
}
this._nameNode = node;
}
return this._nameNode;
},
_onclick: function(e) {
if (this._handler) {
this._handler(e);
}
}
}
});

View file

@ -0,0 +1,177 @@
/**
* @provides phuix-dropdown-menu
* @requires javelin-install
* javelin-util
* javelin-dom
* javelin-vector
* javelin-stratcom
* @javelin
*/
/**
* Basic interaction for a dropdown menu.
*
* The menu is unaware of the content inside it, so it can not close itself
* when an item is selected. Callers must make a call to @{method:close} after
* an item is chosen in order to close the menu.
*/
JX.install('PHUIXDropdownMenu', {
construct : function(node) {
this._node = node;
JX.DOM.listen(
this._node,
'click',
null,
JX.bind(this, this._onclick));
JX.Stratcom.listen(
'mousedown',
null,
JX.bind(this, this._onanyclick));
JX.Stratcom.listen(
'resize',
null,
JX.bind(this, this._adjustposition));
JX.Stratcom.listen('phuix.dropdown.open', null, JX.bind(this, this.close));
},
events: ['open'],
properties: {
width: null,
align: 'right'
},
members: {
_node: null,
_menu: null,
_open: false,
_content: null,
setContent: function(content) {
JX.DOM.setContent(this._getMenuNode(), content);
return this;
},
open: function() {
if (this._open) {
return;
}
this.invoke('open');
JX.Stratcom.invoke('phuix.dropdown.open');
this._open = true;
this._show();
return this;
},
close: function() {
if (!this._open) {
return;
}
this._open = false;
this._hide();
return this;
},
_getMenuNode: function() {
if (!this._menu) {
var attrs = {
className: 'phuix-dropdown-menu',
role: 'button'
};
var menu = JX.$N('div', attrs);
this._node.setAttribute('aria-haspopup', 'true');
this._node.setAttribute('aria-expanded', 'false');
this._menu = menu;
}
return this._menu;
},
_onclick : function(e) {
if (this._open) {
this.close();
} else {
this.open();
}
e.prevent();
},
_onanyclick : function(e) {
if (!this._open) {
return;
}
if (JX.Stratcom.pass(e)) {
return;
}
var t = e.getTarget();
while (t) {
if (t == this._menu || t == this._node) {
return;
}
t = t.parentNode;
}
this.close();
},
_show : function() {
document.body.appendChild(this._menu);
if (this.getWidth()) {
new JX.Vector(this.getWidth(), null).setDim(this._menu);
}
this._adjustposition();
JX.DOM.alterClass(this._node, 'phuix-dropdown-open', true);
this._node.setAttribute('aria-expanded', 'true');
},
_hide : function() {
JX.DOM.remove(this._menu);
JX.DOM.alterClass(this._node, 'phuix-dropdown-open', false);
this._node.setAttribute('aria-expanded', 'false');
},
_adjustposition : function() {
if (!this._open) {
return;
}
var m = JX.Vector.getDim(this._menu);
var v = JX.$V(this._node);
var d = JX.Vector.getDim(this._node);
switch (this.getAlign()) {
case 'right':
v = v.add(d)
.add(JX.$V(-m.x, 0));
break;
default:
v = v.add(0, d.y);
break;
}
v.setPos(this._menu);
}
}
});