feat: add block movement
This commit is contained in:
parent
3b51161203
commit
8b1de14f79
@ -1,6 +1,7 @@
|
||||
import { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getNote } from "@/lib/notes";
|
||||
import { Metadata } from "next";
|
||||
import Editor from "@/components/editor/Editor";
|
||||
|
||||
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
|
||||
const { id } = await params;
|
||||
@ -20,8 +21,8 @@ export default async function Note({ params }: { params: Promise<{ id: string }>
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{note.name}
|
||||
<div className="flex justify-center">
|
||||
<Editor defaultTitle={note.name} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { ChangeEvent } from "react";
|
||||
import { Lock, LockOpen, Menu, Minus, Plus, X } from "lucide-react";
|
||||
import { ArrowDown, ArrowUp, Lock, LockOpen, Minus, Plus, X } from "lucide-react";
|
||||
import { Action, IBlock, ILine } from "@/lib/editorReducer";
|
||||
import IconOnlyButton from "../ui/IconOnlyButton";
|
||||
import LineInput from "./LineInput";
|
||||
@ -33,6 +33,14 @@ export default function Block({
|
||||
dispatch({ type: "delete_block", blockId: block.id });
|
||||
}
|
||||
|
||||
const handleBlockUp = () => {
|
||||
dispatch({ type: "move_block_up", blockId: block.id });
|
||||
}
|
||||
|
||||
const handleBlockDown = () => {
|
||||
dispatch({ type: "move_block_down", blockId: block.id });
|
||||
}
|
||||
|
||||
const handleToggleLock = () => {
|
||||
dispatch({ type: "toggle_lock", blockId: block.id });
|
||||
}
|
||||
@ -57,14 +65,17 @@ export default function Block({
|
||||
/>
|
||||
))}
|
||||
<div className="flex items-center mx-2 mt-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex gap-1 mr-4">
|
||||
<IconOnlyButton onClick={handleAddLine} icon={<Plus size={18} />} />
|
||||
<IconOnlyButton onClick={handleDeleteLine} icon={<Minus size={18} />} />
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<IconOnlyButton onClick={handleBlockUp} icon={<ArrowUp size={18} />} />
|
||||
<IconOnlyButton onClick={handleBlockDown} icon={<ArrowDown size={18} />} />
|
||||
</div>
|
||||
<div className="flex gap-2 ml-auto">
|
||||
<IconOnlyButton onClick={handleDeleteBlock} icon={<X size={18} />} />
|
||||
<IconOnlyButton onClick={handleToggleLock} icon={block.locked ? <Lock size={18} /> : <LockOpen size={18} />} alwaysOn={block.locked} />
|
||||
<IconOnlyButton icon={<Menu size={18} />} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,22 +3,29 @@
|
||||
import { useReducer } from "react";
|
||||
import { Copy, Plus } from "lucide-react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { editorReducer } from "@/lib/editorReducer";
|
||||
import { editorReducer, IBlock } from "@/lib/editorReducer";
|
||||
import IconOnlyButton from "../ui/IconOnlyButton";
|
||||
import Block from "./Block";
|
||||
|
||||
export default function Editor() {
|
||||
const [state, dispatch] = useReducer(editorReducer, [
|
||||
{
|
||||
interface EditorProps {
|
||||
defaultTitle?: string;
|
||||
defaultBlocks?: IBlock[];
|
||||
}
|
||||
|
||||
const defaultBlocks = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
tag: "",
|
||||
locked: false,
|
||||
lines: Array.from({ length: 4 }, () => ({
|
||||
id: uuidv4(),
|
||||
tag: "",
|
||||
locked: false,
|
||||
lines: Array.from({ length: 4 }, () => ({
|
||||
id: uuidv4(),
|
||||
text: "",
|
||||
})),
|
||||
}
|
||||
]);
|
||||
text: "",
|
||||
})),
|
||||
}
|
||||
];
|
||||
|
||||
export default function Editor(props: EditorProps) {
|
||||
const [state, dispatch] = useReducer(editorReducer, props.defaultBlocks || defaultBlocks);
|
||||
|
||||
const handleAddBlock = () => {
|
||||
dispatch({ type: "add_block" });
|
||||
@ -30,7 +37,7 @@ export default function Editor() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center px-4 max-w-2xl w-full gap-4">
|
||||
<input className="font-bold text-xl w-full text-center focus:outline-none" defaultValue="Untitled" />
|
||||
<input className="font-bold text-xl w-full text-center focus:outline-none" defaultValue={props.defaultTitle || "Untitled"} />
|
||||
{state.map((block) => <Block key={block.id} block={block} dispatch={dispatch} /> )}
|
||||
<div className="flex gap-2">
|
||||
<IconOnlyButton onClick={handleAddBlock} icon={<Plus size={24} />} />
|
||||
|
@ -22,6 +22,8 @@ export type Action =
|
||||
| { type: "delete_line", blockId: string }
|
||||
| { type: "update_line_text"; blockId: string; lineId: string; text: string }
|
||||
| { type: "update_tag"; blockId: string; tag: string }
|
||||
| { type: "move_block_up", blockId: string }
|
||||
| { type: "move_block_down", blockId: string }
|
||||
| { type: "toggle_lock", blockId: string };
|
||||
|
||||
export function editorReducer(state: EditorState, action: Action): EditorState {
|
||||
@ -48,7 +50,11 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
|
||||
copyText += `[${block.tag}]\n`;
|
||||
}
|
||||
|
||||
block.lines.forEach((line) => copyText += line.text + "\n")
|
||||
block.lines.forEach((line) => {
|
||||
if (line.text !== "") {
|
||||
copyText += line.text + "\n";
|
||||
}
|
||||
})
|
||||
copyText += "\n";
|
||||
});
|
||||
|
||||
@ -115,6 +121,22 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
|
||||
}
|
||||
});
|
||||
|
||||
case "move_block_up": {
|
||||
const index = state.findIndex((b) => b.id === action.blockId);
|
||||
if (index <= 0) return state;
|
||||
const newState = [...state];
|
||||
[newState[index - 1], newState[index]] = [newState[index], newState[index - 1]];
|
||||
return newState;
|
||||
}
|
||||
|
||||
case "move_block_down": {
|
||||
const index = state.findIndex((b) => b.id === action.blockId);
|
||||
if (index === state.length - 1) return state;
|
||||
const newState = [...state];
|
||||
[newState[index], newState[index + 1]] = [newState[index + 1], newState[index]];
|
||||
return newState;
|
||||
}
|
||||
|
||||
case "toggle_lock":
|
||||
return state.map((block) => {
|
||||
if (block.id === action.blockId) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user