[feat] drag and drop order and folders

This commit is contained in:
kwaroran
2023-05-23 04:51:47 +09:00
parent d174aa0796
commit c42a8710e1
12 changed files with 895 additions and 340 deletions

View File

@@ -6,7 +6,7 @@
settingsOpen,
sideBarStore,
} from "../../ts/stores";
import { DataBase } from "../../ts/database";
import { DataBase, setDatabase, type folder } from "../../ts/database";
import BarIcon from "./BarIcon.svelte";
import SidebarIndicator from "./SidebarIndicator.svelte";
import {
@@ -21,6 +21,8 @@
ListIcon,
LayoutGridIcon,
PlusIcon,
FolderIcon,
FolderOpenIcon,
} from "lucide-svelte";
import {
characterFormatUpdate,
@@ -36,6 +38,10 @@
import { isEqual } from "lodash";
import SidebarAvatar from "./SidebarAvatar.svelte";
import BaseRoundedButton from "../UI/BaseRoundedButton.svelte";
import { get } from "svelte/store";
import { findCharacterIndexbyId, findCharacterbyId, getCharacterIndexObject } from "src/ts/util";
import { v4 } from "uuid";
import { checkCharOrder } from "src/ts/globalApi";
let openPresetList = false;
let sideBarMode = 0;
let editMode = false;
@@ -72,13 +78,47 @@
CharEmotion.set({});
}
let charImages: string[] = [];
type sortTypeNormal = { type:'normal',img: string; index: number; }
type sortType = sortTypeNormal|{type:'folder',folder:sortTypeNormal[],id:string}
let charImages: sortType[] = [];
let IconRounded = false
let openFolders:string[] = []
const unsub = DataBase.subscribe((db) => {
let newCharImages: string[] = [];
for (const cha of db.characters) {
newCharImages.push(cha.image ?? "");
let newCharImages: sortType[] = [];
const idObject = getCharacterIndexObject()
for (const id of db.characterOrder) {
if(typeof(id) === 'string'){
const index = idObject[id] ?? -1
if(index !== -1){
const cha = db.characters[index]
newCharImages.push({
img:cha.image ?? "",
index:index,
type: "normal"
});
}
}
else{
const folder = id
let folderCharImages: sortTypeNormal[] = []
for(const id of folder.data){
const index = idObject[id] ?? -1
if(index !== -1){
const cha = db.characters[index]
folderCharImages.push({
img:cha.image ?? "",
index:index,
type: "normal"
});
}
}
newCharImages.push({
folder: folderCharImages,
type: "folder",
id: folder.id
});
}
}
if (!isEqual(charImages, newCharImages)) {
charImages = newCharImages;
@@ -88,11 +128,160 @@
}
});
const inserter = (mainIndex:DragData, targetIndex:DragData) => {
if(mainIndex.index === targetIndex.index && mainIndex.folder === targetIndex.folder){
return
}
let db = get(DataBase)
let mainFolderIndex = mainIndex.folder ? getFolderIndex(mainIndex.folder) : null
let targetFolderIndex = targetIndex.folder ? getFolderIndex(targetIndex.folder) : null
let mainFolderId = mainIndex.folder ? (db.characterOrder[mainFolderIndex] as folder).id : ''
let mainId = ''
if(mainIndex.folder){
mainId = (db.characterOrder[mainFolderIndex] as folder).data[mainIndex.index]
}
else{
const da = db.characterOrder[mainIndex.index]
if(typeof(da) !== 'string'){
return
}
mainId = da
}
if(targetIndex.folder){
const folder = db.characterOrder[targetFolderIndex] as folder
folder.data.splice(targetIndex.index,0,mainId)
db.characterOrder[targetFolderIndex] = folder
}
else{
db.characterOrder.splice(targetIndex.index,0,mainId)
}
if(mainIndex.folder){
mainFolderIndex = -1
for(let i=0;i<db.characterOrder.length;i++){
const a =db.characterOrder[i]
if(typeof(a) !== 'string'){
if(a.id === mainFolderId){
mainFolderIndex = i
break
}
}
}
if(mainFolderIndex !== -1){
const folder:folder = db.characterOrder[mainFolderIndex] as folder
const ind = mainIndex.index > targetIndex.index ? folder.data.lastIndexOf(mainId) : folder.data.indexOf(mainId)
if(ind !== -1){
folder.data.splice(ind, 1)
}
db.characterOrder[mainFolderIndex] = folder
}
else{
console.log('folder not found')
}
}
else{
const ind = mainIndex.index > targetIndex.index ? db.characterOrder.lastIndexOf(mainId) : db.characterOrder.indexOf(mainId)
if(ind !== -1){
db.characterOrder.splice(ind, 1)
}
}
setDatabase(db)
checkCharOrder()
}
function getFolderIndex(id:string){
let db = get(DataBase)
for(let i=0;i<db.characterOrder.length;i++){
const data = db.characterOrder[i]
if(typeof(data) !== 'string' && data.id === id){
return i
}
}
return -1
}
const createFolder = (mainIndex:DragData, targetIndex:DragData) => {
if(mainIndex.index === targetIndex.index && mainIndex.folder === targetIndex.folder){
return
}
let db = get(DataBase)
let mainFolderIndex = mainIndex.folder ? getFolderIndex(mainIndex.folder) : null
let mainFolder = db.characterOrder[mainFolderIndex] as folder
if(targetIndex.folder){
return
}
const main = mainIndex.folder ? mainFolder.data[mainIndex.index] : db.characterOrder[mainIndex.index]
const target = db.characterOrder[targetIndex.index]
if(typeof(main) !== 'string'){
return
}
if(typeof (target) === 'string'){
const newFolder:folder = {
name: "New Folder",
data: [main, target],
color: "",
id: v4()
}
db.characterOrder[targetIndex.index] = newFolder
if(mainIndex.folder){
mainFolder.data.splice(mainIndex.index, 1)
db.characterOrder[mainFolderIndex] = mainFolder
}
else{
db.characterOrder.splice(mainIndex.index, 1)
}
}
else{
target.data.push(main)
if(mainIndex.folder){
mainFolder.data.splice(mainIndex.index, 1)
db.characterOrder[mainFolderIndex] = mainFolder
}
else{
db.characterOrder.splice(mainIndex.index, 1)
}
}
setDatabase(db)
}
type DragEv = DragEvent & {
currentTarget: EventTarget & HTMLDivElement;
}
type DragData = {
index:number,
folder?:string
}
const avatarDragStart = (ind:DragData, e:DragEv) => {
e.dataTransfer.setData("application/json", JSON.stringify({
type: "risuDrag",
index: ind
}))
const avatar = e.currentTarget.querySelector('.avatar')
if(avatar){
e.dataTransfer.setDragImage(avatar, 10, 10);
}
}
const avatarDragOver = (e:DragEv) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
}
const avatarDrop = (ind:DragData, e:DragEv) => {
e.preventDefault()
try {
const da = JSON.parse(e.dataTransfer.getData("application/json"))
if(da.type === "risuDrag"){
createFolder(da.index,ind)
}
} catch (error) {}
}
onDestroy(unsub);
</script>
<div
class="flex h-full w-20 min-w-20 flex-col items-center overflow-x-hidden overflow-y-scroll bg-bgcolor text-white shadow-lg gap-2"
class="flex h-full w-20 min-w-20 flex-col items-center overflow-x-hidden overflow-y-scroll bg-bgcolor text-white shadow-lg"
class:editMode
>
<button
@@ -102,57 +291,148 @@
}}><ListIcon /></button
>
<div class="h-8 min-h-8 w-14 min-w-14 bg-transparent" />
<div class="h-3 w-14" on:dragover={(e) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
e.currentTarget.classList.add('bg-green-500')
}} on:dragleave={(e) => {
e.currentTarget.classList.remove('bg-green-500')
}} on:drop={(e) => {
e.preventDefault()
e.currentTarget.classList.remove('bg-green-500')
const da = JSON.parse(e.dataTransfer.getData("application/json"))
if(da.type === "risuDrag"){
inserter(da.index,{index:0})
}
}} />
{#if menuMode === 0}
{#each charImages as charimg, i}
<div class="group relative flex items-center px-2">
{#each charImages as char, ind}
<div class="group relative flex items-center px-2"
draggable="true"
on:dragstart={(e) => {avatarDragStart({index:ind}, e)}}
on:dragover={avatarDragOver}
on:drop={(e) => {avatarDrop({index:ind}, e)}}
>
<SidebarIndicator
isActive={$selectedCharID === i && sideBarMode !== 1}
isActive={char.type === 'normal' && $selectedCharID === char.index && sideBarMode !== 1}
/>
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div
on:click={() => {
changeChar(i);
}}
on:keydown={(e) => {
if (e.key === "Enter") {
changeChar(i);
on:click={() => {
if(char.type === "normal"){
changeChar(char.index);
}
}}
on:keydown={(e) => {
if (e.key === "Enter") {
if(char.type === "normal"){
changeChar(char.index);
}
}
}}
tabindex="0"
>
{#if char.type === 'normal'}
<SidebarAvatar src={char.img ? getCharImage(char.img, "plain") : "/none.webp"} size="56" rounded={IconRounded} />
{:else if char.type === "folder"}
<SidebarAvatar src="slot" size="56" rounded={IconRounded} onClick={() => {
if(char.type !== 'folder'){
return
}
if(openFolders.includes(char.id)){
openFolders.splice(openFolders.indexOf(char.id), 1)
}
else{
openFolders.push(char.id)
}
openFolders = openFolders
}}>
{#if openFolders.includes(char.id)}
<FolderOpenIcon />
{:else}
<FolderIcon />
{/if}
</SidebarAvatar>
{/if}
</div>
</div>
{#if char.type === 'folder' && openFolders.includes(char.id)}
<div class="w-full flex flex-col items-center py-1 mt-1 rounded-lg relative">
<div class="absolute top-0 left-1 bg-darkbg w-full h-full rounded-lg z-0"></div>
<div class="h-3 w-14 relative z-10" on:dragover={(e) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
e.currentTarget.classList.add('bg-green-500')
}} on:dragleave={(e) => {
e.currentTarget.classList.remove('bg-green-500')
}} on:drop={(e) => {
e.preventDefault()
e.currentTarget.classList.remove('bg-green-500')
const da = JSON.parse(e.dataTransfer.getData("application/json"))
if(da.type === "risuDrag", char.type === 'folder'){
inserter(da.index,{index:0,folder:char.id})
}
}}
tabindex="0"
>
<SidebarAvatar src={charImages[i] ? getCharImage(charImages[i], "plain") : "/none.webp"} size="56" rounded={IconRounded} />
</div>
{#if editMode}
<div class="mt-2 flex flex-col">
<button
on:click={() => {
let chars = $DataBase.characters;
if (chars[i - 1]) {
const currentchar = chars[i];
chars[i] = chars[i - 1];
chars[i - 1] = currentchar;
$DataBase.characters = chars;
}
}}
}} />
{#each char.folder as char2, ind}
<div class="group relative flex items-center px-2 z-10"
draggable="true"
on:dragstart={(e) => {if(char.type === 'folder'){avatarDragStart({index: ind, folder:char.id}, e)}}}
on:dragover={avatarDragOver}
on:drop={(e) => {if(char.type === 'folder'){avatarDrop({index: ind, folder:char.id}, e)}}}
>
<ArrowUp size={20} />
</button>
<button
on:click={() => {
let chars = $DataBase.characters;
if (chars[i + 1]) {
const currentchar = chars[i];
chars[i] = chars[i + 1];
chars[i + 1] = currentchar;
$DataBase.characters = chars;
}
}}
>
<ArrowDown size={22} />
</button>
</div>
{/if}
</div>
<SidebarIndicator
isActive={$selectedCharID === char2.index && sideBarMode !== 1}
/>
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div
on:click={() => {
if(char2.type === "normal"){
changeChar(char2.index);
}
}}
on:keydown={(e) => {
if (e.key === "Enter") {
if(char2.type === "normal"){
changeChar(char2.index);
}
}
}}
tabindex="0"
>
<SidebarAvatar src={char2.img ? getCharImage(char2.img, "plain") : "/none.webp"} size="56" rounded={IconRounded} />
</div>
</div>
<div class="h-3 w-14 relative z-10" on:dragover={(e) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
e.currentTarget.classList.add('bg-green-500')
}} on:dragleave={(e) => {
e.currentTarget.classList.remove('bg-green-500')
}} on:drop={(e) => {
e.preventDefault()
e.currentTarget.classList.remove('bg-green-500')
const da = JSON.parse(e.dataTransfer.getData("application/json"))
if(da.type === "risuDrag" && char.type === 'folder'){
inserter(da.index,{index:ind+1,folder:char.id})
}
}} />
{/each}
</div>
{/if}
<div class="h-3 w-14" on:dragover={(e) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
e.currentTarget.classList.add('bg-green-500')
}} on:dragleave={(e) => {
e.currentTarget.classList.remove('bg-green-500')
}} on:drop={(e) => {
e.preventDefault()
e.currentTarget.classList.remove('bg-green-500')
const da = JSON.parse(e.dataTransfer.getData("application/json"))
if(da.type === "risuDrag"){
inserter(da.index,{index:ind+1})
}
}} />
{/each}
<div class="flex flex-col items-center space-y-2 px-2">
<BaseRoundedButton