Created
December 10, 2021 15:17
-
-
Save wizhippo/0498da2d48f0a6b7c318fa1b4c5e2be9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{# @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 %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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