🏠 Home 

Reddit Multi Column

Multi column layout for reddit redesign

// ==UserScript==
// @name         Reddit Multi Column
// @namespace    https://gist.github.com/c6p/463892bb243f611f2a3cfa4268c6435e
// @version      0.2.4
// @description  Multi column layout for reddit redesign
// @author       Can Altıparmak
// @homepageURL  https://gist.github.com/c6p/463892bb243f611f2a3cfa4268c6435e
// @match        https://www.reddit.com/*
// @match        https://new.reddit.com/*
// @grant        none
// ==/UserScript==
/* jshint esversion: 6 */
(function() {
'use strict';
const MIN_WIDTH = 400;
const COLUMNS = 4;
let columns = COLUMNS;
let cleanup = null;
let parent = null;
const cardIcon = () => document?.querySelector('shreddit-sort-dropdown[header-text="View"]')?.shadowRoot?.querySelector('svg');
const shouldClean =(icon) => icon === undefined ? false : icon.getAttribute('icon-name') !== "view-card-outline";
cleanup = shouldClean()
let postMap = new Map()
const indexOfSmallest = function (a) {
let lowest = 0;
for (let i = 1; i<a.length; i++) {
if (a[i] < (a[lowest]-1)) lowest = i;
}
return lowest;
};
const makeLayout = function(changes=[]) {
if (cleanup) return;
if (!parent) return;
if (parent.style.position !== "relative") {
document.querySelector("main").style.maxWidth = "100%";
document.querySelector("div.subgrid-container").classList.remove("m:w-[1120px]") // make wide
document.getElementById("right-sidebar-container").style.display = "none" // hide sidebar
parent.style.position = "relative"
}
const cols = Math.floor(parent.offsetWidth / MIN_WIDTH);
columns = cols;
const WIDTH = Math.floor((100-columns)/columns);
const nodes = [...parent.querySelectorAll("article, faceplate-partial").values()]
for (const article of nodes) {
const key = article.ariaLabel
if (key === null) /* faceplate-partial */ {
} else if (key in postMap) {
const post = postMap[key]
if (post.height !== article.offsetHeight) {
post.height = article.offsetHeight
}
} else {
postMap.set(key, {height:article.offsetHeight, col:0, top:0})
}
}
let tops = Array(columns).fill(0);
for (const post of postMap.values()) {
post.col = indexOfSmallest(tops)
post.top = tops[post.col]
tops[post.col] += post.height
}
const height = Math.max(...tops)
if (height) {
parent.style.height = height + 500 + "px"
}
for (const article of nodes) {
const key = article.ariaLabel
const {col, top} = postMap.get(key) ?? {col:0, top:tops[0]}
article.setAttribute("style", cleanup ? "" : `position:absolute; width:${WIDTH}%; top:${top}px; left:${col*(WIDTH+1)}%`)
}
for (const batch of parent.querySelectorAll("faceplate-batch").values()) {
if (!batch.style.height) {
batch.style.height = [...batch.childNodes].reduce((height,c) => height + c.clientHeight, 0) + "px"
}
}
};
const setLayout = function(changes, observer) {
const c = shouldClean(cardIcon());
if (c !== cleanup) {
cleanup = c;
window.requestAnimationFrame(makeLayout)
}
};
const requestLayout = () => window.requestAnimationFrame(makeLayout)
const pageChange = new MutationObserver(requestLayout);
window.addEventListener('resize', requestLayout);
window.addEventListener('scrollend', requestLayout);
const layoutSwitch = new MutationObserver(setLayout);
const watch = function(changes, observer) {
postMap = new Map()
parent = document.querySelector("article + hr + faceplate-partial").parentNode
if (parent === null) return;
pageChange.observe(parent, {childList: true});
const timeout = setTimeout(() => {
const icon = cardIcon();
if (icon !== undefined) {
clearTimeout(timeout);
layoutSwitch.observe(icon, {attributes: true});
}
})
window.requestAnimationFrame(makeLayout);
};
const apply = new MutationObserver(watch);
const app = document.querySelector("shreddit-app")
apply.observe(app, {attributes: true});
watch();
})();