Dropdown implementation, switch refactoring

* Various fixes

* Fix tests

* Dropdown names deduced from pin names

* Remove update callbacks

* Fix double pins issue

* return undefined if not switch
This commit is contained in:
barsdeveloper
2023-04-22 12:44:37 +02:00
committed by GitHub
parent e06589bc46
commit 8a96af670e
32 changed files with 1024 additions and 365 deletions

View File

@@ -22,7 +22,8 @@ export default class NodeTemplate extends ISelectableDraggableTemplate {
toggleAdvancedDisplayHandler = () => {
this.element.toggleShowAdvancedPinDisplay()
this.element.addNextUpdatedCallbacks(() => this.element.acknowledgeReflow(), true)
this.element.requestUpdate()
this.element.updateComplete.then(() => this.element.acknowledgeReflow())
}
/** @param {NodeElement} element */

View File

@@ -0,0 +1,49 @@
import { html } from "lit"
import ITemplate from "../ITemplate.js"
import MouseIgnore from "../../input/mouse/MouseIgnore.js"
/**
* @typedef {import ("../../element/DropdownElement.js").default} DropdownElement
* @typedef {import("lit").PropertyValues} PropertyValues
*/
/** @extends {ITemplate<DropdownElement>} */
export default class DropdownTemplate extends ITemplate {
/** @type {HTMLSelectElement} */
#selectElement
render() {
return html`
<select class="ueb-pin-input-content">
${this.element.options.map(([k, v]) => html`
<option value="${k}" ?selected="${k === this.element.selected}">${v}</option>
`)}
</select>
`
}
/** @param {PropertyValues} changedProperties */
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties)
this.#selectElement = this.element.querySelector("select")
const event = new Event("input", { bubbles: true })
this.#selectElement.dispatchEvent(event)
}
createInputObjects() {
return [
...super.createInputObjects(),
// Prevents creating links when selecting text and other undesired mouse actions detection
new MouseIgnore(this.element, this.blueprint),
]
}
setSelectedValue(value) {
/** @type {HTMLOptionElement} */(this.element.querySelector(`option[value="${value}"]`)).defaultSelected = true
}
getSelectedValue() {
return this.#selectElement.value
}
}

View File

@@ -0,0 +1,60 @@
import { html } from "lit"
import IInputPinTemplate from "./IInputPinTemplate.js"
/**
* @typedef {import("../../element/DropdownElement.js").default} DropdownElement
* @typedef {import("../../element/PinElement.js").AnyValue} AnyValue
* @typedef {import("../../entity/EnumEntity.js").default} EnumEntity
* @typedef {import("lit").PropertyValues} PropertyValues
*/
/**
* @template {AnyValue} T
* @typedef {import("../../element/PinElement.js").default<T>} PinElement
*/
/** @extends IInputPinTemplate<EnumEntity> */
export default class EnumPinTemplate extends IInputPinTemplate {
static saveEachInputChange = true // Otherwise save only on focus out
/** @type {DropdownElement} */
#dropdownElement
#dropdownEntries = []
setup() {
super.setup()
const enumEntries = this.element.nodeElement.entity.EnumEntries
if (enumEntries) {
this.#dropdownEntries = enumEntries.map(k => [
k,
this.element.nodeElement.getPinEntities().find(pinEntity => k === pinEntity.PinName)
?.PinFriendlyName.toString()
?? k
])
this.element.requestUpdate()
}
}
renderInput() {
const entity = this.element.nodeElement.entity
return html`
<ueb-dropdown
class="ueb-pin-input"
.options="${this.#dropdownEntries}"
.selected="${this.element.defaultValue.value}"
>
</ueb-dropdown>
`
}
/** @param {PropertyValues} changedProperties */
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties)
this.#dropdownElement = this.element.querySelector("ueb-dropdown")
}
getInputs() {
return [this.#dropdownElement.getValue()]
}
}

View File

@@ -1,6 +1,5 @@
import { html } from "lit"
import Configuration from "../../Configuration.js"
import MouseIgnore from "../../input/mouse/MouseIgnore.js"
import PinTemplate from "./PinTemplate.js"
import Utility from "../../Utility.js"
@@ -14,12 +13,10 @@ export default class IInputPinTemplate extends PinTemplate {
static singleLineInput = false
static selectOnFocus = true
static saveEachInputChange = false // Otherwise save only on focus out
/** @type {HTMLElement[]} */
#inputContentElements
get inputContentElements() {
return this.#inputContentElements
}
/** @param {String} value */
static stringFromInputToUE(value) {
@@ -35,8 +32,8 @@ export default class IInputPinTemplate extends PinTemplate {
.replace(/(?<=\n\s*)$/, "\n") // Put back trailing double newline
}
#onFocusOutHandler = () => this.setInputs(this.getInputs(), true)
/** @param {InputEvent} event */
#setInput = () => this.setInputs(this.getInputs(), true)
/** @param {Event} event */
#onInputCheckWrapHandler = event => this.#updateWrapClass(/** @type {HTMLElement} */(event.target))
/** @param {HTMLElement} inputElement*/
@@ -53,38 +50,34 @@ export default class IInputPinTemplate extends PinTemplate {
/** @param {PropertyValues} changedProperties */
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties)
this.#inputContentElements = /** @type {HTMLElement[]} */([...this.element.querySelectorAll("ueb-input")])
if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) {
this.element.addEventListener("input", this.#onInputCheckWrapHandler)
this.nameWidth = this.blueprint.scaleCorrect(
this.element.querySelector(".ueb-pin-name").getBoundingClientRect().width
)
this.inputContentElements.forEach(inputElement => this.#updateWrapClass(inputElement))
}
this.#inputContentElements = /** @type {HTMLElement[]} */([...this.element.querySelectorAll("ueb-input")])
}
setup() {
super.setup()
this.#inputContentElements.forEach(element => {
element.addEventListener("focusout", this.#onFocusOutHandler)
if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) {
element.addEventListener("input", this.#onInputCheckWrapHandler)
}
})
const Self = /** @type {typeof IInputPinTemplate} */(this.constructor)
if (Self.saveEachInputChange) {
this.element.addEventListener("input", this.#setInput)
} else {
this.element.addEventListener("focusout", this.#setInput)
}
if (/** @type {typeof IInputPinTemplate} */(this.constructor).canWrapInput) {
this.element.addEventListener("input", this.#onInputCheckWrapHandler)
}
}
cleanup() {
super.cleanup()
this.#inputContentElements.forEach(element => {
element.removeEventListener("focusout", this.#onFocusOutHandler)
element.removeEventListener("input", this.#onInputCheckWrapHandler)
})
}
this.element.removeEventListener("input", this.#onInputCheckWrapHandler)
this.element.removeEventListener("input", this.#setInput)
this.element.removeEventListener("focusout", this.#setInput)
createInputObjects() {
return [
...super.createInputObjects(),
...this.#inputContentElements.map(elem => new MouseIgnore(elem, this.blueprint)),
]
}
getInput() {
@@ -107,7 +100,8 @@ export default class IInputPinTemplate extends PinTemplate {
if (updateDefaultValue) {
this.setDefaultValue(values.map(v => IInputPinTemplate.stringFromInputToUE(v)), values)
}
this.element.addNextUpdatedCallbacks(() => this.element.nodeElement.acknowledgeReflow())
this.element.requestUpdate()
this.element.nodeElement.acknowledgeReflow()
}
setDefaultValue(values = [], rawValues = values) {
@@ -118,8 +112,9 @@ export default class IInputPinTemplate extends PinTemplate {
}
renderInput() {
const singleLine = /** @type {typeof IInputPinTemplate} */(this.constructor).singleLineInput
const selectOnFocus = /** @type {typeof IInputPinTemplate} */(this.constructor).selectOnFocus
const Self = /** @type {typeof IInputPinTemplate} */(this.constructor)
const singleLine = Self.singleLineInput
const selectOnFocus = Self.selectOnFocus
return html`
<div class="ueb-pin-input">
<ueb-input .singleLine="${singleLine}" .selectOnFocus="${selectOnFocus}"

View File

@@ -1,6 +1,10 @@
import ITemplate from "../ITemplate.js"
import MouseIgnore from "../../input/mouse/MouseIgnore.js"
/** @typedef {import ("../../element/InputElement").default} InputElement */
/**
* @typedef {import ("../../element/InputElement").default} InputElement
* @typedef {import ("lit").PropertyValues} PropertyValues
*/
/** @extends {ITemplate<InputElement>} */
export default class InputTemplate extends ITemplate {
@@ -17,7 +21,7 @@ export default class InputTemplate extends ITemplate {
getSelection().removeAllRanges() // Deselect eventually selected text inside the input
}
/** @param {InputEvent} e */
/** @param {Event} e */
#inputSingleLineHandler = e =>
/** @type {HTMLElement} */(e.target).querySelectorAll("br").forEach(br => br.remove())
@@ -36,6 +40,21 @@ export default class InputTemplate extends ITemplate {
this.element.contentEditable = "true"
}
/** @param {PropertyValues} changedProperties */
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties)
const event = new Event("input", { bubbles: true })
this.element.dispatchEvent(event)
}
createInputObjects() {
return [
...super.createInputObjects(),
// Prevents creating links when selecting text and other undesired mouse actions detection
new MouseIgnore(this.element, this.blueprint),
]
}
setup() {
super.setup()
this.element.addEventListener("focus", this.#focusHandler)

View File

@@ -9,7 +9,6 @@ export default class IntPinTemplate extends INumericPinTemplate {
setDefaultValue(values = [], rawValues = values) {
const integer = this.element.getDefaultValue(true)
integer.value = values[0]
this.inputContentElements[0].innerText = this.element.getDefaultValue()?.toString() // needed
this.element.requestUpdate()
}

View File

@@ -103,8 +103,8 @@ export default class PinTemplate extends ITemplate {
if (this.element.isInput() && changedProperties.has("isLinked")) {
// When connected, an input may drop its input fields which means the node has to reflow
const node = this.element.nodeElement
node.addNextUpdatedCallbacks(() => node.acknowledgeReflow())
node.requestUpdate()
this.element.requestUpdate()
this.element.updateComplete.then(() => node.acknowledgeReflow())
}
}