0 ));
$app = Factory::getApplication();
$input = $app->input;
diff --git a/administrator/components/com_fields/tmpl/field/modal.php b/administrator/components/com_fields/tmpl/field/modal.php
index 23970f554bf80..6ab9a96aab686 100644
--- a/administrator/components/com_fields/tmpl/field/modal.php
+++ b/administrator/components/com_fields/tmpl/field/modal.php
@@ -16,7 +16,6 @@
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect');
$app = Factory::getApplication();
$input = $app->input;
diff --git a/administrator/components/com_fields/tmpl/fields/default_batch_body.php b/administrator/components/com_fields/tmpl/fields/default_batch_body.php
index 6060fa285541e..b2be9c0cafde4 100644
--- a/administrator/components/com_fields/tmpl/fields/default_batch_body.php
+++ b/administrator/components/com_fields/tmpl/fields/default_batch_body.php
@@ -12,8 +12,6 @@
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect');
-
HTMLHelper::_('script', 'com_fields/admin-fields-default-batch.js', ['version' => 'auto', 'relative' => true]);
$context = $this->escape($this->state->get('filter.context'));
diff --git a/administrator/components/com_fields/tmpl/fields/modal.php b/administrator/components/com_fields/tmpl/fields/modal.php
index 0aa6a68b17178..25fc4a5c18519 100644
--- a/administrator/components/com_fields/tmpl/fields/modal.php
+++ b/administrator/components/com_fields/tmpl/fields/modal.php
@@ -21,7 +21,6 @@
}
HTMLHelper::_('behavior.core');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect');
HTMLHelper::_('bootstrap.popover', '.hasPopover', array('placement' => 'bottom'));
HTMLHelper::_('script', 'com_fields/admin-fields-modal.js', array('version' => 'auto', 'relative' => true));
diff --git a/administrator/components/com_fields/tmpl/group/edit.php b/administrator/components/com_fields/tmpl/group/edit.php
index 4b78214cb0904..f296629836839 100644
--- a/administrator/components/com_fields/tmpl/group/edit.php
+++ b/administrator/components/com_fields/tmpl/group/edit.php
@@ -17,7 +17,6 @@
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
HTMLHelper::_('behavior.tabstate');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect');
$app = Factory::getApplication();
$input = $app->input;
diff --git a/administrator/components/com_fields/tmpl/groups/default.php b/administrator/components/com_fields/tmpl/groups/default.php
index 43a42fc03a2e3..01588a65bdc99 100644
--- a/administrator/components/com_fields/tmpl/groups/default.php
+++ b/administrator/components/com_fields/tmpl/groups/default.php
@@ -18,7 +18,6 @@
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
HTMLHelper::_('behavior.multiselect');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect');
$app = Factory::getApplication();
$user = Factory::getUser();
diff --git a/administrator/components/com_fields/tmpl/groups/default_batch_body.php b/administrator/components/com_fields/tmpl/groups/default_batch_body.php
index 55f3bec2c8dec..3d5d20ebed07c 100644
--- a/administrator/components/com_fields/tmpl/groups/default_batch_body.php
+++ b/administrator/components/com_fields/tmpl/groups/default_batch_body.php
@@ -11,7 +11,6 @@
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Layout\LayoutHelper;
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect');
?>
diff --git a/administrator/components/com_modules/Field/ModulesPositionField.php b/administrator/components/com_modules/Field/ModulesPositionField.php
index c31aaf39306c4..1207d748f1d62 100644
--- a/administrator/components/com_modules/Field/ModulesPositionField.php
+++ b/administrator/components/com_modules/Field/ModulesPositionField.php
@@ -12,17 +12,15 @@
defined('JPATH_BASE') or die;
use Joomla\CMS\Factory;
-use Joomla\CMS\Form\FormHelper;
+use Joomla\CMS\Form\Field\ListField;
use Joomla\Component\Modules\Administrator\Helper\ModulesHelper;
-FormHelper::loadFieldClass('list');
-
/**
* Modules Position field.
*
* @since 3.4.2
*/
-class ModulesPositionField extends \JFormFieldList
+class ModulesPositionField extends ListField
{
/**
* The form field type.
diff --git a/administrator/components/com_modules/Field/ModulesPositioneditField.php b/administrator/components/com_modules/Field/ModulesPositioneditField.php
new file mode 100644
index 0000000000000..7688cbe806e50
--- /dev/null
+++ b/administrator/components/com_modules/Field/ModulesPositioneditField.php
@@ -0,0 +1,64 @@
+getLayoutData();
+
+ $clientId = Factory::getApplication()->input->get('client_id', 0, 'int');
+ $positions = HTMLHelper::_('modules.positions', $clientId, 1, $this->value);
+
+ $data['client'] = $clientId;
+ $data['positions'] = $positions;
+
+ $renderer = $this->getRenderer($this->layout);
+ $renderer->setComponent('com_modules');
+ $renderer->setClient(1);
+
+ return $renderer->render($data);
+ }
+}
diff --git a/administrator/components/com_modules/Helper/ModulesHelper.php b/administrator/components/com_modules/Helper/ModulesHelper.php
index 4cc87de546d39..45b2788c45ce1 100644
--- a/administrator/components/com_modules/Helper/ModulesHelper.php
+++ b/administrator/components/com_modules/Helper/ModulesHelper.php
@@ -107,6 +107,10 @@ public static function getPositions($clientId, $editPositions = false)
{
$options[] = HTMLHelper::_('select.option', 'none', Text::_('COM_MODULES_NONE'));
}
+ elseif (!$position)
+ {
+ $options[] = HTMLHelper::_('select.option', '', Text::_('COM_MODULES_NONE'));
+ }
else
{
$options[] = HTMLHelper::_('select.option', $position, $position);
diff --git a/administrator/components/com_modules/Service/HTML/Modules.php b/administrator/components/com_modules/Service/HTML/Modules.php
index e9b003dbbcb58..3264eeb62e4ad 100644
--- a/administrator/components/com_modules/Service/HTML/Modules.php
+++ b/administrator/components/com_modules/Service/HTML/Modules.php
@@ -148,7 +148,7 @@ public function positions($clientId, $state = 1, $selectedPosition = '')
$templateGroups = array();
// Add an empty value to be able to deselect a module position
- $option = ModulesHelper::createOption();
+ $option = ModulesHelper::createOption('', Text::_('COM_MODULES_NONE'));
$templateGroups[''] = ModulesHelper::createOptionGroup('', array($option));
// Add positions from templates
@@ -181,8 +181,7 @@ public function positions($clientId, $state = 1, $selectedPosition = '')
// Add custom position to options
$customGroupText = Text::_('COM_MODULES_CUSTOM_POSITION');
-
- $editPositions = true;
+ $editPositions = true;
$customPositions = ModulesHelper::getPositions($clientId, $editPositions);
$templateGroups[$customGroupText] = ModulesHelper::createOptionGroup($customGroupText, $customPositions);
diff --git a/administrator/components/com_modules/View/Modules/HtmlView.php b/administrator/components/com_modules/View/Modules/HtmlView.php
index 4157b75d64944..23477545d91d8 100644
--- a/administrator/components/com_modules/View/Modules/HtmlView.php
+++ b/administrator/components/com_modules/View/Modules/HtmlView.php
@@ -184,7 +184,6 @@ protected function addToolbar()
if ($user->authorise('core.create', 'com_modules') && $user->authorise('core.edit', 'com_modules')
&& $user->authorise('core.edit.state', 'com_modules'))
{
- HTMLHelper::_('bootstrap.renderModal', 'collapseModal');
$title = Text::_('JTOOLBAR_BATCH');
// Instantiate a new FileLayout instance and render the batch button
diff --git a/administrator/components/com_modules/forms/module.xml b/administrator/components/com_modules/forms/module.xml
index 7a80a3a3cc298..145de7d805e96 100644
--- a/administrator/components/com_modules/forms/module.xml
+++ b/administrator/components/com_modules/forms/module.xml
@@ -1,16 +1,16 @@
- loadTemplate('positions'); ?>
+ form->getInput('position'); ?>
diff --git a/administrator/components/com_modules/tmpl/module/edit_positions.php b/administrator/components/com_modules/tmpl/module/edit_positions.php
deleted file mode 100644
index c1ddf7094a8a2..0000000000000
--- a/administrator/components/com_modules/tmpl/module/edit_positions.php
+++ /dev/null
@@ -1,32 +0,0 @@
-item->client_id;
-$state = 1;
-$selectedPosition = $this->item->position;
-$positions = HTMLHelper::_('modules.positions', $clientId, $state, $selectedPosition);
-
-// Add custom position to options
-$customGroupText = Text::_('COM_MODULES_CUSTOM_POSITION');
-
-// Build field
-$attr = array(
- 'id' => 'jform_position',
- 'list.select' => $this->item->position,
- 'list.attr' => 'class="chosen-custom-value"',
-);
-
-JHtml::_('formbehavior.chosen', '#jform_position');
-
-echo HTMLHelper::_('select.groupedlist', $positions, 'jform[position]', $attr);
diff --git a/administrator/components/com_modules/tmpl/modules/default_batch_body.php b/administrator/components/com_modules/tmpl/modules/default_batch_body.php
index e305603b59b4b..2ccf347aefb5c 100644
--- a/administrator/components/com_modules/tmpl/modules/default_batch_body.php
+++ b/administrator/components/com_modules/tmpl/modules/default_batch_body.php
@@ -9,6 +9,7 @@
defined('_JEXEC') or die;
+use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
@@ -23,19 +24,17 @@
$positions['']['items'][] = ModulesHelper::createOption('nochange', Text::_('COM_MODULES_BATCH_POSITION_NOCHANGE'));
$positions['']['items'][] = ModulesHelper::createOption('noposition', Text::_('COM_MODULES_BATCH_POSITION_NOPOSITION'));
-// Add custom position to options
-$customGroupText = Text::_('COM_MODULES_CUSTOM_POSITION');
-
// Build field
$attr = array(
- 'id' => 'batch-position-id',
- 'list.attr' => 'class="chosen-custom-value" '
- . 'data-custom_group_text="' . $customGroupText . '" '
- . 'data-no_results_text="' . Text::_('COM_MODULES_ADD_CUSTOM_POSITION') . '" '
- . 'data-placeholder="' . Text::_('COM_MODULES_TYPE_OR_SELECT_POSITION') . '" '
+ 'id' => 'batch-position-id',
);
-HTMLHelper::_('formbehavior.chosen', '.chosen-custom-value');
+Text::script('JGLOBAL_SELECT_NO_RESULTS_MATCH');
+Text::script('JGLOBAL_SELECT_PRESS_TO_SELECT');
+
+Factory::getDocument()->getWebAssetManager()->enableAsset('choicesjs');
+HTMLHelper::_('webcomponent', 'system/webcomponents/joomla-field-fancy-select.min.js', ['version' => 'auto', 'relative' => true]);
+
?>
@@ -74,7 +73,9 @@
+
+
diff --git a/administrator/components/com_newsfeeds/tmpl/newsfeed/edit.php b/administrator/components/com_newsfeeds/tmpl/newsfeed/edit.php
index 88e9f25884d13..a8be9822cfa37 100644
--- a/administrator/components/com_newsfeeds/tmpl/newsfeed/edit.php
+++ b/administrator/components/com_newsfeeds/tmpl/newsfeed/edit.php
@@ -18,7 +18,6 @@
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
-HTMLHelper::_('formbehavior.chosen', '#jform_catid', null, array('disable_search_threshold' => 0 ));
$app = Factory::getApplication();
$input = $app->input;
diff --git a/administrator/components/com_workflow/tmpl/stage/edit.php b/administrator/components/com_workflow/tmpl/stage/edit.php
index c2321caf0f636..f4e04d263c138 100644
--- a/administrator/components/com_workflow/tmpl/stage/edit.php
+++ b/administrator/components/com_workflow/tmpl/stage/edit.php
@@ -17,7 +17,6 @@
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect', null, array('disable_search_threshold' => 0));
$app = Factory::getApplication();
$input = $app->input;
diff --git a/administrator/components/com_workflow/tmpl/transition/edit.php b/administrator/components/com_workflow/tmpl/transition/edit.php
index ff79170b8d53b..81a0a64823596 100644
--- a/administrator/components/com_workflow/tmpl/transition/edit.php
+++ b/administrator/components/com_workflow/tmpl/transition/edit.php
@@ -16,7 +16,6 @@
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect', null, array('disable_search_threshold' => 0 ));
// In case of modal
$isModal = $this->input->get('layout') == 'modal' ? true : false;
diff --git a/administrator/components/com_workflow/tmpl/workflow/edit.php b/administrator/components/com_workflow/tmpl/workflow/edit.php
index d2ba332f8f593..b9be3e3048d80 100644
--- a/administrator/components/com_workflow/tmpl/workflow/edit.php
+++ b/administrator/components/com_workflow/tmpl/workflow/edit.php
@@ -17,7 +17,6 @@
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('behavior.keepalive');
-HTMLHelper::_('formbehavior.chosen', '.advancedSelect', null, array('disable_search_threshold' => 0 ));
$app = Factory::getApplication();
$input = $app->input;
diff --git a/administrator/language/en-GB/en-GB.ini b/administrator/language/en-GB/en-GB.ini
index 1d2efed5c8e5a..cf780aa91a6bf 100644
--- a/administrator/language/en-GB/en-GB.ini
+++ b/administrator/language/en-GB/en-GB.ini
@@ -523,6 +523,7 @@ JGLOBAL_SEF_NOIDS_LABEL="Remove IDs from URLs"
JGLOBAL_SELECT_ALLOW_DENY_GROUP="Change %s permission for %s group."
JGLOBAL_SELECT_AN_OPTION="Select an option"
JGLOBAL_SELECT_NO_RESULTS_MATCH="No results match"
+JGLOBAL_SELECT_PRESS_TO_SELECT="Press to select"
JGLOBAL_SELECT_SOME_OPTIONS="Select some options"
JGLOBAL_SELECTION_ALL="Select All"
JGLOBAL_SELECTION_INVERT="Toggle Selection"
diff --git a/administrator/templates/atum/scss/template.scss b/administrator/templates/atum/scss/template.scss
index e7ddc12fa3edf..544f3ee9e5403 100644
--- a/administrator/templates/atum/scss/template.scss
+++ b/administrator/templates/atum/scss/template.scss
@@ -47,6 +47,7 @@
@import "vendor/bootstrap/pagination";
@import "vendor/bootstrap/table";
@import "vendor/chosen";
+@import "vendor/choicesjs";
@import "vendor/dragula";
@import "vendor/minicolors";
@import "vendor/tinymce";
diff --git a/administrator/templates/atum/scss/vendor/_choicesjs.scss b/administrator/templates/atum/scss/vendor/_choicesjs.scss
new file mode 100644
index 0000000000000..4a6138a566d26
--- /dev/null
+++ b/administrator/templates/atum/scss/vendor/_choicesjs.scss
@@ -0,0 +1,82 @@
+// choices.js
+
+// Fix position
+.choices__list--dropdown {
+ z-index: 10;
+}
+
+// Fix close button
+.choices__button_joomla {
+ position: relative;
+ text-indent: -9999px;
+ cursor: pointer;
+ background: none;
+ border: 0;
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ appearance: none;
+
+ &::before {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ text-align: center;
+ text-indent: 0;
+ content: "\00d7";
+ }
+
+ &:focus {
+ outline: none;
+ }
+}
+
+.choices[data-type*="select-one"] {
+ .choices__button_joomla {
+ position: absolute;
+ top: 50%;
+ right: 0;
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ margin-top: -10px;
+ margin-right: 25px;
+ border-radius: 10em;
+ opacity: .5;
+ &:hover, &:focus { opacity: 1; }
+ &:focus { box-shadow: 0 0 0 2px #00bcd4; }
+ }
+
+ &[dir="rtl"] {
+ .choices__button_joomla {
+ right: auto;
+ left: 0;
+ margin-right: 0;
+ margin-left: 25px;
+ }
+ }
+}
+
+.choices[data-type*="select-multiple"],
+.choices[data-type*="text"] {
+ .choices__button_joomla {
+ position: relative;
+ display: inline-block;
+ width: 8px;
+ padding-left: 16px;
+ margin-top: 0;
+ margin-right: -4px;
+ margin-bottom: 0;
+ margin-left: 8px;
+ line-height: 1;
+ border-left: 1px solid #008fa1;
+ opacity: .75;
+ &:hover, &:focus { opacity: 1; }
+ &::before {
+ color: #fff;
+ }
+ }
+}
+
diff --git a/build/build-modules-js/settings.json b/build/build-modules-js/settings.json
index ab0c719c7581d..bd6d67ad46708 100644
--- a/build/build-modules-js/settings.json
+++ b/build/build-modules-js/settings.json
@@ -14,6 +14,7 @@
"field-simple-color",
"field-send-test-mail",
"field-subform",
+ "field-fancy-select",
"editor-codemirror",
"hidden-mail",
"editor-none",
@@ -55,6 +56,9 @@
"field-subform": {
"css": "media/system/webcomponents/css",
"js": "media/system/webcomponents/js"
+ },
+ "field-fancy-select": {
+ "js": "media/system/webcomponents/js"
}
},
"vendors": {
@@ -151,6 +155,26 @@
"dependencies": [],
"licenseFilename": "LICENSE"
},
+ "choices.js": {
+ "name": "choicesjs",
+ "js": {
+ "assets/scripts/dist/choices.js": "js/choices.js",
+ "assets/scripts/dist/choices.min.js": "js/choices.min.js"
+ },
+ "css": {
+ "assets/styles/css/choices.css": "css/choices.css",
+ "assets/styles/css/choices.min.css": "css/choices.min.css"
+ },
+ "provideAssets": [
+ {
+ "name": null,
+ "js": ["choices.min.js"],
+ "css": ["choices.min.css"]
+ }
+ ],
+ "dependencies": [],
+ "licenseFilename": "LICENSE"
+ },
"diff": {
"name": "diff",
"js": {
diff --git a/build/media/webcomponents/js/field-fancy-select/field-fancy-select.js b/build/media/webcomponents/js/field-fancy-select/field-fancy-select.js
new file mode 100644
index 0000000000000..e35a5ad3e33c5
--- /dev/null
+++ b/build/media/webcomponents/js/field-fancy-select/field-fancy-select.js
@@ -0,0 +1,180 @@
+/**
+ * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+/**
+ * Fancy select field, which use Choices.js
+ *
+ * Example:
+ *
+ *
+ *
+ *
+ * Possible attributes:
+ *
+ * allow-custom Whether allow User to dynamically add a new value.
+ * new-item-prefix="" Prefix for a dynamically added value.
+ *
+ * remote-search Enable remote search.
+ * url="" Url for remote search.
+ * term-key="term" Variable key name for searched term, will be appended to Url.
+ *
+ * min-term-length="1" The minimum length a search value should be before choices are searched.
+ * placeholder="" The value of the inputs placeholder.
+ * search-placeholder="" The value of the search inputs placeholder.
+ */
+;(function(customElements){
+ "use strict";
+
+ class JoomlaFieldFancySelect extends HTMLElement {
+
+ // Properties getters/setters
+ get allowCustom() { return this.hasAttribute('allow-custom'); }
+ get remoteSearch() { return this.hasAttribute('remote-search'); }
+ get url() { return this.getAttribute('url'); }
+ get termKey() { return this.getAttribute('term-key') || 'term'; }
+ get minTermLength() { return parseInt(this.getAttribute('min-term-length')) || 1; }
+ get newItemPrefix() { return this.getAttribute('new-item-prefix') || ''; }
+ get placeholder() { return this.getAttribute('placeholder'); }
+ get searchPlaceholder() { return this.getAttribute('search-placeholder'); }
+ get value() { return this.choicesInstance.getValue(true); }
+ set value($val) { this.choicesInstance.setValueByChoice('' + $val); }
+
+ connectedCallback() {
+ if (!window.Choices) {
+ throw new Error('JoomlaFieldFancySelect require Choices.js to work');
+ }
+
+ // Get a
- loadTemplate('positions'); ?>
+ form->getInput('position'); ?>
diff --git a/components/com_config/tmpl/modules/default_positions.php b/components/com_config/tmpl/modules/default_positions.php
deleted file mode 100644
index c331344b9633e..0000000000000
--- a/components/com_config/tmpl/modules/default_positions.php
+++ /dev/null
@@ -1,30 +0,0 @@
- 0));
-
-// Add custom position to options
-$customGroupText = Text::_('COM_MODULES_CUSTOM_POSITION');
-
-// Build field
-$attr = array(
- 'id' => 'jform_position',
- 'list.select' => $this->item['position'],
- 'list.attr' => 'class="chosen-custom-value custom-select" '
- . 'data-custom_group_text="' . $customGroupText . '" '
- . 'data-no_results_text="' . Text::_('COM_MODULES_ADD_CUSTOM_POSITION') . '" '
- . 'data-placeholder="' . Text::_('COM_MODULES_TYPE_OR_SELECT_POSITION') . '" '
-);
-
-echo HTMLHelper::_('select.groupedlist', $this->positions, 'jform[position]', $attr);
diff --git a/components/com_content/tmpl/form/edit.php b/components/com_content/tmpl/form/edit.php
index 1e9e3e46bb2a2..c54e69e1240a7 100644
--- a/components/com_content/tmpl/form/edit.php
+++ b/components/com_content/tmpl/form/edit.php
@@ -18,7 +18,6 @@
HTMLHelper::_('behavior.tabstate');
HTMLHelper::_('behavior.keepalive');
HTMLHelper::_('behavior.formvalidator');
-HTMLHelper::_('formbehavior.chosen', '#jform_catid', null, array('disable_search_threshold' => 0));
HTMLHelper::_('script', 'com_content/form-edit.js', ['version' => 'auto', 'relative' => true]);
diff --git a/language/en-GB/en-GB.ini b/language/en-GB/en-GB.ini
index 5cd911f6aa48b..e2c8dbd31d079 100644
--- a/language/en-GB/en-GB.ini
+++ b/language/en-GB/en-GB.ini
@@ -276,6 +276,7 @@ JGLOBAL_SECRETKEY="Secret Key"
JGLOBAL_SECRETKEY_HELP="If you have enabled two factor authentication in your user account please enter your secret key. If you do not know what this means, you can leave this field blank."
JGLOBAL_SELECT_AN_OPTION="Select an option"
JGLOBAL_SELECT_NO_RESULTS_MATCH="No results match"
+JGLOBAL_SELECT_PRESS_TO_SELECT="Press to select"
JGLOBAL_SELECT_SOME_OPTIONS="Select some options"
JGLOBAL_START_PUBLISH_AFTER_FINISH="Item start publishing date must be before finish publishing date"
JGLOBAL_SUBCATEGORIES="Subcategories"
diff --git a/layouts/joomla/form/field/accesslevel-fancy-select.php b/layouts/joomla/form/field/accesslevel-fancy-select.php
new file mode 100644
index 0000000000000..554856737d827
--- /dev/null
+++ b/layouts/joomla/form/field/accesslevel-fancy-select.php
@@ -0,0 +1,79 @@
+ section in form XML.
+ * @var boolean $hidden Is this field hidden in the form?
+ * @var string $hint Placeholder for the field.
+ * @var string $id DOM id of the field.
+ * @var string $label Label of the field.
+ * @var string $labelclass Classes to apply to the label.
+ * @var boolean $multiple Does this field support multiple values?
+ * @var string $name Name of the input field.
+ * @var string $onchange Onchange attribute for the field.
+ * @var string $onclick Onclick attribute for the field.
+ * @var string $pattern Pattern (Reg Ex) of value of the form field.
+ * @var boolean $readonly Is this field read only?
+ * @var boolean $repeat Allows extensions to duplicate elements.
+ * @var boolean $required Is this field required?
+ * @var integer $size Size attribute of the input.
+ * @var boolean $spellcheck Spellcheck state for the form field.
+ * @var string $validate Validation rules to apply.
+ * @var string $value Value attribute of the field.
+ * @var array $checkedOptions Options that will be set as checked.
+ * @var boolean $hasValue Has this field a value assigned?
+ * @var array $options Options available for this field.
+ * @var array $inputType Options available for this field.
+ */
+
+$attr = '';
+
+// Initialize some field attributes.
+$attr .= $disabled ? ' disabled' : '';
+$attr .= !empty($size) ? ' size="' . $size . '"' : '';
+$attr .= $multiple ? ' multiple' : '';
+$attr .= $autofocus ? ' autofocus' : '';
+$attr .= $onchange ? ' onchange="' . $onchange . '"' : '';
+
+$attr2 = '';
+$attr2 .= !empty($class) ? ' class="' . $class . '"' : '';
+$attr2 .= ' placeholder="' . $this->escape($hint ?: Text::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS')) . '" ';
+
+if ($required)
+{
+ $attr .= ' required class="required"';
+ $attr2 .= ' required';
+}
+
+Text::script('JGLOBAL_SELECT_NO_RESULTS_MATCH');
+Text::script('JGLOBAL_SELECT_PRESS_TO_SELECT');
+
+Factory::getDocument()->getWebAssetManager()->enableAsset('choicesjs');
+HTMLHelper::_('webcomponent', 'system/webcomponents/joomla-field-fancy-select.min.js', ['version' => 'auto', 'relative' => true]);
+
+?>
+
+>
diff --git a/layouts/joomla/form/field/accesslevel.php b/layouts/joomla/form/field/accesslevel.php
new file mode 100644
index 0000000000000..cdf2eda51cf1a
--- /dev/null
+++ b/layouts/joomla/form/field/accesslevel.php
@@ -0,0 +1,59 @@
+ section in form XML.
+ * @var boolean $hidden Is this field hidden in the form?
+ * @var string $hint Placeholder for the field.
+ * @var string $id DOM id of the field.
+ * @var string $label Label of the field.
+ * @var string $labelclass Classes to apply to the label.
+ * @var boolean $multiple Does this field support multiple values?
+ * @var string $name Name of the input field.
+ * @var string $onchange Onchange attribute for the field.
+ * @var string $onclick Onclick attribute for the field.
+ * @var string $pattern Pattern (Reg Ex) of value of the form field.
+ * @var boolean $readonly Is this field read only?
+ * @var boolean $repeat Allows extensions to duplicate elements.
+ * @var boolean $required Is this field required?
+ * @var integer $size Size attribute of the input.
+ * @var boolean $spellcheck Spellcheck state for the form field.
+ * @var string $validate Validation rules to apply.
+ * @var string $value Value attribute of the field.
+ * @var array $checkedOptions Options that will be set as checked.
+ * @var boolean $hasValue Has this field a value assigned?
+ * @var array $options Options available for this field.
+ * @var array $inputType Options available for this field.
+ */
+
+$attr = '';
+
+// Initialize some field attributes.
+$attr .= !empty($class) ? ' class="custom-select ' . $class . '"' : ' class="custom-select"';
+$attr .= $disabled ? ' disabled' : '';
+$attr .= !empty($size) ? ' size="' . $size . '"' : '';
+$attr .= $multiple ? ' multiple' : '';
+$attr .= $required ? ' required' : '';
+$attr .= $autofocus ? ' autofocus' : '';
+$attr .= $onchange ? ' onchange="' . $onchange . '"' : '';
+
+echo HTMLHelper::_('access.level', $name, $value, $attr, $options, $id);
diff --git a/layouts/joomla/form/field/list-fancy-select.php b/layouts/joomla/form/field/list-fancy-select.php
new file mode 100644
index 0000000000000..794c951f1bc63
--- /dev/null
+++ b/layouts/joomla/form/field/list-fancy-select.php
@@ -0,0 +1,112 @@
+ section in form XML.
+ * @var boolean $hidden Is this field hidden in the form?
+ * @var string $hint Placeholder for the field.
+ * @var string $id DOM id of the field.
+ * @var string $label Label of the field.
+ * @var string $labelclass Classes to apply to the label.
+ * @var boolean $multiple Does this field support multiple values?
+ * @var string $name Name of the input field.
+ * @var string $onchange Onchange attribute for the field.
+ * @var string $onclick Onclick attribute for the field.
+ * @var string $pattern Pattern (Reg Ex) of value of the form field.
+ * @var boolean $readonly Is this field read only?
+ * @var boolean $repeat Allows extensions to duplicate elements.
+ * @var boolean $required Is this field required?
+ * @var integer $size Size attribute of the input.
+ * @var boolean $spellcheck Spellcheck state for the form field.
+ * @var string $validate Validation rules to apply.
+ * @var string $value Value attribute of the field.
+ * @var array $checkedOptions Options that will be set as checked.
+ * @var boolean $hasValue Has this field a value assigned?
+ * @var array $options Options available for this field.
+ * @var array $inputType Options available for this field.
+ */
+
+$html = array();
+$attr = '';
+
+// Initialize the field attributes.
+$attr .= !empty($size) ? ' size="' . $size . '"' : '';
+$attr .= $multiple ? ' multiple' : '';
+$attr .= $autofocus ? ' autofocus' : '';
+$attr .= $onchange ? ' onchange="' . $onchange . '"' : '';
+
+// To avoid user's confusion, readonly="readonly" should imply disabled="disabled".
+if ($readonly || $disabled)
+{
+ $attr .= ' disabled="disabled"';
+}
+
+$attr2 = '';
+$attr2 .= !empty($class) ? ' class="' . $class . '"' : '';
+$attr2 .= ' placeholder="' . $this->escape($hint ?: Text::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS')) . '" ';
+
+if ($required)
+{
+ $attr .= ' required class="required"';
+ $attr2 .= ' required';
+}
+
+// Create a read-only list (no name) with hidden input(s) to store the value(s).
+if ($readonly)
+{
+ $html[] = HTMLHelper::_('select.genericlist', $options, '', trim($attr), 'value', 'text', $value, $id);
+
+ // E.g. form field type tag sends $this->value as array
+ if ($multiple && is_array($value))
+ {
+ if (!count($value))
+ {
+ $value[] = '';
+ }
+
+ foreach ($value as $val)
+ {
+ $html[] = '';
+ }
+ }
+ else
+ {
+ $html[] = '';
+ }
+}
+else
+// Create a regular list.
+{
+ $html[] = HTMLHelper::_('select.genericlist', $options, $name, trim($attr), 'value', 'text', $value, $id);
+}
+
+Text::script('JGLOBAL_SELECT_NO_RESULTS_MATCH');
+Text::script('JGLOBAL_SELECT_PRESS_TO_SELECT');
+
+Factory::getDocument()->getWebAssetManager()->enableAsset('choicesjs');
+HTMLHelper::_('webcomponent', 'system/webcomponents/joomla-field-fancy-select.min.js', ['version' => 'auto', 'relative' => true]);
+
+?>
+
+>
diff --git a/layouts/joomla/form/field/list.php b/layouts/joomla/form/field/list.php
new file mode 100644
index 0000000000000..a23785d3f065d
--- /dev/null
+++ b/layouts/joomla/form/field/list.php
@@ -0,0 +1,94 @@
+ section in form XML.
+ * @var boolean $hidden Is this field hidden in the form?
+ * @var string $hint Placeholder for the field.
+ * @var string $id DOM id of the field.
+ * @var string $label Label of the field.
+ * @var string $labelclass Classes to apply to the label.
+ * @var boolean $multiple Does this field support multiple values?
+ * @var string $name Name of the input field.
+ * @var string $onchange Onchange attribute for the field.
+ * @var string $onclick Onclick attribute for the field.
+ * @var string $pattern Pattern (Reg Ex) of value of the form field.
+ * @var boolean $readonly Is this field read only?
+ * @var boolean $repeat Allows extensions to duplicate elements.
+ * @var boolean $required Is this field required?
+ * @var integer $size Size attribute of the input.
+ * @var boolean $spellcheck Spellcheck state for the form field.
+ * @var string $validate Validation rules to apply.
+ * @var string $value Value attribute of the field.
+ * @var array $checkedOptions Options that will be set as checked.
+ * @var boolean $hasValue Has this field a value assigned?
+ * @var array $options Options available for this field.
+ * @var array $inputType Options available for this field.
+ */
+
+$html = array();
+$attr = '';
+
+// Initialize the field attributes.
+$attr .= !empty($class) ? ' class="custom-select ' . $class . '"' : ' class="custom-select"';
+$attr .= !empty($size) ? ' size="' . $size . '"' : '';
+$attr .= $multiple ? ' multiple' : '';
+$attr .= $required ? ' required' : '';
+$attr .= $autofocus ? ' autofocus' : '';
+$attr .= $onchange ? ' onchange="' . $onchange . '"' : '';
+
+// To avoid user's confusion, readonly="readonly" should imply disabled="disabled".
+if ($readonly || $disabled)
+{
+ $attr .= ' disabled="disabled"';
+}
+
+// Create a read-only list (no name) with hidden input(s) to store the value(s).
+if ($readonly)
+{
+ $html[] = HTMLHelper::_('select.genericlist', $options, '', trim($attr), 'value', 'text', $value, $id);
+
+ // E.g. form field type tag sends $this->value as array
+ if ($multiple && is_array($value))
+ {
+ if (!count($value))
+ {
+ $value[] = '';
+ }
+
+ foreach ($value as $val)
+ {
+ $html[] = '';
+ }
+ }
+ else
+ {
+ $html[] = '';
+ }
+}
+else
+// Create a regular list.
+{
+ $html[] = HTMLHelper::_('select.genericlist', $options, $name, trim($attr), 'value', 'text', $value, $id);
+}
+
+echo implode($html);
diff --git a/layouts/joomla/form/field/tag.php b/layouts/joomla/form/field/tag.php
new file mode 100644
index 0000000000000..1f26b4b9e0ab2
--- /dev/null
+++ b/layouts/joomla/form/field/tag.php
@@ -0,0 +1,129 @@
+ section in form XML.
+ * @var boolean $hidden Is this field hidden in the form?
+ * @var string $hint Placeholder for the field.
+ * @var string $id DOM id of the field.
+ * @var string $label Label of the field.
+ * @var string $labelclass Classes to apply to the label.
+ * @var boolean $multiple Does this field support multiple values?
+ * @var string $name Name of the input field.
+ * @var string $onchange Onchange attribute for the field.
+ * @var string $onclick Onclick attribute for the field.
+ * @var string $pattern Pattern (Reg Ex) of value of the form field.
+ * @var boolean $readonly Is this field read only?
+ * @var boolean $repeat Allows extensions to duplicate elements.
+ * @var boolean $required Is this field required?
+ * @var integer $size Size attribute of the input.
+ * @var boolean $spellcheck Spellcheck state for the form field.
+ * @var string $validate Validation rules to apply.
+ * @var string $value Value attribute of the field.
+ * @var array $checkedOptions Options that will be set as checked.
+ * @var boolean $hasValue Has this field a value assigned?
+ * @var array $options Options available for this field.
+ * @var array $inputType Options available for this field.
+ * @var boolean $allowCustom Flag, to allow add custom values
+ * @var boolean $remoteSearch Flag, to enable remote search
+ * @var integer $minTermLength Minimum length of the term to start searching
+ */
+
+$html = array();
+$attr = '';
+
+// Initialize some field attributes.
+$attr .= $multiple ? ' multiple' : '';
+$attr .= $autofocus ? ' autofocus' : '';
+$attr .= $onchange ? ' onchange="' . $onchange . '"' : '';
+
+// To avoid user's confusion, readonly="readonly" should imply disabled="disabled".
+if ($readonly || $disabled)
+{
+ $attr .= ' disabled="disabled"';
+}
+
+$attr2 = '';
+$attr2 .= !empty($class) ? ' class="' . $class . '"' : '';
+$attr2 .= ' placeholder="' . $this->escape($hint ?: Text::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS')) . '" ';
+
+if ($allowCustom)
+{
+ $attr2 .= $allowCustom ? ' allow-custom' : '';
+ $attr2 .= $allowCustom ? ' new-item-prefix="#new#"' : '';
+}
+
+if ($remoteSearch)
+{
+ $attr2 .= ' remote-search';
+ $attr2 .= ' url="' . Uri::root(true) . '/index.php?option=com_tags&task=tags.searchAjax"';
+ $attr2 .= ' term-key="like"';
+ $attr2 .= ' min-term-length="' . $minTermLength .'"';
+}
+
+if ($required)
+{
+ $attr .= ' required class="required"';
+ $attr2 .= ' required';
+}
+
+// Create a read-only list (no name) with hidden input(s) to store the value(s).
+if ($readonly)
+{
+ $html[] = HTMLHelper::_('select.genericlist', $options, '', trim($attr), 'value', 'text', $value, $id);
+
+ // E.g. form field type tag sends $this->value as array
+ if ($multiple && is_array($value))
+ {
+ if (!count($value))
+ {
+ $value[] = '';
+ }
+
+ foreach ($value as $val)
+ {
+ $html[] = '';
+ }
+ }
+ else
+ {
+ $html[] = '';
+ }
+}
+else
+// Create a regular list.
+{
+ $html[] = HTMLHelper::_('select.genericlist', $options, $name, trim($attr), 'value', 'text', $value, $id);
+}
+
+Text::script('JGLOBAL_SELECT_NO_RESULTS_MATCH');
+Text::script('JGLOBAL_SELECT_PRESS_TO_SELECT');
+
+Factory::getDocument()->getWebAssetManager()->enableAsset('choicesjs');
+HTMLHelper::_('webcomponent', 'system/webcomponents/joomla-field-fancy-select.min.js', ['version' => 'auto', 'relative' => true]);
+
+?>
+
+>
diff --git a/libraries/cms/html/formbehavior.php b/libraries/cms/html/formbehavior.php
index 31be8b6be885e..9de64ef8e527b 100644
--- a/libraries/cms/html/formbehavior.php
+++ b/libraries/cms/html/formbehavior.php
@@ -17,7 +17,9 @@
/**
* Utility class for form related behaviors
*
- * @since 3.0
+ * @since 3.0
+ *
+ * @deprecated 5.0 Without replacement
*/
abstract class JHtmlFormbehavior
{
diff --git a/libraries/cms/html/tag.php b/libraries/cms/html/tag.php
index 9559664f55c5e..8d8df40a929b0 100644
--- a/libraries/cms/html/tag.php
+++ b/libraries/cms/html/tag.php
@@ -165,6 +165,8 @@ public static function tags($config = array('filter.published' => array(0, 1)))
* @return void
*
* @since 3.1
+ *
+ * @deprecated 5.0 Without replacement
*/
public static function ajaxfield($selector = '#jform_tags', $allowCustom = true)
{
diff --git a/libraries/src/Form/Field/AccesslevelField.php b/libraries/src/Form/Field/AccesslevelField.php
index 8c4b8e853ce66..d505a22d93966 100644
--- a/libraries/src/Form/Field/AccesslevelField.php
+++ b/libraries/src/Form/Field/AccesslevelField.php
@@ -10,11 +10,6 @@
defined('JPATH_PLATFORM') or die;
-use Joomla\CMS\Form\FormHelper;
-use Joomla\CMS\HTML\HTMLHelper;
-
-FormHelper::loadFieldClass('list');
-
/**
* Form Field class for the Joomla Platform.
* Provides a list of access levels. Access levels control what users in specific
@@ -23,7 +18,7 @@
* @see JAccess
* @since 1.7.0
*/
-class AccesslevelField extends \JFormFieldList
+class AccesslevelField extends ListField
{
/**
* The form field type.
@@ -34,30 +29,10 @@ class AccesslevelField extends \JFormFieldList
protected $type = 'Accesslevel';
/**
- * Method to get the field input markup.
+ * Name of the layout being used to render the field
*
- * @return string The field input markup.
- *
- * @since 1.7.0
+ * @var string
+ * @since __DEPLOY_VERSION__
*/
- protected function getInput()
- {
- $attr = '';
-
- // Initialize some field attributes.
- $attr .= !empty($this->class) ? ' class="custom-select ' . $this->class . '"' : ' class="custom-select"';
- $attr .= $this->disabled ? ' disabled' : '';
- $attr .= !empty($this->size) ? ' size="' . $this->size . '"' : '';
- $attr .= $this->multiple ? ' multiple' : '';
- $attr .= $this->required ? ' required' : '';
- $attr .= $this->autofocus ? ' autofocus' : '';
-
- // Initialize JavaScript field attributes.
- $attr .= $this->onchange ? ' onchange="' . $this->onchange . '"' : '';
-
- // Get the field options.
- $options = $this->getOptions();
-
- return HTMLHelper::_('access.level', $this->name, $this->value, $attr, $options, $this->id);
- }
+ protected $layout = 'joomla.form.field.accesslevel';
}
diff --git a/libraries/src/Form/Field/ListField.php b/libraries/src/Form/Field/ListField.php
index 6d0e198221b89..c08b7c3d285c1 100644
--- a/libraries/src/Form/Field/ListField.php
+++ b/libraries/src/Form/Field/ListField.php
@@ -14,7 +14,6 @@
use Joomla\CMS\Factory;
use Joomla\CMS\Form\FormField;
use Joomla\CMS\Helper\ModuleHelper;
-use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
@@ -37,6 +36,14 @@ class ListField extends FormField
*/
protected $type = 'List';
+ /**
+ * Name of the layout being used to render the field
+ *
+ * @var string
+ * @since __DEPLOY_VERSION__
+ */
+ protected $layout = 'joomla.form.field.list';
+
/**
* Method to get the field input markup for a generic list.
* Use the multiple attribute to enable multiselect.
@@ -47,61 +54,11 @@ class ListField extends FormField
*/
protected function getInput()
{
- $html = array();
- $attr = '';
-
- // Initialize some field attributes.
- $attr .= !empty($this->class) ? ' class="custom-select ' . $this->class . '"' : ' class="custom-select"';
- $attr .= !empty($this->size) ? ' size="' . $this->size . '"' : '';
- $attr .= $this->multiple ? ' multiple' : '';
- $attr .= $this->required ? ' required' : '';
- $attr .= $this->autofocus ? ' autofocus' : '';
-
- // To avoid user's confusion, readonly="true" should imply disabled="true".
- if ((string) $this->readonly == '1'
- || (string) $this->readonly == 'true'
- || (string) $this->disabled == '1'
- || (string) $this->disabled == 'true')
- {
- $attr .= ' disabled="disabled"';
- }
-
- // Initialize JavaScript field attributes.
- $attr .= $this->onchange ? ' onchange="' . $this->onchange . '"' : '';
-
- // Get the field options.
- $options = (array) $this->getOptions();
+ $data = $this->getLayoutData();
- // Create a read-only list (no name) with hidden input(s) to store the value(s).
- if ((string) $this->readonly == '1' || (string) $this->readonly == 'true')
- {
- $html[] = HTMLHelper::_('select.genericlist', $options, '', trim($attr), 'value', 'text', $this->value, $this->id);
-
- // E.g. form field type tag sends $this->value as array
- if ($this->multiple && is_array($this->value))
- {
- if (!count($this->value))
- {
- $this->value[] = '';
- }
-
- foreach ($this->value as $value)
- {
- $html[] = '';
- }
- }
- else
- {
- $html[] = '';
- }
- }
- else
- // Create a regular list.
- {
- $html[] = HTMLHelper::_('select.genericlist', $options, $this->name, trim($attr), 'value', 'text', $this->value, $this->id);
- }
+ $data['options'] = (array) $this->getOptions();
- return implode($html);
+ return $this->getRenderer($this->layout)->render($data);
}
/**
diff --git a/libraries/src/Form/Field/TagField.php b/libraries/src/Form/Field/TagField.php
index 0fd4d0d1500bc..e7b2496f240a1 100644
--- a/libraries/src/Form/Field/TagField.php
+++ b/libraries/src/Form/Field/TagField.php
@@ -12,20 +12,16 @@
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
-use Joomla\CMS\Form\FormHelper;
use Joomla\CMS\Helper\TagsHelper;
-use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\Utilities\ArrayHelper;
-FormHelper::loadFieldClass('list');
-
/**
* List of Tags field.
*
* @since 3.1
*/
-class TagField extends \JFormFieldList
+class TagField extends ListField
{
/**
* A flexible tag list that respects access controls
@@ -51,6 +47,14 @@ class TagField extends \JFormFieldList
*/
protected $comParams = null;
+ /**
+ * Name of the layout being used to render the field
+ *
+ * @var string
+ * @since __DEPLOY_VERSION__
+ */
+ protected $layout = 'joomla.form.field.tag';
+
/**
* Constructor
*
@@ -73,16 +77,7 @@ public function __construct()
*/
protected function getInput()
{
- // AJAX mode requires ajax-chosen
- if (!$this->isNested())
- {
- // Get the field id
- $id = $this->element['id'] ?? null;
- $cssId = '#' . $this->getId($id, $this->element['name']);
-
- // Load the ajax-chosen customised field
- HTMLHelper::_('tag.ajaxfield', $cssId, $this->allowCustom());
- }
+ $data = $this->getLayoutData();
if (!is_array($this->value) && !empty($this->value))
{
@@ -103,9 +98,17 @@ protected function getInput()
{
$this->value = explode(',', $this->value);
}
+
+ $data['value'] = $this->value;
}
- return parent::getInput();
+ $data['remoteSearch'] = $this->isRemoteSearch();
+ $data['options'] = $this->getOptions();
+ $data['isNested'] = $this->isNested();
+ $data['allowCustom'] = $this->allowCustom();
+ $data['minTermLength'] = (int) $this->comParams->get('min_term_length', 3);
+
+ return $this->getRenderer($this->layout)->render($data);
}
/**
@@ -121,6 +124,12 @@ protected function getOptions()
$app = Factory::getApplication();
$tag = $app->getLanguage()->getTag();
+ // Return only basic options, everything else will be searched via AJAX
+ if ($this->isRemoteSearch() && !$this->value)
+ {
+ return parent::getOptions();
+ }
+
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('DISTINCT a.id AS value, a.path, a.title AS text, a.level, a.published, a.lft')
@@ -154,6 +163,12 @@ protected function getOptions()
$query->where($db->quoteName('a.lft') . ' > 0');
+ // Preload only active values, everything else will be searched via AJAX
+ if ($this->isRemoteSearch() && $this->value)
+ {
+ $query->where('a.id IN (' . implode(',', $this->value) . ')');
+ }
+
// Filter on the published state
if (is_numeric($published))
{
@@ -261,11 +276,28 @@ public function isNested()
*/
public function allowCustom()
{
- if (isset($this->element['custom']) && (string) $this->element['custom'] === 'deny')
+ if ($this->element['custom'] && in_array((string) $this->element['custom'], array('0', 'false', 'deny')))
{
return false;
}
- return true;
+ return Factory::getUser()->authorise('core.create', 'com_tags');
+ }
+
+ /**
+ * Check whether need to enable AJAX search
+ *
+ * @return boolean
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function isRemoteSearch()
+ {
+ if ($this->element['remote-search'])
+ {
+ return !in_array((string) $this->element['remote-search'], array('0', 'false', ''));
+ }
+
+ return $this->comParams->get('tag_field_ajax_mode', 1) == 1;
}
}
diff --git a/modules/mod_languages/tmpl/default.php b/modules/mod_languages/tmpl/default.php
index 5b4fbb038ea4e..269244c56acf4 100644
--- a/modules/mod_languages/tmpl/default.php
+++ b/modules/mod_languages/tmpl/default.php
@@ -15,10 +15,6 @@
HTMLHelper::_('stylesheet', 'mod_languages/template.css', array('version' => 'auto', 'relative' => true));
-if ($params->get('dropdown', 1) && !$params->get('dropdownimage', 0))
-{
- HTMLHelper::_('formbehavior.chosen');
-}
?>
diff --git a/package.json b/package.json
index 489017513a79c..9ffb39e9ad66e 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"chosen-js": "1.6.2",
"codemirror": "5.35.0",
"cropperjs": "1.2.2",
+ "choices.js": "^3.0.4",
"css-vars-ponyfill": "^1.9.0",
"diff": "3.4.0",
"dragula": "3.7.2",
diff --git a/templates/cassiopeia/scss/template.scss b/templates/cassiopeia/scss/template.scss
index 4141eb5a7e9ba..ab4f3635955f2 100644
--- a/templates/cassiopeia/scss/template.scss
+++ b/templates/cassiopeia/scss/template.scss
@@ -51,6 +51,7 @@
@import "vendor/bootstrap/pagination";
@import "vendor/bootstrap/table";
@import "vendor/chosen";
+@import "vendor/choicesjs";
@import "vendor/dragula";
@import "vendor/minicolors";
@import "vendor/tinymce";
diff --git a/templates/cassiopeia/scss/vendor/_choicesjs.scss b/templates/cassiopeia/scss/vendor/_choicesjs.scss
new file mode 100644
index 0000000000000..4a6138a566d26
--- /dev/null
+++ b/templates/cassiopeia/scss/vendor/_choicesjs.scss
@@ -0,0 +1,82 @@
+// choices.js
+
+// Fix position
+.choices__list--dropdown {
+ z-index: 10;
+}
+
+// Fix close button
+.choices__button_joomla {
+ position: relative;
+ text-indent: -9999px;
+ cursor: pointer;
+ background: none;
+ border: 0;
+ -moz-appearance: none;
+ -webkit-appearance: none;
+ appearance: none;
+
+ &::before {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ text-align: center;
+ text-indent: 0;
+ content: "\00d7";
+ }
+
+ &:focus {
+ outline: none;
+ }
+}
+
+.choices[data-type*="select-one"] {
+ .choices__button_joomla {
+ position: absolute;
+ top: 50%;
+ right: 0;
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ margin-top: -10px;
+ margin-right: 25px;
+ border-radius: 10em;
+ opacity: .5;
+ &:hover, &:focus { opacity: 1; }
+ &:focus { box-shadow: 0 0 0 2px #00bcd4; }
+ }
+
+ &[dir="rtl"] {
+ .choices__button_joomla {
+ right: auto;
+ left: 0;
+ margin-right: 0;
+ margin-left: 25px;
+ }
+ }
+}
+
+.choices[data-type*="select-multiple"],
+.choices[data-type*="text"] {
+ .choices__button_joomla {
+ position: relative;
+ display: inline-block;
+ width: 8px;
+ padding-left: 16px;
+ margin-top: 0;
+ margin-right: -4px;
+ margin-bottom: 0;
+ margin-left: 8px;
+ line-height: 1;
+ border-left: 1px solid #008fa1;
+ opacity: .75;
+ &:hover, &:focus { opacity: 1; }
+ &::before {
+ color: #fff;
+ }
+ }
+}
+