Skip to content

Commit ed56eef

Browse files
committed
default modal handler seems to be working
1 parent d81af6e commit ed56eef

8 files changed

Lines changed: 298 additions & 7 deletions

File tree

include/wf.hrl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,26 @@
10701070
vessel :: id() | text(),
10711071
delegate :: module()
10721072
}).
1073+
-record(modal, {?ACTION_BASE(action_modal),
1074+
text="" :: text() | undefined,
1075+
body :: body() | undefined,
1076+
title_text :: text() | undefined,
1077+
title_body :: body() | undefined,
1078+
buttons=[] :: body() | undefined |
1079+
[#button{} |
1080+
{text(), Postback :: any()} |
1081+
{text(), Delegate :: module(), Postback :: any()}],
1082+
1083+
close_text :: text() | undefined,
1084+
close_body :: body() | undefined,
1085+
show_close_button=true :: boolean(),
1086+
options=[] :: term()
1087+
}).
1088+
-record(close_modal, {?ACTION_BASE(action_modal),
1089+
id :: undefined | id(),
1090+
options=[] :: term()
1091+
}).
1092+
10731093
-record(console_log, {?ACTION_BASE(action_console_log),
10741094
text="" :: any()
10751095
}).

priv/www/nitrogen.css

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,32 @@ div.inplace_textbox .LV_invalid {
176176
right:0px;
177177
}
178178

179+
.lightbox {
180+
z-index: 9999;
181+
}
182+
179183
.lightbox_background {
180-
background: #000000;
181-
z-index:98;
184+
background: rgba(128, 128, 128, 0.7);
185+
z-index:9990;
182186
}
183187

184188
.lightbox_table {
185189
width:100%;
186190
height:100%;
187-
z-index:99;
191+
z-index:9995;
188192
overflow:auto;
189193
}
190194

195+
.lightbox_form {
196+
z-index: 100000;
197+
background: var(---lightbox-form-bg, white);
198+
border: 1px solid var(---lightbox-form-border, black);
199+
border-radius: 10px;
200+
box-shadow: var(--lightbox-form-shadow, #000) 4px 4px 4px;
201+
padding: 20px;
202+
color: var(---lightbox-form-color, #000);
203+
}
204+
191205
/*** Gmail-style Uploading ***/
192206

193207
.upload_drop {

priv/www/nitrogen.js

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ function NitrogenClass(o) {
5858
this.$websocket_connecting_start=0;
5959
this.$last_websocket_received=0;
6060

61+
this.$modals = new Array();
62+
6163
this.$anchor_root_path = document;
6264
this.$params = new Object();
6365
this.$event_queue = new Array();
@@ -1353,13 +1355,80 @@ NitrogenClass.prototype.$set_cookie = function(cookie, value, path, minutes_to_l
13531355
expires,
13541356
"; path=",path
13551357
].join("");
1356-
}
1358+
};
13571359

1358-
/*** DATE PICKER ***/
1360+
NitrogenClass.prototype.$add_modal = function(id) {
1361+
var zindex = this.$max_modal_zindex() + 100;
1362+
var modal = {
1363+
zindex: zindex,
1364+
id: id
1365+
};
1366+
this.$modals.push(modal);
1367+
};
1368+
1369+
NitrogenClass.prototype.$add_modal_zindex = function(id) {
1370+
var max = this.$max_modal_zindex();
1371+
var z = objs(id).css('z-index');
1372+
objs(id).css('z-index', z + max);
1373+
};
1374+
1375+
NitrogenClass.prototype.$max_modal_zindex = function() {
1376+
if(this.$modals.length==0) {
1377+
return 0;
1378+
}else{
1379+
var max = 0;
1380+
this.$modals.forEach(function(x) {
1381+
max = (x.zindex > max) ? x.zindex : max;
1382+
});
1383+
return max;
1384+
}
1385+
};
13591386

1387+
NitrogenClass.prototype.$remove_modal = function(id) {
1388+
console.log("Removing: " + id);
1389+
if(id == undefined) {
1390+
var modal = this.$modals.pop();
1391+
return modal.id;
1392+
}else{
1393+
var item = this.$get_modal_index(id);
1394+
console.log("found item with id=" + item.modal.id);
1395+
if(item == null) {
1396+
return;
1397+
}else{
1398+
// delete the indexth itemm from this.$modals
1399+
this.$modals.splice(item.index, 1);
1400+
console.log("returning item.modal.id = " + item.modal.id);
1401+
return item.modal.id;
1402+
}
1403+
}
1404+
};
1405+
1406+
NitrogenClass.prototype.$get_modal_index = function(id) {
1407+
var found;
1408+
this.$modals.forEach(function(x, i) {
1409+
console.log("comparing " + id + " with " + x.id);
1410+
if(x.id==id) {
1411+
found = {
1412+
index: i,
1413+
modal: x
1414+
}
1415+
}
1416+
});
1417+
return found;
1418+
};
1419+
1420+
NitrogenClass.prototype.$scroll_to_modal = function(id) {
1421+
var o = objs(id);
1422+
if(o.css('position')=='absolute') {
1423+
var top = $(document).scrollTop() + 'px';
1424+
o.css('top', top);
1425+
}
1426+
};
1427+
1428+
/*** DATE PICKER ***/
13601429
NitrogenClass.prototype.$datepicker = function(pickerObj, pickerOptions) {
13611430
jQuery(pickerObj).datepicker(pickerOptions);
1362-
}
1431+
};
13631432

13641433
/*** AUTOCOMPLETE TEXTBOX ***/
13651434
NitrogenClass.prototype.$autocomplete = function(path, autocompleteOptions, enterPostbackInfo, selectPostbackInfo) {
@@ -1571,7 +1640,6 @@ NitrogenClass.prototype.$ws_send = function(data) {
15711640
return "closed";
15721641
}
15731642
};
1574-
15751643

15761644

15771645
NitrogenClass.prototype.$enable_websockets = function() {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
% vim: sw=4 ts=4 et ft=erlang
2+
% Nitrogen Web Framework for Erlang
3+
% Copyright (c) 2025 Jesse Gumm
4+
% See MIT-LICENSE for licensing information.
5+
6+
-module(default_modal_handler).
7+
-include("wf.hrl").
8+
-behaviour(modal_handler).
9+
-export ([
10+
init/2,
11+
finish/2,
12+
process_open_action/3,
13+
process_close_action/3
14+
]).
15+
16+
%-record(state, {}).
17+
18+
init(_Config, _State) ->
19+
{ok, []}.
20+
21+
finish(_Config, _State) ->
22+
{ok, []}.
23+
24+
process_open_action(Rec = #modal{}, _Config, _State) ->
25+
ID = wf:temp_id(),
26+
Body = build_body(ID, Rec),
27+
28+
%% Rendering Body and capturing the Actions, otherwise the button actions will get wired to the page before the buttons exist
29+
{ok, RenderedBody, BodyActions} = wf:render_isolated(Body),
30+
31+
Action = [
32+
#insert_top{
33+
trigger = Rec#modal.trigger,
34+
anchor = Rec#modal.anchor,
35+
target="body",
36+
elements=[
37+
#lightbox{id=ID, body=[
38+
#panel{class=lightbox_form, body=RenderedBody}
39+
]}
40+
]
41+
},
42+
BodyActions,
43+
%% TODO: replace with scrollTo when it's added
44+
#js_fun{function="Nitrogen.$scroll_to_modal", args=[ID]},
45+
#js_fun{function="Nitrogen.$add_modal", args=[ID]},
46+
#js_fun{function="Nitrogen.$add_modal_zindex", args=[ID]}
47+
],
48+
49+
{ok, ID, Action}.
50+
51+
build_body(ID, #modal{text=Text, body=Body,
52+
title_text=TitleT, title_body=TitleB,
53+
close_text=CloseT, close_body=CloseB,
54+
show_close_button=ShowCloseButton,
55+
buttons=Buttons0, options=_Opts}) ->
56+
Buttons = process_buttons(Buttons0),
57+
[
58+
title(TitleT, TitleB),
59+
body(Text, Body),
60+
#panel{class=lightbox_buttons, body=[
61+
Buttons,
62+
close_button(ShowCloseButton, ID, CloseT, CloseB)
63+
]}
64+
].
65+
66+
title(Text, Body) when ?WF_BLANK(Text) andalso ?WF_BLANK(Body) ->
67+
[];
68+
title(Text, Body) ->
69+
#h3{class=modal_title, text=Text, body=Body}.
70+
71+
body(Text, Body) when ?WF_BLANK(Text) andalso ?WF_BLANK(Body) ->
72+
[];
73+
body(Text, Body) ->
74+
#panel{class=modal_body, text=Text, body=Body}.
75+
76+
process_buttons([X | Rest]) when ?WF_BLANK(X) ->
77+
process_buttons(Rest);
78+
process_buttons([{Text, Postback} | Rest]) ->
79+
[#button{text=Text, postback=Postback} | process_buttons(Rest)];
80+
process_buttons([{Text, Delegate, Postback} | Rest]) ->
81+
[#button{text=Text, postback=Postback, delegate=Delegate} | process_buttons(Rest)];
82+
process_buttons([Button | Rest]) when ?IS_ELEMENT(Button) ->
83+
[Button | process_buttons(Rest)];
84+
process_buttons([]) ->
85+
[].
86+
87+
close_button(false, _, _, _) ->
88+
[];
89+
close_button(true, ID, Text, Body) when ?WF_BLANK(Text) andalso ?WF_BLANK(Body) ->
90+
#button{text="Close", click=#close_modal{id=ID}};
91+
close_button(true, ID, Text, Body) ->
92+
#button{text=Text, body=Body, click=#close_modal{id=ID}}.
93+
94+
process_close_action(#close_modal{id=ID}, _Config, _State) ->
95+
RemoveModalAndGetID = #js_fun{function="Nitrogen.$remove_modal", args=[ID]},
96+
Script = #js_fun{function="Nitrogen.$remove", args=["body", RemoveModalAndGetID]},
97+
{ok, ID, Script}.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
% vim: sw=4 ts=4 et ft=erlang
2+
% Nitrogen Web Framework for Erlang
3+
% Copyright (c) 2025 Jesse Gumm
4+
% See MIT-LICENSE for licensing information.
5+
6+
-module(modal_handler).
7+
-include("wf.hrl").
8+
-export ([
9+
process_open_action/1,
10+
render_open_action/1,
11+
process_close_action/1,
12+
render_close_action/1,
13+
open/1,
14+
close/1
15+
]).
16+
17+
%% using "process" instead of "render" since "render" implies the return value is something that be pumped through `wf:wire`
18+
-callback init( handler_config(),
19+
handler_state()) -> {ok, handler_state()}.
20+
-callback finish( handler_config(),
21+
handler_state()) -> {ok, handler_state()}.
22+
-callback process_open_action(#modal{},
23+
handler_config(),
24+
handler_state()) -> {ok, id(), script()}.
25+
-callback process_close_action(#close_modal{},
26+
handler_config(),
27+
handler_state()) -> {ok, id(), script()}.
28+
29+
-spec process_open_action(#modal{}) -> {ok, id(), script()}.
30+
process_open_action(Rec = #modal{}) ->
31+
wf_handler:call_readonly(modal_handler, process_open_action, [Rec]).
32+
33+
-spec process_close_action(#close_modal{}) -> {ok, id(), script()}.
34+
process_close_action(Rec = #close_modal{}) ->
35+
wf_handler:call_readonly(modal_handler, process_close_action, [Rec]).
36+
37+
38+
-spec render_open_action(#modal{}) -> script().
39+
render_open_action(Rec = #modal{}) ->
40+
{ok, _ID, Action} = process_open_action(Rec),
41+
Action.
42+
43+
-spec open(#modal{}) -> id().
44+
open(Rec = #modal{}) ->
45+
{ok, ID, Action} = process_open_action(Rec),
46+
wf:wire(Action),
47+
ID.
48+
49+
50+
-spec render_close_action(#close_modal{}) -> script().
51+
render_close_action(Rec = #close_modal{}) ->
52+
{ok, _ID, Action} = process_close_action(Rec),
53+
Action.
54+
55+
-spec close(#close_modal{}) -> id().
56+
close(Rec = #close_modal{}) ->
57+
{ok, ID, Action} = process_close_action(Rec),
58+
wf:wire(Action),
59+
ID.
60+
61+
62+
%open(Body) ->
63+
%
64+
%open(Body, CloseButton) ->
65+
%
66+
%

src/handlers/new_handler.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/sh
2+
3+
if [ $# -ne 1 ]; then
4+
echo "Usage: $0 <handler name>"
5+
exit 1
6+
fi
7+
8+
HANDLER="$1"
9+
10+
11+
# Check if source directory exists
12+
if [ -d "$HANDLER" ]; then
13+
echo "Error: Handler $HANDLER already exists"
14+
exit 1
15+
fi
16+
17+
18+
mkdir "$HANDLER"
19+
20+
cp prototype/example_handler.erl.prototype "$HANDLER/${HANDLER}_handler.erl"
21+
cp prototype/default_example_handler.erl.prototype "$HANDLER/default_${HANDLER}_handler.erl"
22+
23+
find $HANDLER -type f -exec sed -i "s/EXAMPLE/$HANDLER/g" {} +
24+
25+
echo "Done! New handler established in $HANDLER/"

src/handlers/prototype/default_example_handler.prototype renamed to src/handlers/prototype/default_example_handler.erl.prototype

File renamed without changes.

src/lib/wf_context.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ init_context(Bridge) ->
488488
make_handler(route_handler, dynamic_route_handler),
489489
make_handler(security_handler, default_security_handler),
490490
make_handler(validation_handler, default_validation_handler),
491+
make_handler(modal_handler, default_modal_handler),
491492
make_handler(postback_handler, default_postback_handler)
492493
]
493494
},

0 commit comments

Comments
 (0)