diff --git a/src/Plugin/TypedDataFormWidget/DateTimeRangeWidget.php b/src/Plugin/TypedDataFormWidget/DateTimeRangeWidget.php new file mode 100644 index 0000000..6e9a3f7 --- /dev/null +++ b/src/Plugin/TypedDataFormWidget/DateTimeRangeWidget.php @@ -0,0 +1,134 @@ + NULL, + 'description' => NULL, + ]; + } + + /** + * {@inheritdoc} + */ + public function isApplicable(DataDefinitionInterface $definition) { + return is_subclass_of($definition->getClass(), DateTimeInterface::class); + } + + /** + * {@inheritdoc} + */ + public function form(TypedDataInterface $data, SubformStateInterface $form_state) { + $value = $data->getValue(); + + $form = SubformState::getNewSubForm(); + $form['#theme_wrappers'][] = 'fieldset'; + + $form['#title'] = $this->configuration['label'] ?: $data->getDataDefinition()->getLabel(); + $form['#description'] = $this->configuration['description'] ?: $data->getDataDefinition()->getDescription(); + + $form['#element_validate'][] = [$this, 'validateStartEnd']; + + $form['value'] = [ + '#type' => 'datetime', + '#title' => $this->t('Start date'), + '#default_value' => (isset($value['value'])) ? new DrupalDateTime($value['value']) : '', + ]; + $form['end_value'] = [ + '#type' => 'datetime', + '#title' => $this->t('End date'), + '#default_value' => (isset($value['end_value'])) ? new DrupalDateTime($value['end_value']) : '', + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function extractFormValues(TypedDataInterface $data, SubformStateInterface $form_state) { + $value = $form_state->getValue('value'); + $end_value = $form_state->getValue('end_value'); + if ($value instanceof DrupalDateTime) { + $value = $value->format('Y-m-d H:i:s'); + } + if ($end_value instanceof DrupalDateTime) { + $end_value = $end_value->format('Y-m-d H:i:s'); + } + $data->setValue([ + 'value' => $value, + 'end_value' => $end_value, + ]); + } + + /** + * {@inheritdoc} + */ + public function flagViolations(TypedDataInterface $data, ConstraintViolationListInterface $violations, SubformStateInterface $formState) { + foreach ($violations as $violation) { + /** @var ConstraintViolationInterface $violation */ + $formState->setErrorByName('value', $violation->getMessage()); + } + } + + /** + * {@inheritdoc} + */ + public function getConfigurationDefinitions(DataDefinitionInterface $definition) { + return [ + 'label' => DataDefinition::create('string') + ->setLabel($this->t('Label')), + 'description' => DataDefinition::create('string') + ->setLabel($this->t('Description')), + ]; + } + + /** + * Ensure that the start date <= the end date. + * + * @param array $element + * An associative array containing the properties and children of the + * generic form element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param array $complete_form + * The complete form structure. + */ + public function validateStartEnd(array &$element, FormStateInterface $form_state, array &$complete_form) { + $start_date = $element['value']['#value']['object']; + $end_date = $element['end_value']['#value']['object']; + + if ($start_date instanceof DrupalDateTime && $end_date instanceof DrupalDateTime) { + if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { + $interval = $start_date->diff($end_date); + if ($interval->invert === 1) { + $form_state->setError($element, $this->t('The @title end date cannot be before the start date', ['@title' => $element['#title']])); + } + } + } + } + +} diff --git a/src/Plugin/TypedDataFormWidget/DateTimeWidget.php b/src/Plugin/TypedDataFormWidget/DateTimeWidget.php new file mode 100644 index 0000000..d5501b9 --- /dev/null +++ b/src/Plugin/TypedDataFormWidget/DateTimeWidget.php @@ -0,0 +1,88 @@ + NULL, + 'description' => NULL, + ]; + } + + /** + * {@inheritdoc} + */ + public function isApplicable(DataDefinitionInterface $definition) { + return is_subclass_of($definition->getClass(), DateTimeInterface::class); + } + + /** + * {@inheritdoc} + */ + public function form(TypedDataInterface $data, SubformStateInterface $form_state) { + $form = SubformState::getNewSubForm(); + $form['value'] = [ + '#type' => 'datetime', + '#title' => $this->configuration['label'] ?: $data->getDataDefinition()->getLabel(), + '#description' => $this->configuration['description'] ?: $data->getDataDefinition()->getDescription(), + '#default_value' => (!empty($data->getValue())) ? new DrupalDateTime($data->getValue()) : '', + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function extractFormValues(TypedDataInterface $data, SubformStateInterface $form_state) { + $value = $form_state->getValue('value'); + if ($value instanceof DrupalDateTime) { + $value = $value->format('Y-m-d H:i:s'); + } + $data->setValue($value); + } + + /** + * {@inheritdoc} + */ + public function flagViolations(TypedDataInterface $data, ConstraintViolationListInterface $violations, SubformStateInterface $formState) { + foreach ($violations as $violation) { + /** @var ConstraintViolationInterface $violation */ + $formState->setErrorByName('value', $violation->getMessage()); + } + } + + /** + * {@inheritdoc} + */ + public function getConfigurationDefinitions(DataDefinitionInterface $definition) { + return [ + 'label' => DataDefinition::create('string') + ->setLabel($this->t('Label')), + 'description' => DataDefinition::create('string') + ->setLabel($this->t('Description')), + ]; + } + +} diff --git a/tests/modules/typed_data_widget_test/src/FormWidgetExampleForm.php b/tests/modules/typed_data_widget_test/src/FormWidgetExampleForm.php index c213921..0e77bf5 100644 --- a/tests/modules/typed_data_widget_test/src/FormWidgetExampleForm.php +++ b/tests/modules/typed_data_widget_test/src/FormWidgetExampleForm.php @@ -44,10 +44,21 @@ public function getExampleContextDefinition($widget_id) { ->setDescription('Some example string with max. 8 characters.') ->setDefaultValue('default') ->addConstraint('Length', ['max' => 8]); + case 'select': return ContextDefinition::create('filter_format') ->setLabel('Filter format') ->setDescription('Some example selection.'); + + case 'datetime': + return ContextDefinition::create('datetime_iso8601') + ->setLabel('Example datetime') + ->setDescription('Some example datetime.'); + + case 'datetime_range': + return ContextDefinition::create('any') + ->setLabel('Example datetime range') + ->setDescription('Some example datetime range.'); } }