fix: slotType serial + feaures
- fix slotType serialisation - show slot images including class if no occupant - filter/search device list
@@ -14,6 +14,7 @@ use grammar::{BatchMode, LogicType, ReagentMode, SlotLogicType};
|
||||
use interpreter::{ICError, LineError};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{EnumIter, EnumString, AsRefStr};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::interpreter::ICState;
|
||||
@@ -348,8 +349,22 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
strum_macros::Display,
|
||||
EnumString,
|
||||
EnumIter,
|
||||
AsRefStr,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[strum(serialize_all = "PascalCase")]
|
||||
pub enum SlotType {
|
||||
Helmet = 1,
|
||||
Suit = 2,
|
||||
@@ -376,7 +391,7 @@ pub enum SlotType {
|
||||
Magazine,
|
||||
Circuit = 24,
|
||||
Bottle,
|
||||
ProgramableChip,
|
||||
ProgrammableChip,
|
||||
Glasses,
|
||||
CreditCard,
|
||||
DirtCanister,
|
||||
@@ -390,7 +405,6 @@ pub enum SlotType {
|
||||
Flare,
|
||||
Blocked,
|
||||
#[default]
|
||||
#[serde(other)]
|
||||
None = 0,
|
||||
}
|
||||
|
||||
@@ -423,8 +437,7 @@ impl Network {
|
||||
}
|
||||
|
||||
pub fn contains_all(&self, ids: &[u32]) -> bool {
|
||||
ids.iter()
|
||||
.all(|id| self.contains(id))
|
||||
ids.iter().all(|id| self.contains(id))
|
||||
}
|
||||
|
||||
pub fn contains_data(&self, id: &u32) -> bool {
|
||||
@@ -588,7 +601,7 @@ impl Device {
|
||||
),
|
||||
]);
|
||||
device.slots.push(Slot::with_occupant(
|
||||
SlotType::ProgramableChip,
|
||||
SlotType::ProgrammableChip,
|
||||
// -744098481 = ItemIntegratedCircuit10
|
||||
SlotOccupant::new(ic, -744098481),
|
||||
));
|
||||
@@ -710,7 +723,7 @@ impl Device {
|
||||
.slots
|
||||
.get(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?;
|
||||
if slot.typ == SlotType::ProgramableChip
|
||||
if slot.typ == SlotType::ProgrammableChip
|
||||
&& slot.occupant.is_some()
|
||||
&& self.ic.is_some()
|
||||
&& typ == SlotLogicType::LineNumber
|
||||
@@ -743,7 +756,7 @@ impl Device {
|
||||
.get(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?;
|
||||
let mut fields = slot.get_fields();
|
||||
if slot.typ == SlotType::ProgramableChip && slot.occupant.is_some() && self.ic.is_some() {
|
||||
if slot.typ == SlotType::ProgrammableChip && slot.occupant.is_some() && self.ic.is_some() {
|
||||
// try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic
|
||||
if let Ok(ic) = vm
|
||||
.ics
|
||||
@@ -784,7 +797,7 @@ impl Device {
|
||||
.slots
|
||||
.get_mut(index as usize)
|
||||
.ok_or(ICError::SlotIndexOutOfRange(index))?;
|
||||
if slot.typ == SlotType::ProgramableChip
|
||||
if slot.typ == SlotType::ProgrammableChip
|
||||
&& slot.occupant.is_some()
|
||||
&& self.ic.is_some()
|
||||
&& typ == SlotLogicType::LineNumber
|
||||
@@ -1114,7 +1127,7 @@ impl VM {
|
||||
let ic = slots
|
||||
.iter()
|
||||
.find_map(|slot| {
|
||||
if slot.typ == SlotType::ProgramableChip && slot.occupant.is_some() {
|
||||
if slot.typ == SlotType::ProgrammableChip && slot.occupant.is_some() {
|
||||
Some(slot.occupant.clone()).flatten()
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
mod utils;
|
||||
mod types;
|
||||
|
||||
use ic10emu::{grammar::{LogicType, SlotLogicType}, DeviceTemplate};
|
||||
use ic10emu::{
|
||||
grammar::{LogicType, SlotLogicType},
|
||||
DeviceTemplate,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{Registers, Stack};
|
||||
|
||||
@@ -61,12 +64,20 @@ impl DeviceRef {
|
||||
|
||||
#[wasm_bindgen(getter, js_name = "prefabName")]
|
||||
pub fn prefab_name(&self) -> Option<String> {
|
||||
self.device.borrow().prefab.as_ref().map(|prefab| prefab.name.clone())
|
||||
self.device
|
||||
.borrow()
|
||||
.prefab
|
||||
.as_ref()
|
||||
.map(|prefab| prefab.name.clone())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter, js_name = "prefabHash")]
|
||||
pub fn prefab_hash(&self) -> Option<i32> {
|
||||
self.device.borrow().prefab.as_ref().map(|prefab| prefab.hash)
|
||||
self.device
|
||||
.borrow()
|
||||
.prefab
|
||||
.as_ref()
|
||||
.map(|prefab| prefab.hash)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter, skip_typescript)]
|
||||
@@ -354,6 +365,10 @@ impl VM {
|
||||
#[wasm_bindgen(js_name = "addDeviceFromTemplate", skip_typescript)]
|
||||
pub fn add_device_from_template(&self, template: JsValue) -> Result<u32, JsError> {
|
||||
let template: DeviceTemplate = serde_wasm_bindgen::from_value(template)?;
|
||||
web_sys::console::log_2(
|
||||
&"(wasm) adding device".into(),
|
||||
&serde_wasm_bindgen::to_value(&template).unwrap(),
|
||||
);
|
||||
Ok(self.vm.borrow_mut().add_device_from_template(template)?)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -58,7 +58,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -80,7 +80,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -178,8 +178,7 @@
|
||||
" candidates.append((name, entry[\"name\"].removeprefix(\"Item\"), entry[\"name\"]))\n",
|
||||
" if entry[\"name\"].removeprefix(\"Structure\") in name:\n",
|
||||
" candidates.append((name, entry[\"name\"].removeprefix(\"Structure\"), entry[\"name\"]))\n",
|
||||
" image_candidates[entry[\"name\"]] = filter_candidates(candidates)\n",
|
||||
"\n"
|
||||
" image_candidates[entry[\"name\"]] = filter_candidates(candidates)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -191,7 +190,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -212,30 +211,30 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ItemBiomass []\n",
|
||||
"StructureBlocker []\n",
|
||||
"CartridgePlantAnalyser []\n",
|
||||
"StructureElevatorLevelIndustrial []\n",
|
||||
"ItemPlantEndothermic_Creative []\n",
|
||||
"Flag_ODA_10m []\n",
|
||||
"Flag_ODA_4m []\n",
|
||||
"Flag_ODA_6m []\n",
|
||||
"Flag_ODA_8m []\n",
|
||||
"ItemBiomass []\n",
|
||||
"ItemHorticultureBelt []\n",
|
||||
"ItemKitLiquidRegulator []\n",
|
||||
"ItemKitPortablesConnector []\n",
|
||||
"ItemMushroom ['ItemMushroom-resources.assets-3022.png', 'ItemMushroom-resources.assets-9304.png']\n",
|
||||
"ItemPlantEndothermic_Creative []\n",
|
||||
"ItemPlantThermogenic_Creative []\n",
|
||||
"Landingpad_GasConnectorInwardPiece []\n",
|
||||
"Landingpad_LiquidConnectorInwardPiece []\n",
|
||||
"ItemMushroom ['ItemMushroom-resources.assets-3022.png', 'ItemMushroom-resources.assets-9304.png']\n",
|
||||
"StructurePlinth []\n",
|
||||
"ItemPlantThermogenic_Creative []\n"
|
||||
"StructureBlocker []\n",
|
||||
"StructureElevatorLevelIndustrial []\n",
|
||||
"StructurePlinth []\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -254,14 +253,95 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1223 of 1223 | 100.00% \n",
|
||||
"missing Egg\n",
|
||||
"missing Appliance\n",
|
||||
"missing Ingot\n",
|
||||
"missing Torpedo\n",
|
||||
"missing Magazine\n",
|
||||
"missing SensorProcessingUnit\n",
|
||||
"missing LiquidCanister\n",
|
||||
"missing LiquidBottle\n",
|
||||
"missing Wreckage\n",
|
||||
"missing SoundCartridge\n",
|
||||
"missing DrillHead\n",
|
||||
"missing ScanningHead\n",
|
||||
"missing Flare\n",
|
||||
"missing Blocked\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"slot_types = [\n",
|
||||
" \"Helmet\",\n",
|
||||
" \"Suit\",\n",
|
||||
" \"Back\",\n",
|
||||
" \"GasFilter\",\n",
|
||||
" \"GasCanister\",\n",
|
||||
" \"MotherBoard\",\n",
|
||||
" \"Circuitboard\",\n",
|
||||
" \"DataDisk\",\n",
|
||||
" \"Organ\",\n",
|
||||
" \"Ore\",\n",
|
||||
" \"Plant\",\n",
|
||||
" \"Uniform\",\n",
|
||||
" \"Entity\",\n",
|
||||
" \"Battery\",\n",
|
||||
" \"Egg\",\n",
|
||||
" \"Belt\",\n",
|
||||
" \"Tool\",\n",
|
||||
" \"Appliance\",\n",
|
||||
" \"Ingot\",\n",
|
||||
" \"Torpedo\",\n",
|
||||
" \"Cartridge\",\n",
|
||||
" \"AccessCard\",\n",
|
||||
" \"Magazine\",\n",
|
||||
" \"Circuit\",\n",
|
||||
" \"Bottle\",\n",
|
||||
" \"ProgrammableChip\",\n",
|
||||
" \"Glasses\",\n",
|
||||
" \"CreditCard\",\n",
|
||||
" \"DirtCanister\",\n",
|
||||
" \"SensorProcessingUnit\",\n",
|
||||
" \"LiquidCanister\",\n",
|
||||
" \"LiquidBottle\",\n",
|
||||
" \"Wreckage\",\n",
|
||||
" \"SoundCartridge\",\n",
|
||||
" \"DrillHead\",\n",
|
||||
" \"ScanningHead\",\n",
|
||||
" \"Flare\",\n",
|
||||
" \"Blocked\",\n",
|
||||
"]\n",
|
||||
"sloticons = []\n",
|
||||
"for typ in slot_types:\n",
|
||||
" try_name = f\"sloticon_{typ.lower()}\"\n",
|
||||
" found = False\n",
|
||||
" for name in names:\n",
|
||||
" if name.startswith(try_name):\n",
|
||||
" sloticons.append([f\"SlotIcon_{typ}\", name])\n",
|
||||
" found = True\n",
|
||||
" if not found:\n",
|
||||
" print(f\"missing {typ}\")\n",
|
||||
"\n",
|
||||
"to_copy.extend(sloticons)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1266 of 1266 | 100.00% \n",
|
||||
"Done\n"
|
||||
]
|
||||
}
|
||||
@@ -284,13 +364,6 @@
|
||||
"print(\"Done\")\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
BIN
www/img/stationpedia/SlotIcon_AccessCard.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
www/img/stationpedia/SlotIcon_Back.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
www/img/stationpedia/SlotIcon_Battery.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
www/img/stationpedia/SlotIcon_Belt.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
www/img/stationpedia/SlotIcon_Bottle.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
www/img/stationpedia/SlotIcon_Cartridge.png
Normal file
|
After Width: | Height: | Size: 665 B |
BIN
www/img/stationpedia/SlotIcon_Circuit.png
Normal file
|
After Width: | Height: | Size: 961 B |
BIN
www/img/stationpedia/SlotIcon_Circuitboard.png
Normal file
|
After Width: | Height: | Size: 961 B |
BIN
www/img/stationpedia/SlotIcon_CreditCard.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
www/img/stationpedia/SlotIcon_DataDisk.png
Normal file
|
After Width: | Height: | Size: 483 B |
BIN
www/img/stationpedia/SlotIcon_DirtCanister.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
www/img/stationpedia/SlotIcon_Entity.png
Normal file
|
After Width: | Height: | Size: 791 B |
BIN
www/img/stationpedia/SlotIcon_GasCanister.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
www/img/stationpedia/SlotIcon_GasFilter.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
www/img/stationpedia/SlotIcon_Glasses.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
www/img/stationpedia/SlotIcon_Helmet.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
www/img/stationpedia/SlotIcon_Motherboard.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
www/img/stationpedia/SlotIcon_Ore.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
www/img/stationpedia/SlotIcon_Organ.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
www/img/stationpedia/SlotIcon_Plant.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
www/img/stationpedia/SlotIcon_ProgrammableChip.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
www/img/stationpedia/SlotIcon_Suit.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
www/img/stationpedia/SlotIcon_Tool.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
www/img/stationpedia/SlotIcon_Uniform.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
@@ -22,6 +22,9 @@ export class IC10Details extends SlDetails {
|
||||
.details__summary-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
.details__content {
|
||||
padding-top: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
SlotOccupantTemplate,
|
||||
SlotLogicType,
|
||||
ConnectionCableNetwork,
|
||||
SlotType,
|
||||
} from "ic10emu_wasm";
|
||||
import { html, css, HTMLTemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
@@ -94,6 +95,13 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
.device-name-hash::part(input) {
|
||||
width: 7rem;
|
||||
}
|
||||
.slot-header.image {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border: var(--sl-panel-border-width) solid var(--sl-panel-border-color);
|
||||
border-radius: var(--sl-border-radius-medium);
|
||||
background-color: var(--sl-color-neutral-0);
|
||||
}
|
||||
sl-divider {
|
||||
--spacing: 0.25rem;
|
||||
}
|
||||
@@ -119,12 +127,35 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
padding: var(--sl-spacing-small) var(--sl-spacing-medium);
|
||||
}
|
||||
sl-tab-group::part(base) {
|
||||
height: 16rem;
|
||||
max-height: 20rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
private _deviceDB: DeviceDB;
|
||||
|
||||
get deviceDB(): DeviceDB {
|
||||
return this._deviceDB;
|
||||
}
|
||||
|
||||
@state()
|
||||
set deviceDB(val: DeviceDB) {
|
||||
this._deviceDB = val;
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
window.VM!.addEventListener(
|
||||
"vm-device-db-loaded",
|
||||
this._handleDeviceDBLoad.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
_handleDeviceDBLoad(e: CustomEvent) {
|
||||
this.deviceDB = e.detail;
|
||||
}
|
||||
|
||||
onImageErr(e: Event) {
|
||||
this.image_err = true;
|
||||
console.log("Image load error", e);
|
||||
@@ -145,7 +176,8 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
}, this);
|
||||
return html`
|
||||
<sl-tooltip content="${this.prefabName}">
|
||||
<img class="image" src="img/stationpedia/${this.prefabName}.png" @onerr=${this.onImageErr} />
|
||||
<img class="image" src="img/stationpedia/${this.prefabName}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
</sl-tooltip>
|
||||
<div class="header-name">
|
||||
<sl-input id="vmDeviceCard${this.deviceID}Id" class="device-id" size="small" pill value=${this.deviceID}
|
||||
@@ -173,7 +205,7 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
const inputIdBase = `vmDeviceCard${this.deviceID}Field`;
|
||||
return html`
|
||||
${fields.map(([name, field], _index, _fields) => {
|
||||
return html` <sl-input id="${inputIdBase}${name}" key="${name}" value="${field.value}"
|
||||
return html` <sl-input id="${inputIdBase}${name}" key="${name}" value="${field.value}" size="small"
|
||||
?disabled=${field.field_type==="Read" } @sl-change=${this._handleChangeField}>
|
||||
<span slot="prefix">${name}</span>
|
||||
<sl-copy-button slot="suffix" from="${inputIdBase}${name}.value"></sl-copy-button>
|
||||
@@ -183,17 +215,53 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
lookupSlotOccupantImg(
|
||||
occupant: SlotOccupant | undefined,
|
||||
typ: SlotType,
|
||||
): string {
|
||||
if (typeof occupant !== "undefined") {
|
||||
const hashLookup = (this.deviceDB ?? {}).names_by_hash ?? {};
|
||||
const prefabName = hashLookup[occupant.prefab_hash] ?? "UnknownHash";
|
||||
return `img/stationpedia/${prefabName}.png`;
|
||||
} else {
|
||||
return `img/stationpedia/SlotIcon_${typ}.png`;
|
||||
}
|
||||
}
|
||||
|
||||
_onSlotImageErr(e: Event) {
|
||||
console.log("image_err", e);
|
||||
}
|
||||
|
||||
static transparentImg =
|
||||
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" as const;
|
||||
|
||||
renderSlot(slot: Slot, slotIndex: number): HTMLTemplateResult {
|
||||
const _fields = this.device.getSlotFields(slotIndex);
|
||||
const fields = Array.from(_fields.entries());
|
||||
const inputIdBase = `vmDeviceCard${this.deviceID}Slot${slotIndex}Field`;
|
||||
const slotImg = this.lookupSlotOccupantImg(slot.occupant, slot.typ);
|
||||
return html`
|
||||
<sl-card class="slot-card">
|
||||
<img slot="header" class="slot-header image" src="${slotImg}"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
<span slot="header" class="slot-header">${slotIndex} : ${slot.typ}</span>
|
||||
${
|
||||
typeof slot.occupant !== "undefined"
|
||||
? html`
|
||||
<span slot="header" class="slot-header">
|
||||
Occupant: ${slot.occupant.id} : ${slot.occupant.prefab_hash}
|
||||
</span>
|
||||
<span slot="header" class="slot-header">
|
||||
Quantity: ${slot.occupant.quantity}/
|
||||
${slot.occupant.max_quantity}
|
||||
</span>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
<div class="slot-fields">
|
||||
${fields.map(
|
||||
([name, field], _index, _fields) => html`
|
||||
<sl-input id="${inputIdBase}${name}" slotIndex=${slotIndex} key="${name}" value="${field.value}"
|
||||
<sl-input id="${inputIdBase}${name}" slotIndex=${slotIndex} key="${name}" value="${field.value}" size="small"
|
||||
?disabled=${field.field_type==="Read" } @sl-change=${this._handleChangeSlotField}>
|
||||
<span slot="prefix">${name}</span>
|
||||
<sl-copy-button slot="suffix" from="${inputIdBase}${name}.value"></sl-copy-button>
|
||||
@@ -221,42 +289,49 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
|
||||
renderNetworks(): HTMLTemplateResult {
|
||||
const vmNetworks = window.VM!.networks;
|
||||
return html`
|
||||
<div class="networks">
|
||||
${this.connections.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" ? connection.CableNetwork : null;
|
||||
return html`
|
||||
<sl-select hoist placement="top" clearable key=${index} value=${conn?.net} ?disabled=${conn===null}
|
||||
@sl-change=${this._handleChangeConnection}>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${vmNetworks.map(
|
||||
(net) =>
|
||||
html`<sl-option value=${net}>Network ${net}</sl-option>`,
|
||||
)}
|
||||
<span slot="prefix"> ${conn?.typ} </span>
|
||||
</sl-select>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
< div class="networks" >
|
||||
${
|
||||
this.connections.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" ? connection.CableNetwork : null;
|
||||
return html`
|
||||
<sl-select hoist placement="top" clearable key=${index} value=${conn?.net} ?disabled=${conn===null}
|
||||
@sl-change=${this._handleChangeConnection}>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${vmNetworks.map(
|
||||
(net) =>
|
||||
html`<sl-option value=${net}>Network ${net}</sl-option>`,
|
||||
)}
|
||||
<span slot="prefix"> ${conn?.typ} </span>
|
||||
</sl-select>
|
||||
`;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
renderPins(): HTMLTemplateResult {
|
||||
const pins = this.pins;
|
||||
const visibleDevices = window.VM!.visibleDevices(this.deviceID);
|
||||
return html`
|
||||
<div class="pins">
|
||||
${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.id}>
|
||||
Device ${device.id} : ${device.name ?? device.prefabName}
|
||||
</sl-option>`,
|
||||
)}
|
||||
</sl-select>`,
|
||||
)}
|
||||
< div class="pins" >
|
||||
${
|
||||
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.id}>
|
||||
Device ${device.id} : ${device.name ?? device.prefabName}
|
||||
</sl-option>
|
||||
`,
|
||||
)}
|
||||
</sl-select>`,
|
||||
)
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -383,9 +458,9 @@ export class VMDeviceList extends BaseElement {
|
||||
}
|
||||
|
||||
protected render(): HTMLTemplateResult {
|
||||
const deviceCards: HTMLTemplateResult[] = this.devices.map(
|
||||
const deviceCards: HTMLTemplateResult[] = this.filteredDeviceIds.map(
|
||||
(id, _index, _ids) =>
|
||||
html`<vm-device-card .deviceID=${id} class="device-list-card"></vm-device-card>`,
|
||||
html`< vm - device - card.deviceID=${ id } class="device-list-card" > </vm-device-card>`,
|
||||
);
|
||||
const result = html`
|
||||
<div class="header">
|
||||
@@ -393,6 +468,9 @@ export class VMDeviceList extends BaseElement {
|
||||
Devices:
|
||||
<sl-badge variant="neutral" pill>${this.devices.length}</sl-badge>
|
||||
</span>
|
||||
<sl-input class="device-filter-input" placeholder="Filter Devices" clearable @sl-input=${this._handleFilterInput}>
|
||||
<sl-icon slot="suffix" name="search"></sl-icon>"
|
||||
</sl-input>
|
||||
<vm-add-device-button class="ms-auto"></vm-add-device-button>
|
||||
</div>
|
||||
<div class="device-list">${deviceCards}</div>
|
||||
@@ -400,6 +478,70 @@ export class VMDeviceList extends BaseElement {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get filteredDeviceIds() {
|
||||
if (typeof this._filteredDeviceIds !== "undefined") {
|
||||
return this._filteredDeviceIds;
|
||||
} else {
|
||||
return this.devices;
|
||||
}
|
||||
}
|
||||
|
||||
private _filteredDeviceIds: number[] | undefined;
|
||||
private _filter: string = "";
|
||||
|
||||
@query(".device-filter-input") accessor filterInput: SlInput;
|
||||
get filter() {
|
||||
return this._filter;
|
||||
}
|
||||
|
||||
@state()
|
||||
set filter(val: string) {
|
||||
this._filter = val;
|
||||
this.performSearch();
|
||||
}
|
||||
|
||||
private filterTimeout: number | undefined;
|
||||
|
||||
_handleFilterInput(_e: CustomEvent) {
|
||||
if (this.filterTimeout) {
|
||||
clearTimeout(this.filterTimeout);
|
||||
}
|
||||
const that = this;
|
||||
this.filterTimeout = setTimeout(() => {
|
||||
that.filter = that.filterInput.value;
|
||||
that.filterTimeout = undefined;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
performSearch() {
|
||||
if (this._filter) {
|
||||
const datapoints: [string, number][] = [];
|
||||
for (const device_id of this.devices) {
|
||||
const device = window.VM.devices.get(device_id);
|
||||
if (device) {
|
||||
if (typeof device.name !== "undefined") {
|
||||
datapoints.push([device.name, device.id]);
|
||||
}
|
||||
if (typeof device.prefabName !== "undefined") {
|
||||
datapoints.push([device.prefabName, device.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
const haystack: string[] = datapoints.map((data) => data[0]);
|
||||
const uf = new uFuzzy({});
|
||||
const [_idxs, info, order] = uf.search(haystack, this._filter, 0, 1e3);
|
||||
|
||||
const filtered = order?.map((infoIdx) => datapoints[info.idx[infoIdx]]);
|
||||
const deviceIds: number[] =
|
||||
filtered
|
||||
?.map((data) => data[1])
|
||||
?.filter((val, index, arr) => arr.indexOf(val) === index) ?? [];
|
||||
this._filteredDeviceIds = deviceIds;
|
||||
} else {
|
||||
this._filteredDeviceIds = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("vm-add-device-button")
|
||||
@@ -465,7 +607,7 @@ export class VMAddDeviceButton extends BaseElement {
|
||||
this.performSearch();
|
||||
}
|
||||
|
||||
_filter: string = "";
|
||||
private _filter: string = "";
|
||||
|
||||
get filter() {
|
||||
return this._filter;
|
||||
@@ -482,7 +624,7 @@ export class VMAddDeviceButton extends BaseElement {
|
||||
private filterTimeout: number | undefined;
|
||||
|
||||
performSearch() {
|
||||
if (this.filter) {
|
||||
if (this._filter) {
|
||||
const uf = new uFuzzy({});
|
||||
const [_idxs, info, order] = uf.search(
|
||||
this._haystack,
|
||||
@@ -556,7 +698,6 @@ export class VMAddDeviceButton extends BaseElement {
|
||||
}
|
||||
|
||||
_handleSearchInput(e: CustomEvent) {
|
||||
console.log("search-input", e);
|
||||
if (this.filterTimeout) {
|
||||
clearTimeout(this.filterTimeout);
|
||||
}
|
||||
@@ -687,12 +828,11 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
const root = super.connectedCallback();
|
||||
super.connectedCallback();
|
||||
window.VM!.addEventListener(
|
||||
"vm-device-db-loaded",
|
||||
this._handleDeviceDBLoad.bind(this),
|
||||
);
|
||||
return root;
|
||||
}
|
||||
|
||||
_handleDeviceDBLoad(e: CustomEvent) {
|
||||
@@ -784,7 +924,8 @@ export class VmDeviceTemplate extends BaseElement {
|
||||
<sl-card class="template-card">
|
||||
<div class="header" slot="header">
|
||||
<sl-tooltip content="${device?.name}">
|
||||
<img class="image" src="img/stationpedia/${device?.name}.png" @onerr=${this.onImageErr} />
|
||||
<img class="image" src="img/stationpedia/${device?.name}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
</sl-tooltip>
|
||||
<div class="vstack">
|
||||
<span class="prefab-name">${device?.name}</span>
|
||||
|
||||