104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeaunamespace ComboStrap; 404fd306cSNickeau 504fd306cSNickeau 604fd306cSNickeauuse ComboStrap\Tag\BarTag; 704fd306cSNickeauuse ComboStrap\TagAttribute\Align; 804fd306cSNickeauuse syntax_plugin_combo_cell; 904fd306cSNickeauuse syntax_plugin_combo_iterator; 1004fd306cSNickeau 1104fd306cSNickeau/** 1204fd306cSNickeau * The implementation of row/col system of Boostrap is called a grid because: 1304fd306cSNickeau * * the children may create may be layout on more than one line 1404fd306cSNickeau * * you can define gutter between the children 1504fd306cSNickeau * * even if this is a layout component that works only on one axis and not two. There is little chance that a user will use the css grid layout system 1604fd306cSNickeau * to layout content 1704fd306cSNickeau * 1804fd306cSNickeau */ 1904fd306cSNickeauclass GridTag 2004fd306cSNickeau{ 2104fd306cSNickeau 2204fd306cSNickeau 2304fd306cSNickeau public const GUTTER = "gutter"; 2404fd306cSNickeau /** 2504fd306cSNickeau * Used when the grid is not contained 2604fd306cSNickeau * and is just below the root 2704fd306cSNickeau * We set a value 2804fd306cSNickeau * @deprecated (30/04/2022) 2904fd306cSNickeau */ 3004fd306cSNickeau public const TYPE_AUTO_VALUE_DEPRECATED = "auto"; 3104fd306cSNickeau /** 3204fd306cSNickeau * By default, div but used in a ul, it could be a li 3304fd306cSNickeau * This is modified in the callstack by the other component 3404fd306cSNickeau * @deprecated with the new {@link Align} (30/04/2022) 3504fd306cSNickeau */ 3604fd306cSNickeau public const HTML_TAG_ATT = "html-tag"; 3704fd306cSNickeau public const KNOWN_TYPES = [self::TYPE_MAX_CHILDREN, GridTag::TYPE_WIDTH_SPECIFIED, GridTag::TYPE_AUTO_VALUE_DEPRECATED, GridTag::TYPE_FIT_VALUE, GridTag::TYPE_FIT_OLD_VALUE]; 3804fd306cSNickeau public const GRID_TAG = "grid"; 3904fd306cSNickeau public const ROW_TAG = "row"; 4004fd306cSNickeau /** 4104fd306cSNickeau * 4204fd306cSNickeau * @deprecated - contained/fit type was the same and has been deprecated for the {@link Align} attribute and the width value (grow/shrink) 4304fd306cSNickeau * (30/04/2022) 4404fd306cSNickeau */ 4504fd306cSNickeau public const TYPE_FIT_VALUE = "fit"; 4604fd306cSNickeau /** 4704fd306cSNickeau * The type row is a hack to be able 4804fd306cSNickeau * to support a row tag (ie flex div) 4904fd306cSNickeau * 5004fd306cSNickeau * Ie we migrate row to grid smoothly without loosing 5104fd306cSNickeau * the possibility to use row as component 5270688403Sgerardnico * @deprecated 5304fd306cSNickeau */ 5404fd306cSNickeau public const TYPE_ROW_TAG = "row"; 5504fd306cSNickeau /** 5604fd306cSNickeau * @deprecated (30/04/2022) 5704fd306cSNickeau */ 5804fd306cSNickeau public const TYPE_FIT_OLD_VALUE = "natural"; 5904fd306cSNickeau public const MAX_CHILDREN_ATTRIBUTE = "max-line"; 6004fd306cSNickeau /** 6104fd306cSNickeau * The value is not `width` as this is also an 6204fd306cSNickeau * attribute {@link Dimension::WIDTH_KEY} 6304fd306cSNickeau * and it will fail the type check at {@link TagAttributes::hasComponentAttribute()} 6404fd306cSNickeau */ 6504fd306cSNickeau public const TYPE_WIDTH_SPECIFIED = "width-specified"; 6604fd306cSNickeau public const TAG = GridTag::GRID_TAG; 6704fd306cSNickeau /** 6804fd306cSNickeau * The strap template permits to 6904fd306cSNickeau * change this value 7004fd306cSNickeau * but because of the new grid system, it has been deprecated 7104fd306cSNickeau * We therefore don't get the grid total columns value from strap 7204fd306cSNickeau * @see {@link https://combostrap.com/dynamic_grid Dynamic Grid } 7304fd306cSNickeau */ 7404fd306cSNickeau public const GRID_TOTAL_COLUMNS = 12; 7504fd306cSNickeau public const TAGS = [GridTag::TAG, GridTag::ROW_TAG]; 7604fd306cSNickeau public const TYPE_MAX_CHILDREN = "max"; 7704fd306cSNickeau public const CANONICAL = GridTag::TAG; 7804fd306cSNickeau const LOGICAL_TAG = self::GRID_TAG; 7970688403Sgerardnico const ITERATOR_DEFAULT_MAX_LINE = 3; 8004fd306cSNickeau 8104fd306cSNickeau 8204fd306cSNickeau public static function processEnter(TagAttributes $attributes, $handler, $match) 8304fd306cSNickeau { 8404fd306cSNickeau 8504fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 8604fd306cSNickeau $parent = $callStack->moveToParent(); 8704fd306cSNickeau 8804fd306cSNickeau /** 8904fd306cSNickeau * We have split row in two: 9004fd306cSNickeau * * grid for a bootstrap grid 9104fd306cSNickeau * 9204fd306cSNickeau * We check 9304fd306cSNickeau */ 9404fd306cSNickeau $rowMatchPrefix = "<row"; 9504fd306cSNickeau $isRowTag = substr($match, 0, strlen($rowMatchPrefix)) == $rowMatchPrefix; 9670688403Sgerardnico if ($parent !== false 9704fd306cSNickeau && !in_array($parent->getTagName(), [ 9804fd306cSNickeau BarTag::BAR_TAG, 9904fd306cSNickeau ContainerTag::TAG, 10004fd306cSNickeau syntax_plugin_combo_cell::TAG, 10104fd306cSNickeau syntax_plugin_combo_iterator::TAG, 10204fd306cSNickeau ]) 10304fd306cSNickeau && $isRowTag 10404fd306cSNickeau ) { 10504fd306cSNickeau // contained not in one 10604fd306cSNickeau $scannedType = self::ROW_TAG; 10704fd306cSNickeau } else { 10804fd306cSNickeau $scannedType = self::GRID_TAG; 10904fd306cSNickeau if ($isRowTag) { 11004fd306cSNickeau LogUtility::warning("A non-contained row has been deprecated for grid. You should rename the row tag to grid"); 11104fd306cSNickeau } 11204fd306cSNickeau } 11304fd306cSNickeau 11404fd306cSNickeau $defaultAttributes = []; 11504fd306cSNickeau if ($scannedType === self::GRID_TAG) { 11604fd306cSNickeau 11704fd306cSNickeau /** 11804fd306cSNickeau * Vertical gutter 11904fd306cSNickeau * On a two cell grid, the content will not 12004fd306cSNickeau * touch on a mobile 12104fd306cSNickeau * 12204fd306cSNickeau * https://getbootstrap.com/docs/4.3/layout/grid/#no-gutters 12304fd306cSNickeau * $attributes->addClassName("no-gutters"); 12404fd306cSNickeau */ 12504fd306cSNickeau $defaultGutter = "y-5"; 12604fd306cSNickeau /** 12704fd306cSNickeau * This is a block 12804fd306cSNickeau * as we give it the same spacing than 12904fd306cSNickeau * a paragraph 13004fd306cSNickeau */ 13104fd306cSNickeau $spacing = "mb-3"; 13204fd306cSNickeau $defaultAttributes = [ 13304fd306cSNickeau self::GUTTER => $defaultGutter, 13404fd306cSNickeau Spacing::SPACING_ATTRIBUTE => $spacing 13504fd306cSNickeau ]; 13604fd306cSNickeau /** 13704fd306cSNickeau * All element are centered 13804fd306cSNickeau * If their is 5 cells and the last one 13904fd306cSNickeau * is going at the line, it will be centered 14004fd306cSNickeau * Y = top (the default of css) 14104fd306cSNickeau */ 14204fd306cSNickeau $defaultAlign[Align::X_AXIS] = Align::X_CENTER_CHILDREN; 14304fd306cSNickeau } else { 14404fd306cSNickeau /** 14504fd306cSNickeau * Row is for now mainly use in a content-list and the content 14604fd306cSNickeau * should be centered on y 14704fd306cSNickeau * Why ? Because by default, a flex place text at the top and if a badge is added 14804fd306cSNickeau * for instance, it will shift the text towards the top 14904fd306cSNickeau */ 15004fd306cSNickeau $defaultAlign[Align::Y_AXIS] = "y-center-children"; 15104fd306cSNickeau } 15204fd306cSNickeau 15304fd306cSNickeau 15404fd306cSNickeau if ($scannedType === self::ROW_TAG) { 15504fd306cSNickeau $attributes->setType(self::TYPE_ROW_TAG); 15604fd306cSNickeau } 15704fd306cSNickeau 15804fd306cSNickeau /** 15904fd306cSNickeau * Default 16004fd306cSNickeau */ 16104fd306cSNickeau foreach ($defaultAttributes as $key => $value) { 16204fd306cSNickeau if (!$attributes->hasComponentAttribute($key)) { 16304fd306cSNickeau $attributes->addComponentAttributeValue($key, $value); 16404fd306cSNickeau } 16504fd306cSNickeau } 16604fd306cSNickeau 16704fd306cSNickeau /** 16804fd306cSNickeau * Align default 16904fd306cSNickeau */ 17004fd306cSNickeau try { 17104fd306cSNickeau $aligns = $attributes->getValues(Align::ALIGN_ATTRIBUTE, []); 17204fd306cSNickeau $alignsByAxis = []; 17304fd306cSNickeau foreach ($aligns as $align) { 17404fd306cSNickeau $alignObject = ConditionalLength::createFromString($align); 17504fd306cSNickeau $alignsByAxis[$alignObject->getAxisOrDefault()] = $align; 17604fd306cSNickeau } 17704fd306cSNickeau foreach ($defaultAlign as $axis => $value) { 17804fd306cSNickeau if (!isset($alignsByAxis[$axis])) { 17904fd306cSNickeau $attributes->addComponentAttributeValue(Align::ALIGN_ATTRIBUTE, $value); 18004fd306cSNickeau } 18104fd306cSNickeau } 18204fd306cSNickeau } catch (ExceptionBadArgument $e) { 18304fd306cSNickeau LogUtility::error("The align attribute default values could not be processed. Error: {$e->getMessage()}"); 18404fd306cSNickeau } 18504fd306cSNickeau 18604fd306cSNickeau 18704fd306cSNickeau /** 18804fd306cSNickeau * The deprecations 18904fd306cSNickeau */ 19004fd306cSNickeau $type = $attributes->getType(); 19104fd306cSNickeau if (($type === self::TYPE_AUTO_VALUE_DEPRECATED)) { 19204fd306cSNickeau LogUtility::warning("The auto rows type has been deprecated.", self::CANONICAL); 19304fd306cSNickeau $attributes->removeType(); 19404fd306cSNickeau } 19504fd306cSNickeau if ($type === self::TYPE_FIT_OLD_VALUE || $type === self::TYPE_FIT_VALUE) { 19604fd306cSNickeau // in case it's the old value 19704fd306cSNickeau $attributes->setType(self::TYPE_ROW_TAG); 19804fd306cSNickeau LogUtility::warning("Deprecation: The type value (" . self::TYPE_FIT_VALUE . " and " . self::TYPE_FIT_OLD_VALUE . ") for a contained row tag.", self::CANONICAL); 19904fd306cSNickeau } 20004fd306cSNickeau 20104fd306cSNickeau 20204fd306cSNickeau $attributes->addComponentAttributeValue(self::HTML_TAG_ATT, "div"); 20304fd306cSNickeau 20404fd306cSNickeau } 20504fd306cSNickeau 20604fd306cSNickeau public static function handleExit(\Doku_Handler $handler): array 20704fd306cSNickeau { 20804fd306cSNickeau 20904fd306cSNickeau $callStack = CallStack::createFromHandler($handler); 21004fd306cSNickeau 21104fd306cSNickeau /** 21204fd306cSNickeau * The returned array 21304fd306cSNickeau * (filed while processing) 21404fd306cSNickeau */ 21504fd306cSNickeau $returnArray = array(); 21604fd306cSNickeau 21704fd306cSNickeau /** 21804fd306cSNickeau * Sizing Type mode determination 21904fd306cSNickeau */ 22004fd306cSNickeau $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall(); 22104fd306cSNickeau $type = $openingCall->getType(); 22204fd306cSNickeau 22304fd306cSNickeau /** 22404fd306cSNickeau * Max-Cells Type ? 22504fd306cSNickeau */ 22604fd306cSNickeau $maxLineAttributeValue = null; // variable declaration to not have a linter warning 22704fd306cSNickeau /** 22804fd306cSNickeau * @var ConditionalLength[] $maxLineArray 22904fd306cSNickeau */ 23004fd306cSNickeau $maxLineArray = []; // variable declaration to not have a linter warning 23104fd306cSNickeau if ($type == null) { 23204fd306cSNickeau 23304fd306cSNickeau $maxLineAttributeValue = $openingCall->getAttribute(GridTag::MAX_CHILDREN_ATTRIBUTE); 23404fd306cSNickeau if ($maxLineAttributeValue !== null) { 23504fd306cSNickeau 23604fd306cSNickeau $maxCellsValues = explode(" ", $maxLineAttributeValue); 23704fd306cSNickeau foreach ($maxCellsValues as $maxCellsValue) { 23804fd306cSNickeau try { 23904fd306cSNickeau $maxCellLength = ConditionalLength::createFromString($maxCellsValue); 24004fd306cSNickeau } catch (ExceptionBadArgument $e) { 24104fd306cSNickeau LogUtility::error("The max-cells attribute value ($maxCellsValue) is not a valid length value. Error: {$e->getMessage()}", GridTag::CANONICAL); 24204fd306cSNickeau continue; 24304fd306cSNickeau } 24404fd306cSNickeau $number = $maxCellLength->getNumerator(); 24504fd306cSNickeau if ($number > 12) { 24604fd306cSNickeau LogUtility::error("The max-cells attribute value ($maxCellsValue) should be less than 12.", GridTag::CANONICAL); 24704fd306cSNickeau } 24804fd306cSNickeau $maxLineArray[$maxCellLength->getBreakpointOrDefault()] = $maxCellLength; 24904fd306cSNickeau } 25004fd306cSNickeau $openingCall->removeAttribute(GridTag::MAX_CHILDREN_ATTRIBUTE); 25104fd306cSNickeau $type = GridTag::TYPE_MAX_CHILDREN; 25204fd306cSNickeau } 25304fd306cSNickeau } 25404fd306cSNickeau 25504fd306cSNickeau 25604fd306cSNickeau /** 25704fd306cSNickeau * Gather the cells children 25804fd306cSNickeau * Is there a template callstack 25904fd306cSNickeau */ 26004fd306cSNickeau $firstChildTag = $callStack->moveToFirstChildTag(); 261*5d2c9b31Sgerardnico if ($firstChildTag === false) { 262*5d2c9b31Sgerardnico LogUtility::warning("The grid seems to not have any closed children"); 263*5d2c9b31Sgerardnico return array(PluginUtility::ATTRIBUTES => $openingCall->getAttributes()); 264*5d2c9b31Sgerardnico } 26504fd306cSNickeau $childrenOpeningTags = []; 26604fd306cSNickeau 26770688403Sgerardnico $fragmentEndTag = null; // the template end tag that has the instructions 26804fd306cSNickeau $callStackTemplate = null; // the instructions in callstack form to modify the children 26904fd306cSNickeau if ($firstChildTag !== false && $firstChildTag->getTagName() === FragmentTag::FRAGMENT_TAG && $firstChildTag->getState() === DOKU_LEXER_ENTER) { 27070688403Sgerardnico $fragmentEndTag = $callStack->next(); 27170688403Sgerardnico if ($fragmentEndTag->getTagName() !== FragmentTag::FRAGMENT_TAG || $fragmentEndTag->getState() !== DOKU_LEXER_EXIT) { 27204fd306cSNickeau LogUtility::error("Error internal: We were unable to find the closing template tag.", GridTag::CANONICAL); 27304fd306cSNickeau return $returnArray; 27404fd306cSNickeau } 27570688403Sgerardnico $templateInstructions = $fragmentEndTag->getPluginData(FragmentTag::CALLSTACK); 27604fd306cSNickeau $callStackTemplate = CallStack::createFromInstructions($templateInstructions); 27704fd306cSNickeau $callStackTemplate->moveToStart(); 27804fd306cSNickeau $firstChildTag = $callStackTemplate->moveToFirstChildTag(); 27904fd306cSNickeau if ($firstChildTag !== false) { 28004fd306cSNickeau $childrenOpeningTags[] = $firstChildTag; 28104fd306cSNickeau while ($actualCall = $callStackTemplate->moveToNextSiblingTag()) { 28204fd306cSNickeau $childrenOpeningTags[] = $actualCall; 28304fd306cSNickeau } 28404fd306cSNickeau } 28504fd306cSNickeau 28604fd306cSNickeau } else { 28704fd306cSNickeau 28804fd306cSNickeau $childrenOpeningTags[] = $firstChildTag; 28904fd306cSNickeau while ($actualCall = $callStack->moveToNextSiblingTag()) { 29004fd306cSNickeau $childrenOpeningTags[] = $actualCall; 29104fd306cSNickeau } 29204fd306cSNickeau 29304fd306cSNickeau } 29404fd306cSNickeau 29504fd306cSNickeau 29604fd306cSNickeau if ($type !== GridTag::TYPE_ROW_TAG) { 29704fd306cSNickeau 29804fd306cSNickeau /** 29904fd306cSNickeau * Scan and process the children for a grid tag 30004fd306cSNickeau * - Add the col class 30104fd306cSNickeau * - Do the cells have a width set ... 30204fd306cSNickeau */ 30304fd306cSNickeau foreach ($childrenOpeningTags as $actualCall) { 30404fd306cSNickeau 30504fd306cSNickeau $actualCall->addClassName("col"); 30604fd306cSNickeau 30704fd306cSNickeau $widthAttributeValue = $actualCall->getAttribute(Dimension::WIDTH_KEY); 30804fd306cSNickeau if ($widthAttributeValue !== null) { 30904fd306cSNickeau $type = GridTag::TYPE_WIDTH_SPECIFIED; 31004fd306cSNickeau $conditionalWidthsLengths = explode(" ", $widthAttributeValue); 31104fd306cSNickeau foreach ($conditionalWidthsLengths as $conditionalWidthsLength) { 31204fd306cSNickeau try { 31304fd306cSNickeau $conditionalLengthObject = ConditionalLength::createFromString($conditionalWidthsLength); 31404fd306cSNickeau } catch (ExceptionBadArgument $e) { 31504fd306cSNickeau $type = null; 31604fd306cSNickeau LogUtility::error("The width length $conditionalWidthsLength is not a valid length value. Error: {$e->getMessage()}"); 31704fd306cSNickeau break; 31804fd306cSNickeau } 31904fd306cSNickeau try { 32004fd306cSNickeau if ($conditionalLengthObject->isRatio()) { 32104fd306cSNickeau $ratio = $conditionalLengthObject->getRatio(); 32204fd306cSNickeau if ($ratio > 1) { 32304fd306cSNickeau LogUtility::warning("The ratio ($ratio) of the width ($conditionalLengthObject) should not be greater than 1 on the children of the row", GridTag::CANONICAL); 32404fd306cSNickeau break; 32504fd306cSNickeau } 32604fd306cSNickeau } 32704fd306cSNickeau } catch (ExceptionBadArgument $e) { 32804fd306cSNickeau $type = null; 32904fd306cSNickeau LogUtility::error("The ratio of the width ($conditionalLengthObject) is not a valid. Error: {$e->getMessage()}"); 33004fd306cSNickeau break; 33104fd306cSNickeau } 33204fd306cSNickeau } 33304fd306cSNickeau } 33404fd306cSNickeau } 33504fd306cSNickeau } 33604fd306cSNickeau 33704fd306cSNickeau if ($type === null) { 33804fd306cSNickeau $type = GridTag::TYPE_MAX_CHILDREN; 33904fd306cSNickeau } 34004fd306cSNickeau /** 34104fd306cSNickeau * Setting the type on the opening tag to see the chosen type in the html attribute 34204fd306cSNickeau */ 34304fd306cSNickeau $openingCall->setType($type); 34404fd306cSNickeau 34504fd306cSNickeau 34604fd306cSNickeau /** 34704fd306cSNickeau * Type is now known 34804fd306cSNickeau * Do the Distribution calculation 34904fd306cSNickeau */ 35004fd306cSNickeau switch ($type) { 35104fd306cSNickeau case GridTag::TYPE_MAX_CHILDREN: 35204fd306cSNickeau $maxLineDefaults = []; 35304fd306cSNickeau try { 35404fd306cSNickeau $maxLineDefaults["xs"] = ConditionalLength::createFromString("1-xs"); 35504fd306cSNickeau $maxLineDefaults["sm"] = ConditionalLength::createFromString("2-sm"); 35604fd306cSNickeau $maxLineDefaults["md"] = ConditionalLength::createFromString("3-md"); 35704fd306cSNickeau $maxLineDefaults["lg"] = ConditionalLength::createFromString("4-lg"); 35804fd306cSNickeau } catch (ExceptionBadArgument $e) { 35904fd306cSNickeau LogUtility::error("Bad default value initialization. Error:{$e->getMessage()}", GridTag::CANONICAL); 36004fd306cSNickeau } 36104fd306cSNickeau /** 36204fd306cSNickeau * Delete the default that are bigger than: 36304fd306cSNickeau * * the asked max-line number 36404fd306cSNickeau * * or the number of children (ie if there is two children, they split the space in two) 36504fd306cSNickeau */ 36604fd306cSNickeau $maxLineDefaultsFiltered = []; 36704fd306cSNickeau $maxLineUsedToFilter = sizeof($childrenOpeningTags); 36870688403Sgerardnico if ($fragmentEndTag !== null) { 36970688403Sgerardnico // there is only one child in a iterator 37070688403Sgerardnico $maxLineUsedToFilter = self::ITERATOR_DEFAULT_MAX_LINE; 37170688403Sgerardnico } 37204fd306cSNickeau if ($maxLineAttributeValue !== null && $maxLineUsedToFilter > $maxLineAttributeValue) { 37304fd306cSNickeau $maxLineUsedToFilter = $maxLineAttributeValue; 37404fd306cSNickeau } 37504fd306cSNickeau foreach ($maxLineDefaults as $breakpoint => $maxLineDefault) { 37604fd306cSNickeau if ($maxLineDefault->getNumerator() <= $maxLineUsedToFilter) { 37704fd306cSNickeau $maxLineDefaultsFiltered[$breakpoint] = $maxLineDefault; 37804fd306cSNickeau } 37904fd306cSNickeau } 38004fd306cSNickeau $maxLineArray = array_merge($maxLineDefaultsFiltered, $maxLineArray); 38104fd306cSNickeau foreach ($maxLineArray as $maxCell) { 38204fd306cSNickeau /** 38304fd306cSNickeau * @var ConditionalLength $maxCell 38404fd306cSNickeau */ 38504fd306cSNickeau try { 38604fd306cSNickeau $openingCall->addClassName($maxCell->toRowColsClass()); 38704fd306cSNickeau } catch (ExceptionBadArgument $e) { 38804fd306cSNickeau LogUtility::error("Error while adding the row-col class. Error: {$e->getMessage()}"); 38904fd306cSNickeau } 39004fd306cSNickeau } 39104fd306cSNickeau break; 39204fd306cSNickeau case GridTag::TYPE_WIDTH_SPECIFIED: 39304fd306cSNickeau 39404fd306cSNickeau foreach ($childrenOpeningTags as $childOpeningTag) { 39504fd306cSNickeau $widthAttributeValue = $childOpeningTag->getAttribute(Dimension::WIDTH_KEY); 39604fd306cSNickeau if ($widthAttributeValue === null) { 39704fd306cSNickeau continue; 39804fd306cSNickeau } 39904fd306cSNickeau $widthValues = explode(" ", $widthAttributeValue); 40004fd306cSNickeau $widthColClasses = null; 40104fd306cSNickeau foreach ($widthValues as $width) { 40204fd306cSNickeau try { 40304fd306cSNickeau $conditionalLengthObject = ConditionalLength::createFromString($width); 40404fd306cSNickeau } catch (ExceptionBadArgument $e) { 40504fd306cSNickeau LogUtility::error("The width value ($width) is not valid length. Error: {$e->getMessage()}"); 40604fd306cSNickeau continue; 40704fd306cSNickeau } 40804fd306cSNickeau if (!$conditionalLengthObject->isRatio()) { 40904fd306cSNickeau continue; 41004fd306cSNickeau } 41104fd306cSNickeau $breakpoint = $conditionalLengthObject->getBreakpointOrDefault(); 41204fd306cSNickeau try { 41304fd306cSNickeau $widthColClasses[$breakpoint] = $conditionalLengthObject->toColClass(); 41404fd306cSNickeau $childOpeningTag->removeAttribute(Dimension::WIDTH_KEY); 41504fd306cSNickeau } catch (ExceptionBadArgument $e) { 41604fd306cSNickeau LogUtility::error("The conditional length $conditionalLengthObject could not be transformed as col class. Error: {$e->getMessage()}"); 41704fd306cSNickeau } 41804fd306cSNickeau } 41904fd306cSNickeau if ($widthColClasses !== null) { 42004fd306cSNickeau if (!isset($widthColClasses["xs"])) { 42104fd306cSNickeau $widthColClasses["xs"] = "col-12"; 42204fd306cSNickeau } 42304fd306cSNickeau foreach ($widthColClasses as $widthClass) { 42404fd306cSNickeau $childOpeningTag->addClassName($widthClass); 42504fd306cSNickeau } 42604fd306cSNickeau } 42704fd306cSNickeau } 42804fd306cSNickeau break; 42904fd306cSNickeau case GridTag::TYPE_ROW_TAG: 43004fd306cSNickeau /** 43104fd306cSNickeau * For all box children that is not the last 43204fd306cSNickeau * one, add a padding right 43304fd306cSNickeau */ 43404fd306cSNickeau $length = sizeof($childrenOpeningTags) - 1; 43504fd306cSNickeau for ($i = 0; $i < $length; $i++) { 43604fd306cSNickeau $childOpeningTag = $childrenOpeningTags[$i]; 43704fd306cSNickeau if ($childOpeningTag->getDisplay() === Call::BlOCK_DISPLAY) { 43804fd306cSNickeau $spacing = $childOpeningTag->getAttribute(Spacing::SPACING_ATTRIBUTE); 43904fd306cSNickeau if ($spacing === null) { 44004fd306cSNickeau $childOpeningTag->setAttribute(Spacing::SPACING_ATTRIBUTE, "me-3"); 44104fd306cSNickeau } 44204fd306cSNickeau } 44304fd306cSNickeau } 44404fd306cSNickeau break; 44504fd306cSNickeau default: 44604fd306cSNickeau LogUtility::error("The grid type ($type) is unknown.", GridTag::CANONICAL); 44704fd306cSNickeau } 44804fd306cSNickeau 44904fd306cSNickeau /** 45004fd306cSNickeau * Template child callstack ? 45104fd306cSNickeau */ 45270688403Sgerardnico if ($fragmentEndTag !== null && $callStackTemplate !== null) { 45370688403Sgerardnico $fragmentEndTag->setPluginData(FragmentTag::CALLSTACK, $callStackTemplate->getStack()); 45404fd306cSNickeau } 45504fd306cSNickeau 45604fd306cSNickeau return array( 45704fd306cSNickeau PluginUtility::ATTRIBUTES => $openingCall->getAttributes() 45804fd306cSNickeau ); 45904fd306cSNickeau } 46004fd306cSNickeau 46104fd306cSNickeau public static function renderEnterXhtml(TagAttributes $attributes): string 46204fd306cSNickeau { 46304fd306cSNickeau 46404fd306cSNickeau /** 46504fd306cSNickeau * Type 46604fd306cSNickeau */ 46704fd306cSNickeau $type = $attributes->getType(); 46804fd306cSNickeau if ($type === GridTag::TYPE_ROW_TAG) { 46904fd306cSNickeau 47004fd306cSNickeau $attributes->addClassName("d-flex"); 47104fd306cSNickeau 47204fd306cSNickeau } else { 47304fd306cSNickeau 47404fd306cSNickeau $attributes->addClassName("row"); 47504fd306cSNickeau 47604fd306cSNickeau /** 47704fd306cSNickeau * Gutter 47804fd306cSNickeau */ 47904fd306cSNickeau $gutterAttributeValue = $attributes->getValueAndRemoveIfPresent(GridTag::GUTTER); 48004fd306cSNickeau $gutters = explode(" ", $gutterAttributeValue); 48104fd306cSNickeau foreach ($gutters as $gutter) { 48204fd306cSNickeau $attributes->addClassName("g$gutter"); 48304fd306cSNickeau } 48404fd306cSNickeau 48504fd306cSNickeau } 48604fd306cSNickeau 48704fd306cSNickeau /** 48804fd306cSNickeau * Render 48904fd306cSNickeau */ 49004fd306cSNickeau $htmlElement = $attributes->getValueAndRemove(GridTag::HTML_TAG_ATT, "div"); 49104fd306cSNickeau return $attributes->toHtmlEnterTag($htmlElement); 49204fd306cSNickeau 49304fd306cSNickeau } 49404fd306cSNickeau 49504fd306cSNickeau 49604fd306cSNickeau public static function renderExitXhtml(TagAttributes $tagAttributes): string 49704fd306cSNickeau { 49804fd306cSNickeau $htmlElement = $tagAttributes->getValue(GridTag::HTML_TAG_ATT); 49904fd306cSNickeau return "</$htmlElement>"; 50004fd306cSNickeau } 50104fd306cSNickeau 50204fd306cSNickeau 50304fd306cSNickeau} 504