xref: /plugin/combo/ComboStrap/GridTag.php (revision 5d2c9b316d6df7c2f5d30237e79f0a4d26d2f8f6)
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