@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+472 -101
+2 -1
webroot/rsrc/css/core/z-index.css
··· 133 133 z-index: 20; 134 134 } 135 135 136 - .dropdown-menu-frame { 136 + .dropdown-menu-frame, 137 + .phuix-dropdown-menu { 137 138 z-index: 32; 138 139 } 139 140
+2 -1
webroot/rsrc/css/phui/phui-button.css
··· 166 166 text-decoration: underline; 167 167 } 168 168 169 - .dropdown-menu-frame { 169 + .dropdown-menu-frame, 170 + .phuix-dropdown-menu { 170 171 position: absolute; 171 172 width: 240px; 172 173 background: #fff;
+1
webroot/rsrc/externals/javelin/lib/DOM.js
··· 944 944 try { node.focus(); } catch (lol_ie) {} 945 945 }, 946 946 947 + 947 948 /** 948 949 * Scroll to the position of an element in the document. 949 950 * @task view
+116 -99
webroot/rsrc/js/application/differential/behavior-dropdown-menus.js
··· 4 4 * javelin-dom 5 5 * javelin-util 6 6 * javelin-stratcom 7 - * phabricator-dropdown-menu 8 - * phabricator-menu-item 7 + * phuix-dropdown-menu 8 + * phuix-action-list-view 9 + * phuix-action-view 9 10 * phabricator-phtize 10 11 */ 11 12 12 13 JX.behavior('differential-dropdown-menus', function(config) { 13 - 14 14 var pht = JX.phtize(config.pht); 15 15 16 16 function show_more(container) { ··· 30 30 } 31 31 } 32 32 33 - function build_menu(button, data) { 33 + JX.Stratcom.listen( 34 + 'click', 35 + 'differential-reveal-all', 36 + function(e) { 37 + var containers = JX.DOM.scry( 38 + JX.$('differential-review-stage'), 39 + 'div', 40 + 'differential-changeset'); 41 + for (var i=0; i < containers.length; i++) { 42 + show_more(containers[i]); 43 + } 44 + e.kill(); 45 + }); 34 46 35 - function link_to(name, uri) { 36 - var item = new JX.PhabricatorMenuItem( 37 - name, 38 - JX.bind(null, window.open, uri), 39 - uri); 40 - item.setDisabled(!uri); 41 - return item; 47 + var buildmenu = function(e) { 48 + var button = e.getNode('differential-view-options'); 49 + var data = JX.Stratcom.getData(button); 50 + 51 + if (data.menu) { 52 + return; 42 53 } 43 54 44 - var reveal_item = new JX.PhabricatorMenuItem('', function () { 45 - show_more(JX.$(data.containerID)); 46 - }); 55 + e.prevent(); 47 56 48 - var diffusion_item; 49 - if (data.diffusionURI) { 50 - // Show this only if we have a link, since when this appears in Diffusion 51 - // it is otherwise potentially confusing. 52 - diffusion_item = link_to(pht('Browse in Diffusion'), data.diffusionURI); 53 - } 57 + var menu = new JX.PHUIXDropdownMenu(button); 58 + var list = new JX.PHUIXActionListView(); 54 59 55 - var menu = new JX.PhabricatorDropdownMenu(buttons[ii]) 56 - .addItem(reveal_item); 60 + var add_link = function(icon, name, href, local) { 61 + if (!href) { 62 + return; 63 + } 57 64 58 - var visible_item = new JX.PhabricatorMenuItem('', function () { 59 - JX.Stratcom.invoke('differential-toggle-file', null, { 60 - diff: JX.DOM.scry(JX.$(data.containerID), 'table', 'differential-diff') 61 - }); 62 - }); 63 - menu.addItem(visible_item); 65 + var link = new JX.PHUIXActionView() 66 + .setIcon(icon) 67 + .setName(name) 68 + .setHref(href) 69 + .setHandler(function(e) { 70 + if (local) { 71 + window.location.assign(href); 72 + } else { 73 + window.open(href); 74 + } 75 + menu.close(); 76 + e.prevent(); 77 + }); 64 78 65 - if (diffusion_item) { 66 - menu.addItem(diffusion_item); 67 - } 79 + list.addItem(link); 80 + return link; 81 + }; 68 82 69 - menu.addItem(link_to(pht('View Standalone'), data.standaloneURI)); 83 + var reveal_item = new JX.PHUIXActionView() 84 + .setIcon('preview'); 85 + list.addItem(reveal_item); 70 86 71 - if (data.leftURI) { 72 - menu.addItem(link_to(pht('Show Raw File (Left)'), data.leftURI)); 73 - } 87 + var visible_item = new JX.PHUIXActionView() 88 + .setHandler(function(e) { 89 + var diff = JX.DOM.scry( 90 + JX.$(data.containerID), 91 + 'table', 92 + 'differential-diff'); 93 + 94 + JX.Stratcom.invoke('differential-toggle-file', null, {diff: diff}); 95 + e.prevent(); 96 + menu.close(); 97 + }); 98 + list.addItem(visible_item); 99 + 100 + add_link('file', pht('Browse in Diffusion'), data.diffusionURI); 101 + add_link('transcript', pht('View Standalone'), data.standaloneURI); 102 + add_link('arrow_left', pht('Show Raw File (Left)'), data.leftURI); 103 + add_link('arrow_right', pht('Show Raw File (Right)'), data.rightURI); 104 + add_link('edit', pht('Open in Editor'), data.editor, true); 105 + add_link('wrench', pht('Configure Editor'), data.editorConfigure); 74 106 75 - if (data.rightURI) { 76 - menu.addItem(link_to(pht('Show Raw File (Right)'), data.rightURI)); 77 - } 78 107 79 - if (data.editor) { 80 - menu.addItem(new JX.PhabricatorMenuItem( 81 - pht('Open in Editor'), 82 - // Open in the same window. 83 - JX.bind(location, location.assign, data.editor), 84 - data.editor)); 85 - } 108 + menu.setContent(list.getNode()); 86 109 87 - if (data.editorConfigure) { 88 - menu.addItem(link_to(pht('Configure Editor'), data.editorConfigure)); 89 - } 110 + menu.listen('open', function() { 90 111 91 - menu.listen( 92 - 'open', 93 - function() { 112 + // When the user opens the menu, check if there are any "Show More" 113 + // links in the changeset body. If there aren't, disable the "Show 114 + // Entire File" menu item since it won't change anything. 94 115 95 - // When the user opens the menu, check if there are any "Show More" 96 - // links in the changeset body. If there aren't, disable the "Show 97 - // Entire File" menu item since it won't change anything. 116 + var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more'); 117 + if (nodes.length) { 118 + reveal_item 119 + .setDisabled(false) 120 + .setName(pht('Show Entire File')) 121 + .setHandler(function(e) { 122 + show_more(JX.$(data.containerID)); 123 + e.prevent(); 124 + menu.close(); 125 + }); 126 + } else { 127 + reveal_item 128 + .setDisabled(true) 129 + .setName(pht('Entire File Shown')) 130 + .setHandler(function(e) { e.prevent(); }); 131 + } 98 132 99 - var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more'); 100 - if (nodes.length) { 101 - reveal_item.setDisabled(false); 102 - reveal_item.setName(pht('Show Entire File')); 103 - } else { 104 - reveal_item.setDisabled(true); 105 - reveal_item.setName(pht('Entire File Shown')); 106 - } 133 + visible_item.setDisabled(true); 134 + visible_item.setName(pht("Can't Toggle Unloaded File")); 135 + var diffs = JX.DOM.scry( 136 + JX.$(data.containerID), 137 + 'table', 138 + 'differential-diff'); 107 139 108 - visible_item.setDisabled(true); 109 - visible_item.setName(pht("Can't Toggle Unloaded File")); 110 - var diffs = JX.DOM.scry(JX.$(data.containerID), 111 - 'table', 'differential-diff'); 112 - if (diffs.length > 1) { 113 - JX.$E( 114 - 'More than one node with sigil "differential-diff" was found in "'+ 115 - data.containerID+'."'); 116 - } else if (diffs.length == 1) { 117 - diff = diffs[0]; 118 - visible_item.setDisabled(false); 119 - if (JX.Stratcom.getData(diff).hidden) { 120 - visible_item.setName(pht('Expand File')); 121 - } else { 122 - visible_item.setName(pht('Collapse File')); 123 - } 140 + if (diffs.length > 1) { 141 + JX.$E( 142 + 'More than one node with sigil "differential-diff" was found in "'+ 143 + data.containerID+'."'); 144 + } else if (diffs.length == 1) { 145 + diff = diffs[0]; 146 + visible_item.setDisabled(false); 147 + if (JX.Stratcom.getData(diff).hidden) { 148 + visible_item 149 + .setName(pht('Expand File')) 150 + .setIcon('unmerge'); 124 151 } else { 125 - // Do nothing when there is no diff shown in the table. For example, 126 - // the file is binary. 152 + visible_item 153 + .setName(pht('Collapse File')) 154 + .setIcon('merge'); 127 155 } 128 - }); 129 - } 130 - 131 - var buttons = JX.DOM.scry(window.document, 'a', 'differential-view-options'); 132 - for (var ii = 0; ii < buttons.length; ii++) { 133 - build_menu(buttons[ii], JX.Stratcom.getData(buttons[ii])); 134 - } 135 - 136 - JX.Stratcom.listen( 137 - 'click', 138 - 'differential-reveal-all', 139 - function(e) { 140 - var containers = JX.DOM.scry( 141 - JX.$('differential-review-stage'), 142 - 'div', 143 - 'differential-changeset'); 144 - for (var i=0; i < containers.length; i++) { 145 - show_more(containers[i]); 156 + } else { 157 + // Do nothing when there is no diff shown in the table. For example, 158 + // the file is binary. 146 159 } 147 - e.kill(); 160 + 148 161 }); 162 + data.menu = menu; 163 + menu.open(); 164 + }; 149 165 166 + JX.Stratcom.listen('click', 'differential-view-options', buildmenu); 150 167 });
+36
webroot/rsrc/js/phuix/PHUIXActionListView.js
··· 1 + /** 2 + * @provides phuix-action-list-view 3 + * @requires javelin-install 4 + * javelin-dom 5 + */ 6 + 7 + JX.install('PHUIXActionListView', { 8 + 9 + construct: function() { 10 + this._items = []; 11 + }, 12 + 13 + members: { 14 + _items: null, 15 + _node: null, 16 + 17 + addItem: function(item) { 18 + this._items.push(item); 19 + this.getNode().appendChild(item.getNode()); 20 + return this; 21 + }, 22 + 23 + getNode: function() { 24 + if (!this._node) { 25 + var attrs = { 26 + className: 'phabricator-action-list-view' 27 + }; 28 + 29 + this._node = JX.$N('ul', attrs); 30 + } 31 + 32 + return this._node; 33 + } 34 + } 35 + 36 + });
+138
webroot/rsrc/js/phuix/PHUIXActionView.js
··· 1 + /** 2 + * @provides phuix-action-view 3 + * @requires javelin-install 4 + * javelin-dom 5 + * javelin-util 6 + * @javelin 7 + */ 8 + 9 + JX.install('PHUIXActionView', { 10 + 11 + members: { 12 + _node: null, 13 + _name: null, 14 + _icon: 'none', 15 + _disabled: false, 16 + _handler: null, 17 + 18 + _iconNode: null, 19 + _nameNode: null, 20 + 21 + setDisabled: function(disabled) { 22 + this._disabled = disabled; 23 + JX.DOM.alterClass( 24 + this.getNode(), 25 + 'phabricator-action-view-disabled', 26 + disabled); 27 + 28 + this._buildIconNode(true); 29 + 30 + return this; 31 + }, 32 + 33 + getDisabled: function() { 34 + return this._disabled; 35 + }, 36 + 37 + setName: function(name) { 38 + this._name = name; 39 + this._buildNameNode(true); 40 + return this; 41 + }, 42 + 43 + setHandler: function(handler) { 44 + this._handler = handler; 45 + this._buildNameNode(true); 46 + return this; 47 + }, 48 + 49 + setIcon: function(icon) { 50 + this._icon = icon; 51 + this._buildIconNode(true); 52 + return this; 53 + }, 54 + 55 + setHref: function(href) { 56 + this._href = href; 57 + this._buildNameNode(true); 58 + return this; 59 + }, 60 + 61 + getNode: function() { 62 + if (!this._node) { 63 + var attr = { 64 + className: 'phabricator-action-view' 65 + }; 66 + 67 + var content = [ 68 + this._buildIconNode(), 69 + this._buildNameNode() 70 + ]; 71 + 72 + this._node = JX.$N('li', attr, content); 73 + } 74 + 75 + return this._node; 76 + }, 77 + 78 + _buildIconNode: function(dirty) { 79 + if (!this._iconNode || dirty) { 80 + var attr = { 81 + className: 'phui-icon-view sprite-icons phabricator-action-view-icon' 82 + }; 83 + var node = JX.$N('span', attr); 84 + 85 + var icon_class = 'icons-' + this._icon; 86 + if (this._disabled) { 87 + icon_class = icon_class + '-grey'; 88 + } 89 + 90 + JX.DOM.alterClass(node, icon_class, true); 91 + 92 + if (this._iconNode && this._iconNode.parentNode) { 93 + JX.DOM.replace(this._iconNode, node); 94 + } 95 + this._iconNode = node; 96 + } 97 + 98 + return this._iconNode; 99 + }, 100 + 101 + _buildNameNode: function(dirty) { 102 + if (!this._nameNode || dirty) { 103 + var attr = { 104 + className: 'phabricator-action-view-item' 105 + }; 106 + 107 + var href = this._href; 108 + if (!href && this._handler) { 109 + href = '#'; 110 + } 111 + if (href) { 112 + attr.href = href; 113 + 114 + } 115 + 116 + var tag = href ? 'a' : 'span'; 117 + 118 + var node = JX.$N(tag, attr, this._name); 119 + JX.DOM.listen(node, 'click', null, JX.bind(this, this._onclick)); 120 + 121 + if (this._nameNode && this._nameNode.parentNode) { 122 + JX.DOM.replace(this._nameNode, node); 123 + } 124 + this._nameNode = node; 125 + } 126 + 127 + return this._nameNode; 128 + }, 129 + 130 + _onclick: function(e) { 131 + if (this._handler) { 132 + this._handler(e); 133 + } 134 + } 135 + 136 + } 137 + 138 + });
+177
webroot/rsrc/js/phuix/PHUIXDropdownMenu.js
··· 1 + /** 2 + * @provides phuix-dropdown-menu 3 + * @requires javelin-install 4 + * javelin-util 5 + * javelin-dom 6 + * javelin-vector 7 + * javelin-stratcom 8 + * @javelin 9 + */ 10 + 11 + 12 + /** 13 + * Basic interaction for a dropdown menu. 14 + * 15 + * The menu is unaware of the content inside it, so it can not close itself 16 + * when an item is selected. Callers must make a call to @{method:close} after 17 + * an item is chosen in order to close the menu. 18 + */ 19 + JX.install('PHUIXDropdownMenu', { 20 + 21 + construct : function(node) { 22 + this._node = node; 23 + 24 + JX.DOM.listen( 25 + this._node, 26 + 'click', 27 + null, 28 + JX.bind(this, this._onclick)); 29 + 30 + JX.Stratcom.listen( 31 + 'mousedown', 32 + null, 33 + JX.bind(this, this._onanyclick)); 34 + 35 + JX.Stratcom.listen( 36 + 'resize', 37 + null, 38 + JX.bind(this, this._adjustposition)); 39 + 40 + JX.Stratcom.listen('phuix.dropdown.open', null, JX.bind(this, this.close)); 41 + }, 42 + 43 + events: ['open'], 44 + 45 + properties: { 46 + width: null, 47 + align: 'right' 48 + }, 49 + 50 + members: { 51 + _node: null, 52 + _menu: null, 53 + _open: false, 54 + _content: null, 55 + 56 + setContent: function(content) { 57 + JX.DOM.setContent(this._getMenuNode(), content); 58 + return this; 59 + }, 60 + 61 + open: function() { 62 + if (this._open) { 63 + return; 64 + } 65 + 66 + this.invoke('open'); 67 + JX.Stratcom.invoke('phuix.dropdown.open'); 68 + 69 + this._open = true; 70 + this._show(); 71 + 72 + return this; 73 + }, 74 + 75 + close: function() { 76 + if (!this._open) { 77 + return; 78 + } 79 + this._open = false; 80 + this._hide(); 81 + 82 + return this; 83 + }, 84 + 85 + _getMenuNode: function() { 86 + if (!this._menu) { 87 + var attrs = { 88 + className: 'phuix-dropdown-menu', 89 + role: 'button' 90 + }; 91 + 92 + var menu = JX.$N('div', attrs); 93 + 94 + this._node.setAttribute('aria-haspopup', 'true'); 95 + this._node.setAttribute('aria-expanded', 'false'); 96 + 97 + this._menu = menu; 98 + } 99 + 100 + return this._menu; 101 + }, 102 + 103 + _onclick : function(e) { 104 + if (this._open) { 105 + this.close(); 106 + } else { 107 + this.open(); 108 + } 109 + e.prevent(); 110 + }, 111 + 112 + _onanyclick : function(e) { 113 + if (!this._open) { 114 + return; 115 + } 116 + 117 + if (JX.Stratcom.pass(e)) { 118 + return; 119 + } 120 + 121 + var t = e.getTarget(); 122 + while (t) { 123 + if (t == this._menu || t == this._node) { 124 + return; 125 + } 126 + t = t.parentNode; 127 + } 128 + 129 + this.close(); 130 + }, 131 + 132 + _show : function() { 133 + document.body.appendChild(this._menu); 134 + 135 + if (this.getWidth()) { 136 + new JX.Vector(this.getWidth(), null).setDim(this._menu); 137 + } 138 + 139 + this._adjustposition(); 140 + 141 + JX.DOM.alterClass(this._node, 'phuix-dropdown-open', true); 142 + 143 + this._node.setAttribute('aria-expanded', 'true'); 144 + }, 145 + 146 + _hide : function() { 147 + JX.DOM.remove(this._menu); 148 + 149 + JX.DOM.alterClass(this._node, 'phuix-dropdown-open', false); 150 + 151 + this._node.setAttribute('aria-expanded', 'false'); 152 + }, 153 + 154 + _adjustposition : function() { 155 + if (!this._open) { 156 + return; 157 + } 158 + 159 + var m = JX.Vector.getDim(this._menu); 160 + 161 + var v = JX.$V(this._node); 162 + var d = JX.Vector.getDim(this._node); 163 + 164 + switch (this.getAlign()) { 165 + case 'right': 166 + v = v.add(d) 167 + .add(JX.$V(-m.x, 0)); 168 + break; 169 + default: 170 + v = v.add(0, d.y); 171 + break; 172 + } 173 + 174 + v.setPos(this._menu); 175 + } 176 + } 177 + });