Refactor toast notifications and modal dialog implementation
- Updated SCSS for toast notifications to support multiple toast types (success, info, warning, error) and improved layout. - Added new SVG icons for error, info, success, and warning notifications. - Created separate HTML templates for toast notifications and modal dialogs. - Enhanced the dev panel with buttons to test different toast notifications and modal dialogs.
This commit is contained in:
parent
7b9df97a32
commit
cf9c6b2a3f
15 changed files with 399 additions and 70 deletions
|
|
@ -1,21 +1,39 @@
|
||||||
@use 'constants' as consts;
|
@use 'constants' as consts;
|
||||||
|
|
||||||
#toast {
|
#toasts {
|
||||||
visibility: hidden;
|
|
||||||
width: 300px;
|
|
||||||
margin-left: -125px;
|
|
||||||
|
|
||||||
border: 0.1em solid consts.$color-3;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
background-color: consts.$color-2;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
padding: consts.$margin;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 30px;
|
top: 15px;
|
||||||
box-shadow: -1px 1px 10px rgba(0, 0, 0, 0.3);
|
transform: translate(-50%, 0%);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
// visibility: hidden;
|
||||||
|
display: none;
|
||||||
|
width: fit-content;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
border: 0.1em solid consts.$color-3;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background-color: consts.$color-2;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
padding: calc(2 * consts.$margin) calc(4 * consts.$margin);
|
||||||
|
box-shadow: -1px 1px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
margin: consts.$margin;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #toast.show {
|
// #toast.show {
|
||||||
|
|
|
||||||
54
backend/static/error.svg
Normal file
54
backend/static/error.svg
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="40.905399mm"
|
||||||
|
height="40.905399mm"
|
||||||
|
viewBox="0 0 40.905399 40.905399"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="error.svg"
|
||||||
|
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="1.4702524"
|
||||||
|
inkscape:cx="-96.582055"
|
||||||
|
inkscape:cy="13.263029"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1369"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="828"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-80.178404,-120.49168)">
|
||||||
|
<circle
|
||||||
|
style="fill:#ff1e1e;fill-opacity:1;stroke:#a00000;stroke-width:7;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
id="path2"
|
||||||
|
cx="100.6311"
|
||||||
|
cy="140.94438"
|
||||||
|
r="16.9527" />
|
||||||
|
<path
|
||||||
|
d="m 107.32373,131.05185 -6.61942,6.62988 q -0.0732,0.0628 -0.13595,0 l -6.629879,-6.62988 q -0.658805,-0.6588 -1.599955,-0.6588 -0.930694,0 -1.589499,0.6588 -0.669262,0.66926 -0.669262,1.61042 0,0.93069 0.669262,1.58949 l 6.619425,6.62989 q 0.0732,0.0627 0,0.12548 l -6.619425,6.62988 q -0.669262,0.65881 -0.669262,1.5895 0,0.94115 0.669262,1.61042 0.658805,0.6588 1.589499,0.6588 0.94115,0 1.599955,-0.6588 l 6.629879,-6.62989 q 0.0628,-0.0627 0.13595,0 l 6.61942,6.62989 q 0.65881,0.6588 1.59996,0.6588 0.94115,0 1.59995,-0.6588 0.65881,-0.66927 0.65881,-1.61042 0,-0.93069 -0.65881,-1.5895 l -6.62988,-6.62988 q -0.0627,-0.0627 0,-0.12548 l 6.62988,-6.62989 q 0.65881,-0.6588 0.65881,-1.58949 0,-0.94116 -0.65881,-1.61042 -0.6588,-0.6588 -1.59995,-0.6588 -0.94115,0 -1.59996,0.6588 z"
|
||||||
|
id="text1"
|
||||||
|
style="font-size:21.4164px;font-family:'Arial Rounded MT Bold';-inkscape-font-specification:'Arial Rounded MT Bold, ';fill:#ffffff;stroke-width:8.36709;stroke-linejoin:round;paint-order:stroke fill markers"
|
||||||
|
aria-label="❌" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
54
backend/static/info.svg
Normal file
54
backend/static/info.svg
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="40.905399mm"
|
||||||
|
height="40.905399mm"
|
||||||
|
viewBox="0 0 40.905399 40.905399"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="info.svg"
|
||||||
|
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="1.4702524"
|
||||||
|
inkscape:cx="-31.287145"
|
||||||
|
inkscape:cy="44.550174"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1369"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="828"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-80.178404,-120.49168)">
|
||||||
|
<circle
|
||||||
|
style="fill:#1e22ff;fill-opacity:1;stroke:#0003a0;stroke-width:7;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
id="path2"
|
||||||
|
cx="100.6311"
|
||||||
|
cy="140.94438"
|
||||||
|
r="16.9527" />
|
||||||
|
<path
|
||||||
|
d="m 103.30394,136.96938 v 15.44961 q 0,1.60566 -0.76367,2.42808 -0.76367,0.82241 -1.93854,0.82241 -1.174876,0 -1.918964,-0.842 -0.724506,-0.84199 -0.724506,-2.40849 v -15.29296 q 0,-1.58608 0.724506,-2.38891 0.744088,-0.80283 1.918964,-0.80283 1.17487,0 1.93854,0.80283 0.76367,0.80283 0.76367,2.23226 z m -2.64347,-5.52191 q -1.116129,0 -1.91896,-0.68534 -0.78325,-0.68535 -0.78325,-1.93855 0,-1.13571 0.802831,-1.86022 0.822413,-0.74408 1.899379,-0.74408 1.03781,0 1.84064,0.66576 0.80283,0.66576 0.80283,1.93854 0,1.23362 -0.78325,1.93855 -0.78325,0.68534 -1.86022,0.68534 z"
|
||||||
|
id="text1"
|
||||||
|
style="font-size:40.1024px;font-family:'Arial Rounded MT Bold';-inkscape-font-specification:'Arial Rounded MT Bold, ';fill:#ffffff;stroke-width:19.8934;stroke-linejoin:round;paint-order:stroke fill markers"
|
||||||
|
aria-label="i" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
54
backend/static/success.svg
Normal file
54
backend/static/success.svg
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="40.905399mm"
|
||||||
|
height="40.905399mm"
|
||||||
|
viewBox="0 0 40.905399 40.905399"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="success.svg"
|
||||||
|
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="0.73512621"
|
||||||
|
inkscape:cx="-352.32046"
|
||||||
|
inkscape:cy="-74.13693"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1369"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="828"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-80.178404,-120.49168)">
|
||||||
|
<circle
|
||||||
|
style="fill:#03b500;fill-opacity:1;stroke:#027500;stroke-width:7;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
id="path2"
|
||||||
|
cx="100.6311"
|
||||||
|
cy="140.94438"
|
||||||
|
r="16.9527" />
|
||||||
|
<path
|
||||||
|
d="m 112.46328,132.63911 q -0.58288,-0.62262 -1.37771,-0.84782 -0.78159,-0.2252 -1.58967,-0.0397 -0.79483,0.17221 -1.41745,0.75509 l -11.962235,11.27338 -3.85494,-3.96092 q -0.596125,-0.60937 -1.390958,-0.82132 -0.781586,-0.21196 -1.576419,-0.0132 -0.794833,0.18546 -1.417452,0.78158 -0.609372,0.59613 -0.821328,1.39096 -0.211955,0.79484 -0.02649,1.58967 0.198708,0.79483 0.794833,1.4042 l 5.974495,6.14671 q 0.887563,0.91406 2.159296,0.94055 1.271733,0.0265 2.199038,-0.84782 l 14.174524,-13.36644 q 0.62262,-0.58288 0.84782,-1.36446 0.2252,-0.79484 0.0397,-1.58967 -0.17221,-0.80808 -0.75509,-1.4307 z"
|
||||||
|
id="text1"
|
||||||
|
style="font-size:27.1303px;font-family:'Arial Rounded MT Bold';-inkscape-font-specification:'Arial Rounded MT Bold, ';fill:#ffffff;stroke-width:13.4584;stroke-linejoin:round;paint-order:stroke fill markers"
|
||||||
|
aria-label="✔️" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
53
backend/static/warning.svg
Normal file
53
backend/static/warning.svg
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="40.905399mm"
|
||||||
|
height="40.905399mm"
|
||||||
|
viewBox="0 0 40.905399 40.905399"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname="warning.svg"
|
||||||
|
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="1.0396254"
|
||||||
|
inkscape:cx="-80.79833"
|
||||||
|
inkscape:cy="65.889115"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1369"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="828"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-80.178404,-120.49168)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffff1a;fill-opacity:1;stroke:#616100;stroke-width:5.513;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
d="m 84.523721,157.05141 16.107569,-32.21437 16.10679,32.21437 z"
|
||||||
|
id="path1"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="font-size:37.4875px;font-family:'Arial Rounded MT Bold';-inkscape-font-specification:'Arial Rounded MT Bold, ';fill:#000000;fill-opacity:1;stroke-width:2.49752"
|
||||||
|
d="m 98.737968,144.66325 -0.550671,-8.24286 q -0.154876,-2.40918 -0.154876,-3.4589 0,-1.4283 0.739965,-2.2199 0.757173,-0.80879 1.978984,-0.80879 1.47992,0 1.97897,1.03251 0.49904,1.01529 0.49904,2.94265 0,1.13576 -0.12046,2.30593 l -0.73996,8.48378 q -0.12047,1.51435 -0.51626,2.32314 -0.39579,0.8088 -1.30784,0.8088 -0.929259,0 -1.290638,-0.77438 -0.361377,-0.79159 -0.516254,-2.39198 z m 1.910142,11.32318 q -1.049718,0 -1.841307,-0.67113 -0.774382,-0.68834 -0.774382,-1.91015 0,-1.06692 0.739965,-1.80688 0.757173,-0.75717 1.841304,-0.75717 1.08413,0 1.84131,0.75717 0.77438,0.73996 0.77438,1.80688 0,1.2046 -0.77438,1.89294 -0.77438,0.68834 -1.80689,0.68834 z"
|
||||||
|
id="text1"
|
||||||
|
aria-label="!" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -23,15 +23,8 @@
|
||||||
dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
|
dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="toasts">
|
{% include "toast.html" %}
|
||||||
<div id="toast"></div>
|
{% include "modal_dialog.html" %}
|
||||||
</div>
|
|
||||||
|
|
||||||
<dialog id="modal-dialog">
|
|
||||||
<div class="content"></div>
|
|
||||||
<input type="button" class="ok" value="OK">
|
|
||||||
<input type="button" class="cancel" value="Cancel">
|
|
||||||
</dialog>
|
|
||||||
|
|
||||||
{% block body_container %}{% endblock %}
|
{% block body_container %}{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,31 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="content" id="dev-panel">
|
<div class="content" id="dev-panel">
|
||||||
<input type="button" class="button" id="test-toast" value="Test toast">
|
<div>
|
||||||
<input type="button" class="button" id="test-modal-dialog" value="Test modal">
|
<input type="button" class="button" id="test-toast" value="Test toast">
|
||||||
|
<input type="button" class="button" id="test-toast-success" value="Test toast success">
|
||||||
|
<input type="button" class="button" id="test-toast-info" value="Test toast info">
|
||||||
|
<input type="button" class="button" id="test-toast-warning" value="Test toast warning">
|
||||||
|
<input type="button" class="button" id="test-toast-error" value="Test toast error">
|
||||||
|
|
||||||
|
<input type="button" class="button" id="test-toast-success-content" value="Test toast content">
|
||||||
|
<input type="button" class="button" id="test-toast-success-content-initializer" value="Test toast content initializer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type="button" class="button" id="test-modal-dialog" value="Test modal">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="hidden-templates">
|
<div id="hidden-templates">
|
||||||
<div class="modal-test-message">
|
<div class="modal-test-message">
|
||||||
This is a message.
|
This is a message.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="toast-test-content">
|
||||||
|
<span>Item 1</span>
|
||||||
|
<span>Item 2</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
6
backend/templates/modal_dialog.html
Normal file
6
backend/templates/modal_dialog.html
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{# Needed by the frontend modal_dialog module. #}
|
||||||
|
<dialog id="modal-dialog">
|
||||||
|
<div class="content"></div>
|
||||||
|
<input type="button" class="ok" value="OK">
|
||||||
|
<input type="button" class="cancel" value="Cancel">
|
||||||
|
</dialog>
|
||||||
8
backend/templates/toast.html
Normal file
8
backend/templates/toast.html
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{# Needed by the frontend toast module. #}
|
||||||
|
<div id="toasts">
|
||||||
|
<div class="toast">
|
||||||
|
<img class="icon" width="24" height="24">
|
||||||
|
<div class="content"></div>
|
||||||
|
<span class="close button">✖</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -44,6 +44,7 @@ web-sys = { version = "0.3", features = [
|
||||||
"HtmlDivElement",
|
"HtmlDivElement",
|
||||||
"HtmlLabelElement",
|
"HtmlLabelElement",
|
||||||
"HtmlInputElement",
|
"HtmlInputElement",
|
||||||
|
"HtmlImageElement",
|
||||||
"HtmlTextAreaElement",
|
"HtmlTextAreaElement",
|
||||||
"HtmlSelectElement",
|
"HtmlSelectElement",
|
||||||
"HtmlDialogElement",
|
"HtmlDialogElement",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use gloo::{console::log, events::EventListener};
|
use gloo::{console::log, events::EventListener, utils::document};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use web_sys::{Element, HtmlInputElement};
|
use web_sys::{Element, HtmlElement, HtmlInputElement};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calendar, modal_dialog,
|
calendar, modal_dialog,
|
||||||
|
|
@ -13,10 +13,56 @@ use crate::{
|
||||||
|
|
||||||
pub fn setup_page() {
|
pub fn setup_page() {
|
||||||
EventListener::new(&by_id("test-toast"), "click", move |_event| {
|
EventListener::new(&by_id("test-toast"), "click", move |_event| {
|
||||||
toast::show_message(Level::Info, "This is a message");
|
toast::show_message("This is a message");
|
||||||
})
|
})
|
||||||
.forget();
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(&by_id("test-toast-success"), "click", move |_event| {
|
||||||
|
toast::show_message_level(Level::Success, "This is a success message");
|
||||||
|
})
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(&by_id("test-toast-error"), "click", move |_event| {
|
||||||
|
toast::show_message_level(Level::Error, "This is a error message");
|
||||||
|
})
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(&by_id("test-toast-info"), "click", move |_event| {
|
||||||
|
toast::show_message_level(Level::Info, "This is an info message");
|
||||||
|
})
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(&by_id("test-toast-warning"), "click", move |_event| {
|
||||||
|
toast::show_message_level(Level::Warning, "This is a warning message");
|
||||||
|
})
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(
|
||||||
|
&by_id("test-toast-success-content"),
|
||||||
|
"click",
|
||||||
|
move |_event| {
|
||||||
|
toast::show_element_level(Level::Success, "#hidden-templates .toast-test-content");
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(
|
||||||
|
&by_id("test-toast-success-content-initializer"),
|
||||||
|
"click",
|
||||||
|
move |_event| {
|
||||||
|
toast::show_element_level_and_initialize(
|
||||||
|
Level::Success,
|
||||||
|
"#hidden-templates .toast-test-content",
|
||||||
|
|element| {
|
||||||
|
let new_span = document().create_element("span").unwrap();
|
||||||
|
new_span.set_inner_html("Item 3");
|
||||||
|
element.append_child(&new_span).unwrap();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.forget();
|
||||||
|
|
||||||
EventListener::new(&by_id("test-modal-dialog"), "click", move |_event| {
|
EventListener::new(&by_id("test-modal-dialog"), "click", move |_event| {
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
modal_dialog::show("#hidden-templates").await;
|
modal_dialog::show("#hidden-templates").await;
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ pub fn setup_page(recipe_id: i64) {
|
||||||
let servings = if n.is_nan() {
|
let servings = if n.is_nan() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// TODO: Find a better way to validate integer numbers.
|
|
||||||
let n = n as u32;
|
let n = n as u32;
|
||||||
servings.set_value_as_number(n as f64);
|
servings.set_value_as_number(n as f64);
|
||||||
Some(n)
|
Some(n)
|
||||||
|
|
@ -108,7 +107,6 @@ pub fn setup_page(recipe_id: i64) {
|
||||||
let time = if n.is_nan() {
|
let time = if n.is_nan() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// TODO: Find a better way to validate integer numbers.
|
|
||||||
let n = n as u32;
|
let n = n as u32;
|
||||||
estimated_time.set_value_as_number(n as f64);
|
estimated_time.set_value_as_number(n as f64);
|
||||||
Some(n)
|
Some(n)
|
||||||
|
|
@ -708,7 +706,7 @@ async fn reload_recipes_list(current_recipe_id: i64) {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
toast::show_message(Level::Info, &format!("Internal server error: {}", error));
|
toast::show_message_level(Level::Error, &format!("Internal server error: {}", error));
|
||||||
}
|
}
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let list = document().get_element_by_id("recipes-list").unwrap();
|
let list = document().get_element_by_id("recipes-list").unwrap();
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ pub fn setup_page(recipe_id: i64, is_user_logged: bool, first_day_of_the_week: W
|
||||||
.shedule_recipe(recipe_id, date, servings, add_ingredients_to_shopping_list)
|
.shedule_recipe(recipe_id, date, servings, add_ingredients_to_shopping_list)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
toast::show_element_and_initialize(
|
toast::show_element_level_and_initialize(
|
||||||
match result {
|
match result {
|
||||||
ScheduleRecipeResult::Ok => Level::Success,
|
ScheduleRecipeResult::Ok => Level::Success,
|
||||||
ScheduleRecipeResult::RecipeAlreadyScheduledAtThisDate => {
|
ScheduleRecipeResult::RecipeAlreadyScheduledAtThisDate => {
|
||||||
|
|
|
||||||
|
|
@ -64,13 +64,13 @@ where
|
||||||
{
|
{
|
||||||
match request.send().await {
|
match request.send().await {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
toast::show_message(Level::Info, &format!("Internal server error: {}", error));
|
toast::show_message_level(Level::Error, &format!("Internal server error: {}", error));
|
||||||
Err(Error::Gloo(error))
|
Err(Error::Gloo(error))
|
||||||
}
|
}
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
if !response.ok() {
|
if !response.ok() {
|
||||||
toast::show_message(
|
toast::show_message_level(
|
||||||
Level::Info,
|
Level::Error,
|
||||||
&format!("HTTP error: {}", response.status_text()),
|
&format!("HTTP error: {}", response.status_text()),
|
||||||
);
|
);
|
||||||
Err(Error::Http(response.status_text()))
|
Err(Error::Http(response.status_text()))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use gloo::{timers::callback::Timeout, utils::document};
|
use gloo::{events::EventListener, timers::callback::Timeout};
|
||||||
use web_sys::{Element, HtmlElement};
|
use web_sys::{Element, HtmlElement, HtmlImageElement};
|
||||||
|
|
||||||
use crate::utils::{SelectorExt, by_id, selector_and_clone};
|
use crate::utils::{SelectorExt, by_id, selector_and_clone};
|
||||||
|
|
||||||
|
|
@ -8,55 +8,82 @@ pub enum Level {
|
||||||
Error,
|
Error,
|
||||||
Info,
|
Info,
|
||||||
Warning,
|
Warning,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
TODO:
|
|
||||||
- Stack multiple toast messages (see #toasts) by cloning #toast
|
|
||||||
- User can close message by clicking a button
|
|
||||||
- Implement level display with icons
|
|
||||||
*/
|
|
||||||
|
|
||||||
const TIME_ANIMATION: u32 = 500; // [ms].
|
const TIME_ANIMATION: u32 = 500; // [ms].
|
||||||
const TIME_DISPLAYED: u32 = 5_000; // [ms].
|
const TIME_DISPLAYED: u32 = 5_000; // [ms].
|
||||||
|
|
||||||
pub fn show_message(level: Level, message: &str) {
|
pub fn show_message(message: &str) {
|
||||||
let toast_element: HtmlElement = by_id("toast");
|
show_message_level(Level::Unknown, message);
|
||||||
toast_element.set_inner_html(message);
|
|
||||||
toast_element.style().set_css_text(&format!(
|
|
||||||
"visibility: visible;
|
|
||||||
animation:
|
|
||||||
fadein {}ms,
|
|
||||||
fadeout {}ms {}ms;",
|
|
||||||
TIME_ANIMATION,
|
|
||||||
TIME_ANIMATION,
|
|
||||||
TIME_DISPLAYED - TIME_ANIMATION
|
|
||||||
));
|
|
||||||
|
|
||||||
Timeout::new(TIME_DISPLAYED, move || {
|
|
||||||
toast_element.style().set_css_text("");
|
|
||||||
})
|
|
||||||
.forget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_element(level: Level, selector: &str) {
|
pub fn show_message_level(level: Level, message: &str) {
|
||||||
show_element_and_initialize(level, selector, |_| {})
|
show_message_content(level, Content::Message(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_element_and_initialize<T>(level: Level, selector: &str, initializer: T)
|
pub fn show_element_level(level: Level, selector: &str) {
|
||||||
|
show_element_level_and_initialize(level, selector, |_| {})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_element_level_and_initialize<T>(level: Level, selector: &str, initializer: T)
|
||||||
where
|
where
|
||||||
T: Fn(Element),
|
T: Fn(Element),
|
||||||
{
|
{
|
||||||
let toast_element = document().get_element_by_id("toast").unwrap();
|
let content_element: Element = selector_and_clone(selector);
|
||||||
|
initializer(content_element.clone());
|
||||||
|
|
||||||
let element: Element = selector_and_clone(selector);
|
show_message_content(level, Content::Element(content_element))
|
||||||
toast_element.set_inner_html("");
|
}
|
||||||
toast_element.append_child(&element).unwrap();
|
|
||||||
initializer(element.clone());
|
enum Content<'a> {
|
||||||
toast_element.set_class_name("show");
|
Message(&'a str),
|
||||||
|
Element(Element),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_message_content(level: Level, content: Content) {
|
||||||
|
let toast_element: HtmlElement = selector_and_clone("#toasts .toast");
|
||||||
|
|
||||||
|
let toast_icon: HtmlImageElement = toast_element.selector(".icon");
|
||||||
|
let toast_content: HtmlElement = toast_element.selector(".content");
|
||||||
|
|
||||||
|
match level {
|
||||||
|
Level::Success => toast_icon.set_src("/static/success.svg"),
|
||||||
|
Level::Error => toast_icon.set_src("/static/error.svg"),
|
||||||
|
Level::Info => toast_icon.set_src("/static/info.svg"),
|
||||||
|
Level::Warning => toast_icon.set_src("/static/warning.svg"),
|
||||||
|
Level::Unknown => toast_icon.remove(),
|
||||||
|
}
|
||||||
|
|
||||||
|
match content {
|
||||||
|
Content::Message(message) => toast_content.set_inner_html(message),
|
||||||
|
Content::Element(element) => {
|
||||||
|
let _ = toast_content.append_child(&element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toast_element.style().set_css_text(&format!(
|
||||||
|
"display: block;
|
||||||
|
animation:
|
||||||
|
fadein {}ms,
|
||||||
|
fadeout {}ms {}ms;",
|
||||||
|
TIME_ANIMATION, TIME_ANIMATION, TIME_DISPLAYED
|
||||||
|
));
|
||||||
|
|
||||||
|
// FIXME: Here the two events will leak memory. How to fix that?
|
||||||
|
// Save them in a global vec variable and remove them manually?
|
||||||
|
let close_button: HtmlElement = toast_element.selector(".close");
|
||||||
|
let toast_element_cloned = toast_element.clone();
|
||||||
|
EventListener::once(&close_button, "click", move |_event| {
|
||||||
|
toast_element_cloned.remove();
|
||||||
|
})
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
let toasts: HtmlElement = by_id("toasts");
|
||||||
|
toasts.append_child(&toast_element).unwrap();
|
||||||
|
|
||||||
Timeout::new(TIME_DISPLAYED, move || {
|
Timeout::new(TIME_DISPLAYED, move || {
|
||||||
toast_element.set_class_name("");
|
toast_element.remove();
|
||||||
})
|
})
|
||||||
.forget();
|
.forget();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue