xref: /template/strap/ComboStrap/Dimension.php (revision 82a60d039cd81033dc8147c27f0a50716b7a5301)
137748cd8SNickeau<?php
237748cd8SNickeau
337748cd8SNickeau
437748cd8SNickeaunamespace ComboStrap;
537748cd8SNickeau
637748cd8SNickeau
737748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin;
837748cd8SNickeauuse syntax_plugin_combo_button;
937748cd8SNickeauuse syntax_plugin_combo_link;
10*82a60d03SNickeauuse syntax_plugin_combo_pageimage;
1137748cd8SNickeau
1237748cd8SNickeauclass Dimension
1337748cd8SNickeau{
1437748cd8SNickeau    /**
1537748cd8SNickeau     * The element that have an width and height
1637748cd8SNickeau     */
1737748cd8SNickeau    const NATURAL_SIZING_ELEMENT = [SvgImageLink::CANONICAL, RasterImageLink::CANONICAL];
1837748cd8SNickeau
1937748cd8SNickeau    const DESIGN_LAYOUT_CONSTRAINED = "constrained"; // fix value
2037748cd8SNickeau    const DESIGN_LAYOUT_FLUID = "fluid"; // adapt
2137748cd8SNickeau
2237748cd8SNickeau    /**
2337748cd8SNickeau     * On the width, if set, the design is fluid and will adapt to all screen
2437748cd8SNickeau     * with a min-width
2537748cd8SNickeau     */
2637748cd8SNickeau    const WIDTH_LAYOUT_DEFAULT = self::DESIGN_LAYOUT_FLUID;
2737748cd8SNickeau    /**
2837748cd8SNickeau     * On height, if set, the design is constrained and overflow
2937748cd8SNickeau     */
3037748cd8SNickeau    const HEIGHT_LAYOUT_DEFAULT = self::DESIGN_LAYOUT_CONSTRAINED;
3137748cd8SNickeau    const SCROLL = "scroll";
32*82a60d03SNickeau
33*82a60d03SNickeau    /**
34*82a60d03SNickeau     * Logical height and width
35*82a60d03SNickeau     * used by default to define the width and height of an image or a css box
36*82a60d03SNickeau     */
3737748cd8SNickeau    const HEIGHT_KEY = 'height';
3837748cd8SNickeau    const WIDTH_KEY = 'width';
3937748cd8SNickeau
40*82a60d03SNickeau    /**
41*82a60d03SNickeau     * The ratio (16:9, ...) permits to change:
42*82a60d03SNickeau     *   * the viewBox in svg
43*82a60d03SNickeau     *   * the intrinsic dimension in raster
44*82a60d03SNickeau     *
45*82a60d03SNickeau     * It's then part of the request
46*82a60d03SNickeau     * because in svg it is the definition of the viewBox
47*82a60d03SNickeau     *
48*82a60d03SNickeau     * The rendering function takes care of it
49*82a60d03SNickeau     * and it's also passed in the fetch url
50*82a60d03SNickeau     */
51*82a60d03SNickeau    public const RATIO_ATTRIBUTE = "ratio";
52*82a60d03SNickeau
5337748cd8SNickeau
5437748cd8SNickeau    /**
5537748cd8SNickeau     * @param TagAttributes $attributes
5637748cd8SNickeau     */
5737748cd8SNickeau    public static function processWidthAndHeight(&$attributes)
5837748cd8SNickeau    {
5937748cd8SNickeau        $widthName = self::WIDTH_KEY;
6037748cd8SNickeau        if ($attributes->hasComponentAttribute($widthName)) {
6137748cd8SNickeau
6237748cd8SNickeau            $widthValue = trim($attributes->getValueAndRemove($widthName));
6337748cd8SNickeau
6437748cd8SNickeau            if ($widthValue == "0") {
6537748cd8SNickeau
6637748cd8SNickeau                /**
6737748cd8SNickeau                 * For an image, the dimension are restricted by height
6837748cd8SNickeau                 */
6937748cd8SNickeau                if ($attributes->hasComponentAttribute(self::HEIGHT_KEY)) {
70*82a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet("width", "auto");
7137748cd8SNickeau                }
7237748cd8SNickeau
7337748cd8SNickeau            } else {
7437748cd8SNickeau
7537748cd8SNickeau
7637748cd8SNickeau                if ($widthValue == "fit") {
7737748cd8SNickeau                    $widthValue = "fit-content";
7837748cd8SNickeau                } else {
7937748cd8SNickeau                    /** Numeric value */
8037748cd8SNickeau                    $widthValue = TagAttributes::toQualifiedCssValue($widthValue);
8137748cd8SNickeau                }
8237748cd8SNickeau
8337748cd8SNickeau
8437748cd8SNickeau                /**
85*82a60d03SNickeau                 * For an image (png, svg)
86*82a60d03SNickeau                 * They have width and height **element** attribute
8737748cd8SNickeau                 */
8837748cd8SNickeau                if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) {
8937748cd8SNickeau
9037748cd8SNickeau                    /**
9137748cd8SNickeau                     * If the image is not ask as static resource (ie HTTP request)
9237748cd8SNickeau                     * but added in HTML
9337748cd8SNickeau                     * (ie {@link \action_plugin_combo_svg})
9437748cd8SNickeau                     */
9537748cd8SNickeau                    $requestedMime = $attributes->getMime();
9637748cd8SNickeau                    if ($requestedMime == TagAttributes::TEXT_HTML_MIME) {
97*82a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue);
98*82a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet('width', "100%");
9937748cd8SNickeau                    }
10037748cd8SNickeau
10137748cd8SNickeau                } else {
10237748cd8SNickeau
10337748cd8SNickeau                    /**
10437748cd8SNickeau                     * For a block
10537748cd8SNickeau                     */
106*82a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue);
10737748cd8SNickeau
10837748cd8SNickeau                }
10937748cd8SNickeau            }
11037748cd8SNickeau
11137748cd8SNickeau        }
11237748cd8SNickeau
11337748cd8SNickeau        $heightName = self::HEIGHT_KEY;
11437748cd8SNickeau        if ($attributes->hasComponentAttribute($heightName)) {
11537748cd8SNickeau            $heightValue = trim($attributes->getValueAndRemove($heightName));
11637748cd8SNickeau            if ($heightValue !== "") {
11737748cd8SNickeau                $heightValue = TagAttributes::toQualifiedCssValue($heightValue);
11837748cd8SNickeau
11937748cd8SNickeau                if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) {
12037748cd8SNickeau
12137748cd8SNickeau                    /**
12237748cd8SNickeau                     * A element with a natural height is responsive, we set only the max-height
12337748cd8SNickeau                     *
12437748cd8SNickeau                     * By default, the image has a `height: auto` due to the img-fluid class
12537748cd8SNickeau                     * Making its height responsive
12637748cd8SNickeau                     */
127*82a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet("max-height", $heightValue);
12837748cd8SNickeau
12937748cd8SNickeau                } else {
13037748cd8SNickeau
13137748cd8SNickeau                    /**
13237748cd8SNickeau                     * HTML Block
13337748cd8SNickeau                     *
13437748cd8SNickeau                     * Without the height value, a block display will collapse
13537748cd8SNickeau                     */
13637748cd8SNickeau                    if (self::HEIGHT_LAYOUT_DEFAULT == self::DESIGN_LAYOUT_CONSTRAINED) {
13737748cd8SNickeau
13837748cd8SNickeau                        /**
13937748cd8SNickeau                         * The box is constrained in height
14037748cd8SNickeau                         * By default, a box is not constrained
14137748cd8SNickeau                         */
142*82a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet("height", $heightValue);
14337748cd8SNickeau
14437748cd8SNickeau                        $scrollMechanism = $attributes->getValueAndRemoveIfPresent("scroll");
14537748cd8SNickeau                        if ($scrollMechanism != null) {
14637748cd8SNickeau                            $scrollMechanism = trim(strtolower($scrollMechanism));
14737748cd8SNickeau                        }
14837748cd8SNickeau                        switch ($scrollMechanism) {
14937748cd8SNickeau                            case "toggle":
15037748cd8SNickeau                                // https://jsfiddle.net/gerardnico/h0g6xw58/
151*82a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("overflow-y", "hidden");
152*82a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("position", "relative");
153*82a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("display", "block");
15437748cd8SNickeau                                // The block should collapse to this height
155*82a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue);
15637748cd8SNickeau                                if ($attributes->hasComponentAttribute("id")) {
15737748cd8SNickeau                                    $id = $attributes->getValue("id");
15837748cd8SNickeau                                } else {
15937748cd8SNickeau                                    $id = $attributes->generateAndSetId();
16037748cd8SNickeau                                }
16137748cd8SNickeau                                /**
16237748cd8SNickeau                                 * Css of the button and other standard attribute
16337748cd8SNickeau                                 */
16437748cd8SNickeau                                PluginUtility::getSnippetManager()->attachCssSnippetForBar("height-toggle");
16537748cd8SNickeau                                /**
16637748cd8SNickeau                                 * Set the color dynamically to the color of the parent
16737748cd8SNickeau                                 */
16837748cd8SNickeau                                PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("height-toggle");
16937748cd8SNickeau                                /**
17037748cd8SNickeau                                 * The height when there is not the show class
17137748cd8SNickeau                                 * is the original height
17237748cd8SNickeau                                 */
17337748cd8SNickeau                                $css = <<<EOF
17437748cd8SNickeau#$id:not(.show){
17537748cd8SNickeau  height: $heightValue;
17637748cd8SNickeau  transition: height .35s ease;
17737748cd8SNickeau}
17837748cd8SNickeauEOF;
17937748cd8SNickeau                                PluginUtility::getSnippetManager()->attachCssSnippetForBar("height-toggle-show", $css);
18037748cd8SNickeau                                $bootstrapDataNameSpace = Bootstrap::getDataNamespace();
18137748cd8SNickeau                                $button = <<<EOF
18237748cd8SNickeau<button class="height-toggle-combo" data$bootstrapDataNameSpace-toggle="collapse" data$bootstrapDataNameSpace-target="#$id" aria-expanded="false"></button>
18337748cd8SNickeauEOF;
18437748cd8SNickeau
18537748cd8SNickeau                                $attributes->addHtmlAfterEnterTag($button);
18637748cd8SNickeau
18737748cd8SNickeau                                break;
18837748cd8SNickeau                            case "lift";
18937748cd8SNickeau                            default:
190*82a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("overflow", "auto");
19137748cd8SNickeau                                break;
19237748cd8SNickeau
19337748cd8SNickeau                        }
19437748cd8SNickeau
19537748cd8SNickeau
19637748cd8SNickeau                    } else {
19737748cd8SNickeau
19837748cd8SNickeau                        /**
19937748cd8SNickeau                         * if fluid
20037748cd8SNickeau                         * min-height and not height to not constraint the box
20137748cd8SNickeau                         */
202*82a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue);
20337748cd8SNickeau
20437748cd8SNickeau                    }
20537748cd8SNickeau                }
20637748cd8SNickeau            }
20737748cd8SNickeau
20837748cd8SNickeau        }
20937748cd8SNickeau    }
21037748cd8SNickeau
21137748cd8SNickeau    /**
21237748cd8SNickeau     *
213*82a60d03SNickeau     * Toggle with a click on the collapsed element
21437748cd8SNickeau     * if there is no control element such as button or link inside
21537748cd8SNickeau     *
21637748cd8SNickeau     * This function is used at the {@link DOKU_LEXER_EXIT} state of a {@link SyntaxPlugin::handle()}
21737748cd8SNickeau     *
21837748cd8SNickeau     * @param CallStack $callStack
21937748cd8SNickeau     */
22037748cd8SNickeau    public static function addScrollToggleOnClickIfNoControl(CallStack $callStack)
22137748cd8SNickeau    {
22237748cd8SNickeau        $callStack->moveToEnd();
22337748cd8SNickeau        $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
22437748cd8SNickeau        $scrollAttribute = $openingCall->getAttribute(Dimension::SCROLL);
22537748cd8SNickeau        if ($scrollAttribute != null && $scrollAttribute == "toggle") {
22637748cd8SNickeau
22737748cd8SNickeau            $controlFound = false;
22837748cd8SNickeau            while ($actualCall = $callStack->next()) {
22937748cd8SNickeau                if (in_array($actualCall->getTagName(),
23037748cd8SNickeau                    [syntax_plugin_combo_button::TAG, syntax_plugin_combo_link::TAG, "internallink", "externallink"])) {
23137748cd8SNickeau                    $controlFound = true;
23237748cd8SNickeau                    break;
23337748cd8SNickeau                }
23437748cd8SNickeau            }
23537748cd8SNickeau            if (!$controlFound) {
23637748cd8SNickeau                $toggleOnClickId = "height-toggle-onclick";
23737748cd8SNickeau                PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar($toggleOnClickId);
23837748cd8SNickeau                $openingCall->addClassName("{$toggleOnClickId}-combo");
23937748cd8SNickeau                $openingCall->addCssStyle("cursor", "pointer");
24037748cd8SNickeau            }
24137748cd8SNickeau
24237748cd8SNickeau        }
24337748cd8SNickeau    }
244*82a60d03SNickeau
245*82a60d03SNickeau    /**
246*82a60d03SNickeau     * @param $value - a css value to a pixel
247*82a60d03SNickeau     * @throws ExceptionCombo
248*82a60d03SNickeau     */
249*82a60d03SNickeau    public static function toPixelValue($value): int
250*82a60d03SNickeau    {
251*82a60d03SNickeau        $targetValue = str_replace("px", "", $value);
252*82a60d03SNickeau        return DataType::toInteger($targetValue);
253*82a60d03SNickeau    }
254*82a60d03SNickeau
255*82a60d03SNickeau    /**
256*82a60d03SNickeau     * Convert 16:9, ... to a float
257*82a60d03SNickeau     * @param string $stringRatio
258*82a60d03SNickeau     * @return float
259*82a60d03SNickeau     * @throws ExceptionCombo
260*82a60d03SNickeau     */
261*82a60d03SNickeau    public static function convertTextualRatioToNumber(string $stringRatio): float
262*82a60d03SNickeau    {
263*82a60d03SNickeau        list($width, $height) = explode(":", $stringRatio, 2);
264*82a60d03SNickeau        try {
265*82a60d03SNickeau            $width = DataType::toInteger($width);
266*82a60d03SNickeau        } catch (ExceptionCombo $e) {
267*82a60d03SNickeau            throw new ExceptionCombo("The width value ($width) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL);
268*82a60d03SNickeau        }
269*82a60d03SNickeau        try {
270*82a60d03SNickeau            $height = DataType::toInteger($height);
271*82a60d03SNickeau        } catch (ExceptionCombo $e) {
272*82a60d03SNickeau            throw new ExceptionCombo("The width value ($height) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL);
273*82a60d03SNickeau        }
274*82a60d03SNickeau        if ($height == 0) {
275*82a60d03SNickeau            throw new ExceptionCombo("The height value of the ratio `$stringRatio` should not be zero", syntax_plugin_combo_pageimage::CANONICAL);
276*82a60d03SNickeau        }
277*82a60d03SNickeau        return floatval($width / $height);
278*82a60d03SNickeau
279*82a60d03SNickeau    }
280*82a60d03SNickeau
281*82a60d03SNickeau
28237748cd8SNickeau}
283