actually bruh
This commit is contained in:
+132
@@ -0,0 +1,132 @@
|
||||
# UI
|
||||
|
||||
Built on top of [HyUI](https://github.com/Elliesaur/HyUI), a third-party library that wraps Hytale's native Custom UI API in an HTML-like markup (HYUIML). HyUI is bundled into the Staff jar via the shade plugin so you don't need it installed separately.
|
||||
|
||||
## Reading the source
|
||||
|
||||
Each page lives in [src/main/java/net/kewwbec/staff/ui/](../src/main/java/net/kewwbec/staff/ui/). Pages don't extend the SDK's `InteractiveCustomUIPage` anymore - they're plain classes that build a `PageBuilder` and expose an `open(store)` method. Navigation between pages is `new OtherPage(...).open(store)`.
|
||||
|
||||
## Page flow
|
||||
|
||||
```
|
||||
/staffui
|
||||
|
|
||||
v
|
||||
[StaffHubPage]
|
||||
|
|
||||
+-> Player Lookup -> [PlayerLookupPage] (text search, click result for detail)
|
||||
|
|
||||
v
|
||||
[PlayerDetailPage]
|
||||
|
|
||||
+-> Ban / TempBan / Mute / TempMute / Kick / Warn --> [PunishFormPage]
|
||||
+-> Unban / Unmute (applied inline, page re-opens)
|
||||
+-> History --> [HistoryPage]
|
||||
```
|
||||
|
||||
Every non-hub page has a Back button. ESC always closes (lifetime is `CanDismiss`).
|
||||
|
||||
## Page reference
|
||||
|
||||
| Page | Purpose |
|
||||
|---|---|
|
||||
| [StaffHubPage](../src/main/java/net/kewwbec/staff/ui/StaffHubPage.java) | Top-level menu. Currently just routes to Player Lookup; add buttons here for new sections. |
|
||||
| [PlayerLookupPage](../src/main/java/net/kewwbec/staff/ui/PlayerLookupPage.java) | Text input + Search button. Online players are listed first; if you type a name not online, the players_seen table is consulted. Click a result to open detail. |
|
||||
| [PlayerDetailPage](../src/main/java/net/kewwbec/staff/ui/PlayerDetailPage.java) | Player info + active ban/mute lines + action buttons. Unban / Unmute fire immediately (no form); the page reopens with refreshed data. |
|
||||
| [PunishFormPage](../src/main/java/net/kewwbec/staff/ui/PunishFormPage.java) | Reason field, plus duration field for timed types. Validation errors re-open the same page with the entered values and a red error line. |
|
||||
| [HistoryPage](../src/main/java/net/kewwbec/staff/ui/HistoryPage.java) | Last 50 entries for the target. Read-only. |
|
||||
|
||||
## HyUI patterns we use
|
||||
|
||||
All pages follow the same skeleton:
|
||||
|
||||
```java
|
||||
public final class SomePage {
|
||||
private final PlayerRef playerRef;
|
||||
private final StaffUIContext sCtx;
|
||||
private final PageBuilder page;
|
||||
|
||||
public SomePage(PlayerRef playerRef, StaffUIContext sCtx, ...args) {
|
||||
this.playerRef = playerRef;
|
||||
this.sCtx = sCtx;
|
||||
|
||||
// Build the HYUIML based on constructor state
|
||||
String html = """
|
||||
<div class="page-overlay">
|
||||
<div class="container" data-hyui-title="Title">
|
||||
<div class="container-contents">
|
||||
<button id="someBtn">Do thing</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
""";
|
||||
|
||||
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||
.fromHtml(html);
|
||||
|
||||
// Wire listeners by element id
|
||||
page.addEventListener("someBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
|
||||
// pCtx.getValue("inputId", String.class).orElse("") to read input values
|
||||
// Navigate by opening a new page instance
|
||||
});
|
||||
}
|
||||
|
||||
public void open(Store<EntityStore> store) { page.open(store); }
|
||||
}
|
||||
```
|
||||
|
||||
### Reading input values
|
||||
|
||||
Inside an event listener, the second argument is the HyUI page context, which exposes `getValue(id, type)`:
|
||||
|
||||
```java
|
||||
page.addEventListener("submitBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
|
||||
String reason = pCtx.getValue("reasonInput", String.class).orElse("").trim();
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
### Re-rendering after state changes
|
||||
|
||||
We do *not* mutate the page in place. Instead, we open a fresh page instance with new state:
|
||||
|
||||
```java
|
||||
new PunishFormPage(playerRef, sCtx, uuid, name, type, durationFromForm, reasonFromForm, "Error: ...").open(store);
|
||||
```
|
||||
|
||||
This is simpler than HyUI's `updatePage(true)` re-render dance and keeps each page render side-effect-free. The cost is rebuilding the HTML each navigation - cheap compared to anything else we do.
|
||||
|
||||
### HYUIML class reference (the bits we use)
|
||||
|
||||
- `<div class="page-overlay">` - root, dims background, captures input
|
||||
- `<div class="container" data-hyui-title="...">` - main framed window with header
|
||||
- `<div class="container-contents">` - inner content area
|
||||
- `<button id="...">Text</button>` - clickable button (use `class="back-button"` for back-themed)
|
||||
- `<input type="text" id="..." value="..." placeholder="...">` - text input
|
||||
- `<p>` / `<label>` - text
|
||||
|
||||
The full HYUIML reference is in `extras to look at/HyUI-Docs/hyuiml-htmlish-in-hytale.md`.
|
||||
|
||||
## Adding a new page
|
||||
|
||||
1. Create `src/main/java/net/kewwbec/staff/ui/MyPage.java` following the skeleton above.
|
||||
2. Use HTMLish in the `fromHtml` block. Stick to alphanumeric IDs (HyUI sanitizes them, but bugs hide in non-trivial chars).
|
||||
3. Wire listeners with `page.addEventListener(id, eventType, handler)`.
|
||||
4. To navigate to it, call `new MyPage(...).open(store)` from another page's handler.
|
||||
5. To open it from a command, do the same thing in the command's `execute` method.
|
||||
|
||||
## Things to be aware of
|
||||
|
||||
- **HyUI must be on the classpath at runtime.** This is handled by the shade plugin in [pom.xml](../pom.xml), which packs HyUI into the Staff jar. Don't drop the shade plugin without a replacement.
|
||||
- **Sanitized IDs.** Use plain alphanumeric ids in HYUIML. Use the same string in `addEventListener` - HyUI translates internally.
|
||||
- **Escape user input** when embedding into HTML strings. We have a tiny `escape(String)` helper in each page; use it for any value that came from a player.
|
||||
- **`value` attribute is what carries text-field state across re-renders.** When you rebuild a form page with an error, set `value="..."` on each input so the user doesn't lose what they typed.
|
||||
- **No `<script>`, no full CSS.** Logic is in Java; layout is via Hytale's `LayoutMode` and `flex-weight` via `style="..."` on `<div>`s if needed.
|
||||
|
||||
## What this UI deliberately doesn't do
|
||||
|
||||
- **Network-wide active punishments view.** Could be added as a Hub button if needed.
|
||||
- **Bulk actions.** No "ban everyone in this list" - intentional.
|
||||
- **In-place updates.** Every state change rebuilds the page. Simpler reasoning, slightly more network chatter.
|
||||
- **Custom themes.** We rely on HyUI's default Container/Button styling. Theming via `<style>` blocks is possible later.
|
||||
Reference in New Issue
Block a user