mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-21 14:24:47 +08:00
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:
@@ -30,7 +30,7 @@ export default class Configuration {
|
||||
begin: "ueb-edit-text-begin",
|
||||
end: "ueb-edit-text-end",
|
||||
}
|
||||
static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom
|
||||
static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 1:1 zoom
|
||||
static expandGridSize = 400
|
||||
static focusEventName = {
|
||||
begin: "blueprint-focus",
|
||||
@@ -112,6 +112,7 @@ export default class Configuration {
|
||||
inputVectorAxisEvent: "/Script/BlueprintGraph.K2Node_InputVectorAxisEvent",
|
||||
isValid: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:IsValid",
|
||||
knot: "/Script/BlueprintGraph.K2Node_Knot",
|
||||
linearColor: "/Script/CoreUObject.LinearColor",
|
||||
macro: "/Script/BlueprintGraph.K2Node_MacroInstance",
|
||||
makeArray: "/Script/BlueprintGraph.K2Node_MakeArray",
|
||||
makeMap: "/Script/BlueprintGraph.K2Node_MakeMap",
|
||||
@@ -120,12 +121,18 @@ export default class Configuration {
|
||||
pawn: "/Script/Engine.Pawn",
|
||||
promotableOperator: "/Script/BlueprintGraph.K2Node_PromotableOperator",
|
||||
reverseForEachLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop",
|
||||
rotator: "/Script/CoreUObject.Rotator",
|
||||
select: "/Script/BlueprintGraph.K2Node_Select",
|
||||
spawnActorFromClass: "/Script/BlueprintGraph.K2Node_SpawnActorFromClass",
|
||||
switchEnum: "/Script/BlueprintGraph.K2Node_SwitchEnum",
|
||||
switchInteger: "/Script/BlueprintGraph.K2Node_SwitchInteger",
|
||||
switchName: "/Script/BlueprintGraph.K2Node_SwitchName",
|
||||
switchString: "/Script/BlueprintGraph.K2Node_SwitchString",
|
||||
userDefinedEnum: "/Script/Engine.UserDefinedEnum",
|
||||
variableGet: "/Script/BlueprintGraph.K2Node_VariableGet",
|
||||
variableSet: "/Script/BlueprintGraph.K2Node_VariableSet",
|
||||
vector: "/Script/CoreUObject.Vector",
|
||||
vector2D: "/Script/CoreUObject.Vector2D",
|
||||
whileLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop",
|
||||
}
|
||||
static pinColor = {
|
||||
@@ -184,6 +191,7 @@ export default class Configuration {
|
||||
/** @param {ObjectReferenceEntity} objectReferenceEntity */
|
||||
static subObjectAttributeNameFromReference = (objectReferenceEntity, nameOnly = false) =>
|
||||
this.subObjectAttributeNamePrefix + (!nameOnly ? "_" + objectReferenceEntity.type : "") + "_" + objectReferenceEntity.path
|
||||
static switchTargetPattern = /\/Script\/[\w\.\/\:]+K2Node_Switch([A-Z]\w+)+/
|
||||
static trackingMouseEventName = {
|
||||
begin: "ueb-tracking-mouse-begin",
|
||||
end: "ueb-tracking-mouse-end",
|
||||
|
||||
@@ -31,6 +31,13 @@ export default class Utility {
|
||||
}
|
||||
}
|
||||
|
||||
static arrayConverter = {
|
||||
/** @param {String} value */
|
||||
fromAttribute: (value, type) => value.split(/(?<!\\),/).map(v => v.trim()),
|
||||
/** @param {String[]} value */
|
||||
toAttribute: (value, type) => value.join(","),
|
||||
}
|
||||
|
||||
/** @param {Number} x */
|
||||
static sigmoid(x, curvature = 1.7) {
|
||||
return 1 / (1 + (x / (1 - x) ** -curvature))
|
||||
@@ -316,7 +323,7 @@ export default class Utility {
|
||||
static clearHTMLWhitespace(value) {
|
||||
return value
|
||||
.replaceAll(" ", "\u00A0") // whitespace
|
||||
.replaceAll(/<br\s*\/>|<br>/, "\n") // newlines
|
||||
.replaceAll(/<br\s*\/>|<br>/g, "\n") // newlines
|
||||
.replaceAll(/(\<!--.*?\-->)/g, "") // html comments
|
||||
}
|
||||
|
||||
|
||||
37
js/element/DropdownElement.js
Normal file
37
js/element/DropdownElement.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import DropdownTemplate from "../template/pin/DropdownTemplate.js"
|
||||
import IElement from "./IElement.js"
|
||||
|
||||
/** @extends {IElement<Object, DropdownTemplate>} */
|
||||
export default class DropdownElement extends IElement {
|
||||
|
||||
static properties = {
|
||||
...super.properties,
|
||||
options: {
|
||||
type: Object,
|
||||
},
|
||||
selected: {
|
||||
type: String,
|
||||
},
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
super.initialize({}, new DropdownTemplate())
|
||||
this.options = /** @type {[String, String][]} */([])
|
||||
this.selected = ""
|
||||
}
|
||||
|
||||
/** @param {[String, String][]} options */
|
||||
static newObject(options) {
|
||||
const result = new DropdownElement()
|
||||
return result
|
||||
}
|
||||
|
||||
initialize() {
|
||||
// Initialized in the constructor, this method does nothing
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.template.getSelectedValue()
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import Configuration from "../Configuration.js"
|
||||
*/
|
||||
export default class IElement extends LitElement {
|
||||
|
||||
#nextUpdatedCallbacks = []
|
||||
|
||||
/** @type {Blueprint} */
|
||||
#blueprint
|
||||
get blueprint() {
|
||||
@@ -83,11 +81,6 @@ export default class IElement extends LitElement {
|
||||
return this
|
||||
}
|
||||
|
||||
/** @param {PropertyValues} changedProperties */
|
||||
shouldUpdate(changedProperties) {
|
||||
return this.isInitialized && this.isConnected
|
||||
}
|
||||
|
||||
setup() {
|
||||
this.template.setup()
|
||||
this.isSetup = true
|
||||
@@ -125,18 +118,6 @@ export default class IElement extends LitElement {
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties)
|
||||
this.template.updated(changedProperties)
|
||||
// Remember the array might change while iterating
|
||||
for (const f of this.#nextUpdatedCallbacks) {
|
||||
f(changedProperties)
|
||||
}
|
||||
this.#nextUpdatedCallbacks = []
|
||||
}
|
||||
|
||||
addNextUpdatedCallbacks(callback, requestUpdate = false) {
|
||||
this.#nextUpdatedCallbacks.push(callback)
|
||||
if (requestUpdate) {
|
||||
this.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
acknowledgeDelete() {
|
||||
|
||||
@@ -13,7 +13,6 @@ import Utility from "../Utility.js"
|
||||
import VariableAccessNodeTemplate from "../template/node/VariableAccessNodeTemplate.js"
|
||||
import VariableConversionNodeTemplate from "../template/node/VariableConversionNodeTemplate.js"
|
||||
import VariableOperationNodeTemplate from "../template/node/VariableOperationNodeTemplate.js"
|
||||
import UnknownPinEntity from "../entity/UnknownPinEntity.js"
|
||||
|
||||
/**
|
||||
* @typedef {import("./IDraggableElement.js").DragEvent} DragEvent
|
||||
@@ -90,7 +89,8 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
// If selected, it will already drag, also must check if under nested comments, it must drag just once
|
||||
if (!this.selected && !this.#commentDragged) {
|
||||
this.#commentDragged = true
|
||||
this.addNextUpdatedCallbacks(() => this.#commentDragged = false)
|
||||
this.requestUpdate()
|
||||
this.updateComplete.then(() => this.#commentDragged = false)
|
||||
this.addLocation(...e.detail.value)
|
||||
}
|
||||
}
|
||||
@@ -193,11 +193,12 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
}
|
||||
}
|
||||
|
||||
getUpdateComplete() {
|
||||
return Promise.all([
|
||||
super.getUpdateComplete(),
|
||||
...this.getPinElements().map(pin => pin.updateComplete)
|
||||
]).then(() => true)
|
||||
async getUpdateComplete() {
|
||||
let result = await super.getUpdateComplete()
|
||||
for (const pin of this.getPinElements()) {
|
||||
result &&= await pin.updateComplete
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {NodeElement} commentNode */
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import BoolPinTemplate from "../template/pin/BoolPinTemplate.js"
|
||||
import Configuration from "../Configuration.js"
|
||||
import ElementFactory from "./ElementFactory.js"
|
||||
import EnumPinTemplate from "../template/pin/EnumPinTemplate.js"
|
||||
import ExecPinTemplate from "../template/pin/ExecPinTemplate.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import GuidEntity from "../entity/GuidEntity.js"
|
||||
@@ -39,18 +41,19 @@ import VectorPinTemplate from "../template/pin/VectorPinTemplate.js"
|
||||
export default class PinElement extends IElement {
|
||||
|
||||
static #inputPinTemplates = {
|
||||
"/Script/CoreUObject.LinearColor": LinearColorPinTemplate,
|
||||
"/Script/CoreUObject.Rotator": RotatorPinTemplate,
|
||||
"/Script/CoreUObject.Vector": VectorPinTemplate,
|
||||
"/Script/CoreUObject.Vector2D": Vector2DPinTemplate,
|
||||
"bool": BoolPinTemplate,
|
||||
"byte": IntPinTemplate,
|
||||
"enum": EnumPinTemplate,
|
||||
"int": IntPinTemplate,
|
||||
"int64": Int64PinTemplate,
|
||||
"MUTABLE_REFERENCE": ReferencePinTemplate,
|
||||
"name": NamePinTemplate,
|
||||
"real": RealPinTemplate,
|
||||
"string": StringPinTemplate,
|
||||
[Configuration.nodeType.linearColor]: LinearColorPinTemplate,
|
||||
[Configuration.nodeType.rotator]: RotatorPinTemplate,
|
||||
[Configuration.nodeType.vector]: VectorPinTemplate,
|
||||
[Configuration.nodeType.vector2D]: Vector2DPinTemplate,
|
||||
}
|
||||
|
||||
static properties = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ColorHandlerElement from "./ColorHandlerElement.js"
|
||||
import ColorSliderElement from "./ColorSliderElement.js"
|
||||
import DropdownElement from "./DropdownElement.js"
|
||||
import ElementFactory from "./ElementFactory.js"
|
||||
import InputElement from "./InputElement.js"
|
||||
import LinkElement from "./LinkElement.js"
|
||||
@@ -9,20 +10,17 @@ import SelectorElement from "./SelectorElement.js"
|
||||
import WindowElement from "./WindowElement.js"
|
||||
|
||||
export default function defineElements() {
|
||||
customElements.define("ueb-color-handler", ColorHandlerElement)
|
||||
ElementFactory.registerElement("ueb-color-handler", ColorHandlerElement)
|
||||
customElements.define("ueb-input", InputElement)
|
||||
ElementFactory.registerElement("ueb-input", InputElement)
|
||||
customElements.define("ueb-link", LinkElement)
|
||||
ElementFactory.registerElement("ueb-link", LinkElement)
|
||||
customElements.define("ueb-node", NodeElement)
|
||||
ElementFactory.registerElement("ueb-node", NodeElement)
|
||||
customElements.define("ueb-pin", PinElement)
|
||||
ElementFactory.registerElement("ueb-pin", PinElement)
|
||||
customElements.define("ueb-selector", SelectorElement)
|
||||
ElementFactory.registerElement("ueb-selector", SelectorElement)
|
||||
customElements.define("ueb-ui-slider", ColorSliderElement)
|
||||
ElementFactory.registerElement("ueb-ui-slider", ColorSliderElement)
|
||||
customElements.define("ueb-window", WindowElement)
|
||||
ElementFactory.registerElement("ueb-window", WindowElement)
|
||||
const define = (tag, type) => {
|
||||
customElements.define(tag, type)
|
||||
ElementFactory.registerElement(tag, type)
|
||||
}
|
||||
define("ueb-color-handler", ColorHandlerElement)
|
||||
define("ueb-dropdown", DropdownElement)
|
||||
define("ueb-input", InputElement)
|
||||
define("ueb-link", LinkElement)
|
||||
define("ueb-node", NodeElement)
|
||||
define("ueb-pin", PinElement)
|
||||
define("ueb-selector", SelectorElement)
|
||||
define("ueb-ui-slider", ColorSliderElement)
|
||||
define("ueb-window", WindowElement)
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export default class IEntity {
|
||||
|
||||
if (!suppressWarns) {
|
||||
if (!(attributeName in attributes)) {
|
||||
const typeName = value instanceof Array ? `[${value[0].constructor.name}]` : value.constructor.name
|
||||
const typeName = value instanceof Array ? `[${value[0]?.constructor.name}]` : value.constructor.name
|
||||
console.warn(
|
||||
`UEBlueprint: Attribute ${attributeName} (of type ${typeName}) in the serialized data is not `
|
||||
+ `defined in ${Self.name}.attributes`
|
||||
|
||||
@@ -90,6 +90,11 @@ export default class ObjectEntity extends IEntity {
|
||||
type: ObjectReferenceEntity,
|
||||
showDefault: false,
|
||||
},
|
||||
EnumEntries: {
|
||||
type: [String],
|
||||
showDefault: false,
|
||||
inlined: true,
|
||||
},
|
||||
InputKey: {
|
||||
type: SymbolEntity,
|
||||
showDefault: false,
|
||||
@@ -293,6 +298,7 @@ export default class ObjectEntity extends IEntity {
|
||||
/** @type {ObjectReferenceEntity?} */ this.TargetType
|
||||
/** @type {MacroGraphReferenceEntity?} */ this.MacroGraphReference
|
||||
/** @type {ObjectReferenceEntity?} */ this.Enum
|
||||
/** @type {String[]?} */ this.EnumEntries
|
||||
/** @type {SymbolEntity?} */ this.InputKey
|
||||
/** @type {Boolean?} */ this.bOverrideFunction
|
||||
/** @type {Boolean?} */ this.bInternalEvent
|
||||
@@ -434,7 +440,14 @@ export default class ObjectEntity extends IEntity {
|
||||
|
||||
/** @returns {PinEntity[]} */
|
||||
getPinEntities() {
|
||||
return this.CustomProperties.filter(v => v instanceof PinEntity)
|
||||
return this.CustomProperties.filter(v => v.constructor === PinEntity)
|
||||
}
|
||||
|
||||
switchTarget() {
|
||||
const switchMatch = this.getClass().match(Configuration.switchTargetPattern)
|
||||
if (switchMatch) {
|
||||
return switchMatch[1]
|
||||
}
|
||||
}
|
||||
|
||||
isEvent() {
|
||||
@@ -496,11 +509,20 @@ export default class ObjectEntity extends IEntity {
|
||||
)}`
|
||||
case Configuration.nodeType.switchEnum:
|
||||
return `Switch on ${this.Enum?.getName() ?? "Enum"}`
|
||||
case Configuration.nodeType.switchInteger:
|
||||
return `Switch on Int`
|
||||
case Configuration.nodeType.variableGet:
|
||||
return ""
|
||||
case Configuration.nodeType.variableSet:
|
||||
return "SET"
|
||||
}
|
||||
let switchTarget = this.switchTarget()
|
||||
if (switchTarget) {
|
||||
if (switchTarget[0] !== "E") {
|
||||
switchTarget = Utility.formatStringName(switchTarget)
|
||||
}
|
||||
return `Switch on ${switchTarget}`
|
||||
}
|
||||
const keyNameSymbol = this.getHIDAttribute()
|
||||
if (keyNameSymbol) {
|
||||
const keyName = keyNameSymbol.toString()
|
||||
@@ -535,7 +557,7 @@ export default class ObjectEntity extends IEntity {
|
||||
switch (memberParent) {
|
||||
case "/Script/Engine.KismetMathLibrary":
|
||||
if (memberName.startsWith("Conv_")) {
|
||||
return "" // Conversion nodes do not have visible names
|
||||
return "" // Conversion nodes do not have visible names
|
||||
}
|
||||
if (memberName.startsWith("Percent_")) {
|
||||
return "%"
|
||||
@@ -602,8 +624,9 @@ export default class ObjectEntity extends IEntity {
|
||||
return Configuration.nodeColors.gray
|
||||
case Configuration.nodeType.dynamicCast:
|
||||
return Configuration.nodeColors.turquoise
|
||||
case Configuration.nodeType.switchEnum:
|
||||
return Configuration.nodeColors.lime
|
||||
}
|
||||
if (this.switchTarget()) {
|
||||
return Configuration.nodeColors.lime
|
||||
}
|
||||
if (this.isEvent()) {
|
||||
return Configuration.nodeColors.red
|
||||
@@ -645,7 +668,9 @@ export default class ObjectEntity extends IEntity {
|
||||
case Configuration.nodeType.makeSet: return SVGIcon.makeSet
|
||||
case Configuration.nodeType.select: return SVGIcon.select
|
||||
case Configuration.nodeType.spawnActorFromClass: return SVGIcon.spawnActor
|
||||
case Configuration.nodeType.switchEnum: return SVGIcon.switch
|
||||
}
|
||||
if (this.switchTarget()) {
|
||||
return SVGIcon.switch
|
||||
}
|
||||
if (this.nodeDisplayName().startsWith("Break")) {
|
||||
return SVGIcon.breakStruct
|
||||
|
||||
@@ -31,10 +31,10 @@ import VectorEntity from "./VectorEntity.js"
|
||||
export default class PinEntity extends IEntity {
|
||||
|
||||
static #typeEntityMap = {
|
||||
"/Script/CoreUObject.LinearColor": LinearColorEntity,
|
||||
"/Script/CoreUObject.Rotator": RotatorEntity,
|
||||
"/Script/CoreUObject.Vector": VectorEntity,
|
||||
"/Script/CoreUObject.Vector2D": Vector2DEntity,
|
||||
[Configuration.nodeType.linearColor]: LinearColorEntity,
|
||||
[Configuration.nodeType.rotator]: RotatorEntity,
|
||||
[Configuration.nodeType.vector]: VectorEntity,
|
||||
[Configuration.nodeType.vector2D]: Vector2DEntity,
|
||||
"bool": Boolean,
|
||||
"byte": ByteEntity,
|
||||
"enum": EnumEntity,
|
||||
@@ -46,9 +46,9 @@ export default class PinEntity extends IEntity {
|
||||
"string": String,
|
||||
}
|
||||
static #alternativeTypeEntityMap = {
|
||||
"/Script/CoreUObject.Vector2D": SimpleSerializationVector2DEntity,
|
||||
"/Script/CoreUObject.Vector": SimpleSerializationVectorEntity,
|
||||
"/Script/CoreUObject.Rotator": SimpleSerializationRotatorEntity,
|
||||
[Configuration.nodeType.vector2D]: SimpleSerializationVector2DEntity,
|
||||
[Configuration.nodeType.vector]: SimpleSerializationVectorEntity,
|
||||
[Configuration.nodeType.rotator]: SimpleSerializationRotatorEntity,
|
||||
}
|
||||
static lookbehind = "Pin"
|
||||
static attributes = {
|
||||
|
||||
@@ -156,6 +156,9 @@ export default class Grammar {
|
||||
) {
|
||||
let result = defaultGrammar
|
||||
if (type instanceof Array) {
|
||||
if (attribute?.inlined) {
|
||||
return this.grammarFor(undefined, type[0])
|
||||
}
|
||||
result = P.seq(
|
||||
P.regex(/\(\s*/),
|
||||
this.grammarFor(undefined, type[0]).sepBy(this.commaSeparation),
|
||||
|
||||
@@ -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 */
|
||||
|
||||
49
js/template/pin/DropdownTemplate.js
Normal file
49
js/template/pin/DropdownTemplate.js
Normal 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
|
||||
}
|
||||
}
|
||||
60
js/template/pin/EnumPinTemplate.js
Normal file
60
js/template/pin/EnumPinTemplate.js
Normal 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()]
|
||||
}
|
||||
}
|
||||
@@ -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}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user