One direction to drop in, and a new note that stays on top
Every new note pulled the arrangement apart. A note was made with no position and left to the tie-break, which put it near the top by age -- which is not the same thing. A separator's position counts the rows above it, so a row appearing without a renumber left every line one row too high. placeOnTop builds the order from the list *without* the new note and puts it in front: measuring the room after the furniture has moved would write the misreading back as the truth. A drop now lands the row under the one it was released on, and only there. Dropping above a row and dropping below the row before it are the same move described twice, so every gap could be aimed at from two sides. The strip above the first row is the one place a drop under something cannot reach. A chapter could only be dropped into, never dragged past: taking the whole row as a place quietly took reordering away. The bottom edge of its header is the gap now, the rest of the row is still the chapter. A link showed the sheet of paper whatever it pointed at. The @ menu dropped the mark on the way into the link, and the refresh that keeps a link current ran once, while the editor was still empty -- content is poured in afterwards without an update event, so nothing ever looked again. The mark in the title bar and the box in a task list were each centred on a line box rather than on the letters in it, which is not the same as being on one line. The mark shares the title's baseline; the box is centred on the capitals and sized in em. Move to... is divided under headings that are deliberately not shaped like entries, and each row carries the mark its chapter or notebook wears. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2ffa357dfc
commit
a9391cdcf2
15 files changed
+724
-189
No files matched your search
@@ -203,6 +203,13 @@ export function toArrangement(entries: Entry[]): Arrangement {
|
||||
*
|
||||
* `notes` must be every note in the notebook, not the filtered view. Returns
|
||||
* null when the move changes nothing, which spares the caller a pointless write.
|
||||
*
|
||||
* A drop lands the row **under** the one it was released on, and only ever
|
||||
* there. Dropping above a row and dropping below the row before it are the same
|
||||
* move described twice, and having both meant every gap could be aimed at from
|
||||
* two sides — which is a hard thing to aim at and a harder thing to read. The
|
||||
* two strips, one above the first row and one below the last, are how the ends
|
||||
* are reached.
|
||||
*/
|
||||
export function reindexEntries(
|
||||
notes: NoteMeta[],
|
||||
@@ -216,21 +223,64 @@ export function reindexEntries(
|
||||
const from = entries.findIndex((e) => e.id === dragId);
|
||||
if (from < 0) return null;
|
||||
|
||||
// The strip below the last row means "put it at the end" — otherwise moving
|
||||
// something past the final note needs a drop onto the note itself, which puts
|
||||
// it above.
|
||||
const to = targetId === END_ID ? entries.length - 1 : entries.findIndex((e) => e.id === targetId);
|
||||
if (to < 0) return null;
|
||||
|
||||
const [moved] = entries.splice(from, 1);
|
||||
if (!moved) return null;
|
||||
entries.splice(to, 0, moved);
|
||||
|
||||
if (targetId === START_ID) entries.unshift(moved);
|
||||
else if (targetId === END_ID) entries.push(moved);
|
||||
else {
|
||||
// Found after the row has been lifted out, so the index is the one in the
|
||||
// list the row is being dropped into.
|
||||
const to = entries.findIndex((e) => e.id === targetId);
|
||||
if (to < 0) return null;
|
||||
entries.splice(to + 1, 0, moved);
|
||||
}
|
||||
return toArrangement(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* The whole list renumbered with one row put at the very top.
|
||||
*
|
||||
* For a note that has just been made. It used to be given no position at all and
|
||||
* left to the tie-break, which put it near the top and moved everything else:
|
||||
* a separator's position counts the rows above it, so a row appearing at the top
|
||||
* without anybody being renumbered left every line one row too high — the
|
||||
* notebook's arrangement came apart a little more with each new note.
|
||||
*
|
||||
* Renumbering the list settles that, and settles it for good: rows that were
|
||||
* never dragged all carried position 0 and were being ordered by how recently
|
||||
* they changed, so they slid about on their own. Afterwards each keeps the place
|
||||
* it is in — which is what a hand-made order is for.
|
||||
*/
|
||||
export function placeOnTop(
|
||||
notes: NoteMeta[],
|
||||
separators: Separator[],
|
||||
chapters: Chapter[],
|
||||
id: string
|
||||
): Arrangement | null {
|
||||
const note = notes.find((n) => n.id === id);
|
||||
if (!note) return null;
|
||||
/* Built from the list *without* the new note, then the note put in front.
|
||||
*
|
||||
* Merging it in first would be measuring the room after the furniture moved:
|
||||
* the new row already counts as a row, so every line reads as sitting one
|
||||
* place further down than it was drawn, and renumbering would write that
|
||||
* misreading back as the truth. */
|
||||
const entries = mergeEntries(
|
||||
notes.filter((n) => n.id !== id),
|
||||
separators,
|
||||
chapters
|
||||
);
|
||||
entries.unshift({ kind: "note", id, order: 0, note });
|
||||
return toArrangement(entries);
|
||||
}
|
||||
|
||||
/** Drop target standing for "after everything". */
|
||||
export const END_ID = "__end__";
|
||||
|
||||
/** And its opposite: the one place a drop under a row cannot reach. */
|
||||
export const START_ID = "__start__";
|
||||
|
||||
/** A new separator, added at the top where it can be seen and then dragged. */
|
||||
export const withNewSeparator = (
|
||||
separators: Separator[],
|
||||
|
||||
Reference in new issue
Block a user