From b99de1d4e79e6c4a548d9dddca730eaeeb63a1c9 Mon Sep 17 00:00:00 2001 From: s-ol Date: Wed, 31 Oct 2018 19:52:16 +1100 Subject: almost there tbh --- lib/init.client.moon | 6 +-- lib/init.server.moon | 9 +---- lib/mmmfs/browser.moon | 105 ++++++++++++++++++++++++++++++++----------------- lib/mmmfs/fileder.moon | 75 ++++++++++++++++++++++++++++++----- lib/mmmfs/init.moon | 28 +++++++++++++ 5 files changed, 168 insertions(+), 55 deletions(-) (limited to 'lib') diff --git a/lib/init.client.moon b/lib/init.client.moon index 981cd25..77ff0ee 100644 --- a/lib/init.client.moon +++ b/lib/init.client.moon @@ -1,4 +1,4 @@ -export MODE, print, warn, relative, append, on_client +export MODE, print, warn, relative, on_client export window, document window = js.global @@ -7,8 +7,9 @@ window = js.global MODE = 'CLIENT' deep_tostring = (tbl, space='') -> - buf = space .. tostring tbl + return tbl if 'userdata' == type tbl + buf = space .. tostring tbl return buf unless 'table' == type tbl buf = buf .. ' {\n' @@ -42,5 +43,4 @@ relative = do name = base .. name if '.' == name\sub 1, 1 _require name -append = document.body\appendChild on_client = (f, ...) -> f ... diff --git a/lib/init.server.moon b/lib/init.server.moon index f81a2dc..198c64e 100644 --- a/lib/init.server.moon +++ b/lib/init.server.moon @@ -1,4 +1,4 @@ -export MODE, print, warn, relative, append, on_client +export MODE, print, warn, relative, on_client MODE = 'SERVER' deep_tostring = (tbl, space='') -> @@ -38,18 +38,13 @@ relative = do name = base .. name if '.' == name\sub 1, 1 _require name --- shorthand to append elements to body -buffer = '' -append = (val) -> - buffer ..= val - import compile, insert_loader from require 'lib.duct_tape' insert_loader! on_client = (fn, ...) -> args = {...} -- warn code - append "" diff --git a/lib/mmmfs/browser.moon b/lib/mmmfs/browser.moon index a4080c2..5f70174 100644 --- a/lib/mmmfs/browser.moon +++ b/lib/mmmfs/browser.moon @@ -6,28 +6,47 @@ import div, span, a, select, option from elements limit = (list, num) -> [v for i,v in ipairs list when i <= num] -class Browser - new: (@root, @path={}, rehydrate=false) => - @path = ReactiveVar @path - -- @path\subscribe (path) -> window.location.hash = '/' .. table.concat path, '/' - @prop = ReactiveVar (@root\find 'mmm/dom') or next @root.props - @active = @path\map (path) -> - fileder = @root - for name in *path - local next - for child in *fileder.children - if name == child\get 'name', 'alpha' - next = child - break +path2tbl = (path) -> + switch type path + when 'string' + path + when 'table' + str = table.concat path, '/' + if '/' != str\sub 1, 1 + str = '/' .. str + str + else + error "path is of wrong type: #{type path}" - if not next - return - - fileder = next +class Browser + new: (@root, path={}, rehydrate=false) => + @path = ReactiveVar path2tbl path - fileder + assert @root, 'root fileder is nil' - @active\subscribe (fileder) -> @prop\set (fileder\find 'mmm/dom') or next fileder.props + -- @path\subscribe (path) -> window.location.hash = '/' .. table.concat path, '/' + @active = @path\map (path) -> @root\walk path + +-- fileder = @root +-- +-- for name in *path +-- local next +-- for child in *fileder.children +-- if name == child\get 'name', 'alpha' +-- next = child +-- break +-- +-- if not next +-- warn "couldn't find node '#{name}'" +-- return +-- +-- fileder = next +-- +-- fileder + + @prop = @active\map (fileder) -> + return unless fileder + (fileder\find 'mmm/dom') or next fileder.props -- retrieve or create the root root = 'div' @@ -72,8 +91,9 @@ class Browser onchange = (_, e) -> @prop\set Key e.target.value - current = @prop\get!\tostring! - with select :onchange + current = @prop\get! + current = current and current\tostring! + with select :onchange, disabled: not fileder if fileder for key, _ in pairs fileder.props value = key\tostring! @@ -83,46 +103,59 @@ class Browser -- append or patch #browser-content node = 'div' node = document\getElementById 'browser-content' if rehydrate - @dom\append ReactiveElement node, { - id: 'browser-content', - style: { - flex: '1 0 0', - overflow: 'auto', - padding: '1em 2em', - }, - @get_content rehydrate and node - } + @dom\append with ReactiveElement node, { + id: 'browser-content', + style: { + flex: '1 0 0', + overflow: 'auto', + padding: '1em 2em', + }, + } + \append @get_content rehydrate and node + + if rehydrate + -- force one update to update onclick handlers etc + @prop\set @prop\get! @node = @dom.node @render = @dom\render get_content: (wrapper) => + disp_error = (msg) -> span msg, style: { color: '#f00' } var = @prop\map (prop) -> active = @active\get! + if not active + return disp_error "fileder not found!" + + if not prop + return disp_error "property not found!" + ok, res = pcall -> - conversions = assert (get_conversions 'mmm/dom', prop.type), "no conversion path" + conversions = get_conversions 'mmm/dom', prop.type value = assert (active\get prop), "value went missing?" + return unless conversions + for i=#conversions,1,-1 { :inp, :out, :transform } = conversions[i] value = transform value, active value - if ok and res - res + if ok + res or disp_error "[no conversion path to mmm/dom]" else warn "error: ", res unless ok - span "cannot display!", style: { color: '#f00' } + disp_error "[unknown error displaying]" -- wrapper was built already so take over the old value if wrapper - var\set wrapper.lastElementChild + return var, wrapper.lastChild -- var\set wrapper.lastElementChild var - navigate: (new) => @path\set new + navigate: (new) => @path\set path2tbl new { :Browser, diff --git a/lib/mmmfs/fileder.moon b/lib/mmmfs/fileder.moon index 75659d4..a1f689e 100644 --- a/lib/mmmfs/fileder.moon +++ b/lib/mmmfs/fileder.moon @@ -37,17 +37,75 @@ class Fileder -- instantiate from props and children tables -- or mix in one table (numeric keys are children, remainder props) -- prop-keys are passed to Key constructor - new: (props, @children) => - if not @children - @children = for i, child in ipairs props + new: (props, children) => + if not children + children = for i, child in ipairs props props[i] = nil child - @props = { (Key k), v for k, v in pairs props } + -- automatically mount children on insert + @children = setmetatable {}, __newindex: (t, k, child) -> + rawset t, k, child + if @path == '/' + child\mount '/' + elseif @path + child\mount @path .. '/' + + -- copy children + for i, child in ipairs children + @children[i] = child + + -- automatically reify string keys on insert + @props = setmetatable {}, __newindex: (t, key, v) -> + rawset t, key, nil -- fix for fengari.io + rawset t, (Key key), v + + -- copy props + for k, v in pairs props + @props[k] = v + + -- recursively walk to and return the fileder with @path == path + walk: (path) => + -- early-out if we are outside of the path already + return unless @path\match '^' .. path + + -- gotcha + return @ if path == @path + + -- @TODO: obviously there is better ways + for child in *@children + result = child\walk path + return if result + + -- recursively mount fileder and children at path + -- * path - the path to mount at (default: '/') + -- * mount_as - dont append own name to path + mount: (path='/', mount_as=false) => + assert not @path, "mounted twice: #{@path} and now #{path}" + + if mount_as + @path = path + else + @path = path .. @gett 'name: alpha' + + if @path == '/' + for child in *@children + child\mount '/' + else + for child in *@children + child\mount @path .. '/' + + -- recursively iterate all children (coroutine) + -- * depth - depth to stop after; 1 = yield only self (default: infinite) + iterate: (depth=0) => + coroutine.yield @ + return if depth == 1 + + for child in *@children + child\iterate depth - 1 -- find property key according to criteria, nil if no value or conversion path - -- * name - property name (optional: defaults to main content) - -- * type - wanted result type + -- * ... - arguments like Key find: (...) => want = Key ... @@ -66,8 +124,7 @@ class Fileder error "couldn't find key after resolution?" -- get property according to criteria, nil if no value or conversion path - -- * name - property name (optional: defaults to main content) - -- * type - wanted result type + -- * ... - arguments like Key get: (...) => want = Key ... @@ -89,7 +146,7 @@ class Fileder want = Key ... value, key = @get want - assert value, "node doesn't have value for #{want\tostring!}" + assert value, "node doesn't have value for '#{want\tostring!}'" value, key { diff --git a/lib/mmmfs/init.moon b/lib/mmmfs/init.moon index bb26505..172f0ef 100644 --- a/lib/mmmfs/init.moon +++ b/lib/mmmfs/init.moon @@ -1,7 +1,35 @@ require = relative ... import Key, Fileder from require '.fileder' +import Browser from require '.browser' +import tohtml from require 'lib.component' + +define_fileders = (...) -> + source_module = ... + + (...) -> + with fileder = Fileder ... + .source_module = source_module + +rehydrate = (path) -> + import Browser from require 'lib.mmmfs.browser' + root = require 'root' + root\mount! + + export BROWSER + BROWSER = Browser root, path, true + +render = (root, path) -> + export BROWSER + BROWSER = Browser root, path + + str = tohtml BROWSER + str ..= on_client rehydrate, path + str { :Key :Fileder + :render + :define_fileders + :module_roots } -- cgit v1.2.3 From d4180f5d3b8079924e62817aa74c401478ad7932 Mon Sep 17 00:00:00 2001 From: s-ol Date: Thu, 1 Nov 2018 17:53:46 +1100 Subject: fix event handles in lib.dom --- lib/dom.client.moon | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/dom.client.moon b/lib/dom.client.moon index a5a319b..acdbfa4 100644 --- a/lib/dom.client.moon +++ b/lib/dom.client.moon @@ -24,9 +24,9 @@ element = (element) -> (...) -> for child in *children if 'string' == type child - e.innerHTML ..= child - else - e\appendChild child + child = document\createTextNode child + + e\appendChild child setmetatable {}, __index: (name) => with val = element name -- cgit v1.2.3 From d8daeb2a0b891bae03a7de2ae4ddf9e7b8b9f66b Mon Sep 17 00:00:00 2001 From: s-ol Date: Thu, 1 Nov 2018 18:10:38 +1100 Subject: smooth navigation --- lib/mmmfs/browser.moon | 56 ++++++++++++++++++++++++++------------------------ lib/mmmfs/fileder.moon | 6 +++--- 2 files changed, 32 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/mmmfs/browser.moon b/lib/mmmfs/browser.moon index 5f70174..bd69fc0 100644 --- a/lib/mmmfs/browser.moon +++ b/lib/mmmfs/browser.moon @@ -24,25 +24,11 @@ class Browser assert @root, 'root fileder is nil' - -- @path\subscribe (path) -> window.location.hash = '/' .. table.concat path, '/' - @active = @path\map (path) -> @root\walk path - --- fileder = @root --- --- for name in *path --- local next --- for child in *fileder.children --- if name == child\get 'name', 'alpha' --- next = child --- break --- --- if not next --- warn "couldn't find node '#{name}'" --- return --- --- fileder = next --- --- fileder + @path\subscribe (path) -> + path ..= '/' unless path\match '/$' + window.history\pushState nil, '', path + + @active = @path\map @root\walk @prop = @active\map (fileder) -> return unless fileder @@ -77,15 +63,26 @@ class Browser background: '#eeeeee', }, span 'path: ', @path\map (path) -> with div style: { display: 'inline-block' } - \append a 'root', href: '#', onclick: (_, e) -> - e\preventDefault! - @navigate {} + path_segment = (name, href) -> + a name, :href, onclick: (_, e) -> + e\preventDefault! + @navigate href + + path = path\match '^/(.*)' + href = '' + + \append path_segment 'root', '/' + + while path + name, rest = path\match '^(%w+)/(.*)' -- or rest + if not name + name = path + + path = rest + href = "#{href}/#{name}" - for i,name in ipairs path \append '/' - \append a name, href: '#', onclick: (_, e) -> - e\preventDefault! - @navigate limit path, i + \append path_segment name, href span 'view property: ', @active\map (fileder) -> onchange = (_, e) -> @@ -131,7 +128,7 @@ class Browser if not prop return disp_error "property not found!" - ok, res = pcall -> + convert = -> conversions = get_conversions 'mmm/dom', prop.type value = assert (active\get prop), "value went missing?" @@ -143,6 +140,11 @@ class Browser value + ok, res = if MODE == 'CLIENT' + pcall convert + else + true, convert! + if ok res or disp_error "[no conversion path to mmm/dom]" else diff --git a/lib/mmmfs/fileder.moon b/lib/mmmfs/fileder.moon index a1f689e..c3e40f7 100644 --- a/lib/mmmfs/fileder.moon +++ b/lib/mmmfs/fileder.moon @@ -65,17 +65,17 @@ class Fileder @props[k] = v -- recursively walk to and return the fileder with @path == path + -- * path - the path to walk to walk: (path) => -- early-out if we are outside of the path already - return unless @path\match '^' .. path + return unless path\match '^' .. @path -- gotcha return @ if path == @path - -- @TODO: obviously there is better ways for child in *@children result = child\walk path - return if result + return result if result -- recursively mount fileder and children at path -- * path - the path to mount at (default: '/') -- cgit v1.2.3 From 29d2b615dafa6f3a54910b72d303f74a97c4d822 Mon Sep 17 00:00:00 2001 From: s-ol Date: Thu, 1 Nov 2018 19:07:40 +1100 Subject: component get_or_create --- lib/component.client.moon | 7 ++++ lib/component.server.moon | 5 +++ lib/mmmfs/browser.moon | 96 +++++++++++++++++++---------------------------- 3 files changed, 50 insertions(+), 58 deletions(-) (limited to 'lib') diff --git a/lib/component.client.moon b/lib/component.client.moon index 6e0e8d8..ffeb084 100644 --- a/lib/component.client.moon +++ b/lib/component.client.moon @@ -136,6 +136,12 @@ class ReactiveElement if 'table' == (type child) and child.destroy child\destroy! +get_or_create = (elem, id, ...) -> + elem = (document\getElementById id) or elem + + with ReactiveElement elem, ... + \set 'id', id + elements = setmetatable {}, __index: (name) => with val = (...) -> ReactiveElement name, ... @[name] = val @@ -143,6 +149,7 @@ elements = setmetatable {}, __index: (name) => { :ReactiveVar, :ReactiveElement, + :get_or_create, -- :join, :tohtml, :append, diff --git a/lib/component.server.moon b/lib/component.server.moon index 85d97eb..d717ff7 100644 --- a/lib/component.server.moon +++ b/lib/component.server.moon @@ -105,9 +105,14 @@ elements = setmetatable {}, __index: (name) => with val = (...) -> ReactiveElement name, ... @[name] = val +get_or_create = (elem, id, ...) -> + with ReactiveElement elem, ... + \set 'id', id + { :ReactiveVar, :ReactiveElement, + :get_or_create, :tohtml, :flush, :append, diff --git a/lib/mmmfs/browser.moon b/lib/mmmfs/browser.moon index bd69fc0..4a37961 100644 --- a/lib/mmmfs/browser.moon +++ b/lib/mmmfs/browser.moon @@ -1,58 +1,47 @@ require = relative ..., 1 import Key from require '.fileder' import get_conversions from require '.conversion' -import ReactiveVar, ReactiveElement, text, elements from require 'lib.component' +import ReactiveVar, get_or_create, text, elements from require 'lib.component' import div, span, a, select, option from elements -limit = (list, num) -> [v for i,v in ipairs list when i <= num] - -path2tbl = (path) -> - switch type path - when 'string' - path - when 'table' - str = table.concat path, '/' - if '/' != str\sub 1, 1 - str = '/' .. str - str - else - error "path is of wrong type: #{type path}" - class Browser - new: (@root, path={}, rehydrate=false) => - @path = ReactiveVar path2tbl path - + new: (@root, path='/', rehydrate=false) => + -- root fileder assert @root, 'root fileder is nil' - @path\subscribe (path) -> - path ..= '/' unless path\match '/$' - window.history\pushState nil, '', path + -- active path + @path = ReactiveVar path + + if MODE == 'CLIENT' + -- update URL bar + @path\subscribe (path) -> + path ..= '/' unless path\match '/$' + window.history\pushState nil, '', path + -- active fileder + -- (re)set every time @path changes @active = @path\map @root\walk + -- currently active property + -- (re)set to default every time @active changes @prop = @active\map (fileder) -> return unless fileder (fileder\find 'mmm/dom') or next fileder.props -- retrieve or create the root - root = 'div' - root = document\getElementById 'browser-root' if rehydrate - @dom = ReactiveElement root, { - id: 'browser-root' - style: { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - display: 'flex', - overflow: 'hidden', - 'flex-direction': 'column', - 'justify-content': 'space-between', - } + @dom = get_or_create 'div', 'browser-root', style: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + display: 'flex', + overflow: 'hidden', + 'flex-direction': 'column', + 'justify-content': 'space-between', } - -- add or prepend the navbar + -- prepend the navbar if MODE == 'CLIENT' @dom\prepend div { style: { @@ -98,28 +87,25 @@ class Browser } -- append or patch #browser-content - node = 'div' - node = document\getElementById 'browser-content' if rehydrate - @dom\append with ReactiveElement node, { - id: 'browser-content', - style: { - flex: '1 0 0', - overflow: 'auto', - padding: '1em 2em', - }, + @dom\append with get_or_create 'div', 'browser-content', style: { + flex: '1 0 0', + overflow: 'auto', + padding: '1em 2em', } - \append @get_content rehydrate and node + \append @get_content!, (rehydrate and .node.lastChild) if rehydrate - -- force one update to update onclick handlers etc + -- force one rerender to set onclick handlers etc @prop\set @prop\get! + -- export mmm/component interface @node = @dom.node @render = @dom\render - get_content: (wrapper) => + -- render #browser-content + get_content: () => disp_error = (msg) -> span msg, style: { color: '#f00' } - var = @prop\map (prop) -> + @prop\map (prop) -> active = @active\get! if not active @@ -151,14 +137,8 @@ class Browser warn "error: ", res unless ok disp_error "[unknown error displaying]" - -- wrapper was built already so take over the old value - if wrapper - return var, wrapper.lastChild -- var\set wrapper.lastElementChild - - var - - navigate: (new) => @path\set path2tbl new + navigate: (new) => @path\set new { - :Browser, + :Browser } -- cgit v1.2.3 From 5162ce307dbd5a8b75570bf8b3c8f6f98ab3fd58 Mon Sep 17 00:00:00 2001 From: s-ol Date: Thu, 1 Nov 2018 19:23:13 +1100 Subject: load static render before UI --- lib/mmmfs/init.moon | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/mmmfs/init.moon b/lib/mmmfs/init.moon index 172f0ef..d0de45b 100644 --- a/lib/mmmfs/init.moon +++ b/lib/mmmfs/init.moon @@ -22,9 +22,8 @@ render = (root, path) -> export BROWSER BROWSER = Browser root, path - str = tohtml BROWSER - str ..= on_client rehydrate, path - str + content = tohtml BROWSER + content, on_client rehydrate, path { :Key -- cgit v1.2.3 From 4803828015e1141528ec66411532e5c0c3a4f5dc Mon Sep 17 00:00:00 2001 From: s-ol Date: Thu, 1 Nov 2018 19:35:02 +1100 Subject: refactor browser SCSS --- lib/mmmfs/browser.moon | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) (limited to 'lib') diff --git a/lib/mmmfs/browser.moon b/lib/mmmfs/browser.moon index 4a37961..ce7709f 100644 --- a/lib/mmmfs/browser.moon +++ b/lib/mmmfs/browser.moon @@ -29,41 +29,26 @@ class Browser (fileder\find 'mmm/dom') or next fileder.props -- retrieve or create the root - @dom = get_or_create 'div', 'browser-root', style: { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - display: 'flex', - overflow: 'hidden', - 'flex-direction': 'column', - 'justify-content': 'space-between', - } + @dom = get_or_create 'div', 'browser-root' -- prepend the navbar - if MODE == 'CLIENT' - @dom\prepend div { - style: { - padding: '1em', - flex: '0 0 auto', - display: 'flex', - 'justify-content': 'space-between', - background: '#eeeeee', - }, + if MODE == 'SERVER' + @dom\append div id: 'browser-navbar' + else + @dom\prepend get_or_create 'div', 'browser-navbar', { span 'path: ', @path\map (path) -> with div style: { display: 'inline-block' } path_segment = (name, href) -> a name, :href, onclick: (_, e) -> e\preventDefault! @navigate href - path = path\match '^/(.*)' href = '' + path = path\match '^/(.*)' \append path_segment 'root', '/' while path - name, rest = path\match '^(%w+)/(.*)' -- or rest + name, rest = path\match '^(%w+)/(.*)' if not name name = path @@ -87,11 +72,7 @@ class Browser } -- append or patch #browser-content - @dom\append with get_or_create 'div', 'browser-content', style: { - flex: '1 0 0', - overflow: 'auto', - padding: '1em 2em', - } + @dom\append with get_or_create 'div', 'browser-content' \append @get_content!, (rehydrate and .node.lastChild) if rehydrate @@ -108,11 +89,8 @@ class Browser @prop\map (prop) -> active = @active\get! - if not active - return disp_error "fileder not found!" - - if not prop - return disp_error "property not found!" + return disp_error "fileder not found!" unless active + return disp_error "property not found!" unless prop convert = -> conversions = get_conversions 'mmm/dom', prop.type -- cgit v1.2.3 From 0fcadd15d802ab20167cade1608e93caa5072723 Mon Sep 17 00:00:00 2001 From: s-ol Date: Thu, 1 Nov 2018 19:54:46 +1100 Subject: fix server-side void tags --- lib/component.server.moon | 15 ++++++++++++--- lib/dom.server.moon | 13 ++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/component.server.moon b/lib/component.server.moon index d717ff7..0026d32 100644 --- a/lib/component.server.moon +++ b/lib/component.server.moon @@ -1,5 +1,8 @@ import opairs from require 'lib.ordered' +void_tags = { 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr' } +void_tags = { t,t for t in *void_tags } + -- convert anything to HTML string -- val must be one of: -- * MMMElement (have a .render method that returns a string) @@ -97,9 +100,15 @@ class ReactiveElement tmp ..= "#{kk}: #{vv}; " v = tmp b ..= " #{k}=\"#{v}\"" - b ..= ">" .. table.concat @children, '' - b ..= "" - b + + if void_tags[@element] + assert #@children == 0, "void tag #{element} cannot have children!" + b .. ">" + else + b .. ">" + b ..= ">" .. table.concat @children, '' + b ..= "" + b elements = setmetatable {}, __index: (name) => with val = (...) -> ReactiveElement name, ... diff --git a/lib/dom.server.moon b/lib/dom.server.moon index 3a7b537..fe6b60c 100644 --- a/lib/dom.server.moon +++ b/lib/dom.server.moon @@ -1,5 +1,8 @@ import opairs from require 'lib.ordered' +void_tags = { 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr' } +void_tags = { t,t for t in *void_tags } + element = (element) -> (...) -> children = { ... } @@ -24,9 +27,13 @@ element = (element) -> (...) -> if #children == 0 children = attributes - b ..= ">" .. table.concat children, '' - b ..= "" - b + if void_tags[element] + assert #children == 0, "void tag #{element} cannot have children!" + b .. ">" + else + b ..= ">" .. table.concat children, '' + b ..= "" + b setmetatable {}, __index: (name) => with val = element name -- cgit v1.2.3