Skip to content

Commit 8073386

Browse files
committed
Workspace view: use dynamic async components in lumino widgets
Also fixes SimpleTree view post-vue3
1 parent 515a14b commit 8073386

File tree

10 files changed

+84
-142
lines changed

10 files changed

+84
-142
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module.exports = {
3737
objects: 'only-multiline',
3838
imports: 'only-multiline',
3939
exports: 'only-multiline',
40-
functions: 'never',
40+
functions: 'only-multiline',
4141
},
4242
],
4343
'template-curly-spacing': [

src/components/cylc/workflow/Lumino.vue

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
<div ref="main" class="main pa-2 fill-height">
1919
<!-- Lumino box panel gets inserted here -->
2020
</div>
21-
<div v-show="false">
22-
<!-- Hidden div acts as staging area for views before they are
23-
moved into a lumino widget -->
24-
<component
25-
v-for="(item, id) of views"
26-
:key="id"
27-
:ref="(ref) => setViewRef(id, ref)"
28-
:is="item.view"
29-
:tab-title="getTabTitle(item.view)"
30-
:workflow-name="workflowName"
31-
:initialOptions="item.initialOptions"
32-
class="h-100"
33-
/>
34-
</div>
21+
<template
22+
v-for="(item, id) in views"
23+
:key="id"
24+
>
25+
<Teleport :to="`#${id}`">
26+
<component
27+
:is="item.view"
28+
:workflow-name="workflowName"
29+
:initialOptions="item.initialOptions"
30+
class="h-100"
31+
/>
32+
</Teleport>
33+
</template>
3534
</template>
3635

3736
<script>
37+
import { startCase } from 'lodash'
3838
import LuminoWidget from '@/components/cylc/workflow/lumino-widget'
3939
import { BoxPanel, DockPanel, Widget } from '@lumino/widgets'
4040
@@ -74,27 +74,17 @@ export default {
7474
type: Array,
7575
required: true
7676
},
77-
/**
78-
* Prop to customize the tab title. Defaults to name.
79-
* If a component does not have the $component.$tabTitleProp
80-
* set, then we still revert to the old default $component.name.
81-
*/
82-
tabTitleProp: {
83-
type: String,
84-
default: 'name'
85-
}
8677
},
8778
8879
emits: [
8980
'lumino:activated',
9081
'lumino:deleted'
9182
],
9283
93-
data () {
94-
return {
95-
/** Keep track of views' refs separate from $refs in order to access
96-
* by ID */
97-
viewRefs: {}
84+
beforeCreate () {
85+
// Populate components
86+
for (const { name, component } of this.allViews) {
87+
this.$options.components[name] = component
9888
}
9989
},
10090
@@ -103,12 +93,6 @@ export default {
10393
* Box panel. In the next tick of Vue, the DOM element and the Vue element/ref are attached.
10494
*/
10595
created () {
106-
// We need to load each view used by this view/component.
107-
// See "local-registration" in Vue.js documentation.
108-
this.allViews.forEach(view => {
109-
this.$options.components[view.name] = view
110-
})
111-
11296
// create a box panel, which holds the dock panel, and controls its layout
11397
this.box = new BoxPanel({ direction: 'left-to-right', spacing: 0 })
11498
// create dock panel, which holds the widgets
@@ -152,40 +136,27 @@ export default {
152136
},
153137
154138
methods: {
155-
/** Keep track of views' refs separate from $refs, allowing access by ID */
156-
setViewRef (id, ref) {
157-
if (ref) {
158-
this.viewRefs[id] = ref
159-
} else {
160-
delete this.viewRefs[id]
161-
}
162-
},
163139
/**
164140
* Look for newly added views, creating a corresponding Lumino Widget
165141
* for each.
166142
*/
167143
syncWidgets (newVal, oldVal) {
168-
const { tabTitleProp } = this.$props
169144
for (const [id, item] of Object.entries(newVal)) {
170145
if (!(id in oldVal)) {
171-
const view = this.$options.components[item.view]
172-
const name = view[tabTitleProp] ?? view.name
173-
this.addWidget(id, name)
146+
this.addWidget(id, item.view)
174147
}
175148
}
176149
},
177150
178151
/**
179-
* Create a widget, add it to the dock, and move the corresponding view
180-
* from the hidden div into it.
152+
* Create a widget and add it to the dock.
181153
*/
182154
addWidget (id, name, onTop = true) {
183-
const luminoWidget = new LuminoWidget(id, name, /* closable */ true)
155+
const luminoWidget = new LuminoWidget(id, startCase(name), /* closable */ true)
184156
this.dock.addWidget(luminoWidget, { mode: 'tab-after' })
185157
// give time for Lumino's widget DOM element to be created
186158
this.$nextTick(() => {
187159
const widgetEl = document.getElementById(id)
188-
widgetEl.appendChild(this.viewRefs[id].$el)
189160
widgetEl.addEventListener('lumino:activated', this.onWidgetActivated)
190161
widgetEl.addEventListener('lumino:deleted', this.onWidgetDeleted)
191162
if (onTop) {
@@ -227,10 +198,6 @@ export default {
227198
widgetEl.removeEventListener('lumino:activated', this.onWidgetActivated)
228199
this.$emit('lumino:deleted', customEvent.detail)
229200
},
230-
231-
getTabTitle (viewName) {
232-
return this.$options.components[viewName].data().widget.title
233-
}
234201
}
235202
}
236203
</script>

src/components/cylc/workflow/Toolbar.vue

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
8989
<v-spacer class="mx-0" />
9090

9191
<v-btn
92+
v-if="$route.name === 'workspace'"
9293
class="add-view"
9394
color="primary"
9495
data-cy="add-view-btn"
@@ -101,20 +102,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
101102
<v-menu
102103
activator="parent"
103104
location="bottom"
104-
v-if="$route.name === 'workspace'"
105-
>
106-
<v-list class="pa-0">
105+
>
106+
<v-list>
107107
<v-list-item
108-
:id="`toolbar-add-${ view.name }-view`"
109108
v-for="view in views"
109+
:id="`toolbar-add-${ view.name }-view`"
110110
:key="view.name"
111111
@click="$emit('add', { viewName: view.name })"
112-
class="py-0 px-8 ma-0 c-add-view"
113112
>
114113
<template v-slot:prepend>
115-
<v-icon>{{ view.data().widget.icon }}</v-icon>
114+
<v-icon>{{ view.icon }}</v-icon>
116115
</template>
117-
<v-list-item-title>{{ view.name }}</v-list-item-title>
116+
<v-list-item-title>{{ startCase(view.name) }}</v-list-item-title>
118117
</v-list-item>
119118
</v-list>
120119
</v-menu>
@@ -148,6 +147,7 @@ import {
148147
mdiStop,
149148
mdiViewList
150149
} from '@mdi/js'
150+
import { startCase } from 'lodash'
151151
import toolbar from '@/mixins/toolbar'
152152
import WorkflowState from '@/model/WorkflowState.model'
153153
import graphql from '@/mixins/graphql'
@@ -158,20 +158,24 @@ import {
158158
159159
export default {
160160
name: 'Toolbar',
161+
161162
mixins: [
162163
toolbar,
163164
graphql
164165
],
166+
165167
props: {
166168
views: {
167169
type: Array,
168170
required: true
169171
}
172+
170173
},
174+
171175
emits: ['add'],
176+
172177
data: () => ({
173178
extended: false,
174-
// FIXME: remove local state once we have this data in the workflow - https://github.com/cylc/cylc-ui/issues/221
175179
svgPaths: {
176180
add: mdiPlusBoxMultiple,
177181
hold: mdiPause,
@@ -187,6 +191,7 @@ export default {
187191
stop: null
188192
}
189193
}),
194+
190195
computed: {
191196
...mapState('app', ['title']),
192197
...mapState('user', ['user']),
@@ -253,6 +258,7 @@ export default {
253258
}
254259
}
255260
},
261+
256262
watch: {
257263
isRunning () {
258264
this.expecting.play = null
@@ -264,6 +270,7 @@ export default {
264270
this.expecting.stop = null
265271
}
266272
},
273+
267274
methods: {
268275
onClickPlay () {
269276
this.$workflowService.mutate(
@@ -285,7 +292,7 @@ export default {
285292
}
286293
})
287294
},
288-
async onClickStop () {
295+
onClickStop () {
289296
this.$workflowService.mutate(
290297
'stop',
291298
this.currentWorkflow.id
@@ -297,7 +304,8 @@ export default {
297304
},
298305
toggleExtended () {
299306
this.extended = !this.extended
300-
}
307+
},
308+
startCase,
301309
}
302310
}
303311
</script>

src/views/Analysis.vue

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ import {
8888
platformOptions
8989
} from '@/components/cylc/analysis/filter'
9090
import {
91-
mdiChartLine,
9291
mdiRefresh
9392
} from '@mdi/js'
9493
@@ -185,10 +184,6 @@ export default {
185184
data () {
186185
const tasks = []
187186
return {
188-
widget: {
189-
title: 'analysis',
190-
icon: mdiChartLine
191-
},
192187
/** Defines controls which get added to the toolbar */
193188
groups: [
194189
{

src/views/Graph.vue

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ import {
108108
import { Graphviz } from '@hpcc-js/wasm/graphviz'
109109
import svgPanZoom from 'svg-pan-zoom'
110110
import {
111-
mdiGraph,
112111
mdiTimer,
113112
mdiImageFilterCenterFocus,
114113
mdiArrowCollapse,
@@ -219,10 +218,6 @@ export default {
219218
220219
data () {
221220
return {
222-
widget: {
223-
title: 'graph',
224-
icon: mdiGraph
225-
},
226221
// the graph orientation
227222
orientation: 'TB',
228223
// the auto-refresh timer

src/views/Log.vue

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
141141
<script>
142142
import {
143143
mdiClockOutline,
144-
mdiFileDocumentMultipleOutline,
145144
mdiFolderRefresh,
146145
mdiPowerPlugOff,
147146
mdiPowerPlug
@@ -253,11 +252,6 @@ export default {
253252
254253
data () {
255254
return {
256-
// metadata for the workspace view
257-
widget: {
258-
title: 'logs',
259-
icon: mdiFileDocumentMultipleOutline
260-
},
261255
// the log subscription query
262256
query: null,
263257
// list of log files for the selected workflow/task/job

src/views/SimpleTree.vue

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
106106
<script>
107107
import gql from 'graphql-tag'
108108
import { mapState, mapGetters } from 'vuex'
109-
import pageMixin from '@/mixins/index'
109+
import { getPageTitle } from '@/utils/index'
110110
import graphqlMixin from '@/mixins/graphql'
111111
import subscriptionComponentMixin from '@/mixins/subscriptionComponent'
112112
import SubscriptionQuery from '@/model/SubscriptionQuery.model'
113-
import { mdiTree } from '@mdi/js'
114113
115114
// Any fields that our view will use (e.g. TaskProxy.status) must be requested
116115
// in the query.
@@ -176,30 +175,18 @@ fragment JobData on Job {
176175
`
177176
178177
export default {
178+
name: 'SimpleTree',
179+
179180
// These mixins enable various functionalities.
180181
mixins: [
181-
pageMixin,
182182
graphqlMixin,
183183
subscriptionComponentMixin
184184
],
185185
186-
// This defines the component name, must be HTML safe.
187-
name: 'SimpleTree',
188-
189186
// This sets the page title.
190-
metaInfo () {
187+
head () {
191188
return {
192-
title: this.getPageTitle('App.workflow', { name: this.workflowName })
193-
}
194-
},
195-
196-
// The view must provide a title and icon for display purposes.
197-
data () {
198-
return {
199-
widget: {
200-
title: 'simple tree',
201-
icon: mdiTree
202-
}
189+
title: getPageTitle('App.workflow', { name: this.workflowName })
203190
}
204191
},
205192
@@ -238,7 +225,10 @@ export default {
238225

239226
<style lang="scss">
240227
.c-simple-tree {
241-
.state:before {
228+
ul, ol {
229+
padding-left: 24px;
230+
}
231+
.state::before {
242232
content: ' ';
243233
}
244234
.state {

src/views/Table.vue

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
2727

2828
<script>
2929
import { mapState, mapGetters } from 'vuex'
30-
import { mdiTable } from '@mdi/js'
3130
import { getPageTitle } from '@/utils/index'
3231
import graphqlMixin from '@/mixins/graphql'
3332
import subscriptionComponentMixin from '@/mixins/subscriptionComponent'
@@ -55,13 +54,6 @@ export default {
5554
}
5655
},
5756
58-
data: () => ({
59-
widget: {
60-
title: 'table',
61-
icon: mdiTable
62-
}
63-
}),
64-
6557
computed: {
6658
...mapState('workflows', ['cylcTree']),
6759
...mapGetters('workflows', ['getNodes']),

0 commit comments

Comments
 (0)