diff --git a/src/renderer/src/stories/JSONSchemaInput.js b/src/renderer/src/stories/JSONSchemaInput.js index 10d2353d2..81093aa04 100644 --- a/src/renderer/src/stories/JSONSchemaInput.js +++ b/src/renderer/src/stories/JSONSchemaInput.js @@ -110,7 +110,6 @@ export class JSONSchemaInput extends LitElement { // onValidate = () => {} updateData(value, forceValidation = false) { - if (this.value === value && !forceValidation) return false; const { path: fullPath } = this; diff --git a/src/renderer/src/stories/List.ts b/src/renderer/src/stories/List.ts index d6e92b37e..c2b8e7480 100644 --- a/src/renderer/src/stories/List.ts +++ b/src/renderer/src/stories/List.ts @@ -51,6 +51,10 @@ export class List extends LitElement { text-overflow: ellipsis } + :host(:not([unordered])) li { + cursor: move; + } + :host([unordered]) ol { list-style-type: none; display: flex; @@ -69,6 +73,15 @@ export class List extends LitElement { align-items: center; } + [contenteditable] { + cursor: text; + } + + [data-idx]{ + background: #f0f0f0; + height: 25px; + } + `; } @@ -121,6 +134,45 @@ export class List extends LitElement { declare listStyles: any + allowDrop = (ev) => ev.preventDefault(); + + + #dragged?: number + #placeholder = document.createElement('div') + + drag = (ev, i) => this.#dragged = i + + dragEnter = (ev, i) => { + ev.preventDefault(); + if (this.#dragged !== i) { + const item = this.shadowRoot.getElementById(`item-${i}`) + this.#placeholder.setAttribute('data-idx', i) + item?.insertAdjacentElement('beforebegin', this.#placeholder) + } else { + this.#removePlaceholder() + } + } + + dragExit = (ev, i) => { + ev.preventDefault(); + if (this.#placeholder && i.toString() !== this.#placeholder.getAttribute('data-idx')){ + this.#removePlaceholder() + } + } + + drop = (ev, i) => { + + ev.preventDefault(); + const draggedIdx = this.#dragged as number + this.#dragged = undefined + + const movedItem = this.items[draggedIdx] + this.items.splice(draggedIdx, 1) + this.items.splice(i, 0, movedItem) + + this.items = [...this.items] + } + constructor(props: ListProps = {}) { super(); @@ -139,21 +191,49 @@ export class List extends LitElement { this.items = [...this.items, item] } + #removePlaceholder = () => { + this.#placeholder.removeAttribute('data-idx') + this.#placeholder.remove() + } + #renderListItem = (item: ListItemType, i: number) => { const { key, value, content = value } = item; const li = document.createElement("li"); + li.id = `item-${i}`; + + if (!this.unordered) { + li.draggable = true; + li.ondragstart = (ev) => this.drag(ev, i); + + li.ondragend = (ev) => { + if (this.#dragged !== undefined) { + const idx = this.#placeholder.getAttribute('data-idx') + if (idx !== null) this.drop(ev, parseInt(idx)) + this.#removePlaceholder() + } + } + + li.ondrop = (ev) => this.drop(ev, i); + + li.ondragover = (ev) => this.dragEnter(ev, i); + // li.ondragenter = (ev) => this.dragEnter(ev, i); + li.ondragleave = (ev) => this.dragExit(ev, i); + } + const outerDiv = document.createElement('div') const div = document.createElement('div') li.append(outerDiv) outerDiv.append(div) - const keyEl = document.createElement("span"); + let editableElement = document.createElement("span"); let resolvedKey = key; const originalValue = resolvedKey; - if (key) { + const isUnordered = !!key + + if (isUnordered) { this.setAttribute('unordered', '') @@ -164,8 +244,9 @@ export class List extends LitElement { resolvedKey = `${originalValue}_${i}`; } + const keyEl = editableElement + keyEl.innerText = resolvedKey; - keyEl.contentEditable = true; keyEl.style.cursor = "text"; const sepEl = document.createElement("span"); @@ -173,14 +254,18 @@ export class List extends LitElement { div.append(keyEl, sepEl); this.object[resolvedKey] = value; - } else this.object[i] = value; + } + + else { + this.object[i] = value; + } if (typeof content === 'string') { - const valueEl = document.createElement("span"); + const valueEl = editableElement = document.createElement("span"); valueEl.innerText = content; div.appendChild(valueEl); } - else li.append(content) + else li.append(editableElement = content) @@ -197,23 +282,30 @@ export class List extends LitElement { outerDiv.appendChild(button); + editableElement.contentEditable = true; + // Stop enter key from creating new line - keyEl.addEventListener("keydown", function (e) { + editableElement.addEventListener("keydown", (e) => { if (e.keyCode === 13) { - keyEl.blur(); + editableElement.blur(); return false; } }); const deleteListItem = () => this.delete(i); - keyEl.addEventListener("blur", () => { - const newKey = keyEl.innerText; - if (newKey === "") keyEl.innerText = resolvedKey; // Reset to original value + editableElement.addEventListener("blur", () => { + const newKey = editableElement.innerText; + if (newKey === "") this.delete(i); // Delete if empty else { - delete this.object[resolvedKey]; - resolvedKey = newKey; - this.object[resolvedKey] = value; + const oKey = isUnordered ? resolvedKey : i; + delete this.object[oKey]; + this.object[newKey] = value; + + if (!isUnordered) { + this.items[i].value = newKey + this.items = [...this.items] + } } }); diff --git a/src/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js b/src/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js index eb0d7f94b..28a9a5591 100644 --- a/src/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js +++ b/src/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js @@ -194,8 +194,7 @@ export class GuidedPathExpansionPage extends Page { if (!sesRef) delete globalResults[sub][ses]; // Delete removed sessions else { - - const globalSesRef = globalResults[sub][ses] + const globalSesRef = globalResults[sub][ses]; for (let name in globalSesRef.source_data) { if (!sesRef.source_data[name]) delete globalSesRef.source_data[name]; // Delete removed interfaces @@ -258,7 +257,10 @@ export class GuidedPathExpansionPage extends Page { // altContent: this.altForm, }); - const structureState = (this.localState = merge(this.info.globalState.structure, { results: {}, keep_existing_data: true })); + const structureState = (this.localState = merge(this.info.globalState.structure, { + results: {}, + keep_existing_data: true, + })); const state = structureState.state; @@ -280,12 +282,11 @@ export class GuidedPathExpansionPage extends Page { // NOTE: These are custom coupled form inputs onUpdate: (path, value) => { - - this.unsavedUpdates = true + this.unsavedUpdates = true; - const parentPath = [...path] + const parentPath = [...path]; const name = parentPath.pop(); - + if (name === "base_directory") { form.getInput([...parentPath, "base_directory"]).value = value; // Update value pre-emptively const input = form.getInput([...parentPath, "format_string_path"]); @@ -296,19 +297,17 @@ export class GuidedPathExpansionPage extends Page { const value = parent[name]; if (fs) { - const baseDir = form.getInput([...parentPath, "base_directory"]) - if (name === "format_string_path") { - + const baseDir = form.getInput([...parentPath, "base_directory"]); + if (name === "format_string_path") { if (value && !baseDir.value) { return [ { message: html`A base directory must be provided to locate your files.`, type: "error", }, - ] + ]; } - const base_directory = [...parentPath, "base_directory"].reduce( (acc, key) => acc[key], this.form.resolved