feat: enhance Html and Http stdlib modules
Html: add RawHtml, Attribute, meta/link/script/iframe/figure/figcaption elements, attr() helper, rawHtml() helper, seoDocument() for SEO meta tags, fix document() to use Attribute instead of DataAttr for standard HTML attributes. Http: add serveStaticFile(), parseFormBody(), getFormField(), sendResponse() convenience helpers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
pub type Html<M> =
|
||||
| Element(String, List<Attr<M>>, List<Html<M>>)
|
||||
| Text(String)
|
||||
| RawHtml(String)
|
||||
| Empty
|
||||
|
||||
// Attributes that can be applied to elements
|
||||
@@ -41,6 +42,7 @@ pub type Attr<M> =
|
||||
| OnKeyDown(fn(String): M)
|
||||
| OnKeyUp(fn(String): M)
|
||||
| DataAttr(String, String)
|
||||
| Attribute(String, String)
|
||||
|
||||
// ============================================================================
|
||||
// Element builders - Container elements
|
||||
@@ -180,6 +182,28 @@ pub fn video<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
||||
pub fn audio<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
||||
Element("audio", attrs, children)
|
||||
|
||||
// ============================================================================
|
||||
// Element builders - Document / Head elements
|
||||
// ============================================================================
|
||||
|
||||
pub fn meta<M>(attrs: List<Attr<M>>): Html<M> =
|
||||
Element("meta", attrs, [])
|
||||
|
||||
pub fn link<M>(attrs: List<Attr<M>>): Html<M> =
|
||||
Element("link", attrs, [])
|
||||
|
||||
pub fn script<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
||||
Element("script", attrs, children)
|
||||
|
||||
pub fn iframe<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
||||
Element("iframe", attrs, children)
|
||||
|
||||
pub fn figure<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
||||
Element("figure", attrs, children)
|
||||
|
||||
pub fn figcaption<M>(attrs: List<Attr<M>>, children: List<Html<M>>): Html<M> =
|
||||
Element("figcaption", attrs, children)
|
||||
|
||||
// ============================================================================
|
||||
// Element builders - Tables
|
||||
// ============================================================================
|
||||
@@ -285,6 +309,12 @@ pub fn onKeyUp<M>(h: fn(String): M): Attr<M> =
|
||||
pub fn data<M>(name: String, value: String): Attr<M> =
|
||||
DataAttr(name, value)
|
||||
|
||||
pub fn attr<M>(name: String, value: String): Attr<M> =
|
||||
Attribute(name, value)
|
||||
|
||||
pub fn rawHtml<M>(content: String): Html<M> =
|
||||
RawHtml(content)
|
||||
|
||||
// ============================================================================
|
||||
// Utility functions
|
||||
// ============================================================================
|
||||
@@ -319,6 +349,7 @@ pub fn renderAttr<M>(attr: Attr<M>): String =
|
||||
Checked(false) => "",
|
||||
Name(n) => " name=\"" + n + "\"",
|
||||
DataAttr(name, value) => " data-" + name + "=\"" + value + "\"",
|
||||
Attribute(name, value) => " " + name + "=\"" + value + "\"",
|
||||
// Event handlers are ignored in static rendering
|
||||
OnClick(_) => "",
|
||||
OnInput(_) => "",
|
||||
@@ -355,6 +386,7 @@ pub fn render<M>(html: Html<M>): String =
|
||||
}
|
||||
},
|
||||
Text(content) => escapeHtml(content),
|
||||
RawHtml(content) => content,
|
||||
Empty => ""
|
||||
}
|
||||
|
||||
@@ -368,15 +400,47 @@ pub fn escapeHtml(s: String): String = {
|
||||
s4
|
||||
}
|
||||
|
||||
// Render a full HTML document
|
||||
// Render a full HTML document (basic)
|
||||
pub fn document(title: String, headExtra: List<Html<M>>, bodyContent: List<Html<M>>): String = {
|
||||
let headElements = List.concat([
|
||||
[Element("meta", [DataAttr("charset", "UTF-8")], [])],
|
||||
[Element("meta", [Name("viewport"), Value("width=device-width, initial-scale=1.0")], [])],
|
||||
[Element("meta", [Attribute("charset", "UTF-8")], [])],
|
||||
[Element("meta", [Name("viewport"), Attribute("content", "width=device-width, initial-scale=1.0")], [])],
|
||||
[Element("title", [], [Text(title)])],
|
||||
headExtra
|
||||
])
|
||||
let doc = Element("html", [DataAttr("lang", "en")], [
|
||||
let doc = Element("html", [Attribute("lang", "en")], [
|
||||
Element("head", [], headElements),
|
||||
Element("body", [], bodyContent)
|
||||
])
|
||||
"<!DOCTYPE html>\n" + render(doc)
|
||||
}
|
||||
|
||||
// Render a full HTML document with SEO meta tags
|
||||
pub fn seoDocument(
|
||||
title: String,
|
||||
description: String,
|
||||
url: String,
|
||||
ogImage: String,
|
||||
headExtra: List<Html<M>>,
|
||||
bodyContent: List<Html<M>>
|
||||
): String = {
|
||||
let headElements = List.concat([
|
||||
[Element("meta", [Attribute("charset", "UTF-8")], [])],
|
||||
[Element("meta", [Name("viewport"), Attribute("content", "width=device-width, initial-scale=1.0")], [])],
|
||||
[Element("title", [], [Text(title)])],
|
||||
[Element("meta", [Name("description"), Attribute("content", description)], [])],
|
||||
[Element("meta", [Attribute("property", "og:title"), Attribute("content", title)], [])],
|
||||
[Element("meta", [Attribute("property", "og:description"), Attribute("content", description)], [])],
|
||||
[Element("meta", [Attribute("property", "og:type"), Attribute("content", "website")], [])],
|
||||
[Element("meta", [Attribute("property", "og:url"), Attribute("content", url)], [])],
|
||||
[Element("meta", [Attribute("property", "og:image"), Attribute("content", ogImage)], [])],
|
||||
[Element("meta", [Name("twitter:card"), Attribute("content", "summary_large_image")], [])],
|
||||
[Element("meta", [Name("twitter:title"), Attribute("content", title)], [])],
|
||||
[Element("meta", [Name("twitter:description"), Attribute("content", description)], [])],
|
||||
[Element("link", [Attribute("rel", "canonical"), Href(url)], [])],
|
||||
headExtra
|
||||
])
|
||||
let doc = Element("html", [Attribute("lang", "en")], [
|
||||
Element("head", [], headElements),
|
||||
Element("body", [], bodyContent)
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user