Compare commits
12 Commits
9ad3f239dd
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| dfb42c11b4 | |||
| 14b0f1b805 | |||
| 2484cf8191 | |||
| 3e0452c4d7 | |||
| 629690d29e | |||
| 12e1e8a90b | |||
| 0c784b3855 | |||
| 0ab8cf026e | |||
| dd3312b7f5 | |||
| 1b4e6193e8 | |||
| 5759618904 | |||
| 77b3a68b76 |
@@ -1,3 +1,6 @@
|
||||
# Coord-Keeper
|
||||
|
||||
Una pequeñisima app que estoy haciendo para almacenar localmente coordenadas de minecraft
|
||||
|
||||
|
||||
[Live Demo](https://fedesrv.ddns.net/coords)
|
||||
|
||||
12
src/App.jsx
12
src/App.jsx
@@ -1,18 +1,18 @@
|
||||
import { useState } from 'react'
|
||||
import { useState } from "react";
|
||||
|
||||
import Barra from './components/Barra';
|
||||
import Keeper from './components/coordkeeper';
|
||||
import Barra from "./components/Barra";
|
||||
import Keeper from "./components/coordkeeper";
|
||||
|
||||
function App() {
|
||||
const [type, setType] = useState("overworld");
|
||||
const [type, setType] = useState("overworld");
|
||||
return (
|
||||
<div>
|
||||
<Barra active={type} setType={setType} />
|
||||
<div class="container border text-white vw-100 mt-2 rounded p-2">
|
||||
<Keeper tipo={type}/>
|
||||
<Keeper tipo={type} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function Barra ({active, setType}) {
|
||||
export default function Barra({ active, setType }) {
|
||||
return (
|
||||
<nav class="navbar navbar-expand-lg bg-dark-subtle h-fit w-100">
|
||||
<div class="container-fluid">
|
||||
@@ -6,33 +6,87 @@ export default function Barra ({active, setType}) {
|
||||
Coords-Keeper
|
||||
</a>
|
||||
|
||||
<div class="justify-content-end">
|
||||
<ul class="navbar-nav">
|
||||
<li className="nav-item">
|
||||
<button
|
||||
className={`btn bg-primary mx-2 ${active === "overworld" ? "text-black active" : "text-gray-100"}`}
|
||||
onClick={() => setType("overworld")}
|
||||
>
|
||||
Overworld
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<button
|
||||
className={`btn bg-danger mx-2 ${active === "nether" ? "text-black active" : "text-gray-100"}`}
|
||||
onClick={() => setType("nether")}
|
||||
>
|
||||
Nether
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<button
|
||||
className={`btn bg-success mx-2 ${active === "end" ? "text-black active" : "text-gray-100"}`}
|
||||
onClick={() => setType("end")}
|
||||
>
|
||||
End
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#offcanvasNavbar"
|
||||
aria-controls="offcanvasNavbar"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasNavbar">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title">Coords-Keeper</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="offcanvas"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<ul class="navbar-nav justify-content-end flex-grow-1">
|
||||
<li className="nav-item">
|
||||
<a
|
||||
className={`nav-link d-lg-none ${active === "overworld" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setType("overworld");
|
||||
document
|
||||
.querySelector('[data-bs-dismiss="offcanvas"]')
|
||||
.click();
|
||||
}}
|
||||
>
|
||||
Overworld
|
||||
</a>
|
||||
<button
|
||||
className={`btn bg-primary mx-2 d-none d-lg-block ${active === "overworld" ? "text-black active" : "text-gray-100"}`}
|
||||
onClick={() => setType("overworld")}
|
||||
>
|
||||
Overworld
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a
|
||||
className={`nav-link d-lg-none ${active === "nether" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setType("nether");
|
||||
document
|
||||
.querySelector('[data-bs-dismiss="offcanvas"]')
|
||||
.click();
|
||||
}}
|
||||
>
|
||||
Nether
|
||||
</a>
|
||||
<button
|
||||
className={`btn bg-danger mx-2 d-none d-lg-block ${active === "nether" ? "text-black active" : "text-gray-100"}`}
|
||||
onClick={() => setType("nether")}
|
||||
>
|
||||
Nether
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a
|
||||
className={`nav-link d-lg-none ${active === "end" ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
setType("end");
|
||||
document
|
||||
.querySelector('[data-bs-dismiss="offcanvas"]')
|
||||
.click();
|
||||
}}
|
||||
>
|
||||
End
|
||||
</a>
|
||||
<button
|
||||
className={`btn bg-success mx-2 d-none d-lg-block ${active === "end" ? "text-black active" : "text-gray-100"}`}
|
||||
onClick={() => setType("end")}
|
||||
>
|
||||
End
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
22
src/components/BotonAdd.jsx
Normal file
22
src/components/BotonAdd.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function BotonAdd({ show, setshow }) {
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="btn btn-primary rounded-circle position-fixed bottom-0 end-0 m-4 d-flex align-items-center justify-content-center"
|
||||
style={{ width: "60px", height: "60px" }}
|
||||
onClick={() => setshow(true)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
className="bi bi-plus"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
15
src/components/EditarIcon.jsx
Normal file
15
src/components/EditarIcon.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function EditarIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m21.561 5.318l-2.879-2.879A1.5 1.5 0 0 0 17.621 2c-.385 0-.768.146-1.061.439L13 6H4a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1v-9l3.561-3.561c.293-.293.439-.677.439-1.061s-.146-.767-.439-1.06M11.5 14.672L9.328 12.5l6.293-6.293l2.172 2.172zm-2.561-1.339l1.756 1.728L9 15zM16 19H5V8h6l-3.18 3.18c-.293.293-.478.812-.629 1.289c-.16.5-.191 1.056-.191 1.47V17h3.061c.414 0 1.108-.1 1.571-.29c.464-.19.896-.347 1.188-.64L16 13zm2.5-11.328L16.328 5.5l1.293-1.293l2.171 2.172z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
126
src/components/EditarModal.jsx
Normal file
126
src/components/EditarModal.jsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from "react";
|
||||
|
||||
export default function EditarModal({ tipo, coord, show, close, setCoords }) {
|
||||
if (coord == null) return null;
|
||||
|
||||
const [showY, setShowY] = React.useState(coord.y !== null);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
const x = formData.get("x");
|
||||
const y = formData.get("y");
|
||||
const z = formData.get("z");
|
||||
const descripcion = formData.get("descripcion");
|
||||
const hasY = formData.get("hasY") === "true";
|
||||
|
||||
let storedTipo = localStorage.getItem(tipo);
|
||||
if (storedTipo) {
|
||||
let arr = JSON.parse(storedTipo);
|
||||
const index = arr.findIndex((item) => item.num === coord.num);
|
||||
if (index !== -1) {
|
||||
arr[index] = {
|
||||
...coord,
|
||||
x: Number(x),
|
||||
y: hasY ? Number(y) : null,
|
||||
z: Number(z),
|
||||
descripcion: descripcion,
|
||||
};
|
||||
localStorage.setItem(tipo, JSON.stringify(arr));
|
||||
setCoords(arr);
|
||||
}
|
||||
}
|
||||
close();
|
||||
};
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="modal"
|
||||
id="formModal"
|
||||
tabIndex="-1"
|
||||
style={{ display: "block" }}
|
||||
>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Update Coord</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={close}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="hasY"
|
||||
value="true"
|
||||
className="form-check-input me-2"
|
||||
defaultChecked={coord.y !== null}
|
||||
onChange={(e) => setShowY(e.target.checked)}
|
||||
/>
|
||||
<label className="form-label">Lleva coordenada Y?</label>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Coordenada X</label>
|
||||
<input
|
||||
type="number"
|
||||
name="x"
|
||||
className="form-control"
|
||||
defaultValue={coord.x}
|
||||
/>
|
||||
</div>
|
||||
{showY && (
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Coordenada Y</label>
|
||||
<input
|
||||
type="number"
|
||||
name="y"
|
||||
className="form-control"
|
||||
defaultValue={coord.y}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Coordenada Z</label>
|
||||
<input
|
||||
type="number"
|
||||
name="z"
|
||||
className="form-control"
|
||||
defaultValue={coord.z}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Descripción</label>
|
||||
<input
|
||||
type="text"
|
||||
name="descripcion"
|
||||
className="form-control"
|
||||
defaultValue={coord.descripcion}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<button type="submit" className="btn btn-primary m-2">
|
||||
Actualizar
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={close}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
52
src/components/ModalImport.jsx
Normal file
52
src/components/ModalImport.jsx
Normal file
@@ -0,0 +1,52 @@
|
||||
export default function ModalImport({ show, handleAddCoord, close }) {
|
||||
if (!show) return null;
|
||||
let coord = "";
|
||||
return (
|
||||
<div
|
||||
className="modal"
|
||||
tabIndex="-1"
|
||||
style={{ display: "block", backgroundColor: "rgba(0,0,0,0.3)" }}
|
||||
>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Añadir Coordenadas</h5>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => close(false)}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="form-floating mb-3">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="coordInput"
|
||||
placeholder="Coordenadas"
|
||||
onChange={(event) => (coord = event.target.value)}
|
||||
/>
|
||||
<label htmlFor="coordInput">Pega la coord</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer d-flex justify-content-between">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={() => close(false)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={() => handleAddCoord(coord)}
|
||||
>
|
||||
Guardar Coord
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useState } from "react";
|
||||
|
||||
export default function NuevaCoord({coord, setcoord, colapsar, tipo}) {
|
||||
export default function NuevaCoord({ coord, setcoord, colapsar, tipo }) {
|
||||
const [llevaY, setLlevaY] = useState(false);
|
||||
const [x, setX] = useState("");
|
||||
const [y, setY] = useState("");
|
||||
const [z, setZ] = useState("");
|
||||
const [descripcion, setDescripcion] = useState("");
|
||||
|
||||
const handleAdd = () => {
|
||||
const newCoord = {
|
||||
@@ -12,44 +13,68 @@ export default function NuevaCoord({coord, setcoord, colapsar, tipo}) {
|
||||
x: x,
|
||||
y: llevaY ? y : null,
|
||||
z: z,
|
||||
descripcion: descripcion,
|
||||
};
|
||||
|
||||
setcoord([...coord, newCoord]);
|
||||
localStorage.setItem(tipo, JSON.stringify([...coord, newCoord]));
|
||||
if (colapsar) colapsar();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input id="1"
|
||||
type="checkbox" className="form-check-input me-2"
|
||||
<input
|
||||
id="1"
|
||||
type="checkbox"
|
||||
className="form-check-input me-2"
|
||||
onChange={(e) => setLlevaY(e.target.checked)}
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseY"
|
||||
/>
|
||||
<label className="form-check-label" htmlFor="1">
|
||||
Lleva coordenada y?
|
||||
Lleva coordenada y?
|
||||
</label>
|
||||
<hr/>
|
||||
<hr />
|
||||
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<div className="input-group mb-3">
|
||||
<span class="input-group-text">x:</span>
|
||||
<input type="text" className="form-control" onChange={(e) =>setX(e.target.value)}/>
|
||||
<span class="input-group-text">X:</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => setX(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3 collapse" id="collapseY">
|
||||
<span class="input-group-text">y:</span>
|
||||
<input type="text" className="form-control" onChange={(e) =>setY(e.target.value)}/>
|
||||
<span class="input-group-text">Y:</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => setY(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="input-group mb-3">
|
||||
<span class="input-group-text">z:</span>
|
||||
<input type="text" className="form-control" onChange={(e) =>setZ(e.target.value)}/>
|
||||
<span class="input-group-text">Z:</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => setZ(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<button className="btn btn-primary" onClick={handleAdd}>Añadir</button>
|
||||
<div className="input-group mb-3">
|
||||
<span class="input-group-text">Descripción:</span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
onChange={(e) => setDescripcion(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<button className="btn btn-primary" onClick={handleAdd}>
|
||||
Añadir
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
15
src/components/RemoveIcon.jsx
Normal file
15
src/components/RemoveIcon.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function RemoveIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7 21q-.825 0-1.412-.587T5 19V6H4V4h5V3h6v1h5v2h-1v13q0 .825-.587 1.413T17 21zm2-4h2V8H9zm4 0h2V8h-2z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
15
src/components/ShareIcon.jsx
Normal file
15
src/components/ShareIcon.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export default function ShareIcon() {
|
||||
return(
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17 22q-1.25 0-2.125-.875T14 19q0-.15.075-.7L7.05 14.2q-.4.375-.925.588T5 15q-1.25 0-2.125-.875T2 12t.875-2.125T5 9q.6 0 1.125.213t.925.587l7.025-4.1q-.05-.175-.062-.337T14 5q0-1.25.875-2.125T17 2t2.125.875T20 5t-.875 2.125T17 8q-.6 0-1.125-.213T14.95 7.2l-7.025 4.1q.05.175.063.338T8 12t-.012.363t-.063.337l7.025 4.1q.4-.375.925-.587T17 16q1.25 0 2.125.875T20 19t-.875 2.125T17 22"
|
||||
></path>
|
||||
</svg>,
|
||||
);
|
||||
}
|
||||
49
src/components/StaticModal.jsx
Normal file
49
src/components/StaticModal.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
export default function Modal({ CloseModal, Coord, EliminarCoord }) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="modal fade show"
|
||||
id="staticBackdrop"
|
||||
data-bs-backdrop="static"
|
||||
style={{ display: "block" }}
|
||||
>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h1 className="modal-title fs-5">Eliminar Coordenada</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={CloseModal}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{console.log(Coord)}
|
||||
Desea eliminar la Coordenada: X: {Coord.x},
|
||||
{Coord.y != null && `Y: ${Coord.y}, `}Z: {Coord.z}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-danger"
|
||||
onClick={() => {
|
||||
EliminarCoord();
|
||||
CloseModal();
|
||||
}}
|
||||
>
|
||||
Eliminar
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
onClick={CloseModal}
|
||||
>
|
||||
Cerrar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
35
src/components/TimedToast.jsx
Normal file
35
src/components/TimedToast.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function TimedToast({ message, durationMs, show, setShow }) {
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setShow(false);
|
||||
}, durationMs);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [durationMs]);
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<div className="position-fixed bottom-0 end-0 p-3" style={{ zIndex: 11 }}>
|
||||
<div
|
||||
className="toast show"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<div className="toast-header">
|
||||
<strong className="me-auto">Notification</strong>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => setShow(false)}
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<div className="toast-body">{message}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +1,123 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import NuevaCoord from "./NuevaCoord";
|
||||
import EditarIcon from "./EditarIcon";
|
||||
import ShareIcon from "./ShareIcon";
|
||||
import RemoveIcon from "./RemoveIcon";
|
||||
import StaticModal from "./StaticModal";
|
||||
import EditarModal from "./EditarModal";
|
||||
import TimedToast from "./TimedToast";
|
||||
import BotonAdd from "./BotonAdd";
|
||||
import ModalImport from "./ModalImport";
|
||||
|
||||
function Modall({ show, CloseModal, Coord, Coords, EliminarCoord, tipo }) {
|
||||
if (show === true && Coord !== null) {
|
||||
return (
|
||||
<StaticModal
|
||||
CloseModal={CloseModal}
|
||||
Coord={Coord}
|
||||
EliminarCoord={() => {
|
||||
const nuevasCoords = Coords.filter((c) => c !== Coord);
|
||||
localStorage.setItem(tipo, JSON.stringify(nuevasCoords));
|
||||
EliminarCoord(nuevasCoords);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function Keeper({ tipo }) {
|
||||
const [coords, setCoords] = useState(
|
||||
JSON.parse(localStorage.getItem(tipo) || "[]"),
|
||||
);
|
||||
|
||||
export default function Keeper({tipo}){
|
||||
const [coords,setCoords] = useState(JSON.parse(localStorage.getItem(tipo)||"[]"));
|
||||
const collapseRef = useRef(null);
|
||||
const [showModal, setModal] = useState(false);
|
||||
const [showEditar, setEditar] = useState(false);
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [selCoord, setCoord] = useState(null);
|
||||
const [showImport, setShowImport] = useState(false);
|
||||
|
||||
console.log(coords);
|
||||
useEffect(() => {
|
||||
const storedCoords = JSON.parse(localStorage.getItem(tipo)||"[]");
|
||||
const storedCoords = JSON.parse(localStorage.getItem(tipo) || "[]");
|
||||
|
||||
console.log(storedCoords);
|
||||
if (storedCoords) {
|
||||
setCoords(storedCoords);
|
||||
}
|
||||
}, [tipo]);
|
||||
|
||||
const añadirCoord = (coord) => {
|
||||
const regex = /- (.*), X: (-?\d+)(?:\s*Y:\s*(-?\d+))?\s*Z:\s*(-?\d+)/;
|
||||
const match = coord.match(regex);
|
||||
|
||||
if (match) {
|
||||
const [_, descripcion, x, y, z] = match;
|
||||
const newCoord = {
|
||||
descripcion,
|
||||
x: parseInt(x),
|
||||
y: y ? parseInt(y) : null,
|
||||
z: parseInt(z),
|
||||
num: coords.length + 1,
|
||||
};
|
||||
|
||||
const newCoords = [...coords, newCoord];
|
||||
setCoords(newCoords);
|
||||
localStorage.setItem(tipo, JSON.stringify(newCoords));
|
||||
setShowToast(true);
|
||||
setShowImport(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handlePaste = async () => {
|
||||
const text = await navigator.clipboard.readText();
|
||||
const regex = /- (.*), X: (-?\d+)(?:\s*Y:\s*(-?\d+))?\s*Z:\s*(-?\d+)/;
|
||||
const match = text.match(regex);
|
||||
|
||||
if (match) {
|
||||
const [_, descripcion, x, y, z] = match;
|
||||
const newCoord = {
|
||||
descripcion,
|
||||
x: parseInt(x),
|
||||
y: y ? parseInt(y) : null,
|
||||
z: parseInt(z),
|
||||
num: coords.length + 1,
|
||||
};
|
||||
|
||||
const newCoords = [...coords, newCoord];
|
||||
setCoords(newCoords);
|
||||
localStorage.setItem(tipo, JSON.stringify(newCoords));
|
||||
setShowToast(true);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.ctrlKey && e.key === "v") {
|
||||
handlePaste(e);
|
||||
}
|
||||
});
|
||||
|
||||
/*return () => {
|
||||
document.removeEventListener("keydown", handlePaste);
|
||||
};*/
|
||||
}, [coords, tipo]);
|
||||
|
||||
const Editar = (coord) => {
|
||||
setCoord(coord);
|
||||
setEditar(true);
|
||||
};
|
||||
|
||||
const Compartir = (coord) => {
|
||||
setCoord(coord);
|
||||
const textToCopy = `- ${coord.descripcion}, X: ${coord.x}${coord.y ? ` Y: ${coord.y}` : ""} Z: ${coord.z}`;
|
||||
navigator.clipboard.writeText(textToCopy);
|
||||
setShowToast(true);
|
||||
};
|
||||
|
||||
const Remover = (coord) => {
|
||||
setCoord(coord);
|
||||
setModal(true);
|
||||
};
|
||||
const collapseAccordion = () => {
|
||||
if (collapseRef.current) {
|
||||
const bsCollapse = new window.bootstrap.Collapse(collapseRef.current, {
|
||||
@@ -25,6 +128,36 @@ export default function Keeper({tipo}){
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="d-lg-none">
|
||||
<BotonAdd show={true} setshow={setShowImport} />
|
||||
</div>
|
||||
|
||||
<ModalImport
|
||||
show={showImport}
|
||||
handleAddCoord={añadirCoord}
|
||||
close={setShowImport}
|
||||
/>
|
||||
<Modall
|
||||
show={showModal}
|
||||
CloseModal={() => setModal(false)}
|
||||
Coord={selCoord}
|
||||
Coords={coords}
|
||||
EliminarCoord={setCoords}
|
||||
tipo={tipo}
|
||||
/>
|
||||
<EditarModal
|
||||
show={showEditar}
|
||||
close={() => setEditar(false)}
|
||||
tipo={tipo}
|
||||
coord={selCoord}
|
||||
setCoords={setCoords}
|
||||
/>
|
||||
<TimedToast
|
||||
durationMs={5000}
|
||||
setShow={setShowToast}
|
||||
show={showToast}
|
||||
message={`Copiada Coordenada [${selCoord?.descripcion}]`}
|
||||
/>
|
||||
<div className="accordion" id="accordionNuevaCoord">
|
||||
<div className="accordion-item">
|
||||
<h2 className="accordion-header" id="headingNuevaCoord">
|
||||
@@ -48,20 +181,69 @@ export default function Keeper({tipo}){
|
||||
ref={collapseRef}
|
||||
>
|
||||
<div className="accordion-body">
|
||||
<NuevaCoord coord={coords} setcoord={setCoords} colapsar={collapseAccordion} tipo={tipo} />
|
||||
<NuevaCoord
|
||||
coord={coords}
|
||||
setcoord={setCoords}
|
||||
colapsar={collapseAccordion}
|
||||
tipo={tipo}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<hr />
|
||||
{coords.length == 0 && (
|
||||
<h2 className="list-group-item text-center">
|
||||
No hay Coordenadas que Mostrar.
|
||||
</h2>
|
||||
)}
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>X:</th>
|
||||
<th>Y:</th>
|
||||
<th>Z:</th>
|
||||
<th>Descripcion</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{coords.length != 0 &&
|
||||
coords.map((x) => (
|
||||
<tr>
|
||||
<td>
|
||||
<b>{x.num}</b>
|
||||
</td>
|
||||
|
||||
{coords.length == 0 &&
|
||||
<h3 className="text-center">No hay Coordenadas que Mostrar.</h3>
|
||||
}
|
||||
{coords.length !=0 && coords.map(x=>
|
||||
<div className="m-2"><b>#{x.num}:</b> x:{x.x} {x.y !== null && `y: ${x.y}`} z:{x.z} <button class="btn btn-outline-warning btn-sm">Editar</button><button className="btn btn-sm btn-outline-info ms-2">Compartir</button></div>
|
||||
|
||||
)}
|
||||
<td>{x.x}</td>
|
||||
<td>{x.y !== null && `Y: ${x.y}`}</td>
|
||||
<td>{x.z}</td>
|
||||
<td>{x.descripcion}</td>
|
||||
<td>
|
||||
<button
|
||||
class="ms-2 btn btn-outline-warning btn-sm"
|
||||
onClick={() => Editar(x)}
|
||||
>
|
||||
<EditarIcon />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-info ms-2"
|
||||
onClick={() => Compartir(x)}
|
||||
>
|
||||
<ShareIcon />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-danger ms-2"
|
||||
onClick={() => Remover(x)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,53 +1,50 @@
|
||||
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user