Implement attachment CollectionType using stimulus

This commit is contained in:
Jan Böhmer 2022-08-03 20:28:27 +02:00
parent bf3dba0fb2
commit 8323f374a4
10 changed files with 141 additions and 231 deletions

View file

@ -0,0 +1,29 @@
import {Controller} from "@hotwired/stimulus";
/**
* This controller synchronizes the filetype filters of the file input type with our selected attachment type
*/
export default class extends Controller
{
_selectInput;
_fileInput;
connect() {
//Find the select input for our attachment form
this._selectInput = this.element.querySelector('select');
//Find the file input for our attachment form
this._fileInput = this.element.querySelector('input[type="file"]');
this._selectInput.addEventListener('change', this.updateAllowedFiletypes.bind(this));
//Update file file on load
this.updateAllowedFiletypes();
}
updateAllowedFiletypes() {
let selected_option = this._selectInput.options[this._selectInput.selectedIndex];
let filetype_filter = selected_option.dataset.filetype_filter;
//Apply filetype filter to file input
this._fileInput.setAttribute('accept', filetype_filter);
}
}

View file

@ -95,9 +95,6 @@ class AttachmentFormType extends AbstractType
'label' => 'attachment.edit.attachment_type', 'label' => 'attachment.edit.attachment_type',
'class' => AttachmentType::class, 'class' => AttachmentType::class,
'disable_not_selectable' => true, 'disable_not_selectable' => true,
'attr' => [
'class' => 'attachment_type_selector',
],
]); ]);
$builder->add('showInTable', CheckboxType::class, [ $builder->add('showInTable', CheckboxType::class, [
@ -139,9 +136,9 @@ class AttachmentFormType extends AbstractType
'mapped' => false, 'mapped' => false,
'required' => false, 'required' => false,
'attr' => [ 'attr' => [
'class' => 'file', /*'class' => 'file',
'data-show-preview' => 'false', 'data-show-preview' => 'false',
'data-show-upload' => 'false', 'data-show-upload' => 'false',*/
], ],
'constraints' => [ 'constraints' => [
//new AllowedFileExtension(), //new AllowedFileExtension(),
@ -186,4 +183,9 @@ class AttachmentFormType extends AbstractType
'allow_builtins' => true, 'allow_builtins' => true,
]); ]);
} }
public function getBlockPrefix()
{
return 'attachment';
}
} }

View file

@ -114,9 +114,11 @@ class StructuralEntityType extends AbstractType
$resolver->setDefault('empty_message', null); $resolver->setDefault('empty_message', null);
$resolver->setDefault('controller', 'elements--selectpicker');
$resolver->setDefault('attr', static function (Options $options) { $resolver->setDefault('attr', static function (Options $options) {
$tmp = [ $tmp = [
'data-controller' => 'elements--selectpicker', 'data-controller' => $options['controller'],
'data-live-search' => true, 'data-live-search' => true,
'title' => 'selectpicker.nothing_selected', 'title' => 'selectpicker.nothing_selected',
]; ];

View file

@ -1,115 +1,3 @@
{% set delete_btn %} {% import "components/attachments.macro.html.twig" as attachments %}
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_attachment_entry(this);">
<i class="fas fa-trash-alt fa-fw"></i>
{% trans %}attachment.delete{% endtrans %}
</button>
{% endset %}
{#{{ form_row(form.master_picture_attachment) }} #} {{ attachments.attachment_edit_list(form.attachments) }}
<table class="table table-striped table-sm" id="attachments_table" data-prototype="{% if form.attachments.vars.prototype is defined %}{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody>
{% for attachment in form.attachments %}
<tr>
<td class="row">
<div class="col-9">
{{ form_widget(attachment) }}
</div>
<div class="ps-0 col-3">
{{ delete_btn }}
{% set attach = attachment.vars.value %}
{# @var attach \App\Entity\Attachments\Attachment #}
{% if attachment_manager.fileExisting(attach) %}
{% if not attach.external %}
<br><br>
<h6>
<span class="badge bg-primary">
<i class="fas fa-fw {{ ext_to_fa_icon(attach.extension) }}"></i> {{ attach.filename }}
</span>
<br>
<span class="badge bg-secondary">
<i class="fas fa-hdd fa-fw"></i> {{ attachment_manager.humanFileSize(attach) }}
</span>
</h6>
{% else %}
<br><br>
<h6>
<span class="badge bg-primary">
<i class="fas fa-fw fa-globe"></i> {% trans %}attachment.external{% endtrans %}
</span>
</h6>
{% endif %}
{% if attach.secure and not is_granted('show_private', attach) %}
{# Leave blank #}
{% elseif attach.picture %}
<a href="{{ attach | entityURL('file_view') }}" target="_blank" rel="noopener" data-turbo="false">
<img class="img-fluid img-thumbnail thumbnail-sm" src="{{ attachment_thumbnail(attach, 'thumbnail_md') }}" alt="{% trans %}attachment.preview.alt{% endtrans %}" />
</a>
{% else %}
<a href="{{ attach | entityURL('file_view') }}" rel="noopener" target="_blank" data-turbo="false" class="link-external">{% trans %}attachment.view{% endtrans %}</a>
{% endif %}
{% else %}
<br><br>
<h6>
<span class="badge bg-warning">
<i class="fas fa-exclamation-circle fa-fw"></i> {% trans %}attachment.file_not_found{% endtrans %}
</span>
</h6>
{% endif %}
{% if attach.secure %}
<h6>
<span class="badge bg-success">
<i class="fas fa-fw fa-shield-alt"></i> {% trans %}attachment.secure{% endtrans %}
</span>
</h6>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success" onclick="create_attachment_entry(this)">
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}attachment.create{% endtrans %}
</button>
<script>
function delete_attachment_entry(btn) {
window.bootbox.confirm('{% trans %}part_lot.edit.delete.confirm{% endtrans %}', function (result) {
if(result) {
$(btn).parents("tr").remove();
}
})
}
function create_attachment_entry(btn) {
//Determine the table, so we can determine, how many entries there are already.
$holder = $("#attachments_table");
var index = $holder.children("tbody").children("tr").length;
var newForm = $holder.data("prototype");
//Increase the index
newForm = newForm.replace(/__name__/g, index);
newForm = '<div class="col-9">' + newForm + '</div>';
$newFormRow = $('<tr></tr>').html("<td class='row'>" + newForm + "</td>");
//Add delete button
$btn = '<div class="col-3">' + '{{ delete_btn|e('js') }}' + '</div>';
$('td' ,$newFormRow).append($btn);
$holder.append($newFormRow);
//Reinit the selectpickers
$(".file").fileinput();
//Reinit typeahead:
$(document).trigger('attachment:create');
}
</script>

View file

@ -115,7 +115,7 @@
{%- block choice_widget_collapsed -%} {%- block choice_widget_collapsed -%}
{# Only add the BS5 form-select class if we dont use bootstrap-selectpicker #} {# Only add the BS5 form-select class if we dont use bootstrap-selectpicker #}
{% if attr["data-controller"] is defined and attr["data-controller"] != "elements--selectpicker" %} {% if attr["data-controller"] is defined and attr["data-controller"] not in ["elements--selectpicker"] %}
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-select')|trim}) -%} {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-select')|trim}) -%}
{% else %} {% else %}
{# If it is an selectpicker add form-control class to fill whole width #} {# If it is an selectpicker add form-control class to fill whole width #}

View file

@ -1,112 +1,5 @@
{% set delete_btn %} {% import "components/attachments.macro.html.twig" as attachments %}
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_attachment_entry(this);" {% if not is_granted('attachments.delete', part) %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i>
{% trans %}attachment.delete{% endtrans %}
</button>
{% endset %}
{{ form_row(form.master_picture_attachment) }} {{ form_row(form.master_picture_attachment) }}
<table class="table table-striped table-sm" id="attachments_table" data-prototype="{% if form.attachments.vars.prototype is defined %}{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}{% endif %}"> {{ attachments.attachment_edit_list(form.attachments) }}
<tbody>
{% for attachment in form.attachments %}
<tr>
<td>
{{ form_widget(attachment) }}
</td>
<td>
{{ delete_btn }}
{% set attach = attachment.vars.value %}
{% if attachment_manager.fileExisting(attach) %}
{% if not attach.external %}
<br><br>
<h6>
<span class="badge bg-primary">
<i class="fas fa-fw {{ ext_to_fa_icon(attach.extension) }}"></i> {{ attach.filename }}
</span>
<br>
<span class="badge bg-secondary">
<i class="fas fa-hdd fa-fw"></i> {{ attachment_manager.humanFileSize(attach) }}
</span>
</h6>
{% else %}
<br><br>
<h6>
<span class="badge bg-primary">
<i class="fas fa-fw fa-globe"></i> {% trans %}attachment.external{% endtrans %}
</span>
</h6>
{% endif %}
{% if attach.secure and not is_granted('show_private', attach) %}
{# Leave blank #}
{% elseif attach.picture %}
<a href="{{ attach | entityURL('file_view') }}" rel="noopener" target="_blank" data-turbo="false">
<img class="img-fluid img-thumbnail thumbnail-sm" src="{{ attachment_thumbnail(attach, 'thumbnail_md') }}" alt="{% trans %}attachment.preview.alt{% endtrans %}" />
</a>
{% else %}
<a href="{{ attach | entityURL('file_view') }}" rel="noopener" target="_blank" data-turbo="false" class="link-external">{% trans %}attachment.view{% endtrans %}</a>
{% endif %}
{% else %}
<br><br>
<h6>
<span class="badge bg-warning">
<i class="fas fa-exclamation-circle fa-fw"></i> {% trans %}attachment.file_not_found{% endtrans %}
</span>
</h6>
{% endif %}
{% if attach.secure %}
<h6>
<span class="badge bg-success">
<i class="fas fa-fw fa-shield-alt"></i> {% trans %}attachment.secure{% endtrans %}
</span>
</h6>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success" onclick="create_attachment_entry(this)" {% if not is_granted('attachments.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}attachment.create{% endtrans %}
</button>
<script>
function delete_attachment_entry(btn) {
window.bootbox.confirm('{% trans %}part_lot.edit.delete.confirm{% endtrans %}', function (result) {
if(result) {
$(btn).parents("tr").remove();
}
})
}
function create_attachment_entry(btn) {
//Determine the table, so we can determine, how many entries there are already.
$holder = $("#attachments_table");
var index = $holder.children("tbody").children("tr").length;
var newForm = $holder.data("prototype");
//Increase the index
newForm = newForm.replace(/__name__/g, index);
newForm = '<td>' + newForm + '</td>';
$newFormRow = $('<tr></tr>').html(newForm);
//Add delete button
$btn = '<td>' + '{{ delete_btn|e('js') }}' + '</td>';
$newFormRow.append($btn);
$holder.append($newFormRow);
$(".file").fileinput();
//Reinit typeahead:
$(document).trigger('attachment:create');
}
</script>

View file

@ -102,3 +102,73 @@
</td> </td>
</tr> </tr>
{% endblock %} {% endblock %}
{% block attachment_widget %}
{% import 'components/collection_type.macro.html.twig' as collection %}
{% dump(form) %}
<tr {{ stimulus_controller('elements/attachmenttype_change') }}>
<td>
{{ form_widget(form) }}
</td>
<td>
<button type="button" class="btn btn-danger lot_btn_delete" {{ collection.delete_btn() }} {# {% if not is_granted('attachments.delete', part) %}disabled{% endif %}#}>
<i class="fas fa-trash-alt fa-fw"></i>
{% trans %}attachment.delete{% endtrans %}
</button>
{% set attach = form.vars.value %}
{% if attach is not null %}
{% if attachment_manager.fileExisting(attach) %}
{% if not attach.external %}
<br><br>
<h6>
<span class="badge bg-primary">
<i class="fas fa-fw {{ ext_to_fa_icon(attach.extension) }}"></i> {{ attach.filename }}
</span>
<br>
<span class="badge bg-secondary">
<i class="fas fa-hdd fa-fw"></i> {{ attachment_manager.humanFileSize(attach) }}
</span>
</h6>
{% else %}
<br><br>
<h6>
<span class="badge bg-primary">
<i class="fas fa-fw fa-globe"></i> {% trans %}attachment.external{% endtrans %}
</span>
</h6>
{% endif %}
{% if attach.secure and not is_granted('show_private', attach) %}
{# Leave blank #}
{% elseif attach.picture %}
<a href="{{ attach | entityURL('file_view') }}" rel="noopener" target="_blank" data-turbo="false">
<img class="img-fluid img-thumbnail thumbnail-sm" src="{{ attachment_thumbnail(attach, 'thumbnail_md') }}" alt="{% trans %}attachment.preview.alt{% endtrans %}" />
</a>
{% else %}
<a href="{{ attach | entityURL('file_view') }}" rel="noopener" target="_blank" data-turbo="false" class="link-external">{% trans %}attachment.view{% endtrans %}</a>
{% endif %}
{% else %}
<br><br>
<h6>
<span class="badge bg-warning">
<i class="fas fa-exclamation-circle fa-fw"></i> {% trans %}attachment.file_not_found{% endtrans %}
</span>
</h6>
{% endif %}
{% if attach.secure %}
<h6>
<span class="badge bg-success">
<i class="fas fa-fw fa-shield-alt"></i> {% trans %}attachment.secure{% endtrans %}
</span>
</h6>
{% endif %}
{% endif %}
</td>
</tr>
{% endblock %}

View file

@ -0,0 +1,20 @@
{# Renders a editable list of all attachments. form is the Attachment CollectionType #}
{% macro attachment_edit_list(form, part_mode = false) %}
{% form_theme form with ['Parts/edit/edit_form_styles.html.twig'] %}
{% import 'components/collection_type.macro.html.twig' as collection %}
<div {{ collection.controller(form, 'attachment.edit.delete.confirm') }}>
<table class="table table-striped table-sm" {{ collection.target() }}>
<tbody>
{% for attachment in form %}
{{ form_widget(attachment) }}
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success" {{ collection.create_btn() }} {% if part_mode and not is_granted('attachments.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}attachment.create{% endtrans %}
</button>
</div>
{% endmacro %}

View file

@ -1,6 +1,6 @@
{% macro controller(form, deleteMessage) %} {% macro controller(form, deleteMessage) %}
{{ stimulus_controller('elements/collection_type', { {{ stimulus_controller('elements/collection_type', {
'deleteMessage': 'parameter.delete.confirm'|trans, 'deleteMessage': deleteMessage|trans,
'prototype': form_widget(form.vars.prototype)|e('html_attr') 'prototype': form_widget(form.vars.prototype)|e('html_attr')
}) }} }) }}
{% endmacro %} {% endmacro %}

View file

@ -9351,5 +9351,11 @@ Element 3</target>
<target>Actions finished successfully.</target> <target>Actions finished successfully.</target>
</segment> </segment>
</unit> </unit>
<unit id="_3l6FO_" name="attachment.edit.delete.confirm">
<segment>
<source>attachment.edit.delete.confirm</source>
<target>Do you really want to delete this attachment?</target>
</segment>
</unit>
</file> </file>
</xliff> </xliff>