Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand/Collapse all search results #904

Merged
merged 9 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/langs/json/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"reset": "Clear Search",
"help": "Use filters like <code>user:</code>, <code>project:</code> or <code>organization:</code> to refine searches. Use <code>sort:</code> to order results.",
"more": "Learn more",
"empty": "No matching results found."
"collapseAll": "Collapse all",
"expandAll": "Expand all",
"empty": "No matching results found"
},
"homeTemplate": {
"signedIn": "Signed in as {name}",
Expand Down
15 changes: 12 additions & 3 deletions src/lib/components/common/Highlight.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

export let title: string = "";
export let segments: string[] = [];
export let inlineTitle: boolean = false;

function sanitize(s: string): string {
return DOMPurify.sanitize(s, { ALLOWED_TAGS: ["em"] });
}
</script>

<div class="container">
<div class="container" class:inlineTitle>
{#if title}<h4 class="ellipsis">{@html sanitize(title)}</h4>{/if}
{#if segments.length > 0}
<blockquote>
Expand All @@ -23,6 +24,9 @@
<style>
.container {
width: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.container blockquote {
margin: 0;
Expand All @@ -31,11 +35,10 @@
overflow: hidden;
}
.container h4 {
flex: 0 0 4rem;
color: var(--gray-4);
font-weight: var(--font-semibold);
font-size: var(--font-sm);
margin-bottom: 0.5rem;
/* margin-bottom: 0.5rem; */
}
.segment {
margin-bottom: 0.5rem;
Expand All @@ -54,4 +57,10 @@
box-shadow: 0 0 0 0.125rem var(--yellow-2);
font-style: normal;
}
.container.inlineTitle {
flex-direction: row;
}
.container.inlineTitle h4 {
flex: 0 0 4rem;
}
</style>
89 changes: 83 additions & 6 deletions src/lib/components/common/HighlightGroup.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,79 @@
<!-- @component A collection of highlights from search results.-->

<script lang="ts" generics="H">
import { createEventDispatcher } from "svelte";
import { _ } from "svelte-i18n";
import {
ChevronDown12,
ChevronRight12,
Fold16,
Unfold16,
} from "svelte-octicons";

import { remToPx } from "$lib/utils/layout";
import Button from "./Button.svelte";

export let open = false;
export let highlights: [string, H][] = [];
export let getHref: (id: string) => string;
export let showAll = false;

let clientWidth = 1000;
$: isSmall = clientWidth < remToPx(27);

const dispatch = createEventDispatcher();

function collapseAll() {
open = false;
dispatch("collapseAll");
}

function expandAll() {
open = true;
dispatch("expandAll");
}
</script>

{#if highlights.length}
<details class="highlights" bind:open>
<details class="highlights" bind:open bind:clientWidth>
<summary>
<slot name="summary">
{$_("search.matchingResults", { values: { n: highlights.length } })}
</slot>
<div class="left">
{#if open}
<ChevronDown12 />
{:else}
<ChevronRight12 />
{/if}
<slot name="summary">
{$_("search.matchingResults", { values: { n: highlights.length } })}
</slot>
</div>
{#if showAll}
<div class="right">
{#if open}
<Button
minW={false}
size="small"
ghost
on:click={collapseAll}
title={$_("search.collapseAll")}
>
<Fold16 height={14} width={14} />
{#if !isSmall}{$_("search.collapseAll")}{/if}
</Button>
{:else}
<Button
minW={false}
size="small"
ghost
on:click={expandAll}
title={$_("search.expandAll")}
>
<Unfold16 height={14} width={14} />
{#if !isSmall}{$_("search.expandAll")}{/if}
</Button>
{/if}
</div>
{/if}
</summary>
<ul>
{#each highlights as [id, highlight]}
Expand All @@ -33,13 +93,30 @@
overflow: hidden;
}
.highlights summary {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
gap: 1rem;
color: var(--gray-4);
fill: var(--gray-4);
padding: 0.5rem 0.75rem;
font-size: var(--font-sm);
font-weight: var(--font-semibold);
}
.highlights summary::marker {
margin-right: 1rem;
.highlights summary .left,
.highlights summary .right {
display: flex;
align-items: center;
gap: 0.5rem;
white-space: no-wrap;
padding: 0 0.5rem 0 0.25rem;
border-radius: 0.5rem;
}
summary .left:hover,
summary .left:focus,
summary:hover .left,
summary:focus .left {
background-color: var(--blue-1);
}
.highlights ul {
list-style-type: none;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@
</svelte:fragment>
</HighlightGroup>
</Story>

<Story name="With All Controls">
<HighlightGroup {highlights} {getHref} showAll>
<svelte:fragment let:id let:highlight>
<Highlight title={id} segments={highlight} />
</svelte:fragment>
</HighlightGroup>
</Story>
25 changes: 21 additions & 4 deletions src/lib/components/documents/NoteHighlights.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,44 @@
-->

<script lang="ts">
import type { Writable } from "svelte/store";
import type { Document } from "$lib/api/types";

import { getContext } from "svelte";
import { _ } from "svelte-i18n";

import type { Document } from "$lib/api/types";
import { noteUrl } from "$lib/api/notes";
import Highlight from "../common/Highlight.svelte";
import HighlightGroup from "../common/HighlightGroup.svelte";

import { noteUrl } from "$lib/api/notes";

export let document: Document;
export let open = true;

const { subscribe } =
getContext<Writable<{ allOpen: boolean }>>("highlightState") ?? {};
$: subscribe?.((state) => {
open = state.allOpen;
});

$: highlights = Object.entries(document.note_highlights ?? {});
$: notes = new Map(document.notes?.map((n) => [n.id, n]));

function noteHref(id: string): string {
const note = notes.get(id);
if (!note) return "";
return noteUrl(document, note).toString();
return noteUrl(document, note).href;
}
</script>

<HighlightGroup {highlights} getHref={noteHref} bind:open>
<HighlightGroup
{highlights}
getHref={noteHref}
bind:open
showAll={Boolean(subscribe)}
on:collapseAll
on:expandAll
>
<svelte:fragment slot="summary">
{$_("documents.matchingNotes", { values: { n: highlights.length } })}
</svelte:fragment>
Expand Down
19 changes: 17 additions & 2 deletions src/lib/components/documents/PageHighlights.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
```
-->
<script lang="ts">
import type { Writable } from "svelte/store";
import type { Document } from "$lib/api/types";

import { getContext } from "svelte";
import { _ } from "svelte-i18n";

import Highlight from "../common/Highlight.svelte";
Expand All @@ -24,21 +26,34 @@
export let document: Document;
export let open = true;

const { subscribe } =
getContext<Writable<{ allOpen: boolean }>>("highlightState") ?? {};
$: subscribe?.((state) => {
open = state.allOpen;
});
$: highlights = Object.entries(document.highlights ?? {});

function pageHref(id: string): string {
const pageNo = pageNumber(id);
return pageUrl(document, pageNo).toString();
return pageUrl(document, pageNo).href;
}
</script>

<HighlightGroup {highlights} getHref={pageHref} bind:open>
<HighlightGroup
{highlights}
getHref={pageHref}
bind:open
showAll={Boolean(subscribe)}
on:collapseAll
on:expandAll
>
<svelte:fragment slot="summary">
{$_("documents.matchingPages", { values: { n: highlights.length } })}
</svelte:fragment>
<svelte:fragment let:id let:highlight>
<Highlight
title="{$_('documents.pageAbbrev')} {pageNumber(id)}"
inlineTitle
segments={highlight}
/>
</svelte:fragment>
Expand Down
Loading
Loading