Simplify ArrangementConfigurator: replace color pickers with compact pills, add click-to-add from pool, use watcher-based auto-select for new/cloned arrangements, remove group_colors from save payload. Enhance SongsBlock preview: color-coded group headers with tinted backgrounds, PDF download button inside preview modal, .pro download link per matched song, show DB ccli_id with fallback to CTS ccli_id. Fix Modal z-index for nested dialogs. Fix SlideUploader duplicate upload on watch by adding deep option and upload guard. Expand API log detail sections by default and increase JSON tree depth. Convert song download button from emit to direct .pro download link.
124 lines
3.2 KiB
Vue
124 lines
3.2 KiB
Vue
<script setup>
|
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
|
|
const props = defineProps({
|
|
show: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
maxWidth: {
|
|
type: String,
|
|
default: '2xl',
|
|
},
|
|
closeable: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['close']);
|
|
const dialog = ref();
|
|
const showSlot = ref(props.show);
|
|
|
|
watch(
|
|
() => props.show,
|
|
() => {
|
|
if (props.show) {
|
|
document.body.style.overflow = 'hidden';
|
|
showSlot.value = true;
|
|
|
|
dialog.value?.showModal();
|
|
} else {
|
|
document.body.style.overflow = '';
|
|
|
|
setTimeout(() => {
|
|
dialog.value?.close();
|
|
showSlot.value = false;
|
|
}, 200);
|
|
}
|
|
},
|
|
);
|
|
|
|
const close = () => {
|
|
if (props.closeable) {
|
|
emit('close');
|
|
}
|
|
};
|
|
|
|
const closeOnEscape = (e) => {
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
|
|
if (props.show) {
|
|
close();
|
|
}
|
|
}
|
|
};
|
|
|
|
onMounted(() => document.addEventListener('keydown', closeOnEscape));
|
|
|
|
onUnmounted(() => {
|
|
document.removeEventListener('keydown', closeOnEscape);
|
|
|
|
document.body.style.overflow = '';
|
|
});
|
|
|
|
const maxWidthClass = computed(() => {
|
|
return {
|
|
sm: 'sm:max-w-sm',
|
|
md: 'sm:max-w-md',
|
|
lg: 'sm:max-w-lg',
|
|
xl: 'sm:max-w-xl',
|
|
'2xl': 'sm:max-w-2xl',
|
|
}[props.maxWidth];
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<dialog
|
|
class="z-50 m-0 min-h-full min-w-full overflow-y-auto bg-transparent backdrop:bg-transparent"
|
|
ref="dialog"
|
|
>
|
|
<div
|
|
class="fixed inset-0 z-50 overflow-y-auto px-4 py-6 sm:px-0"
|
|
scroll-region
|
|
>
|
|
<Transition
|
|
enter-active-class="ease-out duration-300"
|
|
enter-from-class="opacity-0"
|
|
enter-to-class="opacity-100"
|
|
leave-active-class="ease-in duration-200"
|
|
leave-from-class="opacity-100"
|
|
leave-to-class="opacity-0"
|
|
>
|
|
<div
|
|
v-show="show"
|
|
class="fixed inset-0 transform transition-all"
|
|
@click="close"
|
|
>
|
|
<div
|
|
class="absolute inset-0 bg-gray-500 opacity-75"
|
|
/>
|
|
</div>
|
|
</Transition>
|
|
|
|
<Transition
|
|
enter-active-class="ease-out duration-300"
|
|
enter-from-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
enter-to-class="opacity-100 translate-y-0 sm:scale-100"
|
|
leave-active-class="ease-in duration-200"
|
|
leave-from-class="opacity-100 translate-y-0 sm:scale-100"
|
|
leave-to-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
>
|
|
<div
|
|
v-show="show"
|
|
class="relative z-10 mb-6 transform overflow-hidden rounded-lg bg-white shadow-xl transition-all sm:mx-auto sm:w-full"
|
|
:class="maxWidthClass"
|
|
>
|
|
<slot v-if="showSlot" />
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
</dialog>
|
|
</template>
|