diff --git a/src/main/java/me/mrletsplay/nojs/NoJS.java b/src/main/java/me/mrletsplay/nojs/NoJS.java index 9538077..9aab75c 100644 --- a/src/main/java/me/mrletsplay/nojs/NoJS.java +++ b/src/main/java/me/mrletsplay/nojs/NoJS.java @@ -7,15 +7,6 @@ import org.slf4j.helpers.NOPLogger; import me.mrletsplay.nojs._test.DynPage; import me.mrletsplay.nojs._test.LinksPage; -import me.mrletsplay.nojs._test.TestPage; -import me.mrletsplay.nojs.action.Action; -import me.mrletsplay.nojs.action.ActionData; -import me.mrletsplay.nojs.component.impl.Button; -import me.mrletsplay.nojs.component.impl.Button.Style; -import me.mrletsplay.nojs.component.impl.Form; -import me.mrletsplay.nojs.component.impl.Link; -import me.mrletsplay.nojs.component.impl.TextInput; -import me.mrletsplay.nojs.page.StaticPage; import me.mrletsplay.simplehttpserver.dom.html.HtmlDocument; import me.mrletsplay.simplehttpserver.dom.html.HtmlElement; import me.mrletsplay.simplehttpserver.dom.html.element.HtmlButton; @@ -67,19 +58,6 @@ public class NoJS { // new TestEndpoint().register(httpServer.getDocumentProvider()); - StaticPage test = new StaticPage() - .add(new Button().text("Click me!").action(new Action("poopy").extraData(new ActionData().put("test", "abc"))).style(Style.LINK)) - .add(new Link().text("Click me!").href("#")) - .add(new Form() - .add(new TextInput("username").placeholder("Username")) - .add(new TextInput("password").placeholder("Password")) - .add(new Button().text("Login")) - .add(new Button().text("Can't click here").enabled(false)) - .action(new Action("beans")) - ); - test.register(httpServer.getDocumentProvider(), "/test"); - - new TestPage().register(httpServer.getDocumentProvider(), "/test2"); new DynPage().register(httpServer.getDocumentProvider(), "/dyn"); new LinksPage().register(httpServer.getDocumentProvider(), "/links"); @@ -94,6 +72,7 @@ public class NoJS { HttpRequestContext ctx = HttpRequestContext.getCurrentContext(); ctx.getServerHeader().setContent(MimeType.CSS, in.readAllBytes()); } catch(IOException ex) { + // TODO: error handling throw new RuntimeException(ex); } }); diff --git a/src/main/java/me/mrletsplay/nojs/_test/DynPage.java b/src/main/java/me/mrletsplay/nojs/_test/DynPage.java index 2c419f8..6a21736 100644 --- a/src/main/java/me/mrletsplay/nojs/_test/DynPage.java +++ b/src/main/java/me/mrletsplay/nojs/_test/DynPage.java @@ -1,7 +1,6 @@ package me.mrletsplay.nojs._test; import java.util.Date; -import java.util.List; import me.mrletsplay.nojs.action.Action; import me.mrletsplay.nojs.component.Component; @@ -12,26 +11,20 @@ import me.mrletsplay.nojs.component.impl.Text; import me.mrletsplay.nojs.page.Page; import me.mrletsplay.nojs.page.layout.GridLayout; import me.mrletsplay.nojs.page.layout.InsetLayout; -import me.mrletsplay.nojs.page.layout.Layout; public class DynPage implements Page { @Override - public List getComponents() { - return List.of( + public Component getContent() { + return new Group().add( new Text().text(new Date().toString()), new Form() .action(Action.empty()) - .layout(new InsetLayout(GridLayout.ofColumns(1), "100px")) + .layout(new InsetLayout(GridLayout.ofColumns(1), "10px")) .add(new Button().text("Refresh")), new Group() .add(new Text().text("Your name is")) - ); - } - - @Override - public Layout getLayout() { - return GridLayout.ofColumns(1); + ).layout(GridLayout.ofColumns(1)); } } diff --git a/src/main/java/me/mrletsplay/nojs/_test/LinksPage.java b/src/main/java/me/mrletsplay/nojs/_test/LinksPage.java index 9eb5023..99a8190 100644 --- a/src/main/java/me/mrletsplay/nojs/_test/LinksPage.java +++ b/src/main/java/me/mrletsplay/nojs/_test/LinksPage.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import me.mrletsplay.nojs.action.Action; +import me.mrletsplay.nojs.action.ActionData; import me.mrletsplay.nojs.action.ActionEvent; import me.mrletsplay.nojs.action.ActionHandler; import me.mrletsplay.nojs.component.Component; @@ -16,7 +17,7 @@ import me.mrletsplay.nojs.component.impl.Text; import me.mrletsplay.nojs.component.impl.TextInput; import me.mrletsplay.nojs.page.Page; import me.mrletsplay.nojs.page.layout.GridLayout; -import me.mrletsplay.nojs.page.layout.Layout; +import me.mrletsplay.nojs.page.layout.HClampLayout; public class LinksPage implements Page { @@ -24,13 +25,28 @@ public class LinksPage implements Page { private String error; @Override - public List getComponents() { + public Component getContent() { Group linksGroup = new Group() .layout(GridLayout.ofColumns(1) - .gap("2px")); + .gap("4px")); for(String s : links) { - linksGroup.add(new Link().href(s).text(s)); + linksGroup.add( + new Form().add( + new Link() + .href(s) + .text(s) + .target(Link.Target.BLANK), + new Button() + .text("Delete") + ) + .action(new Action("removeLink") + .extraData(new ActionData().put("link", s))) + .layout(new GridLayout() + .columns("1fr", "auto") + .gap("4px") + .vAlignment(GridLayout.VAlignment.START)) + ); } if(links.isEmpty()) { @@ -38,34 +54,39 @@ public class LinksPage implements Page { } if(error != null) { - linksGroup.add(new Message().text(error)); + linksGroup.add(new Message() + .text(error) + .type(Message.Type.ERROR)); error = null; } - return List.of( - linksGroup, - new Form() - .add(new Text().text("Add a new link:")) - .add(new Group() - .add(new TextInput("link").placeholder("Insert link here")) - .add(new Button().text("Add Link")) - .layout(new GridLayout().columns("1fr", "auto").gap("4px")) + return new Group() + .add( + new Group().add( + linksGroup, + new Form().add( + new Text().text("Add a new link:"), + new Group().add( + new TextInput("linkName").placeholder("Link Name"), + new TextInput("link").placeholder("Insert link here"), + new Button().text("Add Link") + ) + .layout(GridLayout.ofColumns(1) + .gap("4px")) + ) + .layout(GridLayout.ofColumns(1).gap("4px")) + .action(new Action("addLink")) ) - .layout(GridLayout.ofColumns(1).gap("4px")) - .action(new Action("addLink")) - ); - } - - @Override - public Layout getLayout() { - return GridLayout.ofColumns(1) - .gap("10px"); + .layout(GridLayout.ofColumns(1).gap("10px")) + ) + .layout(new HClampLayout("200px", "100%", "800px") + .hAlignment(HClampLayout.HAlignment.CENTER)); } @ActionHandler("addLink") public void addLink(ActionEvent event) { String link = event.getData("link"); - if(link == null || link.isBlank()) { + if(link == null || link.isBlank() || (!link.startsWith("http://") && !link.startsWith("https://"))) { error = "Invalid link"; return; } @@ -73,4 +94,10 @@ public class LinksPage implements Page { links.add(event.getData("link")); } + @ActionHandler("removeLink") + public void removeLink(ActionEvent event) { + String link = event.getData("link"); + links.remove(link); + } + } diff --git a/src/main/java/me/mrletsplay/nojs/_test/TestPage.java b/src/main/java/me/mrletsplay/nojs/_test/TestPage.java deleted file mode 100644 index 9e67848..0000000 --- a/src/main/java/me/mrletsplay/nojs/_test/TestPage.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.mrletsplay.nojs._test; - -import me.mrletsplay.nojs.action.Action; -import me.mrletsplay.nojs.action.ActionEvent; -import me.mrletsplay.nojs.action.ActionHandler; -import me.mrletsplay.nojs.component.impl.Button; -import me.mrletsplay.nojs.component.impl.Form; -import me.mrletsplay.nojs.component.impl.Link; -import me.mrletsplay.nojs.component.impl.TextInput; -import me.mrletsplay.nojs.page.StaticPage; -import me.mrletsplay.nojs.page.layout.GridLayout; - -public class TestPage extends StaticPage { - - public TestPage() { - add(new Form() - .add(new TextInput("beans")) - .add(new Button()) - .add(new Link().href("/test").text("Link to other page")) - .action(new Action("submit")) - .layout(GridLayout.ofSize(2, 2)) - ); - } - - @ActionHandler("submit") - public void onSubmit(ActionEvent event) { - System.out.println("Got event " + event.getAction()); - } - -} diff --git a/src/main/java/me/mrletsplay/nojs/component/ContainerComponent.java b/src/main/java/me/mrletsplay/nojs/component/ContainerComponent.java index 29dcd3b..5c5c446 100644 --- a/src/main/java/me/mrletsplay/nojs/component/ContainerComponent.java +++ b/src/main/java/me/mrletsplay/nojs/component/ContainerComponent.java @@ -4,7 +4,7 @@ import me.mrletsplay.nojs.page.layout.Layout; public interface ContainerComponent extends Component { - public ContainerComponent add(Component component); + public ContainerComponent add(Component... components); public ContainerComponent layout(Layout layout); diff --git a/src/main/java/me/mrletsplay/nojs/component/impl/Form.java b/src/main/java/me/mrletsplay/nojs/component/impl/Form.java index b9528c5..614a138 100644 --- a/src/main/java/me/mrletsplay/nojs/component/impl/Form.java +++ b/src/main/java/me/mrletsplay/nojs/component/impl/Form.java @@ -21,8 +21,12 @@ public class Form implements ActionableComponent, ContainerComponent { } @Override - public Form add(Component component) { - components.add(component); + public Form add(Component... components) { + for(Component c : components) { + if(c == null) continue; + this.components.add(c); + } + return this; } diff --git a/src/main/java/me/mrletsplay/nojs/component/impl/Group.java b/src/main/java/me/mrletsplay/nojs/component/impl/Group.java index b9eebf6..623432c 100644 --- a/src/main/java/me/mrletsplay/nojs/component/impl/Group.java +++ b/src/main/java/me/mrletsplay/nojs/component/impl/Group.java @@ -18,8 +18,12 @@ public class Group implements ContainerComponent { } @Override - public Group add(Component component) { - components.add(component); + public Group add(Component... components) { + for(Component c : components) { + if(c == null) continue; + this.components.add(c); + } + return this; } diff --git a/src/main/java/me/mrletsplay/nojs/component/impl/Link.java b/src/main/java/me/mrletsplay/nojs/component/impl/Link.java index a262dd7..63b321c 100644 --- a/src/main/java/me/mrletsplay/nojs/component/impl/Link.java +++ b/src/main/java/me/mrletsplay/nojs/component/impl/Link.java @@ -7,10 +7,12 @@ public class Link implements Component { private String text; private String href; + private Target target; public Link() { this.text = "Link"; this.href = "#"; + this.target = Target.SELF; } public Link text(String text) { @@ -23,13 +25,31 @@ public class Link implements Component { return this; } + public Link target(Target target) { + this.target = target != null ? target : Target.SELF; + return this; + } + @Override public HtmlElement toHtml() { HtmlElement link = HtmlElement.of("a"); link.addClass("link"); + + if(target != Target.SELF) { + link.setAttribute("target", "_" + target.name().toLowerCase()); + } + link.setText(text); link.setAttribute("href", href); return link; } + public static enum Target { + + SELF, + BLANK, + ; + + } + } diff --git a/src/main/java/me/mrletsplay/nojs/component/impl/Message.java b/src/main/java/me/mrletsplay/nojs/component/impl/Message.java index f429654..2ac293a 100644 --- a/src/main/java/me/mrletsplay/nojs/component/impl/Message.java +++ b/src/main/java/me/mrletsplay/nojs/component/impl/Message.java @@ -12,9 +12,11 @@ import me.mrletsplay.simplehttpserver.dom.html.element.HtmlRaw; public class Message implements Component { private String text; + private Type type; public Message() { this.text = "Message"; + this.type = Type.INFO; } public Message text(String text) { @@ -22,11 +24,18 @@ public class Message implements Component { return this; } + public Message type(Type type) { + this.type = type != null ? type : Type.INFO; + return this; + } + @Override public HtmlElement toHtml() { HtmlElement message = HtmlElement.of("div"); message.addClass("message"); + message.setAttribute("data-type", type.name().toLowerCase()); + HtmlElement textSpan = HtmlElement.of("span"); textSpan.setText(text); message.appendChild(textSpan); @@ -46,4 +55,14 @@ public class Message implements Component { return message; } + public static enum Type { + + INFO, + SUCCESS, + WARNING, + ERROR, + ; + + } + } diff --git a/src/main/java/me/mrletsplay/nojs/component/impl/Space.java b/src/main/java/me/mrletsplay/nojs/component/impl/Space.java new file mode 100644 index 0000000..68440a1 --- /dev/null +++ b/src/main/java/me/mrletsplay/nojs/component/impl/Space.java @@ -0,0 +1,38 @@ +package me.mrletsplay.nojs.component.impl; + +import me.mrletsplay.nojs.component.Component; +import me.mrletsplay.simplehttpserver.dom.html.HtmlElement; + +public class Space implements Component { + + private String width; + private String height; + + public Space width(String width) { + this.width = width; + return this; + } + + public Space height(String height) { + this.height = height; + return this; + } + + @Override + public HtmlElement toHtml() { + HtmlElement space = HtmlElement.of("span"); + space.addClass("space"); + + if(width != null) { + space.appendAttribute("style", "min-width:" + width + ";"); + } + + if(height != null) { + space.appendAttribute("style", "min-height:" + height + ";"); + } + + return space; + } + + +} diff --git a/src/main/java/me/mrletsplay/nojs/page/Page.java b/src/main/java/me/mrletsplay/nojs/page/Page.java index 67be9e6..bb32c10 100644 --- a/src/main/java/me/mrletsplay/nojs/page/Page.java +++ b/src/main/java/me/mrletsplay/nojs/page/Page.java @@ -1,11 +1,8 @@ package me.mrletsplay.nojs.page; -import java.util.List; - import me.mrletsplay.nojs.action.ActionEvent; import me.mrletsplay.nojs.action.ActionHandlers; import me.mrletsplay.nojs.component.Component; -import me.mrletsplay.nojs.page.layout.Layout; import me.mrletsplay.simplehttpserver.dom.css.CssElement; import me.mrletsplay.simplehttpserver.dom.css.CssSelector; import me.mrletsplay.simplehttpserver.dom.css.StyleSheet; @@ -22,11 +19,7 @@ import me.mrletsplay.simplehttpserver.http.response.HtmlResponse; public interface Page extends HttpDocument, ActionHandlers { - public List getComponents(); - - public default Layout getLayout() { - return null; - } + public Component getContent(); public default HtmlDocument toHtml() { HtmlDocument doc = new HtmlDocument(); @@ -40,14 +33,7 @@ public interface Page extends HttpDocument, ActionHandlers { doc.getHeadNode().appendChild(HtmlElement.style(sheet)); - Layout layout = getLayout(); - if(layout != null) { - layout.apply(doc.getBodyNode()); - } - - for(Component c : getComponents()) { - doc.getBodyNode().appendChild(c.toHtml()); - } + doc.getBodyNode().appendChild(getContent().toHtml()); return doc; } @@ -59,7 +45,6 @@ public interface Page extends HttpDocument, ActionHandlers { if(ctx.getClientHeader().getMethod() == HttpRequestMethod.POST) { // TODO: error handling UrlEncoded data = ctx.getClientHeader().getPostData().getParsedAs(DefaultClientContentTypes.URLENCODED); - System.out.println(data); ActionEvent e = new ActionEvent(data); onAction(e); diff --git a/src/main/java/me/mrletsplay/nojs/page/StaticPage.java b/src/main/java/me/mrletsplay/nojs/page/StaticPage.java deleted file mode 100644 index 7992ca3..0000000 --- a/src/main/java/me/mrletsplay/nojs/page/StaticPage.java +++ /dev/null @@ -1,56 +0,0 @@ -package me.mrletsplay.nojs.page; - -import java.util.ArrayList; -import java.util.List; - -import me.mrletsplay.nojs.action.ActionEvent; -import me.mrletsplay.nojs.action.ActionHandlers; -import me.mrletsplay.nojs.component.Component; -import me.mrletsplay.nojs.page.layout.Layout; - -public class StaticPage implements Page { - - private List components; - private Layout layout; - private List actionHandlers; - - public StaticPage() { - this.components = new ArrayList<>(); - this.actionHandlers = new ArrayList<>(); - } - - public StaticPage add(Component component) { - components.add(component); - return this; - } - - public StaticPage layout(Layout layout) { - this.layout = layout; - return this; - } - - public StaticPage addActionHandler(ActionHandlers handler) { - actionHandlers.add(handler); - return this; - } - - @Override - public List getComponents() { - return components; - } - - @Override - public Layout getLayout() { - return layout; - } - - @Override - public void onAction(ActionEvent event) { - Page.super.onAction(event); - - for(ActionHandlers handler : actionHandlers) { - handler.onAction(event); - } - } - -} diff --git a/src/main/java/me/mrletsplay/nojs/page/layout/GridLayout.java b/src/main/java/me/mrletsplay/nojs/page/layout/GridLayout.java index 6525b63..f39eb5f 100644 --- a/src/main/java/me/mrletsplay/nojs/page/layout/GridLayout.java +++ b/src/main/java/me/mrletsplay/nojs/page/layout/GridLayout.java @@ -14,11 +14,15 @@ public class GridLayout implements Layout { private List rows; private String gap; private Flow flow; + private HAlignment hAlignment; + private VAlignment vAlignment; public GridLayout() { this.columns = new ArrayList<>(); this.rows = new ArrayList<>(); this.flow = Flow.ROW; + this.hAlignment = HAlignment.NORMAL; + this.vAlignment = VAlignment.NORMAL; } public GridLayout columns(Collection columns) { @@ -49,6 +53,16 @@ public class GridLayout implements Layout { return this; } + public GridLayout hAlignment(HAlignment hAlignment) { + this.hAlignment = hAlignment != null ? hAlignment : HAlignment.NORMAL; + return this; + } + + public GridLayout vAlignment(VAlignment vAlignment) { + this.vAlignment = vAlignment != null ? vAlignment : VAlignment.NORMAL; + return this; + } + @Override public void apply(HtmlElement html) { String css = "display:grid;"; @@ -69,7 +83,15 @@ public class GridLayout implements Layout { css += "grid-auto-flow:" + flow.name().toLowerCase() + ";"; } - html.setAttribute("style", css); + if(hAlignment != HAlignment.NORMAL) { + css += "justify-content:" + hAlignment.name().toLowerCase() + ";"; + } + + if(vAlignment != VAlignment.NORMAL) { + css += "align-items:" + vAlignment.name().toLowerCase() + ";"; + } + + html.appendAttribute("style", css); } public static GridLayout ofColumns(int columns) { @@ -112,4 +134,27 @@ public class GridLayout implements Layout { } + public static enum HAlignment { + + NORMAL, + START, + CENTER, + END, + STRETCH, + ; + + } + + public static enum VAlignment { + + NORMAL, + START, + CENTER, + END, + STRETCH, + ; + + } + + } diff --git a/src/main/java/me/mrletsplay/nojs/page/layout/HClampLayout.java b/src/main/java/me/mrletsplay/nojs/page/layout/HClampLayout.java new file mode 100644 index 0000000..0986f09 --- /dev/null +++ b/src/main/java/me/mrletsplay/nojs/page/layout/HClampLayout.java @@ -0,0 +1,50 @@ +package me.mrletsplay.nojs.page.layout; + +import me.mrletsplay.simplehttpserver.dom.html.HtmlElement; + +public class HClampLayout implements Layout { + + private String + min, + preferred, + max; + + private HAlignment hAlignment; + + public HClampLayout(String min, String preferred, String max) { + this.min = min == null ? "0" : min; + this.preferred = preferred == null ? "100%" : preferred; + this.max = max == null ? "100%" : max; + this.hAlignment = HAlignment.NORMAL; + } + + public HClampLayout hAlignment(HAlignment hAlignment) { + this.hAlignment = hAlignment != null ? hAlignment : HAlignment.NORMAL; + return this; + } + + @Override + public void apply(HtmlElement html) { + String css = "display:grid;"; + + css += "grid-template-columns:clamp(" + min + "," + preferred + "," + max + ");"; + + if(hAlignment != HAlignment.NORMAL) { + css += "justify-content:" + hAlignment.name().toLowerCase() + ";"; + } + + html.appendAttribute("style", css); + } + + public static enum HAlignment { + + NORMAL, + START, + CENTER, + END, + STRETCH, + ; + + } + +} diff --git a/src/main/resources/style/base.css b/src/main/resources/style/base.css index 0c1a44a..c7533e3 100644 --- a/src/main/resources/style/base.css +++ b/src/main/resources/style/base.css @@ -4,6 +4,11 @@ html { --accent: #3584e4; --text: #fff; --link: var(--accent); + + --info: var(--accent); + --success: green; + --warning: darkorange; + --error: orangered; } * { @@ -66,13 +71,28 @@ body { justify-content: space-between; align-items: center; position: fixed; - top: 0; + bottom: 0; left: 0; min-height: 30px; - background-color: orangered; width: 100vw; } +.message[data-type="info"] { + background-color: var(--info); +} + +.message[data-type="success"] { + background-color: var(--success); +} + +.message[data-type="warning"] { + background-color: var(--warning); +} + +.message[data-type="error"] { + background-color: var(--error); +} + .message>label>input { display: none; } @@ -92,28 +112,18 @@ body { .message>label { position: relative; - /*background: var(--accent);*/ cursor: pointer; - font-weight: bold; display: flex; align-items: center; justify-content: center; } .message>label>svg { - fill: white; + fill: var(--text); width: 20px; height: 20px; } -/*.message>label:after { - display: block; - text-align: center; - content: "x"; - width: 100%; - height: 100%; -}*/ - .message:has(:checked) { display: none; }