aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/web/component/navigation/index.css66
-rw-r--r--src/web/component/navigation/index.html11
-rw-r--r--src/web/component/navigation/index.js11
-rw-r--r--src/web/component/navigation/root.zig8
-rw-r--r--src/web/component/root.zig1
-rw-r--r--src/web/index.css1
-rw-r--r--src/web/index.html2
-rw-r--r--src/z/js/parser/builtin.zig17
-rw-r--r--src/z/js/parser/lexical_grammar.zig3
-rw-r--r--src/z/js/parser/parser.zig117
-rw-r--r--src/z/js/parser/result.zig4
-rw-r--r--src/z/js/parser/root.zig11
-rw-r--r--src/z/z.js21
13 files changed, 272 insertions, 1 deletions
diff --git a/src/web/component/navigation/index.css b/src/web/component/navigation/index.css
new file mode 100644
index 0000000..836e3b5
--- /dev/null
+++ b/src/web/component/navigation/index.css
@@ -0,0 +1,66 @@
+#toggle {
+ --radius-open: 200px;
+
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ width: 45px;
+ height: 45px;
+ border-radius: 100%;
+ background: red;
+ z-index: 500;
+ cursor: pointer;
+ text-align: center;
+ align-content: center;
+ transition:
+ border-radius 0.1s ease,
+ right 0.1s ease,
+ bottom 0.1s ease,
+ width 0.1s ease,
+ height 0.1s ease;
+
+ .bi {
+ baseline-shift: -2px;
+ }
+
+ .items {
+ position: relative;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ }
+
+ .item {
+ display: none;
+ width: 45px;
+ height: 45px;
+ border-radius: 100%;
+ background: blue;
+ position: absolute;
+ right: calc(cos(18deg * var(--index)) * var(--radius-open));
+ bottom: calc(sin(18deg * var(--index)) * var(--radius-open));
+ font-size: calc(var(--index) * 1px);
+ }
+
+ .item {
+ --index: sibling-index();
+ }
+}
+
+#toggle.open {
+ width: var(--radius-open);
+ height: var(--radius-open);
+ bottom: 0px;
+ right: 0px;
+ border-radius: 0px;
+ border-top-left-radius: 250px;
+
+ .bi {
+ display: none;
+ }
+
+ .item {
+ display: block;
+ }
+}
diff --git a/src/web/component/navigation/index.html b/src/web/component/navigation/index.html
new file mode 100644
index 0000000..b3b5ce0
--- /dev/null
+++ b/src/web/component/navigation/index.html
@@ -0,0 +1,11 @@
+<div id="toggle">
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-three-dots-vertical" viewBox="0 0 16 16">
+ <path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
+ </svg>
+ <div class="items">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ </div>
+</div>
diff --git a/src/web/component/navigation/index.js b/src/web/component/navigation/index.js
new file mode 100644
index 0000000..2c996a0
--- /dev/null
+++ b/src/web/component/navigation/index.js
@@ -0,0 +1,11 @@
+
+const toggle = Z.by_id('toggle');
+
+toggle.onclick = (event) => {
+ toggle.classList.toggle('open');
+ event.stopPropagation();
+};
+
+document.addEventListener('click', () => {
+ toggle.classList.remove('open');
+});
diff --git a/src/web/component/navigation/root.zig b/src/web/component/navigation/root.zig
new file mode 100644
index 0000000..919cddb
--- /dev/null
+++ b/src/web/component/navigation/root.zig
@@ -0,0 +1,8 @@
+const z = @import("z");
+
+pub const component: z.Component = .{
+ .name = .navigation,
+ .body = @embedFile("index.html"),
+ .style = @embedFile("index.css"),
+ .script = @embedFile("index.js"),
+};
diff --git a/src/web/component/root.zig b/src/web/component/root.zig
index 226f3f7..ba2f565 100644
--- a/src/web/component/root.zig
+++ b/src/web/component/root.zig
@@ -3,4 +3,5 @@ const z = @import("z");
pub const components: []const z.Component = &.{
@import("image/root.zig").component,
@import("timeline/root.zig").component,
+ @import("navigation/root.zig").component,
};
diff --git a/src/web/index.css b/src/web/index.css
index 6b1927f..bcdecae 100644
--- a/src/web/index.css
+++ b/src/web/index.css
@@ -1,6 +1,7 @@
html {
height: 100vh;
width: 100vw;
+ overflow: hidden;
}
body {
diff --git a/src/web/index.html b/src/web/index.html
index d2bc6fe..bcbb165 100644
--- a/src/web/index.html
+++ b/src/web/index.html
@@ -1,2 +1,4 @@
<z-component type="timeline" id="timeline">
</z-component>
+<z-component type="navigation" id="navigation">
+</z-component>
diff --git a/src/z/js/parser/builtin.zig b/src/z/js/parser/builtin.zig
new file mode 100644
index 0000000..fc96fc3
--- /dev/null
+++ b/src/z/js/parser/builtin.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+
+const parser = @import("root.zig");
+const Parser = parser.Parser;
+const Result = parser.Result;
+
+pub fn literal(lit: []const u8) Parser(void) {
+ return .fromType(struct {
+ pub fn parse(buffer: []const u8) !Result(void) {
+ if (std.mem.startsWith(u8, buffer, lit)) {
+ return .{ void{}, buffer[lit.len..] };
+ }
+
+ return error.Literal;
+ }
+ });
+}
diff --git a/src/z/js/parser/lexical_grammar.zig b/src/z/js/parser/lexical_grammar.zig
new file mode 100644
index 0000000..7060766
--- /dev/null
+++ b/src/z/js/parser/lexical_grammar.zig
@@ -0,0 +1,3 @@
+const parser = @import("root.zig");
+const Result = parser.Result;
+
diff --git a/src/z/js/parser/parser.zig b/src/z/js/parser/parser.zig
new file mode 100644
index 0000000..b73d895
--- /dev/null
+++ b/src/z/js/parser/parser.zig
@@ -0,0 +1,117 @@
+const std = @import("std");
+
+const mod = @import("root.zig");
+const Result = mod.Result;
+
+pub fn Parser(T: type) type {
+ const R = Result(T);
+
+ return struct {
+ parse: *const fn ([]const u8) anyerror!R,
+
+ pub fn parseAll(self: *const @This(), buffer: []const u8) !T {
+ const value, const next = try self.parse(buffer);
+
+ if (next.len > 0) {
+ return error.NotExhaustive;
+ }
+
+ return value;
+ }
+
+ pub fn fromType(P: type) @This() {
+ return .{ .parse = P.parse };
+ }
+
+ pub fn variants(V: type) @This() {
+ if (@typeInfo(T) != .@"union") {
+ @compileError("variants is only allowed with an union as result type");
+ }
+
+ return .{
+ .parse = struct {
+ fn parse(buffer: []const u8) !R {
+ inline for (std.meta.fields(T)) |field| {
+ if (@field(V, field.name).parse(buffer)) |result| {
+ const value, const next = result;
+ return .{
+ @unionInit(T, field.name, value),
+ next,
+ };
+ } else |_| { }
+ }
+
+ return error.Variants;
+ }
+ }.parse,
+ };
+ }
+
+ pub fn any(P: type, comptime parsers: []const struct { Parser(P), T }) @This() {
+ return .{
+ .parse = struct {
+ fn parse(buffer: []const u8) !R {
+ inline for (parsers) |pair| {
+ const parser, const value = pair;
+
+ if (parser.parse(buffer)) |result| {
+ _, const next = result;
+ return .{ value, next };
+ } else |_| { }
+ }
+ return error.Any;
+ }
+ }.parse,
+ };
+ }
+ };
+}
+
+test "variants" {
+ const Char = union(enum) {
+ upper: u8,
+ lower: u8,
+ space: void,
+ };
+
+ const parser: Parser(Char) = .variants(struct {
+ const upper: Parser(u8) = .any(void, &.{
+ .{ mod.builtin.literal("A"), 'A' },
+ .{ mod.builtin.literal("B"), 'B' },
+ });
+
+ const lower: Parser(u8) = .any(void, &.{
+ .{ mod.builtin.literal("a"), 'a' },
+ .{ mod.builtin.literal("b"), 'b' },
+ });
+
+ const space: Parser(void) = mod.builtin.literal(" ");
+ });
+
+ {
+ const result = try parser.parseAll("a");
+ try std.testing.expect(result.lower == 'a');
+ }
+
+ {
+ const result = try parser.parseAll("b");
+ try std.testing.expect(result.lower == 'b');
+ }
+
+ {
+ const result = try parser.parseAll("A");
+ try std.testing.expect(result.upper == 'A');
+ }
+
+ {
+ const result = try parser.parseAll("B");
+ try std.testing.expect(result.upper == 'B');
+ }
+
+ {
+ const result = try parser.parseAll(" ");
+ try std.testing.expect(result == .space);
+ }
+
+ try std.testing.expect(parser.parseAll("c") == error.Variants);
+}
diff --git a/src/z/js/parser/result.zig b/src/z/js/parser/result.zig
new file mode 100644
index 0000000..5cc5878
--- /dev/null
+++ b/src/z/js/parser/result.zig
@@ -0,0 +1,4 @@
+
+pub fn Result(T: type) type {
+ return struct { T, []const u8 };
+}
diff --git a/src/z/js/parser/root.zig b/src/z/js/parser/root.zig
new file mode 100644
index 0000000..a6be9ca
--- /dev/null
+++ b/src/z/js/parser/root.zig
@@ -0,0 +1,11 @@
+const std = @import("std");
+
+pub const lexical_grammar = @import("lexical_grammar.zig");
+
+pub const Result = @import("result.zig").Result;
+pub const Parser = @import("parser.zig").Parser;
+pub const builtin = @import("builtin.zig");
+
+test {
+ _ = std.testing.refAllDecls(@This());
+}
diff --git a/src/z/z.js b/src/z/z.js
index ba07b9e..e279f42 100644
--- a/src/z/z.js
+++ b/src/z/z.js
@@ -1,10 +1,14 @@
class ZComponent extends HTMLElement {
+ static observedAttributes = [ 'type' ];
+
constructor() {
super();
this.attachShadow({ mode: 'open' });
+ this.onconnected = () => {};
+ this.ondisconnected = () => {};
}
- connectedCallback() {
+ build() {
const type = this.getAttribute('type');
if (type == null) {
@@ -22,6 +26,21 @@ class ZComponent extends HTMLElement {
Zenv.constructors[`component__${type}`](new Zenv(this));
}
+
+ attributeChangedCallback(name, previous, current) {
+ if (name == 'type') {
+ this.shadowRoot.innerHTML = '';
+ this.build()
+ }
+ }
+
+ connectedCallback() {
+ this.onconnected();
+ }
+
+ disconnectedCallback() {
+ this.ondisconnected();
+ }
}
class Zenv {