yash chapani
yash chapani

Reputation: 45

Item layout overlap only first time render that content

When I used the custom masonry package implement on the angular with help of custome directive then first time content overlapped, I did not calculate proper position Why did this happen?

I check debug then targetindex not proper someTime that's why they overlap with above content so What can i do?

I think the issue happened only in the directive.

enter image description here

import {  Directive, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, Renderer2 } from '@angular/core';
import $ from 'jquery';

@Directive({
  selector: '[appPinGrid]',
  // standalone: true
})
export class PinGridDirective implements OnChanges, OnDestroy  {

  @Input()
  gutter: number = 0;

  @Input()
  columns: number = 0;

  @Input()
  minColWidth: number = 2;

  @Input()
  maxColWidth: number = 10;

  @Input()
  doubleColumnWidth: number = 10;

  @Input()
  itemClass: string = '';

  @Input() public data: any;

  @Output() onLayoutComplete: EventEmitter<any> = new EventEmitter();

  container: any;
  containerWidth: number = 0;
  containeHeight: number = 0;
  columnWidth: any;
  items: any;

  // columns of grid
  columnHeightArray: any;

  // timer for detect changes
  controlSkipper: any;
  resizeControlSkipper: any;

  // host listener for resize window
  // resizeListener: any;

  isSearchFirstTime: any = true;
  constructor(public el: ElementRef, public renderer: Renderer2) { }
  ngOnDestroy() {
    // unsubscribe event listener
    this.resizeListener();
  }

  resizeListener(){
 this.renderer.listen('window', 'resize', () => {
    if (this.resizeControlSkipper)
      clearTimeout(this.controlSkipper)
    this.resizeControlSkipper = setTimeout(() => {
      // console.log('relayout call');
      this.reLayout();
    }, 400);
  });
} 
    

  ngOnChanges(changes: { data: any; }) {
    if (this.isSearchFirstTime) {
      this.isSearchFirstTime = false;
      // this is for skip first changes detect for initialieze all directive (scroll unnecessary api of second page from here so this solution is apply)
    } else {
      if (changes.data) {
        if (this.controlSkipper)
          clearTimeout(this.controlSkipper)

        this.controlSkipper = setTimeout(async () => {
          this.reLayout();
        }, 500);
      }
    }
  }

  reLayout() {
    // console.log('relayout');
    this.container = this.el.nativeElement;
    this.items = this.el.nativeElement.childNodes;
    this.items.forEach((element: any) => {
      $(element).css('opacity', '0');
    });
    this.layoutItem();
  }

  async layoutItem() {
    // console.log('layout');
    this.getColumnWidth();
    this.initColumnHeightarray();

    for (var i = 0; i < this.items.length; i++) {
      this.placeItem(this.items[i]);
    }
    this.items.forEach((element: any) => {
      console.log('element: ', element);
      $(element).css('opacity', '1');
    });
    console.log('layout complete in function');
    await $(this.container).height(Math.max.apply({}, this.columnHeightArray));
    // console.log('set wrapper height', Math.max.apply({}, this.columnHeightArray));
    await this.onLayoutComplete.emit('LayoutComplete');
  }

  initColumnHeightarray() {
    this.resetColumnHeight();
    for (var i = 0; i < this.columns; i++) {
      this.columnHeightArray.push(0);
    }
  }

  resetColumnHeight() {
    this.columnHeightArray = [];
  }

  getColumnWidth() {
    this.containerWidth = this.el.nativeElement.offsetWidth;
    this.columnWidth = Math.floor((this.containerWidth / this.columns) - this.gutter);
    if (this.columnWidth <= Number(this.minColWidth) && this.columns >= 2) {
      this.columns--;
      this.getColumnWidth();
    }
    // in below condition I check double column width
    // because column minus if card width < 200 and suppose column = 1 then it's width is increase 
    // now maximum card width is 300 and container is about 465(200 column width + 2 number of column + 2 * gutter size) something, so that, below condition is become true and add one column i.e. 2.
    // now two column card width is again < 200 thus it will gose to infonite.
    // so I check that if container have enough space to display two column then I add column othersize it will display only one column
    if (this.columnWidth >= Number(this.maxColWidth) && this.containerWidth > this.doubleColumnWidth) {
      this.columns++;
      this.getColumnWidth();
    }
  }

  calculateImageSize() {
  }

  async placeItem(item: { nodeType: number; getElementsByClassName: (arg0: string) => { (): any; new(): any; length: number; }; }) {
    if (item.nodeType != Node.ELEMENT_NODE) {
        return; // Skip non-element nodes
    }

    // Find the column with the minimum height
    const minimumColHeight = Math.min.apply({}, this.columnHeightArray);
    const minColIndex = $.inArray(minimumColHeight, this.columnHeightArray);

    // Calculate the position
    const targetIndex = minColIndex;
    const fixMarginLeft = (this.containerWidth - this.columnWidth * this.columns - this.gutter * (this.columns - 1)) / 2;

    // If the item has the specified class, adjust its size
    if (item.getElementsByClassName(this.itemClass).length > 0) {
        const img = item.getElementsByClassName(this.itemClass)[0];
        $(img).css('position', 'initial');
        // Ensure width and height are correctly set for proper placement
        const aspectRatio = $(img).width() / ($(img).height() || 1);
        $(img).css('width', this.columnWidth);
        $(img).css('height', Math.floor(this.columnWidth / aspectRatio));
    }

    // Calculate the position for the item
    console.log('this.columnHeightArray[targetIndex]: ', this.columnHeightArray[targetIndex]);
    const position = {
      left: (this.columnWidth + this.gutter) * targetIndex + fixMarginLeft,
      top: this.columnHeightArray[targetIndex] + this.gutter // Adding gutter to the top position
    };

    // Position the item
    $(item).css('position', 'absolute');
    $(item).css('top', position.top);
    $(item).css('left', position.left);

    // Update the column height to account for the new item
    this.columnHeightArray[targetIndex] += $(item).outerHeight() + this.gutter; // Update column height including gutter
    console.log('this.columnHeightArray[targetIndex]: ', this.columnHeightArray[targetIndex]);
  }

}

I want to do proper adjust position how can i do?

Upvotes: 0

Views: 28

Answers (0)

Related Questions