custom select to replace select-2
Some checks failed
Build and Push Docker Image / build (push) Failing after 7m5s

it's called 'corbz-select' because I suck at naming things
This commit is contained in:
Corban-Lee Jones 2024-10-27 00:13:26 +01:00
parent 198d70d072
commit 5da1012718
4 changed files with 305 additions and 5 deletions

View File

@ -264,6 +264,12 @@ async function loadSubModalOptions($input, url) {
// Re-enable input
$input.prop("disabled", false);
// Re-init the component
if ($input.next('.corbz-select-container').length) {
$input.next('.corbz-select-container').remove();
$input.initCorbzSelect();
}
}
// Channel options aren't loaded from an API, like other options.
@ -288,6 +294,12 @@ async function loadChannelOptions() {
})
$input.prop("disabled", false);
// Re-init the component
if ($input.next('.corbz-select-container').length) {
$input.next('.corbz-select-container').remove();
$input.initCorbzSelect();
}
}

View File

@ -35,3 +35,125 @@
&:disabled, &.disabled { color: var(--bs-secondary-color) }
}
// Custom Select Component
.corbz-select {
}
.corbz-select-container {
position: relative;
display: block;
appearance: none;
width: 100%;
font-size: 1rem;
line-height: 1.5;
font-weight: 400;
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
background-clip: padding-box;
border: var(--bs-border-width) solid var(--bs-border-color);
border-radius: var(--bs-border-radius-sm);
&.active {
border-radius: var(--bs-border-radius-sm) var(--bs-border-radius-sm) 0 0;
}
.corbz-select-selected {
width: 100%;
max-width: 100%;
text-wrap: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 100%;
padding: 0.375rem 0.75rem;
cursor: pointer;
}
.corbz-select-dropdown {
display: none;
position: absolute;
top: 100%;
left: 0;
width: calc(100% + 2.25px);
transform: translateX(-1px);
background-color: var(--bs-body-bg);
border: 1px solid var(--bs-border-color);
border-radius: 0 0 var(--bs-border-radius-sm) var(--bs-border-radius-sm);
box-shadow: var(--bs-box-shadow);
max-height: 200px;
overflow-y: auto;
z-index: 1000;
.corbz-select-search {
// display: none; // disabled for now
width: 100%;
padding: 0.375rem 0.75rem;
background: none;
border: none;
border-bottom: 1px solid var(--bs-border-color);
outline: none;
}
.corbz-select-option {
padding: 0.375rem 0.75rem;
cursor: pointer;
&:hover { background-color: var(--bs-tertiary-bg); }
&.active {
color: var(--bs-white);
background-color: var(--bs-primary);
}
// Multi-select versions
> .corbz-option-checkbox {
--bs-form-check-bg-image: none;
width: 1rem;
height: 1rem;
appearance: none;
background-color: var(--bs-body-bg);
background-image: var(--bs-form-check-bg-image);
background-repeat: no-repeat;
background-position: center;
background-size: contain;
border: var(--bs-border-width) solid var(--bs-border-color);
border-radius: var(--bs-border-radius-sm);
vertical-align: middle;
&:checked {
--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
background-color: var(--bs-primary);
border-color: var(--bs-primary);
}
}
> span {
display: inline-block;
vertical-align: middle;
margin-left: 0.75rem;
}
}
}
}

View File

@ -29,7 +29,7 @@
<div class="col-lg-6 pe-lg-4">
<div class="mb-4">
<label for="subMessageStyle" class="form-label">Message Style</label>
<select name="subMessageStyle" id="subMessageStyle" class="select-2" data-field="message_style" data-default="firstOption" tabindex="3">
<select name="subMessageStyle" id="subMessageStyle" class="corbz-select" data-field="message_style" data-default="firstOption" tabindex="3">
</select>
<div class="form-text">Appearance of delivered content.</div>
</div>
@ -44,21 +44,21 @@
<div class="col-lg-6 pe-lg-4">
<div class="mb-4">
<label for="subChannels" class="form-label">Channels</label>
<select name="subChannels" id="subChannels" class="select-2" multiple data-field="channels" tabindex="5"></select>
<select name="subChannels" id="subChannels" class="corbz-select" multiple data-field="channels" tabindex="5"></select>
<div class="form-text">Send content to these channels.</div>
</div>
</div>
<div class="col-lg-6 ps-lg-4">
<div class="mb-4">
<label for="subFilters" class="form-label">Filters</label>
<select name="subFilters" id="subFilters" class="select-2" multiple data-field="filters" tabindex="6"></select>
<select name="subFilters" id="subFilters" class="corbz-select" multiple data-field="filters" tabindex="6"></select>
<div class="form-text">Filter out unwanted content.</div>
</div>
</div>
<div class="col-lg-6 pe-lg-4">
<div class="mb-4 mb-lg-0">
<label for="subUniqueRules" class="form-label">Uniqueness Rules</label>
<select name="subUniqueRules" id="subUniqueRules" class="select-2" multiple required data-field="unique_rules" tabindex="7"></select>
<select name="subUniqueRules" id="subUniqueRules" class="corbz-select" multiple required data-field="unique_rules" tabindex="7"></select>
<div class="form-text">Rules on telling content apart.</div>
</div>
</div>

View File

@ -82,6 +82,10 @@ $(document).ready(function() {
});
});
$(".corbz-select").each(function() {
$(this).initCorbzSelect();
});
$.notify.addStyle("bootstrap", {
html:
'<div class="toast mt-3 overflow-hidden" role="alert">' +
@ -102,4 +106,166 @@ $(document).ready(function() {
// Load theme
initThemeChoice();
});
});
// Custom Select Jquery
(($) => {
function initCorbzSelect() {
const defaultSelectedHtml = "&nbsp;";
const updateSelectedDisplay = ($select, $selected, $dropdown, settings) => {
const values = $select.val() || [];
const names = [];
// Update names based on values
$select.find("option").each(function() {
if (values.includes($(this).val())) {
names.push($(this).text());
}
});
console.log(JSON.stringify(names))
const len = values.length;
const total = $select.find("option").length;
if (len === 0) { $selected.html(defaultSelectedHtml); }
else if (len === total) {$selected.text("All Selected")}
else if (len > 3) { $selected.text(`${values.length} Selected`); }
else { $selected.text(names.join(", ")); }
if (!settings.multiple) {
$dropdown.find(".corbz-select-option").removeClass("active").each(function() {
if (names.includes($(this).data("name"))) {
$(this).addClass("active");
}
});
}
$dropdown.find(".corbz-option-checkbox").each(function() {
$(this).prop("checked", values.includes($(this).val()));
});
}
this.each(function() {
// The original select element, this will be hidden and replaced
// with the custom input 'container'
const $select = $(this);
// The custom input to replace the original select
const $container = $('<div class="corbz-select-container">');
// Contains text indicating the 'selected' options, acts as a
// button to open the dropdown.
const $selected = $('<div class="corbz-select-selected">');
$selected.html(defaultSelectedHtml);
// The dropdown box when accessing the container
const $dropdown = $('<div class="corbz-select-dropdown">');
// Search input to filter the dropdown results
const $search = $('<input type="text" class="corbz-select-search" placeholder="Search ...">');
const settings = {
id: $select.prop("id"),
multiple: $select.prop("multiple"),
required: $select.prop("required"),
disabled: $select.prop("disabled"),
minResultsForSearch: 5
}
// Add custom elements to the DOM
$dropdown.prepend($search);
$container.append($selected).append($dropdown);
$select.hide().after($container);
// Show/Hide the dropdown when clicking the custom input
$selected.on("click", function(event) {
event.stopPropagation();
$('.corbz-select-dropdown').not($dropdown).hide();
$dropdown.toggle();
$container.toggleClass("active", $dropdown.is(":visible"));
});
// Hide dropdown if clicking outside
$(document).on('click', function() {
$dropdown.hide();
$container.removeClass("active");
// Reset search filtering
$search.val("");
$search.trigger("keyup");
});
// Use or hide search
$search.toggle($select.find("option").length > settings.minResultsForSearch);
// Prevent closing the dropdown when selecting the search input
$search.on("click", function (event) { event.stopPropagation(); });
// Search to filter through options
$search.on("keyup", function () {
const searchValue = $(this).val().toLowerCase();
$dropdown.find(".corbz-select-option").each(function() {
const optionText = $(this).text().toLowerCase();
$(this).toggle(optionText.includes(searchValue))
})
});
const updateSelectedDisplayWrapper = () => {
updateSelectedDisplay($select, $selected, $dropdown, settings)
}
$select.find("option").each(function() {
const $option = $('<div class="corbz-select-option">');
$option.data("name", $(this).text());
$option.data("value", $(this).val());
if (!settings.multiple) {
$option.text($(this).text());
$option.on("click", function() {
$dropdown.hide();
$container.removeClass("active");
$select.val($option.data("value"));
updateSelectedDisplayWrapper();
});
}
else {
const $checkbox = $(`<input type="checkbox" name="${settings.id}" class="corbz-option-checkbox" value="${$(this).val()}">`).val($(this).val());
const $label = $("<span>").text($(this).text());
$option.prepend($checkbox).append($label);
$option.on("click", function(event) {
event.stopPropagation();
// Handle if the user clicked inside the checkbox itself
if (!$(event.target).hasClass("corbz-option-checkbox")) {
$checkbox.prop("checked", !$checkbox.prop("checked"));
}
const selectedElems = $dropdown.find(`input[type="checkbox"][name="${settings.id}"]:checked`).map(function() {
return $(this).closest(".corbz-select-option");
});
// Get and internally set the selected values
const selectedVals = selectedElems.map(function() {
return this.data("value");
});
$select.val(selectedVals).trigger("change");
updateSelectedDisplayWrapper();
});
}
$dropdown.append($option)
});
// Initialize display, and detect future changes
updateSelectedDisplayWrapper();
$select.on("change", updateSelectedDisplayWrapper);
});
}
$.fn.initCorbzSelect = initCorbzSelect;
})(jQuery);