Responsive Design Splitscreen Article Search Results Update

This commit is contained in:
Nino Righi
2023-03-28 15:08:31 +02:00
parent 52278b8baf
commit 3fcf3d9396
13 changed files with 225 additions and 223 deletions

View File

@@ -8,14 +8,14 @@
bottom: 5rem;
}
main {
@apply w-full mx-auto px-px-15 desktop:px-6 self-stretch;
@apply w-full mx-auto max-w-desktop px-px-15 desktop:px-6 self-stretch;
}
.shell-header-wrapper {
@apply fixed top-0 left-0 right-0 bg-white;
shell-header {
@apply w-full mx-auto;
@apply w-full max-w-desktop px-px-15 desktop:px-6 mx-auto;
}
button.notifications-btn {
@@ -35,7 +35,7 @@ main {
top: 5.125rem;
shell-process {
@apply w-full mx-auto;
@apply w-full max-w-desktop px-px-15 desktop:px-6 mx-auto;
height: 52px;
}
}
@@ -49,7 +49,7 @@ shell-process {
@apply fixed bottom-0 left-0 right-0 bg-white z-fixed shadow-card;
shell-footer {
@apply w-full mx-auto;
@apply w-full max-w-desktop px-px-15 desktop:px-6 mx-auto;
.active {
@apply font-bold;

View File

@@ -20,7 +20,7 @@
<div class="page-price-update-item__item-details">
<div class="page-price-update-item__item-contributors flex flex-row">
{{ environment.isTablet() ? (item?.product?.contributors | substr: 42) : item?.product?.contributors }}
{{ environment.isTablet() ? (item?.product?.contributors | substr: 38) : item?.product?.contributors }}
</div>
<div
@@ -43,7 +43,7 @@
src="assets/images/Icon_{{ item?.product?.format }}.svg"
[alt]="item?.product?.formatDetail"
/>
{{ item?.product?.formatDetail }}
{{ environment.isTablet() ? (item?.product?.formatDetail | substr: 25) : item?.product?.formatDetail }}
</div>
</div>

View File

@@ -76,9 +76,13 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
queryParams: params,
});
} else {
this._router.navigate(['/kunde', processId, 'product', { outlets: { main: 'results', left: null, right: null } }], {
queryParams: params,
});
const item = state.items.find((f) => f);
this._router.navigate(
['/kunde', processId, 'product', { outlets: { main: null, left: 'results', right: ['details', item.id] } }],
{
queryParams: params,
}
);
}
}
}

View File

@@ -8,7 +8,12 @@
<a
class="btn-close"
type="button"
[routerLink]="['/kunde', application.activatedProcessId, 'product', { outlets: { main: 'results', left: null, right: null } }]"
[routerLink]="[
'/kunde',
application.activatedProcessId,
'product',
{ outlets: { main: null, left: 'results', right: ['details', item?.id] } }
]"
queryParamsHandling="preserve"
>
<ui-icon icon="close" size="20px"></ui-icon>

View File

@@ -29,6 +29,10 @@ export class ArticleSearchFilterComponent implements OnInit {
return this._environment.isTablet();
}
get item() {
return this.articleSearch.items.find((_) => true);
}
constructor(
private articleSearch: ArticleSearchService,
private _environment: EnvironmentService,

View File

@@ -1,5 +1,5 @@
:host {
@apply flex flex-row rounded-card bg-white mb-2 p-4;
@apply flex flex-row rounded-card bg-white mb-2 p-4 desktop:w-[496px];
height: 187px;
}

View File

@@ -1,81 +1,113 @@
<a class="product-list-result-content" [routerLink]="detailsLink" [queryParamsHandling]="isTablet ? 'preserve' : ''">
<div class="item-thumbnail">
<img loading="lazy" *ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl" [src]="thumbnailUrl" [alt]="item?.product?.name" />
</div>
<div class="item-contributors">
<a
*ngFor="let contributor of contributors; let last = last"
[routerLink]="['/kunde', applicationService.activatedProcessId, 'product', 'search', 'results']"
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
(click)="$event?.stopPropagation()"
>
{{ contributor }}{{ last ? '' : ';' }}
</a>
</div>
<div
class="item-title"
[class.xl]="item?.product?.name?.length >= 35"
[class.lg]="item?.product?.name?.length >= 40"
[class.md]="item?.product?.name?.length >= 50"
[class.sm]="item?.product?.name?.length >= 60"
[class.xs]="item?.product?.name?.length >= 100"
>
{{ item?.product?.name }}
</div>
<div class="item-price">
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR':'code' }}
</div>
<div *ngIf="selectable" class="item-data-selector">
<ui-select-bullet [ngModel]="selected" (ngModelChange)="setSelected($event)"></ui-select-bullet>
</div>
<div class="item-stock z-dropdown" [uiOverlayTrigger]="tooltip" [overlayTriggerDisabled]="!(stockTooltipText$ | async)">
<ng-container *ngIf="isOrderBranch$ | async">
<div class="flex flex-row items-center justify-between">
<ui-icon icon="home" size="1em"></ui-icon>
<span
*ngIf="inStock$ | async; let stock"
[class.skeleton]="stock.inStock === undefined"
class="min-w-[1rem] text-right inline-block"
>{{ stock?.inStock }}</span
>
<span>x</span>
</div>
</ng-container>
<ng-container *ngIf="!(isOrderBranch$ | async)">
<div class="flex flex-row items-center justify-between z-dropdown">
<ui-icon class="block" icon="home" size="1em"></ui-icon>
<span class="min-w-[1rem] text-center inline-block">-</span>
<span>x</span>
</div>
</ng-container>
</div>
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [closeable]="true">
{{ stockTooltipText$ | async }}
</ui-tooltip>
<!-- <div class="item-stock"><ui-icon icon="home" size="1em"></ui-icon> {{ item?.stockInfos | stockInfos }} x</div> -->
<div class="item-ssc" [class.xs]="item?.catalogAvailability?.sscText?.length >= 60">
{{ item?.catalogAvailability?.ssc }} - {{ item?.catalogAvailability?.sscText }}
</div>
<div class="item-format" *ngIf="item?.product?.format && item?.product?.formatDetail">
<a
class="page-search-result-item__item-card p-5 desktop:p-px-10 h-[212px] desktop:h-[181px] bg-white rounded-card"
[routerLink]="detailsLink"
[queryParamsHandling]="isTablet ? 'preserve' : ''"
>
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[50px] h-[79px]">
<img
*ngIf="item?.product?.format !== '--'"
class="page-search-result-item__item-image w-[50px] h-[79px]"
loading="lazy"
src="assets/images/Icon_{{ item?.product?.format }}.svg"
[alt]="item?.product?.formatDetail"
*ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl"
[src]="thumbnailUrl"
[alt]="item?.product?.name"
/>
{{ item?.product?.formatDetail }}
</div>
<div class="item-misc">
{{ item?.product?.manufacturer | substr: 18 }} | {{ item?.product?.ean }} <br />
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
{{ publicationDate }}
<div class="grid-container-test">
<div
class="page-search-result-item__item-contributors desktop:text-sm flex flex-row font-bold text-[#0556B4] text-ellipsis overflow-hidden max-w-[600px] whitespace-nowrap"
>
<a
*ngFor="let contributor of contributors; let last = last"
class="mr-1"
[routerLink]="['/kunde', applicationService.activatedProcessId, 'product', 'search', 'results']"
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
(click)="$event?.stopPropagation()"
>
{{ contributor }}{{ last ? '' : ';' }}
</a>
</div>
<div
class="page-search-result-item__item-title font-bold text-2xl"
[class.text-xl]="item?.product?.name?.length >= 35"
[class.text-lg]="item?.product?.name?.length >= 40"
[class.text-md]="item?.product?.name?.length >= 50"
[class.text-sm]="item?.product?.name?.length >= 60 || !isTablet"
[class.text-xs]="item?.product?.name?.length >= 100 || (!isTablet && item?.product?.name?.length >= 70)"
>
{{ item?.product?.name }}
</div>
<div class="page-search-result-item__item-format desktop:text-sm">
<div *ngIf="item?.product?.format && item?.product?.formatDetail" class="font-bold flex flex-row">
<img
class="mr-3"
*ngIf="item?.product?.format !== '--'"
loading="lazy"
src="assets/images/Icon_{{ item?.product?.format }}.svg"
[alt]="item?.product?.formatDetail"
/>
{{ item?.product?.formatDetail | substr: 25 }}
</div>
</div>
<div class="page-search-result-item__item-manufacturer desktop:text-sm">
{{ item?.product?.manufacturer | substr: 18 }} | {{ item?.product?.ean }}
</div>
<div class="page-search-result-item__item-misc desktop:text-sm">
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
{{ publicationDate }}
</div>
<div class="page-search-result-item__item-price desktop:text-sm font-bold justify-self-end">
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR':'code' }}
</div>
<div class="page-search-result-item__item-select-bullet justify-self-end">
<input
*ngIf="selectable"
(click)="$event.stopPropagation()"
[ngModel]="selected$ | async"
(ngModelChange)="setSelected()"
class="isa-select-bullet"
type="checkbox"
/>
</div>
<div
class="page-search-result-item__item-stock desktop:text-sm font-bold z-dropdown justify-self-end"
[uiOverlayTrigger]="tooltip"
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
>
<ng-container *ngIf="isOrderBranch$ | async">
<div class="flex flex-row items-center justify-between">
<ui-icon icon="home" size="1em"></ui-icon>
<span
*ngIf="inStock$ | async; let stock"
[class.skeleton]="stock.inStock === undefined"
class="min-w-[1rem] text-right inline-block"
>{{ stock?.inStock }}</span
>
<span>x</span>
</div>
</ng-container>
<ng-container *ngIf="!(isOrderBranch$ | async)">
<div class="flex flex-row items-center justify-between z-dropdown">
<ui-icon class="block" icon="home" size="1em"></ui-icon>
<span class="min-w-[1rem] text-center inline-block">-</span>
<span>x</span>
</div>
</ng-container>
</div>
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [closeable]="true">
{{ stockTooltipText$ | async }}
</ui-tooltip>
<div class="page-search-result-item__item-ssc desktop:text-sm" [class.xs]="item?.catalogAvailability?.sscText?.length >= 60">
<strong>{{ item?.catalogAvailability?.ssc }}</strong> -
{{ !isTablet ? item?.catalogAvailability?.sscText : (item?.catalogAvailability?.sscText | substr: 18) }}
</div>
</div>
</a>

View File

@@ -1,113 +1,61 @@
.product-list-result-content {
@apply text-black no-underline grid;
grid-template-columns: 102px 50% auto;
grid-template-rows: auto;
:host {
@apply flex flex-col w-full h-[212px] desktop:h-[181px];
}
.page-search-result-item__item-card {
@apply grid grid-flow-col;
grid-template-columns: 63px auto;
box-shadow: 0px 0px 10px rgba(220, 226, 233, 0.5);
}
.grid-container-test {
@apply grid grid-flow-row gap-[6px];
grid-template-areas:
'item-thumbnail item-contributors item-contributors'
'item-thumbnail item-title item-price'
'item-thumbnail item-title item-data-selector'
'item-thumbnail item-format item-stock'
'item-thumbnail item-misc item-ssc';
'contributors contributors contributors'
'title title price'
'title title price'
'title title select'
'format format select'
'manufacturer manufacturer stock'
'misc ssc ssc';
}
.item-thumbnail {
grid-area: item-thumbnail;
width: 70px;
@apply mr-8;
img {
max-width: 100%;
max-height: 150px;
@apply rounded-card shadow-cta;
}
.page-search-result-item__item-contributors {
grid-area: contributors;
}
.item-contributors {
grid-area: item-contributors;
height: 22px;
text-overflow: ellipsis;
overflow: hidden;
max-width: 600px;
white-space: nowrap;
a {
@apply text-active-customer font-bold no-underline;
}
.page-search-result-item__item-price {
grid-area: price;
}
.item-title {
grid-area: item-title;
@apply font-bold text-2xl;
height: 64px;
max-height: 64px;
.page-search-result-item__item-title {
grid-area: title;
}
.item-title.xl {
@apply font-bold text-xl;
.page-search-result-item__item-format {
grid-area: format;
}
.item-title.lg {
@apply font-bold text-lg;
.page-search-result-item__item-manufacturer {
grid-area: manufacturer;
}
.item-title.md {
@apply font-bold text-base;
.page-search-result-item__item-misc {
grid-area: misc;
}
.item-title.sm {
@apply font-bold text-sm;
.page-search-result-item__item-select-bullet {
grid-area: select;
}
.item-title.xs {
@apply font-bold text-xs;
.page-search-result-item__item-stock {
grid-area: stock;
}
.item-price {
grid-area: item-price;
@apply font-bold text-xl text-right;
.page-search-result-item__item-ssc {
@apply justify-self-end;
grid-area: ssc;
}
.item-format {
grid-area: item-format;
@apply flex flex-row items-center font-bold text-lg whitespace-nowrap;
img {
@apply mr-2;
}
}
.item-stock {
grid-area: item-stock;
@apply flex flex-row justify-end items-baseline font-bold text-lg;
ui-icon {
@apply text-active-customer mr-2;
}
}
.item-misc {
grid-area: item-misc;
}
.item-ssc {
grid-area: item-ssc;
@apply font-bold text-right;
}
.item-ssc.xs {
@apply font-bold text-xs;
}
.item-data-selector {
@apply w-full flex justify-end;
grid-area: item-data-selector;
}
ui-select-bullet {
@apply p-4 -m-4 z-dropdown;
}
@media (min-width: 1025px) {
.item-contributors {
max-width: 780px;
}
.page-search-result-item__item-image {
box-shadow: 0px 6px 18px rgba(0, 0, 0, 0.197935);
}

View File

@@ -38,16 +38,7 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
readonly item$ = this.select((s) => s.item);
@Input()
get selected() {
return this.get((s) => s.selected);
}
set selected(selected: boolean) {
if (this.selected !== selected) {
this.patchState({ selected });
}
}
readonly selected$ = this.select((s) => s.selected);
selected$ = this._articleSearchService.selectedItemIds$.pipe(map((selectedItemIds) => selectedItemIds.includes(this.item?.id)));
@Input()
get selectable() {
@@ -149,7 +140,26 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
});
}
setSelected(selected: boolean) {
this._articleSearchService.setSelected({ selected, itemId: this.item?.id });
setSelected() {
const isSelected = this._articleSearchService.selectedItemIds.includes(this.item?.id);
this._articleSearchService.setSelected({ selected: !isSelected, itemId: this.item?.id });
// this._articleSearchService.setSelected({ selected, itemId: this.item?.id });
// setSelected({ selected, itemUid }: { selected: boolean; itemUid: string }) {
// if (selected) {
// this.patchState({
// selectedItemUids: [...this.selectedItemUids, itemUid],
// });
// } else if (!selected) {
// this.patchState({
// selectedItemUids: this.selectedItemUids.filter((id) => id !== itemUid),
// });
// }
// }
}
// setSelected() {
// const isSelected = this._store.selectedItemUids.includes(this.item?.uId);
// this._store.setSelected({ selected: !isSelected, itemUid: this.item?.uId });
// }
}

View File

@@ -1,38 +1,43 @@
<a
*ngIf="!isTablet"
class="filter"
[class.active]="hasFilter$ | async"
[routerLink]="['/kunde', application.activatedProcessId, 'product', { outlets: { main: null, left: 'results', right: 'filter' } }]"
queryParamsHandling="preserve"
>
<ui-icon size="20px" icon="filter_alit"></ui-icon>
<span class="label">Filter</span>
</a>
<div class="page-search-results__header bg-background-liste flex flex-col items-end pb-4">
<a
[class.active]="hasFilter$ | async"
class="page-search-results__filter h-14 rounded-card font-bold px-5 mb-4 text-lg bg-cadet-blue flex flex-row flex-nowrap items-center justify-center"
[routerLink]="['/kunde', application.activatedProcessId, 'product', { outlets: { main: null, left: 'results', right: 'filter' } }]"
queryParamsHandling="preserve"
>
<ui-svg-icon class="mr-2" icon="filter-variant"></ui-svg-icon>
Filter
</a>
<div class="filter-wrapper">
<div class="hits" *ngIf="hits$ | async; let hits">{{ hits }} Titel</div>
<div *ngIf="hits$ | async; let hits" class="page-search-results__items-count inline-flex flex-row items-center pr-5 text-sm">
{{ hits ??
0 }}
Titel
</div>
</div>
<div class="page-search-results__order-by h-[53px] flex flex-row items-center justify-center bg-white rounded-t-card">
<ui-order-by-filter [orderBy]="(filter$ | async)?.orderBy" (selectedOrderByChange)="search(); updateBreadcrumbs()"> </ui-order-by-filter>
</div>
<cdk-virtual-scroll-viewport
#scrollContainer
class="product-list scroll-bar scroll-bar-margin"
class="product-list"
[itemSize]="187"
minBufferPx="1200"
maxBufferPx="1200"
(scrolledIndexChange)="scrolledIndexChange($event)"
>
<div class="product-list-result" *cdkVirtualFor="let item of results$ | async; trackBy: trackByItemId">
<search-result-item
[selected]="item | searchResultSelected: searchService.selectedItemIds"
[selectable]="isSelectable(item)"
[item]="item"
></search-result-item>
</div>
<search-result-item
class="mb-px-10"
*cdkVirtualFor="let item of results$ | async; trackBy: trackByItemId"
[selectable]="isSelectable(item)"
[item]="item"
></search-result-item>
<page-search-result-item-loading *ngIf="fetching$ | async"></page-search-result-item-loading>
</cdk-virtual-scroll-viewport>
<div class="actions">
<div class="actions z-fixed">
<button
[disabled]="loading$ | async"
*ngIf="(selectedItemIds$ | async)?.length > 0"

View File

@@ -1,18 +1,10 @@
:host {
@apply box-border grid;
max-height: calc(100vh - 364px);
height: 100vh;
@apply box-border grid h-[100vh] max-h-[calc(100vh-364px)] desktop:max-h-[calc(100vh-300px)];
grid-template-rows: auto auto 1fr;
}
.product-list {
@apply m-0 p-0 mt-2;
}
.product-list-result {
@apply list-none bg-white rounded-card p-4 mb-2;
height: 187px;
max-height: 187px;
@apply m-0 p-0 mt-px-2;
}
.filter {

View File

@@ -9,7 +9,8 @@
<ng-template #desktop>
<router-outlet name="main"></router-outlet>
<div class="grid grid-cols-[40%_60%]">
<div class="grid grid-cols-[168px_496px_728px]">
<div class="bg-white mr-6"></div>
<div class="mr-6">
<router-outlet name="left"></router-outlet>
</div>

View File

@@ -1,10 +1,10 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./apps/**/*.{html,ts}'],
darkMode: 'media', // or 'media' or 'class'
darkMode: 'media', // or 'media' or 'class',
theme: {
screens: {
tablet: '640px', // 744 x 1133 iPad Optimale Auflösung
tablet: '744px', // 744 x 1133 iPad Optimale Auflösung
desktop: '1024px', // 1440 x 1024 Desktop Optimale Auflösung
},
zIndex: {
@@ -35,6 +35,7 @@ module.exports = {
},
maxWidth: {
content: '1024px',
desktop: '1440px',
// content: '1280px',
},
spacing: {