<?php
-/**
- * @file
- * Contains \Drupal\bootstrap\Utility\Element.
- */
namespace Drupal\bootstrap\Utility;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element as CoreElement;
/**
* Provides helper methods for Drupal render elements.
* Throws this error when the name is a property (key starting with #).
*/
public function &__get($key) {
- if (\Drupal\Core\Render\Element::property($key)) {
+ if (CoreElement::property($key)) {
throw new \InvalidArgumentException('Cannot dynamically retrieve element property. Please use \Drupal\bootstrap\Utility\Element::getProperty instead.');
}
$instance = new self($this->offsetGet($key, []));
* Throws this error when the name is a property (key starting with #).
*/
public function __set($key, $value) {
- if (\Drupal\Core\Render\Element::property($key)) {
+ if (CoreElement::property($key)) {
throw new \InvalidArgumentException('Cannot dynamically retrieve element property. Use \Drupal\bootstrap\Utility\Element::setProperty instead.');
}
$this->offsetSet($key, ($value instanceof Element ? $value->getArray() : $value));
* Throws this error when the name is a property (key starting with #).
*/
public function __isset($name) {
- if (\Drupal\Core\Render\Element::property($name)) {
+ if (CoreElement::property($name)) {
throw new \InvalidArgumentException('Cannot dynamically check if an element has a property. Use \Drupal\bootstrap\Utility\Element::unsetProperty instead.');
}
return parent::__isset($name);
* Throws this error when the name is a property (key starting with #).
*/
public function __unset($name) {
- if (\Drupal\Core\Render\Element::property($name)) {
+ if (CoreElement::property($name)) {
throw new \InvalidArgumentException('Cannot dynamically unset an element property. Use \Drupal\bootstrap\Utility\Element::hasProperty instead.');
}
parent::__unset($name);
* The array keys of the element's children.
*/
public function childKeys($sort = FALSE) {
- return \Drupal\Core\Render\Element::children($this->array, $sort);
+ return CoreElement::children($this->array, $sort);
}
/**
"$prefix-primary", "$prefix-success", "$prefix-info",
"$prefix-warning", "$prefix-danger", "$prefix-link",
// Default should be last.
- "$prefix-default"
+ "$prefix-default",
];
// Set the class to "btn-default" if it shouldn't be colorized.
return $old;
}
+ /**
+ * Traverses the element to find the closest button.
+ *
+ * @return \Drupal\bootstrap\Utility\Element|false
+ * The first button element or FALSE if no button could be found.
+ */
+ public function &findButton() {
+ $button = FALSE;
+ foreach ($this->children() as $child) {
+ if ($child->isButton()) {
+ $button = $child;
+ break;
+ }
+ if ($result = &$child->findButton()) {
+ $button = $result;
+ break;
+ }
+ }
+ return $button;
+ }
+
/**
* Retrieves the render array for the element.
*
* @param mixed $default
* Optional. The default value to use if the context $name isn't set.
*
- * @return mixed|NULL
+ * @return mixed|null
* The context value or the $default value if not set.
*/
public function &getContext($name, $default = NULL) {
* The array keys of the element's visible children.
*/
public function getVisibleChildren() {
- return \Drupal\Core\Render\Element::getVisibleChildren($this->array);
+ return CoreElement::getVisibleChildren($this->array);
}
/**
* TRUE or FALSE.
*/
public function isButton() {
- return !empty($this->array['#is_button']) || $this->isType(['button', 'submit', 'reset', 'image_button']) || $this->hasClass('btn');
+ $button_types = ['button', 'submit', 'reset', 'image_button'];
+ return !empty($this->array['#is_button']) || $this->isType($button_types) || $this->hasClass('btn');
}
/**
* Whether the given element is empty.
*/
public function isEmpty() {
- return \Drupal\Core\Render\Element::isEmpty($this->array);
+ return CoreElement::isEmpty($this->array);
}
/**
* TRUE if the element is visible, otherwise FALSE.
*/
public function isVisible() {
- return \Drupal\Core\Render\Element::isVisibleElement($this->array);
+ return CoreElement::isVisibleElement($this->array);
}
/**
* @return $this
*/
public function map(array $map) {
- \Drupal\Core\Render\Element::setAttributes($this->array, $map);
+ CoreElement::setAttributes($this->array, $map);
return $this;
}
* An array of property keys for the element.
*/
public function properties() {
- return \Drupal\Core\Render\Element::properties($this->array);
+ return CoreElement::properties($this->array);
}
/**
* The name of the property to set.
* @param mixed $value
* The value of the property to set.
+ * @param bool $recurse
+ * Flag indicating wither to set the same property on child elements.
*
* @return $this
*/
- public function setProperty($name, $value) {
+ public function setProperty($name, $value, $recurse = FALSE) {
$this->array["#$name"] = $value instanceof Element ? $value->getArray() : $value;
+ if ($recurse) {
+ foreach ($this->children() as $child) {
+ $child->setProperty($name, $value, $recurse);
+ }
+ }
return $this;
}
/**
* Converts an element description into a tooltip based on certain criteria.
*
- * @param array|\Drupal\bootstrap\Utility\Element|NULL $target_element
+ * @param array|\Drupal\bootstrap\Utility\Element|null $target_element
* The target element render array the tooltip is to be attached to, passed
* by reference or an existing Element object. If not set, it will default
* this Element instance.
}
// Allow a different element to attach the tooltip.
- /** @var Element $target */
+ /** @var \Drupal\bootstrap\Utility\Element $target */
if (is_object($target_element) && $target_element instanceof self) {
$target = $target_element;
}
// Return if element or target shouldn't have "simple" tooltip descriptions.
$html = FALSE;
- if (($input_only && !$target->hasProperty('input'))
- // Ignore if the actual element has no #description set.
- || !$this->hasProperty('description')
+
+ // If the description is a render array, it must first be pre-rendered so
+ // it can be later passed to Unicode::isSimple() if needed.
+ $description = $this->hasProperty('description') ? $this->getProperty('description') : FALSE;
+ if (static::isRenderArray($description)) {
+ $description = static::createStandalone($description)->renderPlain();
+ }
+
+ if (
+ // Ignore if element has no #description.
+ !$description
+
+ // Ignore if description is not a simple string or MarkupInterface.
+ || (!is_string($description) && !($description instanceof MarkupInterface))
+
+ // Ignore if element is not an input.
+ || ($input_only && !$target->hasProperty('input'))
// Ignore if the target element already has a "data-toggle" attribute set.
|| $target->hasAttribute('data-toggle')
|| !$target->getProperty('smart_description', TRUE)
// Ignore if the description is not "simple".
- || !Unicode::isSimple($this->getProperty('description'), $length, $allowed_tags, $html)
+ || !Unicode::isSimple($description, $length, $allowed_tags, $html)
) {
// Set the both the actual element and the target element
// #smart_description property to FALSE.
$attributes = $target->getAttributes($type);
// Set the tooltip attributes.
- $attributes['title'] = $allowed_tags !== FALSE ? Xss::filter((string) $this->getProperty('description'), $allowed_tags) : $this->getProperty('description');
+ $attributes['title'] = $allowed_tags !== FALSE ? Xss::filter((string) $description, $allowed_tags) : $description;
$attributes['data-toggle'] = 'tooltip';
if ($html || $allowed_tags === FALSE) {
$attributes['data-html'] = 'true';