MelissaMenu: busca no topo do mm-side (estilo rail)
Input com pi-search a esquerda + botao limpar a direita. Quando query tem texto, substitui a lista de categorias por uma lista flat de sub-itens que casam (com nome da categoria a direita como breadcrumb). Click no resultado dispara clicarSubItem (mesma logica de navegacao) e limpa o termo. Empty state pra "nenhum resultado". Visual segue mm-aside: bg --m-bg-soft, border --m-border, focus border --p-primary-color. Hover dos resultados usa color-mix primary 12% (mesmo pattern do .mm-foot-item). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,31 @@ function categoryKeyFor(itemKey) {
|
|||||||
const selectedKey = ref(categoryKeyFor(props.secaoAtiva) || CATEGORIAS[0].key);
|
const selectedKey = ref(categoryKeyFor(props.secaoAtiva) || CATEGORIAS[0].key);
|
||||||
const copiado = ref(false);
|
const copiado = ref(false);
|
||||||
|
|
||||||
|
// Busca — quando query tem texto, substitui a lista de categorias
|
||||||
|
// por uma lista flat de sub-itens que casam com o termo.
|
||||||
|
const query = ref('');
|
||||||
|
const searchResults = computed(() => {
|
||||||
|
const q = query.value.trim().toLowerCase();
|
||||||
|
if (!q) return [];
|
||||||
|
const out = [];
|
||||||
|
for (const cat of CATEGORIAS) {
|
||||||
|
for (const group of cat.groups) {
|
||||||
|
for (const item of group.items) {
|
||||||
|
if (item.tipo === 'link-cadastro') continue;
|
||||||
|
const label = (item.label || '').toLowerCase();
|
||||||
|
if (label.includes(q)) {
|
||||||
|
out.push({ catLabel: cat.label, item });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
function clicarResultado(item) {
|
||||||
|
clicarSubItem(item);
|
||||||
|
query.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
// Sincroniza o destaque com a sessao ativa — se o user troca de
|
// Sincroniza o destaque com a sessao ativa — se o user troca de
|
||||||
// "Lancamentos" pra "Agenda" sem fechar o menu, o selecionado
|
// "Lancamentos" pra "Agenda" sem fechar o menu, o selecionado
|
||||||
// acompanha a sessao corrente.
|
// acompanha a sessao corrente.
|
||||||
@@ -382,7 +407,48 @@ async function sair() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mm-side__list">
|
<!-- Busca (estilo rail) — filtra sub-itens de todas as categorias -->
|
||||||
|
<div class="mm-search">
|
||||||
|
<i class="pi pi-search mm-search__icon" />
|
||||||
|
<input
|
||||||
|
v-model="query"
|
||||||
|
type="text"
|
||||||
|
inputmode="search"
|
||||||
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
placeholder="Encontrar menu..."
|
||||||
|
class="mm-search__input"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
v-if="query"
|
||||||
|
class="mm-search__clear"
|
||||||
|
type="button"
|
||||||
|
aria-label="Limpar busca"
|
||||||
|
@click="query = ''"
|
||||||
|
>
|
||||||
|
<i class="pi pi-times" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resultados de busca (flat, com nome da categoria) -->
|
||||||
|
<div v-if="query.trim()" class="mm-search__results">
|
||||||
|
<button
|
||||||
|
v-for="r in searchResults"
|
||||||
|
:key="`${r.catLabel}_${r.item.key}`"
|
||||||
|
class="mm-search__result"
|
||||||
|
@click="clicarResultado(r.item)"
|
||||||
|
>
|
||||||
|
<i :class="r.item.icon" class="mm-search__result-icon" />
|
||||||
|
<span class="mm-search__result-label">{{ r.item.label }}</span>
|
||||||
|
<span class="mm-search__result-cat">{{ r.catLabel }}</span>
|
||||||
|
</button>
|
||||||
|
<div v-if="!searchResults.length" class="mm-search__empty">
|
||||||
|
Nenhum resultado pra "{{ query }}"
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lista normal de categorias (so quando busca vazia) -->
|
||||||
|
<div v-else class="mm-side__list">
|
||||||
<button
|
<button
|
||||||
v-for="c in CATEGORIAS"
|
v-for="c in CATEGORIAS"
|
||||||
:key="c.key"
|
:key="c.key"
|
||||||
@@ -724,6 +790,121 @@ async function sair() {
|
|||||||
transition: background-color 140ms ease;
|
transition: background-color 140ms ease;
|
||||||
}
|
}
|
||||||
.mm-side__close:hover { background: var(--m-bg-soft-hover); }
|
.mm-side__close:hover { background: var(--m-bg-soft-hover); }
|
||||||
|
/* Busca no topo do mm-side — espelha o pattern do AppMenu rail */
|
||||||
|
.mm-search {
|
||||||
|
position: relative;
|
||||||
|
margin: 0 8px 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--m-bg-soft);
|
||||||
|
border: 1px solid var(--m-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: border-color 140ms ease, background-color 140ms ease;
|
||||||
|
}
|
||||||
|
.mm-search:focus-within {
|
||||||
|
border-color: var(--p-primary-color);
|
||||||
|
background: var(--m-bg-medium);
|
||||||
|
}
|
||||||
|
.mm-search__icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.mm-search:focus-within .mm-search__icon { color: var(--p-primary-color); }
|
||||||
|
.mm-search__input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 30px 8px 32px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--m-text);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.mm-search__input::placeholder { color: var(--m-text-faint); }
|
||||||
|
.mm-search__clear {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
transition: color 120ms ease, background-color 120ms ease;
|
||||||
|
}
|
||||||
|
.mm-search__clear:hover {
|
||||||
|
background: var(--m-bg-soft-hover);
|
||||||
|
color: var(--m-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-search__results {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0 8px 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--m-border-strong) transparent;
|
||||||
|
}
|
||||||
|
.mm-search__results::-webkit-scrollbar { width: 5px; }
|
||||||
|
.mm-search__results::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--m-border-strong);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.mm-search__result {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--m-text);
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
transition: background-color 120ms ease, color 120ms ease;
|
||||||
|
}
|
||||||
|
.mm-search__result:hover {
|
||||||
|
background: color-mix(in srgb, var(--p-primary-color) 12%, transparent);
|
||||||
|
color: var(--p-primary-color);
|
||||||
|
}
|
||||||
|
.mm-search__result-icon {
|
||||||
|
width: 16px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--p-primary-color);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.mm-search__result-label {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.mm-search__result-cat {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
.mm-search__empty {
|
||||||
|
padding: 16px 12px;
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mm-side__list {
|
.mm-side__list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user