The frontend/UI server, written in JS using the MarkoJS library
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.
 
 
 
 

154 lines
3.6 KiB

module.exports = class {
onCreate(input) {
this.state = {
characters: [],
channels: [],
eventNames: [],
filters: [],
search: "",
activeMap: {
events: {},
characters: {},
channels: {},
},
}
}
onInput(input) {
this.update(input)
}
onMount(input) {
this.getEl("search").addEventListener("keydown", ev => {
if (this.waiting) {
return
}
if (this.timeout != null) {
clearTimeout(this.timeout)
}
if (ev.key === "Enter") {
this.emit("add", "search", ev.target.value)
this.getEl("search").value = ""
}
this.timeout = setTimeout(() => {
this.timeout = null
this.state.search = ev.target.value
this.update(this.input)
}, 200)
})
}
update(input) {
if (input.filter) {
this.updateActive(input)
}
const search = this.state.search.toLocaleLowerCase()
this.state.characters = input.characters.filter(c => !this.state.activeMap.characters[c.id]).filter(c => this.matches(c.name, search)).sort((a,b) => compareFilters(a.name, b.name, search))
this.state.channels = input.channels.filter(c => !this.state.activeMap.channels[c.name]).filter(c => this.matches(c.name, search)).sort((a,b) => compareFilters(a.name, b.name, search))
this.state.eventNames = input.eventNames.filter(e => !this.state.activeMap.events[e]).filter(e => this.matches(e, search)).sort((a,b) => compareFilters(a, b, search))
}
updateActive({filter, characters, channels, eventNames}) {
const filters = []
const activeMap = {
events: {},
characters: {},
channels: {},
}
if (filter.search != null) {
filters.push({
type: "search",
key: "search",
icon: "T",
color: "color-text",
text: filter.search,
})
}
for (const character of (filter.characters || [])) {
activeMap.characters[character] = true
filters.push({
type: "characters",
key: character,
icon: "C",
color: "color-tag-character",
id: character,
text: (characters.find(c => c.id === character) || {name: "Unknown ("+character+")"}).name,
})
}
for (const channel of (filter.channels || [])) {
activeMap.channels[channel] = true
filters.push({
type: "channels",
key: channel.replace(/'/g, "__"),
icon: "#",
color: "color-tag-location",
id: channel,
text: channel,
})
}
for (const event of (filter.events || [])) {
activeMap.events[event] = true
filters.push({
type: "events",
key: event.replace(/['\s\.]/g, "__"),
icon: "E",
color: "color-tag-event",
id: event,
text: event,
})
}
this.state.filters = filters
this.state.activeMap = activeMap
}
matches(str, search) {
if (search == "") {
return true
}
if (search.length > str.length) {
return false
}
return str.toLowerCase().indexOf(search) !== -1
}
}
/**
*
* @param {string} a
* @param {string} b
* @param {string} search
*/
function compareFilters(a, b, search) {
if (search != "") {
if (search.charAt(0) != "#" && a.charAt(0) == "#" && b.charAt(0) == "#") {
search = '#' + search
}
const aStarts = a.toLocaleLowerCase().startsWith(search)
const bStarts = b.toLocaleLowerCase().startsWith(search)
if (aStarts && !bStarts) {
return -1
} else if (bStarts && !aStarts) {
return 1
}
}
return a.localeCompare(b)
}