mirror of https://github.com/jenkinsci/jenkins.git
Update link dropdown menus to use new Tippy dropdowns (#7474)
* Squashed commit of the following: commit 0fafc6f2b50c817c11a6149fb4374bfb1a1d486a Merge: 9046da814fd3d1c88d29
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Dec 4 21:31:15 2022 +0000 Merge branch 'master' into add-model-link-dropdowns commit 9046da814f3aa26ec19ece970b25a29375377bfa Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Dec 4 21:31:09 2022 +0000 Reset commit 5f81d254112c7179f9c83e7dd3650aa9ba0b0c37 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sat Nov 26 00:56:02 2022 +0000 Update prototype.js commit dab47993c6c5f9bd77e83d392c737f3d6d3ba830 Merge: b166f016a3221ff946b3
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Nov 25 22:32:28 2022 +0000 Merge branch 'master' into add-model-link-dropdowns commit b166f016a3e81b3787702e061cb7793a79295a49 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Nov 25 20:11:45 2022 +0000 Add support for breadcrumb-config-outline commit cc05b743192bdfbf68d1399f77a32583f2a65182 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Nov 23 23:03:40 2022 +0000 Imports commit d23b82247e9e2defb5501622f4c3a2b3c4ee95c7 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Nov 23 23:02:25 2022 +0000 Tidy up JS commit a578f6aa1f1b35db83a3543417b87381f636c3c7 Merge: f48e7ef37051e084a01a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Nov 23 11:13:00 2022 +0000 Merge branch 'master' into add-model-link-dropdowns commit f48e7ef370b7e807689b2edea81d137ca1e7dddb Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Nov 23 09:57:41 2022 +0000 Update dropdowns.less commit 8e54b2d9f17afd8c9d7ccf9f481ad1c571ed43d2 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Nov 23 00:54:33 2022 +0000 Init commit 0a161523b2e195dc89c75296111a0be0a9f9319e Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon Nov 21 19:56:48 2022 +0000 Update yarn.lock commit 8aa08f0fc1ac4e5d2c082e3e9b96344cb255257b Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon Nov 21 19:40:26 2022 +0000 Fix commit 7a2668ccfde9ec79dfb1fd20d0da70fb4817b1d7 Merge: faf3888e27df89aa6855
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon Nov 21 19:39:51 2022 +0000 Merge branch 'master' into add-dropdown-button commit faf3888e279e5ffb2ec99f47506548c435659c76 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:57:35 2022 +0100 Update overflowButton.jelly commit 2c33dee5ec1f6cbcad335f12db58e84966af1ade Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:55:31 2022 +0100 Remove unused method, fix docs commit ed8c2b02e13306e810da721c69486011cfd80308 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:48:51 2022 +0100 Fix link appearance commit 9d04e8092ff8bab60b76bdfb41d2431a6737ce6c Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:45:40 2022 +0100 Fix up commit 109dda954e29d8f85d5dcbaf9b630ce02d17648b Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:22:10 2022 +0100 Small cleanup commit 0f27194eba82073f513b205da991b6a729b40bb0 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:19:39 2022 +0100 Lint commit e63c95620cec8f26fca33605389e5bac751471ac Merge: dccceff2187cb5eb78c0
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Sep 23 23:18:39 2022 +0100 Merge branch 'master' into add-dropdown-button commit dccceff2185fb4870f1c4639579d472866975cb7 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 19:21:39 2022 +0100 Update dropdowns.less commit 373b8d90a056453ebb8ccdbe613a8d2eb5c0be51 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 19:20:40 2022 +0100 Update table.jelly commit 62dff833f3593981c093c0a2a63291cae0adda26 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 19:19:51 2022 +0100 Update index.jelly commit 9bacedaac4b02d0b7786d22e06277903c2556c62 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 19:19:32 2022 +0100 Update rowSelectionController.jelly commit 06591b1e01f6eb51721edce588f9f818ee9a4cad Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 19:18:30 2022 +0100 Update row selection controller to use new dropdown component commit 21b5844fde72cf81d0ff4a57358a9e35d98c8afb Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 19:02:52 2022 +0100 Restyle commit 9966fe79472fdd7b9229143089d4161a6ddf0ab6 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 18:49:06 2022 +0100 WB commit 81b073a7143256b6369470794f947b6d989bd957 Merge: 8b58f7234b315b2f410c
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 27 18:29:11 2022 +0100 Merge branch 'master' into add-dropdown-button commit 8b58f7234b96acb0263f02d54cffc3e1fe4705e1 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 20 08:41:48 2022 +0100 Replace icon with hand made divs commit 22908c71d286dbb907acc52df1cea49f7966c5d9 Merge: fb89bc5891b3320ca766
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Tue Jul 19 23:28:58 2022 +0100 Merge branch 'new-buttons-1-dashboard' into add-dropdown-button commit fb89bc5891ba15558fef93ddad30e881c7a08175 Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Tue Jul 19 23:28:37 2022 +0100 Init commitb3320ca766
Merge:1fa24ea550
f52d99a0e5
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jul 14 09:50:06 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit1fa24ea550
Merge:8598f99f00
a990ebcda4
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 13 00:02:33 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit8598f99f00
Author: Alexander Brandes <mc.cache@web.de> Date: Mon Jul 11 15:11:36 2022 +0200 style: Address stylelint violations commitfd4da119c4
Merge:0480268a50
aba1c17ede
Author: Alexander Brandes <mc.cache@web.de> Date: Sun Jul 10 23:30:55 2022 +0200 Merge branch 'master' into new-buttons-1-dashboard commit0480268a50
Merge:9b145b1ae4
ab0bb84958
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sat Jul 9 21:52:43 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit9b145b1ae4
Merge:418ab063b2
03617e0b43
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Jul 8 19:44:20 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit418ab063b2
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jul 7 22:41:32 2022 +0100 Update buttons-deprecated.less commit2798afa6df
Merge:296e309afc
6de288f424
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jul 7 22:40:35 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit296e309afc
Merge:1e646ff7ee
22dcefcd8f
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jul 7 14:50:14 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit1e646ff7ee
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 6 22:59:13 2022 +0100 Use rem, fix cursor for dashboard icon size commit21d4c73d9e
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 6 22:56:05 2022 +0100 Update with master, make minor changes commit1d59bd4a21
Merge:f6fc19bd3e
644a261e78
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jul 6 22:48:59 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commitf6fc19bd3e
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 26 22:58:12 2022 +0100 Update dashboard.less commit665daa0b85
Merge:a57f13ac88
c60ea9210c
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 26 22:52:33 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commita57f13ac88
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon Jun 20 19:01:18 2022 +0100 Update simple-page.less commit2d3695d163
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 19 22:24:32 2022 +0100 Update theme.less commit361f06c7ef
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 19 22:22:48 2022 +0100 Split out vars commit205336fe9f
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 19 21:48:33 2022 +0100 Move files about, fix focus state, fix colors commit204bafbef1
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 19 21:00:57 2022 +0100 Update buttons to support colors commit1d4be07f52
Merge:c4075772f0
2391319f45
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 19 20:01:19 2022 +0100 Merge branch 'move-manage-jenkins-less' into new-buttons-1-dashboard commitc4075772f0
Merge:0c8d538242
65fcda1350
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sun Jun 19 20:01:02 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit2391319f45
Merge:c9e2823d4a
ac66b476d9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 16 17:47:45 2022 +0100 Merge branch 'master' into move-manage-jenkins-less commitc9e2823d4a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 16 17:35:37 2022 +0100 Update colors commitc78dfd6b50
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 16 17:33:47 2022 +0100 Update theme.less commit0996e759d6
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 16 17:32:52 2022 +0100 Update theme.less commite57bc28553
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 16 17:29:15 2022 +0100 Update theme.less commit69c1633e1e
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 16 17:00:06 2022 +0100 Update style.less commite21d4e7ccb
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 15 19:03:50 2022 +0100 Update breadcrumbs to use new item mixin commit90fc4ba147
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 15 18:57:51 2022 +0100 Rename mixin commit28d06386ce
Merge:d1f42acfd5
65fcda1350
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Tue Jun 14 23:35:10 2022 +0100 Merge branch 'master' into move-manage-jenkins-less commitd1f42acfd5
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Jun 10 11:13:58 2022 +0100 Update table.less commit13d5729892
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Thu Jun 9 10:03:01 2022 +0100 Move from px to rem commit68f5425a7b
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 8 16:40:41 2022 +0100 Update theme.less commit929e2d5e5a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 8 16:28:06 2022 +0100 Update mixins.less commit8c4b4662a2
Merge:8b22270188
77a36fcee9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 8 16:27:25 2022 +0100 Merge branch 'master' into move-manage-jenkins-less commit8b22270188
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 8 16:19:42 2022 +0100 Update theme.less commite925490722
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Jun 8 16:17:02 2022 +0100 Add mixin for items commit0c8d538242
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Sat Jun 4 00:15:05 2022 +0100 Update button styling commit45c9d078be
Merge:e9cb78a924
77a36fcee9
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Fri Jun 3 23:38:49 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commitd579dc5ae2
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Tue May 17 00:52:29 2022 +0100 Update section.less commit43e4f5c48a
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Tue May 17 00:29:22 2022 +0100 Init commite9cb78a924
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Apr 20 21:01:06 2022 +0100 Add backplate to table size toggles commit8cbfe00561
Merge:41d45b5e1f
b28d225d61
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Wed Apr 20 20:30:39 2022 +0100 Merge branch 'master' into new-buttons-1-dashboard commit41d45b5e1f
Author: Jan Faracik <43062514+janfaracik@users.noreply.github.com> Date: Mon Apr 18 12:50:17 2022 +0100 Initial * Remove old import * Simplify code * Update rowSelectionController.jelly * Update inpage-jumplist.js * Escape menu item labels * Temp commenting out * More HTMLUnit fixes * Revert "Temp commenting out" This reverts commitd61d94332f
. * Add support for left/right keyboard nav in menus, fix keyboard priority in submenus, add scroll into view, remove old menu target element * Send error to console rather than displaying it in dropdown * Use 'auto' for overscroll-y * Remove Util.escape(...) from withDisplayName builder * Remove unused import * Show 'No items' placeholder for menus with no items * Keep dropdown parent links active when expanded * Update jumplists.js * Potential fix for failing tests * Update utils.js * Update utils.js * Add badge support to dropdown menus * Remove font weight from badge * Merge master --------- Co-authored-by: Daniel Beck <1831569+daniel-beck@users.noreply.github.com>
This commit is contained in:
parent
42cd0b7c85
commit
54eb759b74
|
@ -2,7 +2,6 @@ package jenkins.model;
|
|||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Functions;
|
||||
import hudson.Util;
|
||||
import hudson.model.Action;
|
||||
import hudson.model.Actionable;
|
||||
import hudson.model.BallColor;
|
||||
|
@ -414,7 +413,7 @@ public interface ModelObjectWithContextMenu extends ModelObject {
|
|||
}
|
||||
|
||||
public MenuItem withDisplayName(String displayName) {
|
||||
this.displayName = Util.escape(displayName);
|
||||
this.displayName = displayName;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,5 @@ THE SOFTWARE.
|
|||
</st:attribute>
|
||||
</st:documentation>
|
||||
|
||||
<st:adjunct includes="lib.form.breadcrumb-config-outline.init"/>
|
||||
<l:breadcrumb title="${attrs.title?:'%configuration'}" id="inpage-nav" />
|
||||
</j:jelly>
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
A.section-anchor {
|
||||
position: relative;
|
||||
top: -2em;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
width: 0px;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
Event.observe(window, "load", function () {
|
||||
/** @type section.SectionNode */
|
||||
var outline = section.buildTree();
|
||||
var menu = new breadcrumbs.ContextMenu();
|
||||
$A(outline.children).each(function (e) {
|
||||
var id = "section" + iota++; // TODO: use human-readable ID
|
||||
var caption = e.getHTML();
|
||||
var cur = $(e.section).down("A.section-anchor");
|
||||
if (cur != null) {
|
||||
id = cur.id;
|
||||
caption = caption.substring(caption.indexOf("</a>") + 4);
|
||||
} else {
|
||||
$(e.section).insert({
|
||||
top: "<a id=" + id + " class='section-anchor'>#</a>",
|
||||
});
|
||||
}
|
||||
menu.add("#" + id, null, caption);
|
||||
});
|
||||
var inpageNav = document.getElementById("inpage-nav");
|
||||
var chevron = document.createElement("li");
|
||||
chevron.classList.add("children");
|
||||
inpageNav.parentNode.insertBefore(chevron, inpageNav.nextSibling);
|
||||
breadcrumbs.attachMenu(chevron, menu);
|
||||
});
|
|
@ -35,8 +35,6 @@ THE SOFTWARE.
|
|||
]]>
|
||||
</st:documentation>
|
||||
|
||||
<st:adjunct includes="lib.layout.breadcrumbs" />
|
||||
|
||||
<j:set var="contents" trim="true">
|
||||
<d:invokeBody />
|
||||
</j:set>
|
||||
|
@ -51,7 +49,7 @@ THE SOFTWARE.
|
|||
hasMenu="${h.isModelWithContextMenu(anc.object)}" />
|
||||
<j:choose>
|
||||
<j:when test="${h.isModelWithChildren(anc.object)}">
|
||||
<li class="children" href="${anc.url}/">
|
||||
<li class="children" data-href="${anc.url}/">
|
||||
<!-- shows '>' for rendering children -->
|
||||
</li>
|
||||
</j:when>
|
||||
|
@ -67,6 +65,4 @@ THE SOFTWARE.
|
|||
<j:out value="${contents}" />
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div id="breadcrumb-menu-target"/><!-- this is where the menu gets rendered -->
|
||||
</j:jelly>
|
||||
|
|
|
@ -1,343 +0,0 @@
|
|||
window.breadcrumbs = (function () {
|
||||
/**
|
||||
* This component actually renders the menu.
|
||||
*
|
||||
* @type {YAHOO.widget.Menu}
|
||||
*/
|
||||
var menu;
|
||||
|
||||
/**
|
||||
* Used for fetching the content of the menu asynchronously from the server
|
||||
*/
|
||||
var controller;
|
||||
|
||||
/**
|
||||
* Current mouse cursor position in the page coordinate.
|
||||
*
|
||||
* @type {YAHOO.util.Point}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var mouse;
|
||||
|
||||
var logger = function () {};
|
||||
// logger = function() { console.log.apply(console,arguments) }; // uncomment this line to enable logging
|
||||
|
||||
// TODO - Use util/security.js xmlEscape in #7474
|
||||
function xmlEscape(str) {
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
return str.replace(/[<>&'"]/g, (match) => {
|
||||
switch (match) {
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "&":
|
||||
return "&";
|
||||
case "'":
|
||||
return "'";
|
||||
case '"':
|
||||
return """;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function makeMenuHtml(icon, iconXml, displayName, badge) {
|
||||
var displaynameSpan = "<span>" + displayName + "</span>";
|
||||
let badgeText;
|
||||
let badgeTooltip;
|
||||
if (badge) {
|
||||
badgeText = xmlEscape(badge.text);
|
||||
badgeTooltip = xmlEscape(badge.tooltip);
|
||||
}
|
||||
const badgeSpan =
|
||||
badge === null
|
||||
? ""
|
||||
: `<span class="yui-menu-badge" tooltip="${badgeTooltip}">${badgeText}</span>`;
|
||||
|
||||
if (iconXml != null) {
|
||||
return iconXml + displaynameSpan + badgeSpan;
|
||||
}
|
||||
|
||||
if (icon === null) {
|
||||
return (
|
||||
"<span style='margin: 2px 4px 2px 2px;' />" +
|
||||
displaynameSpan +
|
||||
badgeSpan
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: move this to the API response in a clean way
|
||||
var isSvgSprite = icon.toLowerCase().indexOf("svg#") !== -1;
|
||||
return isSvgSprite
|
||||
? "<svg class='svg-icon' width='24' height='24' style='margin: 2px 4px 2px 2px;' aria-label='' focusable='false'>" +
|
||||
"<use href='" +
|
||||
icon +
|
||||
"' />" +
|
||||
"</svg>" +
|
||||
displaynameSpan +
|
||||
badgeSpan
|
||||
: "<img src='" +
|
||||
icon +
|
||||
"' width=24 height=24 style='margin: 2px 4px 2px 2px;' alt=''>" +
|
||||
displaynameSpan +
|
||||
badgeSpan;
|
||||
}
|
||||
|
||||
Event.observe(window, "load", function () {
|
||||
menu = new YAHOO.widget.Menu("breadcrumb-menu", {
|
||||
position: "dynamic",
|
||||
hidedelay: 1000,
|
||||
zIndex: 2001,
|
||||
scrollincrement: 2,
|
||||
});
|
||||
});
|
||||
|
||||
Event.observe(document, "mousemove", function (ev) {
|
||||
mouse = new YAHOO.util.Point(ev.pageX, ev.pageY);
|
||||
});
|
||||
|
||||
function combinePath(a, b) {
|
||||
var qs;
|
||||
var i = a.indexOf("?");
|
||||
if (i >= 0) {
|
||||
qs = a.substring(i);
|
||||
a = a.substring(0, i);
|
||||
} else {
|
||||
qs = "";
|
||||
}
|
||||
|
||||
i = a.indexOf("#");
|
||||
if (i >= 0) {
|
||||
a = a.substring(0, i);
|
||||
}
|
||||
|
||||
if (a.endsWith("/")) {
|
||||
return a + b + qs;
|
||||
}
|
||||
return a + "/" + b + qs;
|
||||
}
|
||||
|
||||
function postRequest(action, event, url) {
|
||||
fetch(url, {
|
||||
method: "post",
|
||||
headers: crumb.wrap({}),
|
||||
});
|
||||
if (event.length == 1 && event[0].target != null) {
|
||||
hoverNotification("Done.", event[0].target);
|
||||
}
|
||||
}
|
||||
|
||||
function requireConfirmation(action, event, cfg) {
|
||||
if (confirm(cfg.displayName + ": are you sure?")) {
|
||||
// TODO I18N
|
||||
var form = document.createElement("form");
|
||||
form.setAttribute("method", cfg.post ? "POST" : "GET");
|
||||
form.setAttribute("action", cfg.url);
|
||||
if (cfg.post) {
|
||||
crumb.appendToForm(form);
|
||||
}
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a delayed action and its cancellation.
|
||||
*/
|
||||
// unsure if used in plugins
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function Delayed(action, timeout) {
|
||||
this.schedule = function () {
|
||||
this.cancel();
|
||||
this.token = window.setTimeout(
|
||||
function () {
|
||||
this.token = null;
|
||||
action();
|
||||
}.bind(this),
|
||||
timeout
|
||||
);
|
||||
logger("Scheduled %s", this.token);
|
||||
};
|
||||
this.cancel = function () {
|
||||
if (this.token != null) {
|
||||
logger("Cancelling %s", this.token);
|
||||
window.clearTimeout(this.token);
|
||||
}
|
||||
this.token = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user clicks a mouse to show a context menu.
|
||||
*
|
||||
* If the mouse stays there for a while, a context menu gets displayed.
|
||||
*
|
||||
* @param {HTMLElement} e
|
||||
* anchor tag
|
||||
* @param {String} contextMenuUrl
|
||||
* The URL that renders JSON for context menu. Optional.
|
||||
*/
|
||||
function invokeContextMenu(e, contextMenuUrl) {
|
||||
contextMenuUrl = contextMenuUrl || "contextMenu";
|
||||
|
||||
function showMenu(items) {
|
||||
menu.hide();
|
||||
var pos = [e, "tl", "bl"];
|
||||
if ($(e).hasClassName("tl-tr")) {
|
||||
pos = [e, "tl", "tr"];
|
||||
}
|
||||
menu.cfg.setProperty("context", pos);
|
||||
menu.clearContent();
|
||||
menu.addItems(items);
|
||||
menu.render("breadcrumb-menu-target");
|
||||
menu.show();
|
||||
|
||||
Behaviour.applySubtree(menu.body);
|
||||
}
|
||||
|
||||
// ignore the currently pending call
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
}
|
||||
|
||||
if (e.items) {
|
||||
// use what's already loaded
|
||||
showMenu(e.items());
|
||||
} else {
|
||||
// fetch menu on demand
|
||||
controller = new AbortController();
|
||||
let { signal } = controller;
|
||||
fetch(combinePath(e.getAttribute("href"), contextMenuUrl), { signal })
|
||||
.then((response) => {
|
||||
response.json().then((json) => {
|
||||
const items = json.items;
|
||||
|
||||
function fillMenuItem(e) {
|
||||
if (e.type === "HEADER") {
|
||||
e.text = makeMenuHtml(
|
||||
e.icon,
|
||||
e.iconXml,
|
||||
"<span class='header'>" + e.displayName + "</span>",
|
||||
e.badge
|
||||
);
|
||||
e.disabled = true;
|
||||
} else if (e.type === "SEPARATOR") {
|
||||
e.text = "<span class='separator'>--</span>";
|
||||
e.disabled = true;
|
||||
} else {
|
||||
e.text = makeMenuHtml(
|
||||
e.icon,
|
||||
e.iconXml,
|
||||
e.displayName,
|
||||
e.badge
|
||||
);
|
||||
}
|
||||
if (e.subMenu != null) {
|
||||
e.subMenu = {
|
||||
id: "submenu" + iota++,
|
||||
itemdata: e.subMenu.items.each(fillMenuItem),
|
||||
};
|
||||
}
|
||||
if (e.requiresConfirmation) {
|
||||
e.onclick = {
|
||||
fn: requireConfirmation,
|
||||
obj: { url: e.url, displayName: e.displayName, post: e.post },
|
||||
};
|
||||
delete e.url;
|
||||
} else if (e.post) {
|
||||
e.onclick = { fn: postRequest, obj: e.url };
|
||||
delete e.url;
|
||||
}
|
||||
}
|
||||
|
||||
items.forEach(fillMenuItem);
|
||||
e.items = function () {
|
||||
return items;
|
||||
};
|
||||
showMenu(items);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.name === "AbortError") {
|
||||
// ignore user aborting request, browser console will get unnecessary spam if we don't catch this
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Behaviour.specify("A.model-link", "breadcrumbs", 0, function (link) {
|
||||
const isFirefox = navigator.userAgent.indexOf("Firefox") !== -1;
|
||||
// Firefox adds unwanted lines when copying buttons in text, so use a span instead
|
||||
const dropdownChevron = document.createElement(
|
||||
isFirefox ? "span" : "button"
|
||||
);
|
||||
dropdownChevron.className = "jenkins-menu-dropdown-chevron";
|
||||
dropdownChevron.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
invokeContextMenu(link);
|
||||
});
|
||||
link.appendChild(dropdownChevron);
|
||||
});
|
||||
|
||||
Behaviour.specify("#breadcrumbs LI.children", "breadcrumbs", 0, function (a) {
|
||||
a.observe("click", function () {
|
||||
invokeContextMenu(this, "childrenContextMenu");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @namespace breadcrumbs
|
||||
* @class ContextMenu
|
||||
* @constructor
|
||||
*/
|
||||
var ContextMenu = function () {
|
||||
this.items = [];
|
||||
};
|
||||
ContextMenu.prototype = {
|
||||
/**
|
||||
* Creates a menu item.
|
||||
*
|
||||
* @return {breadcrumbs.MenuItem}
|
||||
*/
|
||||
add: function (url, icon, displayName) {
|
||||
this.items.push({
|
||||
url: url,
|
||||
text: makeMenuHtml(icon, null, displayName),
|
||||
});
|
||||
return this;
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Activates the context menu for the specified breadcrumb element.
|
||||
*
|
||||
* @param {String|HTMLElement} li
|
||||
* The LI tag to which you associate the menu (or its ID)
|
||||
* @param {Function|breadcrumbs.ContextMenu} menu
|
||||
* Pass in the configured menu object. If a function is given, this function
|
||||
* is called each time a menu needs to be displayed. This is convenient for dynamically
|
||||
* populating the content.
|
||||
*/
|
||||
attachMenu: function (li, menu) {
|
||||
$(li).items =
|
||||
typeof menu == "function"
|
||||
? menu
|
||||
: function () {
|
||||
return menu.items;
|
||||
};
|
||||
$(li).addEventListener("click", function () {
|
||||
invokeContextMenu($(li));
|
||||
});
|
||||
},
|
||||
|
||||
ContextMenu: ContextMenu,
|
||||
};
|
||||
})();
|
|
@ -1,7 +1,9 @@
|
|||
import Dropdowns from "@/components/dropdowns";
|
||||
import Notifications from "@/components/notifications";
|
||||
import SearchBar from "@/components/search-bar";
|
||||
import Tooltips from "@/components/tooltips";
|
||||
|
||||
Dropdowns.init();
|
||||
Notifications.init();
|
||||
SearchBar.init();
|
||||
Tooltips.init();
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import Jumplists from "@/components/dropdowns/jumplists";
|
||||
import InpageJumplist from "@/components/dropdowns/inpage-jumplist";
|
||||
|
||||
function init() {
|
||||
Jumplists.init();
|
||||
InpageJumplist.init();
|
||||
}
|
||||
|
||||
export default { init };
|
|
@ -0,0 +1,26 @@
|
|||
import { toId } from "@/util/dom";
|
||||
|
||||
/*
|
||||
* Generates a jump list for the active breadcrumb to jump to
|
||||
* sections on the page (if using <f:breadcrumb-config-outline />)
|
||||
*/
|
||||
function init() {
|
||||
const inpageNavigationBreadcrumb = document.querySelector("#inpage-nav");
|
||||
|
||||
if (inpageNavigationBreadcrumb) {
|
||||
const chevron = document.createElement("li");
|
||||
chevron.classList.add("children");
|
||||
chevron.items = Array.from(
|
||||
document.querySelectorAll(
|
||||
"form > div > div > .jenkins-section > .jenkins-section__title"
|
||||
)
|
||||
).map((section) => {
|
||||
section.id = toId(section.textContent);
|
||||
return { label: section.textContent, url: "#" + section.id };
|
||||
});
|
||||
|
||||
inpageNavigationBreadcrumb.after(chevron);
|
||||
}
|
||||
}
|
||||
|
||||
export default { init };
|
|
@ -0,0 +1,123 @@
|
|||
import Path from "@/util/path";
|
||||
import behaviorShim from "@/util/behavior-shim";
|
||||
import Utils from "@/components/dropdowns/utils";
|
||||
|
||||
function init() {
|
||||
generateJumplistAccessors();
|
||||
generateDropdowns();
|
||||
}
|
||||
|
||||
/*
|
||||
* Appends a ⌄ button at the end of links which support jump lists
|
||||
*/
|
||||
function generateJumplistAccessors() {
|
||||
document.querySelectorAll("A.model-link").forEach((link) => {
|
||||
const isFirefox = navigator.userAgent.indexOf("Firefox") !== -1;
|
||||
// Firefox adds unwanted lines when copying buttons in text, so use a span instead
|
||||
const dropdownChevron = document.createElement(
|
||||
isFirefox ? "span" : "button"
|
||||
);
|
||||
dropdownChevron.className = "jenkins-menu-dropdown-chevron";
|
||||
dropdownChevron.dataset.href = link.href;
|
||||
dropdownChevron.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
link.appendChild(dropdownChevron);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates the dropdowns for the jump lists
|
||||
*/
|
||||
function generateDropdowns() {
|
||||
behaviorShim.specify(
|
||||
"li.children, #menuSelector, .jenkins-menu-dropdown-chevron",
|
||||
"-dropdown-",
|
||||
1000,
|
||||
(element) =>
|
||||
Utils.generateDropdown(element, (instance) => {
|
||||
const href = element.dataset.href;
|
||||
const jumplistType = !element.classList.contains("children")
|
||||
? "contextMenu"
|
||||
: "childrenContextMenu";
|
||||
|
||||
if (element.items) {
|
||||
instance.setContent(Utils.generateDropdownItems(element.items));
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(Path.combinePath(href, jumplistType))
|
||||
.then((response) => response.json())
|
||||
.then((json) =>
|
||||
instance.setContent(
|
||||
Utils.generateDropdownItems(
|
||||
mapChildrenItemsToDropdownItems(json.items)
|
||||
)
|
||||
)
|
||||
)
|
||||
.catch((error) => console.log(`Jumplist request failed: ${error}`))
|
||||
.finally(() => (instance.loaded = true));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates the contents for the dropdown
|
||||
*/
|
||||
function mapChildrenItemsToDropdownItems(items) {
|
||||
return items.map((item) => {
|
||||
if (item.type === "HEADER") {
|
||||
return {
|
||||
type: "HEADER",
|
||||
label: item.displayName,
|
||||
};
|
||||
}
|
||||
|
||||
if (item.type === "SEPARATOR") {
|
||||
return {
|
||||
type: "SEPARATOR",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
icon: item.icon,
|
||||
iconXml: item.iconXml,
|
||||
label: item.displayName,
|
||||
url: item.url,
|
||||
type: item.post || item.requiresConfirmation ? "button" : "link",
|
||||
badge: item.badge,
|
||||
onClick: () => {
|
||||
if (item.post || item.requiresConfirmation) {
|
||||
if (item.requiresConfirmation) {
|
||||
if (confirm((item.text || item.displayName) + ": are you sure?")) {
|
||||
// TODO I18N
|
||||
const form = document.createElement("form");
|
||||
form.setAttribute("method", item.post ? "POST" : "GET");
|
||||
form.setAttribute("action", item.url);
|
||||
if (item.post) {
|
||||
crumb.appendToForm(form);
|
||||
}
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
} else {
|
||||
fetch(item.url, {
|
||||
method: "post",
|
||||
headers: crumb.wrap({}),
|
||||
});
|
||||
if (event.length === 1 && event[0].target != null) {
|
||||
hoverNotification("Done.", event[0].target);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
subMenu: item.subMenu
|
||||
? () => {
|
||||
return mapChildrenItemsToDropdownItems(item.subMenu.items);
|
||||
}
|
||||
: null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default { init };
|
|
@ -0,0 +1,103 @@
|
|||
import { createElementFromHtml } from "@/util/dom";
|
||||
import { xmlEscape } from "@/util/security";
|
||||
|
||||
function dropdown() {
|
||||
return {
|
||||
content: "<p class='jenkins-spinner'></p>",
|
||||
interactive: true,
|
||||
trigger: "click",
|
||||
allowHTML: true,
|
||||
placement: "bottom-start",
|
||||
arrow: false,
|
||||
theme: "dropdown",
|
||||
appendTo: document.body,
|
||||
offset: [0, 0],
|
||||
animation: "dropdown",
|
||||
onShow: (instance) => {
|
||||
const referenceParent = instance.reference.parentNode;
|
||||
|
||||
if (referenceParent.classList.contains("model-link")) {
|
||||
referenceParent.classList.add("model-link--open");
|
||||
}
|
||||
},
|
||||
onHide: (instance) => {
|
||||
const referenceParent = instance.reference.parentNode;
|
||||
referenceParent.classList.remove("model-link--open");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function menuItem(options) {
|
||||
const itemOptions = Object.assign(
|
||||
{
|
||||
type: "link",
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
const label = xmlEscape(itemOptions.label);
|
||||
let badgeText;
|
||||
let badgeTooltip;
|
||||
if (itemOptions.badge) {
|
||||
badgeText = xmlEscape(itemOptions.badge.text);
|
||||
badgeTooltip = xmlEscape(itemOptions.badge.tooltip);
|
||||
}
|
||||
const tag = itemOptions.type === "link" ? "a" : "button";
|
||||
|
||||
const item = createElementFromHtml(`
|
||||
<${tag} class="jenkins-dropdown__item" href="${itemOptions.url}">
|
||||
${
|
||||
itemOptions.icon
|
||||
? `<div class="jenkins-dropdown__item__icon">${
|
||||
itemOptions.iconXml
|
||||
? itemOptions.iconXml
|
||||
: `<img alt="${label}" src="${itemOptions.icon}" />`
|
||||
}</div>`
|
||||
: ``
|
||||
}
|
||||
${label}
|
||||
${
|
||||
itemOptions.badge != null
|
||||
? `<span class="jenkins-dropdown__item__badge" tooltip="${badgeTooltip}">${badgeText}</span>`
|
||||
: ``
|
||||
}
|
||||
${
|
||||
itemOptions.subMenu != null
|
||||
? `<span class="jenkins-dropdown__item__chevron"></span>`
|
||||
: ``
|
||||
}
|
||||
</${tag}>
|
||||
`);
|
||||
|
||||
if (options.onClick) {
|
||||
item.addEventListener("click", () => options.onClick());
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function heading(label) {
|
||||
return createElementFromHtml(
|
||||
`<p class="jenkins-dropdown__heading">${label}</p>`
|
||||
);
|
||||
}
|
||||
|
||||
function separator() {
|
||||
return createElementFromHtml(
|
||||
`<div class="jenkins-dropdown__separator"></div>`
|
||||
);
|
||||
}
|
||||
|
||||
function placeholder(label) {
|
||||
return createElementFromHtml(
|
||||
`<p class="jenkins-dropdown__placeholder">${label}</p>`
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
dropdown,
|
||||
menuItem,
|
||||
heading,
|
||||
separator,
|
||||
placeholder,
|
||||
};
|
|
@ -0,0 +1,135 @@
|
|||
import Templates from "@/components/dropdowns/templates";
|
||||
import makeKeyboardNavigable from "@/util/keyboard";
|
||||
import tippy from "tippy.js";
|
||||
import behaviorShim from "@/util/behavior-shim";
|
||||
|
||||
const SELECTED_ITEM_CLASS = "jenkins-dropdown__item--selected";
|
||||
|
||||
/*
|
||||
* Generates the dropdowns for the given element
|
||||
* Preloads the data on hover for speed
|
||||
* @param element - the element to generate the dropdown for
|
||||
* @param callback - called to retrieve the list of dropdown items
|
||||
*/
|
||||
function generateDropdown(element, callback) {
|
||||
tippy(
|
||||
element,
|
||||
Object.assign({}, Templates.dropdown(), {
|
||||
onCreate(instance) {
|
||||
instance.reference.addEventListener("mouseenter", () => {
|
||||
if (instance.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
instance.popper.addEventListener("click", () => {
|
||||
instance.hide();
|
||||
});
|
||||
|
||||
callback(instance);
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates the contents for the dropdown
|
||||
*/
|
||||
function generateDropdownItems(items) {
|
||||
const menuItems = document.createElement("div");
|
||||
menuItems.classList.add("jenkins-dropdown");
|
||||
|
||||
items
|
||||
.map((item) => {
|
||||
if (item.type === "HEADER") {
|
||||
return Templates.heading(item.label);
|
||||
}
|
||||
|
||||
if (item.type === "SEPARATOR") {
|
||||
return Templates.separator();
|
||||
}
|
||||
|
||||
const menuItem = Templates.menuItem(item);
|
||||
|
||||
if (item.subMenu != null) {
|
||||
tippy(
|
||||
menuItem,
|
||||
Object.assign({}, Templates.dropdown(), {
|
||||
content: generateDropdownItems(item.subMenu()),
|
||||
trigger: "mouseenter",
|
||||
placement: "right-start",
|
||||
offset: [-8, 0],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
})
|
||||
.forEach((item) => menuItems.appendChild(item));
|
||||
|
||||
if (items.length === 0) {
|
||||
menuItems.appendChild(Templates.placeholder("No items"));
|
||||
}
|
||||
|
||||
makeKeyboardNavigable(
|
||||
menuItems,
|
||||
() => menuItems.querySelectorAll(".jenkins-dropdown__item"),
|
||||
SELECTED_ITEM_CLASS,
|
||||
(selectedItem, key) => {
|
||||
switch (key) {
|
||||
case "ArrowLeft": {
|
||||
const root = selectedItem.closest("[data-tippy-root]");
|
||||
if (root) {
|
||||
const tippyReference = root._tippy;
|
||||
if (tippyReference) {
|
||||
tippyReference.hide();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "ArrowRight": {
|
||||
const tippyRef = selectedItem._tippy;
|
||||
if (!tippyRef) {
|
||||
break;
|
||||
}
|
||||
|
||||
tippyRef.show();
|
||||
tippyRef.props.content
|
||||
.querySelector(".jenkins-dropdown__item")
|
||||
.classList.add(SELECTED_ITEM_CLASS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
(container) => {
|
||||
const isVisible =
|
||||
window.getComputedStyle(container).visibility === "visible";
|
||||
const isLastDropdown = Array.from(
|
||||
document.querySelectorAll(".jenkins-dropdown")
|
||||
)
|
||||
.filter((dropdown) => container !== dropdown)
|
||||
.filter(
|
||||
(dropdown) =>
|
||||
window.getComputedStyle(dropdown).visibility === "visible"
|
||||
)
|
||||
.every(
|
||||
(dropdown) =>
|
||||
!(
|
||||
container.compareDocumentPosition(dropdown) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING
|
||||
)
|
||||
);
|
||||
|
||||
return isVisible && isLastDropdown;
|
||||
}
|
||||
);
|
||||
|
||||
behaviorShim.applySubtree(menuItems);
|
||||
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
export default {
|
||||
generateDropdown,
|
||||
generateDropdownItems,
|
||||
};
|
|
@ -3,4 +3,9 @@ function specify(selector, id, priority, behavior) {
|
|||
Behaviour.specify(selector, id, priority, behavior);
|
||||
}
|
||||
|
||||
export default { specify };
|
||||
function applySubtree(startNode, includeSelf) {
|
||||
// eslint-ignore-next-line
|
||||
Behaviour.applySubtree(startNode, includeSelf);
|
||||
}
|
||||
|
||||
export default { specify, applySubtree };
|
||||
|
|
|
@ -2,26 +2,29 @@
|
|||
* @param {Element} container - the container for the items
|
||||
* @param {function(): NodeListOf<Element>} itemsFunc - function which returns the list of items
|
||||
* @param {string} selectedClass - the class to apply to the selected item
|
||||
* @param {function()} additionalBehaviours - add additional keyboard shortcuts to the focused item
|
||||
* @param hasKeyboardPriority - set if custom behaviour is needed to decide whether the element has keyboard priority
|
||||
*/
|
||||
export default function makeKeyboardNavigable(
|
||||
container,
|
||||
itemsFunc,
|
||||
selectedClass
|
||||
selectedClass,
|
||||
additionalBehaviours = () => {},
|
||||
hasKeyboardPriority = () =>
|
||||
window.getComputedStyle(container).visibility === "visible"
|
||||
) {
|
||||
window.addEventListener("keydown", (e) => {
|
||||
let items = itemsFunc();
|
||||
let selectedItem = Array.from(items).find((a) =>
|
||||
a.classList.contains(selectedClass)
|
||||
);
|
||||
const isVisible =
|
||||
window.getComputedStyle(container).visibility === "visible";
|
||||
let items = Array.from(itemsFunc());
|
||||
let selectedItem = items.find((a) => a.classList.contains(selectedClass));
|
||||
|
||||
// Only navigate through the list of items if the container is active on the screen
|
||||
if (container && isVisible) {
|
||||
if (container && hasKeyboardPriority(container)) {
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
|
||||
if (selectedItem) {
|
||||
selectedItem.classList.remove(selectedClass);
|
||||
const next = selectedItem.nextSibling;
|
||||
const next = items[items.indexOf(selectedItem) + 1];
|
||||
|
||||
if (next) {
|
||||
selectedItem = next;
|
||||
|
@ -33,12 +36,15 @@ export default function makeKeyboardNavigable(
|
|||
}
|
||||
|
||||
if (selectedItem !== null) {
|
||||
selectedItem.scrollIntoView(false);
|
||||
selectedItem.classList.add(selectedClass);
|
||||
}
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
|
||||
if (selectedItem) {
|
||||
selectedItem.classList.remove(selectedClass);
|
||||
const previous = selectedItem.previousSibling;
|
||||
const previous = items[items.indexOf(selectedItem) - 1];
|
||||
|
||||
if (previous) {
|
||||
selectedItem = previous;
|
||||
|
@ -50,12 +56,15 @@ export default function makeKeyboardNavigable(
|
|||
}
|
||||
|
||||
if (selectedItem !== null) {
|
||||
selectedItem.scrollIntoView(false);
|
||||
selectedItem.classList.add(selectedClass);
|
||||
}
|
||||
} else if (e.key === "Enter") {
|
||||
if (selectedItem !== null) {
|
||||
selectedItem.click();
|
||||
}
|
||||
} else {
|
||||
additionalBehaviours(selectedItem, e.key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
function combinePath(pathOne, pathTwo) {
|
||||
let queryParams;
|
||||
let i = pathOne.indexOf("?");
|
||||
if (i >= 0) {
|
||||
queryParams = pathOne.substring(i);
|
||||
} else {
|
||||
queryParams = "";
|
||||
}
|
||||
|
||||
i = pathOne.indexOf("#");
|
||||
if (i >= 0) {
|
||||
pathOne = pathOne.substring(0, i);
|
||||
}
|
||||
|
||||
if (pathOne.endsWith("/")) {
|
||||
return pathOne + pathTwo + queryParams;
|
||||
}
|
||||
return pathOne + "/" + pathTwo + queryParams;
|
||||
}
|
||||
|
||||
export default { combinePath };
|
|
@ -62,7 +62,8 @@
|
|||
margin-right: 30px !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover,
|
||||
&--open {
|
||||
margin-right: 30px !important;
|
||||
}
|
||||
}
|
||||
|
@ -210,6 +211,14 @@
|
|||
margin-left: -1.5rem !important;
|
||||
}
|
||||
|
||||
&--open {
|
||||
&::before {
|
||||
background-color: var(--item-background--hover) !important;
|
||||
right: -30px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&--open,
|
||||
&:hover {
|
||||
& + a.jenkins-table__badge {
|
||||
margin-left: -1.5rem !important;
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
@use "../abstracts/mixins";
|
||||
|
||||
.tippy-box[data-theme~="dropdown"] {
|
||||
padding: 0.4rem;
|
||||
border-radius: 15px;
|
||||
box-shadow: var(--dropdown-box-shadow);
|
||||
outline: none !important;
|
||||
backdrop-filter: var(--dropdown-backdrop-filter);
|
||||
max-width: unset !important;
|
||||
max-height: 75vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.tippy-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
|
||||
.jenkins-spinner {
|
||||
margin: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tippy-box[data-animation="dropdown"][data-state="hidden"] {
|
||||
opacity: 0;
|
||||
transform: scale(0.975);
|
||||
|
||||
&[data-placement^="top"] {
|
||||
transform-origin: bottom;
|
||||
transform: translateY(10px) scale(0.975);
|
||||
}
|
||||
|
||||
&[data-placement^="bottom"] {
|
||||
transform-origin: top;
|
||||
transform: translateY(-10px) scale(0.975);
|
||||
}
|
||||
}
|
||||
|
||||
.jenkins-dropdown {
|
||||
display: contents;
|
||||
|
||||
&__separator {
|
||||
position: relative;
|
||||
height: 0.125rem;
|
||||
margin: 0.3rem -0.3rem;
|
||||
border: none;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: var(--text-color-secondary);
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
&__heading {
|
||||
color: var(--text-color-secondary) !important;
|
||||
margin: 0.4rem 0.55rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
opacity: 0.8;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__placeholder {
|
||||
color: var(--text-color-secondary) !important;
|
||||
margin: 0.4rem 0.55rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&__item {
|
||||
--item-background--hover: var(--button-background--hover);
|
||||
--item-background--active: var(--button-background--active);
|
||||
--item-box-shadow--focus: var(--button-box-shadow--focus);
|
||||
|
||||
@include mixins.item;
|
||||
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
padding: 0.4rem 1.75rem 0.4rem 0.6rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none !important;
|
||||
background: transparent;
|
||||
color: var(--text-color) !important;
|
||||
border-radius: 0.66rem;
|
||||
cursor: pointer;
|
||||
min-height: 36px;
|
||||
white-space: nowrap;
|
||||
gap: 1.2ch;
|
||||
min-width: 180px;
|
||||
user-select: none;
|
||||
|
||||
&__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
margin-right: 0.1rem;
|
||||
|
||||
svg,
|
||||
img {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
filter: saturate(0.6);
|
||||
}
|
||||
|
||||
&[class*="color"] {
|
||||
background: transparent;
|
||||
color: var(--color) !important;
|
||||
|
||||
&::before {
|
||||
background: currentColor !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
box-shadow: 0 0 0 0.66rem currentColor;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
&::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
&::after {
|
||||
box-shadow: 0 0 0 0.33rem currentColor;
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__badge {
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
margin-right: -0.85rem;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -0.0625rem -0.375rem;
|
||||
background: var(--text-color-secondary);
|
||||
opacity: 0.1;
|
||||
border-radius: 100vmax;
|
||||
}
|
||||
}
|
||||
|
||||
&__chevron {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0.125rem;
|
||||
bottom: 0;
|
||||
background: var(--text-color-secondary);
|
||||
width: 1rem;
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'%3E%3Ctitle%3EChevron Forward%3C/title%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='48' d='M184 112l144 144-144 144'/%3E%3C/svg%3E");
|
||||
mask-size: contain;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.jenkins-dropdown__item--selected {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jenkins-dropdown__item--selected {
|
||||
background: var(--item-background--hover);
|
||||
animation: pulse 1s ease-in-out forwards;
|
||||
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
background: var(--item-background--active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jenkins-overflow-button__ellipsis {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.15rem;
|
||||
width: 1.4rem;
|
||||
height: 1.1rem;
|
||||
margin-inline: -0.1rem;
|
||||
|
||||
span {
|
||||
min-width: 5px;
|
||||
min-height: 5px;
|
||||
border: 1px solid currentColor;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
@use "./modules/buttons";
|
||||
@use "./modules/buttons-deprecated";
|
||||
@use "./modules/content-blocks";
|
||||
@use "./modules/dropdowns";
|
||||
@use "./modules/icons";
|
||||
@use "./modules/modals";
|
||||
@use "./modules/notifications";
|
||||
|
|
Loading…
Reference in New Issue