added indent and outdent buttons on mobile when editing task description

This commit is contained in:
2026-02-05 11:01:05 -06:00
parent 8c318bdfb5
commit 2f4207a44a
2 changed files with 70 additions and 6 deletions
+69 -6
View File
@@ -1,10 +1,12 @@
import { type Component, createEffect, untrack } from "solid-js"; import { type Component, createEffect, untrack, Show, createSignal } from "solid-js";
import { createTiptapEditor } from "solid-tiptap"; import { createTiptapEditor } from "solid-tiptap";
import StarterKit from "@tiptap/starter-kit"; import StarterKit from "@tiptap/starter-kit";
import Placeholder from "@tiptap/extension-placeholder"; import Placeholder from "@tiptap/extension-placeholder";
import TaskList from "@tiptap/extension-task-list"; import TaskList from "@tiptap/extension-task-list";
import TaskItem from "@tiptap/extension-task-item"; import TaskItem from "@tiptap/extension-task-item";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Indent, Outdent } from "lucide-solid";
import { Button } from "./ui/button";
import { SlashCommands } from "@/lib/slash-command"; import { SlashCommands } from "@/lib/slash-command";
import { suggestion } from "@/lib/slash-renderer"; import { suggestion } from "@/lib/slash-renderer";
@@ -19,6 +21,7 @@ interface TaskEditorProps {
export const TaskEditor: Component<TaskEditorProps> = (props) => { export const TaskEditor: Component<TaskEditorProps> = (props) => {
let editorRef: HTMLDivElement | undefined; let editorRef: HTMLDivElement | undefined;
const [isFocused, setIsFocused] = createSignal(false);
const editor = createTiptapEditor(() => ({ const editor = createTiptapEditor(() => ({
element: editorRef!, element: editorRef!,
@@ -59,6 +62,11 @@ export const TaskEditor: Component<TaskEditorProps> = (props) => {
onUpdate: ({ editor }) => { onUpdate: ({ editor }) => {
props.onUpdate(editor.getHTML()); props.onUpdate(editor.getHTML());
}, },
onFocus: () => setIsFocused(true),
onBlur: () => {
// Delay blur to allow button clicks
setTimeout(() => setIsFocused(false), 200);
},
editorProps: { editorProps: {
attributes: { attributes: {
class: cn( class: cn(
@@ -95,11 +103,66 @@ export const TaskEditor: Component<TaskEditorProps> = (props) => {
} }
}); });
const handleIndent = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
const eInstance = editor();
if (!eInstance) return;
if (eInstance.can().sinkListItem('taskItem')) {
eInstance.chain().focus().sinkListItem('taskItem').run();
} else if (eInstance.can().sinkListItem('listItem')) {
eInstance.chain().focus().sinkListItem('listItem').run();
}
};
const handleOutdent = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
const eInstance = editor();
if (!eInstance) return;
if (eInstance.can().liftListItem('taskItem')) {
eInstance.chain().focus().liftListItem('taskItem').run();
} else if (eInstance.can().liftListItem('listItem')) {
eInstance.chain().focus().liftListItem('listItem').run();
}
};
return ( return (
<div <div class="relative w-full">
ref={editorRef} <div
class="w-full cursor-text" ref={editorRef}
onClick={() => editor()?.chain().focus().run()} class="w-full cursor-text"
/> onClick={() => editor()?.chain().focus().run()}
/>
{/* Mobile Indentation Toolbar */}
<Show when={isFocused() && props.editable !== false}>
<div class="md:hidden fixed bottom-[env(safe-area-inset-bottom,0px)] left-0 right-0 z-[100] bg-background/95 backdrop-blur-md border-t border-border px-4 py-2 flex items-center justify-center gap-6 animate-in slide-in-from-bottom duration-200">
<Button
variant="ghost"
size="sm"
class="h-10 w-12 flex flex-col items-center gap-0.5 text-muted-foreground active:scale-95 active:bg-muted"
onMouseDown={handleOutdent}
>
<Outdent size={18} />
<span class="text-[9px] font-bold uppercase tracking-tighter">Outdent</span>
</Button>
<div class="w-px h-6 bg-border/50" />
<Button
variant="ghost"
size="sm"
class="h-10 w-12 flex flex-col items-center gap-0.5 text-muted-foreground active:scale-95 active:bg-muted"
onMouseDown={handleIndent}
>
<Indent size={18} />
<span class="text-[9px] font-bold uppercase tracking-tighter">Indent</span>
</Button>
</div>
</Show>
</div>
); );
}; };
+1
View File
@@ -34,6 +34,7 @@ export interface ButtonProps extends VariantProps<typeof buttonVariants> {
onClick?: (e: MouseEvent) => void onClick?: (e: MouseEvent) => void
onMouseEnter?: (e: MouseEvent) => void onMouseEnter?: (e: MouseEvent) => void
onMouseLeave?: (e: MouseEvent) => void onMouseLeave?: (e: MouseEvent) => void
onMouseDown?: (e: MouseEvent) => void
children?: JSX.Element children?: JSX.Element
size?: "default" | "sm" | "lg" | "icon" size?: "default" | "sm" | "lg" | "icon"
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link"