Skip to content

Commit

Permalink
feat: acandy.Raw
Browse files Browse the repository at this point in the history
  • Loading branch information
AmeroHan committed Jan 26, 2024
1 parent 02f1aa6 commit 9c55c45
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 37 deletions.
30 changes: 28 additions & 2 deletions acandy/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ local MTKEY_FRAG_LIKE = '__acandy_fragment_like'
local MTKEY_PROPS_LIKE = '__acandy_props_like'


local Raw_mt ---@type metatable

--- Create a Raw object, which would not be encoded when converted to string.
--- @param val any value to be converted to string by `tostring()`
--- @return table
local function Raw(val)
return setmetatable({[SYM_STRING] = tostring(val)}, Raw_mt)
end

Raw_mt = {
__tostring = function(self)
return self[SYM_STRING]
end,
__concat = function(left, right)
if getmetatable(left) ~= Raw_mt or getmetatable(right) ~= Raw_mt then
error('Raw object can only be concatenated with another Raw object', 2)
end
return setmetatable({[SYM_STRING] = left[SYM_STRING]..right[SYM_STRING]}, Raw_mt)
end,
__newindex = function()
error('Raw object is not mutable', 2)
end,
}


--[[
## Fragment
Expand Down Expand Up @@ -86,7 +111,7 @@ local function extend_strings_with_fragment(strs, frag, strs_len, no_encode)
elseif node_type == 'string' then
strs_len = strs_len + 1
strs[strs_len] = no_encode and node or utils.html_encode(node)
else -- others: Element, boolean, number
else -- others: Raw, Element, boolean, number
strs_len = strs_len + 1
strs[strs_len] = tostring(node)
end
Expand Down Expand Up @@ -366,7 +391,7 @@ local function new_built_elem_from_props(self, props_or_child)
local tag_name = self[SYM_TAG_NAME]
local attr_map = rawget(self, SYM_ATTR_MAP) or {}
local new_attr_map = utils.shallow_copy(attr_map)
local arg_is_props_like = type(props_or_child) == 'table' and is_table_props_like(props_or_child)
local arg_is_props_like = type(props_or_child) == 'table' and is_table_props_like(props_or_child)

if VOID_ELEMS[tag_name] then -- void element, e.g. <br>, <img>
if arg_is_props_like then
Expand Down Expand Up @@ -733,6 +758,7 @@ local some = setmetatable({}, {

acandy = setmetatable({
Fragment = Fragment,
Raw = Raw,
from_yields = from_yields,
some = some,
}, acandy_mt)
Expand Down
4 changes: 4 additions & 0 deletions acandy/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ function utils.shallow_icopy(t)
end


--- Apply `func` to each element of `...` and return a table.
---@param func function
---@vararg any
---@return any[]
function utils.map_varargs(func, ...)
local t = {}
for i, v in ipairs {...} do
Expand Down
87 changes: 52 additions & 35 deletions examples.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
local a = require("acandy")


-----------------
-- EXAMPLE 1 --
-- TODO: test cases


-- 1. Basic usage
-----------------

local example1 = (
Expand Down Expand Up @@ -51,9 +53,8 @@ print(example1)
]]


-----------------
-- EXAMPLE 2 --
-----------------
-- 2. Custom component
----------------------

local Card = function(props)
return a.div { class="card",
Expand All @@ -79,9 +80,8 @@ print(example2)
]]


-----------------
-- EXAMPLE 3 --
-----------------
-- 3. Element manipulation
--------------------------

local example3 = a.div()
print(example3)
Expand All @@ -108,9 +108,8 @@ print(example3)
--> <br id="example3">


-----------------
-- EXAMPLE 4 --
-----------------
-- 4. acandy.from_yields - derive a Fragment from a generator function
----------------------------------------------------------------------

local height_weights = {
{ name = "Alice", height = 160, weight = 50 },
Expand All @@ -126,8 +125,8 @@ local example4 = a.table {
for _, item in ipairs(height_weights) do
yield(a.tr {
a.td(item.name),
a.td(item.height..' cm'),
a.td(item.weight..' kg'),
a.td(item.height.." cm"),
a.td(item.weight.." kg"),
})
end
end, -- or `a.from_yields(function(yield) ... end)`
Expand Down Expand Up @@ -159,9 +158,8 @@ print(example4)
]]


-----------------
-- EXAMPLE 5 --
-----------------
-- 5. Shorthand attributes
--------------------------

local example5 = a.div["#my-div cls1 cls2"] {
a.p "You know what it is.",
Expand All @@ -170,9 +168,9 @@ print(example5)
--> <div id="my-div" class="cls1 cls2"><p>You know what it is.</p></div>


-----------------
-- EXAMPLE 6 --
-----------------
-- 6. Element chains
--------------------

local template_attrs = { class="foo", style="color: green;" }
local example6 = a.header["site-header"] / a.nav / a.ul {
a.li["foo"] / a.a { href="/home", "Home" },
Expand All @@ -193,14 +191,27 @@ print(example6)
]]


-----------------
-- EXAMPLE 7 --
-----------------

-- 7. acandy.some - Construct multiple elements
-----------------------------------------------

--[[
```
some.th[<index>](<arg1>, <arg2>, ...)
```
is equivalent to
```
a.Fragment {
a.th[<index>](<arg1>),
a.th[<index>](<arg2>),
...
}
```
]]
local some = a.some
local example7 = a.table {
a.tr / some.th['foo']("One", "Two", "Three"),
a.tr / some.td('A', 'B', 'C'),
a.tr / some.th["foo"]("One", "Two", "Three"),
a.tr / some.td("A", "B", "C"),
}
print(example7)
--[[ Output (formated):
Expand All @@ -217,16 +228,22 @@ print(example7)
</tr>
</table>
]]
--[[
```
some.th[<index>](<arg1>, <arg2>, ...)
```
is equivalent to
```
a.Fragment {
a.th[<index>](<arg1>),
a.th[<index>](<arg2>),
...


-- 8. Raw strings
-----------------

local example8_a = a.ul {
a.li "Encoded: <br>",
a.li / a.Raw("Remain: <br>"),
}
```
print(example8_a)
--[[ Output (formated):
<ul>
<li>Encoded: &lt;br&gt;</li>
<li>Remain: <br></li>
</ul>
]]

local example8_b = a.div(a.Raw("<span>")..a.Raw("</span>"))
print(example8_b) --> <div><span></span></div>

0 comments on commit 9c55c45

Please sign in to comment.