mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-13 18:40:50 +08:00
explorer: Clamp long node descriptions
Collapse the node description to three lines with a Show more toggle when it overflows, and let the sidebar scroll on wide screens so the node details below it stay reachable.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
grid-area: left;
|
||||
background-color: var(--color-surface-base);
|
||||
height: calc(100vh - var(--global-header-height));
|
||||
overflow-y: auto;
|
||||
}
|
||||
.center {
|
||||
grid-area: center;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type { BaseUrl, Node, NodeStats } from "@http-client";
|
||||
|
||||
import { tick } from "svelte";
|
||||
|
||||
import dompurify from "dompurify";
|
||||
import { markdown, sanitizeConfig } from "@app/lib/markdown";
|
||||
|
||||
@@ -23,6 +25,33 @@
|
||||
export let stats: NodeStats;
|
||||
export let node: Node;
|
||||
|
||||
let descriptionContent: HTMLDivElement | undefined = undefined;
|
||||
let descriptionExpanded = false;
|
||||
let descriptionTruncated = false;
|
||||
|
||||
// Clamp to three lines, but only when that hides at least two: a Show more
|
||||
// that reveals a single line costs as much space as the line itself.
|
||||
function measureDescription() {
|
||||
if (descriptionContent && !descriptionExpanded) {
|
||||
const lineHeight = parseFloat(
|
||||
getComputedStyle(descriptionContent).lineHeight,
|
||||
);
|
||||
descriptionTruncated = descriptionContent.offsetHeight > 4.5 * lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function observeSize(element: HTMLElement) {
|
||||
const observer = new ResizeObserver(measureDescription);
|
||||
observer.observe(element);
|
||||
return { destroy: () => observer.disconnect() };
|
||||
}
|
||||
|
||||
$: {
|
||||
descriptionExpanded = false;
|
||||
void node.description;
|
||||
void tick().then(measureDescription);
|
||||
}
|
||||
|
||||
function render(content: string): string {
|
||||
return dompurify.sanitize(
|
||||
markdown({ linkify: true, emojis: true }).parse(content) as string,
|
||||
@@ -75,6 +104,34 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.description.clamped {
|
||||
max-height: 3.75rem;
|
||||
overflow: hidden;
|
||||
mask-image: linear-gradient(to bottom, black 50%, transparent);
|
||||
}
|
||||
|
||||
.description :global(p:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.description :global(p:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.description-toggle {
|
||||
margin-top: 0.25rem;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font: var(--txt-body-m-semibold);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.description-toggle:hover {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.sidebar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -138,8 +195,22 @@
|
||||
</div>
|
||||
|
||||
{#if node.description}
|
||||
<div class="description txt-body-m-regular">
|
||||
{@html render(node.description)}
|
||||
<div>
|
||||
<div
|
||||
class="description txt-body-m-regular"
|
||||
class:clamped={descriptionTruncated && !descriptionExpanded}>
|
||||
<div bind:this={descriptionContent} use:observeSize>
|
||||
{@html render(node.description)}
|
||||
</div>
|
||||
</div>
|
||||
{#if descriptionTruncated}
|
||||
<button
|
||||
class="description-toggle"
|
||||
aria-expanded={descriptionExpanded}
|
||||
on:click={() => (descriptionExpanded = !descriptionExpanded)}>
|
||||
{descriptionExpanded ? "Show less" : "Show more"}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user