feat: Instance card dragging

This commit is contained in:
LynithDev 2024-03-17 15:12:50 +01:00
parent 0f5b035e17
commit 71c98255a1
5 changed files with 167 additions and 21 deletions

View file

@ -18,6 +18,7 @@
"@solidjs/router": "^0.13.0",
"@tauri-apps/api": "2.0.0-beta.5",
"@tauri-apps/plugin-shell": "2.0.0-beta.2",
"@thisbeyond/solid-dnd": "^0.7.5",
"@untitled-theme/icons-solid": "^0.6.0",
"overlayscrollbars": "^2.6.1",
"overlayscrollbars-solid": "^0.5.3",

12
pnpm-lock.yaml generated
View file

@ -17,6 +17,9 @@ dependencies:
'@tauri-apps/plugin-shell':
specifier: 2.0.0-beta.2
version: 2.0.0-beta.2
'@thisbeyond/solid-dnd':
specifier: ^0.7.5
version: 0.7.5(solid-js@1.8.15)
'@untitled-theme/icons-solid':
specifier: ^0.6.0
version: 0.6.0(solid-js@1.8.15)
@ -1113,6 +1116,15 @@ packages:
'@tauri-apps/api': 2.0.0-beta.4
dev: false
/@thisbeyond/solid-dnd@0.7.5(solid-js@1.8.15):
resolution: {integrity: sha512-DfI5ff+yYGpK9M21LhYwIPlbP2msKxN2ARwuu6GF8tT1GgNVDTI8VCQvH4TJFoVApP9d44izmAcTh/iTCH2UUw==}
engines: {node: '>=18.0.0', pnpm: '>=8.6.0'}
peerDependencies:
solid-js: ^1.5
dependencies:
solid-js: 1.8.15
dev: false
/@types/babel__core@7.20.5:
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
dependencies:

View file

@ -1,6 +1,6 @@
import { A } from '@solidjs/router';
import { createSignal } from 'solid-js';
import { Bell01Icon, Cloud01Icon, Settings01Icon, TerminalBrowserIcon, TerminalIcon } from '@untitled-theme/icons-solid';
import { Bell01Icon, Cloud01Icon, Settings01Icon, TerminalBrowserIcon } from '@untitled-theme/icons-solid';
import useAccount from '../hooks/useAccount';
import PolyfrostFull from './logos/PolyfrostFull';
import AccountPopup from './overlay/AccountPopup';

View file

@ -1,8 +1,88 @@
import type { DragEventHandler, Id } from '@thisbeyond/solid-dnd';
import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter, createSortable, useDragDropContext } from '@thisbeyond/solid-dnd';
import type { JSX } from 'solid-js';
import { For, createSignal } from 'solid-js';
declare module 'solid-js' {
// eslint-disable-next-line ts/no-namespace
namespace JSX {
interface Directives {
sortable: any;
}
}
}
interface ItemProps {
item: boolean | Id | Node | JSX.ArrayElement | null | undefined;
};
function Item(props: ItemProps) {
const id = () => props.item as Id;
// eslint-disable-next-line solid/reactivity
const sortable = createSortable(id());
// @ts-expect-error - `state` is not defined in the type definition
const [state] = useDragDropContext();
return (
<div
use:sortable
class="sortable"
classList={{
'opacity-25': sortable.isActiveDraggable,
'transition-transform': !!state.active.draggable,
}}
>
{props.item}
</div>
);
}
function List() {
const [items, setItems] = createSignal([1, 2, 3]);
const [activeItem, setActiveItem] = createSignal<number | null>(null);
const ids = () => items();
const onDragStart: DragEventHandler = ({ draggable }) => setActiveItem(draggable.id as number);
const onDragEnd: DragEventHandler = ({ draggable, droppable }) => {
if (draggable && droppable) {
const currentItems = ids();
const fromIndex = currentItems.indexOf(draggable.id as number);
const toIndex = currentItems.indexOf(droppable.id as number);
if (fromIndex !== toIndex) {
const updatedItems = currentItems.slice();
updatedItems.splice(toIndex, 0, ...updatedItems.splice(fromIndex, 1));
setItems(updatedItems);
}
}
};
return (
<DragDropProvider
onDragStart={onDragStart}
onDragEnd={onDragEnd}
collisionDetector={closestCenter}
>
<DragDropSensors />
<div class="column self-stretch">
<SortableProvider ids={ids()}>
<For each={items()}>{item => <Item item={item} />}</For>
</SortableProvider>
</div>
<DragOverlay>
<div class="sortable">{activeItem()}</div>
</DragOverlay>
</DragDropProvider>
);
}
function BrowserPage() {
return (
<h1>
Browser
</h1>
<div class="flex flex-col gap-y-4">
<h1>Browser</h1>
<List />
</div>
);
}

View file

@ -6,7 +6,9 @@ import {
PlusIcon,
SearchMdIcon,
} from '@untitled-theme/icons-solid';
import { For, createEffect, createSignal } from 'solid-js';
import { For, createSignal } from 'solid-js';
import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter, createSortable, useDragDropContext } from '@thisbeyond/solid-dnd';
import type { DragEventHandler, Id } from '@thisbeyond/solid-dnd';
import image from '../../assets/images/header.png';
import Button from '../components/base/Button';
import Tag from '../components/base/Tag';
@ -61,14 +63,28 @@ interface InstanceCardProps {
name: string;
version: string;
client: game.Client;
id: number;
mods?: number;
cover?: string;
lastPlayed?: number;
}
declare module 'solid-js' {
// eslint-disable-next-line ts/no-namespace
namespace JSX {
interface Directives {
sortable: any;
}
}
}
function InstanceCard(props: InstanceCardProps) {
const id = () => props.id as Id;
// eslint-disable-next-line solid/reactivity
const sortable = createSortable(id());
return (
<div class="h-[152px] group flex flex-col rounded-xl bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed border border-gray-0.05 overflow-hidden">
<div use:sortable={sortable} class="h-[152px] group flex flex-col rounded-xl bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed border border-gray-0.05 overflow-hidden">
<div class="flex-1 relative overflow-hidden">
<div
class="absolute h-full w-full group-hover:!scale-110 transition-transform"
@ -105,27 +121,44 @@ interface InstanceGroupProps {
}
function InstanceGroup(props: InstanceGroupProps) {
const [sorted, setSorted] = createSignal<InstanceCardProps[]>([]);
// eslint-disable-next-line solid/reactivity
const [instances, setInstances] = createSignal(props.instances);
const ids = () => instances().map(instance => instance.id);
createEffect(() => {
setSorted(props.instances.sort((a, b) => {
if (a.lastPlayed && b.lastPlayed)
return b.lastPlayed - a.lastPlayed;
const onDragStart: DragEventHandler = ({ draggable }) => {
draggable.node.style.zIndex = '1000';
};
return 0;
}));
});
const onDragEnd: DragEventHandler = ({ draggable, droppable }) => {
draggable.node.style.zIndex = '';
if (draggable && droppable) {
const currentItems = ids();
const fromIndex = currentItems.indexOf(draggable.id as number);
const toIndex = currentItems.indexOf(droppable.id as number);
if (fromIndex !== toIndex) {
const updatedItems = instances().slice();
updatedItems.splice(toIndex, 0, ...updatedItems.splice(fromIndex, 1));
setInstances(updatedItems);
}
}
};
return (
<div class="flex flex-col gap-y-4">
<h4>{props.title}</h4>
<div class="grid grid-cols-4 gap-4">
<For each={sorted()}>
{instance => (
<InstanceCard {...instance} />
)}
</For>
</div>
<DragDropProvider
onDragStart={onDragStart}
onDragEnd={onDragEnd}
collisionDetector={closestCenter}
>
<DragDropSensors />
<div class="grid grid-cols-4 gap-4">
<SortableProvider ids={ids()}>
<For each={instances()}>{item => <InstanceCard {...item} />}</For>
</SortableProvider>
</div>
</DragDropProvider>
</div>
);
}
@ -136,41 +169,61 @@ function HomePage() {
name: 'Hypixel',
client: 'vanilla',
version: '1.8.9',
id: 1,
},
{
name: 'OneConfig ??',
client: 'forge',
version: '1.8.9',
mods: 12,
id: 2,
},
{
name: 'Fabulously Optimised',
client: 'fabric',
version: '1.20.4',
mods: 48,
id: 3,
},
{
name: 'sus client',
client: 'other',
version: '1.8.9',
id: 4,
},
{
name: 'Hypixel',
client: 'vanilla',
version: '1.8.9',
id: 5,
},
{
name: 'OneConfig ??',
client: 'forge',
version: '1.8.9',
mods: 12,
id: 6,
},
{
name: 'Fabulously Optimised',
client: 'fabric',
version: '1.20.4',
mods: 48,
id: 7,
},
{
name: 'sus client',
client: 'other',
version: '1.8.9',
id: 8,
},
// {
// name: 'OneConfig ??',
// client: 'forge',
// version: '1.8.9',
// mods: 12,
// id: 5,
// },
];
return (