From 5e7333fc83b8c7981e9073f6c5af1326fe287f41 Mon Sep 17 00:00:00 2001 From: RNL Date: Fri, 27 Mar 2026 23:28:24 +1100 Subject: [PATCH] js and filament form for transparent color --- .../0002_filament_is_transparent.py | 27 +++ .../static/bambu_run/js/filament_form.js | 156 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 bambu_run/migrations/0002_filament_is_transparent.py create mode 100644 bambu_run/static/bambu_run/js/filament_form.js diff --git a/bambu_run/migrations/0002_filament_is_transparent.py b/bambu_run/migrations/0002_filament_is_transparent.py new file mode 100644 index 0000000..2642007 --- /dev/null +++ b/bambu_run/migrations/0002_filament_is_transparent.py @@ -0,0 +1,27 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bambu_run", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="filamentcolor", + name="is_transparent", + field=models.BooleanField( + default=False, + help_text="True for clear/transparent filaments — display as checkerboard, not solid color", + ), + ), + migrations.AddField( + model_name="filament", + name="is_transparent", + field=models.BooleanField( + default=False, + help_text="True for clear/transparent filaments — display as checkerboard, not solid color", + ), + ), + ] diff --git a/bambu_run/static/bambu_run/js/filament_form.js b/bambu_run/static/bambu_run/js/filament_form.js new file mode 100644 index 0000000..ace6253 --- /dev/null +++ b/bambu_run/static/bambu_run/js/filament_form.js @@ -0,0 +1,156 @@ +/** + * filament_form.js — Filament add/edit form interactions. + * + * Handles: + * - Filament type preset → auto-fill Type / Sub Type / Brand + * - Transparent checkbox → toggle color picker vs. checkerboard swatch + * - Color picker ↔ hex text sync + * - Delete confirmation modal + */ + +document.addEventListener('DOMContentLoaded', function () { + + // ── Filament type preset auto-fill ──────────────────────────────────────── + + const dataEl = document.getElementById('filament-type-data'); + const filamentTypeMap = dataEl ? JSON.parse(dataEl.textContent) : {}; + + const filamentTypeSelect = document.getElementById('id_filament_type'); + const typeField = document.getElementById('id_type'); + const subTypeField = document.getElementById('id_sub_type'); + const brandField = document.getElementById('id_brand'); + + if (filamentTypeSelect) { + filamentTypeSelect.addEventListener('change', function () { + const mapping = filamentTypeMap[this.value]; + if (mapping && typeField && subTypeField && brandField) { + typeField.value = mapping.type; + subTypeField.value = mapping.sub_type; + brandField.value = mapping.brand; + } + }); + } + + // ── Transparent toggle ──────────────────────────────────────────────────── + + const transparentCheckbox = document.getElementById('id_is_transparent'); + const transparentSwatch = document.getElementById('transparent-swatch'); + const colorPicker = document.getElementById('id_color_hex_picker'); + const colorText = document.getElementById('id_color_hex_text'); + + /** + * Show checkerboard swatch and disable color inputs when transparent, + * restore normal color picker when not transparent. + * @param {boolean} isTransparent + */ + function applyTransparentState(isTransparent) { + if (!colorPicker) return; + if (isTransparent) { + transparentSwatch.style.display = 'block'; + colorPicker.style.display = 'none'; + colorPicker.disabled = true; + if (colorText) { colorText.disabled = true; colorText.value = ''; } + } else { + transparentSwatch.style.display = 'none'; + colorPicker.style.display = ''; + colorPicker.disabled = false; + if (colorText) { colorText.disabled = false; } + } + } + + if (transparentCheckbox) { + applyTransparentState(transparentCheckbox.checked); + transparentCheckbox.addEventListener('change', function () { + applyTransparentState(this.checked); + }); + } + + // ── Color picker ↔ hex text sync ────────────────────────────────────────── + + if (colorPicker && colorText) { + colorPicker.addEventListener('input', function () { + colorText.value = this.value.toUpperCase(); + }); + + colorText.addEventListener('input', function () { + const value = this.value.trim(); + if (/^#[0-9A-Fa-f]{6}$/.test(value)) { + colorPicker.value = value; + this.classList.remove('is-invalid'); + } else if (value.length === 7) { + this.classList.add('is-invalid'); + } + }); + + if (colorText.value && /^#[0-9A-Fa-f]{6}$/.test(colorText.value)) { + colorPicker.value = colorText.value; + } else if (colorPicker.value && !colorText.value) { + colorText.value = colorPicker.value.toUpperCase(); + } + } + + // ── Delete confirmation modal ───────────────────────────────────────────── + + const deleteConfirmText = document.getElementById('deleteConfirmText'); + const confirmDeleteBtn = document.getElementById('confirmDeleteBtn'); + const deleteForm = document.getElementById('deleteForm'); + const deleteModal = document.getElementById('deleteModal'); + + if (deleteConfirmText && confirmDeleteBtn) { + deleteConfirmText.addEventListener('input', function () { + const value = this.value.trim(); + if (value === 'DELETE') { + confirmDeleteBtn.disabled = false; + this.classList.remove('is-invalid'); + this.classList.add('is-valid'); + } else { + confirmDeleteBtn.disabled = true; + this.classList.remove('is-valid'); + if (value.length > 0) { + this.classList.add('is-invalid'); + } else { + this.classList.remove('is-invalid'); + } + } + }); + + if (deleteForm) { + deleteForm.addEventListener('submit', function (e) { + if (confirmDeleteBtn.disabled) { + e.preventDefault(); + alert('Please type DELETE to confirm deletion'); + return false; + } + return true; + }); + } + + if (deleteModal) { + deleteModal.addEventListener('hidden.bs.modal', function () { + deleteConfirmText.value = ''; + confirmDeleteBtn.disabled = true; + deleteConfirmText.classList.remove('is-valid', 'is-invalid'); + }); + + deleteModal.addEventListener('shown.bs.modal', function () { + deleteConfirmText.focus(); + }); + } + } + + // ── Delete button modal opener (backup) ─────────────────────────────────── + + const deleteBtn = document.getElementById('deleteBtn'); + if (deleteBtn && deleteModal) { + deleteBtn.addEventListener('click', function () { + if (!deleteModal.classList.contains('show')) { + if (typeof bootstrap !== 'undefined') { + bootstrap.Modal.getOrCreateInstance(deleteModal).show(); + } else if (typeof coreui !== 'undefined' && coreui.Modal) { + coreui.Modal.getOrCreateInstance(deleteModal).show(); + } + } + }); + } + +});