diff --git a/css/backoffice/components/_badge.scss b/css/backoffice/components/_badge.scss index 52fdddde3..885fa1cb3 100644 --- a/css/backoffice/components/_badge.scss +++ b/css/backoffice/components/_badge.scss @@ -24,6 +24,8 @@ $ibo-badge-colors: ( .ibo-badge { + display: inline-block; + white-space: nowrap; padding : $ibo-badge--padding-y $ibo-badge--padding-x; border-radius : $ibo-badge--border-radius; @extend %ibo-font-ral-med-50; diff --git a/css/backoffice/components/input/_input-toggler.scss b/css/backoffice/components/input/_input-toggler.scss index 90b11a49a..333466198 100644 --- a/css/backoffice/components/input/_input-toggler.scss +++ b/css/backoffice/components/input/_input-toggler.scss @@ -8,6 +8,7 @@ $ibo-toggler--wrapper--height: 20px !default; $ibo-toggler--slider--border-radius: $ibo-border-radius-900 !default; $ibo-toggler--slider--background-color: $ibo-color-secondary-600 !default; +$ibo-toggler--slider--disabled--background-color: $ibo-color-secondary-200 !default; $ibo-toggler--slider--before--left: 3px !default; $ibo-toggler--slider--before--bottom: 3px !default; @@ -17,6 +18,7 @@ $ibo-toggler--slider--before--border-radius: $ibo-border-radius-full !default; $ibo-toggler--slider--before--background-color: $ibo-color-grey-100 !default; $ibo-toggler--slider--checked--background-color: $ibo-color-primary-600 !default; +$ibo-toggler--slider--checked-disabled--background-color: $ibo-color-primary-200 !default; $ibo-toggler--slider--focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default; $ibo-toggler--label--margin-left: 4px !default; @@ -61,6 +63,13 @@ $ibo-toggler--label--margin-left: 4px !default; background-color: $ibo-toggler--slider--checked--background-color; } +.ibo-toggler--wrapper input:disabled + .ibo-toggler--slider { + background-color: $ibo-toggler--slider--disabled--background-color; +} +.ibo-toggler--wrapper input:checked:disabled + .ibo-toggler--slider { + background-color: $ibo-toggler--slider--checked-disabled--background-color; +} + input:focus + .ibo-toggler--slider { box-shadow: $ibo-toggler--slider--focus--box-shadow; } diff --git a/css/backoffice/layout/extension/_extension-details.scss b/css/backoffice/layout/extension/_extension-details.scss index 87b569183..cfb778579 100644 --- a/css/backoffice/layout/extension/_extension-details.scss +++ b/css/backoffice/layout/extension/_extension-details.scss @@ -1,13 +1,39 @@ .ibo-extension-details{ - display:flex; + display:inline-flex; flex-direction : row; justify-content: space-between; align-items: center; - width:300px; + width:450px; } .ibo-extension-details--information{ flex-grow:1; display:flex; flex-direction : column; +} +.ibo-extension-details--actions{ + display:flex; +} +.ibo-extension-details--information--label{ + @extend %ibo-font-ral-med-150; +} +.ibo-extension-details--information--metadata{ + @extend %ibo-font-ral-med-100; + color:$ibo-color-grey-700; +} +.ibo-extension-details--information--description{ + @extend %ibo-font-ral-med-100; +} + +.ibo-extension-details--information--metadata span + span:before{ + content: " - "; +} + +.ibo-extension-details:has(input:checked) .ibo-badge.unchecked, .ibo-extension-details:has(input:not(:checked)) .ibo-badge.checked{ + display:none; +} + +.ibo-extension-details--actions > button{ + position:relative; + top:-3px; } \ No newline at end of file diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 7525defb2..e987207b7 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -271,6 +271,7 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Dashboard\\DashboardLayout' => $baseDir . '/sources/Application/UI/Base/Layout/Dashboard/DashboardLayout.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Dashboard\\DashboardRow' => $baseDir . '/sources/Application/UI/Base/Layout/Dashboard/DashboardRow.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Extension\\ExtensionDetails' => $baseDir . '/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Extension\\ExtensionDetailsFactory' => $baseDir . '/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\MultiColumn\\Column\\Column' => $baseDir . '/sources/Application/UI/Base/Layout/MultiColumn/Column/Column.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\MultiColumn\\Column\\ColumnUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Layout/MultiColumn/Column/ColumnUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\MultiColumn\\MultiColumn' => $baseDir . '/sources/Application/UI/Base/Layout/MultiColumn/MultiColumn.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index b0f0a081a..c37a8373b 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -652,6 +652,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Dashboard\\DashboardLayout' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/Dashboard/DashboardLayout.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Dashboard\\DashboardRow' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/Dashboard/DashboardRow.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Extension\\ExtensionDetails' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\Extension\\ExtensionDetailsFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\MultiColumn\\Column\\Column' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/MultiColumn/Column/Column.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\MultiColumn\\Column\\ColumnUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/MultiColumn/Column/ColumnUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Layout\\MultiColumn\\MultiColumn' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Layout/MultiColumn/MultiColumn.php', diff --git a/sources/Application/UI/Base/Component/Badge/Badge.php b/sources/Application/UI/Base/Component/Badge/Badge.php index 3cdbf135a..cf571d914 100644 --- a/sources/Application/UI/Base/Component/Badge/Badge.php +++ b/sources/Application/UI/Base/Component/Badge/Badge.php @@ -1,9 +1,11 @@ sTooltip; } /** * @param string $sTooltip */ - public function SetTooltip(string $sTooltip) { + public function SetTooltip(string $sTooltip) + { $this->sTooltip = $sTooltip; return $this; } @@ -91,4 +100,4 @@ class Badge extends UIBlock{ $this->sColor = $sColor; return $this; } -} \ No newline at end of file +} diff --git a/sources/Application/UI/Base/Component/Badge/BadgeUIBlockFactory.php b/sources/Application/UI/Base/Component/Badge/BadgeUIBlockFactory.php index 2a5baba99..7020ca1b1 100644 --- a/sources/Application/UI/Base/Component/Badge/BadgeUIBlockFactory.php +++ b/sources/Application/UI/Base/Component/Badge/BadgeUIBlockFactory.php @@ -1,20 +1,43 @@ sCode = $sCode; $this->sLabel = $sLabel; $this->sDescription = $sDescription; $this->aMetaData = $aMetaData; + $this->aBadges = $aBadges; + $this->sAbout = $sAbout; $this->InitializeToggler(); + $this->InitializePopoverMenu(); + } + + public function GetSubBlocks(): array + { + return [$this->oToggler->GetId() => $this->oToggler, $this->oMoreActions->GetId() => $this->oMoreActions]; } /** * @return string */ - public function GetCode(): string { + public function GetCode(): string + { return $this->sCode; } /** * @param string $sCode + * + * @return ExtensionDetails */ - public function SetCode(string $sCode) { + public function SetCode(string $sCode): static + { $this->sCode = $sCode; return $this; } @@ -44,14 +67,18 @@ class ExtensionDetails extends UIContentBlock { /** * @return string */ - public function GetLabel(): string { + public function GetLabel(): string + { return $this->sLabel; } /** * @param string $sLabel + * + * @return ExtensionDetails */ - public function SetLabel(string $sLabel) { + public function SetLabel(string $sLabel): static + { $this->sLabel = $sLabel; return $this; } @@ -59,14 +86,18 @@ class ExtensionDetails extends UIContentBlock { /** * @return array */ - public function GetMetaData(): array { + public function GetMetaData(): array + { return $this->aMetaData; } /** * @param array $aMetaData + * + * @return ExtensionDetails */ - public function SetMetaData(array $aMetaData) { + public function SetMetaData(array $aMetaData): static + { $this->aMetaData = $aMetaData; return $this; } @@ -74,29 +105,37 @@ class ExtensionDetails extends UIContentBlock { /** * @return string */ - public function GetDescription(): string { + public function GetDescription(): string + { return $this->sDescription; } /** * @param string $sDescription + * + * @return ExtensionDetails */ - public function SetDescription(string $sDescription) { + public function SetDescription(string $sDescription): static + { $this->sDescription = $sDescription; return $this; } /** - * @return \Combodo\iTop\Application\UI\Base\Component\Input\Toggler + * @return Toggler */ - public function GetToggler(): Toggler { + public function GetToggler(): Toggler + { return $this->oToggler; } /** - * @param \Combodo\iTop\Application\UI\Base\Component\Input\Toggler $oToggler + * @param Toggler $oToggler + * + * @return ExtensionDetails */ - public function SetToggler(Toggler $oToggler) { + public function SetToggler(Toggler $oToggler): static + { $this->oToggler = $oToggler; return $this; } @@ -104,21 +143,73 @@ class ExtensionDetails extends UIContentBlock { /** * @return array */ - public function GetBadges(): array { + public function GetBadges(): array + { return $this->aBadges; } /** * @param array $aBadges + * + * @return ExtensionDetails */ - public function SetBadges(array $aBadges) { + public function SetBadges(array $aBadges): static + { $this->aBadges = $aBadges; return $this; } - - protected function InitializeToggler(){ + + public function AddBadge(Badge $oBadge): static + { + $this->aBadges[] = $oBadge; + return $this; + } + + public function GetMoreActions(): UIContentBlock + { + return $this->oMoreActions; + } + + protected function InitializeToggler() + { $this->oToggler = new Toggler(); $this->oToggler->SetName('ExtensionToggler'); } -} \ No newline at end of file + protected function InitializePopoverMenu() + { + $sModalLabel = 'About '.$this->sLabel; + $sModalText = $this->sAbout; + $oModifyButton = new JSButtonItem( + 'extension_details', + 'More informations', + <<oPopoverMenu = new PopoverMenu(); + $this->oPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oModifyButton)); + $oPopoverOpenButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', 'Show more actions'); + $this->oPopoverMenu->SetTogglerFromBlock($oPopoverOpenButton); + $this->oMoreActions = new UIContentBlock(); + $this->oMoreActions->AddSubBlock($this->oPopoverMenu); + $this->oMoreActions->AddSubBlock($oPopoverOpenButton); + } + + public function AllowForceUninstall() + { + $oForceUninstallButton = new JSButtonItem( + 'force_uninstall', + 'Force uninstall', + <<oPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oForceUninstallButton)); + } + +} diff --git a/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsFactory.php b/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsFactory.php new file mode 100644 index 000000000..7e99bf7a8 --- /dev/null +++ b/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsFactory.php @@ -0,0 +1,63 @@ +AddCSSClass('checked'); + $aBadges[] = $oBadgeInstalled; + $oBadgeToBeUninstalled = BadgeUIBlockFactory::MakeRed('to be uninstalled'); + $oBadgeToBeUninstalled->AddCSSClass('unchecked'); + $aBadges[] = $oBadgeToBeUninstalled; + + $oExtensionDetails = new ExtensionDetails($sCode, $sLabel, $sDescription, $aMetaData, $aBadges, $sAbout); + $oExtensionDetails->GetToggler()->SetIsToggled(true); + if (!$bUninstallable) { + $oExtensionDetails->AllowForceUninstall(); + $oExtensionDetails->GetToggler()->SetIsDisabled(true); + } + return $oExtensionDetails; + } + + public static function MakeNotInstalled(string $sCode, string $sLabel, string $sDescription = '', array $aMetaData = [], array $aExtraFlags = [], string $sAbout = '') + { + $aBadges = []; + $bUninstallable = $aExtraFlags['uninstallable'] ?? true; + self::AddExtraBadges($aBadges, $bUninstallable, false); + $oBadgeInstalled = BadgeUIBlockFactory::MakeGrey('not installed'); + $oBadgeInstalled->AddCSSClass('unchecked'); + $aBadges[] = $oBadgeInstalled; + $oBadgeToBeUninstalled = BadgeUIBlockFactory::MakeCyan('to be installed'); + $oBadgeToBeUninstalled->AddCSSClass('checked'); + $aBadges[] = $oBadgeToBeUninstalled; + + return new ExtensionDetails($sCode, $sLabel, $sDescription, $aMetaData, $aBadges, $sAbout); + } + + private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk) + { + if (!$bUninstallable) { + $aBadges[] = BadgeUIBlockFactory::MakeOrange('cannot be uninstalled'); + } + if ($bMissingFromDisk) { + $aBadges[] = BadgeUIBlockFactory::MakeRed('missing from disk'); + } + } +} diff --git a/templates/base/components/input/input-toggler.ready.js.twig b/templates/base/components/input/input-toggler.ready.js.twig index d3fdecc0f..ec3e102fb 100644 --- a/templates/base/components/input/input-toggler.ready.js.twig +++ b/templates/base/components/input/input-toggler.ready.js.twig @@ -1,5 +1,8 @@ + $('#{{ oUIBlock.GetId() }}').parent().on('click', function() { - let oInput = $(this).find('.ibo-toggler'); - oInput.prop('checked', !oInput.prop('checked')); - oInput.trigger('change'); + let oInput = $(this).find('.ibo-toggler'); + if (!oInput.prop('disabled')) { + oInput.prop('checked', !oInput.prop('checked')); + oInput.trigger('change'); + } }); \ No newline at end of file diff --git a/templates/base/layouts/extension/extension-details/layout.html.twig b/templates/base/layouts/extension/extension-details/layout.html.twig index f66915c33..0c7d19802 100644 --- a/templates/base/layouts/extension/extension-details/layout.html.twig +++ b/templates/base/layouts/extension/extension-details/layout.html.twig @@ -1,10 +1,12 @@ {# @copyright Copyright (C) 2010-2024 Combodo SAS #} {# @license http://opensource.org/licenses/AGPL-3.0 #} -
- +
{{ oUIBlock.GetLabel() }} + {% for oBadge in oUIBlock.GetBadges() %} + {{ render_block(oBadge, {aPage: aPage}) }} + {% endfor %}
-
{{ render_block(oUIBlock.GetToggler(), {aPage: aPage}) }} - ... + {{ render_block(oUIBlock.GetMoreActions(), {aPage: aPage}) }}
\ No newline at end of file diff --git a/templates/base/layouts/extension/extension-details/layout.ready.js.twig b/templates/base/layouts/extension/extension-details/layout.ready.js.twig new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/templates/base/layouts/extension/extension-details/layout.ready.js.twig @@ -0,0 +1 @@ + diff --git a/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php b/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php index 7421a47d3..4e9a6d8ab 100644 --- a/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php +++ b/tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php @@ -36,17 +36,21 @@ 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\Input\InputUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\Set\SetUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Input\Toggler; use Combodo\iTop\Application\UI\Base\Component\Panel\Panel; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Pill\PillFactory; use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu; +use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory; use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\Extension\ExtensionDetails; +use Combodo\iTop\Application\UI\Base\Layout\Extension\ExtensionDetailsFactory; use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectFactory; use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockWithJSRefreshCallback; use Combodo\iTop\Application\WebPage\iTopWebPage; +use JSButtonItem; use LoginWebPage; use MetaModel; @@ -582,13 +586,49 @@ $oPage->AddUiBlock($oSimpleSetBlockOql); $oSimpleSetBlockOql2 = SetUIBlockFactory::MakeForOQL('SetOql2', 'Location', 'SELECT Location', null, [], null, 'OqlSet2'); $oPage->AddUiBlock($oSimpleSetBlockOql2); -$oSampleBadge = new Badge('badge red',Badge::ENUM_COLOR_SCHEME_RED,'Tooltip'); -$oPage->AddUiBlock($oSampleBadge); +$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Toggler', 3)); -$oSampleBadgeNeutral = BadgeUIBlockFactory::MakeNeutral('badge neutral','Tooltip'); +$oToggler = new Toggler(); +$oToggler->SetName('SampleToggler'); +$oPage->AddUiBlock($oToggler); + +$oTogglerActivated = new Toggler(); +$oTogglerActivated->SetName('SampleTogglerActivated'); +$oTogglerActivated->SetIsToggled(true); +$oPage->AddUiBlock($oTogglerActivated); + +$oTogglerDisabled = new Toggler(); +$oTogglerDisabled->SetName('SampleTogglerDisabled'); +$oTogglerDisabled->SetIsDisabled(true); +$oPage->AddUiBlock($oTogglerDisabled); + +$oTogglerActivatedDisabled = new Toggler(); +$oTogglerActivatedDisabled->SetName('SampleTogglerDisabled'); +$oTogglerActivatedDisabled->SetIsToggled(true); +$oTogglerActivatedDisabled->SetIsDisabled(true); +$oPage->AddUiBlock($oTogglerActivatedDisabled); + +$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Badges', 3)); + +$oSampleBadgeNeutral = BadgeUIBlockFactory::MakeNeutral('badge neutral', 'Tooltip'); $oPage->AddUiBlock($oSampleBadgeNeutral); +$oSampleBadgeCyan = BadgeUIBlockFactory::MakeCyan('badge cyan', 'Tooltip'); +$oPage->AddUiBlock($oSampleBadgeCyan); +$oSampleBadgeGreen = BadgeUIBlockFactory::MakeGreen('badge green', 'Tooltip'); +$oPage->AddUiBlock($oSampleBadgeGreen); +$oSampleBadgeGrey = BadgeUIBlockFactory::MakeGrey('badge grey', 'Tooltip'); +$oPage->AddUiBlock($oSampleBadgeGrey); +$oSampleBadgeOrange = BadgeUIBlockFactory::MakeOrange('badge orange', 'Tooltip'); +$oPage->AddUiBlock($oSampleBadgeOrange); +$oSampleBadgeRed = BadgeUIBlockFactory::MakeRed('badge red', 'Tooltip'); +$oPage->AddUiBlock($oSampleBadgeRed); -$oExtensionDetail = new ExtensionDetails('itop-sample', 'My extension', 'This is for test only'); -$oPage->AddUiBlock($oExtensionDetail); +$oPage->AddUiBlock(TitleUIBlockFactory::MakeNeutral('Extensions details layout', 3)); + +$oExtensionDetailInstalledFromFactory = ExtensionDetailsFactory::MakeInstalled('itop-sample', 'My extension v2', 'This is for test only', ['v1.1.1', 'Designer', '12/12/2012'], ['uninstallable' => false,'missing' => true]); +$oPage->AddUiBlock($oExtensionDetailInstalledFromFactory); + +$oExtensionDetailInstalledWithLongTitle = ExtensionDetailsFactory::MakeNotInstalled('itop-sample', 'My extension with a very long title', 'This is for test only', ['v1.1.1', 'Designer', '12/12/2012'], ['uninstallable' => false]); +$oPage->AddUiBlock($oExtensionDetailInstalledWithLongTitle); $oPage->output();