Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<link href="/images/SW_favicon.png" rel="icon" type="image/png" />

<!-- descriptive things -->
<title>AequilibraE Explore</title>
<title>Web Polaris</title>
<meta content="SimWrapper" name="twitter:title" />
<meta content="SimWrapper" name="og:title" />
<meta content="From the VSP team at TU-Berlin" name="twitter:description" />
Expand Down
1,187 changes: 956 additions & 231 deletions package-lock.json

Large diffs are not rendered by default.

45 changes: 0 additions & 45 deletions simwrapper.code-workspace

This file was deleted.

1 change: 1 addition & 0 deletions src/dash-panels/_allPanels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const panelLookup: { [key: string]: AsyncComponent } = {

// full-screen map visualizations:
aequilibrae: defineAsyncComponent(() => import('./aequilibrae.vue')),
polaris: defineAsyncComponent(() => import('./polaris.vue')),
carriers: defineAsyncComponent(() => import('./carriers.vue')),
flowmap: defineAsyncComponent(() => import('./flowmap.vue')),
links: defineAsyncComponent(() => import('./links.vue')),
Expand Down
178 changes: 178 additions & 0 deletions src/dash-panels/polaris.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<template lang="pug">
.polaris-panel
.loading(v-if="isLoading") Loading polaris.yaml...
.error(v-else-if="loadError") {{ loadError }}
.polaris-scroll-content(v-else)
//- Render items in YAML order
.polaris-item(v-for="(item, idx) in displayItems" :key="idx" :class="{'is-map': item.type === 'map'}" :style="getItemStyle(item)")
.polaris-item-title(v-if="item.title") {{ item.title }}
polaris-reader(
:root="rootSlug"
:subfolder="item.subfolder"
:config="item.config"
:itemType="item.type"
:thumbnail="false"
@isLoaded="isLoaded"
@error="$emit('error', $event)"
)

</template>

<script lang="ts">
import { defineComponent } from 'vue'
import YAML from 'yaml'

import PolarisReader from '@/plugins/polaris/PolarisReader.vue'
import HTTPFileSystem from '@/js/HTTPFileSystem'
import { FileSystemConfig } from '@/Globals'

export default defineComponent({
name: 'PolarisPanel',
components: { PolarisReader },
props: {
// Plugin mode props
root: { type: String, default: '' },
// Dashboard panel mode props
config: Object,
datamanager: Object,
fileSystemConfig: Object,
subfolder: { type: String, default: '' },
yamlConfig: String,
thumbnail: Boolean,
},
data() {
return {
loadedItems: [] as any[],
isLoading: true,
loadError: '',
}
},
computed: {
// Get file system config from props or store
fsConfig(): FileSystemConfig | null {
if (this.fileSystemConfig) return this.fileSystemConfig as FileSystemConfig
if (this.root) {
return this.$store.state.svnProjects.find((p: FileSystemConfig) => p.slug === this.root) || null
}
return null
},
rootSlug(): string {
return this.fsConfig?.slug || this.root || ''
},
displayItems(): Array<{ title?: string; subfolder: string; config: any; type: string; height?: number }> {
// Use loadedItems if we loaded from YAML, otherwise use config prop
const items = this.loadedItems.length ? this.loadedItems : (Array.isArray(this.config) ? this.config : [])
return items.map((item: any) => ({
title: item.item || item.title,
type: item.type || 'dashboard',
subfolder: item.subfolder || this.subfolder,
config: { ...item },
height: item.height,
}))
},
},
methods: {
getItemStyle(item: { type: string; height?: number }): any {
// For map items, apply height from config (height * 60 = pixels)
if (item.type === 'map' && item.height) {
const heightPx = item.height * 60
return { height: `${heightPx}px`, minHeight: `${heightPx}px` }
}
return {}
},
isLoaded() {
this.$emit('isLoaded')
},
},
async mounted() {
// If config is already an array (passed from dashboard), use it directly
if (Array.isArray(this.config) && this.config.length) {
this.isLoading = false
return
}

// Otherwise, load polaris.yaml from the subfolder
try {
if (!this.fsConfig) {
throw new Error('No file system configuration available')
}

const fileApi = new HTTPFileSystem(this.fsConfig)
const yamlPath = `${this.subfolder}/polaris.yaml`
const yamlText = await fileApi.getFileText(yamlPath)
const parsed = YAML.parse(yamlText)

// Extract webpolaris items array
if (Array.isArray(parsed.webpolaris)) {
this.loadedItems = parsed.webpolaris
} else if (parsed.webpolaris) {
// Legacy object format - wrap in array with single item
this.loadedItems = [{ type: 'map', ...parsed.webpolaris }]
}

this.isLoading = false
} catch (e) {
this.loadError = `Failed to load polaris.yaml: ${e}`
this.isLoading = false
}
},
})
</script>

<style scoped lang="scss">
@import '@/styles.scss';

.polaris-panel {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}

.polaris-scroll-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 0.5rem;
}

.loading, .error {
padding: 2rem;
text-align: center;
font-size: 1.1rem;
color: var(--textMuted, #666);
}

.error {
color: #c00;
}

.polaris-item {
position: relative;
z-index: 1;
margin-bottom: 0.75rem;
border: 1px solid rgba(0,0,0,0.08);
border-radius: 8px;
overflow: hidden;
background: var(--bgCardFrame, #fff);
box-shadow: 0 2px 6px rgba(0,0,0,0.06);
flex-shrink: 0;
}

.polaris-item.is-map {
/* Default height, can be overridden by inline :style binding */
height: 450px;
min-height: 360px;
}

.polaris-item-title {
padding: 0.5rem 0.75rem;
font-weight: 600;
border-bottom: 1px solid rgba(0,0,0,0.08);
background: linear-gradient(135deg, rgba(78,121,167,0.06), rgba(89,161,79,0.06));
}
</style>
9 changes: 9 additions & 0 deletions src/layout-manager/DashBoard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ export default defineComponent({
this.setupRows(subtab.layout, subtab.subtabFolder)
} else if (this.yaml.layout) {
this.setupRows(this.yaml.layout)
} else if (this.yaml.webpolaris && Array.isArray(this.yaml.webpolaris)) {
// Convert webpolaris array format to dashboard layout format
const layout = {}
this.yaml.webpolaris.forEach((item: any, index: number) => {
// Create a row for each webpolaris item
const rowKey = item.item || item.title || `item-${index}`
layout[rowKey] = [item]
})
this.setupRows(layout)
} else {
this.$store.commit(
'error',
Expand Down
52 changes: 32 additions & 20 deletions src/layout-manager/TabbedDashboardView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
:style="{opacity: tab===activeTab ? 1.0 : 0.75}"
@click="switchLeftTab(tab,index)"
)
a(v-if="dashboards[tab].header"
a(v-if="dashboards[tab]?.header"
@click="switchLeftTab(tab,index)"
) {{ dashboards[tab].header.tab }}
) {{ dashboards[tab]?.header?.tab || tab }}

//- mobile: dashboard dropdown-button
.dashboard-mobile-section(v-show="!isZoomed && Object.keys(dashboards).length > 1 && isMobile")
.dropdown
b-button.dropbtn(@click="dropDownClicked()") {{ dashboards[activeTab].header.tab || 'Dashboards' }}
b-button.dropbtn(@click="dropDownClicked()") {{ dashboards[activeTab]?.header?.tab || 'Dashboards' }}
i.fa.fa-caret-down

.dropdown-content(v-if="showDropDown")
Expand All @@ -31,11 +31,11 @@
:style="{opacity: tab===activeTab ? 1.0 : 0.75}"
@click="switchLeftTab(tab,index)"
)
a(v-if="dashboards[tab].header" @click="switchLeftTab(tab,index)") {{ dashboards[tab].header.tab }}
a(v-if="dashboards[tab]?.header" @click="switchLeftTab(tab,index)") {{ dashboards[tab]?.header?.tab || tab }}

//-- The actual dashboard for this tab (if there is one) ------------------
.dashboard-content(
v-if="dashboardTabWithDelay && dashboardTabWithDelay !== 'FILE__BROWSER' && dashboards[dashboardTabWithDelay] && dashboards[dashboardTabWithDelay].header.tab !== '...'"
v-if="dashboardTabWithDelay && dashboardTabWithDelay !== 'FILE__BROWSER' && dashboards[dashboardTabWithDelay] && dashboards[dashboardTabWithDelay].header && dashboards[dashboardTabWithDelay].header.tab !== '...'"
:class="{'is-breadcrumbs-hidden': !isShowingBreadcrumbs && !isZoomed}"
)
dash-board(
Expand Down Expand Up @@ -103,7 +103,7 @@ export default defineComponent({
allConfigFiles: { dashboards: {}, topsheets: {}, vizes: {}, configs: {} } as YamlConfigs,
crumbs: [] as any,
customCSS: '',
dashboards: [] as any[],
dashboards: {} as Record<string, any>,
dashboardDataManager: null as DashboardDataManager | null,
dashboardTabWithDelay: '',
finalFolder: '',
Expand Down Expand Up @@ -290,24 +290,36 @@ export default defineComponent({
}

// // Start on correct tab
const dashboardKeys = Object.keys(this.dashboards)
if (this.$route.query.tab) {
if (this.$route.query.tab === 'files') {
this.activeTab = 'FILE__BROWSER'
} else {
try {
const userSupplied = parseInt('' + this.$route.query.tab) - 1
const userTab = dashboardKeys[userSupplied]
this.activeTab = userTab || dashboardKeys[0]
} catch (e) {
// user spam; just use first tab
this.activeTab = dashboardKeys[0]
let dashboardKeys = Object.keys(this.dashboards)

// If nothing to show, prefer Files tab when available
if (!dashboardKeys.length && this.globalState.isShowingFilesTab) {
Vue.set(this.dashboards, 'FILE__BROWSER', { header: { tab: 'Files' } })
dashboardKeys = Object.keys(this.dashboards)
}

if (dashboardKeys.length) {
if (this.$route.query.tab) {
if (this.$route.query.tab === 'files') {
this.activeTab = 'FILE__BROWSER'
} else {
try {
const userSupplied = parseInt('' + this.$route.query.tab) - 1
const userTab = dashboardKeys[userSupplied]
this.activeTab = userTab || dashboardKeys[0]
} catch (e) {
this.activeTab = dashboardKeys[0]
}
}
} else {
this.activeTab = dashboardKeys[0]
}
this.dashboardTabWithDelay = this.activeTab
} else {
this.activeTab = dashboardKeys[0]
// no dashboards and no files: keep empty but avoid render crashes
this.activeTab = ''
this.dashboardTabWithDelay = ''
}
this.dashboardTabWithDelay = this.activeTab
} catch (e) {
// Bad things happened! Tell user
console.warn({ eeee: e })
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/pluginRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ const plugins = [
kebabName: "aeq-reader",
filePatterns: ['**/aeqviz-*.y?(a)ml'],
component: defineAsyncComponent(() => import('./aequilibrae/AequilibraEReader.vue')),
},
{
kebabName: "polaris-reader",
filePatterns: ['**/polaris.y?(a)ml'],
component: defineAsyncComponent(() => import('@/dash-panels/polaris.vue')),
}
]

Expand Down
Loading