Unterstützung für IPN-Suche in der BOM-Konfiguration bei Part- und Assembly-Auswahl hinzufügen.

JavaScript BOM-Synchronisation (nur ein Part oder ein Assembly wählbar) ergänzen.
laceholder für Name-Angabe setzen: Entweder Part- oder Assembly-Name.
This commit is contained in:
Marcel Diegelmann 2026-04-08 15:45:43 +02:00
parent ca6254cc53
commit a126a8f7b6
10 changed files with 155 additions and 21 deletions

View file

@ -10,12 +10,19 @@ export default class extends Controller {
connect() {
//Check if tomselect is inside an modal and do not attach the dropdown to body in that case (as it breaks the modal)
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input', 'clear_button'],
searchField: ["name", "description", "category", "footprint"],
plugins: ['dropdown_input', this.element.required ? null : 'clear_button'],
searchField: ["name", "description", "category", "footprint", "ipn"],
valueField: "id",
labelField: "name",
dropdownParent: dropdownParent,
preload: "focus",
render: {
item: (data, escape) => {
@ -42,8 +49,9 @@ export default class extends Controller {
};
if (this.element.dataset.autocomplete) {
const base_url = this.element.dataset.autocomplete;
if (this.element.dataset.autocomplete || this.element.querySelector('[data-autocomplete]')) {
const autocompleteElement = this.element.dataset.autocomplete ? this.element : this.element.querySelector('[data-autocomplete]');
const base_url = autocompleteElement.dataset.autocomplete;
settings.valueField = "id";
settings.load = (query, callback) => {
const url = base_url.replace('__QUERY__', encodeURIComponent(query));
@ -57,7 +65,8 @@ export default class extends Controller {
};
this._tomSelect = new TomSelect(this.element, settings);
const targetElement = this.element instanceof HTMLInputElement || this.element instanceof HTMLSelectElement ? this.element : this.element.querySelector('select, input');
this._tomSelect = new TomSelect(targetElement, settings);
//this._tomSelect.clearOptions();
}
}
@ -67,4 +76,4 @@ export default class extends Controller {
//Destroy the TomSelect instance
this._tomSelect.destroy();
}
}
}

View file

@ -0,0 +1,94 @@
import {Controller} from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["part", "assembly", "name"];
connect() {
this.updatePlaceholder();
// Give TomSelect some time to initialize and set values
setTimeout(() => this.updatePlaceholder(), 100);
setTimeout(() => this.updatePlaceholder(), 500);
}
updatePlaceholder() {
const partSelect = this.hasPartTarget ? this.partTarget.querySelector('select, input') : null;
const assemblySelect = this.hasAssemblyTarget ? this.assemblyTarget.querySelector('select, input') : null;
const nameInput = this.hasNameTarget ? this.nameTarget : null;
if (!nameInput) return;
let selectedName = "";
// Helper to get name from tomselect
const getNameFromTS = (el) => {
if (el && el.tomselect) {
const val = el.tomselect.getValue();
if (val) {
const data = el.tomselect.options[val];
if (data && data.name) return data.name;
}
}
// Fallback for raw select
if (el && el.value && el.options && el.selectedIndex >= 0) {
return el.options[el.selectedIndex].text;
}
return "";
};
selectedName = getNameFromTS(partSelect);
if (!selectedName) {
selectedName = getNameFromTS(assemblySelect);
}
if (selectedName) {
nameInput.placeholder = selectedName;
if (nameInput.value === "") {
nameInput.style.opacity = "0.6";
} else {
nameInput.style.opacity = "1";
}
} else {
nameInput.placeholder = nameInput.dataset.originalPlaceholder || "";
nameInput.style.opacity = "1";
}
}
// This method will be called via action when a change occurs
sync(event) {
// Handle mutual exclusion: if a part is selected, clear the assembly (and vice-versa)
// We identify which field was changed by looking at the event target
const changedElement = event.target;
const partSelect = this.hasPartTarget ? this.partTarget.querySelector('select, input') : null;
const assemblySelect = this.hasAssemblyTarget ? this.assemblyTarget.querySelector('select, input') : null;
// If part was changed and has a value, clear assembly
if (partSelect && (changedElement === partSelect || partSelect.contains(changedElement))) {
const val = partSelect.tomselect ? partSelect.tomselect.getValue() : partSelect.value;
if (val && assemblySelect) {
if (assemblySelect.tomselect) {
assemblySelect.tomselect.clear(true); // true to silent event to avoid loops
} else {
assemblySelect.value = "";
}
}
}
// If assembly was changed and has a value, clear part
if (assemblySelect && (changedElement === assemblySelect || assemblySelect.contains(changedElement))) {
const val = assemblySelect.tomselect ? assemblySelect.tomselect.getValue() : assemblySelect.value;
if (val && partSelect) {
if (partSelect.tomselect) {
partSelect.tomselect.clear(true); // true to silent event to avoid loops
} else {
partSelect.value = "";
}
}
}
// Delay slightly to allow TomSelect to update its internal state if needed
setTimeout(() => {
this.updatePlaceholder();
}, 100);
}
}

View file

@ -19,7 +19,7 @@ export default class extends Controller {
let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input', this.element.required ? null : 'clear_button'],
searchField: ["name", "description", "category", "footprint"],
searchField: ["name", "description", "category", "footprint", "ipn"],
valueField: "id",
labelField: "name",
dropdownParent: dropdownParent,
@ -53,8 +53,9 @@ export default class extends Controller {
};
if (this.element.dataset.autocomplete) {
const base_url = this.element.dataset.autocomplete;
if (this.element.dataset.autocomplete || this.element.querySelector('[data-autocomplete]')) {
const autocompleteElement = this.element.dataset.autocomplete ? this.element : this.element.querySelector('[data-autocomplete]');
const base_url = autocompleteElement.dataset.autocomplete;
settings.valueField = "id";
settings.load = (query, callback) => {
const url = base_url.replace('__QUERY__', encodeURIComponent(query));
@ -68,7 +69,8 @@ export default class extends Controller {
};
this._tomSelect = new TomSelect(this.element, settings);
const targetElement = this.element instanceof HTMLInputElement || this.element instanceof HTMLSelectElement ? this.element : this.element.querySelector('select, input');
this._tomSelect = new TomSelect(targetElement, settings);
//this._tomSelect.clearOptions();
}
}