From 10386b1ce9b3ed404e16b5ad601a1239d6a0a534 Mon Sep 17 00:00:00 2001 From: badbl0cks <4161747+badbl0cks@users.noreply.github.com> Date: Tue, 15 Apr 2025 00:51:47 -0700 Subject: [PATCH] make card_multiselect DRY, closes #26 --- static/css/base.css | 56 ----- static/css/card-multiselect.css | 88 ++++++++ static/js/base.js | 1 - static/js/card-multiselect.js | 219 +++++++++++++++++++ theme/templates/base.html | 3 + theme/templatetags/card_multiselect.html | 254 +---------------------- 6 files changed, 311 insertions(+), 310 deletions(-) create mode 100644 static/css/card-multiselect.css create mode 100644 static/js/card-multiselect.js diff --git a/static/css/base.css b/static/css/base.css index 2bec08b..305e738 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -11,62 +11,6 @@ border-radius: 4px; } -select.card-multiselect { - height: calc(var(--spacing) * 35); - /*background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); */ - background-image: none; -} - -.choices.is-disabled .choices__inner, -.choices.is-disabled .choices__input { - background-color: var(--color-neutral); -} -.choices[data-type*=select-one] .choices__input { - border-bottom: 1px solid var(--btn-shadow); - background-color: var(--color-base-100); -} -.choices[data-type*=select-one] .choices__button:focus { - box-shadow: 0 0 0 2px #005F75; -} -.choices__inner { - background-color: var(--color-base-100); - border: 1px solid var(--btn-shadow); -} -.is-focused .choices__inner, .is-open .choices__inner { - border-color: var(--btn-shadow); -} -.choices__list--multiple .choices__item { - background-color: var(--color-base-100); - border: 1px solid var(--btn-shadow); - color: var(--color-primary); -} -.choices__list--multiple .choices__item.is-highlighted { - background-color: var(--color-base-100); - border: 1px solid var(--btn-shadow); -} -.is-disabled .choices__list--multiple .choices__item { - background-color: var(--color-neutral); - border: 1px solid var(--btn-shadow); -} -.choices__list--dropdown, .choices__list[aria-expanded] { - background-color: var(--color-base-100); - border: 1px solid var(--btn-shadow); -} -.is-open .choices__list--dropdown, .is-open .choices__list[aria-expanded] { - border-color: var(--btn-shadow); -} -.choices__list--dropdown .choices__item--selectable.is-highlighted, .choices__list[aria-expanded] .choices__item--selectable.is-highlighted { - background-color: var(--color-base-100); - border: 1px solid var(--btn-shadow); -} -.choices__heading { - border-bottom: 1px solid var(--btn-shadow); - color: var(--color-neutral); -} -.choices__input { - background-color: var(--color-base-100); -} - .gravatar-hovercard .gravatar-hovercard__inner { background-color: var(--color-base-100) !important; border-color: var(--color-base-300) !important; diff --git a/static/css/card-multiselect.css b/static/css/card-multiselect.css new file mode 100644 index 0000000..cd14427 --- /dev/null +++ b/static/css/card-multiselect.css @@ -0,0 +1,88 @@ +select.card-multiselect { + height: calc(var(--spacing) * 35); + /*background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); */ + background-image: none; +} +.choices.is-disabled .choices__inner, +.choices.is-disabled .choices__input { + background-color: var(--color-neutral); +} +.choices[data-type*=select-one] .choices__input { + border-bottom: 1px solid var(--btn-shadow); + background-color: var(--color-base-100); +} +.choices[data-type*=select-one] .choices__button:focus { + box-shadow: 0 0 0 2px #005F75; +} +.choices__inner { + background-color: var(--color-base-100); + border: 1px solid var(--btn-shadow); +} +.is-focused .choices__inner, .is-open .choices__inner { + border-color: var(--btn-shadow); +} +.choices__list--multiple .choices__item { + background-color: var(--color-base-100); + border: 1px solid var(--btn-shadow); + color: var(--color-primary); +} +.choices__list--multiple .choices__item.is-highlighted { + background-color: var(--color-base-100); + border: 1px solid var(--btn-shadow); +} +.is-disabled .choices__list--multiple .choices__item { + background-color: var(--color-neutral); + border: 1px solid var(--btn-shadow); +} +.choices__list--dropdown, .choices__list[aria-expanded] { + background-color: var(--color-base-100); + border: 1px solid var(--btn-shadow); +} +.is-open .choices__list--dropdown, .is-open .choices__list[aria-expanded] { + border-color: var(--btn-shadow); +} +.choices__list--dropdown .choices__item--selectable.is-highlighted, .choices__list[aria-expanded] .choices__item--selectable.is-highlighted { + background-color: var(--color-base-100); + border: 1px solid var(--btn-shadow); +} +.choices__heading { + border-bottom: 1px solid var(--btn-shadow); + color: var(--color-neutral); +} +.choices__input { + background-color: var(--color-base-100); +} +.choices.select { + height: inherit; + padding-inline-start: 0; +} +.choices__inner { + border: 1px solid var(--color-gray-500) !important; +} +.choices__list { + border: none !important; +} +.choices__list--dropdown { + border-left: 1px solid var(--color-gray-500) !important; + border-right: 1px solid var(--color-gray-500) !important; + border-bottom: 1px solid var(--color-gray-500) !important; + border-top: none !important; +} +.choices.select[data-type*="select-one"]::after { + display: none; +} +.choices__inner.bg-secondary { + background-color: var(--color-secondary); + border: none; + z-index: 10; +} +.choices__item.mx-auto.w-max:hover { + background-color: #e2e8f0; +} +.choices__input, +.choices__input--cloned { + width: 100% !important; +} +.choices__list--dropdown span.card-quantity-badge { + display: none; +} \ No newline at end of file diff --git a/static/js/base.js b/static/js/base.js index 88a6164..e0465d1 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -119,7 +119,6 @@ const $$ = selector => Array.from(document.querySelector(selector)); */ function processMarqueeElements() { document.querySelectorAll('.marquee-calc').forEach(element => { - console.log(element.innerHTML, element.offsetWidth, element.scrollWidth); if (element.offsetWidth >= 148 || element.offsetWidth < element.scrollWidth) { element.innerHTML = ''; } diff --git a/static/js/card-multiselect.js b/static/js/card-multiselect.js new file mode 100644 index 0000000..a649398 --- /dev/null +++ b/static/js/card-multiselect.js @@ -0,0 +1,219 @@ +document.addEventListener('DOMContentLoaded', function() { + if (!window.updateGlobalCardFilters) { + window.updateGlobalCardFilters = function() { + const selects = document.querySelectorAll('.card-multiselect'); + + // Rebuild global selections and rarity filtering. + const globalSelectedIds = []; + let globalRarity = null; + + selects.forEach(select => { + const selectedValues = select.choicesInstance ? select.choicesInstance.getValue(true) : []; + selectedValues.forEach(cardId => { + if (cardId && globalSelectedIds.indexOf(cardId) === -1) { + globalSelectedIds.push(cardId); + } + }); + if (selectedValues.length > 0 && globalRarity === null) { + const option = select.querySelector('option[value="${selectedValues[0]}"]'); + if (option) { + globalRarity = option.getAttribute('data-rarity'); + } + } + }); + + selects.forEach(select => { + if (select.choicesInstance && select.choicesInstance.dropdown.element) { + // Reset all options to enabled. + select.querySelectorAll('option').forEach(function(option) { + option.disabled = false; + }); + // Reset all items to visible. + select.choicesInstance.dropdown.element.querySelectorAll('[data-card-id]').forEach(function(item) { + item.style.display = ''; + }); + // Filter out options/items that do not match the global rarity. + if (globalRarity) { + select.querySelectorAll('option[data-rarity]:not([data-rarity="'+globalRarity+'"])').forEach(function(option) { + option.disabled = true; + }); + select.choicesInstance.dropdown.element.querySelectorAll('[data-rarity]:not([data-rarity="'+globalRarity+'"])').forEach(function(item) { + item.style.display = 'none'; + }); + } + // Filter out options/items that match the global selected card IDs. + for (const cardId of globalSelectedIds) { + select.choicesInstance.dropdown.element.querySelectorAll('[data-card-id="' + cardId + '"]').forEach(function(item) { + item.style.display = 'none'; + }); + select.querySelectorAll('option[data-card-id="'+cardId+'"]:not(option[selected])').forEach(function(option) { + option.disabled = true; + }); + } + } + }); + }; + } + + if (!window.updateOptionQuantity) { + window.updateOptionQuantity = function(item, quantity) { + const cardId = item.getAttribute('data-card-id'); + const option = item.closest('.choices__inner').querySelector('option[value="' + cardId + '"]'); + if (option) { + option.setAttribute('data-quantity', quantity); + } + } + } + + if (!window.getOptionQuantity) { + window.getOptionQuantity = function(item) { + const cardId = item.getAttribute('data-card-id'); + const option = item.closest('.choices__inner').querySelector('option[value="' + cardId + '"]'); + return option ? parseInt(option.getAttribute('data-quantity')) : ""; + } + } + + + const selectFields = document.querySelectorAll('.card-multiselect'); + selectFields.forEach(selectField => { + const placeholder = selectField.getAttribute('data-placeholder') || ''; + + const choicesInstance = new Choices(selectField, { + removeItemButton: false, + placeholderValue: placeholder, + searchEnabled: true, + shouldSort: false, + allowHTML: true, + closeDropdownOnSelect: true, + removeItemButton: true, + searchFields: ['label'], + resetScrollPosition: false, + callbackOnCreateTemplates: function(template) { + const getCardContent = (data) => { + let htmlContent = (data.element && data.element.getAttribute('data-html-content')) || data.label; + let quantity = data.element.getAttribute('data-quantity'); + quantity = quantity ? parseInt(quantity) : 1; + htmlContent = htmlContent.replace('__QUANTITY__', quantity); + return htmlContent; + }; + + const renderCard = (classNames, data, type) => { + const rarity = data.element ? data.element.getAttribute('data-rarity') : ''; + const cardId = data.element ? data.element.getAttribute('data-card-id') : 0; + const cardname = data.element ? data.element.getAttribute('data-name') : ''; + const content = getCardContent(data); + if (type === 'item') { + return template(` +