mirror of
https://github.com/Combodo/iTop.git
synced 2026-09-15 02:28:20 +02:00
- ButtonBar block with overflow controller
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
@import "alert";
|
||||
@import "button";
|
||||
@import "button-bar";
|
||||
@import "button-separator";
|
||||
@import "button-group";
|
||||
@import "breadcrumbs";
|
||||
|
||||
43
css/backoffice/components/_button-bar.scss
Normal file
43
css/backoffice/components/_button-bar.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button-bar--gap: 2px !default;
|
||||
$ibo-button-bar--track--gap: 6px !default;
|
||||
|
||||
/* button-bar.css */
|
||||
ibo-button-bar{
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
gap: $ibo-button-bar--gap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Keep content visible even if JS initialization is delayed. */
|
||||
|
||||
ibo-button-bar .ibo-button-bar--track{
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
gap: $ibo-button-bar--track--gap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
ibo-button-bar .ibo-button-bar--item{
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ibo-button-bar .ibo-button-bar--item[hidden]{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
ibo-button-bar .ibo-button-bar--extra{
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
@@ -61,6 +61,7 @@ $ibo-caselog-entry-form--lock-message--margin-left: 1rem !default;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ibo-caselog-entry-form--action-buttons--extra-actions .ibo-button-separator{
|
||||
vertical-align: center ;
|
||||
.ibo-caselog-entry-form--action-buttons--extra-actions{
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
}
|
||||
217
js/components/button-bar.js
Normal file
217
js/components/button-bar.js
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2024 Combodo SAS
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
class ButtonBar extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
// Guard against duplicate setup if the node is re-attached.
|
||||
this._initialized = false;
|
||||
// Retry counter for delayed jQuery plugin availability.
|
||||
this._popoverInitAttempts = 0;
|
||||
// Recompute distribution on resize.
|
||||
this._onResize = this.refresh.bind(this);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
this._initialized = true;
|
||||
|
||||
// Core DOM references rendered by the Twig template.
|
||||
this._track = this.querySelector('[data-role="ibo-button-bar--track"]') || this.querySelector('[data-role="ibo-overflow-line--track"]');
|
||||
this._extra = this.querySelector('[data-role="ibo-button-bar--extra"]') || this.querySelector('[data-role="ibo-overflow-line--extra"]');
|
||||
this._popover = this.querySelector('[data-role="ibo-popover-menu"]');
|
||||
// Toggler id is deterministic: <overflow-id>--toggler.
|
||||
const sTogglerId = `${this.id}--toggler`;
|
||||
const oToggler = document.getElementById(sTogglerId);
|
||||
this._toggler = oToggler && this.contains(oToggler) ? oToggler : null;
|
||||
|
||||
if (!this._track || !this._extra || !this._popover || !this._toggler) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._bindEvents();
|
||||
|
||||
if (window.ResizeObserver) {
|
||||
this._resizeObserver = new ResizeObserver(this._onResize);
|
||||
this._resizeObserver.observe(this);
|
||||
this._resizeObserver.observe(this._track);
|
||||
} else {
|
||||
window.addEventListener("resize", this._onResize);
|
||||
}
|
||||
|
||||
// Observe both source actions and popover entries (both can be updated dynamically).
|
||||
this._mutationObserver = new MutationObserver(() => this.refresh());
|
||||
this._mutationObserver.observe(this._track, { childList: true, subtree: true, characterData: true });
|
||||
this._mutationObserver.observe(this._popover, { childList: true, subtree: true });
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this._resizeObserver?.disconnect();
|
||||
this._mutationObserver?.disconnect();
|
||||
if (this._popoverInitTimer) {
|
||||
clearTimeout(this._popoverInitTimer);
|
||||
}
|
||||
this._popover?.removeEventListener("click", this._onPopoverClick);
|
||||
window.removeEventListener("resize", this._onResize);
|
||||
}
|
||||
|
||||
_bindEvents() {
|
||||
this._onPopoverClick = (event) => {
|
||||
// Popover entries map to source actions through data-overflow-item-id.
|
||||
const oMenuItem = event.target.closest('[data-role="ibo-popover-menu--item"][data-overflow-item-id]');
|
||||
if (!oMenuItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
const sItemId = oMenuItem.dataset.overflowItemId;
|
||||
const oSource = this._itemsById[sItemId];
|
||||
if (!oSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward click to the original UI action.
|
||||
const oClickable = oSource.querySelector('a[href], button, [role="tab"], [data-role="ibo-tab-container--tab-toggler"]');
|
||||
(oClickable || oSource).click();
|
||||
};
|
||||
|
||||
this._popover.addEventListener("click", this._onPopoverClick);
|
||||
}
|
||||
|
||||
_refreshCollections() {
|
||||
// Source actions are direct children of the track.
|
||||
this._items = Array.from(this._track.children).filter((oItem) => Boolean(oItem.dataset.overflowItemId));
|
||||
this._itemsById = {};
|
||||
this._items.forEach((oItem) => {
|
||||
if (oItem.dataset.overflowItemId) {
|
||||
this._itemsById[oItem.dataset.overflowItemId] = oItem;
|
||||
}
|
||||
});
|
||||
|
||||
// Popover entries are generated server-side with the same mapping id.
|
||||
this._menuItems = Array.from(this._popover.querySelectorAll('[data-role="ibo-popover-menu--item"][data-overflow-item-id]'));
|
||||
this._menuItemsById = {};
|
||||
this._menuItems.forEach((oItem) => {
|
||||
this._menuItemsById[oItem.dataset.overflowItemId] = oItem;
|
||||
});
|
||||
}
|
||||
|
||||
_outerWidth(oElem) {
|
||||
const oRect = oElem.getBoundingClientRect();
|
||||
const oStyle = getComputedStyle(oElem);
|
||||
return oRect.width + (parseFloat(oStyle.marginLeft) || 0) + (parseFloat(oStyle.marginRight) || 0);
|
||||
}
|
||||
|
||||
_flexGap(oElem) {
|
||||
const oStyle = getComputedStyle(oElem);
|
||||
const iGap = parseFloat(oStyle.columnGap);
|
||||
return Number.isFinite(iGap) ? iGap : 0;
|
||||
}
|
||||
|
||||
_closePopoverIfOpen() {
|
||||
if (window.jQuery && window.jQuery(this._popover).data("itop-popover_menu")) {
|
||||
window.jQuery(this._popover).popover_menu("closePopup");
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._refreshCollections();
|
||||
this.layout();
|
||||
}
|
||||
|
||||
layout() {
|
||||
if (!this._items || this._items.length === 0) {
|
||||
this._extra.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) Reset visibility before computing overflow.
|
||||
this._items.forEach((oItem) => {
|
||||
oItem.hidden = false;
|
||||
});
|
||||
this._menuItems.forEach((oItem) => {
|
||||
oItem.hidden = true;
|
||||
});
|
||||
|
||||
// 2) No mapping => keep source actions visible and hide overflow controls.
|
||||
if (this._menuItems.length === 0) {
|
||||
this._extra.hidden = true;
|
||||
this._closePopoverIfOpen();
|
||||
return;
|
||||
}
|
||||
|
||||
const iHostWidth = this.clientWidth;
|
||||
if (iHostWidth <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const aWidths = this._items.map((oItem) => this._outerWidth(oItem));
|
||||
const iTrackGap = this._flexGap(this._track);
|
||||
const iTotalWidth = aWidths.reduce((iSum, iWidth) => iSum + iWidth, 0) + Math.max(0, this._items.length - 1) * iTrackGap;
|
||||
|
||||
// 3) Everything fits: hide the overflow button.
|
||||
if (iTotalWidth <= iHostWidth) {
|
||||
this._extra.hidden = true;
|
||||
this._closePopoverIfOpen();
|
||||
return;
|
||||
}
|
||||
this._extra.hidden = false;
|
||||
|
||||
// 4) Keep items while there is space, move overflowing ones to popover.
|
||||
const iHostGap = this._flexGap(this);
|
||||
const iAvailableWidth = Math.max(0, iHostWidth - this._outerWidth(this._extra) - iHostGap);
|
||||
let iUsedWidth = 0;
|
||||
let bHasHiddenItems = false;
|
||||
let bOverflowStarted = false;
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
const oItem = this._items[i];
|
||||
const iWidth = aWidths[i];
|
||||
const iGapBeforeItem = i > 0 ? iTrackGap : 0;
|
||||
|
||||
if (!bOverflowStarted && iUsedWidth + iGapBeforeItem + iWidth <= iAvailableWidth) {
|
||||
iUsedWidth += iGapBeforeItem + iWidth;
|
||||
continue;
|
||||
}
|
||||
|
||||
bOverflowStarted = true;
|
||||
oItem.hidden = true;
|
||||
|
||||
const sItemId = oItem.dataset.overflowItemId;
|
||||
const oMenuItem = sItemId ? this._menuItemsById[sItemId] : null;
|
||||
if (!oMenuItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
oMenuItem.hidden = false;
|
||||
bHasHiddenItems = true;
|
||||
}
|
||||
|
||||
this._extra.hidden = !bHasHiddenItems;
|
||||
if (!bHasHiddenItems) {
|
||||
this._closePopoverIfOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!customElements.get("ibo-button-bar")) {
|
||||
customElements.define("ibo-button-bar", ButtonBar);
|
||||
}
|
||||
@@ -165,6 +165,8 @@ return array(
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Badge\\Badge' => $baseDir . '/sources/Application/UI/Base/Component/Badge/Badge.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Badge\\BadgeUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Badge/BadgeUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Breadcrumbs\\Breadcrumbs' => $baseDir . '/sources/Application/UI/Base/Component/Breadcrumbs/Breadcrumbs.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonBar\\ButtonBar' => $baseDir . '/sources/Application/UI/Base/Component/ButtonBar/ButtonBar.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonBar\\ButtonBarUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/ButtonBar/ButtonBarUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonGroup\\ButtonGroup' => $baseDir . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroup.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonGroup\\ButtonGroupUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroupUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Button\\Button' => $baseDir . '/sources/Application/UI/Base/Component/Button/Button.php',
|
||||
@@ -223,6 +225,8 @@ return array(
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\tInputLabel' => $baseDir . '/sources/Application/UI/Base/Component/Input/tInputLabel.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\MedallionIcon\\MedallionIcon' => $baseDir . '/sources/Application/UI/Base/Component/MedallionIcon/MedallionIcon.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Modal\\DoNotShowAgainOptionBlock' => $baseDir . '/sources/Application/UI/Base/Component/Modal/DoNotShowAgainOptionBlock.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\OverflowLine\\OverflowLine' => $baseDir . '/sources/Application/UI/Base/Component/OverflowLine/OverflowLine.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\OverflowLine\\OverflowLineUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/OverflowLine/OverflowLineUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\Panel' => $baseDir . '/sources/Application/UI/Base/Component/Panel/Panel.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\PanelUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Panel/PanelUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Pill\\Pill' => $baseDir . '/sources/Application/UI/Base/Component/Pill/Pill.php',
|
||||
@@ -4024,5 +4028,5 @@ return array(
|
||||
'privUITransactionFile' => $baseDir . '/application/transaction.class.inc.php',
|
||||
'privUITransactionSession' => $baseDir . '/application/transaction.class.inc.php',
|
||||
'utils' => $baseDir . '/application/utils.inc.php',
|
||||
'©' => $vendorDir . '/symfony/cache/Traits/ValueWrapper.php',
|
||||
'<EFBFBD>' => $vendorDir . '/symfony/cache/Traits/ValueWrapper.php',
|
||||
);
|
||||
|
||||
@@ -566,6 +566,8 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Badge\\Badge' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Badge/Badge.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Badge\\BadgeUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Badge/BadgeUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Breadcrumbs\\Breadcrumbs' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Breadcrumbs/Breadcrumbs.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonBar\\ButtonBar' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/ButtonBar/ButtonBar.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonBar\\ButtonBarUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/ButtonBar/ButtonBarUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonGroup\\ButtonGroup' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroup.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\ButtonGroup\\ButtonGroupUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/ButtonGroup/ButtonGroupUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Button\\Button' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Button/Button.php',
|
||||
@@ -624,6 +626,8 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\tInputLabel' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/tInputLabel.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\MedallionIcon\\MedallionIcon' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/MedallionIcon/MedallionIcon.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Modal\\DoNotShowAgainOptionBlock' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Modal/DoNotShowAgainOptionBlock.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\OverflowLine\\OverflowLine' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/OverflowLine/OverflowLine.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\OverflowLine\\OverflowLineUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/OverflowLine/OverflowLineUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\Panel' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Panel/Panel.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\PanelUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Panel/PanelUIBlockFactory.php',
|
||||
'Combodo\\iTop\\Application\\UI\\Base\\Component\\Pill\\Pill' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Pill/Pill.php',
|
||||
@@ -4425,7 +4429,7 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'privUITransactionFile' => __DIR__ . '/../..' . '/application/transaction.class.inc.php',
|
||||
'privUITransactionSession' => __DIR__ . '/../..' . '/application/transaction.class.inc.php',
|
||||
'utils' => __DIR__ . '/../..' . '/application/utils.inc.php',
|
||||
'©' => __DIR__ . '/..' . '/symfony/cache/Traits/ValueWrapper.php',
|
||||
'<EFBFBD>' => __DIR__ . '/..' . '/symfony/cache/Traits/ValueWrapper.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
namespace Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem;
|
||||
|
||||
use ApplicationPopupMenuItem;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonJS;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonSeparator;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonURL;
|
||||
use JSPopupMenuItem;
|
||||
use SeparatorPopupMenuItem;
|
||||
use URLPopupMenuItem;
|
||||
@@ -128,6 +132,35 @@ class PopoverMenuItemFactory
|
||||
return $oPopoverMenuItem;
|
||||
}
|
||||
|
||||
public static function MakeApplicationPopupMenuItemFromButton(Button|ButtonSeparator $oButton, string $sUid): PopoverMenuItem|SeparatorPopupMenuItem
|
||||
{
|
||||
if ($oButton instanceof ButtonSeparator) {
|
||||
$oPopoverMenuItem = PopoverMenuItemFactory::MakeSeparator();
|
||||
} elseif ($oButton instanceof ButtonURL) {
|
||||
$oPopoverMenuItem = PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem(
|
||||
new URLPopupMenuItem($sUid, $oButton->GetLabel(), $oButton->GetURL(), $oButton->GetTarget())
|
||||
);
|
||||
} elseif ($oButton instanceof ButtonJS) {
|
||||
$oPopoverMenuItem = PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem(
|
||||
new JSPopupMenuItem($sUid, $oButton->GetLabel(), $oButton->GetOnClickJsCode())
|
||||
);
|
||||
} else {
|
||||
$oPopoverMenuItem = PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem(
|
||||
new URLPopupMenuItem($sUid, $oButton->GetLabel(), '#')
|
||||
);
|
||||
}
|
||||
|
||||
if ($oButton instanceof Button) {
|
||||
if ($oButton->GetIconClass() !== '') {
|
||||
$oPopoverMenuItem->SetIconClass($oButton->GetIconClass());
|
||||
}
|
||||
if ($oButton->GetTooltip() !== '') {
|
||||
$oPopoverMenuItem->SetTooltip($oButton->GetTooltip());
|
||||
}
|
||||
}
|
||||
|
||||
return $oPopoverMenuItem;
|
||||
}
|
||||
/**
|
||||
* Make a separator item for the popover menu
|
||||
*
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm
|
||||
|
||||
use AttributeCaseLog;
|
||||
use cmdbAbstractObject;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonSeparator;
|
||||
use Combodo\iTop\Application\UI\Base\Component\ButtonBar\ButtonBar;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\RichText\RichText;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\UI\Base\UIBlock;
|
||||
@@ -53,8 +56,8 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
protected $oTextInput;
|
||||
/** @var array $aMainActionButtons The form main actions (send, cancel, ...) */
|
||||
protected $aMainActionButtons;
|
||||
/** @var array $aExtraActionButtons The form extra actions, can be populated through a public API */
|
||||
protected $aExtraActionButtons;
|
||||
/** @var ButtonBar $oExtraActionButtonBar Extra actions button bar */
|
||||
protected ButtonBar $oExtraActionButtonBar;
|
||||
|
||||
/**
|
||||
* CaseLogEntryForm constructor.
|
||||
@@ -69,8 +72,8 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->sSubmitMode = static::DEFAULT_SUBMIT_MODE;
|
||||
$this->aMainActionButtons = [];
|
||||
$this->aExtraActionButtons = [];
|
||||
$this->InitTextInput();
|
||||
$this->InitExtraActionButtonBar();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,12 +276,27 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function InitExtraActionButtonBar(): void
|
||||
{
|
||||
$this->oExtraActionButtonBar = new ButtonBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ButtonBar
|
||||
*/
|
||||
public function GetExtraActionButtonBar(): ButtonBar
|
||||
{
|
||||
return $this->oExtraActionButtonBar;
|
||||
}
|
||||
/**
|
||||
* @return \Combodo\iTop\Application\UI\Base\UIBlock[]
|
||||
*/
|
||||
public function GetExtraActionButtons(): array
|
||||
{
|
||||
return $this->aExtraActionButtons;
|
||||
return $this->oExtraActionButtonBar->GetButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,7 +309,7 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
*/
|
||||
public function SetExtraActionButtons(array $aExtraActionButtons)
|
||||
{
|
||||
$this->aExtraActionButtons = $aExtraActionButtons;
|
||||
$this->oExtraActionButtonBar->SetButtons($aExtraActionButtons);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -302,9 +320,12 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
* @see $aExtraActionButtons
|
||||
*
|
||||
*/
|
||||
public function AddExtraActionButtons(UIBlock $oExtraActionButton)
|
||||
public function AddExtraActionButtons(UIBlock $oExtraActionButton): self
|
||||
{
|
||||
$this->aExtraActionButtons[] = $oExtraActionButton;
|
||||
if (!($oExtraActionButton instanceof Button) && !($oExtraActionButton instanceof ButtonSeparator)) {
|
||||
throw new \InvalidArgumentException('Extra action buttons must be either a Button or a ButtonSeparator');
|
||||
}
|
||||
$this->oExtraActionButtonBar->AddButton($oExtraActionButton);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -316,9 +337,7 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
$aSubBlocks = [];
|
||||
$aSubBlocks[$this->GetTextInput()->GetId()] = $this->GetTextInput();
|
||||
|
||||
foreach ($this->GetExtraActionButtons() as $oExtraActionButton) {
|
||||
$aSubBlocks[$oExtraActionButton->GetId()] = $oExtraActionButton;
|
||||
}
|
||||
$aSubBlocks[$this->GetExtraActionButtonBar()->GetId()] = $this->GetExtraActionButtonBar();
|
||||
|
||||
foreach ($this->GetMainActionButtons() as $oMainActionButton) {
|
||||
$aSubBlocks[$oMainActionButton->GetId()] = $oMainActionButton;
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
<div class="ibo-caselog-entry-form--actions">
|
||||
<div class="ibo-caselog-entry-form--action-buttons--extra-actions"
|
||||
data-role="ibo-caselog-entry-form--action-buttons--extra-actions">
|
||||
{% for TextInputActionButton in oUIBlock.GetExtraActionButtons() %}
|
||||
{{ render_block(TextInputActionButton, {aPage: aPage}) }}
|
||||
{% endfor %}
|
||||
{{ render_block(oUIBlock.GetExtraActionButtonBar(), {aPage: aPage}) }}
|
||||
</div>
|
||||
<div class="ibo-caselog-entry-form--action-buttons--main-actions" data-role="ibo-caselog-entry-form--action-buttons--main-actions">
|
||||
{% for FormActionButton in oUIBlock.GetMainActionButtons() %}
|
||||
|
||||
@@ -27,7 +27,9 @@ use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Badge\Badge;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Badge\BadgeUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonSeparator;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\ButtonBar\ButtonBarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroup;
|
||||
use Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSection;
|
||||
@@ -35,6 +37,7 @@ use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\HtmlFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\Set\SetUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\Toggler;
|
||||
@@ -71,6 +74,12 @@ $oPage->add_style(
|
||||
hr {
|
||||
background-color: var(--ibo-color-grey-950);
|
||||
}
|
||||
.btn-bar-600{
|
||||
max-width: 600px;
|
||||
}
|
||||
.btn-bar-600{
|
||||
max-width: 800px;
|
||||
}
|
||||
CSS
|
||||
);
|
||||
|
||||
@@ -215,6 +224,140 @@ $oPageContentLayout->AddMainBlock(new ButtonGroup(
|
||||
|
||||
$oPageContentLayout->AddMainBlock(new Html('<hr/>'));
|
||||
|
||||
//////////////
|
||||
// ButtonBar
|
||||
//////////////
|
||||
|
||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('ButtonBar example', 2, 'title-button-bar'));
|
||||
|
||||
$oPage->AddUiBlock(HtmlFactory::MakeParagraph('The button bar is a horizontal list of buttons, with an overflow menu for the buttons that do not fit in the available space. The overflow menu is a popover menu that can be opened by clicking on the "..." button. The overflow menu can contain any number of buttons, and can be used to group related actions together.'));
|
||||
|
||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Full page', 3, 'title-button-bar1'));
|
||||
|
||||
$oBtn1 = ButtonUIBlockFactory::MakeNeutral('Action 1');
|
||||
$oBtn1->SetIconClass('fas fa-thumbs-up');
|
||||
|
||||
$oBtn5 = ButtonUIBlockFactory::MakeNeutral('Action 5');
|
||||
$oBtn5->SetIconClass('fas fa-thumbs-down');
|
||||
|
||||
$oBtn11 = ButtonUIBlockFactory::MakeNeutral('Action 11');
|
||||
$oBtn11->SetIconClass('fas fa-bomb');
|
||||
|
||||
$oButtonBar = ButtonBarUIBlockFactory::MakeStandard(
|
||||
[
|
||||
$oBtn1,
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 2'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 3'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 4'),
|
||||
new ButtonSeparator(),
|
||||
$oBtn5,
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 6'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 7'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 8'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 9'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 10'),
|
||||
new ButtonSeparator(),
|
||||
$oBtn11,
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 12'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 13'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 14'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 15'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 16'),
|
||||
],
|
||||
'See overflow actions',
|
||||
'button-bar-test1'
|
||||
);
|
||||
$oPageContentLayout->AddMainBlock($oButtonBar);
|
||||
|
||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Max width 600px', 3, 'title-button-bar2'));
|
||||
|
||||
$oBtn1 = ButtonUIBlockFactory::MakeNeutral('Action 1');
|
||||
$oBtn1->SetIconClass('fas fa-thumbs-up');
|
||||
|
||||
$oBtn5 = ButtonUIBlockFactory::MakeNeutral('Action 5');
|
||||
$oBtn5->SetIconClass('fas fa-thumbs-down');
|
||||
|
||||
$oBtn11 = ButtonUIBlockFactory::MakeNeutral('Action 11');
|
||||
$oBtn11->SetIconClass('fas fa-bomb');
|
||||
|
||||
$oButtonBar = ButtonBarUIBlockFactory::MakeStandard(
|
||||
[
|
||||
$oBtn1,
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 2'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 3'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 4'),
|
||||
new ButtonSeparator(),
|
||||
$oBtn5,
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 6'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 7'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 8'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 9'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 10'),
|
||||
new ButtonSeparator(),
|
||||
$oBtn11,
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 12'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 13'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 14'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 15'),
|
||||
ButtonUIBlockFactory::MakeNeutral('Action 16'),
|
||||
],
|
||||
null,
|
||||
'button-bar-test2'
|
||||
);
|
||||
$oButtonBar->AddCSSClass('btn-bar-600');
|
||||
$oPageContentLayout->AddMainBlock($oButtonBar);
|
||||
|
||||
//$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Max width 800px', 3, 'title-button-bar3'));
|
||||
//
|
||||
//$oBtn1 = ButtonUIBlockFactory::MakeNeutral('Action 1');
|
||||
//$oBtn1->SetIconClass('fas fa-thumbs-up');
|
||||
//
|
||||
//$oBtn5 = ButtonUIBlockFactory::MakeNeutral('Action 5');
|
||||
//$oBtn5->SetIconClass('fas fa-thumbs-down');
|
||||
//
|
||||
//$oBtn11 = ButtonUIBlockFactory::MakeNeutral('Action 11');
|
||||
//$oBtn11->SetIconClass('fas fa-bomb');
|
||||
//
|
||||
//$oButtonBar = ButtonBarUIBlockFactory::MakeStandard(
|
||||
// [
|
||||
// $oBtn1,
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 2'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 3'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 4'),
|
||||
// ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu(
|
||||
// ButtonUIBlockFactory::MakeForPositiveAction('Validation with options'),
|
||||
// new PopoverMenu()
|
||||
// ),
|
||||
// new ButtonSeparator(),
|
||||
// $oBtn5,
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 6'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 7'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 8'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 9'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 10'),
|
||||
// ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu(
|
||||
// ButtonUIBlockFactory::MakeForPositiveAction('Validation with options'),
|
||||
// new PopoverMenu()
|
||||
// ),
|
||||
// new ButtonSeparator(),
|
||||
// $oBtn11,
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 12'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 13'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 14'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 15'),
|
||||
// ButtonUIBlockFactory::MakeNeutral('Action 16'),
|
||||
// ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu(
|
||||
// ButtonUIBlockFactory::MakeForPositiveAction('Validation with options'),
|
||||
// new PopoverMenu()
|
||||
// ),
|
||||
// ],
|
||||
// null,
|
||||
// 'button-bar-test3'
|
||||
//);
|
||||
$oButtonBar->AddCSSClass('btn-bar-800');
|
||||
$oPageContentLayout->AddMainBlock($oButtonBar);
|
||||
|
||||
$oPageContentLayout->AddMainBlock(new Html('<hr/>'));
|
||||
/////////
|
||||
// Panels
|
||||
/////////
|
||||
|
||||
Reference in New Issue
Block a user