I use the Synchronous Clipboard API to implement copy/paste in my Elm note-taking app. As the clipboardData only is available during the event, I rely on Native code to do side effects:
Html.div
[ Html.Events.onWithOptions "copy"
preventDefault
(Decode.at [ "clipboardData" ] Decode.value
|> Decode.map
(\clipboard ->
let
_ =
Clipboard.setData "text/plain" selectionToText model clipboard
_ =
Clipboard.setData "text/x-rexpad-content" selectionContent model clipboard
in
NoAction
)
)
, ...
]
[ ... ]
Where Clipboard.setData has the type setData : String -> (a -> String) -> a -> Decode.Value -> Decode.Value with this JavaScript implementation:
function setData(mimeType, transformer, model, clipboardData) {
var data = transformer(model);
if (data !== "") {
clipboardData.setData(mimeType, data);
}
return clipboardData;
}
How could I do this without Native code on 0.18? Will 0.19 bring something that makes it possible?
What prevents you from using a port for this? You have the clipboardDataValue at hand, why not just put that through an outgoing port and do the sideeffects there?
The clipboardData value can not be used outside of the clipboard event. This is for privacy reasons I believe, preventing web pages to spy on the clipboard without the user knowing about it. During clipboard events, it is clear that the user intends to give access. Most things in Elm are asynchronous, but decoding happens synchronously.
Ah, I didn’t know about that privacy feature. Hmm, I only heard that user interaction handlers will be synchronous in 0.19, with an opt-in for async, so maybe the message will get forwarded to JavaScript via a port completely synchronous? I’m not sure, though. Interesting topic, I’m curious what the answer to this is.
We do this through delegated listeners, so we listen for click events on the body (in vanilla JS) that happen to have a data-copy-clipboard attribute that points to the stuff we want to copy, something like
Yes, this is a good click-to-copy-to-clipboard solution! I guess I could do something similar. I’m not sure about the performance though as I would have to prepare the to-be-copied text for several mime times in real time while drag-selecting the text.
In 0.19 the scheduling of ports will change so that you can run functions that need to be in the same call stack as a user event handler. See: Window.confirm without native code?
Thanks! I should look more into custom elements. In my case, I want to react on the clipboard events (onpaste and like) directly on the contenteditable (with content rendered in Elm).
Right on! The one really important thing is that if you have a custom element with children that are rendered by Elm, you just can’t modify those children in a way that makes them different from what the virtual DOM thinks is true. As long as that rule is followed you can attach event listeners to whatever elements you want and do whatever you need to do.
Hi @luke I want to ask do you need any special webpack to make this technique (copyedit elm + js) to work? I’m using create-elm-app and I couldn’t get the js file to load. Thanks.
update
I’ve done more research on this and add webcomponents polyfill and fixed this.
Leave it here for any other people who came across this later.