Node-Red configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ui_dropdown.html 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. <script type="text/javascript">
  2. (function () {
  3. function hasProperty (obj, prop) {
  4. return Object.prototype.hasOwnProperty.call(obj, prop)
  5. }
  6. RED.nodes.registerType('ui-dropdown', {
  7. category: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.label.category'),
  8. color: RED._('@flowfuse/node-red-dashboard/ui-base:ui-base.colors.light'),
  9. defaults: {
  10. group: { type: 'ui-group', required: true },
  11. name: { value: '' },
  12. label: { value: 'Select Option:' },
  13. tooltip: { value: '' },
  14. order: { value: 0 },
  15. width: {
  16. value: 0,
  17. validate: function (v) {
  18. const width = v || 0
  19. const currentGroup = $('#node-input-group').val() || this.group
  20. const groupNode = RED.nodes.node(currentGroup)
  21. const valid = !groupNode || +width <= +groupNode.width
  22. $('#node-input-size').toggleClass('input-error', !valid)
  23. return valid
  24. }
  25. },
  26. height: { value: 0 },
  27. passthru: { value: false },
  28. multiple: { value: false },
  29. chips: { value: false },
  30. clearable: { value: false },
  31. options: {
  32. value: [{ value: '', label: '' }],
  33. validate: function (v) {
  34. const unique = new Set(v.map(function (o) { return o.value }))
  35. return v.length === unique.size
  36. }
  37. },
  38. payload: { value: '' },
  39. topic: { value: 'topic', validate: (hasProperty(RED.validators, 'typedInput') ? RED.validators.typedInput('topicType') : function (v) { return true }) },
  40. topicType: { value: 'msg' },
  41. className: { value: '' },
  42. // This field controls if the used component is going to be a `v-combox` or a `v-select`, `v-combox` allows typing and filtering possible values
  43. typeIsComboBox: { value: true }
  44. },
  45. inputs: 1,
  46. outputs: 1,
  47. icon: 'font-awesome/fa-bars',
  48. paletteLabel: 'dropdown',
  49. label: function () { return this.name || (~this.label.indexOf('{' + '{') ? null : this.label) || 'dropdown' },
  50. labelStyle: function () { return this.name ? 'node_label_italic' : '' },
  51. oneditprepare: function () {
  52. if (this.multiple === undefined) {
  53. $('#node-input-multiple').prop('checked', false)
  54. }
  55. if (this.chips === undefined) {
  56. $('#node-input-chips').prop('checked', false)
  57. }
  58. if (this.clearable === undefined) {
  59. $('#node-input-clearable').prop('checked', false)
  60. }
  61. if (this.typeIsComboBox === undefined) {
  62. $('#node-input-typeIsComboBox').prop('checked', true)
  63. }
  64. // if this groups parent is a subflow template, set the node-config-input-width and node-config-input-height up
  65. // as typedInputs and hide the elementSizer (as it doesn't make sense for subflow templates)
  66. if (RED.nodes.subflow(this.z)) {
  67. // change inputs from hidden to text & display them
  68. $('#node-input-width').attr('type', 'text')
  69. $('#node-input-height').attr('type', 'text')
  70. $('div.form-row.nr-db-ui-element-sizer-row').hide()
  71. $('div.form-row.nr-db-ui-manual-size-row').show()
  72. } else {
  73. // not in a subflow, use the elementSizer
  74. $('div.form-row.nr-db-ui-element-sizer-row').show()
  75. $('div.form-row.nr-db-ui-manual-size-row').hide()
  76. $('#node-input-size').elementSizer({
  77. width: '#node-input-width',
  78. height: '#node-input-height',
  79. group: '#node-input-group'
  80. })
  81. }
  82. const un = new Set(this.options.map(function (o) { return o.value }))
  83. if (this.options.length === un.size) { $('#valWarning').hide() } else { $('#valWarning').show() }
  84. function generateOption (i, option) {
  85. const container = $('<li/>', { style: 'background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px; border-bottom: 1px solid var(--red-ui-form-input-border-color, #ccc);' })
  86. const row = $('<div/>').appendTo(container)
  87. $('<div/>', { style: 'padding-top:5px; padding-left:175px;' }).appendTo(container)
  88. $('<div/>', { style: 'padding-top:5px; padding-left:120px;' }).appendTo(container)
  89. $('<i style="color: var(--red-ui-form-text-color, #eee); cursor:move; margin-left:3px;" class="node-input-option-handle fa fa-bars"></i>').appendTo(row)
  90. $('<input/>', { class: 'node-input-option-value', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: 'Value', value: option.value }).appendTo(row).typedInput({ default: option.type || 'str', types: ['str', 'num', 'bool'] })
  91. $('<input/>', { class: 'node-input-option-label', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: 'Label', value: option.label }).appendTo(row)
  92. const finalSpan = $('<span/>', { style: 'float:right; margin-right:8px;' }).appendTo(row)
  93. const deleteButton = $('<a/>', { href: '#', class: 'editor-button editor-button-small', style: 'margin-top:7px; margin-left:5px;' }).appendTo(finalSpan)
  94. $('<i/>', { class: 'fa fa-remove' }).appendTo(deleteButton)
  95. deleteButton.click(function () {
  96. container.css({ background: 'var(--red-ui-secondary-background-inactive, #fee)' })
  97. container.fadeOut(300, function () {
  98. $(this).remove()
  99. })
  100. })
  101. $('#node-input-option-container').append(container)
  102. }
  103. $('#node-input-add-option').click(function () {
  104. generateOption($('#node-input-option-container').children().length + 1, {})
  105. $('#node-input-option-container-div').scrollTop($('#node-input-option-container-div').get(0).scrollHeight)
  106. })
  107. for (let i = 0; i < this.options.length; i++) {
  108. const option = this.options[i]
  109. generateOption(i + 1, option)
  110. }
  111. $('#node-input-option-container').sortable({
  112. axis: 'y',
  113. handle: '.node-input-option-handle',
  114. cursor: 'move'
  115. })
  116. $('#node-input-topic').typedInput({
  117. default: 'str',
  118. typeField: $('#node-input-topicType'),
  119. types: ['str', 'msg', 'flow', 'global']
  120. })
  121. // use jQuery UI tooltip to convert the plain old title attribute to a nice tooltip
  122. $('.ui-node-popover-title').tooltip({
  123. show: {
  124. effect: 'slideDown',
  125. delay: 150
  126. }
  127. })
  128. },
  129. oneditsave: function () {
  130. const options = $('#node-input-option-container').children()
  131. const node = this
  132. node.options = []
  133. options.each(function (i) {
  134. const option = $(this)
  135. const o = {
  136. label: option.find('.node-input-option-label').val(),
  137. value: option.find('.node-input-option-value').typedInput('value'),
  138. type: option.find('.node-input-option-value').typedInput('type')
  139. }
  140. if (option.find('.node-input-option-value').typedInput('type') === 'num') {
  141. o.value = Number(o.value)
  142. }
  143. if (option.find('.node-input-option-value').typedInput('type') === 'bool') {
  144. o.value = (o.value === 'true')
  145. }
  146. node.options.push(o)
  147. })
  148. },
  149. oneditresize: function () {
  150. }
  151. })
  152. })()
  153. </script>
  154. <script type="text/html" data-template-name="ui-dropdown">
  155. <div class="form-row">
  156. <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
  157. <input type="text" id="node-input-name">
  158. </div>
  159. <div class="form-row">
  160. <label for="node-input-group"><i class="fa fa-table"></i> Group</label>
  161. <input type="text" id="node-input-group">
  162. </div>
  163. <div class="form-row nr-db-ui-element-sizer-row">
  164. <label><i class="fa fa-object-group"></i> <span data-i18n="ui-dropdown.label.size">Size</label>
  165. <button class="editor-button" id="node-input-size"></button>
  166. </div>
  167. <div class="form-row nr-db-ui-manual-size-row">
  168. <label><i class="fa fa-arrows-h"></i> <span data-i18n="ui-dropdown.label.width">Width</label>
  169. <input type="hidden" id="node-input-width">
  170. </div>
  171. <div class="form-row nr-db-ui-manual-size-row">
  172. <label><i class="fa fa-arrows-v"></i> <span data-i18n="ui-dropdown.label.height">Height</label>
  173. <input type="hidden" id="node-input-height">
  174. </div>
  175. <div class="form-row">
  176. <label for="node-input-label"><i class="fa fa-tag"></i> Label</label>
  177. <input type="text" id="node-input-label" placeholder="optional label">
  178. </div>
  179. <div class="form-row">
  180. <label for="node-input-className"><i class="fa fa-code"></i> Class</label>
  181. <div style="display: inline;">
  182. <input style="width: 70%;" type="text" id="node-input-className" placeholder="Optional CSS class name(s)" style="flex-grow: 1;">
  183. <a
  184. data-html="true"
  185. 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."
  186. class="red-ui-button ui-node-popover-title"
  187. 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;">
  188. <i style="font-family: ui-serif;">fx</i>
  189. </a>
  190. </div>
  191. </div>
  192. <!--<div class="form-row">
  193. <label for="node-input-tooltip"><i class="fa fa-info-circle"></i> Tooltip</label>
  194. <input type="text" id="node-input-tooltip" placeholder="optional tooltip">
  195. </div>-->
  196. <div class="form-row form-row-flex node-input-option-container-row" style="margin-bottom: 0px;width: 100%">
  197. <label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> Options</label>
  198. <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%;">
  199. <span id="valWarning" style="color: var(--red-ui-text-color-error, #910000)"><b>All Values must be unique.</b></span>
  200. <ol id="node-input-option-container" style="list-style-type:none; margin:0;"></ol>
  201. </div>
  202. <a
  203. data-html="true"
  204. title="Dynamic Property: Send 'msg.options' in order to override this property."
  205. class="red-ui-button ui-node-popover-title"
  206. 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;">
  207. <i style="font-family: ui-serif;">fx</i>
  208. </a>
  209. </div>
  210. <div class="form-row">
  211. <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>option</span></a>
  212. </div>
  213. <div class="form-row">
  214. <label style="width:auto" for="node-input-multiple"><i class="fa fa-th-list"></i> Allow multiple selections from list: </label>
  215. <input type="checkbox" checked id="node-input-multiple" style="display: inline-block; width: auto; margin: 0px 0px 0px 4px;">
  216. </div>
  217. <div class="form-row">
  218. <label style="width:auto" for="node-input-chips"><i class="fa fa-circle-o"></i> Show selected elements in chips </label>
  219. <input type="checkbox" checked id="node-input-chips" style="display: inline-block; width: auto; margin: 0px 0px 0px 4px;">
  220. </div>
  221. <div class="form-row">
  222. <label style="width:auto" for="node-input-clearable"><i class="fa fa-times"></i> Clear selection with button </label>
  223. <input type="checkbox" checked id="node-input-clearable" style="display: inline-block; width: auto; margin: 0px 0px 0px 4px;">
  224. </div>
  225. <div class="form-row">
  226. <label style="width:auto" for="node-input-typeIsComboBox"><i class="fa fa-pencil-square"></i> Allow Search </label>
  227. <input type="checkbox" checked id="node-input-typeIsComboBox" style="display: inline-block; width: auto; margin: 0px 0px 0px 4px;">
  228. </div>
  229. </div>
  230. <div class="form-row">
  231. <label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
  232. <input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
  233. </div>
  234. <div class="form-row">
  235. <label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
  236. <input type="text" id="node-input-topic" style="width:70%" placeholder="optional msg.topic">
  237. <input type="hidden" id="node-input-topicType">
  238. </div>
  239. </script>