Stories renaming
This commit is contained in:
parent
061f79c473
commit
b274d0d018
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => (
|
||||
<button
|
||||
class={clsx(styles.item, active && styles.active)}
|
||||
onClick={onSelect}
|
||||
>
|
||||
{story.title}
|
||||
</button>
|
||||
);
|
||||
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 (
|
||||
<div class={clsx(styles.itemWrapper, active && styles.active)}>
|
||||
<input
|
||||
class={styles.input}
|
||||
value={editTitle}
|
||||
onInput={(e) => setEditTitle((e.target as HTMLInputElement).value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={clsx(styles.itemWrapper, active && styles.active)}>
|
||||
<button
|
||||
class={clsx(styles.item, active && styles.active)}
|
||||
onClick={onSelect}
|
||||
>
|
||||
{story.title}
|
||||
</button>
|
||||
<div class={styles.actions}>
|
||||
<button class={styles.actionButton} onClick={() => setIsEditing(true)} title="Rename">
|
||||
✎
|
||||
</button>
|
||||
<button class={styles.actionButton} onClick={onDelete} title="Delete">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// ─── 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 (
|
||||
<Sidebar side="left">
|
||||
<div class={styles.menu}>
|
||||
|
|
@ -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)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue