From 637dd120ab211004b2ec8d6e53ffb1a1893a1866 Mon Sep 17 00:00:00 2001 From: Simon Karlen Date: Tue, 13 Jul 2021 16:50:02 +0200 Subject: [PATCH] added popover widget --- src/Popover.php | 195 ++++++++++++++++++++++++++++++++++++++++++ tests/PopoverTest.php | 14 +++ 2 files changed, 209 insertions(+) create mode 100644 src/Popover.php create mode 100644 tests/PopoverTest.php diff --git a/src/Popover.php b/src/Popover.php new file mode 100644 index 0000000..87d29e1 --- /dev/null +++ b/src/Popover.php @@ -0,0 +1,195 @@ + + */ + +namespace yii\bootstrap5; + +use yii\helpers\ArrayHelper; + +/** + * Popover renders a popover that can be toggled by clicking on a button. + * + * The following example will show the content enclosed between the [[begin()]] + * and [[end()]] calls within the modal window: + * + * ~~~php + * Popover::begin([ + * 'title' => 'Hello world', + * 'toggleButton' => ['label' => 'click me'], + * ]); + * + * echo 'Say hello...'; + * + * Popover::end(); + * ~~~ + * + * @see https://getbootstrap.com/docs/5.0/components/popovers/ + * @author Simon Karlen + */ +class Popover extends Widget +{ + const PLACEMENT_AUTO = 'auto'; + const PLACEMENT_TOP = 'top'; + const PLACEMENT_BOTTOM = 'bottom'; + const PLACEMENT_LEFT = 'left'; + const PLACEMENT_RIGHT = 'right'; + + const TRIGGER_CLICK = 'click'; + const TRIGGER_HOVER = 'hover'; + const TRIGGER_FOCUS = 'focus'; + const TRIGGER_MANUAL = 'manual'; + + /** + * @var string|null the tile content in the popover. + */ + public ?string $title; + /** + * @var array additional header options + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public array $headerOptions = []; + /** + * @var array body options + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public array $bodyOptions = []; + /** + * @var array arrow options + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public array $arrowOptions = []; + /** + * @var string How to position the popover - [[PLACEMENT_AUTO]] | [[PLACEMENT_TOP]] | [[PLACEMENT_BOTTOM]] | + * [[PLACEMENT_LEFT]] | [[PLACEMENT_RIGHT]]. When auto is specified, it will dynamically reorient the popover. + */ + public string $placement = self::PLACEMENT_AUTO; + /** + * @var array|false the options for rendering the toggle button tag. + * The toggle button is used to toggle the visibility of the popover. + * If this property is false, no toggle button will be rendered. + * + * The following special options are supported: + * + * - tag: string, the tag name of the button. Defaults to 'button'. + * - label: string, the label of the button. Defaults to 'Show'. + * + * The rest of the options will be rendered as the HTML attributes of the button tag. + * Please refer to the [Popover plugin help](https://getbootstrap.com/docs/5.0/components/popovers/) + * for the supported HTML attributes. + */ + public $toggleButton = false; + + /** + * {@inheritDoc} + */ + public function init() + { + parent::init(); + + $this->initOptions(); + + ob_start(); + } + + /** + * {@inheritDoc} + */ + public function run(): ?string + { + $content = ob_get_clean(); + + $this->clientOptions['content'] = $content; + $html = $this->renderToggleButton(); + + $this->registerPlugin('popover'); + + return $html; + } + + /** + * Renders the arrow HTML markup of the popover + * @return string the rendering result + */ + protected function renderArrow(): string + { + Html::addCssClass($this->arrowOptions, ['widget' => 'popover-arrow']); + return Html::tag('div', '', $this->arrowOptions); + } + + /** + * Renders the header HTML markup of the popover + * @return string the rendering result + */ + protected function renderHeader(): string + { + if (isset($this->title)) { + $header = $this->title; + } else { + $header = ''; + } + + Html::addCssClass($this->headerOptions, ['widget' => 'popover-header']); + return Html::tag('h3', "\n" . $header . "\n", $this->headerOptions); + } + + /** + * Renders the opening tag of the modal body. + * @return string the rendering result + */ + protected function renderBody(): string + { + Html::addCssClass($this->bodyOptions, ['widget' => 'popover-body']); + return Html::tag('div', $this->bodyOptions); + } + + /** + * Renders the toggle button. + * @return string the rendering result + */ + protected function renderToggleButton(): ?string + { + if (($toggleButton = $this->toggleButton) !== false) { + $tag = ArrayHelper::remove($toggleButton, 'tag', 'button'); + $label = ArrayHelper::remove($toggleButton, 'label', 'Show'); + $toggleButton['id'] = $this->options['id']; + + return Html::tag($tag, $label, $toggleButton); + } else { + return null; + } + } + + /** + * Initializes the widget options. + * This method sets the default values for various options. + */ + protected function initOptions() + { + + $options = array_merge([ + 'role' => 'tooltip', + ], $this->options, ['id' => $this->options['id'] . '-popover']); + Html::addCssClass($options, ['widget' => 'popover']); + $template = Html::tag('div', $options); + $template .= $this->renderArrow(); + $template .= $this->renderHeader(); + $template .= $this->renderBody(); + + $this->clientOptions = array_merge([ + 'template' => $template, + 'placement' => $this->placement, + 'title' => $this->title, + 'sanitize' => false, + 'html' => true + ], $this->clientOptions); + + if ($this->toggleButton !== false) { + $this->toggleButton = array_merge([ + 'data-bs-toggle' => 'popover', + 'type' => 'button', + ], $this->toggleButton); + } + } +} diff --git a/tests/PopoverTest.php b/tests/PopoverTest.php new file mode 100644 index 0000000..2e97fe9 --- /dev/null +++ b/tests/PopoverTest.php @@ -0,0 +1,14 @@ +