🌊 qBittorrent Settings Scaffold (#90)
* 🌊 qBittorrent settings scaffold * 🔧 Added scaffold for the qBittorrent connection form * 🔧 Some refactoring * 🔧 Cleaned up folder structure * 🔧 Fixed broken paths * 🔧 Cleaned up Search and Import component hierarchy * 🔧 More path fixes * 🔧 Tooling changes * 📝 Qbittorrent form scaffold * ⬆️ Bumped @dnd-kit deps * 🧑🏼🔧 Fixed the hostname regex * 🏗️ Adding fields to the settings form * 🔧 Formatting and more layout changes * 🔧 Added Prowlarr settings items in JSON * 📝 Purified Card Component * 📝 Abstracted connection form into a component * 🏗️ Reorganized tabs * Migrating from Redux to RTK-query * ⬇️ Fetched qBittorrent settings * 🏗️ Trying out react-query * 🧩 Added react-query query to qBittorrentSettings page * 📝 qbittorrent form RU actions first draft * 🏗️ Added loading state check * 🏗 Added error check state * 🏗️ Refactored AirDCPP context using react-query * 🏗️ Refactoring AirDCPP Settings Form with react-query * 🔧 Removed context * 🔧 Removing context from AirDCPP settings page * 🔧 Fixed early init error on the store * 🐛 Debugging AirDCPP Settings Form page * 🧸 Zustand-ified AirDCPP Form * ❌ AirDCPP code cleaned up from App.tsx * ➕ Re-added yarn.lock
This commit was merged in pull request #90.
This commit is contained in:
25
src/client/components/shared/Draggable/Cover.tsx
Normal file
25
src/client/components/shared/Draggable/Cover.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
export const Cover = forwardRef(
|
||||
({ url, index, faded, style, ...props }, ref) => {
|
||||
const inlineStyles = {
|
||||
opacity: faded ? "0.2" : "1",
|
||||
transformOrigin: "0 0",
|
||||
minHeight: index === 0 ? 300 : 300,
|
||||
maxWidth: 200,
|
||||
gridRowStart: index === 0 ? "span" : null,
|
||||
gridColumnStart: index === 0 ? "span" : null,
|
||||
backgroundImage: `url("${url}")`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundColor: "grey",
|
||||
border: "1px solid #CCC",
|
||||
borderRadius: "10px",
|
||||
...style,
|
||||
};
|
||||
|
||||
return <div ref={ref} style={inlineStyles} {...props}></div>;
|
||||
},
|
||||
);
|
||||
|
||||
Cover.displayName = "Cover";
|
||||
94
src/client/components/shared/Draggable/DnD.tsx
Normal file
94
src/client/components/shared/Draggable/DnD.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import React, { ReactElement, useState } from "react";
|
||||
// https://codesandbox.io/s/dndkit-sortable-image-grid-py6ve?file=/src/Grid.jsx
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
MouseSensor,
|
||||
TouchSensor,
|
||||
DragOverlay,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from "@dnd-kit/core";
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
rectSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
|
||||
import { Grid } from "./Grid";
|
||||
import { SortableCover } from "./SortableCover";
|
||||
import { Cover } from "./Cover";
|
||||
import { map } from "lodash";
|
||||
|
||||
export const DnD = (data) => {
|
||||
const [items, setItems] = useState(data.data);
|
||||
const [activeId, setActiveId] = useState(null);
|
||||
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor));
|
||||
|
||||
function handleDragStart(event) {
|
||||
setActiveId(event.active.id);
|
||||
}
|
||||
|
||||
function handleDragEnd(event) {
|
||||
const { active, over } = event;
|
||||
|
||||
if (active.id !== over.id) {
|
||||
setItems((items) => {
|
||||
const oldIndex = items.indexOf(active.id);
|
||||
const newIndex = items.indexOf(over.id);
|
||||
|
||||
return arrayMove(items, oldIndex, newIndex);
|
||||
});
|
||||
}
|
||||
setActiveId(null);
|
||||
}
|
||||
|
||||
function handleDragCancel() {
|
||||
setActiveId(null);
|
||||
}
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
onDragCancel={handleDragCancel}
|
||||
>
|
||||
<SortableContext items={items} strategy={rectSortingStrategy}>
|
||||
<Grid columns={4}>
|
||||
{map(items, (url, index) => {
|
||||
return (
|
||||
<div>
|
||||
<SortableCover key={url} url={url} index={index} />
|
||||
<div
|
||||
className="mt-2 mb-2"
|
||||
onClick={(e) => data.onClickHandler(url)}
|
||||
>
|
||||
<div className="box p-2 pl-3 control-palette">
|
||||
<span className="tag is-warning mr-2">{index}</span>
|
||||
<span className="icon is-small mr-2">
|
||||
<i className="fa-solid fa-vial"></i>
|
||||
</span>
|
||||
<span className="icon is-small mr-2">
|
||||
<i className="fa-solid fa-bullseye"></i>
|
||||
</span>
|
||||
<span className="icon is-small mr-2">
|
||||
<i className="fa-regular fa-trash-can"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</SortableContext>
|
||||
<DragOverlay adjustScale={true}>
|
||||
{activeId ? (
|
||||
<Cover url={activeId} index={items.indexOf(activeId)} />
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default DnD;
|
||||
17
src/client/components/shared/Draggable/Grid.tsx
Normal file
17
src/client/components/shared/Draggable/Grid.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
|
||||
export function Grid({ children, columns }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${columns}, 200px)`,
|
||||
columnGap: 1,
|
||||
gridGap: 10,
|
||||
padding: 10,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
32
src/client/components/shared/Draggable/SortableCover.tsx
Normal file
32
src/client/components/shared/Draggable/SortableCover.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
|
||||
import { Cover } from "./Cover";
|
||||
|
||||
export const SortableCover = (props) => {
|
||||
const sortable = useSortable({ id: props.url });
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
isDragging,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
} = sortable;
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
return (
|
||||
<Cover
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...props}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user