Replace jQuery with VanillaJS
This commit is contained in:
parent
7bbdb95885
commit
282f4b45ac
|
@ -3,6 +3,7 @@
|
|||
"extends": [ "plugin:editorconfig/all" ],
|
||||
"plugins": [ "editorconfig" ],
|
||||
"rules": {
|
||||
"prefer-arrow-callback": "error",
|
||||
"no-var": "error",
|
||||
"quotes": [
|
||||
"error",
|
||||
|
|
|
@ -38,13 +38,13 @@ function form_spinner($name, $label, $value)
|
|||
</button>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(\'#spinner-' . $name . '-down\').click(function () {
|
||||
const spinner = $(\'#spinner-' . $name . '\');
|
||||
spinner.val(parseInt(spinner.val()) - 1);
|
||||
document.getElementById("spinner-' . $name . '-down")?.addEventListener("click", () => {
|
||||
const spinner = document.getElementById("spinner-' . $name . '");
|
||||
spinner.value = parseInt(spinner.value, 10) - 1;
|
||||
});
|
||||
$(\'#spinner-' . $name . '-up\').click(function () {
|
||||
const spinner = $(\'#spinner-' . $name . '\');
|
||||
spinner.val(parseInt(spinner.val()) + 1);
|
||||
document.getElementById("spinner-' . $name . '-up")?.addEventListener("click", () => {
|
||||
const spinner = document.getElementById("spinner-' . $name . '");
|
||||
spinner.value = parseInt(spinner.value, 10) + 1;
|
||||
});
|
||||
</script>
|
||||
', 'spinner-' . $name);
|
||||
|
|
|
@ -389,15 +389,18 @@ function render_table($columns, $rows, $data = true)
|
|||
* @param string $href
|
||||
* @param string $label
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
function button($href, $label, $class = '')
|
||||
function button($href, $label, $class = '', $id = '')
|
||||
{
|
||||
if (!Str::contains(str_replace(['btn-sm', 'btn-xl'], '', $class), 'btn-')) {
|
||||
$class = 'btn-secondary' . ($class ? ' ' . $class : '');
|
||||
}
|
||||
|
||||
return '<a href="' . $href . '" class="btn ' . $class . '">' . $label . '</a>';
|
||||
$idAttribute = $id ? 'id="' . $id . '"' : '';
|
||||
|
||||
return '<a ' . $idAttribute . ' href="' . $href . '" class="btn ' . $class . '">' . $label . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,24 +41,16 @@ function public_dashboard_view($stats, $free_shifts)
|
|||
stats(__('Angels needed for nightshifts'), $stats['needed-night']),
|
||||
stats(__('Angels currently working'), $stats['angels-working'], 'default'),
|
||||
stats(__('Hours to be worked'), $stats['hours-to-work'], 'default'),
|
||||
'<script>
|
||||
$(function () {
|
||||
setInterval(function () {
|
||||
$(\'#content .wrapper\').load(window.location.href + \' #public-dashboard\');
|
||||
}, 60000);
|
||||
})
|
||||
</script>'
|
||||
], 'statistics'),
|
||||
$needed_angels
|
||||
], 'public-dashboard'),
|
||||
]),
|
||||
div('first col-md-12 text-center', [buttons([
|
||||
button_js(
|
||||
'
|
||||
$(\'#navbar-collapse-1,.navbar-nav,.navbar-toggler,#footer,#fullscreen-button\').remove();
|
||||
$(\'.navbar-brand\').append(\' ' . __('Public Dashboard') . '\');
|
||||
',
|
||||
icon('fullscreen') . __('Fullscreen')
|
||||
button(
|
||||
'#',
|
||||
icon('fullscreen') . __('Fullscreen'),
|
||||
'',
|
||||
'dashboard-fullscreen'
|
||||
),
|
||||
auth()->user() ? button(
|
||||
public_dashboard_link($isFiltered ? [] : ['filtered' => 1] + $filter),
|
||||
|
|
|
@ -53,20 +53,20 @@ function UserDriverLicense_edit_view($user_source, $user_driver_license)
|
|||
]),
|
||||
'
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
const checkbox = $(\'#wants_to_drive\');
|
||||
if (checkbox.is(\':checked\'))
|
||||
$(\'#driving_license\').show();
|
||||
else
|
||||
$(\'#driving_license\').hide();
|
||||
const drivingLicenseElement = document.getElementById("driving_license");
|
||||
|
||||
checkbox.click(function () {
|
||||
if ($(\'#wants_to_drive\').is(\':checked\'))
|
||||
$(\'#driving_license\').show();
|
||||
else
|
||||
$(\'#driving_license\').hide();
|
||||
if (drivingLicenseElement) {
|
||||
const checkbox = document.getElementById("wants_to_drive");
|
||||
drivingLicenseElement.style.display = checkbox?.checked
|
||||
? ""
|
||||
: "none";
|
||||
|
||||
checkbox.addEventListener("click", () => {
|
||||
drivingLicenseElement.style.display = document.getElementById("wants_to_drive")?.checked
|
||||
? ""
|
||||
: "none";
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
'
|
||||
], true);
|
||||
|
|
|
@ -69,14 +69,13 @@ function formatFromNow(timestamp) {
|
|||
/**
|
||||
* Initialises all countdown fields on the page.
|
||||
*/
|
||||
ready(function () {
|
||||
$.each($('[data-countdown-ts]'), function (i, e) {
|
||||
const span = $(e);
|
||||
const timestamp = span.data('countdown-ts');
|
||||
const text = span.html();
|
||||
span.html(text.replace('%c', formatFromNow(timestamp)));
|
||||
setInterval(function () {
|
||||
span.html(text.replace('%c', formatFromNow(timestamp)));
|
||||
ready(() => {
|
||||
document.querySelectorAll('[data-countdown-ts]').forEach((element) => {
|
||||
const timestamp = element.dataset.countdownTs;
|
||||
const template = element.innerHTML;
|
||||
element.innerHTML = template.replace('%c', formatFromNow(timestamp));
|
||||
setInterval(() => {
|
||||
element.innerHTML = template.replace('%c', formatFromNow(timestamp));
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { ready } from './ready';
|
||||
|
||||
ready(() => {
|
||||
if (!document.getElementById('public-dashboard')) return;
|
||||
|
||||
// reload page every minute
|
||||
setInterval(async () => {
|
||||
const response = await fetch(window.location.href);
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('error loading dashboard');
|
||||
return;
|
||||
}
|
||||
|
||||
const responseData = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const dummyDocument = parser.parseFromString(responseData, 'text/html');
|
||||
const dashboardContent = dummyDocument.getElementById('public-dashboard');
|
||||
document.querySelector('#content .wrapper').innerHTML = dashboardContent.outerHTML;
|
||||
}, 60000);
|
||||
|
||||
// Handle fullscreen button
|
||||
// - Remove some elements from UI
|
||||
// - Add "Public Dashboard" to title
|
||||
document.getElementById('dashboard-fullscreen')
|
||||
?.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
document.querySelectorAll(
|
||||
'#navbar-collapse-1,.navbar-nav,.navbar-toggler,#footer,#fullscreen-button'
|
||||
).forEach((element) => {
|
||||
element.parentNode.removeChild(element);
|
||||
});
|
||||
|
||||
document.querySelector('.navbar-brand')
|
||||
?.appendChild(document.createTextNode('Dashboard'));
|
||||
});
|
||||
});
|
|
@ -1,7 +1,15 @@
|
|||
require('select2');
|
||||
import 'select2';
|
||||
import { formatDay, formatTime } from './date';
|
||||
import { ready } from './ready';
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
const triggerChange = (element) => {
|
||||
const changeEvent = new Event('change');
|
||||
element.dispatchEvent(changeEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all checkboxes to the wanted state
|
||||
*
|
||||
|
@ -9,8 +17,8 @@ import { ready } from './ready';
|
|||
* @param {boolean} checked True if the checkboxes should be checked
|
||||
*/
|
||||
global.checkAll = (id, checked) => {
|
||||
$('#' + id + ' input[type="checkbox"]').each(function () {
|
||||
this.checked = checked;
|
||||
document.querySelectorAll('#' + id + ' input[type="checkbox"]').forEach((element) => {
|
||||
element.checked = checked;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -18,11 +26,12 @@ global.checkAll = (id, checked) => {
|
|||
* Sets the checkboxes according to the given type
|
||||
*
|
||||
* @param {string} id The elements ID
|
||||
* @param {list} shiftsList A list of numbers
|
||||
* @param {int[]} shiftsList A list of numbers
|
||||
*/
|
||||
global.checkOwnTypes = (id, shiftsList) => {
|
||||
$('#' + id + ' input[type="checkbox"]').each(function () {
|
||||
this.checked = $.inArray(parseInt(this.value), shiftsList) != -1;
|
||||
document.querySelectorAll('#' + id + ' input[type="checkbox"]').forEach((element) => {
|
||||
const value = parseInt(element.value, 10);
|
||||
element.checked = shiftsList.includes(value);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -37,21 +46,23 @@ global.checkOwnTypes = (id, shiftsList) => {
|
|||
* @param {Date} to
|
||||
*/
|
||||
global.setInput = (from, to) => {
|
||||
const fromDay = $('#start_day');
|
||||
const fromTime = $('#start_time');
|
||||
const toDay = $('#end_day');
|
||||
const toTime = $('#end_time');
|
||||
const fromDay = document.getElementById('start_day');
|
||||
const fromTime = document.getElementById('start_time');
|
||||
const toDay = document.getElementById('end_day');
|
||||
const toTime = document.getElementById('end_time');
|
||||
|
||||
if (!fromDay || !fromTime || !toDay || !toTime) {
|
||||
console.warn('cannot set input date because of missing field');
|
||||
return;
|
||||
}
|
||||
|
||||
fromDay.val(formatDay(from)).trigger('change');
|
||||
fromTime.val(formatTime(from));
|
||||
fromDay.value = formatDay(from);
|
||||
triggerChange(fromDay);
|
||||
fromTime.value = formatTime(from);
|
||||
|
||||
toDay.val(formatDay(to)).trigger('change');
|
||||
toTime.val(formatTime(to));
|
||||
toDay.value = formatDay(to);
|
||||
triggerChange(toDay);
|
||||
toTime.value = formatTime(to);
|
||||
};
|
||||
|
||||
global.setDay = (days) => {
|
||||
|
@ -86,33 +97,42 @@ global.setHours = (hours) => {
|
|||
setInput(from, to);
|
||||
};
|
||||
|
||||
ready(function () {
|
||||
ready(() => {
|
||||
/**
|
||||
* Disable every submit button after clicking (to prevent double-clicking)
|
||||
*/
|
||||
$('form').submit(function (ev) {
|
||||
$('input[type="submit"]').prop('readonly', true).addClass('disabled');
|
||||
return true;
|
||||
* Disable every submit button after clicking (to prevent double-clicking)
|
||||
*/
|
||||
document.querySelectorAll('form').forEach((formElement) => {
|
||||
formElement.addEventListener('submit', () => {
|
||||
document.querySelectorAll('input[type="submit"],button[type="submit"]').forEach((element) => {
|
||||
element.readOnly = true;
|
||||
element.classList.add('disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
* Button to set current time in time input fields.
|
||||
*/
|
||||
ready(function () {
|
||||
$('.input-group.time').each(function () {
|
||||
const elem = $(this);
|
||||
elem.find('button').on('click', function () {
|
||||
ready(() => {
|
||||
document.querySelectorAll('.input-group.time').forEach((element) => {
|
||||
const button = element.querySelector('button');
|
||||
if (!button) return;
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
const now = new Date();
|
||||
const input = elem.children('input').first();
|
||||
input.val(formatTime(now));
|
||||
const daySelector = $('#' + input.attr('id').replace('time', 'day'));
|
||||
const days = daySelector.children('option');
|
||||
const input = element.querySelector('input');
|
||||
if (!input) return;
|
||||
|
||||
input.value = formatTime(now);
|
||||
const daySelector = document.getElementById(input.id.replace('time', 'day'));
|
||||
if (!daySelector) return;
|
||||
|
||||
const dayElements = daySelector.querySelectorAll('option');
|
||||
const yyyyMMDD = formatDay(now);
|
||||
days.each(function (i) {
|
||||
if ($(days[i]).val() === yyyyMMDD) {
|
||||
daySelector.val($(days[i]).val());
|
||||
dayElements.forEach((dayElement) => {
|
||||
if (dayElement.value === yyyyMMDD) {
|
||||
daySelector.value = dayElement.value;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
@ -120,7 +140,7 @@ ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
ready(function () {
|
||||
ready(() => {
|
||||
$('select').select2({
|
||||
theme: 'bootstrap-5',
|
||||
width: '100%',
|
||||
|
@ -130,15 +150,17 @@ ready(function () {
|
|||
/**
|
||||
* Show oauth buttons on welcome title click
|
||||
*/
|
||||
ready(function () {
|
||||
$('#welcome-title').on('click', function () {
|
||||
$('.btn-group.btn-group .btn.d-none').removeClass('d-none');
|
||||
});
|
||||
$('#settings-title').on('click', function () {
|
||||
$('.user-settings .nav-item').removeClass('d-none');
|
||||
});
|
||||
$('#oauth-settings-title').on('click', function () {
|
||||
$('table tr.d-none').removeClass('d-none');
|
||||
ready(() => {
|
||||
[
|
||||
['welcome-title', '.btn-group.btn-group .btn.d-none'],
|
||||
['settings-title', '.user-settings .nav-item'],
|
||||
['oauth-settings-title', 'table tr.d-none'],
|
||||
].forEach(([id, selector]) => {
|
||||
document.getElementById(id)?.addEventListener('click', () => {
|
||||
document.querySelectorAll(selector).forEach((element) => {
|
||||
element.classList.remove('d-none');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -149,7 +171,7 @@ ready(function () {
|
|||
*/
|
||||
ready(() => {
|
||||
const filter = document.getElementById('collapseShiftsFilterSelect');
|
||||
if (!filter || localStorage.getItem('collapseShiftsFilterSelect') !== 'hidden') {
|
||||
if (!filter || localStorage.getItem('collapseShiftsFilterSelect') !== 'hidden.bs.collapse') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -165,7 +187,9 @@ ready(() => {
|
|||
localStorage.setItem('collapseShiftsFilterSelect', e.type);
|
||||
};
|
||||
|
||||
$('#collapseShiftsFilterSelect')
|
||||
.on('hidden.bs.collapse', onChange)
|
||||
.on('shown.bs.collapse', onChange);
|
||||
document.getElementById('collapseShiftsFilterSelect')
|
||||
?.addEventListener('hidden.bs.collapse', onChange);
|
||||
|
||||
document.getElementById('collapseShiftsFilterSelect')
|
||||
?.addEventListener('shown.bs.collapse', onChange);
|
||||
});
|
||||
|
|
|
@ -1,34 +1,44 @@
|
|||
import { ready } from './ready';
|
||||
|
||||
/**
|
||||
* @param {NodeList} elements
|
||||
* @param {string} styleProp
|
||||
* @param {*} value
|
||||
*/
|
||||
const applyStyle = (elements, prop, value) => {
|
||||
elements.forEach((element) => {
|
||||
element.style[prop] = value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the fixed headers and time lane for the shift-calendar and datatables
|
||||
*/
|
||||
ready(function () {
|
||||
if ($('.shift-calendar').length) {
|
||||
const timeLanes = $('.shift-calendar .time');
|
||||
const headers = $('.shift-calendar .header');
|
||||
const topReference = $('.container-fluid .row');
|
||||
timeLanes.css({
|
||||
'position': 'relative',
|
||||
'z-index': 999
|
||||
});
|
||||
headers.css({
|
||||
'position': 'relative',
|
||||
'z-index': 900
|
||||
});
|
||||
$(window).scroll(
|
||||
function () {
|
||||
const top = headers.parent().offset().top;
|
||||
const left = 15;
|
||||
timeLanes.css({
|
||||
'left': Math.max(0, $(window).scrollLeft() - left) + 'px'
|
||||
});
|
||||
headers.css({
|
||||
'top': Math.max(0, $(window).scrollTop() - top
|
||||
- 13
|
||||
+ topReference.offset().top)
|
||||
+ 'px'
|
||||
});
|
||||
});
|
||||
}
|
||||
ready(() => {
|
||||
if (!document.querySelector('.shift-calendar')) return;
|
||||
|
||||
const headers = document.querySelectorAll('.shift-calendar .header');
|
||||
const timeLane = document.querySelector('.shift-calendar .time');
|
||||
const topReference = document.querySelector('.container-fluid .row');
|
||||
|
||||
if (!headers.length || !timeLane || !topReference) return;
|
||||
|
||||
timeLane.style.position = 'relative';
|
||||
timeLane.style.zIndex = 999;
|
||||
|
||||
applyStyle(headers, 'position', 'relative');
|
||||
applyStyle(headers, 'zIndex', 900);
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const top = headers.item(0).parentNode.getBoundingClientRect().top;
|
||||
const left = 15;
|
||||
|
||||
timeLane.style.left = Math.max(0, window.scrollX - left) + 'px';
|
||||
|
||||
const headersTop = Math.max(
|
||||
0,
|
||||
window.scrollY - top - 13 + topReference.getBoundingClientRect().top
|
||||
) + 'px';
|
||||
applyStyle(headers, 'top', headersTop);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
require('core-js/stable');
|
||||
import 'core-js/stable';
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
window.bootstrap = require('bootstrap');
|
||||
require('./forms');
|
||||
require('./sticky-headers');
|
||||
require('./countdown');
|
||||
|
||||
|
||||
$.ajaxSetup({
|
||||
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}
|
||||
});
|
||||
import './forms';
|
||||
import './sticky-headers';
|
||||
import './countdown';
|
||||
import './dashboard';
|
||||
|
|
|
@ -249,13 +249,27 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-lg-2">
|
||||
Submit button<br>
|
||||
{{ f.submit() }}
|
||||
Form submit
|
||||
<form id="form">
|
||||
{{ f.submit() }}
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-3 col-lg-2">
|
||||
<div id="checkboxes" class="col-md-3 col-lg-2">
|
||||
Checkbox<br>
|
||||
{{ f.checkbox('form-input-checkbox', 'Checkbox', true) }}
|
||||
{{ f.checkbox('form-input-checkbox-2', 'Checkbox 2') }}
|
||||
{{ f.checkbox('form-input-checkbox', 'Checkbox 1', true, '1') }}
|
||||
{{ f.checkbox('form-input-checkbox-2', 'Checkbox 2', false, '2') }}
|
||||
{{ f.checkbox('form-input-checkbox-3', 'Checkbox 3', false, '3') }}
|
||||
<div class="d-grid gap-2">
|
||||
<div id="select-all-checkboxes" class="btn btn-sm btn-block btn-primary">
|
||||
Select all
|
||||
</div>
|
||||
<div id="select-23-checkboxes" class="btn btn-sm btn-block btn-primary">
|
||||
Select 2, 3
|
||||
</div>
|
||||
<div id="unselect-all-checkboxes" class="btn btn-sm btn-block btn-danger">
|
||||
Unselect all
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-lg-2 checkbox-inline">
|
||||
Radio<br>
|
||||
|
@ -406,4 +420,22 @@
|
|||
{{ bar_chart | raw }}
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('select-all-checkboxes').addEventListener('click', () => {
|
||||
checkAll('checkboxes', true);
|
||||
});
|
||||
|
||||
document.getElementById('unselect-all-checkboxes').addEventListener('click', () => {
|
||||
checkAll('checkboxes', false);
|
||||
});
|
||||
|
||||
document.getElementById('select-23-checkboxes').addEventListener('click', () => {
|
||||
checkOwnTypes('checkboxes', [2, 3]);
|
||||
});
|
||||
|
||||
document.getElementById('form').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue