separate indexing for states & nodes

This commit is contained in:
David Mosbach 2023-06-04 01:09:44 +02:00
parent f66a6247bb
commit 209d9a2763
3 changed files with 146 additions and 103 deletions

View File

@ -19,7 +19,7 @@ body {
width: 100%; width: 100%;
position: fixed; position: fixed;
padding-left: 10px; padding-left: 10px;
z-index: 5; z-index: 11;
float: left; float: left;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
@ -55,16 +55,14 @@ body {
color: rgb(75, 151, 151); color: rgb(75, 151, 151);
} }
#searchmenu {
width: 400px;
overflow: hidden;
}
#searchmenu #search-container {
/* position: fixed; .menuitem #search-container {
position: fixed;
top: 0px; top: 0px;
bottom: 0px; */ bottom: 0px;
padding: 5 5 5 5; width: 400px;
padding: 2 2 2 2;
background-color: transparent; background-color: transparent;
border-top: 4px solid transparent; border-top: 4px solid transparent;
border-bottom: 4px solid transparent; border-bottom: 4px solid transparent;
@ -74,27 +72,27 @@ body {
} }
#search-container input { #search-container input {
/* height: 100%; */ height: 100%;
height: 40px; /* height: 40px; */
width: 100%; width: 100%;
border-radius: 15px; border-radius: 10px;
border: 1px solid rgb(117, 117, 117); border: .5px solid rgba(0, 0, 0, 0.123);
padding-left: 10px; padding-left: 38px;
padding-right: 38px; padding-right: 10px;
outline: none; outline: none;
transition: all 100ms ease-out 50ms; transition: all 100ms ease-out 50ms;
} }
#search-container:hover input, #search-container input:active, #search-container input:focus { #search-container:hover input, #search-container input:active, #search-container input:focus {
border: 2px solid rgb(117, 117, 117); /* border: 2px solid rgb(117, 117, 117); */
/* box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); */ box-shadow: 0 0px 10px 0 rgba(0, 0, 0, 0.12);
padding-left: 9px; /* padding-left: 9px;
padding-right: 37px; padding-right: 37px; */
} }
#search-container input:focus { #search-container input:focus {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); box-shadow: 0 0px 10px 0 rgba(0, 0, 0, 0.24);
transform: scale(1.02); /* transform: scale(1.02); */
} }
::selection { ::selection {
@ -111,29 +109,33 @@ body {
align-items: center; align-items: center;
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
top: 10; top: 7;
right: 10; left: 7;
background-color: transparent; background-color: transparent;
transition: all 100ms ease-out 50ms; transition: all 100ms ease-out 50ms;
/* background-color: rgb(120, 120, 120); */ /* background-color: rgb(120, 120, 120); */
} }
#search-container .search-button:hover {
transform: scale(1.1);
}
#search-container .search-button svg { #search-container .search-button svg {
stroke: rgb(117, 117, 117); stroke: rgb(117, 117, 117);
} }
#search-results { #search-results {
display: none; width: 400px;
position: relative; overflow: hidden;
padding: 20 20 20 20; }
#search-options {
padding: 15px;
width: 100%;
}
#search-result-list {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
#search-results div { #search-result-list div {
line-height: 2; line-height: 2;
padding-top: 5px; padding-top: 5px;
padding-bottom: 5px; padding-bottom: 5px;
@ -143,15 +145,27 @@ body {
transition: all 100ms ease-out 50ms; transition: all 100ms ease-out 50ms;
} }
#search-results div:hover { #search-result-list div:hover {
font-weight: 500; font-weight: 500;
color: rgb(75, 151, 151); color: rgb(75, 151, 151);
} }
#search-results div:active { #search-result-list div:active {
font-weight: 600; font-weight: 600;
} }
.checkbox {
/* position: relative;
display: block; */
padding-right: 10px;
cursor: pointer;
}
.checkbox input {
cursor: pointer;
}
.submenu { .submenu {
font-family: 'Inter'; font-family: 'Inter';
width: fit-content; width: fit-content;

View File

@ -126,18 +126,31 @@
<div class="submenu menu-lightmode">Visualiser & editor for Uni2work workflows</div> <div class="submenu menu-lightmode">Visualiser & editor for Uni2work workflows</div>
</div> </div>
<div class="menuitem" onclick="openSearchMenu(this)"> <div class="menuitem" onclick="openSearchMenu(this)">
<div class="menubutton">Search</div> <div id="search-container">
<div id="searchmenu" class="submenu menu-lightmode" onclick="event.stopPropagation()"> <input id="search-input" type="text" placeholder="Search" onclick="showSearchResults()" oninput="search(this.value)">
<div id="search-container"> <span class="search-button">
<input id="search-input" type="text" placeholder="Nodes, Edges, ..." onclick="showSearchResults()" oninput="search(this.value)"> <svg height="18" width="18" xmlns="http://www.w3.org/2000/svg">
<span class="search-button"> <circle cx="11" cy="7" r="6" stroke-width="2" fill="none" />
<svg height="18" width="18" xmlns="http://www.w3.org/2000/svg"> <polyline points="8,10 2,16" style="fill:none;stroke-width:2" stroke-linecap="round" />
<circle cx="11" cy="7" r="6" stroke-width="2" fill="none" /> </svg>
<polyline points="8,10 2,16" style="fill:none;stroke-width:2" stroke-linecap="round" /> </span>
</svg> </div>
</span> <div id="search-results" class="submenu menu-lightmode" onclick="event.stopPropagation()">
<div id="search-options">
<label class="checkbox" title="Nodes">
<input id="search-option-states" type="checkbox" checked="checked">
Nodes
</label>
<label class="checkbox" title="Edges">
<input id="search-option-edges" type="checkbox" checked="checked">
Edges
</label>
<label class="checkbox" title="Roles">
<input id="search-option-roles" type="checkbox">
Roles
</label>
</div> </div>
<div id="search-results"></div> <div id="search-result-list"></div>
</div> </div>
</div> </div>
<div class="menuitem"> <div class="menuitem">

136
editor.js
View File

@ -52,6 +52,20 @@ var selectedMenuItem = null;
Array.from(document.getElementsByClassName('submenu')).forEach(subMenu => subMenu.style.top = mainMenu.offsetHeight + 15); Array.from(document.getElementsByClassName('submenu')).forEach(subMenu => subMenu.style.top = mainMenu.offsetHeight + 15);
var lastSubMenu = null;
function positionSubmenuBackdrop() {
var smRect = lastSubMenu.getBoundingClientRect();
submenuBackdrop.style.top = smRect.top;// sideHeading.offsetHeight + parseFloat(smStyle.paddingTop) + parseFloat(shStyle.marginTop) + parseFloat(shStyle.marginBottom);
submenuBackdrop.style.left = smRect.left;
submenuBackdrop.style.width = lastSubMenu.offsetWidth;
submenuBackdrop.style.height = lastSubMenu.offsetHeight;
// var sbStyle = window.getComputedStyle(sideButtons);
// sideContent.style.bottom = sideButtons.offsetHeight + parseFloat(smStyle.paddingBottom) + parseFloat(sbStyle.marginTop) + parseFloat(sbStyle.marginBottom);
// console.log(sideHeading.offsetHeight + shStyle.marginTop + shStyle.marginBottom);
// var width =
}
/** /**
* *
* @param {HTMLElement} menuitem * @param {HTMLElement} menuitem
@ -71,24 +85,11 @@ function openMenuItem(menuitem) {
fadeOut(null, ...fadeOuts); fadeOut(null, ...fadeOuts);
menuitem.classList.add('selectedmenuitem'); menuitem.classList.add('selectedmenuitem');
var fadeIns = [{element: submenuBackdrop, max: 1}]; var fadeIns = [{element: submenuBackdrop, max: 1}];
var lastSubMenu = null;
Array.from(menuitem.getElementsByClassName('submenu')).forEach(subMenu => { Array.from(menuitem.getElementsByClassName('submenu')).forEach(subMenu => {
fadeIns.push({element: subMenu, max: 1}); fadeIns.push({element: subMenu, max: 1});
lastSubMenu = subMenu; lastSubMenu = subMenu;
}); });
function callback() { fadeIn(positionSubmenuBackdrop, ...fadeIns);
var smRect = lastSubMenu.getBoundingClientRect();
console.log(smRect);
submenuBackdrop.style.top = smRect.top;// sideHeading.offsetHeight + parseFloat(smStyle.paddingTop) + parseFloat(shStyle.marginTop) + parseFloat(shStyle.marginBottom);
submenuBackdrop.style.left = smRect.left;
submenuBackdrop.style.width = lastSubMenu.offsetWidth;
submenuBackdrop.style.height = lastSubMenu.offsetHeight;
// var sbStyle = window.getComputedStyle(sideButtons);
// sideContent.style.bottom = sideButtons.offsetHeight + parseFloat(smStyle.paddingBottom) + parseFloat(sbStyle.marginTop) + parseFloat(sbStyle.marginBottom);
// console.log(sideHeading.offsetHeight + shStyle.marginTop + shStyle.marginBottom);
// var width =
}
fadeIn(callback, ...fadeIns);
selectedMenuItem = menuitem; selectedMenuItem = menuitem;
} }
@ -128,59 +129,70 @@ function openAboutMenu(menuitem) {
} }
function openSearchMenu(menuitem) { function openSearchMenu(menuitem) {
document.getElementById('search-input').value = null; if (selectedMenuItem === menuitem) return;
while (searchResults.firstChild) var val = document.getElementById('search-input').value;
searchResults.removeChild(searchResults.lastChild); if (val === '' || val === null)
while (searchResultList.firstChild)
searchResultList.removeChild(searchResultList.lastChild);
openMenuItem(menuitem); openMenuItem(menuitem);
} }
document.getElementById('filepanel').style.opacity = 0; document.getElementById('filepanel').style.opacity = 0;
//Search //Search
const searchIndex = new FlexSearch.Index({tokenize: 'forward'}); const nodeIndex = new FlexSearch.Index({tokenize: 'forward'});
const actionIndex = new FlexSearch.Index({tokenize: 'forward'});
// const searchDocument = new FlexSearch.Document(); // const searchDocument = new FlexSearch.Document();
// const searchWorker = new FlexSearch.Worker(); // const searchWorker = new FlexSearch.Worker();
const soStates = document.getElementById('search-option-states');
const soEdges = document.getElementById('search-option-edges');
function search(text) { function search(text) {
while (searchResults.firstChild) while (searchResultList.firstChild)
searchResults.removeChild(searchResults.lastChild); searchResultList.removeChild(searchResultList.lastChild);
var results = searchIndex.search(text, 10); var searchStates = soStates.checked;
results.forEach(result => { var searchActions = soEdges.checked
var target = null; var stateResults = searchStates ? nodeIndex.search(text, searchActions ? 5 : 10) : null;
if (result.startsWith('state_name_')) { var actionResults = searchActions ? actionIndex.search(text, searchStates ? 5 : 10) : null;
result = result.replace('state_name_', '');
workflow.states.forEach(state => { function defineFocus(div, target) {
if (state.id === result) div.onclick = (_ => {
target = state; var x = y = null;
if (target.actionData) {
x = target.source.x + (target.target.x - target.source.x);
y = target.source.y + (target.target.y - target.source.y);
} else {
x = target.x;
y = target.y;
}
document.getElementById('search-input').value = null;
closeMenuItem();
wfGraph.centerAt(x, y, 400);
wfGraph.zoom(5, 400);
select(target);
});
}
function format(possibleTargets, results, heading) {
var h = document.createElement('h3');
h.innerHTML = heading;
searchResultList.appendChild(h);
results.forEach(result => {
var target = null;
possibleTargets.forEach(stateOrEdge => {
if (stateOrEdge.id === result)
target = stateOrEdge;
}); });
} else { var r = document.createElement('div');
result = result.replace('action_name_', ''); r.innerHTML = target.name;
workflow.actions.forEach(action => { searchResultList.appendChild(r);
if (action.id === result) defineFocus(r, target);
target = action; })
}); }
}
var r = document.createElement('div'); stateResults && format(workflow.states, stateResults, 'States');
r.innerHTML = target.name; actionResults && format(workflow.actions, actionResults, 'Edges');
searchResults.appendChild(r); positionSubmenuBackdrop();
function defineFocus(div, target) {
div.onclick = (_ => {
var x = y = null;
if (target.actionData) {
x = target.source.x + (target.target.x - target.source.x);
y = target.source.y + (target.target.y - target.source.y);
} else {
x = target.x;
y = target.y;
}
closeMenuItem();
wfGraph.centerAt(x, y, 400);
wfGraph.zoom(5, 400);
select(target);
});
}
defineFocus(r, target);
})
} }
function showSearchResults() { function showSearchResults() {
@ -369,8 +381,9 @@ const contextMenuBg = document.getElementById('ctmenubg'); //Click on background
const contextMenuSt = document.getElementById('ctmenust'); //Click on state const contextMenuSt = document.getElementById('ctmenust'); //Click on state
const contextMenuEd = document.getElementById('ctmenued'); //Click on edge const contextMenuEd = document.getElementById('ctmenued'); //Click on edge
//Search //Search
const searchContainer = document.getElementById('search-container'); const searchContainer = document.getElementById('search-container');
const searchResults = document.getElementById('search-results'); const searchResults = document.getElementById('search-results');
const searchResultList = document.getElementById('search-result-list');
// Counters for placeholder IDs of states/actions added via GUI // Counters for placeholder IDs of states/actions added via GUI
var stateIdCounter = 0; var stateIdCounter = 0;
var actionIdCounter = 0; var actionIdCounter = 0;
@ -378,6 +391,9 @@ var stateAbbreviations = [];
var newStateCoords = {'x': 0, 'y': 0}; //Initial coordinates of the next new state var newStateCoords = {'x': 0, 'y': 0}; //Initial coordinates of the next new state
sidePanel.style.top = mainMenu.offsetHeight + 15; sidePanel.style.top = mainMenu.offsetHeight + 15;
searchContainer.style.left = mainMenu.offsetWidth / 2 - searchContainer.offsetWidth / 2;
searchResults.style.left = searchContainer.style.left;
searchResults.style.maxHeight = 0.6 * window.innerHeight;
/** /**
@ -633,7 +649,7 @@ function prepareWorkflow() {
state.stateData.viewers.forEach(v => viewers.push(new Role(v))); state.stateData.viewers.forEach(v => viewers.push(new Role(v)));
state.stateData.viewers = viewers; state.stateData.viewers = viewers;
state.stateData.payload = new Payload(state.stateData.payload); state.stateData.payload = new Payload(state.stateData.payload);
searchIndex.add('state_name_' + state.id, state.name); nodeIndex.add(state.id, state.name);
}) })
workflow.actions.forEach(action => { workflow.actions.forEach(action => {
@ -650,7 +666,7 @@ function prepareWorkflow() {
action.actionData['actor Viewers'].forEach(v => viewActors.push(new Role(v))); action.actionData['actor Viewers'].forEach(v => viewActors.push(new Role(v)));
action.actionData['actor Viewers'] = viewActors; action.actionData['actor Viewers'] = viewActors;
action.actionData.form = new Payload(action.actionData.form); action.actionData.form = new Payload(action.actionData.form);
searchIndex.add('action_name_' + action.id, action.name); actionIndex.add(action.id, action.name);
}) })
workflow.actions.forEach(act => act.actionData.actors.forEach(a => { workflow.actions.forEach(act => act.actionData.actors.forEach(a => {