Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
41 changes: 0 additions & 41 deletions package-lock.json

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

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: calc(2 * 0.5rem);
row-gap: 1.5rem;
gap: 1.5rem;
align-items: start;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,10 @@ <h3>Get started</h3>
<div class="active-line"></div>
</div>
</div>
<div class="masonry-wrapper">
<!-- Left Column -->
<div class="masonry-column">
<ng-container *ngFor="let card of cases$; let i = index">
<ng-container *ngIf="i % 2 === 0">
<ng-container *ngTemplateOutlet="cardTemplate; context: { $implicit: card }"></ng-container>
</ng-container>
</ng-container>
</div>

<!-- Right Column -->
<div class="masonry-column">
<ng-container *ngFor="let card of cases$; let i = index">
<ng-container *ngIf="i % 2 === 1">
<ng-container *ngTemplateOutlet="cardTemplate; context: { $implicit: card }"></ng-container>
</ng-container>
</ng-container>
</div>
<div class="masonry-wrapper" appMasonry [masonryGap]="16" [masonryRowHeight]="1" masonryItemSelector=".card">
<ng-container *ngFor="let card of cases$; let i = index">
<ng-container *ngTemplateOutlet="cardTemplate; context: { $implicit: card }"></ng-container>
</ng-container>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,20 @@ $text-dark: #2c2c2c;
}

.masonry-wrapper {
display: flex;
flex-direction: column;
display: grid;
grid-template-columns: 1fr;
grid-auto-rows: 1px;
gap: $gap-size;
width: 100%;
max-width: 1100px;
align-items: start;
grid-auto-flow: row dense;

@media (min-width: 768px) {
flex-direction: row;
align-items: flex-start;
grid-template-columns: repeat(2, 1fr);
}
}

.masonry-column {
display: flex;
flex-direction: column;
gap: $gap-size;
flex: 1;
width: 100%;
}

// --- Card Styles ---
.card {
background: #fff;
Expand All @@ -74,6 +68,10 @@ $text-dark: #2c2c2c;
overflow: hidden;
display: flex;
flex-direction: column;
height: fit-content;

// Dynamic grid-row-end will be set via JavaScript for true masonry effect
grid-row-end: span var(--row-span, auto);

&:hover {
.icon-box {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import { MatIcon } from '@angular/material/icon';
import { Utils } from '@pega/angular-sdk-components';
import { QUICK_LINKS_DATA } from './quick-create.utils';
import { MasonryDirective } from '../../directives/masonry.directive';

interface QuickCreateProps {
// If any, enter additional props that only exist on this component
Expand All @@ -15,7 +16,7 @@ interface QuickCreateProps {
selector: 'app-quick-create',
templateUrl: './quick-create.component.html',
styleUrls: ['./quick-create.component.scss'],
imports: [CommonModule, MatIcon]
imports: [CommonModule, MatIcon, MasonryDirective]
})
export class QuickCreateComponent implements OnInit, OnChanges {
@Input() pConn$: typeof PConnect;
Expand All @@ -27,6 +28,7 @@ export class QuickCreateComponent implements OnInit, OnChanges {
showCaseIcons$?: boolean;
classFilter$: any;
cases$: any = [];

constructor(private utils: Utils) {}

ngOnInit() {
Expand Down
92 changes: 92 additions & 0 deletions src/app/_samples/mediaco/directives/masonry.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Directive, ElementRef, AfterViewInit, OnDestroy, Input, HostListener } from '@angular/core';

@Directive({
selector: '[appMasonry]',
standalone: true
})
export class MasonryDirective implements AfterViewInit, OnDestroy {
@Input() masonryGap: number = 16;
@Input() masonryRowHeight: number = 1;
@Input() masonryItemSelector: string = '.card';

private resizeTimeout: any;
private mutationObserver: MutationObserver | null = null;

constructor(private elementRef: ElementRef) {}

ngAfterViewInit() {
// Apply masonry layout after view is initialized
setTimeout(() => {
this.applyMasonryLayout();
}, 100);

// Watch for DOM changes (new items added/removed)
this.setupMutationObserver();
}

ngOnDestroy() {
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
}
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
}

@HostListener('window:resize', ['$event'])
onWindowResize() {
// Throttle resize events to avoid excessive recalculations
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
}

this.resizeTimeout = setTimeout(() => {
this.applyMasonryLayout();
}, 150);
}

private setupMutationObserver() {
this.mutationObserver = new MutationObserver(() => {
// Debounce DOM changes
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
}

this.resizeTimeout = setTimeout(() => {
this.applyMasonryLayout();
}, 100);
});

this.mutationObserver.observe(this.elementRef.nativeElement, {
childList: true,
subtree: true
});
}

private applyMasonryLayout() {
const grid = this.elementRef.nativeElement;
if (!grid) return;

const items = grid.querySelectorAll(this.masonryItemSelector);
if (items.length === 0) return;

// Reset any existing row spans first
items.forEach((item: HTMLElement) => {
item.style.removeProperty('--row-span');
});

// Wait for layout to settle after reset, then recalculate
requestAnimationFrame(() => {
items.forEach((item: HTMLElement) => {
const itemHeight = item.getBoundingClientRect().height;
const rowSpan = Math.ceil((itemHeight + this.masonryGap) / (this.masonryRowHeight + this.masonryGap));
item.style.setProperty('--row-span', rowSpan.toString());
});
});
}

// Public method to manually trigger layout recalculation
public recalculateLayout() {
this.applyMasonryLayout();
}
}
Loading