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

LWS-225: improve display of libraries in holdings panel #1093

Merged
merged 2 commits into from
Aug 23, 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
24 changes: 24 additions & 0 deletions lxl-web/src/lib/assets/json/display-web.json
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,30 @@
"fresnel:contentFirst": ""
}
},
"Collection-qualifier-format": {
"@id": "Collection-qualifier-format",
"@type": "fresnel:Format",
"fresnel:classFormatDomain": ["Collection"],
"fresnel:propertyFormatDomain": ["qualifier"],
"fresnel:propertyFormat": {
"fresnel:contentBefore": ", ",
"fresnel:contentFirst": "",
"fresnel:contentAfter": "",
"fresnel:contentLast": ""
}
},
"Collection-sigel-format": {
"@id": "Collection-sigel-format",
"@type": "fresnel:Format",
"fresnel:classFormatDomain": ["Collection"],
"fresnel:propertyFormatDomain": ["sigel"],
"fresnel:propertyFormat": {
"fresnel:contentBefore": " (",
"fresnel:contentFirst": "(",
"fresnel:contentAfter": ")",
"fresnel:contentLast": ")"
}
},
"default-separators": {
"@id": "default-separators",
"@type": "fresnel:Format",
Expand Down
6 changes: 4 additions & 2 deletions lxl-web/src/lib/components/find/FacetGroup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { type FacetGroup } from '$lib/types/search';
import BiChevronRight from '~icons/bi/chevron-right';
import FacetRange from './FacetRange.svelte';
import DecoratedData from '../DecoratedData.svelte';
import { ShowLabelsOptions } from '$lib/types/decoratedData';

export let group: FacetGroup;
export let locale: LocaleCode;
Expand Down Expand Up @@ -74,8 +76,8 @@
>
<span class="mr-1" aria-hidden="true">{facet.selected ? '☑' : '☐'}</span>
{/if}
<span
>{facet.str}
<span>
<DecoratedData data={facet.object} showLabels={ShowLabelsOptions.Never} />
<span class="ml-0.5 text-xs text-primary/40">{facet.discriminator}</span>
</span>
</span>
Expand Down
24 changes: 23 additions & 1 deletion lxl-web/src/lib/types/holdings.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
export type bibIdObj = {
import type { DisplayDecorated } from './xl';

export type BibIdObj = {
bibId: string;
'@type': string;
holders: string[];
onr: string | null;
isbn: string[];
issn: string[];
};

export type HoldingsByInstanceId = {
[id: string]: {
'@id': string;
'@type': string;
heldBy: DecoratedHolder;
itemOf: {
'@id': string;
};
}[];
};

export type HoldersByType = {
[type: string]: DecoratedHolder[];
};

export type DecoratedHolder = {
obj: DisplayDecorated;
sigel: string;
};
43 changes: 38 additions & 5 deletions lxl-web/src/lib/utils/holdings.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { pushState } from '$app/navigation';
import isFnurgel from '$lib/utils/isFnurgel';
import { relativizeUrl } from '$lib/utils/http';
import type { FramedData } from '$lib/types/xl';
import type { bibIdObj } from '$lib/types/holdings';
import { LensType, type FramedData } from '$lib/types/xl';
import type { BibIdObj, HoldersByType, HoldingsByInstanceId } from '$lib/types/holdings';
import { DisplayUtil } from '$lib/utils/xl.js';
import type { LocaleCode } from '$lib/i18n/locales';

export function getHoldingsLink(url: URL, value: string) {
const newSearchParams = new URLSearchParams([...Array.from(url.searchParams.entries())]);
Expand Down Expand Up @@ -41,20 +43,32 @@ function sortHoldings(holdings) {
});
}

export function getHoldingsByInstanceId(mainEntity) {
export function getHoldingsByInstanceId(
mainEntity,
displayUtil: DisplayUtil,
locale: LocaleCode
): HoldingsByInstanceId {
return mainEntity['@reverse']?.instanceOf?.reduce((acc, instanceOfItem) => {
const id = relativizeUrl(instanceOfItem['@id'])?.replace('#it', '');
if (!id) {
return acc;
}
return {
...acc,
[id]: sortHoldings(instanceOfItem?.['@reverse']?.itemOf || [])
[id]: sortHoldings(instanceOfItem?.['@reverse']?.itemOf || []).map((holding) => {
return {
...holding,
heldBy: {
obj: displayUtil.lensAndFormat(holding.heldBy, LensType.Chip, locale),
sigel: holding.heldBy?.sigel
}
};
})
};
}, {});
}

export function getBibIdsByInstanceId(mainEntity, record): Record<string, bibIdObj> {
export function getBibIdsByInstanceId(mainEntity, record): Record<string, BibIdObj> {
return mainEntity['@reverse']?.instanceOf?.reduce((acc, instanceOfItem) => {
const id = relativizeUrl(instanceOfItem['@id'])?.replace('#it', '');

Expand Down Expand Up @@ -118,3 +132,22 @@ export function getHoldingsByType(mainEntity: FramedData) {
}, {});
return sortedHoldingsByType;
}

export function getHoldersByType(
holdingsByType,
displayUtil: DisplayUtil,
locale: LocaleCode
): HoldersByType {
return Object.entries(holdingsByType).reduce((acc, [type, holdings]) => {
const heldBys = holdings.map((holdingItem) => {
return {
obj: displayUtil.lensAndFormat(holdingItem.heldBy, LensType.Chip, locale),
sigel: holdingItem.heldBy.sigel
};
});
const uniqueHeldBys = [
...new Map(heldBys.map((heldByItem) => [heldByItem.obj['@id'], heldByItem])).values()
];
return { ...acc, [type]: uniqueHeldBys };
}, {});
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import getAtPath from '$lib/utils/getAtPath';
import {
getHoldingsByInstanceId,
getHoldingsByType,
getHoldersByType,
getBibIdsByInstanceId
} from '$lib/utils/holdings.js';

Expand Down Expand Up @@ -60,16 +61,10 @@ export const load = async ({ params, url, locals, fetch }) => {
const sortedInstances = getSortedInstances([...instances]);

const images = getImages(mainEntity, locale).map((i) => toSecure(i, env.AUXD_SECRET));
const holdingsByInstanceId = getHoldingsByInstanceId(mainEntity);
const holdingsByInstanceId = getHoldingsByInstanceId(mainEntity, displayUtil, locale);
const bibIdsByInstanceId = getBibIdsByInstanceId(mainEntity, resource);
const holdingsByType = getHoldingsByType(mainEntity);
const holdersByType = Object.entries(holdingsByType).reduce((acc, [type, holdings]) => {
const heldBys = holdings.map((holdingItem) => holdingItem.heldBy);
const uniqueHeldBys = [
...new Map(heldBys.map((heldByItem) => [heldByItem['@id'], heldByItem])).values()
];
return { ...acc, [type]: uniqueHeldBys };
}, {});
const holdersByType = getHoldersByType(holdingsByType, displayUtil, locale);

return {
type: mainEntity[JsonLd.TYPE],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
<article>
<div class="resource gap-8 find-layout page-padding" class:bg-header={shouldShowHeaderBackground}>
<div
class="w-full mb-2 mt-4 flex justify-center self-center object-center md:mx-auto md:justify-start md:self-start md:px-2 xl:px-0"
class="mb-2 mt-4 flex w-full justify-center self-center object-center md:mx-auto md:justify-start md:self-start md:px-2 xl:px-0"
class:hidden={!$page.data.images?.length}
>
{#if data.images.length}
Expand Down Expand Up @@ -202,34 +202,30 @@
<div>
<h2 class="font-bold">
{data.t('holdings.availableAt')}
{#if isFnurgel(latestHoldingUrl)}
{#if latestHoldingUrl && isFnurgel(latestHoldingUrl)}
{data.holdingsByInstanceId[latestHoldingUrl].length}
{data.holdingsByInstanceId[latestHoldingUrl].length === 1
? data.t('holdings.library')
: data.t('holdings.libraries')}
{:else if data.holdersByType?.[latestHoldingUrl]}
{:else if latestHoldingUrl && data.holdersByType?.[latestHoldingUrl]}
{data.holdersByType[latestHoldingUrl].length}
{data.holdersByType[latestHoldingUrl].length === 1
? data.t('holdings.library')
: data.t('holdings.libraries')}
{/if}
</h2>
<ul class="w-full text-sm">
{#if isFnurgel(latestHoldingUrl)}
{#if latestHoldingUrl && isFnurgel(latestHoldingUrl)}
<!-- holdings list by instance -->
{#if data.holdingsByInstanceId[selectedHolding]}
{#if selectedHolding && data.holdingsByInstanceId[selectedHolding]}
{#each data.holdingsByInstanceId[selectedHolding] as holdingItem}
<HoldingStatus sigel={holdingItem?.heldBy?.sigel} {holdingUrl}>
<span slot="name" class="flex-1">{holdingItem?.heldBy?.name}</span>
</HoldingStatus>
<HoldingStatus holder={holdingItem?.heldBy} {holdingUrl} />
{/each}
{/if}
<!-- holdings list by type -->
{:else if data.holdersByType?.[latestHoldingUrl]}
{:else if latestHoldingUrl && data.holdersByType?.[latestHoldingUrl]}
{#each data.holdersByType[latestHoldingUrl] as holderItem}
<HoldingStatus sigel={holderItem?.sigel} {holdingUrl}>
<span slot="name" class="flex-1">{holderItem?.name}</span>
</HoldingStatus>
<HoldingStatus holder={holderItem} {holdingUrl} />
{/each}
{/if}
</ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<script lang="ts">
import { type HoldingStatus } from '$lib/types/api';
import { type bibIdObj } from '$lib/types/holdings';
import type { BibIdObj, DecoratedHolder } from '$lib/types/holdings';
import { ShowLabelsOptions } from '$lib/types/decoratedData';
import { page } from '$app/stores';
export let sigel: string;
export let holdingUrl: string;
import DecoratedData from '$lib/components/DecoratedData.svelte';
import BiChevronRight from '~icons/bi/chevron-right';

export let holder: DecoratedHolder;
export let holdingUrl: string;

const sigel = holder?.sigel;
let loading = false;
let statusData: HoldingStatus[] | undefined;
let error: string | undefined;
Expand All @@ -19,7 +23,7 @@
.filter((i) => $page.data.bibIdsByInstanceId[i].holders.includes(sigel))
.map((i) => $page.data.bibIdsByInstanceId[i]);

async function fetchHoldingStatus(ids: bibIdObj[]) {
async function fetchHoldingStatus(ids: BibIdObj[]) {
const promises = ids.map((id) => {
if (id) {
const searchParams = new URLSearchParams();
Expand Down Expand Up @@ -101,12 +105,13 @@

<li class="border-b-primary/16 [&:not(:last-child)]:border-b">
<details on:toggle={getHoldingStatus}>
<summary class="flex h-11 items-center">
<span class="arrow mr-2">
<summary class="my-3 flex items-baseline">
<span class="arrow mr-2 h-3">
<BiChevronRight />
</span>
<slot name="name" />
<span class="text-secondary">{sigel ? `(${sigel})` : ''}</span>
<span class="holder-label">
<DecoratedData data={holder.obj} showLabels={ShowLabelsOptions['Never']} />
</span>
</summary>
<div class="mb-4 flex flex-col gap-2">
{#if loading}
Expand Down Expand Up @@ -182,8 +187,18 @@
</li>

<style lang="postcss">
details[open] .arrow {
@apply rotate-90;
details[open] {
& .arrow {
@apply rotate-90;
}

& .holder-label {
@apply whitespace-normal;
}
}

.holder-label {
@apply flex-1 overflow-hidden text-ellipsis whitespace-nowrap;
}

.arrow {
Expand Down
Loading