Skip to content

Commit

Permalink
Merge pull request #472 from FlowFuse/440-template-at-ui-page
Browse files Browse the repository at this point in the history
UI Template - Add "Page-Scoped" and "UI-Scoped" options for widget placement
  • Loading branch information
joepavitt authored Jan 4, 2024
2 parents 370f2b7 + 1d0260d commit 9703846
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 30 deletions.
4 changes: 3 additions & 1 deletion nodes/widgets/locales/en-US/ui_template.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"ui-template": {
"label": {
"scope": "Type",
"local": "Widget",
"widget-group": "Widget (Group-Scoped)",
"widget-page": "Widget (Page-Scoped)",
"widget-ui": "Widget (UI-Scoped)",
"site-style": "CSS (All Pages)",
"page-style": "CSS (Single Page)",
"group": "Group",
Expand Down
69 changes: 51 additions & 18 deletions nodes/widgets/ui_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
color: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.colors.dark'),
defaults: {
group: { type: 'ui-group', required: true }, // for when template is scoped to 'local' (default)
dashboard: { type: 'ui-base', required: false }, // for when template is scoped to 'site'
page: { type: 'ui-page', required: false }, // for when template is scoped to 'page'
ui: { type: 'ui-base', required: false }, // for when template is scoped to 'site'
name: { value: '' },
order: { value: 0 },
width: {
Expand Down Expand Up @@ -108,7 +108,9 @@
label: function () {
if (this.name) { return this.name }
const knownLabels = {
local: 'template',
local: 'template', // widget:group - kept as local for backward compatability
'widget:page': 'template',
'widget:ui': 'template',
'site:style': 'site style',
'page:style': 'page style'
}
Expand Down Expand Up @@ -151,29 +153,39 @@
$templateScope.on('change', function () {
that._def.defaults.group.required = false
that._def.defaults.page.required = false
that._def.defaults.dashboard.required = false
$('#template-row-group, #template-row-page, #template-row-dashboard, #template-row-class').hide()
that._def.defaults.ui.required = false
$('#template-row-group, #template-row-page, #template-row-ui, #template-row-class').hide()
switch ($templateScope.val()) {
case 'site:style':
$('#template-row-dashboard').show()
that._def.defaults.dashboard.required = true
$('#template-row-ui').show()
that._def.defaults.ui.required = true
that.editor.getSession().setMode('ace/mode/css')
break
case 'page:style':
$('#template-row-page').show()
that._def.defaults.dashboard.required = true
that._def.defaults.ui.required = true
that.editor.getSession().setMode('ace/mode/css')
break
case 'site:script':
$('#template-row-dashboard').show()
that._def.defaults.dashboard.required = true
$('#template-row-ui').show()
that._def.defaults.ui.required = true
that.editor.getSession().setMode('ace/mode/javascript')
break
case 'page:script':
$('#template-row-page').show()
that._def.defaults.dashboard.required = true
that._def.defaults.ui.required = true
that.editor.getSession().setMode('ace/mode/javascript')
break
case 'widget:ui':
$('#template-row-ui').show()
that._def.defaults.ui.required = true
that.editor.getSession().setMode('ace/mode/html')
break
case 'widget:page':
$('#template-row-page').show()
that._def.defaults.page.required = true
that.editor.getSession().setMode('ace/mode/html')
break
default:
$('#template-row-group, #template-row-class').show()
that._def.defaults.group.required = true
Expand Down Expand Up @@ -240,6 +252,25 @@
$('#node-input-format').val(this.editor.getValue())
this.editor.destroy()
delete this.editor

// ensure we only have one of group/page/dashboard defined
const scope = $('#node-input-templateScope').val()
if (scope === 'local') {
$('#node-input-ui').val('_ADD_')
$('#node-input-page').val('_ADD_')
} else if (scope === 'widget:page') {
$('#node-input-ui').val('_ADD_')
$('#node-input-group').val('_ADD_')
} else if (scope === 'widget:ui') {
$('#node-input-page').val('_ADD_')
$('#node-input-group').val('_ADD_')
} else if (scope === 'site:style') {
$('#node-input-page').val('_ADD_')
$('#node-input-group').val('_ADD_')
} else if (scope === 'page:style') {
$('#node-input-ui').val('_ADD_')
$('#node-input-group').val('_ADD_')
}
},
oneditcancel: function () {
this.editor.destroy()
Expand Down Expand Up @@ -275,25 +306,27 @@
<div class="form-row">
<label for="node-input-temlplateScope"><i class="fa fa-dot-circle-o"></i> <span data-i18n="ui-template.label.scope"></span></label>
<select style="width:76%" id="node-input-templateScope">
<option value="local" data-i18n="ui-template.label.local"></option>
<option value="local" data-i18n="ui-template.label.widget-group"></option>
<option value="widget:page" data-i18n="ui-template.label.widget-page"></option>
<option value="widget:ui" data-i18n="ui-template.label.widget-ui"></option>
<option value="site:style" data-i18n="ui-template.label.site-style"></option>
<option value="page:style" data-i18n="ui-template.label.page-style"></option>
<!-- FUTURE? <option value="site:script" data-i18n="ui-template.label.site-script"></option>
<option value="page:script" data-i18n="ui-template.label.page-script"></option> -->
</select>
</div>
<div id="template-row-group" class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div id="template-row-dashboard" class="form-row">
<label for="node-input-dashboard"><i class="fa fa-bookmark"></i> Dashboard</label>
<input type="text" id="node-input-dashboard">
<div id="template-row-ui" class="form-row">
<label for="node-input-ui"><i class="fa fa-bookmark"></i> UI</label>
<input type="text" id="node-input-ui">
</div>
<div id="template-row-page" class="form-row">
<label for="node-input-page"><i class="fa fa-bookmark"></i> Page</label>
<input type="text" id="node-input-page">
</div>
<div id="template-row-group" class="form-row">
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
<input type="text" id="node-input-group">
</div>
<div class="form-row">
<label><i class="fa fa-object-group"></i> Size</label>
<input type="hidden" id="node-input-width">
Expand Down
20 changes: 15 additions & 5 deletions nodes/widgets/ui_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@ module.exports = function (RED) {
// create node in Node-RED
RED.nodes.createNode(this, config)

// which group are we rendering this widget
const group = RED.nodes.getNode(config.group)

const evts = {
onAction: true // TODO: think we need an onSend event for template nodes that matches up with a `widget-send` message
}
// inform the dashboard UI that we are adding this node
group.register(node, config, evts)

// which group are we rendering this widget
if (config.group) {
const group = RED.nodes.getNode(config.group)
// inform the dashboard UI that we are adding this node
group.register(node, config, evts)
} else if (config.page) {
const page = RED.nodes.getNode(config.page)
// inform the dashboard UI that we are adding this node
page.register(null, node, config, evts)
} else if (config.ui) {
const ui = RED.nodes.getNode(config.ui)
// inform the dashboard UI that we are adding this node
ui.register(null, null, node, config, evts)
}
}

RED.nodes.registerType('ui-template', TemplateNode)
Expand Down
2 changes: 1 addition & 1 deletion ui/src/layouts/Baseline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default {
},
uiWidgets: function () {
// get widgets scoped to the UI, not a group/page
const widgets = Object.values(this.widgets).filter(w => Object.hasOwn(w.props, 'ui'))
const widgets = Object.values(this.widgets).filter(w => Object.hasOwn(w.props, 'ui') && !!w.props.ui)
return widgets
}
},
Expand Down
16 changes: 15 additions & 1 deletion ui/src/layouts/Flex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@
</v-card>
</div>
</div>
<div>
<!-- Render any widgets with a 'page' scope -->
<component
:is="widget.component"
v-for="widget in pageWidgets"
:id="widget.id"
:key="widget.id"
:props="widget.props"
:state="widget.state"
/>
</div>
</BaselineLayout>
</template>

Expand All @@ -53,7 +64,7 @@ export default {
computed: {
...mapState('ui', ['groups', 'widgets', 'pages']),
...mapState('data', ['properties']),
...mapGetters('ui', ['groupsByPage', 'widgetsByGroup']),
...mapGetters('ui', ['groupsByPage', 'widgetsByGroup', 'widgetsByPage']),
orderedGroups: function () {
// get groups on this page
const groups = this.groupsByPage(this.$route.meta.id)
Expand All @@ -66,6 +77,9 @@ export default {
})
return groups
},
pageWidgets: function () {
return this.widgetsByPage(this.$route.meta.id)
},
page: function () {
return this.pages[this.$route.meta.id]
}
Expand Down
16 changes: 15 additions & 1 deletion ui/src/layouts/Grid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@
</v-card>
</div>
</div>
<div>
<!-- Render any widgets with a 'page' scope -->
<component
:is="widget.component"
v-for="widget in pageWidgets"
:id="widget.id"
:key="widget.id"
:props="widget.props"
:state="widget.state"
/>
</div>
</BaselineLayout>
</template>

Expand All @@ -55,7 +66,7 @@ export default {
computed: {
...mapState('ui', ['groups', 'widgets', 'pages']),
...mapState('data', ['properties']),
...mapGetters('ui', ['groupsByPage', 'widgetsByGroup']),
...mapGetters('ui', ['groupsByPage', 'widgetsByGroup', 'widgetsByPage']),
orderedGroups: function () {
// get groups on this page
const groups = this.groupsByPage(this.$route.meta.id)
Expand All @@ -68,6 +79,9 @@ export default {
})
return groups
},
pageWidgets: function () {
return this.widgetsByPage(this.$route.meta.id)
},
page: function () {
return this.pages[this.$route.meta.id]
}
Expand Down
16 changes: 15 additions & 1 deletion ui/src/layouts/Notebook.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@
</v-card>
</div>
</div>
<div>
<!-- Render any widgets with a 'page' scope -->
<component
:is="widget.component"
v-for="widget in pageWidgets"
:id="widget.id"
:key="widget.id"
:props="widget.props"
:state="widget.state"
/>
</div>
</BaselineLayout>
</template>

Expand All @@ -52,7 +63,7 @@ export default {
computed: {
...mapState('ui', ['groups', 'widgets', 'pages']),
...mapState('data', ['properties']),
...mapGetters('ui', ['groupsByPage', 'widgetsByGroup']),
...mapGetters('ui', ['groupsByPage', 'widgetsByGroup', 'widgetsByPage']),
orderedGroups: function () {
// get groups on this page
const groups = this.groupsByPage(this.$route.meta.id)
Expand All @@ -65,6 +76,9 @@ export default {
})
return groups
},
pageWidgets: function () {
return this.widgetsByPage(this.$route.meta.id)
},
page: function () {
return this.pages[this.$route.meta.id]
}
Expand Down
9 changes: 9 additions & 0 deletions ui/src/store/ui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ const getters = {
})
}
},
widgetsByPage: (state) => (pageId) => {
if (state.widgets) {
const widgetsOnPage = Object.values(state.widgets).filter((w) => {
// return all widgets that belong to the specified group (so long as it is not a non-local scoped ui-template)
return w.props.page && w.props.page === pageId
})
return widgetsOnPage
}
},
widgetsByGroup: (state) => (groupId) => {
if (state.widgets) {
const widgetsInGroup = Object.values(state.widgets).filter((w) => {
Expand Down
5 changes: 5 additions & 0 deletions ui/src/stylesheets/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ main {
padding: var(--nrdb-main-padding);
}

#app-bar-actions {
display: flex;
gap: 6px;
}

/**
* Loading Animation
*/
Expand Down
4 changes: 2 additions & 2 deletions ui/src/widgets/ui-template/UITemplate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default {
const setup = {}
if (!_props || _props.templateScope === 'local') {
if (!_props || ['local', 'widget:page', 'widget:ui'].includes(_props.templateScope)) {
if (!head) {
return {}
} else if (Array.isArray(head) && head.length > 0) {
Expand Down Expand Up @@ -130,7 +130,7 @@ export default {
}
return setup
},
template: props.props.templateScope !== 'local' ? undefined : template,
template: ['local', 'widget:page', 'widget:ui'].includes(props.props.templateScope) ? template : undefined,
watch: {
...component?.watch
},
Expand Down

0 comments on commit 9703846

Please sign in to comment.