reusable, extendable tags

This commit is contained in:
tristan 2024-01-01 12:42:18 +00:00
parent 8e97ebe2ce
commit e76305f71a
9 changed files with 85 additions and 76 deletions

View file

@ -10,30 +10,29 @@
packages.${system} = { packages.${system} = {
raw = nixite.mkSite (let raw = nixite.mkSite (let
readme = { readme = {
type = "link";
name = "readme"; name = "readme";
content = nixite.md.mdToPage ./README.md; content = nixite.md.mdToPage ./README.md;
}; };
markup = { markup = {
"test" = with nixite.elems; "index.html" = with nixite.elems;
let let
blue = nixite.style.tag "span" "blue" { blue = nixite.style.component span "blue" {
style = { color = "blue"; }; style = { color = "blue"; };
}; };
underblue = nixite.style.extend blue "under" { underblue = nixite.style.component blue "under" {
style = { text-decoration = "underline"; }; style = { text-decoration = "underline"; };
}; };
in (Doc { } [ in (Doc { } [
[ (title { } "Nixite") ] [ (title "Nixite") ]
(main { } [ (main [
(a { href = readme; } "Readme") (a { href = nixite.site.link readme; } "Readme")
(a "/blog" "blog") (a "/blog" "blog")
(List { } [ "item 1" "item 2" "item 3" ]) (List { } [ "item 1" "item 2" "item 3" ])
(p { } [ (p [
"check out my" "check out my"
(blue "blue span") (blue "blue span")
"isn't it" "isn't it"
(underblue "great!") (underblue { onclick = "alert(1)"; } "great!")
]) ])
]) ])
]); ]);

View file

@ -28,27 +28,29 @@ in rec {
else else
keyvalue key value) attrs))); keyvalue key value) attrs)));
tag = tag: { tag = t: {
inherit tag; tag = t;
__toString = self: toString (self { } ""); attrs = {};
__functor = self: attrs: __toString = self: toString (self "");
if !(builtins.isAttrs attrs) then __functor = self: child:
throw "HTML tag requires attribute set" (if !(isTag child) then
(if isSet child then
self // ({attrs = self.attrs // child;})
else
throw "tag child must be tag, list, or string, got ${
builtins.typeOf child
}"
)
else { else {
inherit tag attrs; tag = t;
__toString = self: toString (self ""); attrs = self.attrs;
__functor = self: child: inherit child;
if !(isTag child) then __toString = toHTML;
throw "tag child must be tag, list, or string, got ${ });
builtins.typeOf child
}"
else {
inherit tag attrs child;
__toString = toHTML;
};
};
}; };
isSet = a: builtins.isAttrs a && !a ? __toString;
isTag = tag: isTag = tag:
(builtins.isString tag || builtins.isList tag (builtins.isString tag || builtins.isList tag
|| (tag ? __toString && builtins.isFunction tag.__toString)); || (tag ? __toString && builtins.isFunction tag.__toString));

View file

@ -112,6 +112,8 @@ in rec {
else else
[ ]); [ ]);
link = {name, content}@page: page // {type = "link";};
copyTo = prefix: site: copyTo = prefix: site:
builtins.toString (builtins.attrValues (builtins.mapAttrs (name: content: builtins.toString (builtins.attrValues (builtins.mapAttrs (name: content:
if builtins.isString content then '' if builtins.isString content then ''

View file

@ -1,5 +1,4 @@
let let
html = import ./html.nix;
styleToString = identifier: styles: styleToString = identifier: styles:
if !builtins.isAttrs styles then if !builtins.isAttrs styles then
@ -21,7 +20,7 @@ let
({ ({
${element.attrs.__id} = element.attrs.style or { }; ${element.attrs.__id} = element.attrs.style or { };
} // (if element.attrs ? __extends then { } // (if element.attrs ? __extends then {
${element.attrs.__extends.attrs.__id} = ${element.attrs.__extends.attrs.__id or element.attrs.__extends.tag} =
element.attrs.__extends.attrs.style or { }; element.attrs.__extends.attrs.style or { };
} else } else
{ })) { }))
@ -42,10 +41,11 @@ let
builtins.zipAttrsWith (name: value: builtins.elemAt value 0) builtins.zipAttrsWith (name: value: builtins.elemAt value 0)
(map getStyles elements); (map getStyles elements);
mkProps = props: tag: class: mkProps = tag: class: props:
props // { props // {
__id = "${tag}.${builtins.concatStringsSep "." class}"; __id = "${tag.tag}.${class}";
class = class ++ (props.class or [ ]); __extends = tag;
class = ( tag.attrs.class or [] ) ++ [ class ] ++ (props.class or [ ]);
}; };
stylesToString = styles: stylesToString = styles:
@ -55,11 +55,6 @@ let
in { in {
inherit getStyle getStyles stylesToString; inherit getStyle getStyles stylesToString;
extend = tag: class: props: component = tag: class: props:
(html.tag tag.tag ((mkProps props tag.tag (tag.attrs.class ++ [ class ])) tag ( mkProps tag class props );
// {
__extends = tag;
}));
tag = tag: class: props: (html.tag tag (mkProps props tag [ class ]));
} }

View file

@ -9,7 +9,7 @@ in with html; [
expected = "p"; expected = "p";
})) }))
(it "keeps attr info in the tag" (let p = tag "p" { class = ""; }; (it "keeps attr info in the tag" (let p = tag "p" { class = ""; };
in { in {
actual = p.attrs; actual = p.attrs;
expected = { class = ""; }; expected = { class = ""; };
@ -81,6 +81,12 @@ in with html; [
expected = ''<p class="foobar"></p>''; expected = ''<p class="foobar"></p>'';
})) }))
(it "can take many sets of props" (let p = tag "p";
in {
actual = toString (p { class = "foobar"; } { style = "a style"; });
expected = ''<p class="foobar" style="a style"></p>'';
}))
(it "works recursively" (let (it "works recursively" (let
attrs = { style = { foo = "bar"; }; }; attrs = { style = { foo = "bar"; }; };
para = (tag "p" attrs); para = (tag "p" attrs);

View file

@ -1,4 +1,5 @@
path: path:
builtins.trace ( "testing " + builtins.baseNameOf path )
'' ''
echo echo
echo 'TEST: ${builtins.baseNameOf path}' echo 'TEST: ${builtins.baseNameOf path}'

View file

@ -4,7 +4,7 @@ msg:
let let
preProcess = v: preProcess = v:
if removeDunders then if removeDunders then
builtins.removeAttrs v [ "__toString" "__functor" ] undunder v
else if asString then else if asString then
toString v toString v
else if asJSON then else if asJSON then
@ -15,11 +15,14 @@ let
a = preProcess actual; a = preProcess actual;
e = preProcess expected; e = preProcess expected;
undunder = v: if builtins.isAttrs v then builtins.removeAttrs v [ "__toString" "__functor" ] else v;
out = (if safeToPrint then builtins.toJSON (undunder actual) else ''{"msg": "refusing to print"}'');
in if (a == e) then '' in if (a == e) then ''
echo 'it ${msg}' echo 'it ${msg}'
'' else '' else
builtins.trace "FAILED ${msg}" builtins.trace builtins.trace "FAILED ${msg}" ''
(if safeToPrint then builtins.toJSON actual else actual) builtins.trace
(if safeToPrint then builtins.toJSON expected else expected) ''
echo FAILED ${msg} echo FAILED ${msg}
echo '${out}' | jq '.'
'' ''

View file

@ -85,13 +85,13 @@ in with site; [
}) })
(it "gets all styles" { (it "gets all styles" {
expected = { expected = {"p"={};"div"={};
"p.class" = { color = "blue"; }; "p.class" = { color = "blue"; };
"a.class2" = { color = "green"; }; "div.class2" = { color = "green"; };
}; };
actual = getStyles (let actual = getStyles (let
p = style.tag "p" "class" { style = { color = "blue"; }; }; p = style.component elems.p "class" { style = { color = "blue"; }; };
g = style.tag "a" "class2" { style = { color = "green"; }; }; g = style.component elems.div "class2" { style = { color = "green"; }; };
in { in {
"index.html" = p ""; "index.html" = p "";
blog = { "index.html" = g ""; }; blog = { "index.html" = g ""; };

View file

@ -1,26 +1,27 @@
let let
style = import ../nixite/style.nix; style = import ../nixite/style.nix;
elems = import ../nixite/elems.nix;
it = import ./it.nix; it = import ./it.nix;
in [ in [
(it "fetches empty style" (let para = (style.tag "p" "para" { }); (it "fetches empty style" (let para = (style.component elems.p "para" { });
in { in {
expected = { "p.para" = { }; }; expected = { "p" = {}; "p.para" = { }; };
actual = style.getStyle (para ""); actual = style.getStyle (para "");
})) }))
(it "fetches style" (let (it "fetches style" (let
attrs = { style = { foo = "bar"; }; }; attrs = { style = { foo = "bar"; }; };
para = (style.tag "p" "para" attrs); para = (style.component elems.p "para" attrs);
in { in {
expected = { "p.para" = attrs.style; }; expected = {"p" = {}; "p.para" = attrs.style; };
actual = style.getStyle (para ""); actual = style.getStyle (para "");
})) }))
(it "appliess class" (let (it "appliess class" (let
attrs = { style = { foo = "bar"; }; }; attrs = { style = { foo = "bar"; }; };
para = (style.tag "p" "para" attrs); para = (style.component elems.p "para" attrs);
in { in {
expected = [ "para" ]; expected = [ "para" ];
actual = (para "").attrs.class; actual = (para "").attrs.class;
@ -31,7 +32,7 @@ in [
style = { foo = "bar"; }; style = { foo = "bar"; };
class = [ "other" "class" ]; class = [ "other" "class" ];
}; };
para = (style.tag "p" "para" attrs); para = (style.component elems.p "para" attrs);
in { in {
expected = [ "para" "other" "class" ]; expected = [ "para" "other" "class" ];
actual = (para "").attrs.class; actual = (para "").attrs.class;
@ -39,32 +40,32 @@ in [
(it "fetches style for class" (let (it "fetches style for class" (let
s = { foo = "bar"; }; s = { foo = "bar"; };
para = (style.tag "p" "para" { style = s; }); para = (style.component elems.p "para" { style = s; });
in { in {
expected = { "p.para" = s; }; expected = { "p" = {}; "p.para" = s; };
actual = style.getStyle (para ""); actual = style.getStyle (para "");
})) }))
(it "fetches style recursively" (let (it "fetches style recursively" (let
s = { s = {"p" = {};"div" = {};
"p.para" = { foo = "bar"; }; "p.para" = { foo = "bar"; };
"a.link" = { this = "that"; }; "div.link" = { this = "that"; };
}; };
para = (style.tag "p" "para" { style = s."p.para"; }); para = (style.component elems.p "para" { style = s."p.para"; });
a = (style.tag "a" "link" { style = s."a.link"; }); div = (style.component elems.div "link" { style = s."div.link"; });
in { in {
expected = s; expected = s;
actual = style.getStyles (para (a "hello")); actual = style.getStyles (para (div "hello"));
removeDunders = true; removeDunders = true;
})) }))
(it "fetches style recursively through lists" (let (it "fetches style recursively through lists" (let
s = { s = {"p" = {};"div" = {};
"p.para" = { foo = "bar"; }; "p.para" = { foo = "bar"; };
"a.link" = { this = "that"; }; "div.link" = { this = "that"; };
}; };
para = (style.tag "p" "para" { style = s."p.para"; }); para = (style.component elems.p "para" { style = s."p.para"; });
a = (style.tag "a" "link" { style = s."a.link"; }); a = (style.component elems.div "link" { style = s."div.link"; });
in { in {
expected = s; expected = s;
actual = style.getStyles (para [ (a "hello") ]); actual = style.getStyles (para [ (a "hello") ]);
@ -72,12 +73,12 @@ in [
})) }))
(it "fetches style recursively with repeats" (let (it "fetches style recursively with repeats" (let
s = { s = {"p" = {};"div" = {};
"p.para" = { foo = "bar"; }; "p.para" = { foo = "bar"; };
"a.link" = { this = "that"; }; "div.link" = { this = "that"; };
}; };
para = (style.tag "p" "para" { style = s."p.para"; }); para = (style.component elems.p "para" { style = s."p.para"; });
a = (style.tag "a" "link" { style = s."a.link"; }); a = (style.component elems.div "link" { style = s."div.link"; });
in { in {
expected = s; expected = s;
actual = style.getStyles (para [ (a "hello") (a "hello") ]); actual = style.getStyles (para [ (a "hello") (a "hello") ]);
@ -85,7 +86,7 @@ in [
})) }))
(it "converts styles to string" (let (it "converts styles to string" (let
s = { s = {"p" = {};
"p.para" = { foo = "bar"; }; "p.para" = { foo = "bar"; };
"a.link" = { this = "that"; }; "a.link" = { this = "that"; };
}; };
@ -104,10 +105,10 @@ in [
(it "extends styled tags" (let (it "extends styled tags" (let
s = { s = {
"p.para" = { foo = "bar"; }; "p.para" = { foo = "bar"; };
"p.para.oof" = { oof = "yes"; }; "p.oof" = { oof = "yes"; };
}; };
para = (style.tag "p" "para" { style = s."p.para"; }); para = (style.component elems.p "para" { style = s."p.para"; });
para2 = (style.extend para "oof" { style = s."p.para.oof"; }); para2 = (style.component para "oof" { style = s."p.oof"; });
in { in {
expected = s; expected = s;
actual = style.getStyles (para2 ""); actual = style.getStyles (para2 "");
@ -115,12 +116,12 @@ in [
})) }))
(it "extends styled tags classes" (let (it "extends styled tags classes" (let
s = { s = {"p" = {}; "div" = {};
"p.para" = { foo = "bar"; }; "p.para" = { foo = "bar"; };
"p.para.oof" = { oof = "yes"; }; "p.para.oof" = { oof = "yes"; };
}; };
para = (style.tag "p" "para" { style = s."p.para"; }); para = (style.component elems.p "para" { style = s."p.para"; });
para2 = (style.extend para "oof" { style = s."p.para.oof"; }); para2 = (style.component para "oof" { style = s."p.para.oof"; });
in { in {
expected = [ "para" "oof" ]; expected = [ "para" "oof" ];
actual = (para2 "").attrs.class; actual = (para2 "").attrs.class;