Something went wrong. Try again.
🦦 a tiny cute lua web framework
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248-- selene: allow(unscoped_variables, global_usage)_G.maivi = _G.maivi or {} --[[@as table]]
maivi.data = {}
-- util =======================================================================
-- https://github.com/neovim/neovim/blob/04f802a2f6387028e3389334e6e04abc28d92587/runtime/lua/vim/F.lua#L58-L66
--- like {...} except preserve the length explicitlyfunction maivi.pack_len(...) return { n = select("#", ...), ... }end
--- like unpack() but use the length set by F.pack_len if presentfunction maivi.unpack_len(t) return unpack(t, 1, t.n)end
function maivi.print(...) local t = maivi.pack_len(...) for i = 1, t.n do print(maivi.inspect(t[i])) endend
---@generic T---@param t table<string, T>---@param f fun(k: string): T---@param mt metatablefunction maivi.defaulttable(t, f, mt) local mt_ = { __index = function(self, k) local v = f(k) rawset(self, k, v) return rawget(self, k) end, } if mt then for k, v in pairs(mt) do mt_[k] = v end end return setmetatable(t, mt_)end
-- elem =======================================================================
---@alias Displayable string | number | boolean | fun(): Displayable---@alias Props table<string, Displayable>---@alias Child Displayable | VNode---@alias Children Child[]---@alias PropsOrChildren { [string]: Displayable, [integer]: Child }|Child
---@alias VNode { type: ElementType?, props: Props, children: Children }---@alias ElementType string | Component---@alias Element fun(args: PropsOrChildren): VNode---@alias Component fun(props: Props, children: Children): VNode
---@class ElementConfig---@field close? boolean---@field empty? boolean
---@class ParsedArgs---@field props Props---@field children Children---@field boolean_props string[]
---@param etype ElementType?---@param props Props---@param children Children---@return VNodefunction maivi.h(etype, props, children) if type(etype) == "function" then return etype(props, children) end return { type = etype, props = props, children = children }end
---@param etype? ElementType---@param config? ElementConfig---@return Elementfunction maivi.elem(etype, config) return function(args) local parsed = maivi.parse(args) return setmetatable(maivi.h(etype, parsed.props, parsed.children), { config = config, }) endend
-- components =================================================================
--- minimized version of `maivi.elem` since components dont use `.config`--- @param f Component--- @return Elementfunction maivi.component(f) return function(args) local parsed = maivi.parse(args) return maivi.h(f, parsed.props, parsed.children) endend
---@alias Slots table<string, VNode>---@param f fun(slots: Slots): Element---@return fun(page: Component): VNodefunction maivi.layout(f) return function(page) return function(args) local parsed = maivi.parse(args) local slots = page(parsed.props, parsed.children) return f(slots)(parsed.props, parsed.children) end endend
-- props & children ============================================================
---@param args PropsOrChildren?---@return ParsedArgsfunction maivi.parse(args) ---@type Props local props = {} ---@type Children local children = {} ---@type string[] local boolean_props = {}
if type(args) == "table" and getmetatable(args) == nil then for k, v in pairs(args) do if type(k) == "number" then table.insert(children, v) elseif k == "_" and type(v) == "table" then for _, prop in ipairs(v) do table.insert(boolean_props, prop) end elseif k == "data" and type(v) == "table" then for kdata, prop in pairs(v) do props["data-" .. kdata] = maivi.display.attr(prop) end else props[k] = maivi.display.attr(v) end end elseif args ~= nil then table.insert(children, args) end
return { props = props, children = children, boolean_props = boolean_props, }end
-- modules ====================================================================
maivi.mod = setmetatable({ graph = {},}, { ---@param t table ---@param modname string __call = function(t, modname) local info = debug.getinfo(2, "Sln") local source = info.source t.graph[source] = t.graph[source] or {} table.insert(t.graph[source], modname)
return function(...) if this.dev then package.loaded[modname] = nil end
local ok, result = pcall(require, modname) if not ok then error( ([[%s:%s error while loading module '%s':\t\n%s]]):format( info.short_src, info.currentline, modname, result ) ) end if type(result) ~= "function" then error( ([[%s:%s return value from module '%s' is not a function]]):format( info.short_src, info.currentline, modname ) ) end return result(...) end end,})
-- hooks =======================================================================
---@alias ctx table<string, any?>---@alias props table<string, any?>---@alias hook fun(ctx: ctx)
maivi._hooks = {}
---@param event string---@param props props---@param fn hookfunction maivi.hook(event, props, fn) maivi._hooks[event] = maivi._hooks[event] or {} table.insert(maivi._hooks[event], { props = props, fn = fn })end
---@param ref props---@param props props---@return booleanlocal function matchhook(ref, props) for k, v in pairs(props) do if k == "pattern" and ref.pattern then return string.match(ref.pattern, v) ~= nil elseif ref[k] ~= v then return false end end
return trueend
---@param ctx ctx---@param event string---@param props propsfunction maivi.trigger(ctx, event, props) local hooks = maivi._hooks[event] if not hooks then return end
for _, hook in ipairs(hooks) do -- fallen can be nil if ctx.fallen ~= true and matchhook(props, hook.props) then hook.fn(ctx) end endend
return maivi