diff --git a/css/backoffice/components/_all.scss b/css/backoffice/components/_all.scss index 7dee4b011..8ef04742a 100644 --- a/css/backoffice/components/_all.scss +++ b/css/backoffice/components/_all.scss @@ -7,6 +7,7 @@ @import "badge"; @import "button"; @import "breadcrumbs"; +@import "collapsible-section"; @import "quick-create"; @import "global-search"; @import "popover-menu/popover-menu"; diff --git a/css/backoffice/components/_collapsible-section.scss b/css/backoffice/components/_collapsible-section.scss new file mode 100644 index 000000000..740fb6b41 --- /dev/null +++ b/css/backoffice/components/_collapsible-section.scss @@ -0,0 +1,102 @@ +/*! + * Copyright (C) 2013-2020 Combodo SARL + * + * 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 + */ + +/* SCSS variables */ +$ibo-collapsible-section--margin-top: 3rem; +$ibo-collapsible-section--title--color: $ibo-color-grey-900 !default; +$ibo-collapsible-section--body--background-color: $ibo-color-white-100 !default; + +$ibo-collapsible-section--highlight--height: 8px !default; + +$ibo-collapsible-section--maximize-minimize-button--right: 5px !default; + + +$ibo-collapsible-section--body--padding-bottom: 16px !default; +$ibo-collapsible-section--body--padding-top: $ibo-collapsible-section--body--padding-bottom + $ibo-collapsible-section--highlight--height !default; +$ibo-collapsible-section--body--padding-x: 16px !default; +$ibo-collapsible-section--body--border-radius: $ibo-border-radius-500 !default; +$ibo-collapsible-section--body--border-size: 1px !default; +$ibo-collapsible-section--body--border-color: $ibo-color-grey-400 !default; + + +/* Rules */ +.ibo-collapsible-section { + margin: $ibo-collapsible-section--margin-top auto; +} + +.ibo-collapsible-section--header { + display: flex; + align-items: stretch; +} + +.ibo-collapsible-section { + &.ibo-is-opened { + .ibo-collapsible-section--minimize-button { + display: block; + } + + .ibo-collapsible-section--maximize-button { + display: none; + } + } + + &:not(.ibo-is-opened) { + .ibo-collapsible-section--minimize-button { + display: none; + } + + .ibo-collapsible-section--maximize-button { + display: block; + } + + .ibo-collapsible-section--body { + display: none; + } + } + + .ibo-collapsible-section--header { + cursor: pointer; + + &:hover i { + opacity: 0.8; + } + + .ibo-collapsible-section--action-button { + &.ibo-collapsible-section--maximize-button, &.ibo-collapsible-section--minimize-button { + margin-right: $ibo-collapsible-section--maximize-minimize-button--right; + } + } + + .ibo-collapsible-section--title { + color: $ibo-collapsible-section--title--color; + @extend %ibo-font-ral-med-250; + flex-grow: 1; + } + } + + .ibo-collapsible-section--body { + position: relative; + padding: $ibo-collapsible-section--body--padding-top $ibo-collapsible-section--body--padding-x $ibo-collapsible-section--body--padding-bottom; + background-color: $ibo-collapsible-section--body--background-color; + border: solid $ibo-collapsible-section--body--border-size $ibo-collapsible-section--body--border-color; + border-radius: $ibo-collapsible-section--body--border-radius; + overflow: hidden; /* To force highlight color to be cropped by the border radius */ + + @extend %ibo-font-ral-nor-200; + } +} diff --git a/js/components/collapsible-section.js b/js/components/collapsible-section.js new file mode 100644 index 000000000..47bd97130 --- /dev/null +++ b/js/components/collapsible-section.js @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013-2020 Combodo SARL + * + * 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 + */ + +; +$(function () { + // the widget definition, where 'itop' is the namespace, + // 'breadcrumbs' the widget name + $.widget('itop.collapsibleSection', + { + // default options + options: + {}, + css_classes: + { + opened: 'ibo-is-opened', + }, + js_selectors: + { + collapse_toggler: '[data-role="ibo-collapsible-section--collapse-toggler"]', + }, + + // the constructor + _create: function () { + this._bindEvents(); + }, + // events bound via _bind are removed automatically + // revert other modifications here + _destroy: function () { + }, + _bindEvents: function () { + const me = this; + + this.element.find(this.js_selectors.collapse_toggler).on('click', function (oEvent) { + me._onCollapseTogglerClick(oEvent); + }); + }, + _onCollapseTogglerClick: function (oEvent) { + this.element.toggleClass(this.css_classes.opened); + } + }) +}); diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index ee3f33757..a214ff058 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -158,6 +158,7 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Breadcrumbs\\Breadcrumbs' => $baseDir . '/sources/application/UI/Base/Component/Breadcrumbs/Breadcrumbs.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Button\\Button' => $baseDir . '/sources/application/UI/Base/Component/Button/Button.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Button\\ButtonFactory' => $baseDir . '/sources/application/UI/Base/Component/Button/ButtonFactory.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\CollapsibleSection\\CollapsibleSection' => $baseDir . '/sources/application/UI/Base/Component/CollapsibleSection/CollapsibleSection.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dashlet\\DashletBadge' => $baseDir . '/sources/application/UI/Base/Component/Dashlet/DashletBadge.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dashlet\\DashletContainer' => $baseDir . '/sources/application/UI/Base/Component/Dashlet/DashletContainer.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dashlet\\DashletFactory' => $baseDir . '/sources/application/UI/Base/Component/Dashlet/DashletFactory.php', @@ -199,7 +200,6 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreate' => $baseDir . '/sources/application/UI/Base/Component/QuickCreate/QuickCreate.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreateFactory' => $baseDir . '/sources/application/UI/Base/Component/QuickCreate/QuickCreateFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreateHelper' => $baseDir . '/sources/application/UI/Base/Component/QuickCreate/QuickCreateHelper.php', - 'Combodo\\iTop\\Application\\UI\\Base\\Component\\SearchBlock\\SearchBlock' => $baseDir . '/sources/application/UI/Base/Component/SearchBlock/SearchBlock.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\Title' => $baseDir . '/sources/application/UI/Base/Component/Title/Title.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\TitleFactory' => $baseDir . '/sources/application/UI/Base/Component/Title/TitleFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\TitleForObjectDetails' => $baseDir . '/sources/application/UI/Base/Component/Title/TitleForObjectDetails.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index aa91c9241..ad6bcbdd0 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -388,6 +388,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Breadcrumbs\\Breadcrumbs' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Breadcrumbs/Breadcrumbs.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Button\\Button' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Button/Button.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Button\\ButtonFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Button/ButtonFactory.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\CollapsibleSection\\CollapsibleSection' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/CollapsibleSection/CollapsibleSection.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dashlet\\DashletBadge' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Dashlet/DashletBadge.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dashlet\\DashletContainer' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Dashlet/DashletContainer.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dashlet\\DashletFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Dashlet/DashletFactory.php', @@ -429,7 +430,6 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreate' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/QuickCreate/QuickCreate.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreateFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/QuickCreate/QuickCreateFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreateHelper' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/QuickCreate/QuickCreateHelper.php', - 'Combodo\\iTop\\Application\\UI\\Base\\Component\\SearchBlock\\SearchBlock' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/SearchBlock/SearchBlock.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\Title' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Title/Title.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\TitleFactory' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Title/TitleFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\TitleForObjectDetails' => __DIR__ . '/../..' . '/sources/application/UI/Base/Component/Title/TitleForObjectDetails.php', diff --git a/pages/run_query.php b/pages/run_query.php index 5f7f3ba0e..10a1d5d6d 100644 --- a/pages/run_query.php +++ b/pages/run_query.php @@ -19,8 +19,10 @@ use Combodo\iTop\Application\UI\Base\Component\Alert\AlertFactory; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonFactory; +use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSection; use Combodo\iTop\Application\UI\Base\Component\Field\Field; use Combodo\iTop\Application\UI\Base\Component\Field\FieldFactory; +use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSet; use Combodo\iTop\Application\UI\Base\Component\Form\Form; use Combodo\iTop\Application\UI\Base\Component\Html\Html; use Combodo\iTop\Application\UI\Base\Component\Input\TextArea; @@ -33,6 +35,10 @@ require_once(APPROOT.'/application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed ApplicationMenu::CheckMenuIdEnabled('RunQueriesMenu'); +/** + * @param WebPage $oP + * @param string $sExpression + */ function ShowExamples($oP, $sExpression) { $bUsingExample = false; @@ -82,16 +88,22 @@ function ShowExamples($oP, $sExpression) } } $aDisplayConfig = array(); - $aDisplayConfig['desc'] = array('label' => Dict::S('UI:RunQuery:HeaderPurpose'), 'description' => Dict::S('UI:RunQuery:HeaderPurpose+')); - $aDisplayConfig['oql'] = array('label' => Dict::S('UI:RunQuery:HeaderOQLExpression'), 'description' => Dict::S('UI:RunQuery:HeaderOQLExpression+')); + $aDisplayConfig['desc'] = array( + 'label' => Dict::S('UI:RunQuery:HeaderPurpose'), + 'description' => Dict::S('UI:RunQuery:HeaderPurpose+'), + ); + $aDisplayConfig['oql'] = array( + 'label' => Dict::S('UI:RunQuery:HeaderOQLExpression'), + 'description' => Dict::S('UI:RunQuery:HeaderOQLExpression+'), + ); $aDisplayConfig['go'] = array('label' => '', 'description' => ''); - foreach ($aDisplayData as $sTopic => $aQueriesDisplayData) - { + foreach ($aDisplayData as $sTopic => $aQueriesDisplayData) { $bShowOpened = $bUsingExample; - $oP->StartCollapsibleSection($sTopic, $bShowOpened); - $oP->table($aDisplayConfig, $aQueriesDisplayData); - $oP->EndCollapsibleSection(); + $sTopicHtml = $oP->GetTable($aDisplayConfig, $aQueriesDisplayData); + $oTopicSection = new CollapsibleSection($sTopic, [new Html($sTopicHtml)]); + $oTopicSection->SetOpenedByDefault($bShowOpened); + $oP->AddUiBlock($oTopicSection); } } @@ -222,10 +234,16 @@ EOF $oP->SetBreadCrumbEntry($sPageId, $sLabel, $oFilter->ToOQL(true), $sUrl, 'fas fa-terminal', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES); - $oP->p(''); - $oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false, 'runQuery'); - $oP->p(Dict::S('UI:RunQuery:DevelopedQuery').htmlentities($oFilter->ToOQL(), ENT_QUOTES, 'UTF-8')); - $oP->p(Dict::S('UI:RunQuery:SerializedFilter').htmlentities($oFilter->serialize(), ENT_QUOTES, 'UTF-8')); + + $aMoreInfoBlocks = []; + + $oDevelopedQuerySet = new FieldSet(Dict::S('UI:RunQuery:DevelopedQuery')); + $oDevelopedQuerySet->AddSubBlock(new Html('
'.utils::HtmlEntities($oFilter->ToOQL()).'
')); + $aMoreInfoBlocks[] = $oDevelopedQuerySet; + + $oSerializedQuerySet = new FieldSet(Dict::S('UI:RunQuery:SerializedFilter')); + $oSerializedQuerySet->AddSubBlock(new Html('
'.utils::HtmlEntities($oFilter->serialize()).'
')); + $aMoreInfoBlocks[] = $oSerializedQuerySet; $aModifierProperties = MetaModel::MakeModifierProperties($oFilter); @@ -244,36 +262,36 @@ EOF if (($oFilter instanceof DBObjectSearch) && !MetaModel::GetConfig()->Get('use_legacy_dbsearch')) { // OQL Developed for Count - $oP->p(''); - $oP->p(Dict::S('UI:RunQuery:DevelopedOQLCount')); $oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oFilter); $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties, null, null, null, $aCountAttToLoad); - $oP->p('
'.$oSQLObjectQueryBuilder->DebugOQLClassTree($oBuild).'
'); + $oCountDevelopedQuerySet = new FieldSet(Dict::S('UI:RunQuery:DevelopedOQLCount')); + $oCountDevelopedQuerySet->AddSubBlock(new Html('
'.$oSQLObjectQueryBuilder->DebugOQLClassTree($oBuild).'
')); + $aMoreInfoBlocks[] = $oCountDevelopedQuerySet; } // SQL Count - $oP->p(''); - $oP->p(Dict::S('UI:RunQuery:ResultSQLCount')); $sSQL = $oFilter->MakeSelectQuery(array(), $aRealArgs, $aCountAttToLoad, null, 0, 0, true); - $oP->p("
$sSQL
"); + $oCountResultQuerySet = new FieldSet(Dict::S('UI:RunQuery:ResultSQLCount')); + $oCountResultQuerySet->AddSubBlock(new Html('
'.$sSQL.'
')); + $aMoreInfoBlocks[] = $oCountResultQuerySet; - if (($oFilter instanceof DBObjectSearch) && !MetaModel::GetConfig()->Get('use_legacy_dbsearch')) - { + if (($oFilter instanceof DBObjectSearch) && !MetaModel::GetConfig()->Get('use_legacy_dbsearch')) { // OQL Developed - $oP->p(''); - $oP->p(Dict::S('UI:RunQuery:DevelopedOQL')); $oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oFilter); $oBuild = new QueryBuilderContext($oFilter, $aModifierProperties); - $oP->p('
'.$oSQLObjectQueryBuilder->DebugOQLClassTree($oBuild).'
'); + $oOqlDevelopedQuerySet = new FieldSet(Dict::S('UI:RunQuery:DevelopedOQL')); + $oOqlDevelopedQuerySet->AddSubBlock(new Html('
'.$oSQLObjectQueryBuilder->DebugOQLClassTree($oBuild).'
')); + $aMoreInfoBlocks[] = $oOqlDevelopedQuerySet; } // SQL - $oP->p(''); - $oP->p(Dict::S('UI:RunQuery:ResultSQL')); $sSQL = $oFilter->MakeSelectQuery($aOrderBy, $aRealArgs, null, null, 0, 0, false); - $oP->p("
$sSQL
"); + $oSqlQuerySet = new FieldSet(Dict::S('UI:RunQuery:ResultSQL')); + $oSqlQuerySet->AddSubBlock(new Html('
'.$sSQL.'
')); + $aMoreInfoBlocks[] = $oSqlQuerySet; - $oP->EndCollapsibleSection(); + $oMoreInfoSection = new CollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), $aMoreInfoBlocks); + $oP->AddUiBlock($oMoreInfoSection); } elseif ($sSyntaxError) { diff --git a/sources/application/UI/Base/Component/CollapsibleSection/CollapsibleSection.php b/sources/application/UI/Base/Component/CollapsibleSection/CollapsibleSection.php new file mode 100644 index 000000000..d68884de0 --- /dev/null +++ b/sources/application/UI/Base/Component/CollapsibleSection/CollapsibleSection.php @@ -0,0 +1,76 @@ +sTitle = $sTitle; + $this->aSubBlocks = $aSubBlocks; + } + + + public function IsOpenedByDefault() + { + return $this->bIsOpenedByDefault; + } + + /** + * @param bool $bIsOpenedByDefault + * + * @return $this + */ + public function SetOpenedByDefault(bool $bIsOpenedByDefault) + { + $this->bIsOpenedByDefault = $bIsOpenedByDefault; + + return $this; + } + + public function GetTitle() + { + return $this->sTitle; + } +} \ No newline at end of file diff --git a/templates/base/components/collapsible-section/layout.html.twig b/templates/base/components/collapsible-section/layout.html.twig new file mode 100644 index 000000000..0dc2a3628 --- /dev/null +++ b/templates/base/components/collapsible-section/layout.html.twig @@ -0,0 +1,18 @@ +
+
+
+
+
+
{{ oUIBlock.GetTitle() }}
+
+
+ {% block iboPanelBody %} + {% for oMainBlock in oUIBlock.GetSubBlocks() %} + {{ render_block(oMainBlock, {aPage: aPage}) }} + {% endfor %} + {% endblock %} +
+
\ No newline at end of file diff --git a/templates/base/components/collapsible-section/layout.js.twig b/templates/base/components/collapsible-section/layout.js.twig new file mode 100644 index 000000000..e6eb589d6 --- /dev/null +++ b/templates/base/components/collapsible-section/layout.js.twig @@ -0,0 +1 @@ +$('#{{ oUIBlock.GetId() }}').collapsibleSection(); \ No newline at end of file diff --git a/test/VisualTest/Backoffice/RenderAllUiBlocks.php b/test/VisualTest/Backoffice/RenderAllUiBlocks.php index 6ab5a8649..e03495dea 100644 --- a/test/VisualTest/Backoffice/RenderAllUiBlocks.php +++ b/test/VisualTest/Backoffice/RenderAllUiBlocks.php @@ -24,6 +24,7 @@ namespace Combodo\iTop\Test\VisualTest\Backoffice; use Combodo\iTop\Application\UI\Base\Component\Alert\AlertFactory; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonFactory; +use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSection; use Combodo\iTop\Application\UI\Base\Component\Html\Html; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelFactory; use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory; @@ -156,4 +157,13 @@ $oPageContentLayout->AddMainBlock($oPanel); $oPageContentLayout->AddMainBlock(new Html('
')); +///////// +// Collapsible Section +///////// +$oCollapsibleSectionTitle = new Html('

Collapsible Sections examples

'); +$oPage->AddUiBlock($oCollapsibleSectionTitle); + +$oCollapsibleSection = new CollapsibleSection('Section title', [new Html("This the section content !")]); +$oPage->AddUiBlock($oCollapsibleSection); + $oPage->output();