You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
5.4 KiB
194 lines
5.4 KiB
(function () { |
|
var overlay = null; |
|
var loadingMessage = null; |
|
var dialog = null; |
|
var dialogTitle = null; |
|
var dialogBody = null; |
|
var dialogIcon = null; |
|
|
|
function cacheElements() { |
|
overlay = document.getElementById('ui-loading-overlay'); |
|
loadingMessage = document.getElementById('ui-loading-message'); |
|
dialog = document.getElementById('ui-dialog'); |
|
dialogTitle = document.getElementById('ui-dialog-title'); |
|
dialogBody = document.getElementById('ui-dialog-body'); |
|
dialogIcon = document.getElementById('ui-dialog-icon'); |
|
} |
|
|
|
function showLoading(message) { |
|
if (!overlay) { |
|
return; |
|
} |
|
|
|
if (loadingMessage) { |
|
loadingMessage.textContent = message || 'Processing...'; |
|
} |
|
|
|
overlay.hidden = false; |
|
overlay.setAttribute('aria-hidden', 'false'); |
|
document.body.classList.add('ui-is-loading'); |
|
} |
|
|
|
function hideLoading() { |
|
if (!overlay) { |
|
return; |
|
} |
|
|
|
overlay.hidden = true; |
|
overlay.setAttribute('aria-hidden', 'true'); |
|
document.body.classList.remove('ui-is-loading'); |
|
|
|
document.querySelectorAll('.ui-btn.is-loading').forEach(function (btn) { |
|
btn.classList.remove('is-loading'); |
|
btn.disabled = false; |
|
}); |
|
} |
|
|
|
function showDialog(type, message, title) { |
|
if (!dialog || !dialogBody) { |
|
return; |
|
} |
|
|
|
var isError = type === 'error'; |
|
var isSuccess = type === 'success'; |
|
|
|
if (dialogTitle) { |
|
dialogTitle.textContent = title || (isError ? 'Gagal' : isSuccess ? 'Berhasil' : 'Informasi'); |
|
} |
|
|
|
dialogBody.textContent = message || ''; |
|
|
|
if (dialogIcon) { |
|
dialogIcon.className = 'ui-dialog__icon'; |
|
dialogIcon.classList.add(isError ? 'is-error' : isSuccess ? 'is-success' : 'is-info'); |
|
dialogIcon.textContent = isError ? '!' : isSuccess ? '✓' : 'i'; |
|
} |
|
|
|
dialog.hidden = false; |
|
dialog.setAttribute('aria-hidden', 'false'); |
|
|
|
var closeBtn = dialog.querySelector('[data-ui-dialog-close].ui-dialog__btn'); |
|
if (closeBtn) { |
|
closeBtn.focus(); |
|
} |
|
} |
|
|
|
function hideDialog() { |
|
if (!dialog) { |
|
return; |
|
} |
|
|
|
dialog.hidden = true; |
|
dialog.setAttribute('aria-hidden', 'true'); |
|
} |
|
|
|
function markButtonLoading(button) { |
|
if (!button || button.disabled) { |
|
return; |
|
} |
|
|
|
button.classList.add('ui-btn', 'is-loading'); |
|
button.disabled = true; |
|
} |
|
|
|
function shouldSkipForm(form) { |
|
if (form.dataset.uiNoLoading !== undefined) { |
|
return true; |
|
} |
|
|
|
var method = (form.getAttribute('method') || 'get').toLowerCase(); |
|
return method === 'get'; |
|
} |
|
|
|
function resolveLoadingMessage(form, submitter) { |
|
if (submitter && submitter.dataset.loadingMessage) { |
|
return submitter.dataset.loadingMessage; |
|
} |
|
|
|
if (form.dataset.loadingMessage) { |
|
return form.dataset.loadingMessage; |
|
} |
|
|
|
return 'Memproses permintaan...'; |
|
} |
|
|
|
function bindForms(root) { |
|
var scope = root || document; |
|
|
|
scope.querySelectorAll('form').forEach(function (form) { |
|
if (shouldSkipForm(form) || form.dataset.uiFormBound === '1') { |
|
return; |
|
} |
|
|
|
form.dataset.uiFormBound = '1'; |
|
form.addEventListener('submit', function (event) { |
|
var submitter = event.submitter || form.querySelector('[type="submit"]'); |
|
showLoading(resolveLoadingMessage(form, submitter)); |
|
|
|
if (submitter) { |
|
markButtonLoading(submitter); |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
function bindDialog() { |
|
if (!dialog) { |
|
return; |
|
} |
|
|
|
dialog.querySelectorAll('[data-ui-dialog-close]').forEach(function (el) { |
|
el.addEventListener('click', hideDialog); |
|
}); |
|
|
|
document.addEventListener('keydown', function (event) { |
|
if (event.key === 'Escape' && dialog && !dialog.hidden) { |
|
hideDialog(); |
|
} |
|
}); |
|
} |
|
|
|
function consumeFlashPayload(root) { |
|
var scope = root || document; |
|
var node = scope.querySelector ? scope.querySelector('#ui-flash-payload') : document.getElementById('ui-flash-payload'); |
|
if (!node) { |
|
return; |
|
} |
|
|
|
try { |
|
var payload = JSON.parse(node.textContent || '{}'); |
|
if (payload.message) { |
|
showDialog(payload.type || 'info', payload.message); |
|
} |
|
node.remove(); |
|
} catch (e) { |
|
console.warn('[UI] Invalid flash payload', e); |
|
} |
|
} |
|
|
|
function onFrameLoad(frame) { |
|
hideLoading(); |
|
bindForms(frame); |
|
consumeFlashPayload(frame); |
|
} |
|
|
|
window.UiFeedback = { |
|
showLoading: showLoading, |
|
hideLoading: hideLoading, |
|
showDialog: showDialog, |
|
hideDialog: hideDialog, |
|
onFrameLoad: onFrameLoad, |
|
}; |
|
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
cacheElements(); |
|
bindForms(); |
|
bindDialog(); |
|
hideLoading(); |
|
consumeFlashPayload(); |
|
}); |
|
|
|
window.addEventListener('pageshow', function () { |
|
hideLoading(); |
|
}); |
|
})();
|
|
|