vlsuite-1.0.x-dev/modules/vlsuite_utility_classes/src/VLSuiteUtilityClassesHelper.php
modules/vlsuite_utility_classes/src/VLSuiteUtilityClassesHelper.php
<?php
namespace Drupal\vlsuite_utility_classes;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Render\Element;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Template\Attribute;
/**
* Helper "VLSuiteUtilityClassesHelper" service object.
*/
class VLSuiteUtilityClassesHelper {
use StringTranslationTrait;
const UTILITY_CLASSES_KEY = 'vlsuite_utility_class';
/**
* The config factory object.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The module handler object.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Utility apply to options.
*
* @var array
*/
protected $utilityApplyToOptions;
/**
* Constructs a "VLSuiteUtilityClassesHelper" object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* A configuration factory instance.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* A module handler instance.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
$this->configFactory = $config_factory;
$this->moduleHandler = $module_handler;
}
/**
* Get utility apply to options.
*
* @return array
* Options.
*/
public function getUtilityApplyToOptions() {
if (empty($this->utilityApplyToOptions)) {
$apply_to_options = [];
$this->moduleHandler->alter('vlsuite_utility_classes_utility_apply_to_options', $apply_to_options);
$this->utilityApplyToOptions = $apply_to_options;
}
return $this->utilityApplyToOptions;
}
/**
* Get utilities map classes.
*
* @return array
* Utilities map by identifiers, with visual names, classes & no apply to.
*/
public function getUtilitiesMapClasses() {
$utilities = $this->configFactory->get('vlsuite_utility_classes.settings')->get('utilities');
$utilities_map = [];
foreach ($utilities as $utility_identifier => $utility) {
$utilities_map[$utility_identifier] = [
'visual_name' => $utility['visual_name'],
'values' => [],
];
foreach ($utility['values'] as $key => $value) {
$utilities_map[$utility_identifier]['values'][$key] = [
'visual_name' => $value['visual_name'],
'classes' => $this->getUtilityKeyValueClasses($utility_identifier, $key),
];
}
}
$column_widths = [];
foreach ($this->configFactory->get('vlsuite_utility_classes.settings')->get('col_classes') as $column_widths_key_raw => $column_widths_classes) {
[, $column_widths_key] = explode('col_', $column_widths_key_raw);
$column_widths[$column_widths_key] = explode(' ', $column_widths_classes);
}
return $utilities_map + ['column_widths' => $column_widths];
}
/**
* Check utility apply to value is valid.
*
* @param string $apply_to
* Apply to.
* @param string $identifier
* Identifier,
* @param string $value
* Value.
*
* @return boolean
* Valid or not.
*/
public function checkUtilityApplyToValueIsValid($apply_to, $identifier, $value) {
$utilities = $this->configFactory->get('vlsuite_utility_classes.settings')->get('utilities');
return !empty($utilities[$identifier]['values'][$value]) &&
!empty($utilities[$identifier]['apply_to'][$apply_to]) ?? FALSE;
}
/**
* Get utility apply to list of utilities.
*
* @param string $apply_to
* Apply to.
*
* @return array
* Utilities for passed apply to.
*/
public function getUtilitiesApplyToList($apply_to) {
$utilities = $this->configFactory->get('vlsuite_utility_classes.settings')->get('utilities');
$utilities_apply_to = [];
foreach ($utilities as $utility_identifier => $utility) {
if (!empty($utility['apply_to'][$apply_to])) {
unset($utility['apply_to']);
$utilities_apply_to[$utility_identifier] = $utility;
}
}
return $utilities_apply_to;
}
/**
* Get utility key value classes.
*
* @param string $utility_key
* Utility key.
* @param string $value
* Utility value.
*
* @return array
* Utility classes for passed value.
*/
public function getUtilityKeyValueClasses(string $utility_key, string $value) {
$utilities = $this->configFactory->get('vlsuite_utility_classes.settings')->get('utilities');
$class_prefix = $utilities[$utility_key]['class_prefix'] ?? '';
$class_value = !empty($utilities[$utility_key]['values'][$value]) ? $utilities[$utility_key]['values'][$value]['class_suffix'] ?? '' : NULL;
// Defined by prefix.
if ($class_value == 'defined-by-prefix') {
$class_value = '';
}
return $class_value !== NULL ? array_filter(explode(' ', $class_prefix . $class_value)) : [];
}
/**
* Get utilities apply to classes.
*
* @param string $apply_to
* Apply to.
* @param array $utility_classes_config
* Utility classes config.
*
* @return array
* Utilities apply to classes.
*/
public function getUtilitiesApplyToClasses($apply_to, array $utility_classes_config) {
$apply_to_utilities = $this->getUtilitiesApplyToList($apply_to);
$apply_to_config = $utility_classes_config ?? [];
$apply_to_classes = [];
foreach (array_keys($apply_to_utilities) as $utility_key) {
if (!empty($apply_to_config[$utility_key]) || (isset($apply_to_config[$utility_key]) && $apply_to_config[$utility_key] == '0')) {
$utility_classes = $this->getUtilityKeyValueClasses($utility_key, $apply_to_config[$utility_key]);
$apply_to_classes = array_merge($apply_to_classes, $utility_classes);
}
}
return $apply_to_classes;
}
/**
* Get container classes.
*
* @return array
* Classes.
*/
public function getContainerClasses() {
$container_classes = $this->configFactory->get('vlsuite_utility_classes.settings')->get('container_classes') ?? '';
return explode(' ', $container_classes);
}
/**
* Get list unstyled classes.
*
* @return array
* Classes.
*/
public function getListUnstyledClasses() {
$list_unstyled_classes = $this->configFactory->get('vlsuite_utility_classes.settings')->get('list_unstyled_classes') ?? '';
return explode(' ', $list_unstyled_classes);
}
/**
* Get row classes.
*
* @return array
* Classes.
*/
public function getRowClasses() {
$container_classes = $this->configFactory->get('vlsuite_utility_classes.settings')->get('row_classes') ?? '';
return explode(' ', $container_classes);
}
/**
* Get col variant keys.
*
* @return array
* List.
*/
public static function getColPercentageOptions() {
return ['100', '80', '75', '67', '60', '50', '40', '33', '25'];
}
/**
* Get col option classes.
*
* @param string $col_option
* Option (percentage or auto).
*
* @return array
* Classes.
*/
public function getColPercentageOptionClasses($col_option) {
$col_classes = $this->configFactory->get('vlsuite_utility_classes.settings')->get('col_classes')['col_' . $col_option] ?? '';
return explode(' ', $col_classes);
}
/**
* Get utilities apply to list form element.
*
* @param array $apply_to_definitions
* Apply to definitions.
* @param array $defaults
* Config for default values.
*
* @return array
* Form element.
*/
public function getUtilitiesApplyToListFormElement(array $apply_to_definitions, array $defaults) {
$element = [
'#type' => 'details',
'#title' => $this->t('Appearance'),
'#description' => $this->t('Use "Appearance" floating UI icons for live previewer UX.'),
'#open' => FALSE,
'#tree' => TRUE,
'#attached' => ['library' => ['vlsuite_utility_classes/previewer']],
'#attributes' => ['class' => ['vlsuite-utility-classes']],
];
$element['previewer'] = [
'#type' => 'container',
'#attributes' => ['class' => ['vlsuite-utility-classes__previewer']],
'text' => ['#markup' => $this->t('New selection previewer')],
];
$has_any = FALSE;
foreach ($apply_to_definitions as $utility_classes_apply_to_key => $definition) {
$utilities = $this->getUtilitiesApplyToList($utility_classes_apply_to_key);
$element[$utility_classes_apply_to_key] = [
'#type' => 'details',
'#title' => $definition['form_element_title'],
'#open' => FALSE,
'#access' => !empty($utilities),
];
$has_any = $has_any ? $has_any : !empty($utilities);
foreach ($utilities as $utility_identifier => $utility) {
$utility_options = [];
$utility_suffix_map = [];
foreach ($utility['values'] as $value_identifier => $value_values) {
$utility_options[$value_identifier] = $value_values['visual_name'];
$utility_suffix_map[$value_identifier] = $value_values['class_suffix'];
}
$element[$utility_classes_apply_to_key][$utility_identifier] = [
'#type' => 'select',
'#empty_option' => $this->t('Default'),
'#options' => $utility_options,
'#title' => $utility['visual_name'],
'#default_value' => $defaults[$utility_classes_apply_to_key][$utility_identifier] ?? NULL,
'#attributes' => [
'class' => ['vlsuite-utility-classes__previewer-option'],
'data-class-prefix' => $utility['class_prefix'],
'data-class-key' => $utility_identifier,
'data-class-suffix-map' => Json::encode($utility_suffix_map),
],
];
}
}
if (!$has_any) {
$element['#access'] = FALSE;
}
return $element;
}
/**
* Get utilities apply to list form element when submit.
*
* @param array $vlsuite_utility_classes
* Form element result raw.
*
* @return array
* Form element result of used utilities.
*/
public function getUtilitiesApplyToListFormElementSubmit(array $vlsuite_utility_classes) {
$utility_classes_config = [];
foreach ($vlsuite_utility_classes as $apply_to => $utilities) {
$utility_classes_config[$apply_to] = array_filter($utilities, 'strlen');
}
$utility_classes_config = array_filter($utility_classes_config);
return $utility_classes_config;
}
/**
* Helper to apply utility classes to build array parents (mainly for block).
*
* @param array $build
* Build.
* @param array $classes
* Classes array.
* @param array $parents_group
* Parents of element where to apply classes.
*/
protected function buildApplyUtilityClassParents(array &$build, array $classes, array $parents_group) {
foreach ($parents_group as $parents) {
$ref = &$build;
foreach (explode(':', $parents) as $parent) {
$new_restriction = (str_contains($parent, 'attributes') || str_contains($parent, 'class'));
if (is_array($ref)) {
if (!isset($ref[$parent]) && $new_restriction) {
$ref[$parent] = [];
}
$ref = &$ref[$parent];
}
elseif (is_object($ref)) {
if (!isset($ref->$parent) && $new_restriction) {
$ref->$parent = [];
}
$ref = &$ref->$parent;
}
}
if ($ref !== NULL) {
$ref = array_merge($ref, $classes);
}
}
}
/**
* Helper to apply utility classes to build array (mainly for block).
*
* @param array $apply_to_list
* Apply to list.
* @param array $build
* Build.
*/
public function buildApplyUtilityClasses(array $apply_to_list, array &$build) {
$is_field = (!empty($build['#theme']) && $build['#theme'] == 'field' || !empty($build[0]['#theme']) && $build[0]['#theme'] == 'field');
// @see https://git.drupalcode.org/project/drupal/-/commit/7598b15a28f370ae194153c183b158b13670703a
$is_field_parent_preffix = $is_field && !empty($build[0]['#theme']) && $build[0]['#theme'] == 'field' ? '0:' : '';
foreach ($apply_to_list as $apply_to => $utility_classes_config) {
$classes = $this->getUtilitiesApplyToClasses($apply_to, $utility_classes_config) ?? NULL;
$apply_to_array = explode(':', $apply_to);
if (count($apply_to_array) == 4) {
[, $bundle, $field, $item] = $apply_to_array;
}
elseif (count($apply_to_array) == 3) {
[, $bundle, $field] = $apply_to_array;
$item = NULL;
}
elseif (count($apply_to_array) == 2) {
[, $bundle] = $apply_to_array;
$item = NULL;
$field = NULL;
}
else {
$bundle = NULL;
$field = NULL;
$item = NULL;
}
$parents_group = [];
if (!empty($item)) {
$build_apply_to = !$is_field && !empty($field) && !empty($build[$field]) ? $build[$field] : $build;
foreach (Element::children($build_apply_to) as $delta) {
// @see template_preprocess_field().
$parents_group[] = (!$is_field ? $field . ':' : $is_field_parent_preffix) . $delta . ':#attributes:class';
// @code $parents_group[] = (!$is_field ? $field . ':' : '') . $delta . ':_attributes:class'; @endcode
$parents_group[] = (!$is_field ? $field . ':' : $is_field_parent_preffix) . $delta . ':#item_attributes:class';
}
}
elseif (!empty($field)) {
$parents_group[] = (!$is_field ? $field . ':' : '') . '#attributes:class';
}
elseif (!empty($bundle)) {
$parents_group[] = '#attributes:class';
}
if (!empty($classes) && !empty($parents_group)) {
$this->buildApplyUtilityClassParents($build, $classes, $parents_group);
}
}
}
/**
* @todo refactor
*/
public function applyLivePreviewerAttributes(Attribute|array &$attributes, $apply_to, array $defaults, array $apply_to_list_extra = []) {
$apply_to_list = $apply_to_list_extra + $this->getUtilitiesApplyToList($apply_to);
[, $apply_to_type] = explode(':', $apply_to);
if ($attributes instanceof Attribute) {
$attributes->setAttribute('data-vlsuite-utility-classes-live-previewer-apply-to', $apply_to);
$attributes->setAttribute('data-vlsuite-utility-classes-live-previewer-type', $apply_to_type);
$attributes->setAttribute('data-vlsuite-utility-classes-live-previewer-identifiers', implode(',', array_keys($apply_to_list)));
$attributes->setAttribute('data-vlsuite-utility-classes-live-previewer-defaults', Json::encode($defaults));
}
else {
$attributes['data-vlsuite-utility-classes-live-previewer-apply-to'] = $apply_to;
$attributes['data-vlsuite-utility-classes-live-previewer-type'] = $apply_to_type;
$attributes['data-vlsuite-utility-classes-live-previewer-identifiers'] = implode(',', array_keys($apply_to_list));
$attributes['data-vlsuite-utility-classes-live-previewer-defaults'] = Json::encode($defaults);
}
}
/**
* @todo refactor
*/
public function buildLivePreviewer(array $apply_to_list, array &$build, array $defaults) {
$is_field = (!empty($build['#theme']) && $build['#theme'] == 'field' || !empty($build[0]['#theme']) && $build[0]['#theme'] == 'field');
// @see https://git.drupalcode.org/project/drupal/-/commit/7598b15a28f370ae194153c183b158b13670703a
$is_field_parent_preffix = $is_field && !empty($build[0]['#theme']) && $build[0]['#theme'] == 'field' ? '0:' : '';
foreach (array_keys($apply_to_list) as $apply_to) {
$apply_to_array = explode(':', $apply_to);
$apply_to_utilities = $this->getUtilitiesApplyToList($apply_to);
$apply_to_type = NULL;
if (count($apply_to_array) == 4) {
$apply_to_type = 'item';
[, $bundle, $field, $item] = $apply_to_array;
}
elseif (count($apply_to_array) == 3) {
$apply_to_type = $is_field ? 'block' : 'field';
[, $bundle, $field] = $apply_to_array;
$item = NULL;
}
elseif (count($apply_to_array) == 2) {
$apply_to_type = 'block';
[, $bundle] = $apply_to_array;
$item = NULL;
$field = NULL;
}
else {
$apply_to_type = 'other';
$bundle = NULL;
$field = NULL;
$item = NULL;
}
$parents_group = [];
if (!empty($item)) {
$build_apply_to = !$is_field && !empty($field) && !empty($build[$field]) ? $build[$field] : $build;
foreach (Element::children($build_apply_to) as $delta) {
// @see template_preprocess_field().
$parents_group[] = (!$is_field ? $field . ':' : $is_field_parent_preffix) . $delta . ':#attributes';
$parents_group[] = (!$is_field ? $field . ':' : $is_field_parent_preffix) . $delta . ':#item_attributes';
}
}
elseif (!empty($field)) {
$parents_group[] = (!$is_field ? $field . ':' : '') . '#attributes';
}
elseif (!empty($bundle)) {
$parents_group[] = '#attributes';
}
if (!empty($parents_group)) {
$this->buildApplyUtilityClassPreviewParents($build, $parents_group, $apply_to, $apply_to_utilities, $defaults[$apply_to] ?? [], $apply_to_type);
}
}
}
/**
* @todo refactor
*/
protected function buildApplyUtilityClassPreviewParents(array &$build, array $parents_group, $apply_to, $utilities, $defaults, $apply_to_type) {
$attributes = [];
$attributes['data-vlsuite-utility-classes-live-previewer-apply-to'] = $apply_to;
$attributes['data-vlsuite-utility-classes-live-previewer-type'] = $apply_to_type;
$attributes['data-vlsuite-utility-classes-live-previewer-identifiers'] = implode(',', array_keys($utilities));
if ($apply_to_type == 'main_regions') {
$attributes['data-vlsuite-utility-classes-group'] = 'main_regions';
}
if (!empty($defaults)) {
$attributes['data-vlsuite-utility-classes-live-previewer-defaults'] = Json::encode($defaults);
}
foreach ($parents_group as $parents) {
$ref = &$build;
foreach (explode(':', $parents) as $parent) {
$new_restriction = (str_contains($parent, 'attributes'));
if (is_array($ref)) {
if (!isset($ref[$parent]) && $new_restriction) {
$ref[$parent] = [];
}
$ref = &$ref[$parent];
}
elseif (is_object($ref)) {
if (!isset($ref->$parent) && $new_restriction) {
$ref->$parent = [];
}
$ref = &$ref->$parent;
}
}
if ($ref !== NULL) {
if ($ref instanceof Attribute) {
foreach ($attributes as $key => $value) {
$ref->setAttribute($key, $value);
}
}
else {
$ref = array_merge($ref, $attributes);
}
}
}
}
}
