auto-git:
[change] src/App.tsx
This commit is contained in:
506
src/App.tsx
506
src/App.tsx
@@ -1324,508 +1324,10 @@ export default function App() {
|
|||||||
<div className="empty">No texts yet.</div>
|
<div className="empty">No texts yet.</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{(foldersByParent.get(null) ?? [])
|
{(foldersByParent.get(null) ?? []).map((folder) => renderFolder(folder, 0))}
|
||||||
.filter((folder) => !hasSearch || visibleFolderIds?.has(folder.id))
|
{(textsByFolder.get(null) ?? []).map((text) =>
|
||||||
.map((folder) => {
|
renderTextItem(text, 0, null)
|
||||||
const expanded = isFolderExpanded(folder.id);
|
)}
|
||||||
const childFolders = foldersByParent.get(folder.id) ?? [];
|
|
||||||
const childTexts = textsByFolder.get(folder.id) ?? [];
|
|
||||||
return (
|
|
||||||
<div key={folder.id} className="folder-node">
|
|
||||||
<div
|
|
||||||
className={`folder-item${expanded ? " is-open" : ""}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) => handleDragStartFolder(event, folder)}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) => handleFolderDrop(event, folder)}
|
|
||||||
onClick={() => toggleFolderExpanded(folder.id)}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="folder-item__toggle"
|
|
||||||
type="button"
|
|
||||||
aria-label={expanded ? "Collapse folder" : "Expand folder"}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderExpanded(folder.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{expanded ? "▾" : "▸"}
|
|
||||||
</button>
|
|
||||||
<div className="folder-item__title">{folder.name}</div>
|
|
||||||
</div>
|
|
||||||
{expanded ? (
|
|
||||||
<div className="folder-children">
|
|
||||||
{childFolders
|
|
||||||
.filter((child) => !hasSearch || visibleFolderIds?.has(child.id))
|
|
||||||
.map((child) => {
|
|
||||||
const childExpanded = isFolderExpanded(child.id);
|
|
||||||
const nestedFolders = foldersByParent.get(child.id) ?? [];
|
|
||||||
const nestedTexts = textsByFolder.get(child.id) ?? [];
|
|
||||||
return (
|
|
||||||
<div key={child.id} className="folder-node">
|
|
||||||
<div
|
|
||||||
className={`folder-item${childExpanded ? " is-open" : ""}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) => handleDragStartFolder(event, child)}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) => handleFolderDrop(event, child)}
|
|
||||||
onClick={() => toggleFolderExpanded(child.id)}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="folder-item__toggle"
|
|
||||||
type="button"
|
|
||||||
aria-label={childExpanded ? "Collapse folder" : "Expand folder"}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderExpanded(child.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{childExpanded ? "▾" : "▸"}
|
|
||||||
</button>
|
|
||||||
<div className="folder-item__title">{child.name}</div>
|
|
||||||
</div>
|
|
||||||
{childExpanded ? (
|
|
||||||
<div className="folder-children">
|
|
||||||
{nestedFolders
|
|
||||||
.filter(
|
|
||||||
(nested) =>
|
|
||||||
!hasSearch || visibleFolderIds?.has(nested.id)
|
|
||||||
)
|
|
||||||
.map((nested) => (
|
|
||||||
<div key={nested.id} className="folder-node">
|
|
||||||
<div
|
|
||||||
className={`folder-item${
|
|
||||||
isFolderExpanded(nested.id) ? " is-open" : ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) =>
|
|
||||||
handleDragStartFolder(event, nested)
|
|
||||||
}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) => handleFolderDrop(event, nested)}
|
|
||||||
onClick={() => toggleFolderExpanded(nested.id)}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="folder-item__toggle"
|
|
||||||
type="button"
|
|
||||||
aria-label={
|
|
||||||
isFolderExpanded(nested.id)
|
|
||||||
? "Collapse folder"
|
|
||||||
: "Expand folder"
|
|
||||||
}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderExpanded(nested.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isFolderExpanded(nested.id) ? "▾" : "▸"}
|
|
||||||
</button>
|
|
||||||
<div className="folder-item__title">
|
|
||||||
{nested.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isFolderExpanded(nested.id) ? (
|
|
||||||
<div className="folder-children">
|
|
||||||
{(foldersByParent.get(nested.id) ?? [])
|
|
||||||
.filter(
|
|
||||||
(nestedChild) =>
|
|
||||||
!hasSearch ||
|
|
||||||
visibleFolderIds?.has(nestedChild.id)
|
|
||||||
)
|
|
||||||
.map((nestedChild) => (
|
|
||||||
<div key={nestedChild.id} className="folder-node">
|
|
||||||
<div
|
|
||||||
className={`folder-item${
|
|
||||||
isFolderExpanded(nestedChild.id)
|
|
||||||
? " is-open"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) =>
|
|
||||||
handleDragStartFolder(event, nestedChild)
|
|
||||||
}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleFolderDrop(event, nestedChild)
|
|
||||||
}
|
|
||||||
onClick={() => toggleFolderExpanded(nestedChild.id)}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="folder-item__toggle"
|
|
||||||
type="button"
|
|
||||||
aria-label={
|
|
||||||
isFolderExpanded(nestedChild.id)
|
|
||||||
? "Collapse folder"
|
|
||||||
: "Expand folder"
|
|
||||||
}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderExpanded(nestedChild.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isFolderExpanded(nestedChild.id) ? "▾" : "▸"}
|
|
||||||
</button>
|
|
||||||
<div className="folder-item__title">
|
|
||||||
{nestedChild.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isFolderExpanded(nestedChild.id) ? (
|
|
||||||
<div className="folder-children">
|
|
||||||
{(foldersByParent.get(nestedChild.id) ?? [])
|
|
||||||
.filter(
|
|
||||||
(deepChild) =>
|
|
||||||
!hasSearch ||
|
|
||||||
visibleFolderIds?.has(deepChild.id)
|
|
||||||
)
|
|
||||||
.map((deepChild) => (
|
|
||||||
<div
|
|
||||||
key={deepChild.id}
|
|
||||||
className="folder-node"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`folder-item${
|
|
||||||
isFolderExpanded(deepChild.id)
|
|
||||||
? " is-open"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) =>
|
|
||||||
handleDragStartFolder(event, deepChild)
|
|
||||||
}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleFolderDrop(event, deepChild)
|
|
||||||
}
|
|
||||||
onClick={() =>
|
|
||||||
toggleFolderExpanded(deepChild.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="folder-item__toggle"
|
|
||||||
type="button"
|
|
||||||
aria-label={
|
|
||||||
isFolderExpanded(deepChild.id)
|
|
||||||
? "Collapse folder"
|
|
||||||
: "Expand folder"
|
|
||||||
}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderExpanded(deepChild.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isFolderExpanded(deepChild.id) ? "▾" : "▸"}
|
|
||||||
</button>
|
|
||||||
<div className="folder-item__title">
|
|
||||||
{deepChild.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isFolderExpanded(deepChild.id) ? (
|
|
||||||
<div className="folder-children">
|
|
||||||
{(textsByFolder.get(deepChild.id) ?? []).map(
|
|
||||||
(text) => (
|
|
||||||
<div
|
|
||||||
key={text.id}
|
|
||||||
className={`prompt-item${
|
|
||||||
text.id === selectedTextId
|
|
||||||
? " is-active"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) =>
|
|
||||||
handleDragStartText(event, text)
|
|
||||||
}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) =>
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleTextDrop(
|
|
||||||
event,
|
|
||||||
text.id,
|
|
||||||
text.folder_id ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={() =>
|
|
||||||
setSelectedTextId(text.id)
|
|
||||||
}
|
|
||||||
onContextMenu={(event) =>
|
|
||||||
handleTextContextMenu(event, text.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="prompt-item__content">
|
|
||||||
<div className="prompt-item__title">
|
|
||||||
{text.title}
|
|
||||||
</div>
|
|
||||||
<div className="prompt-item__meta">
|
|
||||||
Updated {formatDate(text.updated_at)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="prompt-item__delete"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setConfirmState({
|
|
||||||
title: "Delete text",
|
|
||||||
message: `Delete \"${text.title}\"? This removes all versions and drafts.`,
|
|
||||||
actionLabel: "Delete text",
|
|
||||||
onConfirm: () =>
|
|
||||||
handleDeleteText(text.id)
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
aria-label="Delete text"
|
|
||||||
title="Delete text"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{(textsByFolder.get(nestedChild.id) ?? []).map(
|
|
||||||
(text) => (
|
|
||||||
<div
|
|
||||||
key={text.id}
|
|
||||||
className={`prompt-item${
|
|
||||||
text.id === selectedTextId ? " is-active" : ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) =>
|
|
||||||
handleDragStartText(event, text)
|
|
||||||
}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleTextDrop(
|
|
||||||
event,
|
|
||||||
text.id,
|
|
||||||
text.folder_id ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={() => setSelectedTextId(text.id)}
|
|
||||||
onContextMenu={(event) =>
|
|
||||||
handleTextContextMenu(event, text.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="prompt-item__content">
|
|
||||||
<div className="prompt-item__title">
|
|
||||||
{text.title}
|
|
||||||
</div>
|
|
||||||
<div className="prompt-item__meta">
|
|
||||||
Updated {formatDate(text.updated_at)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="prompt-item__delete"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setConfirmState({
|
|
||||||
title: "Delete text",
|
|
||||||
message: `Delete \"${text.title}\"? This removes all versions and drafts.`,
|
|
||||||
actionLabel: "Delete text",
|
|
||||||
onConfirm: () =>
|
|
||||||
handleDeleteText(text.id)
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
aria-label="Delete text"
|
|
||||||
title="Delete text"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{(textsByFolder.get(nested.id) ?? []).map((text) => (
|
|
||||||
<div
|
|
||||||
key={text.id}
|
|
||||||
className={`prompt-item${
|
|
||||||
text.id === selectedTextId ? " is-active" : ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) => handleDragStartText(event, text)}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleTextDrop(
|
|
||||||
event,
|
|
||||||
text.id,
|
|
||||||
text.folder_id ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={() => setSelectedTextId(text.id)}
|
|
||||||
onContextMenu={(event) =>
|
|
||||||
handleTextContextMenu(event, text.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="prompt-item__content">
|
|
||||||
<div className="prompt-item__title">{text.title}</div>
|
|
||||||
<div className="prompt-item__meta">
|
|
||||||
Updated {formatDate(text.updated_at)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="prompt-item__delete"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setConfirmState({
|
|
||||||
title: "Delete text",
|
|
||||||
message: `Delete \"${text.title}\"? This removes all versions and drafts.`,
|
|
||||||
actionLabel: "Delete text",
|
|
||||||
onConfirm: () => handleDeleteText(text.id)
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
aria-label="Delete text"
|
|
||||||
title="Delete text"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{nestedTexts.map((text) => (
|
|
||||||
<div
|
|
||||||
key={text.id}
|
|
||||||
className={`prompt-item${
|
|
||||||
text.id === selectedTextId ? " is-active" : ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) => handleDragStartText(event, text)}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleTextDrop(
|
|
||||||
event,
|
|
||||||
text.id,
|
|
||||||
text.folder_id ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={() => setSelectedTextId(text.id)}
|
|
||||||
onContextMenu={(event) =>
|
|
||||||
handleTextContextMenu(event, text.id)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="prompt-item__content">
|
|
||||||
<div className="prompt-item__title">{text.title}</div>
|
|
||||||
<div className="prompt-item__meta">
|
|
||||||
Updated {formatDate(text.updated_at)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="prompt-item__delete"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setConfirmState({
|
|
||||||
title: "Delete text",
|
|
||||||
message: `Delete \"${text.title}\"? This removes all versions and drafts.`,
|
|
||||||
actionLabel: "Delete text",
|
|
||||||
onConfirm: () => handleDeleteText(text.id)
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
aria-label="Delete text"
|
|
||||||
title="Delete text"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{childTexts.map((text) => (
|
|
||||||
<div
|
|
||||||
key={text.id}
|
|
||||||
className={`prompt-item${
|
|
||||||
text.id === selectedTextId ? " is-active" : ""
|
|
||||||
}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) => handleDragStartText(event, text)}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) =>
|
|
||||||
handleTextDrop(event, text.id, text.folder_id ?? null)
|
|
||||||
}
|
|
||||||
onClick={() => setSelectedTextId(text.id)}
|
|
||||||
onContextMenu={(event) => handleTextContextMenu(event, text.id)}
|
|
||||||
>
|
|
||||||
<div className="prompt-item__content">
|
|
||||||
<div className="prompt-item__title">{text.title}</div>
|
|
||||||
<div className="prompt-item__meta">
|
|
||||||
Updated {formatDate(text.updated_at)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="prompt-item__delete"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setConfirmState({
|
|
||||||
title: "Delete text",
|
|
||||||
message: `Delete \"${text.title}\"? This removes all versions and drafts.`,
|
|
||||||
actionLabel: "Delete text",
|
|
||||||
onConfirm: () => handleDeleteText(text.id)
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
aria-label="Delete text"
|
|
||||||
title="Delete text"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{(textsByFolder.get(null) ?? []).map((text) => (
|
|
||||||
<div
|
|
||||||
key={text.id}
|
|
||||||
className={`prompt-item${text.id === selectedTextId ? " is-active" : ""}`}
|
|
||||||
draggable
|
|
||||||
onDragStart={(event) => handleDragStartText(event, text)}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragOver={(event) => event.preventDefault()}
|
|
||||||
onDrop={(event) => handleTextDrop(event, text.id, text.folder_id ?? null)}
|
|
||||||
onClick={() => setSelectedTextId(text.id)}
|
|
||||||
onContextMenu={(event) => handleTextContextMenu(event, text.id)}
|
|
||||||
>
|
|
||||||
<div className="prompt-item__content">
|
|
||||||
<div className="prompt-item__title">{text.title}</div>
|
|
||||||
<div className="prompt-item__meta">Updated {formatDate(text.updated_at)}</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="prompt-item__delete"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setConfirmState({
|
|
||||||
title: "Delete text",
|
|
||||||
message: `Delete \"${text.title}\"? This removes all versions and drafts.`,
|
|
||||||
actionLabel: "Delete text",
|
|
||||||
onConfirm: () => handleDeleteText(text.id)
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
aria-label="Delete text"
|
|
||||||
title="Delete text"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user