Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Obsidian Editor inside AddOrEditTaskModal #50

Open
2 tasks
tu2-atmanand opened this issue Oct 10, 2024 · 6 comments
Open
2 tasks

Obsidian Editor inside AddOrEditTaskModal #50

tu2-atmanand opened this issue Oct 10, 2024 · 6 comments
Labels
enhancement An existing feature can be enhanced/improved. feature New feature or request future idea New Future or improvement, but not urgent, can be implemented in new branch

Comments

@tu2-atmanand
Copy link
Owner

tu2-atmanand commented Oct 10, 2024

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.

There is a much better approach I have found from the Hover Editor plugin, check out this discussion, also comment below in this issue : nothingislost/obsidian-hover-editor#293 (comment)

@tu2-atmanand tu2-atmanand converted this from a draft issue Oct 10, 2024
@tu2-atmanand

This comment has been minimized.

@tu2-atmanand
Copy link
Owner Author

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 tu2-atmanand added feature New feature or request enhancement An existing feature can be enhanced/improved. future idea New Future or improvement, but not urgent, can be implemented in new branch labels Oct 10, 2024
@tu2-atmanand
Copy link
Owner Author

  • Use the following new code given by GPT :

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
Copy link
Owner Author

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
Copy link
Owner Author

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.

@tu2-atmanand
Copy link
Owner Author

tu2-atmanand commented Nov 25, 2024

A suggestion from Hover Editor developer

As for integrating with a modal, HE isn't just integrating an editor - we're integrating a workspace root with a leaf and a view and all the rest, which is why we can show anything in the popover, not just markdown. (See the rootSplit property of hover editors.)

You can probably do something similar in a modal, but we have a LOT of monkeypatches to make it work right, and you'll have even more to deal with in a modal. Notably, modals override the default keyboard event handling which means commands won't work in the modal. You may also run into z-ordering problems because modals are layered above where this stuff normally renders.

If I were trying to do what you're doing, I'd probably just do my own modal class copying the HTML rather than extending Obsidian's modal. That would let me bypass both the keyboard and z-order issues, though there might also be some challenges still with what happens if somebody switches away from the view.

Well, that's what I'd do if I wanted to be able to display notes in a modal; if all you want to do is to be able to edit some markdown in one, you might be better off looking at the Kanban plugin, which pops up little mini-markdown editors when you edit Kanban cards, and it still supports all the editor hotkeys and such. I'm fairly sure they're managing that with a lot less monkeypatching, though again you may run into some issues with z-order and hotkeys if you're integrating with Obsidian's Modal class.

Link : nothingislost/obsidian-hover-editor#293 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An existing feature can be enhanced/improved. feature New feature or request future idea New Future or improvement, but not urgent, can be implemented in new branch
Projects
Status: Future Ideas
Development

When branches are created from issues, their pull requests are automatically linked.

1 participant