Browse Source

revert the revert + fixes

bohdan.kotenko 2 years ago
parent
commit
b78e1e20ff

+ 2 - 0
include/comboLookup.hrl

@@ -11,6 +11,8 @@
 -record(comboInsert, { uid=[], dom=[], rows=[], chunks=[], status=[], delegate=[], feed=[] }).
 -record(comboAdd,    { list_id=[], value=[], bind=[], delegate=[], pos=[], feed=[], default=[]}).
 -record(comboModify, { list_id=[], item_id=[], value=[], bind=[], modify_bind=[], delegate=[], pos=[], feed=[] }).
+-record(comboGroup,  { dom=[], value=[], delegate=[] }).
+-record(comboDraft,  { dom=[], list=[], values=[], group=[], subtitle=[], delegate=[] }).
 -record(comboLookup, { ?ELEMENT_BASE(element_comboLookup),
     value=[],
     disabled=false,

+ 11 - 0
include/comboLookupGroup.hrl

@@ -0,0 +1,11 @@
+-ifndef(COMBO_LOOKUP_GROUP_HRL).
+-define(COMBO_LOOKUP_GROUP_HRL, true).
+
+-include_lib("nitro/include/nitro.hrl").
+-record(comboLookupGroup, {?ELEMENT_BASE(element_comboLookupGroup),
+                           input = [],
+                           disabled = false,
+                           values = [],
+                           subtitle = []}).
+
+-endif.

+ 9 - 0
include/comboLookupGroup_item.hrl

@@ -0,0 +1,9 @@
+-ifndef(COMBO_LOOKUP_GROUP_ITEM_HRL).
+-define(COMBO_LOOKUP_GROUP_ITEM_HRL, true).
+
+-include_lib("nitro/include/nitro.hrl").
+-record(comboLookupGroup_item, {?ELEMENT_BASE(element_comboLookupGroup_item),
+                                value = [],
+                                group = []}).
+
+-endif.

+ 11 - 0
include/comboLookupGroup_list.hrl

@@ -0,0 +1,11 @@
+-ifndef(COMBO_LOOKUP_GROUP_LIST_HRL).
+-define(COMBO_LOOKUP_GROUP_LIST_HRL, true).
+
+-include_lib("nitro/include/nitro.hrl").
+-record(comboLookupGroup_list, {?ELEMENT_BASE(element_comboLookupGroup_list),
+                                disabled = false,
+                                values = [],
+                                subtitle = [],
+                                group = []}).
+
+-endif.

+ 2 - 1
lib/NITRO.ex

@@ -2,7 +2,8 @@ defmodule NITRO do
   require Record
 
   files = ["calendar.hrl", "nitro.hrl", "comboLookup.hrl", "nitro_pi.hrl", "comboLookupVec.hrl", "comboLookupEdit.hrl",
-           "comboLookupText.hrl", "comboLookupModify.hrl", "comboLookupModify_item.hrl", "koatuuControl.hrl", "n2o.hrl"]
+           "comboLookupText.hrl", "comboLookupModify.hrl", "comboLookupModify_item.hrl", "comboLookupGroup.hrl",
+           "comboLookupGroup_list.hrl", "comboLookupGroup_item.hrl", "koatuuControl.hrl", "n2o.hrl"]
 
   hrl_files =
     Enum.filter(files, fn f ->

+ 39 - 0
lib/combo.ex

@@ -17,6 +17,8 @@ defmodule NITRO.Combo do
   def proto(NITRO.comboInsert(delegate: []) = msg), do: comboInsert(msg)
   def proto(NITRO.comboAdd(delegate: []) = msg), do: comboAdd(msg)
   def proto(NITRO.comboModify(delegate: []) = msg), do: comboModify(msg)
+  def proto(NITRO.comboGroup(delegate: []) = msg), do: comboGroup(msg)
+  def proto(NITRO.comboDraft(delegate: []) = msg), do: comboDraft(msg)
 
   def proto(NITRO.comboKey(delegate: module) = msg) do
     case has_function(module, :keyUp) do
@@ -60,6 +62,20 @@ defmodule NITRO.Combo do
     end
   end
 
+  def proto(NITRO.comboGroup(delegate: module) = msg) do
+    case has_function(module, :comboGroup) do
+      true -> module.comboGroup(msg)
+      false -> comboGroup(msg)
+    end
+  end
+
+  def proto(NITRO.comboDraft(delegate: module) = msg) do
+    case has_function(module, :comboDraft) do
+      true -> module.comboDraft(msg)
+      false -> comboDraft(msg)
+    end
+  end
+
   def select(NITRO.comboSelect(uid: uid, dom: field)), do:
     NITRO.Combo.Search.stop(uid, field)
 
@@ -93,6 +109,29 @@ defmodule NITRO.Combo do
     )
   end
 
+  def comboGroup(NITRO.comboGroup(dom: dom, value: value, delegate: module)) do
+    :nitro.insert_bottom(
+      dom,
+      NITRO.comboLookupGroup_item(value: view_value(value, module, []), bind: value, group: :draft)
+    )
+  end
+
+  def comboDraft(NITRO.comboDraft(values: [])), do: []
+  def comboDraft(NITRO.comboDraft(dom: dom, list: list_id, values: values, group: group, subtitle: subtitle, delegate: module)) do
+    value_pairs = {:view_value_pairs, :lists.map(fn val -> {view_value(val, module, []), val} end, values)}
+    proto_item = NITRO.comboLookupGroup_list(subtitle: subtitle, delegate: module)
+    draft_id = :form.atom([dom, :draft])
+
+    case group do
+      :draft ->
+        :nitro.update(draft_id, NITRO.comboLookupGroup_list(proto_item, id: draft_id, group: :draft))
+        :nitro.insert_bottom(dom, NITRO.comboLookupGroup_list(proto_item, id: :form.atom([dom, :erp.guid()]), values: value_pairs, group: :saved))
+      _ ->
+        :nitro.update(draft_id, NITRO.comboLookupGroup_list(proto_item, id: draft_id, values: value_pairs, group: :draft))
+        :nitro.remove(list_id)
+    end
+  end
+
   def dropDown0(uid, obj, dom0, module, feed) do
     case has_function(module, :dropDown) do
       true ->

+ 1 - 1
mix.exs

@@ -4,7 +4,7 @@ defmodule NITRO.Mixfile do
   def project do
     [
       app: :nitro,
-      version: "7.6.2",
+      version: "7.7.0",
       description: "NITRO Nitrogen Web Framework",
       package: package(),
       deps: deps()

+ 14 - 2
priv/css/sortable.css

@@ -107,7 +107,10 @@ body {
 
 .add-btn,
 .delete-btn,
-.modify_item-close {
+.modify_item-close,
+.group-button__confirm,
+.group-button__delete,
+.group-button__edit {
   position: absolute;
   top: 8px;
   right: 10px;
@@ -122,7 +125,16 @@ body {
 }
 
 .delete-btn,
-.modify_item-close {
+.modify_item-close,
+.group-button__delete {
   background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.6666 4.27325L11.7266 3.33325L7.99992 7.05992L4.27325 3.33325L3.33325 4.27325L7.05992 7.99992L3.33325 11.7266L4.27325 12.6666L7.99992 8.93992L11.7266 12.6666L12.6666 11.7266L8.93992 7.99992L12.6666 4.27325Z' fill='%23757575'/%3E%3C/svg%3E%0A");
   right: 30px;
+}
+
+.group-button__confirm {
+  background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm6 1 4-4 1 1-5 5-3-2 1-1 2 1Z' fill='%230D63B3'/%3E%3C/svg%3E");
+}
+
+.group-button__edit {
+  background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m12.246 2.193 1.56 1.56c.26.26.26.68 0 .94l-1.22 1.22-2.5-2.5 1.22-1.22A.656.656 0 0 1 11.772 2c.174 0 .34.06.474.193ZM1.999 11.5V14h2.5l7.373-7.373-2.5-2.5L2 11.5Zm1.947 1.167h-.614v-.614l6.04-6.04.614.614-6.04 6.04Z' fill='%23757575'/%3E%3C/svg%3E");
 }

+ 121 - 50
priv/js/comboLookup.js

@@ -42,37 +42,106 @@ function comboClear(dom) {
 }
 
 function comboSelect(uid, dom, row, feed, mod, id) {
-    let elem = qi(dom);
-    comboClear(dom);
-    if (qi(id)) elem.setAttribute("data-bind", qi(id).getAttribute('data-bind'));
-    elem.value = row;
-    elem.style.backgroundColor = 'white';
-    var dropdown = qi(dom).closest('.dropdown');
-    dropdown.classList.remove('dropdown-open');
-    dropdown.classList.remove('is-reversed');
-    dropdown.classList.add('dropdown-bind');
-    let value = qi(id) ? dec(unbase64(qi(id).getAttribute('data-bind'))) : string(row);
-    const modifyItem = qi(elem.getAttribute('nested'));
-    if (modifyItem) {
-      const list = modifyItem.parentNode;
-      direct(tuple(atom('comboModify'),
-                   string(list.id),
-                   string(modifyItem.id),
-                   string(modifyItem.firstChild.innerHTML),
-                   dec(unbase64(modifyItem.getAttribute('data-bind'))),
-                   value,
-                   dec(unbase64(list.getAttribute('data-delegate'))),
-                   dec(unbase64(list.getAttribute('data-pos'))),
-                   dec(unbase64(list.getAttribute('data-feed')))));
-    } else {
-      direct(tuple(atom('comboSelect'), bin(uid), value, string(dom), string(feed), atom(mod)));
+  let elem = qi(dom);
+  elem.value = '';
+  comboClear(dom);
+
+  if (qi(elem.getAttribute('nested'))) {
+    comboSelectNested(...arguments);
+  } else if (elem.parentNode.parentNode.getAttribute('data-modify-input')) {
+    comboSelectModify(...arguments);
+  } else if (elem.parentNode.parentNode.parentNode.getAttribute('data-vector-input')) {
+    comboSelectVector(...arguments);
+  } else if (elem.parentNode.parentNode.getAttribute('data-group-input')) {
+    comboSelectGroup(...arguments);
+  } else {
+    comboSelectDefault(...arguments);
+  }
+}
+
+function comboSelectDefault(uid, dom, row, feed, mod, id) {
+  let elem = qi(dom);
+  elem.closest('.dropdown').classList.add('dropdown-bind')
+  elem.value = row;
+
+  let value = string(row);
+  const selected = qi(id);
+  if (selected) {
+    elem.setAttribute('data-bind', selected.getAttribute('data-bind'));
+    value = dec(unbase64(selected.getAttribute('data-bind')));
+  }
+
+  direct(tuple(atom('comboSelect'), bin(uid), value, string(dom), string(feed), atom(mod)));
+  comboLookupTextApply(dom);
+};
+
+function comboSelectNested(uid, dom, row, feed, mod, id) {
+  const modifyItem = qi(qi(dom).getAttribute('nested'));
+  const list = modifyItem.parentNode;
+  const value = qi(id) ? dec(unbase64(qi(id).getAttribute('data-bind'))) : string(row)
+
+  direct(tuple(atom('comboModify'),
+               string(list.id),
+               string(modifyItem.id),
+               string(modifyItem.firstChild.innerHTML),
+               dec(unbase64(modifyItem.getAttribute('data-bind'))),
+               value,
+               dec(unbase64(list.getAttribute('data-delegate'))),
+               dec(unbase64(list.getAttribute('data-pos'))),
+               dec(unbase64(list.getAttribute('data-feed')))));
+}
+
+function comboSelectModify(uid, dom, row, feed, mod, id) {
+  comboSelectDefault(...arguments);
+  let listSplit = dom.split('_');
+  listSplit.pop();
+  const listId = listSplit.join('_') + '_list';
+  const input = qi(dom);
+  const list = qi(listId);
+  if (list && input && input.value != '') {
+    const data = querySourceRaw(dom);
+    if (data && data.hasOwnProperty('text') && data.hasOwnProperty('bind')) {
+      const bind = data.bind;
+      const value = data.text;
+      if (bind !== '' && bind !== 'null') {
+        clearInput(dom);
+        direct(tuple(atom('comboAdd'),
+                     string(listId),
+                     string(value),
+                     dec(unbase64(bind)),
+                     dec(unbase64(list.getAttribute('data-delegate'))),
+                     dec(unbase64(list.getAttribute('data-pos'))),
+                     dec(unbase64(list.getAttribute('data-feed'))),
+                     dec(unbase64(list.getAttribute('data-default')))));
+      }
     }
-    comboLookupTextApply(dom);
+  }
+}
+
+function comboSelectVector(uid, dom, row, feed, mod, id) {
+  comboSelectDefault(...arguments);
+  let listSplit = dom.split('_');
+  listSplit.pop();
+  const list = '#' + listSplit.join('_') + '_list';
+  addSortableItemFrom(list, dom);
+}
+
+function comboSelectGroup(uid, dom, row, feed, mod, id) {
+  const selected = qi(id);
+  if (selected) {
+    const parent = qi(dom).parentNode.parentNode;
+    const bind = selected.getAttribute('data-bind');
+    if (!parent.querySelector(`[data-group-item='data-group-item'][data-bind='${bind}']`)) {
+      const draft = parent.id + '_draft';
+      const value = dec(unbase64(bind));
+      direct(tuple(atom('comboGroup'), string(draft), value, atom(mod)));
+    }
+  }
 }
 
 function comboLookupChange(dom) {
   let elem = qi(dom);
-  if (elem) {
+  if (elem && !elem.getAttribute('nested')) {
     elem.removeAttribute("data-bind");
     const dropdown = qi(dom).closest('.dropdown');
     if (dropdown) { dropdown.classList.remove('dropdown-bind'); }
@@ -133,6 +202,8 @@ function comboLookupKeydown(uid, dom, feed, mod) {
 
 function comboLookupKeyup(uid, dom, feed, mod) {
     var dropdown = qi(dom).closest('.dropdown')
+    if (event.key == 'Shift') { return }
+    if (event.key == 'Tab') { dropdown.classList.remove('dropdown-open'); return }
     var char = event.which || event.keyCode;
     if (char == 27 || (char == 8 || char == 46) && qi(dom).value == '') { clearInput(dom); return }
     if (char == 13 && currentItem) { currentItem.click(); return }
@@ -176,29 +247,6 @@ function clearInput(dom) {
   comboClear(dom);
 }
 
-function comboLookupModifyAdd(listId, inputId) {
-  const input = qi(inputId);
-  const list = qi(listId);
-  if (list && input && input.value != '') {
-    const data = querySourceRaw(inputId);
-    if (data && data.hasOwnProperty('text') && data.hasOwnProperty('bind')) {
-      const bind = data.bind;
-      const value = data.text;
-      if (bind !== '' && bind !== 'null') {
-        clearInput(inputId);
-        direct(tuple(atom('comboAdd'),
-                     string(listId),
-                     string(value),
-                     dec(unbase64(bind)),
-                     dec(unbase64(list.getAttribute('data-delegate'))),
-                     dec(unbase64(list.getAttribute('data-pos'))),
-                     dec(unbase64(list.getAttribute('data-feed'))),
-                     dec(unbase64(list.getAttribute('data-default')))));
-      }
-    }
-  }
-}
-
 function comboLookupModifyValues(listId) {
   const list = qi(listId);
   if (list) {
@@ -210,10 +258,33 @@ function comboLookupModifyValues(listId) {
   }
 }
 
+function comboLookupGroupDraft(id, group, subtitle, mod) {
+  const elem = qi(id);
+  const values = Array.from(elem.querySelectorAll('[data-group-item]')).map(function (el) {return dec(unbase64(el.getAttribute('data-bind')))});
+  direct(tuple(atom('comboDraft'), string(elem.parentNode.id), string(id), values, atom(group), bin(subtitle), atom(mod)));
+}
+
+function groupListDrag(e) { e.dataTransfer.setData('text', e.target.id); }
+
+function groupListDrop(e) {
+  e.preventDefault();
+  const node1 = e.currentTarget;
+  const node2 = qi(e.dataTransfer.getData('text'));
+  const parent = node1.parentNode;
+  const sibling = node1.nextSibling === node2 ? node1 : node1.nextSibling;
+
+  node2.parentNode.insertBefore(node1, node2);
+  parent.insertBefore(node2, sibling);
+}
+
+function groupListAllowDrop(e) {
+  e.preventDefault();
+  e.dataTransfer.dropEffect = 'move';
+}
+
 document.addEventListener("click", () => {
   if (activeCombo && event.target.className != 'triangle' &&
     !event.target.closest('#comboContainer_' + activeCombo)) {
-    qi(activeCombo).value = '';
     comboClear(activeCombo);
   } else if (activeForm && event.target.className != 'triangle' &&
     !event.target.closest("#" + activeForm)) {

+ 3 - 2
src/elements/combo/element_comboLookup.erl

@@ -9,7 +9,9 @@ proto(#comboSelect{delegate=Module}=Msg) -> Module:proto(Msg);
 proto(#comboScroll{delegate=Module}=Msg) -> Module:proto(Msg);
 proto(#comboInsert{delegate=Module}=Msg) -> Module:proto(Msg);
 proto(#comboAdd{delegate=Module}=Msg)    -> Module:proto(Msg);
-proto(#comboModify{delegate=Module}=Msg) -> Module:proto(Msg).
+proto(#comboModify{delegate=Module}=Msg) -> Module:proto(Msg);
+proto(#comboGroup{delegate=Module}=Msg)  -> Module:proto(Msg);
+proto(#comboDraft{delegate=Module}=Msg)  -> Module:proto(Msg).
 
 render_element(#comboLookup{id=Id, style=Style, value = Val, bind = Object,
   feed = Feed, disabled = Disabled, delegate = Module, class = Class, nested = Nested} = Data) ->
@@ -36,7 +38,6 @@ render_element(#comboLookup{id=Id, style=Style, value = Val, bind = Object,
                         bind = Object,
                         value = Val, style = Style, class = column},
                  #panel{class=['triangle'],
-                        body="▾",
                         onclick =
                           case Disabled of
                             true -> [];

+ 21 - 0
src/elements/combo/element_comboLookupGroup.erl

@@ -0,0 +1,21 @@
+-module(element_comboLookupGroup).
+-include_lib("nitro/include/comboLookupGroup.hrl").
+-include_lib("nitro/include/comboLookupGroup_list.hrl").
+-include_lib("nitro/include/nitro.hrl").
+-include_lib("nitro/include/event.hrl").
+-export([render_element/1]).
+
+render_element(#comboLookupGroup{id = Id, input = Input, disabled = Disabled, validation = Validation, values = Values, subtitle = Subtitle, delegate = Delegate}) ->
+  ProtoItem = #comboLookupGroup_list{disabled = Disabled, subtitle = Subtitle, delegate = Delegate},
+  nitro:render(
+    #panel{
+      id = Id,
+      validation = Validation,
+      data_fields = [{<<"data-group-input">>, <<"data-group-input">>}],
+      body = lists:flatten([
+        case Disabled of true -> []; _ -> Input end,
+        ProtoItem#comboLookupGroup_list{id = form:atom([Id, "draft"]), group = draft},
+        [ProtoItem#comboLookupGroup_list{id = form:atom([Id, erp:guid()]), values = Val, group = saved} || Val <- Values]
+      ])
+    }
+  ).

+ 20 - 0
src/elements/combo/element_comboLookupGroup_item.erl

@@ -0,0 +1,20 @@
+-module(element_comboLookupGroup_item).
+-include_lib("nitro/include/comboLookupGroup_item.hrl").
+-include_lib("nitro/include/nitro.hrl").
+-include_lib("nitro/include/event.hrl").
+-export([render_element/1]).
+
+render_element(#comboLookupGroup_item{value = Value, bind = Bind, group = Group}) ->
+  nitro:render(
+    #panel{
+      class = <<"group-list__item">>,
+      data_fields = lists:flatten([
+        {<<"data-group-item">>, <<"data-group-item">>},
+        case Bind of [] -> []; _ -> {<<"data-bind">>, base64:encode(term_to_binary(Bind))} end
+      ]),
+      body = lists:flatten([
+        #panel{class = <<"group-list__item-content">>, body = Value},
+        case Group of draft -> #button{class = [<<"button">>, <<"group-button__delete">>], onclick = nitro:jse("this.parentNode.remove();")}; _ -> [] end
+      ])
+    }
+  ).

+ 50 - 0
src/elements/combo/element_comboLookupGroup_list.erl

@@ -0,0 +1,50 @@
+-module(element_comboLookupGroup_list).
+-include_lib("nitro/include/comboLookupGroup_list.hrl").
+-include_lib("nitro/include/comboLookupGroup_item.hrl").
+-include_lib("nitro/include/nitro.hrl").
+-include_lib("nitro/include/event.hrl").
+-export([render_element/1]).
+
+render_element(#comboLookupGroup_list{disabled = true, group = draft}) -> [];
+render_element(#comboLookupGroup_list{id = Id, values = Values, subtitle = Subtitle, group = Group, delegate = Delegate, disabled = Disabled}) ->
+  ProtoItem = #comboLookupGroup_item{group = Group},
+  Click = nitro:jse("comboLookupGroupDraft('" ++ nitro:to_list(Id) ++ "', '" ++ nitro:to_list(Group) ++ "', '" ++ nitro:to_list(Subtitle) ++ "', '" ++ nitro:to_list(Delegate) ++ "');"),
+  Drag =
+    case Group of
+      draft -> [];
+      _ when not Disabled -> [
+        {<<"draggable">>, <<"true">>},
+        {<<"ondragstart">>, <<"groupListDrag(event)">>},
+        {<<"ondrop">>, <<"groupListDrop(event)">>},
+        {<<"ondragover">>, <<"groupListAllowDrop(event)">>}
+      ];
+      _ -> []
+    end,
+  Control =
+    case Group of
+      draft -> [
+        #panel{class = <<"group-list__label">>, body = Subtitle},
+        #button{class = [<<"button">>, <<"group-button__confirm">>], onclick = Click}
+      ];
+      _ when not Disabled -> [
+        #button{class = [<<"button">>, <<"group-button__edit">>], onclick = Click},
+        #button{class = [<<"button">>, <<"group-button__delete">>], onclick = nitro:jse("this.parentNode.remove();")}
+      ];
+      _ -> []
+    end,
+  Body =
+    case Values of
+      {view_value_pairs, List} -> [ProtoItem#comboLookupGroup_item{value = Val, bind = Bind} || {Val, Bind} <- List];
+      _ -> [ProtoItem#comboLookupGroup_item{value = Val} || Val <- Values]
+    end,
+  nitro:render(
+    #panel{
+      id = Id,
+      class = case Group of draft -> <<"group-list__draft">>; _ -> <<"group-list__saved">> end,
+      data_fields = lists:flatten([
+        {<<"data-group-list">>, Group},
+        Drag
+      ]),
+      body = lists:flatten([Control, Body])
+    }
+  ).

+ 1 - 13
src/elements/combo/element_comboLookupModify.erl

@@ -8,18 +8,6 @@
 render_element(#comboLookupModify{id = Id, input = Input, disabled = Disabled, validation = Validation, values = Values,
   modify_pos = Pos, modify_feed = Feed, modify_module = Module, modify_default = Default}) ->
   ListId = form:atom([Id, "list"]),
-  InputId = element(#element.id, Input),
-  InputBody =
-    case Disabled of
-      true -> [];
-      _ ->
-        #panel{
-          body = [
-            Input,
-            #link{class = 'add-btn', onclick = nitro:jse("comboLookupModifyAdd('" ++ ListId ++ "', '" ++ InputId ++ "');")}
-          ]
-        }
-    end,
   ProtoItem = #comboLookupModify_item{list_id = ListId, pos = Pos, feed = Feed, delegate = Module, default = Default, disabled = Disabled},
   ListBody =
     case Values of
@@ -32,7 +20,7 @@ render_element(#comboLookupModify{id = Id, input = Input, disabled = Disabled, v
       data_fields = [{<<"data-modify-input">>, <<"data-modify-input">>}],
       validation = Validation,
       body = [
-        InputBody,
+        case Disabled of true -> []; _ -> Input end,
         #panel{
           id = ListId,
           data_fields = [

+ 1 - 7
src/elements/combo/element_comboLookupVec.erl

@@ -19,13 +19,7 @@ render_element(#comboLookupVec{id=Id, input=Input, disabled=Disabled, validation
           body =
             case Disabled of
               true -> [];
-              _ ->
-                [ Input,
-                  #link{
-                    class = [button, sgreen, 'add-btn'],
-                    style = "min-width: 40px; text-align: center; height: fit-content; margin-left: 5px;",
-                    onclick = nitro:jse("addSortableItemFrom('#" ++ ListId ++ "', '" ++ InputId ++ "')"),
-                    body = <<"+">>} ] end },
+              _ -> Input end },
         % TODO: Add validation for each list_item and/or "+" button
         % TODO?: Maybe show message "Empty list" when Values == []
         #sortable_list{id = ListId, values = Values, closeable = true, disabled = Disabled}]}).

+ 1 - 1
src/nitro.app.src

@@ -1,6 +1,6 @@
 {application, nitro, [
     {description,  "NITRO Nitrogen Web Framework"},
-    {vsn,          "7.6.2"},
+    {vsn,          "7.7.0"},
     {applications, [kernel, stdlib]},
     {modules, []},
     {registered,   []},