mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Refactor design of autocomplete dropdown (#2119)
Refactor design of Autocomplete Menu dropdown
This commit is contained in:
parent
50edb83c8e
commit
43c3e22cc9
8 changed files with 296 additions and 89 deletions
|
|
@ -786,6 +786,10 @@
|
|||
"message": "Use monochrome toolbar icon",
|
||||
"description": "Use monochrome toolbar icon checkbox text."
|
||||
},
|
||||
"optionsCheckboxUseCompactMode": {
|
||||
"message": "Use compact mode",
|
||||
"description": "Whether the compact mode (legacy dropdown) should be enabled."
|
||||
},
|
||||
"optionsSaveDomainOnly": {
|
||||
"message": "Save domain only",
|
||||
"description": "Save domain only checkbox text."
|
||||
|
|
@ -915,7 +919,7 @@
|
|||
"description": "Autocomplete Usernames option help text."
|
||||
},
|
||||
"optionsShowGroupNameInAutocompleteHelpText": {
|
||||
"message": "When credentials are returned from different groups, the group name will be shown.",
|
||||
"message": "When credentials are returned from different groups, the group name will be shown. Only affects the compact mode.",
|
||||
"description": "Show group name in autocomplete help text."
|
||||
},
|
||||
"optionsShowNotificationsHelpText": {
|
||||
|
|
@ -926,6 +930,10 @@
|
|||
"message": "Use a monochrome icon with colored badges in toolbar that adapts to light/dark browser theme.",
|
||||
"description": "Use monochrome toolbar icon option help text."
|
||||
},
|
||||
"optionsUseCompactModeHelpText": {
|
||||
"message": "Use a compact version of the dropdown list to select credentials.",
|
||||
"description": "Use compact (legacy) mode help text."
|
||||
},
|
||||
"optionsSaveDomainOnlyHelpText": {
|
||||
"message": "When saving new credentials, save only the domain instead of full URL.",
|
||||
"description": "Save domain only option help text."
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ const defaultSettings = {
|
|||
afterFillSorting: SORT_BY_MATCHING_CREDENTIALS_SETTING,
|
||||
afterFillSortingTotp: SORT_BY_RELEVANT_ENTRY,
|
||||
autoCompleteUsernames: true,
|
||||
showGroupNameInAutocomplete: true,
|
||||
autoFillAndSend: false,
|
||||
autoFillSingleEntry: false,
|
||||
autoFillSingleTotp: false,
|
||||
|
|
@ -24,12 +23,14 @@ const defaultSettings = {
|
|||
redirectAllowance: 1,
|
||||
saveDomainOnly: true,
|
||||
showGettingStartedGuideAlert: true,
|
||||
showTroubleshootingGuideAlert: true,
|
||||
showGroupNameInAutocomplete: true,
|
||||
showLoginFormIcon: true,
|
||||
showLoginNotifications: true,
|
||||
showNotifications: true,
|
||||
useMonochromeToolbarIcon: false,
|
||||
showOTPIcon: true,
|
||||
showTroubleshootingGuideAlert: true,
|
||||
useCompactMode: false,
|
||||
useMonochromeToolbarIcon: false,
|
||||
useObserver: true,
|
||||
usePredefinedSites: true,
|
||||
usePasswordGeneratorIcons: false,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ class Autocomplete {
|
|||
this.elements = [];
|
||||
this.index = -1;
|
||||
this.input = undefined;
|
||||
this.showGroup = true;
|
||||
this.useCompactMode = false;
|
||||
this.wrapper = undefined;
|
||||
this.shadowRoot = undefined;
|
||||
this.container = undefined;
|
||||
|
|
@ -25,25 +27,28 @@ class Autocomplete {
|
|||
this.elements = [];
|
||||
}
|
||||
|
||||
async click(e) {
|
||||
async click(e) {}
|
||||
|
||||
}
|
||||
async itemClick(e, input, uuid) {}
|
||||
|
||||
async itemClick(e, input, uuid) {
|
||||
async itemEnter(index, item) {}
|
||||
|
||||
}
|
||||
|
||||
async itemEnter(index, item) {
|
||||
|
||||
}
|
||||
|
||||
async create(input, showListInstantly = false, autoSubmit = false, afterFillSort = SORT_BY_MATCHING_CREDENTIALS_SETTING) {
|
||||
async create(
|
||||
input,
|
||||
showListInstantly = false,
|
||||
autoSubmit = false,
|
||||
useCompactMode = false,
|
||||
showGroup = true,
|
||||
afterFillSort = SORT_BY_MATCHING_CREDENTIALS_SETTING,
|
||||
) {
|
||||
if (input.readOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.autoSubmit = autoSubmit;
|
||||
this.afterFillSort = afterFillSort;
|
||||
this.showGroup = showGroup;
|
||||
this.useCompactMode = useCompactMode;
|
||||
|
||||
if (!this.autocompleteList.includes(input)) {
|
||||
input.addEventListener('click', e => this.click(e));
|
||||
|
|
@ -63,9 +68,12 @@ class Autocomplete {
|
|||
return;
|
||||
}
|
||||
this.deselectItem();
|
||||
e.target.classList.add('kpxcAutocomplete-active');
|
||||
|
||||
const itemContainer = e.target.classList.contains('kpxcAutocomplete-item') ? e.target : e.target.parentElement;
|
||||
itemContainer.classList.add('kpxcAutocomplete-item--active');
|
||||
|
||||
const items = this.getAllItems();
|
||||
this.index = Array.from(items).indexOf(e.target);
|
||||
this.index = Array.from(items).indexOf(itemContainer);
|
||||
}
|
||||
|
||||
async showList(inputField) {
|
||||
|
|
@ -89,14 +97,21 @@ class Autocomplete {
|
|||
this.wrapper = kpxcUI.createElement('div');
|
||||
this.wrapper.style.all = 'unset';
|
||||
this.wrapper.style.display = 'none';
|
||||
styleSheet.addEventListener('load', () => this.wrapper.style.display = 'block');
|
||||
this.container = kpxcUI.createElement('div', 'kpxcAutocomplete-container', { 'id': 'kpxcAutocomplete-container' });
|
||||
styleSheet.addEventListener('load', () => (this.wrapper.style.display = 'block'));
|
||||
this.container = kpxcUI.createElement('div', 'kpxcAutocomplete-container', {
|
||||
id: 'kpxcAutocomplete-container',
|
||||
});
|
||||
|
||||
// Apply compact mode class
|
||||
if (kpxc.settings.useCompactMode) {
|
||||
this.container.classList.add('kpxcAutocomplete-container--compact');
|
||||
}
|
||||
|
||||
this.shadowRoot = this.wrapper.attachShadow({ mode: 'closed' });
|
||||
this.shadowRoot.append(colorStyleSheet);
|
||||
this.shadowRoot.append(styleSheet);
|
||||
|
||||
this.list = kpxcUI.createElement('div', 'kpxcAutocomplete-items', { 'id': 'kpxcAutocomplete-list' });
|
||||
this.list = kpxcUI.createElement('div', 'kpxcAutocomplete-items', { id: 'kpxcAutocomplete-list' });
|
||||
initColorTheme(this.container);
|
||||
|
||||
this.container.append(this.list);
|
||||
|
|
@ -111,7 +126,7 @@ class Autocomplete {
|
|||
}
|
||||
|
||||
this.updateList();
|
||||
this.container.style.display = 'block';
|
||||
this.container.classList.add('kpxcAutocomplete-container--visible');
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
|
|
@ -131,45 +146,85 @@ class Autocomplete {
|
|||
|
||||
// Update credentials to menu div
|
||||
for (const c of this.elements) {
|
||||
const item = document.createElement('div');
|
||||
item.textContent = c.label;
|
||||
item.setAttribute('uuid', c.uuid);
|
||||
const item = this.generateItem(c);
|
||||
|
||||
const itemInput = kpxcUI.createElement('input', '', { 'type': 'hidden', 'value': c.value });
|
||||
const itemInput = kpxcUI.createElement('input', '', { type: 'hidden', value: c.value });
|
||||
item.append(itemInput);
|
||||
item.addEventListener('click', e => this.itemClick(e, this.input, c.uuid));
|
||||
|
||||
// These events prevent the double hover effect if both keyboard and mouse are used
|
||||
item.addEventListener('mousemove', e => this.mouseMove(e));
|
||||
|
||||
item.addEventListener('mousedown', e => e.stopPropagation());
|
||||
item.addEventListener('mouseup', e => e.stopPropagation());
|
||||
|
||||
// If this page has an associated uuid and it matches this credential, then put it on top of the list
|
||||
if (username === c.value
|
||||
|| (this.afterFillSort === SORT_BY_RELEVANT_ENTRY && c.uuid === pageUuid)) {
|
||||
if (username === c.value || (this.afterFillSort === SORT_BY_RELEVANT_ENTRY && c.uuid === pageUuid)) {
|
||||
this.list.prepend(item);
|
||||
} else {
|
||||
this.list.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Use only two columns if groups are not shown
|
||||
if (this.useCompactMode && !this.showGroup) {
|
||||
this.list.style.gridTemplateColumns = 'repeat(2, auto)';
|
||||
}
|
||||
|
||||
this.selectItem();
|
||||
}
|
||||
|
||||
// Add parentElement parameter
|
||||
generateItem(credential) {
|
||||
// Create the DOM element for the list item wrapper.
|
||||
const item = kpxcUI.createElement('div', 'kpxcAutocomplete-item', {
|
||||
title: `${credential.title} | ${credential.value} | ${credential.group}`
|
||||
});
|
||||
item.setAttribute('uuid', credential.uuid);
|
||||
|
||||
// Create the DOM element for the list item header.
|
||||
const itemHeader = document.createElement('div');
|
||||
itemHeader.classList.add('kpxcAutocomplete-item__header');
|
||||
|
||||
// Create the DOM element for showing the group only when the group name is available.
|
||||
if (!this.useCompactMode) {
|
||||
const itemGroup = kpxcUI.createElement('div', 'kpxcAutocomplete-item__group', {}, credential.group);
|
||||
itemHeader.append(itemGroup);
|
||||
}
|
||||
|
||||
// Create the DOM element for showing the credentials name.
|
||||
const itemLabel = kpxcUI.createElement('div', 'kpxcAutocomplete-item__label', {}, credential.title);
|
||||
itemHeader.append(itemLabel);
|
||||
|
||||
// Append the header to the list item.
|
||||
item.append(itemHeader);
|
||||
|
||||
// Create the DOM element for showing the credentials username field.
|
||||
const itemClassName = this.showGroup ? 'kpxcAutocomplete-item__value' : 'kpxcAutocomplete-item__value--last';
|
||||
const itemValue = kpxcUI.createElement('div', itemClassName, {}, credential.value);
|
||||
item.append(itemValue);
|
||||
|
||||
// If compact mode is enabled, append group to the end
|
||||
if (this.useCompactMode && this.showGroup) {
|
||||
const itemGroup = kpxcUI.createElement('div', 'kpxcAutocomplete-item__group', {}, credential.group);
|
||||
item.append(itemGroup);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
selectItem() {
|
||||
this.deselectItem();
|
||||
const items = this.getAllItems();
|
||||
const item = items[this.index];
|
||||
if (item !== undefined) {
|
||||
item.classList.add('kpxcAutocomplete-active');
|
||||
item.classList.add('kpxcAutocomplete-item--active');
|
||||
item.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
deselectItem() {
|
||||
const items = this.list.querySelectorAll('div.kpxcAutocomplete-active');
|
||||
items.forEach(item => item.classList.remove('kpxcAutocomplete-active'));
|
||||
const items = this.list.querySelectorAll('div.kpxcAutocomplete-item--active');
|
||||
items.forEach(item => item.classList.remove('kpxcAutocomplete-item--active'));
|
||||
}
|
||||
|
||||
closeList() {
|
||||
|
|
@ -178,11 +233,11 @@ class Autocomplete {
|
|||
return;
|
||||
}
|
||||
|
||||
this.container.style.display = 'none';
|
||||
this.container.classList.remove('kpxcAutocomplete-container--visible');
|
||||
}
|
||||
|
||||
getAllItems() {
|
||||
return this.list.getElementsByTagName('div');
|
||||
return this.list.getElementsByClassName('kpxcAutocomplete-item');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -191,7 +246,7 @@ class Autocomplete {
|
|||
* - ArrowUp selects item above, or the last item (first is active)
|
||||
* - Enter or Tab selects the item
|
||||
* - Backspace and Delete shows the list if input field is empty. First item is activated
|
||||
*/
|
||||
*/
|
||||
async keyDown(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
|
|
@ -211,7 +266,7 @@ class Autocomplete {
|
|||
} else {
|
||||
// Activate next item
|
||||
const items = this.getAllItems();
|
||||
this.index = (this.index+1) % items.length;
|
||||
this.index = (this.index + 1) % items.length;
|
||||
this.selectItem();
|
||||
}
|
||||
} else if (e.key === 'ArrowUp' && this.list) {
|
||||
|
|
@ -274,23 +329,18 @@ class Autocomplete {
|
|||
return;
|
||||
}
|
||||
|
||||
const rect = this.input.getBoundingClientRect();
|
||||
this.container.style.minWidth = Pixels(this.input.offsetWidth);
|
||||
|
||||
// Calculate Y offset if menu does not fit to the bottom of the screen -> show it at the top of the input field
|
||||
const rect = this.input.getBoundingClientRect();
|
||||
const menuRect = this.container.getBoundingClientRect();
|
||||
const totalHeight = menuRect.height + rect.height;
|
||||
const menuOffset = (totalHeight + rect.y) > window.self.visualViewport.height ? totalHeight : 0;
|
||||
if (menuOffset > 0) {
|
||||
this.container.classList.add('kpxcAutocomplete-container-on-top');
|
||||
} else {
|
||||
this.container.classList.remove('kpxcAutocomplete-container-on-top');
|
||||
}
|
||||
const menuOffset = totalHeight + rect.y > window.self.visualViewport.height ? totalHeight : 0;
|
||||
|
||||
const scrollTop = kpxcUI.getScrollTop();
|
||||
const scrollLeft = kpxcUI.getScrollLeft();
|
||||
if (kpxcUI.bodyStyle.position.toLowerCase() === 'relative') {
|
||||
this.container.style.top = Pixels(rect.top - kpxcUI.bodyRect.top + scrollTop + this.input.offsetHeight - menuOffset);
|
||||
this.container.style.top = Pixels(
|
||||
rect.top - kpxcUI.bodyRect.top + scrollTop + this.input.offsetHeight - menuOffset,
|
||||
);
|
||||
this.container.style.left = Pixels(rect.left - kpxcUI.bodyRect.left + scrollLeft);
|
||||
} else {
|
||||
this.container.style.top = Pixels(rect.top + scrollTop + this.input.offsetHeight - menuOffset);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ CredentialAutocomplete.prototype.itemClick = async function(e, input, uuid) {
|
|||
e.stopPropagation();
|
||||
|
||||
const index = Array.prototype.indexOf.call(e.currentTarget.parentElement.childNodes, e.currentTarget);
|
||||
const usernameValue = e.target.getElementsByTagName('input')[0].value;
|
||||
const usernameValue = e.currentTarget.getElementsByTagName('input')[0]?.value;
|
||||
await this.fillPassword(usernameValue, index, uuid);
|
||||
|
||||
this.closeList();
|
||||
|
|
|
|||
|
|
@ -269,14 +269,35 @@ kpxc.initAutocomplete = function() {
|
|||
|
||||
for (const c of kpxc.combinations) {
|
||||
if (c.username) {
|
||||
kpxcUserAutocomplete.create(c.username, false, kpxc.settings.autoSubmit, kpxc.settings.afterFillSorting);
|
||||
kpxcUserAutocomplete.create(
|
||||
c.username,
|
||||
false,
|
||||
kpxc.settings.autoSubmit,
|
||||
kpxc.settings.useCompactMode,
|
||||
kpxc.settings.showGroupNameInAutocomplete,
|
||||
kpxc.settings.afterFillSorting,
|
||||
);
|
||||
} else if (!c.username && c.password) {
|
||||
// Single password field
|
||||
kpxcUserAutocomplete.create(c.password, false, kpxc.settings.autoSubmit, kpxc.settings.afterFillSorting);
|
||||
kpxcUserAutocomplete.create(
|
||||
c.password,
|
||||
false,
|
||||
kpxc.settings.autoSubmit,
|
||||
kpxc.settings.useCompactMode,
|
||||
kpxc.settings.showGroupNameInAutocomplete,
|
||||
kpxc.settings.afterFillSorting,
|
||||
);
|
||||
}
|
||||
|
||||
if (c.totp) {
|
||||
kpxcTOTPAutocomplete.create(c.totp, false, kpxc.settings.autoSubmit, kpxc.settings.afterFillSortingTotp);
|
||||
kpxcTOTPAutocomplete.create(
|
||||
c.totp,
|
||||
false,
|
||||
kpxc.settings.autoSubmit,
|
||||
kpxc.settings.useCompactMode,
|
||||
kpxc.settings.showGroupNameInAutocomplete,
|
||||
kpxc.settings.afterFillSortingTotp,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -382,7 +403,7 @@ kpxc.initLoginPopup = function() {
|
|||
|
||||
return {
|
||||
title: title,
|
||||
group: group,
|
||||
group: credential.group,
|
||||
visibleLogin: visibleLogin,
|
||||
login: credential.login,
|
||||
loginId: loginId,
|
||||
|
|
@ -440,6 +461,8 @@ kpxc.initLoginPopup = function() {
|
|||
popupLoginItems.push({ text: l.text, uuid: l.uuid });
|
||||
|
||||
kpxcUserAutocomplete.elements.push({
|
||||
group: l.group,
|
||||
title: l.title,
|
||||
label: l.text,
|
||||
value: l.login,
|
||||
uuid: l.uuid,
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
.kpxcAutocomplete-container {
|
||||
display: none;
|
||||
z-index: 2147483646;
|
||||
position: absolute !important;
|
||||
background-color: var(--kpxc-background-color);
|
||||
border: var(--kpxc-autocomplete-menu-border);
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
overflow: hidden; /* this fixes an issue with the border radius not showing up clearly */
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--kpxc-box-shadow);
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1em;
|
||||
min-width: 16rem;
|
||||
overflow: hidden; /* Prevent the content from bleeding over the border radius. */
|
||||
padding: 6px;
|
||||
position: absolute !important;
|
||||
z-index: 2147483646;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-container-on-top {
|
||||
border-bottom-right-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
.kpxcAutocomplete-container--visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-items {
|
||||
|
|
@ -20,37 +24,138 @@
|
|||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-items div {
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
.kpxcAutocomplete-item {
|
||||
background-color: var(--kpxc-background-color);
|
||||
border-bottom: 1px solid rgba(0,0,0,.125);
|
||||
width: auto;
|
||||
border-radius: 4px;
|
||||
color: var(--kpxc-text-color);
|
||||
font-size: max(10px, .9em) !important;
|
||||
cursor: pointer;
|
||||
letter-spacing: normal !important;
|
||||
word-spacing: normal !important;
|
||||
text-rendering: auto !important;
|
||||
padding: 12px;
|
||||
text-align: start !important;
|
||||
text-rendering: auto !important;
|
||||
width: auto;
|
||||
word-spacing: normal !important;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-items div:last-child {
|
||||
.kpxcAutocomplete-container--compact .kpxcAutocomplete-items {
|
||||
cursor: pointer;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, auto);
|
||||
max-height: 250px;
|
||||
max-width: 600px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-container--compact .kpxcAutocomplete-item {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-container--compact .kpxcAutocomplete-item__header {
|
||||
align-self: flex-start;
|
||||
border-radius: 4px 0 0 4px;
|
||||
display: grid;
|
||||
height: 14px;
|
||||
padding: 6px 24px 6px 6px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-container--compact .kpxcAutocomplete-item__group {
|
||||
align-self: center;
|
||||
border-radius: 0 4px 4px 0;
|
||||
display: flex;
|
||||
height: 14px;
|
||||
justify-content: flex-end;
|
||||
padding: 6px 6px 6px 24px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-container--compact .kpxcAutocomplete-item__value {
|
||||
align-self: flex-end;
|
||||
height: 14px;
|
||||
justify-self: start;
|
||||
margin: 0;
|
||||
padding: 6px 0 6px 0;
|
||||
pointer-events: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Separate class for value if group is not shown */
|
||||
.kpxcAutocomplete-container--compact .kpxcAutocomplete-item__value--last {
|
||||
align-self: center;
|
||||
border-radius: 0 4px 4px 0;
|
||||
color: var(--kpxc-lighter-text-color);
|
||||
display: flex;
|
||||
font-size: .85em;
|
||||
height: 14px;
|
||||
justify-content: flex-end;
|
||||
overflow: hidden;
|
||||
padding: 6px 6px 6px 24px;
|
||||
pointer-events: auto;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-active {
|
||||
.kpxcAutocomplete-item__header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
gap: 6px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-item__group {
|
||||
color: var(--kpxc-light-text-color);
|
||||
font-size: .7em;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.05em;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-item__label {
|
||||
font-size: .9em;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-item__value {
|
||||
color: var(--kpxc-lighter-text-color);
|
||||
font-size: .85em;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-item--active {
|
||||
background-color: #28a745 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-item--active * {
|
||||
background-color: #28a745 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-container footer {
|
||||
padding: 5px;
|
||||
background-color: #ddd;
|
||||
border-top: 1px solid rgba(0,0,0,.125) !important;
|
||||
width: auto;
|
||||
color: #000;
|
||||
background-color: var(--kpxc-autocomplete-footer-color);
|
||||
border-radius: 0 0 4px 4px;
|
||||
color: var(--kpxc-lighter-text-color);
|
||||
font-size: max(9px, .8em) !important;
|
||||
font-style: italic;
|
||||
padding: 5px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
|
@ -62,11 +167,4 @@
|
|||
background: var(--kpxc-background-color);
|
||||
color: var(--kpxc-text-color);
|
||||
}
|
||||
|
||||
.kpxc-pwgen-input {
|
||||
background-color: var(--kpxc-input-background-color) !important;
|
||||
border: var(--kpxc-input-border);
|
||||
border-color: var(--kpxc-input-border-color);
|
||||
color: var(--kpxc-text-color) !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
:root, :host {
|
||||
:root,
|
||||
:host {
|
||||
--kpxc-autocomplete-footer-color: #f2f2f2;
|
||||
--kpxc-autocomplete-menu-border: 1px solid #ddd;
|
||||
--kpxc-background-color: #fff;
|
||||
--kpxc-box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--kpxc-card-background-color: #fff;
|
||||
--kpxc-card-border-color: rgba(0,0,0,.125);
|
||||
--kpxc-card-border-color: rgba(0, 0, 0, .125);
|
||||
--kpxc-card-header-color: #fafafa;
|
||||
--kpxc-checkbox-color: #5b5b5d;
|
||||
--kpxc-checkbox-background-color: #2b2a2a;
|
||||
|
|
@ -11,7 +14,7 @@
|
|||
--kpxc-input-background-color: #fff;
|
||||
--kpxc-input-border: 1px solid #2b4d1a;
|
||||
--kpxc-input-border-color: #292a2a;
|
||||
--kpxc-input-main-border-color: rgba(0,0,0,.125);
|
||||
--kpxc-input-main-border-color: rgba(0, 0, 0, .125);
|
||||
--kpxc-link-color: #3a8233;
|
||||
--kpxc-link-hover-color: #429f14;
|
||||
--kpxc-sidebar-background-color: #27272a;
|
||||
|
|
@ -19,10 +22,15 @@
|
|||
--kpxc-table-hover-color: #f2f2f2;
|
||||
--kpxc-table-odd-color: #f2f2f2;
|
||||
--kpxc-text-color: #000;
|
||||
--kpxc-lighter-text-color: rgba(0, 0, 0, 0.8);
|
||||
--kpxc-light-text-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root, :host {
|
||||
|
||||
:root,
|
||||
:host {
|
||||
--kpxc-autocomplete-footer-color: #2b2a2a;
|
||||
--kpxc-autocomplete-menu-border: 1px solid #3b3b3d;
|
||||
--kpxc-background-color: #3b3b3d;
|
||||
--kpxc-card-background-color: #2b2a2a;
|
||||
|
|
@ -42,11 +50,14 @@
|
|||
--kpxc-table-hover-color: #474948;
|
||||
--kpxc-table-odd-color: #383a39;
|
||||
--kpxc-text-color: #cbcfcb;
|
||||
--kpxc-lighter-text-color: rgba(203, 207, 203, 0.8);
|
||||
--kpxc-light-text-color: rgba(203, 207, 203, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Same colors for manual switching */
|
||||
[data-bs-theme='dark'] {
|
||||
--kpxc-autocomplete-footer-color: #2b2a2a;
|
||||
--kpxc-autocomplete-menu-border: 1px solid #3b3b3d;
|
||||
--kpxc-background-color: #3b3b3d;
|
||||
--kpxc-card-background-color: #2b2a2a;
|
||||
|
|
@ -66,13 +77,17 @@
|
|||
--kpxc-table-hover-color: #474948;
|
||||
--kpxc-table-odd-color: #383a39;
|
||||
--kpxc-text-color: #cbcfcb;
|
||||
--kpxc-lighter-text-color: rgba(203, 207, 203, 0.8);
|
||||
--kpxc-light-text-color: rgba(203, 207, 203, 0.5);
|
||||
}
|
||||
|
||||
[data-bs-theme='light'] {
|
||||
--kpxc-autocomplete-footer-color: #f2f2f2;
|
||||
--kpxc-autocomplete-menu-border: 1px solid #ddd;
|
||||
--kpxc-background-color: #fff;
|
||||
--kpxc-box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--kpxc-card-background-color: #fff;
|
||||
--kpxc-card-border-color: rgba(0,0,0,.125);
|
||||
--kpxc-card-border-color: rgba(0, 0, 0, .125);
|
||||
--kpxc-card-header-color: #fafafa;
|
||||
--kpxc-checkbox-color: #5b5b5d;
|
||||
--kpxc-checkbox-background-color: #5b5b5d;
|
||||
|
|
@ -81,11 +96,14 @@
|
|||
--kpxc-input-background-color: #fff;
|
||||
--kpxc-input-border: 1px solid #2b4d1a;
|
||||
--kpxc-input-border-color: #292a2a;
|
||||
--kpxc-input-main-border-color: rgba(0,0,0,.125);
|
||||
--kpxc-input-main-border-color: rgba(0, 0, 0, .125);
|
||||
--kpxc-link-color: #3a8233;
|
||||
--kpxc-link-hover-color: #429f14;
|
||||
--kpxc-table-border-color: rgb(222, 226, 230);
|
||||
--kpxc-table-hover-color: #f2f2f2;
|
||||
--kpxc-table-odd-color: #f2f2f2;
|
||||
--kpxc-text-color: #000;
|
||||
--kpxc-lighter-text-color: rgba(0, 0, 0, 0.8);
|
||||
--kpxc-light-text-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,15 @@
|
|||
<div class="form-text" data-i18n="optionsUseMonochromeToolbarIconHelpText"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Use compact mode -->
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="useCompactMode" id="useCompactModeCheckBox" value="true">
|
||||
<label class="form-check-label" for="useCompactModeCheckBox" data-i18n="optionsCheckboxUseCompactMode"></label>
|
||||
<div class="form-text" data-i18n="optionsUseCompactModeHelpText"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue