diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2018-10-18 14:16:10 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2018-10-18 14:16:10 +0000 |
| commit | 8844d29223626240ff99a0cdaccf1ee5a36cb3f6 (patch) | |
| tree | 1740fd0291959aa8d3d0b266158d7eaaa685224d /lib | |
| parent | update dependencies (diff) | |
| download | mmm-8844d29223626240ff99a0cdaccf1ee5a36cb3f6.tar.gz mmm-8844d29223626240ff99a0cdaccf1ee5a36cb3f6.zip | |
towards self-compiling
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/canvasapp.shared.moon | 34 | ||||
| -rw-r--r-- | lib/color.shared.moon | 20 | ||||
| -rw-r--r-- | lib/component.client.moon | 128 | ||||
| -rw-r--r-- | lib/component.server.moon | 115 | ||||
| -rw-r--r-- | lib/html.client.moon | 36 | ||||
| -rw-r--r-- | lib/html.server.moon | 37 |
6 files changed, 370 insertions, 0 deletions
diff --git a/lib/canvasapp.shared.moon b/lib/canvasapp.shared.moon new file mode 100644 index 0000000..f05bfec --- /dev/null +++ b/lib/canvasapp.shared.moon @@ -0,0 +1,34 @@ +window = js.global + +class CanvasApp + width: 500 + height: 400 + new: => + @canvas = window.document\createElement 'canvas' + @canvas.width, @canvas.height = @width, @height + @ctx = @canvas\getContext '2d' + @time = 0 + + @canvas.tabIndex = 0 + @canvas\addEventListener 'click', (_, e) -> @click and @click e.offsetX, e.offsetY, e.button + @canvas\addEventListener 'keydown', (_, e) -> @keydown and @keydown e.key, e.code + + lastMillis = window.performance\now! + t = @ + animationFrame = (_, millis) -> + if not @paused + @update (millis - lastMillis) / 1000 + @ctx\resetTransform! + @draw! + window\setTimeout (-> + window\requestAnimationFrame animationFrame + ), 0 + lastMillis = millis + window\requestAnimationFrame animationFrame + + update: (dt) => + @time += dt + +{ + :CanvasApp +} diff --git a/lib/color.shared.moon b/lib/color.shared.moon new file mode 100644 index 0000000..6233b47 --- /dev/null +++ b/lib/color.shared.moon @@ -0,0 +1,20 @@ +rgb = (r, g, b) -> + r, g, b = table.unpack r if 'table' == type r + "rgb(#{r * 255}, #{g * 255}, #{b * 255})" + +rgba = (r, g, b, a) -> + r, g, b, a = table.unpack r if 'table' == type r + "rgba(#{r * 255}, #{g * 255}, #{b * 255}, #{a or 1})" + +hsl = (h, s, l) -> + h, s, l = table.unpack h if 'table' == type h + "hsl(#{h * 360}, #{s * 100}%, #{l * 100}%)" + +hsla = (h, s, l, a) -> + h, s, l, a = table.unpack h if 'table' == type h + "hsla(#{h * 360}, #{s * 100}%, #{l * 100}%, #{a or 1})" + +{ + :rgb, :rgba, + :hsl, :hsla, +} diff --git a/lib/component.client.moon b/lib/component.client.moon new file mode 100644 index 0000000..c012427 --- /dev/null +++ b/lib/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 diff --git a/lib/component.server.moon b/lib/component.server.moon new file mode 100644 index 0000000..456ca71 --- /dev/null +++ b/lib/component.server.moon @@ -0,0 +1,115 @@ +-- convert anything to HTML string +-- val must be one of: +-- * MMMElement (have a .render method that returns a string) +-- * string +tohtml = (val) -> + if 'table' == type val + assert val.render, "Table doesn't have .render" + val = val\render! + if 'string' == type val + 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 = (...) -> table.concat { ... }, ' ' + +class ReactiveVar + @isinstance: (val) -> 'table' == (type val) and val.subscribe + + new: (@value) => + + set: (value) => + error "attempting to update ReactiveVar serverside" + + get: => @value + + subscribe: (callback) => + error "attempting to subscribe to ReactiveVar serverside" + + map: (transform) => + ReactiveVar transform @value + +class ReactiveElement + @isinstance: (val) -> 'table' == (type val) and val.render + + new: (element, ...) => + @element = element + @attrs = { style: {} } + @children = {} + + 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: => + + set: (attr, value) => + if attr == 'style' and 'table' == type value + for k,v in pairs value + @attrs.style[k] = v + return + + @attrs[attr] = value + + append: (child, last) => + assert not last, "last passed to append on server" + if ReactiveVar.isinstance child + child = child\get! + + child = tohtml child + table.insert @children, child + + remove: (child) => + error "remove called serverside" + + render: => + b = "<#{@element}" + for k,v in pairs @attrs + if 'table' == type v + tmp = '' + for kk, vv in pairs v + tmp ..= "#{kk}: #{vv}; " + v = tmp + b ..= " #{k}=\"#{v}\"" + b ..= ">" .. table.concat @children, '' + b ..= "</#{@element}>" + b + +with exports = { + :ReactiveVar, + :ReactiveElement, + :tohtml, + :flush, + :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 diff --git a/lib/html.client.moon b/lib/html.client.moon new file mode 100644 index 0000000..2538f8d --- /dev/null +++ b/lib/html.client.moon @@ -0,0 +1,36 @@ +document = js.global.document + +element = (element) -> (...) -> + children = { ... } + + -- attributes are last arguments but mustn't be a ReactiveVar + attributes = children[#children] + if 'table' == (type attributes) and not attributes.node + table.remove children + else + attributes = {} + + with e = document\createElement element + for k,v in pairs attributes + e[k] = v + + -- if there is only one argument, + -- children can be in attributes table too + if #children == 0 + children = attributes + + for child in *children + if 'string' == type child + e.innerHTML ..= child + else + e\appendChild child + +elements = {} +add = (e) -> elements[e] = element 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', 'pre'} do add e +for e in *{'br', 'hr', 'img', 'input', 'p', 'textarea'} do add e +for i=1,8 do add "h" .. i + +elements diff --git a/lib/html.server.moon b/lib/html.server.moon new file mode 100644 index 0000000..bbeaa9d --- /dev/null +++ b/lib/html.server.moon @@ -0,0 +1,37 @@ +element = (element) -> (...) -> + children = { ... } + + -- attributes are last arguments but mustn't be a ReactiveVar + attributes = children[#children] + if 'table' == (type attributes) and not attributes.node + table.remove children + else + attributes = {} + + b = "<#{element}" + for k,v in pairs attributes + if 'table' == type v + tmp = '' + for kk, vv in pairs v + tmp ..= "#{kk}: #{vv}; " + v = tmp + b ..= " #{k}=\"#{v}\"" + + -- if there is only one argument, + -- children can be in attributes table too + if #children == 0 + children = attributes + + b ..= ">" .. table.concat children, '' + b ..= "</#{element}>" + b + +elements = {} +add = (e) -> elements[e] = element 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', 'pre'} do add e +for e in *{'br', 'hr', 'img', 'input', 'p', 'textarea'} do add e +for i=1,8 do add "h" .. i + +elements |
