diff --git a/assets/index.css b/assets/index.css index 2d36296f..14c45247 100644 --- a/assets/index.css +++ b/assets/index.css @@ -43,7 +43,6 @@ img { flex: 0 0; display: flex; flex-wrap: wrap; - /*justify-content: space-around;*/ align-items: center; border-bottom: 1px solid #666; } @@ -119,6 +118,10 @@ img { display: none; } +.expandable-label > label { + cursor: pointer; +} + .expandable-input ~ .expandable-label .expandable-visualizer { font-family: monospace; display: inline-flex; diff --git a/src/web/ui.rs b/src/web/ui.rs index 2de8ccf4..edf0a6a6 100644 --- a/src/web/ui.rs +++ b/src/web/ui.rs @@ -1,11 +1,12 @@ //! Defines the HTTP-based UI. These endpoints generally return HTML and SVG. -use std::{path::Path, sync::Arc, time::Duration}; +use std::{borrow::Cow, path::Path, sync::Arc, time::Duration}; use futures::{future, Future}; use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode}; +use maplit::hashmap; use rbx_dom_weak::{RbxId, RbxValue}; -use ritz::{html, Fragment, HtmlContent}; +use ritz::{html, Fragment, HtmlContent, HtmlSelfClosingTag}; use crate::{ imfs::{Imfs, ImfsDebug, ImfsFetcher}, @@ -201,25 +202,17 @@ impl UiService { let children_container = if children_list.is_empty() { HtmlContent::None } else { - html! { -
- + let section = ExpandableSection { + title: "Children", + class_name: "instance-children", + id, + expanded: true, + content: html! { + { Fragment::new(children_list) } + }, + }; -

- -

-
- { Fragment::new(children_list) } -
-
- } + section.render() }; let mut properties: Vec<_> = instance.properties().iter().collect(); @@ -239,24 +232,17 @@ impl UiService { let property_container = if property_list.is_empty() { HtmlContent::None } else { - html! { -
- + let section = ExpandableSection { + title: "Properties", + class_name: "instance-properties", + id, + expanded: false, + content: html! { + { Fragment::new(property_list) } + }, + }; -

- -

-
- { Fragment::new(property_list) } -
-
- } + section.render() }; let class_name_specifier = if instance.name() == instance.class_name() { @@ -355,3 +341,50 @@ impl UiService { } } } + +struct ExpandableSection<'a> { + title: &'a str, + class_name: &'a str, + id: RbxId, + expanded: bool, + content: HtmlContent<'a>, +} + +impl<'a> ExpandableSection<'a> { + fn render(self) -> HtmlContent<'a> { + let input_id = format!("{}-{}", self.class_name, self.id); + + // We need to specify this input manually because Ritz doesn't have + // support for conditional attributes like `checked`. + let mut input = HtmlSelfClosingTag { + name: Cow::Borrowed("input"), + attributes: hashmap! { + Cow::Borrowed("class") => Cow::Borrowed("expandable-input"), + Cow::Borrowed("id") => Cow::Owned(input_id.clone()), + Cow::Borrowed("type") => Cow::Borrowed("checkbox"), + }, + }; + + if self.expanded { + input + .attributes + .insert(Cow::Borrowed("checked"), Cow::Borrowed("checked")); + } + + html! { +
+ { input } + +

+ +

+
+ { self.content } +
+
+ } + } +}