nixfiles/libs/web/lib/html/default.nix
Sebastian Walz 860d31cee1
Tohu vaBohu
2023-04-21 00:22:52 +02:00

230 lines
10 KiB
Nix

{ core, ... }:
let
inherit(core) debug indentation list path set string type;
flat
= body:
type.matchPrimitiveOrPanic body
{
list = list.concatMap flat body;
null = [];
set
= if Tag.isInstanceOf body
then
Tag.flat body
else
[ body ];
string = [ body ];
};
formatHead
= {
_blank ? null,
_parent ? null,
_self ? null,
_top ? null,
application-name ? null,
author ? null,
charset ? "UTF-8",
content-security-policy ? null,
content-type ? null,
default-style ? null,
description ? null,
generator ? null,
icon ? null,
keywords ? null,
refresh ? null,
script ? null,
scripts ? [],
style ? null,
stylesheets ? {},
title ? null,
viewport ? null,
}:
let
inherit(list) ifOrEmpty;
link
= rel:
set.mapToList
(
href:
{ ... } @ attrs:
tags.link (attrs // { inherit href rel; })
);
in
flat
([]
++ (ifOrEmpty (_blank != null) (tags.base { target = "_blank"; href = _blank; } ))
++ (ifOrEmpty (_parent != null) (tags.base { target = "_parent"; href = _parent; } ))
++ (ifOrEmpty (_self != null) (tags.base { target = "_self"; href = _self; } ))
++ (ifOrEmpty (_top != null) (tags.base { target = "_top"; href = _top; } ))
++ (ifOrEmpty (icon != null) (tags.link ( icon // { rel = "icon"; } ) ))
++ (link "stylesheet" stylesheets)
++ (ifOrEmpty (charset != null) (tags.meta { inherit charset; } ))
++ (ifOrEmpty (application-name != null) (tags.meta { name = "application-name"; content = application-name; } ))
++ (ifOrEmpty (author != null) (tags.meta { name = "author"; content = author; } ))
++ (ifOrEmpty (content-security-policy != null) (tags.meta { http-equiv = "content-security-policy"; content = content-security-policy; } ))
++ (ifOrEmpty (content-type != null) (tags.meta { http-equiv = "content-type"; content = content-type; } ))
++ (ifOrEmpty (default-style != null) (tags.meta { http-equiv = "default-style"; content = default-style; } ))
++ (ifOrEmpty (description != null) (tags.meta { name = "description"; content = description; } ))
++ (ifOrEmpty (generator != null) (tags.meta { name = "generator"; content = generator; } ))
++ (ifOrEmpty (keywords != null) (tags.meta { name = "keywords"; content = keywords; } ))
++ (ifOrEmpty (refresh != null) (tags.meta { http-equiv = "refresh"; content = refresh; } ))
++ (ifOrEmpty (viewport != null) (tags.meta { name = "viewport"; content = viewport; } ))
++ (list.map (src: tags.script { inherit src; } null) scripts)
++ (ifOrEmpty (script != null) (tags.script script ))
++ (ifOrEmpty (style != null) (tags.style style ))
++ (ifOrEmpty (title != null) (tags.title title ))
);
formatHead'
= head:
type.matchPrimitiveOrPanic head
{
list = flat head;
set = formatHead head;
};
tags
= set.map
(
name:
{ ... } @ data:
data // (Tag name)
)
(path.import ./tags.nix);
Comment
= body:
if list.isInstanceOf body
then
[ "<!--" indentation.more ]
++ (flat body)
++ [ indentation.less "-->" ]
else
[ "<!-- ${body} -->" ];
HTML
= type "HTML"
{
from
= { language ? null, ... }:
HTML.instanciate
{
inherit language;
lines = [ "<!DOCTYPE html>" ];
__functor
= { language, lines, ... } @ self:
{ head ? {}, body ? [] }:
self
// {
__functor = null;
lines
= lines
++ (
flat
(
tags.html
(
if language != null
then
{ lang = language; }
else
{}
)
[
(tags.head (formatHead head))
(tags.body (flat body))
]
)
);
};
__toString
= { lines, ... }:
indentation {} lines;
};
};
Tag
= let
mapAttributes
= string.concatMapped'
(
name:
value:
if value != null
then
" ${name}=\"${string value}\""
else
" ${name}"
);
in
type "Tag"
{
flat
= { attributes, body, name, ... } @ tag:
if list.isInstanceOf body
then
[ "<${name}${mapAttributes attributes}>" indentation.more ]
++ (flat body)
++ [ indentation.less "</${name}>" ]
else
[ "${tag}" ];
from
= name:
Tag.instanciate
{
inherit name;
attributes = {};
body = null;
expectBody = false;
__functor
= { attributes, body, expectBody, name, ... } @ self:
attrsOrBody:
let
isAttributes
= !expectBody
&& set.isInstanceOf attrsOrBody
&& attrsOrBody.__type__ or null == null;
in
self
// (
if isAttributes then { attributes = attrsOrBody; }
else if attrsOrBody == null then {}
else if body == null then { body = attrsOrBody; }
else if string.isInstanceOf body then { body = "${body}${string attrsOrBody}"; }
else
debug.panic [ "Tag" "__functor" ]
{
text = "Cannot extend body:";
data = body;
}
null
)
// { expectBody = true; };
__toString
= { attributes, body, name, ... } @ self:
type.matchPrimitiveOrPanic body
{
null = "<${name}${mapAttributes attributes} />";
list
= indentation {}
(
[ "<${name}${mapAttributes attributes}>" indentation.more ]
++ (flat body)
++ [ indentation.less "</${name}>" ]
);
string = "<${name}${mapAttributes attributes}>${body}</${name}>";
};
};
};
in
HTML
// { inherit Comment HTML Tag tags; }
// ( tags )