mirror of
https://github.com/booklore-app/booklore.git
synced 2025-12-23 22:28:11 -05:00
Merge branch 'develop' into feature/serve-angular-from-spring-drop-nginx
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
<div class="h-[calc(100dvh-6.1rem)] rounded-xl overflow-hidden bg-[var(--card-background)] flex flex-col border-[1px] border-solid border-[var(--p-content-border-color)]">
|
||||
<div class="book-filter-container">
|
||||
|
||||
<div class="flex items-center justify-between px-4 py-4" style="border-bottom:1px solid var(--p-content-border-color)">
|
||||
<span class="text font-semibold">Filters</span>
|
||||
<div class="filter-header">
|
||||
<span class="filter-title">Filters</span>
|
||||
<p-selectButton
|
||||
[options]="filterModeOptions"
|
||||
[(ngModel)]="selectedFilterMode"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
class="text-sm"
|
||||
class="filter-mode-select"
|
||||
allowEmpty="false"
|
||||
styleClass="h-8"
|
||||
pTooltip="Filter selection mode: AND / OR / Single">></p-selectButton>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto pb-4 pr-1" style="overscroll-behavior: contain;">
|
||||
<div class="filter-content">
|
||||
<p-accordion [value]="expandedPanels">
|
||||
@for (filterType of filterTypes; track trackByFilterType(i, filterType); let i = $index) {
|
||||
<p-accordion-panel [value]="i">
|
||||
<p-accordion-header>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<span class="filter-type-label">
|
||||
{{ filterLabels[filterType] || (filterType | titlecase) }}
|
||||
@if (activeFilters[filterType]?.length) {
|
||||
<span class="text-sm text-[var(--primary-color)]">
|
||||
<span class="active-filter-count">
|
||||
({{ activeFilters[filterType].length }})
|
||||
</span>
|
||||
}
|
||||
@@ -30,14 +30,12 @@
|
||||
|
||||
<p-accordion-content>
|
||||
@if (filterStreams[filterType] | async; as filters) {
|
||||
<div
|
||||
class="max-h-[27.5rem] overflow-y-auto overscroll-contain pr-1"
|
||||
style="overscroll-behavior: contain;">
|
||||
<div class="filter-list">
|
||||
@for (filter of filters; track trackByFilter(j, filter); let j = $index) {
|
||||
<div
|
||||
class="filter-row cursor-pointer transition-colors duration-200 ease-in-out pb-1 flex flex-row items-center justify-between gap-2"
|
||||
class="filter-row"
|
||||
[ngClass]="{
|
||||
'text-[var(--primary-color)]': activeFilters[filterType]?.includes(filter.value?.id || filter.value)
|
||||
'active': activeFilters[filterType]?.includes(filter.value?.id || filter.value)
|
||||
}"
|
||||
(click)="handleFilterClick(filterType, filter.value?.id || filter.value)">
|
||||
{{ filter.value.name || filter.value }}
|
||||
@@ -45,7 +43,7 @@
|
||||
</div>
|
||||
}
|
||||
@if (truncatedFilters[filterType]) {
|
||||
<div class="text-xs text-gray-500 text-center pt-2 border-t border-gray-200">
|
||||
<div class="truncation-notice">
|
||||
Showing first 250 items
|
||||
</div>
|
||||
}
|
||||
@@ -56,7 +54,7 @@
|
||||
}
|
||||
</p-accordion>
|
||||
|
||||
<div class="text-xs text-gray-500 text-center pt-4 px-4 mt-2">
|
||||
<div class="footer-notice">
|
||||
Note: Top 500 items are displayed per filter category
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,91 @@
|
||||
--p-accordion-header-padding: 0.6rem 1rem;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
align-items: flex-start;
|
||||
:host ::ng-deep .p-togglebutton {
|
||||
--p-togglebutton-content-padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.book-filter-container {
|
||||
height: calc(100dvh - 6.1rem);
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
background: var(--card-background);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--p-content-border-color);
|
||||
}
|
||||
|
||||
.filter-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--p-content-border-color);
|
||||
}
|
||||
|
||||
.filter-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.filter-mode-select {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 1rem;
|
||||
padding-right: 0.25rem;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.filter-type-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.active-filter-count {
|
||||
font-size: 0.875rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.filter-list {
|
||||
max-height: 27.5rem;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
cursor: pointer;
|
||||
transition: colors 200ms ease-in-out;
|
||||
padding-bottom: 0.25rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
|
||||
&.active {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.truncation-notice {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.footer-notice {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
padding-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
|
||||
<div class="scroller-fields">
|
||||
<div class="scroller-field">
|
||||
<div class="scroller-field" [class.expanded]="scroller.type !== 'magicShelf'">
|
||||
<label>Type</label>
|
||||
<p-select
|
||||
fluid
|
||||
@@ -41,49 +41,51 @@
|
||||
</p-select>
|
||||
</div>
|
||||
|
||||
<div class="scroller-field" [style.visibility]="scroller.type === 'magicShelf' ? 'visible' : 'hidden'">
|
||||
<label>Magic Shelf</label>
|
||||
<p-select
|
||||
fluid
|
||||
[options]="(magicShelves$ | async)!"
|
||||
[(ngModel)]="scroller.magicShelfId"
|
||||
placeholder="Select magic shelf"
|
||||
appendTo="body">
|
||||
</p-select>
|
||||
</div>
|
||||
@if (scroller.type === 'magicShelf') {
|
||||
<div class="scroller-field">
|
||||
<label>Magic Shelf</label>
|
||||
<p-select
|
||||
fluid
|
||||
[options]="(magicShelves$ | async)!"
|
||||
[(ngModel)]="scroller.magicShelfId"
|
||||
placeholder="Select magic shelf"
|
||||
appendTo="body">
|
||||
</p-select>
|
||||
</div>
|
||||
|
||||
<div class="scroller-field" [style.visibility]="scroller.type === 'magicShelf' ? 'visible' : 'hidden'">
|
||||
<label>Sort Field</label>
|
||||
<p-select
|
||||
fluid
|
||||
[options]="sortFieldOptions"
|
||||
[(ngModel)]="scroller.sortField"
|
||||
placeholder="Select field"
|
||||
appendTo="body">
|
||||
</p-select>
|
||||
</div>
|
||||
<div class="scroller-field">
|
||||
<label>Sort Field</label>
|
||||
<p-select
|
||||
fluid
|
||||
[options]="sortFieldOptions"
|
||||
[(ngModel)]="scroller.sortField"
|
||||
placeholder="Select field"
|
||||
appendTo="body">
|
||||
</p-select>
|
||||
</div>
|
||||
|
||||
<div class="scroller-field" [style.visibility]="scroller.type === 'magicShelf' ? 'visible' : 'hidden'">
|
||||
<label>Direction</label>
|
||||
<p-select
|
||||
fluid
|
||||
[options]="sortDirectionOptions"
|
||||
[(ngModel)]="scroller.sortDirection"
|
||||
placeholder="Select direction"
|
||||
appendTo="body">
|
||||
</p-select>
|
||||
</div>
|
||||
<div class="scroller-field">
|
||||
<label>Direction</label>
|
||||
<p-select
|
||||
fluid
|
||||
[options]="sortDirectionOptions"
|
||||
[(ngModel)]="scroller.sortDirection"
|
||||
placeholder="Select direction"
|
||||
appendTo="body">
|
||||
</p-select>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="scroller-field">
|
||||
<label>Max Items</label>
|
||||
<p-inputNumber
|
||||
fluid
|
||||
[(ngModel)]="scroller.maxItems"
|
||||
[min]="MIN_ITEMS"
|
||||
[max]="MAX_ITEMS"
|
||||
[showButtons]="true">
|
||||
</p-inputNumber>
|
||||
</div>
|
||||
<div class="scroller-field max-items-field">
|
||||
<label>Max Items</label>
|
||||
<p-inputNumber
|
||||
fluid
|
||||
[(ngModel)]="scroller.maxItems"
|
||||
[min]="MIN_ITEMS"
|
||||
[max]="MAX_ITEMS"
|
||||
[showButtons]="true">
|
||||
</p-inputNumber>
|
||||
</div>
|
||||
|
||||
<div class="scroller-controls-right">
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
.scroller-fields {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(120px, 1fr) minmax(150px, 1.5fr) minmax(120px, 1fr) minmax(100px, 1fr) 90px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 0.75rem;
|
||||
flex: 1;
|
||||
|
||||
@@ -98,6 +98,23 @@
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
flex: 1;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
flex: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.max-items-field {
|
||||
max-width: 9rem;
|
||||
flex-shrink: 0;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
Reference in New Issue
Block a user