lingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
255 lines • 11 kB
JavaScript
import { jsx as _jsx } from "preact/jsx-runtime";
import { forceGetInstance, lazy, omit, pull } from "@lincode/utils";
import resetIcon from "./icons/resetIcon";
import getDefaultValue, { equalsDefaultValue } from "../../interface/utils/getDefaultValue";
import { emitEditorEdit } from "../../events/onEditorEdit";
import connectorInIcon from "./icons/connectorInIcon";
import connectorOutIcon from "./icons/connectorOutIcon";
import { setRuntimeValue } from "../../utils/getRuntimeValue";
import unsafeSetValue from "../../utils/unsafeSetValue";
import { render } from "preact";
import { unmountComponentAtNode } from "preact/compat";
import Toggle from "./Toggle";
import { isNullableCallbackParamInstance } from "../../interface/utils/NullableCallback";
import { isDefaultMethodArgInstance } from "../../interface/utils/DefaultMethod";
import { isPoint } from "../../utils/isPoint";
import executeIcon from "./icons/executeIcon";
import { getOriginalInstance, PassthroughCallback } from "./createParams";
import getStaticProperties from "../../display/utils/getStaticProperties";
import { stopPropagation } from "../utils/stopPropagation";
import { emitEditorRefresh } from "../../events/onEditorRefresh";
import { uuidMap } from "../../collections/idCollections";
import { isDefaultMethodArg, isNullableCallbackParam, isTemplateNode } from "../../collections/typeGuards";
import { inputSkipChangeSet } from "../../collections/inputSkipChangeSet";
import { defaultsOptionsMap } from "../../collections/defaultsCollections";
import { tweakpaneDownPtr } from "../../pointers/tweanpaneDownPtr";
import { tweakpaneChangePtr } from "../../pointers/tweakpaneChangePtr";
import { refreshInputSystem } from "../../systems/refreshInputSystem";
const processValue = (value) => {
if (typeof value === "string") {
if (!value)
return value;
if (value === "true" || value === "false")
return value === "true" ? true : false;
const num = Number(value);
if (!Number.isNaN(num))
return num;
}
return value;
};
let draggingItem;
const initConnectorOut = (connectorOut, target, key, connection, xyz) => {
stopPropagation(connectorOut);
connectorOut.draggable = true;
connectorOut.ondragstart = (e) => {
e.stopPropagation();
draggingItem = {
manager: target,
prop: key,
xyz
};
connectorOut.style.background = "rgba(255, 255, 255, 0.5)";
connection.onDragStart?.(e);
};
connectorOut.ondrag = (e) => {
e.stopPropagation();
connection.onDrag?.(e);
};
connectorOut.ondragend = (e) => {
e.stopPropagation();
connectorOut.style.background = "";
connection.onDragEnd?.(e, draggingItem);
draggingItem = undefined;
};
return connectorOut;
};
export const initConnectorIn = (connectorIn, key, connection) => {
connectorIn.ondragenter = (e) => {
e.stopPropagation();
connectorIn.style.background = "rgba(255, 255, 255, 0.5)";
};
connectorIn.ondragleave = (e) => {
e.stopPropagation();
connectorIn.style.background = "";
};
connectorIn.ondrop = (e) => {
e.stopPropagation();
connectorIn.style.background = "";
draggingItem && connection.onDrop?.(e, draggingItem, key);
};
return connectorIn;
};
const omitOptionsMap = new WeakMap();
export default async (handle, pane, title, target, params, prepend, connection, toggle) => {
if (!prepend)
await Promise.resolve();
const paramKeys = Object.keys(params);
if (!paramKeys.length)
return {};
const { defaults } = getStaticProperties(target);
const options = defaultsOptionsMap.get(defaults);
const optionsOmitted = options && omitOptionsMap.has(options)
? omit(options, omitOptionsMap.get(options))
: options;
const lazyFolder = lazy(() => {
const folder = pane.addFolder({ title });
handle.then(() => folder.dispose());
return folder;
});
const lazyCallbacksFolder = lazy(() => {
const folder = pane.addFolder({ title: "callbacks" });
handle.then(() => folder.dispose());
return folder;
});
const lazyMethodsFolder = lazy(() => {
const folder = pane.addFolder({ title: "methods" });
handle.then(() => folder.dispose());
return folder;
});
const instance = getOriginalInstance(target);
const result = Object.fromEntries(Object.keys(params).map((key) => {
let input;
const paramValue = params[key];
if (isDefaultMethodArg(paramValue)) {
const isDefaultValue = isDefaultMethodArgInstance(paramValue);
if (isDefaultValue)
params[key] = paramValue.value ?? "";
const execute = () => {
const paramValue = params[key];
if (isPoint(paramValue))
instance[key](paramValue.x, paramValue.y, paramValue.z);
else
instance[key](paramValue);
};
if ((isDefaultValue &&
paramValue.value !== undefined &&
paramValue.allowInput) ||
isPoint(paramValue))
input = lazyMethodsFolder().addInput(params, key);
else {
input = lazyMethodsFolder().addButton({
title: key,
label: key
});
input.on("click", execute);
}
const executeButton = executeIcon.cloneNode(true);
input.element.appendChild(executeButton);
executeButton.onclick = execute;
}
else if (isNullableCallbackParam(paramValue)) {
const isDefaultValue = isNullableCallbackParamInstance(paramValue);
if (isDefaultValue)
params[key] = paramValue.value ?? "";
if ((isDefaultValue &&
paramValue.value !== undefined &&
paramValue.allowInput) ||
isPoint(paramValue)) {
input = lazyCallbacksFolder().addInput(params, key);
unsafeSetValue(target, key,
// in createParams, callbacks are extended with PassthroughCallback
// so that when the callback is called, inputs will be refreshed
new PassthroughCallback((val) => {
params[key] = val;
inputSkipChangeSet.add(input);
input.refresh();
}, handle));
}
else
input = lazyCallbacksFolder().addButton({
title: key,
label: key
});
}
else {
input = lazyFolder().addInput(params, key, optionsOmitted?.[key]);
refreshInputSystem.add(input, { key, params, target });
const resetButton = resetIcon.cloneNode(true);
input.element.prepend(resetButton);
const updateResetButton = () => {
const unchanged = equalsDefaultValue(params[key], target, key) &&
!(options && omitOptionsMap.get(options)?.includes(key));
resetButton.style.opacity = unchanged ? "0.1" : "0.5";
resetButton.style.cursor = unchanged ? "auto" : "pointer";
};
updateResetButton();
resetButton.onclick = () => {
params[key] = structuredClone(getDefaultValue(target, key, true, true));
input.refresh();
const omitKeys = options && omitOptionsMap.get(options);
omitKeys && pull(omitKeys, key) && emitEditorRefresh();
};
input.on("change", ({ value }) => {
updateResetButton();
if (inputSkipChangeSet.has(input)) {
inputSkipChangeSet.delete(input);
return;
}
if (value === "custom" && options) {
forceGetInstance(omitOptionsMap, options, Array).push(key);
emitEditorRefresh();
return;
}
const processed = processValue(value);
!tweakpaneDownPtr[0] &&
emitEditorEdit({
phase: "start",
key,
value: processed
});
tweakpaneChangePtr[0] = [key, processed];
setRuntimeValue(target, defaults, key, processed);
if (isTemplateNode(instance)) {
const spawnNode = uuidMap.get(instance.spawnNode);
unsafeSetValue(forceGetInstance(spawnNode.patch, instance.uuid, Object), key, processed);
}
!tweakpaneDownPtr[0] &&
emitEditorEdit({
phase: "end",
key,
value: processed
});
});
}
if (connection) {
const els = input.element.querySelectorAll(".tp-pndtxtv_a");
if (els.length) {
let i = 0;
for (const el of els) {
const xyz = "xyz"[i++];
const connectorOut = initConnectorOut(connectorOutIcon.cloneNode(true), target, key, connection, xyz);
connectorOut.id =
target.uuid + " " + key + "-" + xyz + " out";
const { nextElementSibling } = el;
if (nextElementSibling)
el.parentElement.insertBefore(connectorOut, nextElementSibling);
else
el.parentElement.appendChild(connectorOut);
el.parentElement.classList.add("lingo3d-flexcenter");
}
return [key, input];
}
const connectorOut = initConnectorOut(connectorOutIcon.cloneNode(true), target, key, connection);
connectorOut.id = target.uuid + " " + key + " out";
input.element.appendChild(connectorOut);
const connectorIn = initConnectorIn(connectorInIcon.cloneNode(true), key, connection);
connectorIn.id = target.uuid + " " + key + " in";
input.element.prepend(connectorIn);
}
if (toggle) {
const div = document.createElement("div");
render(_jsx(Toggle, { manager: target, property: key }), div);
handle.then(() => unmountComponentAtNode(div));
input.element.appendChild(div);
}
return [key, input];
}));
handle.then(() => {
for (const input of Object.values(result)) {
refreshInputSystem.delete(input);
input.dispose();
}
});
return result;
};
//# sourceMappingURL=addInputs.js.map