Gisle Aune
6 years ago
14 changed files with 282 additions and 62 deletions
-
5marko/components/right-menu/index.marko
-
26marko/components/right-menu/style.less
-
5marko/global/base.less
-
129marko/page/logs/components/filter-menu/component.js
-
36marko/page/logs/components/filter-menu/index.marko
-
24marko/page/logs/components/filter-menu/style.less
-
49marko/page/logs/components/logs-menu/component.js
-
4marko/page/logs/components/logs-menu/index.marko
-
12marko/page/logs/components/logs-table-row/style.less
-
2marko/page/logs/components/logs-table/style.less
-
32marko/page/logs/components/page/component.js
-
6marko/page/logs/components/page/index.marko
-
9marko/page/logs/list.marko
-
3routes/logs/index.js
@ -0,0 +1,5 @@ |
|||||
|
<div class=["right-menu", input.class]> |
||||
|
<div class="menu-body"> |
||||
|
<include(input.renderBody) /> |
||||
|
</div> |
||||
|
</div> |
@ -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; |
||||
|
} |
||||
|
} |
@ -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 |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
<right-menu class="filter-menu"> |
||||
|
<input key="search" /> |
||||
|
<menu-gap /> |
||||
|
<menu-header if(state.filters.length > 0)>Active Filters</menu-header> |
||||
|
<div class="filter-section"> |
||||
|
<menu-link for(filter in state.filters) |
||||
|
on-click("emit", "remove", filter.type, filter.id) |
||||
|
textClass=filter.color |
||||
|
unselectable |
||||
|
icon=filter.icon >${filter.text}</menu-link> |
||||
|
</div> |
||||
|
<menu-gap if(state.filters.length > 0) /> |
||||
|
<menu-header>Characters</menu-header> |
||||
|
<div class="filter-section"> |
||||
|
<menu-link for(character in state.characters) |
||||
|
on-click("emit", "add", "characters", character.id) |
||||
|
icon=character.name.charAt(0).toUpperCase() |
||||
|
textClass="color-tag-character">${character.name}</menu-link> |
||||
|
</div> |
||||
|
<menu-gap /> |
||||
|
<menu-header>Channels</menu-header> |
||||
|
<div class="filter-section"> |
||||
|
<menu-link for(channel in state.channels) |
||||
|
on-click("emit", "add", "channels", channel.name) |
||||
|
icon=channel.name.charAt(1).toUpperCase() |
||||
|
textClass="color-tag-location">${channel.name}</menu-link> |
||||
|
</div> |
||||
|
<menu-gap /> |
||||
|
<menu-header>Events</menu-header> |
||||
|
<div class="filter-section"> |
||||
|
<menu-link for(eventName in state.eventNames) |
||||
|
on-click("emit", "add", "events", eventName) |
||||
|
icon=eventName.charAt(0).toUpperCase() |
||||
|
textClass="color-tag-event">${eventName}</menu-link> |
||||
|
</div> |
||||
|
</right-menu> |
@ -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; |
||||
|
} |
||||
|
} |
@ -1,7 +1,7 @@ |
|||||
<background src="/assets/images/bg.png" opacity=0.25 /> |
<background src="/assets/images/bg.png" opacity=0.25 /> |
||||
<logs-menu key="menu" on-select("open") on-removefilter("removeFilter") selected=(input.selected || {}) user=input.user filter=state.filter characters=input.characters /> |
|
||||
<main> |
|
||||
|
<logs-menu key="menu" on-select("open") selected=(input.selected || {}) user=input.user filter=state.filter characters=input.characters /> |
||||
|
<filter-menu on-add("addFilter") on-remove("removeFilter") filter=state.filter characters=input.characters channels=input.channels eventNames=input.eventNames /> |
||||
|
<main class="with-right"> |
||||
<logs-table logs=state.logs on-addfilter("addFilter") /> |
<logs-table logs=state.logs on-addfilter("addFilter") /> |
||||
</main> |
</main> |
||||
<add-log-modal enabled=(state.modal === "log.add") on-close("close") /> |
<add-log-modal enabled=(state.modal === "log.add") on-close("close") /> |
||||
<add-filter-modal enabled=(state.modal === "filter.add") characters=input.characters filter=state.filter on-add("addFilter") on-close("close") /> |
|
@ -1,6 +1,11 @@ |
|||||
<include("../layout", {title: "Logs", site: "logs"})> |
<include("../layout", {title: "Logs", site: "logs"})> |
||||
<@body> |
<@body> |
||||
<!-- Page needed to get component.js functionality --> |
|
||||
<page logs=input.logs filter=input.filter characters=input.characters user=input.user selected=input.selected /> |
|
||||
|
<page logs=input.logs |
||||
|
filter=input.filter |
||||
|
characters=input.characters |
||||
|
channels=input.channels |
||||
|
eventNames=input.eventNames |
||||
|
user=input.user |
||||
|
selected=input.selected /> |
||||
</@body> |
</@body> |
||||
</include> |
</include> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue