diff --git a/src/@fuse/components/masonry/index.ts b/src/@fuse/components/masonry/index.ts new file mode 100644 index 00000000..ac978a78 --- /dev/null +++ b/src/@fuse/components/masonry/index.ts @@ -0,0 +1 @@ +export * from '@fuse/components/card/public-api'; diff --git a/src/@fuse/components/masonry/masonry.component.html b/src/@fuse/components/masonry/masonry.component.html new file mode 100644 index 00000000..4b0a6719 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/@fuse/components/masonry/masonry.component.scss b/src/@fuse/components/masonry/masonry.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/@fuse/components/masonry/masonry.component.ts b/src/@fuse/components/masonry/masonry.component.ts new file mode 100644 index 00000000..b8ae5043 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.component.ts @@ -0,0 +1,97 @@ +import { AfterViewInit, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { FuseAnimations } from '@fuse/animations'; +import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; + +@Component({ + selector : 'fuse-masonry', + templateUrl : './masonry.component.html', + styleUrls : ['./masonry.component.scss'], + encapsulation: ViewEncapsulation.None, + animations : FuseAnimations, + exportAs : 'fuseMasonry' +}) +export class FuseMasonryComponent implements OnChanges, AfterViewInit +{ + @Input() columnsTemplate: TemplateRef; + @Input() columns: number; + @Input() items: any[] = []; + distributedColumns: any[] = []; + + /** + * Constructor + */ + constructor(private _fuseMediaWatcherService: FuseMediaWatcherService) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On changes + * + * @param changes + */ + ngOnChanges(changes: SimpleChanges): void + { + // Columns + if ( 'columns' in changes ) + { + if ( changes.columns.isFirstChange() ) + { + return; + } + + // Distribute the items + this._distributeItems(); + } + + // Items + if ( 'items' in changes ) + { + if ( changes.columns.isFirstChange() ) + { + return; + } + + // Distribute the items + this._distributeItems(); + } + } + + /** + * After view init + */ + ngAfterViewInit(): void + { + // Distribute the items for the first time + this._distributeItems(); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Private methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Distribute items into columns + */ + private _distributeItems(): void + { + // Return an empty array if there are no items + if ( this.items.length === 0 ) + { + this.distributedColumns = []; + return; + } + + // Prepare the distributed columns array + this.distributedColumns = Array.from(Array(this.columns), item => ({items: []})); + + // Distribute the items to columns + for ( let i = 0; i < this.items.length; i++ ) + { + this.distributedColumns[i % this.columns].items.push(this.items[i]); + } + } +} diff --git a/src/@fuse/components/masonry/masonry.module.ts b/src/@fuse/components/masonry/masonry.module.ts new file mode 100644 index 00000000..651c5510 --- /dev/null +++ b/src/@fuse/components/masonry/masonry.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FuseMasonryComponent } from '@fuse/components/masonry/masonry.component'; + +@NgModule({ + declarations: [ + FuseMasonryComponent + ], + imports : [ + CommonModule + ], + exports : [ + FuseMasonryComponent + ] +}) +export class FuseMasonryModule +{ +} diff --git a/src/@fuse/components/masonry/public-api.ts b/src/@fuse/components/masonry/public-api.ts new file mode 100644 index 00000000..e074c48c --- /dev/null +++ b/src/@fuse/components/masonry/public-api.ts @@ -0,0 +1,2 @@ +export * from '@fuse/components/masonry/masonry.component'; +export * from '@fuse/components/masonry/masonry.module'; diff --git a/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html new file mode 100644 index 00000000..04fd53cf --- /dev/null +++ b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.html @@ -0,0 +1,334 @@ +
+ + +
+
+ +
+ + +
+ + Components +
+
+ +
+

+ Masonry +

+
+
+ +
+ +
+ +

+ fuse-masonry is a basic Masonry layout component to show items in Masonry grid layout. Since it doesn't use any other third party library or complex calculations to + keep everything smooth and fast; +

+
    +
  • It does NOT work with elements with different widths +
  • It does NOT sort items based on their heights (This here is the real performance killer)
  • +
+

+ If you don't need to mix and match different width items and don't need to sort items based on their heights, fuse-masonry will do the job for you and it will do it very smoothly. +

+

+ Exported as: fuseMasonry +

+ +

Module

+ + +

Properties

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionDefault
+
@Input()
+
columnsTemplate: TemplateRef
+
+ Column template for masonry component to lay out. + + undefined +
+
@Input()
+
columns: number
+
+ Number of columns to create + + undefined +
+
@Input()
+
items: any[]
+
+ Items to distribute into columns + + [] +
+
+ +

Methods

+

This component doesn't have any public methods.

+ +

Usage

+

+ fuse-masonry doesn't provide a default template or styling to the items. You can think of it as a helper function but in a component form: +

+ +
+ +
+
Example
+
+ + + + + + + +
+
+ + + + + +
+ + +
+ {{item}} +
+
+
+
+
+
+
+ +
+ +
+ + + + + + + + + +
+ +
+ +

Responsive

+

+ fuse-masonry doesn't provide a way of changing the column count depending on breakpoints but this can easily be handled by + combining fuse-masonry with FuseMediaWatcherService: +

+ +
+ +
+
Example
+
+ + + + + + + +
+
+ + + + + + +
+ + +
+ {{item}} +
+
+
+
+
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
diff --git a/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts new file mode 100644 index 00000000..1d65a474 --- /dev/null +++ b/src/app/modules/admin/docs/core-features/components/masonry/masonry.component.ts @@ -0,0 +1,81 @@ +import { Component, OnInit } from '@angular/core'; +import { CoreFeaturesComponent } from 'app/modules/admin/docs/core-features/core-features.component'; +import { Subject } from 'rxjs'; +import { FuseMediaWatcherService } from '@fuse/services/media-watcher'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector : 'masonry', + templateUrl: './masonry.component.html', + styles : [''] +}) +export class MasonryComponent implements OnInit +{ + columns: number = 4; + private _unsubscribeAll: Subject = new Subject(); + + /** + * Constructor + */ + constructor( + private _coreFeaturesComponent: CoreFeaturesComponent, + private _fuseMediaWatcherService: FuseMediaWatcherService + ) + { + } + + // ----------------------------------------------------------------------------------------------------- + // @ Lifecycle hooks + // ----------------------------------------------------------------------------------------------------- + + /** + * On init + */ + ngOnInit(): void + { + // Subscribe to media changes + this._fuseMediaWatcherService.onMediaChange$ + .pipe(takeUntil(this._unsubscribeAll)) + .subscribe(({matchingAliases}) => { + + // Set the masonry columns + // + // This if block structured in a way so that only the + // biggest matching alias will be used to set the column + // count. + if ( matchingAliases.includes('xl') ) + { + this.columns = 5; + } + else if ( matchingAliases.includes('lg') ) + { + this.columns = 4; + } + else if ( matchingAliases.includes('md') ) + { + this.columns = 3; + } + else if ( matchingAliases.includes('sm') ) + { + this.columns = 2; + } + else + { + this.columns = 1; + } + }); + } + + // ----------------------------------------------------------------------------------------------------- + // @ Public methods + // ----------------------------------------------------------------------------------------------------- + + /** + * Toggle the drawer + */ + toggleDrawer(): void + { + // Toggle the drawer + this._coreFeaturesComponent.matDrawer.toggle(); + } +} diff --git a/src/app/modules/admin/docs/core-features/core-features.component.ts b/src/app/modules/admin/docs/core-features/core-features.component.ts index 23637cec..7d0d10cd 100644 --- a/src/app/modules/admin/docs/core-features/core-features.component.ts +++ b/src/app/modules/admin/docs/core-features/core-features.component.ts @@ -77,6 +77,12 @@ export class CoreFeaturesComponent implements OnInit, OnDestroy type : 'basic', link : '/docs/core-features/components/highlight' }, + { + id : 'core-features.components.masonry', + title: 'Masonry', + type : 'basic', + link : '/docs/core-features/components/masonry' + }, { id : 'core-features.components.navigation', title: 'Navigation', diff --git a/src/app/modules/admin/docs/core-features/core-features.module.ts b/src/app/modules/admin/docs/core-features/core-features.module.ts index 07a2801d..d927298f 100644 --- a/src/app/modules/admin/docs/core-features/core-features.module.ts +++ b/src/app/modules/admin/docs/core-features/core-features.module.ts @@ -10,6 +10,7 @@ import { FuseDateRangeModule } from '@fuse/components/date-range'; import { FuseDrawerModule } from '@fuse/components/drawer'; import { FuseHighlightModule } from '@fuse/components/highlight'; import { FuseAlertModule } from '@fuse/components/alert'; +import { FuseMasonryModule } from '@fuse/components/masonry/masonry.module'; import { FuseNavigationModule } from '@fuse/components/navigation'; import { FuseScrollResetModule } from '@fuse/directives/scroll-reset'; import { SharedModule } from 'app/shared/shared.module'; @@ -21,6 +22,7 @@ import { DateRangeComponent } from 'app/modules/admin/docs/core-features/compone import { DrawerComponent } from 'app/modules/admin/docs/core-features/components/drawer/drawer.component'; import { HighlightComponent } from 'app/modules/admin/docs/core-features/components/highlight/highlight.component'; import { NavigationComponent } from 'app/modules/admin/docs/core-features/components/navigation/navigation.component'; +import { MasonryComponent } from 'app/modules/admin/docs/core-features/components/masonry/masonry.component'; import { AutogrowComponent } from 'app/modules/admin/docs/core-features/directives/autogrow/autogrow.component'; import { ScrollbarComponent } from 'app/modules/admin/docs/core-features/directives/scrollbar/scrollbar.component'; import { ScrollResetComponent } from 'app/modules/admin/docs/core-features/directives/scroll-reset/scroll-reset.component'; @@ -40,6 +42,7 @@ import { coreFeaturesRoutes } from 'app/modules/admin/docs/core-features/core-fe DateRangeComponent, DrawerComponent, HighlightComponent, + MasonryComponent, NavigationComponent, AutogrowComponent, ScrollbarComponent, @@ -62,6 +65,7 @@ import { coreFeaturesRoutes } from 'app/modules/admin/docs/core-features/core-fe FuseDateRangeModule, FuseDrawerModule, FuseHighlightModule, + FuseMasonryModule, FuseNavigationModule, FuseScrollResetModule, SharedModule diff --git a/src/app/modules/admin/docs/core-features/core-features.routing.ts b/src/app/modules/admin/docs/core-features/core-features.routing.ts index d7d39b35..0ab76c3f 100644 --- a/src/app/modules/admin/docs/core-features/core-features.routing.ts +++ b/src/app/modules/admin/docs/core-features/core-features.routing.ts @@ -6,6 +6,7 @@ import { CardComponent } from 'app/modules/admin/docs/core-features/components/c import { DateRangeComponent } from 'app/modules/admin/docs/core-features/components/date-range/date-range.component'; import { DrawerComponent } from 'app/modules/admin/docs/core-features/components/drawer/drawer.component'; import { HighlightComponent } from 'app/modules/admin/docs/core-features/components/highlight/highlight.component'; +import { MasonryComponent } from 'app/modules/admin/docs/core-features/components/masonry/masonry.component'; import { NavigationComponent } from 'app/modules/admin/docs/core-features/components/navigation/navigation.component'; import { AutogrowComponent } from 'app/modules/admin/docs/core-features/directives/autogrow/autogrow.component'; import { ScrollbarComponent } from 'app/modules/admin/docs/core-features/directives/scrollbar/scrollbar.component'; @@ -63,6 +64,10 @@ export const coreFeaturesRoutes: Route[] = [ path : 'highlight', component: HighlightComponent }, + { + path : 'masonry', + component: MasonryComponent + }, { path : 'navigation', component: NavigationComponent