255 lines
7.2 KiB
Svelte
255 lines
7.2 KiB
Svelte
<script lang="ts">
|
|
//@ts-nocheck
|
|
import { quintOut } from "svelte/easing";
|
|
import { crossfade } from "svelte/transition";
|
|
import { flip } from "svelte/animate";
|
|
// FLIP ANIMATION
|
|
const [send, receive] = crossfade({
|
|
duration: (d) => Math.sqrt(d * 200),
|
|
fallback(node, params) {
|
|
const style = getComputedStyle(node);
|
|
const transform = style.transform === "none" ? "" : style.transform;
|
|
return {
|
|
duration: 600,
|
|
easing: quintOut,
|
|
css: (t) => `
|
|
transform: ${transform} scale(${t});
|
|
opacity: ${t}
|
|
`,
|
|
};
|
|
},
|
|
});
|
|
// DRAG AND DROP
|
|
let isOver = false;
|
|
let isDragging = false;
|
|
const getDraggedParent = (node) => {
|
|
return node.dataset && node.dataset.index
|
|
? node.dataset
|
|
: getDraggedParent(node.parentNode);
|
|
};
|
|
const click = (ev) => {
|
|
dispatch("slotclick", {
|
|
source: ev.target.parentElement.dataset.index,
|
|
sourceItem: list[ev.target.parentElement.dataset.index],
|
|
});
|
|
};
|
|
const start = (ev) => {
|
|
isDragging = true;
|
|
ev.dataTransfer.setData("source", ev.target.dataset.index);
|
|
};
|
|
const over = (ev) => {
|
|
ev.preventDefault();
|
|
let dragged = getDraggedParent(ev.target);
|
|
if (isOver !== dragged.id) isOver = JSON.parse(dragged.id);
|
|
};
|
|
const leave = (ev) => {
|
|
let dragged = getDraggedParent(ev.target);
|
|
if (isOver === dragged.id) isOver = false;
|
|
};
|
|
const drop = (ev) => {
|
|
isOver = false;
|
|
ev.preventDefault();
|
|
let dragged = getDraggedParent(ev.target);
|
|
let from = ev.dataTransfer.getData("source");
|
|
let to = dragged.index;
|
|
reorder({ from, to });
|
|
isDragging = false;
|
|
};
|
|
// DISPATCH REORDER
|
|
import {
|
|
createEventDispatcher,
|
|
onMount,
|
|
afterUpdate,
|
|
beforeUpdate,
|
|
tick,
|
|
} from "svelte";
|
|
|
|
const dispatch = createEventDispatcher();
|
|
const reorder = ({ from, to }) => {
|
|
let newList = [...list];
|
|
let temp = newList[from];
|
|
newList[from] = newList[to];
|
|
newList[to] = temp;
|
|
//newList[from] = [newList[to], (newList[to] = newList[from])][0];
|
|
dispatch("sort", { data: newList, from: from, to: to });
|
|
};
|
|
// UTILS
|
|
const getKey = (item) => (key ? item[key] : item);
|
|
|
|
type ListItem = {
|
|
displayTileSize: number;
|
|
endsGroup: boolean;
|
|
startsNewGroup: boolean;
|
|
};
|
|
// PROPS
|
|
export let list: ListItem[][];
|
|
export let key;
|
|
export let ulclass;
|
|
|
|
export let breakon: string | null = null;
|
|
|
|
export let breakComponent;
|
|
// return `<><li class='${breakClass}' style='height:100%; grid-column: span 5;'><div style='width:100%;height:inherit;'></li>`;
|
|
|
|
// export let breakPosition = "beforebegin";
|
|
let breakClass = "group-break";
|
|
|
|
const Elements = () => document.getElementsByClassName(key);
|
|
|
|
const removeOldBreaks = (parent) => {
|
|
if (breakon == null) return;
|
|
if (isDragging) return;
|
|
|
|
if (parent.length) {
|
|
const old = parent[0].getElementsByClassName(breakClass);
|
|
|
|
for (let i = 0; i < old.length; i = 0) {
|
|
old[i].remove(); // removing the node from the DOM also removes it from this array (WTF??)
|
|
}
|
|
}
|
|
};
|
|
|
|
let lastCollection;
|
|
|
|
let updatingBreakPoints = false;
|
|
|
|
const updateBreak = () => {
|
|
if (isDragging || updatingBreakPoints) return;
|
|
if (breakon == null) return;
|
|
|
|
try {
|
|
updatingBreakPoints = true;
|
|
|
|
let breaks = 0;
|
|
let last = null;
|
|
let carry = 0;
|
|
let elements = Elements();
|
|
|
|
for (let x = 0; x < list.length; x++) {
|
|
if (last != null) {
|
|
if (list[x][breakon] > last) {
|
|
if (elements.length) {
|
|
let targetIndex = x + breaks;
|
|
|
|
if (
|
|
list.length !==
|
|
elements[0].children.length - breaks
|
|
) {
|
|
if (lastCollection)
|
|
if (lastCollection.length)
|
|
targetIndex += lastCollection.length;
|
|
}
|
|
// elements[0].children[targetIndex].insertAdjacentHTML(breakPosition, breakHtml(carry + 1));
|
|
breaks++;
|
|
carry = 0;
|
|
}
|
|
} else {
|
|
carry++;
|
|
}
|
|
}
|
|
|
|
last = list[x][breakon] > last ? list[x][breakon] : last;
|
|
}
|
|
|
|
lastCollection = list;
|
|
} finally {
|
|
updatingBreakPoints = false;
|
|
}
|
|
};
|
|
|
|
//beforeUpdate(() => {updateBreak()});
|
|
|
|
let updating = false;
|
|
|
|
afterUpdate(async () => {
|
|
if (isDragging) return;
|
|
if (breakon == null) return;
|
|
|
|
updating = true;
|
|
removeOldBreaks(Elements());
|
|
await tick();
|
|
updateBreak();
|
|
|
|
updating = false;
|
|
});
|
|
|
|
const getClass = () => {
|
|
let ret = "";
|
|
if (key) ret += ` ${key}`;
|
|
if (ulclass) ret += ` ${ulclass}`;
|
|
|
|
return ret;
|
|
};
|
|
</script>
|
|
|
|
{#if list && list.length}
|
|
<ul class={getClass()}>
|
|
{#each list as item, index (getKey(item))}
|
|
<li
|
|
class:small={item.displayTileSize === 2}
|
|
class:medium={item.displayTileSize === 3}
|
|
class:large={item.displayTileSize === 4}
|
|
class:startsNewGroup={item.startsNewGroup}
|
|
class:endsGroup={item.endsGroup}
|
|
data-index={index}
|
|
data-item={JSON.stringify(item)}
|
|
data-id={JSON.stringify(getKey(item))}
|
|
draggable="true"
|
|
on:dragstart={start}
|
|
on:dragover={over}
|
|
on:dragleave={leave}
|
|
on:drop={drop}
|
|
on:click={click}
|
|
in:receive={{ key: getKey(item) }}
|
|
out:send={{ key: getKey(item) }}
|
|
animate:flip={{ duration: 300 }}
|
|
class:over={getKey(item) === isOver}
|
|
>
|
|
<slot {item} {index}>
|
|
<p>{getKey(item)}</p>
|
|
</slot>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
|
|
<style>
|
|
ul {
|
|
display: inline-grid;
|
|
list-style: none;
|
|
padding: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
li {
|
|
display: inline-block;
|
|
border: 2px dotted transparent;
|
|
transition: border 0.1s linear;
|
|
width: 100%;
|
|
height: 100%;
|
|
margin: 0.5%;
|
|
}
|
|
|
|
.startsNewGroup {
|
|
grid-column-start: 1;
|
|
}
|
|
|
|
.small {
|
|
grid-column-end: span 1;
|
|
grid-row: span 1;
|
|
}
|
|
|
|
.medium {
|
|
grid-column-end: span 2;
|
|
grid-row: span 1;
|
|
}
|
|
|
|
.large {
|
|
grid-column-end: span 2;
|
|
/*grid-row: span 2; large buttons are double height, but they also push the row below them down (they dont fit around eachother like css-grid) */
|
|
}
|
|
|
|
.over {
|
|
border-color: rgba(48, 12, 200, 0.2);
|
|
}
|
|
</style>
|