Fixing ui issues - adding better color highlights - added bg to separate areas.

This commit is contained in:
Julio Ruiz 2026-05-11 23:55:20 -05:00
parent f9e3eee394
commit 07594aeac0
3 changed files with 152 additions and 180 deletions

View File

@ -41,25 +41,26 @@
/* Client-side highlights inside the detail body. */ /* Client-side highlights inside the detail body. */
mark.search-match { mark.search-match {
background-color: #fde68a; background-color: #fdff32;
color: #78350f; color: #000;
padding: 0 2px; padding: 2px;
border-radius: 2px; border-radius: 2px;
font-weight: 600; font-weight: 600;
box-shadow: 0 0 0 1px #f59e0b inset; box-shadow: 0 0 0 1px #e9ff32 inset;
} }
.dark mark.search-match { .dark mark.search-match {
background-color: #78350f; background-color: #fdff32;
color: #fde68a; color: #000;
box-shadow: 0 0 0 1px #f59e0b inset; box-shadow: 0 0 0 1px #e9ff32 inset;
} }
/* The currently-focused match (the one the user navigated to). */ /* The currently-focused match (the one the user navigated to). */
mark.search-match.is-current { mark.search-match.is-current {
background-color: #f97316; background-color: #8cff32;
color: #ffffff; padding: 2px;
box-shadow: 0 0 0 2px #c2410c inset; color: #000;
box-shadow: 0 0 0 2px #8cff32 inset;
animation: search-match-pulse 0.9s ease-out 1; animation: search-match-pulse 0.9s ease-out 1;
} }
@ -70,7 +71,7 @@ mark.search-match.is-current {
} }
@keyframes search-match-pulse { @keyframes search-match-pulse {
0% { box-shadow: 0 0 0 0 rgba(249, 115, 22, 0.75), 0 0 0 2px #c2410c inset; } 0% { box-shadow: 0 0 0 0 #e9ff32, 0 0 0 2px #8cff32 inset; }
70% { box-shadow: 0 0 0 14px rgba(249, 115, 22, 0), 0 0 0 2px #c2410c inset; } 70% { box-shadow: 0 0 0 14px rgba(249, 115, 22, 0), 0 0 0 2px #8cff32 inset; }
100% { box-shadow: 0 0 0 0 rgba(249, 115, 22, 0), 0 0 0 2px #c2410c inset; } 100% { box-shadow: 0 0 0 0 rgba(249, 115, 22, 0), 0 0 0 2px #8cff32 inset; }
} }

View File

@ -367,30 +367,90 @@ function onInputKey(e: KeyboardEvent) {
<!-- El botón de favorito ya no vive aquí: ahora es un FAB flotante <!-- El botón de favorito ya no vive aquí: ahora es un FAB flotante
en la esquina inferior derecha del panel para que siga visible en la esquina inferior derecha del panel para que siga visible
mientras se lee. Ver más abajo en el scroll container. --> mientras se lee. Ver más abajo en el scroll container. -->
<UButton <UTooltip :text="isFav ? 'Quitar de mi lista' : 'Guardar en mi lista'">
v-if="matchUrl" <UButton
:to="matchUrl" :icon="isFav ? 'i-lucide-bookmark-check' : 'i-lucide-bookmark-plus'"
target="_blank" :color="isFav ? 'primary' : 'neutral'"
icon="i-lucide-external-link" variant="ghost"
label="Ver en sitio" :aria-label="isFav ? 'Quitar de mi lista' : 'Guardar en mi lista'"
color="primary" @click="onToggleFavorite"
variant="solid" />
size="sm" </UTooltip>
/>
</template> </template>
</UDashboardNavbar> </UDashboardNavbar>
<div class="flex flex-col sm:flex-row justify-between gap-2 p-4 sm:px-6 border-b border-default"> <div class="flex flex-col sm:flex-row justify-between gap-2 p-4 sm:px-6 border-b border-default shadow-lg z-10">
<div class="min-w-0"> <div class="min-w-0 flex gap-1 sm:gap-4 sm:items-center flex-col sm:flex-row">
<p class="font-semibold text-sm text-highlighted"> <p class=" text-sm text-highlighted flex items-center gap-1 mb-0.5">
<UIcon name="ph:calendar" class="size-5 text-green-600" />
{{ safeDate(activity) }} {{ safeDate(activity) }}
</p> </p>
<p class="text-muted text-sm truncate"> <p class="text-sm truncate flex items-center gap-1">
<UIcon name="ph:map-pin" class="size-5 text-green-600" />
{{ formatLocation(activity) }} {{ formatLocation(activity) }}
</p> </p>
</div> </div>
<ul v-if="fileLinks.length" class="flex flex-wrap gap-3 text-xs text-muted"> <div
v-if="activity?.body"
class="flex items-center gap-2"
>
<UFieldGroup>
<UInput
v-model="localQuery"
icon="i-lucide-search"
placeholder="Buscar frase exacta en este documento..."
class="w-full"
:ui="{ trailing: 'pe-1' }"
@keydown="onInputKey"
>
<template #trailing>
<UButton
v-if="localQuery"
icon="i-lucide-x"
variant="link"
aria-label="Limpiar"
@click="clearLocalQuery"
/>
</template>
</UInput>
<UBadge v-if="localQuery" class="bg-gray-400 text-white">
<span
class="text-xs tabular-nums whitespace-nowrap min-w-[3.5rem] text-center text-white"
:class="totalMatches ? 'text-toned font-medium' : 'text-dimmed'"
>
<template v-if="localQuery">
{{ totalMatches ? `${currentIdx + 1} / ${totalMatches}` : '0 / 0' }}
</template>
</span>
</UBadge>
<UTooltip text="Anterior (Shift+Enter)">
<UButton
icon="i-lucide-chevron-up"
:disabled="!totalMatches"
aria-label="Coincidencia anterior"
class="bg-gray-400"
color="neutral"
@click="prevMatch"
/>
</UTooltip>
<UTooltip text="Siguiente (Enter)">
<UButton
icon="i-lucide-chevron-down"
:disabled="!totalMatches"
aria-label="Coincidencia siguiente"
class="bg-gray-400"
color="neutral"
@click="nextMatch"
/>
</UTooltip>
</UFieldGroup>
</div>
<!-- <ul v-if="fileLinks.length" class="flex flex-wrap gap-3 text-xs text-muted">
<li <li
v-for="(f, idx) in fileLinks" v-for="(f, idx) in fileLinks"
:key="idx" :key="idx"
@ -405,77 +465,15 @@ function onInputKey(e: KeyboardEvent) {
<span>{{ f.label }}</span> <span>{{ f.label }}</span>
</ULink> </ULink>
</li> </li>
</ul> </ul> -->
</div> </div>
<div ref="scrollContainer" class="flex-1 overflow-y-auto relative"> <div ref="scrollContainer" class="flex-1 overflow-y-auto relative bg-gray-100">
<!-- Find-in-document toolbar: sticky at the top of the scroll area so <div class="p-1 sm:p-4 bg-white rounded-lg shadow-md max-w-4xl m-4 sm:mx-auto sm:my-6">
the user can always reach it while reading. -->
<div
v-if="activity?.body"
class="sticky top-0 z-10 bg-default/95 backdrop-blur-sm border-b border-default px-4 sm:px-6 py-2 flex items-center gap-2"
>
<div class="relative flex-1 min-w-0 max-w-md">
<UInput
v-model="localQuery"
icon="i-lucide-search"
placeholder="Buscar frase exacta en este documento..."
size="sm"
class="w-full"
:ui="{ trailing: 'pe-1' }"
@keydown="onInputKey"
>
<template #trailing>
<UButton
v-if="localQuery"
icon="i-lucide-x"
color="neutral"
variant="link"
size="xs"
aria-label="Limpiar"
@click="clearLocalQuery"
/>
</template>
</UInput>
</div>
<span
class="text-xs tabular-nums whitespace-nowrap min-w-[3.5rem] text-center"
:class="totalMatches ? 'text-toned font-medium' : 'text-dimmed'"
>
<template v-if="localQuery">
{{ totalMatches ? `${currentIdx + 1} / ${totalMatches}` : '0 / 0' }}
</template>
</span>
<UTooltip text="Anterior (Shift+Enter)">
<UButton
icon="i-lucide-chevron-up"
color="neutral"
variant="ghost"
size="sm"
:disabled="!totalMatches"
aria-label="Coincidencia anterior"
@click="prevMatch"
/>
</UTooltip>
<UTooltip text="Siguiente (Enter)">
<UButton
icon="i-lucide-chevron-down"
color="neutral"
variant="ghost"
size="sm"
:disabled="!totalMatches"
aria-label="Coincidencia siguiente"
@click="nextMatch"
/>
</UTooltip>
</div>
<article <article
v-if="activity?.body" v-if="activity?.body"
ref="bodyContainer" ref="bodyContainer"
class="prose prose-sm max-w-none dark:prose-invert p-4 sm:p-6 pb-24" class="prose prose-sm max-w-none dark:prose-invert p-4 sm:p-6 pb-24 prose-p:mb-2 text-base/7 prose:text-base/7"
/> />
<p v-else class="p-4 sm:p-6 text-sm text-muted"> <p v-else class="p-4 sm:p-6 text-sm text-muted">
No hay contenido disponible para esta coincidencia. No hay contenido disponible para esta coincidencia.
@ -484,34 +482,6 @@ function onInputKey(e: KeyboardEvent) {
<ULink :to="matchUrl" target="_blank" class="text-primary">verla en el sitio</ULink>. <ULink :to="matchUrl" target="_blank" class="text-primary">verla en el sitio</ULink>.
</span> </span>
</p> </p>
<!--
Botón flotante (FAB) para guardar/quitar de favoritos.
- Vive dentro del contenedor de scroll para anclarse al panel y no
interferir con otros layouts (e.g. layouts multi-panel).
- Truco: el wrapper es `sticky bottom-0 h-0` con `pointer-events-none`,
de forma que ocupa cero altura en el flujo del documento pero
mantiene al hijo absoluto pegado al borde inferior visible mientras
el usuario hace scroll. Así el botón siempre está a un toque,
igual en escritorio y en móvil.
- El padding-bottom extra del article (`pb-24`) evita que el FAB
tape el final del texto cuando se llega al final del documento.
-->
<div
v-if="collection"
class="sticky bottom-0 inset-x-0 h-0 z-20 pointer-events-none"
>
<UTooltip :text="isFav ? 'Quitar de mi lista' : 'Guardar en mi lista'">
<UButton
:icon="isFav ? 'i-lucide-bookmark-check' : 'i-lucide-bookmark-plus'"
:color="isFav ? 'primary' : 'neutral'"
variant="solid"
size="xl"
:aria-label="isFav ? 'Quitar de mi lista' : 'Guardar en mi lista'"
class="absolute bottom-4 end-4 sm:bottom-6 sm:end-6 rounded-full shadow-lg shadow-black/15 dark:shadow-black/40 ring-1 ring-default pointer-events-auto transition-transform hover:scale-105 active:scale-95"
@click="onToggleFavorite"
/>
</UTooltip>
</div> </div>
</div> </div>
</UDashboardPanel> </UDashboardPanel>

View File

@ -674,7 +674,7 @@ useIntersectionObserver(
:ref="(el) => { activitiesRefs[row.activity._id] = el as Element | null }" :ref="(el) => { activitiesRefs[row.activity._id] = el as Element | null }"
> >
<div <div
class="p-4 sm:px-6 text-sm cursor-pointer border-l-2 transition-colors" class="bg-gray-100 p-4 sm:px-4 text-sm cursor-pointer border-l-2 transition-colors"
:class="[ :class="[
row.activity.unread ? 'text-highlighted' : 'text-toned', row.activity.unread ? 'text-highlighted' : 'text-toned',
selectedActivity && selectedActivity._id === row.activity._id selectedActivity && selectedActivity._id === row.activity._id
@ -683,70 +683,71 @@ useIntersectionObserver(
]" ]"
@click="selectedActivity = row.activity" @click="selectedActivity = row.activity"
> >
<div class="flex items-start justify-between gap-2 mb-1"> <div class="">
<div class="text-sm font-semibold line-clamp-2" v-html="row.title" /> <div class="flex items-start justify-between gap-2 mb-1 ">
<div class="flex items-center gap-1 shrink-0"> <div class="text-sm font-semibold line-clamp-2" v-html="row.title" />
<UChip v-if="row.activity.unread" /> <div class="flex items-center gap-1 shrink-0">
<UTooltip <UChip v-if="row.activity.unread" />
v-if="collection" <UTooltip
:text="favorites.isFavorite(collection, row.activity._id) ? 'Quitar de mi lista' : 'Guardar en mi lista'" v-if="collection"
:text="favorites.isFavorite(collection, row.activity._id) ? 'Quitar de mi lista' : 'Guardar en mi lista'"
>
<UButton
:icon="favorites.isFavorite(collection, row.activity._id) ? 'i-lucide-bookmark-check' : 'i-lucide-bookmark-plus'"
:color="favorites.isFavorite(collection, row.activity._id) ? 'primary' : 'neutral'"
variant="ghost"
size="xs"
:aria-label="favorites.isFavorite(collection, row.activity._id) ? 'Quitar de mi lista' : 'Guardar en mi lista'"
@click="(ev: MouseEvent) => onToggleFavorite(row.activity, ev)"
/>
</UTooltip>
</div>
</div>
<p class="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 text-sm sm:text-xs mb-1 justify-between ">
<span v-if="hasDate(row.activity)" class="flex items-center gap-1"><UIcon name="ph:calendar" class="size-5 text-green-600" />{{ safeDate(row.activity) }}</span>
<span class="truncate flex items-center"><UIcon name="ph:map-pin" class="size-5 text-green-600" />{{ formatLocation(row.activity) }}</span>
</p>
<div class="mt-2 !text-sm" v-if="row.body">
<div
v-if="row.bodyApproximate"
class="text-[11px] text-warning flex items-center gap-1 mb-0.5 bg-white p-2 rounded-xl"
> >
<UButton <UIcon name="i-lucide-info" class="size-3" />
:icon="favorites.isFavorite(collection, row.activity._id) ? 'i-lucide-bookmark-check' : 'i-lucide-bookmark-plus'" <span>Coincidencia en el documento (abre para ubicarla con la búsqueda interna)</span>
:color="favorites.isFavorite(collection, row.activity._id) ? 'primary' : 'neutral'" </div>
variant="ghost" <div
size="xs" class="mt-1 snippet-html text-dimmed text-sm sm:text-xs bg-white p-3 rounded-lg"
:aria-label="favorites.isFavorite(collection, row.activity._id) ? 'Quitar de mi lista' : 'Guardar en mi lista'" v-html="row.body"
@click="(ev: MouseEvent) => onToggleFavorite(row.activity, ev)" />
/>
</UTooltip>
</div> </div>
</div>
<p class="flex items-center gap-2 text-xs text-muted mb-1">
<span v-if="hasDate(row.activity)">{{ safeDate(row.activity) }}</span>
<USeparator v-if="hasDate(row.activity)" orientation="vertical" class="h-3" />
<span class="truncate">{{ formatLocation(row.activity) }}</span>
</p>
<div class="bg-gray-100 p-1 rounded-xl !text-sm" v-if="row.body">
<div <div
v-if="row.bodyApproximate" v-for="extra in row.extraSnippets"
class="text-[11px] text-warning flex items-center gap-1 mb-0.5" :key="extra.field"
class="mt-1.5 text-xs text-dimmed flex items-start gap-1.5"
> >
<UIcon name="i-lucide-info" class="size-3" /> <UBadge
<span>Coincidencia en el documento (abre para ubicarla con la búsqueda interna)</span> :label="fieldLabel(extra.field)"
size="xs"
color="warning"
variant="subtle"
class="mt-0.5 shrink-0 capitalize"
/>
<span class="snippet-html line-clamp-2" v-html="extra.snippet" />
</div> </div>
<div <div
class="snippet-html text-dimmed line-clamp-3 text-xs" v-if="!row.extraSnippets.length && row.extraFields.length"
v-html="row.body" class="mt-1.5 text-xs text-muted flex items-center gap-1.5 "
/> >
</div> <UIcon name="i-lucide-info" class="size-3.5" />
<span>
<div Coincide en:
v-for="extra in row.extraSnippets" <span class="font-medium capitalize">{{ row.extraFields.map(fieldLabel).join(', ') }}</span>
:key="extra.field" </span>
class="mt-1.5 text-xs text-dimmed flex items-start gap-1.5" </div>
>
<UBadge
:label="fieldLabel(extra.field)"
size="xs"
color="warning"
variant="subtle"
class="mt-0.5 shrink-0 capitalize"
/>
<span class="snippet-html line-clamp-2" v-html="extra.snippet" />
</div>
<div
v-if="!row.extraSnippets.length && row.extraFields.length"
class="mt-1.5 text-xs text-muted flex items-center gap-1.5"
>
<UIcon name="i-lucide-info" class="size-3.5" />
<span>
Coincide en:
<span class="font-medium capitalize">{{ row.extraFields.map(fieldLabel).join(', ') }}</span>
</span>
</div> </div>
</div> </div>
</div> </div>