Našeptávače
parent
6a39a66e73
commit
302edd094b
|
|
@ -124,6 +124,49 @@ class FakturaFacade
|
|||
", $fakturaId)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Názvy položek pro našeptávač. Jednotka a cena jsou z posledního použití,
|
||||
* pořadí je podle počtu výskytů na fakturách za poslední rok.
|
||||
*
|
||||
* @return list<array{nazev: string, jednotka: string, cena: float}>
|
||||
*/
|
||||
public function getKatalogPolozek(): array
|
||||
{
|
||||
$od = (new \DateTimeImmutable('today'))->modify('-1 year')->format('Y-m-d');
|
||||
$rows = $this->kufrikExplorer->query("
|
||||
SELECT katalog.NAZEV, katalog.JEDNOTKA, katalog.CENA_JEDNOTKA
|
||||
FROM (
|
||||
SELECT
|
||||
P.NAZEV,
|
||||
P.JEDNOTKA,
|
||||
P.CENA_JEDNOTKA,
|
||||
ROW_NUMBER() OVER (PARTITION BY P.NAZEV ORDER BY P.ID DESC) AS rn
|
||||
FROM dbo.POLOZKA P
|
||||
WHERE P.NAZEV IS NOT NULL AND LTRIM(RTRIM(P.NAZEV)) <> ''
|
||||
) AS katalog
|
||||
LEFT JOIN (
|
||||
SELECT P.NAZEV, COUNT(*) AS POCET
|
||||
FROM dbo.POLOZKA P
|
||||
INNER JOIN dbo.FAKTURA F ON F.ID = P.FAKTURA
|
||||
WHERE F.DATUM_VYSTAVENI >= ?
|
||||
GROUP BY P.NAZEV
|
||||
) AS pouziti ON pouziti.NAZEV = katalog.NAZEV
|
||||
WHERE katalog.rn = 1
|
||||
ORDER BY ISNULL(pouziti.POCET, 0) DESC, katalog.NAZEV ASC
|
||||
", $od)->fetchAll();
|
||||
|
||||
$katalog = [];
|
||||
foreach ($rows as $row) {
|
||||
$katalog[] = [
|
||||
'nazev' => (string) $row->NAZEV,
|
||||
'jednotka' => trim((string) ($row->JEDNOTKA ?? '')),
|
||||
'cena' => round((float) $row->CENA_JEDNOTKA, 2),
|
||||
];
|
||||
}
|
||||
|
||||
return $katalog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maska, období číslování a výchozí splatnost uložené u firmy.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</script>
|
||||
|
||||
{* naše vlastní styly *}
|
||||
<link href="{$basePath}/css/layout.css?v={$appVersion ?? '1.0'}-6" rel="stylesheet">
|
||||
<link href="{$basePath}/css/layout.css?v={$appVersion ?? '1.0'}-7" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,10 @@ class FakturaEditPresenter extends BasePresenter
|
|||
}
|
||||
}
|
||||
|
||||
$this->template->katalogPolozekJson = json_encode(
|
||||
$this->fakturaFacade->getKatalogPolozek(),
|
||||
JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_THROW_ON_ERROR
|
||||
);
|
||||
$this->template->polozky = $polozky;
|
||||
$this->template->platby = $platby;
|
||||
$this->template->celkemFaktury = $celkem;
|
||||
|
|
@ -135,16 +139,28 @@ class FakturaEditPresenter extends BasePresenter
|
|||
{
|
||||
$form = new Form;
|
||||
|
||||
// 1. Načteme všechny zákazníky (parametr 'vse' projde bez filtru AKTIVNI)
|
||||
$zakaznici = $this->zakaznikFacade->getFiltered('', 'vse');
|
||||
$zakaznici = $this->zakaznikFacade->getFiltered('', 'aktivni');
|
||||
if ($this->id) {
|
||||
$faktura = $this->fakturaFacade->getById($this->id);
|
||||
$zakaznikId = $faktura ? (int) $faktura->ZAKAZNIK : 0;
|
||||
$uzJeVSeznamu = false;
|
||||
foreach ($zakaznici as $z) {
|
||||
if ((int) $z->ID === $zakaznikId) {
|
||||
$uzJeVSeznamu = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($zakaznikId && !$uzJeVSeznamu) {
|
||||
$stavajici = $this->zakaznikFacade->getById($zakaznikId);
|
||||
if ($stavajici) {
|
||||
$zakaznici[] = $stavajici;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$zakazniciPairs = [];
|
||||
foreach ($zakaznici as $z) {
|
||||
$nazev = trim(($z->PRIJMENI ?? '') . ' ' . ($z->JMENO ?? ''));
|
||||
if (!empty($z->IC)) {
|
||||
$nazev .= " (IČ: {$z->IC})";
|
||||
}
|
||||
// Kličem musí být int
|
||||
$zakazniciPairs[(int) $z->ID] = $nazev;
|
||||
$zakazniciPairs[(int) $z->ID] = $this->nazevZakaznika($z);
|
||||
}
|
||||
|
||||
$form->addSelect('ZAKAZNIK_ID', 'Odběratel:', $zakazniciPairs)
|
||||
|
|
@ -184,6 +200,23 @@ class FakturaEditPresenter extends BasePresenter
|
|||
return $form;
|
||||
}
|
||||
|
||||
private function nazevZakaznika(object $zakaznik): string
|
||||
{
|
||||
$nazev = trim(($zakaznik->PRIJMENI ?? '') . ' ' . ($zakaznik->JMENO ?? ''));
|
||||
$doplnek = [];
|
||||
if (!empty($zakaznik->MESTO)) {
|
||||
$doplnek[] = (string) $zakaznik->MESTO;
|
||||
}
|
||||
if (!empty($zakaznik->IC)) {
|
||||
$doplnek[] = 'IČ: ' . $zakaznik->IC;
|
||||
}
|
||||
if ($doplnek) {
|
||||
$nazev .= ' (' . implode(', ', $doplnek) . ')';
|
||||
}
|
||||
|
||||
return $nazev;
|
||||
}
|
||||
|
||||
public function fakturaFormSucceeded(Form $form, \stdClass $data): void
|
||||
{
|
||||
$firmaId = $this->getUser()->getIdentity()->firma_id;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<!-- Hlavička Faktury -->
|
||||
<div class="col-md-6">
|
||||
<label n:name="ZAKAZNIK_ID" class="form-label fw-bold">Odběratel *</label>
|
||||
<select n:name="ZAKAZNIK_ID" class="form-select"></select>
|
||||
<select n:name="ZAKAZNIK_ID" class="form-select" data-suggest="zakaznik" {if $isEdit}data-locked="1"{/if}></select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label n:name="CISLO" class="form-label fw-bold">Číslo faktury *</label>
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
<td data-label="Pořadí" class="text-center align-middle drag-handle" style="cursor: grab;">
|
||||
<i class="bi bi-grip-vertical text-muted fs-5"></i>
|
||||
</td>
|
||||
<td data-label="Název"><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" required></td>
|
||||
<td data-label="Název"><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" placeholder="Začněte psát název práce nebo zboží" required autocomplete="off"></td>
|
||||
<td data-label="Počet"><input type="number" step="0.01" name="polozky[{$index}][POCET]" value="{$p->POCET}" class="form-control item-pocet text-end" required></td>
|
||||
<td data-label="Jedn."><input type="text" name="polozky[{$index}][JEDNOTKA]" value="{$p->JEDNOTKA}" class="form-control"></td>
|
||||
<td data-label="Cena/Jedn."><input type="number" step="0.01" name="polozky[{$index}][CENA_JEDNOTKA]" value="{$p->CENA_JEDNOTKA}" class="form-control item-cena text-end" required></td>
|
||||
|
|
@ -209,6 +209,8 @@ document.getElementById('edit-platba-splatnost').addEventListener('click', funct
|
|||
});
|
||||
</script>
|
||||
|
||||
<script type="application/json" id="katalog-polozek">{$katalogPolozekJson|noescape}</script>
|
||||
|
||||
{syntax off}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
|
@ -260,6 +262,328 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
document.getElementById('total-sum').textContent = grandTotal.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK';
|
||||
}
|
||||
|
||||
function fold(value) {
|
||||
return String(value).normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
|
||||
}
|
||||
|
||||
function formatMoney(value) {
|
||||
return Number(value).toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
function bindSuggest(input, options) {
|
||||
if (input.dataset.suggestBound) {
|
||||
return;
|
||||
}
|
||||
input.dataset.suggestBound = '1';
|
||||
input.setAttribute('autocomplete', 'off');
|
||||
input.setAttribute('role', 'combobox');
|
||||
input.setAttribute('aria-autocomplete', 'list');
|
||||
input.setAttribute('aria-expanded', 'false');
|
||||
|
||||
const menu = document.createElement('ul');
|
||||
menu.className = 'suggest-menu';
|
||||
menu.hidden = true;
|
||||
menu.setAttribute('role', 'listbox');
|
||||
document.body.appendChild(menu);
|
||||
input._suggestMenu = menu;
|
||||
|
||||
let activeIndex = -1;
|
||||
let visible = [];
|
||||
|
||||
function closeMenu() {
|
||||
menu.hidden = true;
|
||||
input.setAttribute('aria-expanded', 'false');
|
||||
activeIndex = -1;
|
||||
}
|
||||
|
||||
function placeMenu() {
|
||||
const rect = input.getBoundingClientRect();
|
||||
const spaceBelow = window.innerHeight - rect.bottom;
|
||||
menu.style.left = Math.max(8, rect.left) + 'px';
|
||||
menu.style.width = Math.max(rect.width, 220) + 'px';
|
||||
menu.hidden = false;
|
||||
const height = menu.offsetHeight;
|
||||
if (spaceBelow < height + 8 && rect.top > spaceBelow) {
|
||||
menu.style.top = Math.max(8, rect.top - height - 4) + 'px';
|
||||
} else {
|
||||
menu.style.top = (rect.bottom + 4) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function render() {
|
||||
const query = input.value;
|
||||
const foldedQuery = fold(query).trim();
|
||||
let matched = options.items.filter(item => {
|
||||
if (!foldedQuery) {
|
||||
return true;
|
||||
}
|
||||
const hay = fold(item.label + ' ' + (item.meta || ''));
|
||||
return foldedQuery.split(/\s+/).every(word => hay.includes(word));
|
||||
});
|
||||
if (!options.keepOrder) {
|
||||
matched.sort((a, b) => {
|
||||
const score = item => {
|
||||
if (!foldedQuery) {
|
||||
return 1;
|
||||
}
|
||||
const label = fold(item.label);
|
||||
if (label.startsWith(foldedQuery)) {
|
||||
return 0;
|
||||
}
|
||||
if (label.includes(foldedQuery)) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
};
|
||||
return score(a) - score(b);
|
||||
});
|
||||
}
|
||||
visible = matched.slice(0, options.limit || 40);
|
||||
|
||||
menu.innerHTML = '';
|
||||
if (!visible.length) {
|
||||
if (!foldedQuery && options.freeText) {
|
||||
closeMenu();
|
||||
return;
|
||||
}
|
||||
const empty = document.createElement('li');
|
||||
empty.className = 'suggest-empty';
|
||||
empty.textContent = options.emptyText || 'Nic nenalezeno';
|
||||
menu.appendChild(empty);
|
||||
activeIndex = -1;
|
||||
placeMenu();
|
||||
input.setAttribute('aria-expanded', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
visible.forEach((item, index) => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'suggest-option' + (index === activeIndex ? ' is-active' : '');
|
||||
li.setAttribute('role', 'option');
|
||||
li.textContent = item.label;
|
||||
if (item.meta) {
|
||||
const meta = document.createElement('small');
|
||||
meta.textContent = item.meta;
|
||||
li.appendChild(meta);
|
||||
}
|
||||
li.addEventListener('mousedown', event => {
|
||||
event.preventDefault();
|
||||
choose(index);
|
||||
});
|
||||
li.addEventListener('mouseenter', () => {
|
||||
activeIndex = index;
|
||||
menu.querySelectorAll('.suggest-option').forEach((node, i) => {
|
||||
node.classList.toggle('is-active', i === activeIndex);
|
||||
});
|
||||
});
|
||||
menu.appendChild(li);
|
||||
});
|
||||
|
||||
if (activeIndex >= visible.length) {
|
||||
activeIndex = visible.length - 1;
|
||||
}
|
||||
placeMenu();
|
||||
input.setAttribute('aria-expanded', 'true');
|
||||
const current = menu.querySelectorAll('.suggest-option')[activeIndex];
|
||||
if (current) {
|
||||
current.classList.add('is-active');
|
||||
current.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
function choose(index) {
|
||||
const item = visible[index];
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
input.value = item.label;
|
||||
closeMenu();
|
||||
options.onPick(item, input);
|
||||
}
|
||||
|
||||
function acceptExact() {
|
||||
const typed = input.value.trim();
|
||||
if (typed === '') {
|
||||
options.onClear?.(input);
|
||||
return;
|
||||
}
|
||||
const foldedTyped = fold(typed);
|
||||
const hits = options.items.filter(item => fold(item.label) === foldedTyped);
|
||||
if (hits.length === 1) {
|
||||
input.value = hits[0].label;
|
||||
options.onPick(hits[0], input);
|
||||
return;
|
||||
}
|
||||
options.onClear?.(input);
|
||||
}
|
||||
|
||||
input.addEventListener('focus', () => {
|
||||
document.querySelectorAll('.suggest-menu').forEach(other => {
|
||||
if (other !== menu) {
|
||||
other.hidden = true;
|
||||
}
|
||||
});
|
||||
activeIndex = -1;
|
||||
render();
|
||||
});
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
activeIndex = -1;
|
||||
if (!options.freeText) {
|
||||
options.onClear?.(input);
|
||||
}
|
||||
render();
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', event => {
|
||||
if (event.key === 'ArrowDown') {
|
||||
event.preventDefault();
|
||||
if (menu.hidden) {
|
||||
render();
|
||||
}
|
||||
if (!visible.length) {
|
||||
return;
|
||||
}
|
||||
activeIndex = (activeIndex + 1) % visible.length;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
if (event.key === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
if (!visible.length) {
|
||||
return;
|
||||
}
|
||||
activeIndex = activeIndex <= 0 ? visible.length - 1 : activeIndex - 1;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Enter' && !menu.hidden) {
|
||||
event.preventDefault();
|
||||
if (activeIndex >= 0) {
|
||||
choose(activeIndex);
|
||||
} else if (visible.length === 1) {
|
||||
choose(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('blur', () => {
|
||||
window.setTimeout(() => {
|
||||
if (!options.freeText) {
|
||||
acceptExact();
|
||||
}
|
||||
closeMenu();
|
||||
}, 120);
|
||||
});
|
||||
|
||||
const followInput = event => {
|
||||
if (menu.hidden || event.target === menu) {
|
||||
return;
|
||||
}
|
||||
placeMenu();
|
||||
};
|
||||
window.addEventListener('resize', followInput);
|
||||
window.addEventListener('scroll', followInput, true);
|
||||
}
|
||||
|
||||
function initZakaznikSuggest() {
|
||||
const select = document.querySelector('select[data-suggest="zakaznik"]');
|
||||
if (!select || select.dataset.locked === '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = Array.from(select.options)
|
||||
.filter(option => option.value !== '')
|
||||
.map(option => ({
|
||||
value: option.value,
|
||||
label: option.textContent.trim(),
|
||||
}));
|
||||
|
||||
const wrap = document.createElement('div');
|
||||
wrap.className = 'suggest';
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.className = 'form-control';
|
||||
input.placeholder = 'Začněte psát jméno, město nebo IČ';
|
||||
input.id = select.id ? select.id + '-suggest' : 'zakaznik-suggest';
|
||||
const label = document.querySelector('label[for="' + select.id + '"]');
|
||||
if (label) {
|
||||
label.htmlFor = input.id;
|
||||
}
|
||||
|
||||
select.parentNode.insertBefore(wrap, select);
|
||||
wrap.appendChild(input);
|
||||
wrap.appendChild(select);
|
||||
select.classList.add('suggest-native');
|
||||
select.setAttribute('aria-hidden', 'true');
|
||||
select.removeAttribute('required');
|
||||
select.tabIndex = -1;
|
||||
|
||||
const selected = select.selectedOptions[0];
|
||||
if (select.value && selected) {
|
||||
input.value = selected.textContent.trim();
|
||||
}
|
||||
|
||||
bindSuggest(input, {
|
||||
items,
|
||||
emptyText: 'Žádný zákazník neodpovídá',
|
||||
onPick(item) {
|
||||
select.value = item.value;
|
||||
},
|
||||
onClear() {
|
||||
select.value = '';
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let katalog = [];
|
||||
try {
|
||||
katalog = JSON.parse(document.getElementById('katalog-polozek').textContent || '[]');
|
||||
} catch (error) {
|
||||
katalog = [];
|
||||
}
|
||||
|
||||
const katalogItems = katalog.map(item => ({
|
||||
label: item.nazev,
|
||||
meta: (item.jednotka ? item.jednotka + ' · ' : '') + formatMoney(item.cena) + ' CZK',
|
||||
payload: item,
|
||||
}));
|
||||
|
||||
function bindPolozkaSuggest(input) {
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
input.placeholder = input.placeholder || 'Začněte psát název práce nebo zboží';
|
||||
bindSuggest(input, {
|
||||
items: katalogItems,
|
||||
freeText: true,
|
||||
keepOrder: true,
|
||||
emptyText: 'V předchozích fakturách nic podobného není',
|
||||
onPick(item) {
|
||||
const row = input.closest('.item-row');
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
const jednotka = row.querySelector('input[name*="[JEDNOTKA]"]');
|
||||
const cena = row.querySelector('.item-cena');
|
||||
if (jednotka && item.payload.jednotka) {
|
||||
jednotka.value = item.payload.jednotka;
|
||||
}
|
||||
if (cena) {
|
||||
cena.value = Number(item.payload.cena).toFixed(2);
|
||||
}
|
||||
calculateTotals();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
initZakaznikSuggest();
|
||||
document.querySelectorAll('.item-nazev').forEach(bindPolozkaSuggest);
|
||||
|
||||
// Přidání nového řádku
|
||||
btnAdd.addEventListener('click', () => {
|
||||
const tr = document.createElement('tr');
|
||||
|
|
@ -268,7 +592,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
<td data-label="Pořadí" class="text-center align-middle drag-handle" style="cursor: grab;">
|
||||
<i class="bi bi-grip-vertical text-muted fs-5"></i>
|
||||
</td>
|
||||
<td data-label="Název"><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Popis práce / zboží" required></td>
|
||||
<td data-label="Název"><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Začněte psát název práce nebo zboží" required autocomplete="off"></td>
|
||||
<td data-label="Počet"><input type="number" step="0.01" name="polozky[${rowIndex}][POCET]" value="1" class="form-control item-pocet text-end" required></td>
|
||||
<td data-label="Jedn."><input type="text" name="polozky[${rowIndex}][JEDNOTKA]" value="ks" class="form-control"></td>
|
||||
<td data-label="Cena/Jedn."><input type="number" step="0.01" name="polozky[${rowIndex}][CENA_JEDNOTKA]" value="0" class="form-control item-cena text-end" required></td>
|
||||
|
|
@ -279,6 +603,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
bindPolozkaSuggest(tr.querySelector('.item-nazev'));
|
||||
rowIndex++;
|
||||
reindexRows();
|
||||
calculateTotals();
|
||||
|
|
@ -287,7 +612,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
// Event delegation pro smazání a přepočet
|
||||
tbody.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.btn-remove-row')) {
|
||||
e.target.closest('.item-row').remove();
|
||||
const row = e.target.closest('.item-row');
|
||||
row.querySelectorAll('.item-nazev').forEach(input => input._suggestMenu?.remove());
|
||||
row.remove();
|
||||
reindexRows();
|
||||
calculateTotals();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -832,4 +832,62 @@ a:hover {
|
|||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Našeptávač zákazníka a položek faktury */
|
||||
.suggest {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
select.suggest-native {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
white-space: nowrap !important;
|
||||
border: 0 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.suggest-menu {
|
||||
position: fixed;
|
||||
z-index: 1080;
|
||||
max-height: 280px;
|
||||
margin: 0;
|
||||
padding: 0.25rem 0;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: 0 8px 24px rgba(20, 50, 100, 0.14);
|
||||
}
|
||||
|
||||
.suggest-menu[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.suggest-option {
|
||||
padding: 0.4rem 0.75rem;
|
||||
cursor: pointer;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.suggest-option.is-active,
|
||||
.suggest-option:hover {
|
||||
background: var(--primary-soft);
|
||||
}
|
||||
|
||||
.suggest-option small {
|
||||
display: block;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.suggest-empty {
|
||||
padding: 0.45rem 0.75rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
Loading…
Reference in New Issue