Focus trap contacts menu with NcHeaderMenu port

Signed-off-by: Christopher Ng <chrng8@gmail.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
Signed-off-by: Christopher Ng <chrng8@gmail.com>
This commit is contained in:
Christopher Ng 2023-01-12 01:35:53 +00:00
parent f7ff54860b
commit f6aa5224c4
17 changed files with 235 additions and 220 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAkBA,yQACC,SACA,UACA,SACA,oBACA,eACA,oBACA,wBACA,eACA,uDACA,qBAGD,6CACC,aAID,kGAEC,gDACA,aAGD,UACC,YAGD,6DACC,cAGD,KACC,gBAGD,MACC,yBACA,iBACA,mBAGD,cACC,gBACA,mBAGD,YACC,sBAGD,EACC,SACA,6BACA,qBACA,eACA,IACC,eAIF,WACC,aACA,0BAGD,MACC,eACA,QACC,eAIF,0BACC,eAGD,GACC,gBAGD,KACC,mBAEA,mCACA,uCACA,6BACA,6BAGD,mBACC,kBAGD,qBACC,kBACA,uBACA,qBACA,2BACA,2DACA,uBAGD,iBACC,qBACA,aACA,gCAGD,eACC,YACA,aAGD,cACC,eACA,MACA,SACA,OACA,YACA,WACA,aACA,kBACA,gDACA,gCACA,iBACA,eACA,kBACC,cACA,kBACA,UACA,QACA,gBAED,gBACC,gCACA,sDACA,4CACC,qCAOH,oBACC,WACA,YAGD,gCACC,+BAGD,0BACC,kCACA,yCACA,+BACA,4BAMD,YACC,8CACA,gCAMD,kBACC,sBAKD,4BAEC,oCACA,kBACA,gBACA,WACA,sDACC,gBAED,sEACC,gBAED,kCACC,mBAED,oHAEC,qBACA,YACA,WACA,mBACA,gcAEC,WAOH,sBACC,WASD,oCACC,kBACA,yBACA,sBACA,qBACA,iBAKD,kBACC,kBACA,UACA,SACA,YAGD,8BACC,WACA,oBACA,wBACA,wBAGD,2EACC,WAED,oGACC,0CACA,UACA,qBAGD,mDACC,6BACA,YACA,WACA,yCACA,4BACA,2BACA,WAOA,qEACC,UAED,qEACC,UAIF,wEACC,aAGD,2CACC,mBAGD,yBACC,kBACA,qBACA,iBAED,qBACC,cACA,QACA,iBACA,kBACA,aAKD,4CACC,eACA,YACA,mCACA,6BACA,qDAIA,2BACC,4BAKD,wBACC,sBACA,4BACA,+BACC,2CACA,qBACA,kBAGF,0BACC,qBACA,gBAIF,YACC,YACA,8BACA,oBACC,sBAIF,eACC,2CAUD,mBACC,kBACA,cACA,2BACC,kBACA,cAIF,UACC,gBAGD,8CACC,UAIA,oGAGC,WAIF,mBACC,WACA,kBACA,QAEA,kDACC,UAIF,WACC,WACA,YAGD,eACC,WAIA,8CACC,UAKD,kDACC,UAKD,0CACC,UAKD,8CACC,8CAIF,KACC,mFAGD,OACC,gBACA,YACA,eACA,qBACA,UACC,qBAIF,2FACC,gBACA,uBAGD,2BACC,yDAGD,2BACC,6DAID,yBACC,gBACA,gBACA,WACA,mCACA,YACA,wBAEA,sKAGC,+BACA,mBAED,2CACC,YACA,eACA,YACA,8CACA,6BAEA,gEACC,cACA,mBAED,oDACC,WAEA,8EACC,yEAED,8EACC,wEAGF,oEACC,UAID,oDACC,mBACA,gCACA,WACA,WACA,YAED,0DACC,yBAGA,+FACC,gDAGD,wOAGC,sCACA,gCACA,iBAGD,yNAEC,gCACA,WAMJ,wCACC,gCACA,wCAKD,yBACC,2BACA,sBACA,mCACA,wBAEA,4CACC,uBAGD,sKAGC,+BACA,mBAED,2CACC,YACA,eACA,YACA,8CACA,6BAEA,gEACC,cACA,mBAIF,qFACC,iBAGA,iDACC,mBACA,gCACA,WACA,yDACC,UACA,WACA,iBAGF,uDACC,yBAGA,0TAIC,sCACA,gCACA,iBAGD,4FACC,gCAGD,qEACC,2CASH,oGACC,aACA,iBACA,8BACA,0GACC,cACA,SACA,YACA,YACA,WACA,aACA,mBACA,uBACA,8GACC,kBACA,kBACA,mBACA,6BACA,cACA,iBACA,WACA,YACA,YACA,eAOJ,8BACC,kBACA,aACA,sBAEA,uCACC,eACA,sBACA,oBAEA,yDACC,uCACA,4BACA,gCAGA,6DACC,eAED,uDACC,iBAED,oEACC,YACA,YAKH,mDACC,kBACA,+BACA,YACA,SACA,aACA,WACA,QACA,MAEA,4KAGC,0CACA,UAIF,iDACC,eACA,YACA,sBACA,oBACA,WACA,gBACA,eACA,8CACA,0CACA,wCACA,kBACA,UACA,QACA,QAEA,gEACC,sCACA,0BACA,WACA,YACA,WACA,WAGD,mDACC,WACA,YACA,gBAGD,uDACC,SACA,gBACA,4DACC,aACA,YAMH,kDACC,sBACA,qBACA,gBACA,OAGA,WACA,kBAED,4CACC,oCACA,kBACA,gBACA,WACA,aAED,wCACC,8CACA,WAED,0DAEC,kBACA,mBAEC,mEACC,4CACA,8CACA,sEACC,UACA,YAIH,0EACC,cACA,aACA,YACA,sBACA,2BACA,sBAED,+EACC,iBACA,iBAGD,6EACC,WACA,WACA,gBACA,qBACA,2BACA,WAED,qQAGC,kBAED,oLAEC,mBAGD,6DACC,aACA,4CAED,2EACC,mBAED,oEACC,gBACA,mBACA,uBACA,qBACA,4BACA,kBACA,4BACA,eAEA,YACA,oFACC,aACA,2FACC,gBACA,gBACA,uBAED,0FACC,gBAIH,oIACC,WAED,oEACC,iBAED,oEAIC,aACA,sBAEA,0EACC,aACA,+CACA,6BACA,aACA,cAEA,6EACC,cACA,kBACA,mCACA,QAhBS,KAiBT,aACA,sBACA,YAGA,gFACC,YACA,UACA,kBACA,mCAEA,yFACC,oBACA,+BACA,wBACA,YA/BU,KAgCV,eACA,yGACC,uBAGF,yFACC,iBACA,WAED,qFACC,aAON,2DACC,gDAIF,WACC,0BAGD,aACC,WACA,sBAMA,0BACC,eACA,iCACC,0BACA,4BACA,0BACA,0BACA,aACA,YAEA,qDACA,gDAGD,iGAGC,qBAKD,mCACC,YAEA,iDACC,6DAMJ,6CAEC,2BACA,iBACA,iBACA,YAEA,2DACC,0BACA,kBACA,iJAEC,qBAIF,sDAEC,kCACA,iBACA,iBACA,gBAEA,8DACC,kBAEA,gEACC,cACA,WACA,eACA,WAKH,sDACC,aACA,kBACA,mBACA,yBAEA,8DACC,YACA,WACA,qBAGD,4DACC,YACA,iBAEA,gEACC,kBACA,WAGD,iJAEC,gBACA,gBACA,mBACA,uBAED,qJACC,oCAIF,4MACC,WACA,YACA,WACA,eAEA,gPACC,aAED,wNACC,wCAGD,6pBAGC,UAIF,2EACC,WAEA,iFACC,2BACA,4CAGD,yFACC,wCAKF,4DACC,SACA,kBAED,0EACC,UAMH,qBACC,wBACA,WACA,YAKD,YACC,6BAMA,qBACC,WACA,aAED,wBACC,cACA,gDACA,WACA,aAED,2BACC,WACA,YACA,6BACC,WAGF,wBACC,wCACA,kBACA,mBACA,gBACA,uBACA,0CACA,kCACA,6DACC,0CAGF,sBACC,UACA,WAKF,YACC,oBAED,UACC,oBACA,kDACA,4BACA,iCACA,YACA,0BACA,cACA,QACA,kBACA,oBACC,QACA,kBACA,sBACC,WAIA,0FACC,cAIF,iCACC,SACA,iBACA,oCACC,iBACA,gBACA,kBACA,kBACA,gEACC,+EAGF,gDACC,aAIH,iBACC,aACA,wBACC,QAGF,2BAEC,kBACA,aACA,WACA,uBACA,mBACA,gBACA,cAEA,gBAEA,kGAGC,oBAGF,0BACC,UACA,WAID,qBACC,iBACA,kBAEA,4BACC,eAGF,mEACC,UAEA,kKAEC,WAOH,QACC,UACA,yCACA,sCACA,qCACA,oCACA,iCACA,oBACC,UAOD,+CACC,SACA,kBAED,mDACC,gBAKF,cACC,mBAMD,mBACC,aACA,QACA,SACA","file":"styles.css"}
{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAkBA,yQACC,SACA,UACA,SACA,oBACA,eACA,oBACA,wBACA,eACA,uDACA,qBAGD,6CACC,aAID,kGAEC,gDACA,aAGD,UACC,YAGD,6DACC,cAGD,KACC,gBAGD,MACC,yBACA,iBACA,mBAGD,cACC,gBACA,mBAGD,YACC,sBAGD,EACC,SACA,6BACA,qBACA,eACA,IACC,eAIF,WACC,aACA,0BAGD,MACC,eACA,QACC,eAIF,0BACC,eAGD,GACC,gBAGD,KACC,mBAEA,mCACA,uCACA,6BACA,6BAGD,mBACC,kBAGD,qBACC,kBACA,uBACA,qBACA,2BACA,2DACA,uBAGD,iBACC,qBACA,aACA,gCAGD,eACC,YACA,aAGD,cACC,eACA,MACA,SACA,OACA,YACA,WACA,aACA,kBACA,gDACA,gCACA,iBACA,eACA,kBACC,cACA,kBACA,UACA,QACA,gBAED,gBACC,gCACA,sDACA,4CACC,qCAOH,oBACC,WACA,YAGD,gCACC,+BAGD,0BACC,kCACA,yCACA,+BACA,4BAMD,YACC,8CACA,gCAMD,kBACC,sBAKD,4BAEC,oCACA,kBACA,gBACA,WACA,sDACC,gBAED,sEACC,gBAED,kCACC,mBAED,oHAEC,qBACA,YACA,WACA,mBACA,gcAEC,WAOH,sBACC,WASD,oCACC,kBACA,yBACA,sBACA,qBACA,iBAKD,kBACC,kBACA,UACA,SACA,YAGD,8BACC,WACA,oBACA,wBACA,wBAGD,2EACC,WAED,oGACC,0CACA,UACA,qBAGD,mDACC,6BACA,YACA,WACA,yCACA,4BACA,2BACA,WAOA,qEACC,UAED,qEACC,UAIF,wEACC,aAGD,2CACC,mBAGD,yBACC,kBACA,qBACA,iBAED,qBACC,cACA,QACA,iBACA,kBACA,aAKD,4CACC,eACA,YACA,mCACA,6BACA,qDAIA,2BACC,4BAKD,wBACC,sBACA,4BACA,+BACC,2CACA,qBACA,kBAGF,0BACC,qBACA,gBAIF,YACC,YACA,8BACA,oBACC,sBAIF,eACC,2CAUD,mBACC,kBACA,cACA,2BACC,kBACA,cAIF,UACC,gBAGD,8CACC,UAIA,oGAGC,WAIF,mBACC,WACA,kBACA,QAEA,kDACC,UAIF,WACC,WACA,YAGD,eACC,WAIA,8CACC,UAKD,kDACC,UAKD,0CACC,UAKD,8CACC,8CAIF,KACC,mFAGD,OACC,gBACA,YACA,eACA,qBACA,UACC,qBAIF,2FACC,gBACA,uBAGD,2BACC,yDAGD,2BACC,6DAID,yBACC,gBACA,gBACA,WACA,mCACA,YACA,wBAEA,sKAGC,+BACA,mBAED,2CACC,YACA,eACA,YACA,8CACA,6BAEA,gEACC,cACA,mBAED,oDACC,WAEA,8EACC,yEAED,8EACC,wEAGF,oEACC,UAID,oDACC,mBACA,gCACA,WACA,WACA,YAED,0DACC,yBAGA,+FACC,gDAGD,wOAGC,sCACA,gCACA,iBAGD,yNAEC,gCACA,WAMJ,wCACC,gCACA,wCAKD,yBACC,2BACA,sBACA,mCACA,wBAEA,4CACC,uBAGD,sKAGC,+BACA,mBAED,2CACC,YACA,eACA,YACA,8CACA,6BAEA,gEACC,cACA,mBAIF,qFACC,iBAGA,iDACC,mBACA,gCACA,WACA,yDACC,UACA,WACA,iBAGF,uDACC,yBAGA,0TAIC,sCACA,gCACA,iBAGD,4FACC,gCAGD,qEACC,2CASH,oGACC,aACA,iBACA,8BACA,0GACC,cACA,SACA,YACA,YACA,WACA,aACA,mBACA,uBACA,8GACC,kBACA,kBACA,mBACA,6BACA,cACA,iBACA,WACA,YACA,YACA,eAOJ,8BACC,kBACA,aACA,sBAEA,uCACC,eACA,sBACA,oBAEA,yDACC,uCACA,4BACA,gCAGA,6DACC,eAED,uDACC,iBAED,oEACC,YACA,YAKH,mDACC,kBACA,+BACA,YACA,SACA,aACA,WACA,QACA,MAEA,4KAGC,0CACA,UAIF,iDACC,eACA,YACA,sBACA,oBACA,WACA,gBACA,eACA,8CACA,0CACA,wCACA,kBACA,UACA,QACA,QAEA,gEACC,sCACA,0BACA,WACA,YACA,WACA,WAGD,mDACC,WACA,YACA,gBAGD,uDACC,SACA,gBACA,4DACC,aACA,YAMH,kDACC,sBACA,qBACA,gBACA,OAGA,WACA,kBAED,4CACC,oCACA,kBACA,gBACA,WACA,aAED,wCACC,8CACA,WAED,0DAEC,kBACA,mBAEC,mEACC,4CACA,8CACA,sEACC,UACA,YAIH,0EACC,cACA,aACA,YACA,sBACA,2BACA,sBAED,+EACC,iBACA,iBAGD,6EACC,WACA,WACA,gBACA,qBACA,2BACA,WAED,qQAGC,kBAED,oLAEC,mBAGD,6DACC,aACA,4CAED,2EACC,mBAED,oEACC,gBACA,mBACA,uBACA,qBACA,4BACA,kBACA,4BACA,eAEA,YACA,oFACC,aACA,2FACC,gBACA,gBACA,uBAED,0FACC,gBAIH,oIACC,WAED,oEACC,iBAED,oEAIC,aACA,sBAEA,0EACC,aACA,+CACA,6BACA,aACA,cAEA,6EACC,cACA,kBACA,mCACA,QAhBS,KAiBT,aACA,sBACA,YAGA,gFACC,YACA,UACA,kBACA,mCAEA,yFACC,oBACA,+BACA,wBACA,YA/BU,KAgCV,eACA,yGACC,uBAGF,yFACC,iBACA,WAED,qFACC,aAON,2DACC,gDAIF,WACC,0BAGD,aACC,WACA,sBAKD,YACC,6BAMA,qBACC,WACA,aAED,wBACC,cACA,gDACA,WACA,aAED,2BACC,WACA,YACA,6BACC,WAGF,wBACC,wCACA,kBACA,mBACA,gBACA,uBACA,0CACA,kCACA,6DACC,0CAGF,sBACC,UACA,WAKF,YACC,oBAED,UACC,oBACA,kDACA,4BACA,iCACA,YACA,0BACA,cACA,QACA,kBACA,oBACC,QACA,kBACA,sBACC,WAIA,0FACC,cAIF,iCACC,SACA,iBACA,oCACC,iBACA,gBACA,kBACA,kBACA,gEACC,+EAGF,gDACC,aAIH,iBACC,aACA,wBACC,QAGF,2BAEC,kBACA,aACA,WACA,uBACA,mBACA,gBACA,cAEA,gBAEA,kGAGC,oBAGF,0BACC,UACA,WAID,qBACC,iBACA,kBAEA,4BACC,eAGF,mEACC,UAEA,kKAEC,WAOH,QACC,UACA,yCACA,sCACA,qCACA,oCACA,iCACA,oBACC,UAOD,+CACC,SACA,kBAED,mDACC,gBAKF,cACC,mBAMD,mBACC,aACA,QACA,SACA","file":"styles.css"}

View file

@ -893,160 +893,6 @@ span.ui-icon {
margin: 3px 7px 30px 0;
}
/* ---- CONTACTS MENU ---- */
#contactsmenu {
.menutoggle {
cursor: pointer;
&:before {
background-size: 20px 20px;
background-repeat: no-repeat;
background-position-x: 3px;
background-position-y: 4px;
padding: 14px;
content: ' ';
// Force white
background-image: var(--original-icon-contacts-white);
filter: var(--background-image-invert-if-bright);
}
&:hover,
&:focus,
&:active {
opacity: 1 !important;
}
}
#contactsmenu-menu {
a {
padding: 2px;
&:focus-visible {
box-shadow: inset 0 0 0 2px var(--color-main-text) !important; // override rule in core/css/headers.scss #header a:focus-visible
}
}
}
}
#header .header-right > div#contactsmenu > .menu {
/* show 2.5 to 4.5 entries depending on the screen height */
height: calc(100vh - 50px * 3);
max-height: calc(50px * 6 + 2px);
min-height: calc(50px * 3.5);
width: 350px;
.emptycontent {
margin-top: 5vh !important;
margin-bottom: 2vh;
.icon-loading,
.icon-search {
display: inline-block;
}
}
.content {
/* fixed max height of the parent container without the search input */
height: calc(100vh - 50px * 3 - 50px);
max-height: calc(50px * 5);
min-height: calc(50px * 3.5 - 50px);
overflow-y: auto;
.footer {
text-align: center;
a {
display: block;
width: 100%;
padding: 12px 0;
opacity: .5;
}
}
}
.contact {
display: flex;
position: relative;
align-items: center;
padding: 3px 3px 3px 10px;
.avatar {
height: 32px;
width: 32px;
display: inline-block;
}
.body {
flex-grow: 1;
padding-left: 8px;
div {
position: relative;
width: 100%;
}
.full-name, .last-message {
/* TODO: don't use fixed width */
max-width: 204px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.last-message, .email-address {
color: var(--color-text-maxcontrast);
}
}
.top-action, .second-action, .other-actions {
width: 16px;
height: 16px;
opacity: .5;
cursor: pointer;
&:not(button) {
padding: 14px;
}
img {
filter: var(--background-invert-if-dark);
}
&:hover,
&:active,
&:focus {
opacity: 1;
}
}
button.other-actions {
width: 44px;
&:focus {
border-color: transparent;
box-shadow: 0 0 0 2px var(--color-main-text);
}
&:focus-visible {
border-radius: var(--border-radius-pill);
}
}
/* actions menu */
.menu {
top: 47px;
margin-right: 13px;
}
.popovermenu::after {
right: 2px;
}
}
}
#contactsmenu-search {
width: calc(100% - 16px);
margin: 8px;
height: 34px;
}
/* ---- TOOLTIPS ---- */
.extra-data {

View file

@ -32,7 +32,7 @@ describe('Contacts menu', function() {
* @returns {Promise}
*/
function openMenu() {
return menu._toggleVisibility(true);
return menu.loadContacts();
}
beforeEach(function(done) {

View file

@ -381,8 +381,7 @@ const ContactsMenuView = View.extend({
/**
* @param {Object} options
* @param {jQuery} options.el
* @param {jQuery} options.trigger
* @param {string} options.el
* @class ContactsMenu
* @memberOf OC
*/
@ -391,12 +390,9 @@ const ContactsMenu = function(options) {
}
ContactsMenu.prototype = {
/** @type {jQuery} */
/** @type {string} */
$el: undefined,
/** @type {jQuery} */
_$trigger: undefined,
/** @type {ContactsMenuView} */
_view: undefined,
@ -405,41 +401,19 @@ ContactsMenu.prototype = {
/**
* @param {Object} options
* @param {jQuery} options.el - the element to render the menu in
* @param {jQuery} options.trigger - the element to click on to open the menu
* @param {string} options.el - the selector of the element to render the menu in
* @returns {undefined}
*/
initialize: function(options) {
this.$el = options.el
this._$trigger = options.trigger
this.$el = $(options.el)
this._view = new ContactsMenuView({
el: this.$el
el: this.$el,
})
this._view.on('search', function(searchTerm) {
this._loadContacts(searchTerm)
this.loadContacts(searchTerm)
}, this)
OC.registerMenu(this._$trigger, this.$el, function() {
this._toggleVisibility(true)
}.bind(this), true)
this.$el.on('beforeHide', function() {
this._toggleVisibility(false)
}.bind(this))
},
/**
* @private
* @param {boolean} show
* @returns {Promise}
*/
_toggleVisibility: function(show) {
if (show) {
return this._loadContacts()
} else {
this.$el.html('')
return Promise.resolve()
}
},
/**
@ -461,7 +435,7 @@ ContactsMenu.prototype = {
* @param {string|undefined} searchTerm
* @returns {undefined}
*/
_loadContacts: function(searchTerm) {
loadContacts: function(searchTerm) {
var self = this
if (!self._contactsPromise) {

View file

@ -2,6 +2,7 @@
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Christopher Ng <chrng8@gmail.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license AGPL-3.0-or-later
@ -21,16 +22,20 @@
*
*/
import $ from 'jquery'
import OC from '../OC'
import Vue from 'vue'
import ContactsMenu from '../views/ContactsMenu.vue'
/**
* @todo move to contacts menu code https://github.com/orgs/nextcloud/projects/31#card-21213129
*/
export const setUp = () => {
// eslint-disable-next-line no-new
new OC.ContactsMenu({
el: $('#contactsmenu .menu'),
trigger: $('#contactsmenu .menutoggle'),
})
const mountPoint = document.getElementById('contactsmenu')
if (mountPoint) {
// eslint-disable-next-line no-new
new Vue({
el: mountPoint,
render: h => h(ContactsMenu),
})
}
}

View file

@ -0,0 +1,198 @@
<!--
- @copyright 2023 Christopher Ng <chrng8@gmail.com>
-
- @author Christopher Ng <chrng8@gmail.com>
-
- @license AGPL-3.0-or-later
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->
<template>
<NcHeaderMenu id="contactsmenu"
:aria-label="t('core', 'Search contacts')"
@open="handleOpen">
<template #trigger>
<Contacts :size="20" />
</template>
<div id="contactsmenu-menu" />
</NcHeaderMenu>
</template>
<script>
import NcHeaderMenu from '@nextcloud/vue/dist/Components/NcHeaderMenu.js'
import Contacts from 'vue-material-design-icons/Contacts.vue'
import OC from '../OC/index.js'
export default {
name: 'ContactsMenu',
components: {
Contacts,
NcHeaderMenu,
},
data() {
return {
contactsMenu: null,
}
},
mounted() {
// eslint-disable-next-line no-new
this.contactsMenu = new OC.ContactsMenu({
el: '#contactsmenu-menu',
})
},
methods: {
handleOpen() {
this.contactsMenu?.loadContacts()
},
},
}
</script>
<style lang="scss" scoped>
#contactsmenu-menu {
/* show 2.5 to 4.5 entries depending on the screen height */
height: calc(100vh - 50px * 3);
max-height: calc(50px * 6 + 2px);
min-height: calc(50px * 3.5);
width: 350px;
&:deep {
.emptycontent {
margin-top: 5vh !important;
margin-bottom: 1.5vh;
.icon-loading,
.icon-search {
display: inline-block;
}
}
#contactsmenu-search {
width: calc(100% - 16px);
margin: 8px;
height: 34px;
}
.content {
/* fixed max height of the parent container without the search input */
height: calc(100vh - 50px * 3 - 50px);
max-height: calc(50px * 5);
min-height: calc(50px * 3.5 - 50px);
overflow-y: auto;
.footer {
text-align: center;
a {
display: block;
width: 100%;
padding: 12px 0;
opacity: .5;
}
}
}
a {
padding: 2px;
&:focus-visible {
box-shadow: inset 0 0 0 2px var(--color-main-text) !important; // override rule in core/css/headers.scss #header a:focus-visible
}
}
.contact {
display: flex;
position: relative;
align-items: center;
padding: 3px 3px 3px 10px;
.avatar {
height: 32px;
width: 32px;
display: inline-block;
}
.body {
flex-grow: 1;
padding-left: 8px;
div {
position: relative;
width: 100%;
}
.full-name, .last-message {
/* TODO: don't use fixed width */
max-width: 204px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.last-message, .email-address {
color: var(--color-text-maxcontrast);
}
}
.top-action, .second-action, .other-actions {
width: 16px;
height: 16px;
opacity: .5;
cursor: pointer;
&:not(button) {
padding: 14px;
}
img {
filter: var(--background-invert-if-dark);
}
&:hover,
&:active,
&:focus {
opacity: 1;
}
}
button.other-actions {
width: 44px;
&:focus {
border-color: transparent;
box-shadow: 0 0 0 2px var(--color-main-text);
}
&:focus-visible {
border-radius: var(--border-radius-pill);
}
}
/* actions menu */
.menu {
top: 47px;
margin-right: 13px;
}
.popovermenu::after {
right: 2px;
}
}
}
}
</style>

View file

@ -69,14 +69,7 @@ $getUserAvatar = static function (int $size) use ($_): string {
<div class="header-right">
<div id="unified-search"></div>
<div id="notifications"></div>
<div id="contactsmenu">
<div class="menutoggle" tabindex="0" role="button"
aria-haspopup="true" aria-controls="contactsmenu-menu" aria-expanded="false">
<span class="hidden-visually"><?php p($l->t('Contacts'));?></span>
</div>
<div id="contactsmenu-menu" class="menu"
aria-label="<?php p($l->t('Contacts menu'));?>"></div>
</div>
<div id="contactsmenu"></div>
<div id="settings">
<div id="expand" tabindex="0" role="button" class="menutoggle"
aria-label="<?php p($l->t('Open settings menu'));?>"

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-login.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/core-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -31,7 +31,7 @@ class ContactsMenuContext implements Context, ActorAwareInterface {
* @return Locator
*/
public static function contactsMenuButton() {
return Locator::forThe()->xpath("//*[@id = 'header']//*[@id = 'contactsmenu']")->
return Locator::forThe()->xpath("//*[@id = 'header']//*[@id = 'contactsmenu']//*[@class = 'header-menu__trigger']")->
describedAs("Contacts menu button");
}
@ -39,8 +39,7 @@ class ContactsMenuContext implements Context, ActorAwareInterface {
* @return Locator
*/
public static function contactsMenu() {
return Locator::forThe()->css(".menu")->
descendantOf(self::contactsMenuButton())->
return Locator::forThe()->xpath("//*[@id = 'header']//*[@id = 'contactsmenu']//*[@id = 'contactsmenu-menu']")->
describedAs("Contacts menu");
}