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.

153 lines
3.6 KiB

  1. module.exports = class {
  2. onCreate(input) {
  3. this.state = {
  4. characters: [],
  5. channels: [],
  6. eventNames: [],
  7. filters: [],
  8. search: "",
  9. activeMap: {
  10. events: {},
  11. characters: {},
  12. channels: {},
  13. },
  14. }
  15. }
  16. onInput(input) {
  17. this.update(input)
  18. }
  19. onMount(input) {
  20. this.getEl("search").addEventListener("keydown", ev => {
  21. if (this.waiting) {
  22. return
  23. }
  24. if (this.timeout != null) {
  25. clearTimeout(this.timeout)
  26. }
  27. if (ev.key === "Enter") {
  28. this.emit("add", "search", ev.target.value)
  29. this.getEl("search").value = ""
  30. }
  31. this.timeout = setTimeout(() => {
  32. this.timeout = null
  33. this.state.search = ev.target.value
  34. this.update(this.input)
  35. }, 200)
  36. })
  37. }
  38. update(input) {
  39. if (input.filter) {
  40. this.updateActive(input)
  41. }
  42. const search = this.state.search.toLocaleLowerCase()
  43. 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))
  44. 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))
  45. 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))
  46. }
  47. updateActive({filter, characters, channels, eventNames}) {
  48. const filters = []
  49. const activeMap = {
  50. events: {},
  51. characters: {},
  52. channels: {},
  53. }
  54. if (filter.search != null) {
  55. filters.push({
  56. type: "search",
  57. key: "search",
  58. icon: "T",
  59. color: "color-text",
  60. text: filter.search,
  61. })
  62. }
  63. for (const character of (filter.characters || [])) {
  64. activeMap.characters[character] = true
  65. filters.push({
  66. type: "characters",
  67. key: character,
  68. icon: "C",
  69. color: "color-tag-character",
  70. id: character,
  71. text: (characters.find(c => c.id === character) || {name: "Unknown ("+character+")"}).name,
  72. })
  73. }
  74. for (const channel of (filter.channels || [])) {
  75. activeMap.channels[channel] = true
  76. filters.push({
  77. type: "channels",
  78. key: channel.replace(/'/g, "__"),
  79. icon: "#",
  80. color: "color-tag-location",
  81. id: channel,
  82. text: channel,
  83. })
  84. }
  85. for (const event of (filter.events || [])) {
  86. activeMap.events[event] = true
  87. filters.push({
  88. type: "events",
  89. key: event.replace(/['\s\.]/g, "__"),
  90. icon: "E",
  91. color: "color-tag-event",
  92. id: event,
  93. text: event,
  94. })
  95. }
  96. this.state.filters = filters
  97. this.state.activeMap = activeMap
  98. }
  99. matches(str, search) {
  100. if (search == "") {
  101. return true
  102. }
  103. if (search.length > str.length) {
  104. return false
  105. }
  106. return str.toLowerCase().indexOf(search) !== -1
  107. }
  108. }
  109. /**
  110. *
  111. * @param {string} a
  112. * @param {string} b
  113. * @param {string} search
  114. */
  115. function compareFilters(a, b, search) {
  116. if (search != "") {
  117. if (search.charAt(0) != "#" && a.charAt(0) == "#" && b.charAt(0) == "#") {
  118. search = '#' + search
  119. }
  120. const aStarts = a.toLocaleLowerCase().startsWith(search)
  121. const bStarts = b.toLocaleLowerCase().startsWith(search)
  122. if (aStarts && !bStarts) {
  123. return -1
  124. } else if (bStarts && !aStarts) {
  125. return 1
  126. }
  127. }
  128. return a.localeCompare(b)
  129. }