Add 'tags' to event model. Add a dataimport system - currently for MaxMind zip files

This commit is contained in:
Dan Milne
2025-11-11 10:31:36 +11:00
parent 772fae7e8b
commit 26216da9ca
34 changed files with 3580 additions and 14 deletions

View File

@@ -0,0 +1,145 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "select" ]
static values = {
options: Array,
placeholder: String
}
connect() {
// Check if the element is visible, if not, wait for it to become visible
if (this.isHidden()) {
// Element is hidden, set up a MutationObserver to watch for visibility changes
this.observer = new MutationObserver(() => {
if (!this.isHidden()) {
this.initializeTomSelect()
this.observer.disconnect()
}
})
this.observer.observe(this.element, {
attributes: true,
attributeFilter: ['class']
})
// Also check periodically as a fallback
this.checkInterval = setInterval(() => {
if (!this.isHidden()) {
this.initializeTomSelect()
this.cleanup()
}
}, 500)
} else {
// Element is already visible, initialize immediately
this.initializeTomSelect()
}
}
isHidden() {
return this.element.offsetParent === null || this.element.classList.contains('hidden')
}
cleanup() {
if (this.observer) {
this.observer.disconnect()
this.observer = null
}
if (this.checkInterval) {
clearInterval(this.checkInterval)
this.checkInterval = null
}
}
initializeTomSelect() {
if (!this.hasSelectTarget) {
console.log('No select target found')
return
}
// Check if Tom Select is available
if (typeof TomSelect === 'undefined') {
console.log('Tom Select is not loaded')
return
}
// If TomSelect is already initialized, destroy it first
if (this.tomSelect) {
this.tomSelect.destroy()
}
console.log('Initializing Tom Select with options:', this.optionsValue.length, 'countries')
console.log('First few country options:', this.optionsValue.slice(0, 3))
// Prepare options for Tom Select
const options = this.optionsValue.map(([display, value]) => ({
value: value,
text: display,
// Add searchable fields for better search
search: display + ' ' + value
}))
// Get currently selected values from the hidden select
const selectedValues = Array.from(this.selectTarget.selectedOptions).map(option => option.value)
try {
// Initialize Tom Select
this.tomSelect = new TomSelect(this.selectTarget, {
options: options,
items: selectedValues,
plugins: ['remove_button'],
maxItems: null,
maxOptions: 1000,
create: false,
placeholder: this.placeholderValue || "Search and select countries...",
searchField: ['text', 'search'],
searchConjunction: 'or',
onItemAdd: function() {
// Clear the search input after selecting an item
this.setTextboxValue('');
this.refreshOptions();
},
render: {
option: function(data, escape) {
return `<div class="flex items-center p-2">
<span>${escape(data.text)}</span>
</div>`
},
item: function(data, escape) {
return `<div class="flex items-center text-sm">
<span>${escape(data.text)}</span>
</div>`
}
},
dropdownParent: 'body',
copyClassesToDropdown: false
})
console.log('Tom Select successfully initialized for country selector')
// Make sure the wrapper is visible
setTimeout(() => {
if (this.tomSelect && this.tomSelect.wrapper) {
this.tomSelect.wrapper.style.visibility = 'visible'
this.tomSelect.wrapper.style.display = 'block'
console.log('Tom Select wrapper made visible')
}
}, 100)
} catch (error) {
console.error('Error initializing Tom Select:', error)
}
}
// Public method to reinitialize if needed
reinitialize() {
this.initializeTomSelect()
}
disconnect() {
this.cleanup()
if (this.tomSelect) {
this.tomSelect.destroy()
}
}
}