add topic sorting & js to support it

This commit is contained in:
2026-05-28 04:19:42 +03:00
parent 303750a281
commit 6b7a0e7a17
8 changed files with 241 additions and 3 deletions

View File

@@ -859,6 +859,53 @@ a.mention {
}
}
ol.sortable-list {
display: flex;
gap: var(--base-padding);
flex-direction: column;
list-style: none;
flex-grow: 1;
margin: 0;
padding-left: 0;
li {
display: flex;
gap: var(--big-padding);
}
li.immovable .dragger {
cursor: not-allowed;
}
}
.plank.dragger {
display: flex;
align-items: center;
/*background-color: var(--bg-color-tertiary);*/
padding: var(--base-padding);
cursor: move;
}
.sortable-item-inner {
display: flex;
gap: var(--base-padding);
flex-grow: 1;
flex-direction: column;
& > * {
flex-grow: 1;
}
&.row {
flex-direction: row;
}
&:not(.row) > * {
margin-right: auto;
}
}
@media (max-width: 768px) {
body {
margin-left: 0;

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="24"
height="24"
viewBox="0 0 24 24"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<rect
style="fill:#000000;stroke:none;stroke-width:0.999996;stroke-linecap:round;stroke-linejoin:round"
id="rect1"
width="16"
height="2"
x="4"
y="8" />
<rect
style="fill:#000000;stroke:none;stroke-width:0.999996;stroke-linecap:round;stroke-linejoin:round"
id="rect2"
width="16"
height="2"
x="4"
y="11" />
<rect
style="fill:#000000;stroke:none;stroke-width:0.999996;stroke-linecap:round;stroke-linejoin:round"
id="rect3"
width="16"
height="2"
x="4"
y="14" />
<path
style="fill:#000000;stroke:none;stroke-linecap:round;stroke-linejoin:round"
d="m 6,6 6,-6 6,6 H 16 L 12,2 8,6 Z"
id="path3" />
<path
style="fill:#000000;stroke:none;stroke-linecap:round;stroke-linejoin:round"
d="m 6,18 6,6 6,-6 h -2 l -4,4 -4,-4 z"
id="path4" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

97
data/static/js/ui.js Normal file
View File

@@ -0,0 +1,97 @@
'use strict';
{
function isBefore(el1, el2) {
if (el2.parentNode === el1.parentNode) {
for (let cur = el1.previousSibling; cur; cur = cur.previousSibling) {
if (cur === el2) return true;
}
}
return false;
}
let draggedItem = null;
function sortableItemDragStart(e, item) {
const box = item.getBoundingClientRect();
const oX = e.clientX - box.left;
const oY = e.clientY - box.top;
draggedItem = item;
item.classList.add('contrast-bg');
e.dataTransfer.setDragImage(item, oX, oY);
e.dataTransfer.effectAllowed = 'move';
}
function sortableItemDragEnd(e, item) {
draggedItem = null;
item.classList.remove('contrast-bg');
}
function sortableItemDragOver(e, item) {
const target = e.target.closest('.sortable-item');
if (!target || target === draggedItem) {
return;
}
const inSameList = draggedItem.dataset.sortableListKey === target.dataset.sortableListKey;
if (!inSameList) {
return;
}
const targetList = draggedItem.closest('.sortable-list');
if (isBefore(draggedItem, target)) {
targetList.insertBefore(draggedItem, target);
} else {
targetList.insertBefore(draggedItem, target.nextSibling);
}
}
const listItemsHandled = new Map();
const getListItemsHandled = (list) => {
return listItemsHandled.get(list) || new Set();
}
function registerSortableList(list) {
list.querySelectorAll('li:not(.immovable)').forEach(item => {
const listItems = getListItemsHandled(list);
listItems.add(item);
listItemsHandled.set(list, listItems);
const dragger = item.querySelector('.dragger');
dragger.addEventListener('dragstart', e => { sortableItemDragStart(e, item) });
dragger.addEventListener('dragend', e => { sortableItemDragEnd(e, item) });
item.addEventListener('dragover', e => { sortableItemDragOver(e, item) });
});
const obs = new MutationObserver(records => {
for (const mutation of records) {
mutation.addedNodes.forEach(node => {
if (!(node instanceof HTMLElement)) return;
if (!node.classList.contains('sortable-item')) return;
const listItems = getListItemsHandled(list);
if (listItems.has(node)) return;
const dragger = node.querySelector('.dragger');
dragger.addEventListener('dragstart', e => { sortableItemDragStart(e, item) });
dragger.addEventListener('dragend', e => { sortableItemDragEnd(e, item) });
node.addEventListener('dragover', e => { sortableItemDragOver(e, node) });
listItems.add(node);
listItemsHandled.set(list, listItems);
});
}
});
obs.observe(list, { childList: true });
}
document.querySelectorAll('.sortable-list').forEach(registerSortableList);
const listsObs = new MutationObserver(records => {
for (const mutation of records) {
mutation.addedNodes.forEach(node => {
if (!(node instanceof HTMLElement)) return;
if (!node.classList.contains('sortable-list')) return;
registerSortableList(node);
});
}
});
listsObs.observe(document.body, { childList: true, subtree: true })
}