Files
cataRio/nuxt4/app/components/cata/SliderIntensidad.vue
josedario87 ab47142966
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m6s
Fix: Validar estructura de datos de colores personalizados
- 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
2025-10-18 03:25:28 -06:00

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>