diff --git a/marko/components/right-menu/index.marko b/marko/components/right-menu/index.marko new file mode 100644 index 0000000..97132f3 --- /dev/null +++ b/marko/components/right-menu/index.marko @@ -0,0 +1,5 @@ +
+ +
\ No newline at end of file diff --git a/marko/components/right-menu/style.less b/marko/components/right-menu/style.less new file mode 100644 index 0000000..0340a63 --- /dev/null +++ b/marko/components/right-menu/style.less @@ -0,0 +1,26 @@ +.right-menu { + position: fixed; + right: 0; + top: 0; + bottom: 0; + width: 28ch; + + padding: 0.333em; + padding-bottom: 1em; + + text-align: left; + + user-select: none; + overflow-x: hidden; + overflow-y: auto; + + .showhide { + display: none; + } +} + +@media screen and (max-width: 800px) { + .right-menu { + display: none; + } +} \ No newline at end of file diff --git a/marko/global/base.less b/marko/global/base.less index b302f07..e4a07cf 100644 --- a/marko/global/base.less +++ b/marko/global/base.less @@ -10,6 +10,11 @@ body { width: calc(100% - 32ch); margin-right: 0; } + @media screen and (min-width: 700px) { + main.with-right { + width: calc(100% - 60ch) + } + } a { text-decoration: none; diff --git a/marko/page/logs/components/filter-menu/component.js b/marko/page/logs/components/filter-menu/component.js new file mode 100644 index 0000000..36bb547 --- /dev/null +++ b/marko/page/logs/components/filter-menu/component.js @@ -0,0 +1,129 @@ +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) => a.name.localeCompare(b.name)) + this.state.channels = input.channels.filter(c => !this.state.activeMap.channels[c.name]).filter(c => this.matches(c.name, search)).sort((a,b) => a.name.localeCompare(b.name)) + this.state.eventNames = input.eventNames.filter(e => !this.state.activeMap.events[e]).filter(e => this.matches(e, search)).sort((a,b) => a.localeCompare(b)) + } + + 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 + } +} \ No newline at end of file diff --git a/marko/page/logs/components/filter-menu/index.marko b/marko/page/logs/components/filter-menu/index.marko new file mode 100644 index 0000000..7c51505 --- /dev/null +++ b/marko/page/logs/components/filter-menu/index.marko @@ -0,0 +1,36 @@ + + + + 0)>Active Filters +
+ ${filter.text} +
+ 0) /> + Characters +
+ ${character.name} +
+ + Channels +
+ ${channel.name} +
+ + Events +
+ ${eventName} +
+
\ No newline at end of file diff --git a/marko/page/logs/components/filter-menu/style.less b/marko/page/logs/components/filter-menu/style.less new file mode 100644 index 0000000..cef6bc2 --- /dev/null +++ b/marko/page/logs/components/filter-menu/style.less @@ -0,0 +1,24 @@ +div.right-menu.filter-menu { + input { + background: none; + border: none; + outline: none; + width: 95%; + margin-top: 1em; + margin-left: 2.5%; + border: 1px solid rgba(119, 119, 119, 0.25); + font-size: 1.1em; + box-sizing: border-box; + + color: #999; + } + input:focus { + color: #FC1; + } + + div.filter-section { + max-height: calc(25vh - 3em); + overflow-y: auto; + overflow-x: hidden; + } +} \ No newline at end of file diff --git a/marko/page/logs/components/logs-menu/component.js b/marko/page/logs/components/logs-menu/component.js index 13fd610..c9bce8c 100644 --- a/marko/page/logs/components/logs-menu/component.js +++ b/marko/page/logs/components/logs-menu/component.js @@ -13,54 +13,7 @@ module.exports = class { return } - const filters = [] - const {characters, channels, events, search} = input.filter - - if (search != null) { - filters.push({ - type: "search", - key: "search", - icon: "T", - color: "color-text", - text: search, - }) - } - - for (const character of (characters || [])) { - filters.push({ - type: "characters", - key: character, - icon: "C", - color: "color-tag-character", - id: character, - text: (input.characters.find(c => c.id === character) || {name: "Unknown ("+character+")"}).name, - }) - } - - for (const channel of (channels || [])) { - filters.push({ - type: "channels", - key: channel.replace(/'/g, "__"), - icon: "#", - color: "color-tag-location", - id: channel, - text: channel, - }) - } - - for (const event of (events || [])) { - 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.filtered = (filters.length > 0) + this.state.filtered = input.filter.search || input.filter.channels || input.filter.characters || input.filter.events } select(value) { diff --git a/marko/page/logs/components/logs-menu/index.marko b/marko/page/logs/components/logs-menu/index.marko index 8051914..16c6ff0 100644 --- a/marko/page/logs/components/logs-menu/index.marko +++ b/marko/page/logs/components/logs-menu/index.marko @@ -4,8 +4,4 @@ Add Log - - Filters - ${filter.text} - Add Filter \ No newline at end of file diff --git a/marko/page/logs/components/logs-table-row/style.less b/marko/page/logs/components/logs-table-row/style.less index 3fde21f..1756092 100644 --- a/marko/page/logs/components/logs-table-row/style.less +++ b/marko/page/logs/components/logs-table-row/style.less @@ -10,11 +10,17 @@ tr.logs-table-row { } td.expand { - width: 2%; + width: 0%; } - td.channel, td.date, td.eventĀ { - width: 12%; + td.date { + width: 16%; + } + td.channel { + width: 14%; + } + td.eventĀ { + width: 18%; } td.character { diff --git a/marko/page/logs/components/logs-table/style.less b/marko/page/logs/components/logs-table/style.less index 9f8091e..da8e81b 100644 --- a/marko/page/logs/components/logs-table/style.less +++ b/marko/page/logs/components/logs-table/style.less @@ -9,7 +9,7 @@ table.logs-table { text-decoration: underline; } - @media screen and (max-width: 900px) { + @media screen and (max-width: 1200px) { td.event, th.event { display: none; } diff --git a/marko/page/logs/components/page/component.js b/marko/page/logs/components/page/component.js index 61aa792..cd3b24a 100644 --- a/marko/page/logs/components/page/component.js +++ b/marko/page/logs/components/page/component.js @@ -19,11 +19,18 @@ module.exports = class { addFilter(type, filter) { if (type === "search") { - this.state.filter = Object.assign({}, this.state.filter, {search: filter}) + if (filter.trim().length > 0) { + this.state.filter = Object.assign({}, this.state.filter, {search: filter}) + } else { + this.state.filter = Object.assign({}, this.state.filter, {search: null}) + } + } else { this.state.filter = Object.assign({}, this.state.filter, {[type]: (this.state.filter[type] || []).filter(f => f !== filter).concat(filter)}) } + this.updateQuery(this.state.filter) + this.refresh() } @@ -41,6 +48,31 @@ module.exports = class { this.refresh() } } + + this.updateQuery(this.state.filter) + } + + updateQuery(filter) { + const queries = [] + + if (filter.characters) { + queries.push("characters=" + filter.characters.join(",")) + } + if (filter.channels) { + queries.push("channels=" + filter.channels.map(c => encodeURIComponent(c)).join(",")) + } + if (filter.events) { + queries.push("events=" + filter.events.map(e => encodeURIComponent(e)).join(",")) + } + if (filter.search) { + queries.push("search=" + encodeURIComponent(filter.search)) + } + + if (queries.length > 0) { + history.replaceState("", "", `/logs/?${queries.join("&")}`) + } else { + history.replaceState("", "", "/logs/") + } } refresh() { diff --git a/marko/page/logs/components/page/index.marko b/marko/page/logs/components/page/index.marko index 2c438f8..774aed1 100644 --- a/marko/page/logs/components/page/index.marko +++ b/marko/page/logs/components/page/index.marko @@ -1,7 +1,7 @@ - -
+ + +
- diff --git a/marko/page/logs/list.marko b/marko/page/logs/list.marko index 99741c2..1793abd 100644 --- a/marko/page/logs/list.marko +++ b/marko/page/logs/list.marko @@ -1,6 +1,11 @@ <@body> - - + \ No newline at end of file diff --git a/routes/logs/index.js b/routes/logs/index.js index 2272189..b73620a 100644 --- a/routes/logs/index.js +++ b/routes/logs/index.js @@ -3,6 +3,7 @@ const router = express.Router() const {logHeaderApi, eventNames} = require("../../rpdata/api/LogHeader") const {charactersApi} = require("../../rpdata/api/Character") +const {channelApi} = require("../../rpdata/api/Channel") const listTemplate = require("../../marko/page/logs/list.marko") @@ -27,6 +28,8 @@ router.get("/", async(req, res) => { filter: filter, logs: logHeaderApi.list(filter), characters: charactersApi.listHeaders(), + channels: channelApi.list(), + eventNames: logHeaderApi.eventNames(), selected: {index: true}, }) } catch(err) {