UNPKG

@hydroperx/tiles

Version:
313 lines (312 loc) 9.26 kB
// Third-party imports import { gsap } from "gsap"; // Local imports import * as Attributes from "./Attributes"; import { BaseLayout } from "./BaseLayout"; /** * Layout. */ export class Layout { /** * Constructor. */ constructor($) { /** * Tiles back-reference. */ Object.defineProperty(this, "$", { enumerable: true, configurable: true, writable: true, value: void 0 }); /** * Ordered groups. */ Object.defineProperty(this, "groups", { enumerable: true, configurable: true, writable: true, value: [] }); this.$ = $; } } /** * Group. */ export class LayoutGroup { /** * Constructor. */ constructor($, id, div, width, height) { Object.defineProperty(this, "$", { enumerable: true, configurable: true, writable: true, value: $ }); Object.defineProperty(this, "id", { enumerable: true, configurable: true, writable: true, value: id }); Object.defineProperty(this, "div", { enumerable: true, configurable: true, writable: true, value: div }); /** * Unordered tiles. * @hidden */ Object.defineProperty(this, "_tiles", { enumerable: true, configurable: true, writable: true, value: new Map() }); /** * A structure with fine-grained control over tiles. * @hidden */ Object.defineProperty(this, "_layout", { enumerable: true, configurable: true, writable: true, value: void 0 }); this._layout = new BaseLayout({ width, height }); } /** * Returns an immutable unordered list of the contained tiles. */ getTiles() { return [...this._tiles.values()]; } /** * Returns a specific tile. */ getTile(id) { return this._tiles.get(id) ?? null; } /** * Returns whether a tile exists in this group. */ hasTile(id) { return this._tiles.has(id); } /** * Returns whether the group is empty or not. */ isEmpty() { return this._tiles.size == 0; } /** * Layout size in small tiles unit (1x1). */ getLayoutSize() { return this._layout.getLayoutSize(); } /** * Rearranges group tiles and resizes the group's tiles div. */ rearrange() { // Reposition tiles (update the group's width/height EM together) let changed = false; let tiles_width_em = 0, tiles_height_em = 0; if (this.$.$._dir == "vertical") { tiles_width_em = this.$.$._group_width * this.$.$._small_size + (this.$.$._group_width - 1) * this.$.$._tile_gap; } else { tiles_height_em = this.$.$._height * this.$.$._small_size + (this.$.$._height - 1) * this.$.$._tile_gap; } const to_tween_y_late = []; for (const [, tile] of this._tiles) { const x_em = tile.x * this.$.$._small_size + tile.x * this.$.$._tile_gap; const y_em = tile.y * this.$.$._small_size + tile.y * this.$.$._tile_gap; const w_em = tile.width * this.$.$._small_size + (tile.width - 1) * this.$.$._tile_gap; const h_em = tile.height * this.$.$._small_size + (tile.height - 1) * this.$.$._tile_gap; // change tiles size em tiles_width_em = Math.max(x_em + w_em, tiles_width_em); tiles_height_em = Math.max(y_em + h_em, tiles_height_em); // new X/Y state const state = this.$.$._state.tiles.get(tile.id); if (state) { const old_x = state.x, old_y = state.y; state.x = tile.x; state.y = tile.y; // state change if (!(old_x == state.x && old_y == state.y)) { changed = true; } // affect button if (tile.button && tile.button.getAttribute(Attributes.ATTR_DRAGGING) != "true") { if (tile.positioned) { if (tile.tween) { tile.tween.kill(); } if (old_x != state.x && old_y != state.y) { // change only Y tile.button.style.transform = `translateX(${x_em}em) translateY(-1000em)`; to_tween_y_late.push({ tile, button: tile.button, hEM: h_em, yEM: y_em }); } else { // change either only X or only Y tile.tween = gsap.to(tile.button, { x: x_em + "em", y: y_em + "em", duration: 0.18 }); } // first position } else { tile.button.style.transform = `translateX(${x_em}em) translateY(${y_em}em)`; tile.positioned = true; } } } } // Tween Y from off view const middle = tiles_height_em / 2; for (const { tile, button, hEM, yEM } of to_tween_y_late) { tile.tween = gsap.fromTo(tile.button, { y: (yEM + hEM / 2 < middle ? -hEM : tiles_height_em + hEM) + "em", }, { y: yEM + "em", duration: 0.18 }); } // Resize groupTiles div if (this.div) { const group_tiles_div = this.div.getElementsByClassName(this.$.$._class_names.groupTiles)[0]; let min_w = 0; if (this.$.$._dir == "horizontal") { min_w = 18; } group_tiles_div.style.width = Math.max(min_w, tiles_width_em) + "em"; group_tiles_div.style.height = tiles_height_em + "em"; } // State update signal if (changed) { this.$.$._deferred_state_update_signal(); } } } /** * Tile. */ export class LayoutTile { /** * Cosntructor. * @param button If `null` indicates this is a placeholder tile. */ constructor(id, button) { Object.defineProperty(this, "id", { enumerable: true, configurable: true, writable: true, value: id }); Object.defineProperty(this, "button", { enumerable: true, configurable: true, writable: true, value: button }); /** * Cached tween. */ Object.defineProperty(this, "tween", { enumerable: true, configurable: true, writable: true, value: null }); /** * Cached indicator for initial position. */ Object.defineProperty(this, "positioned", { enumerable: true, configurable: true, writable: true, value: false }); /** * Parent layout group. */ Object.defineProperty(this, "$", { enumerable: true, configurable: true, writable: true, value: null }); } /** * Attempts to contributes the tile to the layout. * If `x` and `y` are both given as `null`, then the * method is guaranteed to always succeed, contributing * the tile to the best last position. */ addTo($, x, y, width, height) { if ($._layout.addTile(this.id, x, y, width, height)) { $._tiles.set(this.id, this); this.$ = $; return true; } return false; } /** * Removes the tile from the parent `LayoutGroup`. * This method does not, however, remove the tile * from the overall state. */ remove() { this.positioned = false; this.$._layout.removeTile(this.id); this.$._tiles.delete(this.id); } /** * X coordinate in small tiles. */ get x() { return this.$._layout.tiles.get(this.id).x; } /** * Y coordinate in small tiles. */ get y() { return this.$._layout.tiles.get(this.id).y; } /** * Width in small tiles. */ get width() { return this.$._layout.tiles.get(this.id).width; } /** * Height in small tiles. */ get height() { return this.$._layout.tiles.get(this.id).height; } /** * Moves position. */ move(x, y) { return this.$._layout.moveTile(this.id, x, y); } /** * Resizes tile. */ resize(width, height) { return this.$._layout.resizeTile(this.id, width, height); } }