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