Namdak Tonpa 3 years ago
parent
commit
f9acfc0225

+ 1 - 1
include/comboLookupEdit.hrl

@@ -2,6 +2,6 @@
 -define(COMBO_LOOKUP_EDIT_HRL, true).
 -define(COMBO_LOOKUP_EDIT_HRL, true).
 
 
 -include_lib("nitro/include/nitro.hrl").
 -include_lib("nitro/include/nitro.hrl").
--record(comboLookupEdit, {?ELEMENT_BASE(element_comboLookupEdit), input, disabled, form, values, multiple}).
+-record(comboLookupEdit, {?ELEMENT_BASE(element_comboLookupEdit), input, disabled, form, values}).
 
 
 -endif.
 -endif.

+ 8 - 0
include/multipleInput.hrl

@@ -0,0 +1,8 @@
+-ifndef(MULTIPLEINPUT_HRL).
+-define(MULTIPLEINPUT_HRL, true).
+
+-include_lib("nitro/include/nitro.hrl").
+
+-record(multipleInput, {?ELEMENT_BASE(element_multipleInput), input=[], disabled=[], values=[]}).
+
+-endif.

+ 6 - 6
include/nitro.hrl

@@ -12,7 +12,7 @@
         ancestor=element, id=[], module=Module, delegate=Delegate, validation=[],
         ancestor=element, id=[], module=Module, delegate=Delegate, validation=[],
         validate=[], actions=[], class=[], style=[], source=[], onmouseover=[], onmouseout=[], onmousemove=[],
         validate=[], actions=[], class=[], style=[], source=[], onmouseover=[], onmouseout=[], onmousemove=[],
         onkeypress=[], onchange=[], onkeyup=[], onkeydown=[], onclick=[],
         onkeypress=[], onchange=[], onkeyup=[], onkeydown=[], onclick=[],
-        data_fields=[], aria_states=[], body=[], role=[], tabindex=[], show_if=true,
+        data_fields=[], aria_states=[], body=[], role=[], tabindex=[], show_if=true, multiple=false,
         html_tag=Tag, title=[], postback=[], accesskey=[], contenteditable=[],
         html_tag=Tag, title=[], postback=[], accesskey=[], contenteditable=[],
         contextmenu=[], dir=[], draggable=[], dropzone=[], hidden=[], lang=[],
         contextmenu=[], dir=[], draggable=[], dropzone=[], hidden=[], lang=[],
         spellcheck=[], translate=[], onblur=[], onerror=[], onfocus=[],
         spellcheck=[], translate=[], onblur=[], onerror=[], onfocus=[],
@@ -25,7 +25,7 @@
 -record(literal, {?ELEMENT_BASE(element_literal), html_encode=true }).
 -record(literal, {?ELEMENT_BASE(element_literal), html_encode=true }).
 -record(dtl, {?ELEMENT_BASE(element_dtl), file="index", bindings=[], app=web, folder="priv/templates", ext="html", bind_script=true, js_escape=false }).
 -record(dtl, {?ELEMENT_BASE(element_dtl), file="index", bindings=[], app=web, folder="priv/templates", ext="html", bind_script=true, js_escape=false }).
 -record(list, {?ELEMENT_BASE(element_list), numbered=false }).
 -record(list, {?ELEMENT_BASE(element_list), numbered=false }).
--record(dropdown, {?ELEMENT_BASE(element_dropdown), options, value, multiple=false, disabled=false, name}).
+-record(dropdown, {?ELEMENT_BASE(element_dropdown), options, value, disabled=false, name}).
 -record(radiogroup, {?ELEMENT_BASE(element_radiogroup)}).
 -record(radiogroup, {?ELEMENT_BASE(element_radiogroup)}).
 -record(spinner, {?ELEMENT_BASE(element_spinner), image="/priv/static/spinner.gif"}).
 -record(spinner, {?ELEMENT_BASE(element_spinner), image="/priv/static/spinner.gif"}).
 
 
@@ -68,19 +68,19 @@
 -record(option,       {?ELEMENT_BASE(element_select), disabled=[], label=[], selected=false, value=[]}).
 -record(option,       {?ELEMENT_BASE(element_select), disabled=[], label=[], selected=false, value=[]}).
 -record(output,       {?ELEMENT_BASE(element_output), for, form, name}).
 -record(output,       {?ELEMENT_BASE(element_output), for, form, name}).
 -record(progress,       {?ELEMENT_BASE(element_progress), max=[], value=[]}).
 -record(progress,       {?ELEMENT_BASE(element_progress), max=[], value=[]}).
--record(select,       {?ELEMENT_BASE(element_select), autofocus=[], disabled=[], form=[], multiple=[], name=[], required=[], size=[]}).
+-record(select,       {?ELEMENT_BASE(element_select), autofocus=[], disabled=[], form=[], name=[], required=[], size=[]}).
 -record(textarea,       {?ELEMENT_BASE(element_textarea), autofocus=[], cols=[], dirname=[], disabled=[], form=[], maxlength, name, placeholder, readonly=[], required=[], rows=[], wrap=[], value=[]}).
 -record(textarea,       {?ELEMENT_BASE(element_textarea), autofocus=[], cols=[], dirname=[], disabled=[], form=[], maxlength, name, placeholder, readonly=[], required=[], rows=[], wrap=[], value=[]}).
 
 
 % HTML Form inputs
 % HTML Form inputs
--record(input,       {?ELEMENT_BASE(element_input), required, autocomplete, autofocus, disabled, form, name, value, type=[], checked=false, placeholder, multiple, min, max, pattern, accept}).
+-record(input,       {?ELEMENT_BASE(element_input), required, autocomplete, autofocus, disabled, form, name, value, type=[], checked=false, placeholder, min, max, pattern, accept}).
 -record(input_button,       {?ELEMENT_BASE(element_input_button),  autofocus, disabled, form, name, value}).
 -record(input_button,       {?ELEMENT_BASE(element_input_button),  autofocus, disabled, form, name, value}).
 -record(checkbox,           {?ELEMENT_BASE(element_checkbox),  autofocus, checked=false, disabled, form, name, required, value}).
 -record(checkbox,           {?ELEMENT_BASE(element_checkbox),  autofocus, checked=false, disabled, form, name, required, value}).
 -record(color,           {?ELEMENT_BASE(element_color),  autocomplete, autofocus, disabled, form, list, name, value}).
 -record(color,           {?ELEMENT_BASE(element_color),  autocomplete, autofocus, disabled, form, list, name, value}).
 -record(date,           {?ELEMENT_BASE(element_date),  autocomplete, autofocus, disabled, form, list, max, min, name, step, readonly, required, value}).
 -record(date,           {?ELEMENT_BASE(element_date),  autocomplete, autofocus, disabled, form, list, max, min, name, step, readonly, required, value}).
 -record(datetime,           {?ELEMENT_BASE(element_datetime),  autocomplete, autofocus, disabled, form, list, max, min, name, step, readonly, required, value}).
 -record(datetime,           {?ELEMENT_BASE(element_datetime),  autocomplete, autofocus, disabled, form, list, max, min, name, step, readonly, required, value}).
 -record(datetime_local,           {?ELEMENT_BASE(element_datetime_local),  autocomplete, autofocus, disabled, form, list, max, min, name, step, readonly, required, value}).
 -record(datetime_local,           {?ELEMENT_BASE(element_datetime_local),  autocomplete, autofocus, disabled, form, list, max, min, name, step, readonly, required, value}).
--record(email,           {?ELEMENT_BASE(element_email),  autocomplete, autofocus, disabled, form, list, maxlength, multiple, name, pattern, placeholder, readonly, required, size, value}).
--record(file,           {?ELEMENT_BASE(element_file),  accept, autofocus, disabled, form, multiple, name, required}).
+-record(email,           {?ELEMENT_BASE(element_email),  autocomplete, autofocus, disabled, form, list, maxlength, name, pattern, placeholder, readonly, required, size, value}).
+-record(file,           {?ELEMENT_BASE(element_file),  accept, autofocus, disabled, form, name, required}).
 -record(hidden,           {?ELEMENT_BASE(element_hidden),  disabled, form, name, value, html_name}).
 -record(hidden,           {?ELEMENT_BASE(element_hidden),  disabled, form, name, value, html_name}).
 -record(input_image,           {?ELEMENT_BASE(element_input_image),  alt, autofocus, disabled, form, formaction, formenctype, formmethod, formnovalue, formtarget, height, name, src, width}).
 -record(input_image,           {?ELEMENT_BASE(element_input_image),  alt, autofocus, disabled, form, formaction, formenctype, formmethod, formnovalue, formtarget, height, name, src, width}).
 -record(month,              {?ELEMENT_BASE(element_month),  alt, autocomplete, autofocus, disabled, form, list, min, max, name, readonly, required, step, value}).
 -record(month,              {?ELEMENT_BASE(element_month),  alt, autocomplete, autofocus, disabled, form, list, min, max, name, readonly, required, step, value}).

+ 1 - 1
mix.exs

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

+ 7 - 1
priv/css/sortable.css

@@ -105,7 +105,8 @@ body {
   cursor: default;
   cursor: default;
 }
 }
 
 
-.add-btn {
+.add-btn,
+.delete-btn {
   position: absolute;
   position: absolute;
   top: 8px;
   top: 8px;
   right: 10px;
   right: 10px;
@@ -118,3 +119,8 @@ body {
   height: 16px;
   height: 16px;
   background-color: #ffffff;
   background-color: #ffffff;
 }
 }
+
+.delete-btn {
+  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;
+}

+ 9 - 0
priv/js/comboLookup.js

@@ -23,6 +23,15 @@ function comboLookupChange(dom) {
   }
   }
 }
 }
 
 
+function clearInput(dom) {
+  const input = qi(dom);
+  if (input) {
+    input.value = '';
+    input.removeAttribute('data-bind');
+  }
+  comboClear(dom);
+}
+
 function comboLookupClick(dom, feed, mod) {
 function comboLookupClick(dom, feed, mod) {
   var char = event.which || event.keyCode;
   var char = event.which || event.keyCode;
   if (char ==  1 && !activeCombo && qi(dom).value == '') {
   if (char ==  1 && !activeCombo && qi(dom).value == '') {

+ 3 - 2
priv/js/sortable.js

@@ -180,7 +180,8 @@ function () {
     key: "getValues",
     key: "getValues",
     value: function getValues() {
     value: function getValues() {
       return Array.from(this.items.map(function(item) {
       return Array.from(this.items.map(function(item) {
-        let text = item.children[1].firstChild.innerHTML;
+        let list = Array.from(item.children).find(x => x.getAttribute("list-item-content"));
+        let text = list.firstChild.innerHTML;
         let bind = item.getAttribute('data-bind');
         let bind = item.getAttribute('data-bind');
         if (bind) return { 'text': text, 'bind': bind };
         if (bind) return { 'text': text, 'bind': bind };
         return text;
         return text;
@@ -223,7 +224,7 @@ function appendItemFromBind(dom,value,bind) {
   template.innerHTML =
   template.innerHTML =
     '<div class="list__item" data-sortable-item="data-sortable-item" style="" data-bind="'+ bind + '">' +
     '<div class="list__item" data-sortable-item="data-sortable-item" style="" data-bind="'+ bind + '">' +
        '<div class="list__item-close" onclick="removeSortableItem(\'#' + sortable.list.id + '\', this.parentNode);"></div>' +
        '<div class="list__item-close" onclick="removeSortableItem(\'#' + sortable.list.id + '\', this.parentNode);"></div>' +
-       '<div class="list__item-content"><div class="list__item-title">' + value + '</div></div>' +
+       '<div class="list__item-content" list-item-content="list-item-content"><div class="list__item-title">' + value + '</div></div>' +
        '<div class="list__item-handle" data-sortable-handle="data-sortable-handle"></div>' +
        '<div class="list__item-handle" data-sortable-handle="data-sortable-handle"></div>' +
     '</div>'
     '</div>'
   var new_item = template.content.firstChild;
   var new_item = template.content.firstChild;

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

@@ -8,9 +8,9 @@ proto(#comboKeyup{delegate=Module}=Msg)  -> Module:proto(Msg);
 proto(#comboSelect{delegate=Module}=Msg) -> Module:proto(Msg).
 proto(#comboSelect{delegate=Module}=Msg) -> Module:proto(Msg).
 
 
 render_element(#comboLookup{id=Id, style=Style, value = Val, bind = Object,
 render_element(#comboLookup{id=Id, style=Style, value = Val, bind = Object,
-  feed = Feed, disabled = Disabled, delegate = Module} = Data) ->
+  feed = Feed, disabled = Disabled, delegate = Module, class = Class} = Data) ->
   nitro:render(
   nitro:render(
-    #link{id=form:atom([lookup, Id]), class=[dropdown],
+    #link{id=form:atom([lookup, Id]), class=lists:flatten([dropdown, Class]), draggable=false,
            body=[#input{id=Id, disabled = Disabled, type="comboLookup",
            body=[#input{id=Id, disabled = Disabled, type="comboLookup",
                         autocomplete = "off",
                         autocomplete = "off",
                         onkeyup = nitro:jse("comboLookupKeyup('"
                         onkeyup = nitro:jse("comboLookupKeyup('"
@@ -36,4 +36,10 @@ render_element(#comboLookup{id=Id, style=Style, value = Val, bind = Object,
                               ++ nitro:to_list(Module) ++ "')")
                               ++ nitro:to_list(Module) ++ "')")
                           end},
                           end},
                  #panel{id=form:atom([comboContainer, Id]),
                  #panel{id=form:atom([comboContainer, Id]),
-                        class = ['dropdown-content']}]}).
+                        class = ['dropdown-content']},
+                 case Disabled of
+                   true -> [];
+                   false -> #link{class = [button, sgreen, 'delete-btn'],
+                                  style = "min-width: 40px; text-align: center; height: fit-content; margin-left: 5px;",
+                                  onclick = nitro:jse("clearInput('" ++ Id ++ "')"),
+                                  body = <<"">>} end]}).

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

@@ -22,7 +22,8 @@ render_element(#comboLookupEdit{id=Id, input=Input, disabled=Disabled, validatio
               case Multiple of
               case Multiple of
                 true ->
                 true ->
                   #link{
                   #link{
-                    class = [add-btn],
+                    class = [button, sgreen, 'add-btn'],
+                    style = "min-width: 40px; text-align: center; height: fit-content; margin-left: 5px;",
                     onclick = nitro:jse("addSortableItemFrom('#" ++ ListId ++ "', '" ++ InputId ++ "')"),
                     onclick = nitro:jse("addSortableItemFrom('#" ++ ListId ++ "', '" ++ InputId ++ "')"),
                     body = <<"+">>};
                     body = <<"+">>};
                 false -> []
                 false -> []
@@ -32,7 +33,7 @@ render_element(#comboLookupEdit{id=Id, input=Input, disabled=Disabled, validatio
                 _ ->
                 _ ->
                   #panel{
                   #panel{
                     id = form:atom([InputId, "form"]),
                     id = form:atom([InputId, "form"]),
-                    class = ['dropdown-content'],
+                    class = ['dropdown-content', 'dropdown-content-form'],
                     body = #panel{class = ['dropdown-item'], body = Form}
                     body = #panel{class = ['dropdown-item'], body = Form}
                   } end ]},
                   } end ]},
             case Multiple of
             case Multiple of

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

@@ -22,7 +22,8 @@ render_element(#comboLookupVec{id=Id, input=Input, disabled=Disabled, validation
               _ ->
               _ ->
                 [ Input,
                 [ Input,
                   #link{
                   #link{
-                    class = ['add-btn'],
+                    class = [button, sgreen, 'add-btn'],
+                    style = "min-width: 40px; text-align: center; height: fit-content; margin-left: 5px;",
                     onclick = nitro:jse("addSortableItemFrom('#" ++ ListId ++ "', '" ++ InputId ++ "')"),
                     onclick = nitro:jse("addSortableItemFrom('#" ++ ListId ++ "', '" ++ InputId ++ "')"),
                     body = <<"+">>} ] end },
                     body = <<"+">>} ] end },
         % TODO: Add validation for each list_item and/or "+" button
         % TODO: Add validation for each list_item and/or "+" button

+ 30 - 0
src/elements/combo/element_multipleInput.erl

@@ -0,0 +1,30 @@
+-module(element_multipleInput).
+-include_lib("nitro/include/multipleInput.hrl").
+-include_lib("nitro/include/sortable_list.hrl").
+-include_lib("nitro/include/nitro.hrl").
+-include_lib("nitro/include/event.hrl").
+-export([render_element/1]).
+
+render_element(#multipleInput{id=Id, input=Input, disabled=Disabled, validation=Validation, values=Values}) ->
+  ListId = form:atom([Id, "list"]),
+  InputId = element(#element.id, Input),
+  nitro:render(
+    #panel{
+      id = Id,
+      validation = Validation,
+      data_fields = [{<<"data-vector-input">>,<<"data-vector-input">>}],
+      body = [
+        #panel{
+          style = "display: flex; width: 100%; justify-content: center;",
+          class=['multi-input'],
+          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 },
+        #sortable_list{id = ListId, values = Values, closeable = true, disabled = Disabled}]}).

+ 22 - 26
src/elements/combo/element_sortable_item.erl

@@ -5,33 +5,29 @@
 -export([render_element/1]).
 -export([render_element/1]).
 
 
 render_element(#sortable_item{list_id=ListId, value=Value, bind=Bind, closeable=Close, disabled=Disabled}) ->
 render_element(#sortable_item{list_id=ListId, value=Value, bind=Bind, closeable=Close, disabled=Disabled}) ->
-  Item = case Disabled of
-    true ->
+  Item =
+    #panel{
+      class = <<"list__item">>,
+      data_fields = [ {<<"data-sortable-item">>,<<"data-sortable-item">>} |
+        case Bind of
+          [] -> [];
+          _ -> [{<<"data-bind">>, base64:encode(term_to_binary(Bind))}] end ],
+      body = [
+        case Close of
+          true ->
+            #panel{
+              class = <<"list__item-close">>,
+              onclick = nitro:jse("removeSortableItem('#" ++ ListId ++ "', this.parentNode);")};
+          _ -> [] end,
       #panel{
       #panel{
-        class = <<"list__item">>,
-        body = #panel{
-            class = <<"list__item-content">>,
-            style = <<"width:100%">>,
-            body = #panel{ class = <<"list__item-title">>, body = Value}}};
-    _ ->
-      #panel{
-        class = <<"list__item">>,
-        data_fields = [ {<<"data-sortable-item">>,<<"data-sortable-item">>} |
-          case Bind of
-            [] -> [];
-            _ -> [{<<"data-bind">>, base64:encode(term_to_binary(Bind))}] end ],
-        body = [
-          case Close of
-            true -> 
-              #panel{
-                class = <<"list__item-close">>,
-                onclick = nitro:jse("removeSortableItem('#" ++ ListId ++ "', this.parentNode);")};
-            _ -> [] end,
-          #panel{
-            class = <<"list__item-content">>,
-            style = case Close of true -> []; _ -> <<"width:100% - 40px">> end,
-            body = #panel{ class = <<"list__item-title">>, body = Value}},
+        class = <<"list__item-content">>,
+        data_fields = [{<<"list-item-content">>,<<"list-item-content">>}],
+        style = case Close of true -> []; _ -> <<"width:100% - 40px">> end,
+        body = #panel{ class = <<"list__item-title">>, body = Value}},
+      case Disabled of
+        true -> [];
+        _ ->
           #panel{
           #panel{
             class = <<"list__item-handle">>,
             class = <<"list__item-handle">>,
-            data_fields = [{<<"data-sortable-handle">>,<<"data-sortable-handle">>}]}]} end,
+            data_fields = [{<<"data-sortable-handle">>,<<"data-sortable-handle">>}]} end]},
   nitro:render(Item).
   nitro:render(Item).

+ 1 - 0
src/elements/combo/element_sortable_list.erl

@@ -8,6 +8,7 @@
 render_element(#sortable_list{id = Id, values = Values, closeable = Close, disabled = Disabled}) ->
 render_element(#sortable_list{id = Id, values = Values, closeable = Close, disabled = Disabled}) ->
   case Disabled of
   case Disabled of
     true ->
     true ->
+      nitro:wire("createSortable('#" ++ Id ++ "');"),
       Closeable = false;
       Closeable = false;
     _ ->
     _ ->
       nitro:wire("createSortable('#" ++ Id ++ "');"),
       nitro:wire("createSortable('#" ++ Id ++ "');"),

+ 1 - 1
src/nitro.app.src

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

+ 1 - 1
src/render/wf_tags.erl

@@ -1,7 +1,7 @@
 -module(wf_tags).
 -module(wf_tags).
 -author('Maxim Sokhatsky').
 -author('Maxim Sokhatsky').
 -include_lib("nitro/include/nitro.hrl").
 -include_lib("nitro/include/nitro.hrl").
--export([emit_tag/2, write_props/1, prop/1, display_property/1]).
+-export([emit_tag/3, emit_tag/2, write_props/1, prop/1, display_property/1]).
 -define(VOID(Tag),  (Tag == <<"br">>     orelse Tag == <<"hr">>
 -define(VOID(Tag),  (Tag == <<"br">>     orelse Tag == <<"hr">>
               orelse Tag == <<"link">>   orelse Tag == <<"img">>
               orelse Tag == <<"link">>   orelse Tag == <<"img">>
               orelse Tag == <<"input">>  orelse Tag == <<"link">>
               orelse Tag == <<"input">>  orelse Tag == <<"link">>