{% macro message(type, content, raw=false, discreet=false) %} <div class="message{{ ' message-discreet' if discreet }}" data-type="{{ type }}"> <i class="icon"></i> <span class="content"> {{ content|safe if raw else content }} </span> </div> {% endmacro %} {% macro messages(flash) %} {% set flashed = flash() %} {% set display = 0 %} {% for type, bag in flashed %} {% if bag|length %} {% set display = 1 %} {% endif %} {% endfor %} {% if display %} <div class="messages"> {% for type, bag in flashed %} {% for content in bag %} {{ message(type, content) }} {% endfor %} {% endfor %} </div> {% endif %} {% endmacro %} {% macro csrf(getCsrfToken) %} <input type="hidden" name="csrf" value="{{ getCsrfToken() }}"> {% endmacro %} {% macro field(_locals, type, name, value, placeholder, hint, validation_attributes='', extraData='', icon=null) %} {% set validation = _locals.validation() %} {% set validation = validation[name] if validation[name] or null %} {% set previousFormData = _locals.previousFormData() %} {% set value = previousFormData[name] or value or validation.value or '' %} {% set prefix = _locals.getFormPrefix() | default('') %} {% if type == 'hidden' %} {% if validation %} {{ message('error', validation.message) }} {% endif %} <input type="hidden" name="{{ name }}" value="{{ value }}"> {% else %} <div class="form-field{{ ' inline' if type == 'checkbox' }}"> <div class="control"> {% if icon != null %} {% if icon.startsWith('fa') %} <i class="{{ icon }} feather icon"></i> {% else %} <i data-feather="{{ icon }}" class="icon"></i> {% endif %} {% endif %} {% if type == 'duration' %} <div class="input-group"> {% for f in extraData %} <div class="time-input"> {% if previousFormData[name] %} {% set v = value[f] %} {% else %} {% set v = (value % 60) if f == 's' else (((value - value % 60) / 60 % 60) if f == 'm' else ((value - value % 3600) / 3600 if f == 'h')) %} {% endif %} <input type="number" name="{{ name }}[{{ f }}]" id="field-{{ prefix }}{{ name }}-{{ f }}" value="{{ v }}" min="0" {{ 'max=60' if (f == 's' or f == 'm') }} {{ validation_attributes }}> <label for="field-{{ prefix }}{{ name }}-{{ f }}">{{ f }}</label> </div> {% endfor %} </div> {% elseif type == 'select' %} <select name="{{ name }}" id="field-{{ prefix }}{{ name }}" {{ validation_attributes|safe }}> {% for option in extraData %} <option value="{% if option.display === undefined or option.value !== undefined %}{{ option.value | default(option) }}{% endif %}" {{ 'selected' if value == (option.value | default(option)) }}>{{ option.display | default(option) }}</option> {% endfor %} </select> <i data-feather="chevron-down"></i> {% elseif type == 'textarea' %} <textarea name="{{ name }}" id="field-{{ prefix }}{{ name }}" {{ validation_attributes|safe }} value="{{ value }}">{{ value }}</textarea> {% else %} <input type="{{ type }}" name="{{ name }}" id="field-{{ prefix }}{{ name }}" {% if type != 'checkbox' %} value="{{ value }}" {% endif %} {{ 'checked' if (type == 'checkbox' and value == 'on') }} {{ validation_attributes|safe }}> {% endif %} <label for="field-{{ prefix }}{{ name }}{{ '-' + extraData[0] if type == 'duration' }}">{{ placeholder }}</label> </div> {{ fieldError(_locals, name) }} {% if hint %} <div class="hint"><i data-feather="info"></i> {{ hint }}</div> {% endif %} </div> {% endif %} {% endmacro %} {% macro fieldError(_locals, name) %} {% set validation = _locals.validation() %} {% set validation = validation[name] if validation[name] or null %} {% if validation %} <div class="error"><i data-feather="x-circle"></i> {{ validation.message }}</div> {% endif %} {% endmacro %} {% macro websocket(websocketUrl, listener, reconnectOnClose = 1, checkFunction = 0) %} <script> document.addEventListener('DOMContentLoaded', () => { {% if checkFunction %} if (!{{ checkFunction }}()) return; {% endif %} const run = () => { const websocket = new WebSocket('{{ websocketUrl }}'); websocket.onopen = (e) => { console.debug('Websocket connected'); }; websocket.onmessage = (e) => { {{ listener }}(websocket, e); }; websocket.onerror = (e) => { console.error('Websocket error', e); }; websocket.onclose = (e) => { console.debug('Websocket closed', e.code, e.reason); {% if reconnectOnClose %} setTimeout(run, 1000); {% endif %} }; }; run(); }); </script> {% endmacro %} {% macro paginate(pagination, routeName, contextSize) %} {% if pagination.hasPrevious() or pagination.hasNext() %} <nav class="pagination"> <ul> {% if pagination.hasPrevious() %} <li><a href="{{ route(routeName, {page: pagination.page - 1}) }}"><i data-feather="chevron-left"></i> Previous</a></li> {% for i in pagination.previousPages(contextSize) %} {% if i == -1 %} <li class="ellipsis">...</li> {% else %} <li><a href="{{ route(routeName, {page: i}) }}">{{ i }}</a></li> {% endif %} {% endfor %} {% endif %} <li class="active"><span>{{ pagination.page }}</span></li> {% if pagination.hasNext() %} {% for i in pagination.nextPages(contextSize) %} {% if i == -1 %} <li class="ellipsis">...</li> {% else %} <li><a href="{{ route(routeName, {page: i}) }}">{{ i }}</a></li> {% endif %} {% endfor %} <li><a href="{{ route(routeName, {page: pagination.page + 1}) }}">Next <i data-feather="chevron-right"></i></a></li> {% endif %} </ul> </nav> {% endif %} {% endmacro %} {% macro breadcrumb(currentPageTitle, pages=[]) %} <nav aria-label="breadcrumb"> <ol class="breadcrumb"> {% for page in pages %} <li><a href="{{ page.link }}">{{ page.title }}</a></li> {% endfor %} <li class="active" aria-current="page">{{ currentPageTitle }}</li> </ol> </nav> {% endmacro %}