refactor(frontend): fix base template component
This commit is contained in:
@@ -432,7 +432,7 @@ export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
const val = parseIntWithHexOrBinary(input.value);
|
||||
if (!isNaN(val)) {
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.changeDeviceID(this.objectID, val)) {
|
||||
if (!vm.changeObjectID(this.objectID, val)) {
|
||||
input.value = this.objectID.toString();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,16 +1,58 @@
|
||||
import { Connection } from "ic10emu_wasm";
|
||||
import { DeviceDBConnection } from "../device_db";
|
||||
|
||||
const CableNetworkTypes: readonly string[] = Object.freeze(["Power", "Data", "PowerAndData"]);
|
||||
export function connectionFromDeviceDBConnection(conn: DeviceDBConnection): Connection {
|
||||
if (CableNetworkTypes.includes(conn.typ)) {
|
||||
return {
|
||||
import {
|
||||
Connection,
|
||||
ConnectionInfo,
|
||||
CableConnectionType,
|
||||
} from "ic10emu_wasm";
|
||||
export function connectionFromConnectionInfo(conn: ConnectionInfo): Connection {
|
||||
let connection: Connection = "None";
|
||||
if (
|
||||
conn.typ === "Power" ||
|
||||
conn.typ === "Data" ||
|
||||
conn.typ === "PowerAndData"
|
||||
) {
|
||||
connection = {
|
||||
CableNetwork: {
|
||||
net: window.VM.vm.ic10vm.defaultNetwork,
|
||||
typ: conn.typ
|
||||
}
|
||||
net: window.VM.vm.defaultNetwork,
|
||||
typ: conn.typ as CableConnectionType,
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "Pipe") {
|
||||
connection = {
|
||||
Pipe: {
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "PipeLiquid") {
|
||||
connection = {
|
||||
PipeLiquid: {
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "Chute") {
|
||||
connection = {
|
||||
Chute: {
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "Elevator") {
|
||||
connection = {
|
||||
Elevator: {
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "LaunchPad") {
|
||||
connection = {
|
||||
LaunchPad: {
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else if (conn.typ === "LandingPad") {
|
||||
connection = {
|
||||
LandingPad: {
|
||||
role: conn.role,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return "Other";
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,42 @@ import type {
|
||||
LogicField,
|
||||
LogicType,
|
||||
Slot,
|
||||
Class,
|
||||
FrozenObject,
|
||||
MemoryAccess,
|
||||
SlotInfo,
|
||||
ConnectionInfo,
|
||||
ObjectID,
|
||||
CableConnectionType,
|
||||
ConnectionRole,
|
||||
ObjectInfo,
|
||||
SlotOccupantInfo,
|
||||
} from "ic10emu_wasm";
|
||||
import { html, css, HTMLTemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
|
||||
import { connectionFromDeviceDBConnection } from "./dbutils";
|
||||
import { connectionFromConnectionInfo } from "./dbutils";
|
||||
import { crc32, displayNumber, parseNumber } from "utils";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
|
||||
import { VMDeviceCard } from "./card";
|
||||
import { VMTemplateDBMixin } from "virtual_machine/base_device";
|
||||
|
||||
export interface SlotTemplate {
|
||||
typ: Class
|
||||
quantity: number,
|
||||
occupant?: FrozenObject,
|
||||
}
|
||||
|
||||
export interface ConnectionCableNetwork {
|
||||
CableNetwork: {
|
||||
net: ObjectID | undefined;
|
||||
typ: CableConnectionType;
|
||||
role: ConnectionRole;
|
||||
};
|
||||
}
|
||||
|
||||
@customElement("vm-device-template")
|
||||
export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
|
||||
@@ -51,11 +75,12 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
`,
|
||||
];
|
||||
|
||||
@state() fields: { [key in LogicType]?: LogicField };
|
||||
@state() fields: Map<LogicType, number>;
|
||||
@state() slots: SlotTemplate[];
|
||||
@state() template: ObjectTemplate;
|
||||
@state() device_id: number | undefined;
|
||||
@state() device_name: string | undefined;
|
||||
@state() pins: (ObjectID | undefined)[];
|
||||
@state() template: FrozenObject;
|
||||
@state() objectId: number | undefined;
|
||||
@state() objectName: string | undefined;
|
||||
@state() connections: Connection[];
|
||||
|
||||
constructor() {
|
||||
@@ -63,41 +88,61 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
this.templateDB = window.VM.vm.templateDB;
|
||||
}
|
||||
|
||||
private _prefab_name: string;
|
||||
private _prefabName: string;
|
||||
private _prefabHash: number;
|
||||
|
||||
get prefab_name(): string {
|
||||
return this._prefab_name;
|
||||
|
||||
get prefabName(): string {
|
||||
return this._prefabName;
|
||||
}
|
||||
get prefabHash(): number {
|
||||
return this._prefabHash;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
set prefab_name(val: string) {
|
||||
this._prefab_name = val;
|
||||
set prefabName(val: string) {
|
||||
this._prefabName = val;
|
||||
this._prefabHash = crc32(this._prefabName);
|
||||
this.setupState();
|
||||
}
|
||||
|
||||
get dbTemplate(): ObjectTemplate {
|
||||
return this.templateDB.get( crc32(this.prefab_name));
|
||||
return this.templateDB.get(this._prefabHash);
|
||||
}
|
||||
|
||||
setupState() {
|
||||
const dbTemplate = this.dbTemplate;
|
||||
|
||||
this.fields = Object.fromEntries(
|
||||
Object.entries(this.dbTemplate?.logic ?? {}).map(([lt, ft]) => {
|
||||
const value = lt === "PrefabHash" ? this.dbTemplate.prefab.prefab_hash : 0.0;
|
||||
return [lt, { field_type: ft, value } as LogicField];
|
||||
this.fields = new Map(
|
||||
(
|
||||
Array.from(
|
||||
"logic" in dbTemplate
|
||||
? dbTemplate.logic.logic_types.entries() ?? []
|
||||
: [],
|
||||
) as [LogicType, MemoryAccess][]
|
||||
).map(([lt, access]) => {
|
||||
const value =
|
||||
lt === "PrefabHash" ? this.dbTemplate.prefab.prefab_hash : 0.0;
|
||||
return [lt, value];
|
||||
}),
|
||||
);
|
||||
|
||||
this.slots = (this.dbTemplate?.slots ?? []).map(
|
||||
this.slots = (
|
||||
("slots" in dbTemplate ? dbTemplate.slots ?? [] : []) as SlotInfo[]
|
||||
).map(
|
||||
(slot, _index) =>
|
||||
({
|
||||
typ: slot.typ,
|
||||
quantity: 0,
|
||||
}) as SlotTemplate,
|
||||
);
|
||||
|
||||
const connections = Object.entries(this.dbTemplate?.conn ?? {}).map(
|
||||
([index, conn]) =>
|
||||
[index, connectionFromDeviceDBConnection(conn)] as const,
|
||||
const connections = (
|
||||
"device" in dbTemplate
|
||||
? dbTemplate.device.connection_list
|
||||
: ([] as ConnectionInfo[])
|
||||
).map(
|
||||
(conn, index) => [index, connectionFromConnectionInfo(conn)] as const,
|
||||
);
|
||||
connections.sort((a, b) => {
|
||||
if (a[0] < b[0]) {
|
||||
@@ -110,12 +155,15 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
});
|
||||
|
||||
this.connections = connections.map((conn) => conn[1]);
|
||||
|
||||
const numPins = "device" in dbTemplate ? dbTemplate.device.device_pins_length : 0;
|
||||
this.pins = new Array(numPins).fill(undefined);
|
||||
}
|
||||
renderFields(): HTMLTemplateResult {
|
||||
const fields = Object.entries(this.fields);
|
||||
return html`
|
||||
${fields.map(([name, field], _index, _fields) => {
|
||||
return html`
|
||||
return html`
|
||||
<sl-input
|
||||
key="${name}"
|
||||
value="${displayNumber(field.value)}"
|
||||
@@ -127,7 +175,7 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
<span slot="suffix">${field.field_type}</span>
|
||||
</sl-input>
|
||||
`;
|
||||
})}
|
||||
})}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -135,9 +183,9 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
const input = e.target as SlInput;
|
||||
const field = input.getAttribute("key")! as LogicType;
|
||||
const val = parseNumber(input.value);
|
||||
this.fields[field].value = val;
|
||||
this.fields.set(field, val);
|
||||
if (field === "ReferenceId" && val !== 0) {
|
||||
this.device_id = val;
|
||||
this.objectId = val;
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
@@ -161,9 +209,11 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
return html`
|
||||
<div class="networks">
|
||||
${connections.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" ? connection.CableNetwork : null;
|
||||
return html`
|
||||
const conn =
|
||||
typeof connection === "object" && "CableNetwork" in connection
|
||||
? connection.CableNetwork
|
||||
: null;
|
||||
return html`
|
||||
<sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
@@ -175,13 +225,13 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${vmNetworks.map(
|
||||
(net) =>
|
||||
html`<sl-option value=${net}>Network ${net}</sl-option>`,
|
||||
)}
|
||||
(net) =>
|
||||
html`<sl-option value=${net}>Network ${net}</sl-option>`,
|
||||
)}
|
||||
<span slot="prefix"> ${conn?.typ} </span>
|
||||
</sl-select>
|
||||
`;
|
||||
})}
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -195,8 +245,48 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
renderPins(): HTMLTemplateResult {
|
||||
const device = this.templateDB.db[this.prefab_name];
|
||||
return html`<div class="pins"></div>`;
|
||||
const networks = this.connections.flatMap((connection, index) => {
|
||||
return typeof connection === "object" && "CableNetwork" in connection
|
||||
? [connection.CableNetwork.net]
|
||||
: [];
|
||||
});
|
||||
const visibleDeviceIds = [
|
||||
...new Set(
|
||||
networks.flatMap((net) => window.VM.vm.networkDataDevices(net)),
|
||||
),
|
||||
];
|
||||
const visibleDevices = visibleDeviceIds.map((id) =>
|
||||
window.VM.vm.objects.get(id),
|
||||
);
|
||||
const pinsHtml = this.pins?.map(
|
||||
(pin, index) =>
|
||||
html` <sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
.value=${pin}
|
||||
@sl-change=${this._handleChangePin}
|
||||
>
|
||||
<span slot="prefix">d${index}</span>
|
||||
${visibleDevices.map(
|
||||
(device, _index) => html`
|
||||
<sl-option value=${device.obj_info.id.toString()}>
|
||||
Device ${device.obj_info.id} :
|
||||
${device.obj_info.name ?? device.obj_info.prefab}
|
||||
</sl-option>
|
||||
`,
|
||||
)}
|
||||
</sl-select>`,
|
||||
);
|
||||
return html`<div class="pins">${pinsHtml}</div>`;
|
||||
}
|
||||
|
||||
_handleChangePin(e: CustomEvent) {
|
||||
const select = e.target as SlSelect;
|
||||
const pin = parseInt(select.getAttribute("key")!);
|
||||
const val = select.value ? parseInt(select.value as string) : undefined;
|
||||
this.pins[pin] = val
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -204,17 +294,17 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
return html`
|
||||
<sl-card class="template-card">
|
||||
<div class="header h-20 w-96" slot="header">
|
||||
<sl-tooltip content="${device?.name}">
|
||||
<sl-tooltip content="${device?.prefab.prefab_name}">
|
||||
<img
|
||||
class="image me-2"
|
||||
src="img/stationpedia/${device?.name}.png"
|
||||
src="img/stationpedia/${device?.prefab.prefab_name}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'"
|
||||
/>
|
||||
</sl-tooltip>
|
||||
<div class="vstack">
|
||||
<span class="prefab-title">${device.title}</span>
|
||||
<span class="prefab-name"><small>${device?.name}</small></span>
|
||||
<span class="prefab-hash"><small>${device?.hash}</small></span>
|
||||
<span>${device.prefab.name}</span>
|
||||
<span><small>${device?.prefab.prefab_name}</small></span>
|
||||
<span><small>${device?.prefab.prefab_hash}</small></span>
|
||||
</div>
|
||||
<sl-button
|
||||
class="ms-auto mt-auto mb-auto"
|
||||
@@ -244,19 +334,68 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
</sl-card>
|
||||
`;
|
||||
}
|
||||
_handleAddButtonClick() {
|
||||
async _handleAddButtonClick() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("add-device-template", { bubbles: true }),
|
||||
);
|
||||
const template: DeviceTemplate = {
|
||||
id: this.device_id,
|
||||
name: this.device_name,
|
||||
prefab_name: this.prefab_name,
|
||||
slots: this.slots,
|
||||
connections: this.connections,
|
||||
fields: this.fields,
|
||||
// Typescript doesn't like fileds defined as `X | undefined` not being present, hence cast
|
||||
const objInfo: ObjectInfo = {
|
||||
id: this.objectId,
|
||||
name: this.objectName,
|
||||
prefab: this.prefabName
|
||||
} as ObjectInfo;
|
||||
|
||||
if (this.slots.length > 0) {
|
||||
const slotOccupants: [FrozenObject, number][] = this.slots.flatMap((slot, index) => {
|
||||
return typeof slot.occupant !== "undefined" ? [[slot.occupant, index]] : [];
|
||||
})
|
||||
let slotOccupantTemplates: FrozenObject[] | null = null;
|
||||
let slotOccupantObjectIds: ObjectID[] | null = null;
|
||||
|
||||
let slotOccupantIdsMap: Map<number, number> = new Map();
|
||||
if (slotOccupants.length > 0) {
|
||||
slotOccupantTemplates = slotOccupants.map(([slot, _]) => slot);
|
||||
slotOccupantObjectIds = await window.VM.vm.addObjectsFrozen(slotOccupantTemplates);
|
||||
slotOccupantIdsMap = new Map(slotOccupants.map((_, index) => {
|
||||
return [index, slotOccupantObjectIds[index]];
|
||||
}))
|
||||
}
|
||||
objInfo.slots = new Map(this.slots.flatMap((slot, index) => {
|
||||
const occupantId = slotOccupantIdsMap.get(index);
|
||||
if (typeof occupantId !== "undefined") {
|
||||
const info: SlotOccupantInfo = {
|
||||
id: occupantId,
|
||||
quantity: slot.quantity
|
||||
};
|
||||
return [[index, info]] as [number, SlotOccupantInfo][];
|
||||
} else {
|
||||
return [] as [number, SlotOccupantInfo][];
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
if (this.connections.length > 0) {
|
||||
objInfo.connections = new Map(
|
||||
this.connections.flatMap((conn, index) => {
|
||||
return typeof conn === "object" &&
|
||||
"CableNetwork" in conn &&
|
||||
typeof conn.CableNetwork.net !== "undefined"
|
||||
? ([[index, conn.CableNetwork.net]] as [number, number][])
|
||||
: ([] as [number, number][]);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (this.fields.size > 0) {
|
||||
objInfo.logic_values = new Map(this.fields)
|
||||
}
|
||||
|
||||
const template: FrozenObject = {
|
||||
obj_info: objInfo,
|
||||
database_template: true,
|
||||
template: undefined,
|
||||
};
|
||||
window.VM.vm.addDeviceFromTemplate(template);
|
||||
await window.VM.vm.addObjectFrozen(template);
|
||||
|
||||
// reset state for new device
|
||||
this.setupState();
|
||||
|
||||
Reference in New Issue
Block a user