parent
090ded9e85
commit
cadf1f3075
Before Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,21 @@ |
|||||||
|
.commandline { |
||||||
|
margin-top: 2rem; |
||||||
|
margin-bottom: 2rem; |
||||||
|
margin-left: auto; |
||||||
|
margin-right: auto; |
||||||
|
background: lightblue; |
||||||
|
position: fixed; |
||||||
|
bottom: 0; |
||||||
|
left: 20%; |
||||||
|
right: 20%; |
||||||
|
|
||||||
|
} |
||||||
|
.commandline input { |
||||||
|
font-size: 16pt; |
||||||
|
border: none; |
||||||
|
padding: 1rem; |
||||||
|
width: calc(100% - 2rem); |
||||||
|
margin-left: 0; |
||||||
|
margin-right: 0; |
||||||
|
background: transparent; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
|
||||||
|
import './CommandEntry.css'; |
||||||
|
|
||||||
|
function CommandEntry({command, onChange}) { |
||||||
|
return ( |
||||||
|
<div className="commandline"> |
||||||
|
<input type="text" placeholder="Enter a command!" value={command} onChange={onChange}/> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default CommandEntry; |
@ -0,0 +1,128 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; |
||||||
|
import { useParams, useNavigate } from 'react-router-dom'; |
||||||
|
import { apiUrl, fetchPage, fetchPageAtEdit, postPage, deletePage } from '../apiTools.jsx'; |
||||||
|
import { useFixLinks } from '../clientStuff.jsx'; |
||||||
|
import { useLoggedIn } from '../AuthProvider.jsx'; |
||||||
|
import Sidebar from './Sidebar.jsx'; |
||||||
|
import CommandEntry from './CommandEntry.jsx'; |
||||||
|
|
||||||
|
import './Pages.css'; |
||||||
|
|
||||||
|
function PageWithSidebar({ editing }) { |
||||||
|
const queryClient = useQueryClient(); |
||||||
|
const navigate = useNavigate(); |
||||||
|
const { pagenumber, editid } = useParams(); |
||||||
|
const loggedIn = useLoggedIn(); |
||||||
|
const noLoad = useFixLinks(); |
||||||
|
const [command, setCommand] = useState(""); |
||||||
|
|
||||||
|
const fetchQuery = useQuery({ // fetch the currrent values |
||||||
|
queryKey: ['page', pagenumber, editid], |
||||||
|
queryFn: () => editid ? fetchPageAtEdit(pagenumber, editid) : fetchPage(pagenumber) |
||||||
|
}) |
||||||
|
|
||||||
|
const postMutation = useMutation({ // for changing the value when we're done with it |
||||||
|
mutationFn: ({id, title, description}) => postPage({id, title, description}), |
||||||
|
onSettled: async (data, error, variables) => { |
||||||
|
// Invalidate and refetch |
||||||
|
await queryClient.invalidateQueries({ queryKey: ['page', variables.id, undefined] }) |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const deleteMutation = useMutation({ // for changing the value when we're done with it |
||||||
|
mutationFn: (id) => deletePage(id), |
||||||
|
onSettled: async (data, error, variables) => { |
||||||
|
// Invalidate and refetch |
||||||
|
await queryClient.invalidateQueries({ queryKey: ['pages'] }) |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const readyToShow = !(fetchQuery.error || fetchQuery.isPending); |
||||||
|
|
||||||
|
let {id, title, description, html, time, author} = fetchQuery.data || {}; |
||||||
|
if (!title) title = "[no title]"; |
||||||
|
if (!html) html = "[body missing]"; |
||||||
|
if (!description) description = "[body missing]"; |
||||||
|
|
||||||
|
function submitChanges(e) { |
||||||
|
const newTitle = document.querySelector('span').innerHTML; |
||||||
|
const newText = document.querySelector('pre').innerHTML; |
||||||
|
postMutation.mutate({ |
||||||
|
id: pagenumber, |
||||||
|
title: newTitle, |
||||||
|
description: newText |
||||||
|
}); |
||||||
|
navigate(`/${pagenumber}`, {replace: true}) |
||||||
|
} |
||||||
|
|
||||||
|
function submitDelete(e) { |
||||||
|
e.preventDefault(); |
||||||
|
deleteMutation.mutate(pagenumber); |
||||||
|
navigate(`/`); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="main-column"> |
||||||
|
<header> |
||||||
|
<h1> |
||||||
|
<a href="/" {...noLoad}>🌳</a> |
||||||
|
{pagenumber}. |
||||||
|
<span |
||||||
|
contentEditable={editing} |
||||||
|
dangerouslySetInnerHTML={{__html: readyToShow ? title : "..." }} /> |
||||||
|
</h1> |
||||||
|
{readyToShow && editid && `saved at ${time} by user #${author}`} |
||||||
|
<hr/> |
||||||
|
</header> |
||||||
|
<section className="page-contents"> |
||||||
|
{ readyToShow ? |
||||||
|
( |
||||||
|
editing ? |
||||||
|
<pre |
||||||
|
contentEditable="true" |
||||||
|
dangerouslySetInnerHTML={{__html: description}} /> |
||||||
|
: |
||||||
|
<div |
||||||
|
dangerouslySetInnerHTML={{__html: html}} |
||||||
|
{...noLoad} /> |
||||||
|
) |
||||||
|
: |
||||||
|
"..." |
||||||
|
} |
||||||
|
</section> |
||||||
|
<button |
||||||
|
onClick={() => navigate(`/${pagenumber}/history`)}> |
||||||
|
History |
||||||
|
</button> |
||||||
|
{editing && ( |
||||||
|
<button |
||||||
|
disabled={postMutation.isPending} |
||||||
|
onClick={submitChanges}> |
||||||
|
{postMutation.isPending ? "Updating..." : "Update"} |
||||||
|
</button>)} |
||||||
|
{!editing && !editid && ( |
||||||
|
<button |
||||||
|
disabled={!loggedIn} |
||||||
|
onClick={() => navigate(`/${pagenumber}/edit`)}> |
||||||
|
Edit Page |
||||||
|
</button>)} |
||||||
|
{loggedIn && ( |
||||||
|
<button |
||||||
|
disabled={deleteMutation.isPending} |
||||||
|
onClick={submitDelete}> |
||||||
|
{deleteMutation.isPending ? "Deleting..." : "Delete this page and entire edit history (no backsies)"} |
||||||
|
</button>)} |
||||||
|
</div> |
||||||
|
<Sidebar pagenumber={pagenumber} hidden="true" sendWord={(word) => setCommand((command + " " + word).trim())}> |
||||||
|
{!editing && <li><button onClick={() => navigate(`/${pagenumber}/edit`)}>edit</button></li>} |
||||||
|
{editing && <li><button disabled={postMutation.isPending} onClick={submitChanges}>save</button></li>} |
||||||
|
{editing && <li><button onClick={() => navigate(`/${pagenumber}`)}>return</button></li>} |
||||||
|
</Sidebar> |
||||||
|
<CommandEntry command={command} onChange={(e) => setCommand(e.target.value)}/> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default PageWithSidebar; |
@ -0,0 +1,25 @@ |
|||||||
|
.sidebar { |
||||||
|
transition: all 0.1s linear; |
||||||
|
margin: 0; |
||||||
|
background: lightblue; |
||||||
|
position: fixed; |
||||||
|
width: 30ch; |
||||||
|
height: 100%; |
||||||
|
right: 0; |
||||||
|
top: 0; |
||||||
|
} |
||||||
|
.sidebar-hidden { |
||||||
|
transform: translateX(20ch); |
||||||
|
} |
||||||
|
|
||||||
|
.sidebar li { |
||||||
|
list-style: none; |
||||||
|
} |
||||||
|
|
||||||
|
.sidebar li button { |
||||||
|
text-transform: uppercase; |
||||||
|
} |
||||||
|
|
||||||
|
.sidebar-hidden li { |
||||||
|
display: none; |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
import { useState } from 'react'; |
||||||
|
import { useLoggedIn } from '../AuthProvider.jsx'; |
||||||
|
|
||||||
|
import './Sidebar.css'; |
||||||
|
|
||||||
|
function Sidebar({children, pagenumber, hidden=false, sendWord=(()=>null)}) { |
||||||
|
const [open, setOpen] = useState(!hidden); |
||||||
|
const loggedIn = useLoggedIn(); |
||||||
|
|
||||||
|
const verbs = ["go", "take"]; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={`sidebar ${!open && "sidebar-hidden"}`}> |
||||||
|
<button onClick={() => setOpen(!open)}>{open ? "hide" : "show"}</button> |
||||||
|
<ul> |
||||||
|
{verbs.map( (name) => ( |
||||||
|
<li key={name}> |
||||||
|
<button onClick={() => sendWord(name)}> |
||||||
|
{name} |
||||||
|
</button> |
||||||
|
</li> |
||||||
|
) )} |
||||||
|
{children} |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default Sidebar; |
Loading…
Reference in new issue