Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
15 changes: 9 additions & 6 deletions .claude/skills/translate/references/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,17 @@ lane header.
A patron pins libraries to a personal list on this device. The list's name is a proper noun in
the UI and keeps each locale's established form when it appears inside a sentence.

| English | German | French | Spanish | Italian |
| ------------ | ------------------ | ----------------- | --------------- | ------------------ |
| My Libraries | Meine Bibliotheken | Mes bibliothèques | Mis bibliotecas | Le mie biblioteche |
| pin | anheften | épingler | anclar | fissare |
| unpin | lösen | désépingler | desanclar | rimuovere |
| English | German | French | Spanish | Italian |
| ------------ | ---------------------- | ----------------- | --------------- | ------------------ |
| My Libraries | Meine Bibliotheken | Mes bibliothèques | Mis bibliotecas | Le mie biblioteche |
| pin | anheften | épingler | anclar | fissare |
| unpin | lösen | désépingler | desanclar | rimuovere |
| reorder | neu anordnen | réorganiser | reordenar | riordinare |
| move up | nach oben verschieben | monter | subir | spostare in alto |
| move down | nach unten verschieben | descendre | bajar | spostare in basso |

`pin` and `unpin` are the verbs for My Libraries. They are unrelated to the `PIN` credential
field, which stays `PIN`.
field, which stays `PIN`. `reorder` changes the order of My Libraries.

- **In running text** German declines the list name (`von Meinen Bibliotheken`), and Italian
merges the article into the preposition (`nelle mie biblioteche`, `dalle mie biblioteche`).
Expand Down
56 changes: 56 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
"@bugsnag/js": "^7.22.4",
"@bugsnag/plugin-react": "^7.19.0",
"@csstools/normalize.css": "^10.1.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.14.0",
"@formatjs/intl-durationformat": "^0.10.18",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/de/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@
"pinButton.pinned": "{{title}} wurde an Meine Bibliotheken angeheftet.",
"pinButton.unpin.ariaLabel": "{{title}} von Meinen Bibliotheken lösen",
"pinButton.unpinned": "{{title}} wurde von Meinen Bibliotheken gelöst.",
"pinnedLibraryList.done": "Fertig",
"pinnedLibraryList.doneLabel": "Fertig mit dem Neuanordnen von Meinen Bibliotheken",
"pinnedLibraryList.moved": "{{title}} ist jetzt an Position {{position}} von {{total}}.",
"pinnedLibraryList.moveDown": "{{title}} nach unten verschieben",
"pinnedLibraryList.moveUp": "{{title}} nach oben verschieben",
"pinnedLibraryList.reorder": "Neu anordnen",
"pinnedLibraryList.reorderLabel": "Meine Bibliotheken neu anordnen",
"pinnedLibraryList.resetWarning": "Warnung zu gemeinsam genutzten Computern zurücksetzen",
"previewButton.couldNotOpenPreview": "Fehler: Die Vorschau konnte nicht geöffnet werden.",
"previewButton.preview": "Vorschau",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@
"pinButton.pinned": "{{title}} pinned to My Libraries.",
"pinButton.unpin.ariaLabel": "Unpin {{title}} from My Libraries",
"pinButton.unpinned": "{{title}} unpinned from My Libraries.",
"pinnedLibraryList.done": "Done",
"pinnedLibraryList.doneLabel": "Done reordering My Libraries",
"pinnedLibraryList.moved": "{{title}} moved to position {{position}} of {{total}}.",
"pinnedLibraryList.moveDown": "Move {{title}} down",
"pinnedLibraryList.moveUp": "Move {{title}} up",
"pinnedLibraryList.reorder": "Reorder",
"pinnedLibraryList.reorderLabel": "Reorder My Libraries",
"pinnedLibraryList.resetWarning": "Reset the shared computer warning",
"previewButton.couldNotOpenPreview": "Error: Could not open preview.",
"previewButton.preview": "Preview",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/es/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@
"pinButton.pinned": "Biblioteca anclada a Mis bibliotecas: {{title}}.",
"pinButton.unpin.ariaLabel": "Desanclar {{title}} de Mis bibliotecas",
"pinButton.unpinned": "Biblioteca desanclada de Mis bibliotecas: {{title}}.",
"pinnedLibraryList.done": "Listo",
"pinnedLibraryList.doneLabel": "Listo: reordenación de Mis bibliotecas",
"pinnedLibraryList.moved": "Nueva posición de {{title}}: {{position}} de {{total}}.",
"pinnedLibraryList.moveDown": "Bajar {{title}}",
"pinnedLibraryList.moveUp": "Subir {{title}}",
"pinnedLibraryList.reorder": "Reordenar",
"pinnedLibraryList.reorderLabel": "Reordenar Mis bibliotecas",
"pinnedLibraryList.resetWarning": "Restablecer el aviso sobre computadoras compartidas",
"previewButton.couldNotOpenPreview": "Error: no se ha podido abrir la vista previa.",
"previewButton.preview": "Vista previa",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/fr/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@
"pinButton.pinned": "Bibliothèque épinglée dans Mes bibliothèques : {{title}}.",
"pinButton.unpin.ariaLabel": "Désépingler {{title}} de Mes bibliothèques",
"pinButton.unpinned": "Bibliothèque désépinglée de Mes bibliothèques : {{title}}.",
"pinnedLibraryList.done": "Terminé",
"pinnedLibraryList.doneLabel": "Terminé : réorganisation de Mes bibliothèques",
"pinnedLibraryList.moved": "Nouvelle position de {{title}} : {{position}} sur {{total}}.",
"pinnedLibraryList.moveDown": "Descendre {{title}}",
"pinnedLibraryList.moveUp": "Monter {{title}}",
"pinnedLibraryList.reorder": "Réorganiser",
"pinnedLibraryList.reorderLabel": "Réorganiser Mes bibliothèques",
"pinnedLibraryList.resetWarning": "Réinitialiser l'avertissement sur les ordinateurs partagés",
"previewButton.couldNotOpenPreview": "Erreur : impossible d'ouvrir l'aperçu.",
"previewButton.preview": "Aperçu",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/it/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@
"pinButton.pinned": "Biblioteca fissata nelle mie biblioteche: {{title}}.",
"pinButton.unpin.ariaLabel": "Rimuovi {{title}} dalle mie biblioteche",
"pinButton.unpinned": "Biblioteca rimossa dalle mie biblioteche: {{title}}.",
"pinnedLibraryList.done": "Fine",
"pinnedLibraryList.doneLabel": "Fine del riordino delle mie biblioteche",
"pinnedLibraryList.moved": "Nuova posizione di {{title}}: {{position}} di {{total}}.",
"pinnedLibraryList.moveDown": "Sposta {{title}} in basso",
"pinnedLibraryList.moveUp": "Sposta {{title}} in alto",
"pinnedLibraryList.reorder": "Riordina",
"pinnedLibraryList.reorderLabel": "Riordina le mie biblioteche",
"pinnedLibraryList.resetWarning": "Reimposta l'avviso sui computer condivisi",
"previewButton.couldNotOpenPreview": "Errore: impossibile aprire l'anteprima.",
"previewButton.preview": "Anteprima",
Expand Down
5 changes: 4 additions & 1 deletion src/components/LibraryCardList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import * as React from "react";

/** The widest a list of library cards gets. */
export const LIBRARY_CARD_LIST_MAX_WIDTH = "40rem";

type LibraryCardListProps = React.HTMLAttributes<HTMLUListElement>;

/** A vertical list of library cards. Each child should be an <li>. */
Expand All @@ -16,7 +19,7 @@ const LibraryCardList: React.FC<LibraryCardListProps> = ({
sx={{
listStyle: "none",
pl: 0,
maxWidth: "40rem",
maxWidth: LIBRARY_CARD_LIST_MAX_WIDTH,
display: "flex",
flexDirection: "column",
gap: 2
Expand Down
116 changes: 73 additions & 43 deletions src/components/MultiLibraryHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ const MultiLibraryHome: React.FC = () => {
);
const shownPinned = useShownPinnedLibraries(data?.libraries ?? []);
const hasPinned = shownPinned.length > 0;
const [reordering, setReordering] = React.useState(false);
// Leaves reorder mode once fewer than two libraries are shown. Otherwise
// the search would stay hidden with no Done button, and My Libraries would
// come back already in that mode.
if (reordering && shownPinned.length < 2) setReordering(false);

if (error)
return (
Expand Down Expand Up @@ -57,31 +62,47 @@ const MultiLibraryHome: React.FC = () => {
);
const librariesBySlug = new Map(sorted.map(lib => [lib.slug, lib]));

// `title` replaces the name as the link content, e.g. to mark search matches.
const renderCard = (library: ClientLibrary, title?: React.ReactNode) => {
// `title` replaces the name as the link content, e.g. to mark search
// matches. `reorderControls` replace the pin button, and the link becomes
// plain text, so the card cannot be opened while it is being reordered.
const renderCard = (
library: ClientLibrary,
{
title,
reorderControls
}: { title?: React.ReactNode; reorderControls?: React.ReactNode } = {}
) => {
const name = library.title || library.slug;
return (
<LibraryCard
logoUrl={library.logoUrl}
description={library.description}
disabled={reorderControls !== undefined}
trailing={
<PinButton
library={{
id: library.id,
slug: library.slug,
title: name,
logoUrl: library.logoUrl,
authDocUrl: library.authDocUrl
}}
onToggle={resetSearch}
/>
reorderControls ?? (
<PinButton
library={{
id: library.id,
slug: library.slug,
title: name,
logoUrl: library.logoUrl,
authDocUrl: library.authDocUrl
}}
onToggle={resetSearch}
/>
)
}
>
{actionProps => (
<LibraryHomeLink slug={library.slug} {...actionProps}>
{title ?? name}
</LibraryHomeLink>
)}
{actionProps =>
reorderControls === undefined ? (
<LibraryHomeLink slug={library.slug} {...actionProps}>
{title ?? name}
</LibraryHomeLink>
) : (
// Keeps the title's weight, but not its link color.
<span sx={{ fontWeight: "medium" }}>{name}</span>
)
}
</LibraryCard>
);
};
Expand All @@ -102,34 +123,43 @@ const MultiLibraryHome: React.FC = () => {
<PinnedLibraryList
libraries={data.libraries}
pinned={shownPinned}
renderItem={library => renderCard(library)}
emptyFocusRef={searchInputRef}
/>
<LibraryFilterList
// A pin or unpin made here starts a fresh search: the box empties
// and, with pins shown, the list hides.
key={searchKey}
inputRef={searchInputRef}
heading={
<h2>
{hasPinned
? t("library.findAnother", "Find another library:", {
ns: "common"
})
: t("multiLibraryHome.choose", "Choose a library:")}
</h2>
renderItem={(library, reorderControls) =>
renderCard(library, { reorderControls })
}
hideUntilFiltered={hasPinned}
items={sorted.map(lib => ({
slug: lib.slug,
label: lib.title || lib.slug
}))}
resultsListId="library-filter-results"
renderItem={({ slug, highlighted }) => {
const library = librariesBySlug.get(slug);
return library ? renderCard(library, highlighted) : null;
}}
reordering={reordering}
onReorderingChange={setReordering}
emptyFocusRef={searchInputRef}
/>
{/* Hidden while reordering, so the page shows only My Libraries. */}
{!reordering && (
<LibraryFilterList
// A pin or unpin made here starts a fresh search: the box empties
// and, with pins shown, the list hides.
key={searchKey}
inputRef={searchInputRef}
heading={
<h2>
{hasPinned
? t("library.findAnother", "Find another library:", {
ns: "common"
})
: t("multiLibraryHome.choose", "Choose a library:")}
</h2>
}
hideUntilFiltered={hasPinned}
items={sorted.map(lib => ({
slug: lib.slug,
label: lib.title || lib.slug
}))}
resultsListId="library-filter-results"
renderItem={({ slug, highlighted }) => {
const library = librariesBySlug.get(slug);
return library
? renderCard(library, { title: highlighted })
: null;
}}
/>
)}
</Themed.root>
</ThemeUIProvider>
);
Expand Down
3 changes: 2 additions & 1 deletion src/components/PinButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
clearStoredCredentials
} from "auth/useCredentials";
import { useTranslation } from "next-i18next/pages";
import { MIN_TOUCH_TARGET_PX } from "constants/a11y";

export type { PinnableLibrary };

Expand Down Expand Up @@ -144,7 +145,7 @@ const PinButton: React.FC<PinButtonProps> = ({
onClick={handleClick}
aria-label={label}
title={label}
sx={{ minWidth: 44, minHeight: 44 }}
sx={{ minWidth: MIN_TOUCH_TARGET_PX, minHeight: MIN_TOUCH_TARGET_PX }}
>
<FontAwesomeIcon
icon={faThumbtack}
Expand Down
Loading
Loading