From cd32a3ca0173676ed31486b5fa4dbd8c744cdec5 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Thu, 28 May 2026 16:30:26 +0200 Subject: implement frontend framework --- src/html/Element.zig | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/html/root.zig | 7 ++++ 2 files changed, 111 insertions(+) create mode 100644 src/html/Element.zig create mode 100644 src/html/root.zig (limited to 'src/html') diff --git a/src/html/Element.zig b/src/html/Element.zig new file mode 100644 index 0000000..8ecf31e --- /dev/null +++ b/src/html/Element.zig @@ -0,0 +1,104 @@ +const Self = @This(); + +pub const Content = union(enum) { + elements: []const Self, + string: []const u8, + void: void, + + pub fn content(string: []const u8) @This() { + return .{ .string = string }; + } + + pub fn children(elements: []const Self) @This() { + return .{ .elements = elements }; + } +}; + +pub const Attribute = struct { + name: []const u8, + value: []const u8, +}; + +tag: []const u8, +attributes: []const Attribute, +content: Content, + +fn attributesFrom(attributes: anytype) []const Attribute { + const T = @TypeOf(attributes); + const info = @typeInfo(T); + + if (info != .@"struct") { + @compileError("struct needed"); + } + + const fields = info.@"struct".fields; + + var attrs: []const Attribute = &.{}; + + for (fields) |field| { + const name = field.name; + const value = @field(attributes, name); + + const next_attr: []const Attribute = &.{ .{ .name = name, .value = value } }; + attrs = attrs ++ next_attr; + } + + return attrs; +} + +pub fn native(name: @EnumLiteral(), attrs: anytype, content: Content) Self { + return .{ + .tag = @tagName(name), + .attributes = attributesFrom(attrs), + .content = content, + }; +} + +pub fn transparent(content: Content) Self { + return .{ + .tag = "", + .attributes = &.{}, + .content = content, + }; +} + +pub fn toHtml(comptime self: Self) []const u8 { + var string: []const u8 = string: { + if (self.tag.len == 0) { + break :string ""; + } + + var string: []const u8 = "<" ++ self.tag; + + inline for (self.attributes) |attr| { + string = string ++ " " ++ attr.name ++ "='" ++ attr.value ++ "'"; + } + + string = string ++ ">"; + + break :string string; + }; + + + switch (self.content) { + .void => {}, + .string => |s| { + string = string ++ s ++ ""; + }, + .elements => |els| { + for (els) |el| { + string = string ++ el.toHtml(); + } + + string = string ++ ""; + } + } + + string = string ++ if (self.tag.len == 0) "" else ""; + + return string; +} + +pub fn toDocument(comptime self: Self) []const u8 { + return "" ++ self.toHtml(); +} diff --git a/src/html/root.zig b/src/html/root.zig new file mode 100644 index 0000000..14a9b3c --- /dev/null +++ b/src/html/root.zig @@ -0,0 +1,7 @@ +const std = @import("std"); + +pub const Element = @import("Element.zig"); + +test { + _ = std.testing.refAllDecls(@This()); +} -- cgit v1.2.3-70-g09d2