UNPKG

react-photo-album

Version:

Responsive photo gallery component for React

150 lines (149 loc) 4.99 kB
import { round, ratio } from "../utils/index.js"; function rankingFunctionComparator(rank) { return (a, b) => rank(b) - rank(a); } function MinHeap(comparator) { let n = 0; const heap = []; const greater = (i, j) => comparator(heap[i], heap[j]) < 0; const swap = (i, j) => { const temp = heap[i]; heap[i] = heap[j]; heap[j] = temp; }; const swim = (i) => { let k = i; let k2 = Math.floor(k / 2); while (k > 1 && greater(k2, k)) { swap(k2, k); k = k2; k2 = Math.floor(k / 2); } }; const sink = (i) => { let k = i; let k2 = k * 2; while (k2 <= n) { if (k2 < n && greater(k2, k2 + 1)) k2 += 1; if (!greater(k, k2)) break; swap(k, k2); k = k2; k2 = k * 2; } }; const push = (element) => { n += 1; heap[n] = element; swim(n); }; const pop = () => { if (n === 0) return void 0; swap(1, n); n -= 1; const max = heap.pop(); sink(1); return max; }; const size = () => n; return { push, pop, size }; } function buildPrecedentsMap(graph, startNode, endNode) { const precedentsMap = /* @__PURE__ */ new Map(); const visited = /* @__PURE__ */ new Set(); const storedShortestPaths = /* @__PURE__ */ new Map(); storedShortestPaths.set(startNode, 0); const queue = MinHeap(rankingFunctionComparator((el) => el[1])); queue.push([startNode, 0]); while (queue.size() > 0) { const [id, weight] = queue.pop(); if (!visited.has(id)) { const neighboringNodes = graph(id); visited.add(id); neighboringNodes.forEach((neighborWeight, neighbor) => { const newWeight = weight + neighborWeight; const currentId = precedentsMap.get(neighbor); const currentWeight = storedShortestPaths.get(neighbor); if (currentWeight === void 0 || currentWeight > newWeight && (currentWeight / newWeight > 1.005 || currentId !== void 0 && currentId < id)) { storedShortestPaths.set(neighbor, newWeight); queue.push([neighbor, newWeight]); precedentsMap.set(neighbor, id); } }); } } return storedShortestPaths.has(endNode) ? precedentsMap : void 0; } function getPathFromPrecedentsMap(precedentsMap, endNode) { if (!precedentsMap) return void 0; const nodes = []; for (let node = endNode; node !== void 0; node = precedentsMap.get(node)) { nodes.push(node); } return nodes.reverse(); } function findShortestPath(graph, startNode, endNode) { return getPathFromPrecedentsMap(buildPrecedentsMap(graph, startNode, endNode), endNode); } function findIdealNodeSearch(photos, containerWidth, targetRowHeight, minPhotos) { return round(containerWidth / targetRowHeight / Math.min(...photos.map((photo) => ratio(photo)))) + (minPhotos || 0) + 2; } function getCommonHeight(photos, containerWidth, spacing, padding) { return (containerWidth - (photos.length - 1) * spacing - 2 * padding * photos.length) / photos.reduce((acc, photo) => acc + ratio(photo), 0); } function cost(photos, i, j, width, spacing, padding, targetRowHeight) { const row = photos.slice(i, j); const commonHeight = getCommonHeight(row, width, spacing, padding); return commonHeight > 0 ? (commonHeight - targetRowHeight) ** 2 * row.length : void 0; } function makeGetRowNeighbors(photos, spacing, padding, containerWidth, targetRowHeight, limitNodeSearch, minPhotos, maxPhotos) { return (node) => { const results = /* @__PURE__ */ new Map(); results.set(node, 0); const startOffset = minPhotos || 1; const endOffset = Math.min(limitNodeSearch, maxPhotos || Infinity); for (let i = node + startOffset; i < photos.length + 1; i += 1) { if (i - node > endOffset) break; const currentCost = cost(photos, node, i, containerWidth, spacing, padding, targetRowHeight); if (currentCost === void 0) break; results.set(i, currentCost); } return results; }; } function computeRowsLayout(photos, spacing, padding, containerWidth, targetRowHeight, minPhotos, maxPhotos) { const limitNodeSearch = findIdealNodeSearch(photos, containerWidth, targetRowHeight, minPhotos); const getNeighbors = makeGetRowNeighbors( photos, spacing, padding, containerWidth, targetRowHeight, limitNodeSearch, minPhotos, maxPhotos ); const path = findShortestPath(getNeighbors, 0, photos.length); if (!path) return void 0; const tracks = []; for (let i = 1; i < path.length; i += 1) { const row = photos.map((photo, index) => ({ photo, index })).slice(path[i - 1], path[i]); const height = getCommonHeight( row.map(({ photo }) => photo), containerWidth, spacing, padding ); tracks.push({ photos: row.map(({ photo, index }) => ({ photo, index, width: height * ratio(photo), height })) }); } return { spacing, padding, containerWidth, tracks, horizontal: true }; } export { computeRowsLayout as default };