Merge pull request #2687 from keepassxreboot/fix/overlay_elements

Use popovers for icons and Autocomplete Menu in DOM
This commit is contained in:
Sami Vänttinen 2025-09-14 11:50:16 +03:00 committed by GitHub
commit e842e01cf3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 39 additions and 26 deletions

View file

@ -100,7 +100,9 @@ class Autocomplete {
styleSheet.addEventListener('load', () => (this.wrapper.style.display = 'block'));
this.container = kpxcUI.createElement('div', 'kpxcAutocomplete-container', {
id: 'kpxcAutocomplete-container',
popover: 'manual',
});
this.container.style.margin = 0;
// Apply compact mode class
if (kpxc.settings.useCompactMode) {
@ -128,6 +130,7 @@ class Autocomplete {
this.updateList();
this.container.classList.add('kpxcAutocomplete-container--visible');
this.container.showPopover({ source: inputField });
this.updatePosition();
}
@ -235,6 +238,7 @@ class Autocomplete {
}
this.container.classList.remove('kpxcAutocomplete-container--visible');
this.container.hidePopover();
}
getAllItems() {
@ -338,7 +342,7 @@ class Autocomplete {
// Get body zoom radio
const zoom = kpxcUI.bodyStyle.zoom || 1;
// Calculate Y offset if menu does not fit to the bottom of the screen -> show it at the top of the input field
const menuRect = this.container.getBoundingClientRect();
const totalHeight = menuRect.height + rect.height;

View file

@ -433,7 +433,7 @@ kpxcFields.isSearchField = function(target) {
// :popover-open selector is supported only with Firefox >= 125 and Chrome >= 114
kpxcFields.discoverOverlays = function() {
try {
try {
kpxcFields.overlays = document.querySelectorAll(':popover-open, [popover]');
} catch (e) {
// Ignore SyntaxError (e.g., unsupported selector)

View file

@ -50,18 +50,17 @@ PasswordIcon.prototype.initField = function(field) {
PasswordIcon.prototype.createIcon = function(field) {
const className = (isFirefox() ? 'key-moz' : 'key');
const size = (field.offsetHeight > 28) ? 24 : 16;
const offset = kpxcUI.calculateIconOffset(field, size);
const size = this.calculateIconSize(field);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-icon ' + className,
{
'title': tr('passwordGeneratorGenerateText'),
'size': size,
'offset': offset,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id') // Needed?
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
'popover': 'manual'
});
icon.style.zIndex = '10000000';
icon.style.margin = 0;
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -75,7 +74,7 @@ PasswordIcon.prototype.createIcon = function(field) {
}
if (e.shiftKey) {
icon.style.display = 'none';
icon.hidePopover();
return;
}
@ -89,6 +88,7 @@ PasswordIcon.prototype.createIcon = function(field) {
kpxcUI.setIconPosition(icon, field, this.rtl);
this.icon = icon;
this.createWrapper('css/pwgen.css');
icon.showPopover();
};

View file

@ -140,18 +140,15 @@ TOTPFieldIcon.prototype.initField = async function(field, segmented) {
TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
const className = (isFirefox() ? 'moz' : 'default');
// Size the icon dynamically, but not greater than 24 or smaller than 14
const size = Math.max(Math.min(24, field.offsetHeight - 4), 14);
const offset = kpxcUI.calculateIconOffset(field, size);
const size = this.calculateIconSize(field);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-totp-icon ' + className,
{
'title': tr('totpFieldText'),
'size': size,
'offset': offset
'popover': 'manual'
});
icon.style.zIndex = '10000000';
icon.style.margin = 0;
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -167,7 +164,7 @@ TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
}
if (e.shiftKey) {
icon.style.display = 'none';
icon.hidePopover();
return;
}
@ -182,4 +179,5 @@ TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) {
kpxcUI.setIconPosition(icon, field, this.rtl, segmented);
this.icon = icon;
this.createWrapper('css/totp.css');
icon.showPopover();
};

View file

@ -7,6 +7,9 @@ const MIN_INPUT_FIELD_OFFSET_WIDTH = 60;
const MIN_OPACITY = 0.7;
const MAX_OPACITY = 1;
const MIN_ICON_SIZE = 14;
const MAX_ICON_SIZE = 24;
const BLUE_BUTTON = 'kpxc-button kpxc-blue-button';
const GREEN_BUTTON = 'kpxc-button kpxc-green-button';
const ORANGE_BUTTON = 'kpxc-button kpxc-orange-button';
@ -52,6 +55,11 @@ class Icon {
}
}
// Size the icon dynamically, but not greater than 24 or smaller than 14
calculateIconSize(field) {
return Math.max(Math.min(MAX_ICON_SIZE, field.offsetHeight - 4), MIN_ICON_SIZE);
}
// Creates a wrapper div that has the icon in Shadow DOM
createWrapper(styleSheetFilename) {
const styleSheet = createStylesheet(styleSheetFilename);

View file

@ -68,9 +68,7 @@ UsernameFieldIcon.prototype.initField = function(field) {
UsernameFieldIcon.prototype.createIcon = function(field) {
const className = getIconClassName(this.databaseState);
// Size the icon dynamically, but not greater than 24 or smaller than 14
const size = Math.max(Math.min(24, field.offsetHeight - 4), 14);
const size = this.calculateIconSize(field);
// Don't create the icon if the input field is too small
if (field.offsetWidth < (size * 1.5) || field.offsetHeight < size) {
@ -78,16 +76,14 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
return;
}
const offset = kpxcUI.calculateIconOffset(field, size);
const icon = kpxcUI.createElement('div', 'kpxc kpxc-username-icon ' + className,
{
'title': getIconText(this.databaseState),
'size': size,
'offset': offset,
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id'),
'popover': 'manual'
});
icon.style.zIndex = '10000000';
icon.style.margin = 0;
icon.style.width = Pixels(size);
icon.style.height = Pixels(size);
@ -97,7 +93,7 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
}
if (e.shiftKey) {
icon.style.display = 'none';
icon.hidePopover();
return;
}
@ -111,6 +107,7 @@ UsernameFieldIcon.prototype.createIcon = function(field) {
kpxcUI.setIconPosition(icon, field, this.rtl);
this.icon = icon;
this.createWrapper('css/username.css');
icon.showPopover();
};
const iconClicked = async function(field, icon) {

View file

@ -1,5 +1,7 @@
.kpxc-pwgen-icon {
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}

View file

@ -1,6 +1,8 @@
.kpxc-totp-icon {
position: absolute;
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}
.kpxc-totp-icon.default {

View file

@ -1,6 +1,8 @@
.kpxc-username-icon {
position: absolute;
border: 0;
cursor: pointer;
padding: 0 !important;
position: absolute;
}
.kpxc-username-icon.disconnected {