diff --git a/package-lock.json b/package-lock.json
index fd89a98d..00a6547a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -357,47 +357,6 @@
"webpack": "^5.1.0"
}
},
- "node_modules/@angular-devkit/build-angular/node_modules/esbuild": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz",
- "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.23.0",
- "@esbuild/android-arm": "0.23.0",
- "@esbuild/android-arm64": "0.23.0",
- "@esbuild/android-x64": "0.23.0",
- "@esbuild/darwin-arm64": "0.23.0",
- "@esbuild/darwin-x64": "0.23.0",
- "@esbuild/freebsd-arm64": "0.23.0",
- "@esbuild/freebsd-x64": "0.23.0",
- "@esbuild/linux-arm": "0.23.0",
- "@esbuild/linux-arm64": "0.23.0",
- "@esbuild/linux-ia32": "0.23.0",
- "@esbuild/linux-loong64": "0.23.0",
- "@esbuild/linux-mips64el": "0.23.0",
- "@esbuild/linux-ppc64": "0.23.0",
- "@esbuild/linux-riscv64": "0.23.0",
- "@esbuild/linux-s390x": "0.23.0",
- "@esbuild/linux-x64": "0.23.0",
- "@esbuild/netbsd-x64": "0.23.0",
- "@esbuild/openbsd-arm64": "0.23.0",
- "@esbuild/openbsd-x64": "0.23.0",
- "@esbuild/sunos-x64": "0.23.0",
- "@esbuild/win32-arm64": "0.23.0",
- "@esbuild/win32-ia32": "0.23.0",
- "@esbuild/win32-x64": "0.23.0"
- }
- },
"node_modules/@angular-devkit/build-angular/node_modules/eslint-scope": {
"version": "5.1.1",
"dev": true,
diff --git a/src/app/_samples/mediaco/components/banner/banner.component.scss b/src/app/_samples/mediaco/components/banner/banner.component.scss
index b22f90e2..55efd4f7 100644
--- a/src/app/_samples/mediaco/components/banner/banner.component.scss
+++ b/src/app/_samples/mediaco/components/banner/banner.component.scss
@@ -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;
}
diff --git a/src/app/_samples/mediaco/components/quick-create/quick-create.component.html b/src/app/_samples/mediaco/components/quick-create/quick-create.component.html
index 7b15284e..a3289b52 100644
--- a/src/app/_samples/mediaco/components/quick-create/quick-create.component.html
+++ b/src/app/_samples/mediaco/components/quick-create/quick-create.component.html
@@ -6,24 +6,10 @@
Get started
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/src/app/_samples/mediaco/components/quick-create/quick-create.component.scss b/src/app/_samples/mediaco/components/quick-create/quick-create.component.scss
index 76a35394..6dda097b 100644
--- a/src/app/_samples/mediaco/components/quick-create/quick-create.component.scss
+++ b/src/app/_samples/mediaco/components/quick-create/quick-create.component.scss
@@ -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;
@@ -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 {
diff --git a/src/app/_samples/mediaco/components/quick-create/quick-create.component.ts b/src/app/_samples/mediaco/components/quick-create/quick-create.component.ts
index 36be0e77..75ae29fc 100644
--- a/src/app/_samples/mediaco/components/quick-create/quick-create.component.ts
+++ b/src/app/_samples/mediaco/components/quick-create/quick-create.component.ts
@@ -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
@@ -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;
@@ -27,6 +28,7 @@ export class QuickCreateComponent implements OnInit, OnChanges {
showCaseIcons$?: boolean;
classFilter$: any;
cases$: any = [];
+
constructor(private utils: Utils) {}
ngOnInit() {
diff --git a/src/app/_samples/mediaco/directives/masonry.directive.ts b/src/app/_samples/mediaco/directives/masonry.directive.ts
new file mode 100644
index 00000000..253ca130
--- /dev/null
+++ b/src/app/_samples/mediaco/directives/masonry.directive.ts
@@ -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();
+ }
+}