mirror of https://github.com/jenkinsci/jenkins.git
Add tab support for user dropdown
This commit is contained in:
parent
43063425f5
commit
7ca2e8f63b
|
@ -1,31 +1,25 @@
|
||||||
<?jelly escape-by-default='true'?>
|
<?jelly escape-by-default='true'?>
|
||||||
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout">
|
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:dd="/lib/layout/dropdowns">
|
||||||
<div class="jenkins-dropdown">
|
<dd:custom>
|
||||||
<a class="jenkins-dropdown__item" href="${rootURL}/${it.user.url}">
|
<a class="jenkins-dropdown__item" href="${rootURL}/${it.user.url}">
|
||||||
<div class="jenkins-dropdown__item__icon">
|
<div class="jenkins-dropdown__item__icon">
|
||||||
<l:icon src="${it.iconFileName}" class="jenkins-avatar" />
|
<l:icon src="${it.iconFileName}" class="jenkins-avatar" />
|
||||||
</div>
|
</div>
|
||||||
${it.user.fullName}
|
${it.user.fullName}
|
||||||
</a>
|
</a>
|
||||||
|
</dd:custom>
|
||||||
|
|
||||||
<div class="jenkins-dropdown__separator" />
|
<dd:separator />
|
||||||
|
|
||||||
<j:forEach var="action" items="${it.actions}">
|
<j:forEach var="action" items="${it.actions}">
|
||||||
<a class="jenkins-dropdown__item" href="${rootURL}/${it.user.url}/${action.urlName}">
|
<dd:item icon="${action.iconFileName}"
|
||||||
<div class="jenkins-dropdown__item__icon">
|
text="${action.displayName}"
|
||||||
<l:icon src="${action.iconFileName}" />
|
href="${rootURL}/${it.user.url}/${action.urlName}" />
|
||||||
</div>
|
</j:forEach>
|
||||||
${action.displayName}
|
|
||||||
</a>
|
|
||||||
</j:forEach>
|
|
||||||
|
|
||||||
<div class="jenkins-dropdown__separator" />
|
<dd:separator />
|
||||||
|
|
||||||
<a class="jenkins-dropdown__item" href="${rootURL}/logout">
|
<dd:item icon="symbol-log-out"
|
||||||
<div class="jenkins-dropdown__item__icon">
|
text="${%Sign out}"
|
||||||
<l:icon src="symbol-log-out" />
|
href="${rootURL}/logout" />
|
||||||
</div>
|
|
||||||
${%Sign out}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</j:jelly>
|
</j:jelly>
|
|
@ -36,48 +36,65 @@
|
||||||
<st:include page="suffix" optional="true" />
|
<st:include page="suffix" optional="true" />
|
||||||
|
|
||||||
<j:forEach var="action" items="${it.actions}">
|
<j:forEach var="action" items="${it.actions}">
|
||||||
<j:set var="isCurrent" value="${h.hyperlinkMatchesCurrentPage(action.urlName)}" />
|
<j:set var="isCurrent" value="${h.hyperlinkMatchesCurrentPage(action.urlName)}" />
|
||||||
|
|
||||||
|
<j:set var="jumplist">
|
||||||
|
<st:include it="${action}" page="jumplist.jelly" optional="true" />
|
||||||
|
</j:set>
|
||||||
|
|
||||||
|
<j:if test="${jumplist.length() == 0}">
|
||||||
<j:set var="tooltip">
|
<j:set var="tooltip">
|
||||||
<j:set var="interactive" value="true" />
|
<st:include it="${action}" page="tooltip.jelly" optional="true" />
|
||||||
<st:include it="${action}" page="jumplist.jelly" optional="true" />
|
|
||||||
</j:set>
|
</j:set>
|
||||||
|
</j:if>
|
||||||
|
|
||||||
<j:if test="${tooltip.length() == 0}">
|
<j:set var="badge" value="${action.badge}" />
|
||||||
<j:set var="interactive" value="false" />
|
<j:if test="${jumplist.length() == 0 and tooltip.length() == 0}">
|
||||||
<j:set var="tooltip">
|
<j:set var="tooltip">
|
||||||
<st:include it="${action}" page="tooltip.jelly" optional="true" />
|
<div style="text-align: center;">${action.displayName}</div>
|
||||||
</j:set>
|
|
||||||
</j:if>
|
|
||||||
|
|
||||||
<j:set var="badge" value="${action.badge}" />
|
|
||||||
<j:if test="${tooltip.length() == 0}">
|
|
||||||
<j:set var="tooltip">
|
|
||||||
<div style="text-align: center;">${action.displayName}</div>
|
|
||||||
<j:if test="${badge != null}">
|
|
||||||
<div style="text-align: center; color: var(--text-color-secondary)">${badge.tooltip}</div>
|
|
||||||
</j:if>
|
|
||||||
</j:set>
|
|
||||||
</j:if>
|
|
||||||
|
|
||||||
<x:element name="${action.urlName == null ? 'button' : 'a'}">
|
|
||||||
<x:attribute name="id">root-action-${action.class.simpleName}</x:attribute>
|
|
||||||
<x:attribute name="href">${h.getActionUrl(app.url, action)}</x:attribute>
|
|
||||||
<x:attribute name="data-html-tooltip" escapeText="false"><j:out value="${tooltip}" /></x:attribute>
|
|
||||||
<x:attribute name="data-tooltip-interactive">${interactive}</x:attribute>
|
|
||||||
<x:attribute name="data-tippy-theme">${interactive ? 'dropdown' : 'tooltip'}</x:attribute>
|
|
||||||
<x:attribute name="data-tippy-trigger">${interactive ? 'mouseenter focus' : 'mouseenter'}</x:attribute>
|
|
||||||
<x:attribute name="data-tippy-touch">false</x:attribute>
|
|
||||||
<x:attribute name="data-type">header-action</x:attribute>
|
|
||||||
<x:attribute name="draggable">false</x:attribute>
|
|
||||||
<x:attribute name="class">jenkins-button ${isCurrent ? '' : 'jenkins-button--tertiary'}</x:attribute>
|
|
||||||
<l:icon src="${action.iconFileName}" />
|
|
||||||
<span class="jenkins-visually-hidden">${action.displayName}</span>
|
|
||||||
<j:if test="${badge != null}">
|
<j:if test="${badge != null}">
|
||||||
<span class="jenkins-badge jenkins-!-${badge.severity}-color" />
|
<div style="text-align: center; color: var(--text-color-secondary)">${badge.tooltip}</div>
|
||||||
</j:if>
|
</j:if>
|
||||||
</x:element>
|
</j:set>
|
||||||
</j:forEach>
|
</j:if>
|
||||||
|
|
||||||
|
<j:set var="interactive" value="${jumplist.length() gt 0}" />
|
||||||
|
<x:element name="${action.urlName == null ? 'button' : 'a'}">
|
||||||
|
<x:attribute name="data-dropdown">${interactive}</x:attribute>
|
||||||
|
<x:attribute name="id">root-action-${action.class.simpleName}</x:attribute>
|
||||||
|
<x:attribute name="href">${h.getActionUrl(app.url, action)}</x:attribute>
|
||||||
|
<j:if test="${interactive}">
|
||||||
|
<x:attribute name="data-tippy-offset">[0, 10]</x:attribute>
|
||||||
|
</j:if>
|
||||||
|
<j:if test="${!interactive}">
|
||||||
|
<x:attribute name="data-html-tooltip" escapeText="false"><j:out value="${tooltip}" /></x:attribute>
|
||||||
|
</j:if>
|
||||||
|
<x:attribute name="data-tooltip-interactive">${interactive}</x:attribute>
|
||||||
|
<x:attribute name="data-tippy-animation">tooltip</x:attribute>
|
||||||
|
<x:attribute name="data-tippy-theme">${interactive ? 'dropdown' : 'tooltip'}</x:attribute>
|
||||||
|
<x:attribute name="data-tippy-trigger">mouseenter focus</x:attribute>
|
||||||
|
<x:attribute name="data-tippy-touch">${interactive}</x:attribute>
|
||||||
|
<x:attribute name="data-type">header-action</x:attribute>
|
||||||
|
<x:attribute name="draggable">false</x:attribute>
|
||||||
|
<x:attribute name="class">jenkins-button ${isCurrent ? '' : 'jenkins-button--tertiary'}</x:attribute>
|
||||||
|
<l:icon src="${action.iconFileName}" />
|
||||||
|
<span class="jenkins-visually-hidden">${action.displayName}</span>
|
||||||
|
<j:if test="${badge != null}">
|
||||||
|
<span class="jenkins-badge jenkins-!-${badge.severity}-color" />
|
||||||
|
</j:if>
|
||||||
|
</x:element>
|
||||||
|
|
||||||
|
<j:if test="${interactive}">
|
||||||
|
<template>
|
||||||
|
<div class="jenkins-dropdown">
|
||||||
|
<j:out value="${jumplist}" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</j:if>
|
||||||
|
|
||||||
|
<j:set var="jumplist" />
|
||||||
|
<j:set var="tooltip" />
|
||||||
|
</j:forEach>
|
||||||
|
|
||||||
<h:login/>
|
<h:login/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,13 +10,21 @@ function init() {
|
||||||
"-dropdown-",
|
"-dropdown-",
|
||||||
1000,
|
1000,
|
||||||
(element) => {
|
(element) => {
|
||||||
Utils.generateDropdown(element, (instance) => {
|
Utils.generateDropdown(
|
||||||
const elements =
|
element,
|
||||||
element.nextElementSibling.content.children[0].children;
|
(instance) => {
|
||||||
const mappedItems = Utils.convertHtmlToItems(elements);
|
const elements =
|
||||||
|
element.nextElementSibling.content.children[0].children;
|
||||||
|
const mappedItems = Utils.convertHtmlToItems(elements);
|
||||||
|
|
||||||
instance.setContent(Utils.generateDropdownItems(mappedItems));
|
instance.setContent(Utils.generateDropdownItems(mappedItems));
|
||||||
});
|
instance.loaded = true;
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
{
|
||||||
|
appendTo: "parent",
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
import { createElementFromHtml } from "@/util/dom";
|
import { createElementFromHtml } from "@/util/dom";
|
||||||
import { xmlEscape } from "@/util/security";
|
import { xmlEscape } from "@/util/security";
|
||||||
|
|
||||||
|
const hideOnPopperBlur = {
|
||||||
|
name: "hideOnPopperBlur",
|
||||||
|
defaultValue: true,
|
||||||
|
fn(instance) {
|
||||||
|
return {
|
||||||
|
onCreate() {
|
||||||
|
instance.popper.addEventListener("focusout", (event) => {
|
||||||
|
if (
|
||||||
|
instance.props.hideOnPopperBlur &&
|
||||||
|
event.relatedTarget &&
|
||||||
|
!instance.popper.contains(event.relatedTarget)
|
||||||
|
) {
|
||||||
|
instance.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function dropdown() {
|
function dropdown() {
|
||||||
return {
|
return {
|
||||||
content: "<p class='jenkins-spinner'></p>",
|
content: "<p class='jenkins-spinner'></p>",
|
||||||
|
@ -11,6 +31,7 @@ function dropdown() {
|
||||||
arrow: false,
|
arrow: false,
|
||||||
theme: "dropdown",
|
theme: "dropdown",
|
||||||
appendTo: document.body,
|
appendTo: document.body,
|
||||||
|
plugins: [hideOnPopperBlur],
|
||||||
offset: [0, 0],
|
offset: [0, 0],
|
||||||
animation: "dropdown",
|
animation: "dropdown",
|
||||||
onShow: (instance) => {
|
onShow: (instance) => {
|
||||||
|
|
|
@ -38,7 +38,9 @@ function generateDropdown(element, callback, immediate, options = {}) {
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
onload();
|
onload();
|
||||||
} else {
|
} else {
|
||||||
instance.reference.addEventListener("mouseenter", onload);
|
["mouseenter", "focus"].forEach((event) => {
|
||||||
|
instance.reference.addEventListener(event, onload);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -110,7 +110,10 @@ function generateOverflowButton() {
|
||||||
const actionsContainer = document.querySelector(".jenkins-header__actions");
|
const actionsContainer = document.querySelector(".jenkins-header__actions");
|
||||||
|
|
||||||
// Insert the new element before the last child
|
// Insert the new element before the last child
|
||||||
actionsContainer.insertBefore(element, actionsContainer.lastChild);
|
actionsContainer.insertBefore(
|
||||||
|
element,
|
||||||
|
actionsContainer.children[actionsContainer.children.length - 2],
|
||||||
|
);
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue