Advertisement
Vit_abo

Untitled

May 23rd, 2025
11
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.32 KB | None | 0 0
  1. <template>
  2. <div class="datetime-input">
  3. <input
  4. type="text"
  5. v-model="displayValue"
  6. @focus="onFocus"
  7. @blur="onBlur"
  8. @keydown="handleKeyDown"
  9. @input="handleInput"
  10. placeholder="DD/MM/YYYY HH:mm"
  11. class="datetime-input__field"
  12. />
  13. <div v-if="error" class="datetime-input__error">{{ error }}</div>
  14. </div>
  15. </template>
  16.  
  17. <script>
  18. import { ref, watch, computed } from 'vue';
  19.  
  20. export default {
  21. name: 'DateTimeInput',
  22. props: {
  23. modelValue: {
  24. type: [Date, String],
  25. default: null
  26. }
  27. },
  28. emits: ['update:modelValue'],
  29. setup(props, { emit }) {
  30. const displayValue = ref('');
  31. const internalDate = ref(null);
  32. const isEditing = ref(false);
  33. const error = ref('');
  34.  
  35. // Initialize
  36. if (props.modelValue) {
  37. updateFromModelValue(props.modelValue);
  38. }
  39.  
  40. // Watch for external changes
  41. watch(() => props.modelValue, (newVal) => {
  42. if (!isEditing.value) {
  43. updateFromModelValue(newVal);
  44. }
  45. });
  46.  
  47. function updateFromModelValue(value) {
  48. if (!value) {
  49. internalDate.value = null;
  50. displayValue.value = '';
  51. return;
  52. }
  53.  
  54. const date = value instanceof Date ? new Date(value) : parseDateString(value);
  55. if (date && !isNaN(date.getTime())) {
  56. internalDate.value = date;
  57. displayValue.value = formatDate(date);
  58. error.value = '';
  59. } else {
  60. error.value = 'Invalid date';
  61. }
  62. }
  63.  
  64. function onFocus() {
  65. isEditing.value = true;
  66. }
  67.  
  68. function onBlur() {
  69. isEditing.value = false;
  70. validateAndUpdate();
  71. }
  72.  
  73. function handleKeyDown(e) {
  74. // Allow: backspace, delete, tab, escape, enter
  75. if ([46, 8, 9, 27, 13].includes(e.keyCode)) return;
  76.  
  77. // Allow: Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X
  78. if ((e.ctrlKey || e.metaKey) && [65, 67, 86, 88].includes(e.keyCode)) return;
  79.  
  80. // Allow: home, end, left, right
  81. if ([35, 36, 37, 39].includes(e.keyCode)) return;
  82.  
  83. // Allow: numbers and numpad numbers
  84. if ((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 96 && e.keyCode <= 105)) {
  85. return;
  86. }
  87.  
  88. // Allow: / and : for date/time separation
  89. if ([191, 186].includes(e.keyCode)) return;
  90.  
  91. // Prevent default for all other keys
  92. e.preventDefault();
  93. }
  94.  
  95. function handleInput(e) {
  96. // Auto-format as user types
  97. const value = e.target.value;
  98. if (value.length === 2 || value.length === 5) {
  99. displayValue.value = value + '/';
  100. } else if (value.length === 10) {
  101. displayValue.value = value + ' ';
  102. } else if (value.length === 13) {
  103. displayValue.value = value + ':';
  104. }
  105. }
  106.  
  107. function validateAndUpdate() {
  108. const parsedDate = parseDateString(displayValue.value);
  109. if (parsedDate && !isNaN(parsedDate.getTime())) {
  110. internalDate.value = parsedDate;
  111. emit('update:modelValue', parsedDate);
  112. error.value = '';
  113. } else {
  114. error.value = 'Invalid date format (DD/MM/YYYY HH:mm)';
  115. // Revert to last valid value
  116. displayValue.value = internalDate.value ? formatDate(internalDate.value) : '';
  117. }
  118. }
  119.  
  120. function formatDate(date) {
  121. if (!date) return '';
  122.  
  123. const pad = num => num.toString().padStart(2, '0');
  124.  
  125. return [
  126. pad(date.getDate()),
  127. pad(date.getMonth() + 1),
  128. date.getFullYear()
  129. ].join('/') + ' ' + [
  130. pad(date.getHours()),
  131. pad(date.getMinutes())
  132. ].join(':');
  133. }
  134.  
  135. function parseDateString(dateString) {
  136. if (!dateString) return null;
  137.  
  138. // Clean input
  139. const cleanString = dateString
  140. .replace(/[^0-9/:]/g, '') // Remove non-digits and non-separators
  141. .replace(/(\/|:)+/g, '$1'); // Remove duplicate separators
  142.  
  143. const [datePart, timePart] = cleanString.split(' ');
  144. const [day, month, year] = (datePart || '').split('/').map(Number);
  145. const [hours, minutes] = (timePart || '').split(':').map(Number);
  146.  
  147. // Validate components
  148. if (!day || !month || !year) return null;
  149. if (day < 1 || day > 31) return null;
  150. if (month < 1 || month > 12) return null;
  151. if (year < 1000 || year > 9999) return null;
  152.  
  153. const date = new Date(
  154. year,
  155. month - 1,
  156. Math.min(day, daysInMonth(year, month - 1)),
  157. hours || 0,
  158. minutes || 0
  159. );
  160.  
  161. return isNaN(date.getTime()) ? null : date;
  162. }
  163.  
  164. function daysInMonth(year, month) {
  165. return new Date(year, month + 1, 0).getDate();
  166. }
  167.  
  168. return {
  169. displayValue,
  170. error,
  171. onFocus,
  172. onBlur,
  173. handleKeyDown,
  174. handleInput
  175. };
  176. }
  177. };
  178. </script>
  179.  
  180. <style scoped>
  181. .datetime-input {
  182. font-family: Arial, sans-serif;
  183. max-width: 250px;
  184. }
  185.  
  186. .datetime-input__field {
  187. width: 100%;
  188. padding: 8px 12px;
  189. border: 1px solid #ccc;
  190. border-radius: 4px;
  191. font-size: 14px;
  192. box-sizing: border-box;
  193. }
  194.  
  195. .datetime-input__field:focus {
  196. outline: none;
  197. border-color: #4a90e2;
  198. box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
  199. }
  200.  
  201. .datetime-input__error {
  202. color: #d32f2f;
  203. font-size: 12px;
  204. margin-top: 4px;
  205. }
  206. </style>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement