lib/zen/src/slides/deck.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const zen = @import("../root.zig");
4 const print = @import("print.zig");
5 const script = @import("script.zig");
6
7 const Allocator = std.mem.Allocator;
8
9 pub const Options = struct {
10 deck_class: []const u8 = "zen-slides",
11 slide_class: []const u8 = "zen-slide",
12 frame_class: []const u8 = "zen-slide-frame",
13 badge_class: []const u8 = "zen-slide-badge",
14 id_prefix: []const u8 = "slide",
15 badge: ?[]const u8 = null,
16 runtime: bool = true,
17 };
18
19 pub const Deck = struct {
20 options: Options = .{},
21 open: bool = false,
22 slide_index: usize = 0,
23 slide_open_start: usize = 0,
24 slide_body_start: usize = 0,
25
26 pub fn begin(self: *Deck, out: *std.ArrayList(u8), allocator: Allocator) Allocator.Error!void {
27 if (self.open) return;
28 try out.appendSlice(allocator, "<div class=\"");
29 try zen.html.appendAttributeEscaped(out, allocator, self.options.deck_class);
30 try out.appendSlice(allocator, "\" data-zen-slides>\n");
31 self.open = true;
32 try self.openSlide(out, allocator);
33 }
34
35 pub fn split(self: *Deck, out: *std.ArrayList(u8), allocator: Allocator) Allocator.Error!void {
36 if (!self.open) try self.begin(out, allocator);
37 if (self.empty(out)) return;
38 try out.appendSlice(allocator, "</div>\n</section>\n");
39 try self.openSlide(out, allocator);
40 }
41
42 pub fn finish(self: *Deck, out: *std.ArrayList(u8), allocator: Allocator) Allocator.Error!void {
43 if (!self.open) return;
44 if (self.empty(out) and self.slide_index > 1) {
45 out.shrinkRetainingCapacity(self.slide_open_start);
46 } else {
47 try out.appendSlice(allocator, "</div>\n</section>\n");
48 }
49 try out.appendSlice(allocator, "</div>\n");
50 try out.appendSlice(allocator, print.stylesheet);
51 if (self.options.runtime) {
52 try out.appendSlice(allocator, script.runtime);
53 }
54 const options = self.options;
55 self.* = .{ .options = options };
56 }
57
58 fn openSlide(self: *Deck, out: *std.ArrayList(u8), allocator: Allocator) Allocator.Error!void {
59 self.slide_index += 1;
60 self.slide_open_start = out.items.len;
61 try out.appendSlice(allocator, "<section class=\"");
62 try zen.html.appendAttributeEscaped(out, allocator, self.options.slide_class);
63 try out.appendSlice(allocator, "\" id=\"");
64 try zen.html.appendAttributeEscaped(out, allocator, self.options.id_prefix);
65 try out.append(allocator, '-');
66 try appendUsize(out, allocator, self.slide_index);
67 try out.appendSlice(allocator, "\" aria-label=\"Slide ");
68 try appendUsize(out, allocator, self.slide_index);
69 try out.appendSlice(allocator, "\">\n");
70 try out.appendSlice(allocator, "<div class=\"");
71 try zen.html.appendAttributeEscaped(out, allocator, self.options.frame_class);
72 try out.appendSlice(allocator, "\">\n");
73 if (self.options.badge) |badge| {
74 try out.appendSlice(allocator, "<span class=\"");
75 try zen.html.appendAttributeEscaped(out, allocator, self.options.badge_class);
76 try out.appendSlice(allocator, "\" aria-label=\"Non-curated content\">");
77 try zen.html.appendEscaped(out, allocator, badge);
78 try out.appendSlice(allocator, "</span>\n");
79 }
80 self.slide_body_start = out.items.len;
81 }
82
83 fn empty(self: Deck, out: *const std.ArrayList(u8)) bool {
84 return out.items.len == self.slide_body_start;
85 }
86 };
87
88 fn appendUsize(out: *std.ArrayList(u8), allocator: Allocator, value: usize) Allocator.Error!void {
89 var buffer: [20]u8 = undefined;
90 const text = std.fmt.bufPrint(&buffer, "{d}", .{value}) catch unreachable;
91 try out.appendSlice(allocator, text);
92 }