-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
313 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
<script type="text/javascript"> | ||
(function () { | ||
function hasProperty (obj, prop) { | ||
return Object.prototype.hasOwnProperty.call(obj, prop) | ||
} | ||
RED.nodes.registerType('ui-file-input', { | ||
category: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.label.category'), | ||
color: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.colors.light'), | ||
defaults: { | ||
group: { type: 'ui-group', required: true }, | ||
name: { value: '' }, | ||
order: { value: 0 }, | ||
width: { | ||
value: 0, | ||
validate: function (v) { | ||
const width = v || 0 | ||
const currentGroup = $('#node-input-group').val() || this.group | ||
const groupNode = RED.nodes.node(currentGroup) | ||
const valid = !groupNode || +width <= +groupNode.width | ||
$('#node-input-size').toggleClass('input-error', !valid) | ||
return valid | ||
} | ||
}, | ||
height: { value: 0 }, | ||
topic: { value: 'topic', validate: (hasProperty(RED.validators, 'typedInput') ? RED.validators.typedInput('topicType') : function (v) { return true }) }, | ||
topicType: { value: 'msg' }, | ||
label: { value: 'File Input' }, | ||
icon: { value: 'paperclip' }, | ||
showFileSize: { value: false }, | ||
allowMultiple: { value: false }, | ||
accept: { value: '' }, | ||
className: { value: '' } | ||
}, | ||
inputs: 1, | ||
outputs: 1, | ||
icon: 'font-awesome/fa-upload', | ||
paletteLabel: 'file input', | ||
oneditprepare: function () { | ||
// if this groups parent is a subflow template, set the node-config-input-width and node-config-input-height up | ||
// as typedInputs and hide the elementSizer (as it doesn't make sense for subflow templates) | ||
if (RED.nodes.subflow(this.z)) { | ||
// change inputs from hidden to text & display them | ||
$('#node-input-width').attr('type', 'text') | ||
$('#node-input-height').attr('type', 'text') | ||
$('div.form-row.nr-db-ui-element-sizer-row').hide() | ||
$('div.form-row.nr-db-ui-manual-size-row').show() | ||
} else { | ||
// not in a subflow, use the elementSizer | ||
$('div.form-row.nr-db-ui-element-sizer-row').show() | ||
$('div.form-row.nr-db-ui-manual-size-row').hide() | ||
$('#node-input-size').elementSizer({ | ||
width: '#node-input-width', | ||
height: '#node-input-height', | ||
group: '#node-input-group' | ||
}) | ||
} | ||
// topic | ||
$('#node-input-topic').typedInput({ | ||
default: 'str', | ||
typeField: $('#node-input-topicType'), | ||
types: ['str', 'msg', 'flow', 'global'] | ||
}) | ||
}, | ||
label: function () { | ||
return this.name || this.label || 'file input' | ||
}, | ||
labelStyle: function () { return this.name ? 'node_label_italic' : '' } | ||
}) | ||
})() | ||
</script> | ||
|
||
<script type="text/html" data-template-name="ui-file-input"> | ||
<div class="form-row"> | ||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> | ||
<input type="text" id="node-input-name"> | ||
</div> | ||
<div 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"> | ||
<input type="hidden" id="node-input-height"> | ||
<button class="editor-button" id="node-input-size"></button> | ||
</div> | ||
<div class="form-row"> | ||
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label> | ||
<input type="text" id="node-input-label"> | ||
</div> | ||
<div class="form-row"> | ||
<label for="node-input-icon"><i class="fa fa-picture-o"></i> Icon</label> | ||
<input type="text" id="node-input-icon"> | ||
</div> | ||
<div class="form-row"> | ||
<label for="node-input-accept"><i class="fa fa-paperclip"></i> Accept</label> | ||
<input type="text" id="node-input-accept"> | ||
</div> | ||
<div class="form-row" style="padding-left: 25px;"> | ||
<input type="checkbox" checked id="node-input-allowMultiple" style="display:inline-block; width:auto; vertical-align:top;"> | ||
<label style="width:auto" for="node-input-allowMultiple"> Allow Multiple</label> | ||
</div> | ||
<div class="form-row" style="padding-left: 25px;"> | ||
<input type="checkbox" checked id="node-input-showFileSize" style="display:inline-block; width:auto; vertical-align:top;"> | ||
<label style="width:auto" for="node-input-showFileSize"> Show File Size</label> | ||
</div> | ||
<div class="form-row"> | ||
<label for="node-input-className"><i class="fa fa-code"></i> Class</label> | ||
<div style="display: inline;"> | ||
<input style="width: 70%;" type="text" id="node-input-className" placeholder="Optional CSS class name(s)" style="flex-grow: 1;"> | ||
<a | ||
data-html="true" | ||
title="Dynamic Property: Send msg.class to append new classes to this widget. NOTE: classes set at runtime will be applied in addition to any class(es) set in the nodes class field." | ||
class="red-ui-button ui-node-popover-title" | ||
style="margin-left: 4px; cursor: help; font-size: 0.625rem; border-radius: 50%; width: 24px; height: 24px; display: inline-flex; justify-content: center; align-items: center;"> | ||
<i style="font-family: ui-serif;">fx</i> | ||
</a> | ||
</div> | ||
</div> | ||
<div class="form-row" style="padding-left: 25px;"> | ||
<label for="node-input-topic" style="margin-right:-25px">Topic</label> | ||
<input type="text" id="node-input-topic"> | ||
<input type="hidden" id="node-input-topicType"> | ||
</div> | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// const datastore = require('../store/data.js') | ||
|
||
module.exports = function (RED) { | ||
function FileInputNode (config) { | ||
const node = this | ||
|
||
// create node in Node-RED | ||
RED.nodes.createNode(this, config) | ||
|
||
// this ndoe need to store content/value from UI | ||
node.value = null | ||
|
||
// which group are we rendering this widget | ||
const group = RED.nodes.getNode(config.group) | ||
|
||
const evts = { | ||
onAction: true | ||
} | ||
|
||
// inform the dashboard UI that we are adding this node | ||
group.register(node, config, evts) | ||
|
||
node.on('close', async function (done) { | ||
done() | ||
}) | ||
} | ||
|
||
RED.nodes.registerType('ui-file-input', FileInputNode) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<template> | ||
<div class="nrdb-ui-file-input"> | ||
<v-file-input | ||
v-if="!uploading && !uploaded" | ||
v-model="files" | ||
density="compact" | ||
:disabled="!state.enabled" | ||
:label="label" :prepend-icon="icon" | ||
:accept="accept" | ||
:show-size="showFileSize" | ||
variant="outlined" | ||
hide-details="auto" | ||
/> | ||
<div v-else-if="!uploaded" class="nrdb-ui-file-input--progress"> | ||
<v-progress-linear :model-value="progress" :height="24"> | ||
<template #default="{ value }"> | ||
<strong>{{ Math.ceil(value) }}%</strong> | ||
</template> | ||
</v-progress-linear> | ||
</div> | ||
<div v-else class="nrdb-ui-file-input--uploaded"> | ||
<v-icon icon="mdi-check-circle" color="success" /> | ||
<label class="v-label">File Uploaded!</label> | ||
<label class="v-label" @click="reset()"> | ||
<a class="nrdb-anchor">Upload Another File</a> | ||
</label> | ||
</div> | ||
<v-btn variant="flat" :disabled="!files || uploading || uploaded" @click="upload(files)"> | ||
{{ uploading ? 'Uploading...' : 'Upload' }} | ||
</v-btn> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import { useDataTracker } from '../data-tracker.mjs' // eslint-disable-line import/order | ||
import { mapState } from 'vuex' // eslint-disable-line import/order | ||
export default { | ||
name: 'DBUIFileInput', | ||
inject: ['$socket'], | ||
props: { | ||
id: { type: String, required: true }, | ||
props: { type: Object, default: () => ({}) }, | ||
state: { type: Object, default: () => ({}) } | ||
}, | ||
setup (props) { | ||
useDataTracker(props.id) | ||
}, | ||
data () { | ||
return { | ||
files: null, | ||
uploading: false, | ||
uploaded: false, | ||
progress: 0 | ||
} | ||
}, | ||
computed: { | ||
...mapState('data', ['messages']), | ||
icon: function () { | ||
const icon = this.props.icon | ||
return icon ? 'mdi-' + icon.replace(/^mdi-/, '') : null | ||
}, | ||
accept: function () { | ||
return this.props.accept | ||
}, | ||
label: function () { | ||
return this.props.label | ||
}, | ||
showFileSize: function () { | ||
return this.props.showFileSize | ||
}, | ||
multiple: function () { | ||
return this.props.multiple | ||
} | ||
}, | ||
methods: { | ||
reset: function () { | ||
this.files = null | ||
this.uploading = false | ||
this.uploaded = false | ||
this.progress = 0 | ||
}, | ||
upload: function (file) { | ||
if (file && !this.multiple) { | ||
// Create a FileReader instance to read the file | ||
const reader = new FileReader() | ||
// When the file is read, send it to Node-RED | ||
reader.onload = () => { | ||
// Prepare the payload to send | ||
const msg = { | ||
payload: file, // File content | ||
file: { | ||
name: file.name, // File name | ||
size: file.size, // File size | ||
type: file.type // File type | ||
} | ||
} | ||
this.uploading = false | ||
this.uploaded = true | ||
this.send(msg) | ||
} | ||
// Track progress of file reading | ||
reader.onprogress = (event) => { | ||
this.progress = event.loaded // Update progress | ||
} | ||
this.uploading = true | ||
// readAsText alternative? | ||
reader.readAsArrayBuffer(file) | ||
} | ||
}, | ||
send (msg) { | ||
this.$socket.emit('widget-send', this.id, msg) | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<style> | ||
.nrdb-ui-file-input { | ||
display: flex; | ||
gap: 12px; | ||
} | ||
.nrdb-ui-file-input--progress { | ||
width: 100%; | ||
display: flex; | ||
align-items: center; | ||
} | ||
.nrdb-ui-file-input--uploaded { | ||
flex-grow: 1; | ||
display: flex; | ||
align-items: center; | ||
justify-content: flex-start; | ||
gap: 6px; | ||
height: var(--widget-row-height); | ||
} | ||
</style> |