first commit + add fe component vue accone
This commit is contained in:
474
libs/money-format.js
Normal file
474
libs/money-format.js
Normal file
@@ -0,0 +1,474 @@
|
||||
(function() {
|
||||
// Definisi directive
|
||||
var moneyFormat = {
|
||||
bind: function(el, binding, vnode) {
|
||||
var inputElement = el.tagName === 'INPUT' ? el : el.querySelector('input');
|
||||
if (!inputElement) return;
|
||||
|
||||
var vm = vnode.context;
|
||||
var modelName = binding.expression;
|
||||
|
||||
// Tambahkan state untuk input
|
||||
el._moneyFormat = {
|
||||
hasDecimal: false, // Flag apakah ada koma desimal
|
||||
decimalPart: '', // Bagian desimal (setelah koma)
|
||||
oldValue: '', // Nilai input sebelumnya
|
||||
oldCursorPosition: 0 // Posisi kursor sebelumnya
|
||||
};
|
||||
|
||||
// Format nilai awal
|
||||
if (vm[modelName]) {
|
||||
var formattedValue = formatNumber(vm[modelName]);
|
||||
inputElement.value = formattedValue;
|
||||
|
||||
// Set flags
|
||||
el._moneyFormat.hasDecimal = formattedValue.includes(',');
|
||||
if (el._moneyFormat.hasDecimal) {
|
||||
var parts = formattedValue.split(',');
|
||||
el._moneyFormat.decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
}
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
}
|
||||
|
||||
// Format angka untuk display
|
||||
function formatNumber(value) {
|
||||
if (!value && value !== 0) return '';
|
||||
|
||||
// Konversi ke string
|
||||
var strValue = String(value);
|
||||
|
||||
// Ganti titik dengan koma untuk desimal
|
||||
strValue = strValue.replace('.', ',');
|
||||
|
||||
// Split berdasarkan koma
|
||||
var parts = strValue.split(',');
|
||||
var integerPart = parts[0];
|
||||
var decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// Format bagian integer dengan titik ribuan
|
||||
var formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Gabungkan kembali
|
||||
return decimalPart ? formattedInteger + ',' + decimalPart : formattedInteger;
|
||||
}
|
||||
|
||||
// Convert dari display format ke nilai untuk model
|
||||
function parseNumber(displayValue) {
|
||||
if (!displayValue) return '';
|
||||
|
||||
// Split berdasarkan koma
|
||||
var parts = displayValue.split(',');
|
||||
var integerPart = parts[0].replace(/\./g, ''); // Hapus titik ribuan
|
||||
var decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// Gabungkan dengan format JavaScript
|
||||
return decimalPart ? integerPart + '.' + decimalPart : integerPart;
|
||||
}
|
||||
|
||||
// Handler saat pengguna menekan tombol (keydown)
|
||||
function onKeydown(e) {
|
||||
// Khusus untuk koma dan titik (input desimal)
|
||||
if (e.key === ',' || e.key === '.') {
|
||||
e.preventDefault(); // Cegah default behavior
|
||||
|
||||
// Jika sudah ada koma, jangan tambahkan lagi
|
||||
if (el._moneyFormat.hasDecimal) return;
|
||||
|
||||
// Posisi kursor saat ini
|
||||
var cursorPos = inputElement.selectionStart;
|
||||
|
||||
// Tambahkan koma pada posisi kursor
|
||||
var currentValue = inputElement.value;
|
||||
var newValue = currentValue.substring(0, cursorPos) + ',' + currentValue.substring(cursorPos);
|
||||
|
||||
// Update nilai input
|
||||
inputElement.value = newValue;
|
||||
|
||||
// Simpan flag
|
||||
el._moneyFormat.hasDecimal = true;
|
||||
el._moneyFormat.decimalPart = '';
|
||||
|
||||
// Update model
|
||||
var modelValue = parseNumber(newValue);
|
||||
vm.$set(vm, modelName, modelValue);
|
||||
|
||||
// Simpan state baru
|
||||
el._moneyFormat.oldValue = newValue;
|
||||
el._moneyFormat.oldCursorPosition = cursorPos + 1;
|
||||
|
||||
// Posisikan kursor setelah koma
|
||||
setTimeout(function() {
|
||||
inputElement.setSelectionRange(cursorPos + 1, cursorPos + 1);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Simpan posisi kursor saat ini untuk referensi input handler
|
||||
el._moneyFormat.oldCursorPosition = inputElement.selectionStart;
|
||||
el._moneyFormat.oldValue = inputElement.value;
|
||||
}
|
||||
|
||||
// Handler saat nilai input berubah
|
||||
function onInput(e) {
|
||||
// Nilai saat ini
|
||||
var currentValue = inputElement.value;
|
||||
var cursorPos = inputElement.selectionStart;
|
||||
|
||||
// Abaikan jika nilai tidak berubah
|
||||
if (currentValue === el._moneyFormat.oldValue) return;
|
||||
|
||||
// Logika utama: Jika sudah ada koma sebelumnya
|
||||
if (el._moneyFormat.hasDecimal) {
|
||||
// Periksa apakah koma masih ada
|
||||
if (currentValue.includes(',')) {
|
||||
// Koma masih ada, format bagian integer dan pertahankan desimal
|
||||
var parts = currentValue.split(',');
|
||||
var integerPart = parts[0];
|
||||
var newDecimalPart = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// Format bagian integer
|
||||
var cleanInteger = integerPart.replace(/\./g, '');
|
||||
var formattedInteger = cleanInteger.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Gabungkan dengan desimal
|
||||
var formattedValue = formattedInteger + ',' + newDecimalPart;
|
||||
|
||||
// Update model
|
||||
var modelValue = cleanInteger + (newDecimalPart ? '.' + newDecimalPart : '');
|
||||
vm.$set(vm, modelName, modelValue);
|
||||
|
||||
// Update nilai input
|
||||
inputElement.value = formattedValue;
|
||||
|
||||
// Update state
|
||||
el._moneyFormat.hasDecimal = true;
|
||||
el._moneyFormat.decimalPart = newDecimalPart;
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
|
||||
// Perbarui posisi kursor
|
||||
var newCursorPos;
|
||||
if (cursorPos <= parts[0].length) {
|
||||
// Kursor di bagian integer
|
||||
var oldDots = (integerPart.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
var newDots = (formattedInteger.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
newCursorPos = cursorPos + (newDots - oldDots);
|
||||
} else {
|
||||
// Kursor di bagian desimal
|
||||
var offsetFromComma = cursorPos - parts[0].length - 1;
|
||||
newCursorPos = formattedInteger.length + 1 + offsetFromComma;
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
inputElement.setSelectionRange(newCursorPos, newCursorPos);
|
||||
el._moneyFormat.oldCursorPosition = newCursorPos;
|
||||
}, 0);
|
||||
} else {
|
||||
// Koma hilang, tambahkan kembali
|
||||
var oldValue = el._moneyFormat.oldValue;
|
||||
var oldCommaPos = oldValue.indexOf(',');
|
||||
|
||||
// Bagian sebelum dan setelah koma di nilai lama
|
||||
var oldBeforeComma = oldValue.substring(0, oldCommaPos);
|
||||
var oldAfterComma = oldValue.substring(oldCommaPos + 1);
|
||||
|
||||
// Kasus khusus: jika user menghapus semua
|
||||
if (currentValue === '') {
|
||||
el._moneyFormat.hasDecimal = false;
|
||||
el._moneyFormat.decimalPart = '';
|
||||
el._moneyFormat.oldValue = '';
|
||||
vm.$set(vm, modelName, '');
|
||||
return;
|
||||
}
|
||||
|
||||
// Logika: koma dihapus, coba pulihkan
|
||||
|
||||
// Kasus 1: Menghapus karakter tepat sebelum koma
|
||||
if (cursorPos === oldCommaPos) {
|
||||
// Format bagian integer
|
||||
var cleanCurrentValue = currentValue.replace(/\./g, '');
|
||||
var formattedInteger = cleanCurrentValue.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Gabungkan dengan bagian desimal
|
||||
var restoredValue = formattedInteger + ',' + el._moneyFormat.decimalPart;
|
||||
|
||||
// Update input
|
||||
inputElement.value = restoredValue;
|
||||
|
||||
// Update model
|
||||
var modelValue = cleanCurrentValue + '.' + el._moneyFormat.decimalPart;
|
||||
vm.$set(vm, modelName, modelValue);
|
||||
|
||||
// Perbarui state
|
||||
el._moneyFormat.oldValue = restoredValue;
|
||||
|
||||
// Set kursor
|
||||
setTimeout(function() {
|
||||
var newCommaPos = formattedInteger.length;
|
||||
inputElement.setSelectionRange(newCommaPos, newCommaPos);
|
||||
el._moneyFormat.oldCursorPosition = newCommaPos;
|
||||
}, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Kasus 2: Menghapus koma itu sendiri
|
||||
if (cursorPos === oldCommaPos || cursorPos === oldCommaPos + 1) {
|
||||
// Format bagian integer
|
||||
var cleanCurrentValue = currentValue.replace(/\./g, '');
|
||||
var formattedInteger = cleanCurrentValue.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Gabungkan dengan bagian desimal yang disimpan
|
||||
var restoredValue = formattedInteger + ',' + el._moneyFormat.decimalPart;
|
||||
|
||||
// Update input
|
||||
inputElement.value = restoredValue;
|
||||
|
||||
// Update model
|
||||
var modelValue = cleanCurrentValue + '.' + el._moneyFormat.decimalPart;
|
||||
vm.$set(vm, modelName, modelValue);
|
||||
|
||||
// Perbarui state
|
||||
el._moneyFormat.oldValue = restoredValue;
|
||||
|
||||
// Set kursor
|
||||
setTimeout(function() {
|
||||
var newCommaPos = formattedInteger.length;
|
||||
var newCursorPos = newCommaPos + 1; // Setelah koma
|
||||
inputElement.setSelectionRange(newCursorPos, newCursorPos);
|
||||
el._moneyFormat.oldCursorPosition = newCursorPos;
|
||||
}, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Format bagian integer dan tambahkan koma
|
||||
var cleanValue = currentValue.replace(/\./g, '');
|
||||
var formattedInteger = cleanValue.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
var restoredValue = formattedInteger + ',' + el._moneyFormat.decimalPart;
|
||||
|
||||
// Update input
|
||||
inputElement.value = restoredValue;
|
||||
|
||||
// Update model
|
||||
var modelValue = cleanValue + '.' + el._moneyFormat.decimalPart;
|
||||
vm.$set(vm, modelName, modelValue);
|
||||
|
||||
// Perbarui state
|
||||
el._moneyFormat.oldValue = restoredValue;
|
||||
|
||||
// Perhitungan kursor
|
||||
setTimeout(function() {
|
||||
var newCursorPos;
|
||||
|
||||
if (cursorPos < oldCommaPos) {
|
||||
// Kursor di bagian integer
|
||||
var oldDots = (oldValue.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
var newDots = (formattedInteger.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
newCursorPos = cursorPos + (newDots - oldDots);
|
||||
} else {
|
||||
// Kursor di bagian desimal, kembalikan ke setelah koma
|
||||
newCursorPos = formattedInteger.length + 1;
|
||||
}
|
||||
|
||||
inputElement.setSelectionRange(newCursorPos, newCursorPos);
|
||||
el._moneyFormat.oldCursorPosition = newCursorPos;
|
||||
}, 0);
|
||||
}
|
||||
} else {
|
||||
// Belum ada koma, format bagian integer saja
|
||||
|
||||
// Kasus khusus: jika user menambahkan koma
|
||||
if (currentValue.includes(',')) {
|
||||
// Ada koma baru, update flags
|
||||
var parts = currentValue.split(',');
|
||||
var integerPart = parts[0];
|
||||
var decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// Format bagian integer
|
||||
var cleanInteger = integerPart.replace(/\./g, '');
|
||||
var formattedInteger = cleanInteger.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Gabungkan
|
||||
var formattedValue = formattedInteger + ',' + decimalPart;
|
||||
|
||||
// Update input
|
||||
inputElement.value = formattedValue;
|
||||
|
||||
// Update model
|
||||
var modelValue = cleanInteger + (decimalPart ? '.' + decimalPart : '.');
|
||||
vm.$set(vm, modelName, modelValue);
|
||||
|
||||
// Perbarui state
|
||||
el._moneyFormat.hasDecimal = true;
|
||||
el._moneyFormat.decimalPart = decimalPart;
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
|
||||
// Set kursor
|
||||
var commaPos = formattedInteger.length;
|
||||
var newCursorPos;
|
||||
|
||||
if (cursorPos <= parts[0].length) {
|
||||
// Kursor di bagian integer
|
||||
var oldDots = (integerPart.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
var newDots = (formattedInteger.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
newCursorPos = cursorPos + (newDots - oldDots);
|
||||
} else {
|
||||
// Kursor di bagian desimal
|
||||
var offsetFromComma = cursorPos - parts[0].length - 1;
|
||||
newCursorPos = commaPos + 1 + offsetFromComma;
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
inputElement.setSelectionRange(newCursorPos, newCursorPos);
|
||||
el._moneyFormat.oldCursorPosition = newCursorPos;
|
||||
}, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Format bagian integer (tanpa desimal)
|
||||
var cleanValue = currentValue.replace(/\./g, '');
|
||||
var formattedValue = cleanValue.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Update hanya jika berbeda
|
||||
if (formattedValue !== currentValue) {
|
||||
// Hitung offset kursor
|
||||
var oldDots = (currentValue.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
var newDots = (formattedValue.substring(0, cursorPos).match(/\./g) || []).length;
|
||||
var offset = newDots - oldDots;
|
||||
|
||||
// Update input
|
||||
inputElement.value = formattedValue;
|
||||
|
||||
// Update model
|
||||
vm.$set(vm, modelName, cleanValue);
|
||||
|
||||
// Perbarui state
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
|
||||
// Update kursor
|
||||
var newCursorPos = cursorPos + offset;
|
||||
setTimeout(function() {
|
||||
inputElement.setSelectionRange(newCursorPos, newCursorPos);
|
||||
el._moneyFormat.oldCursorPosition = newCursorPos;
|
||||
}, 0);
|
||||
} else {
|
||||
// Tidak perlu format ulang
|
||||
vm.$set(vm, modelName, cleanValue);
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
el._moneyFormat.oldCursorPosition = cursorPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format ulang saat keluar dari input
|
||||
function onBlur() {
|
||||
var modelValue = vm[modelName];
|
||||
if (modelValue !== undefined && modelValue !== null) {
|
||||
var formattedValue = formatNumber(modelValue);
|
||||
inputElement.value = formattedValue;
|
||||
|
||||
// Update flags
|
||||
el._moneyFormat.hasDecimal = formattedValue.includes(',');
|
||||
if (el._moneyFormat.hasDecimal) {
|
||||
var parts = formattedValue.split(',');
|
||||
el._moneyFormat.decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
} else {
|
||||
el._moneyFormat.decimalPart = '';
|
||||
}
|
||||
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Register event handlers
|
||||
inputElement.addEventListener('keydown', onKeydown);
|
||||
inputElement.addEventListener('input', onInput);
|
||||
inputElement.addEventListener('blur', onBlur);
|
||||
|
||||
// Store untuk cleanup
|
||||
el._moneyFormat.handlers = {
|
||||
keydown: onKeydown,
|
||||
input: onInput,
|
||||
blur: onBlur
|
||||
};
|
||||
},
|
||||
|
||||
update: function(el, binding, vnode) {
|
||||
var inputElement = el.tagName === 'INPUT' ? el : el.querySelector('input');
|
||||
if (!inputElement || !el._moneyFormat) return;
|
||||
|
||||
var vm = vnode.context;
|
||||
var modelName = binding.expression;
|
||||
|
||||
// Format untuk display
|
||||
function formatNumber(value) {
|
||||
if (!value && value !== 0) return '';
|
||||
|
||||
// Konversi ke string
|
||||
var strValue = String(value);
|
||||
|
||||
// Ganti titik dengan koma untuk desimal
|
||||
strValue = strValue.replace('.', ',');
|
||||
|
||||
// Split berdasarkan koma
|
||||
var parts = strValue.split(',');
|
||||
var integerPart = parts[0];
|
||||
var decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// Format bagian integer dengan titik ribuan
|
||||
var formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
// Gabungkan kembali
|
||||
return decimalPart ? formattedInteger + ',' + decimalPart : formattedInteger;
|
||||
}
|
||||
|
||||
// Update display jika tidak sedang fokus
|
||||
if (document.activeElement !== inputElement) {
|
||||
var modelValue = vm[modelName];
|
||||
if (modelValue !== undefined && modelValue !== null) {
|
||||
var formattedValue = formatNumber(modelValue);
|
||||
inputElement.value = formattedValue;
|
||||
|
||||
// Update flags
|
||||
el._moneyFormat.hasDecimal = formattedValue.includes(',');
|
||||
if (el._moneyFormat.hasDecimal) {
|
||||
var parts = formattedValue.split(',');
|
||||
el._moneyFormat.decimalPart = parts.length > 1 ? parts[1] : '';
|
||||
} else {
|
||||
el._moneyFormat.decimalPart = '';
|
||||
}
|
||||
|
||||
el._moneyFormat.oldValue = formattedValue;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
unbind: function(el) {
|
||||
var inputElement = el.tagName === 'INPUT' ? el : el.querySelector('input');
|
||||
if (!inputElement || !el._moneyFormat || !el._moneyFormat.handlers) return;
|
||||
|
||||
// Remove event listeners
|
||||
inputElement.removeEventListener('keydown', el._moneyFormat.handlers.keydown);
|
||||
inputElement.removeEventListener('input', el._moneyFormat.handlers.input);
|
||||
inputElement.removeEventListener('blur', el._moneyFormat.handlers.blur);
|
||||
|
||||
// Cleanup
|
||||
delete el._moneyFormat;
|
||||
}
|
||||
};
|
||||
|
||||
// Register directive globally jika Vue tersedia
|
||||
if (typeof Vue !== 'undefined') {
|
||||
Vue.directive('money-format', moneyFormat);
|
||||
}
|
||||
|
||||
// Export untuk CommonJS/AMD
|
||||
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
||||
module.exports = moneyFormat;
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(function() { return moneyFormat; });
|
||||
} else {
|
||||
// Browser global
|
||||
window.moneyFormat = moneyFormat;
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user