@flowfuse/node-red-dashboard 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +53 -0
  3. package/dist/css/app.d047b42b.css +1 -0
  4. package/dist/css/chunk-vendors.2378ce49.css +24 -0
  5. package/dist/fonts/materialdesignicons-webfont.3de8526e.woff +0 -0
  6. package/dist/fonts/materialdesignicons-webfont.477c6ab0.woff2 +0 -0
  7. package/dist/fonts/materialdesignicons-webfont.48a1ce0c.eot +0 -0
  8. package/dist/fonts/materialdesignicons-webfont.dfd403cf.ttf +0 -0
  9. package/dist/index.html +1 -0
  10. package/dist/js/app.854a8cd5.js +2 -0
  11. package/dist/js/app.854a8cd5.js.map +1 -0
  12. package/dist/js/chunk-vendors.174e8921.js +43 -0
  13. package/dist/js/chunk-vendors.174e8921.js.map +1 -0
  14. package/nodes/config/locales/en-US/ui_base.json +19 -0
  15. package/nodes/config/locales/en-US/ui_group.html +4 -0
  16. package/nodes/config/locales/en-US/ui_group.json +16 -0
  17. package/nodes/config/ui_base.html +807 -0
  18. package/nodes/config/ui_base.js +678 -0
  19. package/nodes/config/ui_group.html +55 -0
  20. package/nodes/config/ui_group.js +34 -0
  21. package/nodes/config/ui_page.html +84 -0
  22. package/nodes/config/ui_page.js +33 -0
  23. package/nodes/config/ui_theme.html +101 -0
  24. package/nodes/config/ui_theme.js +15 -0
  25. package/nodes/store/index.js +34 -0
  26. package/nodes/utils/index.js +35 -0
  27. package/nodes/widgets/locales/en-US/ui_button.html +7 -0
  28. package/nodes/widgets/locales/en-US/ui_button.json +24 -0
  29. package/nodes/widgets/locales/en-US/ui_chart.html +41 -0
  30. package/nodes/widgets/locales/en-US/ui_chart.json +17 -0
  31. package/nodes/widgets/locales/en-US/ui_dropdown.html +24 -0
  32. package/nodes/widgets/locales/en-US/ui_form.html +16 -0
  33. package/nodes/widgets/locales/en-US/ui_form.json +36 -0
  34. package/nodes/widgets/locales/en-US/ui_markdown.html +10 -0
  35. package/nodes/widgets/locales/en-US/ui_slider.html +9 -0
  36. package/nodes/widgets/locales/en-US/ui_switch.html +32 -0
  37. package/nodes/widgets/locales/en-US/ui_template.html +59 -0
  38. package/nodes/widgets/locales/en-US/ui_template.json +18 -0
  39. package/nodes/widgets/locales/en-US/ui_text.html +16 -0
  40. package/nodes/widgets/locales/en-US/ui_text_input.html +19 -0
  41. package/nodes/widgets/ui_button.html +146 -0
  42. package/nodes/widgets/ui_button.js +65 -0
  43. package/nodes/widgets/ui_chart.html +314 -0
  44. package/nodes/widgets/ui_chart.js +195 -0
  45. package/nodes/widgets/ui_dropdown.html +199 -0
  46. package/nodes/widgets/ui_dropdown.js +19 -0
  47. package/nodes/widgets/ui_form.html +368 -0
  48. package/nodes/widgets/ui_form.js +18 -0
  49. package/nodes/widgets/ui_markdown.html +134 -0
  50. package/nodes/widgets/ui_markdown.js +14 -0
  51. package/nodes/widgets/ui_notification.html +139 -0
  52. package/nodes/widgets/ui_notification.js +14 -0
  53. package/nodes/widgets/ui_radio_group.html +186 -0
  54. package/nodes/widgets/ui_radio_group.js +20 -0
  55. package/nodes/widgets/ui_slider.html +162 -0
  56. package/nodes/widgets/ui_slider.js +31 -0
  57. package/nodes/widgets/ui_switch.html +194 -0
  58. package/nodes/widgets/ui_switch.js +98 -0
  59. package/nodes/widgets/ui_table.html +149 -0
  60. package/nodes/widgets/ui_table.js +16 -0
  61. package/nodes/widgets/ui_template.html +283 -0
  62. package/nodes/widgets/ui_template.js +19 -0
  63. package/nodes/widgets/ui_text.html +358 -0
  64. package/nodes/widgets/ui_text.js +98 -0
  65. package/nodes/widgets/ui_text_input.html +141 -0
  66. package/nodes/widgets/ui_text_input.js +37 -0
  67. package/package.json +114 -0
@@ -0,0 +1,368 @@
1
+ <script type="text/javascript">
2
+ (function () {
3
+ // convert to i18 text
4
+ function c_ (x) {
5
+ return RED._('@flowforge/node-red-dashboard/ui-form:ui-form.' + x)
6
+ }
7
+ function hasProperty (obj, prop) {
8
+ return Object.prototype.hasOwnProperty.call(obj, prop)
9
+ }
10
+ RED.nodes.registerType('ui-form', {
11
+ category: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.label.category'),
12
+ color: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.colors.light'),
13
+ defaults: {
14
+ name: { value: '' },
15
+ group: { type: 'ui-group', required: true },
16
+ label: { value: '' },
17
+ order: { value: 0 },
18
+ width: {
19
+ value: 0,
20
+ validate: function (v) {
21
+ const width = v || 0
22
+ const currentGroup = $('#node-input-group').val() || this.group
23
+ const groupNode = RED.nodes.node(currentGroup)
24
+ const valid = !groupNode || +width <= +groupNode.width
25
+ $('#node-input-size').toggleClass('input-error', !valid)
26
+ return valid
27
+ }
28
+ },
29
+ height: { value: 0 },
30
+ options: {
31
+ value: [{ value: '', label: '', type: '', required: true }],
32
+ validate: function (value) {
33
+ if (value.length) {
34
+ for (let i = 0; i < value.length; i++) {
35
+ if (!value[i].key) {
36
+ return false
37
+ }
38
+ }
39
+ } else {
40
+ return false
41
+ }
42
+ return true
43
+ },
44
+ required: true
45
+ },
46
+ formValue: { value: {} },
47
+ payload: { value: '' },
48
+ submit: { value: 'submit' },
49
+ cancel: { value: 'clear' },
50
+ resetOnSubmit: { value: true },
51
+ topic: { value: 'topic', validate: (hasProperty(RED.validators, 'typedInput') ? RED.validators.typedInput('topicType') : function (v) { return true }) },
52
+ topicType: { value: 'msg' },
53
+ splitLayout: { value: '' },
54
+ className: { value: '' }
55
+ },
56
+ inputs: 0,
57
+ outputs: 1,
58
+ icon: 'font-awesome/fa-list-alt',
59
+ paletteLabel: 'form',
60
+ label: function () { return this.name || this.label || 'form' },
61
+ labelStyle: function () { return this.name ? 'node_label_italic' : '' },
62
+ oneditprepare: function () {
63
+ if ($('#node-input-submit').val() === null) { $('#node-input-submit').val('submit') }
64
+ if ($('#node-input-cancel').val() === null) { $('#node-input-cancel').val('cancel') }
65
+ $('#node-input-size').elementSizer({
66
+ width: '#node-input-width',
67
+ height: '#node-input-height',
68
+ group: '#node-input-group'
69
+ })
70
+
71
+ this.resizeRule = function (option, newWidth) {
72
+ // option.find(".node-input-option-type").width(newWidth);
73
+ // option.find(".node-input-option-label").width(newWidth);
74
+ // option.find(".node-input-option-key").width(newWidth);
75
+ }
76
+
77
+ function generateOption (i, option) {
78
+ const container = $('<li/>', { style: 'margin:0; padding:8px 0px 0px; border-bottom:1px solid var(--red-ui-form-input-border-color, #ccc);' })
79
+ const row = $('<div/>').appendTo(container)
80
+ $('<div/>', { style: 'padding-top:5px; padding-left:175px;' }).appendTo(container)
81
+ $('<div/>', { style: 'padding-top:5px; padding-left:120px;' }).appendTo(container)
82
+
83
+ $('<i style="cursor:move; margin-left:3px;" class="node-input-option-handle fa fa-bars"></i>').appendTo(row)
84
+
85
+ // label field
86
+ $('<input/>', { class: 'node-input-option-label', type: 'text', style: 'margin-left:7px; width:20%;', placeholder: c_('label.egName'), value: option.label }).appendTo(row)
87
+
88
+ // key field
89
+ let keyClass = 'node-input-option-key'
90
+ if (!option.key) { keyClass = 'node-input-option-key input-error' }
91
+ const keyField = $('<input/>', { class: keyClass, type: 'text', style: 'margin-left:7px; width:20%;', placeholder: c_('label.egName2'), value: option.key }).appendTo(row)
92
+ keyField.keyup(function () {
93
+ if ($(this).val() && $(this).hasClass('input-error')) {
94
+ $(this).removeClass('input-error')
95
+ } else {
96
+ if (!$(this).val()) {
97
+ $(this).addClass('input-error')
98
+ }
99
+ }
100
+ })
101
+
102
+ // type field
103
+ const typeField = $('<select/>', { class: 'node-input-option-type', type: 'text', style: 'margin-left:7px; width:16%' }).appendTo(row)// .typedInput({default:'str',types:['str', 'num']});
104
+
105
+ const arr = [
106
+ { val: 'text', text: c_('label.text') },
107
+ { val: 'multiline', text: c_('label.multiline') },
108
+ { val: 'number', text: c_('label.number') },
109
+ { val: 'email', text: c_('label.email') },
110
+ { val: 'password', text: c_('label.password') },
111
+ { val: 'checkbox', text: c_('label.checkbox') },
112
+ { val: 'switch', text: c_('label.switch') },
113
+ { val: 'date', text: c_('label.date') },
114
+ { val: 'time', text: c_('label.time') }
115
+ ]
116
+
117
+ // var sel = $('<select>').appendTo('body');
118
+ $(arr).each(function () {
119
+ let isSelected = false
120
+ if (option.type === this.val) {
121
+ isSelected = true
122
+ }
123
+ typeField.append($('<option>').attr('value', this.val).text(this.text).prop('selected', isSelected))
124
+ })
125
+
126
+ // which input types don't need a 'require' option
127
+ const noReqd = ['checkbox', 'switch']
128
+
129
+ // required
130
+ const requiredContainer = $('<div/>', { style: 'display:inline-block; height:34px; width:13%; vertical-align: middle' }).appendTo(row)
131
+ const requiredInnerContainer = $('<div/>', { style: 'left:35%; position:relative; width:30px' }).appendTo(requiredContainer)
132
+ const reqVis = noReqd.includes(option.type) ? 'hidden' : 'visible'
133
+ const reqRow = $('<label />', { class: 'switch', style: 'top:10px; width:30px;' }).css('visibility', reqVis).appendTo(requiredInnerContainer)
134
+ const reqd = $('<input/>', { class: 'node-input-option-required', type: 'checkbox', checked: option.required, style: 'vertical-align:top;' }).appendTo(reqRow)// labelForRequried);//.typedInput({default:'str',types:['str', 'num']});
135
+ $('<div />', { class: 'slider round' }).appendTo(reqRow)
136
+
137
+ // ui rows
138
+ const rowsVis = option.rows ? 'visible' : 'hidden'
139
+ const rowsField = $('<input/>', { class: 'node-input-option-rows', type: 'number', style: 'width:10%;', placeholder: 'Rows', value: option.rows }).css('visibility', rowsVis).appendTo(row)
140
+
141
+ const finalspan = $('<div/>', { style: 'display:inline-block; width:5%;' }).appendTo(row)
142
+ const deleteButton = $('<a/>', { href: '#', class: 'editor-button', style: 'font-size:1.3em; left:45%; position:relative;' }).appendTo(finalspan)
143
+ $('<i/>', { class: 'fa fa-trash-o' }).appendTo(deleteButton)
144
+
145
+ typeField.change(function (e) {
146
+ // decide whether we need to show the "rows" option
147
+ if (e.target.value !== 'multiline') {
148
+ rowsField.val(undefined)
149
+ option.rows = null
150
+ rowsField.css('visibility', 'hidden')
151
+ } else {
152
+ rowsField.css('visibility', 'visible')
153
+ if (!rowsField[0].value) rowsField[0].value = 3
154
+ }
155
+
156
+ // device whether we need to show the "required" option
157
+ if (noReqd.includes(e.target.value)) {
158
+ reqd.val(false)
159
+ option.required = false
160
+ reqRow.css('visibility', 'hidden')
161
+ } else {
162
+ reqRow.css('visibility', 'visible')
163
+ }
164
+ })
165
+
166
+ deleteButton.click(function () {
167
+ container.find('.node-input-option-key').removeAttr('required')
168
+ container.css({ background: 'var(--red-ui-secondary-background-inactive, #fee)' })
169
+ container.fadeOut(300, function () {
170
+ $(this).remove()
171
+ })
172
+ })
173
+
174
+ $('#node-input-option-container').append(container)
175
+ }
176
+
177
+ $('#node-input-add-option').click(function () {
178
+ generateOption($('#node-input-option-container').children().length + 1, {})
179
+ $('#node-input-option-container-div').scrollTop($('#node-input-option-container-div').get(0).scrollHeight)
180
+ })
181
+
182
+ for (let i = 0; i < this.options.length; i++) {
183
+ const option = this.options[i]
184
+ generateOption(i + 1, option)
185
+ }
186
+
187
+ $('#node-input-topic').typedInput({
188
+ default: 'str',
189
+ typeField: $('#node-input-topicType'),
190
+ types: ['str', 'msg', 'flow', 'global']
191
+ })
192
+
193
+ $('#node-input-option-container').sortable({
194
+ axis: 'y',
195
+ handle: '.node-input-option-handle',
196
+ cursor: 'move'
197
+ })
198
+
199
+ // use jQuery UI tooltip to convert the plain old title attribute to a nice tooltip
200
+ $('.ui-node-popover-title').tooltip({
201
+ show: {
202
+ effect: 'slideDown',
203
+ delay: 150
204
+ }
205
+ })
206
+ },
207
+ oneditsave: function () {
208
+ const options = $('#node-input-option-container').children()
209
+ const node = this
210
+ node.options = []
211
+ node.formValue = {}
212
+ options.each(function (i) {
213
+ const option = $(this)
214
+ const o = {
215
+ label: option.find('.node-input-option-label').val(),
216
+ key: option.find('.node-input-option-key').val(),
217
+ type: option.find('.node-input-option-type').val(),
218
+ required: option.find('.node-input-option-required').is(':checked'),
219
+ rows: parseInt(option.find('.node-input-option-rows').val())
220
+ }
221
+ node.formValue[o.key] = o.type === 'checkbox' || o.type === 'switch' ? false : ''
222
+ node.options.push(o)
223
+ })
224
+ },
225
+ oneditresize: function () {
226
+ const options = $('#node-input-option-container').children()
227
+ const newWidth = ($('#node-input-option-container').width() - 175) / 2
228
+ const node = this
229
+ options.each(function (i) {
230
+ node.resizeRule($(this), newWidth)
231
+ })
232
+ }
233
+ })
234
+ })()
235
+ </script>
236
+
237
+ <script type="text/html" data-template-name="ui-form">
238
+ <style>
239
+ .switch {
240
+ position: relative;
241
+ display: inline-block;
242
+ width: 30px;
243
+ height: 18px;
244
+ }
245
+
246
+ .switch input {display:none;}
247
+
248
+ .slider {
249
+ position: absolute;
250
+ cursor: pointer;
251
+ top: 0;
252
+ left: 0;
253
+ right: 0;
254
+ bottom: 0;
255
+ background-color: var(--red-ui-tertiary-border-color, #ccc);
256
+ -webkit-transition: .4s;
257
+ transition: .4s;
258
+ }
259
+
260
+ .slider:before {
261
+ position: absolute;
262
+ content: "";
263
+ height: 15px;
264
+ width: 15px;
265
+ left: 2px;
266
+ bottom: 2px;
267
+ background-color: var(--red-ui-secondary-background, white);
268
+ -webkit-transition: .4s;
269
+ transition: .4s;
270
+ }
271
+
272
+ input:checked + .slider {
273
+ background-color: #910000;
274
+ }
275
+
276
+ input:focus + .slider {
277
+ box-shadow: 0 0 1px #2196F3;
278
+ }
279
+
280
+ input:checked + .slider:before {
281
+ -webkit-transform: translateX(11px);
282
+ -ms-transform: translateX(11px);
283
+ transform: translateX(11px);
284
+ }
285
+
286
+ /* Rounded sliders */
287
+ .slider.round {
288
+ border-radius: 34px;
289
+ }
290
+
291
+ .slider.round:before {
292
+ border-radius: 50%;
293
+ }
294
+ </style>
295
+ <div class="form-row">
296
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
297
+ <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
298
+ </div>
299
+ <div class="form-row">
300
+ <label for="node-input-group"><i class="fa fa-table"></i> <span data-i18n="ui-form.label.group"></label>
301
+ <input type="text" id="node-input-group">
302
+ </div>
303
+ <div class="form-row">
304
+ <label><i class="fa fa-object-group"></i> <span data-i18n="ui-form.label.size"></label>
305
+ <input type="hidden" id="node-input-width">
306
+ <input type="hidden" id="node-input-height">
307
+ <button class="editor-button" id="node-input-size"></button>
308
+ </div>
309
+ <div class="form-row">
310
+ <label for="node-input-label"><i class="fa fa-tag"></i> <span data-i18n="ui-form.label.label"></label>
311
+ <input type="text" id="node-input-label" data-i18n="[placeholder]ui-form.label.optionalLabel">
312
+ </div>
313
+ <div class="form-row">
314
+ <label for="node-input-className"><i class="fa fa-code"></i> Class</label>
315
+ <div style="display: inline;">
316
+ <input style="width: 70%;" type="text" id="node-input-className" placeholder="Optional CSS class name(s)" style="flex-grow: 1;">
317
+ <a
318
+ data-html="true"
319
+ title="Dynamic Property: Class names can also be set by sending a message to the node with a msg.topic of 'ui-property:class' and a payload containing the class name(s) to be applied. NOTE: classes set at runtime will be applied in addition to any class(es) set in the nodes class field."
320
+ class="red-ui-button ui-node-popover-title"
321
+ 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;">
322
+ <i style="font-family: ui-serif;">fx</i>
323
+ </a>
324
+ </div>
325
+ </div>
326
+ <div class="form-row node-input-option-container-row" style="margin-bottom:0px; width:100%; min-width:520px">
327
+ <label style="vertical-align:top;"><i class="fa fa-list-alt"></i> <span data-i18n="ui-form.label.formElements"></label>
328
+ <div style="display:inline-block; width:78%; border:1px solid var(--red-ui-form-input-border-color, #ccc); border-radius:5px; box-sizing:border-box;">
329
+ <div class="red-ui-tray-header" style="width:100%; display: inline-block; padding-top:10px; padding-bottom:10px; border-top:0px solid; border-radius:5px 5px 0 0; border-bottom:1px solid var(--red-ui-form-input-border-color, #ccc);">
330
+ <div style="width:94%; display:inline-block; margin-left:27px">
331
+ <div style="width:20%; text-align:center; float:left;" data-i18n="ui-form.label.label"></span></div>
332
+ <div style="width:20%; text-align:center; float:left; margin-left:9px" data-i18n="node-red:common.label.name"></div>
333
+ <div style="margin-left:7px; width:16%; text-align:center; float:left; margin-left:9px" data-i18n="ui-form.label.type"></div>
334
+ <div style="width:16%; text-align:center; float:left;" data-i18n="ui-form.label.required"></div>
335
+ <div style="width:10%; text-align:center; float:left;" data-i18n="ui-form.label.rows"></div>
336
+ <div style="width:12%; text-align:center; float:left;" data-i18n="ui-form.label.remove"></div>
337
+ </div>
338
+ </div>
339
+ <div id="node-input-option-container-div" style=" height: 257px; padding: 5px; overflow-y:scroll;">
340
+ <ol id="node-input-option-container" style=" list-style-type:none; margin: 0;"></ol>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ <div class="form-row">
345
+ <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 data-i18n="ui-form.label.element"></span></a>
346
+ </div>
347
+ <div class="form-row">
348
+ <label for="node-input-submit"><i class="fa fa-square"></i> <span data-i18n="ui-form.label.buttons"></label>
349
+ <i class="fa fa-thumbs-o-up"></i> <input type="text" id="node-input-submit" data-i18n="[placeholder]ui-form.label.submitButtonText" style="width:35%;">
350
+ <span style="margin-left:16px"><i class="fa fa-thumbs-o-down"></i></span>
351
+ <input type="text" id="node-input-cancel" data-i18n="[placeholder]ui-form.label.cancelButtonText" style="width:35%;">
352
+ </div>
353
+ <div class="form-row">
354
+ <label></label>
355
+ <input type="checkbox" id="node-input-splitLayout" style="display:inline-block; width:auto; vertical-align:top;">
356
+ <label for="node-input-splitLayout" style="width: auto" data-i18n="ui-form.label.splitLayout">
357
+ </div>
358
+ <div class="form-row">
359
+ <label></label>
360
+ <input type="checkbox" id="node-input-resetOnSubmit" style="display:inline-block; width:auto; vertical-align:top;">
361
+ <label for="node-input-resetOnSubmit" style="width: auto" data-i18n="ui-form.label.resetOnSubmit"></label>
362
+ </div>
363
+ <div class="form-row">
364
+ <label for="node-input-topic"><i class="fa fa-tasks"></i> <span data-i18n="ui-form.label.topic"></label>
365
+ <input type="text" id="node-input-topic" style="width:70%" data-i18n="[placeholder]ui-form.label.optionalMsgTopic">
366
+ <input type="hidden" id="node-input-topicType">
367
+ </div>
368
+ </script>
@@ -0,0 +1,18 @@
1
+ module.exports = function (RED) {
2
+ function FormNode (config) {
3
+ RED.nodes.createNode(this, config)
4
+
5
+ const node = this
6
+
7
+ const group = RED.nodes.getNode(config.group)
8
+ if (!group) { return }
9
+
10
+ const evts = {
11
+ onAction: true
12
+ }
13
+
14
+ // inform the dashboard UI that we are adding this node
15
+ group.register(node, config, evts)
16
+ }
17
+ RED.nodes.registerType('ui-form', FormNode)
18
+ }
@@ -0,0 +1,134 @@
1
+ <script type="text/javascript">
2
+ (function () {
3
+ // convert to i18 text
4
+ function c_ (x) {
5
+ return RED._('@flowforge/node-red-dashboard/ui-markdown:ui-markdown.' + x)
6
+ }
7
+
8
+ RED.nodes.registerType('ui-markdown', {
9
+ category: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.label.category'),
10
+ color: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.colors.dark'),
11
+ defaults: {
12
+ group: { type: 'ui-group', required: true },
13
+ name: { value: '' },
14
+ order: { value: 0 },
15
+ width: {
16
+ value: 0,
17
+ validate: function (v) {
18
+ let valid = true
19
+ if (this.templateScope !== 'global') {
20
+ const width = v || 0
21
+ const currentGroup = $('#node-input-group').val() || this.group
22
+ const groupNode = RED.nodes.node(currentGroup)
23
+ valid = !groupNode || +width <= +groupNode.width
24
+ $('#node-input-size').toggleClass('input-error', !valid)
25
+ }
26
+ return valid
27
+ }
28
+ },
29
+ height: { value: 0 },
30
+ content: { value: '# Markdown Content\n\nGoes here...' },
31
+ className: { value: '' }
32
+ },
33
+ inputs: 1,
34
+ outputs: 1,
35
+ icon: 'ui-markdown.png',
36
+ paletteLabel: 'markdown',
37
+ label: function () { return this.name || 'markdown' },
38
+ labelStyle: function () { return this.name ? 'node_label_italic' : '' },
39
+ oneditprepare: function () {
40
+ if (RED.editor.__debug === true) {
41
+ console.log('ui-markdown: oneditprepare')
42
+ }
43
+ $('#node-input-size').elementSizer({
44
+ width: '#node-input-width',
45
+ height: '#node-input-height',
46
+ group: '#node-input-group'
47
+ })
48
+ this.editor = RED.editor.createEditor({
49
+ id: 'node-input-content-editor',
50
+ mode: 'ace/mode/markdown',
51
+ value: $('#node-input-content').val()
52
+ })
53
+ RED.library.create({
54
+ url: 'uimarkdown', // where to get the data from
55
+ type: 'ui-markdown', // the type of object the library is for
56
+ editor: this.editor, // the field name the main text body goes to
57
+ mode: 'ace/mode/markdown',
58
+ fields: ['name']
59
+ })
60
+
61
+ this.editor.focus()
62
+
63
+ RED.popover.tooltip($('#node-markdown-expand-editor'), c_('label.expand'))
64
+
65
+ // use jQuery UI tooltip to convert the plain old title attribute to a nice tooltip
66
+ $('.ui-node-popover-title').tooltip({
67
+ show: {
68
+ effect: 'slideDown',
69
+ delay: 150
70
+ }
71
+ })
72
+ },
73
+ oneditsave: function () {
74
+ $('#node-input-content').val(this.editor.getValue())
75
+ this.editor.destroy()
76
+ delete this.editor
77
+ },
78
+ oneditcancel: function () {
79
+ this.editor.destroy()
80
+ delete this.editor
81
+ },
82
+ oneditresize: function (size) {
83
+ const rows = $('#dialog-form>div:not(.node-text-editor-row)')
84
+ let height = $('#dialog-form').height()
85
+ for (let i = 0; i < rows.size(); i++) {
86
+ height = height - $(rows[i]).outerHeight(true)
87
+ }
88
+ const editorRow = $('#dialog-form>div.node-text-editor-row')
89
+ height -= (parseInt(editorRow.css('marginTop')) + parseInt(editorRow.css('marginBottom')))
90
+ $('.node-text-editor').css('height', height + 'px')
91
+ this.editor.resize()
92
+ }
93
+ })
94
+ })()
95
+ </script>
96
+
97
+ <script type="text/html" data-template-name="ui-markdown">
98
+ <div class="form-row">
99
+ <label for="node-input-group"><i class="fa fa-table"></i> Group</label>
100
+ <input style="flex-grow:1" type="text" id="node-input-group">
101
+ </div>
102
+ <div class="form-row">
103
+ <label><i class="fa fa-object-group"></i> Size</label>
104
+ <input type="hidden" id="node-input-width">
105
+ <input type="hidden" id="node-input-height">
106
+ <button class="editor-button" id="node-input-size"></button>
107
+ </div>
108
+ <div class="form-row">
109
+ <label for="node-input-className"><i class="fa fa-code"></i> Class</label>
110
+ <div style="display: inline;">
111
+ <input style="width: 70%;" type="text" id="node-input-className" placeholder="Optional CSS class name(s)" style="flex-grow: 1;">
112
+ <a
113
+ data-html="true"
114
+ title="Dynamic Property: Class names can also be set by sending a message to the node with a msg.topic of 'ui-property:class' and a payload containing the class name(s) to be applied. NOTE: classes set at runtime will be applied in addition to any class(es) set in the nodes class field."
115
+ class="red-ui-button ui-node-popover-title"
116
+ 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;">
117
+ <i style="font-family: ui-serif;">fx</i>
118
+ </a>
119
+ </div>
120
+ </div>
121
+ <div class="form-row">
122
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
123
+ <div style="flex-grow: 1">
124
+ <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
125
+ </div>
126
+ </div>
127
+ <div class="form-row" style="margin-bottom:0px;">
128
+ <label for="node-input-content"><i class="fa fa-copy"></i> Content</label>
129
+ <input type="hidden" id="node-input-content">
130
+ </div>
131
+ <div class="form-row node-text-editor-row" style="display: block;">
132
+ <div style="height:250px; min-height:100px" class="node-text-editor" id="node-input-content-editor" ></div>
133
+ </div>
134
+ </script>
@@ -0,0 +1,14 @@
1
+ module.exports = function (RED) {
2
+ function MarkdownNode (config) {
3
+ const node = this
4
+
5
+ RED.nodes.createNode(this, config)
6
+
7
+ // which group are we rendering this widget
8
+ const group = RED.nodes.getNode(config.group)
9
+
10
+ // inform the dashboard UI that we are adding this node
11
+ group.register(node, config)
12
+ }
13
+ RED.nodes.registerType('ui-markdown', MarkdownNode)
14
+ }