Edit on StackBlitz
import { SortableComponent, SortableItemComponent } from "@lisn.js/react";
import "lisn.js/sortable.css";
import styles from "./demo.module.css";
export default function Page() {
return (
<>
<div className={styles.wrapper}>
<SortableComponent className={styles.demo} config={{ mode: "swap" }}>
<SortableItemComponent
className={[styles.box, styles.r1, styles.c4].join(" ")}
>
<span className={styles.letter}>N</span>
</SortableItemComponent>
<SortableItemComponent
className={[styles.box, styles.r2, styles.c2].join(" ")}
>
<span className={styles.letter}>I</span>
</SortableItemComponent>
<SortableItemComponent
className={[styles.box, styles.r1, styles.c2].join(" ")}
>
<span className={styles.letter}>I</span>
</SortableItemComponent>
<SortableItemComponent
className={[styles.box, styles.r2, styles.c1].join(" ")}
>
<span className={styles.letter}>L</span>
</SortableItemComponent>
<div>{/* dummy */}</div>
<SortableItemComponent
className={[styles.box, styles.r2, styles.c4].join(" ")}
>
<span className={styles.letter}>N</span>
</SortableItemComponent>
<SortableItemComponent
className={[styles.box, styles.r1, styles.c1].join(" ")}
>
<span className={styles.letter}>L</span>
</SortableItemComponent>
<SortableItemComponent
className={[styles.box, styles.r2, styles.c3].join(" ")}
>
<span className={styles.letter}>S</span>
</SortableItemComponent>
<SortableItemComponent
className={[styles.box, styles.r1, styles.c3].join(" ")}
>
<span className={styles.letter}>S</span>
</SortableItemComponent>
<div className={[styles.line, styles.lineV].join(" ")}></div>
<div className={[styles.line, styles.lineH].join(" ")}></div>
</SortableComponent>
</div>
</>
);
}
/* Container */
.wrapper {
position: fixed;
top: 0;
left: 0;
width: 100vw;
width: 100dvw;
height: 100vh;
height: 100dvh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
}
/* Grid */
.demo {
display: grid;
justify-items: center;
align-items: center;
grid-template-rows: repeat(2, 1fr) 0px;
grid-template-columns: repeat(4, 1fr) 0px;
background: var(--text-color-lighter);
box-shadow: var(--text-color) 0px 0px 14px -3px;
}
/* Grid box and content */
.box {
background: var(--bg-color-lighter);
width: 75px;
height: 75px;
text-align: center;
color: var(--text-color-lighter);
font-family: "Roboto Mono", ui-monospace, monospace, sans-serif;
font-size: 100px;
line-height: 1.5em;
position: relative;
overflow: hidden;
margin: 0 -1px;
}
.box .letter {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, 0%);
}
.box.r2 .letter {
transform: translate(-50%, -50%);
}
/* Dummies to create a gap */
.line {
transition-property: width, height;
transition-duration: 1.5s;
}
.lineH {
width: 95px;
}
.lineV {
height: 95px;
}
/* When complete (ordered) */
.box.r1.c1
+ .box.r1.c2
+ .box.r1.c3
+ .box.r1.c4
~ .box.r2.c1
+ .box.r2.c2
+ .box.r2.c3
+ .box.r2.c4
+ .lineV {
height: 75px;
}
.box.r1.c1
+ .box.r1.c2
+ .box.r1.c3
+ .box.r1.c4
~ .box.r2.c1
+ .box.r2.c2
+ .box.r2.c3
+ .box.r2.c4
~ .lineH {
width: 73px;
}
Edit on CodePen
document.addEventListener("DOMContentLoaded", () => {
const sortable = document.getElementById("demo");
const items = sortable.querySelectorAll(".box");
new LISN.widgets.Sortable(sortable, {
items,
mode: "swap",
});
});
<div id="demo">
<div class="box r1 c4"><span class="letter">N</span></div>
<div class="box r2 c2"><span class="letter">I</span></div>
<div class="box r1 c2"><span class="letter">I</span></div>
<div class="box r2 c1"><span class="letter">L</span></div>
<div></div>
<div class="box r2 c4"><span class="letter">N</span></div>
<div class="box r1 c1"><span class="letter">L</span></div>
<div class="box r2 c3"><span class="letter">S</span></div>
<div class="box r1 c3"><span class="letter">S</span></div>
<div class="line line-v"></div>
<div class="line line-h"></div>
</div>
/* Grid */
#demo {
display: grid;
justify-items: center;
align-items: center;
grid-template-rows: repeat(2, 1fr) 0px;
grid-template-columns: repeat(4, 1fr) 0px;
background: var(--text-color-lighter);
box-shadow: var(--text-color) 0px 0px 14px -3px;
}
/* Grid box and content */
#demo .box {
background: var(--bg-color-lighter);
width: 75px;
height: 75px;
text-align: center;
color: var(--text-color-lighter);
font-family: "Roboto Mono", ui-monospace, monospace, sans-serif;
font-size: 100px;
line-height: 1.5em;
position: relative;
overflow: hidden;
margin: 0 -1px;
}
#demo .box .letter {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, 0%);
}
#demo .box.r2 .letter {
transform: translate(-50%, -50%);
}
/* Dummies to create a gap */
#demo .line {
transition-property: width, height;
transition-duration: 1.5s;
}
#demo .line-h {
width: 95px;
}
#demo .line-v {
height: 95px;
}
/* When complete (ordered) */
#demo
.box.r1.c1
+ .box.r1.c2
+ .box.r1.c3
+ .box.r1.c4
~ .box.r2.c1
+ .box.r2.c2
+ .box.r2.c3
+ .box.r2.c4
+ .line-v {
height: 75px;
}
#demo
.box.r1.c1
+ .box.r1.c2
+ .box.r1.c3
+ .box.r1.c4
~ .box.r2.c1
+ .box.r2.c2
+ .box.r2.c3
+ .box.r2.c4
~ .line-h {
width: 73px;
}
Edit on CodePen
<div id="demo" data-lisn-sortable="mode=swap">
<div class="box r1 c4" data-lisn-sortable-item>
<span class="letter">N</span>
</div>
<div class="box r2 c2" data-lisn-sortable-item>
<span class="letter">I</span>
</div>
<div class="box r1 c2" data-lisn-sortable-item>
<span class="letter">I</span>
</div>
<div class="box r2 c1" data-lisn-sortable-item>
<span class="letter">L</span>
</div>
<div></div>
<div class="box r2 c4" data-lisn-sortable-item>
<span class="letter">N</span>
</div>
<div class="box r1 c1" data-lisn-sortable-item>
<span class="letter">L</span>
</div>
<div class="box r2 c3" data-lisn-sortable-item>
<span class="letter">S</span>
</div>
<div class="box r1 c3" data-lisn-sortable-item>
<span class="letter">S</span>
</div>
<div class="line line-v"></div>
<div class="line line-h"></div>
</div>
/* Grid */
#demo {
display: grid;
justify-items: center;
align-items: center;
grid-template-rows: repeat(2, 1fr) 0px;
grid-template-columns: repeat(4, 1fr) 0px;
background: var(--text-color-lighter);
box-shadow: var(--text-color) 0px 0px 14px -3px;
}
/* Grid box and content */
#demo .box {
background: var(--bg-color-lighter);
width: 75px;
height: 75px;
text-align: center;
color: var(--text-color-lighter);
font-family: "Roboto Mono", ui-monospace, monospace, sans-serif;
font-size: 100px;
line-height: 1.5em;
position: relative;
overflow: hidden;
margin: 0 -1px;
}
#demo .box .letter {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, 0%);
}
#demo .box.r2 .letter {
transform: translate(-50%, -50%);
}
/* Dummies to create a gap */
#demo .line {
transition-property: width, height;
transition-duration: 1.5s;
}
#demo .line-h {
width: 95px;
}
#demo .line-v {
height: 95px;
}
/* When complete (ordered) */
#demo
.box.r1.c1
+ .box.r1.c2
+ .box.r1.c3
+ .box.r1.c4
~ .box.r2.c1
+ .box.r2.c2
+ .box.r2.c3
+ .box.r2.c4
+ .line-v {
height: 75px;
}
#demo
.box.r1.c1
+ .box.r1.c2
+ .box.r1.c3
+ .box.r1.c4
~ .box.r2.c1
+ .box.r2.c2
+ .box.r2.c3
+ .box.r2.c4
~ .line-h {
width: 73px;
}