moveToParent(); /** * We have split row in two: * * grid for a bootstrap grid * * We check */ $rowMatchPrefix = "getTagName(), [ BarTag::BAR_TAG, ContainerTag::TAG, syntax_plugin_combo_cell::TAG, syntax_plugin_combo_iterator::TAG, ]) && $isRowTag ) { // contained not in one $scannedType = self::ROW_TAG; } else { $scannedType = self::GRID_TAG; if ($isRowTag) { LogUtility::warning("A non-contained row has been deprecated for grid. You should rename the row tag to grid"); } } $defaultAttributes = []; if ($scannedType === self::GRID_TAG) { /** * Vertical gutter * On a two cell grid, the content will not * touch on a mobile * * https://getbootstrap.com/docs/4.3/layout/grid/#no-gutters * $attributes->addClassName("no-gutters"); */ $defaultGutter = "y-5"; /** * This is a block * as we give it the same spacing than * a paragraph */ $spacing = "mb-3"; $defaultAttributes = [ self::GUTTER => $defaultGutter, Spacing::SPACING_ATTRIBUTE => $spacing ]; /** * All element are centered * If their is 5 cells and the last one * is going at the line, it will be centered * Y = top (the default of css) */ $defaultAlign[Align::X_AXIS] = Align::X_CENTER_CHILDREN; } else { /** * Row is for now mainly use in a content-list and the content * should be centered on y * Why ? Because by default, a flex place text at the top and if a badge is added * for instance, it will shift the text towards the top */ $defaultAlign[Align::Y_AXIS] = "y-center-children"; } if ($scannedType === self::ROW_TAG) { $attributes->setType(self::TYPE_ROW_TAG); } /** * Default */ foreach ($defaultAttributes as $key => $value) { if (!$attributes->hasComponentAttribute($key)) { $attributes->addComponentAttributeValue($key, $value); } } /** * Align default */ try { $aligns = $attributes->getValues(Align::ALIGN_ATTRIBUTE, []); $alignsByAxis = []; foreach ($aligns as $align) { $alignObject = ConditionalLength::createFromString($align); $alignsByAxis[$alignObject->getAxisOrDefault()] = $align; } foreach ($defaultAlign as $axis => $value) { if (!isset($alignsByAxis[$axis])) { $attributes->addComponentAttributeValue(Align::ALIGN_ATTRIBUTE, $value); } } } catch (ExceptionBadArgument $e) { LogUtility::error("The align attribute default values could not be processed. Error: {$e->getMessage()}"); } /** * The deprecations */ $type = $attributes->getType(); if (($type === self::TYPE_AUTO_VALUE_DEPRECATED)) { LogUtility::warning("The auto rows type has been deprecated.", self::CANONICAL); $attributes->removeType(); } if ($type === self::TYPE_FIT_OLD_VALUE || $type === self::TYPE_FIT_VALUE) { // in case it's the old value $attributes->setType(self::TYPE_ROW_TAG); LogUtility::warning("Deprecation: The type value (" . self::TYPE_FIT_VALUE . " and " . self::TYPE_FIT_OLD_VALUE . ") for a contained row tag.", self::CANONICAL); } $attributes->addComponentAttributeValue(self::HTML_TAG_ATT, "div"); } public static function handleExit(\Doku_Handler $handler): array { $callStack = CallStack::createFromHandler($handler); /** * The returned array * (filed while processing) */ $returnArray = array(); /** * Sizing Type mode determination */ $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall(); $type = $openingCall->getType(); /** * Max-Cells Type ? */ $maxLineAttributeValue = null; // variable declaration to not have a linter warning /** * @var ConditionalLength[] $maxLineArray */ $maxLineArray = []; // variable declaration to not have a linter warning if ($type == null) { $maxLineAttributeValue = $openingCall->getAttribute(GridTag::MAX_CHILDREN_ATTRIBUTE); if ($maxLineAttributeValue !== null) { $maxCellsValues = explode(" ", $maxLineAttributeValue); foreach ($maxCellsValues as $maxCellsValue) { try { $maxCellLength = ConditionalLength::createFromString($maxCellsValue); } catch (ExceptionBadArgument $e) { LogUtility::error("The max-cells attribute value ($maxCellsValue) is not a valid length value. Error: {$e->getMessage()}", GridTag::CANONICAL); continue; } $number = $maxCellLength->getNumerator(); if ($number > 12) { LogUtility::error("The max-cells attribute value ($maxCellsValue) should be less than 12.", GridTag::CANONICAL); } $maxLineArray[$maxCellLength->getBreakpointOrDefault()] = $maxCellLength; } $openingCall->removeAttribute(GridTag::MAX_CHILDREN_ATTRIBUTE); $type = GridTag::TYPE_MAX_CHILDREN; } } /** * Gather the cells children * Is there a template callstack */ $firstChildTag = $callStack->moveToFirstChildTag(); if ($firstChildTag === false) { LogUtility::warning("The grid seems to not have any closed children"); return array(PluginUtility::ATTRIBUTES => $openingCall->getAttributes()); } $childrenOpeningTags = []; $fragmentEndTag = null; // the template end tag that has the instructions $callStackTemplate = null; // the instructions in callstack form to modify the children if ($firstChildTag !== false && $firstChildTag->getTagName() === FragmentTag::FRAGMENT_TAG && $firstChildTag->getState() === DOKU_LEXER_ENTER) { $fragmentEndTag = $callStack->next(); if ($fragmentEndTag->getTagName() !== FragmentTag::FRAGMENT_TAG || $fragmentEndTag->getState() !== DOKU_LEXER_EXIT) { LogUtility::error("Error internal: We were unable to find the closing template tag.", GridTag::CANONICAL); return $returnArray; } $templateInstructions = $fragmentEndTag->getPluginData(FragmentTag::CALLSTACK); $callStackTemplate = CallStack::createFromInstructions($templateInstructions); $callStackTemplate->moveToStart(); $firstChildTag = $callStackTemplate->moveToFirstChildTag(); if ($firstChildTag !== false) { $childrenOpeningTags[] = $firstChildTag; while ($actualCall = $callStackTemplate->moveToNextSiblingTag()) { $childrenOpeningTags[] = $actualCall; } } } else { $childrenOpeningTags[] = $firstChildTag; while ($actualCall = $callStack->moveToNextSiblingTag()) { $childrenOpeningTags[] = $actualCall; } } if ($type !== GridTag::TYPE_ROW_TAG) { /** * Scan and process the children for a grid tag * - Add the col class * - Do the cells have a width set ... */ foreach ($childrenOpeningTags as $actualCall) { $actualCall->addClassName("col"); $widthAttributeValue = $actualCall->getAttribute(Dimension::WIDTH_KEY); if ($widthAttributeValue !== null) { $type = GridTag::TYPE_WIDTH_SPECIFIED; $conditionalWidthsLengths = explode(" ", $widthAttributeValue); foreach ($conditionalWidthsLengths as $conditionalWidthsLength) { try { $conditionalLengthObject = ConditionalLength::createFromString($conditionalWidthsLength); } catch (ExceptionBadArgument $e) { $type = null; LogUtility::error("The width length $conditionalWidthsLength is not a valid length value. Error: {$e->getMessage()}"); break; } try { if ($conditionalLengthObject->isRatio()) { $ratio = $conditionalLengthObject->getRatio(); if ($ratio > 1) { LogUtility::warning("The ratio ($ratio) of the width ($conditionalLengthObject) should not be greater than 1 on the children of the row", GridTag::CANONICAL); break; } } } catch (ExceptionBadArgument $e) { $type = null; LogUtility::error("The ratio of the width ($conditionalLengthObject) is not a valid. Error: {$e->getMessage()}"); break; } } } } } if ($type === null) { $type = GridTag::TYPE_MAX_CHILDREN; } /** * Setting the type on the opening tag to see the chosen type in the html attribute */ $openingCall->setType($type); /** * Type is now known * Do the Distribution calculation */ switch ($type) { case GridTag::TYPE_MAX_CHILDREN: $maxLineDefaults = []; try { $maxLineDefaults["xs"] = ConditionalLength::createFromString("1-xs"); $maxLineDefaults["sm"] = ConditionalLength::createFromString("2-sm"); $maxLineDefaults["md"] = ConditionalLength::createFromString("3-md"); $maxLineDefaults["lg"] = ConditionalLength::createFromString("4-lg"); } catch (ExceptionBadArgument $e) { LogUtility::error("Bad default value initialization. Error:{$e->getMessage()}", GridTag::CANONICAL); } /** * Delete the default that are bigger than: * * the asked max-line number * * or the number of children (ie if there is two children, they split the space in two) */ $maxLineDefaultsFiltered = []; $maxLineUsedToFilter = sizeof($childrenOpeningTags); if ($fragmentEndTag !== null) { // there is only one child in a iterator $maxLineUsedToFilter = self::ITERATOR_DEFAULT_MAX_LINE; } if ($maxLineAttributeValue !== null && $maxLineUsedToFilter > $maxLineAttributeValue) { $maxLineUsedToFilter = $maxLineAttributeValue; } foreach ($maxLineDefaults as $breakpoint => $maxLineDefault) { if ($maxLineDefault->getNumerator() <= $maxLineUsedToFilter) { $maxLineDefaultsFiltered[$breakpoint] = $maxLineDefault; } } $maxLineArray = array_merge($maxLineDefaultsFiltered, $maxLineArray); foreach ($maxLineArray as $maxCell) { /** * @var ConditionalLength $maxCell */ try { $openingCall->addClassName($maxCell->toRowColsClass()); } catch (ExceptionBadArgument $e) { LogUtility::error("Error while adding the row-col class. Error: {$e->getMessage()}"); } } break; case GridTag::TYPE_WIDTH_SPECIFIED: foreach ($childrenOpeningTags as $childOpeningTag) { $widthAttributeValue = $childOpeningTag->getAttribute(Dimension::WIDTH_KEY); if ($widthAttributeValue === null) { continue; } $widthValues = explode(" ", $widthAttributeValue); $widthColClasses = null; foreach ($widthValues as $width) { try { $conditionalLengthObject = ConditionalLength::createFromString($width); } catch (ExceptionBadArgument $e) { LogUtility::error("The width value ($width) is not valid length. Error: {$e->getMessage()}"); continue; } if (!$conditionalLengthObject->isRatio()) { continue; } $breakpoint = $conditionalLengthObject->getBreakpointOrDefault(); try { $widthColClasses[$breakpoint] = $conditionalLengthObject->toColClass(); $childOpeningTag->removeAttribute(Dimension::WIDTH_KEY); } catch (ExceptionBadArgument $e) { LogUtility::error("The conditional length $conditionalLengthObject could not be transformed as col class. Error: {$e->getMessage()}"); } } if ($widthColClasses !== null) { if (!isset($widthColClasses["xs"])) { $widthColClasses["xs"] = "col-12"; } foreach ($widthColClasses as $widthClass) { $childOpeningTag->addClassName($widthClass); } } } break; case GridTag::TYPE_ROW_TAG: /** * For all box children that is not the last * one, add a padding right */ $length = sizeof($childrenOpeningTags) - 1; for ($i = 0; $i < $length; $i++) { $childOpeningTag = $childrenOpeningTags[$i]; if ($childOpeningTag->getDisplay() === Call::BlOCK_DISPLAY) { $spacing = $childOpeningTag->getAttribute(Spacing::SPACING_ATTRIBUTE); if ($spacing === null) { $childOpeningTag->setAttribute(Spacing::SPACING_ATTRIBUTE, "me-3"); } } } break; default: LogUtility::error("The grid type ($type) is unknown.", GridTag::CANONICAL); } /** * Template child callstack ? */ if ($fragmentEndTag !== null && $callStackTemplate !== null) { $fragmentEndTag->setPluginData(FragmentTag::CALLSTACK, $callStackTemplate->getStack()); } return array( PluginUtility::ATTRIBUTES => $openingCall->getAttributes() ); } public static function renderEnterXhtml(TagAttributes $attributes): string { /** * Type */ $type = $attributes->getType(); if ($type === GridTag::TYPE_ROW_TAG) { $attributes->addClassName("d-flex"); } else { $attributes->addClassName("row"); /** * Gutter */ $gutterAttributeValue = $attributes->getValueAndRemoveIfPresent(GridTag::GUTTER); $gutters = explode(" ", $gutterAttributeValue); foreach ($gutters as $gutter) { $attributes->addClassName("g$gutter"); } } /** * Render */ $htmlElement = $attributes->getValueAndRemove(GridTag::HTML_TAG_ATT, "div"); return $attributes->toHtmlEnterTag($htmlElement); } public static function renderExitXhtml(TagAttributes $tagAttributes): string { $htmlElement = $tagAttributes->getValue(GridTag::HTML_TAG_ATT); return ""; } }