Skip to content

Commit

Permalink
added communication between tabs (#25)
Browse files Browse the repository at this point in the history
* added data propagation between tabs

* added highlight from reg. tab to func
  • Loading branch information
ivicic-petr authored Dec 14, 2023
1 parent aa18977 commit 08c3a7b
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 156 deletions.
25 changes: 13 additions & 12 deletions src/html/component/content-pane/content-pane.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { css, html, LitElement, type TemplateResult, unsafeCSS } from 'lit'
import { customElement, property } from 'lit/decorators.js'
import style_less from './content-pane.less?inline'
import { type TabData } from '../../util/tab-data'
import { library, icon, findIconDefinition } from '@fortawesome/fontawesome-svg-core'
import { ContentData, TabData } from '../../util/tab-data'
import { library, icon } from '@fortawesome/fontawesome-svg-core'
import '../regulations-editor/regulations-editor'
import '../functions-editor/functions-editor'
import { faLock, faLockOpen } from '@fortawesome/free-solid-svg-icons'
import { aeonState } from '../../../aeon_events'

library.add(faLock, faLockOpen)

@customElement('content-pane')
export class ContentPane extends LitElement {
static styles = css`${unsafeCSS(style_less)}`

@property() declare private readonly tab: TabData
@property() private readonly tab: TabData = TabData.create()
@property() private readonly data = ContentData.create()

private pin (): void {
if (this.tab.pinned) {
Expand All @@ -23,15 +26,13 @@ export class ContentPane extends LitElement {
}

protected render (): TemplateResult {
const locked = icon(findIconDefinition({ prefix: 'fas', iconName: 'lock' })).node
const unlocked = icon(findIconDefinition({ prefix: 'fas', iconName: 'lock-open' })).node
return html`
<div class="content-pane uk-container uk-container-expand">
<button class="uk-button uk-button-small uk-button-secondary pin-button" @click="${this.pin}">
${this.tab.pinned ? locked : unlocked}
</button>
${this.tab.data}
</div>
`
<div class="content-pane uk-container uk-container-expand">
<button class="uk-button uk-button-small uk-button-secondary pin-button" @click="${this.pin}">
${this.tab.pinned ? icon(faLock).node : icon(faLockOpen).node}
</button>
${this.tab.content(this.data)}
</div>
`
}
}
6 changes: 6 additions & 0 deletions src/html/component/functions-editor/functions-editor.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import "uikit/src/less/uikit.theme.less";

.function-list {
height: 90%;
overflow: auto;
}
33 changes: 33 additions & 0 deletions src/html/component/functions-editor/functions-editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { html, css, unsafeCSS, LitElement, type TemplateResult } from 'lit'
import { customElement, property, state } from 'lit/decorators.js'
import style_less from './functions-editor.less?inline'
import { map } from 'lit/directives/map.js'
import { ContentData } from '../../util/tab-data'

@customElement('functions-editor')
class FunctionsEditor extends LitElement {
static styles = css`${unsafeCSS(style_less)}`
@property() contentData: ContentData = ContentData.create()
@state() focusedFunctionId = ''

constructor () {
super()
this.addEventListener('focus-function', this.focusedFunction)
}

private focusedFunction (event: Event): void {
this.focusedFunctionId = (event as CustomEvent).detail.nodeId
}

protected render (): TemplateResult {
return html`
<ul class="function-list uk-list uk-list-divider uk-text-center">
${map(this.contentData?.nodes, (node) => html`
<li class="${node.id === this.focusedFunctionId ? 'uk-background-primary' : ''} uk-margin-remove-top uk-padding-small">
${node.id} / ${node.name}
</li>
`)}
</ul>
`
}
}
13 changes: 12 additions & 1 deletion src/html/component/regulations-editor/float-menu/float-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class FloatMenu extends LitElement {
this.addEdge()
break
case 'F':
this.updateFunction()
break
case 'DELETE':
case 'BACKSPACE':
Expand Down Expand Up @@ -81,7 +82,7 @@ class FloatMenu extends LitElement {
{
icon: () => icon(faCalculator).node[0],
label: () => 'Edit update function (F)',
click: () => {}
click: this.updateFunction
},
{
icon: () => icon(faTrash).node[0],
Expand Down Expand Up @@ -197,6 +198,16 @@ class FloatMenu extends LitElement {
}))
}

private updateFunction (): void {
this.dispatchEvent(new CustomEvent('update-function', {
detail: {
nodeId: this.data?.id
},
bubbles: true,
composed: true
}))
}

render (): TemplateResult {
let buttons: IButton[]
let yOffset = 0
Expand Down
16 changes: 16 additions & 0 deletions src/html/component/regulations-editor/graph-interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { type Position } from 'cytoscape'
import { type Monotonicity } from './element-type'

export interface INodeData {
id: string
name: string
position: Position
}

export interface IEdgeData {
id: string
source: string
target: string
observable: boolean
monotonicity: Monotonicity
}
145 changes: 37 additions & 108 deletions src/html/component/regulations-editor/regulations-editor.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { css, html, LitElement, type TemplateResult, unsafeCSS } from 'lit'
import { customElement, query, state } from 'lit/decorators.js'
import { customElement, property, query, state } from 'lit/decorators.js'
import style_less from './regulations-editor.less?inline'
import cytoscape, { type Core, type EdgeSingular, type NodeSingular, type Position } from 'cytoscape'
import dagre from 'cytoscape-dagre'
import edgeHandles, { type EdgeHandlesInstance } from 'cytoscape-edgehandles'
import dblclick from 'cytoscape-dblclick'
import './float-menu/float-menu'
import { edgeOptions, initOptions } from './regulations-editor.config'
import { ElementType, Monotonicity } from './element-type'
import { ElementType, type Monotonicity } from './element-type'
import { dialog } from '@tauri-apps/api'
import { appWindow, WebviewWindow } from '@tauri-apps/api/window'
import { type Event as TauriEvent } from '@tauri-apps/api/event'
import UIkit from 'uikit'
import { type IEdgeData, type INodeData } from './graph-interfaces'
import { dummyData } from '../../util/dummy-data'
import { ContentData } from '../../util/tab-data'

const SAVE_NODES = 'nodes'
const SAVE_EDGES = 'edges'
Expand All @@ -31,8 +34,7 @@ class RegulationsEditor extends LitElement {
cy: Core | undefined
edgeHandles: EdgeHandlesInstance | undefined
lastTabCount = 1
@state() _nodes: INodeData[] = []
@state() _edges: IEdgeData[] = []
@property() contentData = ContentData.create()
@state() menuType = ElementType.NONE
@state() menuPosition = { x: 0, y: 0 }
@state() menuZoom = 1.0
Expand All @@ -48,18 +50,27 @@ class RegulationsEditor extends LitElement {
this.addEventListener('adjust-graph', this.adjustPan)
this.addEventListener('add-edge', this.addEdge)
this.addEventListener('mousemove', this.hoverFix)
this.addEventListener('remove-element', (e) => { void this.removeElement(e) })
this.addEventListener('rename-node', (e) => { void this.renameNode(e) })
this.addEventListener('remove-element', (e) => {
void this.removeElement(e)
})
this.addEventListener('rename-node', (e) => {
void this.renameNode(e)
})
this.addEventListener('update-function', () => { this.toggleMenu(ElementType.NONE) })

this.editorElement = document.createElement('div')
this.editorElement.id = 'cytoscape-editor'
}

render (): TemplateResult {
return html`
<button @click=${this.loadDummyData} class="uk-button uk-button-danger uk-button-small uk-margin-large-left uk-position-absolute uk-position-z-index-high">reset (debug)</button>
${this.editorElement}
<float-menu .type=${this.menuType} .position=${this.menuPosition} .zoom=${this.menuZoom} .data=${this.menuData}></float-menu>
<button @click=${this.loadDummyData}
class="uk-button uk-button-danger uk-button-small uk-margin-large-left uk-position-absolute uk-position-z-index-high">
reset (debug)
</button>
${this.editorElement}
<float-menu .type=${this.menuType} .position=${this.menuPosition} .zoom=${this.menuZoom}
.data=${this.menuData}></float-menu>
`
}

Expand Down Expand Up @@ -88,7 +99,7 @@ class RegulationsEditor extends LitElement {
this.cy = cytoscape(initOptions(this.editorElement))
this.edgeHandles = this.cy.edgehandles(edgeOptions)

this.cy.on('add remove position', this.saveState)
this.cy.on('add remove position ehcomplete', this.saveState)

this.cy.on('zoom', () => {
this.renderMenuForSelectedNode()
Expand All @@ -102,6 +113,7 @@ class RegulationsEditor extends LitElement {
if (e.target !== this.cy) return // dont trigger when mouse is over cy elements
const name = (Math.random() + 1).toString(36).substring(8).toUpperCase()
this.addNode(name, name, e.position)
this.saveState()
})
this.cy.on('mouseover', 'node', function (e) {
e.target.addClass('hover')
Expand Down Expand Up @@ -219,7 +231,7 @@ class RegulationsEditor extends LitElement {
const node = this.cy?.$id(nodeId)
if (node === undefined) return
const position = node.position()
const edges = this._edges.filter((edge) => edge.source === nodeId || edge.target === nodeId)
const edges = this.contentData.edges.filter((edge) => edge.source === nodeId || edge.target === nodeId)
node.remove()
this.addNode(event.payload.id, event.payload.name, position)
edges.forEach((edge) => {
Expand All @@ -229,8 +241,11 @@ class RegulationsEditor extends LitElement {
this.ensureRegulation({ ...edge, target: event.payload.id })
}
})
this.saveState()
})
void renameDialog.onCloseRequested(() => {
this.dialogs[nodeId] = undefined
})
void renameDialog.onCloseRequested(() => { this.dialogs[nodeId] = undefined })
}

adjustPan (event: Event): void {
Expand Down Expand Up @@ -281,7 +296,7 @@ class RegulationsEditor extends LitElement {
this.saveState()
}

ensureRegulation (regulation: IRegulation): void {
ensureRegulation (regulation: IEdgeData): void {
// const currentEdge = this._findRegulationEdge(regulation.regulator, regulation.target)
// if (currentEdge !== undefined) {
// // Edge exists - just make sure to update data
Expand Down Expand Up @@ -345,13 +360,20 @@ class RegulationsEditor extends LitElement {
}
})
if (nodes.length > 0) {
this._nodes = nodes
localStorage.setItem(SAVE_NODES, JSON.stringify(nodes))
}
if (edges.length > 0) {
this._edges = edges
localStorage.setItem(SAVE_EDGES, JSON.stringify(edges))
}
this.contentData = ContentData.create({ nodes, edges })
this.shadowRoot?.dispatchEvent(new CustomEvent('update-data', {
detail: {
nodes,
edges
},
composed: true,
bubbles: true
}))
}

loadCachedNodes (): boolean {
Expand Down Expand Up @@ -390,96 +412,3 @@ class RegulationsEditor extends LitElement {
})
}
}

const dummyData: { nodes: INodeData[], edges: IEdgeData[] } = {
nodes: [
{
id: 'YOX1',
name: 'YOX1',
position: { x: 297, y: 175 }
},
{
id: 'CLN3',
name: 'CLN3',
position: { x: 128, y: 68 }
},
{
id: 'YHP1',
name: 'YHP1',
position: { x: 286, y: 254 }
},
{
id: 'ACE2',
name: 'ACE2',
position: { x: 74, y: 276 }
},
{
id: 'SWI5',
name: 'SWI5',
position: { x: 47, y: 207 }
},
{
id: 'MBF',
name: 'MBF',
position: { x: 219, y: 96 }
},
{
id: 'SBF',
name: 'SBF',
position: { x: 281, y: 138 }
},
{
id: 'HCM1',
name: 'HCM1',
position: { x: 305, y: 217 }
},
{
id: 'SFF',
name: 'SFF',
position: { x: 186, y: 302 }
}
],
edges: [
{ source: 'MBF', target: 'YOX1', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SBF', target: 'YOX1', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'YOX1', target: 'CLN3', observable: true, monotonicity: Monotonicity.INHIBITION, id: '' },
{ source: 'YHP1', target: 'CLN3', observable: true, monotonicity: Monotonicity.INHIBITION, id: '' },
{ source: 'ACE2', target: 'CLN3', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SWI5', target: 'CLN3', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'MBF', target: 'YHP1', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SBF', target: 'YHP1', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SFF', target: 'ACE2', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SFF', target: 'SWI5', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'CLN3', target: 'MBF', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'MBF', target: 'SBF', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'YOX1', target: 'SBF', observable: true, monotonicity: Monotonicity.INHIBITION, id: '' },
{ source: 'YHP1', target: 'SBF', observable: true, monotonicity: Monotonicity.INHIBITION, id: '' },
{ source: 'CLN3', target: 'SBF', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'MBF', target: 'HCM1', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SBF', target: 'HCM1', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'SBF', target: 'SFF', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' },
{ source: 'HCM1', target: 'SFF', observable: true, monotonicity: Monotonicity.ACTIVATION, id: '' }
]

}

interface IRegulation {
source: string
target: string
observable: boolean
monotonicity: Monotonicity
}

interface INodeData {
id: string
name: string
position: Position
}

interface IEdgeData {
id: string
source: string
target: string
observable: boolean
monotonicity: Monotonicity
}
Empty file.
Loading

0 comments on commit 08c3a7b

Please sign in to comment.