Rewrite all styles to tailwind CSS from Bootstrap by @arccik (#58)

* add tailwindcss

* add header component with logo and add torrent buttons

* remove bootstrap from few files replace it with tailwindcss classes, add card which diplay all nessesarry information about torrent and current state

* Add modal component and reorganize components folder

* add useModal hook to render modal though react portal, remove UrlPromptModal and replace it with useModal.

* add taliwindcss to Desctop app

* removed bootstrap from deleteTorrentModal replace it with useModal

* replacing bootstrap with useModal

* saving

* Saving

* Header and cards now look good

* Modals still broken...

* still doesnt work

* Finally it scrolls

* Continuing to fix bugs

* Continuing to fix bugs

* Aler

* Getting better

* Desktop doesnt work with tailwind somehow

* Desktop now works with tailwind

* Styles fully work

* (De)select all buttons

* fix alert styles

* Animate progress bar

* Progress bar + error colors

* Fix error message

* Torrent status icon (#56)

* add statusIcon component to display icon of the torrent status

* change props name and remove isDownloading variable

* Tweak styles for icon

* Tweak styles

* Update styles

---------

Co-authored-by: Artur Lozovski <arccik@gmail.com>
This commit is contained in:
Igor Katson 2023-12-14 10:37:29 +00:00 committed by GitHub
parent 911bf3a0d5
commit 50fc7f2f01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 7454 additions and 1776 deletions

View file

@ -1,9 +1,15 @@
import React, { useState } from "react";
import React, { ReactNode, useState } from "react";
import { RqbitDesktopConfig } from "./configuration";
import { Button, Form, Modal, Row, Tab, Tabs } from "react-bootstrap";
import { ErrorComponent } from "rqbit-webui/src/components/ErrorComponent";
import { invokeAPI } from "./api";
import { ErrorDetails } from "rqbit-webui/src/api-types";
import { FormCheckbox } from "rqbit-webui/src/components/forms/FormCheckbox";
import { FormInput as FI } from "rqbit-webui/src/components/forms/FormInput";
import { ModalBody } from "rqbit-webui/src/components/modal/ModalBody";
import { Modal } from "rqbit-webui/src/components/modal/Modal";
import { Fieldset } from "rqbit-webui/src/components/forms/Fieldset";
import { ModalFooter } from "rqbit-webui/src/components/modal/ModalFooter";
import { Button } from "rqbit-webui/src/components/buttons/Button";
const FormCheck: React.FC<{
label: string;
@ -14,19 +20,14 @@ const FormCheck: React.FC<{
help?: string;
}> = ({ label, name, checked, onChange, disabled, help }) => {
return (
<Form.Group as={Row} controlId={name} className="mb-3">
<Form.Label className="col-4">{label}</Form.Label>
<div className="col-8">
<Form.Check
type="switch"
name={name}
checked={checked}
onChange={onChange}
disabled={disabled}
/>
</div>
{help && <div className="form-text">{help}</div>}
</Form.Group>
<FormCheckbox
label={label}
name={name}
checked={checked}
onChange={onChange}
disabled={disabled}
help={help}
/>
);
};
@ -40,22 +41,47 @@ const FormInput: React.FC<{
help?: string;
}> = ({ label, name, value, inputType, onChange, disabled, help }) => {
return (
<Form.Group as={Row} controlId={name} className="mb-3">
<Form.Label className="col-4 col-form-label">{label}</Form.Label>
<div className="col-8">
<Form.Control
type={inputType}
name={name}
value={value}
onChange={onChange}
disabled={disabled}
/>
</div>
{help && <div className="form-text">{help}</div>}
</Form.Group>
<FI
inputType={inputType}
name={name}
value={value as string}
onChange={onChange}
disabled={disabled}
label={label}
help={help}
/>
);
};
type TAB =
| "Home"
| "DHT"
| "Session"
| "Peer options"
| "HTTP API"
| "TCP Listen";
const TABS: readonly TAB[] = [
"Home",
"DHT",
"Session",
"TCP Listen",
"Peer options",
"HTTP API",
] as const;
const Tab: React.FC<{
name: TAB;
currentTab: TAB;
children: ReactNode;
}> = ({ name, currentTab, children }) => {
const show = name === currentTab;
if (!show) {
return;
}
return <div>{children}</div>;
};
export const ConfigModal: React.FC<{
show: boolean;
handleStartReconfigure: () => void;
@ -74,6 +100,8 @@ export const ConfigModal: React.FC<{
let [config, setConfig] = useState<RqbitDesktopConfig>(initialConfig);
let [loading, setLoading] = useState<boolean>(false);
let [tab, setTab] = useState<TAB>("Home");
const [error, setError] = useState<any | null>(null);
const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
@ -143,27 +171,46 @@ export const ConfigModal: React.FC<{
};
return (
<Modal show={show} size="xl" onHide={handleCancel}>
<Modal.Header closeButton>
<Modal.Title>Configure Rqbit desktop</Modal.Title>
</Modal.Header>
<Modal.Body>
<Modal
title="Configure Rqbit desktop"
isOpen={show}
onClose={handleCancel}
className="max-w-4xl"
>
<ModalBody>
<ErrorComponent error={error}></ErrorComponent>
<Tabs defaultActiveKey="home" id="rqbit-config" className="mb-3">
<Tab className="mb-3" eventKey="home" title="Home">
<FormInput
label="Default download folder"
name="default_download_location"
value={config.default_download_location}
inputType="text"
onChange={handleInputChange}
help="Where to download torrents by default. You can override this per torrent."
/>
</Tab>
<div className="flex border-b mb-4">
{TABS.map((t, i) => {
const isActive = t === tab;
let classNames = "text-slate-300";
if (isActive) {
classNames = "text-slate-800 border-b-2 border-blue-800";
}
return (
<button
key={i}
className={`p-2 ${classNames}`}
onClick={() => setTab(t)}
>
{t}
</button>
);
})}
</div>
<Tab className="mb-3" eventKey="dht" title="DHT">
<legend>DHT config</legend>
<Tab name="Home" currentTab={tab}>
<FormInput
label="Default download folder"
name="default_download_location"
value={config.default_download_location}
inputType="text"
onChange={handleInputChange}
help="Where to download torrents by default. You can override this per torrent."
/>
</Tab>
<Tab name="DHT" currentTab={tab}>
<Fieldset label="DHT config">
<FormCheck
label="Enable DHT"
name="dht.disable"
@ -190,11 +237,11 @@ export const ConfigModal: React.FC<{
onChange={handleInputChange}
help="The filename to store DHT state into"
/>
</Tab>
<Tab className="mb-3" eventKey="tcp_listen" title="TCP">
<legend>TCP Listener config</legend>
</Fieldset>
</Tab>
<Tab name="TCP Listen" currentTab={tab}>
<Fieldset label="TCP Listener config">
<FormCheck
label="Listen on TCP"
name="tcp_listen.disable"
@ -230,11 +277,11 @@ export const ConfigModal: React.FC<{
onChange={handleInputChange}
help="The max port to try to listen on."
/>
</Tab>
<Tab className="mb-3" eventKey="session_persistence" title="Session">
<legend>Session persistence</legend>
</Fieldset>
</Tab>
<Tab name="Session" currentTab={tab}>
<Fieldset label="Session persistence">
<FormCheck
label="Enable persistence"
name="persistence.disable"
@ -251,11 +298,11 @@ export const ConfigModal: React.FC<{
onChange={handleInputChange}
disabled={config.persistence.disable}
/>
</Tab>
<Tab className="mb-3" eventKey="peer_opts" title="Peer options">
<legend>Peer connection options</legend>
</Fieldset>
</Tab>
<Tab name="Peer options" currentTab={tab}>
<Fieldset label="Peer connection options">
<FormInput
label="Connect timeout (seconds)"
inputType="number"
@ -273,11 +320,11 @@ export const ConfigModal: React.FC<{
onChange={handleInputChange}
help="Peer socket read/write timeout."
/>
</Tab>
<Tab className="mb-3" eventKey="http_api" title="HTTP API">
<legend>HTTP API config</legend>
</Fieldset>
</Tab>
<Tab name="HTTP API" currentTab={tab}>
<Fieldset label="HTTP API config">
<FormCheck
label="Enable HTTP API"
name="http_api.disable"
@ -313,10 +360,10 @@ export const ConfigModal: React.FC<{
onChange={handleInputChange}
help={`You'll access the API at http://${config.http_api.listen_addr}`}
/>
</Tab>
</Tabs>
</Modal.Body>
<Modal.Footer>
</Fieldset>
</Tab>
</ModalBody>
<ModalFooter>
{!!handleCancel && (
<Button variant="secondary" onClick={handleCancel}>
Cancel
@ -328,7 +375,7 @@ export const ConfigModal: React.FC<{
<Button variant="primary" onClick={handleOkClick} disabled={loading}>
OK
</Button>
</Modal.Footer>
</ModalFooter>
</Modal>
);
};

View file

@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client";
import { invoke } from "@tauri-apps/api";
import { CurrentDesktopState, RqbitDesktopConfig } from "./configuration";
import { RqbitDesktop } from "./rqbit-desktop";
import "./styles/index.css";
async function get_version(): Promise<string> {
return invoke<string>("get_version");

View file

@ -2,7 +2,7 @@ import { useState } from "react";
import { RqbitWebUI } from "rqbit-webui/src/rqbit-web";
import { CurrentDesktopState, RqbitDesktopConfig } from "./configuration";
import { ConfigModal } from "./configure";
import { IconButton } from "rqbit-webui/src/components/IconButton";
import { IconButton } from "rqbit-webui/src/components/buttons/IconButton";
import { BsSliders2 } from "react-icons/bs";
import { APIContext } from "rqbit-webui/src/context";
import { makeAPI } from "./api";
@ -20,7 +20,6 @@ export const RqbitDesktop: React.FC<{
const configButton = (
<IconButton
className="p-3 text-primary"
onClick={() => {
setConfigurationOpened(true);
}}
@ -33,7 +32,7 @@ export const RqbitDesktop: React.FC<{
<APIContext.Provider value={makeAPI(config)}>
{configured && (
<RqbitWebUI
title={`Rqbit Desktop v${version}`}
title={`Rqbit Desktop - v${version}`}
menuButtons={[configButton]}
></RqbitWebUI>
)}

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;