Skip to content

Commit a3865b3

Browse files
tammy-a11ytumms2021389
authored andcommitted
feat: Mediaco Listview in both table and gallery
1 parent a9b75fc commit a3865b3

14 files changed

+928
-2
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<div class="carousel-host-container">
2+
<div class="carousel-frame">
3+
<div class="carousel-scroll-area" #scrollContainer>
4+
<div class="card-wrapper" *ngFor="let item of displayItems" #cardItem>
5+
<mat-card class="inner-material-card">
6+
<img [src]="item.img" alt="Card Image" />
7+
<div class="card-overlay">
8+
<h3>{{ item.title }}</h3>
9+
</div>
10+
</mat-card>
11+
</div>
12+
</div>
13+
</div>
14+
<div class="carousel-footer"></div>
15+
</div>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
:host {
2+
display: block;
3+
width: 100%;
4+
min-width: 0;
5+
max-width: 100vw;
6+
overflow: hidden;
7+
contain: content;
8+
}
9+
10+
.carousel-host-container {
11+
width: 100%;
12+
position: relative;
13+
overflow: hidden;
14+
display: flex;
15+
flex-direction: column;
16+
}
17+
18+
.header {
19+
display: flex;
20+
justify-content: space-between;
21+
align-items: center;
22+
margin-bottom: 10px;
23+
padding: 0 16px;
24+
25+
h2 {
26+
margin: 0;
27+
font-size: 20px;
28+
font-weight: 500;
29+
}
30+
}
31+
32+
.carousel-scroll-area {
33+
display: flex;
34+
align-items: center;
35+
width: 100%;
36+
max-width: 100%;
37+
height: 400px;
38+
padding: 0;
39+
overflow-x: auto;
40+
overflow-y: hidden;
41+
scroll-behavior: auto;
42+
box-sizing: border-box;
43+
box-sizing: border-box;
44+
45+
&::-webkit-scrollbar {
46+
display: none;
47+
}
48+
scrollbar-width: none;
49+
}
50+
51+
.card-wrapper {
52+
flex: 0 0 200px;
53+
height: 350px;
54+
margin: 0 10px;
55+
transition:
56+
flex-basis 0.1s linear,
57+
min-width 0.1s linear;
58+
will-change: flex-basis, min-width;
59+
min-width: 0;
60+
}
61+
62+
.inner-material-card {
63+
width: 100%;
64+
height: 100%;
65+
padding: 0 !important;
66+
overflow: hidden;
67+
position: relative;
68+
background: #000;
69+
border-radius: 8px;
70+
71+
img {
72+
width: 100%;
73+
height: 100%;
74+
object-fit: cover;
75+
display: block;
76+
}
77+
78+
.card-overlay {
79+
position: absolute;
80+
bottom: 0;
81+
left: 0;
82+
width: 100%;
83+
padding: 16px;
84+
background: linear-gradient(transparent, rgba(0, 0, 0, 0.9));
85+
color: white;
86+
87+
h3 {
88+
margin: 0;
89+
font-size: 16px;
90+
white-space: nowrap;
91+
overflow: hidden;
92+
text-overflow: ellipsis;
93+
}
94+
}
95+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import {
2+
Component,
3+
ElementRef,
4+
ViewChildren,
5+
QueryList,
6+
AfterViewInit,
7+
OnDestroy,
8+
NgZone,
9+
Input,
10+
OnChanges,
11+
SimpleChanges,
12+
ViewChild
13+
} from '@angular/core';
14+
import { CommonModule } from '@angular/common';
15+
import { MatButtonModule } from '@angular/material/button';
16+
import { MatCardModule } from '@angular/material/card';
17+
import { MatDialog } from '@angular/material/dialog';
18+
import { MatIconModule } from '@angular/material/icon';
19+
// import { GalleryGridComponent } from '../gallery-grid/gallery-grid.component';
20+
21+
@Component({
22+
selector: 'app-carousel',
23+
standalone: true,
24+
imports: [CommonModule, MatButtonModule, MatCardModule, MatIconModule],
25+
templateUrl: './carousel.component.html',
26+
styleUrls: ['./carousel.component.scss']
27+
})
28+
export class CarouselComponent implements AfterViewInit, OnDestroy, OnChanges {
29+
@Input() data: any[] = [];
30+
@ViewChildren('cardItem') cardItems!: QueryList<ElementRef>;
31+
@ViewChild('scrollContainer') scrollContainer!: ElementRef<HTMLElement>;
32+
33+
originalItems: any[] = [];
34+
displayItems: any[] = [];
35+
36+
constructor(
37+
private ngZone: NgZone,
38+
private dialog: MatDialog
39+
) {}
40+
41+
ngOnChanges(changes: SimpleChanges) {
42+
if (changes['data'] && this.data) {
43+
this.buildCarouselItems();
44+
}
45+
}
46+
47+
buildCarouselItems() {
48+
const mappedData = this.data.map(item => {
49+
return {
50+
title: item.Carouselheading || item.Description || 'Untitled',
51+
img: item.ImageURL,
52+
...item
53+
};
54+
});
55+
this.originalItems = mappedData;
56+
let loopList = [...mappedData];
57+
// If you have 2 items, we duplicate them until we have at least 12.
58+
const MIN_ITEMS = 12;
59+
if (loopList.length > 0) {
60+
while (loopList.length < MIN_ITEMS) {
61+
loopList = [...loopList, ...loopList];
62+
}
63+
}
64+
//CREATE 3 SETS: [Left Buffer] [Middle (Active)] [Right Buffer]
65+
this.displayItems = [...loopList, ...loopList, ...loopList];
66+
}
67+
68+
ngAfterViewInit() {
69+
this.ngZone.runOutsideAngular(() => {
70+
const container = this.scrollContainer?.nativeElement;
71+
if (container) {
72+
container.addEventListener('scroll', this.onScroll.bind(this));
73+
setTimeout(() => {
74+
if (container.scrollWidth > 0) {
75+
const singleSetWidth = container.scrollWidth / 3;
76+
container.scrollLeft = singleSetWidth;
77+
this.onScroll({ target: container } as any);
78+
}
79+
}, 50);
80+
}
81+
});
82+
}
83+
84+
ngOnDestroy() {
85+
const container = this.scrollContainer?.nativeElement;
86+
if (container) {
87+
container.removeEventListener('scroll', this.onScroll.bind(this));
88+
}
89+
}
90+
91+
onScroll(event: Event) {
92+
const container = event.target as HTMLElement;
93+
if (!container) return;
94+
95+
requestAnimationFrame(() => {
96+
const totalWidth = container.scrollWidth;
97+
const singleSetWidth = totalWidth / 3;
98+
const currentScroll = container.scrollLeft;
99+
100+
if (currentScroll < 100) {
101+
container.scrollLeft = currentScroll + singleSetWidth;
102+
} else if (currentScroll >= singleSetWidth * 2 - 100) {
103+
container.scrollLeft = currentScroll - singleSetWidth;
104+
}
105+
const containerRect = container.getBoundingClientRect();
106+
if (containerRect.width === 0) return;
107+
108+
this.cardItems.forEach(item => {
109+
const el = item.nativeElement;
110+
const rect = el.getBoundingClientRect();
111+
const cardCenter = rect.left - containerRect.left + rect.width / 2;
112+
const containerCenter = containerRect.width / 2;
113+
const distance = Math.abs(containerCenter - cardCenter);
114+
115+
const activeZone = 400;
116+
const minWidth = 200;
117+
const maxWidth = 500;
118+
let currentWidth = minWidth;
119+
let opacity = 0.7;
120+
121+
if (distance < activeZone) {
122+
const factor = 1 - distance / activeZone;
123+
currentWidth = minWidth + (maxWidth - minWidth) * factor;
124+
opacity = 0.7 + 0.3 * factor;
125+
}
126+
127+
el.style.flexBasis = `${currentWidth}px`;
128+
el.style.minWidth = `${currentWidth}px`;
129+
el.style.opacity = `${opacity}`;
130+
});
131+
});
132+
}
133+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<div class="dialog-content">
2+
<div class="dialog-header">
3+
<h2 mat-dialog-title>All Content</h2>
4+
<button mat-icon-button (click)="close()">
5+
<mat-icon>close</mat-icon>
6+
</button>
7+
</div>
8+
9+
<div *ngIf="data.dataPage === 'D_CarouselitemList'" mat-dialog-content class="grid-container">
10+
<div class="grid-item" *ngFor="let item of data.items">
11+
<img [src]="item.ImageURL" alt="Item Image" />
12+
<p>{{ item.Carouselheading }}</p>
13+
</div>
14+
</div>
15+
16+
<div *ngIf="data.dataPage === 'D_AccountHistoryList'" class="grid">
17+
<table-template-card *ngFor="let item of data.items; trackBy: trackByTitle; index as i" [data]="item" [index]="i"> </table-template-card>
18+
</div>
19+
</div>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
:host {
2+
display: block;
3+
height: 100%;
4+
display: flex;
5+
flex-direction: column;
6+
box-sizing: border-box;
7+
}
8+
9+
.dialog-content {
10+
display: flex;
11+
flex-direction: column;
12+
height: 100%;
13+
overflow: hidden;
14+
}
15+
16+
.dialog-header {
17+
display: flex;
18+
justify-content: space-between;
19+
align-items: center;
20+
padding: 16px 24px;
21+
border-bottom: 1px solid #e0e0e0;
22+
background: #fff;
23+
flex-shrink: 0;
24+
25+
h2 {
26+
margin: 0;
27+
font-size: 22px;
28+
font-weight: 500;
29+
}
30+
}
31+
32+
.grid-container {
33+
flex: 1;
34+
display: grid;
35+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
36+
gap: 20px;
37+
padding: 24px;
38+
overflow-y: auto;
39+
}
40+
41+
.grid-item {
42+
border: 1px solid #e0e0e0;
43+
border-radius: 8px;
44+
overflow: hidden;
45+
display: flex;
46+
flex-direction: column;
47+
background: white;
48+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
49+
transition: transform 0.2s;
50+
height: 240px;
51+
52+
&:hover {
53+
transform: translateY(-4px);
54+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
55+
}
56+
57+
img {
58+
width: 100%;
59+
height: 160px;
60+
object-fit: cover;
61+
display: block;
62+
}
63+
64+
p {
65+
margin: 0;
66+
padding: 16px;
67+
text-align: center;
68+
font-weight: 500;
69+
color: #333;
70+
flex: 1;
71+
display: flex;
72+
align-items: center;
73+
justify-content: center;
74+
}
75+
}
76+
77+
.grid {
78+
display: grid;
79+
grid-template-columns: 1fr;
80+
gap: 18px;
81+
padding: 12px;
82+
overflow-y: scroll;
83+
84+
@media (min-width: 900px) {
85+
grid-template-columns: 1fr 1fr;
86+
gap: 22px;
87+
}
88+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Component, Inject } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
import { MatDialogModule, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
4+
import { MatIconModule } from '@angular/material/icon';
5+
import { MatButtonModule } from '@angular/material/button';
6+
import { TableTemplateCardComponent } from './table-template-card/table-template-card';
7+
8+
@Component({
9+
selector: 'app-gallery-grid',
10+
standalone: true,
11+
imports: [CommonModule, MatDialogModule, MatIconModule, MatButtonModule, TableTemplateCardComponent],
12+
templateUrl: './gallery-grid-component.html',
13+
styleUrls: ['./gallery-grid-component.scss']
14+
})
15+
export class GalleryGridComponent {
16+
trackByTitle = (_: number, item: { title: string }) => item.title;
17+
18+
// Inject the data passed from the parent component
19+
constructor(
20+
public dialogRef: MatDialogRef<GalleryGridComponent>,
21+
@Inject(MAT_DIALOG_DATA) public data: { items: any[]; dataPage: string }
22+
) {}
23+
24+
close() {
25+
this.dialogRef.close();
26+
}
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<mat-card class="m-card">
2+
<div class="m-row">
3+
<div class="m-icon" [ngClass]="'bg-' + index">
4+
<img class="m-icon-img" [src]="data.icon" [ngClass]="'color-' + index" />
5+
</div>
6+
<div class="m-content">
7+
<div class="m-title">{{ data.title }}</div>
8+
<div class="m-desc">{{ data.description }}</div>
9+
</div>
10+
</div>
11+
</mat-card>

0 commit comments

Comments
 (0)