chore: Start work on drag n drop
This commit is contained in:
parent
2ec1a04c8b
commit
5dcc24e682
5 changed files with 151 additions and 27 deletions
|
|
@ -1,5 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashMap, path::PathBuf, str::FromStr};
|
||||
|
||||
use onelauncher::cluster::content::logger;
|
||||
use onelauncher::cluster::{self};
|
||||
|
|
@ -25,6 +24,12 @@ pub async fn get_clusters() -> Result<Vec<Cluster>, String> {
|
|||
Ok(onelauncher::cluster::list(None).await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_clusters_grouped() -> Result<HashMap<String, Vec<Cluster>>, String> {
|
||||
Ok(onelauncher::cluster::list_grouped(None).await?)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
pub struct CreateCluster {
|
||||
name: String,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ macro_rules! collect_commands {
|
|||
remove_cluster,
|
||||
get_cluster,
|
||||
get_clusters,
|
||||
get_clusters_grouped,
|
||||
run_cluster,
|
||||
get_cluster_logs,
|
||||
get_cluster_log,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,21 @@ import {
|
|||
SearchMdIcon,
|
||||
} from '@untitled-theme/icons-solid';
|
||||
import type { Accessor, Setter } from 'solid-js';
|
||||
import { For, Match, Switch, createSignal } from 'solid-js';
|
||||
import { For, Show, batch, createMemo, createSignal, untrack } from 'solid-js';
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
import {
|
||||
DragDropProvider,
|
||||
DragDropSensors,
|
||||
type DragEventHandler,
|
||||
DragOverlay,
|
||||
type Draggable,
|
||||
type Droppable,
|
||||
SortableProvider,
|
||||
closestCenter,
|
||||
createDroppable,
|
||||
createSortable,
|
||||
} from '@thisbeyond/solid-dnd';
|
||||
import { mergeRefs } from '@solid-primitives/refs';
|
||||
import image from '../../assets/images/header.png';
|
||||
import ContextMenu from '../components/overlay/ContextMenu';
|
||||
import Button from '../components/base/Button';
|
||||
|
|
@ -16,15 +29,42 @@ import Tag from '../components/base/Tag';
|
|||
import TextField from '../components/base/TextField';
|
||||
import ClusterCover from '~ui/components/game/ClusterCover';
|
||||
import type { Cluster } from '~bindings';
|
||||
import useCommand from '~ui/hooks/useCommand';
|
||||
import useCommand, { tryResult } from '~ui/hooks/useCommand';
|
||||
import { bridge } from '~imports';
|
||||
import { upperFirst } from '~utils/primitives';
|
||||
import { useClusterModalController } from '~ui/components/overlay/cluster/ClusterCreationModal';
|
||||
|
||||
function HomePage() {
|
||||
const [clusters, { refetch }] = useCommand(bridge.commands.getClusters);
|
||||
const [clusters, { refetch, mutate }] = useCommand(bridge.commands.getClustersGrouped);
|
||||
const controller = useClusterModalController();
|
||||
|
||||
function setClusters(group: string, clusters: Cluster[]) {
|
||||
mutate((prev) => {
|
||||
if (prev === undefined)
|
||||
prev = {};
|
||||
|
||||
prev[group] = clusters;
|
||||
return prev;
|
||||
});
|
||||
}
|
||||
|
||||
const containerIds = () => Object.keys(clusters);
|
||||
|
||||
const isContainer = (id: string) => containerIds().includes(id);
|
||||
|
||||
function getContainer(id: string) {
|
||||
const list = untrack(() => clusters());
|
||||
|
||||
if (list === undefined)
|
||||
return undefined;
|
||||
|
||||
for (const [key, items] of Object.entries(list))
|
||||
if (items.find(cluster => cluster.uuid === id))
|
||||
return key;
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
async function newCluster() {
|
||||
try {
|
||||
await controller.start();
|
||||
|
|
@ -36,6 +76,44 @@ function HomePage() {
|
|||
refetch();
|
||||
}
|
||||
|
||||
const move = (draggable: Draggable, droppable: Droppable, onlyWhenChangingContainer: boolean) => {
|
||||
// console.log('Draggable', draggable, 'Droppable', droppable);
|
||||
const draggableContainer = getContainer(draggable.id);
|
||||
const droppableContainer = isContainer(droppable.id)
|
||||
? droppable.id
|
||||
: getContainer(droppable.id);
|
||||
|
||||
if (
|
||||
draggableContainer != droppableContainer
|
||||
|| !onlyWhenChangingContainer
|
||||
) {
|
||||
const containerItemIds = clusters()![droppableContainer];
|
||||
let index = containerItemIds.indexOf(droppable.id);
|
||||
if (index === -1)
|
||||
index = containerItemIds.length;
|
||||
|
||||
batch(() => {
|
||||
setClusters(draggableContainer, items =>
|
||||
items.filter(item => item !== draggable.id));
|
||||
setClusters(droppableContainer, items => [
|
||||
...items.slice(0, index),
|
||||
draggable.id,
|
||||
...items.slice(index),
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onDragEnd: DragEventHandler = (event) => {
|
||||
if (event.draggable && event.droppable)
|
||||
move(event.draggable, event.droppable, false);
|
||||
};
|
||||
|
||||
const onDragOver: DragEventHandler = (event) => {
|
||||
if (event.draggable && event.droppable)
|
||||
move(event.draggable, event.droppable, true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="flex flex-col h-full gap-y-4 text-fg-primary">
|
||||
<Banner />
|
||||
|
|
@ -54,17 +132,32 @@ function HomePage() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Switch>
|
||||
<Match when={clusters()?.length === 0}>
|
||||
<DragDropProvider
|
||||
onDragOver={onDragOver}
|
||||
onDragEnd={onDragEnd}
|
||||
>
|
||||
<DragDropSensors />
|
||||
<Show
|
||||
when={Object.keys(clusters() ?? {}).length === 0}
|
||||
fallback={(
|
||||
<For each={Object.entries(clusters() ?? {})}>
|
||||
{([group, clusters]) => (
|
||||
<ClusterGroup title={group} clusters={clusters} />
|
||||
)}
|
||||
</For>
|
||||
)}
|
||||
>
|
||||
<div class="flex flex-col flex-1 gap-y-4 max-h-64 justify-center items-center">
|
||||
<span class="text-lg font-bold uppercase text-fg-secondary">No clusters were found.</span>
|
||||
<span class="text-xl font-bold">Create one now with the New Cluster button.</span>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={clusters() !== undefined}>
|
||||
<ClusterGroup title="Clusters" clusters={clusters()!} />
|
||||
</Match>
|
||||
</Switch>
|
||||
</Show>
|
||||
<DragOverlay>
|
||||
{draggable => (
|
||||
draggable?.node.cloneNode(true)
|
||||
)}
|
||||
</DragOverlay>
|
||||
</DragDropProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -170,6 +263,7 @@ function ClusterCard(props: Cluster) {
|
|||
const navigate = useNavigate();
|
||||
const [pos, setPos] = createSignal({ x: 0, y: 0 });
|
||||
const [contextMenuVisible, setContextMenuVisible] = createSignal(false);
|
||||
const sortable = createSortable(props.uuid);
|
||||
let ref!: HTMLDivElement;
|
||||
|
||||
function openClusterPage(_e: MouseEvent) {
|
||||
|
|
@ -192,7 +286,7 @@ function ClusterCard(props: Cluster) {
|
|||
onClick={e => openClusterPage(e)}
|
||||
onMouseDown={e => onMouseDown(e)}
|
||||
onContextMenu={e => openContextMenu(e)}
|
||||
ref={ref}
|
||||
ref={mergeRefs(ref, sortable)}
|
||||
class="relative h-[152px] group flex flex-col rounded-xl bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed border border-gray-05"
|
||||
>
|
||||
<div class="flex-1 relative overflow-hidden rounded-t-xl">
|
||||
|
|
@ -239,11 +333,16 @@ interface ClusterGroupProps {
|
|||
}
|
||||
|
||||
function ClusterGroup(props: ClusterGroupProps) {
|
||||
const droppable = createDroppable(props.title);
|
||||
const ids = createMemo(() => props.clusters.map(cluster => cluster.uuid));
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<div ref={droppable} class="flex flex-col gap-y-4">
|
||||
<h4>{props.title}</h4>
|
||||
<div class="grid grid-cols-4 2xl:grid-cols-6 gap-4">
|
||||
<For each={props.clusters}>{item => <ClusterCard {...item} />}</For>
|
||||
<SortableProvider ids={ids()}>
|
||||
<For each={props.clusters}>{item => <ClusterCard {...item} />}</For>
|
||||
</SortableProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,25 @@ pub async fn list(clear: Option<bool>) -> crate::Result<Vec<Cluster>> {
|
|||
.collect())
|
||||
}
|
||||
|
||||
/// get a Map of [`Cluster`] group names containing lists of [`Cluster`]s
|
||||
#[tracing::instrument]
|
||||
pub async fn list_grouped(clear: Option<bool>) -> crate::Result<HashMap<String, Vec<Cluster>>> {
|
||||
// TODO: This can 100% be made better
|
||||
let list = list(clear).await?;
|
||||
let mut map = HashMap::<String, Vec<Cluster>>::new();
|
||||
|
||||
for cluster in list {
|
||||
let group = cluster.meta.group.clone().unwrap_or(String::from("Ungrouped"));
|
||||
if let Some(items) = map.get_mut(&group) {
|
||||
items.push(cluster);
|
||||
} else {
|
||||
map.insert(group, vec![cluster]);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// run a Minecraft [`Cluster`] using the default credentials.
|
||||
#[tracing::instrument]
|
||||
pub async fn run(path: &ClusterPath) -> crate::Result<Arc<RwLock<ProcessorChild>>> {
|
||||
|
|
|
|||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
|
|
@ -95,13 +95,13 @@ importers:
|
|||
version: 2.0.0-beta.16
|
||||
'@tauri-apps/plugin-clipboard-manager':
|
||||
specifier: github:tauri-apps/tauri-plugin-clipboard-manager#v2
|
||||
version: https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/d503abbce40595d0af6b36c10439b85c640b4c53
|
||||
version: https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/20e84cfbdebcd86bb1854846feb0aaff4481fbd0
|
||||
'@tauri-apps/plugin-dialog':
|
||||
specifier: 2.0.0-beta.8
|
||||
version: 2.0.0-beta.8
|
||||
'@tauri-apps/plugin-shell':
|
||||
specifier: github:tauri-apps/tauri-plugin-shell#v2
|
||||
version: https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/adb938ddf434547ae35f74dc1a080908da03ea96
|
||||
version: https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5cc36dadeaad0520111c6f5e32ecdbbae7cb1311
|
||||
'@thisbeyond/solid-dnd':
|
||||
specifier: ^0.7.5
|
||||
version: 0.7.5(solid-js@1.8.19)
|
||||
|
|
@ -1268,16 +1268,16 @@ packages:
|
|||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@tauri-apps/plugin-clipboard-manager@https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/d503abbce40595d0af6b36c10439b85c640b4c53':
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/d503abbce40595d0af6b36c10439b85c640b4c53}
|
||||
version: 2.1.0-beta.5
|
||||
'@tauri-apps/plugin-clipboard-manager@https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/20e84cfbdebcd86bb1854846feb0aaff4481fbd0':
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/20e84cfbdebcd86bb1854846feb0aaff4481fbd0}
|
||||
version: 2.1.0-beta.6
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.0.0-beta.8':
|
||||
resolution: {integrity: sha512-6iVhEBceWYLUgX6ygBc4/yJ1o/D4YwgoAGt7ebFUTjEyIhp79PeFtE8OTjdyJbtVOc6bAcDeAn4+pbWtSw9/4w==}
|
||||
|
||||
'@tauri-apps/plugin-shell@https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/adb938ddf434547ae35f74dc1a080908da03ea96':
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/adb938ddf434547ae35f74dc1a080908da03ea96}
|
||||
version: 2.0.0-beta.8
|
||||
'@tauri-apps/plugin-shell@https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5cc36dadeaad0520111c6f5e32ecdbbae7cb1311':
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5cc36dadeaad0520111c6f5e32ecdbbae7cb1311}
|
||||
version: 2.0.0-beta.9
|
||||
|
||||
'@testing-library/dom@10.4.0':
|
||||
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
||||
|
|
@ -4565,17 +4565,17 @@ snapshots:
|
|||
'@tauri-apps/cli-win32-ia32-msvc': 2.0.0-beta.23
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.0.0-beta.23
|
||||
|
||||
'@tauri-apps/plugin-clipboard-manager@https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/d503abbce40595d0af6b36c10439b85c640b4c53':
|
||||
'@tauri-apps/plugin-clipboard-manager@https://codeload.github.com/tauri-apps/tauri-plugin-clipboard-manager/tar.gz/20e84cfbdebcd86bb1854846feb0aaff4481fbd0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.0-beta.15
|
||||
'@tauri-apps/api': 2.0.0-beta.16
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.0.0-beta.8':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.0-beta.16
|
||||
|
||||
'@tauri-apps/plugin-shell@https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/adb938ddf434547ae35f74dc1a080908da03ea96':
|
||||
'@tauri-apps/plugin-shell@https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5cc36dadeaad0520111c6f5e32ecdbbae7cb1311':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.0-beta.15
|
||||
'@tauri-apps/api': 2.0.0-beta.16
|
||||
|
||||
'@testing-library/dom@10.4.0':
|
||||
dependencies:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue