All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m6s
- Agregar validación completa en loadCustomColors para detectar formato incompatible - Proteger setCustomColors asegurando que la estructura existe antes de asignar - Validar estructura en getCurrentColors y retornar defaults si no existe - Validar estructura en hasCustomColors y retornar false si no existe - Validar estructura en inicializar antes de aplicar colores - Limpiar localStorage automáticamente si detecta formato antiguo o corrupto Soluciona error: TypeError Cannot create property 'primary' on string cuando localStorage contiene datos de versión anterior
233 lines
6.0 KiB
Vue
233 lines
6.0 KiB
Vue
<template>
|
|
<div class="slider-intensidad cata-fade-in" :style="customColorStyle">
|
|
<!-- Slider Container -->
|
|
<div class="relative">
|
|
<USlider
|
|
:id="inputId"
|
|
:model-value="modelValue ?? undefined"
|
|
:min="min"
|
|
:max="max"
|
|
:step="step"
|
|
:disabled="disabled"
|
|
:tooltip="tooltipConfig"
|
|
:ui="sliderUi"
|
|
@update:model-value="handleChange"
|
|
/>
|
|
|
|
<!-- Indicadores de rango -->
|
|
<div class="flex justify-between mt-2 text-xs cata-text opacity-60">
|
|
<span>{{ min }}</span>
|
|
<span v-if="modelValue !== null" class="font-semibold opacity-100">
|
|
{{ modelValue }}
|
|
</span>
|
|
<span>{{ max }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Descripción del tipo -->
|
|
<p v-if="showDescription" class="text-xs mt-1 cata-text opacity-75">
|
|
{{ tipoDescription }}
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { TooltipProps } from '@nuxt/ui'
|
|
|
|
interface SliderIntensidadProps {
|
|
/** Tipo de intensidad: descriptiva (1-10) o afectiva (1-15) */
|
|
tipo: 'descriptiva' | 'afectiva'
|
|
/** Valor actual del slider */
|
|
modelValue: number | null
|
|
/** Etiqueta del slider */
|
|
label?: string
|
|
/** ID para el input (auto-generado si no se provee) */
|
|
id?: string
|
|
/** Deshabilitar el slider */
|
|
disabled?: boolean
|
|
/** Marcar como requerido */
|
|
required?: boolean
|
|
/** Mostrar descripción del tipo */
|
|
showDescription?: boolean
|
|
/** Color personalizado para el slider */
|
|
color?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<SliderIntensidadProps>(), {
|
|
disabled: false,
|
|
required: false,
|
|
showDescription: false,
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: number | null]
|
|
}>()
|
|
|
|
// ID único para el input
|
|
const inputId = computed(() => props.id || `slider-${Math.random().toString(36).substring(2, 9)}`)
|
|
|
|
// Configuración según el tipo
|
|
const min = computed(() => 1)
|
|
const max = computed(() => props.tipo === 'descriptiva' ? 10 : 15)
|
|
const step = computed(() => 1)
|
|
|
|
// Descripción del tipo de intensidad
|
|
const tipoDescription = computed(() => {
|
|
return props.tipo === 'descriptiva'
|
|
? 'Intensidad descriptiva: qué tan intensa es la característica (sin importar si es buena o mala)'
|
|
: 'Intensidad afectiva: qué tan buena o mala consideras esta característica'
|
|
})
|
|
|
|
// Configuración del tooltip
|
|
const tooltipConfig = computed<boolean | TooltipProps>(() => ({
|
|
disableClosingTrigger: true,
|
|
text: props.modelValue !== null ? String(props.modelValue) : '',
|
|
}))
|
|
|
|
// Configuración UI personalizada del slider
|
|
const sliderUi = computed(() => {
|
|
const baseClasses = {
|
|
root: 'cata-slider-root',
|
|
track: `cata-slider-track ${props.color ? 'cata-slider-track-custom' : ''}`,
|
|
thumb: `cata-slider-thumb ${props.color ? 'cata-slider-thumb-custom' : ''} ${props.tipo === 'descriptiva' ? 'cata-slider-thumb-descriptivo' : 'cata-slider-thumb-afectivo'}`,
|
|
}
|
|
|
|
// Si hay color personalizado, usamos una clase custom para el range
|
|
if (props.color) {
|
|
return {
|
|
...baseClasses,
|
|
range: props.tipo === 'descriptiva'
|
|
? 'cata-slider-range-custom cata-slider-range-descriptivo'
|
|
: 'cata-slider-range-custom cata-slider-range-afectivo',
|
|
}
|
|
}
|
|
|
|
// Si no hay color personalizado, usamos las clases por defecto
|
|
return {
|
|
...baseClasses,
|
|
range: props.tipo === 'descriptiva'
|
|
? 'bg-primary/20 dark:bg-primary/30'
|
|
: 'bg-primary/40 dark:bg-primary/50',
|
|
}
|
|
})
|
|
|
|
// Manejar cambio de valor
|
|
const handleChange = (value: number | number[] | undefined) => {
|
|
if (value === undefined || value === null) {
|
|
emit('update:modelValue', null)
|
|
return
|
|
}
|
|
const newValue = Array.isArray(value) ? (value[0] ?? null) : value
|
|
emit('update:modelValue', newValue)
|
|
}
|
|
|
|
// Estilo dinámico para el color personalizado
|
|
const customColorStyle = computed(() => {
|
|
if (!props.color) return {}
|
|
return {
|
|
'--slider-custom-color': props.color,
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.slider-intensidad {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Personalización adicional para el slider */
|
|
:deep(.cata-slider-root) {
|
|
padding-top: 0.5rem;
|
|
padding-bottom: 0.5rem;
|
|
}
|
|
|
|
:deep(.cata-slider-track) {
|
|
height: 0.5rem;
|
|
border-radius: 9999px;
|
|
background: transparent;
|
|
border: var(--cata-border-width) solid;
|
|
border-color: color-mix(in srgb, var(--cata-primary) 50%, transparent);
|
|
}
|
|
|
|
:deep(.cata-slider-track-custom) {
|
|
border-color: color-mix(in srgb, var(--slider-custom-color) 50%, transparent);
|
|
}
|
|
|
|
/* Range (parte rellenada) con color personalizado */
|
|
:deep(.cata-slider-range-custom) {
|
|
background-color: var(--slider-custom-color);
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
/* Diferentes opacidades para descriptivo vs afectivo */
|
|
:deep(.cata-slider-range-descriptivo) {
|
|
opacity: 0.4;
|
|
}
|
|
|
|
:deep(.cata-slider-range-afectivo) {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
:deep(.cata-slider-thumb) {
|
|
width: 1.75rem;
|
|
height: 1.75rem;
|
|
border-radius: 9999px;
|
|
background-color: var(--cata-bg);
|
|
border: 2px solid var(--cata-primary);
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
transition: all 0.2s ease;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
:deep(.cata-slider-thumb-custom) {
|
|
border-color: var(--slider-custom-color);
|
|
}
|
|
|
|
/* Iconos para diferenciar descriptivo de afectivo */
|
|
:deep(.cata-slider-thumb-descriptivo::before) {
|
|
content: '📊';
|
|
position: absolute;
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
:deep(.cata-slider-thumb-afectivo::before) {
|
|
content: '❤️';
|
|
position: absolute;
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
:deep(.cata-slider-thumb:hover) {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
:deep(.cata-slider-thumb:active) {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.dark :deep(.cata-slider-thumb) {
|
|
box-shadow: 0 0 6px var(--cata-primary);
|
|
}
|
|
|
|
.dark :deep(.cata-slider-thumb-custom) {
|
|
box-shadow: 0 0 6px var(--slider-custom-color);
|
|
}
|
|
|
|
.dark :deep(.cata-slider-thumb:hover) {
|
|
box-shadow: 0 0 12px var(--cata-primary);
|
|
}
|
|
|
|
.dark :deep(.cata-slider-thumb-custom:hover) {
|
|
box-shadow: 0 0 12px var(--slider-custom-color);
|
|
}
|
|
|
|
/* Animación de fade in */
|
|
.slider-intensidad.cata-fade-in {
|
|
animation: cata-fade-in 0.3s ease-out;
|
|
}
|
|
</style>
|