diff --git a/css/yamlform.element.options.css b/css/yamlform.element.options.css
new file mode 100755
index 0000000..b1c7a4f
--- /dev/null
+++ b/css/yamlform.element.options.css
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Styles for YAML form options element.
+ */
+
+.form-type-yamlform-options table td {
+ white-space: nowrap;
+}
+
+.form-type-yamlform-options table th:last-child,
+.form-type-yamlform-options table td:last-child {
+ padding: 0 4px;
+ vertical-align: middle;
+ text-align: center;
+ width: 24px;
+}
+
+.form-type-yamlform-options table td input[type="text"] {
+ width: 95%;
+}
+
+.form-type-yamlform-options table td input[type="image"] {
+ padding: 0;
+ width: 16px;
+ height: 16px;
+}
+
+.form-type-yamlform-options .container-inline input[type="submit"] {
+ width: inherit !important;
+}
+
+/**
+ * Hide AJAX throbber.
+ */
+.form-type-yamlform-options .ajax-progress-throbber {
+ display: none;
+}
+
+/**
+ * Suppress table drag warnings.
+ */
+.form-type-yamlform-options .tabledrag-changed-warning,
+.form-type-yamlform-options .tabledrag-changed{
+ display: none !important;
+}
+
+
diff --git a/css/yamlform.theme.seven.css b/css/yamlform.theme.seven.css
index 6566e8b..042bb49 100755
--- a/css/yamlform.theme.seven.css
+++ b/css/yamlform.theme.seven.css
@@ -3,8 +3,8 @@
* YAML form seven theme styling.
*/
-td {
- vertical-align: top !important;
+table td {
+ vertical-align: top;
}
.yamlform-help-details {
diff --git a/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php b/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php
index 5742710..6e93908 100644
--- a/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php
+++ b/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php
@@ -41,13 +41,6 @@ abstract class YamlFormUiElementFormBase extends FormBase {
protected $yamlform;
/**
- * A YAML form element.
- *
- * @var \Drupal\yamlform\YamlFormElementInterface
- */
- protected $yamlformElement;
-
- /**
* The YAML form element.
*
* @var array
@@ -95,10 +88,9 @@ abstract class YamlFormUiElementFormBase extends FormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, YamlFormInterface $yamlform = NULL, $key = NULL, $parent_key = '') {
- $plugin_id = $this->elementManager->getElementPluginId($this->element);
-
$this->yamlform = $yamlform;
- $this->yamlformElement = $this->elementManager->createInstance($plugin_id, $this->element);
+
+ $yamlform_element = $this->getYamlFormElement();
$form['parent_key'] = [
'#type' => 'value',
@@ -122,13 +114,13 @@ abstract class YamlFormUiElementFormBase extends FormBase {
$form['key']['#description'] = NULL;
}
- $form['properties'] = $this->yamlformElement->buildConfigurationForm([], $form_state);
+ $form['properties'] = $yamlform_element->buildConfigurationForm([], $form_state);
// Add type to the general details.
$form['properties']['general']['type'] = [
'#type' => 'item',
'#title' => $this->t('Type'),
- '#markup' => $this->yamlformElement->getPluginLabel(),
+ '#markup' => $yamlform_element->getPluginLabel(),
'#weight' => -100,
'#parents' => ['type'],
];
@@ -144,6 +136,7 @@ abstract class YamlFormUiElementFormBase extends FormBase {
'#type' => 'submit',
'#value' => $this->t('Save'),
'#button_type' => 'primary',
+ '#_validate_form' => TRUE,
];
$form = $this->buildDialog($form, $form_state);
@@ -155,13 +148,21 @@ abstract class YamlFormUiElementFormBase extends FormBase {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
+ // Only validate the submit button.
+ $button = $form_state->getTriggeringElement();
+ if (empty($button['#_validate_form'])) {
+ return;
+ }
+
+ $yamlform_element = $this->getYamlFormElement();
+
// The YAML form element configuration is stored in the 'properties' key in
// the form, pass that through for validation.
$element_form_state = (new FormState())->setValues($form_state->getValue('properties') ?: []);
$element_form_state->setFormObject($this);
// Validate configuration form and set form errors.
- $this->yamlformElement->validateConfigurationForm($form, $element_form_state);
+ $yamlform_element->validateConfigurationForm($form, $element_form_state);
$element_errors = $element_form_state->getErrors();
foreach ($element_errors as $element_error) {
$form_state->setErrorByName(NULL, $element_error);
@@ -173,17 +174,19 @@ abstract class YamlFormUiElementFormBase extends FormBase {
}
// Set element properties.
- $properties = $this->yamlformElement->getConfigurationFormProperties($form, $element_form_state);
+ $properties = $yamlform_element->getConfigurationFormProperties($form, $element_form_state);
$parent_key = $form_state->getValue('parent_key');
$key = $form_state->getValue('key');
- $this->yamlform->setElementProperties($key, $properties, $parent_key);
-
- // Validate elements.
- if ($messages = $this->elementsValidator->validate($this->yamlform)) {
- $t_args = [':href' => Url::fromRoute('entity.yamlform.source_form', ['yamlform' => $this->yamlform->id()])->toString()];
- $form_state->setErrorByName('elements', $this->t('There has been error validating the elements. You may need to edit the YAML source to resolve the issue.', $t_args));
- foreach ($messages as $message) {
- drupal_set_message($message, 'error');
+ if ($key) {
+ $this->yamlform->setElementProperties($key, $properties, $parent_key);
+
+ // Validate elements.
+ if ($messages = $this->elementsValidator->validate($this->yamlform)) {
+ $t_args = [':href' => Url::fromRoute('entity.yamlform.source_form', ['yamlform' => $this->yamlform->id()])->toString()];
+ $form_state->setErrorByName('elements', $this->t('There has been error validating the elements. You may need to edit the YAML source to resolve the issue.', $t_args));
+ foreach ($messages as $message) {
+ drupal_set_message($message, 'error');
+ }
}
}
}
@@ -192,6 +195,8 @@ abstract class YamlFormUiElementFormBase extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
+ $yamlform_element = $this->getYamlFormElement();
+
if ($response = $this->validateDialog($form, $form_state)) {
return $response;
}
@@ -199,7 +204,7 @@ abstract class YamlFormUiElementFormBase extends FormBase {
// The YAML form element configuration is stored in the 'properties' key in
// the form, pass that through for submission.
$element_data = (new FormState())->setValues($form_state->getValue('properties'));
- $this->yamlformElement->submitConfigurationForm($form, $element_data);
+ $yamlform_element->submitConfigurationForm($form, $element_data);
$this->yamlform->save();
// Display status message.
@@ -245,7 +250,8 @@ abstract class YamlFormUiElementFormBase extends FormBase {
* A YAML form element.
*/
public function getYamlFormElement() {
- return $this->yamlformElement;
+ $plugin_id = $this->elementManager->getElementPluginId($this->element);
+ return $this->elementManager->createInstance($plugin_id, $this->element);
}
}
diff --git a/src/Element/YamlFormElementOptions.php b/src/Element/YamlFormElementOptions.php
index 27898ee..4019c77 100755
--- a/src/Element/YamlFormElementOptions.php
+++ b/src/Element/YamlFormElementOptions.php
@@ -6,7 +6,7 @@ use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Url;
-use Drupal\yamlform\Entity\YamlFormOptions;
+use Drupal\yamlform\Entity\YamlFormOptions as YamlFormOptionsEntity;
use Drupal\yamlform\Utility\YamlFormElementHelper;
/**
@@ -41,23 +41,27 @@ class YamlFormElementOptions extends FormElement {
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
-
- $default_value = isset($element['#default_value']) ? $element['#default_value'] : NULL;
-
- if (!$default_value) {
- return $element;
- }
-
- if (is_string($default_value) && YamlFormOptions::load($default_value)) {
- $element['options']['#default_value'] = $default_value;
+ if (isset($element['#default_value'])) {
+ if(is_string($element['#default_value'])) {
+ return (YamlFormOptionsEntity::load($element['#default_value'])) ? $element['#default_value'] : [];
+ }
+ else {
+ return $element['#default_value'];
+ }
}
else {
- $element['options']['#default_value'] = self::CUSTOM_OPTION;
- $element['custom']['#default_value'] = (is_array($default_value)) ? Yaml::encode($default_value) : $default_value;
+ return [];
}
- return $element;
}
- return NULL;
+ elseif (!empty($input['options'])) {
+ return $input['options'];
+ }
+ elseif (isset($input['custom']['options'])) {
+ return $input['custom']['options'];
+ }
+ else {
+ return [];
+ }
}
/**
@@ -69,7 +73,7 @@ class YamlFormElementOptions extends FormElement {
// Predefined options.
// @see (/admin/structure/yamlform/settings/options/manage)
$options = [];
- $yamlform_options = YamlFormOptions::loadMultiple();
+ $yamlform_options = YamlFormOptionsEntity::loadMultiple();
foreach ($yamlform_options as $id => $yamlform_option) {
// Filter likert options for answers to the likert element.
if ($element['#likert'] && strpos($id, 'likert_') !== 0) {
@@ -79,36 +83,40 @@ class YamlFormElementOptions extends FormElement {
}
asort($options);
- $element['options']['#type'] = 'select';
- $element['options']['#options'] = [
- self::CUSTOM_OPTION => t('Custom...'),
- ] + $options;
- $element['options']['#attributes']['class'][] = 'js-' . $element['#id'] . '-options';
- $element['options']['#error_no_message'] = TRUE;
$t_args = [
'@type' => ($element['#likert']) ? t('answers') : t('options'),
':href' => Url::fromRoute('entity.yamlform_options.collection')->toString(),
];
- $element['options']['#description'] = t('Please select predefined @type or enter custom @type.', $t_args);
- // Custom options.
- $element['custom']['#title'] = $element['#title'];
- $element['custom']['#title_display'] = 'invisible';
- $element['custom']['#type'] = 'yamlform_codemirror';
- $element['custom']['#mode'] = 'yaml';
- $element['custom']['#states'] = [
- 'visible' => [
- 'select.js-' . $element['#id'] . '-options' => ['value' => ''],
+ // Select options.
+ $element['options'] = [
+ '#type' => 'select',
+ '#description' => t('Please select predefined @type or enter custom @type.', $t_args),
+ '#options' => [
+ self::CUSTOM_OPTION => t('Custom...'),
+ ] + $options,
+ '#attributes' => [
+ 'class' => ['js-' . $element['#id'] . '-options'],
],
+ '#error_no_message' => TRUE,
+ '#default_value' => (!is_array($element['#default_value'])) ? $element['#default_value'] : '',
];
- $element['custom']['#error_no_message'] = TRUE;
- $t_args = [
- '@type' => ($element['#likert']) ? t('answer') : t('option'),
+
+ // Custom options.
+ $element['custom'] = [
+ '#type' => 'yamlform_options',
+ '#title' => $element['#title'],
+ '#title_display' => 'invisible',
+ '#label' => ($element['#likert']) ? t('answer') : t('option'),
+ '#labels' => ($element['#likert']) ? t('answers') : t('options'),
+ '#states' => [
+ 'visible' => [
+ 'select.js-' . $element['#id'] . '-options' => ['value' => ''],
+ ],
+ ],
+ '#error_no_message' => TRUE,
+ '#default_value' => (!is_string($element['#default_value'])) ? $element['#default_value'] : [],
];
- $element['custom']['#description'] = t('Key-value pairs MUST be specified as "safe_key: \'Some readable @type\'". Use of only alphanumeric characters and underscores is recommended in keys. One @type per line.', $t_args);
- if (!$element['#likert']) {
- $element['custom']['#description'] .= t('Option groups can be created by using just the group name followed by indented group options.');
- }
$element['#element_validate'] = [[get_called_class(), 'validateYamlFormElementOptions']];
@@ -128,7 +136,7 @@ class YamlFormElementOptions extends FormElement {
$value = $options_value;
if ($options_value == self::CUSTOM_OPTION) {
try {
- $value = Yaml::decode($custom_value);
+ $value = (is_string($custom_value)) ? Yaml::decode($custom_value) : $custom_value;
}
catch (\Exception $exception) {
// Do nothing since the 'yamlform_codemirror' element will have already
@@ -136,9 +144,7 @@ class YamlFormElementOptions extends FormElement {
}
}
- $is_empty = ($value === '' || $value === NULL) ? TRUE : FALSE;
-
- if ($element['#required'] && $is_empty) {
+ if ($element['#required'] && empty($value)) {
$form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']]));
}
diff --git a/src/Element/YamlFormOptions.php b/src/Element/YamlFormOptions.php
new file mode 100644
index 0000000..50d2fa7
--- /dev/null
+++ b/src/Element/YamlFormOptions.php
@@ -0,0 +1,437 @@
+ TRUE,
+ '#size' => 60,
+ '#label' => t('option'),
+ '#labels' => t('options'),
+ '#empty_options' => 1,
+ '#process' => [
+ [$class, 'processOptions'],
+ ],
+ '#theme_wrappers' => ['form_element'],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
+ if ($input === FALSE) {
+ if (isset($element['#default_value'])) {
+ return (is_string($element['#default_value'])) ? Yaml::decode($element['#default_value']) : $element['#default_value'];
+ }
+ else {
+ return [];
+ }
+ }
+ elseif (is_array($input) && isset($input['options'])) {
+ return (is_string($input['options'])) ? Yaml::decode($input['options']) : self::convertValuesToOptions($input['options']);
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ /**
+ * Expand an email confirm field into two HTML5 email elements.
+ */
+ public static function processOptions(&$element, FormStateInterface $form_state, &$complete_form) {
+ if (isset($element['#default_value']) && is_array($element['#default_value']) && self::hasOptGroup($element['#default_value'])) {
+ // Build table.
+ $element['options'] = [
+ '#type' => 'yamlform_codemirror',
+ '#mode' => 'yaml',
+ '#default_value' => Yaml::encode($element['#default_value']),
+ '#description' => t('Key-value pairs MUST be specified as "safe_key: \'Some readable options\'". Use of only alphanumeric characters and underscores is recommended in keys. One option per line.') . '
' .
+ t('Option groups can be created by using just the group name followed by indented group options.'),
+ ];
+
+ // Add validate callback that extracts the associative array of options.
+ $element['#element_validate'] = [[get_called_class(), 'validateYamlFormOptions']];
+
+ // Wrap this $element in a
that handle #states.
+ YamlFormElementHelper::fixStates($element);
+
+ return $element;
+ }
+
+
+ // Get unique key used to store the current number of options.
+ $number_of_options_storage_key = self::getStorageKey($element, 'number_of_options');
+
+ // Store the number of options which is the number of
+ // #default_values + number of empty_options.
+ if ($form_state->get($number_of_options_storage_key) === NULL) {
+ $number_of_default_values = (isset($element['#default_value']) && is_array($element['#default_value'])) ? count($element['#default_value']) : 0;
+ $number_of_empty_options = (int) $element['#empty_options'];
+ $form_state->set($number_of_options_storage_key, $number_of_default_values + $number_of_empty_options);
+ }
+
+ $number_of_options = $form_state->get($number_of_options_storage_key);
+
+ $table_id = implode('_', $element['#parents']) . '-table';
+ $ajax_settings = [
+ 'callback' => [get_called_class(), 'ajaxCallback'],
+ 'wrapper' => $table_id,
+ ];
+
+ // DEBUG: Disable AJAX callback so that we can see any errors.
+ // $ajax_settings = [];
+
+ $element['#tree'] = TRUE;
+
+ // Build header.
+ $t_args = ['@label' => Unicode::ucfirst($element['#label'])];
+ $header = [
+ t('@label value', $t_args),
+ t('@label text', $t_args),
+ t('Weight'),
+ '',
+ ];
+
+ // Build rows.
+ $row_index = 0;
+ $weight = 0;
+ $rows = [];
+ if (!$form_state->isRebuilding() && isset($element['#default_value']) && is_array($element['#default_value'])) {
+ foreach ($element['#default_value'] as $value => $label) {
+ $rows[$row_index] = self::buildOptionRow($table_id, $row_index, $value, $label, $weight++, $ajax_settings);
+ $row_index++;
+ }
+ }
+ while ($row_index < $number_of_options) {
+ $rows[$row_index] = self::buildOptionRow($table_id, $row_index, '', '', $weight++, $ajax_settings);
+ $row_index++;
+ }
+
+ // Build table.
+ $element['options'] = [
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#type' => 'table',
+ '#header' => $header,
+ '#tabledrag' => [
+ [
+ 'action' => 'order',
+ 'relationship' => 'sibling',
+ 'group' => 'yamlform-options-sort-weight',
+ ],
+ ],
+ ] + $rows;
+
+ // Build add options actions.
+ $element['add'] = [
+ '#prefix' => '
',
+ '#suffix' => '
',
+ ];
+ $element['add']['submit'] = [
+ '#type' => 'submit',
+ '#value' => t('Add'),
+ '#limit_validation_errors' => array(),
+ '#submit' => [[get_called_class(), 'addOptionsSubmit']],
+ '#ajax' => $ajax_settings,
+ '#name' => $table_id . '-add',
+ ];
+ $element['add']['more_options'] = [
+ '#type' => 'number',
+ '#min' => 1,
+ '#max' => 100,
+ '#default_value' => $element['#empty_options'],
+ '#field_suffix' => t('more @labels', ['@labels' => $element['#labels']]),
+ ];
+
+ // Add validate callback that extracts the associative array of options.
+ $element['#element_validate'] = [[get_called_class(), 'validateYamlFormOptions']];
+
+ $element['#attached']['library'][] = 'yamlform/yamlform.element.options';
+
+ // Wrap this $element in a
that handle #states.
+ YamlFormElementHelper::fixStates($element);
+
+ return $element;
+ }
+
+ /**
+ * Build an option's row that contains the options value, text, and weight.
+ *
+ * @param string $table_id
+ * The option element's table id.
+ * @param int $row_index
+ * The option's row index.
+ * @param string $value
+ * The option's value.
+ * @param string $text
+ * The option's text.
+ * @param int $weight
+ * The option's weight.
+ * @param array $ajax_settings
+ * An array containing AJAX callback settings.
+ *
+ * @return array
+ * A render array containing inputs for an option's value, text, and weight.
+ */
+ public static function buildOptionRow($table_id, $row_index, $value, $text, $weight, array $ajax_settings) {
+ $row = [];
+ $row['value'] = [
+ '#type' => 'textfield',
+ '#title' => t('Option value'),
+ '#title_display' => 'invisible',
+ '#size' => 25,
+ '#placeholder' => t('Enter value'),
+ '#default_value' => $value,
+ ];
+ $row['text'] = [
+ '#type' => 'textfield',
+ '#title' => t('Option text'),
+ '#title_display' => 'invisible',
+ '#size' => 25,
+ '#placeholder' => t('Enter text'),
+ '#default_value' => $text,
+ ];
+ $row['weight'] = [
+ '#type' => 'weight',
+ '#delta' => 1000,
+ '#title' => t('Option weight'),
+ '#title_display' => 'invisible',
+ '#attributes' => [
+ 'style' => 'width:4em',
+ 'class' => ['yamlform-options-sort-weight'],
+ ],
+ '#default_value' => $weight,
+ ];
+
+ $row['remove'] = [
+ '#type' => 'image_button',
+ '#src' => 'core/misc/icons/787878/ex.svg',
+ '#limit_validation_errors' => array(),
+ '#submit' => [[get_called_class(), 'removeOptionSubmit']],
+ '#ajax' => $ajax_settings,
+ // Issue #1342066 Document that buttons with the same #value need a unique
+ // #name for the form API to distinguish them, or change the form API to
+ // assign unique #names automatically
+ '#row_index' => $row_index,
+ '#name' => $table_id . '_remove_' . $row_index,
+ ];
+
+ $row['#weight'] = $weight;
+ $row['#attributes']['class'][] = 'draggable';
+ return $row;
+ }
+
+ /****************************************************************************/
+ // Callbacks.
+ /****************************************************************************/
+
+ /**
+ * Form submission handler for adding more options.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ */
+ public static function addOptionsSubmit(array &$form, FormStateInterface $form_state) {
+ // Get the YAML form options element by going up two levels.
+ $button = $form_state->getTriggeringElement();
+ $element =& NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
+
+ // Add more options to the number of options.
+ $number_of_options_storage_key = self::getStorageKey($element, 'number_of_options');
+ $number_of_options = $form_state->get($number_of_options_storage_key);
+ $more_options = (int) $element['add']['more_options']['#value'];
+ $form_state->set($number_of_options_storage_key, $number_of_options + $more_options);
+
+ // Reset values.
+ $element['options']['#value'] = array_values($element['options']['#value']);
+ $form_state->setValueForElement($element['options'], $element['options']['#value']);
+ NestedArray::setValue($form_state->getUserInput(), $element['options']['#parents'], $element['options']['#value']);
+
+ // Rebuild the form.
+ $form_state->setRebuild();
+ }
+
+ /**
+ * Form submission handler for adding more options.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ */
+ public static function removeOptionSubmit(array &$form, FormStateInterface $form_state) {
+ $button = $form_state->getTriggeringElement();
+ $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3));
+
+ // Remove options.
+ unset($element['options']['#value'][$button['#row_index']]);
+
+ // Remove one option from the 'number of options'.
+ $number_of_options_storage_key = self::getStorageKey($element, 'number_of_options');
+ $number_of_options = $form_state->get($number_of_options_storage_key);
+ $form_state->set($number_of_options_storage_key, $number_of_options - 1);
+
+ // Reset values.
+ $element['options']['#value'] = array_values($element['options']['#value']);
+ $form_state->setValueForElement($element['options'], $element['options']['#value']);
+ NestedArray::setValue($form_state->getUserInput(), $element['options']['#parents'], $element['options']['#value']);
+
+ // Rebuild the form.
+ $form_state->setRebuild();
+ }
+
+ /**
+ * Form submission AJAX callback the returns the options table.
+ */
+ public function ajaxCallback(array &$form, FormStateInterface $form_state) {
+ $button = $form_state->getTriggeringElement();
+ $parent_length = (isset($button['#row_index'])) ? -3 : -2;
+ $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, $parent_length));
+ return $element['options'];
+ }
+
+ /**
+ * Validates YAML form options element.
+ */
+ public static function validateYamlFormOptions(&$element, FormStateInterface $form_state, &$complete_form) {
+ if (isset($element['options']['#value']) && is_string($element['options']['#value'])) {
+ $options = Yaml::decode($element['options']['#value']);
+ }
+ else {
+ // Collect the option values in a sortable array.
+ $values = [];
+ foreach (Element::children($element['options']) as $child_key) {
+ $row = $element['options'][$child_key];
+ $values[] = [
+ 'value' => $row['value']['#value'],
+ 'text' => $row['text']['#value'],
+ 'weight' => $row['weight']['#value'],
+ ];
+ }
+
+ // Sort the option values.
+ uasort($values, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
+
+ // Convert values to options.
+ $options = self::convertValuesToOptions($values);
+
+ }
+
+ // Validate required options.
+ if (!empty($element['#required']) && empty($options)) {
+ $form_state->setError($element, t('@name field is required.', array('@name' => $element['#title'])));
+ return $element;
+ }
+
+ // Clear the element's value by setting it to NULL.
+ $form_state->setValueForElement($element, NULL);
+
+ // Now, set the sorted options as the element's value.
+ $form_state->setValueForElement($element, $options);
+
+ return $element;
+ }
+
+ /****************************************************************************/
+ // Helper functions.
+ /****************************************************************************/
+
+ /**
+ * Get unique key used to store the number of options for an element.
+ *
+ * @param array $element
+ * An element.
+ *
+ * @return string
+ * A unique key used to store the number of options for an element.
+ */
+ public static function getStorageKey(array $element, $name) {
+ return 'yamlform_options__' . $element['#name'] . '__' . $name;
+ }
+
+ /**
+ * Convert an array containing of option value, text, and weight to an associative array of options.
+ *
+ * @param array $values
+ * An array containing of option value, text, and weight.
+ *
+ * @return array
+ * An associative array of options.
+ */
+ public static function convertValuesToOptions(array $values) {
+ // Sort the option values.
+ uasort($values, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
+
+ // Now build the associative array of options.
+ $options = [];
+ foreach ($values as $value) {
+ $option_value = $value['value'];
+ $option_text = $value['text'];
+ // Skip completely empty options.
+ if ($option_value === '' && $option_text === '') {
+ continue;
+ }
+
+ // Populate empty option value or option text.
+ if ($option_value === '') {
+ $option_value = $option_text;
+ }
+ elseif ($option_text === '') {
+ $option_text = $option_value;
+ }
+
+ $options[$option_value] = $option_text;
+ }
+
+ return $options;
+ }
+
+ /**
+ * Determine if options array contains an OptGroup.
+ *
+ * @param array $options
+ * An array of options.
+ *
+ * @return bool
+ * TRUE if options array contains an OptGroup.
+ */
+ public static function hasOptGroup(array $options) {
+ foreach ($options as $option_value => $option_text) {
+ if (is_array($option_text)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+}
diff --git a/src/Plugin/YamlFormElement/YamlFormLikert.php b/src/Plugin/YamlFormElement/YamlFormLikert.php
index 96e8b34..3a183a5 100644
--- a/src/Plugin/YamlFormElement/YamlFormLikert.php
+++ b/src/Plugin/YamlFormElement/YamlFormLikert.php
@@ -302,10 +302,10 @@ class YamlFormLikert extends YamlFormElementBase {
'#open' => TRUE,
];
$form['likert']['questions'] = [
- '#title' => $this->t('Questions (YAML)'),
- '#type' => 'yamlform_codemirror',
- '#mode' => 'yaml',
- '#description' => $this->t('Key-value pairs MUST be specified as "safe_key: \'Some readable @type\'". Use of only alphanumeric characters and underscores is recommended in keys. One @type per line.', ['@type' => 'question']),
+ '#title' => $this->t('Questions'),
+ '#type' => 'yamlform_options',
+ '#label' => $this->t('question'),
+ '#labels' => $this->t('questions'),
'#required' => TRUE,
];
$form['likert']['answers'] = [
diff --git a/src/YamlFormEntitySettingsForm.php b/src/YamlFormEntitySettingsForm.php
index eb63fea..ab4df75 100644
--- a/src/YamlFormEntitySettingsForm.php
+++ b/src/YamlFormEntitySettingsForm.php
@@ -476,15 +476,19 @@ class YamlFormEntitySettingsForm extends EntityForm {
/** @var \Drupal\yamlform\YamlFormInterface $yamlform */
$yamlform = $this->getEntity();
- // Remove main properties.
- unset(
- $values['id'],
- $values['title'],
- $values['description'],
- $values['template'],
- $values['status'],
- $values['uid']
- );
+ /* Set properties and remove them from the values array. */
+
+ // Set template.
+ $yamlform->set('template', (bool) $values['template']);
+ unset($values['template']);
+
+ // Set status.
+ $yamlform->set('status', $values['status']);
+ unset($values['status']);
+
+ // Set owner.
+ $yamlform->set('uid', (int) $values['uid']);
+ unset($values['uid']);
// Set settings and save the form.
$yamlform->setSettings($values)->save();
diff --git a/tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_options.yml b/tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_options.yml
new file mode 100644
index 0000000..a6ac8a8
--- /dev/null
+++ b/tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_options.yml
@@ -0,0 +1,124 @@
+langcode: en
+status: true
+dependencies: { }
+uid: null
+template: false
+id: test_element_options
+title: 'Test: Element: Options'
+description: 'Test options element.'
+elements: |
+ yamlform_options_elements:
+ '#type': details
+ '#title': 'YAML form options'
+ '#open': true
+ yamlform_options:
+ '#type': yamlform_options
+ '#title': 'yamlform_options'
+ yamlform_options_default_value:
+ '#type': yamlform_options
+ '#title': 'yamlform_options (default_value)'
+ '#default_value':
+ one: One
+ two: Two
+ three: Three
+ yamlform_options_optgroup:
+ '#type': yamlform_options
+ '#title': 'yamlform_options (optgroup)'
+ '#required': true
+ '#default_value':
+ 'Group One':
+ one: One
+ 'Group Two':
+ two: Two
+ 'Group Three':
+ three: Three
+ yamlform_element_options_elements:
+ '#type': details
+ '#title': 'YAML form element options'
+ '#open': true
+ yamlform_element_options_entity:
+ '#type': yamlform_element_options
+ '#title': 'yamlform_element_options (entity)'
+ '#required': true
+ '#default_value': 'yes_no'
+ yamlform_element_options_custom:
+ '#type': yamlform_element_options
+ '#title': 'yamlform_element_options (custom)'
+ '#required': true
+ '#default_value':
+ one: One
+ two: Two
+ three: Three
+settings:
+ page: true
+ page_submit_path: ''
+ page_confirm_path: ''
+ form_closed_message: ''
+ form_exception_message: ''
+ form_submit_label: ''
+ form_confidential: false
+ form_confidential_message: ''
+ form_prepopulate: false
+ form_novalidate: false
+ wizard_progress_bar: true
+ wizard_progress_pages: false
+ wizard_progress_percentage: false
+ wizard_prev_button_label: ''
+ wizard_next_button_label: ''
+ wizard_complete: true
+ wizard_start_label: ''
+ wizard_complete_label: ''
+ preview: 0
+ preview_next_button_label: ''
+ preview_prev_button_label: ''
+ preview_message: ''
+ draft: false
+ draft_auto_save: false
+ draft_button_label: ''
+ draft_saved_message: ''
+ draft_loaded_message: ''
+ confirmation_type: message
+ confirmation_message: ''
+ confirmation_url: ''
+ limit_total: null
+ entity_limit_total: null
+ limit_total_message: ''
+ limit_user: null
+ entity_limit_user: null
+ limit_user_message: ''
+ results_disabled: true
+access:
+ create:
+ roles:
+ - anonymous
+ - authenticated
+ users: { }
+ view_any:
+ roles: { }
+ users: { }
+ update_any:
+ roles: { }
+ users: { }
+ delete_any:
+ roles: { }
+ users: { }
+ purge_any:
+ roles: { }
+ users: { }
+ view_own:
+ roles: { }
+ users: { }
+ update_own:
+ roles: { }
+ users: { }
+ delete_own:
+ roles: { }
+ users: { }
+handlers:
+ debug:
+ id: debug
+ label: Debug
+ handler_id: debug
+ status: true
+ weight: 0
+ settings: { }
diff --git a/yamlform.install b/yamlform.install
index bdb27c3..5b29d39 100644
--- a/yamlform.install
+++ b/yamlform.install
@@ -836,18 +836,3 @@ function yamlform_update_8036() {
\Drupal::configFactory()->getEditable($yamlform_config_name)->setData($data)->save();
}
}
-
-/**
- * Issue #2781713: Remove id, title, and description from setting.
- */
-function yamlform_update_8037() {
- // Update 'yamlform.yamlform.*' configuration.
- $config_factory = \Drupal::configFactory();
- foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) {
- $yamlform_config = $config_factory->getEditable($yamlform_config_name);
- $yamlform_config->clear('settings.id');
- $yamlform_config->clear('settings.title');
- $yamlform_config->clear('settings.description');
- $yamlform_config->save(TRUE);
- }
-}
diff --git a/yamlform.libraries.yml b/yamlform.libraries.yml
index b4a8bb3..fda3eec 100755
--- a/yamlform.libraries.yml
+++ b/yamlform.libraries.yml
@@ -141,6 +141,12 @@ yamlform.element.likert:
component:
css/yamlform.element.likert.css: {}
+yamlform.element.options:
+ version: 1.x
+ css:
+ component:
+ css/yamlform.element.options.css: {}
+
yamlform.element.rating:
version: 1.x
css: