diff --git a/src/renderer/src/stories/CodeBlock.js b/src/renderer/src/stories/CodeBlock.js new file mode 100644 index 000000000..6c1f8c73d --- /dev/null +++ b/src/renderer/src/stories/CodeBlock.js @@ -0,0 +1,35 @@ +import { LitElement, css, html } from "lit"; + +export class CodeBlock extends LitElement { + static get styles() { + return css` + :host { + display: block; + font-size: 85%; + background: #f2f1f1; + border-radius: 10px; + border: 1px solid gray; + overflow: hidden; + } + + pre { + overflow: auto; + padding: 5px 10px; + box-sizing: border-box; + user-select: text; + margin: 0; + } + `; + } + + constructor({ text = "" }) { + super(); + this.text = text; + } + + render() { + return html`
${this.text}`; + } +} + +customElements.get("code-block") || customElements.define("code-block", CodeBlock); diff --git a/src/renderer/src/stories/InfoBox.js b/src/renderer/src/stories/InfoBox.js index 42130b898..c11b4c54b 100644 --- a/src/renderer/src/stories/InfoBox.js +++ b/src/renderer/src/stories/InfoBox.js @@ -76,37 +76,40 @@ export class InfoBox extends LitElement { `; } - constructor({ header = "Info", content, type = "info" } = {}) { + constructor({ header = "Info", content, type = "info", open = false } = {}) { super(); this.header = header; this.content = content; this.type = type; + this.open = open; } updated() { - const infoDropdowns = this.shadowRoot.querySelectorAll(".guided--info-dropdown"); - for (const infoDropdown of Array.from(infoDropdowns)) { - const infoTextElement = infoDropdown.querySelector("#header"); - - // Auto-add icons if they're not there - if (this.type === "info") infoTextElement.insertAdjacentHTML("beforebegin", `ℹ️`); - if (this.type === "warning") - infoTextElement.insertAdjacentHTML("beforebegin", ` ⚠️`); - - infoDropdown.onclick = () => { - const infoContainer = infoDropdown.nextElementSibling; - const infoContainerChevron = infoDropdown.querySelector("nwb-chevron"); - - const infoContainerIsopen = infoContainer.classList.contains("container-open"); - - if (infoContainerIsopen) { - infoContainerChevron.direction = "right"; - infoContainer.classList.remove("container-open"); - } else { - infoContainerChevron.direction = "bottom"; - infoContainer.classList.add("container-open"); - } - }; + const infoDropdown = this.shadowRoot.querySelector(".guided--info-dropdown"); + const infoTextElement = infoDropdown.querySelector("#header"); + + // Auto-add icons if they're not there + if (this.type === "info") infoTextElement.insertAdjacentHTML("beforebegin", `ℹ️`); + if (this.type === "warning") infoTextElement.insertAdjacentHTML("beforebegin", ` ⚠️`); + + const infoContainer = infoDropdown.nextElementSibling; + infoDropdown.onclick = () => this.onToggle(!infoContainer.classList.contains("container-open")); + + this.onToggle(); + } + + onToggle(open = this.open) { + const infoDropdown = this.shadowRoot.querySelector(".guided--info-dropdown"); + + const infoContainer = infoDropdown.nextElementSibling; + const infoContainerChevron = infoDropdown.querySelector("nwb-chevron"); + + if (open) { + infoContainerChevron.direction = "bottom"; + infoContainer.classList.add("container-open"); + } else { + infoContainerChevron.direction = "right"; + infoContainer.classList.remove("container-open"); } } diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index f8c82241a..0753e421b 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -96,15 +96,15 @@ hr { margin: 1em 0 1.5em 0; } -pre { - white-space: pre-wrap; /* Since CSS 2.1 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ - font-family: unset; - color: DimGray; -} + pre { + white-space: pre-wrap; /* Since CSS 2.1 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + font-family: unset; + color: DimGray; + } .required label:after { content: " *"; @@ -434,6 +434,7 @@ export class JSONSchemaForm extends LitElement { ${interactiveInput} + `; }; @@ -587,6 +588,8 @@ export class JSONSchemaForm extends LitElement { ? valid?.filter((info) => info.type === "error" || (isRequired && info.missing)) : []; + const info = Array.isArray(valid) ? valid?.filter((info) => info.type === "info") : []; + const hasLinks = this.#getLink(externalPath); if (hasLinks) { if (checkLinks) { @@ -612,6 +615,7 @@ export class JSONSchemaForm extends LitElement { // Clear old errors and warnings this.#clearMessages(fullPath, "errors"); this.#clearMessages(fullPath, "warnings"); + this.#clearMessages(fullPath, "info"); const isFunction = typeof valid === "function"; const isValid = @@ -629,6 +633,7 @@ export class JSONSchemaForm extends LitElement { // Show aggregated errors and warnings (if any) warnings.forEach((info) => this.#addMessage(fullPath, info, "warnings")); + info.forEach((info) => this.#addMessage(fullPath, info, "info")); if (isValid && errors.length === 0) { element.classList.remove("invalid"); diff --git a/src/renderer/src/stories/OptionalSection.js b/src/renderer/src/stories/OptionalSection.js index 90d27e901..15347c59f 100644 --- a/src/renderer/src/stories/OptionalSection.js +++ b/src/renderer/src/stories/OptionalSection.js @@ -7,7 +7,6 @@ export class OptionalSection extends LitElement { return css` :host { display: block; - text-align: center; } h2 { @@ -15,6 +14,10 @@ export class OptionalSection extends LitElement { margin-bottom: 15px; } + .optional-section__toggle { + padding-bottom: 20px; + } + .optional-section__content { text-align: left; } @@ -99,17 +102,15 @@ export class OptionalSection extends LitElement { render() { return html` -
+ Consider a dataset of that includes video recordings from three cameras, stored in the following + directory structure. +
+ ${new CodeBlock({ text: exampleFileStructure })} + +
+ Using mylab
as the base directory, the correct format string to extract the subject ID,
+ session start time, and session number would be:
+
+ The above example applies all of the supported f-string variables, which are used to extract information + into the resulting metadata: +
+ ${new List({ + items: [ + { + value: "subject_id", + }, + { + value: "session_id", + }, + { + value: "session_start_time", + }, + ], + editable: false, + })} + +Additional variables (or blank braces) can be specified to indicate wildcard patterns.
+ ++ Consequently, the metadata extracted from the first file found using this approach would be the + following: +
+ ${new CodeBlock({ text: JSON.stringify(exampleMetadata, null, 2) })} + +