1<?php 2 3 4use ComboStrap\Call; 5use ComboStrap\CallStack; 6use ComboStrap\PluginUtility; 7use ComboStrap\StyleUtility; 8use ComboStrap\TagAttributes; 9 10require_once(__DIR__ . '/../class/StyleUtility.php'); 11require_once(__DIR__ . '/../class/SnippetManager.php'); 12 13 14/** 15 * Class syntax_plugin_combo_list 16 * Implementation of a list 17 * 18 * Content list is a list implementation that permits to 19 * create simple and complex list such as media list 20 * 21 * https://getbootstrap.com/docs/4.0/layout/media-object/#media-list - Bootstrap media list 22 * https://getbootstrap.com/docs/5.0/utilities/flex/#media-object 23 * https://github.com/material-components/material-components-web/tree/master/packages/mdc-list - mdc list 24 * 25 * It's implemented on the basis of: 26 * * bootstrap list-group 27 * * flex utility on the list-group-item 28 * * with the row/cell (grid) adjusted in order to add automatically a space between col (cell) 29 * 30 * Note: 31 * * The cell inside a row are centered vertically automatically 32 * * The illustrative image does not get any [[ui:image#link|link]] 33 * 34 * Documentation: 35 * https://getbootstrap.com/docs/4.1/components/list-group/ 36 * https://getbootstrap.com/docs/5.0/components/list-group/ 37 * 38 * https://getbootstrap.com/docs/5.0/utilities/flex/ 39 * https://getbootstrap.com/docs/5.0/utilities/flex/#media-object 40 * 41 */ 42class syntax_plugin_combo_contentlist extends DokuWiki_Syntax_Plugin 43{ 44 45 const DOKU_TAG = "contentlist"; 46 47 /** 48 * To allow a minus 49 */ 50 const MARKI_TAG = "content-list"; 51 const COMBO_TAG_OLD = "list"; 52 const COMBO_TAGS = [self::MARKI_TAG, self::COMBO_TAG_OLD]; 53 54 55 /** 56 * Syntax Type. 57 * 58 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 59 * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 60 * @see DokuWiki_Syntax_Plugin::getType() 61 */ 62 function getType() 63 { 64 return 'container'; 65 } 66 67 /** 68 * How Dokuwiki will add P element 69 * 70 * * 'normal' - The plugin can be used inside paragraphs (inline or inside) 71 * * 'block' - Open paragraphs need to be closed before plugin output (box) - block should not be inside paragraphs 72 * * 'stack' - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs 73 * 74 * @see DokuWiki_Syntax_Plugin::getPType() 75 * @see https://www.dokuwiki.org/devel:syntax_plugins#ptype 76 */ 77 function getPType() 78 { 79 return 'block'; 80 } 81 82 /** 83 * @return array 84 * Allow which kind of plugin inside 85 * 86 * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs') 87 * because we manage self the content and we call self the parser 88 * 89 * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php 90 */ 91 function getAllowedTypes() 92 { 93 return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 94 } 95 96 public function accepts($mode) 97 { 98 99 return syntax_plugin_combo_preformatted::disablePreformatted($mode); 100 101 } 102 103 104 function getSort() 105 { 106 return 15; 107 } 108 109 110 function connectTo($mode) 111 { 112 113 foreach (self::COMBO_TAGS as $tag) { 114 $pattern = PluginUtility::getContainerTagPattern($tag); 115 $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent())); 116 } 117 118 } 119 120 public function postConnect() 121 { 122 foreach (self::COMBO_TAGS as $tag) { 123 $this->Lexer->addExitPattern('</' . $tag . '>', PluginUtility::getModeFromTag($this->getPluginComponent())); 124 } 125 126 } 127 128 129 /** 130 * 131 * The handle function goal is to parse the matched syntax through the pattern function 132 * and to return the result for use in the renderer 133 * This result is always cached until the page is modified. 134 * @param string $match 135 * @param int $state 136 * @param int $pos - byte position in the original source file 137 * @param Doku_Handler $handler 138 * @return array|bool 139 * @see DokuWiki_Syntax_Plugin::handle() 140 * 141 */ 142 function handle($match, $state, $pos, Doku_Handler $handler) 143 { 144 145 switch ($state) { 146 147 case DOKU_LEXER_ENTER : 148 149 $attributes = TagAttributes::createFromTagMatch($match); 150 151 if ($attributes->hasComponentAttribute(TagAttributes::TYPE_KEY)) { 152 $type = trim(strtolower($attributes->getType())); 153 if ($type == "flush") { 154 // https://getbootstrap.com/docs/5.0/components/list-group/#flush 155 // https://getbootstrap.com/docs/4.1/components/list-group/#flush 156 $attributes->addClassName("list-group-flush"); 157 } 158 } 159 return array( 160 PluginUtility::STATE => $state, 161 PluginUtility::ATTRIBUTES => $attributes->toCallStackArray() 162 ); 163 164 case DOKU_LEXER_UNMATCHED : 165 166 return PluginUtility::handleAndReturnUnmatchedData(self::MARKI_TAG, $match, $handler); 167 168 case DOKU_LEXER_EXIT : 169 170 /** 171 * Add to all row the list-group-item 172 */ 173 $callStack = CallStack::createFromHandler($handler); 174 $callStack->moveToPreviousCorrespondingOpeningCall(); 175 while ($actualCall = $callStack->next()) { 176 if($actualCall->getTagName()==syntax_plugin_combo_contentlistitem::DOKU_TAG){ 177 // List item were added by the user 178 break; 179 } 180 if ($actualCall->getTagName() == syntax_plugin_combo_row::TAG) { 181 $actualState = $actualCall->getState(); 182 switch ($actualState) { 183 case DOKU_LEXER_ENTER: 184 $callStack->insertBefore(Call::createComboCall( 185 syntax_plugin_combo_contentlistitem::DOKU_TAG, 186 DOKU_LEXER_ENTER 187 )); 188 break; 189 case DOKU_LEXER_EXIT: 190 $callStack->insertAfter(Call::createComboCall( 191 syntax_plugin_combo_contentlistitem::DOKU_TAG, 192 DOKU_LEXER_EXIT 193 )); 194 $callStack->next(); 195 break; 196 } 197 198 } 199 } 200 201 202 return array(PluginUtility::STATE => $state); 203 204 205 } 206 return array(); 207 208 } 209 210 /** 211 * Render the output 212 * @param string $format 213 * @param Doku_Renderer $renderer 214 * @param array $data - what the function handle() return'ed 215 * @return boolean - rendered correctly? (however, returned value is not used at the moment) 216 * @see DokuWiki_Syntax_Plugin::render() 217 * 218 * 219 */ 220 function render($format, Doku_Renderer $renderer, $data) 221 { 222 if ($format == 'xhtml') { 223 224 /** @var Doku_Renderer_xhtml $renderer */ 225 $state = $data[PluginUtility::STATE]; 226 switch ($state) { 227 case DOKU_LEXER_ENTER : 228 229 PluginUtility::getSnippetManager()->attachCssSnippetForBar(self::MARKI_TAG); 230 $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES], self::MARKI_TAG); 231 $tagAttributes->addClassName("list-group"); 232 $renderer->doc .= $tagAttributes->toHtmlEnterTag("ul") . DOKU_LF; 233 234 break; 235 case DOKU_LEXER_EXIT : 236 $renderer->doc .= "</ul>" . DOKU_LF; 237 break; 238 case DOKU_LEXER_UNMATCHED : 239 $renderer->doc .= PluginUtility::renderUnmatched($data); 240 break; 241 } 242 return true; 243 } 244 245 // unsupported $mode 246 return false; 247 } 248 249 250} 251 252