Move a note in one press, and stop Draw inventing a page
Moving lived under a right-click, behind a submenu: two steps before the list of places appeared at all, and on a phone a long press followed by a flyout. There is a button on the row now, and it opens one screen with the chapters of this notebook and the other notebooks together, marking where the note already is. The spare sheet in Draw mode was drawn as solid paper, so pressing Draw on a one-page note made a second page appear from nowhere while the count still said "of 1". It exists for a good reason — pages otherwise come only from how far the writing runs, so drawing on page two would mean writing two pages first — but it should look like the offer it is. Dashed outline, half opacity, and it says what it is for. It counts once something is on it. And the two buttons above the list did not match: the one I added last round was larger and bolder than the one it sat beside, and the older one's target was nineteen pixels tall, which a finger misses. Both are one shape now. A separator's label matches a chapter's name in size for the same reason — two type treatments in one list is noise, and the difference was not saying anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9d5ac1db27
commit
632ea5dcd9
7 files changed
+329
-29
No files matched your search
@@ -0,0 +1,180 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { I } from "./icons.jsx";
|
||||
import { coverBackground } from "./constants.jsx";
|
||||
|
||||
/**
|
||||
* Where a note should go.
|
||||
*
|
||||
* Moving used to live only under a right-click, behind a submenu — two steps
|
||||
* before the list of places even appeared, and on a phone a long press followed
|
||||
* by a flyout. This is the same list, reached by one press of a button on the
|
||||
* row.
|
||||
*
|
||||
* Everything is on one screen: the chapters of the notebook the note is already
|
||||
* in, and then the other notebooks. Sending it to another notebook takes it out
|
||||
* of whatever chapter it was in, because a chapter belongs to the book it is in
|
||||
* — which is worth saying here rather than leaving to be discovered.
|
||||
*/
|
||||
export function MoveNoteDialog({ note, notebooks, chapters, onMove, onClose, dark }) {
|
||||
const T = useMemo(
|
||||
() => ({
|
||||
bg: dark ? "#161b22" : "#fff",
|
||||
border: dark ? "#30363d" : "#d0d7de",
|
||||
text: dark ? "#e6edf3" : "#1f2328",
|
||||
muted: dark ? "#8b949e" : "#656d76",
|
||||
accent: dark ? "#388bfd" : "#0969da",
|
||||
row: dark ? "#0d1117" : "#f6f8fa",
|
||||
hover: dark ? "#21262d" : "#eaeef2",
|
||||
}),
|
||||
[dark]
|
||||
);
|
||||
|
||||
const here = chapters.filter((c) => c.notebookId === note.folderId);
|
||||
const others = notebooks.filter((nb) => nb.id !== note.folderId);
|
||||
|
||||
const go = (folderId, chapterId) => {
|
||||
onMove(note.id, folderId, chapterId);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const Row = ({ children, onClick, disabled, dot, icon }) => (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
data-move-target
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
width: "100%",
|
||||
padding: "11px 12px",
|
||||
borderRadius: 10,
|
||||
border: "none",
|
||||
background: disabled ? T.row : "transparent",
|
||||
color: disabled ? T.muted : T.text,
|
||||
cursor: disabled ? "default" : "pointer",
|
||||
fontSize: 13,
|
||||
textAlign: "left",
|
||||
}}
|
||||
onMouseEnter={(e) => !disabled && (e.currentTarget.style.background = T.hover)}
|
||||
onMouseLeave={(e) => !disabled && (e.currentTarget.style.background = "transparent")}
|
||||
>
|
||||
{dot && <div style={{ width: 12, height: 16, borderRadius: 3, background: dot, flexShrink: 0 }} />}
|
||||
{icon && <span style={{ display: "flex", opacity: 0.7, flexShrink: 0 }}>{icon}</span>}
|
||||
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{children}</span>
|
||||
{disabled && <span style={{ marginLeft: "auto", fontSize: 11, flexShrink: 0 }}>here now</span>}
|
||||
</button>
|
||||
);
|
||||
|
||||
const heading = (text) => (
|
||||
<div
|
||||
style={{
|
||||
fontSize: 11,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 0.6,
|
||||
textTransform: "uppercase",
|
||||
color: T.muted,
|
||||
margin: "12px 12px 4px",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-move-dialog
|
||||
onClick={onClose}
|
||||
style={{
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
background: "rgba(0,0,0,0.5)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
zIndex: 4000,
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: 400,
|
||||
maxHeight: "80vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
background: T.bg,
|
||||
border: `1px solid ${T.border}`,
|
||||
borderRadius: 16,
|
||||
padding: "20px 10px 12px",
|
||||
fontFamily: "'DM Sans',sans-serif",
|
||||
}}
|
||||
>
|
||||
<h3 style={{ margin: "0 12px 4px", fontSize: 16, fontWeight: 700, color: T.text }}>Move note</h3>
|
||||
<div
|
||||
style={{
|
||||
margin: "0 12px 8px",
|
||||
fontSize: 12,
|
||||
color: T.muted,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{note.title || "Untitled"}
|
||||
</div>
|
||||
|
||||
<div style={{ overflowY: "auto", flex: 1 }}>
|
||||
{here.length > 0 && heading("In this notebook")}
|
||||
{here.length > 0 && (
|
||||
<Row icon={I.note} disabled={!note.chapterId} onClick={() => go(note.folderId, null)}>
|
||||
The notebook itself
|
||||
</Row>
|
||||
)}
|
||||
{here.map((c) => (
|
||||
<Row
|
||||
key={c.id}
|
||||
icon={I.books}
|
||||
disabled={note.chapterId === c.id}
|
||||
onClick={() => go(note.folderId, c.id)}
|
||||
>
|
||||
{c.name}
|
||||
</Row>
|
||||
))}
|
||||
|
||||
{others.length > 0 && heading("Another notebook")}
|
||||
{others.map((nb) => (
|
||||
<Row key={nb.id} dot={coverBackground(nb.cover, nb.coverPattern)} onClick={() => go(nb.id, null)}>
|
||||
{nb.name}
|
||||
</Row>
|
||||
))}
|
||||
|
||||
{here.length === 0 && others.length === 0 && (
|
||||
<div style={{ padding: "18px 12px", fontSize: 12.5, color: T.muted }}>
|
||||
There is nowhere else to put it yet — make another notebook, or a chapter in this one.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", padding: "10px 12px 0" }}>
|
||||
<button
|
||||
onClick={onClose}
|
||||
style={{
|
||||
padding: "8px 16px",
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${T.border}`,
|
||||
background: "transparent",
|
||||
color: T.text,
|
||||
cursor: "pointer",
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user