tu2-atmanand / Task-Board

An Obsidian plugin to view and manage all your task in a much more efficient Kanban Board format. Easily manage your tasks throught your vault.
https://tu2-atmanand.github.io/task-board-docs/
GNU General Public License v3.0
7 stars 0 forks source link

Obsidian Editor inside AddOrEditTaskModal #50

Open tu2-atmanand opened 1 month ago

tu2-atmanand commented 1 month ago

Right now, in the AddOrEdiTaskModal i only have a MarkdownRenderer, which renders the passed content in the format of Obsidians Reading View mode. But i want to now convert it into Obsidian's Edit View. Where the user can see the live Preview of the content the user has entered, as well as, the user will able to edit right from the modal itself, the things he want to edit. Also more powerful features like making font bold, italic, adding code, pasting images and documents, literally everything just like the Obsidian Editor.

So I just have to find a way to add this Editor inside this modal. Then i can simply remove the TextArea input element to take task Description and mention as a message that, add the Description in the below Editor.

tu2-atmanand commented 1 month ago

Yes the above thing makes sense, because, if the user add Task Description and i also have a Markdown Renderer at the bottom. That doesnt makes sense, becase the ultimate goal is to make the AddTaskModal/EditTaskModal like adding issues in Github Project. So, the best solution here is remove the Task Description part from the Modal. Now First there will be TaskTitle, then Subtasks. After that i can directly put the Task Description and i can tell user by mentioning a small note : "Edit the Task from belive live preview". So in this live preview user will able to add task Description, create more sub-subTasks and also if its possible user will able to paste images or drag and drop files as well, since its a MarkdownRenderedSection.

tu2-atmanand commented 1 month ago

So, Basically what i want is a function by Obsidian, similar to MarkdownRenderer, where i wont be passing a complete file to edit. I will only pass the previous task content, or the content which the user has entered, and now the user can edit that content, add anything he wants and then, i will receive this content entered/edited by user in a variable. From this received data, i will fetch all the fields and update them in json and file when the user will press save button.

This editor, will also help me for the adding Comments feature i want to have in future just like this Github. But, notice one this in, github also, right now i am adding this comment using simple text editor. And there is a button to Preview this into Markdow, if you have entered any image or any other document. But in Obsidian it will be live, just like Obisidian's Live Editor.

tu2-atmanand commented 1 month ago

I am going to use the code from the following link and create a new file in my project : https://github.com/mgmeyers/obsidian-kanban/blob/main/src/components/Editor/MarkdownEditor.tsx

Then I will update the below code in my AddOrEditTaskModal to use this MarkdownEditor :

To embed the `MarkdownEditor` inside the `EditTaskContent` component and replace the existing `textarea`, here’s how you can modify the code.

### Steps:
1. **Create the `EmbeddedMarkdownEditor.tsx` file**:
   Place the provided `MarkdownEditor` code in the `EmbeddedMarkdownEditor.tsx` file as planned.

2. **Modify `EditTaskContent` to Use `MarkdownEditor`**:
   Replace the existing `textarea` with the `MarkdownEditor` from your `EmbeddedMarkdownEditor.tsx` file, while handling the necessary props, such as `value`, `onChange`, `onSubmit`, etc.

Here is how you can update the `EditTaskContent` component to use the new `MarkdownEditor`:

### Code Changes in `EditTaskContent`:

Replace the `textarea` block with the following:

```tsx
// Import the MarkdownEditor from the EmbeddedMarkdownEditor file
import { MarkdownEditor } from './Editor/EmbeddedMarkdownEditor';

// ...

const EditTaskContent: React.FC<{ app: App, plugin: TaskBoard, root: HTMLElement, task?: taskItem, taskExists?: boolean, filePath: string; onSave: (updatedTask: taskItem) => void; onClose: () => void }> = ({ app, plugin, root, task = taskItemEmpty, taskExists, filePath, onSave, onClose }) => {
  // Replace the textarea state handler with the MarkdownEditor state handler
  const [taskContent, setTaskContent] = useState(task.body ? task.body.join('\n') : '');

  // Function to handle the submission of the task from MarkdownEditor
  const handleMarkdownSubmit = (editor: EditorView) => {
    const updatedContent = editor.state.doc.toString();
    const updatedTask = parseTaskContent(updatedContent);
    setTaskContent(updatedContent);
    setUpdatedTask(updatedTask);
    onSave(updatedTask);
    onClose();
  };

  return (
    <div className="EditTaskModalHome">
      {/* Your other code */}

      {/* Conditional rendering based on active tab */}
      <div className={`EditTaskModalTabContent ${activeTab === 'preview' ? 'show' : 'hide'}`}>
        {/* Preview Section */}
        <div className="EditTaskModalHomePreview" style={{ display: activeTab === 'preview' ? 'block' : 'none' }}>
          {/* Your Preview logic */}
        </div>
      </div>

      <div className={`EditTaskModalTabContent ${activeTab === 'editor' ? 'show' : 'hide'}`}>
        <div className="EditTaskModalHomePreviewHeader">
          Directly Edit any value or add more sub tasks and description for this task.
        </div>

        {/* Embed the MarkdownEditor */}
        <MarkdownEditor
          value={taskContent}
          onChange={(update) => setTaskContent(update.state.doc.toString())}
          onSubmit={handleMarkdownSubmit}
          className="EditTaskModalBodyDescription"
          placeholder="Body content"
        />
      </div>

      {/* Rest of your component code */}
      <button className="EditTaskModalHomeSaveBtn" onClick={handleSave}>Save</button>
    </div>
  );
};

// Rest of your modal logic

Key Changes:

  1. MarkdownEditor Integration:

    • The MarkdownEditor replaces the textarea to provide a richer editing experience.
    • The onSubmit handler is configured to save and close the modal when the user finishes editing.
  2. State Management:

    • The content of the editor is managed by taskContent and updated using the onChange event of the MarkdownEditor.
  3. Preview Tab:

    • The preview logic remains unchanged, but when switching to the editor tab, users will now have the full Markdown editing experience.

By following these steps, you'll be able to replace the standard textarea with the custom MarkdownEditor while keeping the task's editing and previewing functionalities intact.

tu2-atmanand commented 1 month ago

Following is the simplest code GPT gave using the codeMirror library, just sharing here to refer, because I am deleting the below file /src/components/MarkdownEditor.tsx :

// /src/components/MarkdownEditor.tsx

import { EditorView, basicSetup } from 'codemirror';
import React, { useEffect, useRef, useState } from 'react';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';

import { indentWithTab } from '@codemirror/commands';
import { keymap } from '@codemirror/view';
import { oneDark } from '@codemirror/theme-one-dark';

class MarkdownEditor {
    private editorView: EditorView | null = null;
    private onChangeCallback: (value: string) => void;

    constructor(onChangeCallback: (value: string) => void) {
        this.onChangeCallback = onChangeCallback;
    }

    initializeEditor(editorContainer: HTMLDivElement, initialValue: string) {
        if (this.editorView) {
            this.editorView.destroy();
        }

        this.editorView = new EditorView({
            doc: initialValue,
            extensions: [
                basicSetup,
                markdown({ base: markdownLanguage }),
                oneDark,
                keymap.of([indentWithTab]), // Correctly wrap keybindings with `keymap.of()`
                EditorView.updateListener.of((update) => {
                    if (update.docChanged) {
                        const currentValue = this.editorView?.state.doc.toString() || '';
                        this.onChangeCallback(currentValue);
                    }
                }),
            ],
            parent: editorContainer,
        });
    }

    destroyEditor() {
        if (this.editorView) {
            this.editorView.destroy();
            this.editorView = null;
        }
    }

    getEditorValue(): string {
        return this.editorView?.state.doc.toString() || '';
    }

    static extractIndentedLines(content: string): string[] {
        return content
            .split('\n')
            .filter((line) => /^\s+[^- \[]/.test(line)); // lines with indentation not starting with `- [ ]`
    }
}

export default function CodeMirrorEditor({ initialContent, onChange }: { initialContent: string, onChange: (bodyContent: string[]) => void }) {
    const editorRef = useRef<HTMLDivElement>(null);
    const [editorInstance, setEditorInstance] = useState<MarkdownEditor | null>(null);

    useEffect(() => {
        if (editorRef.current) {
            const editor = new MarkdownEditor((value: string) => {
                const indentedLines = MarkdownEditor.extractIndentedLines(value);
                onChange(indentedLines);
            });
            editor.initializeEditor(editorRef.current, initialContent);
            setEditorInstance(editor);

            return () => {
                editor.destroyEditor();
            };
        }
    }, [initialContent, onChange]);

    return <div ref={editorRef} className="markdown-editor"></div>;
}
tu2-atmanand commented 1 month ago

Creating a new branch for this Markdown Editor Implementation called markdownEditorEmbed As of now, i have the TextArea element to edit/add description for the task. Will release the plugin with this state. The work can be parallely dont on this branch to achieve this feature, with the current state of the plugin, later on, i can simply merge.