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
-
34marko/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 /> |
|||
<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") /> |
|||
</main> |
|||
<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"})> |
|||
<@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> |
|||
</include> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue