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

#4636 - Ability to collapse groups in annotation sidebar #4637

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,20 @@
import { compareSpanText, groupBy, renderLabel, uniqueLabels } from "./Utils";
import { sortByScore, recommendationsFirst } from "./AnnotationBrowserState"

export let ajaxClient: DiamAjax;
export let data: AnnotatedText;
export let pinnedGroups: string[];
export let ajaxClient: DiamAjax
export let data: AnnotatedText
export let pinnedGroups: string[]

let groupedAnnotations: Record<string, Annotation[]>;
let sortedLabels: string[];
let groupedAnnotations: Record<string, Annotation[]>
let groups: { label: string, collapsed: boolean }[]
let collapsedGroups = new Set<string>()

$: {
sortedLabels = [...pinnedGroups, ...uniqueLabels(data).filter(v => !pinnedGroups.includes(v))]
const sortedLabels = [...pinnedGroups, ...uniqueLabels(data).filter(v => !pinnedGroups.includes(v))]

groups = sortedLabels.map(label => {
return { label: label, collapsed: collapsedGroups.has(label) };
});

const relations = data?.relations.values() || []
const spans = data?.spans.values() || []
Expand Down Expand Up @@ -97,9 +102,32 @@
function mouseOverAnnotation(event: MouseEvent, annotation: Annotation) {
event.target.dispatchEvent(new AnnotationOverEvent(annotation, event))
}

function mouseOutAnnotation(event: MouseEvent, annotation: Annotation) {
event.target.dispatchEvent(new AnnotationOutEvent(annotation, event))
}

function toggleCollapsed(group) {
if (!collapsedGroups.has(group.label)) {
collapsedGroups.add(group.label)
}
else {
collapsedGroups.delete(group.label)
}
data = data // Trigger reactive update
}

function collapseAll() {
for (const group of groups) {
collapsedGroups.add(group.label)
}
data = data // Trigger reactive update
}

function expandAll() {
collapsedGroups.clear()
data = data // Trigger reactive update
}
</script>

{#if !data}
Expand All @@ -111,7 +139,7 @@
</div>
</div>
{:else}
<div class="d-flex flex-column">
<div class="d-flex flex-row flex-wrap">
<div class="form-check form-switch mx-2">
<input
class="form-check-input"
Expand All @@ -133,23 +161,36 @@
bind:checked={$recommendationsFirst}
/>
<label class="form-check-label" for="recommendationsFirst"
>Recommendations first</label
>Suggestions first</label
>
</div>
</div>
<div class="d-flex flex-row flex-wrap">
<button class="btn btn-outline-secondary btn-sm p-0 m-1" style="width: 2em;" on:click={expandAll}>
<i class="fas fa-caret-down"/>
</button>
<button class="btn btn-outline-secondary btn-sm p-0 m-1" style="width: 2em;" on:click={collapseAll}>
<i class="fas fa-caret-down group-collapsed"/>
</button>
</div>
<div class="flex-content fit-child-snug">
{#if sortedLabels || sortedLabels?.length}
{#if groups || groups?.length}
<ul class="scrolling flex-content list-group list-group-flush">
{#each sortedLabels as label}
{#each groups as group}
<li class="list-group-item py-0 px-0 border-0">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="px-2 py-1 bg-light-subtle fw-bold sticky-top border-top border-bottom"
on:click={() => toggleCollapsed(group)}
>
{label || "No label"}
<button class="btn btn-link p-0" style="color: var(--bs-body-color)">
<i class="fas fa-caret-down d-inline-block" class:group-collapsed={group.collapsed}/>
</button>
<span>{group.label || "No label"}</span>
</div>
<ul class="px-0 list-group list-group-flush">
{#if groupedAnnotations[label]}
{#each groupedAnnotations[label] as ann}
<ul class="px-0 list-group list-group-flush" class:d-none={group.collapsed}>
{#if groupedAnnotations[group.label]}
{#each groupedAnnotations[group.label] as ann}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<li
class="list-group-item list-group-item-action p-0 d-flex"
Expand Down Expand Up @@ -218,4 +259,8 @@
.list-group-flush > .list-group-item:last-child {
border-bottom-width: 1px;
}

.group-collapsed {
transform: rotate(-90deg);
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,15 @@
export let data: AnnotatedText;

let groupedAnnotations: Record<string, Annotation[]>;
let sortedLayers: Layer[];
let groups: { layer: Layer, collapsed: boolean }[]
let collapsedGroups = new Set<number>()

$: {
sortedLayers = uniqueLayers(data)
const sortedLayers = uniqueLayers(data)

groups = sortedLayers.map(layer => {
return { layer: layer, collapsed: collapsedGroups.has(layer.id) };
});

const relations = data?.relations.values() || []
const spans = data?.spans.values() || []
Expand Down Expand Up @@ -97,9 +102,32 @@
function mouseOverAnnotation(event: MouseEvent, annotation: Annotation) {
event.target.dispatchEvent(new AnnotationOverEvent(annotation, event))
}

function mouseOutAnnotation(event: MouseEvent, annotation: Annotation) {
event.target.dispatchEvent(new AnnotationOutEvent(annotation, event))
}

function toggleCollapsed(group) {
if (!collapsedGroups.has(group.layer.id)) {
collapsedGroups.add(group.layer.id)
}
else {
collapsedGroups.delete(group.layer.id)
}
data = data // Trigger reactive update
}

function collapseAll() {
for (const group of groups) {
collapsedGroups.add(group.layer.id)
}
data = data // Trigger reactive update
}

function expandAll() {
collapsedGroups.clear()
data = data // Trigger reactive update
}
</script>

{#if !data}
Expand All @@ -111,7 +139,7 @@
</div>
</div>
{:else}
<div class="d-flex flex-column">
<div class="d-flex flex-row flex-wrap">
<div class="form-check form-switch mx-2">
<input
class="form-check-input"
Expand All @@ -133,23 +161,36 @@
bind:checked={$recommendationsFirst}
/>
<label class="form-check-label" for="recommendationsFirst"
>Recommendations first</label
>Suggestions first</label
>
</div>
</div>
<div class="d-flex flex-row flex-wrap">
<button class="btn btn-outline-secondary btn-sm p-0 m-1" style="width: 2em;" on:click={expandAll}>
<i class="fas fa-caret-down"/>
</button>
<button class="btn btn-outline-secondary btn-sm p-0 m-1" style="width: 2em;" on:click={collapseAll}>
<i class="fas fa-caret-down group-collapsed"/>
</button>
</div>
<div class="flex-content fit-child-snug">
{#if sortedLayers || sortedLayers?.length}
{#if groups || groups?.length}
<ul class="scrolling flex-content list-group list-group-flush">
{#each sortedLayers as layer}
{#each groups as group}
<li class="list-group-item py-0 px-0 border-0">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="px-2 py-1 bg-light-subtle fw-bold sticky-top border-top border-bottom"
on:click={() => toggleCollapsed(group)}
>
{layer.name}
<button class="btn btn-link p-0" style="color: var(--bs-body-color)">
<i class="fas fa-caret-down d-inline-block" class:group-collapsed={group.collapsed}/>
</button>
<span>{group.layer.name}</span>
</div>
<ul class="px-0 list-group list-group-flush">
{#if groupedAnnotations[layer.name]}
{#each groupedAnnotations[layer.name] as ann}
<ul class="px-0 list-group list-group-flush" class:d-none={group.collapsed}>
{#if groupedAnnotations[group.layer.name]}
{#each groupedAnnotations[group.layer.name] as ann}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<li
class="list-group-item list-group-item-action p-0 d-flex"
Expand Down Expand Up @@ -218,4 +259,8 @@
.list-group-flush > .list-group-item:last-child {
border-bottom-width: 1px;
}

.group-collapsed {
transform: rotate(-90deg);
}
</style>