Giter Site home page Giter Site logo

markupeditor's People

Contributors

johnkuan avatar stevengharris avatar thomasmengelatte avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

markupeditor's Issues

Notify MarkupDelegate when a local image is deleted

Altho we notify when a local image is added (so, for example, the user can copy the image file somewhere), we also need to know when a local image is deleted (so, for example, we can remove the copy we made).

Multiple nested indent on UL/OL

Hi Steve,

I am looking into the behaviour of the indentation for the ordered and unordered list.

This does not work or constructible.

        • H

where as this the current behaviour

  1. Here is a numbered sublist.

    1. With two items.

    2. I

The second item can be indented and that becomes the first item on the newer indent level.

I went through the code and this line in the function _indentListItem
if (!previousListItem || (previousListItem.nodeName !== 'LI')) { return null };
returns null because the previous item is null when I tried to indent on the first item.

I have seen this comment that explains the reason to not indent.
// If previousElementSibling doesn't exist or is not
// an LI, then the indent is not allowed.

Is this logic intentional that we are not able to do multiple indent on the list for the first item on the indent level?

Touch device selection doesn't work properly

The MarkupEditor SelectionState depends on various click/mouse events in ways that don't work properly on touch devices. As a result, a lot of the basic editing operations don't work properly on touch devices.

Keyboard Avoidance Issue

As noted in #87, the MarkupEditorView does not avoid the keyboard when in a ScrollView. Raising this issue here just so it's clearer.

Here is an example that shows the different behavior between a MarkupEditorView and a TextField.

struct ContentView: View, MarkupDelegate {
        
    @State private var text = "Hello World"
    @State private var loaded: Bool = false
    @State private var demoHtml: String = "<h1>Hello World</h1>"
    
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                Rectangle()
                    .frame(height: 1000)
                MarkupEditorView(markupDelegate: self, html: $demoHtml).id(1)
                    .frame(height: 200)
                    .overlay(
                        RoundedRectangle(cornerRadius: 6.0)
                            .stroke(.gray, lineWidth: 1.0)
                    )
                    .onChange(of: loaded) { _ in
                        withAnimation {
                            proxy.scrollTo(1)
                        }
                    }
                TextField("Name:", text: $text)
            }
            .padding()
        }
    }
    
    func markup(_ view: MarkupWKWebView, heightDidChange height: Int) {
        loaded = true
    }
    
}

If you select in the TextField, the ScrollView scrolls upward to expose the insertion point. With MarkupEditorView, the insertion point will be covered by the keyboard. They MarkupEditorView needs to have the same behavior as TextField.

Pasteboard alert on iOS 16

Hello,

Is there a way to disable the paste alerts that are thrown at initialization?

I know I can disable the alert in the app settings as of iOS 16.1, but I really prefer it to not be there in the first place. I can't ask all my users to go there.

Simulator Screen Shot - iPhone 14 Pro - 2022-12-09 at 17 48 10

Selection caret is missing on startup

Although SelectionState is set properly, the selection caret is sometimes missing, especially when more than 1 MarkupWKWebView is present and selection is accomplished using MarkupEditor.firstResponder.

Binding property in SwiftUI not working

@State var value = "sample"      

MarkupEditorView(html:$value)   

No errors the editor works, but any changes do not affect the "value" property, in this example it is always "sample". The demo in the repository is the same.
ios 16.1 simulator and device

Undo/Redo does not work properly in MacOS Ventura

When you "do" an action (e.g., bold, indent, etc) in the MarkupEditor, a change is pushed on the native undo stack by executing:

document.execCommand('selectAll');
document.execCommand('insertText', false, nextID);

while focused on a hidden div in the document. The nextID tracks a pointer into the internal undo stack. The code to do this is in markup.js in the Undoer class.

Version 0.5.1 works properly on Big Sur. As you "do" actions, you can see that the Undo menu option in the Edit menu is enabled, because the execCommand('insertText') runs and pushes the change to the div's content onto the native undo stack. Once the Undo and/or Redo are enabled, then the standard hot-keys (⌘Z and ⇧⌘Z) work which in turn invoke the undo operation in the MarkupEditor.

In Ventura, the same code executes, but the native undo stack is not changed. The Undo and Redo never get enabled in the Edit as you invoke operations on the document, and therefore neither the menu or hot-keys work for Undo/Redo.

Editing operations that don't have to do with MarkupEditor actions (e.g., type some text then undo) work properly in both versions. Only the MarkupEditor operations (think: things that are exposed via the MarkupToolbar) are impacted.

FWIW, the undo/redo tests work properly because the invoke the operations queued in the internal undo stack.

In a sad bit of irony, this is one of the very few places where the MarkupEditor uses the deprecated and browser-dependent execCommand.

Outdent at one line in a multi-line blockquote should outdent only the one line

If you have a blockquote with multiple lines like:

This is a multiline
blockquote

and you try to outdent one line, both get outdented. This happens because Enter in a blockquote just creates a new paragraph within the blockquote, so outdent is just removing the one blockquote that surrounds both <p> elements. This is probably better done by having enter produce a separate blockquote for each <p>, especially now that multidenting allows you to select multiple dented paragraphs and take an action on them collectively.

Fix Toolbar selection state when selection spans multiple elements

With operations supported when multiple elements are selected, the SelectionState needs to stop relying on anchorNode. In some cases the SelectionState is preventing an operation from being applied across multiple elements because it is used to determine is Toolbar buttons are disabled.

Support for Span Style Tag for inline styling

Hi @stevengharris, I was testing around the addition of span tag, alongside H1-H6, it seems possible to include in the StyleContext.swift file, but it also will need support on the markup.js file.

https://www.w3schools.com/tags/tag_span.asp

Since H1-H6 are block level styling, I'm wondering whether will you consider inline styling using span in the future?

I am looking at the possibility to define <span style="font-size: 23px";>Example</span> syntax for a user who wants to have finer control on the styling.

[Intel only] MacOS 13 (Ventura) editing is buggy due to keydown event not firing

The MarkupEditor handles keydown specially in markup.js for some specific cases:

  • Enter when inside of a list or blockquote/indent and at the beginning or end of a styled paragraph
  • Backspace/Delete when at the beginning of a styled paragraph
  • Arrow keys (for specially handling near an image)
  • Tab navigation in tables

The special handling deals with things like creating a new list item, moving between table cells, etc. When no special handling is required, the standard browser behavior happens, which is the case almost all of the time. As an example, when pressing Enter when the selection is in a list, we want to create a new list item of the same type as the list we are in. However, the standard browser behavior is just to insert a <br>, which is not what anyone expects and makes it impossible to add items to a list just by typing and pressing Enter at the end of each item.

On Mac Catalyst as of MacOS 13 (Ventura), we never receive a keydown event, so none of the special handling happens. As a result, only the standard (and as described above, incorrect) browser behavior occurs. As a result, from a user perspective, a lot of editing on Mac is buggy as of the Ventura release. Editing on iOS (as of iOS 16) works fine.

The problem is reproducible in the simplest app that uses a WKWebView. For example, using the following in your ContentView in a clean SwiftUI app created with Xcode will show the click event triggering, but the keydown event never triggers. Note:

  • Both events trigger on iOS.
  • Both events trigger on Mac Catalyst in MacOS 12.
  • The input element has the same problem as the contenteditable div on Mac Catalyst on MacOS 13.
import SwiftUI
import WebKit

struct ContentView: View {
    var div: String = "<div contenteditable=\"true\" onkeydown=\"console.log('keydown: ' + event.key);\" onclick=\"console.log('click');\"<h1>Hello world.</h1></div>"
    var input: String = "<input type=\"text\" onkeydown=\"console.log('keydown: ' + event.key);\" onclick=\"console.log('click');\">"

    var body: some View {
        VStack {
            WebView(html: div)
            WebView(html: input)
        }
        .padding()
        .background(Color.red)
    }
}

struct WebView: UIViewRepresentable {
    
    var html: String
    
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    
    func updateUIView(_ webView: WKWebView, context: Context) {
        webView.loadHTMLString(html, baseURL: nil)
    }
}

Customize toolbar

Hello,

I found possible problem. I would like to customize toolbar and I use following code:

`
MarkupEditor.style = .labeled
MarkupEditor.allowLocalImages = false

       let myToolbarContents = ToolbarContents(leftToolbar: false, correction: false, insert: false, style: true, format: true, rightToolbar: false, insertContents: InsertContents(link: false, image: false, table: false), styleContents: StyleContents(list: true, dent: false), formatContents: FormatContents(code: false, strike: false, subSuper: false))
       
       ToolbarContents.custom = myToolbarContents

`

but I still see button with select "normal", "header 1-6". 

a need to hide this button. (stay only LIST (ul and li) and B I U)

Zrzut ekranu 2022-09-1 o 10 55 58

Piotr

Table toolbar presentation/dismissal issues

In environments with a non-hardware keyboard, the MarkupToolbar is presented as an inputAccessoryView with the keyboard. The TableToolbar is presented as a SubToolbar that extends from the MarkupToolbar. This approach works okay for the Mac, but it presents (pun intended) problems on dismissal. For example, when the TableSizer is presented, and the user drags across the grid, the TableSizer should be dismissed when the drag is complete. This doesn't work on iOS.

The second issue with the TableToolbar presentation is that when the MarkupToolbar is embedded in another view's toolbar along with other ToolbarItems, the SubToolbar type of style of extending from the MarkupToolbar just looks bad. The SubToolbar extends for the MarkupToolbar width, but the MarkupToolbar is embedded in a larger toolbar, so the SubToolbar is a kind of stub that extends below the larger toolbar and looks weird.

The TableToolbar should be presented as a .popover from the "insert table" button of the InsertToolbar. This behavior would also be consistent with the link and image presentation approach when those buttons are pressed.

Provide context menus

Context menus are required on touch devices at a minimum for copy/cut/paste, and should mirror the Format menu on Mac Catalyst.

Image sizes should be relative to width

Current image sizing maintains proportionality but is absolute. This is bad when the same document is edited on devices with different window size/resolution, like a Mac and an iPhone.

toolbar customization

you should be able to customize the toolbar by being able to remove any of the toolbars (ex. if you want to remove the InsertToolbar )
and if you want to remove some of the features in a toolbar (ex.remove the underline in the FormatToolbar)

Complete Menu/HotKey Support

Currently the menus and hotkeys exposed in the demo apps only support some operations for the MarkupEditor (e.g., standard editing ones like Ctrl-B, Ctrl-I, etc). Features need to be fully supported in menus and hotkey.

InputAccessoryView disappears on device rotation

The inputAccessoryView in MarkupWKWebView disappears when you rotate the device, which is even more of a problem when that means there is no way to dismiss the keyboard that no longer has an inputAccessoryView.

Formatting logic seems to be wrong when adding <p>

Hi @stevengharris,

For the formatting logic (Bold, Italics, Underline, etc), the insertion of newline "p" appears to break the formatting structure of previous text. This is especially apparent when the structure becomes nested with permutations of B/I/U and P tags.
I am comparing this behaviour with email clients like outlook and gmail which provides similar formatting functionalities and it does not feel like this matches the behaviour of those email clients.

I have attached a screen recording of this occurrence.
https://user-images.githubusercontent.com/4402214/167060824-cecd4578-36df-4932-9813-d0f4be8f114e.mp4

Do let me know if this is an intended behaviour for formatting the text, and also do let me know if you require additional assistance on this.

Treatment of <code> blocks

The MarkupEditor does not treat <code> as a block element, since it's not one. It does, however, have markup.css set up to format code inside of <p> and other "style" block elements differently than when <code> is inside of a <blockquote>, which is a block element itself. For <p> and the other "style" elements, it <code> uses css "display: inline", while outside of the "style" elements (e.g., in <blockquote>) it uses css "display: block". This kind of works, but it means that in the MarkupEditor currently, the only time you can get code to display as a block is in an indented block, which is not what everyone (including me) wants. To fix this, I think <code> needs to be displayable inside of a <pre> block element. This in turn will require special handling for Enter and for multistyle operations.

An associated problem with <code> formatting is that it needs to be scrollable horizontally. This is fixable by including "overflow-x: scroll" in code's css. However, given the other problem above, the scrolling only works currently when code is in a blockquote, since the css uses "display: inline" inside of the styling elements.

Question: RTL language support

Hi @stevengharris, are you looking into the possibility of RTL language support in the future? I tested the editor with arabic language and while it does the basic BLU formatting, the bullet list and others are generally LTR supported only. The text carat is also on the right instead of the left for RTL language, but the characters are appended on the left.

Simulator.Screen.Recording.-.iPad.mini.6th.generation.-.2022-05-17.at.16.19.58.mp4

Focus / making the editor first responder

I'm using this in a SwiftUI project and the only thing I haven't been able to figure out is how to programmatically set focus into the editor.

I have found StackOverflow posts that suggest WKWebView intentionally tries to prevent automatically setting focus without user interaction.

Is there a way in MarkupEditor that I can have it assume focus? My scenario is that I present the editor in a sheet and want the user to be able to start typing in the editor without having to tap into the editor first.

HiddenInput div appears when hitting backspace on empty div

Hi @stevengharris,

I have noticed that when the content is empty, hitting backspace will cause a div counter to be visible at the bottom of the main editable div. I went through the logic in markup.js file to get a gist of it, its related to the undo and redo function, and there is a logic that modifies the visibility style of the div. I am not sure what is the purpose of it being visible.

I have attached a screen recording of this occurring.
https://user-images.githubusercontent.com/4402214/167055277-ac743213-878e-4e9a-be7a-8282dc5e37da.mp4

Insert link/image loses selection

When you insert a link, the text selection is not backed up before the modal input for the link information. The same is true of inserting images. The result is that the url is placed at the previous selection point.

MarkupEditorView problem

Hello,

I have a problem with run Yours MarkupEditor

  1. I added Package from GitHub link end add to my project
  2. add yours example code
import SwiftUI
import MarkupEditor

struct ContentView: View {
    @State private var demoHtml: String = "<h1>Hello World</h1>"
       
       var body: some View {
           MarkupEditorView(html: $demoHtml)
       }
}

and I get error

Cannot find 'MarkupEditorView' in scope

when I open I Package Dependencies tree with MarkupEditor 0.3.3 I does not see MarkupEditorView file. This is correct?

Zrzut ekranu 2022-08-31 o 13 44 10

Zrzut ekranu 2022-08-31 o 13 46 46

Thanks for help,
Piotr

Various operations inside of indents are not working properly

  1. Enter at end of block with a formatted element doesn't work properly. For example, starting with:
<blockquote>
    <p><b>​Hello</b></p>
</blockquote>

and hitting Enter after "Hello" results in:

<blockquote>
    <p><b>Hello</b></p>
</blockquote>

<blockquote>
    <p></p><b><br></b>
</blockquote>

The new paragraph should be:

<blockquote>
    <p><b><br></b></p>
</blockquote>
  1. The multi-style operation does not work properly when the paragraphs are indented. Only the first element is restyled.

Keyboard Issue

EditorToolbar is not going to top of Keyboard inside scrollview? any solutions?

Input is flagged as changing during prepImage even if HTML did not change

The prepImage function in markup.js ensures that images are resizable, selectable, and specify their width and height. In prepImage, we _callback('input') when done. Ultimately this causes the HTML to be fetched on the Swift side even in the case when prepImage made no changes to the HTML. This seems like a minor change, but if you are relying on callbacks to 'input' to indicate changes in the document, these are both unnecessary and may have side-effects for you.

Auto style text with markdown

I'm trying to implement auto formatting markdown syntax similar to Typora. i.e convert **Bold Text** to <b>Bold Text</b> on the fly without needing the tool bar. I managed to get the replacement to work by adding a regex function to markup.js function const _doEnter which triggers when returning to a new line. But using the demo project I can't get my changes to render:

I'm sure this isn't the best way to implement this so I'd love some suggestions on how to implement this

function parseMarkdown(markdownText) {
    const htmlText = markdownText
    .replace(/^### (.*$)/gim, '<h3>$1</h3>')
    .replace(/^## (.*$)/gim, '<h2>$1</h2>')
    .replace(/^# (.*$)/gim, '<h1>$1</h1>')
    .replace(/^\> (.*$)/gim, '<blockquote>$1</blockquote>')
    .replace(/\*\*(.*)\*\*/gim, '<b>$1</b>')
    .replace(/\*(.*)\*/gim, '<i>$1</i>')
    .replace(/!\[(.*?)\]\((.*?)\)/gim, "<img alt='$1' src='$2' />")
    .replace(/\[(.*?)\]\((.*?)\)/gim, "<a href='$2'>$1</a>")
    .replace(/\n$/gim, '<br />')
    
    return htmlText.trim()
}

/**
 * Handle the Enter key to avoid <div> being inserted instead of <p>
 *
 * @returns {HTML Paragraph Element || null}    The newly created P to preventDefault handling; else, null.
 */
const _doEnter = function(undoable=true) {
    let sel = document.getSelection();
     ...
    const parent = _findFirstParentElementInNodeNames(selNode, _paragraphStyleTags);
    
    _consoleLog(selNode.textContent)
    selNode.textContent = parseMarkdown(selNode.textContent) // *** Parse markdown from line selection 
    _consoleLog(selNode.textContent)
    
    if (selectionAtEnd && nextSiblingIsEmpty && parent) {
       ...
    };
    return null;    // Let the MarkupWKWebView do its normal thing
};

Local images are not cached at the resourcesUrl relativePath

When specifying a resourcesUrl for local images, new local images should be created within the caches directory at the relativePath for resourcesUrl. For example, if you paste an image in, and resourcesUrl is 'resources' relative to the base URL, then the image src should be 'resources/.'. Currently it is just '.'.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.