diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2018-09-05 00:36:00 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2018-09-05 00:36:00 +0000 |
| commit | ece199a92220deaa3212fad30f6026693ae86c63 (patch) | |
| tree | f576a8c160c8827050ef34f474961d89bdff1ac4 /app/component.client.moon | |
| parent | some fixes (diff) | |
| download | mmm-ece199a92220deaa3212fad30f6026693ae86c63.tar.gz mmm-ece199a92220deaa3212fad30f6026693ae86c63.zip | |
polymorphic!
Diffstat (limited to 'app/component.client.moon')
| -rw-r--r-- | app/component.client.moon | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/app/component.client.moon b/app/component.client.moon new file mode 100644 index 0000000..c012427 --- /dev/null +++ b/app/component.client.moon @@ -0,0 +1,128 @@ +{ :document } = js.global + +-- convert anything to a DOM Node +-- val must be one of: +-- * DOM Node (instanceof window.Node) +-- * MMMElement (have a .node value that is instanceof window.Node) +-- * string +-- note that strings won't survive identity comparisons after tohtml +tohtml = (val) -> + if 'string' == type val + return document\createTextNode val + if 'table' == type val + assert val.node, "Table doesn't have .node" + val = val.node + if 'userdata' == type val + assert (js.instanceof val, js.global.Node), "userdata is not a Node" + val + else + error "not a Node: #{val}, #{type val}" + +-- overloaded append +-- see tohtml for acceptable values +g_append = append +append = (value) -> g_append tohtml value + +-- shorthand to form a text node from strings +text = (...) -> document\createTextNode table.concat { ... }, ' ' + +class ReactiveVar + @isinstance: (val) -> 'table' == (type val) and val.subscribe + + new: (@value) => + @listeners = setmetatable {}, __mode: 'kv' + + set: (value) => + old = @value + @value = value + for k, callback in pairs @listeners + callback @value, old + + get: => @value + + transform: (transform) => @set transform @get! + + subscribe: (callback) => + with -> @listeners[callback] = nil + @listeners[callback] = callback + + map: (transform) => + with ReactiveVar transform @value + .upstream = @subscribe (...) -> \set transform ... + +class ReactiveElement + @isinstance: (val) -> 'table' == (type val) and val.node + + new: (element, ...) => + @node = document\createElement element + @_subscriptions = {} + + children = { ... } + -- attributes are last arguments but mustn't be a ReactiveVar + attributes = children[#children] + if 'table' == (type attributes) and + (not ReactiveElement.isinstance attributes) and + (not ReactiveVar.isinstance attributes) + table.remove children + else + attributes = {} + + for k,v in pairs attributes + @set k, v if 'string' == type k + + -- if there is only one argument, + -- children can be in attributes table too + if #children == 0 + children = attributes + + for child in *children + @append child + + destroy: => + for unsub in *@_subscriptions do unsub! + + set: (attr, value) => + if 'table' == (type value) and ReactiveVar.isinstance value + table.insert @_subscriptions, value\subscribe (...) -> @set attr, ... + value = value\get! + + if attr == 'style' and 'table' == type value + for k,v in pairs value + @node.style[k] = v + return + + @node[attr] = value + + append: (child, last) => + if ReactiveVar.isinstance child + table.insert @_subscriptions, child\subscribe (...) -> @append ... + child = child\get! + + if 'string' == type last + error 'cannot replace string node' + + child = tohtml child + ok, last = pcall tohtml, last + if ok + @node\replaceChild child, last + else + @node\appendChild child + + remove: (child) => + @node\removeChild tohtml child + if 'table' == (type child) and child.destroy + child\destroy! + +with exports = { + :ReactiveVar, + :ReactiveElement, + :tohtml, + :append, + :text, + } + add = (e) -> exports[e] = (...) -> ReactiveElement e, ... + + for e in *{'div', 'form', 'span', 'a', 'p', 'button', 'ul', 'ol', 'li', 'i', 'b', 'u', 'tt'} do add e + for e in *{'article', 'section', 'header', 'footer', 'content'} do add e + for e in *{'br', 'hr', 'img', 'input', 'p', 'textarea'} do add e + for i=1,8 do add "h" .. i |
