Skip to content

Commit 429981c

Browse files
author
Piotr
committed
context menu
1 parent 1c67632 commit 429981c

File tree

4 files changed

+213
-4
lines changed

4 files changed

+213
-4
lines changed

src/App.vue

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import DoubleRight from "./components/icon/DoubleRight.vue"
2020
import Flag from "./components/icon/Flag.vue"
2121
import ArrowRightUp from "./components/icon/ArrowRightUp.vue"
2222
import HideColumnIcon from "./components/HideColumnIcon.vue"
23+
import FilterIcon from "./components/icon/Filter.vue"
2324
import ExportLogs from "./components/ExportLogs.vue"
25+
import ContextMenu from "./components/ContextMenu.vue";
2426
import { useMainStore, InitSettings } from './store';
2527
import { useFilterStore } from "./stores/filter";
2628
import { startDragging, endDragging, startColumnDragging } from './dragging';
@@ -30,6 +32,8 @@ import loadAnalytics from './analytics';
3032
import { client } from "./api"
3133
import TopBar from "./components/TopBar.vue"
3234
import Close from './components/icon/Close.vue';
35+
import { useContextMenuStore } from './stores/contextMenu';
36+
import { globalEventBus } from './event_bus';
3337
3438
const store = useMainStore()
3539
const storeFilter = useFilterStore()
@@ -494,6 +498,11 @@ const updateSearchbar = () => {
494498
store.searchbar = searchbar.value
495499
}
496500
501+
globalEventBus.on('searchbar-update', (value: string) => {
502+
store.searchbar = value
503+
searchbar.value = value
504+
})
505+
497506
const clearSearchbar = () => {
498507
store.searchClear()
499508
searchbar.value = ""
@@ -591,6 +600,7 @@ const updateSampleLine = () => {
591600
<LoadLogs v-if="store.modalShow == 'load-logs'" />
592601
<FeedbackModal v-if="store.modalShow == 'feedback'" />
593602
</Modal>
603+
<ContextMenu v-if="useContextMenuStore().display" />
594604
<Confirm />
595605

596606
<SettingsDrawer v-if="store.settingsDrawer" @close="store.settingsDrawer = false" :layout="(store.layout as Layout)"
@@ -699,8 +709,10 @@ const updateSampleLine = () => {
699709
<table class="table" cellspacing="0" cellpadding="0">
700710
<tr>
701711
<th></th>
702-
<th v-for="col in columns" :style="{ width: col.width + 'px', cursor: 'auto' }" class="column-name">
712+
<th v-for="col in columns" :style="{ width: col.width + 'px', cursor: 'auto' }" class="column-name"
713+
@contextmenu.prevent="useContextMenuStore().show($event, { type: 'column_header', name: col.name })">
703714
<span style="cursor: auto;">{{ col.name }}</span>
715+
<FilterIcon v-if="col.faceted" :style="{ opacity: store.isFacetActive(col.name) ? 1 : 0.2 }" />
704716
<div class="hide-icon"
705717
style="height: 12px; width: 12px; display: inline; visibility: hidden; opacity: 0.4; cursor: pointer; margin-left: 3px;"
706718
@click="hideColumn(col)">
@@ -719,10 +731,14 @@ const updateSampleLine = () => {
719731
720732
</span>
721733
</td>
722-
<td class="cell" v-for="_, k2 in columns" :style="row.cells[k2].style as StyleValue || {}">
734+
<td class="cell" v-for="c, k2 in columns" :style="row.cells[k2].style as StyleValue || {}">
723735
<div :style="{ width: columns[k2].width + 'px' }" v-if="row.cells[k2].allowHtmlInText"
724-
v-html="row.cells[k2].text || '&nbsp;'"></div>
725-
<div :style="{ width: columns[k2].width + 'px' }" v-else>{{ row.cells[k2].text || "&nbsp;" }}</div>
736+
v-html="row.cells[k2].text !== undefined ? row.cells[k2].text : '&nbsp;'"
737+
@contextmenu.prevent="useContextMenuStore().show($event, { type: 'cell', value: row.cells[k2].text, columnId: c.id })">
738+
</div>
739+
<div :style="{ width: columns[k2].width + 'px' }"
740+
@contextmenu.prevent="useContextMenuStore().show($event, { type: 'cell', value: row.cells[k2].text, columnId: c.id })"
741+
v-else>{{ row.cells[k2].text !== undefined ? row.cells[k2].text : "&nbsp;" }}</div>
726742

727743
</td>
728744
<td class="cell" v-if="store.correlationFilter" style="min-width: 50px;">

src/components/ContextMenu.vue

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<script setup lang="ts">
2+
import { useContextMenuStore } from '../stores/contextMenu';
3+
</script>
4+
5+
<template>
6+
<div>
7+
<div class="overlay" @click="useContextMenuStore().hide()"></div>
8+
9+
<div class="context-menu"
10+
:style="{ top: useContextMenuStore().y + 'px', left: useContextMenuStore().x + 'px' }">
11+
<div v-for="action in useContextMenuStore().actions" @click="action.fn()"
12+
:class="{ disabled: action.disabled && action.disabled() }">
13+
{{ action.label }}
14+
</div>
15+
</div>
16+
</div>
17+
</template>
18+
19+
<style scoped>
20+
.context-menu {
21+
position: absolute;
22+
background: var(--hl-bg3);
23+
min-width: 150px;
24+
z-index: 101;
25+
font-size: 12px;
26+
font-family: 'Roboto mono', monospace;
27+
28+
.disabled {
29+
opacity: 0.5;
30+
pointer-events: none;
31+
}
32+
}
33+
34+
.context-menu div {
35+
cursor: pointer;
36+
padding: 4px 8px;
37+
}
38+
39+
.context-menu div:hover {
40+
background-color: var(--hl-bg2);
41+
}
42+
43+
.overlay {
44+
position: fixed;
45+
top: 0;
46+
left: 0;
47+
width: 100%;
48+
height: 100%;
49+
z-index: 100;
50+
}
51+
52+
.overlay::before {
53+
content: '';
54+
position: absolute;
55+
width: 100%;
56+
height: 100%;
57+
}
58+
</style>

src/store.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,17 @@ export const useMainStore = defineStore("main", () => {
437437
return false
438438
}
439439

440+
const clearFacet = (name: string) => {
441+
for (let f in facets.value) {
442+
if (facets.value[f].name !== name) {
443+
continue
444+
}
445+
facets.value[f].items.forEach(f => {
446+
f.selected = false
447+
})
448+
}
449+
}
450+
440451
const notificationBar = computed(() => {
441452

442453
return false // for until its finished
@@ -520,6 +531,7 @@ export const useMainStore = defineStore("main", () => {
520531

521532
facets,
522533
isFacetActive,
534+
clearFacet,
523535
searchbar,
524536
searchbarValid,
525537
searchClear,

src/stores/contextMenu.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { defineStore } from "pinia";
2+
import { ref } from "vue";
3+
import { useMainStore } from "../store";
4+
import { globalEventBus } from "../event_bus";
5+
6+
interface Action {
7+
label: string
8+
fn: () => void
9+
disabled?: () => boolean
10+
}
11+
12+
interface ActionColumnHeader {
13+
type: "column_header", name: string
14+
}
15+
interface ActionCell {
16+
type: "cell", value: string, columnId: string
17+
}
18+
19+
type ActionTypes = ActionColumnHeader | ActionCell
20+
21+
22+
export const useContextMenuStore = defineStore("context_menu", () => {
23+
24+
const show = (e: MouseEvent, type: ActionTypes) => {
25+
e.preventDefault();
26+
x.value = e.clientX + 1;
27+
y.value = e.clientY + 1;
28+
29+
switch (type.type) {
30+
case "cell":
31+
const column = useMainStore().layout.getColumn(type.columnId)
32+
if (column?.faceted) {
33+
let ff = useMainStore().facets[column.name].items.find(i => i.label == type.value)
34+
let label = "Filter by facet"
35+
if (ff?.selected) {
36+
label = "Unfilter by facet"
37+
}
38+
actions.value?.push({
39+
label,
40+
fn: () => {
41+
if (ff) {
42+
ff.selected = !ff.selected
43+
}
44+
hide()
45+
}
46+
})
47+
}
48+
actions.value?.push({
49+
label: "Search by value",
50+
fn: () => {
51+
let v = type.value
52+
switch (typeof v) {
53+
case "string":
54+
v = `"${v}"`
55+
break
56+
case "number":
57+
case "boolean":
58+
v = `${v}`
59+
break
60+
}
61+
globalEventBus.emit('searchbar-update', `data.${column.name} == ${v}`)
62+
hide()
63+
}
64+
})
65+
actions.value?.push({
66+
label: "Copy value",
67+
fn: () => {
68+
navigator.clipboard.writeText(type.value)
69+
hide()
70+
}
71+
})
72+
actions.value?.push({
73+
label: "Copy column name",
74+
fn: () => {
75+
navigator.clipboard.writeText(column.name)
76+
hide()
77+
}
78+
})
79+
break
80+
case "column_header":
81+
actions.value?.push({
82+
label: "Copy column name",
83+
fn: () => {
84+
navigator.clipboard.writeText(type.name)
85+
hide()
86+
}
87+
})
88+
actions.value?.push({
89+
label: "Clear facet values",
90+
fn: () => {
91+
useMainStore().clearFacet(type.name)
92+
hide()
93+
},
94+
disabled: () => !useMainStore().isFacetActive(type.name)
95+
})
96+
break
97+
}
98+
99+
display.value = true
100+
}
101+
102+
const hide = () => {
103+
display.value = false
104+
actions.value = []
105+
}
106+
107+
const display = ref<Boolean>(false)
108+
109+
const x = ref<Number>()
110+
const y = ref<Number>()
111+
112+
const actions = ref<Action[]>([])
113+
114+
115+
return {
116+
show,
117+
hide,
118+
x,
119+
y,
120+
display,
121+
actions
122+
};
123+
});

0 commit comments

Comments
 (0)