Skip to content

Instantly share code, notes, and snippets.

@wizhippo
Created December 10, 2021 15:17
Show Gist options
  • Save wizhippo/0498da2d48f0a6b7c318fa1b4c5e2be9 to your computer and use it in GitHub Desktop.
Save wizhippo/0498da2d48f0a6b7c318fa1b4c5e2be9 to your computer and use it in GitHub Desktop.
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% extends ea.templatePath('layout') %}
{% trans_default_domain ea.i18n.translationDomain %}
{% block body_id 'ea-detail-' ~ entity.name ~ '-' ~ entity.primaryKeyValue %}
{% block body_class 'ea-detail ea-detail-' ~ entity.name %}
{% block content_title %}
{%- apply spaceless -%}
{% set custom_page_title = ea.crud.customPageTitle(pageName, entity ? entity.instance : null) %}
{{ custom_page_title is null
? (ea.crud.defaultPageTitle|trans(ea.i18n.translationParameters, 'EasyAdminBundle'))|raw
: (custom_page_title|trans(ea.i18n.translationParameters))|raw }}
{%- endapply -%}
{% endblock %}
{% block page_actions %}
{% for action in entity.actions %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
{% endblock %}
{% block content_footer_wrapper '' %}
{% block main %}
{% block detail_fields %}
{% set has_tab = false %}
{% for field in entity.fields %}
{% if not has_tab %}
{% set has_tab = 'field-form_tab' in field.cssClass %}
{% endif %}
{% endfor %}
{% if has_tab %}
<div class="col-12">
<div class="nav-tabs-custom form-tabs">
<ul class="nav nav-tabs">
{% for field in entity.fields %}
{% set is_form_field_tab = 'field-form_tab' in field.cssClass %}
{% if is_form_field_tab %}
{% set tab_name = field is null ? null : 'content-' ~ field.uniqueId %}
{% set tab_icon = field is null ? null : (field.customOptions.get('icon')|default(false)) %}
{% set tab_label = field is null ? null : field.label %}
<li class="nav-item">
<a class="nav-link {% if loop.first %}active{% endif %}" href="#{{ tab_name }}" id="{{ tab_name }}-tab" data-bs-toggle="tab">
{%- if tab_icon -%}
<i class="fa fa-fw fa-{{ tab_icon }}"></i>
{%- endif -%}
{{ tab_label|trans(domain = ea.i18n.translationDomain) }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
<div class="tab-content">
{% endif %}
{% set form_tab_is_already_open = false %}
{% set form_panel_is_already_open = false %}
{% for field in entity.fields %}
{% set is_form_field_tab = 'field-form_tab' in field.cssClass %}
{% set is_form_field_panel = 'field-form_panel' in field.cssClass %}
{% if has_tab and (is_form_field_tab or (loop.first and not is_form_field_tab)) %}
{% if form_panel_is_already_open %}
{{ _self.close_form_field_panel() }}
{% endif %}
{% if form_tab_is_already_open %}
{{ _self.close_form_field_tab() }}
{% set form_tab_is_already_open = false %}
{% endif %}
{{ _self.open_form_field_tab(is_form_field_tab ? field : null, loop.first) }}
{% set form_tab_is_already_open = true %}
{% if form_panel_is_already_open %}
{{ _self.open_form_field_panel(null) }}
{% endif %}
{% endif %}
{% if is_form_field_panel or (loop.first and not is_form_field_panel) %}
{% if form_panel_is_already_open %}
{{ _self.close_form_field_panel() }}
{% set form_panel_is_already_open = false %}
{% endif %}
{{ _self.open_form_field_panel(is_form_field_panel ? field : null) }}
{% set form_panel_is_already_open = true %}
{% endif %}
{% block detail_field %}
{% if not (is_form_field_panel or is_form_field_tab) %}
{{ _self.render_field(entity, field) }}
{% endif %}
{% endblock %}
{% endfor %}
{{ _self.close_form_field_panel() }}
{% if has_tab %}
{{ _self.close_form_field_tab() }}
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block delete_form %}
{{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', { entity_id: entity.primaryKeyValue }, with_context = false) }}
{% endblock delete_form %}
{% endblock %}
{% macro open_form_field_tab(field = null, active = true) %}
{% set tab_name = field is null ? null : 'content-' ~ field.uniqueId %}
{% set tab_help = field is null ? null : field.help|default(false)%}
<div id="{{ tab_name }}" class="tab-pane {% if active %}active{% endif %}">
{% if tab_help %}
<div class="content-header-help tab-help">
{{ tab_help|trans(domain = ea.i18n.translationDomain)|raw }}
</div>
{% endif %}
<div class="row">
{% endmacro %}
{% macro close_form_field_tab() %}
</div>
</div>
{% endmacro %}
{% macro open_form_field_panel(field = null) %}
{% set panel_name = field is null ? null : 'content-' ~ field.uniqueId %}
{% set collapsible = field is null ? false : field.customOption('collapsible') %}
{% set collapsed = field is null ? false : field.customOption('collapsed') %}
{% set panel_icon = field is null ? null : (field.customOptions.get('icon')|default(false)) %}
{% set panel_label = field is null ? null : field.label %}
{% set panel_help = field is null ? null : field.help|default(false)%}
{% set panel_has_header = collapsible or panel_icon or panel_label or panel_help %}
<div class="{{ field.cssClass ?? '' }}">
<div class="form-panel">
{% if panel_has_header %}
<div class="form-panel-header {{ collapsible ? 'collapsible' }} {{ panel_help is not empty ? 'with-help' }}">
<div class="form-panel-title">
<a {% if not collapsible %}
href="#" class="not-collapsible"
{% else %}
href="#{{ panel_name }}" data-bs-toggle="collapse"
class="form-panel-collapse {{ collapsed ? 'collapsed' }}"
aria-expanded="{{ collapsed ? 'false' : 'true' }}" aria-controls="{{ panel_name }}"
{% endif %}
>
{% if collapsible %}
<i class="fas fw fa-chevron-right form-panel-collapse-marker"></i>
{% endif %}
{% if panel_icon %}
<i class="form-panel-icon {{ panel_icon }}"></i>
{% endif %}
{{ panel_label|raw }}
</a>
{% if panel_help %}
<div class="form-panel-help">{{ panel_help|raw }}</div>
{% endif %}
</div>
</div>
{% endif %}
<div {% if panel_name %}id="{{ panel_name }}"{% endif %} class="form-panel-body {{ collapsible ? 'collapse' }} {{ not collapsed ? 'show'}}">
<dl class="datalist">
{% endmacro %}
{% macro close_form_field_panel() %}
</dl>
</div>
</div>
</div>
{% endmacro %}
{% macro render_field(entity, field) %}
<div class="data-row {{ field.cssClass }}">
<dt>
{{ field.label|raw }}
{% if field.help is not empty %}
<span class="data-help">
<i class="far fa-question-circle" data-bs-toggle="tooltip" title="{{ field.help|e('html_attr') }}"></i>
</span>
{% endif %}
</dt>
<dd>
{{ include(field.templatePath, { field: field, entity: entity }, with_context = false) }}
</dd>
</div>
{% endmacro %}
<?php
namespace EasyCorp\Bundle\EasyAdminBundle\Field;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EaFormPanelType;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EaFormRowType;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\EasyAdminTabType;
use Symfony\Component\Uid\Ulid;
/**
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
*/
final class FormField implements FieldInterface
{
use FieldTrait;
public const OPTION_ICON = 'icon';
public const OPTION_COLLAPSIBLE = 'collapsible';
public const OPTION_COLLAPSED = 'collapsed';
public const OPTION_ROW_BREAKPOINT = 'rowBreakPoint';
/**
* @internal Use the other named constructors instead (addPanel(), etc.)
*
* @param string|false|null $label
*/
public static function new(string $propertyName, $label = null)
{
throw new \RuntimeException('Instead of this method, use the "addPanel()" method.');
}
/**
* @param string|false|null $label
*/
public static function addPanel($label = false, ?string $icon = null): self
{
$field = new self();
return $field
->setFieldFqcn(__CLASS__)
->hideOnIndex()
->setProperty('ea_form_panel_'.(new Ulid()))
->setLabel($label)
->setFormType(EaFormPanelType::class)
->addCssClass('field-form_panel')
->setFormTypeOptions(['mapped' => false, 'required' => false])
->setCustomOption(self::OPTION_ICON, $icon)
->setCustomOption(self::OPTION_COLLAPSIBLE, false)
->setCustomOption(self::OPTION_COLLAPSED, false);
}
/**
* @param string $breakpointName The name of the breakpoint where the new row is inserted
* It must be a valid Bootstrap 5 name ('', 'sm', 'md', 'lg', 'xl', 'xxl')
*/
public static function addRow(string $breakpointName = ''): self
{
$field = new self();
$validBreakpointNames = ['', 'sm', 'md', 'lg', 'xl', 'xxl'];
if (!\in_array($breakpointName, $validBreakpointNames, true)) {
throw new \InvalidArgumentException(sprintf('The value passed to the "addRow()" method of "FormField" can only be one of these values: "%s" ("%s" was given).', implode(', ', $validBreakpointNames), $breakpointName));
}
return $field
->setFieldFqcn(__CLASS__)
->hideOnIndex()
->setProperty('ea_form_row_'.(new Ulid()))
->setFormType(EaFormRowType::class)
->addCssClass('field-form_row')
->setFormTypeOptions(['mapped' => false, 'required' => false])
->setCustomOption(self::OPTION_ROW_BREAKPOINT, $breakpointName);
}
/**
* @return static
*/
public static function addTab(string $label, ?string $icon = null): self
{
$field = new self();
return $field
->setFieldFqcn(__CLASS__)
->hideOnIndex()
->setProperty('ea_form_tab_'.(new Ulid()))
->setLabel($label)
->setFormType(EasyAdminTabType::class)
->addCssClass('field-form_tab')
->setFormTypeOptions(['mapped' => false, 'required' => false])
->setCustomOption(self::OPTION_ICON, $icon);
}
public function setIcon(string $iconCssClass): self
{
$this->setCustomOption(self::OPTION_ICON, $iconCssClass);
return $this;
}
public function collapsible(bool $collapsible = true): self
{
if (!$this->hasLabelOrIcon()) {
throw new \InvalidArgumentException(sprintf('The %s() method used in one of your panels requires that the panel defines either a label or an icon, but it defines none of them.', __METHOD__));
}
$this->setCustomOption(self::OPTION_COLLAPSIBLE, $collapsible);
return $this;
}
public function renderCollapsed(bool $collapsed = true): self
{
if (!$this->hasLabelOrIcon()) {
throw new \InvalidArgumentException(sprintf('The %s() method used in one of your panels requires that the panel defines either a label or an icon, but it defines none of them.', __METHOD__));
}
$this->setCustomOption(self::OPTION_COLLAPSIBLE, true);
$this->setCustomOption(self::OPTION_COLLAPSED, $collapsed);
return $this;
}
private function hasLabelOrIcon(): bool
{
// don't use empty() because the label can contain only white spaces (it's a valid edge-case)
return (null !== $this->dto->getLabel() && '' !== $this->dto->getLabel())
|| null !== $this->dto->getCustomOption(self::OPTION_ICON);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment