1531e725cSNickeau<?php 2531e725cSNickeau/** 3531e725cSNickeau * DokuWiki Syntax Plugin Combostrap. 4531e725cSNickeau * 5531e725cSNickeau */ 6531e725cSNickeau 7*04fd306cSNickeauuse ComboStrap\ContainerTag; 8*04fd306cSNickeauuse ComboStrap\LogUtility; 9531e725cSNickeauuse ComboStrap\PluginUtility; 10*04fd306cSNickeauuse ComboStrap\SiteConfig; 11*04fd306cSNickeauuse ComboStrap\TagAttribute\BackgroundAttribute; 12*04fd306cSNickeauuse ComboStrap\TagAttributes; 13*04fd306cSNickeauuse ComboStrap\XmlTagProcessing; 14531e725cSNickeau 15531e725cSNickeauif (!defined('DOKU_INC')) { 16531e725cSNickeau die(); 17531e725cSNickeau} 18531e725cSNickeau 1937748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 20531e725cSNickeau 21531e725cSNickeau/** 22531e725cSNickeau * All DokuWiki plugins to extend the parser/rendering mechanism 23531e725cSNickeau * need to inherit from this class 24531e725cSNickeau * 25531e725cSNickeau * The name of the class must follow a pattern (don't change it) 26531e725cSNickeau * ie: 27531e725cSNickeau * syntax_plugin_PluginName_ComponentName 28531e725cSNickeau * 29531e725cSNickeau * See also: doc : 30531e725cSNickeau * https://getbootstrap.com/docs/5.0/components/navbar/ 31531e725cSNickeau * https://material.io/components/app-bars-top 32531e725cSNickeau * 33531e725cSNickeau * Name: 34531e725cSNickeau * * header bar: http://themenectar.com/docs/salient/theme-options/header-navigation 35531e725cSNickeau * * menu bar: https://en.wikipedia.org/wiki/Menu_bar 36531e725cSNickeau * * app bar: https://material.io/components/app-bars-top 37531e725cSNickeau * * navbar: https://getbootstrap.com/docs/5.0/examples/navbars/# 38531e725cSNickeau */ 39531e725cSNickeauclass syntax_plugin_combo_menubar extends DokuWiki_Syntax_Plugin 40531e725cSNickeau{ 41531e725cSNickeau 42531e725cSNickeau const TAG = 'menubar'; 43531e725cSNickeau const OLD_TAG = "navbar"; 44531e725cSNickeau const TAGS = [self::TAG, self::OLD_TAG]; 45*04fd306cSNickeau const BREAKPOINT_ATTRIBUTE = "breakpoint"; 46*04fd306cSNickeau const POSITION = "position"; 47*04fd306cSNickeau const CANONICAL = self::TAG; 48*04fd306cSNickeau const THEME_ATTRIBUTE = "theme"; 49*04fd306cSNickeau const ALIGN_ATTRIBUTE = "align"; 50*04fd306cSNickeau const CONTAINER_ATTRIBUTE = "container"; 51531e725cSNickeau 52531e725cSNickeau /** 53531e725cSNickeau * Do we need to add a container 54531e725cSNickeau * @var bool 55531e725cSNickeau */ 56531e725cSNickeau private $containerInside = false; 57531e725cSNickeau 58531e725cSNickeau /** 59531e725cSNickeau * Syntax Type. 60531e725cSNickeau * 61531e725cSNickeau * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 62531e725cSNickeau * @see DokuWiki_Syntax_Plugin::getType() 63531e725cSNickeau */ 64531e725cSNickeau function getType() 65531e725cSNickeau { 66531e725cSNickeau return 'container'; 67531e725cSNickeau } 68531e725cSNickeau 69531e725cSNickeau /** 70531e725cSNickeau * @return array 71531e725cSNickeau * Allow which kind of plugin inside 72531e725cSNickeau * All 73531e725cSNickeau */ 74531e725cSNickeau public function getAllowedTypes() 75531e725cSNickeau { 76531e725cSNickeau return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 77531e725cSNickeau } 78531e725cSNickeau 79531e725cSNickeau public function accepts($mode) 80531e725cSNickeau { 81531e725cSNickeau 82531e725cSNickeau $accept = syntax_plugin_combo_preformatted::disablePreformatted($mode); 83531e725cSNickeau 84531e725cSNickeau 85531e725cSNickeau // Create P element 86531e725cSNickeau if ($mode == "eol") { 87531e725cSNickeau $accept = false; 88531e725cSNickeau } 89531e725cSNickeau 90531e725cSNickeau return $accept; 91531e725cSNickeau 92531e725cSNickeau } 93531e725cSNickeau 94531e725cSNickeau /** 95531e725cSNickeau * How Dokuwiki will add P element 96531e725cSNickeau * 97531e725cSNickeau * * 'normal' - The plugin can be used inside paragraphs 98531e725cSNickeau * * 'block' - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs 99531e725cSNickeau * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 100531e725cSNickeau * 101531e725cSNickeau * @see DokuWiki_Syntax_Plugin::getPType() 102531e725cSNickeau */ 103531e725cSNickeau function getPType() 104531e725cSNickeau { 105531e725cSNickeau return 'block'; 106531e725cSNickeau } 107531e725cSNickeau 108531e725cSNickeau /** 109531e725cSNickeau * @see Doku_Parser_Mode::getSort() 110531e725cSNickeau * 111531e725cSNickeau * the mode with the lowest sort number will win out 112531e725cSNickeau * the container (parent) must then have a lower number than the child 113531e725cSNickeau */ 114531e725cSNickeau function getSort() 115531e725cSNickeau { 116531e725cSNickeau return 100; 117531e725cSNickeau } 118531e725cSNickeau 119531e725cSNickeau /** 120531e725cSNickeau * Create a pattern that will called this plugin 121531e725cSNickeau * 122531e725cSNickeau * @param string $mode 123531e725cSNickeau * @see Doku_Parser_Mode::connectTo() 124531e725cSNickeau */ 125531e725cSNickeau function connectTo($mode) 126531e725cSNickeau { 127531e725cSNickeau 128531e725cSNickeau foreach (self::TAGS as $tag) { 129*04fd306cSNickeau $pattern = XmlTagProcessing::getContainerTagPattern($tag); 1309337a630SNickeau $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 131531e725cSNickeau } 132531e725cSNickeau 133531e725cSNickeau } 134531e725cSNickeau 135531e725cSNickeau public function postConnect() 136531e725cSNickeau { 137531e725cSNickeau foreach (self::TAGS as $tag) { 1389337a630SNickeau $this->Lexer->addExitPattern('</' . $tag . '>', PluginUtility::getModeFromTag($this->getPluginComponent())); 139531e725cSNickeau } 140531e725cSNickeau 141531e725cSNickeau } 142531e725cSNickeau 143531e725cSNickeau 144531e725cSNickeau /** 145531e725cSNickeau * 146531e725cSNickeau * The handle function goal is to parse the matched syntax through the pattern function 147531e725cSNickeau * and to return the result for use in the renderer 148531e725cSNickeau * This result is always cached until the page is modified. 149531e725cSNickeau * @param string $match 150531e725cSNickeau * @param int $state 151531e725cSNickeau * @param int $pos 152531e725cSNickeau * @param Doku_Handler $handler 153531e725cSNickeau * @return array|bool 154531e725cSNickeau * @see DokuWiki_Syntax_Plugin::handle() 155531e725cSNickeau * 156531e725cSNickeau */ 157531e725cSNickeau function handle($match, $state, $pos, Doku_Handler $handler) 158531e725cSNickeau { 159531e725cSNickeau 160531e725cSNickeau switch ($state) { 161531e725cSNickeau 162531e725cSNickeau case DOKU_LEXER_ENTER: 163531e725cSNickeau 164*04fd306cSNickeau $default[BackgroundAttribute::BACKGROUND_COLOR] = 'light'; 165*04fd306cSNickeau $default[self::BREAKPOINT_ATTRIBUTE] = "lg"; 166*04fd306cSNickeau $default[self::THEME_ATTRIBUTE] = "light"; 167*04fd306cSNickeau $default[self::POSITION] = "normal"; 168*04fd306cSNickeau $default[ContainerTag::CONTAINER_ATTRIBUTE] = SiteConfig::getConfValue( 169*04fd306cSNickeau ContainerTag::DEFAULT_LAYOUT_CONTAINER_CONF, 170*04fd306cSNickeau ContainerTag::DEFAULT_LAYOUT_CONTAINER_DEFAULT_VALUE 171*04fd306cSNickeau ); 172*04fd306cSNickeau $tagAttributes = TagAttributes::createFromTagMatch($match, $default); 173531e725cSNickeau return array( 174531e725cSNickeau PluginUtility::STATE => $state, 175*04fd306cSNickeau PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray() 176531e725cSNickeau ); 177531e725cSNickeau 178531e725cSNickeau case DOKU_LEXER_UNMATCHED: 179531e725cSNickeau 180531e725cSNickeau return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler); 181531e725cSNickeau 182531e725cSNickeau case DOKU_LEXER_EXIT : 183531e725cSNickeau 184531e725cSNickeau return array( 185531e725cSNickeau PluginUtility::STATE => $state 186531e725cSNickeau ); 187531e725cSNickeau 188531e725cSNickeau 189531e725cSNickeau } 190531e725cSNickeau 191531e725cSNickeau return array(); 192531e725cSNickeau 193531e725cSNickeau } 194531e725cSNickeau 195531e725cSNickeau /** 196531e725cSNickeau * Render the output 197531e725cSNickeau * @param string $format 198531e725cSNickeau * @param Doku_Renderer $renderer 199531e725cSNickeau * @param array $data - what the function handle() return'ed 200531e725cSNickeau * @return boolean - rendered correctly? (however, returned value is not used at the moment) 201531e725cSNickeau * @see DokuWiki_Syntax_Plugin::render() 202531e725cSNickeau * 203531e725cSNickeau * 204531e725cSNickeau */ 2054cadd4f8SNickeau function render($format, Doku_Renderer $renderer, $data): bool 206531e725cSNickeau { 207531e725cSNickeau 208531e725cSNickeau if ($format == 'xhtml') { 209531e725cSNickeau 210531e725cSNickeau /** @var Doku_Renderer_xhtml $renderer */ 211531e725cSNickeau $state = $data[PluginUtility::STATE]; 212531e725cSNickeau switch ($state) { 213531e725cSNickeau 214531e725cSNickeau case DOKU_LEXER_ENTER : 215531e725cSNickeau 216531e725cSNickeau 217*04fd306cSNickeau $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]); 218*04fd306cSNickeau $tagAttributes->addClassName('navbar'); 219*04fd306cSNickeau 220531e725cSNickeau 221531e725cSNickeau /** 222531e725cSNickeau * Without the expand, the flex has a row direction 223531e725cSNickeau * and not a column 224531e725cSNickeau */ 225*04fd306cSNickeau $breakpoint = $tagAttributes->getValueAndRemoveIfPresent(self::BREAKPOINT_ATTRIBUTE); 226*04fd306cSNickeau $tagAttributes->addClassName("navbar-expand-$breakpoint"); 227531e725cSNickeau 228531e725cSNickeau // Grab the position 229*04fd306cSNickeau 230*04fd306cSNickeau $position = $tagAttributes->getValueAndRemove(self::POSITION); 231*04fd306cSNickeau switch ($position) { 232*04fd306cSNickeau case "top": 233*04fd306cSNickeau $fixedTopClass = 'fixed-top'; 234*04fd306cSNickeau /** 235*04fd306cSNickeau * We don't set the class directly 236*04fd306cSNickeau * because bootstrap uses `position: fixed` 237*04fd306cSNickeau * Meaning that the content comes below 238*04fd306cSNickeau * 239*04fd306cSNickeau * We calculate the padding-top via the javascript but 240*04fd306cSNickeau * it will then create a non-wanted layout-shift 241*04fd306cSNickeau * 242*04fd306cSNickeau * https://getbootstrap.com/docs/5.0/components/navbar/#placement 243*04fd306cSNickeau * 244*04fd306cSNickeau * We set the class and padding-top with the javascript 245*04fd306cSNickeau */ 246*04fd306cSNickeau $tagAttributes->addOutputAttributeValue("data-type", $fixedTopClass); 247*04fd306cSNickeau $fixedTopSnippetId = self::TAG . "-" . $fixedTopClass; 24885e82846SNickeau // See http://stackoverflow.com/questions/17181355/boostrap-using-fixed-navbar-and-anchor-tags-to-jump-to-sections 249*04fd306cSNickeau PluginUtility::getSnippetManager()->attachJavascriptFromComponentId($fixedTopSnippetId); 250*04fd306cSNickeau break; 251*04fd306cSNickeau case "normal": 252*04fd306cSNickeau // nothing 253*04fd306cSNickeau break; 254*04fd306cSNickeau default: 255*04fd306cSNickeau LogUtility::error("The position value ($position) is not yet implemented", self::CANONICAL); 256*04fd306cSNickeau break; 257531e725cSNickeau } 258531e725cSNickeau 259531e725cSNickeau // Theming 260*04fd306cSNickeau $theme = $tagAttributes->getValueAndRemove(self::THEME_ATTRIBUTE); 261*04fd306cSNickeau $tagAttributes->addClassName("navbar-$theme"); 262531e725cSNickeau 263*04fd306cSNickeau // Container 264*04fd306cSNickeau /** 265*04fd306cSNickeau * Deprecated 266*04fd306cSNickeau */ 267*04fd306cSNickeau $align = $tagAttributes->getValueAndRemoveIfPresent(self::ALIGN_ATTRIBUTE); 268*04fd306cSNickeau $container = null; 269*04fd306cSNickeau if ($align !== null) { 270*04fd306cSNickeau LogUtility::warning("The align attribute has been deprecated, you should delete it or use the container instead", self::CANONICAL); 271531e725cSNickeau 272531e725cSNickeau // Container 273531e725cSNickeau if ($align === "center") { 274*04fd306cSNickeau $container = "sm"; 275*04fd306cSNickeau } else { 276*04fd306cSNickeau $container = "fluid"; 277*04fd306cSNickeau } 278531e725cSNickeau } 279531e725cSNickeau 280*04fd306cSNickeau if ($container === null) { 281*04fd306cSNickeau $container = $tagAttributes->getValueAndRemoveIfPresent(self::CONTAINER_ATTRIBUTE); 282531e725cSNickeau } 283*04fd306cSNickeau $containerClass = ContainerTag::getClassName($container); 284*04fd306cSNickeau // The container should always be be inside to allow background 285*04fd306cSNickeau $tagAttributes->addHtmlAfterEnterTag("<div class=\"$containerClass\">"); 286*04fd306cSNickeau $renderer->doc .= $tagAttributes->toHtmlEnterTag("nav"); 287531e725cSNickeau 288531e725cSNickeau break; 289531e725cSNickeau 290531e725cSNickeau case DOKU_LEXER_UNMATCHED: 291531e725cSNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 292531e725cSNickeau break; 293531e725cSNickeau 294531e725cSNickeau case DOKU_LEXER_EXIT : 295*04fd306cSNickeau $renderer->doc .= "</div></nav>"; 296531e725cSNickeau break; 297531e725cSNickeau } 298531e725cSNickeau return true; 299531e725cSNickeau } 300531e725cSNickeau return false; 301531e725cSNickeau } 302531e725cSNickeau 303531e725cSNickeau 304531e725cSNickeau} 305