xref: /template/strap/ComboStrap/Dimension.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
137748cd8SNickeau<?php
237748cd8SNickeau
337748cd8SNickeau
437748cd8SNickeaunamespace ComboStrap;
537748cd8SNickeau
637748cd8SNickeau
737748cd8SNickeauuse dokuwiki\Extension\SyntaxPlugin;
837748cd8SNickeauuse syntax_plugin_combo_button;
937748cd8SNickeauuse syntax_plugin_combo_link;
1082a60d03SNickeauuse 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";
3282a60d03SNickeau
3382a60d03SNickeau    /**
3482a60d03SNickeau     * Logical height and width
3582a60d03SNickeau     * used by default to define the width and height of an image or a css box
3682a60d03SNickeau     */
3737748cd8SNickeau    const HEIGHT_KEY = 'height';
3837748cd8SNickeau    const WIDTH_KEY = 'width';
3937748cd8SNickeau
4082a60d03SNickeau    /**
4182a60d03SNickeau     * The ratio (16:9, ...) permits to change:
4282a60d03SNickeau     *   * the viewBox in svg
4382a60d03SNickeau     *   * the intrinsic dimension in raster
4482a60d03SNickeau     *
4582a60d03SNickeau     * It's then part of the request
4682a60d03SNickeau     * because in svg it is the definition of the viewBox
4782a60d03SNickeau     *
4882a60d03SNickeau     * The rendering function takes care of it
4982a60d03SNickeau     * and it's also passed in the fetch url
5082a60d03SNickeau     */
5182a60d03SNickeau    public const RATIO_ATTRIBUTE = "ratio";
52*4cadd4f8SNickeau    const ZOOM_ATTRIBUTE = "zoom";
5382a60d03SNickeau
5437748cd8SNickeau
5537748cd8SNickeau    /**
5637748cd8SNickeau     * @param TagAttributes $attributes
5737748cd8SNickeau     */
58*4cadd4f8SNickeau    public static function processWidthAndHeight(TagAttributes &$attributes)
5937748cd8SNickeau    {
6037748cd8SNickeau        $widthName = self::WIDTH_KEY;
6137748cd8SNickeau        if ($attributes->hasComponentAttribute($widthName)) {
6237748cd8SNickeau
6337748cd8SNickeau            $widthValue = trim($attributes->getValueAndRemove($widthName));
6437748cd8SNickeau
6537748cd8SNickeau            if ($widthValue == "0") {
6637748cd8SNickeau
6737748cd8SNickeau                /**
6837748cd8SNickeau                 * For an image, the dimension are restricted by height
6937748cd8SNickeau                 */
7037748cd8SNickeau                if ($attributes->hasComponentAttribute(self::HEIGHT_KEY)) {
7182a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet("width", "auto");
7237748cd8SNickeau                }
7337748cd8SNickeau
7437748cd8SNickeau            } else {
7537748cd8SNickeau
7637748cd8SNickeau
7737748cd8SNickeau                if ($widthValue == "fit") {
7837748cd8SNickeau                    $widthValue = "fit-content";
7937748cd8SNickeau                } else {
8037748cd8SNickeau                    /** Numeric value */
8137748cd8SNickeau                    $widthValue = TagAttributes::toQualifiedCssValue($widthValue);
8237748cd8SNickeau                }
8337748cd8SNickeau
8437748cd8SNickeau
8537748cd8SNickeau                /**
8682a60d03SNickeau                 * For an image (png, svg)
8782a60d03SNickeau                 * They have width and height **element** attribute
8837748cd8SNickeau                 */
8937748cd8SNickeau                if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) {
9037748cd8SNickeau
9137748cd8SNickeau                    /**
9237748cd8SNickeau                     * If the image is not ask as static resource (ie HTTP request)
9337748cd8SNickeau                     * but added in HTML
9437748cd8SNickeau                     * (ie {@link \action_plugin_combo_svg})
9537748cd8SNickeau                     */
9637748cd8SNickeau                    $requestedMime = $attributes->getMime();
9737748cd8SNickeau                    if ($requestedMime == TagAttributes::TEXT_HTML_MIME) {
9882a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue);
9982a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet('width', "100%");
10037748cd8SNickeau                    }
10137748cd8SNickeau
10237748cd8SNickeau                } else {
10337748cd8SNickeau
10437748cd8SNickeau                    /**
10537748cd8SNickeau                     * For a block
10637748cd8SNickeau                     */
10782a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet('max-width', $widthValue);
10837748cd8SNickeau
10937748cd8SNickeau                }
11037748cd8SNickeau            }
11137748cd8SNickeau
11237748cd8SNickeau        }
11337748cd8SNickeau
11437748cd8SNickeau        $heightName = self::HEIGHT_KEY;
11537748cd8SNickeau        if ($attributes->hasComponentAttribute($heightName)) {
11637748cd8SNickeau            $heightValue = trim($attributes->getValueAndRemove($heightName));
11737748cd8SNickeau            if ($heightValue !== "") {
11837748cd8SNickeau                $heightValue = TagAttributes::toQualifiedCssValue($heightValue);
11937748cd8SNickeau
12037748cd8SNickeau                if (in_array($attributes->getLogicalTag(), self::NATURAL_SIZING_ELEMENT)) {
12137748cd8SNickeau
12237748cd8SNickeau                    /**
12337748cd8SNickeau                     * A element with a natural height is responsive, we set only the max-height
12437748cd8SNickeau                     *
12537748cd8SNickeau                     * By default, the image has a `height: auto` due to the img-fluid class
12637748cd8SNickeau                     * Making its height responsive
12737748cd8SNickeau                     */
12882a60d03SNickeau                    $attributes->addStyleDeclarationIfNotSet("max-height", $heightValue);
12937748cd8SNickeau
13037748cd8SNickeau                } else {
13137748cd8SNickeau
13237748cd8SNickeau                    /**
13337748cd8SNickeau                     * HTML Block
13437748cd8SNickeau                     *
13537748cd8SNickeau                     * Without the height value, a block display will collapse
13637748cd8SNickeau                     */
13737748cd8SNickeau                    if (self::HEIGHT_LAYOUT_DEFAULT == self::DESIGN_LAYOUT_CONSTRAINED) {
13837748cd8SNickeau
13937748cd8SNickeau                        /**
14037748cd8SNickeau                         * The box is constrained in height
14137748cd8SNickeau                         * By default, a box is not constrained
14237748cd8SNickeau                         */
14382a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet("height", $heightValue);
14437748cd8SNickeau
14537748cd8SNickeau                        $scrollMechanism = $attributes->getValueAndRemoveIfPresent("scroll");
14637748cd8SNickeau                        if ($scrollMechanism != null) {
14737748cd8SNickeau                            $scrollMechanism = trim(strtolower($scrollMechanism));
14837748cd8SNickeau                        }
14937748cd8SNickeau                        switch ($scrollMechanism) {
15037748cd8SNickeau                            case "toggle":
15137748cd8SNickeau                                // https://jsfiddle.net/gerardnico/h0g6xw58/
15282a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("overflow-y", "hidden");
15382a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("position", "relative");
15482a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("display", "block");
15537748cd8SNickeau                                // The block should collapse to this height
15682a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue);
15737748cd8SNickeau                                if ($attributes->hasComponentAttribute("id")) {
15837748cd8SNickeau                                    $id = $attributes->getValue("id");
15937748cd8SNickeau                                } else {
16037748cd8SNickeau                                    $id = $attributes->generateAndSetId();
16137748cd8SNickeau                                }
16237748cd8SNickeau                                /**
16337748cd8SNickeau                                 * Css of the button and other standard attribute
16437748cd8SNickeau                                 */
165*4cadd4f8SNickeau                                PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("height-toggle");
16637748cd8SNickeau                                /**
16737748cd8SNickeau                                 * Set the color dynamically to the color of the parent
16837748cd8SNickeau                                 */
169*4cadd4f8SNickeau                                PluginUtility::getSnippetManager()->attachInternalJavascriptForSlot("height-toggle");
17037748cd8SNickeau                                /**
17137748cd8SNickeau                                 * The height when there is not the show class
17237748cd8SNickeau                                 * is the original height
17337748cd8SNickeau                                 */
17437748cd8SNickeau                                $css = <<<EOF
17537748cd8SNickeau#$id:not(.show){
17637748cd8SNickeau  height: $heightValue;
17737748cd8SNickeau  transition: height .35s ease;
17837748cd8SNickeau}
17937748cd8SNickeauEOF;
180*4cadd4f8SNickeau                                PluginUtility::getSnippetManager()->attachCssInternalStyleSheetForSlot("height-toggle-show", $css);
18137748cd8SNickeau                                $bootstrapDataNameSpace = Bootstrap::getDataNamespace();
18237748cd8SNickeau                                $button = <<<EOF
18337748cd8SNickeau<button class="height-toggle-combo" data$bootstrapDataNameSpace-toggle="collapse" data$bootstrapDataNameSpace-target="#$id" aria-expanded="false"></button>
18437748cd8SNickeauEOF;
18537748cd8SNickeau
18637748cd8SNickeau                                $attributes->addHtmlAfterEnterTag($button);
18737748cd8SNickeau
18837748cd8SNickeau                                break;
18937748cd8SNickeau                            case "lift";
19037748cd8SNickeau                            default:
19182a60d03SNickeau                                $attributes->addStyleDeclarationIfNotSet("overflow", "auto");
19237748cd8SNickeau                                break;
19337748cd8SNickeau
19437748cd8SNickeau                        }
19537748cd8SNickeau
19637748cd8SNickeau
19737748cd8SNickeau                    } else {
19837748cd8SNickeau
19937748cd8SNickeau                        /**
20037748cd8SNickeau                         * if fluid
20137748cd8SNickeau                         * min-height and not height to not constraint the box
20237748cd8SNickeau                         */
20382a60d03SNickeau                        $attributes->addStyleDeclarationIfNotSet("min-height", $heightValue);
20437748cd8SNickeau
20537748cd8SNickeau                    }
20637748cd8SNickeau                }
20737748cd8SNickeau            }
20837748cd8SNickeau
20937748cd8SNickeau        }
21037748cd8SNickeau    }
21137748cd8SNickeau
21237748cd8SNickeau    /**
21337748cd8SNickeau     *
21482a60d03SNickeau     * Toggle with a click on the collapsed element
21537748cd8SNickeau     * if there is no control element such as button or link inside
21637748cd8SNickeau     *
21737748cd8SNickeau     * This function is used at the {@link DOKU_LEXER_EXIT} state of a {@link SyntaxPlugin::handle()}
21837748cd8SNickeau     *
21937748cd8SNickeau     * @param CallStack $callStack
22037748cd8SNickeau     */
22137748cd8SNickeau    public static function addScrollToggleOnClickIfNoControl(CallStack $callStack)
22237748cd8SNickeau    {
22337748cd8SNickeau        $callStack->moveToEnd();
22437748cd8SNickeau        $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
22537748cd8SNickeau        $scrollAttribute = $openingCall->getAttribute(Dimension::SCROLL);
22637748cd8SNickeau        if ($scrollAttribute != null && $scrollAttribute == "toggle") {
22737748cd8SNickeau
22837748cd8SNickeau            $controlFound = false;
22937748cd8SNickeau            while ($actualCall = $callStack->next()) {
23037748cd8SNickeau                if (in_array($actualCall->getTagName(),
23137748cd8SNickeau                    [syntax_plugin_combo_button::TAG, syntax_plugin_combo_link::TAG, "internallink", "externallink"])) {
23237748cd8SNickeau                    $controlFound = true;
23337748cd8SNickeau                    break;
23437748cd8SNickeau                }
23537748cd8SNickeau            }
23637748cd8SNickeau            if (!$controlFound) {
23737748cd8SNickeau                $toggleOnClickId = "height-toggle-onclick";
238*4cadd4f8SNickeau                PluginUtility::getSnippetManager()->attachInternalJavascriptForSlot($toggleOnClickId);
23937748cd8SNickeau                $openingCall->addClassName("{$toggleOnClickId}-combo");
24037748cd8SNickeau                $openingCall->addCssStyle("cursor", "pointer");
24137748cd8SNickeau            }
24237748cd8SNickeau
24337748cd8SNickeau        }
24437748cd8SNickeau    }
24582a60d03SNickeau
24682a60d03SNickeau    /**
24782a60d03SNickeau     * @param $value - a css value to a pixel
24882a60d03SNickeau     * @throws ExceptionCombo
24982a60d03SNickeau     */
25082a60d03SNickeau    public static function toPixelValue($value): int
25182a60d03SNickeau    {
252*4cadd4f8SNickeau
253*4cadd4f8SNickeau        preg_match("/[a-z]/i", $value, $matches, PREG_OFFSET_CAPTURE);
254*4cadd4f8SNickeau        $unit = null;
255*4cadd4f8SNickeau        if (sizeof($matches) > 0) {
256*4cadd4f8SNickeau            $firstPosition = $matches[0][1];
257*4cadd4f8SNickeau            $unit = strtolower(substr($value, $firstPosition));
258*4cadd4f8SNickeau            $value = DataType::toFloat((substr($value, 0, $firstPosition)));
259*4cadd4f8SNickeau        }
260*4cadd4f8SNickeau        switch ($unit) {
261*4cadd4f8SNickeau            case "rem":
262*4cadd4f8SNickeau                $remValue = Site::getRem();
263*4cadd4f8SNickeau                $targetValue = $value * $remValue;
264*4cadd4f8SNickeau                break;
265*4cadd4f8SNickeau            case "px":
266*4cadd4f8SNickeau            default:
267*4cadd4f8SNickeau                $targetValue = $value;
268*4cadd4f8SNickeau        }
26982a60d03SNickeau        return DataType::toInteger($targetValue);
27082a60d03SNickeau    }
27182a60d03SNickeau
27282a60d03SNickeau    /**
27382a60d03SNickeau     * Convert 16:9, ... to a float
27482a60d03SNickeau     * @param string $stringRatio
27582a60d03SNickeau     * @return float
27682a60d03SNickeau     * @throws ExceptionCombo
27782a60d03SNickeau     */
27882a60d03SNickeau    public static function convertTextualRatioToNumber(string $stringRatio): float
27982a60d03SNickeau    {
28082a60d03SNickeau        list($width, $height) = explode(":", $stringRatio, 2);
28182a60d03SNickeau        try {
28282a60d03SNickeau            $width = DataType::toInteger($width);
28382a60d03SNickeau        } catch (ExceptionCombo $e) {
28482a60d03SNickeau            throw new ExceptionCombo("The width value ($width) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL);
28582a60d03SNickeau        }
28682a60d03SNickeau        try {
28782a60d03SNickeau            $height = DataType::toInteger($height);
28882a60d03SNickeau        } catch (ExceptionCombo $e) {
28982a60d03SNickeau            throw new ExceptionCombo("The width value ($height) of the ratio `$stringRatio` is not numeric", syntax_plugin_combo_pageimage::CANONICAL);
29082a60d03SNickeau        }
29182a60d03SNickeau        if ($height == 0) {
29282a60d03SNickeau            throw new ExceptionCombo("The height value of the ratio `$stringRatio` should not be zero", syntax_plugin_combo_pageimage::CANONICAL);
29382a60d03SNickeau        }
29482a60d03SNickeau        return floatval($width / $height);
29582a60d03SNickeau
29682a60d03SNickeau    }
29782a60d03SNickeau
29882a60d03SNickeau
29937748cd8SNickeau}
300