Last active
July 23, 2020 12:21
-
-
Save slejnej/b0dfad183d7aec0bd76e3c31832d12b4 to your computer and use it in GitHub Desktop.
Drupal 8 - entity_reference_tree (new inline display support)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/css/entity-reference-tree.css b/css/entity-reference-tree.css | |
index 7dff60d..5b19f09 100644 | |
--- a/css/entity-reference-tree.css | |
+++ b/css/entity-reference-tree.css | |
@@ -1,4 +1,9 @@ | |
-#entity-reference-tree-selected-text { | |
+.entity-reference-tree-selected-text { | |
font-weight: 600; | |
margin-top: 1em; | |
} | |
+ | |
+/* Hide the actual entity reference autocomplete field */ | |
+.field--widget-inline-entity-reference-tree-widget .form-autocomplete { | |
+ display: none; | |
+} | |
diff --git a/js/entity_reference_tree.js b/js/entity_reference_tree.js | |
index 950c136..af1e948 100644 | |
--- a/js/entity_reference_tree.js | |
+++ b/js/entity_reference_tree.js | |
@@ -8,144 +8,158 @@ | |
(function($, Drupal) { | |
Drupal.behaviors.entityReferenceTree = { | |
attach: function(context, settings) { | |
- $("#entity-reference-tree-wrapper", context) | |
- .once("jstreeBehavior") | |
- .each(function() { | |
- const treeContainer = $(this); | |
- const fieldEditName = $("#entity-reference-tree-widget-field").val(); | |
- const widgetElement = $("#" + fieldEditName); | |
- const theme = treeContainer.attr("theme"); | |
- const dots = treeContainer.attr("dots"); | |
- // Avoid ajax callback from running following codes again. | |
- if (widgetElement.length) { | |
- const entityType = $("#entity-reference-tree-entity-type").val(); | |
- const bundle = $("#entity-reference-tree-entity-bundle").val(); | |
- const token = settings["entity_tree_token_" + fieldEditName]; | |
- const idIsString = bundle === "*"; | |
- const limit = parseInt(settings["tree_limit_" + fieldEditName]); | |
- let selectedNodes; | |
- // Selected nodes. | |
- if (idIsString) { | |
- selectedNodes = widgetElement.val().match(/\([a-z 0-9 _]+\)/g); | |
- } else { | |
- selectedNodes = widgetElement.val().match(/\((\d+)\)/g); | |
- } | |
+ $(".entity-reference-tree-wrapper", context) | |
+ .each(function() { | |
+ const treeContainer = $(this); | |
+ const treeJs = treeContainer.find('.entity-reference-tree'); | |
+ const fieldEditName = treeContainer.find(".entity-reference-tree-widget-field").val(); | |
+ const widgetElement = $("#" + fieldEditName); | |
+ const theme = treeJs.attr("theme"); | |
+ const dots = treeJs.attr("dots"); | |
+ const widgetType = settings['widget_type']; | |
+ // Avoid ajax callback from running following codes again. | |
+ if (widgetElement.length) { | |
+ const entityType = treeContainer.find(".entity-reference-tree-entity-type").val(); | |
+ const bundle = treeContainer.find(".entity-reference-tree-entity-bundle").val(); | |
+ const token = settings["entity_tree_token_" + fieldEditName]; | |
+ const idIsString = bundle === "*"; | |
+ const limit = parseInt(settings["tree_limit_" + fieldEditName]); | |
+ let selectedNodes; | |
+ // Selected nodes. | |
+ if (idIsString) { | |
+ selectedNodes = widgetElement.val().match(/\([a-z 0-9 _]+\)/g); | |
+ } else { | |
+ selectedNodes = widgetElement.val().match(/\((\d+)\)/g); | |
+ } | |
- if (selectedNodes) { | |
- // Pick up nodes id. | |
- for (let i = 0; i < selectedNodes.length; i++) { | |
- // Remove the round brackets. | |
- if (idIsString) { | |
- selectedNodes[i] = selectedNodes[i].slice( | |
- 1, | |
- selectedNodes[i].length - 1 | |
- ); | |
- } else { | |
- selectedNodes[i] = parseInt( | |
- selectedNodes[i].slice(1, selectedNodes[i].length - 1), | |
- 10 | |
- ); | |
+ if (selectedNodes) { | |
+ // Pick up nodes id. | |
+ for (let i = 0; i < selectedNodes.length; i++) { | |
+ // Remove the round brackets. | |
+ if (idIsString) { | |
+ selectedNodes[i] = selectedNodes[i].slice( | |
+ 1, | |
+ selectedNodes[i].length - 1 | |
+ ); | |
+ } else { | |
+ selectedNodes[i] = parseInt( | |
+ selectedNodes[i].slice(1, selectedNodes[i].length - 1), | |
+ 10 | |
+ ); | |
+ } | |
} | |
+ } else { | |
+ selectedNodes = []; | |
} | |
- } else { | |
- selectedNodes = []; | |
- } | |
- // Populate the selected entities text. | |
- $("#entity-reference-tree-selected-node").val(widgetElement.val()); | |
- $("#entity-reference-tree-selected-text").text( | |
- Drupal.t("Selected entities") + ": " + widgetElement.val() | |
- ); | |
- // Build the tree. | |
- treeContainer.jstree({ | |
- core: { | |
- data: { | |
- url: function(node) { | |
- return Drupal.url( | |
- "admin/entity_reference_tree/json/" + | |
- entityType + | |
- "/" + | |
- bundle + | |
- "?token=" + | |
- token | |
- ); | |
+ // Populate the selected entities text. | |
+ if (widgetType == 'inline') { | |
+ $(widgetElement).val(widgetElement.val()) | |
+ } | |
+ else { | |
+ treeContainer.find(".entity-reference-tree-selected-node").val(widgetElement.val()); | |
+ treeContainer.find(".entity-reference-tree-selected-text").text( | |
+ Drupal.t("Selected entities") + ": " + widgetElement.val() | |
+ ); | |
+ } | |
+ | |
+ // Build the tree. | |
+ treeJs.jstree({ | |
+ core: { | |
+ data: { | |
+ url: function(node) { | |
+ return Drupal.url( | |
+ "admin/entity_reference_tree/json/" + | |
+ entityType + | |
+ "/" + | |
+ bundle + | |
+ "?token=" + | |
+ token | |
+ ); | |
+ }, | |
+ data: function(node) { | |
+ return { | |
+ id: node.id, | |
+ text: node.text, | |
+ parent: node.parent | |
+ }; | |
+ } | |
}, | |
- data: function(node) { | |
- return { | |
- id: node.id, | |
- text: node.text, | |
- parent: node.parent | |
- }; | |
+ themes: { | |
+ dots: dots === "1", | |
+ name: theme | |
+ } | |
+ }, | |
+ checkbox: { | |
+ three_state: false | |
+ }, | |
+ search: { | |
+ show_only_matches: true | |
+ }, | |
+ conditionalselect : function (node, event) { | |
+ if (limit > 0) { | |
+ return this.get_selected().length < limit || node.state.selected; | |
+ } | |
+ else { | |
+ // No limit. | |
+ return true; | |
} | |
+ | |
}, | |
- themes: { | |
- dots: dots === "1", | |
- name: theme | |
+ plugins: ["search", "changed", "checkbox", "conditionalselect"] | |
+ }); | |
+ // Initialize the selected node. | |
+ treeJs.on("ready.jstree", function(e, data) { | |
+ data.instance.select_node(selectedNodes); | |
+ if (widgetType == "modal") { | |
+ // Make modal window height scaled automatically. | |
+ $("#drupal-modal").dialog("option", { height: 'auto' }); | |
} | |
- }, | |
- checkbox: { | |
- three_state: false | |
- }, | |
- search: { | |
- show_only_matches: true | |
- }, | |
- conditionalselect : function (node, event) { | |
- if (limit > 0) { | |
- return this.get_selected().length < limit || node.state.selected; | |
- } | |
- else { | |
- // No limit. | |
- return true; | |
- } | |
- | |
- }, | |
- plugins: ["search", "changed", "checkbox", "conditionalselect"] | |
- }); | |
- // Initialize the selected node. | |
- treeContainer.on("ready.jstree", function(e, data) { | |
- data.instance.select_node(selectedNodes); | |
- // Make modal window height scaled automatically. | |
- $("#drupal-modal").dialog( "option", { height: 'auto' } ); | |
- }); | |
- // Selected event. | |
- treeContainer.on("changed.jstree", function(evt, data) { | |
- // selected node objects; | |
- const choosedNodes = data.selected; | |
- const r = []; | |
+ }); | |
+ // Selected event. | |
+ treeJs.on("changed.jstree", function(evt, data) { | |
+ // selected node objects; | |
+ const choosedNodes = data.selected; | |
+ const r = []; | |
- for (let i = 0; i < choosedNodes.length; i++) { | |
- const node = data.instance.get_node(choosedNodes[i]); | |
- // node text escaping double quote. | |
- let nodeText = | |
- node.text.replace(/"/g, '""') + " (" + node.id + ")"; | |
- // Comma is a special character for autocomplete widge. | |
- if ( | |
- nodeText.indexOf(",") !== -1 || | |
- nodeText.indexOf("'") !== -1 | |
- ) { | |
- nodeText = '"' + nodeText + '"'; | |
+ for (let i = 0; i < choosedNodes.length; i++) { | |
+ const node = data.instance.get_node(choosedNodes[i]); | |
+ // node text escaping double quote. | |
+ let nodeText = | |
+ node.text.replace(/"/g, '""') + " (" + node.id + ")"; | |
+ // Comma is a special character for autocomplete widge. | |
+ if ( | |
+ nodeText.indexOf(",") !== -1 || | |
+ nodeText.indexOf("'") !== -1 | |
+ ) { | |
+ nodeText = '"' + nodeText + '"'; | |
+ } | |
+ r.push(nodeText); | |
} | |
- r.push(nodeText); | |
- } | |
- const selectedText = r.join(", "); | |
- $("#entity-reference-tree-selected-node").val(selectedText); | |
- $("#entity-reference-tree-selected-text").text( | |
- Drupal.t("Selected entities") + ": " + selectedText | |
- ); | |
- }); | |
- // Search filter box. | |
- let to = false; | |
- $("#entity-reference-tree-search").keyup(function() { | |
- const searchInput = $(this); | |
- if (to) { | |
- clearTimeout(to); | |
- } | |
- to = setTimeout(function() { | |
- const v = searchInput.val(); | |
- treeContainer.jstree(true).search(v); | |
- }, 250); | |
- }); | |
- } | |
- }); | |
+ const selectedText = r.join(", "); | |
+ if (widgetType == "inline") { | |
+ $(widgetElement).val(selectedText); | |
+ } | |
+ else { | |
+ treeContainer.find(".entity-reference-tree-selected-node").val(selectedText); | |
+ treeContainer.find(".entity-reference-tree-selected-text").text( | |
+ Drupal.t("Selected entities") + ": " + selectedText | |
+ ); | |
+ } | |
+ }); | |
+ // Search filter box. | |
+ let to = false; | |
+ treeContainer.find(".entity-reference-tree-search").keyup(function() { | |
+ const searchInput = $(this); | |
+ if (to) { | |
+ clearTimeout(to); | |
+ } | |
+ to = setTimeout(function() { | |
+ const v = searchInput.val(); | |
+ treeJs.jstree(true).search(v); | |
+ }, 250); | |
+ }); | |
+ } | |
+ }); | |
} | |
}; | |
})(jQuery, Drupal); | |
@@ -153,7 +167,7 @@ | |
// Codes just run once the DOM has loaded. | |
// @See https://www.drupal.org/docs/8/api/javascript-api/javascript-api-overview | |
(function($) { | |
- // Search form sumbit function. | |
+ // Search form sumbit function. | |
// Argument passed from InvokeCommand defined in Drupal\entity_reference_tree\Form\SearchForm | |
$.fn.entitySearchDialogAjaxCallback = function(fieldEditID, selectedEntites) { | |
if ($("#" + fieldEditID).length) { | |
diff --git a/src/Form/SearchForm.php b/src/Form/SearchForm.php | |
index 5c62467..6fdb358 100644 | |
--- a/src/Form/SearchForm.php | |
+++ b/src/Form/SearchForm.php | |
@@ -37,7 +37,7 @@ class SearchForm extends FormBase { | |
/** | |
* {@inheritdoc} | |
*/ | |
- public function buildForm(array $form, FormStateInterface $form_state, $field_edit_id = '', $bundles = '', $entity_type = '', $theme = 'default', $dots = false) { | |
+ public function buildForm(array $form, FormStateInterface $form_state, $field_edit_id = '', $bundles = '', $entity_type = '', $theme = 'default', $dots = FALSE) { | |
// Do nothing after the form is submitted. | |
if (!empty($form_state->getValues())) { | |
return []; | |
@@ -45,64 +45,77 @@ class SearchForm extends FormBase { | |
// Limit number of selected nodes of tree. | |
$limit = $this->getRequest()->get('limit'); | |
+ // All around wrapper. | |
+ $form['tree_container'] = [ | |
+ '#type' => 'html_tag', | |
+ '#tag' => 'div', | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree-wrapper', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
// The status messages that will contain any form errors. | |
- $form['status_messages'] = [ | |
+ $form['tree_container']['status_messages'] = [ | |
'#type' => 'status_messages', | |
'#weight' => -10, | |
]; | |
// Selectd entity text. | |
- $form['selected_text'] = [ | |
- '#type' => 'html_tag', | |
- '#tag' => 'div', | |
- '#value' => $this | |
+ $form['tree_container']['selected_text'] = [ | |
+ '#type' => 'html_tag', | |
+ '#tag' => 'div', | |
+ '#value' => $this | |
->t('Selected Entities'), | |
- '#weight' => 1000, | |
- '#attributes' => [ | |
- 'class' => [ | |
- 'selected-entities-text', | |
- ], | |
- 'id' => [ | |
- 'entity-reference-tree-selected-text', | |
- ], | |
+ '#weight' => 1000, | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'selected-entities-text', | |
+ 'entity-reference-tree-selected-text', | |
], | |
+ ], | |
]; | |
+ | |
// Hidden field for submitting selected entity IDs. | |
- $form['selected_node'] = [ | |
+ $form['tree_container']['selected_node'] = [ | |
'#type' => 'hidden', | |
'#attributes' => [ | |
- 'id' => [ | |
+ 'class' => [ | |
'entity-reference-tree-selected-node', | |
], | |
], | |
]; | |
+ | |
// Search filter box. | |
- $form['tree_search'] = [ | |
+ $form['tree_container']['tree_search'] = [ | |
'#type' => 'textfield', | |
'#title' => $this | |
->t('Search'), | |
'#size' => 60, | |
'#attributes' => [ | |
- 'id' => [ | |
+ 'class' => [ | |
'entity-reference-tree-search', | |
], | |
], | |
]; | |
+ | |
// JsTree container. | |
- $form['tree_container'] = [ | |
+ $form['tree_container']['js_tree'] = [ | |
'#type' => 'html_tag', | |
'#tag' => 'div', | |
'#attributes' => [ | |
- 'id' => [ | |
- 'entity-reference-tree-wrapper', | |
+ 'class' => [ | |
+ 'entity-reference-tree', | |
], | |
'theme' => $theme, | |
- 'dots' => $dots, | |
+ 'dots' => $dots, | |
], | |
]; | |
+ | |
// Submit button. | |
- $form['actions'] = ['#type' => 'actions']; | |
- $form['actions']['send'] = [ | |
+ $form['tree_container']['actions'] = ['#type' => 'actions']; | |
+ $form['tree_container']['actions']['send'] = [ | |
'#type' => 'submit', | |
'#value' => $this->t('Save'), | |
'#attributes' => [ | |
@@ -116,55 +129,59 @@ class SearchForm extends FormBase { | |
], | |
]; | |
- $form['#attached']['library'][] = 'entity_reference_tree/jstree'; | |
- $form['#attached']['library'][] = 'entity_reference_tree/entity_tree'; | |
- $form['#attached']['library'][] = 'entity_reference_tree/jstree_' . $theme . '_theme'; | |
- | |
- // Disable the cache for this form. | |
- $form_state->setCached(FALSE); | |
- $form['#cache'] = ['max-age' => 0]; | |
- $form['#attributes']['data-user-info-from-browser'] = FALSE; | |
// Field element id. | |
- $form['field_id'] = [ | |
+ $form['tree_container']['field_id'] = [ | |
'#name' => 'field_id', | |
'#type' => 'hidden', | |
'#weight' => 80, | |
'#value' => $field_edit_id, | |
'#attributes' => [ | |
- 'id' => [ | |
+ 'class' => [ | |
'entity-reference-tree-widget-field', | |
], | |
], | |
]; | |
+ | |
// Entity type. | |
- $form['entity_type'] = [ | |
+ $form['tree_container']['entity_type'] = [ | |
'#name' => 'entity_type', | |
'#type' => 'hidden', | |
'#weight' => 80, | |
'#value' => $entity_type, | |
'#attributes' => [ | |
- 'id' => [ | |
+ 'class' => [ | |
'entity-reference-tree-entity-type', | |
], | |
], | |
]; | |
+ | |
// Entity bundle. | |
- $form['entity_bundle'] = [ | |
+ $form['tree_container']['entity_bundle'] = [ | |
'#name' => 'entity_bundle', | |
'#type' => 'hidden', | |
'#weight' => 80, | |
'#value' => $bundles, | |
'#attributes' => [ | |
- 'id' => [ | |
+ 'class' => [ | |
'entity-reference-tree-entity-bundle', | |
], | |
], | |
]; | |
- | |
+ | |
+ // Disable the cache for this form. | |
+ $form_state->setCached(FALSE); | |
+ $form['#cache'] = ['max-age' => 0]; | |
+ $form['#attributes']['data-user-info-from-browser'] = FALSE; | |
+ | |
+ $form['#attached']['library'][] = 'entity_reference_tree/jstree'; | |
+ $form['#attached']['library'][] = 'entity_reference_tree/entity_tree'; | |
+ $form['#attached']['library'][] = 'entity_reference_tree/jstree_' . $theme . '_theme'; | |
+ | |
// Pass data to js file. | |
$form['#attached']['drupalSettings'] = [ | |
- 'entity_tree_token_' . $field_edit_id => \Drupal::csrfToken()->get($bundles), | |
- 'tree_limit_' . $field_edit_id => empty($limit) ? -1 : $limit, | |
+ 'entity_tree_token_' . $field_edit_id => \Drupal::csrfToken()->get($bundles), | |
+ 'tree_limit_' . $field_edit_id => empty($limit) ? -1 : $limit, | |
+ 'widget_type' => 'modal', | |
]; | |
return $form; | |
@@ -178,7 +195,7 @@ class SearchForm extends FormBase { | |
// If there are any form errors, re-display the form. | |
if ($form_state->hasAnyErrors()) { | |
- $response->addCommand(new ReplaceCommand('#entity_reference_tree_wrapper', $form)); | |
+ $response->addCommand(new ReplaceCommand('.entity_reference_tree_wrapper', $form)); | |
} | |
else { | |
$response->addCommand(new InvokeCommand(NULL, 'entitySearchDialogAjaxCallback', [$form_state->getValue('field_id'), $form_state->getValue('selected_node')])); | |
diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceTreeWidgetBase.php b/src/Plugin/Field/FieldWidget/EntityReferenceTreeWidgetBase.php | |
new file mode 100644 | |
index 0000000..7b017bc | |
--- /dev/null | |
+++ b/src/Plugin/Field/FieldWidget/EntityReferenceTreeWidgetBase.php | |
@@ -0,0 +1,93 @@ | |
+<?php | |
+ | |
+namespace Drupal\entity_reference_tree\Plugin\Field\FieldWidget; | |
+ | |
+use Drupal\Core\Form\FormStateInterface; | |
+use Drupal\Core\Field\FieldItemListInterface; | |
+use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget; | |
+ | |
+/** | |
+ * Base class for 'Entity reference tree' widget plugin implementations. | |
+ */ | |
+abstract class EntityReferenceTreeWidgetBase extends EntityReferenceAutocompleteWidget { | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public static function defaultSettings() { | |
+ return [ | |
+ // JsTree theme. | |
+ 'theme' => 'default', | |
+ // Using dot line. | |
+ 'dots' => 0, | |
+ ] + parent::defaultSettings(); | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function settingsForm(array $form, FormStateInterface $form_state) { | |
+ $element = []; | |
+ | |
+ // JsTRee theme. | |
+ $element['theme'] = [ | |
+ '#type' => 'radios', | |
+ '#title' => $this->t('JsTree theme'), | |
+ '#default_value' => $this->getSetting('theme'), | |
+ '#required' => TRUE, | |
+ '#options' => [ | |
+ 'default' => $this->t('Default'), | |
+ 'default-dark' => $this->t('Default Dark'), | |
+ ], | |
+ ]; | |
+ | |
+ // Tree dot. | |
+ $element['dots'] = [ | |
+ '#type' => 'radios', | |
+ '#title' => $this->t('Dot line'), | |
+ '#default_value' => $this->getSetting('dots'), | |
+ '#options' => [ | |
+ 0 => $this->t('No'), | |
+ 1 => $this->t('Yes'), | |
+ ], | |
+ ]; | |
+ | |
+ return $element; | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function settingsSummary() { | |
+ $summary = []; | |
+ | |
+ // JsTree theme. | |
+ $summary[] = $this->t('JsTree theme: @theme', ['@theme' => $this->getSetting('theme')]); | |
+ | |
+ return $summary; | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { | |
+ $element = parent::formElement($items, $delta, $element, $form, $form_state); | |
+ | |
+ // The id of autocomple text field. | |
+ $edit_id = 'edit-' . str_replace('_', '-', $items->getName()) . '-target-id'; | |
+ | |
+ $element['target_id']['#id'] = $edit_id; | |
+ $element['target_id']['#tags'] = TRUE; | |
+ $element['target_id']['#default_value'] = $items->referencedEntities(); | |
+ | |
+ return $element; | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { | |
+ return $values['target_id']; | |
+ } | |
+ | |
+} | |
diff --git a/src/Plugin/Field/FieldWidget/InlineEntityReferenceTreeWidget.php b/src/Plugin/Field/FieldWidget/InlineEntityReferenceTreeWidget.php | |
new file mode 100644 | |
index 0000000..d10e47f | |
--- /dev/null | |
+++ b/src/Plugin/Field/FieldWidget/InlineEntityReferenceTreeWidget.php | |
@@ -0,0 +1,134 @@ | |
+<?php | |
+ | |
+namespace Drupal\entity_reference_tree\Plugin\Field\FieldWidget; | |
+ | |
+use Drupal\Core\Form\FormStateInterface; | |
+use Drupal\Core\Field\FieldItemListInterface; | |
+ | |
+/** | |
+ * Plugin implementation of the 'inline_entity_reference_tree_widget' widget. | |
+ * | |
+ * @FieldWidget( | |
+ * id = "inline_entity_reference_tree_widget", | |
+ * module = "dd_tree_builder", | |
+ * label = @Translation("Inline entity reference tree widget"), | |
+ * field_types = { | |
+ * "entity_reference" | |
+ * }, | |
+ * multiple_values = TRUE | |
+ * ) | |
+ */ | |
+class InlineEntityReferenceTreeWidget extends EntityReferenceTreeWidgetBase { | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { | |
+ $element = parent::formElement($items, $delta, $element, $form, $form_state); | |
+ | |
+ $theme = $this->getSetting('theme'); | |
+ $dots = $this->getSetting('dots'); | |
+ $field_edit_id = $element['target_id']['#id']; | |
+ $entity_type = $element['target_id']['#target_type']; | |
+ $arr_target = $element['target_id']['#selection_settings']['target_bundles']; | |
+ $bundles = empty($arr_target) ? '*' : implode(',', $arr_target); | |
+ | |
+ // All around wrapper. | |
+ $element['tree_container'] = [ | |
+ '#type' => 'html_tag', | |
+ '#tag' => 'div', | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree-wrapper', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
+ // The status messages that will contain any form errors. | |
+ $element['tree_container']['status_messages'] = [ | |
+ '#type' => 'status_messages', | |
+ '#weight' => -10, | |
+ ]; | |
+ | |
+ // Search filter box. | |
+ $element['tree_container']['tree_search'] = [ | |
+ '#type' => 'textfield', | |
+ '#title' => $this | |
+ ->t('Search'), | |
+ '#size' => 60, | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree-search', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
+ // Field element id. | |
+ $element['tree_container']['field_id'] = [ | |
+ '#name' => 'field_id', | |
+ '#type' => 'hidden', | |
+ '#weight' => 80, | |
+ '#value' => $field_edit_id, | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree-widget-field', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
+ // Entity type. | |
+ $element['tree_container']['entity_type'] = [ | |
+ '#name' => 'entity_type', | |
+ '#type' => 'hidden', | |
+ '#weight' => 80, | |
+ '#value' => $entity_type, | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree-entity-type', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
+ // Entity bundle. | |
+ $element['tree_container']['entity_bundle'] = [ | |
+ '#name' => 'entity_bundle', | |
+ '#type' => 'hidden', | |
+ '#weight' => 80, | |
+ '#value' => $bundles, | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree-entity-bundle', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
+ // JsTree container. | |
+ $element['tree_container']['js_tree'] = [ | |
+ '#type' => 'html_tag', | |
+ '#tag' => 'div', | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'entity-reference-tree', | |
+ ], | |
+ 'theme' => $theme, | |
+ 'dots' => $dots, | |
+ ], | |
+ ]; | |
+ | |
+ // Attach libraries. | |
+ $form['#attached']['library'][] = 'entity_reference_tree/jstree'; | |
+ $form['#attached']['library'][] = 'entity_reference_tree/jstree_' . $theme . '_theme'; | |
+ $form['#attached']['library'][] = 'entity_reference_tree/entity_tree'; | |
+ $form['#attached']['library'][] = 'entity_reference_tree/widget'; | |
+ | |
+ // Pass data to js file. | |
+ $element['#attached']['drupalSettings'] = [ | |
+ 'entity_tree_token_' . $field_edit_id => \Drupal::csrfToken()->get($bundles), | |
+ 'tree_limit_' . $field_edit_id => -1, | |
+ 'widget_type' => 'inline', | |
+ ]; | |
+ | |
+ return $element; | |
+ } | |
+ | |
+} | |
diff --git a/src/Plugin/Field/FieldWidget/ModalEntityReferenceTreeWidget.php b/src/Plugin/Field/FieldWidget/ModalEntityReferenceTreeWidget.php | |
new file mode 100644 | |
index 0000000..daeb1ea | |
--- /dev/null | |
+++ b/src/Plugin/Field/FieldWidget/ModalEntityReferenceTreeWidget.php | |
@@ -0,0 +1,110 @@ | |
+<?php | |
+ | |
+namespace Drupal\entity_reference_tree\Plugin\Field\FieldWidget; | |
+ | |
+use Drupal\Core\Field\FieldItemListInterface; | |
+use Drupal\Core\Form\FormStateInterface; | |
+use Drupal\Core\Url; | |
+ | |
+/** | |
+ * A entity reference tree widget. | |
+ * | |
+ * @FieldWidget( | |
+ * id = "entity_reference_tree", | |
+ * label = @Translation("Entity reference tree widget"), | |
+ * field_types = { | |
+ * "entity_reference", | |
+ * }, | |
+ * multiple_values = TRUE | |
+ * ) | |
+ */ | |
+class ModalEntityReferenceTreeWidget extends EntityReferenceTreeWidgetBase { | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public static function defaultSettings() { | |
+ return [ | |
+ // Button label. | |
+ 'label' => '', | |
+ ] + parent::defaultSettings(); | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function settingsForm(array $form, FormStateInterface $form_state) { | |
+ $element = parent::settingsForm($form, $form_state); | |
+ | |
+ // Button label. | |
+ $element['label'] = [ | |
+ '#type' => 'textfield', | |
+ '#title' => $this->t('Button label'), | |
+ '#default_value' => $this->getSetting('label'), | |
+ ]; | |
+ | |
+ return $element; | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function settingsSummary() { | |
+ $summary = parent::settingsSummary(); | |
+ | |
+ // Button label. | |
+ if ($label = $this->getSetting('label')) { | |
+ $summary[] = t('Button label: @label', ['@label' => $label]); | |
+ } | |
+ | |
+ return $summary; | |
+ } | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) | |
+ { | |
+ | |
+ $arr_element = parent::formElement($items, $delta, $element, $form, $form_state); | |
+ $arr_target = empty($arr_element['target_id']['#selection_settings']['target_bundles']) ? [] : $arr_element['target_id']['#selection_settings']['target_bundles']; | |
+ $str_target_type = $arr_element['target_id']['#target_type']; | |
+ | |
+ $edit_id = $arr_element['target_id']['#id']; | |
+ | |
+ $label = $this->getSetting('label'); | |
+ if (!$label) { | |
+ $label = $this->t('@label tree', [ | |
+ '@label' => ucfirst(str_replace('_', ' ', $str_target_type)), | |
+ ]); | |
+ } else { | |
+ $label = $this->t('@label', ['@label' => $label]); | |
+ } | |
+ | |
+ $arr_element['dialog_link'] = [ | |
+ '#type' => 'link', | |
+ '#title' => $label, | |
+ '#url' => Url::fromRoute( | |
+ 'entity_reference_tree.widget_form', | |
+ [ | |
+ 'field_edit_id' => $edit_id, | |
+ 'bundle' => empty($arr_target) ? '*' : implode(',', $arr_target), | |
+ 'entity_type' => $str_target_type, | |
+ 'theme' => $this->getSetting('theme'), | |
+ 'dots' => $this->getSetting('dots'), | |
+ 'limit' => $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(), | |
+ ]), | |
+ '#attributes' => [ | |
+ 'class' => [ | |
+ 'use-ajax', | |
+ 'button', | |
+ ], | |
+ ], | |
+ ]; | |
+ | |
+ // Attach libraries. | |
+ $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; | |
+ $form['#attached']['library'][] = 'entity_reference_tree/widget'; | |
+ return $arr_element; | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment