Compare commits
2 Commits
4a109d152c
...
3310be9e9b
| Author | SHA1 | Date | |
|---|---|---|---|
| 3310be9e9b | |||
| c7083a9b56 |
35
src/components/EditToggle.tsx
Normal file
35
src/components/EditToggle.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import * as Icons from "./Icons";
|
||||
import { useState, Children } from "react";
|
||||
|
||||
export function EditToggle({ children }: React.PropsWithChildren) {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const editChildren = (
|
||||
Children.toArray(children) as React.ReactElement[]
|
||||
).filter((c) => c.type === Editing);
|
||||
const nonEditChildren = (
|
||||
Children.toArray(children) as React.ReactElement[]
|
||||
).filter((c) => c.type !== Editing);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="absolute right-0 top-0 z-50">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center rounded-full bg-violet-600 hover:bg-violet-700 text-white w-8 h-8 focus:outline-none focus:ring-2 focus:ring-violet-400"
|
||||
aria-label={isEditing ? "Exit edit mode" : "Enter edit mode"}
|
||||
onClick={() => setIsEditing(!isEditing)}
|
||||
>
|
||||
<Icons.Edit />
|
||||
</button>
|
||||
</div>
|
||||
{isEditing ? editChildren : nonEditChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const Editing = ({ children }: React.PropsWithChildren) => (
|
||||
<>{children}</>
|
||||
);
|
||||
export const NotEditing = ({ children }: React.PropsWithChildren) => (
|
||||
<>{children}</>
|
||||
);
|
||||
15
src/components/documents/BasicPreview.tsx
Normal file
15
src/components/documents/BasicPreview.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { FormattedText } from "../FormattedText";
|
||||
|
||||
export type Props = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export const BasicPreview = ({ title, description }: Props) => {
|
||||
return (
|
||||
<div>
|
||||
{title && <h4 className="font-bold">{title}</h4>}
|
||||
{description && <FormattedText>{description}</FormattedText>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
68
src/components/documents/DocumentPreview.tsx
Normal file
68
src/components/documents/DocumentPreview.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
// Shows a preview of a document with it's relationships.
|
||||
import { makeDocumentPath } from "@/lib/documentPath";
|
||||
import { relationshipsForDocument } from "@/lib/relationships";
|
||||
import { type AnyDocument } from "@/lib/types";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { Editing, EditToggle, NotEditing } from "../EditToggle";
|
||||
import { BasicPreview } from "./BasicPreview";
|
||||
import { DocumentEditForm } from "./DocumentEditForm";
|
||||
|
||||
export const DocumentPreview = ({ doc }: { doc: AnyDocument }) => {
|
||||
const relationships = relationshipsForDocument(doc);
|
||||
return (
|
||||
<div>
|
||||
<EditToggle>
|
||||
<Editing>
|
||||
<DocumentEditForm document={doc} />
|
||||
</Editing>
|
||||
<NotEditing>
|
||||
<ShowDocument doc={doc} />
|
||||
</NotEditing>
|
||||
</EditToggle>
|
||||
<ul>
|
||||
{relationships.map((relType) => (
|
||||
<li>
|
||||
<Link to={makeDocumentPath(doc.id, relType)}>{relType}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ShowDocument = ({ doc }: { doc: AnyDocument }) => {
|
||||
switch (doc.type) {
|
||||
case "location":
|
||||
return (
|
||||
<BasicPreview
|
||||
title={doc.data.name}
|
||||
description={doc.data.description}
|
||||
/>
|
||||
);
|
||||
|
||||
case "monster":
|
||||
return <BasicPreview title={doc.data.name} />;
|
||||
|
||||
case "npc":
|
||||
return (
|
||||
<BasicPreview
|
||||
title={doc.data.name}
|
||||
description={doc.data.description}
|
||||
/>
|
||||
);
|
||||
|
||||
case "session":
|
||||
return (
|
||||
<BasicPreview title={doc.created} description={doc.data.strongStart} />
|
||||
);
|
||||
|
||||
case "secret":
|
||||
return <BasicPreview title={doc.data.text} />;
|
||||
|
||||
case "scene":
|
||||
return <BasicPreview description={doc.data.text} />;
|
||||
|
||||
case "treasure":
|
||||
return <BasicPreview title={doc.data.text} />;
|
||||
}
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { DocumentTitle } from "./DocumentTitle";
|
||||
import { Tab, TabbedLayout } from "../layout/TabbedLayout";
|
||||
import { DocumentEditForm } from "./DocumentEditForm";
|
||||
import { RelatedDocumentList } from "./RelatedDocumentList";
|
||||
import { DocumentPreview } from "./DocumentPreview";
|
||||
|
||||
export function DocumentView({
|
||||
documentId,
|
||||
@@ -59,6 +60,7 @@ export function DocumentView({
|
||||
tabs={[
|
||||
<Tab
|
||||
to="/document/$documentId"
|
||||
key="attributes"
|
||||
params={{
|
||||
documentId,
|
||||
}}
|
||||
@@ -68,6 +70,7 @@ export function DocumentView({
|
||||
...relationshipList.map((relationshipEntry) => (
|
||||
<Tab
|
||||
to="/document/$documentId/$relationshipType"
|
||||
key={relationshipEntry}
|
||||
params={{
|
||||
documentId,
|
||||
relationshipType: relationshipEntry,
|
||||
@@ -101,5 +104,5 @@ function Flyout({ docId }: { docId: DocumentId }) {
|
||||
|
||||
const doc = docResult.value.doc;
|
||||
|
||||
return <DocumentEditForm document={doc} />;
|
||||
return <DocumentPreview doc={doc} />;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ export const SessionRow = ({ session }: { session: Session }) => {
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
to="/document/$documentId"
|
||||
params={{ documentId: session.id }}
|
||||
to="/campaigns/$campaignId"
|
||||
params={{ campaignId: session.campaign }}
|
||||
search={{ tab: "sessions", docId: session.id }}
|
||||
className="block font-semibold text-lg text-slate-300"
|
||||
>
|
||||
<FormattedDate date={session.created} />
|
||||
|
||||
@@ -18,15 +18,17 @@ export function TabbedLayout({
|
||||
<div className="grow p-2 flex flex-col gap-2">
|
||||
<div className="flex flex-row gap-2">{navigation}</div>
|
||||
<div>{title}</div>
|
||||
<div className="flex flex-row justify-start grow">
|
||||
<div className="shrink-0 grow-0 w-40 p-0">{tabs}</div>
|
||||
<div className="flex flex-col md:flex-row justify-start grow">
|
||||
<div className="shrink-0 grow-0 md:w-40 p-0 flex flex-row flex-wrap md:flex-col md:flex-nowrap">
|
||||
{tabs}
|
||||
</div>
|
||||
<div
|
||||
className={`grow p-2 bg-slate-800 border-t border-b border-r border-slate-700`}
|
||||
className={`grow p-2 bg-slate-800 border-t border-b border-r border-slate-700 ${flyout && "hidden"} md:block`}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
{flyout && (
|
||||
<div className="w-md p-2 bg-slate-800 border border-slate-700">
|
||||
<div className="grow md:w-md p-2 bg-slate-800 border border-slate-700">
|
||||
{flyout}
|
||||
</div>
|
||||
)}
|
||||
@@ -38,8 +40,8 @@ export function TabbedLayout({
|
||||
export type TabProps = {
|
||||
label: string;
|
||||
to: string;
|
||||
params: Record<string, any>;
|
||||
search: Record<string, any>;
|
||||
params?: Record<string, any>;
|
||||
search?: Record<string, any>;
|
||||
active?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ export const DocumentContext = createContext<DocumentContextValue | undefined>(
|
||||
export function DocumentProvider({ children }: { children: ReactNode }) {
|
||||
const [state, dispatch] = useReducer(reducer, initialState());
|
||||
|
||||
console.log("State: ", state);
|
||||
|
||||
return (
|
||||
<DocumentContext.Provider
|
||||
value={{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { DocumentPreview } from "@/components/documents/DocumentPreview";
|
||||
import { DocumentRow } from "@/components/documents/DocumentRow";
|
||||
import { SessionRow } from "@/components/documents/session/SessionRow";
|
||||
import { Tab, TabbedLayout } from "@/components/layout/TabbedLayout";
|
||||
import { Loader } from "@/components/Loader";
|
||||
import { DocumentLoader } from "@/context/document/DocumentLoader";
|
||||
import { useDocument } from "@/context/document/hooks";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import type { Campaign, DocumentId, Relationship, Session } from "@/lib/types";
|
||||
@@ -166,11 +168,14 @@ function Flyout({ docId }: { docId: DocumentId }) {
|
||||
const { docResult } = useDocument(docId);
|
||||
|
||||
if (docResult?.type !== "ready") {
|
||||
return <Loader />;
|
||||
return (
|
||||
<DocumentLoader documentId={docId}>
|
||||
<Loader />
|
||||
</DocumentLoader>
|
||||
);
|
||||
}
|
||||
|
||||
const doc = docResult.value.doc;
|
||||
|
||||
// TODO: Document preview
|
||||
return <DocumentRow document={doc} root={doc} />;
|
||||
return <DocumentPreview doc={doc} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user