Files
hnh-map/frontend-nuxt/components/ConfirmModal.vue
Nikolay Tatarinov adfdfd01c4 Update frontend components for accessibility and functionality improvements
- Modified docker-compose.dev.yml to conditionally run npm install based on the presence of package-lock.json.
- Upgraded Nuxt version in package-lock.json from 3.21.1 to 4.3.1 for enhanced features.
- Enhanced ConfirmModal component with aria-modal attribute for better accessibility.
- Updated MapErrorBoundary component's error message for clarity.
- Added role and aria-label attributes to MapView and MapSearch components for improved screen reader support.
- Refactored various components to manage focus behavior on modal close, enhancing user experience.
- Improved ToastContainer styling for better responsiveness and visibility.
- Updated layout components to include skip navigation links for improved accessibility.
2026-03-04 01:00:56 +03:00

77 lines
2.1 KiB
Vue

<template>
<dialog ref="dialogRef" class="modal" :aria-labelledby="titleId" aria-modal="true" @close="onClose">
<div class="modal-box">
<h2 :id="titleId" class="font-bold text-lg mb-2">{{ title }}</h2>
<p>{{ message }}</p>
<div class="modal-action">
<form method="dialog">
<button type="button" class="btn" @click="cancel">Cancel</button>
</form>
<button
type="button"
:class="danger ? 'btn btn-error' : 'btn btn-primary'"
:disabled="loading"
@click="confirm"
>
<span v-if="loading" class="loading loading-spinner loading-sm" />
<span v-else>{{ confirmLabel }}</span>
</button>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button type="button" aria-label="Close" @click="cancel">Close</button>
</form>
</dialog>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{
modelValue: boolean
title: string
message: string
confirmLabel?: string
danger?: boolean
loading?: boolean
}>(),
{ confirmLabel: 'Confirm', danger: false, loading: false }
)
const emit = defineEmits<{
'update:modelValue': [value: boolean]
confirm: []
}>()
const titleId = computed(() => `confirm-modal-title-${Math.random().toString(36).slice(2)}`)
const dialogRef = ref<HTMLDialogElement | null>(null)
let previousActiveElement: HTMLElement | null = null
watch(
() => props.modelValue,
(open) => {
if (open) {
previousActiveElement = (import.meta.client ? document.activeElement : null) as HTMLElement | null
dialogRef.value?.showModal()
} else {
dialogRef.value?.close()
}
}
)
function cancel() {
dialogRef.value?.close()
emit('update:modelValue', false)
}
function onClose() {
emit('update:modelValue', false)
if (import.meta.client && previousActiveElement && typeof previousActiveElement.focus === 'function' && document.contains(previousActiveElement)) {
nextTick(() => previousActiveElement?.focus())
}
}
function confirm() {
emit('confirm')
}
</script>