raynos / mercury Goto Github PK
View Code? Open in Web Editor NEWA truly modular frontend framework
Home Page: http://raynos.github.io/mercury/
License: MIT License
A truly modular frontend framework
Home Page: http://raynos.github.io/mercury/
License: MIT License
The easiest example is probably a popover with a single local state opened
. (and arbitrary other state that is passed in)
Its not really clear to me yet how to do this. Right now its all a big piece of spaghetti with everything in the global observ
. It should be a lot easier that that. There needs to be an example or documentation on how to achieve this.
I noticed mercury.app appends html to the target element, while react completely replaces everything in the target element. If I pre-render the html server-side, react knows what to replace because every element has a 'react-id'. Trying to replicate that functionality I changed mercury.app so that it erases the target element before rendering.
function app(elem, observ, render, opts) {
mercury.Delegator(opts);
var loop = mercury.main(observ(), render, opts);
elem.innerHTML = '';
elem.appendChild(loop.target);
return observ(loop.update);
}
I'm not sure if this is the best way? With React, I noticed that sending pre-rendered html made the sites load instantaneously, instead waiting up to a few seconds for the React library to load and render the page. I load the libraries async - the user doesn't even realize what's happening. I'm testing with mercury by async loading via scriptjs after x seconds, and it looks good thus far. I'm not sure how it'll look with the bigger examples. I was just wondering if something like this was planned in the design? It's modular enough to implement this pretty easily. I'm not sure how to render the h server-side properly thou, I'm just creating html and replacing it at the moment.
If anyone is building an app with mercury, whether open source or private i would love to hear about it.
I want to start gathering a list of websites / projects build with it
Feel free to reply even if it's not done yet :)
Open source mercury apps:
cc @nrw
watch is just a few lines of code - is it too much to add to mercury? i think it's useful for dynamically wiring state into slots of the global atom - e.g. globalState.currentPage
a contrived example:
// lazily generate a page based on the route and some data
var newPageState = pages[key](data)
if (removeCurrentPage) removeCurrentPage();
removeCurrentPage = watch(newPageState, globalState.currentPage.set);
this is a result of trying out #70
i may have an issue with Raynos/main-loop@33c28cf - in theory it's excellent, but in practice there could be some things which potentially cannot avoid mutating state during an update... i know - it's not good:exclamation:
the first thing i've found that throws this error is for an open popup component that has a blur handler that is being invoked during a patch - patching nodes can cause a blur if the current active element is removed from the document and so my handler responds during a patch. in the handler, i close the popup, which involves updating state, and hence the error is thrown. i don't know that i can use a setTimeout
to defer updating the state because this will break logic that depends on the sequencing of blur and corresponding focus events - this stuff is ugly but unavoidable when trying to respond to blur and focus events.
i think in this case i'll be able to ignore the blur event by checking if the node is in the document but that doesn't fix the issue where you might be doing something like preventing focus from moving when validating an input when it's blurred.
your suggestion in irc was possibly to allow mutation that has originated from dom-delegator
when in a web browser the elem
is still not loaded this line occults that error and don't give any type of feedback
this can be painful for newbies
The observ-array package is at release v2.0.0, and notably includes transactions, which are very helpful for batching together diffs on mercury arrays. Could this project's package.json file be updated to that version number?
In the TodoMVC example, after adding an item and double-clicking on one to edit it, the input does not seem to respond to keyboard inputs.
please find a way to build dist before tagging. it's not a huge deal but if dist is included in the repo then it should be updated before each tag.
anything stored in a vtree that survives for two frames of a raf()
should use object pools.
The things that can use object pools transparently are
h()
including any hooks created inside h()
vdom-thunk
main-loop
will then have a hook right here to free(tree)
before overwriting it.
This means majority of objects created in Render()
that exist for two frames will not cause GC churn.
This needs benchmarking to see whether it helps.
cc @Matt-Esch
I took a stab at using time-travel, but it doesn't seem to be exported with the mercury object. Most likely I am missing something obvious, but I would very much like to see how you expect it to be used.
Thank you
Was trying canvas example and found this:
return h('div.counter', [
'The state ', h('code', 'clickCount'),
' has value: ' + clickCount + '.', h('input.button', {
type: 'button',
value: 'Click me!',
'ev-click': mercury.event(clicks)
})
]);
Which throws an error like:
//cc @Raynos
Pretty often I need to do an event.preventDefault()
on the actual DOM event. Initially I had a simple way of dealing with it in mercury by passing in an array of events like:
h('a', {
href: '#',
'ev-click': [
function(e){ e.preventDefault() },
mercury.event(myevent, { foo: 'bar' })
]
})
Eventually this got out of hand and I ended up drying up the code by overwriting value-event/event so that a preventDefault
key on the data argument was treated appropriately.
h('a', {
href: '#',
'ev-click': mercury.event(myevent, { preventDefault: true })
})
Usually preventing default is what I want when attaching events in general so I ended up setting it's default value to true
. There is a reference implementation here.
h('a', {
href: '#',
'ev-click': mercury.event(myevent, { preventDefault: true })
})
I think adding this might be useful to others, I wanted to see if anyone else had ideas or concerns about it before making any PRs.
https://github.com/jonase/elements
Create a new branch + example for this
I tried to install value-event
separately and require base-event.js
from it, but then I ran into problems when HANDLER_STORE(handle)
in dom-delegator didn't have a func
property.
@Raynos in Matt-Esch/virtual-dom#90 (comment) you mentioned that testing mercury is a manual process currently. this MUST become automated.
i may be able to contribute towards this effort but in order to get contributions to help move towards the goal of CI, what is your plan for automated testing? what tools and technology do you plan on using to have tests running in browsers? even if there are currently no tests, put the mechanism in place to run tests in supported browsers so that all that's left is for tests to be written.
NOTE: anything short of running tests in browsers is not sufficient - i.e. phantomjs, min-document, jsdom, etc are not enough.
So many ideas :)
(from irc, just so this is tracked as a ticket)
I have some object embedded deep in the state tree.
I want to mutate it in an event handler.
The render() function that binds the events works with a plain copy of the state.
While inside the event handler, I have the state as an observ
.
I can’t just pass in the plain object to the handler, instead I have to pass in the path
inside the observ, and walk that path in the event handler.
This is annoyingly verbose:
render():
h('.foo', {'data-click': event(events.handler, {i: i, j: j, k: k})})
function handler(state, {i, j, k}) {
state.arr.get(i).get(j).get(k).prop.set('foo');
}
There got to be some more convenient way :-)
I cannot get any event handler of the type ev-event
or ev-click
to work. As a sanity check, I tried editing the basic counter example.
http://requirebin.com/?gist=b118243c4957fd421022
The only change relative to the original example is "ev-click": function(e){ alert("should show"); }
. The alert is not called if I press the button. Neither ev-event
works. This is neither a browser issue, I tested in Firefox 30 and Chrome 36 in OS X.
We should document how to write & run tests with tape in many environments
See the virtual-dom
tests as an example.
The mercury tests are not a good example for running them because they use on the fly AST transforms, if I can get those to work in browserify they might be a good example.
I've been answering adhoc questions on irc about mercury.
I want to collect these questions and answers into an faq document.
If anyone has any other questions they want to pre-emptively ask so i can add them to the FAQ then please go ahead
I'm trying to write some tests of a mercury app using jsdom, and I'm trying to understand a bit more about how mercury works so I can get the test setup working.
Inspecting index.js#app when my app is running, it appears that loop.target
is supposed to be a real DOM object. However, in my test setup, loop.target
seems to be a virtualdom object instead of a "real" (in this case, jsdom) DOM object. Appending a virtualdom object into a jsdom object has no effect, and as a result my component is not rendered into the test DOM. Any suggestions on why this might be happening?
Here's a quick extract of my test:
var jsdom = require('jsdom').jsdom;
var mercury = require('mercury');
describe('...', function() {
beforeEach(function(done) {
var doc = jsdom('<html><body></body></html>');
global.window = doc.parentWindow;
global.document = window.document;
global.navigator = window.navigator;
global.$ = undefined;
jsdom.jQueryify(doc.parentWindow, "../../../bower_components/jquery/dist/jquery.js", function () {
done();
});
});
it('...', function() {
var component = MySubject;
var div = document.createElement('div');
document.body.appendChild(div);
mercury.app(div, new component().state, component.render);
global.$ = function(selector) {
return window.$(div).find(selector);
}
// Here, I would expect my rendered component to have been appended to `div`, but `div` is still empty
console.log(div.innerHTML);
expect($('input').val()).to.equal('');
});
});
Help keep the package size down and distribute only what is needed. Would ignore concepts
, docs
, examples
and test
. Not sure what you're using dist
for (non-npm?).
geval
documentationvdom-thunk
virtual-hyperscript
virtual-dom
split into vdom
and vtree
(@Matt-Esch)modular-react
Keyboard
interfaceCurrently mercury allows transient state.
i.e. h('input')
where you don't specify the value
for the input and allow that state to live in the DOM as a mutable value.
This may cause subtle bugs like a virtual-dom diff / patch cycle not resetting the value to the empty string because it was left transient.
This is either a "feature" or a "bug".
One solution is to have mercury throw an exception if the user "accidentally" uses transient state and to make transient state opt in with something like h('input', { value: h.TRANSIENT })
cc @avh4
To demonstrate how to write a re-usable component using mercury we should author a few.
One that is interesting to author is a tab view component.
Basically a list of tab headers and a list of tab bodies, this will show how to handle internal state & DOM events. This component will also need a public interface to show any tab at any time.
We may also want to experiment with adding structural styling to the component using something like rcss.
This is an issue for blockers on the upgrade
examples/bmi-counter.js
Any chance of a TypeScript binding?
Facebook released an Immutable
library.
We should add an example to unidirectional swapping out observ-*
for Immutable
.
don't geval and observ do roughly the same thing (a very minor difference)? is it redundant to include both? maybe observ is sufficient for everything?
var observ = require('observ');
module.exports = Event;
function Event() {
var value = observ();
return { broadcast: value.set.bind(value), listen: value };
}
i need an example for this and tests.
@neonstalwart any suggestions what a good example might be ?
It appears that the require bin example is broken. As in the count is not being incremented when clicking the button.
The example here however, works as expected.
This weekend I am evaluating how hard would be to port the current project I'm working on, from React to Mercury, since I kinda dislike how React moves along and changes are subjective to their needs.
It looks like that, so it will be quite complex, it's a CMS for a WISP, to share content on their network. I can't share the actual project, NDA related, but if I decide it's doable, I'll share the decisions I make and hopefully I'll have your support.
if you do h("input", { value: value })
there is a race condition.
Users types 'foo'. User then types 'b', a Render gets flushed that renders 'foo' to the vdom.
The free variable 'foob' on the input gets lost.
cc @Matt-Esch
On the android browser (not chrome) it's not rendering the todos, though it seems to update the number of items indicator.
I've taken a look at this. It seems if I force the browser to render in desktop mode the todos are visible, but otherwise they vanish. Perhaps there is something causing this to vanish in the CSS?
related to Raynos/virtual-hyperscript#8
I'm thinking I want to build a tool that shows you an optional graph in your app.
The graph would show in the top right and would show you an hour or so worth of stats (maybe a log scale so the right hand side is second granularity and the left hand side is minute granularity).
The graph would measure:
Render
diff
patch
patch
Update
per secondinput
per secondThe idea is to show this graph permanently and to persist an hour worth of history in IndexedDB.
The use case is to add things like mercury.partial
or key
to your render call and visually see the time spend in diff
, Render
and patch
being reduced.
The other use case is to optimize your Render
and see the number of DOM
operations being reduced.
The last use case is to optimize your events in Render
and your logic in Update
to see the time spend doing state updates being reduced.
In theory if you reduce ANY of these numbers your performance goes up.
It should also be noted that all these metrics are app agnostic as long as you use the Input, State, Update, Render
architecture.
cc @Matt-Esch
raf
so it exposes polyfill without event emitterbrowser-split
from virtual-hyperscript
This should reduce filesize of mercury from 100kb to 50kb
the toggle is actually a "mark all as complete" and works like this:
This checkbox toggles all the todos to the same state as itself.
We should document the core philosophy of mercury.
The following hold.
render(A); render(A); === render(A);
render(A); render(B); === render(B);
Following this discussion #45 (comment) which led to a solution with min-document
, here is a minimal repro example with jsdom
. The event does not get fired.
var jsdom = require('jsdom').jsdom;
var mercury = require('mercury');
var h = mercury.h;
var event = require('synthetic-dom-events');
var callCount = 0;
var click = mercury.input();
click(function() { callCount++; });
function render(onClick) {
return h('button', {'ev-click': mercury.event(onClick)}, 'Click Me');
};
describe('integration tests', function() {
var document, $;
beforeEach(function(done) {
var doc = jsdom('<html><body></body></html>');
var window = doc.parentWindow;
document = window.document;
jsdom.jQueryify(doc.parentWindow, "../node_modules/jquery/dist/jquery.js", function() {
$ = window.$;
done();
});
});
describe('clicking the button', function() {
beforeEach(function(done) {
var div = document.createElement('div');
mercury.app(div, mercury.value(click), render, {document: document});
document.body.appendChild(div);
$('button').click();
// Also tried $('button').trigger('click');
setTimeout(done);
});
it('increments the counter', function() {
expect(callCount).to.equal(1);
});
});
});
value
on inputs do correct thingIf I execute node bin/build.js
to rebuild the TodoMVC example, then navigate /mercury/examples/todomvc/
, and add an item, the item is added twice to the list. It should only be added once.
many of the APIs have an id
argument that is only there because dom-delegator needs the id
. for example, nothing within event-sinks uses the id
but the APIs require it even though everything in event-sinks would work without it. ideally, it would be good for these anti-framework APIs to make sense on their own - i.e. without dom-delegator.
the first preference would be to find a way to avoid id
completely (even in dom-delegator) because it feels clunky but the next best thing would be to isolate it just to dom-delegator if possible.
We could change the way we do events so that we use
ev-click
instead of data-click
.
This will mean that from a user point of view
h('div', {
ev-focus: fn,
data-focus: hook
})
Does not cause collisions. They can use both the elem.focus()
hook and the focus
event handler through dom-delegator
.
Other benefits include that we always know properties in ev-*
namespace are event handlers so we can invoke dom-delegator functions directly, this will allow dom-delegator
to manage lazy event handlers.
cc @Matt-Esch
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.