Improvements to default location

This commit is contained in:
Donkie
2024-11-24 14:45:16 +01:00
parent 755e06c989
commit 0827cc8a5a
5 changed files with 64 additions and 27 deletions

View File

@@ -4,9 +4,10 @@ import { useRef, useState } from "react";
import { useDrag, useDrop } from "react-dnd"; import { useDrag, useDrop } from "react-dnd";
import { DeleteOutlined } from "@ant-design/icons"; import { DeleteOutlined } from "@ant-design/icons";
import { useUpdate } from "@refinedev/core"; import { useTranslate, useUpdate } from "@refinedev/core";
import { ISpool } from "../../spools/model"; import { ISpool } from "../../spools/model";
import { DragItem, ItemTypes, SpoolDragItem } from "../dnd"; import { DragItem, ItemTypes, SpoolDragItem } from "../dnd";
import { EMPTYLOC } from "../functions";
import { SpoolList } from "./spoolList"; import { SpoolList } from "./spoolList";
export function Location({ export function Location({
@@ -30,6 +31,7 @@ export function Location({
locationSpoolOrder: number[]; locationSpoolOrder: number[];
setLocationSpoolOrder: (spoolOrder: number[]) => void; setLocationSpoolOrder: (spoolOrder: number[]) => void;
}) { }) {
const t = useTranslate();
const [editTitle, setEditTitle] = useState(false); const [editTitle, setEditTitle] = useState(false);
const [newTitle, setNewTitle] = useState(title); const [newTitle, setNewTitle] = useState(title);
const { mutate: updateSpool } = useUpdate({ const { mutate: updateSpool } = useUpdate({
@@ -47,9 +49,11 @@ export function Location({
}); });
}; };
const dropTypes = title == EMPTYLOC ? [ItemTypes.SPOOL] : [ItemTypes.CONTAINER, ItemTypes.SPOOL];
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const [{ handlerId }, drop] = useDrop<DragItem, void, { handlerId: Identifier | null }>({ const [{ handlerId }, drop] = useDrop<DragItem, void, { handlerId: Identifier | null }>({
accept: [ItemTypes.CONTAINER, ItemTypes.SPOOL], accept: dropTypes,
collect(monitor) { collect(monitor) {
return { return {
handlerId: monitor.getHandlerId(), handlerId: monitor.getHandlerId(),
@@ -113,7 +117,7 @@ export function Location({
const [{ isDragging }, drag] = useDrag({ const [{ isDragging }, drag] = useDrag({
type: ItemTypes.CONTAINER, type: ItemTypes.CONTAINER,
canDrag: !editTitle, canDrag: !editTitle && title != EMPTYLOC,
item: () => { item: () => {
return { title, index }; return { title, index };
}, },
@@ -122,11 +126,20 @@ export function Location({
}), }),
}); });
const displayTitle = title == EMPTYLOC ? t("locations.no_location") : title;
const opacity = isDragging ? 0 : 1; const opacity = isDragging ? 0 : 1;
drag(drop(ref)); drag(drop(ref));
const canEditTitle = title != EMPTYLOC;
return ( return (
<div className="loc-container" ref={ref} style={{ opacity }} data-handler-id={handlerId}> <div
className={"loc-container " + (title != EMPTYLOC ? "grabable" : "")}
ref={ref}
style={{ opacity }}
data-handler-id={handlerId}
>
<h3> <h3>
{editTitle ? ( {editTitle ? (
<Input <Input
@@ -142,12 +155,14 @@ export function Location({
/> />
) : ( ) : (
<span <span
className={canEditTitle ? "editable" : ""}
onClick={() => { onClick={() => {
if (!canEditTitle) return;
setNewTitle(title); setNewTitle(title);
setEditTitle(true); setEditTitle(true);
}} }}
> >
{title} {displayTitle}
</span> </span>
)} )}
{showDelete && <Button icon={<DeleteOutlined />} size="small" type="text" onClick={onDelete} />} {showDelete && <Button icon={<DeleteOutlined />} size="small" type="text" onClick={onDelete} />}

View File

@@ -1,15 +1,14 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import { useInvalidate, useList, useTranslate } from "@refinedev/core"; import { useList, useTranslate } from "@refinedev/core";
import { Button } from "antd"; import { Button } from "antd";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { useSetSetting } from "../../../utils/querySettings"; import { useSetSetting } from "../../../utils/querySettings";
import { ISpool } from "../../spools/model"; import { ISpool } from "../../spools/model";
import { useLocations, useLocationsSpoolOrders, useRenameSpoolLocation } from "../functions"; import { EMPTYLOC, useLocations, useLocationsSpoolOrders, useRenameSpoolLocation } from "../functions";
import { Location } from "./location"; import { Location } from "./location";
export function LocationContainer() { export function LocationContainer() {
const t = useTranslate(); const t = useTranslate();
const invalidate = useInvalidate();
const renameSpoolLocation = useRenameSpoolLocation(); const renameSpoolLocation = useRenameSpoolLocation();
const settingsLocations = useLocations(); const settingsLocations = useLocations();
@@ -41,7 +40,7 @@ export function LocationContainer() {
const grouped: Record<string, ISpool[]> = {}; const grouped: Record<string, ISpool[]> = {};
spools.forEach((spool) => { spools.forEach((spool) => {
const loc = spool.location ?? t("locations.no_location"); const loc = spool.location ?? EMPTYLOC;
if (!grouped[loc]) { if (!grouped[loc]) {
grouped[loc] = []; grouped[loc] = [];
} }
@@ -71,12 +70,18 @@ export function LocationContainer() {
// Create list of locations that's sorted // Create list of locations that's sorted
const locationsList = useMemo(() => { const locationsList = useMemo(() => {
// Start with the locations setting // Start with the default loc
let allLocs = settingsLocations; let allLocs = [];
if (EMPTYLOC in spoolLocations) {
allLocs.push(EMPTYLOC);
}
// Add from the locations setting
if (settingsLocations) allLocs.push(...settingsLocations);
// Add any missing locations from the spools // Add any missing locations from the spools
for (const loc of Object.keys(spoolLocations)) { for (const loc of Object.keys(spoolLocations)) {
if (!allLocs.includes(loc)) { if (loc != EMPTYLOC && !allLocs.includes(loc)) {
allLocs.push(loc); allLocs.push(loc);
} }
} }
@@ -88,13 +93,14 @@ export function LocationContainer() {
const newLocs = [...locationsList]; const newLocs = [...locationsList];
newLocs.splice(dragIndex, 1); newLocs.splice(dragIndex, 1);
newLocs.splice(hoverIndex, 0, locationsList[dragIndex]); newLocs.splice(hoverIndex, 0, locationsList[dragIndex]);
setLocationsSetting.mutate(newLocs); setLocationsSetting.mutate(newLocs.filter((loc) => loc != EMPTYLOC));
}; };
const onEditTitle = async (location: string, newTitle: string) => { const onEditTitle = async (location: string, newTitle: string) => {
if (newTitle == location) return; if (location == "") return; // Can't edit the default location
if (newTitle == "") return; if (newTitle == location) return; // No change
if (locationsList.includes(newTitle)) return; if (newTitle == "") return; // Can't have an empty location
if (locationsList.includes(newTitle)) return; // Location already exists
// Update all spool locations in the database // Update all spool locations in the database
if (spoolLocations[location] && spoolLocations[location].length > 0) { if (spoolLocations[location] && spoolLocations[location].length > 0) {
@@ -102,7 +108,7 @@ export function LocationContainer() {
} }
// Update the value in the settings // Update the value in the settings
const newLocs = [...locationsList]; const newLocs = [...locationsList].filter((loc) => loc != EMPTYLOC);
newLocs[locationsList.indexOf(location)] = newTitle; newLocs[locationsList.indexOf(location)] = newTitle;
setLocationsSetting.mutate(newLocs); setLocationsSetting.mutate(newLocs);
}; };
@@ -140,10 +146,11 @@ export function LocationContainer() {
// Update locations settings so it always includes all spool locations // Update locations settings so it always includes all spool locations
useEffect(() => { useEffect(() => {
// Check if they're not the same // Check if they're not the same
if (JSON.stringify(locationsList) !== JSON.stringify(settingsLocations)) { const curLocList = locationsList.filter((l) => l != EMPTYLOC);
setLocationsSetting.mutate(locationsList); if (settingsLocations != null && JSON.stringify(curLocList) !== JSON.stringify(settingsLocations)) {
setLocationsSetting.mutate(curLocList);
} }
}, [spoolLocations]); }, [locationsList, settingsLocations, setLocationsSetting]);
if (isLoading) { if (isLoading) {
return <div>Loading...</div>; return <div>Loading...</div>;

View File

@@ -29,7 +29,7 @@ export function SpoolCard({
}) { }) {
const { token } = useToken(); const { token } = useToken();
const t = useTranslate(); const t = useTranslate();
const { editUrl, showUrl } = useNavigation(); const { showUrl } = useNavigation();
// Using a global state for this, because the drag handlers are reset when the spool changes location // Using a global state for this, because the drag handlers are reset when the spool changes location
const { draggedSpoolId, setDraggedSpoolId } = useCurrentDraggedSpool(); const { draggedSpoolId, setDraggedSpoolId } = useCurrentDraggedSpool();

View File

@@ -5,6 +5,8 @@ import { useGetSetting } from "../../utils/querySettings";
import { getAPIURL } from "../../utils/url"; import { getAPIURL } from "../../utils/url";
import { ISpool } from "../spools/model"; import { ISpool } from "../spools/model";
export const EMPTYLOC = "";
interface LocationRename { interface LocationRename {
old: string; old: string;
new: string; new: string;
@@ -74,11 +76,11 @@ export function useRenameSpoolLocation() {
}); });
} }
export function useLocations(): string[] { export function useLocations(): string[] | null {
const query = useGetSetting("locations"); const query = useGetSetting("locations");
return useMemo(() => { return useMemo(() => {
if (!query.data) return []; if (!query.data) return null;
try { try {
let data = (JSON.parse(query.data.value) ?? []) as string[]; let data = (JSON.parse(query.data.value) ?? []) as string[];
@@ -86,7 +88,7 @@ export function useLocations(): string[] {
return data; return data;
} catch { } catch {
console.warn("Failed to parse locations", query.data.value); console.warn("Failed to parse locations", query.data.value);
return []; return null;
} }
}, [query.data]); }, [query.data]);
} }

View File

@@ -6,7 +6,14 @@
.loc-container { .loc-container {
padding: 1em; padding: 1em;
width: 24em; width: 24em;
cursor: pointer; }
.loc-container.grabable,
.loc-container .spool {
cursor: move;
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
} }
.loc-container h3 { .loc-container h3 {
@@ -15,6 +22,13 @@
justify-content: space-between; justify-content: space-between;
font-size: 21px; font-size: 21px;
width: 100%; width: 100%;
}
.loc-container h3 span {
cursor: default;
}
.loc-container h3 span.editable {
cursor: text; cursor: text;
} }
@@ -31,7 +45,7 @@
flex-direction: column; flex-direction: column;
gap: 0.5em; gap: 0.5em;
min-height: 3em; min-height: 3em;
max-height: 100em; max-height: 50em;
overflow-y: scroll; overflow-y: scroll;
} }
@@ -39,7 +53,6 @@
padding: 0.5em 0.5em 0.5em 0; padding: 0.5em 0.5em 0.5em 0;
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer;
border-radius: 0.5em; border-radius: 0.5em;
} }