github.com/hernad/nomad@v1.6.112/ui/app/components/flex-masonry.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 import Component from '@glimmer/component'; 7 import { tracked } from '@glimmer/tracking'; 8 import { next } from '@ember/runloop'; 9 import { action } from '@ember/object'; 10 import { minIndex, max } from 'd3-array'; 11 12 export default class FlexMasonry extends Component { 13 @tracked element = null; 14 15 @action 16 captureElement(element) { 17 this.element = element; 18 } 19 20 @action 21 reflow() { 22 next(() => { 23 // There's nothing to do if there is no element 24 if (!this.element) return; 25 26 const items = this.element.querySelectorAll( 27 ':scope > .flex-masonry-item' 28 ); 29 30 // Clear out specified order and flex-basis values in case this was once a multi-column layout 31 if (this.args.columns === 1 || !this.args.columns) { 32 for (let item of items) { 33 item.style.flexBasis = null; 34 item.style.order = null; 35 } 36 this.element.style.maxHeight = null; 37 return; 38 } 39 40 const columns = new Array(this.args.columns).fill(null).map(() => ({ 41 height: 0, 42 elements: [], 43 })); 44 45 // First pass: assign each element to a column based on the running heights of each column 46 for (let item of items) { 47 const styles = window.getComputedStyle(item); 48 const marginTop = parseFloat(styles.marginTop); 49 const marginBottom = parseFloat(styles.marginBottom); 50 const height = item.clientHeight; 51 52 // Pick the shortest column accounting for margins 53 const column = columns[minIndex(columns, (c) => c.height)]; 54 55 // Add the new element's height to the column height 56 column.height += marginTop + height + marginBottom; 57 column.elements.push(item); 58 } 59 60 // Second pass: assign an order to each element based on their column and position in the column 61 columns 62 .mapBy('elements') 63 .flat() 64 .forEach((dc, index) => { 65 dc.style.order = index; 66 }); 67 68 // Guarantee column wrapping as predicted (if the first item of a column is shorter than the difference 69 // beteen the height of the column and the previous column, then flexbox will naturally place the first 70 // item at the end of the previous column). 71 columns.forEach((column, index) => { 72 const nextHeight = 73 index < columns.length - 1 ? columns[index + 1].height : 0; 74 const item = column.elements.lastObject; 75 if (item) { 76 item.style.flexBasis = 77 item.clientHeight + Math.max(0, nextHeight - column.height) + 'px'; 78 } 79 }); 80 81 // Set the max height of the container to the height of the tallest column 82 this.element.style.maxHeight = max(columns.mapBy('height')) + 1 + 'px'; 83 }); 84 } 85 }