mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-24 18:58:46 +02:00
Part select element now works properly
This commit is contained in:
parent
670dd76ef5
commit
7558d57545
3 changed files with 109 additions and 13 deletions
|
@ -12,19 +12,27 @@ export default class extends Controller {
|
||||||
|
|
||||||
let settings = {
|
let settings = {
|
||||||
allowEmptyOption: true,
|
allowEmptyOption: true,
|
||||||
|
plugins: ['dropdown_input'],
|
||||||
searchField: "name",
|
searchField: "name",
|
||||||
valueField: "id",
|
valueField: "id",
|
||||||
|
labelField: "name",
|
||||||
render: {
|
render: {
|
||||||
item: (data, escape) => {
|
item: (data, escape) => {
|
||||||
return '<span>' + "<img style='height: 1.5rem;' ' src='" + data.image + "'/>" + escape(data.name) + '</span>';
|
return '<span>' + (data.image ? "<img style='height: 1.5rem;' ' src='" + data.image + "'/>" : "") + escape(data.name) + '</span>';
|
||||||
},
|
},
|
||||||
option: (data, escape) => {
|
option: (data, escape) => {
|
||||||
|
if(data.text) {
|
||||||
|
return '<span>' + escape(data.text) + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
let tmp = '<div class="row m-0">' +
|
let tmp = '<div class="row m-0">' +
|
||||||
"<div class='col-2 p-0 d-flex align-items-center'><img class='typeahead-image' src='" + data.image + "'/></div>" +
|
"<div class='col-2 p-0 d-flex align-items-center'>" +
|
||||||
|
(data.image ? "<img class='typeahead-image' src='" + data.image + "'/>" : "") +
|
||||||
|
"</div>" +
|
||||||
"<div class='col-10'>" +
|
"<div class='col-10'>" +
|
||||||
'<h6 class="m-0">' + escape(data.name) + '</h6>' +
|
'<h6 class="m-0">' + escape(data.name) + '</h6>' +
|
||||||
'<p class="m-0">' + marked.parseInline(data.description) + '</p>' +
|
(data.description ? '<p class="m-0">' + marked.parseInline(data.description) + '</p>' : "") +
|
||||||
'<p class="m-0"><span class="fa-solid fa-tags fa-fw"></span> ' + escape(data.category);
|
(data.category ? '<p class="m-0"><span class="fa-solid fa-tags fa-fw"></span> ' + escape(data.category) : "");
|
||||||
|
|
||||||
if (data.footprint) { //If footprint is defined for the part show it next to the category
|
if (data.footprint) { //If footprint is defined for the part show it next to the category
|
||||||
tmp += ' <span class="fa-solid fa-microchip fa-fw"></span> ' + escape(data.footprint);
|
tmp += ' <span class="fa-solid fa-microchip fa-fw"></span> ' + escape(data.footprint);
|
||||||
|
@ -53,6 +61,7 @@ export default class extends Controller {
|
||||||
|
|
||||||
|
|
||||||
this._tomSelect = new TomSelect(this.element, settings);
|
this._tomSelect = new TomSelect(this.element, settings);
|
||||||
|
//this._tomSelect.clearOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,19 +3,74 @@
|
||||||
namespace App\Form\Type;
|
namespace App\Form\Type;
|
||||||
|
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
|
use App\Services\Attachments\AttachmentURLGenerator;
|
||||||
|
use App\Services\Attachments\PartPreviewGenerator;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||||
|
use Symfony\Component\Form\DataMapperInterface;
|
||||||
|
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\FormEvent;
|
||||||
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
|
||||||
class PartSelectType extends AbstractType
|
class PartSelectType extends AbstractType implements DataMapperInterface
|
||||||
{
|
{
|
||||||
private UrlGeneratorInterface $urlGenerator;
|
private UrlGeneratorInterface $urlGenerator;
|
||||||
|
private EntityManagerInterface $em;
|
||||||
|
private PartPreviewGenerator $previewGenerator;
|
||||||
|
private AttachmentURLGenerator $attachmentURLGenerator;
|
||||||
|
|
||||||
public function __construct(UrlGeneratorInterface $urlGenerator)
|
public function __construct(UrlGeneratorInterface $urlGenerator, EntityManagerInterface $em, PartPreviewGenerator $previewGenerator,
|
||||||
|
AttachmentURLGenerator $attachmentURLGenerator)
|
||||||
{
|
{
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
$this->em = $em;
|
||||||
|
$this->previewGenerator = $previewGenerator;
|
||||||
|
$this->attachmentURLGenerator = $attachmentURLGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
//At initialization we have to fill the form element with our selected data, so the user can see it
|
||||||
|
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
|
||||||
|
$form = $event->getForm();
|
||||||
|
$config = $form->getConfig()->getOptions();
|
||||||
|
$data = $event->getData() ?? [];
|
||||||
|
|
||||||
|
$config['compound'] = false;
|
||||||
|
$config['choices'] = is_iterable($data) ? $data : [$data];
|
||||||
|
$config['error_bubbling'] = true;
|
||||||
|
|
||||||
|
$form->add('autocomplete', EntityType::class, $config);
|
||||||
|
});
|
||||||
|
|
||||||
|
//After form submit, we have to add the selected element as choice, otherwise the form will not accept this element
|
||||||
|
$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
|
||||||
|
$data = $event->getData();
|
||||||
|
$form = $event->getForm();
|
||||||
|
$options = $form->get('autocomplete')->getConfig()->getOptions();
|
||||||
|
|
||||||
|
|
||||||
|
if (!isset($data['autocomplete']) || '' === $data['autocomplete']) {
|
||||||
|
$options['choices'] = [];
|
||||||
|
} else {
|
||||||
|
//Extract the ID from the submitted data
|
||||||
|
$id = $data['autocomplete'];
|
||||||
|
//Find the element in the database
|
||||||
|
$element = $this->em->find($options['class'], $id);
|
||||||
|
|
||||||
|
//Add the element as choice
|
||||||
|
$options['choices'] = [$element];
|
||||||
|
$options['error_bubbling'] = true;
|
||||||
|
$form->add('autocomplete', EntityType::class, $options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$builder->setDataMapper($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
@ -23,7 +78,9 @@ class PartSelectType extends AbstractType
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'class' => Part::class,
|
'class' => Part::class,
|
||||||
'choice_label' => 'name',
|
'choice_label' => 'name',
|
||||||
'placeholder' => 'None'
|
//'placeholder' => 'None',
|
||||||
|
'compound' => true,
|
||||||
|
'error_bubbling' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
|
@ -32,23 +89,49 @@ class PartSelectType extends AbstractType
|
||||||
'data-autocomplete' => $this->urlGenerator->generate('typeahead_parts', ['query' => '__QUERY__']),
|
'data-autocomplete' => $this->urlGenerator->generate('typeahead_parts', ['query' => '__QUERY__']),
|
||||||
//Disable browser autocomplete
|
//Disable browser autocomplete
|
||||||
'autocomplete' => 'off',
|
'autocomplete' => 'off',
|
||||||
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$resolver->setDefaults(['choices' => []]);
|
|
||||||
|
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
|
//Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request
|
||||||
'choice_attr' => ChoiceList::attr($this, function (?Part $part) {
|
'choice_attr' => ChoiceList::attr($this, function (?Part $part) {
|
||||||
|
if($part) {
|
||||||
|
//Determine the picture to show:
|
||||||
|
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($part);
|
||||||
|
if ($preview_attachment !== null) {
|
||||||
|
$preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment,
|
||||||
|
'thumbnail_sm');
|
||||||
|
} else {
|
||||||
|
$preview_url = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $part ? [
|
return $part ? [
|
||||||
//'data-description' => $part->getDescription(),
|
'data-description' => mb_strimwidth($part->getDescription(), 0, 127, '...'),
|
||||||
//'data-category' => $part->getCategory() ? $part->getCategory()->getName() : '',
|
'data-category' => $part->getCategory() ? $part->getCategory()->getName() : '',
|
||||||
|
'data-footprint' => $part->getFootprint() ? $part->getFootprint()->getName() : '',
|
||||||
|
'data-image' => $preview_url,
|
||||||
] : [];
|
] : [];
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getParent()
|
public function getBlockPrefix()
|
||||||
{
|
{
|
||||||
return EntityType::class;
|
return 'part_select';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function mapDataToForms($data, $forms)
|
||||||
|
{
|
||||||
|
$form = current(iterator_to_array($forms, false));
|
||||||
|
$form->setData($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mapFormsToData($forms, &$data)
|
||||||
|
{
|
||||||
|
$form = current(iterator_to_array($forms, false));
|
||||||
|
$data = $form->getData();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -124,3 +124,7 @@
|
||||||
|
|
||||||
{{- block("choice_widget_collapsed", "bootstrap_base_layout.html.twig") -}}
|
{{- block("choice_widget_collapsed", "bootstrap_base_layout.html.twig") -}}
|
||||||
{%- endblock choice_widget_collapsed -%}
|
{%- endblock choice_widget_collapsed -%}
|
||||||
|
|
||||||
|
{% block part_select_widget %}
|
||||||
|
{{ form_widget(form.autocomplete) }}
|
||||||
|
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue