diff --git a/modules/site_tools_msc_2020/js/msc-select.js b/modules/site_tools_msc_2020/js/msc-select.js new file mode 100644 index 0000000..28732dc --- /dev/null +++ b/modules/site_tools_msc_2020/js/msc-select.js @@ -0,0 +1,5 @@ +/** + * @file + * Reservado para melhorias futuras do widget MSC 2020. + * A lógica de cascata entre os selects é gerida via Drupal AJAX (#ajax). + */ diff --git a/modules/site_tools_msc_2020/site_tools_msc_2020.libraries.yml b/modules/site_tools_msc_2020/site_tools_msc_2020.libraries.yml new file mode 100644 index 0000000..5bc43ad --- /dev/null +++ b/modules/site_tools_msc_2020/site_tools_msc_2020.libraries.yml @@ -0,0 +1,2 @@ +msc_term_select_widget: + version: VERSION diff --git a/modules/site_tools_msc_2020/src/Plugin/Field/FieldWidget/MscTermSelectWidget.php b/modules/site_tools_msc_2020/src/Plugin/Field/FieldWidget/MscTermSelectWidget.php new file mode 100644 index 0000000..3413bc7 --- /dev/null +++ b/modules/site_tools_msc_2020/src/Plugin/Field/FieldWidget/MscTermSelectWidget.php @@ -0,0 +1,222 @@ +get('entity_type.manager'), + ); + } + + public static function isApplicable(FieldDefinitionInterface $field_definition): bool { + $storage = $field_definition->getFieldStorageDefinition(); + if ($storage->getSetting('target_type') !== 'taxonomy_term') { + return FALSE; + } + $handler_settings = $field_definition->getSetting('handler_settings'); + return !empty($handler_settings['target_bundles']['msc_2020']); + } + + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array { + $data = $this->buildTermData(); + + // getUserInput() contém os dados crus submetidos e NÃO é afetado pelo + // #limit_validation_errors do botão "Add another item" (que descarta + // valores fora do caminho do campo em $form_state->getValues()). + $field_name = $this->fieldDefinition->getName(); + $field_parents = $element['#field_parents'] ?? $form['#parents'] ?? []; + $base = array_merge($field_parents, [$field_name, $delta]); + + $raw_input = $form_state->getUserInput(); + $parent_exists = FALSE; + $submitted_parent = NestedArray::getValue( + $raw_input, + array_merge($base, ['parent_select']), + $parent_exists, + ); + + if ($parent_exists) { + // Rebuild pós-submissão (AJAX de parent_select, "Add another item", etc.) + $initial_parent = $submitted_parent !== '' ? (int) $submitted_parent : NULL; + $child_exists = FALSE; + $submitted_child = NestedArray::getValue( + $raw_input, + array_merge($base, ['child_select']), + $child_exists, + ); + $initial_child = ($child_exists && $submitted_child !== '' && $submitted_child !== NULL) + ? (int) $submitted_child + : $initial_parent; + } + else { + // Carga inicial: deriva do valor persistido na entidade. + $current_value = $items[$delta]->target_id ? (int) $items[$delta]->target_id : NULL; + [$initial_parent, $initial_child] = $this->resolveInitialValues($current_value, $data); + } + + // Opções do segundo select: pré-populadas para a categoria pai atual. + $child_options = ['' => $this->t('— selecione uma categoria primeiro —')]; + if ($initial_parent !== NULL) { + $child_options = []; + $child_options[$initial_parent] = $this->t('— área geral (sem subcampo) —'); + $child_options += $data['children'][$initial_parent] ?? []; + } + + $field_id = $field_name . '--' . $delta; + $wrapper_id = Html::getId('msc-child-' . $field_id); + + $element['parent_select'] = [ + '#type' => 'select', + '#title' => $element['#title'] ?? $this->t('Área MSC 2020'), + '#title_display' => $element['#title_display'] ?? 'before', + '#options' => ['' => $this->t('— selecione uma categoria —')] + $data['parents'], + '#default_value' => $initial_parent ?? '', + '#required' => !empty($element['#required']), + '#attributes' => ['class' => ['msc-parent-select']], + '#ajax' => [ + 'callback' => [static::class, 'rebuildChildSelect'], + 'wrapper' => $wrapper_id, + 'event' => 'change', + ], + ]; + + // O child_select é o elemento cujo valor é salvo no campo. + // #validated => TRUE: aceita o valor submetido sem verificar #options, + // necessário pois as opções podem ter mudado entre submissões. + $element['child_select'] = [ + '#type' => 'select', + '#title' => $this->t('Subcampo'), + '#prefix' => '