aboutsummaryrefslogtreecommitdiffstats
path: root/app/component.client.moon
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2018-09-05 00:36:00 +0000
committers-ol <s-ol@users.noreply.github.com>2018-09-05 00:36:00 +0000
commitece199a92220deaa3212fad30f6026693ae86c63 (patch)
treef576a8c160c8827050ef34f474961d89bdff1ac4 /app/component.client.moon
parentsome fixes (diff)
downloadmmm-ece199a92220deaa3212fad30f6026693ae86c63.tar.gz
mmm-ece199a92220deaa3212fad30f6026693ae86c63.zip
polymorphic!
Diffstat (limited to 'app/component.client.moon')
-rw-r--r--app/component.client.moon128
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