248 lines
7.3 KiB
PHP
248 lines
7.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
|
|
namespace TYPO3\CMS\Backend\Template\Components\Buttons;
|
|
|
|
use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownItemInterface;
|
|
use TYPO3\CMS\Backend\Template\Components\Buttons\DropDown\DropDownRadio;
|
|
use TYPO3\CMS\Core\Imaging\Icon;
|
|
use TYPO3\CMS\Core\Imaging\IconSize;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
|
|
/**
|
|
* This button type is a container for dropdown items.
|
|
* It will render a dropdown containing all items attached
|
|
* to it. There are different kinds available, each item
|
|
* needs to implement the DropDownItemInterface. When this
|
|
* type contains elements of type DropDownRadio it will use
|
|
* the icon of the first active item of this type.
|
|
*
|
|
* Example:
|
|
*
|
|
* ```
|
|
* public function __construct(
|
|
* protected readonly ComponentFactory $componentFactory,
|
|
* ) {}
|
|
*
|
|
* public function myAction(): ResponseInterface
|
|
* {
|
|
* $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
|
* $dropDownButton = $this->componentFactory->createDropDownButton()
|
|
* ->setLabel('Dropdown')
|
|
* ->setTitle('Save')
|
|
* ->setIcon($this->iconFactory->getIcon('actions-heart'))
|
|
* ->setShowLabelText(true)
|
|
* ->addItem(
|
|
* $this->componentFactory->createDropDownItem()
|
|
* ->setLabel('Item')
|
|
* ->setHref('#')
|
|
* );
|
|
* $buttonBar->addButton($dropDownButton, ButtonBar::BUTTON_POSITION_RIGHT, 2);
|
|
* }
|
|
* ```
|
|
*/
|
|
class DropDownButton implements ButtonInterface
|
|
{
|
|
protected ?Icon $icon = null;
|
|
protected string $label = '';
|
|
protected ?string $title = null;
|
|
protected array $items = [];
|
|
protected bool $showLabelText = false;
|
|
protected bool $showActiveLabelText = false;
|
|
protected bool $disabled = false;
|
|
protected ButtonSize $size = ButtonSize::SMALL;
|
|
|
|
public function getIcon(): ?Icon
|
|
{
|
|
return $this->icon;
|
|
}
|
|
|
|
public function setIcon(?Icon $icon): static
|
|
{
|
|
$icon?->setSize(IconSize::SMALL);
|
|
$this->icon = $icon;
|
|
return $this;
|
|
}
|
|
|
|
public function getLabel(): string
|
|
{
|
|
return $this->label;
|
|
}
|
|
|
|
public function setLabel(string $label): static
|
|
{
|
|
$this->label = $label;
|
|
return $this;
|
|
}
|
|
|
|
public function getTitle(): string
|
|
{
|
|
return $this->title ?? $this->label;
|
|
}
|
|
|
|
public function setTitle(?string $title): static
|
|
{
|
|
$this->title = $title;
|
|
return $this;
|
|
}
|
|
|
|
public function getShowLabelText(): bool
|
|
{
|
|
return $this->showLabelText;
|
|
}
|
|
|
|
public function setShowLabelText(bool $showLabelText): static
|
|
{
|
|
$this->showLabelText = $showLabelText;
|
|
return $this;
|
|
}
|
|
|
|
public function getShowActiveLabelText(): bool
|
|
{
|
|
return $this->showActiveLabelText;
|
|
}
|
|
|
|
public function setShowActiveLabelText(bool $showActiveLabelText): static
|
|
{
|
|
$this->showActiveLabelText = $showActiveLabelText;
|
|
return $this;
|
|
}
|
|
|
|
public function isDisabled(): bool
|
|
{
|
|
return $this->disabled;
|
|
}
|
|
|
|
public function setDisabled(bool $disabled): static
|
|
{
|
|
$this->disabled = $disabled;
|
|
return $this;
|
|
}
|
|
|
|
public function addItem(DropDownItemInterface $item): static
|
|
{
|
|
if (!$item->isValid()) {
|
|
throw new \InvalidArgumentException(
|
|
'Only valid items may be assigned to a DropdownButton. "'
|
|
. $item->getType()
|
|
. '" did not pass validation',
|
|
1667645426
|
|
);
|
|
}
|
|
|
|
$this->items[] = clone $item;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getSize(): ButtonSize
|
|
{
|
|
return $this->size;
|
|
}
|
|
|
|
public function setSize(ButtonSize $size): DropDownButton
|
|
{
|
|
$this->size = $size;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return DropDownItemInterface[]
|
|
*/
|
|
public function getItems(): array
|
|
{
|
|
return $this->items;
|
|
}
|
|
|
|
public function isValid(): bool
|
|
{
|
|
return !empty($this->getLabel())
|
|
&& ($this->getShowLabelText() || $this->getIcon())
|
|
&& !empty($this->getItems());
|
|
}
|
|
|
|
public function getType(): string
|
|
{
|
|
return static::class;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
$items = $this->getItems();
|
|
|
|
/**
|
|
* @var DropDownRadio|null $activeItem
|
|
*/
|
|
$activeItem = null;
|
|
/**
|
|
* @var DropDownRadio[] $activeItems
|
|
*/
|
|
$activeItems = array_filter($items, static function (DropDownItemInterface $item): bool {
|
|
return $item instanceof DropDownRadio && $item->isActive();
|
|
});
|
|
if (!empty($activeItems)) {
|
|
$activeItem = array_shift($activeItems);
|
|
}
|
|
|
|
$attributes = [
|
|
'type' => 'button',
|
|
'class' => 'btn ' . $this->getSize()->value . ' btn-default dropdown-toggle',
|
|
'data-bs-toggle' => 'dropdown',
|
|
'aria-expanded' => 'false',
|
|
];
|
|
if ($this->isDisabled()) {
|
|
$attributes['disabled'] = 'disabled';
|
|
}
|
|
|
|
$buttonLabel = '';
|
|
if ($this->getShowLabelText()) {
|
|
if ($activeItem !== null && $this->getShowActiveLabelText()) {
|
|
// Render label with visually-hidden span for screen readers
|
|
$buttonLabel = '<span class="visually-hidden">' . htmlspecialchars($this->getLabel()) . ':</span> ' . htmlspecialchars($activeItem->getLabel());
|
|
} else {
|
|
// No active item or showActiveLabelText is false, just render the label
|
|
$buttonLabel = htmlspecialchars($this->getLabel());
|
|
}
|
|
} else {
|
|
// Add aria-label and title for accessibility when label text is not shown
|
|
// Both serve different purposes: aria-label for screen readers, title for visual tooltip
|
|
if ($activeItem !== null && $this->getShowActiveLabelText()) {
|
|
$attributes['aria-label'] = htmlspecialchars($this->getTitle() . ': ' . $activeItem->getLabel());
|
|
$attributes['title'] = htmlspecialchars($this->getTitle() . ': ' . $activeItem->getLabel());
|
|
} else {
|
|
$attributes['aria-label'] = $this->getTitle();
|
|
$attributes['title'] = $this->getTitle();
|
|
}
|
|
}
|
|
|
|
$icon = $activeItem?->getIcon()?->render() ?? $this->getIcon()?->render() ?? '';
|
|
$buttonContent = $icon . ($icon !== '' && $buttonLabel !== '' ? ' ' : '') . $buttonLabel;
|
|
|
|
return sprintf(
|
|
'<div class="btn-group"><button %s>%s</button><ul class="dropdown-menu">%s</ul></div>',
|
|
GeneralUtility::implodeAttributes($attributes, true),
|
|
$buttonContent,
|
|
implode('', array_map(static fn(DropDownItemInterface $item) => '<li>' . $item->render() . '</li>', $items))
|
|
);
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
return $this->render();
|
|
}
|
|
}
|