UNPKG

@hydroperx/tiles

Version:
173 lines (172 loc) 6.66 kB
// Third-party imports import getOffset from "getoffset"; // Local imports import * as OffsetUtils from "./utils/OffsetUtils"; import * as ScaleUtils from "./utils/ScaleUtils"; import { Layout } from "./Layout"; import * as Attributes from "./Attributes"; /** * Vertical layout. */ export class VerticalLayout extends Layout { /** * Constructor. */ constructor($) { super($); } /** * Rearranges group tiles. */ rearrange() { // Column Y in EM const column_y = new Map(); // Group width in EM const group_w = this.$._group_width * this.$._small_size + (this.$._group_width - 1) * this.$._tile_gap; // Parent width in EM const parent_w = this.$._inline_groups * group_w + (this.$._inline_groups - 1) * this.$._group_gap; // Parent height in EM let parent_h = 0; let max_rows_found = new Map(); // Rearrange group tiles and reposition groups for (let i = 0; i < this.groups.length; i++) { const group = this.groups[i]; group.rearrange(); // ignore from layout any group being dragged. if (group.div?.getAttribute(Attributes.ATTR_DRAGGING) == "true") { continue; } // Reposition group const column = i % this.$._inline_groups; const left = column * group_w + column * this.$._group_gap; const top = column_y.get(column) ?? 0; let h = 0; if (group.div) { group.div.style.transform = `translateX(${left}em) translateY(${top}em)`; h = ((group.div.getBoundingClientRect().height / ScaleUtils.getScale(group.div).y) / this.$._em); } else { h = this.$._small_size * 2; } parent_h = Math.max(parent_h, top + h); column_y.set(column, top + h + this.$._group_gap); max_rows_found.set(column, (max_rows_found.get(column) ?? 0) + 1); } // parent height has some additional increase parent_h += this.$._small_size * 2; // Set parent size const max_rows = Math.max(...Array.from(max_rows_found.values())); parent_h += max_rows == 0 ? 0 : (max_rows - 1) * this.$._group_gap; this.$._container.style.width = parent_w + "em"; this.$._container.style.height = parent_h + "em"; } /** * Snaps location to grid. */ snapToGrid(tile) { // Base offset const offset = getOffset(tile, this.$._container); OffsetUtils.divideOffsetBy(offset, this.$._em); // Basics let resultX = 0, resultY = 0; const column_y = new Map(); // resultX: Find group and tile index const groupWidth = this.$._group_width * this.$._small_size + (this.$._group_width - 1) * this.$._tile_gap; if (offset.x < (-this.$._small_size * 2) - this.$._tile_gap * 4) { return null; } let groupIdx = -1; let groupColumn = -1; let groupStartX = 0; for (let col = 0; col < this.$._inline_groups; col++) { const startX = col * groupWidth + col * this.$._group_gap; const endX = startX + groupWidth; if (offset.x >= startX - this.$._small_size / 2 - this.$._tile_gap && offset.x < endX + this.$._small_size / 2) { groupColumn = col; groupStartX = startX; break; } } if (groupColumn === -1) { return null; } // Find groupIdx in this.groups for this column for (let i = 0, col = 0; i < this.groups.length; i++, col = i % this.$._inline_groups) { if (col === groupColumn) { groupIdx = i; break; } } // Find tile index within group let tileX = 0; let tileStartX = groupStartX; for (; tileX < this.$._group_width; tileX++) { if (offset.x < tileStartX + this.$._small_size / 2) { break; } tileStartX += this.$._small_size + this.$._tile_gap; } resultX = tileX; // resultY // Compute vertical positions for each group as in rearrange() const groupTops = new Map(); // group index -> top Y for (let i = 0; i < this.groups.length; i++) { const column = i % this.$._inline_groups; const prevY = column_y.get(column) ?? 0; groupTops.set(i, prevY); const group = this.groups[i]; let h = 0; if (group.div) { h = ((group.div.getBoundingClientRect().height / ScaleUtils.getScale(group.div).y) / this.$._em); } else { h = this.$._small_size * 2; } column_y.set(column, prevY + h + this.$._group_gap); } // Now snap to the group in the correct column whose bounds contain offset.y for (let i = 0; i < this.groups.length; i++) { const group = this.groups[i]; const column = i % this.$._inline_groups; if (column !== groupColumn) continue; const groupStartY = groupTops.get(i); let h = 0; if (group.div) { h = ((group.div.getBoundingClientRect().height / ScaleUtils.getScale(group.div).y) / this.$._em); } else { h = this.$._small_size * 2; } const groupEndY = groupStartY + h; if (offset.y >= groupStartY - this.$._small_size && offset.y < groupEndY + this.$._small_size) { // Snap to tile within group let accY = groupStartY + this.$._label_height + this.$._tile_gap; let tileY = 0; for (; accY < groupEndY; tileY++) { if (offset.y < accY + this.$._small_size / 2) { resultY = tileY; return { group: group.id, x: resultX, y: resultY, }; } accY += this.$._small_size + this.$._tile_gap; } resultY = tileY; return { group: group.id, x: resultX, y: resultY, }; } } // Request an anonymous group return { group: undefined, x: 0, y: 0, }; } }