367 lines
9.4 KiB
PHP
367 lines
9.4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @author Tassos Marinos <info@tassos.gr>
|
|
* @link http://www.tassos.gr
|
|
* @copyright Copyright © 2022 Tassos Marinos All Rights Reserved
|
|
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
|
*/
|
|
|
|
namespace NRFramework\Controls;
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
class Control
|
|
{
|
|
/**
|
|
* The CSS selector related to this control.
|
|
*
|
|
* @var string|array
|
|
*/
|
|
protected $selector;
|
|
|
|
/**
|
|
* The CSS property related to this control.
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $property;
|
|
|
|
/**
|
|
* The CSS property used when there are conditions and we fail to use the property, so we override it using this property.
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $fallback_property;
|
|
|
|
/**
|
|
* The CSS property used when there are no conditions and we fail to use the property, so we override it using this value.
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $fallback_value;
|
|
|
|
/**
|
|
* Some controls may render CSS conditionally, based on the given value.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $values;
|
|
|
|
/**
|
|
* The control value.
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $value;
|
|
|
|
/**
|
|
* The raw control value.
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $value_raw;
|
|
|
|
/**
|
|
* The control value unit.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $unit;
|
|
|
|
/**
|
|
* Exclude specific breakpoints from the control's CSS.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $exclude_breakpoints = [];
|
|
|
|
/**
|
|
* The existing controls we have parsed so far.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $parsedControls = [];
|
|
|
|
/**
|
|
* A control may require some conditions to be set.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $conditions = [];
|
|
|
|
/**
|
|
* Whether to ignore "inherit" values.
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $skip_inherit_value = false;
|
|
|
|
/**
|
|
* The current breakpoint we are checking against to set CSS.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $current_breakpoint = 'desktop';
|
|
|
|
public function __construct($payload = [])
|
|
{
|
|
$this->parsedControls = isset($payload['parsedControls']) ? $payload['parsedControls'] : null;
|
|
$this->selector = isset($payload['selector']) ? $payload['selector'] : null;
|
|
$this->conditions = isset($payload['conditions']) ? $payload['conditions'] : [];
|
|
$this->property = isset($payload['property']) ? $payload['property'] : null;
|
|
$this->skip_inherit_value = isset($payload['skip_inherit_value']) ? $payload['skip_inherit_value'] : $this->skip_inherit_value;
|
|
// The fallback_property is used when we have conditions and we don't have a value, so we override it using this property.
|
|
$this->fallback_property = isset($payload['fallback_property']) ? $payload['fallback_property'] : null;
|
|
// The fallback_value is used when we don't have conditions and we don't have a value, so we override it using this value.
|
|
$this->fallback_value = isset($payload['fallback_value']) ? $payload['fallback_value'] : null;
|
|
$this->values = isset($payload['values']) ? $payload['values'] : [];
|
|
$this->exclude_breakpoints = isset($payload['exclude_breakpoints']) ? $payload['exclude_breakpoints'] : null;
|
|
$this->value = isset($payload['value']['value']) ? $payload['value']['value'] : (isset($payload['value']) ? $payload['value'] : null);
|
|
$this->value_raw = isset($payload['value']['value']) ? $payload['value']['value'] : (isset($payload['value']) ? $payload['value'] : null);
|
|
$this->unit = isset($payload['value']['unit']) ? $payload['value']['unit'] : (isset($payload['unit']) ? $payload['unit'] : null);
|
|
|
|
if (isset($this->value['unit']))
|
|
{
|
|
unset($this->value['unit']);
|
|
}
|
|
}
|
|
|
|
public function getCSS()
|
|
{
|
|
if (!$this->isResponsive())
|
|
{
|
|
$this->value = $this->generateCSSProperty($this->value, $this->unit);
|
|
return $this->value;
|
|
}
|
|
|
|
// Prepare value for arrays
|
|
foreach ($this->value as $breakpoint => &$value)
|
|
{
|
|
$this->current_breakpoint = $breakpoint;
|
|
|
|
// If this breakpoint is excluded, skip
|
|
if (is_array($this->exclude_breakpoints) && count($this->exclude_breakpoints) && in_array($breakpoint, $this->exclude_breakpoints))
|
|
{
|
|
unset($this->value[$breakpoint]);
|
|
continue;
|
|
}
|
|
|
|
if (is_scalar($value))
|
|
{
|
|
$value = $this->generateCSSProperty($value, $this->unit);
|
|
}
|
|
else
|
|
{
|
|
$unit = isset($value['unit']) ? $value['unit'] : $this->unit;
|
|
|
|
// Remove "unit" property
|
|
if ($unit)
|
|
{
|
|
unset($value['unit']);
|
|
}
|
|
|
|
// Remove "linked" property
|
|
if (isset($value['linked']))
|
|
{
|
|
unset($value['linked']);
|
|
}
|
|
|
|
$value = isset($value['value']) ? $value['value'] : $value;
|
|
|
|
// Remove responsive value if no actual value is set
|
|
if (!$value && $value != '0' && $unit != 'auto')
|
|
{
|
|
unset($this->value[$breakpoint]);
|
|
continue;
|
|
}
|
|
|
|
$value = $this->generateCSSProperty($value, $unit);
|
|
}
|
|
|
|
if (is_null($value))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return $this->value;
|
|
}
|
|
|
|
protected function generateCSSProperty($value, $unit)
|
|
{
|
|
if ($this->shouldSkipPropertyGeneration($value, $unit))
|
|
{
|
|
return;
|
|
}
|
|
|
|
$conditions_pass = $this->conditionsPass($value);
|
|
|
|
$conditionsNotMet = (!$conditions_pass && !$this->fallback_property);
|
|
$emptyValueWithUnitNotAuto = ($value === '' || is_null($value)) && !$this->fallback_property && $unit !== 'auto';
|
|
|
|
if ($conditionsNotMet || $emptyValueWithUnitNotAuto)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ($this->values)
|
|
{
|
|
return $this->generateValueCSS($value);
|
|
}
|
|
|
|
$properties = $this->conditions && !$conditions_pass && $this->fallback_property ? $this->fallback_property : $this->property;
|
|
|
|
// If we have no conditions and no value, but we have a fallback value, use it.
|
|
if (!$this->conditions && !$value && $this->fallback_value)
|
|
{
|
|
$properties = $this->fallback_value;
|
|
}
|
|
|
|
|
|
if (is_array($properties))
|
|
{
|
|
return $this->generateArrayPropertyCSS($properties, $conditions_pass, $value, $unit);
|
|
}
|
|
|
|
if ($value === 'inherit' && $this->skip_inherit_value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
return $this->generateSinglePropertyCSS($value, $unit);
|
|
}
|
|
|
|
private function shouldSkipPropertyGeneration($value, $unit)
|
|
{
|
|
return !$value && $value != '0' && $unit != 'auto' && !$this->fallback_property;
|
|
}
|
|
|
|
private function generateValueCSS($value)
|
|
{
|
|
$css = '';
|
|
|
|
$value = explode(' ', $value);
|
|
|
|
foreach ($this->values as $key => $css_value)
|
|
{
|
|
if (!in_array($key, $value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$css .= implode('', $css_value);
|
|
}
|
|
|
|
return $css;
|
|
}
|
|
|
|
private function generateArrayPropertyCSS($properties, $conditions_pass, $value, $unit)
|
|
{
|
|
$css = '';
|
|
|
|
// If the conditions did pass and we do not have a value, do not set any CSS.
|
|
if ($conditions_pass && !$value && $unit !== 'auto')
|
|
{
|
|
return $css;
|
|
}
|
|
|
|
foreach ($properties as $prop_key => $prop_value)
|
|
{
|
|
$css_line = $prop_key . ':' . str_replace('%value%', $value . $unit, $prop_value) . ';';
|
|
$css_line = str_replace('%value_raw%', $value , $css_line);
|
|
|
|
$css .= $css_line;
|
|
}
|
|
|
|
return $css;
|
|
}
|
|
|
|
private function generateSinglePropertyCSS($value, $unit)
|
|
{
|
|
$value = \NRFramework\Helpers\Controls\Control::findUnitInValue($value);
|
|
$val = isset($value['value']) ? $value['value'] : $value;
|
|
$unit = isset($value['unit']) && $value['unit'] ? $value['unit'] : $unit;
|
|
|
|
return $this->getProperty() . ':' . $val . $unit . ';';
|
|
}
|
|
|
|
private function conditionsPass(&$value)
|
|
{
|
|
if (!$this->conditions)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
foreach ($this->conditions as $conditionItem)
|
|
{
|
|
if (!$this->checkCondition($conditionItem, $value))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function checkCondition($conditionItem, &$value)
|
|
{
|
|
$foundCondition = array_search($conditionItem['property'], array_column($this->parsedControls, 'property'));
|
|
|
|
if ($foundCondition === false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$foundConditionControl = isset($this->parsedControls[$foundCondition]) ? $this->parsedControls[$foundCondition] : false;
|
|
|
|
if (!$foundConditionControl)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$conditionValue = $conditionItem['property'] . ':' . $conditionItem['value'] . ';';
|
|
$foundConditionControlValues = $foundConditionControl['control']->getValue();
|
|
$breakpointValue = is_array($foundConditionControlValues) ? ($foundConditionControlValues[$this->current_breakpoint] ?? null) : $foundConditionControlValues;
|
|
|
|
if (\NRFramework\Functions::endsWith($breakpointValue, ':inherit;'))
|
|
{
|
|
$prevBreakpoint = $this->current_breakpoint === 'tablet' ? 'desktop' : 'tablet';
|
|
$breakpointValue = is_array($foundConditionControlValues) ? ($foundConditionControlValues[$prevBreakpoint] ?? null) : $foundConditionControlValues;
|
|
|
|
// Also update the value
|
|
$value = isset($this->value_raw[$prevBreakpoint]['value']) || isset($this->value_raw[$prevBreakpoint]) ? '' : $value;
|
|
|
|
if (\NRFramework\Functions::endsWith($breakpointValue, ':inherit;') && $this->current_breakpoint === 'mobile' && $prevBreakpoint === 'tablet')
|
|
{
|
|
$breakpointValue = is_array($foundConditionControlValues) ? ($foundConditionControlValues['desktop'] ?? null) : $foundConditionControlValues;
|
|
|
|
// Also update the value
|
|
$value = isset($this->value_raw['desktop']['value']) || isset($this->value_raw['desktop']) ? '' : $value;
|
|
}
|
|
}
|
|
|
|
return $conditionValue === $breakpointValue;
|
|
}
|
|
|
|
public function getValue()
|
|
{
|
|
return $this->value;
|
|
}
|
|
|
|
public function getSelector()
|
|
{
|
|
return $this->selector;
|
|
}
|
|
|
|
public function getProperty()
|
|
{
|
|
return $this->property;
|
|
}
|
|
|
|
public function isResponsive()
|
|
{
|
|
$keys = ['desktop', 'tablet', 'mobile'];
|
|
return is_array($this->value) && !empty(array_intersect_key(array_flip($keys), $this->value));
|
|
}
|
|
} |