mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 06:20:21 +00:00
feat(datepicker): introduce ZDatepicker component for enhanced date selection functionality
This commit is contained in:
1 parent
205b3a7e42
commit
a1e71006f0
4 files changed
+506
-29
No files matched your search
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<div
|
||||
class="z-datepicker-wrapper"
|
||||
:class="{ 'has-value': hasValue, 'is-focused': isFocused }"
|
||||
@click="handleClick"
|
||||
>
|
||||
<label v-if="label" class="z-datepicker-label">{{ label }}</label>
|
||||
<Datepicker
|
||||
ref="datepickerRef"
|
||||
:model-value="modelValue"
|
||||
@update:model-value="handleUpdate"
|
||||
@open="handleOpen"
|
||||
@closed="handleClose"
|
||||
v-bind="$attrs"
|
||||
:placeholder="hasValue ? ' ' : placeholder"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Datepicker from '@vuepic/vue-datepicker';
|
||||
|
||||
export default {
|
||||
name: 'ZDatepicker',
|
||||
|
||||
components: {
|
||||
Datepicker,
|
||||
},
|
||||
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Date, String, null],
|
||||
default: null,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['update:modelValue', 'open', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isFocused: false,
|
||||
preventInputFocus: false,
|
||||
savedScrollPosition: null,
|
||||
focusListeners: new Map(),
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
hasValue() {
|
||||
return this.modelValue != null && this.modelValue !== '';
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Attach focus listeners on mount to catch any focus events
|
||||
this.$nextTick(() => {
|
||||
this.attachFocusListeners();
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleUpdate(value) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
|
||||
handleOpen(event) {
|
||||
console.log(
|
||||
'🔵 [z-datepicker] handleOpen called at:',
|
||||
new Date().toISOString(),
|
||||
);
|
||||
this.isFocused = true;
|
||||
|
||||
// IMPORTANT: Set flag and save position BEFORE any other action
|
||||
this.preventInputFocus = true;
|
||||
console.log(
|
||||
'🔵 [z-datepicker] preventInputFocus set to:',
|
||||
this.preventInputFocus,
|
||||
);
|
||||
|
||||
// Save scroll position
|
||||
const modalContent = document.querySelector('.v-dialog .modal-content');
|
||||
if (modalContent) {
|
||||
this.savedScrollPosition = modalContent.scrollTop;
|
||||
console.log(
|
||||
'🔵 [z-datepicker] Saved scroll position:',
|
||||
this.savedScrollPosition,
|
||||
);
|
||||
} else {
|
||||
console.log('🔵 [z-datepicker] Modal content not found');
|
||||
}
|
||||
|
||||
// Reattach focus event listeners (for dynamically added inputs)
|
||||
this.attachFocusListeners();
|
||||
|
||||
// Reset after datepicker is opened
|
||||
setTimeout(() => {
|
||||
this.preventInputFocus = false;
|
||||
this.savedScrollPosition = null;
|
||||
console.log(
|
||||
'🔵 [z-datepicker] Reset preventInputFocus and savedScrollPosition after 500ms',
|
||||
);
|
||||
}, 500);
|
||||
|
||||
// Emit to parent (for backward compatibility)
|
||||
const wrapperElement =
|
||||
event?.target?.closest?.('.z-datepicker-wrapper') || true;
|
||||
console.log('🔵 [z-datepicker] Emitting open event to parent');
|
||||
this.$emit('open', wrapperElement);
|
||||
},
|
||||
|
||||
handleClose(event) {
|
||||
this.isFocused = false;
|
||||
this.$emit('closed', event);
|
||||
},
|
||||
|
||||
handleClick(event) {
|
||||
console.log('🔵 [z-datepicker] Wrapper clicked');
|
||||
// Prevent any default behavior
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// CRITICAL: Set prevention flag BEFORE opening menu to catch early focus events
|
||||
this.preventInputFocus = true;
|
||||
|
||||
// Save scroll position immediately
|
||||
const modalContent = document.querySelector('.v-dialog .modal-content');
|
||||
if (modalContent) {
|
||||
this.savedScrollPosition = modalContent.scrollTop;
|
||||
console.log(
|
||||
'🔵 [z-datepicker] Pre-saved scroll position in handleClick:',
|
||||
this.savedScrollPosition,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.$refs.datepickerRef?.openMenu) {
|
||||
this.$refs.datepickerRef.openMenu();
|
||||
}
|
||||
},
|
||||
|
||||
openMenu() {
|
||||
if (this.$refs.datepickerRef?.openMenu) {
|
||||
this.$refs.datepickerRef.openMenu();
|
||||
}
|
||||
},
|
||||
|
||||
attachFocusListeners() {
|
||||
console.log('🔵 [z-datepicker] attachFocusListeners called');
|
||||
// Remove old listeners first
|
||||
this.removeFocusListeners();
|
||||
|
||||
// Attach new listeners to all autocomplete inputs AND text fields
|
||||
const selectors = [
|
||||
'.v-autocomplete input',
|
||||
'.v-text-field input',
|
||||
'input[type="text"]',
|
||||
];
|
||||
|
||||
let totalListeners = 0;
|
||||
selectors.forEach((selector) => {
|
||||
const inputs = document.querySelectorAll(selector);
|
||||
let selectorCount = 0;
|
||||
|
||||
inputs.forEach((input) => {
|
||||
// Skip the datepicker's own input
|
||||
if (input.closest('.dp__input_wrap')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const listener = this.onInputFocus.bind(this);
|
||||
this.focusListeners.set(input, listener);
|
||||
input.addEventListener('focus', listener, true); // Use capture phase
|
||||
selectorCount++;
|
||||
totalListeners++;
|
||||
});
|
||||
|
||||
console.log(
|
||||
`🔵 [z-datepicker] Found ${selectorCount} inputs for selector: ${selector}`,
|
||||
);
|
||||
});
|
||||
|
||||
console.log(
|
||||
`🔵 [z-datepicker] Total focus listeners attached: ${totalListeners}`,
|
||||
);
|
||||
},
|
||||
|
||||
onInputFocus(event) {
|
||||
const target = event.target;
|
||||
console.log('🔵 [z-datepicker] onInputFocus triggered on:', {
|
||||
tagName: target.tagName,
|
||||
type: target.type,
|
||||
id: target.id,
|
||||
className: target.className,
|
||||
preventInputFocus: this.preventInputFocus,
|
||||
savedScrollPosition: this.savedScrollPosition,
|
||||
});
|
||||
|
||||
// Skip if it's the datepicker's own input
|
||||
if (
|
||||
target.closest('.dp__input_wrap') ||
|
||||
target.closest('.z-datepicker-wrapper')
|
||||
) {
|
||||
console.log(
|
||||
"🔵 [z-datepicker] Skipping - this is datepicker's own input",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// If prevention flag is set, block the focus immediately
|
||||
if (this.preventInputFocus) {
|
||||
console.log('🔵 [z-datepicker] PREVENTING FOCUS on:', target);
|
||||
const scrollBefore = document.querySelector(
|
||||
'.v-dialog .modal-content',
|
||||
)?.scrollTop;
|
||||
console.log('🔵 [z-datepicker] Scroll position before:', scrollBefore);
|
||||
|
||||
// Prevent the focus - stop all propagation
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Blur the focused element immediately (no timeout)
|
||||
target.blur();
|
||||
|
||||
// Restore scroll position if we have one saved
|
||||
if (this.savedScrollPosition !== null) {
|
||||
const modalContent = document.querySelector(
|
||||
'.v-dialog .modal-content',
|
||||
);
|
||||
if (modalContent) {
|
||||
modalContent.scrollTop = this.savedScrollPosition;
|
||||
const scrollAfter = modalContent.scrollTop;
|
||||
console.log(
|
||||
'🔵 [z-datepicker] Scroll position restored to:',
|
||||
scrollAfter,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
'🔵 [z-datepicker] Focus allowed (preventInputFocus:',
|
||||
this.preventInputFocus,
|
||||
', savedScrollPosition:',
|
||||
this.savedScrollPosition,
|
||||
')',
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
removeFocusListeners() {
|
||||
this.focusListeners.forEach((listener, input) => {
|
||||
input.removeEventListener('focus', listener);
|
||||
});
|
||||
this.focusListeners.clear();
|
||||
},
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
// Cleanup listeners on component unmount
|
||||
this.removeFocusListeners();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="sass">
|
||||
.z-datepicker-wrapper
|
||||
position: relative
|
||||
width: 100%
|
||||
cursor: pointer
|
||||
|
||||
.z-datepicker-label
|
||||
position: absolute
|
||||
left: 16px
|
||||
top: 50%
|
||||
transform: translateY(-50%)
|
||||
font-size: 16px
|
||||
color: rgba(0, 0, 0, 0.38)
|
||||
background: transparent
|
||||
padding: 0
|
||||
z-index: 1
|
||||
pointer-events: none
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1)
|
||||
opacity: 0
|
||||
visibility: hidden
|
||||
|
||||
&.has-value .z-datepicker-label
|
||||
opacity: 1
|
||||
visibility: visible
|
||||
top: 8px
|
||||
transform: translateY(0)
|
||||
font-size: 12px
|
||||
background: white
|
||||
padding: 0 4px
|
||||
left: 13px
|
||||
|
||||
&.is-focused .z-datepicker-label
|
||||
color: rgba(0, 0, 0, 0.87)
|
||||
|
||||
&.has-value .z-datepicker-label
|
||||
color: rgba(0, 0, 0, 0.6)
|
||||
|
||||
&.has-value :deep(.dp__input_icon)
|
||||
top: 33px !important
|
||||
|
||||
&.has-value :deep(.dp__input)
|
||||
padding-top: 20px !important
|
||||
padding-bottom: 8px !important
|
||||
|
||||
:deep(.dp__input)
|
||||
padding-top: 14px !important
|
||||
padding-bottom: 14px !important
|
||||
|
||||
:deep(.dp__input_wrap)
|
||||
pointer-events: none
|
||||
|
||||
:deep(.dp__clear_icon)
|
||||
pointer-events: auto !important
|
||||
cursor: pointer !important
|
||||
top: 28px !important
|
||||
|
||||
:deep(.dp__input_icon)
|
||||
top: 27px !important
|
||||
left: 3px !important
|
||||
</style>
|
||||
@@ -9,9 +9,20 @@ export default {
|
||||
|
||||
methods: {
|
||||
setFocusEvent() {
|
||||
document.querySelectorAll('.v-autocomplete input').forEach((input) => {
|
||||
const inputs = document.querySelectorAll('.v-autocomplete input');
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] setFocusEvent - Found',
|
||||
inputs.length,
|
||||
'autocomplete inputs',
|
||||
);
|
||||
inputs.forEach((input) => {
|
||||
input.addEventListener('focus', this.onInputFocus);
|
||||
});
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Attached focus listeners to',
|
||||
inputs.length,
|
||||
'inputs',
|
||||
);
|
||||
},
|
||||
|
||||
onDateFocus($event) {
|
||||
@@ -21,31 +32,69 @@ export default {
|
||||
},
|
||||
|
||||
onDateOpen($event) {
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] onDateOpen called at:',
|
||||
new Date().toISOString(),
|
||||
'with event:',
|
||||
$event,
|
||||
);
|
||||
// $event should be the element reference (or any truthy value to trigger saving)
|
||||
if ($event) {
|
||||
// Find the scrollable modal container
|
||||
const modalContent = document.querySelector('.v-dialog .modal-content');
|
||||
if (modalContent) {
|
||||
this.savedScrollPosition = modalContent.scrollTop;
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Saved scroll position:',
|
||||
this.savedScrollPosition,
|
||||
);
|
||||
} else {
|
||||
console.log('🟢 [VuetifyFixFocusMixin] Modal content not found');
|
||||
}
|
||||
|
||||
this.preventInputFocus = true;
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] preventInputFocus set to:',
|
||||
this.preventInputFocus,
|
||||
);
|
||||
|
||||
// Reset after datepicker is opened
|
||||
setTimeout(() => {
|
||||
this.preventInputFocus = false;
|
||||
this.savedScrollPosition = null;
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Reset preventInputFocus and savedScrollPosition after 500ms',
|
||||
);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
onInputFocus($event) {
|
||||
const target = $event.target;
|
||||
console.log('🟢 [VuetifyFixFocusMixin] onInputFocus triggered on:', {
|
||||
tagName: target.tagName,
|
||||
type: target.type,
|
||||
id: target.id,
|
||||
className: target.className,
|
||||
preventInputFocus: this.preventInputFocus,
|
||||
savedScrollPosition: this.savedScrollPosition,
|
||||
});
|
||||
|
||||
// If we have a saved scroll position and an unwanted focus happened
|
||||
if (this.preventInputFocus && this.savedScrollPosition !== null) {
|
||||
const target = $event.target;
|
||||
|
||||
// Only intercept text input focus (autocomplete fields)
|
||||
if (target.tagName === 'INPUT' && target.type === 'text') {
|
||||
console.log('🟢 [VuetifyFixFocusMixin] PREVENTING FOCUS on:', target);
|
||||
const scrollBefore = document.querySelector(
|
||||
'.v-dialog .modal-content',
|
||||
)?.scrollTop;
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Scroll position before:',
|
||||
scrollBefore,
|
||||
);
|
||||
|
||||
// Blur the focused element
|
||||
setTimeout(() => {
|
||||
target.blur();
|
||||
@@ -55,9 +104,26 @@ export default {
|
||||
);
|
||||
if (modalContent && this.savedScrollPosition !== null) {
|
||||
modalContent.scrollTop = this.savedScrollPosition;
|
||||
const scrollAfter = modalContent.scrollTop;
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Scroll position restored to:',
|
||||
scrollAfter,
|
||||
);
|
||||
}
|
||||
}, 0);
|
||||
} else {
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Not a text input, skipping prevention',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
'🟢 [VuetifyFixFocusMixin] Focus allowed (preventInputFocus:',
|
||||
this.preventInputFocus,
|
||||
', savedScrollPosition:',
|
||||
this.savedScrollPosition,
|
||||
')',
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -57,24 +57,22 @@
|
||||
|
||||
<div class="splitted-inputs">
|
||||
<!-- TODO: Bialko validacie -->
|
||||
<div
|
||||
@click="handleDatepickerClick"
|
||||
class="datepicker-wrapper left"
|
||||
ref="datepicker-wrapper"
|
||||
>
|
||||
<Datepicker
|
||||
<div class="datepicker-wrapper left" ref="datepicker-wrapper">
|
||||
<Z-datepicker
|
||||
v-model="mastertask.deadline"
|
||||
ref="datepicker"
|
||||
label="Dátum"
|
||||
:auto-apply="true"
|
||||
:min-date="new Date()"
|
||||
class="modal-input datepicker-input"
|
||||
class="modal-input"
|
||||
format="dd.MM.yyyy"
|
||||
locale="sk"
|
||||
select-text="Vybrať"
|
||||
cancel-text="Zrušiť"
|
||||
placeholder="Dátum"
|
||||
placeholder="Deadline"
|
||||
:class="{ error: v$.mastertask.deadline.$errors?.length }"
|
||||
:validations="v$.mastertask.deadline"
|
||||
@open="onDateOpen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -118,7 +116,7 @@
|
||||
class="modal-input"
|
||||
variant="solo"
|
||||
type="number"
|
||||
:label="`${mastertask.duration} h`"
|
||||
label="Naplánované hodiny"
|
||||
:disabled="true"
|
||||
></v-text-field>
|
||||
|
||||
@@ -312,7 +310,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Datepicker from '@vuepic/vue-datepicker';
|
||||
import ZDatepicker from '@/plugins/tm@_components/z-datepicker.vue';
|
||||
import ZFilesUpload from '@/plugins/tm@_components/z-files-upload.vue';
|
||||
import useVuelidate from '@vuelidate/core';
|
||||
import { required, minLength } from '@vuelidate/validators';
|
||||
@@ -344,7 +342,7 @@ export default {
|
||||
components: {
|
||||
AMastertaskDescription,
|
||||
ATasksTable,
|
||||
Datepicker,
|
||||
ZDatepicker,
|
||||
ZFilesUpload,
|
||||
// ZAutocomplete,
|
||||
QuillEditor,
|
||||
@@ -461,11 +459,29 @@ export default {
|
||||
|
||||
methods: {
|
||||
handleDatepickerClick() {
|
||||
// Pass the datepicker wrapper element reference to the mixin
|
||||
const scrollTarget = this.$refs['datepicker-wrapper'];
|
||||
this.onDateOpen(scrollTarget);
|
||||
// Open the datepicker
|
||||
this.$refs['datepicker'].openMenu();
|
||||
// Open the datepicker - it now opens automatically via click on wrapper
|
||||
},
|
||||
|
||||
onDateOpen($event) {
|
||||
// $event should be the element reference (or any truthy value to trigger saving)
|
||||
if ($event) {
|
||||
// Find the scrollable modal container
|
||||
const modalContent = document.querySelector('.v-dialog .modal-content');
|
||||
if (modalContent) {
|
||||
this.savedScrollPosition = modalContent.scrollTop;
|
||||
}
|
||||
|
||||
this.preventInputFocus = true;
|
||||
|
||||
// Reattach focus event listeners
|
||||
this.setFocusEvent();
|
||||
|
||||
// Reset after datepicker is opened
|
||||
setTimeout(() => {
|
||||
this.preventInputFocus = false;
|
||||
this.savedScrollPosition = null;
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
closeBothModals() {
|
||||
@@ -527,11 +543,26 @@ export default {
|
||||
async showModal({ mastertask, editMode = false }) {
|
||||
this.isSaving = false;
|
||||
this.editMode = null;
|
||||
|
||||
// CRITICAL: Prevent v-dialog autofocus BEFORE modal opens
|
||||
this.preventInputFocus = true;
|
||||
console.log(
|
||||
'🟠 [z-master-task-modal] showModal - preventInputFocus set to true BEFORE modal opens',
|
||||
);
|
||||
|
||||
this.visible = true;
|
||||
this.mastertask = null;
|
||||
this.$nextTick(() => {
|
||||
this.setFocusEvent();
|
||||
this.v$.$reset();
|
||||
|
||||
// Reset after modal is fully rendered and v-dialog autofocus has been blocked
|
||||
setTimeout(() => {
|
||||
this.preventInputFocus = false;
|
||||
console.log(
|
||||
'🟠 [z-master-task-modal] showModal - preventInputFocus reset to false after 300ms',
|
||||
);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -787,8 +818,4 @@ export default {
|
||||
|
||||
.datepicker-wrapper
|
||||
width: 100%
|
||||
.datepicker-input
|
||||
width: 100% !important
|
||||
height: 100%
|
||||
pointer-events: none
|
||||
</style>
|
||||
@@ -57,17 +57,15 @@
|
||||
|
||||
<div class="splitted-inputs">
|
||||
<!-- TODO: Bialko validacie -->
|
||||
<div
|
||||
@click="openDatepicker('datepicker-01')"
|
||||
class="datepicker-wrapper"
|
||||
>
|
||||
<Datepicker
|
||||
<div class="w-100 modal-input">
|
||||
<Z-datepicker
|
||||
v-model="subtask.deadline"
|
||||
@open="onDateOpen"
|
||||
ref="datepicker-01"
|
||||
label="Deadline"
|
||||
:auto-apply="true"
|
||||
:min-date="new Date()"
|
||||
class="modal-input left datepicker-input"
|
||||
class="modal-input"
|
||||
format="dd.MM.yyyy"
|
||||
locale="sk"
|
||||
select-text="Vybrať"
|
||||
@@ -348,7 +346,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Datepicker from '@vuepic/vue-datepicker';
|
||||
import ZDatepicker from '@/plugins/tm@_components/z-datepicker.vue';
|
||||
import ZFilesUpload from '@/plugins/tm@_components/z-files-upload.vue';
|
||||
import useVuelidate from '@vuelidate/core';
|
||||
import { required, requiredIf, optional } from '@vuelidate/validators';
|
||||
@@ -375,7 +373,7 @@ export default {
|
||||
ZTaskReadonlyInfo,
|
||||
ATimeblock,
|
||||
ZFilesUpload,
|
||||
Datepicker,
|
||||
ZDatepicker,
|
||||
// ATaskTimeblocks,
|
||||
QuillEditor,
|
||||
},
|
||||
@@ -505,6 +503,46 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
// Override onDateOpen from VuetifyFixFocusMixin to add logging
|
||||
onDateOpen($event) {
|
||||
console.log(
|
||||
'🟡 [z-task-modal] onDateOpen called at:',
|
||||
new Date().toISOString(),
|
||||
'with event:',
|
||||
$event,
|
||||
);
|
||||
// Call the parent mixin's onDateOpen method
|
||||
if (
|
||||
VuetifyFixFocusMixin.methods &&
|
||||
VuetifyFixFocusMixin.methods.onDateOpen
|
||||
) {
|
||||
VuetifyFixFocusMixin.methods.onDateOpen.call(this, $event);
|
||||
}
|
||||
},
|
||||
|
||||
// Override onInputFocus from VuetifyFixFocusMixin to add logging
|
||||
onInputFocus($event) {
|
||||
const target = $event?.target;
|
||||
console.log(
|
||||
'🟡 [z-task-modal] onInputFocus called at:',
|
||||
new Date().toISOString(),
|
||||
'on:',
|
||||
{
|
||||
tagName: target?.tagName,
|
||||
type: target?.type,
|
||||
id: target?.id,
|
||||
className: target?.className,
|
||||
},
|
||||
);
|
||||
// Call the parent mixin's onInputFocus method
|
||||
if (
|
||||
VuetifyFixFocusMixin.methods &&
|
||||
VuetifyFixFocusMixin.methods.onInputFocus
|
||||
) {
|
||||
VuetifyFixFocusMixin.methods.onInputFocus.call(this, $event);
|
||||
}
|
||||
},
|
||||
|
||||
toggleEditMode() {
|
||||
if (this.subtask.type == 'office365')
|
||||
return this.$events.emit('show-unable-to-edit-office-task-modal', {
|
||||
@@ -560,6 +598,13 @@ export default {
|
||||
async showModal({ subtask, editMode = false, mastertask = {} }) {
|
||||
this.isSaving = false;
|
||||
this.subtask = null;
|
||||
|
||||
// CRITICAL: Prevent v-dialog autofocus BEFORE modal opens
|
||||
this.preventInputFocus = true;
|
||||
console.log(
|
||||
'🟡 [z-task-modal] showModal - preventInputFocus set to true BEFORE modal opens',
|
||||
);
|
||||
|
||||
this.visible = true;
|
||||
this.timeblocks = [
|
||||
{
|
||||
@@ -571,6 +616,14 @@ export default {
|
||||
this.$nextTick(() => {
|
||||
this.v$.$reset();
|
||||
this.setFocusEvent();
|
||||
|
||||
// Reset after modal is fully rendered and v-dialog autofocus has been blocked
|
||||
setTimeout(() => {
|
||||
this.preventInputFocus = false;
|
||||
console.log(
|
||||
'🟡 [z-task-modal] showModal - preventInputFocus reset to false after 300ms',
|
||||
);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user