From 0a5f12f38c3c2a0322cddb8950645aac4a29a3e3 Mon Sep 17 00:00:00 2001 From: Muppetteer Date: Wed, 17 Dec 2025 15:28:26 +1100 Subject: [PATCH] fix: Bookdrop UI mobile support (#1911) * fix: Bookdrop UI mobile support * fix: Wrap long select option items in mobile mode --- ...okdrop-file-metadata-picker.component.html | 150 +++-- ...okdrop-file-metadata-picker.component.scss | 123 +++- ...bookdrop-file-metadata-picker.component.ts | 26 +- .../bookdrop-file-review.component.html | 552 ++++++++++-------- .../bookdrop-file-review.component.scss | 174 ++++-- .../bookdrop-file-review.component.ts | 3 +- 6 files changed, 666 insertions(+), 362 deletions(-) diff --git a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component.html b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component.html index dc5bb36b..f5cb409e 100644 --- a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component.html +++ b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component.html @@ -2,9 +2,9 @@
- +
-

Current Metadata

+

File Metadata

@@ -22,32 +22,31 @@ icon="pi pi-angle-double-left" class="mx-2" [outlined]="true" - pTooltip="Move all fields" + pTooltip="Overwrite all fields with fetched metadata" tooltipPosition="bottom" (onClick)="copyAll()" >
-

Fetched Metadata

+

Fetched

-
- +
+
- + - +
@for (field of metadataFieldsTop; track field) { -
- -
+
+ +
- +
} @for (field of metadataChips; track field) { -
- -
+
+ +
+ class="src w-full" + [ngClass]="{ + 'notneeded': !fetchedMetadata[field.fetchedKey], + }"/>
} @for (field of metadataDescription; track field) { -
- -
+
+ +
+ class="src md:!w-1/2" + disabled + [ngClass]="{ + 'notneeded': !fetchedMetadata[field.fetchedKey], + }">
} @for (field of metadataFieldsBottom; track field) { -
- -
- + +
+ @@ -189,13 +210,22 @@ [ngClass]=" { 'green-outlined-button': isValueCopied(field.controlName) && !hoveredFields[field.controlName], - 'red-outlined-button': isValueCopied(field.controlName) && hoveredFields[field.controlName] + 'red-outlined-button': isValueCopied(field.controlName) && hoveredFields[field.controlName], + 'notneeded' : !fetchedMetadata[field.fetchedKey] }" class="arrow-button" (click)="hoveredFields[field.controlName] && isValueCopied(field.controlName) ? resetField(field.controlName) : copyFetchedToCurrent(field.controlName)" (mouseenter)="onMouseEnter(field.controlName)" (mouseleave)="onMouseLeave(field.controlName)"/> - +
} @@ -203,40 +233,38 @@
} @else { -
+

- - Unable to fetch new metadata for this file + Unable to fetch metadata for this file

-
-
-
- +
+
+ Book Thumbnail
-
+
@for (field of metadataFieldsTop; track field) { -
- +
+
- +
+
@@ -259,17 +287,17 @@ } @for (field of metadataDescription; track field) { -
- +
+
- +
} @for (field of metadataFieldsBottom; track field) { -
- +
+
-
-
-
-

- Review Bookdrop Files - - - -

-

- These files were uploaded to the - Bookdrop Folder. - Review their fetched metadata, assign a library and subpath, and finalize where they belong in your collection. -

-
+
+
+
+

+ Review Bookdrop Files + + + +

+

+ These files were uploaded to the + Bookdrop Folder. + Review their fetched metadata, assign a library and subpath, and finalize where they belong in your collection. +

+
-
- - +
+ + +
+
+
+ @if (loading) { + +
+
+ + Loading Bookdrop files. Please wait...
- @if (loading) { + } @else { -
-
+
+ @if (saving) { +
- Loading Bookdrop files. Please wait... +
+ + Organizing and moving files to their designated libraries. Please wait... + +
-
+ } - } @else { - -
- @if (saving) { -
- -
- - Organizing and moving files to their designated libraries. Please wait... - -
+ @if (bookdropFileUis.length !== 0) { +
+
+ + + + +
- } + +
+ - @if (bookdropFileUis.length !== 0) { -
-
- - - + + + + + + + +
+
+ } +
+ +
+ @if (bookdropFileUis.length === 0) { +
+ No bookdrop files to review. +
+ } @else { + @for (file of bookdropFileUis; track file) { + +
+
+ + + [(ngModel)]="file.selected" + (ngModelChange)="toggleFileSelection(file.file.id, $event)"> - -
- -
- - - - + @if (file.metadataForm.get('thumbnailUrl')?.value) { + Cover + } @else { +
?
+ } - - -
-
- } -
+ @if (file.file.fetchedMetadata?.thumbnailUrl) { + Fetched Cover + } @else { +
?
+ } -
- @if (bookdropFileUis.length === 0) { -
- No bookdrop files to review. -
- } @else { - @for (file of bookdropFileUis; track file) { +
+ {{ file.file.fileName }} +
-
-
- - - - - - - - @if (file.metadataForm.get('thumbnailUrl')?.value) { - Cover - } - - @if (file.file.fetchedMetadata?.thumbnailUrl) { - Fetched Cover - } - -
- {{ file.file.fileName }} -
- - + @@ -174,8 +177,9 @@ [options]="libraryOptions" optionLabel="label" optionValue="value" - placeholder="Select Library" + placeholder="Library" class="library-select" + appendTo="body" [(ngModel)]="file.selectedLibraryId" (onChange)="onLibraryChange(file)"> @@ -185,7 +189,7 @@ [options]="file.availablePaths" optionLabel="name" optionValue="id" - placeholder="Select Subpath" + placeholder="Subpath" class="path-select" appendTo="body" [(ngModel)]="file.selectedPathId"> @@ -193,59 +197,71 @@ + [pTooltip]="file.showDetails + ? 'Hide metadata' + : file.file.fetchedMetadata?.title + ? 'Review and copy fetched metadata' + : 'Show file metadata (no fetched metadata)'" + tooltipPosition="left">
- - @if (file.showDetails) { - - - }
- } + + @if (file.showDetails) { + + + } +
+ } + } +
+ } +
+ + @if (!loading) { + + + + + + + + }
diff --git a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss index e7858dea..5af585f1 100644 --- a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss +++ b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.scss @@ -1,7 +1,3 @@ -.container { - overflow-x: auto; -} - .main-card { display: flex; flex-direction: column; @@ -9,22 +5,24 @@ border-radius: 0.75rem; overflow: hidden; background: var(--card-background); - min-width: 60rem; border: 1px solid var(--p-content-border-color); + + @media (max-width: 768px) { + height: calc(100dvh - 4.9rem); + } } .header { - padding: 1.5rem 1rem 1rem; + padding: 1.5rem; display: flex; - flex-direction: column; + flex-direction: row; + align-items: center; + justify-content: space-between; border-bottom: 1px solid var(--p-content-border-color); - margin-bottom: 1.5rem; - - @media (min-width: 768px) { - padding: 1.5rem 1.5rem 1rem; - flex-direction: row; - align-items: center; - justify-content: space-between; + gap: 1rem; + + @media (max-width: 768px) { + padding: 1rem; } } @@ -41,18 +39,24 @@ p { font-size: 0.875rem; color: rgb(156, 163, 175); - + strong { color: var(--primary-color); } } + + @media (max-width: 768px) { + flex-shrink: 1; + + p { + display: none; + } + } } .header-actions { - margin-top: 1rem; - - @media (min-width: 768px) { - margin-top: 0; + @media (max-width: 768px) { + display: flex; } } @@ -62,6 +66,13 @@ font-size: 0.9rem; } +.card-body { + display: flex; + flex: 1; + flex-direction: column; + overflow: auto; +} + .loading-overlay { position: absolute; inset: 0; @@ -82,7 +93,7 @@ } span { - color: rgb(209, 213, 219); + color: var(--text-color-secondary); } p-progressSpinner { @@ -92,7 +103,7 @@ } .controls-section { - padding: 0 1.5rem 1.5rem; + padding: 1rem; margin: 0; border-bottom: 1px solid var(--p-content-border-color); } @@ -135,40 +146,62 @@ justify-content: space-between; align-items: center; gap: 1rem; - padding: 0 0.25rem; + flex-wrap: wrap; + + label { + font-size: 0.875rem; + color: var(--text-color-secondary); + font-weight: 500; + } } .default-controls { display: flex; gap: 1rem; align-items: center; + justify-content: space-between; p-select { - min-width: 8rem; - max-width: 16rem; + width: 8rem; } + + @media (max-width: 768px) { + width: 100%; + + label { + display: none; + } + + p-select { + flex-basis: 50%; + max-width: 250px; + } + } +} + +::ng-deep .p-select-overlay { + @media (max-width: 768px) { + max-width: 70vw; + + .p-select-option { + white-space: normal; + } + } +} + +.default-controls i.pi { + color: var(--p-button-outlined-info-color); } .action-buttons { display: flex; gap: 1rem; align-items: center; - - span { - font-size: 0.875rem; - color: rgb(209, 213, 219); - font-weight: 500; - } } .content-area { flex: 1; - overflow-y: auto; - padding: 1rem; - - > * + * { - margin-top: 0.5rem; - } + padding: 0 0 1px; } .empty-state { @@ -191,9 +224,9 @@ display: flex; align-items: center; gap: 1rem; - border: 1px solid var(--border-color); - border-radius: 0.75rem 0.75rem 0 0; - padding: 0.5rem 1rem; + border-bottom: 1px solid var(--border-color); + padding: 1rem; + flex-wrap: wrap; } .status-indicator { @@ -212,6 +245,17 @@ transform: scale(1.05); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } + + @media (max-width: 768px) { + display: none !important; + } +} + +div.cover-image { + display: block; + background-color: rgba(255, 255, 255, 0.1); + text-align: center; + line-height: 2rem; } .file-name { @@ -220,7 +264,11 @@ font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; - white-space: nowrap; + white-space: wrap; + + @media (max-width: 768px) { + max-width: 100%; + } } .metadata-status { @@ -237,34 +285,58 @@ } } +.file-library { + display: flex; + gap: 1rem; + justify-content: space-between; + align-items: center; + + @media (max-width: 768px) { + width: 100%; + } +} + .library-select, .path-select { - min-width: 8rem; - max-width: 16rem; + width: 8rem; + + @media (max-width: 768px) { + flex-basis: 50%; + max-width: 250px; + } } .details-section { - border-left: 1px solid var(--border-color); - border-right: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); - border-top: none; - border-radius: 0 0 0.625rem 0.625rem; } -.footer { - padding: 0.5rem 1rem; +.footer, .footer-mobile { + padding: 1rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; } +.footer { + @media (max-width: 768px) { + display: none; + } +} + +.footer-mobile { + flex-wrap: wrap; + + @media (min-width: 768px) { + display: none; + } +} + .footer-left, .footer-right { display: flex; gap: 1rem; align-items: center; - min-width: 10rem; } .footer-left { @@ -280,6 +352,10 @@ flex-grow: 1; display: flex; justify-content: center; + + @media (max-width: 768px) { + width: 100%; + } } .spacer { diff --git a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.ts b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.ts index 24945bba..06ccaa05 100644 --- a/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.ts +++ b/booklore-ui/src/app/features/bookdrop/component/bookdrop-file-review/bookdrop-file-review.component.ts @@ -21,7 +21,7 @@ import {AppSettingsService} from '../../../../shared/service/app-settings.servic import {BookMetadata} from '../../../book/model/book.model'; import {UrlHelperService} from '../../../../shared/service/url-helper.service'; import {Checkbox} from 'primeng/checkbox'; -import {NgClass, NgStyle} from '@angular/common'; +import {NgClass} from '@angular/common'; import {Paginator} from 'primeng/paginator'; import {ActivatedRoute} from '@angular/router'; import {BookdropFileMetadataPickerComponent} from '../bookdrop-file-metadata-picker/bookdrop-file-metadata-picker.component'; @@ -53,7 +53,6 @@ export interface BookdropFileUI { Tooltip, Divider, Checkbox, - NgStyle, NgClass, Paginator, ],