@5minds/node-red-dashboard-2-processcube-dynamic-table
Version:
A ui component for showing dynamic Data with actions in a table
418 lines (376 loc) • 19.4 kB
HTML
<script type="text/javascript">
(function () {
function hasProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
RED.nodes.registerType('ui-dynamic-table', {
category: 'ProcessCube UI',
color: '#00aed7',
defaults: {
name: { value: '' },
tableName: { value: '' },
group: { type: 'ui-group', required: true },
order: { value: 1 },
data: { value: 'payload' },
data_type: { value: 'msg' },
options: {
value: [{ label: '', label_type: 'str', condition: '' }],
validate: function (v) {
const unique = new Set(
v.map(function (o) {
return o.label;
})
);
return v.length === unique.size;
},
},
columns: {
value: [{ value: '', label: '' }],
validate: function (v) {
const unique = new Set(
v.map(function (o) {
return o.value;
})
);
return v.length === unique.size;
},
},
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 },
outputs: { value: 1 },
title_text: { value: '' },
title_style: { value: 'default' },
title_custom_text_styling: { value: '' },
title_icon: { value: '' },
},
inputs: 1,
outputs: 1,
outputLabels: function (index) {
return this.options[index].label;
},
icon: 'ui_dynamic_table.svg',
paletteLbel: 'file',
label: function () {
return this.name || 'dynamic-table';
},
oneditprepare: function () {
$('#node-input-size').elementSizer({
width: '#node-input-width',
height: '#node-input-height',
group: '#node-input-group',
});
$('#node-input-title_style').typedInput({
types: [
{
value: 'default',
label: 'Default',
hasValue: false,
},
{
value: 'minimal',
label: 'Minimal',
hasValue: false,
},
{
value: 'outside',
label: 'Outside of card',
hasValue: false,
},
],
});
function generateOption(i, option) {
const container = $('<li/>', {
style: 'background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px;',
});
// Create input fields for value and label
const row = $('<div/>').appendTo(container);
$('<input/>', {
class: 'node-input-option-label',
type: 'text',
style: 'margin-left:7px; width:calc(50% - 32px);',
placeholder: 'Label',
value: option.label,
})
.appendTo(row)
.typedInput({
default: option.label_type || 'str',
types: [
'msg',
'str',
'jsonata',
{
value: 'row',
label: 'row.',
hasValue: true,
},
],
});
// Set the typed input value and type
const labelInput = row.find('.node-input-option-label');
if (option.label_type) {
labelInput.typedInput('type', option.label_type);
}
if (option.label) {
labelInput.typedInput('value', option.label);
}
$('<input/>', {
class: 'node-input-option-condition',
type: 'text',
style: 'margin-left:7px; width:calc(50% - 32px);',
placeholder: 'Condition',
value: option.condition,
})
.appendTo(row)
.typedInput({
type: 'str',
types: ['str'],
});
// Create delete button for the option
const finalSpan = $('<span/>', {
style: 'float:right; margin-right:8px;',
}).appendTo(row);
const deleteButton = $('<a/>', {
href: '#',
class: 'editor-button editor-button-small',
style: 'margin-top:7px; margin-left:5px;',
}).appendTo(finalSpan);
$('<i/>', { class: 'fa fa-remove' }).appendTo(deleteButton);
deleteButton.click(function () {
container.css({
background: 'var(--red-ui-secondary-background-inactive, #fee)',
});
container.fadeOut(300, function () {
$(this).remove();
});
});
$('#node-input-option-container').append(container);
}
function generateColumn(i, column) {
const container = $('<li/>', {
style: 'background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px;',
});
// Create input fields for value and label
const row = $('<div/>').appendTo(container);
$('<input/>', {
class: 'node-input-column-value',
type: 'text',
style: 'margin-left:7px; width:calc(50% - 32px);',
placeholder: 'Value',
value: column.value,
})
.appendTo(row)
.typedInput({ default: column.type || 'str', types: ['str'] });
$('<input/>', {
class: 'node-input-column-label',
type: 'text',
style: 'margin-left:7px; width:calc(50% - 32px);',
placeholder: 'Label',
value: column.label,
})
.appendTo(row)
.typedInput({
type: 'str',
types: ['str'],
});
// Create delete button for the column
const finalSpan = $('<span/>', {
style: 'float:right; margin-right:8px;',
}).appendTo(row);
const deleteButton = $('<a/>', {
href: '#',
class: 'editor-button editor-button-small',
style: 'margin-top:7px; margin-left:5px;',
}).appendTo(finalSpan);
$('<i/>', { class: 'fa fa-remove' }).appendTo(deleteButton);
deleteButton.click(function () {
container.css({
background: 'var(--red-ui-secondary-background-inactive, #fee)',
});
container.fadeOut(300, function () {
$(this).remove();
});
});
$('#node-input-column-container').append(container);
}
$('#node-input-add-column').click(function () {
generateColumn($('#node-input-column-container').children().length + 1, {});
$('#node-input-column-container-div').scrollTop(
$('#node-input-column-container-div').get(0).scrollHeight
);
});
for (let i = 0; i < this.columns.length; i++) {
const column = this.columns[i];
generateColumn(i + 1, column);
}
$('#node-input-column-container').sortable({
axis: 'y',
handle: '.node-input-column-handle',
cursor: 'move',
});
$('#node-input-add-option').click(function () {
generateOption($('#node-input-option-container').children().length + 1, {});
$('#node-input-option-container-div').scrollTop(
$('#node-input-option-container-div').get(0).scrollHeight
);
});
for (let i = 0; i < this.options.length; i++) {
const option = this.options[i];
generateOption(i + 1, option);
}
$('#node-input-option-container').sortable({
axis: 'y',
handle: '.node-input-option-handle',
cursor: 'move',
});
$('#node-input-data').typedInput({
default: 'msg',
types: ['msg', 'json', 'flow', 'global'],
});
$('#node-input-data').typedInput('value', this.data);
$('#node-input-data').typedInput('type', this.data_type);
},
oneditsave: function () {
const options = $('#node-input-option-container').children();
const node = this;
node.options = [];
options.each(function (i) {
const option = $(this);
const o = {
label: option.find('.node-input-option-label').typedInput('value'),
label_type: option.find('.node-input-option-label').typedInput('type'),
condition: option.find('.node-input-option-condition').val(),
};
node.options.push(o);
});
const columns = $('#node-input-column-container').children();
node.columns = [];
columns.each(function (i) {
const column = $(this);
const c = {
label: column.find('.node-input-column-label').val(),
value: column.find('.node-input-column-value').typedInput('value'),
type: column.find('.node-input-column-value').typedInput('type'),
};
if (column.find('.node-input-column-value').typedInput('type') === 'num') {
c.value = Number(c.value);
}
if (column.find('.node-input-column-value').typedInput('type') === 'bool') {
c.value = c.value === 'true';
}
node.columns.push(c);
});
this.outputs = node.options.length || 1;
(this.data = $('#node-input-data').typedInput('value')),
(this.data_type = $('#node-input-data').typedInput('type'));
},
});
})();
</script>
<script type="text/html" data-template-name="ui-dynamic-table">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-tableName"><i class="fa fa-tag"></i> Table name</label>
<input type="text" id="node-input-tableName" placeholder="My Table">
</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> <span data-i18n="ui-dynamic-table.label.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-data"><i class="fa fa-tag"></i> Data</label>
<input type="text" id="node-input-data" placeholder="Data">
</div>
<div class="form-row form-row-flex node-input-option-container-row" style="margin-bottom: 0px;width: 100%">
<label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> Actions</label>
<div id="node-input-option-container-div" style="box-sizing:border-box; border-radius:5px; height:257px; padding:5px; border:1px solid var(--red-ui-form-input-border-color, #ccc); overflow-y:scroll; display:inline-block; width: 70%;">
<span id="valWarning" style="color: var(--red-ui-text-color-error, #910000)"><b>All Values must be unique.</b></span>
<ol id="node-input-option-container" style="list-style-type:none; margin:0;"></ol>
</div>
<a
data-html="true"
title="Dynamic Property: Send 'msg.options' in order to override this property."
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>
<!-- Add Option Button -->
<div class="form-row">
<a href="#" class="editor-button editor-button-small" id="node-input-add-option" style="margin-top:4px; margin-left:103px;"><i class="fa fa-plus"></i> <span>action</span></a>
</div>
<div class="form-row form-row-flex node-input-column-container-row" style="margin-bottom: 0px;width: 100%">
<label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> Columns</label>
<div id="node-input-column-container-div" style="box-sizing:border-box; border-radius:5px; height:257px; padding:5px; border:1px solid var(--red-ui-form-input-border-color, #ccc); overflow-y:scroll; display:inline-block; width: 70%;">
<span id="valWarning" style="color: var(--red-ui-text-color-error, #910000)"><b>All Values must be unique.</b></span>
<ol id="node-input-column-container" style="list-style-type:none; margin:0;"></ol>
</div>
<a
data-html="true"
title="Dynamic Property: Send 'msg.columns' in order to override this property."
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>
<!-- Add Column Button -->
<div class="form-row">
<a href="#" class="editor-button editor-button-small" id="node-input-add-column" style="margin-top:4px; margin-left:103px;"><i class="fa fa-plus"></i> <span>column</span></a>
</div>
<hr />
<h4>Title Configuration</h4>
<div class="form-row">
<label for="node-input-title_text"><i class="fa fa-hand"></i>Title text</label>
<input type="text" id="node-input-title_text" title="Enter the text to show as a title.">
</div>
<div class="form-row">
<label for="node-input-title_style"><i class="fa fa-hand"></i>Style</label>
<input type="text" id="node-input-title_style" title="Select the base styling layout for your title.">
</div>
<div class="form-row">
<label for="node-input-title_custom_text_styling"><i class="fa fa-hand"></i>Custom text styling</label>
<input type="text" id="node-input-title_custom_text_styling" title="Use CSS properties like font-size, color or padding to influence the appearance of the title">
</div>
<div class="form-row">
<label for="node-input-title_icon"><i class="fa fa-hand"></i>Title icon</label>
<input type="text" id="node-input-title_icon" title="Enter an URL to fetch an icon from. Use a dataurl for images, that are not available online. This value will be used as a HTML <img>'s src value.">
</div>
<hr />
</script>
<script type="text/markdown" data-help-name="ui-dynamic-table">
A Node to dynamicly display arrays of data in a table with actions for every row.
## Actions
Define a label for the action button that is going be displayed and a condition for when it should be displayed.
Inside the condition you have access to every key from the current row.
### Inputs
: Table name (string) : The name of the table.
: Group (string) : The groud in which the Component is rendered.
: Size (string) : The size of the table within the group.
: Data (string) : The property of msg.payload from where the table data is sourced.
: Actions (string) : The actions of each row. Each action corresponds to an output of the node.
: Columns (string) : The columns of the table. Must correspond to a key of the data property.
### Outputs
The outputs are generated dynamicly based on the actions of the table.
### Details
`msg.payload` is used as the base for the data form there on the data source can be specified further with the `data` input field.
### Referece
[https://processcube.io/docs/solutions/node-red](https://processcube.io/docs/solutions/node-red)
</script>