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 | |
| parent | some fixes (diff) | |
| download | mmm-ece199a92220deaa3212fad30f6026693ae86c63.tar.gz mmm-ece199a92220deaa3212fad30f6026693ae86c63.zip | |
polymorphic!
Diffstat (limited to 'app')
| -rw-r--r-- | app/center_of_mass.moon (renamed from app/centerofmass.moon) | 18 | ||||
| -rw-r--r-- | app/component.client.moon (renamed from app/component.moon) | 28 | ||||
| -rw-r--r-- | app/component.server.moon | 115 | ||||
| -rw-r--r-- | app/html.client.moon (renamed from app/html.moon) | 0 | ||||
| -rw-r--r-- | app/html.server.moon | 36 | ||||
| -rw-r--r-- | app/index.moon | 71 | ||||
| -rw-r--r-- | app/play_tags.moon | 6 | ||||
| -rw-r--r-- | app/realities.moon | 193 | ||||
| -rw-r--r-- | app/test_component.moon | 63 | ||||
| -rw-r--r-- | app/todo.moon | 2 | ||||
| -rw-r--r-- | app/twisted.moon | 4 |
11 files changed, 346 insertions, 190 deletions
diff --git a/app/centerofmass.moon b/app/center_of_mass.moon index 6348937..d799b66 100644 --- a/app/centerofmass.moon +++ b/app/center_of_mass.moon @@ -1,9 +1,9 @@ window = js.global document = window.document -import CanvasApp from require './canvasapp.moon' -import rgb from require './color.moon' -import h1, p, div, span, input, button from require './html.moon' +import CanvasApp from require 'app.canvasapp' +import rgb from require 'app.color' +import h1, p, div, span, input, button from require 'app.html' fast = true center_char = do @@ -92,15 +92,15 @@ class CenterOfMass extends CanvasApp @ctx\fillText char, x + w/2 - cx, y - cy x += w -document.body\appendChild h1 'Fonts aligned by Center-of-Mass' -document.body\appendChild p 'Click below and type away :)' +append h1 'Fonts aligned by Center-of-Mass' +append p 'Click below and type away :)' app = CenterOfMass "What's up", "Times New Roman", 40 -document.body\appendChild app.canvas +append app.canvas app.canvas.style.backgroundColor = '#eee' add = => - document.body\appendChild div { + append div { span 'font: ', with @font_input = input! .type = 'text' @@ -109,7 +109,7 @@ add = => .onclick = (_, e) -> app.font = @font_input.value } - document.body\appendChild div { + append div { span 'size: ', input type: 'range', min: 2, max: 120, value: 40, onchange: (_, e) -> size = e.target.value @@ -119,7 +119,7 @@ add = => '' } - document.body\appendChild div { + append div { span 'optimize inner loop: ', input type: 'checkbox', checked: fast, onchange: (_, e) -> fast = e.target.checked diff --git a/app/component.moon b/app/component.client.moon index 6ce76bc..c012427 100644 --- a/app/component.moon +++ b/app/component.client.moon @@ -1,14 +1,12 @@ { :document } = js.global -warn = warn or -> print - -- 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 asnode -asnode = (val) -> +-- note that strings won't survive identity comparisons after tohtml +tohtml = (val) -> if 'string' == type val return document\createTextNode val if 'table' == type val @@ -20,9 +18,10 @@ asnode = (val) -> else error "not a Node: #{val}, #{type val}" --- shorthand to append elements to body --- see #asnode for permitted values -append = (val) -> document.body\appendChild asnode 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 { ... }, ' ' @@ -98,25 +97,26 @@ class ReactiveElement if ReactiveVar.isinstance child table.insert @_subscriptions, child\subscribe (...) -> @append ... child = child\get! - if 'string' == type child - warn 'string from ReactiveVar implicitly converted to TextNode, updating may fail' - child = asnode child - ok, last = pcall asnode, last + 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 asnode child + @node\removeChild tohtml child if 'table' == (type child) and child.destroy child\destroy! with exports = { :ReactiveVar, :ReactiveElement, - :asnode, + :tohtml, :append, :text, } @@ -124,5 +124,5 @@ with exports = { 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', 'img', 'input', 'p', 'textarea'} 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/app/component.server.moon b/app/component.server.moon new file mode 100644 index 0000000..456ca71 --- /dev/null +++ b/app/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/app/html.moon b/app/html.client.moon index fdec714..fdec714 100644 --- a/app/html.moon +++ b/app/html.client.moon diff --git a/app/html.server.moon b/app/html.server.moon new file mode 100644 index 0000000..6bd05e1 --- /dev/null +++ b/app/html.server.moon @@ -0,0 +1,36 @@ +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', 'span', 'a', 'p', 'pre', 'button', 'ul', 'li', 'i', 'b', 'u', 'tt'} do add e +for e in *{'br', 'img', 'input', 'p'} do add e +for i=1,8 do add "h" .. i + +elements diff --git a/app/index.moon b/app/index.moon index daec767..581a4db 100644 --- a/app/index.moon +++ b/app/index.moon @@ -1,10 +1,15 @@ window = js.global document = window.document -import h1, p, a, br, ul, tt, li from require './html.moon' +export MODE, append, print +MODE = 'CLIENT' +append = document.body\appendChild +print = window.console\log + +import h1, p, a, br, ul, tt, li from require 'app.html' moon = '\xe2\x98\xbd' -demos = +demos = 'twisted': 'canvas animation', 'todo': 'Todo demo of a simple reactive UI framework', 'realities': 'draft of a paper on virtual and other realities', @@ -13,46 +18,36 @@ demos = 'play-tags': 'Playground for Functional Tags', back_button = -> - document.body\appendChild p a { '< back', href: '/' } + append p a { '< back', href: '/' } -switch window.location.search - when '?twisted' - back_button! - require './twisted.moon' - when '?center-of-mass' then - back_button! - require './centerofmass.moon' - when '?todo' then - back_button! - require './todo.moon' - when '?test-component' then - back_button! - require './test_component.moon' - when '?realities' then - back_button! - require './realities.moon' - when '?play-tags' then +if window.location.search and #window.location.search > 1 + name = window.location.search\sub 2 + if demos[name] back_button! - require './play_tags.moon' + filename = name\gsub '-', '_' + require "app.#{filename}" else - document.body\appendChild h1 'mmm' - document.body\appendChild p { - tt 'mmm' - ' is not the ' - tt 'www' - ', because it runs on ' - a { 'MoonScript', href: 'https://moonscript.org' } - '.' - br! - 'You can find the source code of everything ' - a { 'here', href: 'https://github.com/s-ol/mmm' } - '.' - } - document.body\appendChild p 'current demos:' - document.body\appendChild ul for name, desc in pairs demos - li (a name, href: "/?#{name}"), ': ', desc + append h1 'are you lost?' + append p a { '(go home)', href: '/' } +else + append h1 'mmm' + append p { + tt 'mmm' + ' is not the ' + tt 'www' + ', because it runs on ' + a { 'MoonScript', href: 'https://moonscript.org' } + '.' + br! + 'You can find the source code of everything ' + a { 'here', href: 'https://github.com/s-ol/mmm' } + '.' + } + append p 'current demos:' + append ul for name, desc in pairs demos + li (a name, href: "/?#{name}"), ': ', desc -document.body\appendChild p { +append p { "made with #{moon} by " a { 's-ol', href: 'https://twitter.com/S0lll0s' } } diff --git a/app/play_tags.moon b/app/play_tags.moon index 68d6710..088f429 100644 --- a/app/play_tags.moon +++ b/app/play_tags.moon @@ -1,5 +1,5 @@ -import add_tag, rmv_tag, Node, Hierarchy, Toggle, NamespacedToggle from require './tags.moon' -import ReactiveVar, append, asnode, text, div, form, span, h3, a, input, textarea, button from require './component.moon' +import add_tag, rmv_tag, Node, Hierarchy, Toggle, NamespacedToggle from require 'app.tags' +import ReactiveVar, append, tohtml, text, div, form, span, h3, a, input, textarea, button from require 'app.component' clone = (set) -> assert set and 'table' == (type set), 'not a set' @@ -32,7 +32,7 @@ class ReactiveNode extends Node margin: '0 5px', } } - @node = asnode @_node + @node = tohtml @_node has: (tag) => @tags\get![tag] add: (tag) => @tags\transform set_append tag diff --git a/app/realities.moon b/app/realities.moon index 1ef4a01..0c9adc2 100644 --- a/app/realities.moon +++ b/app/realities.moon @@ -1,91 +1,105 @@ -window = js.global -{ :document, :eval } = window - -import asnode, h1, h2, p, a, i, div, ol, li, br, span, button, section, article from require './component.moon' -require 'svg.js' - -SVG = - doc: window\eval "(function() { return SVG(document.createElement('svg')); })", - G: window\eval "(function() { return new SVG.G(); })", -setmetatable SVG, __call: => @doc! - -o = do - mkobj = window\eval "(function () { return {}; })" - (tbl) -> - with obj = mkobj! - for k,v in pairs(tbl) - obj[k] = v - -print = window.console\log +import append, tohtml, h1, h2, p, a, i, div, ol, li, br, hr, span, button, section, article from require 'app.component' +local Diagram, o GRID_W = 50 GRID_H = 40 - -class Diagram - new: => - @svg = SVG! - @arrows = SVG.G! - @width, @height = 0, 0 - @y = 0 - - txtattr = o { - fill: 'white', - 'font-size': '14px', - 'text-anchor': 'middle', - } - block: (color, label, h=1) => - @svg\add with SVG.G! - with \rect GRID_W, h * GRID_H - \attr o fill: color - if label - with \plain label - \move GRID_W/2, 0 - \attr txtattr - - \move @width * GRID_W, (@y + h) * -GRID_H - @y += h - if @y > @height - @height = @y - - arrattr = o { - fill: 'white', - 'font-size': '18px', - 'text-anchor': 'middle', - } - arrow: (char, x, y) => - with @arrows\plain char - \attr arrattr - \move (x + 1) * GRID_W, (y - 0.5) * -GRID_H - 11 - - -- inout: (x=@width, y=@y) => @arrow '⇋', x, y -- U+21CB - -- inn: (x=@width, y=@y) => @arrow '↼', x, y+0.25 -- U+21BC - -- out: (x=@width, y=@y) => @arrow '⇁', x, y-0.25 -- U+21C1 - inout: (x=@width, y=@y) => @arrow '⇆', x, y -- U+21C6 - inn: (x=@width, y=@y) => @arrow '←', x, y+0.25 -- U+2190 - out: (x=@width, y=@y) => @arrow '→', x, y-0.25 -- U+2192 - - mind: (label='mind', ...) => @block '#fac710', label, ... - phys: (label='phys', ...) => @block '#8fd13f', label, ... - digi: (label='digi', ...) => @block '#9510ac', label, ... - - next: => - @y = 0 - @width += 1 - - finish: => - return if @node - @svg\add @arrows - - @width += 1 - w, h = @width * GRID_W, @height * GRID_H - - l = GRID_W / 6.5 - @svg\add with @svg\line 0, -GRID_H, w, -GRID_H - \stroke o width: 2, color: '#ffffff', dasharray: "#{l}, #{l}" - - @svg\size w, h - @svg\viewbox 0, -h, w, h - @node = @svg.node +if MODE == 'CLIENT' + require 'svg.js' + eval = js.global\eval + + + SVG = + doc: eval "(function() { return SVG(document.createElement('svg')); })", + G: eval "(function() { return new SVG.G(); })", + setmetatable SVG, __call: => @doc! + + o = do + mkobj = eval "(function () { return {}; })" + (tbl) -> + with obj = mkobj! + for k,v in pairs(tbl) + obj[k] = v + + class Diagram + new: => + @svg = SVG! + @arrows = SVG.G! + @width, @height = 0, 0 + @y = 0 + + txtattr = o { + fill: 'white', + 'font-size': '14px', + 'text-anchor': 'middle', + } + block: (color, label, h=1) => + @svg\add with SVG.G! + with \rect GRID_W, h * GRID_H + \attr o fill: color + if label + with \plain label + \move GRID_W/2, 0 + \attr txtattr + + \move @width * GRID_W, (@y + h) * -GRID_H + @y += h + if @y > @height + @height = @y + + arrattr = o { + fill: 'white', + 'font-size': '18px', + 'text-anchor': 'middle', + } + arrow: (char, x, y) => + with @arrows\plain char + \attr arrattr + \move (x + 1) * GRID_W, (y - 0.5) * -GRID_H - 11 + + -- inout: (x=@width, y=@y) => @arrow '⇋', x, y -- U+21CB + -- inn: (x=@width, y=@y) => @arrow '↼', x, y+0.25 -- U+21BC + -- out: (x=@width, y=@y) => @arrow '⇁', x, y-0.25 -- U+21C1 + inout: (x=@width, y=@y) => @arrow '⇆', x, y -- U+21C6 + inn: (x=@width, y=@y) => @arrow '←', x, y+0.25 -- U+2190 + out: (x=@width, y=@y) => @arrow '→', x, y-0.25 -- U+2192 + + mind: (label='mind', ...) => @block '#fac710', label, ... + phys: (label='phys', ...) => @block '#8fd13f', label, ... + digi: (label='digi', ...) => @block '#9510ac', label, ... + + next: => + @y = 0 + @width += 1 + + finish: => + return if @node + @svg\add @arrows + + @width += 1 + w, h = @width * GRID_W, @height * GRID_H + + l = GRID_W / 6.5 + @svg\add with @svg\line 0, -GRID_H, w, -GRID_H + \stroke o width: 2, color: '#ffffff', dasharray: "#{l}, #{l}" + + @svg\size w, h + @svg\viewbox 0, -h, w, h + @node = @svg.node +else + class Diagram + inout: => + inn: => + out: => + block: => + mind: => + phys: => + digi: => + next: => + finish: => + render: => + rplc = with div style: { display: 'inline-block', width: 200, height: 60 } + \append '(diagram goes here)' + rplc\render! addlabel = (label, diagram) -> with div style: { display: 'inline-block', margin: '20px', 'text-align': 'center' } @@ -142,7 +156,7 @@ sources = do } ref = do - fmt = (id) -> + fmt = (id) -> a { (sources[id]\short id), href: "##{id}" } ref = (...) -> @@ -162,7 +176,7 @@ sect = (label) -> with section style: 'page-break-inside': 'avoid' \append h2 label -document.body\append asnode with article style: { margin: 'auto', 'max-width': '750px' } +append with article style: { margin: 'auto', 'max-width': '750px' } \append div 'Sol Bekic', style: 'text-align': 'right' \append h1 { @@ -199,7 +213,7 @@ document.body\append asnode with article style: { margin: 'auto', 'max-width': ' "phys, mind, digi": "abbreviations for physical, mental and digital reality respectively.", } if 'number' == type definition - \append document\createElement 'hr' + \append hr! continue \append with div style: { 'margin-left': '2rem' } \append span term, style: { @@ -395,7 +409,8 @@ document.body\append asnode with article style: { margin: 'auto', 'max-width': ' \next! \block col, '', 2 - .svg\plain('phys + digi')\attr(o fill: 'white', 'font-size': '14px')\move 6, -2 * GRID_H + if MODE == 'CLIENT' + .svg\plain('phys + digi')\attr(o fill: 'white', 'font-size': '14px')\move 6, -2 * GRID_H \finish! \append p "Despite the similarities of VR and AR, the two can be considered polar opposites, as becomes evident when diff --git a/app/test_component.moon b/app/test_component.moon index 538368a..a8f8934 100644 --- a/app/test_component.moon +++ b/app/test_component.moon @@ -1,36 +1,42 @@ -{ :document, Node } = js.global - -import div, h1, ul, li, pre from require './html.moon' +import div, h1, ul, li, pre from require 'app.html' +last = nil test_group = (name) -> - list = ul! - root = div (h1 name), list - root, (name, test) -> + if last + append div (h1 name), ul last + + last = {} + (name, test) -> ok, err = pcall test - list\appendChild li if ok + table.insert last, li if ok "passed '#{name}'" else "failed '#{name}'", pre err -node, run_test = test_group 'component.moon' -document.body\appendChild node +expect = (expected, note, ...) -> + ok, msg = pcall ... + print ok, msg\find expected + if ok or not msg\find expected + error note + +run_test = test_group 'component.moon' local ReactiveVar, ReactiveElement run_test "exports ReactiveVar, ReactiveElement", -> - import ReactiveVar, ReactiveElement from require './component.moon' + import ReactiveVar, ReactiveElement from require 'app.component' assert ReactiveVar, "ReactiveVar not exported" assert ReactiveElement, "ReactiveElement not exported" -run_test "exports asnode helper", -> - import asnode from require './component.moon' - assert 'function' == (type asnode), "asnode not exported" +run_test "exports tohtml helper", -> + import tohtml from require 'app.component' + assert 'function' == (type tohtml), "tohtml not exported" run_test "exports append helper", -> - import append from require './component.moon' + import append from require 'app.component' assert 'function' == (type append), "append not exported" run_test "exports text helper", -> - import text from require './component.moon' + import text from require 'app.component' assert 'function' == (type text), "text not exported" node = text 'a test string' @@ -38,13 +44,12 @@ run_test "exports text helper", -> assert node.data == 'a test string', "expected text to store the string" run_test "text joins multiple arguments", -> - import text from require './component.moon' + import text from require 'app.component' node = text 'a', 'test', 'string' assert node.data == 'a test string', "expected text to join arguments with spaces" -node, run_test = test_group 'ReactiveVar' -document.body\appendChild node +run_test = test_group 'ReactiveVar' run_test "stores a value", -> reactive = ReactiveVar 'test' @@ -137,8 +142,7 @@ run_test "tracks multiple subscriptions at once", -> reactive\set 'test' assert done, "expected to reach the end" -node, run_test = test_group 'ReactiveElement' -document.body\appendChild node +run_test = test_group 'ReactiveElement' run_test "creates a HTML element", -> elem = ReactiveElement 'span' @@ -190,21 +194,12 @@ run_test "can unwrap and track children from ReactiveVars", -> assert elem.node.childElementCount == 2, "expected node to have two children" run_test "warns when appending a string from a ReactiveVar", -> - import text from require './component.moon' - export print - _print = print - - calls = 0 - print = (msg) -> calls += 1 if 'WARN: string' == msg\sub 1, 12 + import text from require 'app.component' str = ReactiveVar 'test' elem = ReactiveElement 'div', str - assert calls == 1, "expected to print warning" - - str\set 'string too' - assert calls == 2, "expected to print warning again" - - str\set text 'this is text' - assert calls == 2, "expected not to print warning with text node" + expect 'cannot replace string node', 'expected error', str\set, 'string too' + elem\destroy! - print = _print + elem = ReactiveElement 'div', str\map text + str\set 'this is text' diff --git a/app/todo.moon b/app/todo.moon index 5bf4fef..0310e0d 100644 --- a/app/todo.moon +++ b/app/todo.moon @@ -1,4 +1,4 @@ -import ReactiveVar, append, text, div, form, span, h3, a, input, textarea, button from require './component.moon' +import ReactiveVar, append, text, div, form, span, h3, a, input, textarea, button from require 'app.component' parent = div! todoItem = (desc, done) -> diff --git a/app/twisted.moon b/app/twisted.moon index f9ff282..1bc7a20 100644 --- a/app/twisted.moon +++ b/app/twisted.moon @@ -3,8 +3,8 @@ document = window.document Math = window.Math -import CanvasApp from require './canvasapp.moon' -import hsl from require './color.moon' +import CanvasApp from require 'app.canvasapp' +import hsl from require 'app.color' class TwistedDemo extends CanvasApp width: 500 |
