Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <template>
- <div class="datetime-input">
- <input
- type="text"
- v-model="displayValue"
- @focus="onFocus"
- @blur="onBlur"
- @keydown="handleKeyDown"
- @input="handleInput"
- placeholder="DD/MM/YYYY HH:mm"
- class="datetime-input__field"
- />
- <div v-if="error" class="datetime-input__error">{{ error }}</div>
- </div>
- </template>
- <script>
- import { ref, watch, computed } from 'vue';
- export default {
- name: 'DateTimeInput',
- props: {
- modelValue: {
- type: [Date, String],
- default: null
- }
- },
- emits: ['update:modelValue'],
- setup(props, { emit }) {
- const displayValue = ref('');
- const internalDate = ref(null);
- const isEditing = ref(false);
- const error = ref('');
- // Initialize
- if (props.modelValue) {
- updateFromModelValue(props.modelValue);
- }
- // Watch for external changes
- watch(() => props.modelValue, (newVal) => {
- if (!isEditing.value) {
- updateFromModelValue(newVal);
- }
- });
- function updateFromModelValue(value) {
- if (!value) {
- internalDate.value = null;
- displayValue.value = '';
- return;
- }
- const date = value instanceof Date ? new Date(value) : parseDateString(value);
- if (date && !isNaN(date.getTime())) {
- internalDate.value = date;
- displayValue.value = formatDate(date);
- error.value = '';
- } else {
- error.value = 'Invalid date';
- }
- }
- function onFocus() {
- isEditing.value = true;
- }
- function onBlur() {
- isEditing.value = false;
- validateAndUpdate();
- }
- function handleKeyDown(e) {
- // Allow: backspace, delete, tab, escape, enter
- if ([46, 8, 9, 27, 13].includes(e.keyCode)) return;
- // Allow: Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X
- if ((e.ctrlKey || e.metaKey) && [65, 67, 86, 88].includes(e.keyCode)) return;
- // Allow: home, end, left, right
- if ([35, 36, 37, 39].includes(e.keyCode)) return;
- // Allow: numbers and numpad numbers
- if ((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 96 && e.keyCode <= 105)) {
- return;
- }
- // Allow: / and : for date/time separation
- if ([191, 186].includes(e.keyCode)) return;
- // Prevent default for all other keys
- e.preventDefault();
- }
- function handleInput(e) {
- // Auto-format as user types
- const value = e.target.value;
- if (value.length === 2 || value.length === 5) {
- displayValue.value = value + '/';
- } else if (value.length === 10) {
- displayValue.value = value + ' ';
- } else if (value.length === 13) {
- displayValue.value = value + ':';
- }
- }
- function validateAndUpdate() {
- const parsedDate = parseDateString(displayValue.value);
- if (parsedDate && !isNaN(parsedDate.getTime())) {
- internalDate.value = parsedDate;
- emit('update:modelValue', parsedDate);
- error.value = '';
- } else {
- error.value = 'Invalid date format (DD/MM/YYYY HH:mm)';
- // Revert to last valid value
- displayValue.value = internalDate.value ? formatDate(internalDate.value) : '';
- }
- }
- function formatDate(date) {
- if (!date) return '';
- const pad = num => num.toString().padStart(2, '0');
- return [
- pad(date.getDate()),
- pad(date.getMonth() + 1),
- date.getFullYear()
- ].join('/') + ' ' + [
- pad(date.getHours()),
- pad(date.getMinutes())
- ].join(':');
- }
- function parseDateString(dateString) {
- if (!dateString) return null;
- // Clean input
- const cleanString = dateString
- .replace(/[^0-9/:]/g, '') // Remove non-digits and non-separators
- .replace(/(\/|:)+/g, '$1'); // Remove duplicate separators
- const [datePart, timePart] = cleanString.split(' ');
- const [day, month, year] = (datePart || '').split('/').map(Number);
- const [hours, minutes] = (timePart || '').split(':').map(Number);
- // Validate components
- if (!day || !month || !year) return null;
- if (day < 1 || day > 31) return null;
- if (month < 1 || month > 12) return null;
- if (year < 1000 || year > 9999) return null;
- const date = new Date(
- year,
- month - 1,
- Math.min(day, daysInMonth(year, month - 1)),
- hours || 0,
- minutes || 0
- );
- return isNaN(date.getTime()) ? null : date;
- }
- function daysInMonth(year, month) {
- return new Date(year, month + 1, 0).getDate();
- }
- return {
- displayValue,
- error,
- onFocus,
- onBlur,
- handleKeyDown,
- handleInput
- };
- }
- };
- </script>
- <style scoped>
- .datetime-input {
- font-family: Arial, sans-serif;
- max-width: 250px;
- }
- .datetime-input__field {
- width: 100%;
- padding: 8px 12px;
- border: 1px solid #ccc;
- border-radius: 4px;
- font-size: 14px;
- box-sizing: border-box;
- }
- .datetime-input__field:focus {
- outline: none;
- border-color: #4a90e2;
- box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
- }
- .datetime-input__error {
- color: #d32f2f;
- font-size: 12px;
- margin-top: 4px;
- }
- </style>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement