Refactor pages, Add Space + HClampLayout

This commit is contained in:
MrLetsplay 2025-01-18 22:50:11 +01:00
parent c6eec9364f
commit a68cc635a8
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
15 changed files with 266 additions and 178 deletions

View File

@ -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);
}
});

View File

@ -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<Component> 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));
}
}

View File

@ -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<Component> 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);
}
}

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,
;
}
}

View File

@ -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,
;
}
}

View File

@ -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;
}
}

View File

@ -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<Component> 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);

View File

@ -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<Component> components;
private Layout layout;
private List<ActionHandlers> 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<Component> 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);
}
}
}

View File

@ -14,11 +14,15 @@ public class GridLayout implements Layout {
private List<String> 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<String> 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,
;
}
}

View File

@ -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,
;
}
}

View File

@ -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;
}