mvcoconut / coconut.vdom Goto Github PK
View Code? Open in Web Editor NEWCoconut rendering through virtual-dom.
License: The Unlicense
Coconut rendering through virtual-dom.
License: The Unlicense
For example, suppose we have an existing vnode <div class="foo"/>
and we are transiting to:
<div/>
or <div class=${null}/>
The actual DOM will have the class=foo
remained.
Current workaround is to specify <div class=""/>
explicitly.
A simple benchmark on Chrome seems to indicate that setting onclick
is faster than addEventListener('click')
: https://try.haxe.org/#c3d21
Should properly verify across browser and make the diffing work accordingly.
It should not be mandatory, but when present, it should be integrated. Refs should be wrapped in xdom.Wrapped
accordingly.
Currently they are lumped together, requiring a runtime check that could be avoided.
Otherwise it would collide with this: https://github.com/back2dos/js-virtual-dom/blob/5a98acd6452829543faec6a7cb863a9c908307ca/src/vdom/Widget.hx#L9
When having a lot of event handlers, it does show up in the profiler. For bubbling events it might be advisable to use event delegation. Or it may just create a lot of bugs. Let's find out \o/
Should be possible via a custom NodeType
.
class Main extends View {
@:state var mystate:Int = 1;
function render() '
<div>
<p>State: ${mystate}</p>
<Menu onClick=${getClick(mystate)}/>
<button onclick=${toggle}>Toggle</button>
</div>
';
function toggle() {
mystate++;
}
inline function getClick(state:Int):Void->Void {
trace('compute ${state}');
return state == 1 ? function() trace('clicked') : null;
}
static function main() {
mount(document.getElementById('app'), hxx('<Main/>'));
}
}
class Menu extends View {
@:attr var onClick:Void->Void = null;
function render() '
<div>
<Button onClick=${onClick}/>
</div>
';
}
class Button extends View {
@:attr var onClick:Void->Void = null;
function render() '
<button onclick=${onClick}>Button</button>
';
}
Expected behavior
Actual behavior:
At step 4: getClick
is never called with state = 2 and clicking "Button" will still trace "clicked"
Note:
I found that the <div>
in Menu
is critical. Removing it will make it behave as expected.
vdom version 35a8d8d
dependencies should be at latest git head as at time of writing
It's really fishy how to deal with keys now to prevent recreation of view elements.
There should be some automatic key assignment and also maybe some explanation when manual keys are need.
Likely affects all SVG elements.
I can mock up a full repro if needed, but the basic steps are easy enough. Just make a view with <path class="test" />
. It will compile as expected, but at runtime you'll get this:
Uncaught TypeError: Cannot assign to read only property 'className' of object '#<SVGPathElement>'
Per the spec, SVGElement
's className
is a read only SVGAnimatedString
instance, and also deprecated. Spec suggests using classList
instead, but setAttribute('class', ... )
would probably work if you're looking to avoid classList
for other reasons.
When I compile a pretty basic vdom app with haxe 4.2.5, it compiles fine. If I update to 4.3.0 or newer, I get:
/src/coconut/vdom/RenderResult.hx:14: characters 12-37 : Void should be coconut.vdom.RenderResult
I can't figure out why. When I trace through it looks like all the code paths should be returning non-void but I might be confused by the macros...
When I add a datetime
attribute to a <time>
element, this attribute never appears in the final HTML.
View template:
<time datetime=${Date.now()}>Now!</time>
Rendered HTML:
<time>Now!</time>
When I query the element in JavaScript, its dateTime
property is empty:
document.querySelector("time").dateTime.length; // => 0
Using coconut.ui
0.12.0 with coconut.vdom
0.10.0.
final myStyle = {
"--color": "red",
color: "var(--color)",
};
return <span style={myStyle}>hello</span>;
In runtime:
SyntaxError: missing name after . operator
Libs version
coconut.ui: [0.12.0]
coconut.vdom: [0.10.1]
When updating styles at 60 FPS, setStyle stands out in the profiler. Perhaps using something other than reflection would help.
<for ... >
<if ${condition} >
<tr onclick=${ e->handler(e) }>
...
</tr>
<else>
<tr> // onclick handler still in place after re-rendering with condition flipped
...
</tr>
</if>
</for>
Hi,
I'm trying to build the Mark Knoll's simple example ( https://gist.github.com/markknol/0de2d8d05e8f3d725946eb5515cc771b )
but compilation fails with this errors:
/Users/dinko/haxe/haxelib/coconut,vdom/git/src/coconut/vdom/macros/Setup.hx:18: characters 23-32 : Null<tink.domspec.TagInfo> has no field domCt
/Users/dinko/haxe/haxelib/coconut,vdom/git/src/coconut/vdom/macros/Setup.hx:34: characters 19-28 : Null<tink.domspec.TagInfo> has no field domCt
I have coconut.ui, coconut.data, coconunt.vdom and tink_hxx all
installed from git so they should be up to date.
I'm not using lix for library management but doing it simple via haxelib and haxelib git install command.
Here's the example project which fails to build.
coco_test.zip
Cheers,
Dinko
When I add a role
attribute to an element, this attribute never appears in the final HTML.
View template:
<a href="#" role="button">Hello folks!</a>
<div class="alert" role="alert">Caution!</div>
Rendered HTML:
<a href="#">Hello folks!</a>
<div class="alert">Caution!</div>
Using coconut.ui
0.11.2 with coconut.vdom
0.8.1.
package;
import tink.state.*;
import coconut.Ui.*;
import js.Browser.*;
class Main {
static var key = {};
static function main()
hxx('<Editor key=${key} onChange=${onChange}/>');
static function onChange(v:String)
trace(v);
}
typedef Data = {
@:optional var onChange(default, never):String->Void;
}
class Editor extends vdom.Foreign {
public function new(data:Observable<Data>) {
super(document.createDivElement());
}
}
src/Main.hx:9: characters 15-21 : <Editor/> must have exactly one spread and no other attributes
(requires haxetink/tink_domspec#8)
import js.Browser.*;
import coconut.Ui.*;
using coconut.ui.Renderer;
class Main extends coconut.ui.View {
static function main() {
document.getElementById('app').mount(<Main/>);
}
function render() '
<div>
<svg width="100%" height="100%">
<polygon points="100,10 40,198 190,78 10,78 160,198" style=${{fill:'lime',stroke:'purple',strokeWidth:'5',fillRule:'evenodd'}} />
<circle cx="50" cy="50" r="40" stroke="black" strokeWidth="10" fill="red" />
</svg>
</div>
'
;
}
This code works in coconut.react-dom but not coconut.vdom. Perhaps the svg attributes need some special handling?
(in vdom, the elements are present but some of the attributes are missing)
class Main extends coconut.ui.View {
static function main() {
coconut.Ui.hxx('<Main/>').renderInto(js.Browser.document.body);
}
function render() '<div><Foo/></div>';
}
class Foo extends coconut.ui.View {
@:state var key:String = '1';
function render() '<div key=${key}/>';
override function viewDidMount() key = '2';
}
See error in console:
TypeError: Cannot read property 'insertBefore' of null
With compiler server it gives the following error every other build:
--macro:1: character 0 : Type name vdom.macros.Loader is redefined from module vdom.macros.Loader
To reproduce:
Create a view component and cause re-renders in any way where the component get destroyed and recreated.
The beforeInit and afterInit lifecycle events doesn't called only the first time.
For example:
beforeInit 1
render 1
afterInit 1
destroy 1
render 2
destroy 2
render 3
destroy 3
render 4
numbers representing the instance unique id for that is recreate
They should be called properly on every new creation of the object.
Hi,
The raw component seems to always be one value behind. At initialization its value is correct but then the first time the value changes the raw component does not change its rendered value.
Afterwards the displayed value is always one step behind.
See the following example
import coconut.vdom.View;
import js.Browser;
import coconut.ui.Renderer;
class Main {
public static function main() {
Renderer.mount(Browser.document.getElementById('app'), "<App />");
}
}
class App extends View {
@:state var count=0;
function handleClick() {
count = count+1;
}
function render() {
return hxx('
<div>
<raw content=${'<b>${count}</b>'}/>
<button onclick=${handleClick}>Click</button>
</div>
');
}
}
At init you will correctly see "0" displayed. The first time you click on the button no change will be seen. The second time you will see "1".
possible to know when a view is unmounted?
import tink.pure.List;
using tink.CoreApi;
class Main extends coconut.ui.View {
static function main() {
js.Browser.document.body.appendChild(new Main({}).toElement());
}
@:state var contents:List<Content> = [
Text('foo'),
Expr(Multi([Text('bar')])),
];
function render() '
<div>
${renderContents(contents)}
</div>
';
function renderContents(contents:List<Content>) {
return @hxx '
<div>
<for ${content in contents}>
<switch ${content}>
<case ${Text(v)}>
<TextWidget value=${v}/>
<case ${Expr(expr)}>
${renderExpr(expr)}
</switch>
</for>
</div>
';
}
function renderExpr(expr:Expr) {
return @hxx '
<div>
<switch ${expr}>
<case ${Multi(v)}>
<let values=${Some(v)}>
<switch ${values}>
<case ${Some(c)}>
${renderContents(c)}
<case ${None}>
${renderContents([])}
</switch>
</let>
</switch>
</div>
';
}
override function afterInit(e) {
haxe.Timer.delay(function() contents = [
Text('foo2'),
Text('bar2'),
], 1000);
}
}
enum Content {
Text(v:String);
Expr(e:Expr);
}
@:pure
enum Expr {
Multi(v:List<Content>);
}
class TextWidget extends coconut.ui.View {
@:attr var value:String;
function render() '
<div>
${value}
</div>
';
}
expected: show foo bar
at startup, then show foo2 bar2
after 1s
actual after 1s shows only foo2
Hey,
In DIY branch lifecycle events are not working when the element is rendered with condition:
function render()
{
return
model.status == "hello"
? @hxx '<HelloSubView />'
: @hxx '<button onclick=${model.test()}>setHello</button>';
}
The HelloSubView will have no lifecycle events, even if they got rendered.
Here is a minimalistic example of the issue:
https://github.com/grosmar/coconut-playground/blob/lifecycle-bug/src/HelloView.hx
I know DIY is discontinued but I would prefer to not go back to the master as GEN4 is coming, but that still seems not really finished...
I am doodling with coconut vdom, but noticed a weird bug. Here is the full source. I have not taken time to track it down to smaller reproducible example.
Whats wrong:
In this example, If I remove <div class="leave-me-out">
(but keep its children) in this example, and click the tink_core
button, the rendering is totally messed up.
<if ${data.current == "tink_core"}>
<if>
, but that's totally wrong because those visible
attribute conditions are not even met.I thought it was related to the <if>
in relation to the wrapped div (because adding a wrapping div makes everything work) but it goes deeper; when I change <HtmlView content="${pageContent}"/>
to <HtmlView content="${url}"/>
, everything works. This maybe suggest something is wrong with @:loaded var pageContent
(PageData) ??
There are probably better ways to set this thing up, but afaik I'm not doing super fancy stuff and thus should work with and without that wrapping <div class="leave-me-out">
.
Here's the full code: https://github.com/markknol/coconut-readme-reader/blob/master/src/Main.hx
(Repo: https://github.com/markknol/coconut-readme-reader)
I am using
-lib markdown
and -lib html-haxe-code-highlighter
(latest from haxelib)# @install: lix --silent download "gh://github.com/MVCoconut/coconut.vdom#99a5f04b9d6432ed0cfaaa513aeddec9bde7ce88" into coconut.vdom/0.6.2/github/99a5f04b9d6432ed0cfaaa513aeddec9bde7ce88
When you don't provide a class
attribute to a <raw />
component, the attribute is still rendered with an "undefined"
value.
Source:
<raw content="Hello World!"/>
Rendered:
<span class="undefined">Hello World!</span>
Not a big issue... unless you already have a .undefined
class defined by your stylesheet.
Using coconut.vdom
0.10.1.
Trying to implement something similar to react portals using Renderer.mount()
and viewDidMount()
I found some unexpected(?) behavior: function view does not update when parent state changes
import coconut.vdom.Renderer;
import coconut.vdom.View;
class TestBugView extends View {
@:state var count:Int = 0;
function viewDidMount() {
Renderer.mount(js.Browser.document.getElementById('fragment'), <>Fragment: {count}</>);
Renderer.mount(js.Browser.document.getElementById('element'), <span>Element: {count}</span>);
Renderer.mount(js.Browser.document.getElementById('function'), <FunctionView count={count} />);
Renderer.mount(js.Browser.document.getElementById('class'), <ClassView count={count}/>);
}
function render()
return <button onclick={count += 1}>click me</button>;
function FunctionView(attr:{ count:Int })
return <span>Function: {count}</span>;
}
class ClassView extends View {
@:attr var count:Int;
function render()
return <span>Class: {count}</span>;
}
hxml
-cp src
-main TestBugMain
--library coconut.ui
--library coconut.vdom
-dce full
-D analyzer-optimize
-D js-es=6
-js bug\bundle.js
Main
class TestBugMain {
static public function main() {
Renderer.mount(js.Browser.document.body, <TestBugView />);
}
}
html
<div id="fragment"></div>
<div id="element"></div>
<div id="function"></div>
<div id="class"></div>
<script src="bundle.js"></script>
Clicking the button updates only class view, while I expect updating function component as well.
I am not very sure about this. But what is happening to me is like this:
function render() '<div class="ui dropdown">...</div>';
function afterInit(e) new JQuery(e).dropdown();
I am using semantic ui and this code doesn't work. When logging the e
, it is actually the div
. But the dropdown initialization doesn't work. However if I wrap the jquery call inside Callback.defer
it works.
I reckon is problem happens if the view is newly rendered by its parent. So that inside the child view's afterInit
, e
is created but not yet mounted to the DOM tree by the parent view, causing this phenomenon.
According to the standard there should be a font tag:
https://www.w3schools.com/tags/tag_font.asp
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.