Merge branch 'master' into feature/automatic-ipn-generation

This commit is contained in:
Jan Böhmer 2025-11-03 00:30:14 +01:00
commit 7a144d31c6
153 changed files with 18678 additions and 1739 deletions

View file

@ -20,11 +20,12 @@
import {Plugin} from 'ckeditor5';
require('./lang/de.js');
require('./lang/en.js');
import { addListToDropdown, createDropdown } from 'ckeditor5';
import {Collection} from 'ckeditor5';
import {Model} from 'ckeditor5';
import {UIModel} from 'ckeditor5';
export default class PartDBLabelUI extends Plugin {
init() {
@ -151,18 +152,28 @@ const PLACEHOLDERS = [
function getDropdownItemsDefinitions(t) {
const itemDefinitions = new Collection();
let first = true;
for ( const group of PLACEHOLDERS) {
//Add group header
itemDefinitions.add({
'type': 'separator',
model: new Model( {
withText: true,
})
});
//Skip separator for first group
if (!first) {
itemDefinitions.add({
'type': 'separator',
model: new UIModel( {
withText: true,
})
});
} else {
first = false;
}
itemDefinitions.add({
type: 'button',
model: new Model( {
model: new UIModel( {
label: t(group.label),
withText: true,
isEnabled: false,
@ -173,7 +184,7 @@ function getDropdownItemsDefinitions(t) {
for ( const entry of group.entries) {
const definition = {
type: 'button',
model: new Model( {
model: new UIModel( {
commandParam: entry[0],
label: t(entry[1]),
tooltip: entry[0],

View file

@ -17,15 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Make sure that the global object is defined. If not, define it.
window.CKEDITOR_TRANSLATIONS = window.CKEDITOR_TRANSLATIONS || {};
import {add} from "ckeditor5";
// Make sure that the dictionary for Polish translations exist.
window.CKEDITOR_TRANSLATIONS[ 'de' ] = window.CKEDITOR_TRANSLATIONS[ 'de' ] || {};
window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary = window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary || {};
// Extend the dictionary for Polish translations with your translations:
Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, {
add( "de", {
'Label Placeholder': 'Label Platzhalter',
'Part': 'Bauteil',
@ -88,5 +82,4 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, {
'Instance name': 'Instanzname',
'Target type': 'Zieltyp',
'URL of this Part-DB instance': 'URL dieser Part-DB Instanz',
} );
});

View file

@ -0,0 +1,84 @@
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {add} from "ckeditor5";
add( "en", {
'Label Placeholder': 'Label placeholder',
'Part': 'Part',
'Database ID': 'Database ID',
'Part name': 'Part name',
'Category': 'Category',
'Category (Full path)': 'Category (full path)',
'Manufacturer': 'Manufacturer',
'Manufacturer (Full path)': 'Manufacturer (full path)',
'Footprint': 'Footprint',
'Footprint (Full path)': 'Footprint (full path)',
'Mass': 'Mass',
'Manufacturer Product Number (MPN)': 'Manufacturer Product Number (MPN)',
'Internal Part Number (IPN)': 'Internal Part Number (IPN)',
'Tags': 'Tags',
'Manufacturing status': 'Manufacturing status',
'Description': 'Description',
'Description (plain text)': 'Description (plain text)',
'Comment': 'Comment',
'Comment (plain text)': 'Comment (plain text)',
'Last modified datetime': 'Last modified datetime',
'Creation datetime': 'Creation datetime',
'IPN as QR code': 'IPN as QR code',
'IPN as Code 128 barcode': 'IPN as Code 128 barcode',
'IPN as Code 39 barcode': 'IPN as Code 39 barcode',
'Lot ID': 'Lot ID',
'Lot name': 'Lot name',
'Lot comment': 'Lot comment',
'Lot expiration date': 'Lot expiration date',
'Lot amount': 'Lot amount',
'Storage location': 'Storage location',
'Storage location (Full path)': 'Storage location (full path)',
'Full name of the lot owner': 'Full name of the lot owner',
'Username of the lot owner': 'Username of the lot owner',
'Barcodes': 'Barcodes',
'Content of the 1D barcodes (like Code 39)': 'Content of the 1D barcodes (like Code 39)',
'Content of the 2D barcodes (QR codes)': 'Content of the 2D barcodes (QR codes)',
'QR code linking to this element': 'QR code linking to this element',
'Code 128 barcode linking to this element': 'Code 128 barcode linking to this element',
'Code 39 barcode linking to this element': 'Code 39 barcode linking to this element',
'Code 93 barcode linking to this element': 'Code 93 barcode linking to this element',
'Datamatrix code linking to this element': 'Datamatrix code linking to this element',
'Location ID': 'Location ID',
'Name': 'Name',
'Full path': 'Full path',
'Parent name': 'Parent name',
'Parent full path': 'Parent full path',
'Full name of the location owner': 'Full name of the location owner',
'Username of the location owner': 'Username of the location owner',
'Username': 'Username',
'Username (including name)': 'Username (including name)',
'Current datetime': 'Current datetime',
'Current date': 'Current date',
'Current time': 'Current time',
'Instance name': 'Instance name',
'Target type': 'Target type',
'URL of this Part-DB instance': 'URL of this Part-DB instance',
} );

View file

@ -34,6 +34,11 @@ export default class extends Controller {
connect() {
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
let settings = {
persistent: false,
create: true,
@ -42,7 +47,7 @@ export default class extends Controller {
selectOnTab: true,
//This a an ugly solution to disable the delimiter parsing of the TomSelect plugin
delimiter: 'VERY_L0NG_D€LIMITER_WHICH_WILL_NEVER_BE_ENCOUNTERED_IN_A_STRING',
dropdownParent: 'body',
dropdownParent: dropdownParent,
render: {
item: (data, escape) => {
return '<span>' + escape(data.label) + '</span>';

View file

@ -28,6 +28,27 @@ import {EditorWatchdog} from 'ckeditor5';
import "ckeditor5/ckeditor5.css";;
import "../../css/components/ckeditor.css";
const translationContext = require.context(
'ckeditor5/translations',
false,
//Only load the translation files we will really need
/(de|it|fr|ru|ja|cs|da|zh|pl|hu)\.js$/
);
function loadTranslation(language) {
if (!language || language === 'en') {
return null;
}
const lang = language.slice(0, 2);
const path = `./${lang}.js`;
if (translationContext.keys().includes(path)) {
const module = translationContext(path);
return module.default;
} else {
return null;
}
}
/* stimulusFetch: 'lazy' */
export default class extends Controller {
connect() {
@ -63,6 +84,13 @@ export default class extends Controller {
}
}
//Load translations if not english
let translations = loadTranslation(language);
if (translations) {
//Keep existing translations (e.g. from other plugins), if any
config.translations = [window.CKEDITOR_TRANSLATIONS, translations];
}
const watchdog = new EditorWatchdog();
watchdog.setCreator((elementOrData, editorConfig) => {
return EDITOR_TYPE.create(elementOrData, editorConfig)

View file

@ -10,13 +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'],
searchField: ["name", "description", "category", "footprint"],
valueField: "id",
labelField: "name",
dropdownParent: 'body',
dropdownParent: dropdownParent,
preload: "focus",
render: {
item: (data, escape) => {

View file

@ -38,13 +38,17 @@ export default class extends Controller {
this._emptyMessage = this.element.getAttribute('title');
}
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
let settings = {
plugins: ["clear_button"],
allowEmptyOption: true,
selectOnTab: true,
maxOptions: null,
dropdownParent: 'body',
dropdownParent: dropdownParent,
render: {
item: this.renderItem.bind(this),

View file

@ -26,10 +26,15 @@ export default class extends Controller {
_tomSelect;
connect() {
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
this._tomSelect = new TomSelect(this.element, {
maxItems: 1000,
allowEmptyOption: true,
dropdownParent: 'body',
dropdownParent: dropdownParent,
plugins: ['remove_button'],
});
}

View file

@ -40,6 +40,11 @@ export default class extends Controller {
connect() {
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
let settings = {
persistent: false,
create: true,
@ -50,7 +55,7 @@ export default class extends Controller {
valueField: 'text',
searchField: 'text',
orderField: 'text',
dropdownParent: 'body',
dropdownParent: dropdownParent,
//This a an ugly solution to disable the delimiter parsing of the TomSelect plugin
delimiter: 'VERY_L0NG_D€LIMITER_WHICH_WILL_NEVER_BE_ENCOUNTERED_IN_A_STRING',

View file

@ -40,7 +40,10 @@ export default class extends Controller {
const allowAdd = this.element.getAttribute("data-allow-add") === "true";
const addHint = this.element.getAttribute("data-add-hint") ?? "";
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
let settings = {
@ -54,7 +57,7 @@ export default class extends Controller {
maxItems: 1,
delimiter: "$$VERY_LONG_DELIMITER_THAT_SHOULD_NEVER_APPEAR$$",
splitOn: null,
dropdownParent: 'body',
dropdownParent: dropdownParent,
searchField: [
{field: "text", weight : 2},

View file

@ -33,6 +33,11 @@ export default class extends Controller {
_tomSelect;
connect() {
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}
let settings = {
plugins: {
remove_button:{},
@ -43,7 +48,7 @@ export default class extends Controller {
selectOnTab: true,
createOnBlur: true,
create: true,
dropdownParent: 'body',
dropdownParent: dropdownParent,
};
if(this.element.dataset.autocomplete) {