From b274d0d0184fb577c2b92e82ffd9e00394481106 Mon Sep 17 00:00:00 2001 From: Pabloader Date: Thu, 19 Mar 2026 16:55:41 +0000 Subject: [PATCH] Stories renaming --- .../assets/menu-sidebar.module.css | 72 ++++++++++++++-- .../storywriter/components/menu-sidebar.tsx | 83 +++++++++++++++++-- 2 files changed, 140 insertions(+), 15 deletions(-) diff --git a/src/games/storywriter/assets/menu-sidebar.module.css b/src/games/storywriter/assets/menu-sidebar.module.css index caee5ce..c3eab60 100644 --- a/src/games/storywriter/assets/menu-sidebar.module.css +++ b/src/games/storywriter/assets/menu-sidebar.module.css @@ -24,21 +24,79 @@ gap: 2px; } +.itemWrapper { + display: flex; + align-items: center; + gap: 4px; + padding: 2px 0; + + &.active { + background: var(--bg-active); + border-radius: 4px; + } +} + .item { - width: 100%; + flex: 1; padding: 6px 8px; text-align: left; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -} - -.active { - color: var(--text); - background: var(--bg-active); + background: transparent; + border: none; + outline: none; + cursor: pointer; + color: inherit; &:hover { - background: var(--bg-active); + background: var(--bg-hover); } } + +.active .item { + background: transparent; + + &:hover { + background: var(--bg-hover); + } +} + +.actions { + display: flex; + gap: 2px; + opacity: 0; + transition: opacity 0.1s; + + .itemWrapper:hover & { + opacity: 1; + } +} + +.actionButton { + padding: 4px 6px; + font-size: 12px; + background: transparent; + border: none; + outline: none; + cursor: pointer; + color: var(--text-muted); + border-radius: 2px; + + &:hover { + background: var(--bg-hover); + color: var(--text); + } +} + +.input { + flex: 1; + padding: 6px 8px; + font-size: 13px; + background: var(--bg-hover); + border: none; + outline: none; + color: var(--text); + border-radius: 4px; +} diff --git a/src/games/storywriter/components/menu-sidebar.tsx b/src/games/storywriter/components/menu-sidebar.tsx index 699a2dd..dccf501 100644 --- a/src/games/storywriter/components/menu-sidebar.tsx +++ b/src/games/storywriter/components/menu-sidebar.tsx @@ -3,6 +3,7 @@ import { Sidebar } from "./sidebar"; import { useAppState } from "../contexts/state"; import type { Story } from "../contexts/state"; import styles from '../assets/menu-sidebar.module.css'; +import { useState } from "preact/hooks"; // ─── Story Item ─────────────────────────────────────────────────────────────── @@ -10,16 +11,68 @@ interface StoryItemProps { story: Story; active: boolean; onSelect: () => void; + onRename: (newTitle: string) => void; + onDelete: () => void; } -const StoryItem = ({ story, active, onSelect }: StoryItemProps) => ( - -); +const StoryItem = ({ story, active, onSelect, onRename, onDelete }: StoryItemProps) => { + const [isEditing, setIsEditing] = useState(false); + const [editTitle, setEditTitle] = useState(story.title); + + const handleSubmit = () => { + if (editTitle.trim()) { + onRename(editTitle.trim()); + } + setIsEditing(false); + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter') { + handleSubmit(); + } else if (e.key === 'Escape') { + setEditTitle(story.title); + setIsEditing(false); + } + }; + + const handleBlur = () => { + handleSubmit(); + }; + + if (isEditing) { + return ( +
+ setEditTitle((e.target as HTMLInputElement).value)} + onKeyDown={handleKeyDown} + onBlur={handleBlur} + autoFocus + /> +
+ ); + } + + return ( +
+ +
+ + +
+
+ ); +}; // ─── Menu Sidebar ───────────────────────────────────────────────────────────── @@ -34,6 +87,18 @@ export const MenuSidebar = () => { dispatch({ type: 'SELECT_STORY', id }); }; + const handleRename = (id: string, newTitle: string) => { + dispatch({ type: 'RENAME_STORY', id, title: newTitle }); + }; + + const handleDelete = (id: string) => { + const story = stories.find(s => s.id === id); + if (!story) return; + if (confirm(`Delete "${story.title}"?`)) { + dispatch({ type: 'DELETE_STORY', id }); + } + }; + return (
@@ -47,6 +112,8 @@ export const MenuSidebar = () => { story={story} active={story.id === currentStory?.id} onSelect={() => handleSelect(story.id)} + onRename={(newTitle) => handleRename(story.id, newTitle)} + onDelete={() => handleDelete(story.id)} /> ))}