You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
9.8 KiB
277 lines
9.8 KiB
{% load i18n %}
|
|
<div class="space-y-3 max-w-2xl" data-multilang-json data-field-name="{{ widget.field_name }}">
|
|
<div class="relative">
|
|
<div class="w-full max-w-2xl overflow-x-auto scrollbar-hover pr-2">
|
|
<div class="inline-flex flex-nowrap items-center gap-1 whitespace-nowrap py-1 min-w-max" data-lang-bar>
|
|
{% for code in widget.languages %}
|
|
<button type="button"
|
|
class="lang-btn px-3 py-1.5 rounded-md border transition-all duration-150 text-xs font-medium
|
|
border-gray-200 text-gray-700 hover:bg-gray-50 hover:border-gray-300
|
|
dark:border-gray-700 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:border-gray-600{% if widget.has_value_codes and code in widget.has_value_codes %} has-value{% endif %}"
|
|
data-lang-code="{{ code }}">
|
|
{{ code|upper }}
|
|
</button>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-3" data-inputs>
|
|
{% for input in widget.inputs %}
|
|
<div class="hidden" data-input-wrapper data-lang-code="{{ input.code }}">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<span class="text-xs font-medium text-font-subtle-light dark:text-font-subtle-dark">
|
|
{{ input.code|upper }}
|
|
</span>
|
|
</div>
|
|
{{ input.html|safe }}
|
|
</div>
|
|
{% endfor %}
|
|
<input type="hidden" name="{{ widget.field_name }}" value='{{ widget.serialized|escapejs }}'>
|
|
</div>
|
|
|
|
<div class="text-xs text-font-subtle-light dark:text-font-subtle-dark">
|
|
{% trans "Click a language code to add or edit its title." %}
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.scrollbar-hover {
|
|
--scrollbar-track: rgb(var(--color-base-100));
|
|
--scrollbar-thumb: rgb(var(--color-base-300));
|
|
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
|
scrollbar-width: none;
|
|
}
|
|
.scrollbar-hover:hover { scrollbar-width: thin; }
|
|
.dark .scrollbar-hover { --scrollbar-track: rgb(var(--color-base-800)); --scrollbar-thumb: rgb(var(--color-base-600)); }
|
|
.scrollbar-hover::-webkit-scrollbar { height: 0; }
|
|
.scrollbar-hover:hover::-webkit-scrollbar { height: 6px; }
|
|
.scrollbar-hover::-webkit-scrollbar-track { background: var(--scrollbar-track); border-radius: 3px; }
|
|
.scrollbar-hover::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 3px; }
|
|
.scrollbar-hover::-webkit-scrollbar-corner { background: var(--scrollbar-track); }
|
|
.scrollbar-hover:hover::-webkit-scrollbar-thumb:hover { background: rgb(var(--color-base-400)); }
|
|
.dark .scrollbar-hover:hover::-webkit-scrollbar-thumb:hover { background: rgb(var(--color-base-500)); }
|
|
|
|
.lang-btn {
|
|
background: transparent;
|
|
cursor: pointer;
|
|
transition: all 0.15s ease;
|
|
}
|
|
.lang-btn:hover {
|
|
background: #f8f9fa;
|
|
border-color: #dee2e6;
|
|
}
|
|
.dark .lang-btn:hover {
|
|
background: #343a40;
|
|
border-color: #6c757d;
|
|
}
|
|
.lang-btn.is-active {
|
|
border-color: #3b82f6;
|
|
background: #eff6ff;
|
|
color: #1d4ed8;
|
|
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.25);
|
|
}
|
|
.dark .lang-btn.is-active {
|
|
border-color: #2563eb;
|
|
background: #1e3a8a;
|
|
color: #dbeafe;
|
|
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.5);
|
|
}
|
|
.lang-btn.has-value {
|
|
border-color: #3b82f6;
|
|
color: #1d4ed8;
|
|
}
|
|
.dark .lang-btn.has-value {
|
|
border-color: #3b82f6;
|
|
color: #dbeafe;
|
|
}
|
|
.lang-btn.is-active:hover {
|
|
background: #eff6ff;
|
|
border-color: #3b82f6;
|
|
}
|
|
.dark .lang-btn.is-active:hover {
|
|
background: #1e3a8a;
|
|
border-color: #2563eb;
|
|
}
|
|
|
|
/* Ensure hidden class works properly */
|
|
[data-input-wrapper].hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
[data-input-wrapper]:not(.hidden) {
|
|
display: block !important;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
(function () {
|
|
function init(root) {
|
|
console.log('Initializing multilang widget:', root);
|
|
var fieldName = root.getAttribute("data-field-name");
|
|
if (!fieldName) {
|
|
console.log('No field name found');
|
|
return;
|
|
}
|
|
|
|
var buttons = root.querySelectorAll(".lang-btn[data-lang-code]");
|
|
var inputsRoot = root.querySelector("[data-inputs]");
|
|
if (!inputsRoot) {
|
|
console.log('No inputs root found');
|
|
return;
|
|
}
|
|
|
|
console.log('Found', buttons.length, 'language buttons');
|
|
|
|
// First, hide all wrappers
|
|
inputsRoot.querySelectorAll('[data-input-wrapper]').forEach(function (w) {
|
|
w.classList.add("hidden");
|
|
});
|
|
|
|
var hasActiveLanguage = false;
|
|
var withValue = [];
|
|
var withoutValue = [];
|
|
buttons.forEach(function (btn) {
|
|
var code = btn.getAttribute("data-lang-code");
|
|
var wrapper = inputsRoot.querySelector('[data-input-wrapper][data-lang-code="' + code + '"]');
|
|
var hasValue = false;
|
|
if (wrapper) {
|
|
var input = wrapper.querySelector('input[name="' + fieldName + '__' + code + '"], textarea[name="' + fieldName + '__' + code + '"], input[id*="' + fieldName + '__' + code + '"]');
|
|
hasValue = !!(input && input.value && input.value.trim() !== "");
|
|
}
|
|
if (hasValue) {
|
|
btn.classList.add("has-value");
|
|
withValue.push(btn);
|
|
if (!hasActiveLanguage && wrapper) {
|
|
btn.classList.add("is-active");
|
|
wrapper.classList.remove("hidden");
|
|
hasActiveLanguage = true;
|
|
console.log('Initializing with active language:', code);
|
|
}
|
|
} else {
|
|
withoutValue.push(btn);
|
|
}
|
|
});
|
|
|
|
if (!hasActiveLanguage && buttons.length) {
|
|
var firstBtn = (withValue[0] || buttons[0]);
|
|
var firstCode = firstBtn.getAttribute("data-lang-code");
|
|
var firstWrapper = inputsRoot.querySelector('[data-input-wrapper][data-lang-code="' + firstCode + '"]');
|
|
if (firstWrapper) {
|
|
firstBtn.classList.add("is-active");
|
|
firstWrapper.classList.remove("hidden");
|
|
console.log('Initializing with first language:', firstCode);
|
|
}
|
|
}
|
|
|
|
var bar = root.querySelector('[data-lang-bar]');
|
|
if (bar) {
|
|
withValue.concat(withoutValue).forEach(function (btn) {
|
|
bar.appendChild(btn);
|
|
});
|
|
}
|
|
|
|
buttons.forEach(function (btn) {
|
|
btn.addEventListener("click", function () {
|
|
console.log('Language button clicked:', btn.getAttribute("data-lang-code"));
|
|
var code = btn.getAttribute("data-lang-code");
|
|
var wrapper = inputsRoot.querySelector('[data-input-wrapper][data-lang-code="' + code + '"]');
|
|
if (!wrapper) {
|
|
console.log('No wrapper found for code:', code);
|
|
return;
|
|
}
|
|
|
|
var isActive = btn.classList.contains("is-active");
|
|
console.log('Button is active:', isActive);
|
|
|
|
// Remove active class from all buttons
|
|
buttons.forEach(function (b) { b.classList.remove("is-active"); });
|
|
|
|
// Hide all input wrappers
|
|
inputsRoot.querySelectorAll('[data-input-wrapper]').forEach(function (w) {
|
|
w.classList.add("hidden");
|
|
console.log('Hiding wrapper for:', w.getAttribute('data-lang-code'));
|
|
});
|
|
|
|
// Always show the clicked wrapper and hide others
|
|
btn.classList.add("is-active");
|
|
wrapper.classList.remove("hidden");
|
|
console.log('Showing wrapper for:', code);
|
|
|
|
var input = wrapper.querySelector('input, textarea');
|
|
if (input) {
|
|
setTimeout(function(){ input.focus(); }, 50);
|
|
}
|
|
|
|
var hidden = root.querySelector('input[type="hidden"][name="' + fieldName + '"]');
|
|
if (hidden) {
|
|
var result = [];
|
|
inputsRoot.querySelectorAll('[data-input-wrapper]').forEach(function (w) {
|
|
var c = w.getAttribute('data-lang-code');
|
|
var inp = w.querySelector('input, textarea');
|
|
if (inp && inp.value && inp.value.trim() !== '') {
|
|
result.push({ language_code: c, title: inp.value });
|
|
}
|
|
});
|
|
try {
|
|
hidden.value = JSON.stringify(result);
|
|
console.log('Updated hidden field value:', hidden.value);
|
|
} catch (e) {
|
|
console.error('JSON stringify error:', e);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
var hidden = root.querySelector('input[type="hidden"][name="' + fieldName + '"]');
|
|
if (hidden) {
|
|
inputsRoot.querySelectorAll('input, textarea').forEach(function (inp) {
|
|
inp.addEventListener('input', function () {
|
|
var result = [];
|
|
inputsRoot.querySelectorAll('[data-input-wrapper]').forEach(function (w) {
|
|
var c = w.getAttribute('data-lang-code');
|
|
var i = w.querySelector('input, textarea');
|
|
if (i && i.value && i.value.trim() !== '') {
|
|
result.push({ language_code: c, title: i.value });
|
|
var btn = root.querySelector('.lang-btn[data-lang-code="' + c + '"]');
|
|
if (btn) btn.classList.add('has-value');
|
|
} else {
|
|
var btn2 = root.querySelector('.lang-btn[data-lang-code="' + c + '"]');
|
|
if (btn2) btn2.classList.remove('has-value');
|
|
}
|
|
});
|
|
try { hidden.value = JSON.stringify(result); } catch (e) { console.error('JSON stringify error:', e); }
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
function initializeWidgets() {
|
|
console.log('Initializing all multilang widgets');
|
|
document.querySelectorAll('[data-multilang-json]').forEach(init);
|
|
}
|
|
|
|
// Try multiple initialization methods
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener("DOMContentLoaded", initializeWidgets);
|
|
} else {
|
|
// DOM is already loaded
|
|
initializeWidgets();
|
|
}
|
|
|
|
// Also try after a short delay to ensure everything is ready
|
|
setTimeout(initializeWidgets, 100);
|
|
|
|
document.addEventListener("formset:added", function (event) {
|
|
var newFormset = event.detail.formsetRow;
|
|
if (newFormset) {
|
|
newFormset.querySelectorAll('[data-multilang-json]').forEach(init);
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|