feat(web): guia do painel com botão ⓘ em cada seção
Um ⓘ ao lado do título de cada seção (Ao vivo, Vida, Eventos, Ajustes, Reações, Conversa, Movimento) abre a explicação daquela parte: o que faz cada opção, onde fica e uma dica. No cartão Vida também há ⓘ para humores, cuidados e ritmo. O ⓘ do topo explica a tela atual. A página Guia (#/guia) junta todos os 21 tópicos em ordem de tutorial, com índice e busca; cada tópico também aparece no Ctrl+K e há atalho em Ajustes › Sistema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
<script lang="ts">
|
||||
import { onMount, tick } from 'svelte';
|
||||
import TopicoGuia from '../lib/componentes/TopicoGuia.svelte';
|
||||
import { ORDEM_GUIA, TOPICOS } from '../lib/guia';
|
||||
|
||||
// Guia completo: todos os tópicos em ordem de tutorial, com índice. #/guia?t=vida abre no tópico.
|
||||
let busca = $state('');
|
||||
const normal = (t: string) => t.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
|
||||
const lista = $derived(
|
||||
ORDEM_GUIA.filter((id) => {
|
||||
if (!busca) return true;
|
||||
const t = TOPICOS[id];
|
||||
const texto = [t.titulo, t.resumo, t.onde, t.dica ?? '', ...(t.itens ?? []).map((i) => i.nome + ' ' + i.texto)].join(' ');
|
||||
return normal(texto).includes(normal(busca));
|
||||
}),
|
||||
);
|
||||
|
||||
async function irPara(id: string) {
|
||||
await tick();
|
||||
document.getElementById('guia-' + id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
// Abre no tópico do endereço, também quando o ⓘ é usado com o guia já aberto.
|
||||
function pelaUrl() {
|
||||
const t = new URLSearchParams(location.hash.split('?')[1] ?? '').get('t');
|
||||
if (t && TOPICOS[t]) irPara(t);
|
||||
}
|
||||
onMount(() => {
|
||||
pelaUrl();
|
||||
addEventListener('hashchange', pelaUrl);
|
||||
return () => removeEventListener('hashchange', pelaUrl);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="guia">
|
||||
<aside class="indice">
|
||||
<label class="busca">
|
||||
<span class="sr">Buscar no guia</span>
|
||||
<input type="search" bind:value={busca} placeholder="Buscar no guia (ex.: ritmo, PIR, dormir)" />
|
||||
</label>
|
||||
<nav aria-label="Tópicos do guia">
|
||||
{#each lista as id (id)}
|
||||
<a href={'#/guia?t=' + id} onclick={(e) => { e.preventDefault(); history.replaceState(null, '', '#/guia?t=' + id); irPara(id); }}>
|
||||
<span aria-hidden="true">{TOPICOS[id].emoji}</span> {TOPICOS[id].titulo}
|
||||
</a>
|
||||
{/each}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="topicos">
|
||||
{#each lista as id (id)}
|
||||
<section class="topico" id={'guia-' + id}>
|
||||
<h2><span aria-hidden="true">{TOPICOS[id].emoji}</span> {TOPICOS[id].titulo}</h2>
|
||||
<TopicoGuia topico={TOPICOS[id]} />
|
||||
</section>
|
||||
{:else}
|
||||
<p class="vazio">Nada no guia com “{busca}”.</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.guia {
|
||||
display: grid;
|
||||
gap: var(--e-5);
|
||||
align-items: start;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.guia {
|
||||
grid-template-columns: 260px minmax(0, 1fr);
|
||||
}
|
||||
.indice {
|
||||
position: sticky;
|
||||
top: calc(var(--topo-altura) + var(--e-4));
|
||||
}
|
||||
}
|
||||
.indice {
|
||||
display: grid;
|
||||
gap: var(--e-3);
|
||||
}
|
||||
.sr {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--e-2);
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
nav {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
}
|
||||
nav a {
|
||||
display: flex;
|
||||
gap: var(--e-2);
|
||||
align-items: center;
|
||||
padding: var(--e-1) var(--e-3);
|
||||
border-radius: var(--raio-pill);
|
||||
background: var(--superficie);
|
||||
border: 1px solid var(--borda);
|
||||
color: var(--texto-2);
|
||||
font-size: var(--t-sm);
|
||||
text-decoration: none;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
nav a {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-radius: var(--raio-sm);
|
||||
}
|
||||
}
|
||||
nav a:hover {
|
||||
background: var(--superficie-2);
|
||||
color: var(--texto);
|
||||
}
|
||||
.topicos {
|
||||
display: grid;
|
||||
gap: var(--e-4);
|
||||
min-width: 0;
|
||||
}
|
||||
.topico {
|
||||
display: grid;
|
||||
gap: var(--e-3);
|
||||
padding: var(--e-5);
|
||||
background: var(--superficie);
|
||||
border: 1px solid var(--borda);
|
||||
border-radius: var(--raio-lg);
|
||||
scroll-margin-top: calc(var(--topo-altura) + var(--e-4));
|
||||
}
|
||||
h2 {
|
||||
font-size: var(--t-lg);
|
||||
text-wrap: balance;
|
||||
}
|
||||
.vazio {
|
||||
color: var(--texto-3);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user