diff --git a/css/toggler.png b/css/toggler.png new file mode 100644 index 0000000..e8db53e Binary files /dev/null and b/css/toggler.png differ diff --git a/css/ui_patterns_settings.css b/css/ui_patterns_settings.css new file mode 100644 index 0000000..970532e --- /dev/null +++ b/css/ui_patterns_settings.css @@ -0,0 +1,28 @@ +.js-ui-patterns-settings__wrapper { + position: relative; +} +.js-ui-patterns-settings__toggler { + position: absolute; + width: 14px; + height: 14px; + right: 0; + top: 0; + display: block; + cursor: pointer; + background-image: url("toggler.png"); + background-repeat: no-repeat; + background-size: cover; +} + + +.js-ui-patterns-settings__wrapper .js-ui-patterns-settings__token-wrapper > * { + display: none; +} + +.js-ui-patterns-settings__wrapper.js-ui-patterns-settings--token-has-value > .js-ui-patterns-settings__input-wrapper > * { + display: none; +} + +.js-ui-patterns-settings__wrapper.js-ui-patterns-settings--token-has-value > .js-ui-patterns-settings__token-wrapper > * { + display: block; +} diff --git a/js/ui_pattern_settings.toggle_token.js b/js/ui_pattern_settings.toggle_token.js new file mode 100644 index 0000000..53d0a3d --- /dev/null +++ b/js/ui_pattern_settings.toggle_token.js @@ -0,0 +1,44 @@ +/** + * @file + * JavaScript file for the UI Pattern settings module. + */ + +(function ($, Drupal, drupalSettings, DrupalCoffee) { + + 'use strict'; + + /** + * Attaches ui patterns settings module behaviors. + * + * Handles enable/disable token element. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attach ui patterns settings toggle functionality to the page. + * + * @todo get most of it out of the behavior in dedicated functions. + */ + Drupal.behaviors.ups_toggle_token = { + attach: function () { + $('.js-ui-patterns-settings__wrapper').once().each(function () { + var wrapper = $(this); + var toggler = $('
'); + $(toggler).click(function () { + var tokenInput = $('.js-ui-patterns-settings__token', wrapper); + if ($(wrapper).hasClass('js-ui-patterns-settings--token-has-value')) { + tokenInput.attr('data-init-val', tokenInput.val()); + tokenInput.val(''); + wrapper.removeClass('js-ui-patterns-settings--token-has-value'); + } else { + tokenInput.val(tokenInput.attr('data-init-val')); + wrapper.addClass('js-ui-patterns-settings--token-has-value'); + } + }); + $('.js-ui-patterns-settings__input-wrapper', wrapper).append(toggler) + $('.js-ui-patterns-settings__token-wrapper', wrapper).append(toggler.clone(true)) + }); + } + }; + +})(jQuery, Drupal, drupalSettings); diff --git a/runner.yml.dist b/runner.yml.dist index 2311f81..1b96879 100644 --- a/runner.yml.dist +++ b/runner.yml.dist @@ -32,7 +32,10 @@ selenium: commands: drupal:site-setup: - { task: "symlink", from: "../../../../src", to: "${drupal.root}/modules/custom/ui_patterns_settings/src" } + - { task: "symlink", from: "../../../../js", to: "${drupal.root}/modules/custom/ui_patterns_settings/js" } + - { task: "symlink", from: "../../../../css", to: "${drupal.root}/modules/custom/ui_patterns_settings/css" } - { task: "symlink", from: "../../../../tests", to: "${drupal.root}/modules/custom/ui_patterns_settings/tests" } + - { task: "symlink", from: "../../../../ui_patterns_settings.libraries.yml", to: "${drupal.root}/modules/custom/ui_patterns_settings/ui_patterns_settings.libraries.yml" } - { task: "symlink", from: "../../../../ui_patterns_settings.info.yml", to: "${drupal.root}/modules/custom/ui_patterns_settings/ui_patterns_settings.info.yml" } - { task: "symlink", from: "../../../../ui_patterns_settings.module", to: "${drupal.root}/modules/custom/ui_patterns_settings/ui_patterns_settings.module" } - { task: "symlink", from: "../../../../ui_patterns_settings.services.yml", to: "${drupal.root}/modules/custom/ui_patterns_settings/ui_patterns_settings.services.yml" } diff --git a/src/Definition/PatternDefinitionSetting.php b/src/Definition/PatternDefinitionSetting.php index 0921257..4a3ed6f 100644 --- a/src/Definition/PatternDefinitionSetting.php +++ b/src/Definition/PatternDefinitionSetting.php @@ -28,6 +28,7 @@ class PatternDefinitionSetting implements \ArrayAccess { 'forced_value' => NULL, 'options' => NULL, 'form_visible' => TRUE, + 'allow_token' => FALSE, ]; /** @@ -39,6 +40,7 @@ public function __construct($name, $value) { $this->definition['label'] = $value; $this->definition['type'] = 'textfield'; $this->definition['preview'] = NULL; + $this->definition['allow_token'] = FALSE; } else { $this->definition['name'] = !isset($value['name']) ? $name : $value['name']; @@ -47,6 +49,7 @@ public function __construct($name, $value) { $this->definition['default_value'] = isset($value['default_value']) ? $value['default_value'] : NULL; $this->definition['preview'] = isset($value['preview']) ? $value['preview'] : NULL; $this->definition['options'] = isset($value['options']) ? $value['options'] : NULL; + $this->definition['allow_token'] = isset($value['allow_token']) ? $value['allow_token'] : FALSE; $this->definition = $value + $this->definition; } } @@ -91,6 +94,16 @@ public function getRequired() { return $this->definition['required']; } + /** + * Get allow token property. + * + * @return bool + * Property value. + */ + public function getAllowToken() { + return $this->definition['allow_token']; + } + /** * Get options array. * @@ -122,6 +135,19 @@ public function setDefaultValue($defaultValue) { return $this; } + /** + * Set allow token value property. + * + * @param bool $allow_token + * Property value. + * + * @return $this + */ + public function setAllowToken($allow_token) { + $this->definition['allow_token'] = $allow_token; + return $this; + } + /** * Get default value property. * diff --git a/src/Element/PatternSettings.php b/src/Element/PatternSettings.php index 31eedad..9cf867c 100644 --- a/src/Element/PatternSettings.php +++ b/src/Element/PatternSettings.php @@ -37,6 +37,17 @@ public static function processPreviewSettings(array $element) { * Render array. */ public static function processSettings(array $element, $preview = FALSE) { + $context = $element['#context']; + + // Handle Variant token. + if (!empty($element['#variant_token'])) { + $variant_token = $element['#variant_token']; + $entity = $context->getProperty('entity'); + if ($entity !== NULL) { + $token_data[$entity->getEntityTypeId()] = $entity; + } + $element['#variant'] = \Drupal::token()->replace($variant_token, $token_data, ['clear' => TRUE]); + } // Make sure we don't render anything in case fields are empty. if (self::hasSettings($element)) { $settings = isset($element['#settings']) ? $element['#settings'] : []; @@ -51,7 +62,6 @@ public static function processSettings(array $element, $preview = FALSE) { $configuration = $layout->getConfiguration(); $settings = isset($configuration['pattern']['settings']) ? $configuration['pattern']['settings'] : []; } - $context = $element['#context']; $pattern_id = $element['#id']; $entity = $context->getProperty('entity'); $variant = isset($element['#variant']) ? $element['#variant'] : NULL; diff --git a/src/Form/SettingsFormBuilder.php b/src/Form/SettingsFormBuilder.php index 1b2d22e..74286a0 100644 --- a/src/Form/SettingsFormBuilder.php +++ b/src/Form/SettingsFormBuilder.php @@ -5,6 +5,7 @@ use Drupal\ui_patterns\Definition\PatternDefinition; use Drupal\ui_patterns\UiPatterns; use Drupal\ui_patterns_settings\UiPatternsSettings; +use Drupal\ui_patterns_settings\UiPatternsSettingsManager; /** * Build settings in manage display form. @@ -23,6 +24,16 @@ class SettingsFormBuilder { */ public static function layoutForm(array &$form, PatternDefinition $definition, array $configuration) { $settings = UiPatternsSettings::getPatternDefinitionSettings($definition); + $form['#attached']['library'][] = 'ui_patterns_settings/widget'; + if (UiPatternsSettingsManager::allowVariantToken($definition)) { + $variant_token_value = isset($configuration['pattern']['variant_token']) ? $configuration['pattern']['variant_token'] : NULL; + $form['variant_token'] = [ + '#type' => 'textfield', + '#title' => 'Variant token', + '#default_value' => $variant_token_value, + ]; + } + $form['variant']['#attributes']['class'][] = 'ui-patterns-variant-selector-' . $definition->id(); if (!empty($settings)) { foreach ($settings as $key => $setting) { @@ -37,8 +48,9 @@ public static function layoutForm(array &$form, PatternDefinition $definition, a ]; } $setting_value = isset($configuration['pattern']['settings'][$key]) ? $configuration['pattern']['settings'][$key] : NULL; - $settingType = UiPatternsSettings::createSettingType($setting); - $form['settings'] += $settingType->buildConfigurationForm([], $setting_value); + $token_value = isset($configuration['pattern']['settings'][$key . "_token"]) ? $configuration['pattern']['settings'][$key . "_token"] : ""; + $settingType = UiPatternsSettings::createSettingType($definition, $setting); + $form['settings'] += $settingType->buildConfigurationForm([], $setting_value, $token_value, 'layouts_display'); } SettingsFormBuilder::buildVariantsForm(".ui-patterns-variant-selector-" . $definition->id(), $form['settings'], $definition); } @@ -53,9 +65,36 @@ public static function layoutForm(array &$form, PatternDefinition $definition, a * Configurations array. */ public static function displayForm(array &$form, array $configuration) { + $form['#attached']['library'][] = 'ui_patterns_settings/widget'; foreach (UiPatterns::getPatternDefinitions() as $pattern_id => $definition) { $settings = UiPatternsSettings::getPatternDefinitionSettings($definition); $form['variants'][$pattern_id]['#attributes']['class'][] = 'ui-patterns-variant-selector-' . $pattern_id; + if (UiPatternsSettingsManager::allowVariantToken($definition)) { + $variant_token_value = isset($configuration['variants_token'][$pattern_id]) ? $configuration['variants_token'][$pattern_id] : NULL; + $form['variants']['#weight'] = 20; + $form['pattern_mapping']['#weight'] = 30; + $form['pattern_settings']['#weight'] = 40; + $form['variants_token'] = [ + '#type' => 'container', + '#title' => t('Pattern Variant'), + '#weight' => 25, + '#states' => [ + 'visible' => [ + 'select[id="patterns-select"]' => ['value' => $pattern_id], + ], + ], + ]; + $form['variants_token'][$pattern_id] = [ + '#type' => 'textfield', + '#title' => t('Variant token'), + '#default_value' => $variant_token_value, + '#states' => [ + 'visible' => [ + 'select[id="patterns-select"]' => ['value' => $pattern_id], + ], + ], + ]; + } if (!empty($settings)) { foreach ($settings as $key => $setting) { if (empty($setting->getType()) || !$setting->isFormVisible()) { @@ -73,9 +112,10 @@ public static function displayForm(array &$form, array $configuration) { ]; } $fieldset = &$form['pattern_settings'][$pattern_id]; - $settingType = UiPatternsSettings::createSettingType($setting); - $setting_value = isset($configuration['pattern_settings'][$pattern_id][$key]) ? $configuration['pattern_settings'][$pattern_id][$key] : ""; - $fieldset += $settingType->buildConfigurationForm([], $setting_value); + $settingType = UiPatternsSettings::createSettingType($definition, $setting); + $setting_value = isset($configuration['pattern_settings'][$pattern_id][$key]) ? $configuration['pattern_settings'][$pattern_id][$key] : NULL; + $token_value = isset($configuration['pattern_settings'][$pattern_id][$key . "_token"]) ? $configuration['pattern_settings'][$pattern_id][$key . "_token"] : NULL; + $fieldset += $settingType->buildConfigurationForm([], $setting_value, $token_value, 'display'); } SettingsFormBuilder::buildVariantsForm('.ui-patterns-variant-selector-' . $pattern_id, $fieldset, $definition); } @@ -106,6 +146,7 @@ private static function buildVariantsForm($select_selector, array &$fieldset, Pa } // Hide configured setting. $fieldset[$name]['#states']['invisible'][][$select_selector]['value'] = $variant->getName(); + $fieldset[$name . '_token']['#states']['invisible'][][$select_selector]['value'] = $variant->getName(); } } } diff --git a/src/Plugin/Layout/PatternSettingsLayout.php b/src/Plugin/Layout/PatternSettingsLayout.php index 63c5660..517c54a 100644 --- a/src/Plugin/Layout/PatternSettingsLayout.php +++ b/src/Plugin/Layout/PatternSettingsLayout.php @@ -20,7 +20,9 @@ public function build(array $regions) { if (isset($configuration['pattern']['settings'])) { $build['#settings'] = $configuration['pattern']['settings']; } - + if (isset($configuration['pattern']['variant_token'])) { + $build['#variant_token'] = $configuration['pattern']['variant_token']; + } return $build; } diff --git a/src/Plugin/PatternSettingTypeBase.php b/src/Plugin/PatternSettingTypeBase.php index 7d7eef7..c6aba51 100644 --- a/src/Plugin/PatternSettingTypeBase.php +++ b/src/Plugin/PatternSettingTypeBase.php @@ -3,6 +3,7 @@ namespace Drupal\ui_patterns_settings\Plugin; use Drupal\Component\Plugin\ConfigurableInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\ui_patterns_settings\Definition\PatternDefinitionSetting; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -12,6 +13,13 @@ */ abstract class PatternSettingTypeBase extends PluginBase implements ConfigurableInterface, PatternSettingTypeInterface { + /** + * Return pattern definitions for setting . + * + * @var \Drupal\ui_patterns\Definition\PatternDefinition + */ + private $patternDefinition; + /** * Return pattern definitions for setting . * @@ -25,7 +33,9 @@ abstract class PatternSettingTypeBase extends PluginBase implements Configurable public function __construct(array $configuration, $plugin_id, array $plugin_definition) { $configuration += $this->defaultConfiguration(); $this->patternSettingDefinition = $configuration['pattern_setting_definition']; + $this->patternDefinition = $configuration['pattern_definition']; unset($configuration['pattern_setting_definition']); + unset($configuration['pattern_definition']); parent::__construct($configuration, $plugin_id, $plugin_definition); } @@ -131,6 +141,92 @@ public function settingsPreprocess($value, array $context, PatternDefinitionSett return $value; } + /** + * Returns the bind form field. + * + * @param array $form + * The form definition array for the settings configuration form. + * @param string $value + * The stored default value. + * @param \Drupal\ui_patterns_settings\Definition\PatternDefinitionSetting $def + * The pattern definition. + * + * @return array + * The form. + */ + protected function tokenForm(array $form, $value, PatternDefinitionSetting $def) { + $form[$def->getName() . "_token"] = [ + '#type' => 'textfield', + '#title' => "Token", + '#description' => $this->t("Token for %label", ['%label' => $def->getLabel()]), + '#default_value' => $this->getValue($value), + '#attributes' => ['class' => ['js-ui-patterns-settings__token']], + '#wrapper_attributes' => ['class' => ['js-ui-patterns-settings__token-wrapper']], + ]; + return $form; + } + + /** + * Check required input fields in layout forms. + * + * @param array $element + * The element to validate. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * @param array $form + * The form. + */ + public static function validateLayout(array $element, FormStateInterface &$form_state, array &$form) { + $parents = $element['#parents']; + $value = $form_state->getValue($parents); + $parents[count($parents) - 1] = $parents[count($parents) - 1] . '_token'; + $token_value = $form_state->getValue($parents); + if (empty($value) && empty($token_value)) { + // Check if a variant is selected and the value + // is provided by the variant. + $variant = $form_state->getValue([ + 'layout_configuration', + 'pattern', + 'variant', + ]); + if (!empty($variant)) { + $variant_def = $element['#pattern_definition']->getVariant($variant); + $variant_ary = $variant_def->toArray(); + if (!empty($variant_ary['settings'][$element['#pattern_setting_definition']->getName()])) { + return; + } + } + + $form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']])); + } + } + + /** + * Add validation and basics classes to the raw input field. + * + * @param array $input + * The input field. + * @param \Drupal\ui_patterns_settings\Definition\PatternDefinitionSetting $def + * The pattern definition. + * @param string $form_type + * The form type. Either layouts_display or display. + */ + protected function handleInput(array &$input, PatternDefinitionSetting $def, $form_type) { + $input['#attributes']['class'][] = 'js-ui-patterns-settings__input'; + $input['#wrapper_attributes']['class'][] = 'js-ui-patterns-settings__input-wrapper'; + if ($def->getRequired()) { + $input['#title'] .= ' *'; + if ($form_type === 'layouts_display') { + $input['#pattern_setting_definition'] = $this->patternSettingDefinition; + $input['#pattern_definition'] = $this->patternDefinition; + $input['#element_validate'][] = [ + PatternSettingTypeBase::class, + 'validateLayout', + ]; + } + } + } + /** * {@inheritdoc} * @@ -142,9 +238,21 @@ public function settingsPreprocess($value, array $context, PatternDefinitionSett * * @see \Drupal\Core\Block\BlockBase::blockForm() */ - public function buildConfigurationForm(array $form, $value) { + public function buildConfigurationForm(array $form, $value, $token_value, $form_type) { $def = $this->getPatternSettingDefinition(); - $form = $this->settingsForm($form, $value, $def); + $form = $this->settingsForm($form, $value, $def, $form_type); + $classes = 'js-ui-patterns-settings__wrapper'; + if ($def->getAllowToken()) { + if (!empty($token_value)) { + $classes .= ' js-ui-patterns-settings--token-has-value'; + } + $form[$def->getName()]['#prefix'] = '