From 769469e5bfa185fb56cc7f5ab8f93b910f376b25 Mon Sep 17 00:00:00 2001
From: Tomotaka Hosomi <61442-hosomitm@users.noreply.drupalcode.org>
Date: Sun, 3 Dec 2023 09:36:46 +0000
Subject: [PATCH] Issue #3401473 by hosomitm, yas: Refactor to native
 JavaScript from jQuery (openstack_form.js)

---
 cloud.libraries.yml                           |  13 ++
 .../openstack/css/openstack_form.css          |  11 ++
 .../openstack/js/openstack_form.js            | 173 ++++--------------
 .../openstack/openstack.libraries.yml         |   6 +-
 4 files changed, 68 insertions(+), 135 deletions(-)
 create mode 100644 modules/cloud_service_providers/openstack/css/openstack_form.css

diff --git a/cloud.libraries.yml b/cloud.libraries.yml
index c56934bb68..e30eae9c08 100644
--- a/cloud.libraries.yml
+++ b/cloud.libraries.yml
@@ -60,6 +60,19 @@ select2:
     theme:
       https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css: { type: external, minified: true }
 
+tom-select:
+  remote: https://github.com/orchidjs/tom-select
+  version: 2.3.1
+  license:
+    name: Apache License, Version 2.0
+    url: https://www.apache.org/licenses/LICENSE-2.0
+    gpl-compatible: false
+  js:
+    https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.complete.min.js: { type: external, minified: true }
+  css:
+    theme:
+      https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/css/tom-select.css: { type: external, minified: true }
+
 datatables:
   remote: https://github.com/fiduswriter/Simple-DataTables
   version: 3.1.2
diff --git a/modules/cloud_service_providers/openstack/css/openstack_form.css b/modules/cloud_service_providers/openstack/css/openstack_form.css
new file mode 100644
index 0000000000..f0677f4228
--- /dev/null
+++ b/modules/cloud_service_providers/openstack/css/openstack_form.css
@@ -0,0 +1,11 @@
+.ts-control {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  transform: translateY(-50%);
+  border: none;
+}
+
+.plugin-dropdown_input.focus.dropdown-active .ts-control {
+  border: none;
+}
diff --git a/modules/cloud_service_providers/openstack/js/openstack_form.js b/modules/cloud_service_providers/openstack/js/openstack_form.js
index da5d39f7ee..9469607ac8 100644
--- a/modules/cloud_service_providers/openstack/js/openstack_form.js
+++ b/modules/cloud_service_providers/openstack/js/openstack_form.js
@@ -1,141 +1,48 @@
-(function ($) {
+(function () {
   'use strict';
 
-  // Select2 custom results adapter.
-  $.fn.select2.amd.define('CustomResultsAdapter', [
-    'select2/utils',
-    'select2/results'
-  ],
-  function (Utils, Results) {
-    function CustomResultsAdapter($element, options, dataAdapter) {
-      this.$element = $element;
-      this.data = dataAdapter;
-      this.options = options;
-
-      CustomResultsAdapter.__super__.constructor.call(this, $element, options, dataAdapter);
-    }
-
-    Utils.Extend(CustomResultsAdapter, Results);
-
-    CustomResultsAdapter.prototype.render = function () {
-      let $results = $(
-        '<tbody class="select2-results__options" role="tree"></tbody>'
-      );
-
-      if (this.options.get('multiple')) {
-        $results.attr('aria-multiselectable', 'true');
-      }
-
-      this.$results = $results;
-
-      return $results;
-    };
-
-    CustomResultsAdapter.prototype.option = function (data) {
-      let option = document.createElement('tr');
-
-      option.className = 'select2-results__option';
-      let attrs = {
-        'role': 'treeitem',
-        'aria-selected': 'false'
-      };
-
-      if (data.disabled) {
-        delete attrs['aria-selected'];
-        attrs['aria-disabled'] = 'true';
-      }
-
-      if (data.id === null) {
-        delete attrs['aria-selected'];
-      }
-
-      if (data._resultId !== null) {
-        option.id = data._resultId;
-      }
-
-      if (data.title) {
-        option.title = data.title;
-      }
-
-      if (data.children) {
-        attrs.role = 'group';
-        attrs['aria-label'] = data.text;
-        delete attrs['aria-selected'];
-      }
-
-      for (let attr in attrs) {
-        if (attrs.hasOwnProperty(attr)) {
-          let val = attrs[attr];
-          option.setAttribute(attr, val);
-        }
-      }
-
-      this.template(data, option);
-      $.data(option, 'data', data);
-      return option;
-    };
-
-    return CustomResultsAdapter;
-  });
-
-  // Select2 custom dropdown adapter.
-  $.fn.select2.amd.define('CustomDropdownAdapter', [
-    'select2/utils',
-    'select2/dropdown',
-    'select2/dropdown/attachBody',
-    'select2/dropdown/attachContainer',
-    'select2/dropdown/search'
-  ],
-  function (Utils, Dropdown, AttachBody, AttachContainer, Search) {
-    function CustomDropdownAdapter($element, options) {
-      this.$element = $element;
-      this.options = options;
-
-      CustomDropdownAdapter.__super__.constructor.call(this, $element, options);
+  // Resets the for attribute of Label rewritten by tom-select to its original value.
+  const resetLabel = function (label_for) {
+    const control_id	= label_for + '-ts-control';
+    const query = "label[for='" + control_id + "']";
+    const label = document.querySelector(query);
+    if (label) {
+      label.setAttribute('for', label_for);
     }
-
-    Utils.Extend(CustomDropdownAdapter, Dropdown);
-
-    CustomDropdownAdapter.prototype.render = function () {
-      let $dropdown = $(
-        '<div class="select2-dropdown"><table class="select2-results"><thead><tr><th>Name</th><th>vCPUs</th><th>RAM (MB)</th></tr></thead></table></div>'
-      );
-
-      $dropdown.attr('dir', this.options.get('dir'));
-      this.$dropdown = $dropdown;
-      return $dropdown;
-    };
-
-    let adapter = Utils.Decorate(CustomDropdownAdapter, Search);
-    adapter = Utils.Decorate(adapter, AttachContainer);
-    adapter = Utils.Decorate(adapter, AttachBody);
-    return adapter;
-  });
+  }
 
   // Flavor.
-  $('#edit-field-flavor').select2({
-    width: '100%',
-    closeOnSelect: true,
-    dropdownAdapter: $.fn.select2.amd.require('CustomDropdownAdapter'),
-    resultsAdapter: $.fn.select2.amd.require('CustomResultsAdapter'),
-    templateResult: function (state) {
-      let parts = state.text.split(':').slice(0, 5);
-      let html = '<td>' + parts.join('</td><td>') + '</td>';
-      if (parts.length === 1) {
-        html = '<td colspan=5>' + parts[0] + '</td>';
-      }
-      let $state = $(html);
-      return $state;
-    },
-    templateSelection: (state) => {
-      if (!state.id) {
-        return state.text;
+  new TomSelect("#edit-field-flavor", {
+    create: false,
+    dropdownParent: 'body',
+    plugins: [
+      'dropdown_input',
+      'no_backspace_delete',
+    ],
+    render: {
+      dropdown: function () {
+        return '<div><div style="font-weight: bold;">' +
+            '<span style="width: 50%; display: inline-block;">Name</span>' +
+            '<span style="width: 25%; display: inline-block;">vCPUs</span>' +
+            '<span style="width: 25%; display: inline-block;">RAM (MB)</span>' +
+            '</div></div>';
+      },
+      option: function (data, escape) {
+        const splitLabel = escape(data.text).split(':');
+        while (splitLabel.length < 3) {
+          splitLabel.push('');
+        }
+        return '<div>' +
+            '<span style="width: 50%; display: inline-block;">' + splitLabel[0] + '</span>' +
+            '<span style="width: 25%; display: inline-block;">' + splitLabel[1] + '</span>' +
+            '<span style="width: 25%; display: inline-block;">' + splitLabel[2] + '</span>' +
+            '</div>';
+      },
+      item: function (data, escape) {
+        const splitLabel = escape(data.text).split(':');
+        return '<div title="' + splitLabel[0] + '">' + splitLabel[0] + '</div>';
       }
-      let $state = $(
-        '<span>' + state.text.split(':').shift() + '</span>'
-      );
-      return $state;
     }
   });
-
-})(jQuery);
+  resetLabel("edit-field-flavor");
+})();
diff --git a/modules/cloud_service_providers/openstack/openstack.libraries.yml b/modules/cloud_service_providers/openstack/openstack.libraries.yml
index 3bda5b9719..bd928ab0ff 100644
--- a/modules/cloud_service_providers/openstack/openstack.libraries.yml
+++ b/modules/cloud_service_providers/openstack/openstack.libraries.yml
@@ -2,9 +2,11 @@ openstack_form:
   version: 5.x-dev
   js:
     js/openstack_form.js: {}
+  css:
+    theme:
+      css/openstack_form.css: {}
   dependencies:
-    - core/jquery
-    - cloud/select2
+    - cloud/tom-select
 
 openstack_launch_template_radios:
   version: 6.x-dev
-- 
GitLab