witheve / Eve

Better tools for thought

Home Page:http://witheve.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Embed into dom elsewhere other than document.body

btheado opened this issue · comments

Both the HTMLWatcher and SVGWatcher have hard-coded 'document.body.appendChild'. Can there be a mechanism to choose some other location in the dom? That way Eve code can be embedded inside another application.

Or is the application expected to extend those classes and override the createRoot method or order to get a different toplevel location?

Root elements (that is, elements with no parents) are automatically parented to the document body. Any other element is inserted into it's parent, but currently the only way to create a valid parent element is within Eve.

The plan is to expose a method on the html watcher itself that lets you register your own root elements to serve as parents. That would look something like this:

import {Program} from "witheve";

let program = new Program("my program");
let htmlWatcher = program.attach("html");

let eveWrapper = document.querySelector("#eve-wrapper");
htmlWatcher.addExternalRoot("my-root", eveWrapper);

You could then render into the #eve-wrapper element like so:

program.bind("render into #eve-wrapper", ({find, record}) => {
  let my_root = find("my-root");
  return [
    my_root.add("children", [
      record("html/element", {tagname: "div", text: "hello, world!"})
    ])
  ];
})

This handles most cases, but not the situation where you have several similar roots that are dynamic (e.g., comment boxes). To resolve this, we could get a little more complicated and let you specify an entire object to identify the root with. I haven't yet decided if that's worthwhile.

Does the above handle all of your use cases? Alternatively, is there some alternative you'd prefer?

Yes, that looks like exactly what I was asking for. Nice!

I was just thinking through what it would take to be able to extend TiddlyWiki using Eve. The TiddlyWiki markup (markdown-like) allows external javascript functionality to be embedded via html tag-like "widgets". The render code for each widget instance receives a dom node as input (see https://github.com/Jermolene/TiddlyWiki5/blob/master/core/modules/widgets/count.js#L30 for a simple example). That domnode would have to be used in order for a "widget" to be implemented with Eve.

It should work to create a new program from within the render method and call your proposed addExternalRoot with the domnode each time. A watcher and inputEAV can be used to shuttle data back and forth between Eve and the TiddlyWiki storage system (i.e. tiddlers).

Just an idea I had and I'm not sure I will follow through with it. Sounds like you already have a good plan for this feature.

I was also having the same problem today. I wanted to embed one or two Eve-controlled programs on my page. No dynamic roots needed so far. Your .addExternalRoot suggestions looks great for my use case.

Sounds like there's some demand, and it's not much work for the static case. I'll get this out in the next preview release, due out soon.

Closed via: #823

Please try it out and let me know how it works for you. Documentation is currently nonexistent, but it works exactly like the example above. If you're using typescript, you can just hint that the htmlWatcher variable is an any (or feel free to properly import the HTMLWatcher from witheve to fully type it). If you have any other issues or comments, I'm always around on the mailing list. :)

Thanks! Does it make sense to make the same functionality available for the svg watcher? Without thinking, I started to try embedding the clock demo before realizing it was based on the svg watcher, which doesn't benefit from your change. When I get back around to this, I will try one of the html based demo programs instead.

@btheado: That certainly makes sense to me. Canvas should already be supported at least. The separation between html and svg is pretty arbitrary, but is forced by a poor implementation decision earlier on. It also currently prevents embedding svg even into regular eve html. I'll be fixing that in the next couple of weeks by updating the SVG watcher to behave more like the Canvas watcher, where SVG elements act as embedded html elements, and then everything should Just Work ™️.

@joshuafcole: I couldn't get your example program to work until I moved the addExternalRoot call to after all my bind calls. Any bind call after addExternalRoot which queries for the #my-root doesn't find a match. Is that the way you expect it to work?

Here is the code I finally got working

import {Program} from "witheve";

let program = new Program("my program");
let htmlWatcher = program.attach("html") as any;
//program.attach("tag browser");

program.bind("Unconditional test div", ({record}) => {
  return [
    record("html/element", "unconditional", {tagname: "div", text: "Unconditional test div"})
  ];
});

program.bind("display text when #my-root found", ({find, record}) => {
  find("my-root");
  return [
    record("html/element", {tagname: "div", text: "#my-root found"})
  ];
}); 

program.bind("add text to child of #my-root", ({find, record}) => {
  let my_root = find("my-root");
  return [
    my_root.add("children", [
      record("html/element", "my-tag", {tagname: "div", text: "Child of #my-root here"})
    ])
  ];
});

htmlWatcher.addExternalRoot("my-root", document.querySelector("body"));

program.inputEAVs([
  [1, "tag", "dummy"]
]);     

Is the SVGWatcher still on the TODO list for this? I haven't been able to get SVG embedding to work.