xref: /plugin/combo/syntax/contentlist.php (revision c3437056399326d621a01da73b649707fbb0ae69)
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